happy-sql 1.3.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/ChangeLog +17 -0
  2. package/lib/parser/fixture/comment-fix.js +3 -0
  3. package/lib/parser/fixture/comment.sql +2 -0
  4. package/lib/parser/fixture/section-multi-fix.js +6 -0
  5. package/lib/parser/fixture/section-multi.sql +3 -0
  6. package/lib/parser/fixture/with-named-fix.js +11 -0
  7. package/lib/parser/fixture/with-named.sql +8 -0
  8. package/lib/parser/parser.js +57 -25
  9. package/lib/parser/visitors/clause/column-definition/column-definition.js +41 -0
  10. package/lib/parser/visitors/clause/create-table/create-table.js +17 -0
  11. package/lib/parser/visitors/clause/create-table/fixture/create-table-autoincrement-fix.js +5 -0
  12. package/lib/parser/visitors/clause/create-table/fixture/create-table-autoincrement.sql +1 -0
  13. package/lib/parser/visitors/clause/create-table/fixture/create-table-serial-fix.js +5 -0
  14. package/lib/parser/visitors/clause/create-table/fixture/create-table-serial.sql +1 -0
  15. package/lib/parser/visitors/clause/delete/delete.js +1 -1
  16. package/lib/parser/visitors/clause/delete/fixture/delete-fix.js +1 -1
  17. package/lib/parser/visitors/clause/from/fixture/join-fix.js +6 -0
  18. package/lib/parser/visitors/clause/from/fixture/join-multi-fix.js +6 -0
  19. package/lib/parser/visitors/clause/from/fixture/join-multi.sql +1 -0
  20. package/lib/parser/visitors/clause/from/fixture/join.sql +1 -0
  21. package/lib/parser/visitors/clause/from/from.js +79 -3
  22. package/lib/parser/visitors/clause/index.js +14 -1
  23. package/lib/parser/visitors/clause/insert/fixture/insert-returning-conflict-fix.js +3 -0
  24. package/lib/parser/visitors/clause/insert/fixture/insert-returning-conflict.sql +1 -0
  25. package/lib/parser/visitors/clause/insert/fixture/insert-returning-fix.js +7 -0
  26. package/lib/parser/visitors/clause/insert/fixture/insert-returning-id-fix.js +7 -0
  27. package/lib/parser/visitors/clause/insert/fixture/insert-returning-id.sql +1 -0
  28. package/lib/parser/visitors/clause/insert/fixture/insert-returning.sql +1 -0
  29. package/lib/parser/visitors/clause/insert/insert.js +38 -7
  30. package/lib/parser/visitors/clause/operand.js +4 -0
  31. package/lib/parser/visitors/clause/select/fixture/select-last-insert-rowid-fix.js +3 -0
  32. package/lib/parser/visitors/clause/select/fixture/select-last-insert-rowid.sql +1 -0
  33. package/lib/parser/visitors/clause/select/fixture/select-lastval-fix.js +3 -0
  34. package/lib/parser/visitors/clause/select/fixture/select-lastval.sql +1 -0
  35. package/lib/parser/visitors/clause/select/select.js +16 -0
  36. package/lib/parser/visitors/clause/where/where.js +6 -17
  37. package/lib/parser/visitors/clause/with-named/with-named.js +31 -0
  38. package/lib/printer/fixture/select.js +3 -0
  39. package/lib/printer/visitors/call-expression/clauses/as/as.js +15 -0
  40. package/lib/printer/visitors/call-expression/clauses/auto-increment/auto-increment.js +1 -0
  41. package/lib/printer/visitors/call-expression/clauses/clauses.js +26 -2
  42. package/lib/printer/visitors/call-expression/clauses/column/column.js +10 -0
  43. package/lib/printer/visitors/call-expression/clauses/count/fixture/count-fix.sql +2 -0
  44. package/lib/printer/visitors/call-expression/clauses/count/fixture/count-one-fix.js +3 -0
  45. package/lib/printer/visitors/call-expression/clauses/count/fixture/count-one.js +1 -0
  46. package/lib/printer/visitors/call-expression/clauses/count/fixture/count.js +1 -0
  47. package/lib/printer/visitors/call-expression/clauses/create-table/create-table.js +18 -0
  48. package/lib/printer/visitors/call-expression/clauses/create-table/fixture/createTable-fix.sql +1 -0
  49. package/lib/printer/visitors/call-expression/clauses/create-table/fixture/createTable.js +5 -0
  50. package/lib/printer/visitors/call-expression/clauses/create-table/fixture/createTableSerial-fix.sql +1 -0
  51. package/lib/printer/visitors/call-expression/clauses/create-table/fixture/createTableSerial.js +5 -0
  52. package/lib/printer/visitors/call-expression/clauses/delete/delete.js +1 -1
  53. package/lib/printer/visitors/call-expression/clauses/delete/fixture/delete.js +3 -0
  54. package/lib/printer/visitors/call-expression/clauses/from/fixture/from-fix.js +3 -0
  55. package/lib/printer/visitors/call-expression/clauses/from/fixture/from.js +1 -0
  56. package/lib/printer/visitors/call-expression/clauses/from/from.js +4 -0
  57. package/lib/printer/visitors/call-expression/clauses/insert/fixture/insert.js +3 -0
  58. package/lib/printer/visitors/call-expression/clauses/insert/insert.js +6 -3
  59. package/lib/printer/visitors/call-expression/clauses/into/fixture/into-fix.js +3 -0
  60. package/lib/printer/visitors/call-expression/clauses/into/fixture/into.js +1 -0
  61. package/lib/printer/visitors/call-expression/clauses/into/fixture/single-column-fix.js +3 -0
  62. package/lib/printer/visitors/call-expression/clauses/into/fixture/single-column-fix.sql +1 -0
  63. package/lib/printer/visitors/call-expression/clauses/into/fixture/single-column.js +3 -0
  64. package/lib/printer/visitors/call-expression/clauses/join/fixture/join-fix.sql +4 -0
  65. package/lib/printer/visitors/call-expression/clauses/join/fixture/join-multi-fix.sql +4 -0
  66. package/lib/printer/visitors/call-expression/clauses/join/fixture/join-multi.js +1 -0
  67. package/lib/printer/visitors/call-expression/clauses/join/fixture/join.js +1 -0
  68. package/lib/printer/visitors/call-expression/clauses/join/join.js +8 -0
  69. package/lib/printer/visitors/call-expression/clauses/last-insert-rowid/fixture/lastInsertRowid-fix.sql +2 -0
  70. package/lib/printer/visitors/call-expression/clauses/last-insert-rowid/fixture/lastInsertRowid.js +3 -0
  71. package/lib/printer/visitors/call-expression/clauses/last-insert-rowid/last-insert-rowid.js +1 -0
  72. package/lib/printer/visitors/call-expression/clauses/lastval/fixture/lastval-fix.sql +2 -0
  73. package/lib/printer/visitors/call-expression/clauses/lastval/fixture/lastval.js +3 -0
  74. package/lib/printer/visitors/call-expression/clauses/lastval/lastval.js +1 -0
  75. package/lib/printer/visitors/call-expression/clauses/on/on.js +4 -0
  76. package/lib/printer/visitors/call-expression/clauses/on-conflict/fixture/on-conflict-multi.js +3 -0
  77. package/lib/printer/visitors/call-expression/clauses/on-conflict/fixture/on-conflict.js +3 -0
  78. package/lib/printer/visitors/call-expression/clauses/primary-key/primary-key.js +1 -0
  79. package/lib/printer/visitors/call-expression/clauses/returning/fixture/returning-conflict-fix.sql +1 -0
  80. package/lib/printer/visitors/call-expression/clauses/returning/fixture/returning-conflict.js +3 -0
  81. package/lib/printer/visitors/call-expression/clauses/returning/fixture/returning-fix.sql +1 -0
  82. package/lib/printer/visitors/call-expression/clauses/returning/fixture/returning-id-fix.sql +1 -0
  83. package/lib/printer/visitors/call-expression/clauses/returning/fixture/returning-id.js +7 -0
  84. package/lib/printer/visitors/call-expression/clauses/returning/fixture/returning-multi-fix.sql +1 -0
  85. package/lib/printer/visitors/call-expression/clauses/returning/fixture/returning-multi.js +3 -0
  86. package/lib/printer/visitors/call-expression/clauses/returning/fixture/returning.js +7 -0
  87. package/lib/printer/visitors/call-expression/clauses/returning/returning.js +11 -0
  88. package/lib/printer/visitors/call-expression/clauses/section/fixture/section-multi-fix.sql +7 -0
  89. package/lib/printer/visitors/call-expression/clauses/section/fixture/section-multi.js +6 -0
  90. package/lib/printer/visitors/call-expression/clauses/section/fixture/section.js +3 -0
  91. package/lib/printer/visitors/call-expression/clauses/section/section.js +13 -0
  92. package/lib/printer/visitors/call-expression/clauses/select/fixture/select.js +3 -0
  93. package/lib/printer/visitors/call-expression/clauses/select/fixture/string.js +3 -0
  94. package/lib/printer/visitors/call-expression/clauses/serial/serial.js +1 -0
  95. package/lib/printer/visitors/call-expression/clauses/update/fixture/update.js +3 -0
  96. package/lib/printer/visitors/call-expression/clauses/where/fixture/where.js +3 -0
  97. package/lib/printer/visitors/call-expression/clauses/with-named/fixture/withNamed-fix.sql +10 -0
  98. package/lib/printer/visitors/call-expression/clauses/with-named/fixture/withNamed.js +11 -0
  99. package/lib/printer/visitors/call-expression/clauses/with-named/with-named.js +21 -0
  100. package/lib/printer/visitors/index.js +2 -0
  101. package/lib/printer/visitors/member-expression.js +5 -0
  102. package/package.json +18 -15
  103. /package/lib/printer/visitors/call-expression/clauses/count/fixture/{count.sql → count-fix.js} +0 -0
  104. /package/lib/printer/visitors/call-expression/clauses/count/fixture/{count-one.sql → count-one-fix.sql} +0 -0
  105. /package/lib/printer/visitors/call-expression/clauses/delete/fixture/{delete.sql → delete-fix.sql} +0 -0
  106. /package/lib/printer/visitors/call-expression/clauses/on-conflict/fixture/{on-conflict.sql → on-conflict-fix.sql} +0 -0
  107. /package/lib/printer/visitors/call-expression/clauses/on-conflict/fixture/{on-conflict-multi.sql → on-conflict-multi-fix.sql} +0 -0
  108. /package/lib/printer/visitors/call-expression/clauses/select/fixture/{select.sql → select-fix.sql} +0 -0
  109. /package/lib/printer/visitors/call-expression/clauses/select/fixture/{string.sql → string-fix.sql} +0 -0
