sumak 0.0.2 → 0.0.4

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/README.md CHANGED
@@ -21,7 +21,7 @@ npm install sumak
21
21
  ```
22
22
 
23
23
  ```ts
24
- import { sumak, pgDialect, serial, text, boolean, integer } from "sumak";
24
+ import { sumak, pgDialect, serial, text, boolean, integer, jsonb } from "sumak"
25
25
 
26
26
  const db = sumak({
27
27
  dialect: pgDialect(),
@@ -30,7 +30,9 @@ const db = sumak({
30
30
  id: serial().primaryKey(),
31
31
  name: text().notNull(),
32
32
  email: text().notNull(),
33
+ age: integer(),
33
34
  active: boolean().defaultTo(true),
35
+ meta: jsonb(),
34
36
  },
35
37
  posts: {
36
38
  id: serial().primaryKey(),
@@ -38,62 +40,422 @@ const db = sumak({
38
40
  userId: integer().references("users", "id"),
39
41
  },
40
42
  },
41
- });
43
+ })
42
44
  ```
43
45
 
44
46
  ## Query Building
45
47
 
48
+ ### SELECT
49
+
46
50
  ```ts
47
- // SELECT
48
51
  db.selectFrom("users")
49
52
  .select("id", "name")
50
53
  .where(({ age, active }) => and(age.gte(18), active.eq(true)))
51
54
  .orderBy("name")
52
55
  .limit(10)
53
- .compile(db.printer());
54
- // → SELECT "id", "name" FROM "users" WHERE ("age" >= $1 AND "active" = $2) ORDER BY "name" ASC LIMIT 10
56
+ .compile(db.printer())
57
+ ```
58
+
59
+ ### INSERT
55
60
 
56
- // INSERT
61
+ ```ts
57
62
  db.insertInto("users")
58
63
  .values({
59
64
  name: "Alice",
60
65
  email: "alice@example.com",
61
66
  })
62
67
  .returningAll()
63
- .compile(db.printer());
68
+ .compile(db.printer())
69
+ ```
70
+
71
+ ### UPDATE
64
72
 
65
- // UPDATE
73
+ ```ts
66
74
  db.update("users")
67
75
  .set({ active: false })
68
76
  .where(({ id }) => id.eq(1))
69
- .compile(db.printer());
77
+ .compile(db.printer())
78
+ ```
79
+
80
+ ### DELETE
70
81
 
71
- // DELETE
82
+ ```ts
72
83
  db.deleteFrom("users")
73
84
  .where(({ id }) => id.eq(1))
74
85
  .returning("id")
75
- .compile(db.printer());
86
+ .compile(db.printer())
76
87
  ```
77
88
 
78
89
  ## Joins
79
90
 
80
91
  ```ts
92
+ // INNER JOIN
81
93
  db.selectFrom("users")
82
94
  .innerJoin("posts", ({ users, posts }) => users.id.eqCol(posts.userId))
