Skip to content

Commit 118e90b

Browse files
authored
add lint rule always using AS to rename columns (#44)
1 parent a0e8107 commit 118e90b

File tree

11 files changed

+149
-11
lines changed

11 files changed

+149
-11
lines changed

packages/server/src/complete.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ function createTablesFromFromNodes(fromNodes: FromTableNode[]): Schema {
130130
}
131131
const columns = c.subquery.columns.map(v => {
132132
if (typeof v === 'string') { return null }
133-
return { columnName: v.as || v.expr.column || '', description: 'alias' }
133+
return { columnName: v.as || (v.expr.type === 'column_ref' && v.expr.column) || '', description: 'alias' }
134134
})
135135
return p.concat({ database: null, columns, tableName: c.as })
136136
}, [])
@@ -200,7 +200,7 @@ function completeSelectStatement(ast: SelectStatement, _pos: Pos, _schema: Schem
200200
const rest = ast.columns.slice(1, ast.columns.length)
201201
const lastColumn = rest.reduce((p, c) => p.location.end.offset < c.location.end.offset ? c : p ,first)
202202
if (
203-
FROM_KEYWORD.label.startsWith(lastColumn.expr.column) ||
203+
(lastColumn.expr.type === 'column_ref' && FROM_KEYWORD.label.startsWith(lastColumn.expr.column)) ||
204204
(lastColumn.as && FROM_KEYWORD.label.startsWith(lastColumn.as))
205205
) {
206206
candidates.push(FROM_KEYWORD)

packages/sql-parser/base/fromClauseParser.js

Lines changed: 4 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/sql-parser/base/parser.js

Lines changed: 4 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/sql-parser/index.d.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ export type ValuesClause = {
116116

117117
export type ColumnListItemNode = {
118118
type: 'column_list_item',
119-
expr: ColumnRefNode,
119+
expr: ColumnRefNode | AggrFuncNode,
120120
as: string | null,
121121
location: NodeRange
122122
}
@@ -128,6 +128,15 @@ export type ColumnRefNode = {
128128
location: NodeRange
129129
}
130130

131+
export type AggrFuncNode = {
132+
type: 'aggr_func'
133+
name: string
134+
args: {
135+
expr: ColumnRefNode
136+
},
137+
location: NodeRange
138+
}
139+
131140
export type StarNode = { type: 'star', value: '*' }
132141

133142
export type FromTableNode = TableNode | SubqueryNode | IncompleteSubqueryNode

packages/sql-parser/parser.pegjs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -831,7 +831,8 @@ aggr_fun_smma
831831
name : name,
832832
args : {
833833
expr : e
834-
}
834+
},
835+
location: location()
835836
}
836837
}
837838

@@ -843,7 +844,8 @@ aggr_fun_count
843844
return {
844845
type : 'aggr_func',
845846
name : name,
846-
args : arg
847+
args : arg,
848+
location: location()
847849
}
848850
}
849851

packages/sqlint/README.md

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ $ sqlint --fix .
7272
"linebreak-after-clause-keyword": "error",
7373
"reserved-word-case": ["error", "upper"],
7474
"space-surrounding-operators": "error",
75-
"where-clause-new-line": "error"
75+
"where-clause-new-line": "error",
76+
"align-where-clause-to-the-first": "error",
7677
}
7778
}
7879
```
@@ -270,4 +271,47 @@ foo.c = 'c' AND
270271
foo.d = 'd'
271272
```
272273

274+
##### align-where-clause-to-the-first
275+
276+
Where clauses must align to the first clause.
273277

278+
Good
279+
```sql
280+
SELECT foo.a
281+
FROM foo
282+
WHERE foo.a = 'a' AND foo.b = 'b' AND
283+
foo.c = 'c' AND
284+
foo.d = 'd'
285+
```
286+
287+
Bad
288+
```sql
289+
SELECT foo.a
290+
FROM foo
291+
WHERE foo.a = 'a' AND foo.b = 'b' AND
292+
foo.c = 'c' AND
293+
foo.d = 'd'
294+
```
295+
##### require-as-to-rename-column
296+
297+
As is always required to rename a column name.
298+
299+
Good
300+
```sql
301+
SELECT
302+
employees.name AS employee_name,
303+
COUNT(tasks.id) AS assigned_task_count
304+
FROM
305+
employees LEFT JOIN tasks
306+
ON employees.id = tasks.id
307+
```
308+
309+
Bad
310+
```sql
311+
SELECT
312+
employees.name employee_name,
313+
COUNT(tasks.id) assigned_task_count
314+
FROM
315+
employees LEFT JOIN tasks
316+
ON employees.id = tasks.id
317+
```

packages/sqlint/jest.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
module.exports = {
22
preset: 'ts-jest',
33
testEnvironment: 'node',
4+
modulePathIgnorePatterns: ['dist']
45
};

packages/sqlint/src/cli/loadConfig.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ const defaultConfig: Config = {
1919
'reserved-word-case': { level: ErrorLevel.Error, option: 'upper' },
2020
'space-surrounding-operators': { level: ErrorLevel.Error },
2121
'where-clause-new-line': { level: ErrorLevel.Error },
22-
'align-where-clause-to-the-first': { level: ErrorLevel.Error }
22+
'align-where-clause-to-the-first': { level: ErrorLevel.Error },
23+
'require-as-to-rename-column': { level: ErrorLevel.Error }
2324
}
2425
}
2526

packages/sqlint/src/rules/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { columnNewLine } from './columnNewLine'
55
import { alignColumnToTheFirst } from './alignColumnToTheFirst'
66
import { whereClauseNewLine } from './whereClauseNewLine'
77
import { alignWhereClauseToTheFirst } from './alignWhereClauseToTheFirst'
8+
import { requireAsToRenameColumn } from './requireAsToRenameColumn'
89
import { parse, NodeRange, AST, BaseNode } from '@joe-re/sql-parser'
910
import { Fixer, FixDescription, createFixer } from '../fixer'
1011

@@ -69,6 +70,7 @@ export function execute(sql: string, config: Config): Diagnostic[] {
6970
registerRule(alignColumnToTheFirst, config, sql)
7071
registerRule(whereClauseNewLine, config, sql)
7172
registerRule(alignWhereClauseToTheFirst, config, sql)
73+
registerRule(requireAsToRenameColumn, config, sql)
7274
const ast = parse(sql)
7375
return walk(ast)
7476
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { SelectStatement } from '@joe-re/sql-parser'
2+
import { Rule, RuleConfig } from './index'
3+
4+
const META = {
5+
name: 'require-as-to-rename-column',
6+
type: 'select'
7+
}
8+
9+
export const requireAsToRenameColumn: Rule<SelectStatement, RuleConfig> = {
10+
meta: META,
11+
create: (context) => {
12+
if (!Array.isArray(context.node.columns)) {
13+
return
14+
}
15+
const invalidColumns = context.node.columns.filter(v => v.as).filter(v => {
16+
return !context.getAfterSQL(v.expr.location).trim().match(/^(AS|as)\s/)
17+
})
18+
return invalidColumns.map(v => {
19+
return {
20+
message: 'Require AS keyword to rename a column',
21+
location: v.location,
22+
fix: (fixer) => {
23+
return fixer.insertText(v.expr.location.end.offset, ' AS')
24+
}
25+
}
26+
})
27+
}
28+
}

0 commit comments

Comments
 (0)