package/ChangeLog CHANGED
@@ -1,3 +1,20 @@
1
+ 2026.08.15, v1.5.0
2
+
3
+ fix:
4
+ - b8aff13 printer: test
5
+
6
+ feature:
7
+ - 465fc93 join: add support
8
+
9
+ 2026.08.14, v1.4.0
10
+
11
+ feature:
12
+ - 1f764d9 extend support of sqlite and postgres
13
+ - 3a39fbf with: improve
14
+ - c7d966c with: add
15
+ - bf17b06 parser/printer: RETURNING clause
16
+ - d779866 parser/printer: multi-statement sections
17
+
1
18
  2026.07.29, v1.3.0
2
19
 
3
20
  feature:
@@ -0,0 +1,3 @@
1
+ [
2
+ select('*', from(users)),
3
+ ];
@@ -0,0 +1,2 @@
1
+ -- hello world
2
+ SELECT * FROM users;
@@ -0,0 +1,6 @@
1
+ [
2
+ section('@fix', [
3
+ update(Identifier, set(name === 'isNaN'), where(id === ':nan_id')),
4
+ update(BinaryExpression, set(operator === '==='), where(id === ':id')),
5
+ ]),
6
+ ];
@@ -0,0 +1,3 @@
1
+ -- @fix
2
+ UPDATE Identifier SET name = 'isNaN' WHERE id = :nan_id;
3
+ UPDATE BinaryExpression SET operator = '===' WHERE id = :id;
@@ -0,0 +1,11 @@
1
+ [
2
+ withNamed(call = insert(into(
3
+ CallExpression,
4
+ parent_id,
5
+ values(':parent_id'),
6
+ ), returning(id)), member = insert(into(
7
+ MemberExpression,
8
+ parent_id,
9
+ select(id, from(call)),
10
+ ), returning(id)), insert(into(Identifier, [name, parent_id], select('x', id, from(member))))),
11
+ ];
@@ -0,0 +1,8 @@
1
+ WITH
2
+ call AS (
3
+ INSERT INTO CallExpression (parent_id) VALUES (:parent_id) RETURNING id
4
+ ),
5
+ member AS (
6
+ INSERT INTO MemberExpression (parent_id) SELECT id FROM call RETURNING id
7
+ )
8
+ INSERT INTO Identifier (name, parent_id) SELECT 'x', id FROM member;
@@ -1,4 +1,5 @@
1
- import {parse} from 'sql-parser-cst';
1
+ import {tryCatch} from 'try-catch';
2
+ import {parse as parseCst} from 'sql-parser-cst';
2
3
  import {types} from '@putout/babel';
