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.
Files changed (38) hide show
  1. package/ChangeLog +34 -0
  2. package/lib/parser/parser.js +11 -11
  3. package/lib/parser/visitors/clause/alter-table/alter-table.js +9 -6
  4. package/lib/parser/visitors/clause/binary.js +17 -8
  5. package/lib/parser/visitors/clause/column-definition/column-definition.js +22 -9
  6. package/lib/parser/visitors/clause/create-index/create-index.js +1 -1
  7. package/lib/parser/visitors/clause/create-table/create-table.js +9 -4
  8. package/lib/parser/visitors/clause/create-view/create-view.js +2 -1
  9. package/lib/parser/visitors/clause/from/from.js +21 -60
  10. package/lib/parser/visitors/clause/from/group-by.js +8 -1
  11. package/lib/parser/visitors/clause/from/limit.js +1 -1
  12. package/lib/parser/visitors/clause/from/order-by.js +2 -1
  13. package/lib/parser/visitors/clause/from/table-func.js +10 -1
  14. package/lib/parser/visitors/clause/index.js +18 -2
  15. package/lib/parser/visitors/clause/insert/insert.js +44 -6
  16. package/lib/parser/visitors/clause/operand.js +11 -1
  17. package/lib/parser/visitors/clause/select/select.js +20 -9
  18. package/lib/parser/visitors/clause/where/where.js +30 -3
  19. package/lib/parser/visitors/clause/with-named/with-named.js +15 -1
  20. package/lib/printer/visitors/binary-expression.js +28 -5
  21. package/lib/printer/visitors/call-expression/clauses/add-constraint/add-constraint.js +4 -0
  22. package/lib/printer/visitors/call-expression/clauses/clauses.js +10 -1
  23. package/lib/printer/visitors/call-expression/clauses/create-index/create-index.js +10 -1
  24. package/lib/printer/visitors/call-expression/clauses/from/from.js +64 -47
  25. package/lib/printer/visitors/call-expression/clauses/into/into.js +5 -2
  26. package/lib/printer/visitors/call-expression/clauses/is-null/is-null.js +14 -0
  27. package/lib/printer/visitors/call-expression/clauses/json-op.js +8 -1
  28. package/lib/printer/visitors/call-expression/clauses/lateral-join/lateral-join.js +7 -11
  29. package/lib/printer/visitors/call-expression/clauses/not/not.js +13 -1
  30. package/lib/printer/visitors/call-expression/clauses/on-conflict/on-conflict.js +9 -2
  31. package/lib/printer/visitors/call-expression/clauses/references/references.js +26 -14
  32. package/lib/printer/visitors/call-expression/clauses/section/section.js +5 -1
  33. package/lib/printer/visitors/call-expression/clauses/select/select.js +7 -6
  34. package/lib/printer/visitors/call-expression/clauses/update/update.js +7 -11
  35. package/lib/printer/visitors/call-expression/clauses/window-frame/window-frame.js +2 -2
  36. package/lib/printer/visitors/logical-expression.js +10 -2
  37. package/lib/printer/visitors/string-literal.js +15 -1
  38. 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 convertCondition(node.expr);
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 ? columns.expr.items.map((column) => stringLiteral(column.name)) : [];
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
- export const BinaryExpression = (path, {write, traverse}) => {
11
- traverse(path.get('left'));
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
- const op = path.node.operator;
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
- write(` ${JS_TO_SQL_OP[op] || op} `);
16
- traverse(path.get('right'));
37
+ print(path.get('left'), operator, printer);
38
+ write(` ${JS_TO_SQL_OP[operator] || operator} `);
39
+ print(path.get('right'), operator, printer);
17
40
  };
@@ -0,0 +1,4 @@
1
+ export const addConstraint = (path, {write, traverse}) => {
2
+ write('ADD ');
3
+ traverse(path.get('arguments')[0]);
4
+ };
@@ -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
- const isCol = (arg) => !isConcurrently(arg) && !isIfNotExists(arg) && !isWhere(arg) && !isAccessMethod(arg);
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.node))
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(path.node.expression))
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 = (node) => isCallExpression(node) && [
28
- 'subquery',
29
- 'lateralSubquery',
30
- ].includes(node.callee.name);
25
+ const isSubqueryAlias = createTypeChecker([
26
+ ['+: node.callee.name', '=', 'subquery'],
27
+ ['+: node.callee.name', '=', 'lateralSubquery'],
28
+ ]);
31
29
 
32
- const CLAUSE_NAMES = [
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 TABLE_ARG_NAMES = [
46
- ...CLAUSE_NAMES,
47
- 'join',
48
- 'leftJoin',
49
- 'rightJoin',
50
- 'rightOuterJoin',
51
- 'innerJoin',
52
- 'leftOuterJoin',
53
- 'fullOuterJoin',
54
- 'naturalJoin',
55
- 'crossJoin',
56
- 'on',
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 isTableFunc = (node) => isCallExpression(node) && !TABLE_ARG_NAMES.includes(node.callee.name);
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(node))
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 = args.filter((arg) => !isClause(arg));
108
- const clauseArgs = args.filter(isClause);
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 {isTSAsExpression} = types;
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.type === 'ArrayExpression') {
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.type === 'ArrayExpression' && node.elements.length === 3 && node.elements[1].type === 'StringLiteral' && JSON_OPS.has(node.elements[1].value);
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 = (node) => isCallExpression(node) && [
9
- 'subquery',
10
- 'lateralSubquery',
11
- ].includes(node.callee.name);
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
- traverse(path.get('arguments')[0]);
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
- const isWhereArg = ({node}) => node.type === 'CallExpression' && node.callee.name === 'where';
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.type === 'Identifier' && action.node.name === 'nothing') {
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
- let wroteCol = false;
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(arg);
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.type === 'ArrayExpression') {
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 = (path) => isCallExpression(path.node) && [
33
- 'limitAll',
34
- 'offset',
35
- 'fetchFirst',
36
- 'forUpdate',
37
- ].includes(path.node.callee.name);
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 {types} from '@putout/babel';
1
+ import {createTypeChecker} from '@putout/printer/type-checker';
2
2
 
3
- const {isCallExpression} = types;
4
-
5
- const OR_ACTION_NAMES = [
6
- 'orReplace',
7
- 'orIgnore',
8
- 'orAbort',
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?.node) {
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?.node) {
26
+ if (expr && expr.node) {
27
27
  traverse(expr);
28
28
  write(' FOLLOWING');
29
29
 
@@ -1,4 +1,12 @@
1
- const needsParensIn = (node, op) => op === '&&' && node.type === 'LogicalExpression' && node.operator === '||';
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 === '*' || value.startsWith(':'))
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