happy-sql 2.2.1 → 2.3.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.
package/ChangeLog CHANGED
@@ -1,3 +1,29 @@
1
+ 2026.08.24, v2.3.0
2
+
3
+ fix:
4
+ - 219bd9b happy-sql: parser: REPLACE INTO
5
+ - 91b29e2 happy-sql: parser: intersect and except
6
+ - 198b65b npmignore: fixture: exclude
7
+
8
+ feature:
9
+ - 9236576 happy-sql: printer: replaceInto
10
+ - 7f333dc happy-sql: printer: intersect and except
11
+
12
+ 2026.08.24, v2.3.0
13
+
14
+ feature:
15
+ - 7f333dc printer: intersect and except
16
+ - 9236576 printer: replaceInto
17
+
18
+ fix:
19
+ - 91b29e2 parser: intersect and except
20
+ - 219bd9b parser: REPLACE INTO
21
+
22
+ test:
23
+ - fb2a0bc bin/convert coverage, 100% coverage
24
+ - 498e25f round-trip: intersect, except
25
+ - 720c219 round-trip: REPLACE INTO
26
+
1
27
  2026.08.24, v2.2.1
2
28
 
3
29
  fix:
@@ -59,7 +59,31 @@ const convertUpsert = (upsertClause) => {
59
59
  return callExpression(identifier('onConflict'), [...targetArgs, actionNode]);
60
60
  };
61
61
 
62
- export const convertInsert = ({clauses}) => {
62
+ const isReplaceInto = ({insertKw, orAction}) => !orAction && insertKw?.name === 'REPLACE';
63
+
64
+ const convertReplaceInto = ({clauses}) => {
65
+ const ic = findClause(clauses, 'insert_clause');
66
+ const rc = findClause(clauses, 'returning_clause');
67
+
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(findClause(clauses, 'values_clause'));
71
+
72
+ return callExpression(identifier('replaceInto'), [
73
+ callExpression(identifier('into'), [
74
+ identifier(ic.table.name),
75
+ colArg,
76
+ source,
77
+ ]),
78
+ rc && convertReturning(rc),
79
+ ].filter(Boolean));
80
+ };
81
+
82
+ export const convertInsert = (stmt) => {
83
+ if (isReplaceInto(stmt.clauses[0]))
84
+ return convertReplaceInto(stmt);
85
+
86
+ const {clauses} = stmt;
63
87
  const [ic] = clauses;
64
88
  const [, vc] = clauses;
65
89
  const uc = findClause(clauses, 'upsert_clause');
@@ -16,10 +16,22 @@ const getOpNames = (operator) => {
16
16
  return [operator.name];
17
17
  };
18
18
 
19
+ const getName = (ops) => {
20
+ if (ops.includes('INTERSECT'))
21
+ return 'intersect';
22
+
23
+ if (ops.includes('EXCEPT'))
24
+ return 'except';
25
+
26
+ if (ops.includes('ALL'))
27
+ return 'unionAll';
28
+
29
+ return 'union';
30
+ };
31
+
19
32
  export const convertUnion = (stmt) => {
20
33
  const ops = getOpNames(stmt.operator);
21
- const isAll = ops.includes('ALL');
22
- const name = isAll ? 'unionAll' : 'union';
34
+ const name = getName(ops);
23
35
 
24
36
  return callExpression(identifier(name), [
25
37
  convertClause(stmt.left),
@@ -10,6 +10,7 @@ import {select} from './select/select.js';
10
10
  import {from} from './from/from.js';
11
11
  import {where} from './where/where.js';
12
12
  import {insert} from './insert/insert.js';
13
+ import {replaceInto} from './replace-into/replace-into.js';
13
14
  import {into} from './into/into.js';
14
15
  import {values} from './values/values.js';
15
16
  import {onConflict} from './on-conflict/on-conflict.js';
@@ -24,6 +25,8 @@ import {withNamed} from './with-named/with-named.js';
24
25
  import {withRecursive} from './with-recursive/with-recursive.js';
25
26
  import {unionAll} from './union-all/union-all.js';
26
27
  import {union} from './union/union.js';
28
+ import {intersect} from './intersect/intersect.js';
29
+ import {except} from './except/except.js';
27
30
  import {groupBy} from './group-by/group-by.js';
28
31
  import {having} from './having/having.js';
29
32
  import {sum} from './sum/sum.js';
@@ -110,6 +113,7 @@ export const clauses = {
110
113
  select,
111
114
  where,
112
115
  insert,
116
+ replaceInto,
113
117
  into,
114
118
  orReplace,
115
119
  orIgnore,
@@ -140,6 +144,8 @@ export const clauses = {
140
144
  withRecursive,
141
145
  unionAll,
142
146
  union,
147
+ intersect,
148
+ except,
143
149
  groupBy,
144
150
  having,
145
151
  sum,
@@ -0,0 +1,9 @@
1
+ export const except = (path, {write, traverse}) => {
2
+ const [left, right] = path.get('arguments');
3
+
4
+ traverse(left);
5
+ write.breakline();
6
+ write('EXCEPT');
7
+ write.breakline();
8
+ traverse(right);
9
+ };
@@ -0,0 +1,9 @@
1
+ export const intersect = (path, {write, traverse}) => {
2
+ const [left, right] = path.get('arguments');
3
+
4
+ traverse(left);
5
+ write.breakline();
6
+ write('INTERSECT');
7
+ write.breakline();
8
+ traverse(right);
9
+ };
@@ -0,0 +1,11 @@
1
+ export const replaceInto = (path, {write, traverse}) => {
2
+ const args = path.get('arguments');
3
+
4
+ write('REPLACE ');
5
+ traverse(args[0]);
6
+
7
+ for (const arg of args.slice(1)) {
8
+ write.breakline();
9
+ traverse(arg);
10
+ }
11
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "happy-sql",
3
- "version": "2.2.1",
3
+ "version": "2.3.0",
4
4
  "type": "module",
5
5
  "description": "sql to js ast parser and printer",
6
6
  "exports": {