3
4
  import {convertClause} from '#parser/clause';
4
5
 
@@ -12,42 +13,73 @@ const {
12
13
  program,
13
14
  } = types;
14
15
 
16
+ const DIALECTS = [
17
+ 'postgresql',
18
+ 'sqlite',
19
+ ];
20
+
21
+ const parseCstDialect = (source, dialect) => parseCst(source, {
22
+ dialect,
23
+ paramTypes: [':name'],
24
+ includeRange: true,
25
+ includeComments: true,
26
+ });
27
+
28
+ export const parseSqlNode = (source) => {
29
+ for (const dialect of DIALECTS) {
30
+ const [, cst] = tryCatch(parseCstDialect, source, dialect);
31
+
32
+ if (cst)
33
+ return convertSource(cst);
34
+ }
35
+
36
+ throw Error('Cannot parse SQL');
37
+ };
38
+
15
39
  const getSectionName = (leading) => {
16
- const comment = leading?.find((c) => c.text.startsWith('-- @'));
40
+ if (!leading)
41
+ return null;
42
+
43
+ const comment = leading.find((c) => c.text.startsWith('-- @'));
17
44
 
18
45
  return comment ? comment.text
19
46
  .slice(2)
20
47
  .trim() : null;
21
48
  };
22
49
 
