happy-sql 2.3.0 → 2.4.1

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 (112) hide show
  1. package/ChangeLog +88 -0
  2. package/bin/convert.js +3 -9
  3. package/lib/parser/parser.js +11 -11
  4. package/lib/parser/visitors/clause/alter-table/alter-table.js +24 -0
  5. package/lib/parser/visitors/clause/binary.js +31 -7
  6. package/lib/parser/visitors/clause/column-definition/column-definition.js +62 -8
  7. package/lib/parser/visitors/clause/count/count.js +5 -2
  8. package/lib/parser/visitors/clause/create-extension/create-extension.js +7 -0
  9. package/lib/parser/visitors/clause/create-index/create-index.js +36 -5
  10. package/lib/parser/visitors/clause/create-schema/create-schema.js +7 -0
  11. package/lib/parser/visitors/clause/create-table/create-table.js +63 -7
  12. package/lib/parser/visitors/clause/create-view/create-view.js +18 -0
  13. package/lib/parser/visitors/clause/drop-index/drop-index.js +3 -0
  14. package/lib/parser/visitors/clause/drop-view/drop-view.js +15 -0
  15. package/lib/parser/visitors/clause/explain/explain.js +16 -0
  16. package/lib/parser/visitors/clause/from/from.js +109 -13
  17. package/lib/parser/visitors/clause/from/group-by.js +8 -1
  18. package/lib/parser/visitors/clause/from/limit.js +13 -0
  19. package/lib/parser/visitors/clause/from/order-by.js +15 -3
  20. package/lib/parser/visitors/clause/from/table-func.js +10 -14
  21. package/lib/parser/visitors/clause/grant/grant.js +26 -0
  22. package/lib/parser/visitors/clause/index.js +66 -2
  23. package/lib/parser/visitors/clause/insert/insert.js +81 -9
  24. package/lib/parser/visitors/clause/operand.js +55 -1
  25. package/lib/parser/visitors/clause/pragma/pragma.js +52 -2
  26. package/lib/parser/visitors/clause/select/select.js +149 -9
  27. package/lib/parser/visitors/clause/set-param/set-param.js +19 -0
  28. package/lib/parser/visitors/clause/table-constraint.js +27 -0
  29. package/lib/parser/visitors/clause/update/update.js +17 -0
  30. package/lib/parser/visitors/clause/where/where.js +17 -0
  31. package/lib/parser/visitors/clause/with-named/with-named.js +15 -1
  32. package/lib/printer/visitors/call-expression/clauses/access-method/access-method.js +4 -0
  33. package/lib/printer/visitors/call-expression/clauses/add-constraint/add-constraint.js +4 -0
  34. package/lib/printer/visitors/call-expression/clauses/analyze/analyze.js +10 -0
  35. package/lib/printer/visitors/call-expression/clauses/array-type/array-type.js +4 -0
  36. package/lib/printer/visitors/call-expression/clauses/clauses.js +134 -2
  37. package/lib/printer/visitors/call-expression/clauses/collate/collate.js +7 -0
  38. package/lib/printer/visitors/call-expression/clauses/concurrently/concurrently.js +2 -0
  39. package/lib/printer/visitors/call-expression/clauses/constraint/constraint.js +11 -0
  40. package/lib/printer/visitors/call-expression/clauses/count-distinct/count-distinct.js +13 -0
  41. package/lib/printer/visitors/call-expression/clauses/create-extension/create-extension.js +4 -0
  42. package/lib/printer/visitors/call-expression/clauses/create-index/create-index.js +43 -9
  43. package/lib/printer/visitors/call-expression/clauses/create-index/create-unique-index.js +5 -19
  44. package/lib/printer/visitors/call-expression/clauses/create-materialized-view/create-materialized-view.js +8 -0
  45. package/lib/printer/visitors/call-expression/clauses/create-schema/create-schema.js +4 -0
  46. package/lib/printer/visitors/call-expression/clauses/create-table/create-table.js +16 -1
  47. package/lib/printer/visitors/call-expression/clauses/create-table-as/create-table-as.js +8 -0
  48. package/lib/printer/visitors/call-expression/clauses/create-temp-table/create-temp-table.js +11 -0
  49. package/lib/printer/visitors/call-expression/clauses/create-view/create-view.js +8 -0
  50. package/lib/printer/visitors/call-expression/clauses/create-virtual-table/create-virtual-table.js +8 -0
  51. package/lib/printer/visitors/call-expression/clauses/{nextval/nextval.js → default-nextval/default-nextval.js} +2 -1
  52. package/lib/printer/visitors/call-expression/clauses/default-values/default-values.js +3 -0
  53. package/lib/printer/visitors/call-expression/clauses/detach/detach.js +6 -0
  54. package/lib/printer/visitors/call-expression/clauses/distinct-on/distinct-on.js +14 -0
  55. package/lib/printer/visitors/call-expression/clauses/do-block/do-block.js +5 -0
  56. package/lib/printer/visitors/call-expression/clauses/drop-index/drop-index.js +13 -1
  57. package/lib/printer/visitors/call-expression/clauses/drop-view/drop-view.js +24 -0
  58. package/lib/printer/visitors/call-expression/clauses/explain/explain.js +4 -0
  59. package/lib/printer/visitors/call-expression/clauses/explain-analyze/explain-analyze.js +4 -0
  60. package/lib/printer/visitors/call-expression/clauses/explain-query-plan/explain-query-plan.js +4 -0
  61. package/lib/printer/visitors/call-expression/clauses/fetch-first/fetch-first.js +5 -0
  62. package/lib/printer/visitors/call-expression/clauses/filter/filter.js +8 -0
  63. package/lib/printer/visitors/call-expression/clauses/for-update/for-update.js +3 -0
  64. package/lib/printer/visitors/call-expression/clauses/from/from.js +49 -5
  65. package/lib/printer/visitors/call-expression/clauses/generated-always-as/generated-always-as.js +10 -0
  66. package/lib/printer/visitors/call-expression/clauses/grant/grant.js +10 -0
  67. package/lib/printer/visitors/call-expression/clauses/grant-on/grant-on.js +10 -0
  68. package/lib/printer/visitors/call-expression/clauses/if-not-exists/if-not-exists.js +2 -0
  69. package/lib/printer/visitors/call-expression/clauses/indexed-by/indexed-by.js +7 -0
  70. package/lib/printer/visitors/call-expression/clauses/into/into.js +34 -3
  71. package/lib/printer/visitors/call-expression/clauses/json-op.js +9 -1
  72. package/lib/printer/visitors/call-expression/clauses/lateral-join/lateral-join.js +26 -3
  73. package/lib/printer/visitors/call-expression/clauses/like/like.js +7 -0
  74. package/lib/printer/visitors/call-expression/clauses/limit-all/limit-all.js +3 -0
  75. package/lib/printer/visitors/call-expression/clauses/limit-clauses.js +4 -0
  76. package/lib/printer/visitors/call-expression/clauses/named-window/named-window.js +16 -0
  77. package/lib/printer/visitors/call-expression/clauses/not/not.js +4 -0
  78. package/lib/printer/visitors/call-expression/clauses/not-indexed/not-indexed.js +4 -0
  79. package/lib/printer/visitors/call-expression/clauses/nulls-order/nulls-order.js +9 -0
  80. package/lib/printer/visitors/call-expression/clauses/offset/offset.js +4 -0
  81. package/lib/printer/visitors/call-expression/clauses/on-conflict/on-conflict.js +24 -2
  82. package/lib/printer/visitors/call-expression/clauses/on-delete/on-delete.js +32 -0
  83. package/lib/printer/visitors/call-expression/clauses/or-abort/or-abort.js +3 -0
  84. package/lib/printer/visitors/call-expression/clauses/or-fail/or-fail.js +3 -0
  85. package/lib/printer/visitors/call-expression/clauses/over/over.js +12 -0
  86. package/lib/printer/visitors/call-expression/clauses/pragma/pragma.js +14 -5
  87. package/lib/printer/visitors/call-expression/clauses/privilege/privilege.js +10 -0
  88. package/lib/printer/visitors/call-expression/clauses/references/references.js +26 -4
  89. package/lib/printer/visitors/call-expression/clauses/reindex/reindex.js +10 -0
  90. package/lib/printer/visitors/call-expression/clauses/release-savepoint/release-savepoint.js +4 -0
  91. package/lib/printer/visitors/call-expression/clauses/rename-table/rename-table.js +4 -0
  92. package/lib/printer/visitors/call-expression/clauses/right-outer-join/right-outer-join.js +8 -0
  93. package/lib/printer/visitors/call-expression/clauses/row-level-security/row-level-security.js +7 -0
  94. package/lib/printer/visitors/call-expression/clauses/section/section.js +5 -1
  95. package/lib/printer/visitors/call-expression/clauses/select/select.js +37 -9
  96. package/lib/printer/visitors/call-expression/clauses/set-param/set-param.js +14 -0
  97. package/lib/printer/visitors/call-expression/clauses/show-param/show-param.js +4 -0
  98. package/lib/printer/visitors/call-expression/clauses/table-primary-key/table-primary-key.js +13 -0
  99. package/lib/printer/visitors/call-expression/clauses/table-unique/table-unique.js +13 -0
  100. package/lib/printer/visitors/call-expression/clauses/to/to.js +10 -0
  101. package/lib/printer/visitors/call-expression/clauses/truncate/truncate.js +12 -0
  102. package/lib/printer/visitors/call-expression/clauses/unary-minus/unary-minus.js +4 -0
  103. package/lib/printer/visitors/call-expression/clauses/update/update.js +24 -3
  104. package/lib/printer/visitors/call-expression/clauses/using/using.js +13 -0
  105. package/lib/printer/visitors/call-expression/clauses/vacuum/vacuum.js +8 -1
  106. package/lib/printer/visitors/call-expression/clauses/window-frame/window-frame.js +38 -0
  107. package/lib/printer/visitors/call-expression/clauses/with-named/with-named.js +8 -20
  108. package/lib/printer/visitors/call-expression/clauses/without-rowid/without-rowid.js +1 -0
  109. package/lib/printer/visitors/logical-expression.js +4 -1
  110. package/lib/printer/visitors/member-expression.js +14 -0
  111. package/lib/printer/visitors/string-literal.js +9 -5
  112. package/package.json +11 -4
