happy-sql 2.2.1 → 2.4.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 (110) hide show
  1. package/ChangeLog +86 -0
  2. package/bin/convert.js +3 -9
  3. package/lib/parser/visitors/clause/alter-table/alter-table.js +21 -0
  4. package/lib/parser/visitors/clause/binary.js +30 -6
  5. package/lib/parser/visitors/clause/column-definition/column-definition.js +49 -8
  6. package/lib/parser/visitors/clause/count/count.js +5 -2
  7. package/lib/parser/visitors/clause/create-extension/create-extension.js +7 -0
  8. package/lib/parser/visitors/clause/create-index/create-index.js +36 -5
  9. package/lib/parser/visitors/clause/create-schema/create-schema.js +7 -0
  10. package/lib/parser/visitors/clause/create-table/create-table.js +58 -7
  11. package/lib/parser/visitors/clause/create-view/create-view.js +17 -0
  12. package/lib/parser/visitors/clause/drop-index/drop-index.js +3 -0
  13. package/lib/parser/visitors/clause/drop-view/drop-view.js +15 -0
  14. package/lib/parser/visitors/clause/explain/explain.js +16 -0
  15. package/lib/parser/visitors/clause/from/from.js +93 -4
  16. package/lib/parser/visitors/clause/from/limit.js +13 -0
  17. package/lib/parser/visitors/clause/from/order-by.js +14 -3
  18. package/lib/parser/visitors/clause/from/table-func.js +5 -18
  19. package/lib/parser/visitors/clause/grant/grant.js +26 -0
  20. package/lib/parser/visitors/clause/index.js +50 -2
  21. package/lib/parser/visitors/clause/insert/insert.js +64 -6
  22. package/lib/parser/visitors/clause/operand.js +55 -1
  23. package/lib/parser/visitors/clause/pragma/pragma.js +52 -2
  24. package/lib/parser/visitors/clause/select/select.js +139 -6
  25. package/lib/parser/visitors/clause/set-param/set-param.js +19 -0
  26. package/lib/parser/visitors/clause/table-constraint.js +27 -0
  27. package/lib/parser/visitors/clause/union/union.js +14 -2
  28. package/lib/parser/visitors/clause/update/update.js +17 -0
  29. package/lib/parser/visitors/clause/where/where.js +17 -0
  30. package/lib/printer/visitors/call-expression/clauses/access-method/access-method.js +4 -0
  31. package/lib/printer/visitors/call-expression/clauses/analyze/analyze.js +10 -0
  32. package/lib/printer/visitors/call-expression/clauses/array-type/array-type.js +4 -0
  33. package/lib/printer/visitors/call-expression/clauses/clauses.js +138 -2
  34. package/lib/printer/visitors/call-expression/clauses/collate/collate.js +7 -0
  35. package/lib/printer/visitors/call-expression/clauses/concurrently/concurrently.js +2 -0
  36. package/lib/printer/visitors/call-expression/clauses/constraint/constraint.js +11 -0
  37. package/lib/printer/visitors/call-expression/clauses/count-distinct/count-distinct.js +13 -0
  38. package/lib/printer/visitors/call-expression/clauses/create-extension/create-extension.js +4 -0
  39. package/lib/printer/visitors/call-expression/clauses/create-index/create-index.js +43 -9
  40. package/lib/printer/visitors/call-expression/clauses/create-index/create-unique-index.js +5 -19
  41. package/lib/printer/visitors/call-expression/clauses/create-materialized-view/create-materialized-view.js +8 -0
  42. package/lib/printer/visitors/call-expression/clauses/create-schema/create-schema.js +4 -0
  43. package/lib/printer/visitors/call-expression/clauses/create-table/create-table.js +16 -1
  44. package/lib/printer/visitors/call-expression/clauses/create-table-as/create-table-as.js +8 -0
  45. package/lib/printer/visitors/call-expression/clauses/create-temp-table/create-temp-table.js +11 -0
  46. package/lib/printer/visitors/call-expression/clauses/create-view/create-view.js +8 -0
  47. package/lib/printer/visitors/call-expression/clauses/create-virtual-table/create-virtual-table.js +8 -0
  48. package/lib/printer/visitors/call-expression/clauses/{nextval/nextval.js → default-nextval/default-nextval.js} +2 -1
  49. package/lib/printer/visitors/call-expression/clauses/default-values/default-values.js +3 -0
  50. package/lib/printer/visitors/call-expression/clauses/detach/detach.js +6 -0
  51. package/lib/printer/visitors/call-expression/clauses/distinct-on/distinct-on.js +14 -0
  52. package/lib/printer/visitors/call-expression/clauses/do-block/do-block.js +5 -0
  53. package/lib/printer/visitors/call-expression/clauses/drop-index/drop-index.js +13 -1
  54. package/lib/printer/visitors/call-expression/clauses/drop-view/drop-view.js +24 -0
  55. package/lib/printer/visitors/call-expression/clauses/except/except.js +9 -0
  56. package/lib/printer/visitors/call-expression/clauses/explain/explain.js +4 -0
  57. package/lib/printer/visitors/call-expression/clauses/explain-analyze/explain-analyze.js +4 -0
  58. package/lib/printer/visitors/call-expression/clauses/explain-query-plan/explain-query-plan.js +4 -0
  59. package/lib/printer/visitors/call-expression/clauses/fetch-first/fetch-first.js +5 -0
  60. package/lib/printer/visitors/call-expression/clauses/filter/filter.js +8 -0
  61. package/lib/printer/visitors/call-expression/clauses/for-update/for-update.js +3 -0
  62. package/lib/printer/visitors/call-expression/clauses/from/from.js +34 -3
  63. package/lib/printer/visitors/call-expression/clauses/generated-always-as/generated-always-as.js +10 -0
  64. package/lib/printer/visitors/call-expression/clauses/grant/grant.js +10 -0
  65. package/lib/printer/visitors/call-expression/clauses/grant-on/grant-on.js +10 -0
  66. package/lib/printer/visitors/call-expression/clauses/if-not-exists/if-not-exists.js +2 -0
  67. package/lib/printer/visitors/call-expression/clauses/indexed-by/indexed-by.js +7 -0
  68. package/lib/printer/visitors/call-expression/clauses/intersect/intersect.js +9 -0
  69. package/lib/printer/visitors/call-expression/clauses/into/into.js +30 -2
  70. package/lib/printer/visitors/call-expression/clauses/json-op.js +1 -0
  71. package/lib/printer/visitors/call-expression/clauses/lateral-join/lateral-join.js +26 -3
  72. package/lib/printer/visitors/call-expression/clauses/like/like.js +7 -0
  73. package/lib/printer/visitors/call-expression/clauses/limit-all/limit-all.js +3 -0
  74. package/lib/printer/visitors/call-expression/clauses/limit-clauses.js +4 -0
  75. package/lib/printer/visitors/call-expression/clauses/named-window/named-window.js +16 -0
  76. package/lib/printer/visitors/call-expression/clauses/not/not.js +4 -0
  77. package/lib/printer/visitors/call-expression/clauses/not-indexed/not-indexed.js +4 -0
  78. package/lib/printer/visitors/call-expression/clauses/nulls-order/nulls-order.js +9 -0
  79. package/lib/printer/visitors/call-expression/clauses/offset/offset.js +4 -0
  80. package/lib/printer/visitors/call-expression/clauses/on-conflict/on-conflict.js +16 -1
  81. package/lib/printer/visitors/call-expression/clauses/on-delete/on-delete.js +32 -0
  82. package/lib/printer/visitors/call-expression/clauses/or-abort/or-abort.js +3 -0
  83. package/lib/printer/visitors/call-expression/clauses/or-fail/or-fail.js +3 -0
  84. package/lib/printer/visitors/call-expression/clauses/over/over.js +12 -0
  85. package/lib/printer/visitors/call-expression/clauses/pragma/pragma.js +14 -5
  86. package/lib/printer/visitors/call-expression/clauses/privilege/privilege.js +10 -0
  87. package/lib/printer/visitors/call-expression/clauses/references/references.js +14 -4
  88. package/lib/printer/visitors/call-expression/clauses/reindex/reindex.js +10 -0
  89. package/lib/printer/visitors/call-expression/clauses/release-savepoint/release-savepoint.js +4 -0
  90. package/lib/printer/visitors/call-expression/clauses/rename-table/rename-table.js +4 -0
  91. package/lib/printer/visitors/call-expression/clauses/replace-into/replace-into.js +11 -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/select/select.js +37 -9
  95. package/lib/printer/visitors/call-expression/clauses/set-param/set-param.js +14 -0
  96. package/lib/printer/visitors/call-expression/clauses/show-param/show-param.js +4 -0
  97. package/lib/printer/visitors/call-expression/clauses/table-primary-key/table-primary-key.js +13 -0
  98. package/lib/printer/visitors/call-expression/clauses/table-unique/table-unique.js +13 -0
  99. package/lib/printer/visitors/call-expression/clauses/to/to.js +10 -0
  100. package/lib/printer/visitors/call-expression/clauses/truncate/truncate.js +12 -0
  101. package/lib/printer/visitors/call-expression/clauses/unary-minus/unary-minus.js +4 -0
  102. package/lib/printer/visitors/call-expression/clauses/update/update.js +24 -3
  103. package/lib/printer/visitors/call-expression/clauses/using/using.js +13 -0
  104. package/lib/printer/visitors/call-expression/clauses/vacuum/vacuum.js +8 -1
  105. package/lib/printer/visitors/call-expression/clauses/window-frame/window-frame.js +38 -0
  106. package/lib/printer/visitors/call-expression/clauses/with-named/with-named.js +8 -20
  107. package/lib/printer/visitors/call-expression/clauses/without-rowid/without-rowid.js +1 -0
  108. package/lib/printer/visitors/member-expression.js +14 -0
  109. package/lib/printer/visitors/string-literal.js +9 -5
  110. package/package.json +9 -2
