happy-sql 2.4.0 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ChangeLog +34 -0
- package/lib/parser/parser.js +11 -11
- package/lib/parser/visitors/clause/alter-table/alter-table.js +9 -6
- package/lib/parser/visitors/clause/binary.js +17 -8
- package/lib/parser/visitors/clause/column-definition/column-definition.js +22 -9
- package/lib/parser/visitors/clause/create-index/create-index.js +1 -1
- package/lib/parser/visitors/clause/create-table/create-table.js +9 -4
- package/lib/parser/visitors/clause/create-view/create-view.js +2 -1
- package/lib/parser/visitors/clause/from/from.js +21 -60
- package/lib/parser/visitors/clause/from/group-by.js +8 -1
- package/lib/parser/visitors/clause/from/limit.js +1 -1
- package/lib/parser/visitors/clause/from/order-by.js +2 -1
- package/lib/parser/visitors/clause/from/table-func.js +10 -1
- package/lib/parser/visitors/clause/index.js +18 -2
- package/lib/parser/visitors/clause/insert/insert.js +44 -6
- package/lib/parser/visitors/clause/operand.js +11 -1
- package/lib/parser/visitors/clause/select/select.js +20 -9
- package/lib/parser/visitors/clause/where/where.js +30 -3
- package/lib/parser/visitors/clause/with-named/with-named.js +15 -1
- package/lib/printer/visitors/binary-expression.js +28 -5
- package/lib/printer/visitors/call-expression/clauses/add-constraint/add-constraint.js +4 -0
- package/lib/printer/visitors/call-expression/clauses/clauses.js +10 -1
- package/lib/printer/visitors/call-expression/clauses/create-index/create-index.js +10 -1
- package/lib/printer/visitors/call-expression/clauses/from/from.js +64 -47
- package/lib/printer/visitors/call-expression/clauses/into/into.js +5 -2
- package/lib/printer/visitors/call-expression/clauses/is-null/is-null.js +14 -0
- package/lib/printer/visitors/call-expression/clauses/json-op.js +8 -1
- package/lib/printer/visitors/call-expression/clauses/lateral-join/lateral-join.js +7 -11
- package/lib/printer/visitors/call-expression/clauses/not/not.js +13 -1
- package/lib/printer/visitors/call-expression/clauses/on-conflict/on-conflict.js +9 -2
- package/lib/printer/visitors/call-expression/clauses/references/references.js +26 -14
- package/lib/printer/visitors/call-expression/clauses/section/section.js +5 -1
- package/lib/printer/visitors/call-expression/clauses/select/select.js +7 -6
- package/lib/printer/visitors/call-expression/clauses/update/update.js +7 -11
- package/lib/printer/visitors/call-expression/clauses/window-frame/window-frame.js +2 -2
- package/lib/printer/visitors/logical-expression.js +10 -2
- package/lib/printer/visitors/string-literal.js +15 -1
- package/package.json +3 -3
|
@@ -7,11 +7,22 @@ const {
|
|
|
7
7
|
logicalExpression,
|
|
8
8
|
identifier,
|
|
9
9
|
callExpression,
|
|
10
|
+
isLogicalExpression,
|
|
10
11
|
} = types;
|
|
11
12
|
|
|
12
13
|
const convertAnd = (node) => logicalExpression('&&', convertCondition(node.left), convertCondition(node.right));
|
|
13
14
|
const convertOr = (node) => logicalExpression('||', convertCondition(node.left), convertCondition(node.right));
|
|
14
15
|
|
|
16
|
+
const isNot = (node) => node.type === 'prefix_op_expr' && node.operator.name === 'NOT';
|
|
17
|
+
|
|
18
|
+
const keepParens = (node) => {
|
|
19
|
+
node.extra = {
|
|
20
|
+
parenthesized: true,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
return node;
|
|
24
|
+
};
|
|
25
|
+
|
|
15
26
|
const isNotBetween = (node) => Array.isArray(node.betweenKw) && node.betweenKw[0].name === 'NOT';
|
|
16
27
|
|
|
17
28
|
export const convertBetween = (node) => {
|
|
@@ -68,12 +79,28 @@ export const convertCondition = (node) => {
|
|
|
68
79
|
convertClause(node.expr.expr),
|
|
69
80
|
]);
|
|
70
81
|
|
|
71
|
-
if (isParenExpr(node))
|
|
72
|
-
return
|
|
82
|
+
if (isNot(node) && isParenExpr(node.expr))
|
|
83
|
+
return callExpression(identifier('not'), [
|
|
84
|
+
convertCondition(node.expr.expr),
|
|
85
|
+
]);
|
|
86
|
+
|
|
87
|
+
if (isParenExpr(node)) {
|
|
88
|
+
const result = convertCondition(node.expr);
|
|
89
|
+
|
|
90
|
+
if (isLogicalExpression(result))
|
|
91
|
+
return keepParens(result);
|
|
92
|
+
|
|
93
|
+
return result;
|
|
94
|
+
}
|
|
73
95
|
|
|
74
96
|
return convertBinary(node);
|
|
75
97
|
};
|
|
76
98
|
|
|
99
|
+
const stripParens = (node) => {
|
|
100
|
+
delete node.extra;
|
|
101
|
+
return node;
|
|
102
|
+
};
|
|
103
|
+
|
|
77
104
|
export const convertWhere = (node) => callExpression(identifier('where'), [
|
|
78
|
-
convertCondition(node.expr),
|
|
105
|
+
stripParens(convertCondition(node.expr)),
|
|
79
106
|
]);
|
|
@@ -15,9 +15,23 @@ const convertTable = ({table, expr}) => {
|
|
|
15
15
|
return assignmentExpression('=', identifier(table.name), convertClause(expr.expr));
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
+
const convertColumn = (column) => stringLiteral(column.name);
|
|
19
|
+
|
|
20
|
+
const getColArgs = (columns) => {
|
|
21
|
+
if (!columns)
|
|
22
|
+
return [];
|
|
23
|
+
|
|
24
|
+
const args = [];
|
|
25
|
+
|
|
26
|
+
for (const col of columns.expr.items)
|
|
27
|
+
args.push(convertColumn(col));
|
|
28
|
+
|
|
29
|
+
return args;
|
|
30
|
+
};
|
|
31
|
+
|
|
18
32
|
// recursive CTE: numbers('value', unionAll(...))
|
|
19
33
|
const convertRecursiveTable = ({table, columns, expr}) => {
|
|
20
|
-
const colArgs = columns
|
|
34
|
+
const colArgs = getColArgs(columns);
|
|
21
35
|
const body = convertClause(expr.expr);
|
|
22
36
|
|
|
23
37
|
return callExpression(identifier(table.name), [...colArgs, body]);
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {types} from '@putout/babel';
|
|
2
|
+
|
|
3
|
+
const {isBinaryExpression} = types;
|
|
4
|
+
|
|
1
5
|
const JS_TO_SQL_OP = {
|
|
2
6
|
'===': '=',
|
|
3
7
|
'!==': '!=',
|
|
@@ -7,11 +11,30 @@ const JS_TO_SQL_OP = {
|
|
|
7
11
|
'<': '<',
|
|
8
12
|
};
|
|
9
13
|
|
|
10
|
-
|
|
11
|
-
|
|
14
|
+
const ADDITIVE = ['+', '-'];
|
|
15
|
+
const MULTIPLICATIVE = ['*', '/', '%'];
|
|
16
|
+
|
|
17
|
+
const needsParens = (node, operator) => {
|
|
18
|
+
if (!isBinaryExpression(node))
|
|
19
|
+
return false;
|
|
20
|
+
|
|
21
|
+
return MULTIPLICATIVE.includes(operator) && ADDITIVE.includes(node.operator);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const print = (path, operator, {write, traverse}) => {
|
|
25
|
+
if (!needsParens(path.node, operator))
|
|
26
|
+
return traverse(path);
|
|
12
27
|
|
|
13
|
-
|
|
28
|
+
write('(');
|
|
29
|
+
traverse(path);
|
|
30
|
+
write(')');
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const BinaryExpression = (path, printer) => {
|
|
34
|
+
const {write} = printer;
|
|
35
|
+
const {operator} = path.node;
|
|
14
36
|
|
|
15
|
-
|
|
16
|
-
|
|
37
|
+
print(path.get('left'), operator, printer);
|
|
38
|
+
write(` ${JS_TO_SQL_OP[operator] || operator} `);
|
|
39
|
+
print(path.get('right'), operator, printer);
|
|
17
40
|
};
|
|
@@ -108,6 +108,7 @@ import {withoutRowid} from './without-rowid/without-rowid.js';
|
|
|
108
108
|
import {createTableAs} from './create-table-as/create-table-as.js';
|
|
109
109
|
import {alterTable} from './alter-table/alter-table.js';
|
|
110
110
|
import {addColumn} from './add-column/add-column.js';
|
|
111
|
+
import {addConstraint} from './add-constraint/add-constraint.js';
|
|
111
112
|
import {dropColumn} from './drop-column/drop-column.js';
|
|
112
113
|
import {renameColumn} from './rename-column/rename-column.js';
|
|
113
114
|
import {alterColumnType} from './alter-column-type/alter-column-type.js';
|
|
@@ -137,10 +138,15 @@ import {lastInsertRowid} from './last-insert-rowid/last-insert-rowid.js';
|
|
|
137
138
|
import {lastval} from './lastval/lastval.js';
|
|
138
139
|
import {defaultNextval} from './default-nextval/default-nextval.js';
|
|
139
140
|
import {like} from './like/like.js';
|
|
140
|
-
import {isNull, isNotNull} from './is-null/is-null.js';
|
|
141
141
|
import {orReplace} from './or-replace/or-replace.js';
|
|
142
142
|
import {orIgnore} from './or-ignore/or-ignore.js';
|
|
143
143
|
import {inList} from './in-list/in-list.js';
|
|
144
|
+
import {
|
|
145
|
+
isNull,
|
|
146
|
+
isNotNull,
|
|
147
|
+
is,
|
|
148
|
+
isNot,
|
|
149
|
+
} from './is-null/is-null.js';
|
|
144
150
|
import {
|
|
145
151
|
identity,
|
|
146
152
|
identityByDefault,
|
|
@@ -278,6 +284,7 @@ export const clauses = {
|
|
|
278
284
|
createTableAs,
|
|
279
285
|
alterTable,
|
|
280
286
|
addColumn,
|
|
287
|
+
addConstraint,
|
|
281
288
|
dropColumn,
|
|
282
289
|
renameColumn,
|
|
283
290
|
alterColumnType,
|
|
@@ -313,6 +320,8 @@ export const clauses = {
|
|
|
313
320
|
isDistinctFrom,
|
|
314
321
|
collate,
|
|
315
322
|
isNull,
|
|
323
|
+
is,
|
|
324
|
+
isNot,
|
|
316
325
|
isNotNull,
|
|
317
326
|
inList,
|
|
318
327
|
identity,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {types} from '@putout/babel';
|
|
2
|
+
import {createTypeChecker} from '@putout/printer/type-checker';
|
|
2
3
|
|
|
3
4
|
const {isCallExpression} = types;
|
|
4
5
|
|
|
@@ -6,7 +7,15 @@ const isConcurrently = (arg) => isCallExpression(arg.node) && arg.node.callee.na
|
|
|
6
7
|
const isIfNotExists = (arg) => isCallExpression(arg.node) && arg.node.callee.name === 'ifNotExists';
|
|
7
8
|
const isWhere = (arg) => isCallExpression(arg.node) && arg.node.callee.name === 'where';
|
|
8
9
|
const isAccessMethod = (arg) => isCallExpression(arg.node) && arg.node.callee.name === 'accessMethod';
|
|
9
|
-
|
|
10
|
+
|
|
11
|
+
const isOption = createTypeChecker([
|
|
12
|
+
['+: node.callee.name', '=', 'concurrently'],
|
|
13
|
+
['+: node.callee.name', '=', 'ifNotExists'],
|
|
14
|
+
['+: node.callee.name', '=', 'where'],
|
|
15
|
+
['+: node.callee.name', '=', 'accessMethod'],
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
const isCol = (arg) => !isOption(arg);
|
|
10
19
|
|
|
11
20
|
export const printIndexBody = (path, {write, traverse}) => {
|
|
12
21
|
const [name, table, ...rest] = path.get('arguments');
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import {types} from '@putout/babel';
|
|
2
|
+
import {createTypeChecker} from '@putout/printer/type-checker';
|
|
2
3
|
|
|
3
|
-
const {
|
|
4
|
-
isTSAsExpression,
|
|
5
|
-
isCallExpression,
|
|
6
|
-
} = types;
|
|
4
|
+
const {isTSAsExpression} = types;
|
|
7
5
|
|
|
8
6
|
const printAsExpression = (path, {write, traverse}) => {
|
|
9
7
|
const expr = path.get('expression');
|
|
10
8
|
|
|
11
|
-
if (isTableFunc(expr
|
|
9
|
+
if (isTableFunc(expr))
|
|
12
10
|
printTableFunc(expr, {
|
|
13
11
|
write,
|
|
14
12
|
traverse,
|
|
@@ -16,7 +14,7 @@ const printAsExpression = (path, {write, traverse}) => {
|
|
|
16
14
|
else
|
|
17
15
|
traverse(expr);
|
|
18
16
|
|
|
19
|
-
if (isSubqueryAlias(
|
|
17
|
+
if (isSubqueryAlias(expr))
|
|
20
18
|
write(' ');
|
|
21
19
|
else
|
|
22
20
|
write(' AS ');
|
|
@@ -24,53 +22,65 @@ const printAsExpression = (path, {write, traverse}) => {
|
|
|
24
22
|
write(path.node.typeAnnotation.literal.value);
|
|
25
23
|
};
|
|
26
24
|
|
|
27
|
-
const isSubqueryAlias = (
|
|
28
|
-
'subquery',
|
|
29
|
-
'lateralSubquery',
|
|
30
|
-
]
|
|
25
|
+
const isSubqueryAlias = createTypeChecker([
|
|
26
|
+
['+: node.callee.name', '=', 'subquery'],
|
|
27
|
+
['+: node.callee.name', '=', 'lateralSubquery'],
|
|
28
|
+
]);
|
|
31
29
|
|
|
32
|
-
const
|
|
33
|
-
'where',
|
|
34
|
-
'groupBy',
|
|
35
|
-
'having',
|
|
36
|
-
'namedWindow',
|
|
37
|
-
'orderBy',
|
|
38
|
-
'limit',
|
|
39
|
-
'limitAll',
|
|
40
|
-
'offset',
|
|
41
|
-
'fetchFirst',
|
|
42
|
-
'forUpdate',
|
|
43
|
-
]
|
|
30
|
+
const isTableFunc = createTypeChecker([
|
|
31
|
+
['-: node.callee.name', '=', 'where'],
|
|
32
|
+
['-: node.callee.name', '=', 'groupBy'],
|
|
33
|
+
['-: node.callee.name', '=', 'having'],
|
|
34
|
+
['-: node.callee.name', '=', 'namedWindow'],
|
|
35
|
+
['-: node.callee.name', '=', 'orderBy'],
|
|
36
|
+
['-: node.callee.name', '=', 'limit'],
|
|
37
|
+
['-: node.callee.name', '=', 'limitAll'],
|
|
38
|
+
['-: node.callee.name', '=', 'offset'],
|
|
39
|
+
['-: node.callee.name', '=', 'fetchFirst'],
|
|
40
|
+
['-: node.callee.name', '=', 'forUpdate'],
|
|
41
|
+
['-: node.callee.name', '=', 'join'],
|
|
42
|
+
['-: node.callee.name', '=', 'leftJoin'],
|
|
43
|
+
['-: node.callee.name', '=', 'rightJoin'],
|
|
44
|
+
['-: node.callee.name', '=', 'rightOuterJoin'],
|
|
45
|
+
['-: node.callee.name', '=', 'innerJoin'],
|
|
46
|
+
['-: node.callee.name', '=', 'leftOuterJoin'],
|
|
47
|
+
['-: node.callee.name', '=', 'fullOuterJoin'],
|
|
48
|
+
['-: node.callee.name', '=', 'naturalJoin'],
|
|
49
|
+
['-: node.callee.name', '=', 'crossJoin'],
|
|
50
|
+
['-: node.callee.name', '=', 'on'],
|
|
51
|
+
['-: node.callee.name', '=', 'subquery'],
|
|
52
|
+
['-: node.callee.name', '=', 'lateralJoin'],
|
|
53
|
+
['-: node.callee.name', '=', 'lateralSubquery'],
|
|
54
|
+
['-: node.callee.name', '=', 'indexedBy'],
|
|
55
|
+
['-: node.callee.name', '=', 'notIndexed'],
|
|
56
|
+
'+: node -> CallExpression',
|
|
57
|
+
]);
|
|
44
58
|
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
'
|
|
48
|
-
'
|
|
49
|
-
'
|
|
50
|
-
'
|
|
51
|
-
'
|
|
52
|
-
'
|
|
53
|
-
'
|
|
54
|
-
'
|
|
55
|
-
'
|
|
56
|
-
|
|
57
|
-
'subquery',
|
|
58
|
-
'lateralJoin',
|
|
59
|
-
'lateralSubquery',
|
|
60
|
-
'indexedBy',
|
|
61
|
-
'notIndexed',
|
|
62
|
-
];
|
|
63
|
-
|
|
64
|
-
const isClause = ({node}) => isCallExpression(node) && CLAUSE_NAMES.includes(node.callee.name);
|
|
59
|
+
const isClause = createTypeChecker([
|
|
60
|
+
['+: node.callee.name', '=', 'where'],
|
|
61
|
+
['+: node.callee.name', '=', 'groupBy'],
|
|
62
|
+
['+: node.callee.name', '=', 'having'],
|
|
63
|
+
['+: node.callee.name', '=', 'namedWindow'],
|
|
64
|
+
['+: node.callee.name', '=', 'orderBy'],
|
|
65
|
+
['+: node.callee.name', '=', 'limit'],
|
|
66
|
+
['+: node.callee.name', '=', 'limitAll'],
|
|
67
|
+
['+: node.callee.name', '=', 'offset'],
|
|
68
|
+
['+: node.callee.name', '=', 'fetchFirst'],
|
|
69
|
+
['+: node.callee.name', '=', 'forUpdate'],
|
|
70
|
+
]);
|
|
65
71
|
|
|
66
72
|
const CAMEL_RE = /([A-Z])/g;
|
|
67
73
|
const toSnake = (a) => a.replace(CAMEL_RE, (_, c) => `_${c.toLowerCase()}`);
|
|
68
74
|
|
|
69
75
|
// single-word ALL-CAPS functions (UNNEST) keep their casing
|
|
70
76
|
const ALL_CAPS_RE = /^[A-Z]+$/;
|
|
71
|
-
const toFuncName = (name) => ALL_CAPS_RE.test(name) ? name : toSnake(name);
|
|
72
77
|
|
|
73
|
-
const
|
|
78
|
+
const toFuncName = (name) => {
|
|
79
|
+
if (ALL_CAPS_RE.test(name))
|
|
80
|
+
return name;
|
|
81
|
+
|
|
82
|
+
return toSnake(name);
|
|
83
|
+
};
|
|
74
84
|
|
|
75
85
|
const printTableFunc = (path, {write, traverse}) => {
|
|
76
86
|
const {name} = path.node.callee;
|
|
@@ -96,7 +106,7 @@ const printArg = (path, printer) => {
|
|
|
96
106
|
if (isTSAsExpression(node))
|
|
97
107
|
return printAsExpression(path, printer);
|
|
98
108
|
|
|
99
|
-
if (isTableFunc(
|
|
109
|
+
if (isTableFunc(path))
|
|
100
110
|
return printTableFunc(path, printer);
|
|
101
111
|
|
|
102
112
|
traverse(path);
|
|
@@ -104,8 +114,15 @@ const printArg = (path, printer) => {
|
|
|
104
114
|
|
|
105
115
|
export const from = (path, {write, traverse}) => {
|
|
106
116
|
const args = path.get('arguments');
|
|
107
|
-
const tableArgs =
|
|
108
|
-
const clauseArgs =
|
|
117
|
+
const tableArgs = [];
|
|
118
|
+
const clauseArgs = [];
|
|
119
|
+
|
|
120
|
+
for (const arg of args) {
|
|
121
|
+
if (isClause(arg))
|
|
122
|
+
clauseArgs.push(arg);
|
|
123
|
+
else
|
|
124
|
+
tableArgs.push(arg);
|
|
125
|
+
}
|
|
109
126
|
|
|
110
127
|
write('FROM ');
|
|
111
128
|
printArg(tableArgs[0], {
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import {types} from '@putout/babel';
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const {
|
|
4
|
+
isTSAsExpression,
|
|
5
|
+
isArrayExpression,
|
|
6
|
+
} = types;
|
|
4
7
|
|
|
5
8
|
const printTable = (table, {write, traverse}) => {
|
|
6
9
|
if (isTSAsExpression(table.node)) {
|
|
@@ -36,7 +39,7 @@ export const into = (path, {write, traverse}) => {
|
|
|
36
39
|
|
|
37
40
|
write(' (');
|
|
38
41
|
|
|
39
|
-
if (columns.node
|
|
42
|
+
if (isArrayExpression(columns.node)) {
|
|
40
43
|
const colArgs = columns.get('elements');
|
|
41
44
|
|
|
42
45
|
for (let i = 0; i < colArgs.length; i++) {
|
|
@@ -9,3 +9,17 @@ export const isNotNull = (path, {write, traverse}) => {
|
|
|
9
9
|
traverse(args[0]);
|
|
10
10
|
write(' IS NOT NULL');
|
|
11
11
|
};
|
|
12
|
+
|
|
13
|
+
export const is = (path, {write, traverse}) => {
|
|
14
|
+
const args = path.get('arguments');
|
|
15
|
+
traverse(args[0]);
|
|
16
|
+
write(' IS ');
|
|
17
|
+
traverse(args[1]);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const isNot = (path, {write, traverse}) => {
|
|
21
|
+
const args = path.get('arguments');
|
|
22
|
+
traverse(args[0]);
|
|
23
|
+
write(' IS NOT ');
|
|
24
|
+
traverse(args[1]);
|
|
25
|
+
};
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
import {types} from '@putout/babel';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
isStringLiteral,
|
|
5
|
+
isArrayExpression,
|
|
6
|
+
} = types;
|
|
7
|
+
|
|
1
8
|
const JSON_OPS = new Set([
|
|
2
9
|
'->',
|
|
3
10
|
'->>',
|
|
@@ -11,7 +18,7 @@ const JSON_OPS = new Set([
|
|
|
11
18
|
'<@',
|
|
12
19
|
]);
|
|
13
20
|
|
|
14
|
-
export const isJsonTuple = (node) => node
|
|
21
|
+
export const isJsonTuple = (node) => isArrayExpression(node) && node.elements.length === 3 && isStringLiteral(node.elements[1]) && JSON_OPS.has(node.elements[1].value);
|
|
15
22
|
|
|
16
23
|
export const printJsonTuple = (path, {write, traverse}) => {
|
|
17
24
|
const elems = path.get('elements');
|
|
@@ -1,21 +1,17 @@
|
|
|
1
1
|
import {types} from '@putout/babel';
|
|
2
|
+
import {createTypeChecker} from '@putout/printer/type-checker';
|
|
2
3
|
|
|
3
|
-
const {
|
|
4
|
-
isTSAsExpression,
|
|
5
|
-
isCallExpression,
|
|
6
|
-
} = types;
|
|
4
|
+
const {isTSAsExpression} = types;
|
|
7
5
|
|
|
8
|
-
const isSubqueryAlias = (
|
|
9
|
-
'subquery',
|
|
10
|
-
'lateralSubquery',
|
|
11
|
-
]
|
|
6
|
+
const isSubqueryAlias = createTypeChecker([
|
|
7
|
+
['+: node.callee.name', '=', 'subquery'],
|
|
8
|
+
['+: node.callee.name', '=', 'lateralSubquery'],
|
|
9
|
+
]);
|
|
12
10
|
|
|
13
11
|
const printAsExpression = (path, {write, traverse}) => {
|
|
14
|
-
const {expression} = path.node;
|
|
15
|
-
|
|
16
12
|
traverse(path.get('expression'));
|
|
17
13
|
|
|
18
|
-
if (isSubqueryAlias(expression))
|
|
14
|
+
if (isSubqueryAlias(path.get('expression')))
|
|
19
15
|
write(' ');
|
|
20
16
|
else
|
|
21
17
|
write(' AS ');
|
|
@@ -1,4 +1,16 @@
|
|
|
1
|
+
import {types} from '@putout/babel';
|
|
2
|
+
|
|
3
|
+
const {isLogicalExpression} = types;
|
|
4
|
+
|
|
1
5
|
export const not = (path, {write, traverse}) => {
|
|
6
|
+
const [arg] = path.get('arguments');
|
|
7
|
+
|
|
2
8
|
write('NOT ');
|
|
3
|
-
|
|
9
|
+
|
|
10
|
+
if (!isLogicalExpression(arg.node))
|
|
11
|
+
return traverse(arg);
|
|
12
|
+
|
|
13
|
+
write('(');
|
|
14
|
+
traverse(arg);
|
|
15
|
+
write(')');
|
|
4
16
|
};
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
import {types} from '@putout/babel';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
isIdentifier,
|
|
5
|
+
isCallExpression,
|
|
6
|
+
} = types;
|
|
7
|
+
|
|
8
|
+
const isWhereArg = ({node}) => isCallExpression(node) && node.callee.name === 'where';
|
|
2
9
|
|
|
3
10
|
export const onConflict = (path, {write, traverse}) => {
|
|
4
11
|
const args = path.get('arguments');
|
|
@@ -34,7 +41,7 @@ export const onConflict = (path, {write, traverse}) => {
|
|
|
34
41
|
|
|
35
42
|
write(' DO ');
|
|
36
43
|
|
|
37
|
-
if (action.node
|
|
44
|
+
if (isIdentifier(action.node) && action.node.name === 'nothing') {
|
|
38
45
|
write('NOTHING');
|
|
39
46
|
return;
|
|
40
47
|
}
|
|
@@ -1,27 +1,39 @@
|
|
|
1
|
+
const getCalleeName = (node) => {
|
|
2
|
+
if (!node || !node.callee)
|
|
3
|
+
return null;
|
|
4
|
+
|
|
5
|
+
return node.callee.name;
|
|
6
|
+
};
|
|
7
|
+
|
|
1
8
|
export const references = (path, {write, traverse}) => {
|
|
2
9
|
const [first, ...other] = path.get('arguments');
|
|
10
|
+
const cols = [];
|
|
11
|
+
const actions = [];
|
|
12
|
+
|
|
13
|
+
for (const arg of other) {
|
|
14
|
+
const name = getCalleeName(arg.node);
|
|
15
|
+
|
|
16
|
+
if (name === 'onDelete' || name === 'onUpdate')
|
|
17
|
+
actions.push(arg);
|
|
18
|
+
else
|
|
19
|
+
cols.push(arg);
|
|
20
|
+
}
|
|
3
21
|
|
|
4
22
|
write('REFERENCES ');
|
|
5
23
|
traverse(first);
|
|
6
24
|
write('(');
|
|
7
25
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
for (const arg of other) {
|
|
11
|
-
const name = arg.node?.callee?.name;
|
|
12
|
-
|
|
13
|
-
if (name === 'onDelete' || name === 'onUpdate') {
|
|
14
|
-
write(' ');
|
|
15
|
-
traverse(arg);
|
|
16
|
-
continue;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
if (wroteCol)
|
|
26
|
+
for (const [i, col] of cols.entries()) {
|
|
27
|
+
if (i > 0)
|
|
20
28
|
write(', ');
|
|
21
29
|
|
|
22
|
-
traverse(
|
|
23
|
-
wroteCol = true;
|
|
30
|
+
traverse(col);
|
|
24
31
|
}
|
|
25
32
|
|
|
26
33
|
write(')');
|
|
34
|
+
|
|
35
|
+
for (const action of actions) {
|
|
36
|
+
write(' ');
|
|
37
|
+
traverse(action);
|
|
38
|
+
}
|
|
27
39
|
};
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
import {types} from '@putout/babel';
|
|
2
|
+
|
|
3
|
+
const {isArrayExpression} = types;
|
|
4
|
+
|
|
1
5
|
export const section = (path, {write, traverse}) => {
|
|
2
6
|
const [name, stmt] = path.get('arguments');
|
|
3
7
|
write(`-- ${name.node.value}\n`);
|
|
4
8
|
|
|
5
|
-
if (stmt.node
|
|
9
|
+
if (isArrayExpression(stmt.node)) {
|
|
6
10
|
const elements = stmt.get('elements');
|
|
7
11
|
const lastIndex = elements.length - 1;
|
|
8
12
|
|
|
@@ -29,12 +29,13 @@ const isFrom = createTypeChecker([
|
|
|
29
29
|
['+: node.callee.name', '=', 'from'],
|
|
30
30
|
]);
|
|
31
31
|
|
|
32
|
-
const isClauseName = (
|
|
33
|
-
'limitAll',
|
|
34
|
-
'offset',
|
|
35
|
-
'fetchFirst',
|
|
36
|
-
'forUpdate',
|
|
37
|
-
|
|
32
|
+
const isClauseName = createTypeChecker([
|
|
33
|
+
['+: node.callee.name', '=', 'limitAll'],
|
|
34
|
+
['+: node.callee.name', '=', 'offset'],
|
|
35
|
+
['+: node.callee.name', '=', 'fetchFirst'],
|
|
36
|
+
['+: node.callee.name', '=', 'forUpdate'],
|
|
37
|
+
['+: node.callee.name', '=', 'where'],
|
|
38
|
+
]);
|
|
38
39
|
|
|
39
40
|
const isDistinctOn = (path) => isCallExpression(path.node) && path.node.callee.name === 'distinctOn';
|
|
40
41
|
|
|
@@ -1,15 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {createTypeChecker} from '@putout/printer/type-checker';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
'
|
|
7
|
-
'
|
|
8
|
-
|
|
9
|
-
'orFail',
|
|
10
|
-
];
|
|
11
|
-
|
|
12
|
-
const isOrAction = (arg) => isCallExpression(arg.node) && OR_ACTION_NAMES.includes(arg.node.callee.name);
|
|
3
|
+
const isOrAction = createTypeChecker([
|
|
4
|
+
['+: node.callee.name', '=', 'orReplace'],
|
|
5
|
+
['+: node.callee.name', '=', 'orIgnore'],
|
|
6
|
+
['+: node.callee.name', '=', 'orAbort'],
|
|
7
|
+
['+: node.callee.name', '=', 'orFail'],
|
|
8
|
+
]);
|
|
13
9
|
|
|
14
10
|
export const update = (path, {write, traverse}) => {
|
|
15
11
|
const args = path.get('arguments');
|
|
@@ -10,7 +10,7 @@ export const rowsBetween = (path, {write, traverse}) => {
|
|
|
10
10
|
export const preceding = (path, {write, traverse}) => {
|
|
11
11
|
const [expr] = path.get('arguments');
|
|
12
12
|
|
|
13
|
-
if (expr
|
|
13
|
+
if (expr && expr.node) {
|
|
14
14
|
traverse(expr);
|
|
15
15
|
write(' PRECEDING');
|
|
16
16
|
|
|
@@ -23,7 +23,7 @@ export const preceding = (path, {write, traverse}) => {
|
|
|
23
23
|
export const following = (path, {write, traverse}) => {
|
|
24
24
|
const [expr] = path.get('arguments');
|
|
25
25
|
|
|
26
|
-
if (expr
|
|
26
|
+
if (expr && expr.node) {
|
|
27
27
|
traverse(expr);
|
|
28
28
|
write(' FOLLOWING');
|
|
29
29
|
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
import {types} from '@putout/babel';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
isLogicalExpression,
|
|
5
|
+
isCallExpression,
|
|
6
|
+
} = types;
|
|
7
|
+
|
|
8
|
+
const isInNot = ({parent}) => isCallExpression(parent) && parent.callee.name === 'not';
|
|
9
|
+
const needsParensIn = (node, op) => op === '&&' && isLogicalExpression(node) && node.operator === '||';
|
|
2
10
|
|
|
3
11
|
const printNestedOr = (node, printer) => {
|
|
4
12
|
const {write, traverse} = printer;
|
|
@@ -15,7 +23,7 @@ export const LogicalExpression = (path, {write, traverse}) => {
|
|
|
15
23
|
const left = path.get('left');
|
|
16
24
|
const right = path.get('right');
|
|
17
25
|
const hasNestedParen = needsParensIn(left.node, node.operator) || needsParensIn(right.node, node.operator);
|
|
18
|
-
const separator = hasNestedParen ? ` ${op} ` : `\n${op} `;
|
|
26
|
+
const separator = hasNestedParen || isInNot(path) ? ` ${op} ` : `\n${op} `;
|
|
19
27
|
|
|
20
28
|
if (needsParensIn(left.node, node.operator))
|
|
21
29
|
printNestedOr(left, {
|
|
@@ -1,7 +1,21 @@
|
|
|
1
|
+
import {createTypeChecker} from '@putout/printer/type-checker';
|
|
2
|
+
|
|
3
|
+
const isValueContext = createTypeChecker([
|
|
4
|
+
'+: parent -> BinaryExpression',
|
|
5
|
+
[
|
|
6
|
+
'+: parent.callee.name',
|
|
7
|
+
'=',
|
|
8
|
+
'inList',
|
|
9
|
+
],
|
|
10
|
+
]);
|
|
11
|
+
|
|
1
12
|
export const StringLiteral = (path, {write}) => {
|
|
2
13
|
const {value} = path.node;
|
|
3
14
|
|
|
4
|
-
if (value === '*'
|
|
15
|
+
if (value === '*' && !isValueContext(path))
|
|
16
|
+
return write(value);
|
|
17
|
+
|
|
18
|
+
if (value.startsWith(':'))
|
|
5
19
|
return write(value);
|
|
6
20
|
|
|
7
21
|
// quoted identifier like "weird col" — written as is
|