sumak 0.0.5 → 0.0.7

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 (43) hide show
  1. package/README.md +694 -416
  2. package/dist/ast/ddl-nodes.d.mts +153 -0
  3. package/dist/ast/ddl-nodes.mjs +1 -0
  4. package/dist/ast/nodes.d.mts +12 -1
  5. package/dist/builder/ddl/alter-table.d.mts +25 -0
  6. package/dist/builder/ddl/alter-table.mjs +146 -0
  7. package/dist/builder/ddl/create-index.d.mts +19 -0
  8. package/dist/builder/ddl/create-index.mjs +67 -0
  9. package/dist/builder/ddl/create-table.d.mts +40 -0
  10. package/dist/builder/ddl/create-table.mjs +186 -0
  11. package/dist/builder/ddl/create-view.d.mts +15 -0
  12. package/dist/builder/ddl/create-view.mjs +57 -0
  13. package/dist/builder/ddl/drop.d.mts +38 -0
  14. package/dist/builder/ddl/drop.mjs +133 -0
  15. package/dist/builder/delete.d.mts +3 -0
  16. package/dist/builder/delete.mjs +36 -0
  17. package/dist/builder/eb.d.mts +122 -6
  18. package/dist/builder/eb.mjs +273 -6
  19. package/dist/builder/select.d.mts +4 -0
  20. package/dist/builder/select.mjs +55 -0
  21. package/dist/builder/typed-delete.d.mts +14 -0
  22. package/dist/builder/typed-delete.mjs +26 -0
  23. package/dist/builder/typed-insert.d.mts +25 -1
  24. package/dist/builder/typed-insert.mjs +51 -0
  25. package/dist/builder/typed-select.d.mts +65 -4
  26. package/dist/builder/typed-select.mjs +132 -2
  27. package/dist/builder/typed-update.d.mts +12 -0
  28. package/dist/builder/typed-update.mjs +22 -0
  29. package/dist/builder/update.d.mts +3 -0
  30. package/dist/builder/update.mjs +36 -0
  31. package/dist/index.d.mts +12 -3
  32. package/dist/index.mjs +12 -2
  33. package/dist/printer/base.d.mts +2 -1
  34. package/dist/printer/base.mjs +25 -3
  35. package/dist/printer/ddl.d.mts +21 -0
  36. package/dist/printer/ddl.mjs +223 -0
  37. package/dist/schema/column.d.mts +10 -1
  38. package/dist/schema/column.mjs +33 -2
  39. package/dist/schema/index.d.mts +1 -1
  40. package/dist/schema/index.mjs +1 -1
  41. package/dist/sumak.d.mts +59 -1
  42. package/dist/sumak.mjs +103 -2
  43. package/package.json +1 -1
@@ -35,6 +35,18 @@ export class Col {
35
35
  return wrap(binOp("LIKE", this._node, rawLit(pattern)));
36
36
  }
37
37
 