@@ -1,20 +1,54 @@
1
- export const createIndex = (path, {write, traverse}) => {
2
- const args = path.get('arguments');
1
+ import {types} from '@putout/babel';
2
+
3
+ const {isCallExpression} = types;
4
+
5
+ const isConcurrently = (arg) => isCallExpression(arg.node) && arg.node.callee.name === 'concurrently';
6
+ const isIfNotExists = (arg) => isCallExpression(arg.node) && arg.node.callee.name === 'ifNotExists';
7
+ const isWhere = (arg) => isCallExpression(arg.node) && arg.node.callee.name === 'where';
8
+ 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
+ export const printIndexBody = (path, {write, traverse}) => {
12
+ const [name, table, ...rest] = path.get('arguments');
13
+ const cols = rest.filter(isCol);
14
+ const hasIfNotExists = rest.some(isIfNotExists);
15
+ const hasConcurrently = rest.some(isConcurrently);
16
+ const whereArg = rest.find(isWhere);
17
+ const accessMethod = rest.find(isAccessMethod);
3
18
 
4
- write('CREATE INDEX ');
5
- traverse(args[0]);
19
+ if (hasIfNotExists)
20
+ write('IF NOT EXISTS ');
21
+
22
+ if (hasConcurrently)
23
+ write('CONCURRENTLY ');
24
+
25
+ traverse(name);
6
26
  write(' ON ');
7
- traverse(args[1]);
27
+ traverse(table);
28
+
29
+ if (accessMethod) {
30
+ write(' USING ');
31
+ traverse(accessMethod.get('arguments.0'));
32
+ }
33
+
8
34
  write(' (');
9
35
 
10
- for (const [i, arg] of args
11
- .slice(2)
12
- .entries()) {
36
+ for (const [i, col] of cols.entries()) {
13
37
  if (i > 0)
14
38
  write(', ');
15
39
 
16
- traverse(arg);
40
+ traverse(col);
17
41
  }
18
42
 
19
43
  write(')');
44
+
45
+ if (whereArg) {
46
+ write.newline();
47
+ traverse(whereArg);
48
+ }
49
+ };
50
+
51
+ export const createIndex = (path, printer) => {
52
+ printer.write('CREATE INDEX ');
53
+ printIndexBody(path, printer);
20
54
  };
@@ -1,20 +1,6 @@
1
- export const createUniqueIndex = (path, {write, traverse}) => {
2
- const args = path.get('arguments');
3
-
4
- write('CREATE UNIQUE INDEX ');
5
- traverse(args[0]);
6
- write(' ON ');
7
- traverse(args[1]);
8
- write(' (');
9
-
10
- for (const [i, arg] of args
11
- .slice(2)
12
- .entries()) {
13
- if (i > 0)
14
- write(', ');
15
-
16
- traverse(arg);
17
- }
18
-
19
- write(')');
1
+ import {printIndexBody} from './create-index.js';
2
+
3
+ export const createUniqueIndex = (path, printer) => {
4
+ printer.write('CREATE UNIQUE INDEX ');
5
+ printIndexBody(path, printer);
20
6
  };
@@ -0,0 +1,8 @@
1
+ export const createMaterializedView = (path, {write, traverse}) => {
2
+ const [name, stmt] = path.get('arguments');
3
+
4
+ write('CREATE MATERIALIZED VIEW ');
5
+ traverse(name);
6
+ write(' AS ');
7
+ traverse(stmt);
8
+ };
@@ -0,0 +1,4 @@
1
+ export const createSchema = (path, {write, traverse}) => {
2
+ write('CREATE SCHEMA ');
3
+ traverse(path.get('arguments')[0]);
4
+ };
@@ -1,3 +1,7 @@
1
+ import {types} from '@putout/babel';
2
+
3
+ const {isCallExpression} = types;
4
+
1
5
  export const printTableBody = (path, {write, traverse}) => {
2
6
  const [name, columns] = path.get('arguments');
3
7
 
@@ -7,9 +11,17 @@ export const printTableBody = (path, {write, traverse}) => {
7
11
  write.newline();
8
12
 
9
13
  const elements = columns.get('elements');
14
+ let wroteRowid = false;
10
15
 
11
16
  for (const [i, element] of elements.entries()) {
12
- if (i > 0) {
17
+ const isRowid = isCallExpression(element.node) && element.node.callee.name === 'withoutRowid';
18
+
19
+ if (isRowid) {
20
+ wroteRowid = true;
21
+ continue;
22
+ }
23
+
24
+ if (i > 0 && !wroteRowid) {
13
25
  write(',');
14
26
  write.newline();
15
27
  }
@@ -18,6 +30,9 @@ export const printTableBody = (path, {write, traverse}) => {
18
30
  }
19
31
 
20
32
  write(')');
33
+
34
+ if (wroteRowid)
35
+ write(' WITHOUT ROWID');
21
36
  };
22
37
 
23
38
  export const createTable = (path, printer) => {
@@ -0,0 +1,8 @@
1
+ export const createTableAs = (path, {write, traverse}) => {
2
+ const [name, stmt] = path.get('arguments');
3
+
4
+ write('CREATE TABLE ');
5
+ traverse(name);
6
+ write(' AS ');
7
+ traverse(stmt);
8
+ };
@@ -0,0 +1,11 @@
1
+ import {printTableBody} from '../create-table/create-table.js';
2
+
3
+ export const createTempTable = (path, printer) => {
4
+ printer.write('CREATE TEMP TABLE ');
5
+ printTableBody(path, printer);
6
+ };
7
+
8
+ export const createTempTableIfNotExists = (path, printer) => {
9
+ printer.write('CREATE TEMP TABLE IF NOT EXISTS ');
10
+ printTableBody(path, printer);
11
+ };
@@ -0,0 +1,8 @@
1
+ export const createView = (path, {write, traverse}) => {
2
+ const [name, stmt] = path.get('arguments');
3
+
4
+ write('CREATE VIEW ');
5
+ traverse(name);
6
+ write(' AS ');
7
+ traverse(stmt);
8
+ };
@@ -0,0 +1,8 @@
1
+ export const createVirtualTable = (path, {write, traverse}) => {
2
+ const [name, module] = path.get('arguments');
3
+
4
+ write('CREATE VIRTUAL TABLE ');
5
+ traverse(name);
6
+ write(' USING ');
7
+ traverse(module);
8
+ };
@@ -1,4 +1,5 @@
1
- export const nextval = (path, {write}) => {
1
+ export const defaultNextval = (path, {write}) => {
2
2
  const [seq] = path.node.arguments;
3
+
3
4
  write(`DEFAULT nextval('${seq.name}')`);
4
5
  };
@@ -0,0 +1,3 @@
1
+ export const defaultValues = (path, {write}) => {
2
+ write('DEFAULT VALUES');
3
+ };
@@ -0,0 +1,6 @@
1
+ export const detach = (path, {write, traverse}) => {
2
+ const [schema] = path.get('arguments');
3
+
4
+ write('DETACH DATABASE ');
5
+ traverse(schema);
6
+ };
@@ -0,0 +1,14 @@
1
+ export const distinctOn = (path, {write, traverse}) => {
2
+ write('DISTINCT ON (');
3
+
4
+ const args = path.get('arguments');
5
+
6
+ for (const [i, arg] of args.entries()) {
7
+ if (i > 0)
8
+ write(', ');
9
+
10
+ traverse(arg);
11
+ }
12
+
13
+ write(')');
14
+ };
@@ -0,0 +1,5 @@
1
+ export const doBlock = (path, {write}) => {
2
+ const [arg] = path.get('arguments');
3
+
4
+ write(`DO $$${arg.node.value}$$`);
5
+ };
@@ -1,9 +1,21 @@
1
+ import {types} from '@putout/babel';
2
+
3
+ const {isCallExpression} = types;
4
+
5
+ const isIfExists = (arg) => isCallExpression(arg.node) && arg.node.callee.name === 'ifExists';
6
+
1
7
  export const dropIndex = (path, {write, traverse}) => {
2
8
  const args = path.get('arguments');
9
+ let rest = args;
3
10
 
4
11
  write('DROP INDEX ');
5
12
 
6
- for (const [i, arg] of args.entries()) {
13
+ if (args.length > 0 && isIfExists(args[0])) {
14
+ write('IF EXISTS ');
15
+ rest = args.slice(1);
16
+ }
17
+
18
+ for (const [i, arg] of rest.entries()) {
7
19
  if (i > 0)
8
20
  write(', ');
9
21
 
@@ -0,0 +1,24 @@
1
+ import {types} from '@putout/babel';
2
+
3
+ const {isCallExpression} = types;
4
+
5
+ const isIfExists = (arg) => isCallExpression(arg.node) && arg.node.callee.name === 'ifExists';
6
+
7
+ export const dropView = (path, {write, traverse}) => {
8
+ const args = path.get('arguments');
9
+ let rest = args;
10
+
11
+ write('DROP VIEW ');
12
+
13
+ if (args.length > 0 && isIfExists(args[0])) {
14
+ write('IF EXISTS ');
15
+ rest = args.slice(1);
16
+ }
17
+
18
+ for (const [i, arg] of rest.entries()) {
19
+ if (i > 0)
20
+ write(', ');
21
+
22
+ traverse(arg);
23
+ }
24
+ };
@@ -0,0 +1,9 @@
1
+ export const except = (path, {write, traverse}) => {
2
+ const [left, right] = path.get('arguments');
3
+
4
+ traverse(left);
5
+ write.breakline();
6
+ write('EXCEPT');
7
+ write.breakline();
8
+ traverse(right);
9
+ };
@@ -0,0 +1,4 @@
1
+ export const explain = (path, {write, traverse}) => {
2
+ write('EXPLAIN ');
3
+ traverse(path.get('arguments')[0]);
4
+ };
@@ -0,0 +1,4 @@
1
+ export const explainAnalyze = (path, {write, traverse}) => {
2
+ write('EXPLAIN ANALYZE ');
3
+ traverse(path.get('arguments')[0]);
4
+ };
@@ -0,0 +1,4 @@
1
+ export const explainQueryPlan = (path, {write, traverse}) => {
2
+ write('EXPLAIN QUERY PLAN ');
3
+ traverse(path.get('arguments')[0]);
4
+ };
@@ -0,0 +1,5 @@
1
+ export const fetchFirst = (path, {write, traverse}) => {
2
+ write('FETCH FIRST ');
3
+ traverse(path.get('arguments')[0]);
4
+ write(' ROWS ONLY');
5
+ };
@@ -0,0 +1,8 @@
1
+ export const filter = (path, {write, traverse}) => {
2
+ const [fn, condition] = path.get('arguments');
3
+
4
+ traverse(fn);
5
+ write(' FILTER (WHERE ');
6
+ traverse(condition);
7
+ write(')');
8
+ };
@@ -0,0 +1,3 @@
1
+ export const forUpdate = (path, {write}) => {
2
+ write('FOR UPDATE');
3
+ };
@@ -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,17 @@ 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
+ const toFuncName = (name) => ALL_CAPS_RE.test(name) ? name : toSnake(name);
72
+
42
73
  const isTableFunc = (node) => isCallExpression(node) && !TABLE_ARG_NAMES.includes(node.callee.name);
43
74
 
44
75
  const printTableFunc = (path, {write, traverse}) => {
45
76
  const {name} = path.node.callee;
46
77
  const args = path.get('arguments');
47
78
 
48
- write(toSnake(name));
79
+ write(toFuncName(name));
49
80
  write('(');
50
81
 
51
82
  for (const [i, arg] of args.entries()) {
@@ -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
+ };
@@ -0,0 +1,9 @@
1
+ export const intersect = (path, {write, traverse}) => {
2
+ const [left, right] = path.get('arguments');
3
+
4
+ traverse(left);
5
+ write.breakline();
6
+ write('INTERSECT');
7
+ write.breakline();
8
+ traverse(right);
9
+ };
@@ -1,11 +1,39 @@
1
+ import {types} from '@putout/babel';
2
+
3
+ const {isTSAsExpression} = types;
4
+
5
+ const printTable = (table, {write, traverse}) => {
6
+ if (isTSAsExpression(table.node)) {
7
+ traverse(table.get('expression'));
8
+ write(` AS ${table.node.typeAnnotation.literal.value}`);
9
+
10
+ return;
11
+ }
12
+
13
+ traverse(table);
14
+ };
15
+
1
16
  export const into = (path, {write, traverse}) => {
2
17
  const args = path.get('arguments');
3
18
  const [table] = args;
19
+
20
+ write('INTO ');
21
+ printTable(table, {
22
+ write,
23
+ traverse,
24
+ });
25
+
26
+ // no column list: INTO t VALUES (...) / INTO t DEFAULT VALUES
27
+ if (args.length === 2) {
28
+ write.space();
29
+ traverse(args[1]);
30
+
31
+ return;
32
+ }
33
+
4
34
  const [, columns] = args;
5
35
  const values = args[2];
6
36
 
7
- write('INTO ');
8
- traverse(table);
9
37
  write(' (');
10
38
 
11
39
  if (columns.node.type === 'ArrayExpression') {
@@ -6,6 +6,7 @@ const JSON_OPS = new Set([
6
6
  '?',
7
7
  '?|',
8
8
  '?&',
9
+ '??',
9
10
  '@>',
10
11
  '<@',
11
12
  ]);
@@ -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,17 @@
1
+ const isWhereArg = ({node}) => node.type === 'CallExpression' && node.callee.name === 'where';
2
+
1
3
  export const onConflict = (path, {write, traverse}) => {
2
4
  const args = path.get('arguments');
3
5
  const action = args.at(-1);
4
- const targets = args.slice(0, -1);
6
+ let whereArg = null;
7
+ let targetCount = args.length - 1;
8
+
9
+ if (targetCount && isWhereArg(args.at(-2))) {
10
+ whereArg = args.at(-2);
11
+ --targetCount;
12
+ }
13
+
14
+ const targets = args.slice(0, targetCount);
5
15
 
6
16
  write('ON CONFLICT');
7
17
 
@@ -17,6 +27,11 @@ export const onConflict = (path, {write, traverse}) => {
17
27
  write(')');
18
28
  }
19
29
 
30
+ if (whereArg) {
31
+ write(' ');
32
+ traverse(whereArg);
33
+ }
34
+
20
35
  write(' DO ');
21
36
 
22
37
  if (action.node.type === 'Identifier' && action.node.name === 'nothing') {