happy-sql 2.0.8 → 2.1.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 (145) hide show
  1. package/ChangeLog +17 -0
  2. package/lib/parser/visitors/clause/count/count.js +4 -0
  3. package/lib/parser/visitors/clause/delete/fixture/delete-where-between-fix.js +14 -0
  4. package/lib/parser/visitors/clause/delete/fixture/delete-where-between.sql +1 -0
  5. package/lib/parser/visitors/clause/from/fixture/generate-series-fix.js +12 -0
  6. package/lib/parser/visitors/clause/from/fixture/generate-series.sql +1 -0
  7. package/lib/parser/visitors/clause/from/fixture/group-by-fix.js +14 -0
  8. package/lib/parser/visitors/clause/from/fixture/group-by-having-fix.js +22 -0
  9. package/lib/parser/visitors/clause/from/fixture/group-by-having.sql +1 -0
  10. package/lib/parser/visitors/clause/from/fixture/group-by-multi-fix.js +13 -0
  11. package/lib/parser/visitors/clause/from/fixture/group-by-multi.sql +1 -0
  12. package/lib/parser/visitors/clause/from/fixture/group-by-order-by-fix.js +17 -0
  13. package/lib/parser/visitors/clause/from/fixture/group-by-order-by.sql +1 -0
  14. package/lib/parser/visitors/clause/from/fixture/group-by-sum-fix.js +14 -0
  15. package/lib/parser/visitors/clause/from/fixture/group-by-sum-having-fix.js +19 -0
  16. package/lib/parser/visitors/clause/from/fixture/group-by-sum-having.sql +1 -0
  17. package/lib/parser/visitors/clause/from/fixture/group-by-sum.sql +1 -0
  18. package/lib/parser/visitors/clause/from/fixture/group-by.sql +1 -0
  19. package/lib/parser/visitors/clause/from/fixture/json-each-fix.js +10 -0
  20. package/lib/parser/visitors/clause/from/fixture/json-each.sql +1 -0
  21. package/lib/parser/visitors/clause/from/fixture/limit-fix.js +11 -0
  22. package/lib/parser/visitors/clause/from/fixture/limit-offset-fix.js +12 -0
  23. package/lib/parser/visitors/clause/from/fixture/limit-offset.sql +1 -0
  24. package/lib/parser/visitors/clause/from/fixture/limit.sql +1 -0
  25. package/lib/parser/visitors/clause/from/fixture/order-by-desc-fix.js +13 -0
  26. package/lib/parser/visitors/clause/from/fixture/order-by-desc.sql +1 -0
  27. package/lib/parser/visitors/clause/from/fixture/order-by-fix.js +11 -0
  28. package/lib/parser/visitors/clause/from/fixture/order-by-multi-fix.js +14 -0
  29. package/lib/parser/visitors/clause/from/fixture/order-by-multi.sql +1 -0
  30. package/lib/parser/visitors/clause/from/fixture/order-by.sql +1 -0
  31. package/lib/parser/visitors/clause/from/fixture/unnest-fix.js +10 -0
  32. package/lib/parser/visitors/clause/from/fixture/unnest.sql +1 -0
  33. package/lib/parser/visitors/clause/from/from.js +37 -1
  34. package/lib/parser/visitors/clause/from/group-by.js +6 -0
  35. package/lib/parser/visitors/clause/from/having.js +8 -0
  36. package/lib/parser/visitors/clause/from/limit.js +18 -0
  37. package/lib/parser/visitors/clause/from/order-by.js +20 -0
  38. package/lib/parser/visitors/clause/from/table-func.js +28 -0
  39. package/lib/parser/visitors/clause/index.js +12 -1
  40. package/lib/parser/visitors/clause/operand.js +14 -0
  41. package/lib/parser/visitors/clause/select/fixture/select-abs-fix.js +8 -0
  42. package/lib/parser/visitors/clause/select/fixture/select-abs.sql +1 -0
  43. package/lib/parser/visitors/clause/select/fixture/select-avg-fix.js +10 -0
  44. package/lib/parser/visitors/clause/select/fixture/select-avg.sql +1 -0
  45. package/lib/parser/visitors/clause/select/fixture/select-count-col-fix.js +10 -0
  46. package/lib/parser/visitors/clause/select/fixture/select-count-col.sql +1 -0
  47. package/lib/parser/visitors/clause/select/fixture/select-distinct-fix.js +10 -0
  48. package/lib/parser/visitors/clause/select/fixture/select-distinct-multi-fix.js +11 -0
  49. package/lib/parser/visitors/clause/select/fixture/select-distinct-multi.sql +1 -0
  50. package/lib/parser/visitors/clause/select/fixture/select-distinct.sql +1 -0
  51. package/lib/parser/visitors/clause/select/fixture/select-min-max-fix.js +13 -0
  52. package/lib/parser/visitors/clause/select/fixture/select-min-max.sql +1 -0
  53. package/lib/parser/visitors/clause/select/fixture/select-sum-fix.js +10 -0
  54. package/lib/parser/visitors/clause/select/fixture/select-sum.sql +1 -0
  55. package/lib/parser/visitors/clause/select/select.js +47 -2
  56. package/lib/parser/visitors/clause/union/fixture/union-all-fix.js +10 -0
  57. package/lib/parser/visitors/clause/union/fixture/union-all.sql +1 -0
  58. package/lib/parser/visitors/clause/union/union.js +11 -0
  59. package/lib/parser/visitors/clause/update/fixture/update-where-not-in-fix.js +16 -0
  60. package/lib/parser/visitors/clause/update/fixture/update-where-not-in.sql +1 -0
  61. package/lib/parser/visitors/clause/where/fixture/where-between-fix.js +15 -0
  62. package/lib/parser/visitors/clause/where/fixture/where-between.sql +1 -0
  63. package/lib/parser/visitors/clause/where/fixture/where-not-in-fix.js +16 -0
  64. package/lib/parser/visitors/clause/where/fixture/where-not-in.sql +1 -0
  65. package/lib/parser/visitors/clause/where/where.js +19 -1
  66. package/lib/parser/visitors/clause/with-named/fixture/with-named-select-fix.js +19 -0
  67. package/lib/parser/visitors/clause/with-named/fixture/with-named-select.sql +1 -0
  68. package/lib/parser/visitors/clause/with-named/fixture/with-recursive-fix.js +27 -0
  69. package/lib/parser/visitors/clause/with-named/fixture/with-recursive-multi-fix.js +38 -0
  70. package/lib/parser/visitors/clause/with-named/fixture/with-recursive-multi.sql +1 -0
  71. package/lib/parser/visitors/clause/with-named/fixture/with-recursive-no-columns-fix.js +20 -0
  72. package/lib/parser/visitors/clause/with-named/fixture/with-recursive-no-columns.sql +1 -0
  73. package/lib/parser/visitors/clause/with-named/fixture/with-recursive.sql +1 -0
  74. package/lib/parser/visitors/clause/with-named/with-named.js +15 -2
  75. package/lib/printer/visitors/call-expression/clauses/avg/avg.js +5 -0
  76. package/lib/printer/visitors/call-expression/clauses/avg/fixture/avg-fix.sql +2 -0
  77. package/lib/printer/visitors/call-expression/clauses/avg/fixture/avg.js +1 -0
  78. package/lib/printer/visitors/call-expression/clauses/between/between.js +9 -0
  79. package/lib/printer/visitors/call-expression/clauses/between/fixture/between-fix.sql +3 -0
  80. package/lib/printer/visitors/call-expression/clauses/between/fixture/between.js +3 -0
  81. package/lib/printer/visitors/call-expression/clauses/clauses.js +28 -0
  82. package/lib/printer/visitors/call-expression/clauses/desc/desc.js +4 -0
  83. package/lib/printer/visitors/call-expression/clauses/desc/fixture/desc-fix.sql +3 -0
  84. package/lib/printer/visitors/call-expression/clauses/desc/fixture/desc.js +13 -0
  85. package/lib/printer/visitors/call-expression/clauses/distinct/distinct.js +12 -0
  86. package/lib/printer/visitors/call-expression/clauses/distinct/fixture/distinct-fix.sql +2 -0
  87. package/lib/printer/visitors/call-expression/clauses/distinct/fixture/distinct-multi-fix.sql +2 -0
  88. package/lib/printer/visitors/call-expression/clauses/distinct/fixture/distinct-multi.js +11 -0
  89. package/lib/printer/visitors/call-expression/clauses/distinct/fixture/distinct.js +10 -0
  90. package/lib/printer/visitors/call-expression/clauses/from/fixture/generate-series-fix.sql +2 -0
  91. package/lib/printer/visitors/call-expression/clauses/from/fixture/generate-series.js +3 -0
  92. package/lib/printer/visitors/call-expression/clauses/from/fixture/group-by-having-fix.sql +5 -0
  93. package/lib/printer/visitors/call-expression/clauses/from/fixture/group-by-having.js +22 -0
  94. package/lib/printer/visitors/call-expression/clauses/from/from.js +58 -11
  95. package/lib/printer/visitors/call-expression/clauses/group-by/fixture/group-by-fix.sql +3 -0
  96. package/lib/printer/visitors/call-expression/clauses/group-by/fixture/group-by-multi-fix.sql +3 -0
  97. package/lib/printer/visitors/call-expression/clauses/group-by/fixture/group-by-multi.js +13 -0
  98. package/lib/printer/visitors/call-expression/clauses/group-by/fixture/group-by-sum-fix.sql +4 -0
  99. package/lib/printer/visitors/call-expression/clauses/group-by/fixture/group-by-sum.js +19 -0
  100. package/lib/printer/visitors/call-expression/clauses/group-by/fixture/group-by.js +14 -0
  101. package/lib/printer/visitors/call-expression/clauses/group-by/group-by.js +12 -0
  102. package/lib/printer/visitors/call-expression/clauses/having/fixture/having-fix.sql +3 -0
  103. package/lib/printer/visitors/call-expression/clauses/having/fixture/having.js +16 -0
  104. package/lib/printer/visitors/call-expression/clauses/having/having.js +4 -0
  105. package/lib/printer/visitors/call-expression/clauses/insert/insert.js +1 -1
  106. package/lib/printer/visitors/call-expression/clauses/join/join.js +6 -4
  107. package/lib/printer/visitors/call-expression/clauses/limit/fixture/limit-fix.sql +3 -0
  108. package/lib/printer/visitors/call-expression/clauses/limit/fixture/limit-offset-fix.sql +4 -0
  109. package/lib/printer/visitors/call-expression/clauses/limit/fixture/limit-offset.js +12 -0
  110. package/lib/printer/visitors/call-expression/clauses/limit/fixture/limit.js +11 -0
  111. package/lib/printer/visitors/call-expression/clauses/limit/limit.js +12 -0
  112. package/lib/printer/visitors/call-expression/clauses/max/fixture/max-fix.sql +2 -0
  113. package/lib/printer/visitors/call-expression/clauses/max/fixture/max.js +1 -0
  114. package/lib/printer/visitors/call-expression/clauses/max/max.js +5 -0
  115. package/lib/printer/visitors/call-expression/clauses/min/fixture/min-fix.sql +2 -0
  116. package/lib/printer/visitors/call-expression/clauses/min/fixture/min.js +1 -0
  117. package/lib/printer/visitors/call-expression/clauses/min/min.js +5 -0
  118. package/lib/printer/visitors/call-expression/clauses/not-in-list/fixture/not-in-list-fix.sql +3 -0
  119. package/lib/printer/visitors/call-expression/clauses/not-in-list/fixture/not-in-list.js +3 -0
  120. package/lib/printer/visitors/call-expression/clauses/not-in-list/not-in-list.js +15 -0
  121. package/lib/printer/visitors/call-expression/clauses/order-by/fixture/order-by-fix.sql +3 -0
  122. package/lib/printer/visitors/call-expression/clauses/order-by/fixture/order-by-multi-fix.sql +3 -0
  123. package/lib/printer/visitors/call-expression/clauses/order-by/fixture/order-by-multi.js +14 -0
  124. package/lib/printer/visitors/call-expression/clauses/order-by/fixture/order-by.js +11 -0
  125. package/lib/printer/visitors/call-expression/clauses/order-by/order-by.js +12 -0
  126. package/lib/printer/visitors/call-expression/clauses/returning/returning.js +5 -3
  127. package/lib/printer/visitors/call-expression/clauses/select/select.js +10 -9
  128. package/lib/printer/visitors/call-expression/clauses/sum/fixture/sum-fix.sql +2 -0
  129. package/lib/printer/visitors/call-expression/clauses/sum/fixture/sum.js +1 -0
  130. package/lib/printer/visitors/call-expression/clauses/sum/sum.js +5 -0
  131. package/lib/printer/visitors/call-expression/clauses/union-all/fixture/union-all-fix.sql +3 -0
  132. package/lib/printer/visitors/call-expression/clauses/union-all/fixture/union-all.js +1 -0
  133. package/lib/printer/visitors/call-expression/clauses/union-all/union-all.js +9 -0
  134. package/lib/printer/visitors/call-expression/clauses/with-named/fixture/with-named-insert-fix.sql +3 -3
  135. package/lib/printer/visitors/call-expression/clauses/with-named/fixture/with-named-select-fix.sql +8 -0
  136. package/lib/printer/visitors/call-expression/clauses/with-named/fixture/with-named-select.js +19 -0
  137. package/lib/printer/visitors/call-expression/clauses/with-named/fixture/withNamed-fix.sql +3 -3
  138. package/lib/printer/visitors/call-expression/clauses/with-recursive/fixture/with-recursive-fix.sql +10 -0
  139. package/lib/printer/visitors/call-expression/clauses/with-recursive/fixture/with-recursive-multi-fix.sql +14 -0
  140. package/lib/printer/visitors/call-expression/clauses/with-recursive/fixture/with-recursive-multi.js +38 -0
  141. package/lib/printer/visitors/call-expression/clauses/with-recursive/fixture/with-recursive-no-columns-fix.sql +8 -0
  142. package/lib/printer/visitors/call-expression/clauses/with-recursive/fixture/with-recursive-no-columns.js +20 -0
  143. package/lib/printer/visitors/call-expression/clauses/with-recursive/fixture/with-recursive.js +27 -0
  144. package/lib/printer/visitors/call-expression/clauses/with-recursive/with-recursive.js +57 -0
  145. package/package.json +2 -1
