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
@@ -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
 
@@ -152,7 +204,7 @@ const flattenJoins = (node, joins = []) => {
152
204
  else
153
205
  joins.push(callExpression(identifier(fnName), [
154
206
  convertAlias(node.right),
155
- convertOn(node.specification),
207
+ convertSpecification(node.specification),
156
208
  ]));
157
209
 
158
210
  return joins;
@@ -163,7 +215,26 @@ const flattenJoins = (node, joins = []) => {
163
215
  return joins;
164
216
  };
165
217
 
166
- export const convertFrom = ({expr, whereClause, groupByClause, havingClause, orderByClause, limitClause}) => {
218
+ const convertWindow = (node) => {
219
+ const {name} = node;
220
+ const {expr} = node.window;
221
+
222
+ const args = [
223
+ identifier(name.name),
224
+ ];
225
+
226
+ const {partitionBy, orderBy} = expr;
227
+
228
+ if (partitionBy)
229
+ args.push(callExpression(identifier('partitionBy'), partitionBy.specifications.items.map(convertOperand)));
230
+
231
+ if (orderBy)
232
+ args.push(convertOrderBy(orderBy));
233
+
234
+ return callExpression(identifier('namedWindow'), args);
235
+ };
236
+
237
+ export const convertFrom = ({expr, whereClause, groupByClause, havingClause, orderByClause, windowClause, limitClause, offsetClause, fetchClause, forClause}) => {
167
238
  const fromArgs = [];
168
239
 
169
240
  if (whereClause)
@@ -177,12 +248,24 @@ export const convertFrom = ({expr, whereClause, groupByClause, havingClause, ord
177
248
  if (havingClause)
178
249
  fromArgs.push(convertHaving(havingClause));
179
250
 
251
+ if (windowClause)
252
+ fromArgs.push(...windowClause.namedWindows.items.map(convertWindow));
253
+
180
254
  if (orderByClause)
181
255
  fromArgs.push(convertOrderBy(orderByClause));
182
256
 
183
257
  if (limitClause)
184
258
  fromArgs.push(convertLimit(limitClause));
185
259
 
260
+ if (offsetClause)
261
+ fromArgs.push(convertOffset(offsetClause));
262
+
263
+ if (fetchClause)
264
+ fromArgs.push(convertFetch(fetchClause));
265
+
266
+ if (forClause)
267
+ fromArgs.push(convertForUpdate());
268
+
186
269
  if (expr.type === 'join_expr') {
187
270
  const parts = flattenJoins(expr);
188
271
  return callExpression(identifier('from'), parts);
@@ -200,8 +283,14 @@ export const convertFrom = ({expr, whereClause, groupByClause, havingClause, ord
200
283
  ...fromArgs,
201
284
  ]);
202
285
 
286
+ if (expr.type === 'member_expr')
287
+ return callExpression(identifier('from'), [
288
+ convertMemberExpr(expr),
289
+ ...fromArgs,
290
+ ]);
291
+
203
292
  return callExpression(identifier('from'), [
204
- identifier(expr.name),
293
+ convertAlias(expr),
205
294
  ...fromArgs,
206
295
  ]);
207
296
  };
@@ -7,6 +7,9 @@ const {
7
7
  } = types;
8
8
 
9
9
  export const convertLimit = (limitClause) => {
10
+ if (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,26 @@ 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
+
11
+ return callExpression(identifier(isLast ? 'nullsLast' : 'nullsFirst'), [col]);
12
+ };
13
+
6
14
  const convertOrderItem = (item) => {
7
15
  if (item.type !== 'sort_specification')
8
16
  return convertOperand(item);
9
17
 
10
- const col = convertOperand(item.expr);
18
+ let col = convertOperand(item.expr);
11
19
  const {direction} = item;
12
20
 
13
- if (direction.type === 'sort_direction_desc')
14
- return callExpression(identifier('desc'), [col]);
21
+ if (direction && isDesc(direction))
22
+ col = callExpression(identifier('desc'), [col]);
23
+
24
+ if (item.nullHandlingKw)
25
+ return convertNullsOrder(col, item.nullHandlingKw);
15
26
 
16
27
  // ASC is default — no wrapper
17
28
  return col;
@@ -1,28 +1,15 @@
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);
16
-
17
- if (arg.type === 'string_literal')
18
- return stringLiteral(arg.value);
19
-
20
- return identifier(arg.name);
21
- };
22
-
23
9
  export const convertTableFunc = (expr) => {
24
- const name = toCamel(expr.name.name);
25
- const args = expr.args.expr.args.items.map(convertArg);
10
+ // multi-word snake_case names are lowercased first (GENERATE_SERIES → generateSeries)
11
+ const name = toCamel(expr.name.name.includes('_') ? expr.name.name.toLowerCase() : expr.name.name);
12
+ const args = expr.args.expr.args.items.map(convertOperand);
26
13
 
27
14
  return callExpression(identifier(name), args);
28
15
  };
@@ -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;
@@ -69,5 +93,29 @@ const clauseVisitors = {
69
93
  pragma_stmt: convertPragma,
70
94
  attach_database_stmt: convertAttach,
71
95
  create_index_stmt: convertCreateIndex,
72
- vacuum_stmt: () => callExpression(identifier('vacuum'), []),
96
+ vacuum_stmt: ({file}) => callExpression(identifier('vacuum'), file ? [
97
+ stringLiteral(file.value),
98
+ ] : []),
99
+ truncate_stmt: ({tables}) => callExpression(identifier('truncate'), tables.items.map(({name}) => identifier(name))),
100
+ detach_database_stmt: ({schema}) => callExpression(identifier('detach'), [
101
+ identifier(schema.name),
102
+ ]),
103
+ explain_stmt: convertExplain,
104
+ set_parameter_stmt: convertSetParam,
105
+ show_parameter_stmt: convertShowParam,
106
+ create_view_stmt: convertCreateView,
107
+ drop_view_stmt: convertDropView,
108
+ create_extension_stmt: convertCreateExtension,
109
+ create_schema_stmt: convertCreateSchema,
110
+ grant_privilege_stmt: convertGrant,
111
+ reindex_stmt: (stmt) => callExpression(identifier('reindex'), stmt.name ? [
112
+ identifier(stmt.name.name),
113
+ ] : []),
114
+ analyze_stmt: (stmt) => callExpression(identifier('analyze'), (stmt.tables?.items ?? []).map(convertTableName)),
115
+ release_savepoint_stmt: ({savepoint}) => callExpression(identifier('releaseSavepoint'), [
116
+ identifier(savepoint.name),
117
+ ]),
118
+ do_stmt: (stmt) => callExpression(identifier('doBlock'), [
119
+ stringLiteral(stmt.body.value),
120
+ ]),
73
121
  };
@@ -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);
@@ -56,24 +60,53 @@ const convertUpsert = (upsertClause) => {
56
60
  const targetArgs = target ? target.expr.items.map(convertExprToIdentifier) : [];
57
61
  const actionNode = action.type === 'upsert_action_nothing' ? identifier('nothing') : convertSet(action.set);
58
62
 
63
+ if (upsertClause.where)
64
+ targetArgs.push(callExpression(identifier('where'), [
65
+ convertCondition(upsertClause.where.expr),
66
+ ]));
67
+
59
68
  return callExpression(identifier('onConflict'), [...targetArgs, actionNode]);
60
69
  };
61
70
 
62
- export const convertInsert = ({clauses}) => {
71
+ const isReplaceInto = ({insertKw, orAction}) => !orAction && insertKw?.name === 'REPLACE';
72
+
73
+ const convertReplaceInto = ({clauses}) => {
74
+ const ic = findClause(clauses, 'insert_clause');
75
+ const rc = findClause(clauses, 'returning_clause');
76
+
77
+ const cols = ic.columns.expr.items;
78
+ const colArg = cols.length === 1 ? identifier(cols[0].name) : arrayExpression(cols.map(convertColToIdentifier));
79
+ const source = convertSource(findClause(clauses, 'values_clause'));
80
+
81
+ return callExpression(identifier('replaceInto'), [
82
+ callExpression(identifier('into'), [
83
+ identifier(ic.table.name),
84
+ colArg,
85
+ source,
86
+ ]),
87
+ rc && convertReturning(rc),
88
+ ].filter(Boolean));
89
+ };
90
+
91
+ export const convertInsert = (stmt) => {
92
+ if (isReplaceInto(stmt.clauses[0]))
93
+ return convertReplaceInto(stmt);
94
+
95
+ const {clauses} = stmt;
63
96
  const [ic] = clauses;
64
97
  const [, vc] = clauses;
65
98
  const uc = findClause(clauses, 'upsert_clause');
66
99
  const rc = findClause(clauses, 'returning_clause');
100
+ const dc = findClause(clauses, 'default_values');
67
101
 
68
- const cols = ic.columns.expr.items;
69
- const colArg = cols.length === 1 ? identifier(cols[0].name) : arrayExpression(cols.map(convertColToIdentifier));
70
- const source = convertSource(vc);
102
+ const colArgs = convertColumns(ic);
103
+ const source = dc ? callExpression(identifier('defaultValues'), []) : convertSource(vc);
71
104
 
72
105
  const args = [
73
106
  ic.orAction && convertOrAction(ic.orAction),
74
107
  callExpression(identifier('into'), [
75
- identifier(ic.table.name),
76
- colArg,
108
+ convertTable(ic),
109
+ ...colArgs,
77
110
  source,
78
111
  ]),
79
112
  uc && convertUpsert(uc),
@@ -82,3 +115,28 @@ export const convertInsert = ({clauses}) => {
82
115
 
83
116
  return callExpression(identifier('insert'), args);
84
117
  };
118
+
119
+ const isAliasTable = ({table}) => table.type === 'alias';
120
+
121
+ const convertTable = (ic) => {
122
+ if (!isAliasTable(ic))
123
+ return identifier(ic.table.name);
124
+
125
+ const {expr, alias} = ic.table;
126
+
127
+ return tsAsExpression(identifier(expr.name), tsLiteralType(stringLiteral(alias.name)));
128
+ };
129
+
130
+ const convertColumns = (ic) => {
131
+ const columns = ic.columns || ic.table.columnAliases;
132
+
133
+ if (!columns)
134
+ return [];
135
+
136
+ const cols = columns.expr.items;
137
+ const colArg = cols.length === 1 ? identifier(cols[0].name) : arrayExpression(cols.map(convertColToIdentifier));
138
+
139
+ return [
140
+ colArg,
141
+ ];
142
+ };
@@ -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')
@@ -1,12 +1,62 @@
1
1
  import {types} from '@putout/babel';
2
+ import {convertMemberExpr} from '#parser/clause/operand';
2
3
 
3
- const {identifier, callExpression} = types;
4
+ const {
5
+ identifier,
6
+ stringLiteral,
7
+ numericLiteral,
8
+ callExpression,
9
+ binaryExpression,
10
+ } = types;
11
+
12
+ const isPragmaFuncCall = ({type}) => type === 'pragma_func_call';
13
+ const isPragmaAssignment = ({type}) => type === 'pragma_assignment';
14
+
15
+ const convertName = (node) => {
16
+ if (node.type === 'member_expr')
17
+ return convertMemberExpr(node);
18
+
19
+ return identifier(node.name);
20
+ };
21
+
22
+ const convertValue = (node) => {
23
+ if (node.type === 'keyword' || node.type === 'identifier')
24
+ return identifier(node.name);
25
+
26
+ if (node.type === 'number_literal')
27
+ return numericLiteral(node.value);
28
+
29
+ return stringLiteral(node.value);
30
+ };
4
31
 
5
32
  export const convertPragma = ({pragma}) => {
33
+ if (isPragmaAssignment(pragma))
34
+ return convertAssignment(pragma);
35
+
36
+ if (isPragmaFuncCall(pragma))
37
+ return convertFuncCall(pragma);
38
+
39
+ return callExpression(identifier('pragma'), [
40
+ convertName(pragma),
41
+ ]);
42
+ };
43
+
44
+ const convertAssignment = ({name, value}) => callExpression(identifier('pragma'), [
45
+ binaryExpression('===', convertName(name), convertValue(value)),
46
+ ]);
47
+
48
+ const convertFuncCall = (pragma) => {
6
49
  const arg = pragma.args.expr;
7
50
 
8
51
  return callExpression(identifier('pragma'), [
9
52
  identifier(pragma.name.name),
10
- identifier(arg.name.toLowerCase()),
53
+ convertArg(arg),
11
54
  ]);
12
55
  };
56
+
57
+ const convertArg = (arg) => {
58
+ if (arg.type === 'string_literal')
59
+ return stringLiteral(arg.value);
60
+
61
+ return identifier(arg.name.toLowerCase());
62
+ };