83
- .compile(db.printer());
84
- // → SELECT * FROM "users" INNER JOIN "posts" ON ("users"."id" = "posts"."userId")
95
+ .select("id", "title")
96
+ .compile(db.printer())
97
+
98
+ // LEFT JOIN — joined columns become nullable
99
+ db.selectFrom("users")
100
+ .leftJoin("posts", ({ users, posts }) => users.id.eqCol(posts.userId))
101
+ .compile(db.printer())
102
+
103
+ // RIGHT JOIN
104
+ db.selectFrom("users")
105
+ .rightJoin("posts", ({ users, posts }) => users.id.eqCol(posts.userId))
106
+ .compile(db.printer())
107
+
108
+ // FULL JOIN — both sides become nullable
109
+ db.selectFrom("users")
110
+ .fullJoin("posts", ({ users, posts }) => users.id.eqCol(posts.userId))
111
+ .compile(db.printer())
112
+
113
+ // CROSS JOIN — cartesian product
114
+ db.selectFrom("users").crossJoin("posts").compile(db.printer())
115
+ ```
116
+
117
+ ## Expression API
118
+
119
+ ### Comparisons
120
+
121
+ ```ts
122
+ .where(({ id }) =>
123
+ id.eq(42),
124
+ )
125
+
126
+ .where(({ age }) =>
127
+ age.gt(18),
128
+ )
129
+
130
+ .where(({ age }) =>
131
+ age.gte(18),
132
+ )
133
+
134
+ .where(({ age }) =>
135
+ age.lt(65),
136
+ )
137
+
138
+ .where(({ age }) =>
139
+ age.lte(65),
140
+ )
141
+
142
+ .where(({ active }) =>
143
+ active.neq(false),
144
+ )
85
145
  ```
86
146
 
147
+ ### String Matching
148
+
149
+ ```ts
150
+ .where(({ name }) =>
151
+ name.like("%ali%"),
152
+ )
153
+ ```
154
+
155
+ ### Range & List
156
+
157
+ ```ts
158
+ .where(({ age }) =>
159
+ age.between(18, 65),
160
+ )
161
+
162
+ .where(({ id }) =>
163
+ id.in([1, 2, 3]),
164
+ )
165
+
166
+ .where(({ id }) =>
167
+ id.notIn([99, 100]),
168
+ )
169
+ ```
170
+
171
+ ### Null Checks
172
+
173
+ ```ts
174
+ .where(({ bio }) =>
175
+ bio.isNull(),
176
+ )
177
+
178
+ .where(({ email }) =>
179
+ email.isNotNull(),
180
+ )
181
+ ```
182
+
183
+ ### Logical Combinators
184
+
185
+ ```ts
186
+ // AND
187
+ .where(({ age, active }) =>
188
+ and(
189
+ age.gt(0),
190
+ active.eq(true),
191
+ ),
192
+ )
193
+
194
+ // OR
195
+ .where(({ name, email }) =>
196
+ or(
197
+ name.like("%alice%"),
198
+ email.like("%alice%"),
199
+ ),
200
+ )
201
+
202
+ // NOT
203
+ .where(({ active }) =>
204
+ not(active.eq(true)),
205
+ )
206
+ ```
207
+
208
+ ### Aggregates
209
+
210
+ ```ts
211
+ import { count, sum, avg, min, max, coalesce } from "sumak"
212
+
213
+ db.selectFrom("users").selectExpr(count(), "total").compile(db.printer())
214
+
215
+ db.selectFrom("orders").selectExpr(sum(col.amount), "totalAmount").compile(db.printer())
216
+
217
+ db.selectFrom("orders").selectExpr(avg(col.amount), "avgAmount").compile(db.printer())
218
+
219
+ db.selectFrom("orders")
220
+ .selectExpr(coalesce(col.discount, val(0)), "safeDiscount")
221
+ .compile(db.printer())
222
+ ```
223
+
224
+ ### EXISTS / NOT EXISTS
225
+
226
+ ```ts
227
+ import { exists, notExists } from "sumak"
228
+
229
+ db.selectFrom("users")
230
+ .where(() =>
231
+ exists(
232
+ db
233
+ .selectFrom("posts")
234
+ .where(({ userId }) => userId.eq(1))
235
+ .build(),
236
+ ),
237
+ )
238
+ .compile(db.printer())
239
+
240
+ db.selectFrom("users")
241
+ .where(() =>
242
+ notExists(
243
+ db
244
+ .selectFrom("posts")
245
+ .where(({ userId }) => userId.eq(1))
246
+ .build(),
247
+ ),
248
+ )
249
+ .compile(db.printer())
250
+ ```
251
+
252
+ ### CASE Expression
253
+
254
+ ```ts
255
+ import { case_, val } from "sumak"
256
+
257
+ db.selectFrom("users")
258
+ .selectExpr(
259
+ case_()
260
+ .when(col.active.eq(true), val("active"))
261
+ .when(col.active.eq(false), val("inactive"))
262
+ .else_(val("unknown"))
263
+ .end(),
264
+ "status",
265
+ )
266
+ .compile(db.printer())
267
+ ```
268
+
269
+ ### CAST
270
+
271
+ ```ts
272
+ import { cast, val } from "sumak"
273
+
274
+ db.selectFrom("users")
275
+ .selectExpr(cast(val(42), "text"), "idAsText")
276
+ .compile(db.printer())
277
+ ```
278
+
279
+ ### JSON Operations
280
+
281
+ ```ts
282
+ import { jsonRef } from "sumak"
283
+
284
+ // -> (JSON object)
285
+ db.selectFrom("users")
286
+ .selectExpr(jsonRef(col.meta, "address", "->"), "address")
287
+ .compile(db.printer())
288
+
289
+ // ->> (text value)
290
+ db.selectFrom("users")
291
+ .selectExpr(jsonRef(col.meta, "name", "->>"), "metaName")
292
+ .compile(db.printer())
293
+ ```
294
+
295
+ ## Set Operations
296
+
297
+ ```ts
298
+ const active = db
299
+ .selectFrom("users")
300
+ .select("id")
301
+ .where(({ active }) => active.eq(true))
302
+
303
+ const premium = db
304
+ .selectFrom("users")
305
+ .select("id")
306
+ .where(({ active }) => active.eq(true))
307
+
308
+ // UNION
309
+ active.union(premium).compile(db.printer())
310
+
311
+ // UNION ALL
312
+ active.unionAll(premium).compile(db.printer())
313
+
314
+ // INTERSECT
315
+ active.intersect(premium).compile(db.printer())
316
+
317
+ // EXCEPT
318
+ active.except(premium).compile(db.printer())
319
+ ```
320
+
321
+ ## CTEs (WITH)
322
+
323
+ ```ts
324
+ // SELECT with CTE
325
+ db.selectFrom("users")
326
+ .with(
327
+ "active_users",
328
+ db
329
+ .selectFrom("users")
330
+ .where(({ active }) => active.eq(true))
331
+ .build(),
332
+ )
333
+ .compile(db.printer())
334
+
335
+ // INSERT with CTE
336
+ db.insertInto("users")
337
+ .with("source", sourceCte)
338
+ .values({ name: "Alice", email: "a@b.com" })
339
+ .compile(db.printer())
340
+
341
+ // UPDATE with CTE
342
+ db.update("users").with("target", targetCte).set({ active: false }).compile(db.printer())
343
+
344
+ // DELETE with CTE
345
+ db.deleteFrom("users")
346
+ .with("to_delete", deleteCte)
347
+ .where(({ id }) => id.eq(1))
348
+ .compile(db.printer())
349
+
350
+ // Recursive CTE
351
+ db.selectFrom("users").with("tree", recursiveQuery, true).compile(db.printer())
352
+ ```
353
+
354
+ ## UPDATE FROM
355
+
356
+ ```ts
357
+ db.update("users")
358
+ .set({ name: "Bob" })
359
+ .from("posts")
360
+ .where(({ id }) => id.eq(1))
361
+ .compile(db.printer())
362
+ // UPDATE "users" SET "name" = $1 FROM "posts" WHERE ("id" = $2)
363
+ ```
364
+
365
+ ## ON CONFLICT
366
+
367
+ ```ts
368
+ // DO NOTHING
369
+ db.insertInto("users")
370
+ .values({ name: "Alice", email: "a@b.com" })
371
+ .onConflictDoNothing("email")
372
+ .compile(db.printer())
373
+
374
+ // DO UPDATE
375
+ db.insertInto("users")
376
+ .values({ name: "Alice", email: "a@b.com" })
377
+ .onConflictDoUpdate(["email"], [{ column: "name", value: val("Alice") }])
378
+ .compile(db.printer())
379
+ ```
380
+
381
+ ## MERGE (SQL:2003)
382
+
383
+ ```ts
384
+ db.mergeInto("users", "staging", "s", ({ target, source }) => target.id.eqCol(source.id))
385
+ .whenMatchedThenUpdate({ name: "updated" })
386
+ .whenNotMatchedThenInsert({
387
+ name: "Alice",
388
+ email: "alice@example.com",
389
+ })
390
+ .compile(db.printer())
391
+
392
+ // MERGE with conditional delete
393
+ db.mergeInto("users", "staging", "s", ({ target, source }) => target.id.eqCol(source.id))
394
+ .whenMatchedThenDelete()
395
+ .compile(db.printer())
396
+ ```
397
+
398
+ ## Full-Text Search
399
+
400
+ Dialect-aware FTS — same API, different SQL per dialect:
401
+
402
+ ```ts
403
+ import { textSearch } from "sumak"
404
+
405
+ // PostgreSQL: to_tsvector("name") @@ to_tsquery('alice')
406
+ db.selectFrom("users")
407
+ .where(({ name }) => textSearch([name.toExpr()], val("alice")))
408
+ .compile(db.printer())
409
+
410
+ // With language config
411
+ db.selectFrom("users")
412
+ .where(({ name }) => textSearch([name.toExpr()], val("alice"), { language: "english" }))
413
+ .compile(db.printer())
414
+
415
+ // MySQL: MATCH(`name`) AGAINST(? IN BOOLEAN MODE)
416
+ // SQLite: ("name" MATCH ?)
417
+ // MSSQL: CONTAINS(([name]), @p0)
418
+ ```
419
+
420
+ ## Temporal Tables (SQL:2011)
421
+
422
+ Query historical data with `FOR SYSTEM_TIME`:
423
+
424
+ ```ts
425
+ // AS OF — point-in-time query
426
+ db.selectFrom("users")
427
+ .forSystemTime({
428
+ kind: "as_of",
429
+ timestamp: lit("2024-01-01"),
430
+ })
431
+ .compile(db.printer())
432
+
433
+ // BETWEEN — time range
434
+ db.selectFrom("users")
435
+ .forSystemTime({
436
+ kind: "between",
437
+ start: lit("2024-01-01"),
438
+ end: lit("2024-12-31"),
439
+ })
440
+ .compile(db.printer())
441
+
442
+ // ALL — full history
443
+ db.selectFrom("users").forSystemTime({ kind: "all" }).compile(db.printer())
444
+ ```
445
+
446
+ Supported modes: `as_of`, `from_to`, `between`, `contained_in`, `all`.
447
+
87
448
  ## Tree Shaking
88
449
 
89
450
  Import only the dialect you need:
90
451
 
91
452
  ```ts