@@ -11,6 +11,8 @@ const {
11
11
  tsLiteralType,
12
12
  callExpression,
13
13
  memberExpression,
14
+ numericLiteral,
15
+ binaryExpression,
14
16
  } = types;
15
17
 
16
18
  const convertColumn = (column) => {
@@ -32,16 +34,44 @@ const convertColumn = (column) => {
32
34
  if (column.type === 'parameter')
33
35
  return stringLiteral(column.text);
34
36
 
37
+ if (column.type === 'number_literal')
38
+ return numericLiteral(column.value);
39
+
40
+ if (column.type === 'binary_expr')
41
+ return binaryExpression(
42
+ column.operator,
43
+ convertOperand(column.left),
44
+ convertOperand(column.right),
45
+ );
46
+
35
47
  return identifier(column.name);
36
48
  };
37
49
 
50
+ const AGGREGATE_NAMES = new Set([
51
+ 'sum',
52
+ 'avg',
53
+ 'min',
54
+ 'max',
55
+ ]);
56
+
38
57
  const convertFuncCall = (column) => {
39
58
  const {name} = column.name;
59
+ const lower = name.toLowerCase();
40
60
 
41
61
  if (name === 'last_insert_rowid')
42
62
  return callExpression(identifier('lastInsertRowid'), []);
43
63
 
44
- return callExpression(identifier('lastval'), []);
64
+ if (name === 'lastval')
65
+ return callExpression(identifier('lastval'), []);
66
+
67
+ if (AGGREGATE_NAMES.has(lower)) {
68
+ const {items} = column.args.expr.args;
69
+ const args = items.map(convertOperand);
70
+
71
+ return callExpression(identifier(lower), args);
72
+ }
73
+
74
+ return callExpression(identifier(name), []);
45
75
  };
46
76
 
47
77
  const convertColumns = (columns) => {
@@ -53,16 +83,31 @@ const convertColumns = (columns) => {
53
83
  return columns.items.map(convertColumn);
54
84
  };
55
85
 
86
+ const isDistinct = (selectClause) => selectClause.modifiers.some(({type}) => type === 'select_distinct');
87
+
56
88
  export const convertSelect = ({clauses}) => {
57
89
  const selectClause = findClause(clauses, 'select_clause');
58
90
  const fromClause = findClause(clauses, 'from_clause');
59
91
  const whereClause = findClause(clauses, 'where_clause');
92
+ const groupByClause = findClause(clauses, 'group_by_clause');
93
+ const havingClause = findClause(clauses, 'having_clause');
94
+ const orderByClause = findClause(clauses, 'order_by_clause');
95
+ const limitClause = findClause(clauses, 'limit_clause');
96
+
97
+ const cols = convertColumns(selectClause.columns);
98
+ const selectArgs = isDistinct(selectClause) ? [
99
+ callExpression(identifier('distinct'), cols),
100
+ ] : cols;
60
101
 
61
102
  const args = [
62
- ...convertColumns(selectClause.columns),
103
+ ...selectArgs,
63
104
  fromClause && convertFrom({
64
105
  expr: fromClause.expr,
65
106
  whereClause,
107
+ groupByClause,
108
+ havingClause,
109
+ orderByClause,
110
+ limitClause,
66
111
  }),
67
112
  ].filter(Boolean);
68
113
 
@@ -0,0 +1,10 @@
1
+ [
2
+ unionAll(
3
+ select(
4
+ 1,
5
+ ),
6
+ select(
7
+ 2,
8
+ ),
9
+ ),
10
+ ];
@@ -0,0 +1 @@
1
+ SELECT 1 UNION ALL SELECT 2
@@ -0,0 +1,11 @@
1
+ import {types} from '@putout/babel';
2
+ import {convertClause} from '#parser/clause';
3
+
4
+ const {callExpression, identifier} = types;
5
+
6
+ export const convertUnionAll = (stmt) => {
7
+ return callExpression(identifier('unionAll'), [
8
+ convertClause(stmt.left),
9
+ convertClause(stmt.right),
10
+ ]);
11
+ };
@@ -0,0 +1,16 @@
1
+ [
2
+ update(
3
+ t,
4
+ set(
5
+ x === 1,
6
+ ),
7
+ where(
8
+ notInList(
9
+ y,
10
+ 1,
11
+ 2,
12
+ 3,
13
+ ),
14
+ ),
15
+ ),
16
+ ];
@@ -0,0 +1 @@
1
+ UPDATE t SET x = 1 WHERE y NOT IN (1, 2, 3)
@@ -0,0 +1,15 @@
1
+ [
2
+ select(
3
+ '*',
4
+ from(
5
+ users,
6
+ where(
7
+ between(
8
+ age,
9
+ 18,
10
+ 65,
11
+ ),
12
+ ),
13
+ ),
14
+ ),
15
+ ];
@@ -0,0 +1 @@
1
+ SELECT * FROM users WHERE age BETWEEN 18 AND 65
@@ -0,0 +1,16 @@
1
+ [
2
+ select(
3
+ '*',
4
+ from(
5
+ users,
6
+ where(
7
+ notInList(
8
+ id,
9
+ 1,
10
+ 2,
11
+ 3,
12
+ ),
13
+ ),
14
+ ),
15
+ ),
16
+ ];
@@ -0,0 +1 @@
1
+ SELECT * FROM users WHERE id NOT IN (1, 2, 3)
@@ -34,6 +34,12 @@ const SQL_TO_JS_OP = {
34
34
  const convertAnd = (node) => logicalExpression('&&', convertCondition(node.left), convertCondition(node.right));
35
35
  const convertOr = (node) => logicalExpression('||', convertCondition(node.left), convertCondition(node.right));
36
36
 
37
+ export const convertBetween = (node) => callExpression(identifier('between'), [
38
+ convertOperand(node.left),
39
+ convertOperand(node.begin),
40
+ convertOperand(node.end),
41
+ ]);
42
+
37
43
  const convertBinary = (node) => {
38
44
  const op = opName(node.operator);
39
45
 
@@ -62,6 +68,15 @@ const convertBinary = (node) => {
62
68
  ]);
63
69
  }
64
70
 
71
+ if (op === 'NOT IN') {
72
+ const items = node.right.expr.items.map(convertOperand);
73
+
74
+ return callExpression(identifier('notInList'), [
75
+ convertOperand(node.left),
76
+ ...items,
77
+ ]);
78
+ }
79
+
65
80
  const jsOp = SQL_TO_JS_OP[op] ?? '===';
66
81
 
67
82
  return binaryExpression(
@@ -71,13 +86,16 @@ const convertBinary = (node) => {
71
86
  );
72
87
  };
73
88
 
74
- const convertCondition = (node) => {
89
+ export const convertCondition = (node) => {
75
90
  if (node.operator?.name === 'AND')
76
91
  return convertAnd(node);
77
92
 
78
93
  if (node.operator?.name === 'OR')
79
94
  return convertOr(node);
80
95
 
96
+ if (node.type === 'between_expr')
97
+ return convertBetween(node);
98
+
81
99
  return convertBinary(node);
82
100
  };
83
101
 
@@ -0,0 +1,19 @@
1
+ [
2
+ withNamed(
3
+ recent = select(
4
+ id,
5
+ from(
6
+ users,
7
+ where(
8
+ kind === 'const',
9
+ ),
10
+ ),
11
+ ),
12
+ select(
13
+ id,
14
+ from(
15
+ recent,
16
+ ),
17
+ ),
18
+ ),
19
+ ];
@@ -0,0 +1 @@
1
+ WITH recent AS (SELECT id FROM users WHERE kind = 'const') SELECT id FROM recent
@@ -0,0 +1,27 @@
1
+ [
2
+ withRecursive(
3
+ numbers(
4
+ 'value',
5
+ unionAll(
6
+ select(
7
+ 1,
8
+ ),
9
+ select(
10
+ value + 1,
11
+ from(
12
+ numbers,
13
+ where(
14
+ value < 10,
15
+ ),
16
+ ),
17
+ ),
18
+ ),
19
+ ),
20
+ select(
21
+ value,
22
+ from(
23
+ numbers,
24
+ ),
25
+ ),
26
+ ),
27
+ ];
@@ -0,0 +1,38 @@
1
+ [
2
+ withRecursive(
3
+ t(
4
+ unionAll(
5
+ select(
6
+ 1,
7
+ ),
8
+ select(
9
+ 2,
10
+ ),
11
+ ),
12
+ ),
13
+ u(
14
+ 'a',
15
+ 'b',
16
+ unionAll(
17
+ select(
18
+ 1,
19
+ 2,
20
+ ),
21
+ select(
22
+ a,
23
+ b,
24
+ from(
25
+ t,
26
+ ),
27
+ ),
28
+ ),
29
+ ),
30
+ select(
31
+ a,
32
+ b,
33
+ from(
34
+ u,
35
+ ),
36
+ ),
37
+ ),
38
+ ];
@@ -0,0 +1 @@
1
+ WITH RECURSIVE t AS (SELECT 1 UNION ALL SELECT 2), u(a, b) AS (SELECT 1, 2 UNION ALL SELECT a, b FROM t) SELECT a, b FROM u
@@ -0,0 +1,20 @@
1
+ [
2
+ withRecursive(
3
+ t(
4
+ unionAll(
5
+ select(
6
+ 1,
7
+ ),
8
+ select(
9
+ 2,
10
+ ),
11
+ ),
12
+ ),
13
+ select(
14
+ '*',
15
+ from(
16
+ t,
17
+ ),
18
+ ),
19
+ ),
20
+ ];
@@ -0,0 +1 @@
1
+ WITH RECURSIVE t AS (SELECT 1 UNION ALL SELECT 2) SELECT * FROM t
@@ -0,0 +1 @@
1
+ WITH RECURSIVE numbers(value) AS (SELECT 1 UNION ALL SELECT value + 1 FROM numbers WHERE value < 10) SELECT value FROM numbers
@@ -6,6 +6,7 @@ const {
6
6
  identifier,
7
7
  assignmentExpression,
8
8
  callExpression,
9
+ stringLiteral,
9
10
  } = types;
10
11
 
11
12
  const isWithClause = (clause) => clause.type !== 'with_clause';
@@ -14,18 +15,30 @@ const convertTable = ({table, expr}) => {
14
15
  return assignmentExpression('=', identifier(table.name), convertClause(expr.expr));
15
16
  };
16
17
 
18
+ // recursive CTE: numbers('value', unionAll(...))
19
+ const convertRecursiveTable = ({table, columns, expr}) => {
20
+ const colArgs = columns ? columns.expr.items.map((column) => stringLiteral(column.name)) : [];
21
+ const body = convertClause(expr.expr);
22
+
23
+ return callExpression(identifier(table.name), [...colArgs, body]);
24
+ };
25
+
17
26
  export const convertWithNamed = (stmt) => {
18
27
  const withClause = findClause(stmt.clauses, 'with_clause');
28
+ const isRecursive = Boolean(withClause.recursiveKw);
29
+ const fnName = isRecursive ? 'withRecursive' : 'withNamed';
19
30
 
20
31
  const finalStatement = convertClause({
21
32
  ...stmt,
22
33
  clauses: stmt.clauses.filter(isWithClause),
23
34
  });
24
35
 
36
+ const convertCte = isRecursive ? convertRecursiveTable : convertTable;
37
+
25
38
  const args = [
26
- ...withClause.tables.items.map(convertTable),
39
+ ...withClause.tables.items.map(convertCte),
27
40
  finalStatement,
28
41
  ];
29
42
 
30
- return callExpression(identifier('withNamed'), args);
43
+ return callExpression(identifier(fnName), args);
31
44
  };
@@ -0,0 +1,5 @@
1
+ export const avg = (path, {write, traverse}) => {
2
+ write('AVG(');
3
+ traverse(path.get('arguments')[0]);
4
+ write(')');
5
+ };
@@ -0,0 +1,2 @@
1
+ SELECT AVG(score)
2
+ FROM results
@@ -0,0 +1 @@
1
+ select(avg(score), from(results));
@@ -0,0 +1,9 @@
1
+ export const between = (path, {write, traverse}) => {
2
+ const [col, lo, hi] = path.get('arguments');
3
+
4
+ traverse(col);
5
+ write(' BETWEEN ');
6
+ traverse(lo);
7
+ write(' AND ');
8
+ traverse(hi);
9
+ };
@@ -0,0 +1,3 @@
1
+ SELECT *
2
+ FROM users
3
+ WHERE age BETWEEN 18 AND 65
@@ -0,0 +1,3 @@
1
+ [
2
+ select('*', from(users, where(between(age, 18, 65)))),
3
+ ];
@@ -14,6 +14,20 @@ import {section} from './section/section.js';
14
14
  import {count} from './count/count.js';
15
15
  import {returning} from './returning/returning.js';
16
16
  import {withNamed} from './with-named/with-named.js';
17
+ import {withRecursive} from './with-recursive/with-recursive.js';
18
+ import {unionAll} from './union-all/union-all.js';
19
+ import {groupBy} from './group-by/group-by.js';
20
+ import {having} from './having/having.js';
21
+ import {sum} from './sum/sum.js';
22
+ import {avg} from './avg/avg.js';
23
+ import {min} from './min/min.js';
24
+ import {max} from './max/max.js';
25
+ import {orderBy} from './order-by/order-by.js';
26
+ import {desc} from './desc/desc.js';
27
+ import {limit} from './limit/limit.js';
28
+ import {distinct} from './distinct/distinct.js';
29
+ import {notInList} from './not-in-list/not-in-list.js';
30
+ import {between} from './between/between.js';
17
31
  import {createTable} from './create-table/create-table.js';
18
32
  import {createSequence} from './create-sequence/create-sequence.js';
19
33
  import {column} from './column/column.js';
@@ -48,6 +62,20 @@ export const clauses = {
48
62
  section,
49
63
  returning,
50
64
  withNamed,
65
+ withRecursive,
66
+ unionAll,
67
+ groupBy,
68
+ having,
69
+ sum,
70
+ avg,
71
+ min,
72
+ max,
73
+ orderBy,
74
+ desc,
75
+ limit,
76
+ distinct,
77
+ notInList,
78
+ between,
51
79
  createTable,
52
80
  createSequence,
53
81
  column,
@@ -0,0 +1,4 @@
1
+ export const desc = (path, {write, traverse}) => {
2
+ traverse(path.get('arguments')[0]);
3
+ write(' DESC');
4
+ };
@@ -0,0 +1,3 @@
1
+ SELECT id
2
+ FROM users
3
+ ORDER BY id DESC
@@ -0,0 +1,13 @@
1
+ [
2
+ select(
3
+ id,
4
+ from(
5
+ users,
6
+ orderBy(
7
+ desc(
8
+ id,
9
+ ),
10
+ ),
11
+ ),
12
+ ),
13
+ ];
@@ -0,0 +1,12 @@
1
+ export const distinct = (path, {write, traverse}) => {
2
+ write('DISTINCT ');
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
+ };
@@ -0,0 +1,2 @@
1
+ SELECT DISTINCT name
2
+ FROM users
@@ -0,0 +1,2 @@
1
+ SELECT DISTINCT name, age
2
+ FROM users
@@ -0,0 +1,11 @@
1
+ [
2
+ select(
3
+ distinct(
4
+ name,
5
+ age,
6
+ ),
7
+ from(
8
+ users,
9
+ ),
10
+ ),
11
+ ];
@@ -0,0 +1,10 @@
1
+ [
2
+ select(
3
+ distinct(
4
+ name,
5
+ ),
6
+ from(
7
+ users,
8
+ ),
9
+ ),
10
+ ];
@@ -0,0 +1,2 @@
1
+ SELECT *
2
+ FROM generate_series(1, 10, 2)
@@ -0,0 +1,3 @@
1
+ [
2
+ select('*', from(generateSeries(1, 10, 2))),
3
+ ];
@@ -0,0 +1,5 @@
1
+ SELECT kind, COUNT(*)
2
+ FROM VariableDeclaration
3
+ WHERE kind = 'const'
4
+ GROUP BY kind
5
+ HAVING COUNT(*) > 1
@@ -0,0 +1,22 @@
1
+ [
2
+ select(
3
+ kind,
4
+ count(
5
+ '*',
6
+ ),
7
+ from(
8
+ VariableDeclaration,
9
+ where(
10
+ kind === 'const',
11
+ ),
12
+ groupBy(
13
+ kind,
14
+ ),
15
+ having(
16
+ count(
17
+ '*',
18
+ ) > 1,
19
+ ),
20
+ ),
21
+ ),
22
+ ];
@@ -1,24 +1,70 @@
1
1
  import {types} from '@putout/babel';
2
2
 
3
- const {isTSAsExpression} = types;
3
+ const {
4
+ isTSAsExpression,
5
+ isCallExpression,
6
+ } = types;
4
7
 
5
8
  const printAsExpression = (path, {write, traverse}) => {
6
9
  traverse(path.get('expression'));
7
10
  write(` AS ${path.node.typeAnnotation.literal.value}`);
8
11
  };
9
12
 
10
- const printArg = (path, ctx) => {
11
- if (isTSAsExpression(path.node))
12
- return printAsExpression(path, ctx);
13
+ const CLAUSE_NAMES = [
14
+ 'where',
15
+ 'groupBy',
16
+ 'having',
17
+ 'orderBy',
18
+ 'limit',
19
+ ];
20
+
21
+ const TABLE_ARG_NAMES = [
22
+ ...CLAUSE_NAMES,
23
+ 'join',
24
+ 'on',
25
+ ];
26
+
27
+ const isClause = ({node}) => node.type === 'CallExpression' && CLAUSE_NAMES.includes(node.callee?.name);
28
+
29
+ const CAMEL_RE = /([A-Z])/g;
30
+ const toSnake = (a) => a.replace(CAMEL_RE, (_, c) => `_${c.toLowerCase()}`);
31
+
32
+ const isTableFunc = (node) => isCallExpression(node) && !TABLE_ARG_NAMES.includes(node.callee.name);
33
+
34
+ const printTableFunc = (path, {write, traverse}) => {
35
+ const {name} = path.node.callee;
36
+ const args = path.get('arguments');
13
37
 
14
- ctx.traverse(path);
38
+ write(toSnake(name));
39
+ write('(');
40
+
41
+ for (const [i, arg] of args.entries()) {
42
+ if (i > 0)
43
+ write(', ');
44
+
45
+ traverse(arg);
46
+ }
47
+
48
+ write(')');
49
+ };
50
+
51
+ const printArg = (path, printer) => {
52
+ const {node} = path;
53
+ const {traverse} = printer;
54
+
55
+ if (isTSAsExpression(node))
56
+ return printAsExpression(path, printer);
57
+
58
+ if (isTableFunc(node))
59
+ return printTableFunc(path, printer);
60
+
61
+ traverse(path);
15
62
  };
16
63
 
17
64
  export const from = (path, {write, traverse}) => {
18
65
  const args = path.get('arguments');
19
- const lastArg = args.at(-1);
20
- const hasWhere = lastArg?.node?.callee?.name === 'where';
21
- const tableArgs = hasWhere ? args.slice(0, -1) : args;
66
+ const tableArgs = args.filter((arg) => !isClause(arg));
67
+ const clauseArgs = args.filter(isClause);
22
68
 
23
69
  write('FROM ');
24
70
  printArg(tableArgs[0], {
@@ -32,8 +78,9 @@ export const from = (path, {write, traverse}) => {
32
78
  traverse,
33
79
  });
34
80
 
35
- if (hasWhere) {
36
- write.newline();
37
- traverse(lastArg);
81
+ for (const clause of clauseArgs) {
82
+ write.breakline();
83
+ traverse(clause);
38
84
  }
39
85
  };
86
+
@@ -0,0 +1,3 @@
1
+ SELECT kind, COUNT(*)
2
+ FROM VariableDeclaration
3
+ GROUP BY kind