38
+ notLike(pattern) {
39
+ return wrap(binOp("NOT LIKE", this._node, rawLit(pattern)));
40
+ }
41
+
42
+ ilike(pattern) {
43
+ return wrap(binOp("ILIKE", this._node, rawLit(pattern)));
44
+ }
45
+
46
+ notIlike(pattern) {
47
+ return wrap(binOp("NOT ILIKE", this._node, rawLit(pattern)));
48
+ }
49
+
38
50
  in(values) {
39
51
  return wrap({
40
52
  type: "in",
@@ -79,13 +91,122 @@ export class Col {
79
91
  });
80
92
  }
81
93
 
94
+ notBetween(low, high) {
95
+ return wrap({
96
+ type: "between",
97
+ expr: this._node,
98
+ low: autoParam(low),
99
+ high: autoParam(high),
100
+ negated: true
101
+ });
102
+ }
103
+
104
+ betweenSymmetric(low, high) {
105
+ return wrap({
106
+ type: "between",
107
+ expr: this._node,
108
+ low: autoParam(low),
109
+ high: autoParam(high),
110
+ negated: false,
111
+ symmetric: true
112
+ });
113
+ }
114
+
115
+ inSubquery(query) {
116
+ return wrap({
117
+ type: "in",
118
+ expr: this._node,
119
+ values: query,
120
+ negated: false
121
+ });
122
+ }
123
+
124
+ notInSubquery(query) {
125
+ return wrap({
126
+ type: "in",
127
+ expr: this._node,
128
+ values: query,
129
+ negated: true
130
+ });
131
+ }
132
+
133
+ eqExpr(value) {
134
+ return wrap(binOp("=", this._node, value.node));
135
+ }
136
+
137
+ neqExpr(value) {
138
+ return wrap(binOp("!=", this._node, value.node));
139
+ }
140
+
141
+ gtExpr(value) {
142
+ return wrap(binOp(">", this._node, value.node));
143
+ }
144
+
145
+ gteExpr(value) {
146
+ return wrap(binOp(">=", this._node, value.node));
147
+ }
148
+
149
+ ltExpr(value) {
150
+ return wrap(binOp("<", this._node, value.node));
151
+ }
152
+
153
+ lteExpr(value) {
154
+ return wrap(binOp("<=", this._node, value.node));
155
+ }
156
+
157
+ isDistinctFrom(value) {
158
+ return wrap(binOp("IS DISTINCT FROM", this._node, autoParam(value)));
159
+ }
160
+
161
+ isNotDistinctFrom(value) {
162
+ return wrap(binOp("IS NOT DISTINCT FROM", this._node, autoParam(value)));
163
+ }
164
+
82
165
  eqCol(other) {
83
166
  return wrap(binOp("=", this._node, other._node));
84
167
  }
85
168
 
169
+ neqCol(other) {
170
+ return wrap(binOp("!=", this._node, other._node));
171
+ }
172
+
173
+ gtCol(other) {
174
+ return wrap(binOp(">", this._node, other._node));
175
+ }
176
+
177
+ ltCol(other) {
178
+ return wrap(binOp("<", this._node, other._node));
179
+ }
180
+
181
+ gteCol(other) {
182
+ return wrap(binOp(">=", this._node, other._node));
183
+ }
184
+
185
+ lteCol(other) {
186
+ return wrap(binOp("<=", this._node, other._node));
187
+ }
188
+
86
189
  toExpr() {
87
190
  return wrap(this._node);
88
191
  }
192
+
193
+ cast(dataType) {
194
+ return wrap(rawCast(this._node, dataType));
195
+ }
196
+
197
+ asc() {
198
+ return {
199
+ expr: wrap(this._node),
200
+ direction: "ASC"
201
+ };
202
+ }
203
+
204
+ desc() {
205
+ return {
206
+ expr: wrap(this._node),
207
+ direction: "DESC"
208
+ };
209
+ }
89
210
  }
90
211
 
91
212
  let _paramIdx = 0;
@@ -114,18 +235,30 @@ export function createColumnProxies(_table) {
114
235
  }
115
236
 
116
237
 