92
- import { sumak } from "sumak";
93
- import { pgDialect } from "sumak/pg";
94
- import { mysqlDialect } from "sumak/mysql";
95
- import { sqliteDialect } from "sumak/sqlite";
96
- import { serial, text } from "sumak/schema";
453
+ import { sumak } from "sumak"
454
+ import { pgDialect } from "sumak/pg"
455
+ import { mssqlDialect } from "sumak/mssql"
456
+ import { mysqlDialect } from "sumak/mysql"
457
+ import { sqliteDialect } from "sumak/sqlite"
458
+ import { serial, text } from "sumak/schema"
97
459
  ```
98
460
 
99
461
  ## Dialects
@@ -104,12 +466,40 @@ Same query, different SQL:
104
466
  // PostgreSQL → SELECT "id" FROM "users" WHERE ("id" = $1)
105
467
  // MySQL → SELECT `id` FROM `users` WHERE (`id` = ?)
106
468
  // SQLite → SELECT "id" FROM "users" WHERE ("id" = ?)
469
+ // MSSQL → SELECT [id] FROM [users] WHERE ([id] = @p0)
470
+ ```
471
+
472
+ ### MSSQL Specifics
473
+
474
+ ```ts
475
+ import { mssqlDialect } from "sumak/mssql"
476
+
477
+ const db = sumak({
478
+ dialect: mssqlDialect(),
479
+ tables: { ... },
480
+ })
481
+
482
+ // LIMIT → TOP N
483
+ // SELECT TOP 10 * FROM [users]
484
+
485
+ // LIMIT + OFFSET → OFFSET/FETCH
486
+ // SELECT * FROM [users] ORDER BY [id] ASC OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY
487
+
488
+ // RETURNING → OUTPUT INSERTED.*
489
+ // INSERT INTO [users] ([name]) OUTPUT INSERTED.* VALUES (@p0)
490
+
491
+ // DELETE RETURNING → OUTPUT DELETED.*
492
+ // DELETE FROM [users] OUTPUT DELETED.* WHERE ([id] = @p0)
107
493
  ```
108
494
 
109
495
  ## Plugins
110
496
 