@@ -6,16 +6,40 @@ const {
6
6
  } = types;
7
7
 
8
8
  const printAsExpression = (path, {write, traverse}) => {
9
- traverse(path.get('expression'));
10
- write(` AS ${path.node.typeAnnotation.literal.value}`);
9
+ const expr = path.get('expression');
10
+
11
+ if (isTableFunc(expr.node))
12
+ printTableFunc(expr, {
13
+ write,
14
+ traverse,
15
+ });
16
+ else
17
+ traverse(expr);
18
+
19
+ if (isSubqueryAlias(path.node.expression))
20
+ write(' ');
21
+ else
22
+ write(' AS ');
23
+
24
+ write(path.node.typeAnnotation.literal.value);
11
25
  };
12
26
 
27
+ const isSubqueryAlias = (node) => isCallExpression(node) && [
28
+ 'subquery',
29
+ 'lateralSubquery',
30
+ ].includes(node.callee.name);
31
+
13
32
  const CLAUSE_NAMES = [
14
33
  'where',
15
34
  'groupBy',
16
35
  'having',
36
+ 'namedWindow',
17
37
  'orderBy',
18
38
  'limit',
39
+ 'limitAll',
40
+ 'offset',
41
+ 'fetchFirst',
42
+ 'forUpdate',
19
43
  ];