23
- const wrapSection = (stmtNode, sectionName) => {
24
- if (!sectionName)
25
- return stmtNode;
26
-
27
- return callExpression(identifier('section'), [
28
- stringLiteral(sectionName),
29
- stmtNode,
30
- ]);
31
- };
32
-
33
- export const parseSqlNode = (source) => {
34
- const cst = parse(source, {
35
- dialect: 'sqlite',
36
- paramTypes: [':name'],
37
- includeRange: true,
38
- includeComments: true,
39
- });
40
-
50
+ const convertSource = (cst) => {
41
51
  const elements = [];
52
+ let currentSection = null;
53
+ const sectionGroups = new Map();
42
54
 
43
- for (const [i, statement] of cst.statements.entries()) {
44
- if (statement.type === 'empty')
55
+ for (const [i, stmt] of cst.statements.entries()) {
56
+ if (stmt.type === 'empty')
45
57
  continue;
46
58
 
47
- const stmtNode = convertClause(statement);
48
- const leading = i ? statement.leading : cst.leading;
59
+ const stmtNode = convertClause(stmt);
60
+ const leading = i ? stmt.leading : cst.leading;
61
+ const name = getSectionName(leading);
62
+
63
+ if (name)
64
+ currentSection = name;
65
+
66
+ if (currentSection) {
67
+ if (!sectionGroups.has(currentSection))
68
+ sectionGroups.set(currentSection, []);
69
+
70
+ sectionGroups.get(currentSection).push(stmtNode);
71
+ } else {
72
+ elements.push(stmtNode);
73
+ }
74
+ }
75
+
76
+ for (const [name, nodes] of sectionGroups) {
77
+ const inner = nodes.length === 1 ? nodes[0] : arrayExpression(nodes);
49
78
 
50
- elements.push(wrapSection(stmtNode, getSectionName(leading)));
79
+ elements.push(callExpression(identifier('section'), [
80
+ stringLiteral(name),
81
+ inner,
82
+ ]));
51
83
  }
52
84
 
53
85
  return file(program([
@@ -0,0 +1,41 @@
1
+ import {types} from '@putout/babel';
2
+
3
+ const {identifier, callExpression} = types;
4
+
5
+ const getDataType = (dataType) => {
6
+ if (dataType.type === 'identifier')
7
+ return dataType;
8
+
9
+ return dataType.name;
10
+ };
11
+
12
+ const isSerial = (dataType) => getDataType(dataType).name === 'SERIAL';
13
+
14
+ const convertConstraint = (constraint) => {
15
+ if (constraint.type === 'constraint_primary_key')
16
+ return callExpression(identifier('primaryKey'), []);
17
+
18
+ return callExpression(identifier('autoIncrement'), []);
19
+ };
20
+
21
+ export const convertColumnDefinition = (column) => {
22
+ const {
23
+ name,
24
+ dataType,
25
+ constraints,
26
+ } = column;
27
+
28
+ const args = [
29
+ identifier(name.name),
30
+ ];
31
+
32
+ if (isSerial(dataType))
33
+ args.push(callExpression(identifier('serial'), []));
34
+ else
35
+ args.push(identifier(getDataType(dataType).text));
36
+
37
+ for (const constraint of constraints)
38
+ args.push(convertConstraint(constraint));
39
+
40
+ return callExpression(identifier('column'), args);
41
+ };
@@ -0,0 +1,17 @@
1
+ import {types} from '@putout/babel';
2
+ import {convertColumnDefinition} from '#parser/clause/column-definition';
3
+
4
+ const {
5
+ identifier,
6
+ callExpression,
7
+ arrayExpression,
8
+ } = types;
9
+
10
+ export const convertCreateTable = ({name, columns}) => {
11
+ const colNodes = columns.expr.items.map(convertColumnDefinition);
12
+
13
+ return callExpression(identifier('createTable'), [
14
+ identifier(name.name),
15
+ arrayExpression(colNodes),
16
+ ]);
17
+ };
@@ -0,0 +1,5 @@
1
+ [
2
+ createTable(t, [
3
+ column(id, INTEGER, primaryKey(), autoIncrement()),
4
+ ]),
5
+ ];
@@ -0,0 +1 @@
1
+ CREATE TABLE t (id INTEGER PRIMARY KEY AUTOINCREMENT)
@@ -0,0 +1,5 @@
1
+ [
2
+ createTable(t, [
3
+ column(id, serial(), primaryKey()),
4
+ ]),
5
+ ];
@@ -0,0 +1 @@
1
+ CREATE TABLE t (id SERIAL PRIMARY KEY)
@@ -15,5 +15,5 @@ export const convertDelete = ({clauses}) => {
15
15
  whereClause && convertWhere(whereClause),
16
16
  ].filter(Boolean);
17
17
 
18
- return callExpression(identifier('delete'), args);
18
+ return callExpression(identifier('deleteFrom'), args);
19
19
  };
@@ -1,3 +1,3 @@
1
1
  [
2
- delete(from(t), where(id === ':id')),
2
+ deleteFrom(from(t), where(id === ':id')),
3
3
  ];
@@ -0,0 +1,6 @@
1
+ [
2
+ select(bin.id, from(as(
3
+ BinaryExpression,
4
+ bin,
5
+ ), join(as(Identifier, nan_id), on(nan_id.parent_id === bin.id && nan_id.name === 'NaN')))),
6
+ ];
@@ -0,0 +1,6 @@
1
+ [
2
+ select(bin.id, arg_id.name, from(as(
3
+ BinaryExpression,
4
+ bin,
5
+ ), join(as(Identifier, nan_id), on(nan_id.parent_id === bin.id)), join(as(Identifier, arg_id), on(arg_id.parent_id === bin.id)))),
6
+ ];
@@ -0,0 +1 @@
1
+ SELECT bin.id, arg_id.name FROM BinaryExpression bin JOIN Identifier nan_id ON nan_id.parent_id = bin.id JOIN Identifier arg_id ON arg_id.parent_id = bin.id
@@ -0,0 +1 @@
1
+ SELECT bin.id FROM BinaryExpression bin JOIN Identifier nan_id ON nan_id.parent_id = bin.id AND nan_id.name = 'NaN'
@@ -1,7 +1,83 @@
1
1
  import {types} from '@putout/babel';
2
2
 
3
- const {identifier, callExpression} = types;
3
+ const {
4
+ identifier,
5
+ callExpression,
6
+ memberExpression,
7
+ binaryExpression,
8
+ logicalExpression,
9
+ stringLiteral,
10
+ numericLiteral,
11
+ } = types;
4
12
 
5
- export const convertFrom = (node) => callExpression(identifier('from'), [
6
- identifier(node.expr.name),
13
+ const convertOperand = (node) => {
14
+ if (node.type === 'member_expr')
15
+ return memberExpression(identifier(node.object.name), identifier(node.property.name));
16
+
17
+ if (node.type === 'parameter')
18
+ return stringLiteral(node.text);
19
+
20
+ if (node.type === 'number_literal')
21
+ return numericLiteral(node.value);
22
+
23
+ if (node.type === 'string_literal')
24
+ return stringLiteral(node.value);
25
+
26
+ return identifier(node.name);
27
+ };
28
+
29
+ const convertCondition = (node) => {
30
+ if (node.operator?.name === 'AND')
31
+ return logicalExpression('&&', convertCondition(node.left), convertCondition(node.right));
32
+
33
+ if (node.operator?.name === 'OR')
34
+ return logicalExpression('||', convertCondition(node.left), convertCondition(node.right));
35
+
36
+ return binaryExpression('===', convertOperand(node.left), convertOperand(node.right));
37
+ };
38
+
39
+ const convertOn = (spec) => callExpression(identifier('on'), [
40
+ convertCondition(spec.expr),
7
41
  ]);
42
+
43
+ const convertAlias = (node) => {
44
+ if (node.type === 'alias')
45
+ return callExpression(identifier('as'), [
46
+ identifier(node.expr.name),
47
+ identifier(node.alias.name),
48
+ ]);
49
+
50
+ return identifier(node.name);
51
+ };
52
+
53
+ const flattenJoins = (node, joins = []) => {
54
+ if (node.type === 'join_expr') {
55
+ flattenJoins(node.left, joins);
56
+ joins.push(callExpression(identifier('join'), [
57
+ convertAlias(node.right),
58
+ convertOn(node.specification),
59
+ ]));
60
+
61
+ return joins;
62
+ }
63
+
64
+ joins.unshift(convertAlias(node));
65
+
66
+ return joins;
67
+ };
68
+
69
+ export const convertFrom = ({expr}) => {
70
+ if (expr.type === 'join_expr') {
71
+ const parts = flattenJoins(expr);
72
+ return callExpression(identifier('from'), parts);
73
+ }
74
+
75
+ if (expr.type === 'alias')
76
+ return callExpression(identifier('from'), [
77
+ convertAlias(expr),
78
+ ]);
79
+
80
+ return callExpression(identifier('from'), [
81
+ identifier(expr.name),
82
+ ]);
83
+ };
@@ -2,6 +2,9 @@ import {convertSelect} from '#parser/clause/select';
2
2
  import {convertInsert} from '#parser/clause/insert';
3
3
  import {convertUpdate} from '#parser/clause/update';
4
4
  import {convertDelete} from '#parser/clause/delete';
5
+ import {convertWithNamed} from '#parser/clause/with-named';
6
+ import {convertCreateTable} from '#parser/clause/create-table';
7
+ import {findClause} from '#parser/clause/find';
5
8
 
6
9
  export const convertClause = (stmt) => {
7
10
  const {type} = stmt;
@@ -13,9 +16,19 @@ export const convertClause = (stmt) => {
13
16
  throw Error(`${type} not supported yet`);
14
17
  };
15
18
 
19
+ const convertInsertOrNamed = (stmt) => {
20
+ const {clauses} = stmt;
21
+
22
+ if (findClause(clauses, 'with_clause'))
23
+ return convertWithNamed(stmt);
24
+
25
+ return convertInsert(stmt);
26
+ };
27
+
16
28
  const clauseVisitors = {
17
29
  select_stmt: convertSelect,
18
- insert_stmt: convertInsert,
30
+ insert_stmt: convertInsertOrNamed,
19
31
  update_stmt: convertUpdate,
20
32
  delete_stmt: convertDelete,
33
+ create_table_stmt: convertCreateTable,
21
34
  };
@@ -0,0 +1,3 @@
1
+ [
2
+ insert(into(t, x, values(':x')), onConflict(x, set(y === ':y')), returning(id)),
3
+ ];
@@ -0,0 +1 @@
1
+ INSERT INTO t (x) VALUES (:x) ON CONFLICT (x) DO UPDATE SET y = :y RETURNING id;
@@ -0,0 +1,7 @@
1
+ [
2
+ insert(into(
3
+ CallExpression,
4
+ parent_id,
5
+ values(':parent_id'),
6
+ ), returning(as(id, 'call_id'))),
7
+ ];
@@ -0,0 +1,7 @@
1
+ [
2
+ insert(into(
3
+ CallExpression,
4
+ parent_id,
5
+ values(':parent_id'),
6
+ ), returning(id)),
7
+ ];
@@ -0,0 +1 @@
1
+ INSERT INTO CallExpression (parent_id) VALUES (:parent_id) RETURNING id;
@@ -0,0 +1 @@
1
+ INSERT INTO CallExpression (parent_id) VALUES (:parent_id) RETURNING id AS call_id;
@@ -1,5 +1,6 @@
1
1
  import {types} from '@putout/babel';
2
2
  import {convertSet} from '#parser/clause/set';
3
+ import {convertSelect} from '#parser/clause/select';
3
4
  import {findClause} from '#parser/clause/find';
4
5
 
5
6
  const {
@@ -12,6 +13,39 @@ const {
12
13
  const convertExprToIdentifier = ({expr}) => identifier(expr.name);
13
14
  const convertColToIdentifier = ({name}) => identifier(name);
14
15
 
16
+ const convertReturningItem = (item) => {
17
+ if (item.type === 'alias') {
18
+ const {name} = item.expr;
19
+ const aliasName = item.alias.name;
20
+
21
+ return callExpression(identifier('as'), [
22
+ identifier(name),
23
+ stringLiteral(aliasName),
24
+ ]);
25
+ }
26
+
27
+ return identifier(item.name);
28
+ };
29
+
30
+ const convertReturning = (returningClause) => {
31
+ const args = returningClause.columns.items.map(convertReturningItem);
32
+
33
+ return callExpression(identifier('returning'), args);
34
+ };
35
+
36
+ const convertSource = (vc) => {
37
+ if (vc.type === 'select_stmt')
38
+ return convertSelect(vc);
39
+
40
+ const vals = vc.values.items[0].expr.items;
41
+ const valArgs = [];
42
+
43
+ for (const p of vals)
44
+ valArgs.push(stringLiteral(p.text));
45
+
46
+ return callExpression(identifier('values'), valArgs);
47
+ };
48
+
15
49
  const convertUpsert = (upsertClause) => {
16
50
  const target = upsertClause.conflictTarget;
17
51
  const {action} = upsertClause;
@@ -26,23 +60,20 @@ export const convertInsert = ({clauses}) => {
26
60
  const [ic] = clauses;
27
61
  const [, vc] = clauses;
28
62
  const uc = findClause(clauses, 'upsert_clause');
63
+ const rc = findClause(clauses, 'returning_clause');
29
64
 
30
65
  const cols = ic.columns.expr.items;
31
- const vals = vc.values.items[0].expr.items;
32
66
  const colArg = cols.length === 1 ? identifier(cols[0].name) : arrayExpression(cols.map(convertColToIdentifier));
33
- const valArgs = [];
34
-
35
- for (const p of vals) {
36
- valArgs.push(stringLiteral(p.text));
37
- }
67
+ const source = convertSource(vc);
38
68
 
39
69
  const args = [
40
70
  callExpression(identifier('into'), [
41
71
  identifier(ic.table.name),
42
72
  colArg,
43
- callExpression(identifier('values'), valArgs),
73
+ source,
44
74
  ]),
45
75
  uc && convertUpsert(uc),
76
+ rc && convertReturning(rc),
46
77
  ].filter(Boolean);
47
78
 
48
79
  return callExpression(identifier('insert'), args);
@@ -4,9 +4,13 @@ const {
4
4
  identifier,
5
5
  stringLiteral,
6
6
  numericLiteral,
7
+ memberExpression,
7
8
  } = types;
8
9
 
9
10
  export const convertOperand = (node) => {
11
+ if (node.type === 'member_expr')
12
+ return memberExpression(identifier(node.object.name), identifier(node.property.name));
13
+
10
14
  if (node.type === 'parameter')
11
15
  return stringLiteral(node.text);
12
16
 
@@ -0,0 +1,3 @@
1
+ [
2
+ select(lastInsertRowid(), from(users)),
3
+ ];
@@ -0,0 +1 @@
1
+ SELECT last_insert_rowid() FROM users
@@ -0,0 +1,3 @@
1
+ [
2
+ select(lastval(), from(users)),
3
+ ];
@@ -0,0 +1 @@
1
+ SELECT LASTVAL() FROM users
@@ -10,21 +10,37 @@ const {
10
10
  stringLiteral,
11
11
  assignmentExpression,
12
12
  callExpression,
13
+ memberExpression,
13
14
  } = types;
14
15
 
15
16
  const convertColumn = (column) => {
16
17
  if (isCount(column))
17
18
  return convertCount(column);
18
19
 
20
+ if (column.type === 'func_call')
21
+ return convertFuncCall(column);
22
+
19
23
  if (column.type === 'alias')
20
24
  return assignmentExpression('=', identifier(column.alias.name), convertOperand(column.expr));
21
25
 
22
26
  if (column.type === 'string_literal')
23
27
  return stringLiteral(column.value);
24
28
 
29
+ if (column.type === 'member_expr')
30
+ return memberExpression(identifier(column.object.name), identifier(column.property.name));
31
+
25
32
  return identifier(column.name);
26
33
  };
27
34
 
35
+ const convertFuncCall = (column) => {
36
+ const {name} = column.name;
37
+
38
+ if (name === 'last_insert_rowid')
39
+ return callExpression(identifier('lastInsertRowid'), []);
40
+
41
+ return callExpression(identifier('lastval'), []);
42
+ };
43
+
28
44
  const convertColumns = (columns) => {
29
45
  if (columns.items.length === 1 && columns.items[0].type === 'all_columns')
30
46
  return [
@@ -1,30 +1,19 @@
1
1
  import {types} from '@putout/babel';
2
+ import {convertOperand} from '#parser/clause/operand';
2
3
 
3
4
  const {
4
- binaryExpression,
5
5
  logicalExpression,
6
6
  identifier,
7
- stringLiteral,
8
- numericLiteral,
9
7
  callExpression,
10
8
  } = types;
11
9
 
12
- const convertOperand = (node) => {
13
- if (node.type === 'parameter')
14
- return stringLiteral(node.text);
15
-
16
- if (node.type === 'number_literal')
17
- return numericLiteral(node.value);
18
-
19
- if (node.type === 'string_literal')
20
- return stringLiteral(node.value);
21
-
22
- return identifier(node.name);
23
- };
24
-
25
10
  const convertAnd = (node) => logicalExpression('&&', convertCondition(node.left), convertCondition(node.right));
26
11
  const convertOr = (node) => logicalExpression('||', convertCondition(node.left), convertCondition(node.right));
27
- const convertEq = (node) => binaryExpression('===', convertOperand(node.left), convertOperand(node.right));
12
+
13
+ const convertEq = (node) => {
14
+ const {binaryExpression} = types;
15
+ return binaryExpression('===', convertOperand(node.left), convertOperand(node.right));
16
+ };
28
17
 
29
18
  const convertCondition = (node) => {
30
19
  if (node.operator?.name === 'AND')
@@ -0,0 +1,31 @@
1
+ import {types} from '@putout/babel';
2
+ import {convertClause} from '#parser/clause';
3
+ import {findClause} from '#parser/clause/find';
4
+
5
+ const {
6
+ identifier,
7
+ assignmentExpression,
8
+ callExpression,
9
+ } = types;
10
+
11
+ const isWithClause = (clause) => clause.type !== 'with_clause';
12
+
13
+ const convertTable = ({table, expr}) => {
14
+ return assignmentExpression('=', identifier(table.name), convertClause(expr.expr));
15
+ };
16
+
17
+ export const convertWithNamed = (stmt) => {
18
+ const withClause = findClause(stmt.clauses, 'with_clause');
19
+
20
+ const finalStatement = convertClause({
21
+ ...stmt,
22
+ clauses: stmt.clauses.filter(isWithClause),
23
+ });
24
+
25
+ const args = [
26
+ ...withClause.tables.items.map(convertTable),
27
+ finalStatement,
28
+ ];
29
+
30
+ return callExpression(identifier('withNamed'), args);
31
+ };
@@ -0,0 +1,3 @@
1
+ [
2
+ select('*', from(users)),
3
+ ];