111
497
  ```ts
112
- import { WithSchemaPlugin, SoftDeletePlugin, CamelCasePlugin } from "sumak";
498
+ import {
499
+ WithSchemaPlugin,
500
+ SoftDeletePlugin,
501
+ CamelCasePlugin,
502
+ } from "sumak"
113
503
 
114
504
  const db = sumak({
115
505
  dialect: pgDialect(),
@@ -118,7 +508,7 @@ const db = sumak({
118
508
  new SoftDeletePlugin({ tables: ["users"] }),
119
509
  ],
120
510
  tables: { ... },
121
- });
511
+ })
122
512
 
123
513
  // SELECT * FROM "public"."users" WHERE ("deleted_at" IS NULL)
124
514
  ```
@@ -128,52 +518,35 @@ const db = sumak({
128
518
  ```ts
129
519
  // Query logging
130
520
  db.hook("query:after", (ctx) => {
131
- console.log(`[SQL] ${ctx.query.sql}`);
132
- });
521
+ console.log(`[SQL] ${ctx.query.sql}`)
522
+ })
133
523
 
134
524
  // Add request tracing
135
525
  db.hook("query:after", (ctx) => {
136
526
  return {
137
527
  ...ctx.query,
138
528
  sql: `${ctx.query.sql} /* request_id=${requestId} */`,
139
- };
140
- });
529
+ }
530
+ })
141
531
 
142
532
  // Modify AST before compilation
143
533
  db.hook("select:before", (ctx) => {
144
534
  // Add tenant isolation, audit filters, etc.
145
- });
535
+ })
146
536
 
147
537
  // Transform results
148
538
  db.hook("result:transform", (rows) => {
149
- return rows.map(toCamelCase);
150
- });
539
+ return rows.map(toCamelCase)
540
+ })
151
541
 
152
542
  // Unregister
153
- const off = db.hook("query:before", handler);
154
- off();
155
- ```
156
-
157
- ## Expression API
158
-
159
- ```ts
160
- .where(({ id }) => id.eq(42)) // "id" = $1
161
- .where(({ name }) => name.like("%ali%")) // "name" LIKE '%ali%'
162
- .where(({ age }) => age.between(18, 65)) // "age" BETWEEN $1 AND $2
163
- .where(({ id }) => id.in([1, 2, 3])) // "id" IN ($1, $2, $3)
164
- .where(({ bio }) => bio.isNull()) // "bio" IS NULL
165
- .where(({ email }) => email.isNotNull()) // "email" IS NOT NULL
166
- .where(({ a, b }) => // ("a" > $1 AND "b" != $2)
167
- and(a.gt(0), b.neq("x")),
168
- )
169
- .where(({ a, b }) => // ("a" = $1 OR "b" = $2)
170
- or(a.eq(1), b.eq(2)),
171
- )
543
+ const off = db.hook("query:before", handler)
544
+ off()
172
545
  ```
173
546
 
174
- ## Why lale?
547
+ ## Why sumak?
175
548
 
176
- | | lale | Drizzle | Kysely |
549
+ | | sumak | Drizzle | Kysely |
177
550
  | ------------------ | ----------------- | --------------- | -------------- |
178
551
  | **Architecture** | AST-first | Template | AST (98 nodes) |
179
552
  | **Type inference** | Auto (no codegen) | Auto | Manual DB type |
@@ -190,10 +563,10 @@ Schema → Builder → AST → Plugin/Hook → Printer → SQL
190
563
  ```
191
564
 
192
565
  - **Schema Layer** — `defineTable()`, `ColumnType<S,I,U>`, auto type inference
193
- - **Builder Layer** — `Lale<DB>`, `TypedSelectBuilder<DB,TB,O>`, proxy-based expressions
566
+ - **Builder Layer** — `Sumak<DB>`, `TypedSelectBuilder<DB,TB,O>`, proxy-based expressions
194
567
  - **AST Layer** — ~35 frozen node types, discriminated unions, visitor pattern
195
- - **Plugin Layer** — `LalePlugin` interface, `Hookable` lifecycle hooks
196
- - **Printer Layer** — `BasePrinter` with dialect subclasses, Wadler document algebra
568
+ - **Plugin Layer** — `SumakPlugin` interface, `Hookable` lifecycle hooks
569
+ - **Printer Layer** — `BasePrinter` with 4 dialect subclasses (PG, MySQL, SQLite, MSSQL), Wadler document algebra
197
570
 
198
571
  ## License
199
572
 
@@ -1 +1 @@
1
- function e(e,t){switch(t){case`pg`:case`sqlite`:return`"${e.replaceAll(`"`,`""`)}"`;case`mysql`:return`\`${e.replaceAll("`","``")}\``}}function t(t,n,r){return r?`${e(r,n)}.${e(t,n)}`:e(t,n)}function n(e,t){switch(t){case`pg`:return`$${e+1}`;case`mysql`:case`sqlite`:return`?`}}var r=class{params=[];dialect;constructor(e){this.dialect=e}print(e){return this.params=[],{sql:this.printNode(e),params:[...this.params]}}printNode(e){switch(e.type){case`select`:return this.printSelect(e);case`insert`:return this.printInsert(e);case`update`:return this.printUpdate(e);case`delete`:return this.printDelete(e);default:return this.printExpression(e)}}printSelect(e){let t=[];e.ctes.length>0&&t.push(this.printCTEs(e.ctes)),t.push(`SELECT`),e.distinct&&t.push(`DISTINCT`),e.columns.length===0?t.push(`*`):t.push(e.columns.map(e=>this.printExpression(e)).join(`, `)),e.from&&(t.push(`FROM`),e.from.type===`subquery`?t.push(this.printSubquery(e.from)):t.push(this.printTableRef(e.from)));for(let n of e.joins)t.push(this.printJoin(n));return e.where&&t.push(`WHERE`,this.printExpression(e.where)),e.groupBy.length>0&&t.push(`GROUP BY`,e.groupBy.map(e=>this.printExpression(e)).join(`, `)),e.having&&t.push(`HAVING`,this.printExpression(e.having)),e.orderBy.length>0&&t.push(`ORDER BY`,e.orderBy.map(e=>this.printOrderBy(e)).join(`, `)),e.limit&&t.push(`LIMIT`,this.printExpression(e.limit)),e.offset&&t.push(`OFFSET`,this.printExpression(e.offset)),e.setOp&&t.push(e.setOp.op,this.printSelect(e.setOp.query)),e.forUpdate&&t.push(`FOR UPDATE`),t.join(` `)}printInsert(t){let n=[];t.ctes.length>0&&n.push(this.printCTEs(t.ctes)),n.push(`INSERT INTO`,this.printTableRef(t.table)),t.columns.length>0&&n.push(`(${t.columns.map(t=>e(t,this.dialect)).join(`, `)})`),n.push(`VALUES`);let r=t.values.map(e=>`(${e.map(e=>this.printExpression(e)).join(`, `)})`);return n.push(r.join(`, `)),t.onConflict&&n.push(this.printOnConflict(t.onConflict)),t.returning.length>0&&n.push(`RETURNING`,t.returning.map(e=>this.printExpression(e)).join(`, `)),n.join(` `)}printOnConflict(t){let n=[`ON CONFLICT`];if(t.columns.length>0&&n.push(`(${t.columns.map(t=>e(t,this.dialect)).join(`, `)})`),t.action===`nothing`)n.push(`DO NOTHING`);else{n.push(`DO UPDATE SET`);let r=t.action.set.map(t=>`${e(t.column,this.dialect)} = ${this.printExpression(t.value)}`);n.push(r.join(`, `))}return t.where&&n.push(`WHERE`,this.printExpression(t.where)),n.join(` `)}printUpdate(t){let n=[];t.ctes.length>0&&n.push(this.printCTEs(t.ctes)),n.push(`UPDATE`,this.printTableRef(t.table),`SET`);let r=t.set.map(t=>`${e(t.column,this.dialect)} = ${this.printExpression(t.value)}`);return n.push(r.join(`, `)),t.from&&n.push(`FROM`,this.printTableRef(t.from)),t.where&&n.push(`WHERE`,this.printExpression(t.where)),t.returning.length>0&&n.push(`RETURNING`,t.returning.map(e=>this.printExpression(e)).join(`, `)),n.join(` `)}printDelete(e){let t=[];return e.ctes.length>0&&t.push(this.printCTEs(e.ctes)),t.push(`DELETE FROM`,this.printTableRef(e.table)),e.where&&t.push(`WHERE`,this.printExpression(e.where)),e.returning.length>0&&t.push(`RETURNING`,e.returning.map(e=>this.printExpression(e)).join(`, `)),t.join(` `)}printExpression(e){switch(e.type){case`column_ref`:return this.printColumnRef(e);case`literal`:return this.printLiteral(e);case`binary_op`:return this.printBinaryOp(e);case`unary_op`:return this.printUnaryOp(e);case`function_call`:return this.printFunctionCall(e);case`param`:return this.printParam(e);case`raw`:return this.printRaw(e);case`subquery`:return this.printSubquery(e);case`between`:return this.printBetween(e);case`in`:return this.printIn(e);case`is_null`:return this.printIsNull(e);case`cast`:return this.printCast(e);case`exists`:return this.printExists(e);case`star`:return this.printStar(e);case`case`:return this.printCase(e);case`json_access`:return this.printJsonAccess(e);case`array_expr`:return this.printArrayExpr(e);case`window_function`:return this.printWindowFunction(e)}}printColumnRef(t){let n=t.table?`${e(t.table,this.dialect)}.${e(t.column,this.dialect)}`:e(t.column,this.dialect);return t.alias&&(n+=` AS ${e(t.alias,this.dialect)}`),n}printLiteral(e){return e.value===null?`NULL`:typeof e.value==`boolean`?e.value?`TRUE`:`FALSE`:typeof e.value==`number`?String(e.value):`'${String(e.value).replaceAll(`'`,`''`)}'`}printBinaryOp(e){return`(${this.printExpression(e.left)} ${e.op} ${this.printExpression(e.right)})`}printUnaryOp(e){return e.position===`postfix`?`(${this.printExpression(e.operand)} ${e.op})`:`(${e.op} ${this.printExpression(e.operand)})`}printFunctionCall(t){let n=`${t.name}(${t.args.map(e=>this.printExpression(e)).join(`, `)})`;return t.alias&&(n+=` AS ${e(t.alias,this.dialect)}`),n}printParam(e){return this.params.push(e.value),n(this.params.length-1,this.dialect)}printRaw(e){return this.params.push(...e.params),e.sql}printSubquery(t){let n=`(${this.printSelect(t.query)})`;return t.alias&&(n+=` AS ${e(t.alias,this.dialect)}`),n}printBetween(e){let t=e.negated?`NOT `:``;return`(${this.printExpression(e.expr)} ${t}BETWEEN ${this.printExpression(e.low)} AND ${this.printExpression(e.high)})`}printIn(e){let t=e.negated?`NOT `:``;return Array.isArray(e.values)?`(${this.printExpression(e.expr)} ${t}IN (${e.values.map(e=>this.printExpression(e)).join(`, `)}))`:`(${this.printExpression(e.expr)} ${t}IN (${this.printSelect(e.values)}))`}printIsNull(e){let t=e.negated?` NOT`:``;return`(${this.printExpression(e.expr)} IS${t} NULL)`}printCase(e){let t=[`CASE`];e.operand&&t.push(this.printExpression(e.operand));for(let n of e.whens)t.push(`WHEN`,this.printExpression(n.condition),`THEN`,this.printExpression(n.result));return e.else_&&t.push(`ELSE`,this.printExpression(e.else_)),t.push(`END`),t.join(` `)}printCast(e){return`CAST(${this.printExpression(e.expr)} AS ${e.dataType})`}printExists(e){return`(${e.negated?`NOT `:``}EXISTS (${this.printSelect(e.query)}))`}printStar(t){return t.table?`${e(t.table,this.dialect)}.*`:`*`}printTableRef(n){let r=t(n.name,this.dialect,n.schema);return n.alias&&(r+=` AS ${e(n.alias,this.dialect)}`),r}printJoin(e){let t=[];return t.push(`${e.joinType} JOIN`),e.table.type===`subquery`?t.push(this.printSubquery(e.table)):t.push(this.printTableRef(e.table)),e.on&&t.push(`ON`,this.printExpression(e.on)),t.join(` `)}printOrderBy(e){let t=`${this.printExpression(e.expr)} ${e.direction}`;return e.nulls&&(t+=` NULLS ${e.nulls}`),t}printCTEs(t){return`${t.some(e=>e.recursive)?`WITH RECURSIVE`:`WITH`} ${t.map(t=>`${e(t.name,this.dialect)} AS (${this.printSelect(t.query)})`).join(`, `)}`}printJsonAccess(t){let n=`${this.printExpression(t.expr)}${t.operator}${this.printLiteral({type:`literal`,value:t.path})}`;return t.alias&&(n+=` AS ${e(t.alias,this.dialect)}`),n}printArrayExpr(e){return`ARRAY[${e.elements.map(e=>this.printExpression(e)).join(`, `)}]`}printWindowFunction(t){let n=[];n.push(this.printFunctionCall(t.fn)),n.push(`OVER`);let r=[];return t.partitionBy.length>0&&r.push(`PARTITION BY ${t.partitionBy.map(e=>this.printExpression(e)).join(`, `)}`),t.orderBy.length>0&&r.push(`ORDER BY ${t.orderBy.map(e=>this.printOrderBy(e)).join(`, `)}`),t.frame&&r.push(this.printFrameSpec(t.frame)),n.push(`(${r.join(` `)})`),t.alias&&n.push(`AS`,e(t.alias,this.dialect)),n.join(` `)}printFrameSpec(e){let t=this.printFrameBound(e.start);return e.end?`${e.kind} BETWEEN ${t} AND ${this.printFrameBound(e.end)}`:`${e.kind} ${t}`}printFrameBound(e){switch(e.type){case`unbounded_preceding`:return`UNBOUNDED PRECEDING`;case`preceding`:return`${e.value} PRECEDING`;case`current_row`:return`CURRENT ROW`;case`following`:return`${e.value} FOLLOWING`;case`unbounded_following`:return`UNBOUNDED FOLLOWING`}}};export{t as i,n,e as r,r as t};
1
+ function e(e,t){switch(t){case`pg`:case`sqlite`:return`"${e.replaceAll(`"`,`""`)}"`;case`mysql`:return`\`${e.replaceAll("`","``")}\``;case`mssql`:return`[${e.replaceAll(`]`,`]]`)}]`}}function t(t,n,r){return r?`${e(r,n)}.${e(t,n)}`:e(t,n)}function n(e,t){switch(t){case`pg`:return`$${e+1}`;case`mysql`:case`sqlite`:return`?`;case`mssql`:return`@p${e}`}}var r=class{params=[];dialect;constructor(e){this.dialect=e}print(e){return this.params=[],{sql:this.printNode(e),params:[...this.params]}}printNode(e){switch(e.type){case`select`:return this.printSelect(e);case`insert`:return this.printInsert(e);case`update`:return this.printUpdate(e);case`delete`:return this.printDelete(e);case`merge`:return this.printMerge(e);default:return this.printExpression(e)}}printSelect(e){let t=[];e.ctes.length>0&&t.push(this.printCTEs(e.ctes)),t.push(`SELECT`),e.distinct&&t.push(`DISTINCT`),e.columns.length===0?t.push(`*`):t.push(e.columns.map(e=>this.printExpression(e)).join(`, `)),e.from&&(t.push(`FROM`),e.from.type===`subquery`?t.push(this.printSubquery(e.from)):t.push(this.printTableRef(e.from)));for(let n of e.joins)t.push(this.printJoin(n));return e.where&&t.push(`WHERE`,this.printExpression(e.where)),e.groupBy.length>0&&t.push(`GROUP BY`,e.groupBy.map(e=>this.printExpression(e)).join(`, `)),e.having&&t.push(`HAVING`,this.printExpression(e.having)),e.orderBy.length>0&&t.push(`ORDER BY`,e.orderBy.map(e=>this.printOrderBy(e)).join(`, `)),e.limit&&t.push(`LIMIT`,this.printExpression(e.limit)),e.offset&&t.push(`OFFSET`,this.printExpression(e.offset)),e.setOp&&t.push(e.setOp.op,this.printSelect(e.setOp.query)),e.forUpdate&&t.push(`FOR UPDATE`),t.join(` `)}printInsert(t){let n=[];t.ctes.length>0&&n.push(this.printCTEs(t.ctes)),n.push(`INSERT INTO`,this.printTableRef(t.table)),t.columns.length>0&&n.push(`(${t.columns.map(t=>e(t,this.dialect)).join(`, `)})`),n.push(`VALUES`);let r=t.values.map(e=>`(${e.map(e=>this.printExpression(e)).join(`, `)})`);return n.push(r.join(`, `)),t.onConflict&&n.push(this.printOnConflict(t.onConflict)),t.returning.length>0&&n.push(`RETURNING`,t.returning.map(e=>this.printExpression(e)).join(`, `)),n.join(` `)}printOnConflict(t){let n=[`ON CONFLICT`];if(t.columns.length>0&&n.push(`(${t.columns.map(t=>e(t,this.dialect)).join(`, `)})`),t.action===`nothing`)n.push(`DO NOTHING`);else{n.push(`DO UPDATE SET`);let r=t.action.set.map(t=>`${e(t.column,this.dialect)} = ${this.printExpression(t.value)}`);n.push(r.join(`, `))}return t.where&&n.push(`WHERE`,this.printExpression(t.where)),n.join(` `)}printUpdate(t){let n=[];t.ctes.length>0&&n.push(this.printCTEs(t.ctes)),n.push(`UPDATE`,this.printTableRef(t.table),`SET`);let r=t.set.map(t=>`${e(t.column,this.dialect)} = ${this.printExpression(t.value)}`);return n.push(r.join(`, `)),t.from&&n.push(`FROM`,this.printTableRef(t.from)),t.where&&n.push(`WHERE`,this.printExpression(t.where)),t.returning.length>0&&n.push(`RETURNING`,t.returning.map(e=>this.printExpression(e)).join(`, `)),n.join(` `)}printDelete(e){let t=[];return e.ctes.length>0&&t.push(this.printCTEs(e.ctes)),t.push(`DELETE FROM`,this.printTableRef(e.table)),e.where&&t.push(`WHERE`,this.printExpression(e.where)),e.returning.length>0&&t.push(`RETURNING`,e.returning.map(e=>this.printExpression(e)).join(`, `)),t.join(` `)}printExpression(e){switch(e.type){case`column_ref`:return this.printColumnRef(e);case`literal`:return this.printLiteral(e);case`binary_op`:return this.printBinaryOp(e);case`unary_op`:return this.printUnaryOp(e);case`function_call`:return this.printFunctionCall(e);case`param`:return this.printParam(e);case`raw`:return this.printRaw(e);case`subquery`:return this.printSubquery(e);case`between`:return this.printBetween(e);case`in`:return this.printIn(e);case`is_null`:return this.printIsNull(e);case`cast`:return this.printCast(e);case`exists`:return this.printExists(e);case`star`:return this.printStar(e);case`case`:return this.printCase(e);case`json_access`:return this.printJsonAccess(e);case`array_expr`:return this.printArrayExpr(e);case`window_function`:return this.printWindowFunction(e);case`aliased_expr`:return this.printAliasedExpr(e);case`full_text_search`:return this.printFullTextSearch(e)}}printColumnRef(t){let n=t.table?`${e(t.table,this.dialect)}.${e(t.column,this.dialect)}`:e(t.column,this.dialect);return t.alias&&(n+=` AS ${e(t.alias,this.dialect)}`),n}printLiteral(e){return e.value===null?`NULL`:typeof e.value==`boolean`?e.value?`TRUE`:`FALSE`:typeof e.value==`number`?String(e.value):`'${String(e.value).replaceAll(`'`,`''`)}'`}printBinaryOp(e){return`(${this.printExpression(e.left)} ${e.op} ${this.printExpression(e.right)})`}printUnaryOp(e){return e.position===`postfix`?`(${this.printExpression(e.operand)} ${e.op})`:`(${e.op} ${this.printExpression(e.operand)})`}printFunctionCall(t){let n=`${t.name}(${t.args.map(e=>this.printExpression(e)).join(`, `)})`;return t.alias&&(n+=` AS ${e(t.alias,this.dialect)}`),n}printParam(e){return this.params.push(e.value),n(this.params.length-1,this.dialect)}printRaw(e){return this.params.push(...e.params),e.sql}printSubquery(t){let n=`(${this.printSelect(t.query)})`;return t.alias&&(n+=` AS ${e(t.alias,this.dialect)}`),n}printBetween(e){let t=e.negated?`NOT `:``;return`(${this.printExpression(e.expr)} ${t}BETWEEN ${this.printExpression(e.low)} AND ${this.printExpression(e.high)})`}printIn(e){let t=e.negated?`NOT `:``;return Array.isArray(e.values)?`(${this.printExpression(e.expr)} ${t}IN (${e.values.map(e=>this.printExpression(e)).join(`, `)}))`:`(${this.printExpression(e.expr)} ${t}IN (${this.printSelect(e.values)}))`}printIsNull(e){let t=e.negated?` NOT`:``;return`(${this.printExpression(e.expr)} IS${t} NULL)`}printCase(e){let t=[`CASE`];e.operand&&t.push(this.printExpression(e.operand));for(let n of e.whens)t.push(`WHEN`,this.printExpression(n.condition),`THEN`,this.printExpression(n.result));return e.else_&&t.push(`ELSE`,this.printExpression(e.else_)),t.push(`END`),t.join(` `)}printCast(e){return`CAST(${this.printExpression(e.expr)} AS ${e.dataType})`}printExists(e){return`(${e.negated?`NOT `:``}EXISTS (${this.printSelect(e.query)}))`}printStar(t){return t.table?`${e(t.table,this.dialect)}.*`:`*`}printTableRef(n){let r=t(n.name,this.dialect,n.schema);return n.temporal&&(r+=` ${this.printTemporalClause(n.temporal)}`),n.alias&&(r+=` AS ${e(n.alias,this.dialect)}`),r}printTemporalClause(e){switch(e.kind){case`as_of`:return`FOR SYSTEM_TIME AS OF ${this.printExpression(e.timestamp)}`;case`from_to`:return`FOR SYSTEM_TIME FROM ${this.printExpression(e.start)} TO ${this.printExpression(e.end)}`;case`between`:return`FOR SYSTEM_TIME BETWEEN ${this.printExpression(e.start)} AND ${this.printExpression(e.end)}`;case`contained_in`:return`FOR SYSTEM_TIME CONTAINED IN (${this.printExpression(e.start)}, ${this.printExpression(e.end)})`;case`all`:return`FOR SYSTEM_TIME ALL`}}printJoin(e){let t=[];return t.push(`${e.joinType} JOIN`),e.table.type===`subquery`?t.push(this.printSubquery(e.table)):t.push(this.printTableRef(e.table)),e.on&&t.push(`ON`,this.printExpression(e.on)),t.join(` `)}printOrderBy(e){let t=`${this.printExpression(e.expr)} ${e.direction}`;return e.nulls&&(t+=` NULLS ${e.nulls}`),t}printCTEs(t){return`${t.some(e=>e.recursive)?`WITH RECURSIVE`:`WITH`} ${t.map(t=>`${e(t.name,this.dialect)} AS (${this.printSelect(t.query)})`).join(`, `)}`}printJsonAccess(t){let n=`${this.printExpression(t.expr)}${t.operator}${this.printLiteral({type:`literal`,value:t.path})}`;return t.alias&&(n+=` AS ${e(t.alias,this.dialect)}`),n}printArrayExpr(e){return`ARRAY[${e.elements.map(e=>this.printExpression(e)).join(`, `)}]`}printWindowFunction(t){let n=[];n.push(this.printFunctionCall(t.fn)),n.push(`OVER`);let r=[];return t.partitionBy.length>0&&r.push(`PARTITION BY ${t.partitionBy.map(e=>this.printExpression(e)).join(`, `)}`),t.orderBy.length>0&&r.push(`ORDER BY ${t.orderBy.map(e=>this.printOrderBy(e)).join(`, `)}`),t.frame&&r.push(this.printFrameSpec(t.frame)),n.push(`(${r.join(` `)})`),t.alias&&n.push(`AS`,e(t.alias,this.dialect)),n.join(` `)}printFrameSpec(e){let t=this.printFrameBound(e.start);return e.end?`${e.kind} BETWEEN ${t} AND ${this.printFrameBound(e.end)}`:`${e.kind} ${t}`}printFrameBound(e){switch(e.type){case`unbounded_preceding`:return`UNBOUNDED PRECEDING`;case`preceding`:return`${e.value} PRECEDING`;case`current_row`:return`CURRENT ROW`;case`following`:return`${e.value} FOLLOWING`;case`unbounded_following`:return`UNBOUNDED FOLLOWING`}}printMerge(t){let n=[];t.ctes.length>0&&n.push(this.printCTEs(t.ctes)),n.push(`MERGE INTO`,this.printTableRef(t.target)),n.push(`USING`),t.source.type===`subquery`?n.push(this.printSubquery(t.source)):n.push(this.printTableRef(t.source)),n.push(`AS`,e(t.sourceAlias,this.dialect)),n.push(`ON`,this.printExpression(t.on));for(let e of t.whens)e.type===`matched`?n.push(this.printMergeWhenMatched(e)):n.push(this.printMergeWhenNotMatched(e));return n.join(` `)}printMergeWhenMatched(t){let n=[`WHEN MATCHED`];if(t.condition&&n.push(`AND`,this.printExpression(t.condition)),t.action===`delete`)n.push(`THEN DELETE`);else{n.push(`THEN UPDATE SET`);let r=(t.set??[]).map(t=>`${e(t.column,this.dialect)} = ${this.printExpression(t.value)}`);n.push(r.join(`, `))}return n.join(` `)}printMergeWhenNotMatched(t){let n=[`WHEN NOT MATCHED`];return t.condition&&n.push(`AND`,this.printExpression(t.condition)),n.push(`THEN INSERT`),n.push(`(${t.columns.map(t=>e(t,this.dialect)).join(`, `)})`),n.push(`VALUES (${t.values.map(e=>this.printExpression(e)).join(`, `)})`),n.join(` `)}printAliasedExpr(t){return`${this.printExpression(t.expr)} AS ${e(t.alias,this.dialect)}`}printFullTextSearch(t){let n=t.columns.map(e=>this.printExpression(e)).join(` || ' ' || `),r=t.language?`'${t.language}', `:``,i=`(to_tsvector(${r}${n}) @@ to_tsquery(${r}${this.printExpression(t.query)}))`;return t.alias&&(i+=` AS ${e(t.alias,this.dialect)}`),i}};export{t as i,n,e as r,r as t};