117
- export function and(left, right) {
118
- return wrap(rawAnd(left.node, right.node));
238
+ export function and(...exprs) {
239
+ if (exprs.length === 0) return wrap(rawLit(true));
240
+ if (exprs.length === 1) return exprs[0];
241
+ return exprs.reduce((acc, expr) => wrap(rawAnd(acc.node, expr.node)));
119
242
  }
120
243
 
121
- export function or(left, right) {
122
- return wrap(rawOr(left.node, right.node));
244
+ export function or(...exprs) {
245
+ if (exprs.length === 0) return wrap(rawLit(false));
246
+ if (exprs.length === 1) return exprs[0];
247
+ return exprs.reduce((acc, expr) => wrap(rawOr(acc.node, expr.node)));
123
248
  }
124
249
 
125
250
  export function val(value) {
126
251
  return wrap(rawLit(value));
127
252
  }
128
253
 
254
+ export function rawExpr(sql, params = []) {
255
+ return wrap({
256
+ type: "raw",
257
+ sql,
258
+ params
259
+ });
260
+ }
261
+
129
262
  export function sqlFn(name, ...args) {
130
263
  return wrap(rawFn(name, args.map((a) => a.node)));
131
264
  }
@@ -148,10 +281,30 @@ export function sum(expr) {
148
281
  return wrap(rawFn("SUM", [expr.node]));
149
282
  }
150
283
 
284
+ export function sumDistinct(expr) {
285
+ const node = {
286
+ type: "function_call",
287
+ name: "SUM",
288
+ args: [expr.node],
289
+ distinct: true
290
+ };
291
+ return wrap(node);
292
+ }
293
+
151
294
  export function avg(expr) {
152
295
  return wrap(rawFn("AVG", [expr.node]));
153
296
  }
154
297
 
298
+ export function avgDistinct(expr) {
299
+ const node = {
300
+ type: "function_call",
301
+ name: "AVG",
302
+ args: [expr.node],
303
+ distinct: true
304
+ };
305
+ return wrap(node);
306
+ }
307
+
155
308
  export function min(expr) {
156
309
  return wrap(rawFn("MIN", [expr.node]));
157
310
  }
@@ -160,14 +313,50 @@ export function max(expr) {
160
313
  return wrap(rawFn("MAX", [expr.node]));
161
314
  }
162
315
 
163
- export function coalesce(expr, fallback) {
164
- return wrap(rawFn("COALESCE", [expr.node, fallback.node]));
316
+ export function coalesce(...args) {
317
+ return wrap(rawFn("COALESCE", args.map((a) => a.node)));
165
318
  }
166
319
 
167
320
  export function not(expr) {
168
321
  return wrap(rawNot(expr.node));
169
322
  }
170
323
 
324
+ export function add(a, b) {
325
+ return wrap(binOp("+", a.node, b.node));
326
+ }
327
+
328
+ export function sub(a, b) {
329
+ return wrap(binOp("-", a.node, b.node));
330
+ }
331
+
332
+ export function mul(a, b) {
333
+ return wrap(binOp("*", a.node, b.node));
334
+ }
335
+
336
+ export function div(a, b) {
337
+ return wrap(binOp("/", a.node, b.node));
338
+ }
339
+
340
+ export function mod(a, b) {
341
+ return wrap(binOp("%", a.node, b.node));
342
+ }
343
+
344
+ export function neg(expr) {
345
+ return wrap({
346
+ type: "unary_op",
347
+ op: "-",
348
+ operand: expr.node,
349
+ position: "prefix"
350
+ });
351
+ }
352
+
353
+ export function subqueryExpr(query) {
354
+ return wrap({
355
+ type: "subquery",
356
+ query
357
+ });
358
+ }
359
+
171
360
  export function exists(query) {
172
361
  return wrap(rawExists(query));
173
362
  }
@@ -280,6 +469,9 @@ export class WindowBuilder {
280
469
  range(start, end) {
281
470
  return this._withFrame("RANGE", start, end);
282
471
  }
472
+ groups(start, end) {
473
+ return this._withFrame("GROUPS", start, end);
474
+ }
283
475
 
284
476
  _withFrame(kind, start, end) {
285
477
  const b = new WindowBuilder();
@@ -386,10 +578,85 @@ export function ceil(expr) {
386
578
  return wrap(rawFn("CEIL", [expr.node]));
387
579
  }
388
580
 
581
+ export function jsonAgg(expr) {
582
+ return wrap(rawFn("JSON_AGG", [expr.node]));
583
+ }
584
+
585
+ export function toJson(expr) {
586
+ return wrap(rawFn("TO_JSON", [expr.node]));
587
+ }
588
+
589
+ export function jsonBuildObject(...pairs) {
590
+ const args = [];
591
+ for (const [key, val] of pairs) {
592
+ args.push(rawLit(key));
593
+ args.push(val.node);
594
+ }
595
+ return wrap(rawFn("JSON_BUILD_OBJECT", args));
596
+ }
597
+
598
+
599
+ export function arrayContains(arr, values) {
600
+ return wrap(binOp("@>", arr.node, values.node));
601
+ }
602
+
603
+ export function arrayContainedBy(arr, values) {
604
+ return wrap(binOp("<@", arr.node, values.node));
605
+ }
606
+
607
+ export function arrayOverlaps(arr, values) {
608
+ return wrap(binOp("&&", arr.node, values.node));
609
+ }
610
+
389
611
  export function floor(expr) {
390
612
  return wrap(rawFn("FLOOR", [expr.node]));
391
613
  }
392
614
 
615
+ export function stringAgg(expr, delimiter, orderBy) {
616
+ const node = {
617
+ type: "function_call",
618
+ name: "STRING_AGG",
619
+ args: [expr.node, rawLit(delimiter)],
620
+ orderBy: orderBy?.map((o) => ({
621
+ expr: o.expr.node,
622
+ direction: o.direction ?? "ASC"
623
+ }))
624
+ };
625
+ return wrap(node);
626
+ }
627
+
628
+ export function arrayAgg(expr, orderBy) {
629
+ const node = {
630
+ type: "function_call",
631
+ name: "ARRAY_AGG",
632
+ args: [expr.node],
633
+ orderBy: orderBy?.map((o) => ({
634
+ expr: o.expr.node,
635
+ direction: o.direction ?? "ASC"
636
+ }))
637
+ };
638
+ return wrap(node);
639
+ }
640
+
641
+ export function aggOrderBy(agg, orderBy) {
642
+ const fnNode = agg.node;
643
+ return wrap({
644
+ ...fnNode,
645
+ orderBy: orderBy.map((o) => ({
646
+ expr: o.expr.node,
647
+ direction: o.direction ?? "ASC"
648
+ }))
649
+ });
650
+ }
651
+
652
+ export function tuple(...exprs) {
653
+ const node = {
654
+ type: "tuple",
655
+ elements: exprs.map((e) => e.node)
656
+ };
657
+ return wrap(node);
658
+ }
659
+
393
660
  export function filter(agg, condition) {
394
661
  const fnNode = agg.node;
395
662
  return wrap({
@@ -9,10 +9,14 @@ export declare class SelectBuilder {
9
9
  distinctOn(...exprs: (string | ExpressionNode)[]): SelectBuilder;
10
10
  from(table: string | TableRefNode | SubqueryNode, alias?: string): SelectBuilder;
11
11
  where(expr: ExpressionNode): SelectBuilder;
12
+ orWhere(expr: ExpressionNode): SelectBuilder;
12
13
  join(type: JoinType, table: string | TableRefNode, on?: ExpressionNode, alias?: string): SelectBuilder;
13
14
  innerJoin(table: string | TableRefNode, on: ExpressionNode, alias?: string): SelectBuilder;
14
15
  leftJoin(table: string | TableRefNode, on: ExpressionNode, alias?: string): SelectBuilder;
15
16
  rightJoin(table: string | TableRefNode, on: ExpressionNode, alias?: string): SelectBuilder;
17
+ innerJoinLateral(subquery: SubqueryNode, on: ExpressionNode): SelectBuilder;
18
+ leftJoinLateral(subquery: SubqueryNode, on: ExpressionNode): SelectBuilder;
19
+ crossJoinLateral(subquery: SubqueryNode): SelectBuilder;
16
20
  groupBy(...exprs: (string | ExpressionNode)[]): SelectBuilder;
17
21
  having(expr: ExpressionNode): SelectBuilder;
18
22
  orderBy(expr: string | ExpressionNode, direction?: OrderDirection, nulls?: "FIRST" | "LAST"): SelectBuilder;
@@ -75,6 +75,23 @@ export class SelectBuilder {
75
75
  where: expr
76
76
  });
77
77
  }
78
+ orWhere(expr) {
79
+ if (this.node.where) {
80
+ return new SelectBuilder({
81
+ ...this.node,
82
+ where: {
83
+ type: "binary_op",
84
+ op: "OR",
85
+ left: this.node.where,
86
+ right: expr
87
+ }
88
+ });
89
+ }
90
+ return new SelectBuilder({
91
+ ...this.node,
92
+ where: expr
93
+ });
94
+ }
78
95
  join(type, table, on, alias) {
79
96
  const tableRef = typeof table === "string" ? {
80
97
  type: "table_ref",
@@ -101,6 +118,44 @@ export class SelectBuilder {
101
118
  rightJoin(table, on, alias) {
102
119
  return this.join("RIGHT", table, on, alias);
103
120
  }
121
+ innerJoinLateral(subquery, on) {
122
+ const join = {
123
+ type: "join",
124
+ joinType: "INNER",
125
+ table: subquery,
126
+ on,
127
+ lateral: true
128
+ };
129
+ return new SelectBuilder({
130
+ ...this.node,
131
+ joins: [...this.node.joins, join]
132
+ });
133
+ }
134
+ leftJoinLateral(subquery, on) {
135
+ const join = {
136
+ type: "join",
137
+ joinType: "LEFT",
138
+ table: subquery,
139
+ on,
140
+ lateral: true
141
+ };
142
+ return new SelectBuilder({
143
+ ...this.node,
144
+ joins: [...this.node.joins, join]
145
+ });
146
+ }
147
+ crossJoinLateral(subquery) {
148
+ const join = {
149
+ type: "join",
150
+ joinType: "CROSS",
151
+ table: subquery,
152
+ lateral: true
153
+ };
154
+ return new SelectBuilder({
155
+ ...this.node,
156
+ joins: [...this.node.joins, join]
157
+ });
158
+ }
104
159
  groupBy(...exprs) {
105
160
  const nodes = exprs.map((e) => typeof e === "string" ? col(e) : e);
106
161
  return new SelectBuilder({
@@ -17,6 +17,12 @@ export declare class TypedDeleteBuilder<
17
17
  * WHERE — callback or raw Expression.
18
18
  */
19
19
  where(exprOrCallback: Expression<boolean> | WhereCallback<DB, TB>): TypedDeleteBuilder<DB, TB>;
20
+ /** USING clause (PG: DELETE FROM t USING other WHERE ...) */
21
+ using<T extends keyof DB & string>(table: T): TypedDeleteBuilder<DB, TB>;
22
+ /** INNER JOIN for DELETE (MySQL pattern) */
23
+ innerJoin(table: string, on: Expression<boolean>): TypedDeleteBuilder<DB, TB>;
24
+ /** LEFT JOIN for DELETE */
25
+ leftJoin(table: string, on: Expression<boolean>): TypedDeleteBuilder<DB, TB>;
20
26
  /**
21
27
  * RETURNING specific columns.
22
28
  */
@@ -31,6 +37,14 @@ export declare class TypedDeleteBuilder<
31
37
  $if(condition: boolean, fn: (qb: TypedDeleteBuilder<DB, TB>) => TypedDeleteBuilder<DB, TB>): TypedDeleteBuilder<DB, TB>;
32
38
  build(): DeleteNode;
33
39
  compile(printer: Printer): CompiledQuery;
40
+ /** EXPLAIN this query. */
41
+ explain(options?: {
42
+ analyze?: boolean;
43
+ format?: "TEXT" | "JSON" | "YAML" | "XML";
44
+ }): {
45
+ build(): import("../ast/nodes.ts").ExplainNode;
46
+ compile(printer: Printer): CompiledQuery;
47
+ };
34
48
  }
35
49
  export declare class TypedDeleteReturningBuilder<
36
50
  DB,
@@ -27,6 +27,18 @@ export class TypedDeleteBuilder {
27
27
  return this._with(this._builder.where(unwrap(exprOrCallback)));
28
28
  }
29
29
 
30
+ using(table) {
31
+ return this._with(this._builder.using(table));
32
+ }
33
+
34
+ innerJoin(table, on) {
35
+ return this._with(this._builder.innerJoin(table, unwrap(on)));
36
+ }
37
+
38
+ leftJoin(table, on) {
39
+ return this._with(this._builder.leftJoin(table, unwrap(on)));
40
+ }
41
+
30
42
  returning(...cols) {
31
43
  const exprs = cols.map((c) => ({
32
44
  type: "column_ref",
@@ -61,6 +73,20 @@ export class TypedDeleteBuilder {
61
73
  compile(printer) {
62
74
  return printer.print(this.build());
63
75
  }
76
+
77
+ explain(options) {
78
+ const node = this.build();
79
+ const explainNode = {
80
+ type: "explain",
81
+ statement: node,
82
+ analyze: options?.analyze,
83
+ format: options?.format
84
+ };
85
+ return {
86
+ build: () => explainNode,
87
+ compile: (p) => p.print(explainNode)
88
+ };
89
+ }
64
90
  }
65
91
  export class TypedDeleteReturningBuilder {
66
92
 
@@ -18,10 +18,18 @@ export declare class TypedInsertBuilder<
18
18
  */
19
19
  values(row: Insertable<DB[TB]>): TypedInsertBuilder<DB, TB>;
20
20
  /**
21
+ * Insert multiple rows at once.
22
+ */
23
+ valuesMany(rows: Insertable<DB[TB]>[]): TypedInsertBuilder<DB, TB>;
24
+ /**
21
25
  * RETURNING specific columns.
22
26
  */
23
27
  returning<K extends keyof DB[TB] & string>(...cols: K[]): TypedInsertReturningBuilder<DB, TB, Pick<SelectRow<DB, TB>, K>>;
24
28
  /**
29
+ * RETURNING with expression and alias.
30
+ */
31
+ returningExpr<Alias extends string>(expr: Expression<any>, alias: Alias): TypedInsertReturningBuilder<DB, TB, SelectRow<DB, TB> & Record<Alias, any>>;
32
+ /**
25
33
  * RETURNING all columns.
26
34
  */
27
35
  returningAll(): TypedInsertReturningBuilder<DB, TB, SelectRow<DB, TB>>;
@@ -30,12 +38,20 @@ export declare class TypedInsertBuilder<
30
38
  */
31
39
  onConflictDoNothing(...columns: (keyof DB[TB] & string)[]): TypedInsertBuilder<DB, TB>;
32
40
  /**
33
- * ON CONFLICT DO UPDATE.
41
+ * ON CONFLICT DO UPDATE — with Expression values.
34
42
  */
35
43
  onConflictDoUpdate(columns: (keyof DB[TB] & string)[], set: {
36
44
  column: keyof DB[TB] & string;
37
45
  value: Expression<any>;
38
46
  }[]): TypedInsertBuilder<DB, TB>;
47
+ /**
48
+ * ON CONFLICT DO UPDATE — with plain object (auto-parameterized).
49
+ *
50
+ * ```ts
51
+ * .onConflictDoUpdateSet(["email"], { name: "Alice Updated" })
52
+ * ```
53
+ */
54
+ onConflictDoUpdateSet(columns: (keyof DB[TB] & string)[], values: Partial<Insertable<DB[TB]>>): TypedInsertBuilder<DB, TB>;
39
55
  /** ON CONFLICT ON CONSTRAINT name DO NOTHING */
40
56
  onConflictConstraintDoNothing(constraint: string): TypedInsertBuilder<DB, TB>;
41
57
  /** ON CONFLICT ON CONSTRAINT name DO UPDATE SET ... */
@@ -62,6 +78,14 @@ export declare class TypedInsertBuilder<
62
78
  $if(condition: boolean, fn: (qb: TypedInsertBuilder<DB, TB>) => TypedInsertBuilder<DB, TB>): TypedInsertBuilder<DB, TB>;
63
79
  build(): InsertNode;
64
80
  compile(printer: Printer): CompiledQuery;
81
+ /** EXPLAIN this query. */
82
+ explain(options?: {
83
+ analyze?: boolean;
84
+ format?: "TEXT" | "JSON" | "YAML" | "XML";
85
+ }): {
86
+ build(): import("../ast/nodes.ts").ExplainNode;
87
+ compile(printer: Printer): CompiledQuery;
88
+ };
65
89
  }
66
90
  export declare class TypedInsertReturningBuilder<
67
91
  DB,
@@ -39,6 +39,14 @@ export class TypedInsertBuilder {
39
39
  return this._withBuilder(builder, this._paramIdx);
40
40
  }
41
41
 
42
+ valuesMany(rows) {
43
+ let current = this;
44
+ for (const row of rows) {
45
+ current = current.values(row);
46
+ }
47
+ return current;
48
+ }
49
+
42
50
  returning(...cols) {
43
51
  const exprs = cols.map((c) => ({
44
52
  type: "column_ref",
@@ -51,6 +59,20 @@ export class TypedInsertBuilder {
51
59
  return new TypedInsertReturningBuilder(builder);
52
60
  }
53
61
 
62
+ returningExpr(expr, alias) {
63
+ const node = unwrap(expr);
64
+ const aliased = {
65
+ type: "aliased_expr",
66
+ expr: node,
67
+ alias
68
+ };
69
+ const builder = new InsertBuilder({
70
+ ...this._builder.build(),
71
+ returning: [...this._builder.build().returning, aliased]
72
+ }, this._paramIdx);
73
+ return new TypedInsertReturningBuilder(builder);
74
+ }
75
+
54
76
  returningAll() {
55
77
  const builder = new InsertBuilder({
56
78
  ...this._builder.build(),
@@ -70,6 +92,21 @@ export class TypedInsertBuilder {
70
92
  }))), this._paramIdx);
71
93
  }
72
94
 
95
+ onConflictDoUpdateSet(columns, values) {
96
+ const set = [];
97
+ let idx = this._paramIdx;
98
+ for (const [col, val] of Object.entries(values)) {
99
+ if (val !== undefined) {
100
+ set.push({
101
+ column: col,
102
+ value: param(idx, val)
103
+ });
104
+ idx++;
105
+ }
106
+ }
107
+ return this._withBuilder(this._builder.onConflictDoUpdate(columns, set), idx);
108
+ }
109
+
73
110
  onConflictConstraintDoNothing(constraint) {
74
111
  return this._withBuilder(this._builder.onConflictConstraintDoNothing(constraint), this._paramIdx);
75
112
  }
@@ -120,6 +157,20 @@ export class TypedInsertBuilder {
120
157
  compile(printer) {
121
158
  return printer.print(this.build());
122
159
  }
160
+
161
+ explain(options) {
162
+ const node = this.build();
163
+ const explainNode = {
164
+ type: "explain",
165
+ statement: node,
166
+ analyze: options?.analyze,
167
+ format: options?.format
168
+ };
169
+ return {
170
+ build: () => explainNode,
171
+ compile: (p) => p.print(explainNode)
172
+ };
173
+ }
123
174
  }
124
175
  export class TypedInsertReturningBuilder {
125
176