20
44
 
21
45
  const TABLE_ARG_NAMES = [
@@ -23,6 +47,7 @@ const TABLE_ARG_NAMES = [
23
47
  'join',
24
48
  'leftJoin',
25
49
  'rightJoin',
50
+ 'rightOuterJoin',
26
51
  'innerJoin',
27
52
  'leftOuterJoin',
28
53
  'fullOuterJoin',
@@ -32,6 +57,8 @@ const TABLE_ARG_NAMES = [
32
57
  'subquery',
33
58
  'lateralJoin',
34
59
  'lateralSubquery',
60
+ 'indexedBy',
61
+ 'notIndexed',
35
62
  ];
36
63
 
37
64
  const isClause = ({node}) => isCallExpression(node) && CLAUSE_NAMES.includes(node.callee.name);
@@ -39,13 +66,23 @@ const isClause = ({node}) => isCallExpression(node) && CLAUSE_NAMES.includes(nod
39
66
  const CAMEL_RE = /([A-Z])/g;
40
67
  const toSnake = (a) => a.replace(CAMEL_RE, (_, c) => `_${c.toLowerCase()}`);
41
68
 
69
+ // single-word ALL-CAPS functions (UNNEST) keep their casing
70
+ const ALL_CAPS_RE = /^[A-Z]+$/;
71
+
72
+ const toFuncName = (name) => {
73
+ if (ALL_CAPS_RE.test(name))
74
+ return name;
75
+
76
+ return toSnake(name);
77
+ };
78
+
42
79
  const isTableFunc = (node) => isCallExpression(node) && !TABLE_ARG_NAMES.includes(node.callee.name);
43
80
 
44
81
  const printTableFunc = (path, {write, traverse}) => {
45
82
  const {name} = path.node.callee;
46
83
  const args = path.get('arguments');
47
84
 
48
- write(toSnake(name));
85
+ write(toFuncName(name));
49
86
  write('(');
50
87
 
51
88
  for (const [i, arg] of args.entries()) {
@@ -73,8 +110,15 @@ const printArg = (path, printer) => {
73
110
 
74
111
  export const from = (path, {write, traverse}) => {
75
112
  const args = path.get('arguments');
76
- const tableArgs = args.filter((arg) => !isClause(arg));
77
- const clauseArgs = args.filter(isClause);
113
+ const tableArgs = [];
114
+ const clauseArgs = [];
115
+
116
+ for (const arg of args) {
117
+ if (isClause(arg))
118
+ clauseArgs.push(arg);
119
+ else
120
+ tableArgs.push(arg);
121
+ }
78
122
 
79
123
  write('FROM ');
80
124
  printArg(tableArgs[0], {
@@ -0,0 +1,10 @@
1
+ export const generatedAlwaysAs = (path, {write, traverse}) => {
2
+ write('GENERATED ALWAYS AS (');
3
+ traverse(path.get('arguments.0'));
4
+ write(')');
5
+
6
+ const [stored] = path.get('arguments').slice(1);
7
+
8
+ if (stored.node.value)
9
+ write(' STORED');
10
+ };
@@ -0,0 +1,10 @@
1
+ export const grant = (path, {write, traverse}) => {
2
+ const [privArg, onArg, toArg] = path.get('arguments');
3
+
4
+ write('GRANT ');
5
+ traverse(privArg);
6
+ write(' ON ');
7
+ traverse(onArg);
8
+ write(' TO ');
9
+ traverse(toArg);
10
+ };
@@ -0,0 +1,10 @@
1
+ export const grantOn = (path, {write, traverse}) => {
2
+ const args = path.get('arguments');
3
+
4
+ for (let i = 0; i < args.length; i++) {
5
+ if (i > 0)
6
+ write(', ');
7
+
8
+ traverse(args[i]);
9
+ }
10
+ };
@@ -0,0 +1,2 @@
1
+ // sentinel — consumed by createIndex/createUniqueIndex printer, never called
2
+ export const ifNotExists = null;
@@ -0,0 +1,7 @@
1
+ export const indexedBy = (path, {write, traverse}) => {
2
+ const [table, index] = path.get('arguments');
3
+
4
+ traverse(table);
5
+ write(' INDEXED BY ');
6
+ traverse(index);
7
+ };
@@ -1,14 +1,45 @@
1
+ import {types} from '@putout/babel';
2
+
3
+ const {
4
+ isTSAsExpression,
5
+ isArrayExpression,
6
+ } = types;
7
+
8
+ const printTable = (table, {write, traverse}) => {
9
+ if (isTSAsExpression(table.node)) {
10
+ traverse(table.get('expression'));
11
+ write(` AS ${table.node.typeAnnotation.literal.value}`);
12
+
13
+ return;
14
+ }
15
+
16
+ traverse(table);
17
+ };
18
+
1
19
  export const into = (path, {write, traverse}) => {
2
20
  const args = path.get('arguments');
3
21
  const [table] = args;
22
+
23
+ write('INTO ');
24
+ printTable(table, {
25
+ write,
26
+ traverse,
27
+ });
28
+
29
+ // no column list: INTO t VALUES (...) / INTO t DEFAULT VALUES
30
+ if (args.length === 2) {
31
+ write.space();
32
+ traverse(args[1]);
33
+
34
+ return;
35
+ }
36
+
4
37
  const [, columns] = args;
5
38
  const values = args[2];
6
39
 
7
- write('INTO ');
8
- traverse(table);
9
40
  write(' (');
10
41
 
11
- if (columns.node.type === 'ArrayExpression') {
42
+ if (isArrayExpression(columns.node)) {
12
43
  const colArgs = columns.get('elements');
13
44
 
14
45
  for (let i = 0; i < colArgs.length; i++) {
@@ -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
  '->>',
@@ -6,11 +13,12 @@ const JSON_OPS = new Set([
6
13
  '?',
7
14
  '?|',
8
15
  '?&',
16
+ '??',
9
17
  '@>',
10
18
  '<@',
11
19
  ]);
12
20
 
13
- 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);
14
22
 
15
23
  export const printJsonTuple = (path, {write, traverse}) => {
16
24
  const elems = path.get('elements');
@@ -1,6 +1,27 @@
1
1
  import {types} from '@putout/babel';
2
2
 
3
- const {isTSAsExpression} = types;
3
+ const {
4
+ isTSAsExpression,
5
+ isCallExpression,
6
+ } = types;
7
+
8
+ const isSubqueryAlias = (node) => isCallExpression(node) && [
9
+ 'subquery',
10
+ 'lateralSubquery',
11
+ ].includes(node.callee.name);
12
+
13
+ const printAsExpression = (path, {write, traverse}) => {
14
+ const {expression} = path.node;
15
+
16
+ traverse(path.get('expression'));
17
+
18
+ if (isSubqueryAlias(expression))
19
+ write(' ');
20
+ else
21
+ write(' AS ');
22
+
23
+ write(path.node.typeAnnotation.literal.value);
24
+ };
4
25
 
5
26
  export const lateralJoin = (path, {write, traverse}) => {
6
27
  write(', ');
@@ -9,8 +30,10 @@ export const lateralJoin = (path, {write, traverse}) => {
9
30
  const {node} = arg;
10
31
 
11
32
  if (isTSAsExpression(node)) {
12
- traverse(arg.get('expression'));
13
- write(` AS ${node.typeAnnotation.literal.value}`);
33
+ printAsExpression(arg, {
34
+ write,
35
+ traverse,
36
+ });
14
37
 
15
38
  return;
16
39
  }
@@ -3,4 +3,11 @@ export const like = (path, {write, traverse}) => {
3
3
  traverse(args[0]);
4
4
  write(' LIKE ');
5
5
  traverse(args[1]);
6
+
7
+ const escape = args[2];
8
+
9
+ if (escape) {
10
+ write(' ESCAPE ');
11
+ traverse(escape);
12
+ }
6
13
  };
@@ -0,0 +1,3 @@
1
+ export const limitAll = (path, {write}) => {
2
+ write('LIMIT ALL');
3
+ };
@@ -0,0 +1,4 @@
1
+ export {limitAll} from './limit-all/limit-all.js';
2
+ export {offset} from './offset/offset.js';
3
+ export {fetchFirst} from './fetch-first/fetch-first.js';
4
+ export {forUpdate} from './for-update/for-update.js';
@@ -0,0 +1,16 @@
1
+ export const namedWindow = (path, {write, traverse}) => {
2
+ const args = path.get('arguments');
3
+
4
+ write('WINDOW ');
5
+ traverse(args[0]);
6
+ write(' AS (');
7
+
8
+ for (let i = 1; i < args.length; i++) {
9
+ if (i > 1)
10
+ write(' ');
11
+
12
+ traverse(args[i]);
13
+ }
14
+
15
+ write(')');
16
+ };
@@ -0,0 +1,4 @@
1
+ export const not = (path, {write, traverse}) => {
2
+ write('NOT ');
3
+ traverse(path.get('arguments')[0]);
4
+ };
@@ -0,0 +1,4 @@
1
+ export const notIndexed = (path, {write, traverse}) => {
2
+ traverse(path.get('arguments')[0]);
3
+ write(' NOT INDEXED');
4
+ };
@@ -0,0 +1,9 @@
1
+ export const nullsFirst = (path, {write, traverse}) => {
2
+ traverse(path.get('arguments')[0]);
3
+ write(' NULLS FIRST');
4
+ };
5
+
6
+ export const nullsLast = (path, {write, traverse}) => {
7
+ traverse(path.get('arguments')[0]);
8
+ write(' NULLS LAST');
9
+ };
@@ -0,0 +1,4 @@
1
+ export const offset = (path, {write, traverse}) => {
2
+ write('OFFSET ');
3
+ traverse(path.get('arguments')[0]);
4
+ };
@@ -1,7 +1,24 @@
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';
9
+
1
10
  export const onConflict = (path, {write, traverse}) => {
2
11
  const args = path.get('arguments');
3
12
  const action = args.at(-1);
4
- const targets = args.slice(0, -1);
13
+ let whereArg = null;
14
+ let targetCount = args.length - 1;
15
+
16
+ if (targetCount && isWhereArg(args.at(-2))) {
17
+ whereArg = args.at(-2);
18
+ --targetCount;
19
+ }
20
+
21
+ const targets = args.slice(0, targetCount);
5
22
 
6
23
  write('ON CONFLICT');
7
24
 
@@ -17,9 +34,14 @@ export const onConflict = (path, {write, traverse}) => {
17
34
  write(')');
18
35
  }
19
36
 
37
+ if (whereArg) {
38
+ write(' ');
39
+ traverse(whereArg);
40
+ }
41
+
20
42
  write(' DO ');
21
43
 
22
- if (action.node.type === 'Identifier' && action.node.name === 'nothing') {
44
+ if (isIdentifier(action.node) && action.node.name === 'nothing') {
23
45
  write('NOTHING');
24
46
  return;
25
47
  }
@@ -0,0 +1,32 @@
1
+ const ACTION_PRINT = {
2
+ SET_NULL: 'SET NULL',
3
+ SET_DEFAULT: 'SET DEFAULT',
4
+ NO_ACTION: 'NO ACTION',
5
+ };
6
+
7
+ export const onDelete = (path, {write, traverse}) => {
8
+ write('ON DELETE ');
9
+ printAction(path, {
10
+ write,
11
+ traverse,
12
+ });
13
+ };
14
+
15
+ export const onUpdate = (path, {write, traverse}) => {
16
+ write('ON UPDATE ');
17
+ printAction(path, {
18
+ write,
19
+ traverse,
20
+ });
21
+ };
22
+
23
+ const printAction = (path, printer) => {
24
+ const {write, traverse} = printer;
25
+ const arg = path.get('arguments.0');
26
+ const {name} = arg.node;
27
+
28
+ if (ACTION_PRINT[name])
29
+ return write(ACTION_PRINT[name]);
30
+
31
+ traverse(arg);
32
+ };
@@ -0,0 +1,3 @@
1
+ export const orAbort = (path, {write}) => {
2
+ write('OR ABORT');
3
+ };
@@ -0,0 +1,3 @@
1
+ export const orFail = (path, {write}) => {
2
+ write('OR FAIL');
3
+ };
@@ -1,9 +1,21 @@
1
+ import {types} from '@putout/babel';
2
+
3
+ const {isIdentifier} = types;
4
+
1
5
  export const over = (path, {write, traverse}) => {
2
6
  const args = path.get('arguments');
3
7
  const [func] = args;
4
8
  const clauses = args.slice(1);
5
9
 
6
10
  traverse(func);
11
+
12
+ if (clauses.length && isIdentifier(clauses[0].node)) {
13
+ write(' OVER ');
14
+ traverse(clauses[0]);
15
+
16
+ return;
17
+ }
18
+
7
19
  write(' OVER (');
8
20
 
9
21
  for (const [i, clause] of clauses.entries()) {
@@ -1,9 +1,18 @@
1
1
  export const pragma = (path, {write, traverse}) => {
2
- const [name, arg] = path.get('arguments');
2
+ const args = path.get('arguments');
3
3
 
4
4
  write('PRAGMA ');
5
- traverse(name);
6
- write('(');
7
- traverse(arg);
8
- write(')');
5
+
6
+ // func-call form: PRAGMA table_info(users)
7
+ if (args.length === 2) {
8
+ traverse(args[0]);
9
+ write('(');
10
+ traverse(args[1]);
11
+ write(')');
12
+
13
+ return;
14
+ }
15
+
16
+ // no-args, qualified or assignment form
17
+ traverse(args[0]);
9
18
  };
@@ -0,0 +1,10 @@
1
+ export const privilege = (path, {write, traverse}) => {
2
+ const args = path.get('arguments');
3
+
4
+ for (let i = 0; i < args.length; i++) {
5
+ if (i > 0)
6
+ write(', ');
7
+
8
+ traverse(args[i]);
9
+ }
10
+ };
@@ -1,17 +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
- write('REFERENCES ');
3
9
  const [first, ...other] = path.get('arguments');
10
+ const cols = [];
11
+ const actions = [];
4
12
 
5
- traverse(first);
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
+ }
6
21
 
22
+ write('REFERENCES ');
23
+ traverse(first);
7
24
  write('(');
8
25
 
9
- for (const [i, arg] of other.entries()) {
26
+ for (const [i, col] of cols.entries()) {
10
27
  if (i > 0)
11
28
  write(', ');
12
29
 
13
- traverse(arg);
30
+ traverse(col);
14
31
  }
15
32
 
16
33
  write(')');
34
+
35
+ for (const action of actions) {
36
+ write(' ');
37
+ traverse(action);
38
+ }
17
39
  };
@@ -0,0 +1,10 @@
1
+ export const reindex = (path, {write, traverse}) => {
2
+ const args = path.get('arguments');
3
+
4
+ write('REINDEX');
5
+
6
+ if (args.length) {
7
+ write.space();
8
+ traverse(args[0]);
9
+ }
10
+ };
@@ -0,0 +1,4 @@
1
+ export const releaseSavepoint = (path, {write, traverse}) => {
2
+ write('RELEASE SAVEPOINT ');
3
+ traverse(path.get('arguments')[0]);
4
+ };
@@ -0,0 +1,4 @@
1
+ export const renameTable = (path, {write, traverse}) => {
2
+ write('RENAME TO ');
3
+ traverse(path.get('arguments')[0]);
4
+ };
@@ -0,0 +1,8 @@
1
+ import {printJoinBody} from '../join/join.js';
2
+
3
+ export const rightOuterJoin = (path, printer) => {
4
+ const {write} = printer;
5
+ write.breakline();
6
+ write('RIGHT OUTER JOIN ');
7
+ printJoinBody(path, printer);
8
+ };
@@ -0,0 +1,7 @@
1
+ export const enableRowLevelSecurity = (path, {write}) => {
2
+ write('ENABLE ROW LEVEL SECURITY');
3
+ };
4
+
5
+ export const disableRowLevelSecurity = (path, {write}) => {
6
+ write('DISABLE ROW LEVEL SECURITY');
7
+ };
@@ -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
 
@@ -2,7 +2,10 @@ import {types} from '@putout/babel';
2
2
  import {createTypeChecker} from '@putout/printer/type-checker';
3
3
  import {isJsonTuple, printJsonTuple} from '../json-op.js';
4
4
 
5
- const {isTSAsExpression} = types;
5
+ const {
6
+ isTSAsExpression,
7
+ isCallExpression,
8
+ } = types;
6
9
 
7
10
  const printAsExpression = (path, {write, traverse}) => {
8
11
  traverse(path.get('expression'));
@@ -26,24 +29,41 @@ const isFrom = createTypeChecker([
26
29
  ['+: node.callee.name', '=', 'from'],
27
30
  ]);
28
31
 
32
+ const isClauseName = (path) => isCallExpression(path.node) && [
33
+ 'limitAll',
34
+ 'offset',
35
+ 'fetchFirst',
36
+ 'forUpdate',
37
+ ].includes(path.node.callee.name);
38
+
39
+ const isDistinctOn = (path) => isCallExpression(path.node) && path.node.callee.name === 'distinctOn';
40
+
29
41
  export const select = (path, printer) => {
30
42
  const {write, traverse} = printer;
31
- const [cols, from] = parseCols(path);
43
+ const [cols, trailing] = parseCols(path);
32
44
 
33
45
  write('SELECT ');
34
46
 
47
+ let first = 0;
48
+
49
+ if (cols.length && isDistinctOn(cols[0])) {
50
+ traverse(cols[0]);
51
+ write(' ');
52
+ first = 1;
53
+ }
54
+
35
55
  const n = cols.length;
36
56
 
37
- for (const [i, col] of cols.entries()) {
38
- if (i && i < n)
57
+ for (let i = first; i < n; i++) {
58
+ if (i > first)
39
59
  write(', ');
40
60
 
41
- printItem(col, printer);
61
+ printItem(cols[i], printer);
42
62
  }
43
63
 
44
- if (isFrom(from)) {
64
+ for (const clause of trailing) {
45
65
  write.breakline();
46
- traverse(from);
66
+ traverse(clause);
47
67
  }
48
68
  };
49
69
 
@@ -54,8 +74,16 @@ function parseCols(path) {
54
74
  if (isFrom(from))
55
75
  return [
56
76
  args.slice(0, -1),
57
- from,
77
+ [from],
58
78
  ];
59
79
 
60
- return [args, null];
80
+ let i = args.length;
81
+
82
+ while (i && isClauseName(args[i - 1]))
83
+ --i;
84
+
85
+ return [
86
+ args.slice(0, i),
87
+ args.slice(i),
88
+ ];
61
89
  }