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
@@ -2,11 +2,20 @@ import {types} from '@putout/babel';
2
2
  import {convertBetween} from '#parser/clause/where';
3
3
  import {convertClause} from '#parser/clause';
4
4
  import {convertBinary} from '#parser/clause/binary';
5
+ import {
6
+ convertOperand,
7
+ convertMemberExpr,
8
+ } from '#parser/clause/operand';
5
9
  import {convertGroupBy} from './group-by.js';
6
10
  import {convertHaving} from './having.js';
7
11
  import {convertOrderBy} from './order-by.js';
8
- import {convertLimit} from './limit.js';
9
12
  import {convertTableFunc} from './table-func.js';
13
+ import {
14
+ convertLimit,
15
+ convertOffset,
16
+ convertFetch,
17
+ convertForUpdate,
18
+ } from './limit.js';
10
19
 
11
20
  const {
12
21
  identifier,
@@ -21,6 +30,20 @@ const isExists = (node) => node.type === 'prefix_op_expr' && node.operator.name
21
30
  const isNotExists = (node) => node.type === 'prefix_op_expr' && node.operator.name === 'NOT' && isExists(node.expr);
22
31
  const isParenExpr = (node) => node.type === 'paren_expr';
23
32
 
33
+ const POSTFIX_OP = {
34
+ ISNULL: 'isNull',
35
+ NOTNULL: 'isNotNull',
36
+ };
37
+
38
+ const convertPostfix = (node) => {
39
+ const {name} = node.operator;
40
+ const fn = POSTFIX_OP[name];
41
+
42
+ return callExpression(identifier(fn), [
43
+ convertOperand(node.expr),
44
+ ]);
45
+ };
46
+
24
47
  const convertCondition = (node) => {
25
48
  if (node.operator && node.operator.name === 'AND')
26
49
  return logicalExpression('&&', convertCondition(node.left), convertCondition(node.right));
@@ -31,6 +54,9 @@ const convertCondition = (node) => {
31
54
  if (node.type === 'between_expr')
32
55
  return convertBetween(node);
33
56
 
57
+ if (node.type === 'postfix_op_expr')
58
+ return convertPostfix(node);
59
+
34
60
  if (isNotExists(node))
35
61
  return callExpression(identifier('notExists'), [
36
62
  convertClause(node.expr.expr.expr),
@@ -51,7 +77,30 @@ const convertOn = (spec) => callExpression(identifier('on'), [
51
77
  convertCondition(spec.expr),
52
78
  ]);
53
79
 
80
+ const isJoinUsing = ({type}) => type === 'join_using_specification';
81
+
82
+ const convertSpecification = (spec) => {
83
+ if (isJoinUsing(spec))
84
+ return callExpression(identifier('using'), spec.expr.expr.items.map(({name}) => identifier(name)));
85
+
86
+ return convertOn(spec);
87
+ };
88
+
89
+ const isIndexedTable = ({type}) => type === 'indexed_table';
90
+ const isNotIndexedTable = ({type}) => type === 'not_indexed_table';
91
+
54
92
  const convertAlias = (node) => {
93
+ if (isIndexedTable(node))
94
+ return callExpression(identifier('indexedBy'), [
95
+ identifier(node.table.name),
96
+ identifier(node.index.name),
97
+ ]);
98
+
99
+ if (isNotIndexedTable(node))
100
+ return callExpression(identifier('notIndexed'), [
101
+ identifier(node.table.name),
102
+ ]);
103
+
55
104
  if (node.type !== 'alias')
56
105
  return identifier(node.name);
57
106
 
@@ -75,6 +124,9 @@ const convertAlias = (node) => {
75
124
  ]), tsLiteralType(stringLiteral(aliasName)));
76
125
  }
77
126
 
127
+ if (node.expr.type === 'func_call')
128
+ return tsAsExpression(convertTableFunc(node.expr), tsLiteralType(stringLiteral(node.alias.name)));
129
+
78
130
  return tsAsExpression(identifier(node.expr.name), tsLiteralType(stringLiteral(node.alias.name)));
79
131
  };
80
132
 
@@ -93,6 +145,11 @@ const joinTypeParts = (operator) => {
93
145
  return parts;
94
146
  };
95
147
 
148
+ const capitalizeWord = (s) => s[0].toUpperCase() +
149
+ s
150
+ .slice(1)
151
+ .toLowerCase();
152
+
96
153
  const convertJoinType = (operator) => {
97
154
  const parts = joinTypeParts(operator);
98
155
 
@@ -103,14 +160,12 @@ const convertJoinType = (operator) => {
103
160
  return 'join';
104
161
 
105
162
  // 'LEFT OUTER' → 'leftOuter' → 'leftOuterJoin'
106
- const camel = parts[0].toLowerCase() +
107
- parts
108
- .slice(1)
109
- .map((s) => s[0].toUpperCase() +
110
- s
111
- .slice(1)
112
- .toLowerCase())
113
- .join('');
163
+ const tail = [];
164
+
165
+ for (const part of parts.slice(1))
166
+ tail.push(capitalizeWord(part));
167
+
168
+ const camel = parts[0].toLowerCase() + tail.join('');
114
169
 
115
170
  return `${camel}Join`;
116
171
  };
@@ -152,7 +207,7 @@ const flattenJoins = (node, joins = []) => {
152
207
  else
153
208
  joins.push(callExpression(identifier(fnName), [
154
209
  convertAlias(node.right),
155
- convertOn(node.specification),
210
+ convertSpecification(node.specification),
156
211
  ]));
157
212
 
158
213
  return joins;
@@ -163,7 +218,26 @@ const flattenJoins = (node, joins = []) => {
163
218
  return joins;
164
219
  };
165
220
 
166
- export const convertFrom = ({expr, whereClause, groupByClause, havingClause, orderByClause, limitClause}) => {
221
+ const convertWindow = (node) => {
222
+ const {name} = node;
223
+ const {expr} = node.window;
224
+
225
+ const args = [
226
+ identifier(name.name),
227
+ ];
228
+
229
+ const {partitionBy, orderBy} = expr;
230
+
231
+ if (partitionBy)
232
+ args.push(callExpression(identifier('partitionBy'), partitionBy.specifications.items.map(convertOperand)));
233
+
234
+ if (orderBy)
235
+ args.push(convertOrderBy(orderBy));
236
+
237
+ return callExpression(identifier('namedWindow'), args);
238
+ };
239
+
240
+ export const convertFrom = ({expr, whereClause, groupByClause, havingClause, orderByClause, windowClause, limitClause, offsetClause, fetchClause, forClause}) => {
167
241
  const fromArgs = [];
168
242
 
169
243
  if (whereClause)
@@ -177,15 +251,31 @@ export const convertFrom = ({expr, whereClause, groupByClause, havingClause, ord
177
251
  if (havingClause)
178
252
  fromArgs.push(convertHaving(havingClause));
179
253
 
254
+ if (windowClause)
255
+ fromArgs.push(...windowClause.namedWindows.items.map(convertWindow));
256
+
180
257
  if (orderByClause)
181
258
  fromArgs.push(convertOrderBy(orderByClause));
182
259
 
183
260
  if (limitClause)
184
261
  fromArgs.push(convertLimit(limitClause));
185
262
 
263
+ if (offsetClause)
264
+ fromArgs.push(convertOffset(offsetClause));
265
+
266
+ if (fetchClause)
267
+ fromArgs.push(convertFetch(fetchClause));
268
+
269
+ if (forClause)
270
+ fromArgs.push(convertForUpdate());
271
+
186
272
  if (expr.type === 'join_expr') {
187
273
  const parts = flattenJoins(expr);
188
- return callExpression(identifier('from'), parts);
274
+
275
+ return callExpression(identifier('from'), [
276
+ ...parts,
277
+ ...fromArgs,
278
+ ]);
189
279
  }
190
280
 
191
281
  if (expr.type === 'alias')
@@ -200,8 +290,14 @@ export const convertFrom = ({expr, whereClause, groupByClause, havingClause, ord
200
290
  ...fromArgs,
201
291
  ]);
202
292
 
293
+ if (expr.type === 'member_expr')
294
+ return callExpression(identifier('from'), [
295
+ convertMemberExpr(expr),
296
+ ...fromArgs,
297
+ ]);
298
+
203
299
  return callExpression(identifier('from'), [
204
- identifier(expr.name),
300
+ convertAlias(expr),
205
301
  ...fromArgs,
206
302
  ]);
207
303
  };
@@ -3,4 +3,11 @@ import {convertOperand} from '#parser/clause/operand';
3
3
 
4
4
  const {callExpression, identifier} = types;
5
5
 
6
- export const convertGroupBy = (clause) => callExpression(identifier('groupBy'), clause.columns.items.map(convertOperand));
6
+ export const convertGroupBy = (clause) => {
7
+ const args = [];
8
+
9
+ for (const item of clause.columns.items)
10
+ args.push(convertOperand(item));
11
+
12
+ return callExpression(identifier('groupBy'), args);
13
+ };
@@ -7,6 +7,9 @@ const {
7
7
  } = types;
8
8
 
9
9
  export const convertLimit = (limitClause) => {
10
+ if (limitClause.count && limitClause.count.type === 'limit_all')
11
+ return callExpression(identifier('limitAll'), []);
12
+
10
13
  const args = [
11
14
  numericLiteral(limitClause.count.value),
12
15
  ];
@@ -16,3 +19,13 @@ export const convertLimit = (limitClause) => {
16
19
 
17
20
  return callExpression(identifier('limit'), args);
18
21
  };
22
+
23
+ export const convertOffset = (offsetClause) => callExpression(identifier('offset'), [
24
+ numericLiteral(offsetClause.offset.value),
25
+ ]);
26
+
27
+ export const convertFetch = (fetchClause) => callExpression(identifier('fetchFirst'), [
28
+ numericLiteral(fetchClause.count.value),
29
+ ]);
30
+
31
+ export const convertForUpdate = () => callExpression(identifier('forUpdate'), []);
@@ -3,15 +3,27 @@ import {convertOperand} from '#parser/clause/operand';
3
3
 
4
4
  const {callExpression, identifier} = types;
5
5
 
6
+ const isDesc = ({type}) => type === 'sort_direction_desc';
7
+
8
+ const convertNullsOrder = (col, nullHandlingKw) => {
9
+ const isLast = nullHandlingKw.at(-1).name === 'LAST';
10
+ const fnName = isLast ? 'nullsLast' : 'nullsFirst';
11
+
12
+ return callExpression(identifier(fnName), [col]);
13
+ };
14
+
6
15
  const convertOrderItem = (item) => {
7
16
  if (item.type !== 'sort_specification')
8
17
  return convertOperand(item);
9
18
 
10
- const col = convertOperand(item.expr);
19
+ let col = convertOperand(item.expr);
11
20
  const {direction} = item;
12
21
 
13
- if (direction.type === 'sort_direction_desc')
14
- return callExpression(identifier('desc'), [col]);
22
+ if (direction && isDesc(direction))
23
+ col = callExpression(identifier('desc'), [col]);
24
+
25
+ if (item.nullHandlingKw)
26
+ return convertNullsOrder(col, item.nullHandlingKw);
15
27
 
16
28
  // ASC is default — no wrapper
17
29
  return col;
@@ -1,28 +1,24 @@
1
1
  import {types} from '@putout/babel';
2
+ import {convertOperand} from '#parser/clause/operand';
2
3
 
3
- const {
4
- callExpression,
5
- identifier,
6
- numericLiteral,
7
- stringLiteral,
8
- } = types;
4
+ const {callExpression, identifier} = types;
9
5
 
10
6
  const SNAKE_RE = /_([a-z])/g;
11
7
  const toCamel = (s) => s.replace(SNAKE_RE, (_, c) => c.toUpperCase());
12
8
 
13
- const convertArg = (arg) => {
14
- if (arg.type === 'number_literal')
15
- return numericLiteral(arg.value);
9
+ const getRawName = (expr) => {
10
+ const raw = expr.name.name;
16
11
 
17
- if (arg.type === 'string_literal')
18
- return stringLiteral(arg.value);
12
+ if (raw.includes('_'))
13
+ return raw.toLowerCase();
19
14
 
20
- return identifier(arg.name);
15
+ return raw;
21
16
  };
22
17
 
23
18
  export const convertTableFunc = (expr) => {
24
- const name = toCamel(expr.name.name);
25
- const args = expr.args.expr.args.items.map(convertArg);
19
+ // multi-word snake_case names are lowercased first (GENERATE_SERIES → generateSeries)
20
+ const name = toCamel(getRawName(expr));
21
+ const args = expr.args.expr.args.items.map(convertOperand);
26
22
 
27
23
  return callExpression(identifier(name), args);
28
24
  };
@@ -0,0 +1,26 @@
1
+ import {types} from '@putout/babel';
2
+
3
+ const {identifier, callExpression} = types;
4
+
5
+ export const convertGrant = (stmt) => {
6
+ const privArgs = [];
7
+
8
+ for (const p of stmt.privileges.items)
9
+ privArgs.push(identifier(p.privilegeKw.name));
10
+
11
+ const tableArgs = [];
12
+
13
+ for (const t of stmt.resource.tables.items)
14
+ tableArgs.push(identifier(t.name));
15
+
16
+ const roleArgs = [];
17
+
18
+ for (const r of stmt.roles.items)
19
+ roleArgs.push(identifier(r.name));
20
+
21
+ return callExpression(identifier('grant'), [
22
+ callExpression(identifier('privilege'), privArgs),
23
+ callExpression(identifier('grantOn'), tableArgs),
24
+ callExpression(identifier('to'), roleArgs),
25
+ ]);
26
+ };
@@ -12,8 +12,19 @@ import {convertDropTable} from '#parser/clause/drop-table';
12
12
  import {convertDropIndex} from '#parser/clause/drop-index';
13
13
  import {convertPragma} from '#parser/clause/pragma';
14
14
  import {convertAttach} from '#parser/clause/attach';
15
+ import {convertExplain} from '#parser/clause/explain';
16
+ import {
17
+ convertSetParam,
18
+ convertShowParam,
19
+ } from '#parser/clause/set-param';
20
+ import {convertCreateView} from '#parser/clause/create-view';
21
+ import {convertDropView} from '#parser/clause/drop-view';
22
+ import {convertCreateExtension} from '#parser/clause/create-extension';
23
+ import {convertCreateSchema} from '#parser/clause/create-schema';
24
+ import {convertGrant} from '#parser/clause/grant';
15
25
  import {convertUnion} from '#parser/clause/union';
16
26
  import {findClause} from '#parser/clause/find';
27
+ import {convertMemberExpr} from '#parser/clause/operand';
17
28
  import {
18
29
  convertBegin,
19
30
  convertCommit,
@@ -21,7 +32,20 @@ import {
21
32
  convertSavepoint,
22
33
  } from '#parser/clause/transaction';
23
34
 
24
- const {identifier, callExpression} = types;
35
+ const {
36
+ identifier,
37
+ callExpression,
38
+ stringLiteral,
39
+ } = types;
40
+
41
+ const isMemberExpr = ({type}) => type === 'member_expr';
42
+
43
+ const convertTableName = (table) => {
44
+ if (isMemberExpr(table))
45
+ return convertMemberExpr(table);
46
+
47
+ return identifier(table.name);
48
+ };
25
49
 
26
50
  export const convertClause = (stmt) => {
27
51
  const {type} = stmt;
@@ -51,6 +75,8 @@ const convertSelectOrNamed = (stmt) => {
51
75
  return convertSelect(stmt);
52
76
  };
53
77
 
78
+ const getAnalyzeTables = ({tables}) => tables.items || tables;
79
+
54
80
  const clauseVisitors = {
55
81
  select_stmt: convertSelectOrNamed,
56
82
  insert_stmt: convertInsertOrNamed,
@@ -69,5 +95,43 @@ const clauseVisitors = {
69
95
  pragma_stmt: convertPragma,
70
96
  attach_database_stmt: convertAttach,
71
97
  create_index_stmt: convertCreateIndex,
72
- vacuum_stmt: () => callExpression(identifier('vacuum'), []),
98
+ vacuum_stmt: ({file}) => callExpression(identifier('vacuum'), file ? [
99
+ stringLiteral(file.value),
100
+ ] : []),
101
+ truncate_stmt: ({tables}) => {
102
+ const args = [];
103
+
104
+ for (const table of tables.items)
105
+ args.push(convertTableName(table));
106
+
107
+ return callExpression(identifier('truncate'), args);
108
+ },
109
+ detach_database_stmt: ({schema}) => callExpression(identifier('detach'), [
110
+ identifier(schema.name),
111
+ ]),
112
+ explain_stmt: convertExplain,
113
+ set_parameter_stmt: convertSetParam,
114
+ show_parameter_stmt: convertShowParam,
115
+ create_view_stmt: convertCreateView,
116
+ drop_view_stmt: convertDropView,
117
+ create_extension_stmt: convertCreateExtension,
118
+ create_schema_stmt: convertCreateSchema,
119
+ grant_privilege_stmt: convertGrant,
120
+ reindex_stmt: (stmt) => callExpression(identifier('reindex'), stmt.name ? [
121
+ identifier(stmt.name.name),
122
+ ] : []),
123
+ analyze_stmt: (stmt) => {
124
+ const args = [];
125
+
126
+ for (const table of getAnalyzeTables(stmt))
127
+ args.push(convertTableName(table));
128
+
129
+ return callExpression(identifier('analyze'), args);
130
+ },
131
+ release_savepoint_stmt: ({savepoint}) => callExpression(identifier('releaseSavepoint'), [
132
+ identifier(savepoint.name),
133
+ ]),
134
+ do_stmt: (stmt) => callExpression(identifier('doBlock'), [
135
+ stringLiteral(stmt.body.value),
136
+ ]),
73
137
  };
@@ -3,12 +3,16 @@ import {convertOperand} from '#parser/clause/operand';
3
3
  import {convertSet} from '#parser/clause/set';
4
4
  import {convertSelect} from '#parser/clause/select';
5
5
  import {convertReturning} from '#parser/clause/returning';
6
+ import {convertCondition} from '#parser/clause/where';
6
7
  import {findClause} from '#parser/clause/find';
7
8
 
8
9
  const {
9
10
  identifier,
10
11
  callExpression,
11
12
  arrayExpression,
13
+ stringLiteral,
14
+ tsAsExpression,
15
+ tsLiteralType,
12
16
  } = types;
13
17
 
14
18
  const convertExprToIdentifier = ({expr}) => identifier(expr.name);
@@ -49,24 +53,60 @@ const convertSource = (vc) => {
49
53
  return callExpression(identifier('values'), rowArgs);
50
54
  };
51
55
 
56
+ const getTargetArgs = (target) => {
57
+ if (!target)
58
+ return [];
59
+
60
+ const args = [];
61
+
62
+ for (const item of target.expr.items)
63
+ args.push(convertExprToIdentifier(item));
64
+
65
+ return args;
66
+ };
67
+
68
+ const convertUpsertAction = (action) => {
69
+ if (action.type === 'upsert_action_nothing')
70
+ return identifier('nothing');
71
+
72
+ return convertSet(action.set);
73
+ };
74
+
52
75
  const convertUpsert = (upsertClause) => {
53
76
  const target = upsertClause.conflictTarget;
54
77
  const {action} = upsertClause;
55
78
 
56
- const targetArgs = target ? target.expr.items.map(convertExprToIdentifier) : [];
57
- const actionNode = action.type === 'upsert_action_nothing' ? identifier('nothing') : convertSet(action.set);
79
+ const targetArgs = getTargetArgs(target);
80
+ const actionNode = convertUpsertAction(action);
81
+
82
+ if (upsertClause.where)
83
+ targetArgs.push(callExpression(identifier('where'), [
84
+ convertCondition(upsertClause.where.expr),
85
+ ]));
58
86
 
59
87
  return callExpression(identifier('onConflict'), [...targetArgs, actionNode]);
60
88
  };
61
89
 
62
- const isReplaceInto = ({insertKw, orAction}) => !orAction && insertKw?.name === 'REPLACE';
90
+ const isReplaceInto = ({insertKw, orAction}) => !orAction && insertKw && insertKw.name === 'REPLACE';
91
+
92
+ const colsToArg = (cols) => {
93
+ if (cols.length === 1)
94
+ return identifier(cols[0].name);
95
+
96
+ const args = [];
97
+
98
+ for (const col of cols)
99
+ args.push(convertColToIdentifier(col));
100
+
101
+ return arrayExpression(args);
102
+ };
63
103
 
64
104
  const convertReplaceInto = ({clauses}) => {
65
105
  const ic = findClause(clauses, 'insert_clause');
66
106
  const rc = findClause(clauses, 'returning_clause');
67
107
 
68
108
  const cols = ic.columns.expr.items;
69
- const colArg = cols.length === 1 ? identifier(cols[0].name) : arrayExpression(cols.map(convertColToIdentifier));
109
+ const colArg = colsToArg(cols);
70
110
  const source = convertSource(findClause(clauses, 'values_clause'));
71
111
 
72
112
  return callExpression(identifier('replaceInto'), [
@@ -79,6 +119,13 @@ const convertReplaceInto = ({clauses}) => {
79
119
  ].filter(Boolean));
80
120
  };
81
121
 
122
+ const getSource = (dc, vc) => {
123
+ if (dc)
124
+ return callExpression(identifier('defaultValues'), []);
125
+
126
+ return convertSource(vc);
127
+ };
128
+
82
129
  export const convertInsert = (stmt) => {
83
130
  if (isReplaceInto(stmt.clauses[0]))
84
131
  return convertReplaceInto(stmt);
@@ -88,16 +135,16 @@ export const convertInsert = (stmt) => {
88
135
  const [, vc] = clauses;
89
136
  const uc = findClause(clauses, 'upsert_clause');
90
137
  const rc = findClause(clauses, 'returning_clause');
138
+ const dc = findClause(clauses, 'default_values');
91
139
 
92
- const cols = ic.columns.expr.items;
93
- const colArg = cols.length === 1 ? identifier(cols[0].name) : arrayExpression(cols.map(convertColToIdentifier));
94
- const source = convertSource(vc);
140
+ const colArgs = convertColumns(ic);
141
+ const source = getSource(dc, vc);
95
142
 
96
143
  const args = [
97
144
  ic.orAction && convertOrAction(ic.orAction),
98
145
  callExpression(identifier('into'), [
99
- identifier(ic.table.name),
100
- colArg,
146
+ convertTable(ic),
147
+ ...colArgs,
101
148
  source,
102
149
  ]),
103
150
  uc && convertUpsert(uc),
@@ -106,3 +153,28 @@ export const convertInsert = (stmt) => {
106
153
 
107
154
  return callExpression(identifier('insert'), args);
108
155
  };
156
+
157
+ const isAliasTable = ({table}) => table.type === 'alias';
158
+
159
+ const convertTable = (ic) => {
160
+ if (!isAliasTable(ic))
161
+ return identifier(ic.table.name);
162
+
163
+ const {expr, alias} = ic.table;
164
+
165
+ return tsAsExpression(identifier(expr.name), tsLiteralType(stringLiteral(alias.name)));
166
+ };
167
+
168
+ const convertColumns = (ic) => {
169
+ const columns = ic.columns || ic.table.columnAliases;
170
+
171
+ if (!columns)
172
+ return [];
173
+
174
+ const cols = columns.expr.items;
175
+ const colArg = colsToArg(cols);
176
+
177
+ return [
178
+ colArg,
179
+ ];
180
+ };
@@ -2,6 +2,7 @@ import {types} from '@putout/babel';
2
2
  import {convertCount, isCount} from '#parser/clause/count';
3
3
  import {convertCaseWhen} from '#parser/clause/case-when';
4
4
  import {convertClause} from '#parser/clause';
5
+ import {convertBinary} from '#parser/clause/binary';
5
6
 
6
7
  const {
7
8
  identifier,
@@ -13,17 +14,40 @@ const {
13
14
  callExpression,
14
15
  } = types;
15
16
 
17
+ const isString = (a) => typeof a === 'string';
18
+
16
19
  export const convertMemberExpr = (node) => {
17
20
  const object = identifier(node.object.name);
18
21
 
19
22
  if (node.property.type === 'all_columns')
20
23
  return memberExpression(object, stringLiteral('*'), true);
21
24
 
25
+ if (node.property.type === 'array_subscript')
26
+ return memberExpression(object, convertOperand(node.property.expr), true);
27
+
22
28
  return memberExpression(object, identifier(node.property.name));
23
29
  };
24
30
 
25
31
  const isFuncCall = ({type}) => type === 'func_call';
26
32
 
33
+ const getDataTypeName = ({name}) => {
34
+ if (isString(name))
35
+ return name;
36
+
37
+ return name.text;
38
+ };
39
+
40
+ const isArrayDataType = ({type}) => type === 'array_data_type';
41
+
42
+ const convertDataType = (dataType) => {
43
+ if (!isArrayDataType(dataType))
44
+ return identifier(getDataTypeName(dataType));
45
+
46
+ return callExpression(identifier('arrayType'), [
47
+ identifier(getDataTypeName(dataType.dataType)),
48
+ ]);
49
+ };
50
+
27
51
  const convertQuantifier = (node) => {
28
52
  const {name} = node.quantifierKw;
29
53
  const inner = node.expr.expr;
@@ -42,6 +66,25 @@ const convertQuantifier = (node) => {
42
66
  return callExpression(identifier(name), args);
43
67
  };
44
68
 
69
+ const convertPrefix = (node) => {
70
+ const {operator} = node;
71
+ const name = operator.name || operator;
72
+
73
+ if (name === 'EXISTS')
74
+ return callExpression(identifier('exists'), [
75
+ convertClause(node.expr.expr),
76
+ ]);
77
+
78
+ if (name === 'NOT')
79
+ return callExpression(identifier('not'), [
80
+ convertOperand(node.expr),
81
+ ]);
82
+
83
+ return callExpression(identifier('unaryMinus'), [
84
+ convertOperand(node.expr),
85
+ ]);
86
+ };
87
+
45
88
  export const convertOperand = (node) => {
46
89
  if (isCount(node))
47
90
  return convertCount(node);
@@ -74,7 +117,18 @@ export const convertOperand = (node) => {
74
117
  if (node.type === 'cast_operator_expr')
75
118
  return callExpression(identifier('pgCast'), [
76
119
  convertOperand(node.left),
77
- identifier(node.right.name),
120
+ convertDataType(node.right),
121
+ ]);
122
+
123
+ if (node.type === 'binary_expr')
124
+ return convertBinary(node);
125
+
126
+ if (node.type === 'prefix_op_expr')
127
+ return convertPrefix(node);
128
+
129
+ if (node.type === 'paren_expr' && node.expr.type === 'select_stmt')
130
+ return callExpression(identifier('subquery'), [
131
+ convertClause(node.expr),
78
132
  ]);
79
133
 
80
134
  if (node.type === 'parameter')