sumak 0.0.4 → 0.0.6
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 +409 -5
- package/dist/ast/expression.d.mts +26 -0
- package/dist/ast/expression.mjs +140 -0
- package/dist/ast/nodes.d.mts +309 -0
- package/dist/ast/nodes.mjs +59 -0
- package/dist/ast/transformer.d.mts +10 -0
- package/dist/ast/transformer.mjs +140 -0
- package/dist/ast/typed-expression.d.mts +37 -0
- package/dist/ast/typed-expression.mjs +77 -0
- package/dist/ast/visitor.d.mts +13 -0
- package/dist/ast/visitor.mjs +11 -0
- package/dist/builder/delete.d.mts +20 -0
- package/dist/builder/delete.mjs +113 -0
- package/dist/builder/eb.d.mts +312 -0
- package/dist/builder/eb.mjs +641 -0
- package/dist/builder/expression.d.mts +5 -0
- package/dist/builder/expression.mjs +10 -0
- package/dist/builder/insert.d.mts +40 -0
- package/dist/builder/insert.mjs +146 -0
- package/dist/builder/merge.d.mts +20 -0
- package/dist/builder/merge.mjs +100 -0
- package/dist/builder/raw.d.mts +2 -0
- package/dist/builder/raw.mjs +4 -0
- package/dist/builder/select.d.mts +41 -0
- package/dist/builder/select.mjs +280 -0
- package/dist/builder/typed-delete.d.mts +49 -0
- package/dist/builder/typed-delete.mjs +89 -0
- package/dist/builder/typed-insert.d.mts +86 -0
- package/dist/builder/typed-insert.mjs +159 -0
- package/dist/builder/typed-merge.d.mts +31 -0
- package/dist/builder/typed-merge.mjs +93 -0
- package/dist/builder/typed-select.d.mts +164 -0
- package/dist/builder/typed-select.mjs +309 -0
- package/dist/builder/typed-update.d.mts +59 -0
- package/dist/builder/typed-update.mjs +110 -0
- package/dist/builder/update.d.mts +20 -0
- package/dist/builder/update.mjs +121 -0
- package/dist/dialect/mssql.d.mts +2 -0
- package/dist/dialect/mssql.mjs +9 -0
- package/dist/dialect/mysql.d.mts +2 -0
- package/dist/dialect/mysql.mjs +9 -0
- package/dist/dialect/pg.d.mts +2 -0
- package/dist/dialect/pg.mjs +9 -0
- package/dist/dialect/sqlite.d.mts +2 -0
- package/dist/dialect/sqlite.mjs +9 -0
- package/dist/dialect/types.d.mts +6 -0
- package/dist/dialect/types.mjs +1 -0
- package/dist/errors.d.mts +12 -0
- package/dist/errors.mjs +24 -0
- package/dist/index.d.mts +50 -806
- package/dist/index.mjs +48 -3
- package/dist/mssql.d.mts +2 -2
- package/dist/mssql.mjs +2 -1
- package/dist/mysql.d.mts +2 -2
- package/dist/mysql.mjs +2 -1
- package/dist/pg.d.mts +2 -2
- package/dist/pg.mjs +2 -1
- package/dist/plugin/camel-case.d.mts +11 -0
- package/dist/plugin/camel-case.mjs +16 -0
- package/dist/plugin/hooks.d.mts +72 -0
- package/dist/plugin/hooks.mjs +49 -0
- package/dist/plugin/plugin-manager.d.mts +17 -0
- package/dist/plugin/plugin-manager.mjs +37 -0
- package/dist/plugin/soft-delete.d.mts +27 -0
- package/dist/plugin/soft-delete.mjs +52 -0
- package/dist/plugin/types.d.mts +19 -0
- package/dist/plugin/types.mjs +1 -0
- package/dist/plugin/with-schema.d.mts +21 -0
- package/dist/plugin/with-schema.mjs +53 -0
- package/dist/printer/base.d.mts +49 -0
- package/dist/printer/base.mjs +472 -0
- package/dist/printer/document.d.mts +45 -0
- package/dist/printer/document.mjs +153 -0
- package/dist/printer/formatter.d.mts +5 -0
- package/dist/printer/formatter.mjs +134 -0
- package/dist/printer/mssql.d.mts +10 -0
- package/dist/printer/mssql.mjs +161 -0
- package/dist/printer/mysql.d.mts +8 -0
- package/dist/printer/mysql.mjs +41 -0
- package/dist/printer/pg.d.mts +6 -0
- package/dist/printer/pg.mjs +9 -0
- package/dist/printer/sqlite.d.mts +8 -0
- package/dist/printer/sqlite.mjs +29 -0
- package/dist/printer/types.d.mts +11 -0
- package/dist/printer/types.mjs +1 -0
- package/dist/schema/column.d.mts +52 -0
- package/dist/schema/column.mjs +120 -0
- package/dist/schema/index.d.mts +6 -0
- package/dist/schema/index.mjs +4 -0
- package/dist/schema/table.d.mts +37 -0
- package/dist/schema/table.mjs +7 -0
- package/dist/schema/type-utils.d.mts +46 -0
- package/dist/schema/type-utils.mjs +1 -0
- package/dist/schema/types.d.mts +64 -0
- package/dist/schema/types.mjs +1 -0
- package/dist/schema.d.mts +2 -2
- package/dist/schema.mjs +1 -1
- package/dist/sqlite.d.mts +2 -2
- package/dist/sqlite.mjs +2 -1
- package/dist/sumak.d.mts +110 -0
- package/dist/sumak.mjs +141 -0
- package/dist/types.d.mts +14 -0
- package/dist/types.mjs +1 -0
- package/dist/utils/identifier.d.mts +3 -0
- package/dist/utils/identifier.mjs +14 -0
- package/dist/utils/param.d.mts +2 -0
- package/dist/utils/param.mjs +8 -0
- package/package.json +1 -1
- package/dist/_chunks/base.mjs +0 -1
- package/dist/_chunks/errors.mjs +0 -1
- package/dist/_chunks/index.d.mts +0 -136
- package/dist/_chunks/mssql.d.mts +0 -11
- package/dist/_chunks/mssql.mjs +0 -1
- package/dist/_chunks/mysql.d.mts +0 -9
- package/dist/_chunks/mysql.mjs +0 -1
- package/dist/_chunks/pg.d.mts +0 -7
- package/dist/_chunks/pg.mjs +0 -1
- package/dist/_chunks/schema.mjs +0 -1
- package/dist/_chunks/sqlite.d.mts +0 -9
- package/dist/_chunks/sqlite.mjs +0 -1
- package/dist/_chunks/types.d.mts +0 -338
package/README.md
CHANGED
|
@@ -150,6 +150,19 @@ db.selectFrom("users").crossJoin("posts").compile(db.printer())
|
|
|
150
150
|
.where(({ name }) =>
|
|
151
151
|
name.like("%ali%"),
|
|
152
152
|
)
|
|
153
|
+
|
|
154
|
+
.where(({ name }) =>
|
|
155
|
+
name.notLike("%bob%"),
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
// Case-insensitive (PG)
|
|
159
|
+
.where(({ name }) =>
|
|
160
|
+
name.ilike("%alice%"),
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
.where(({ email }) =>
|
|
164
|
+
email.notIlike("%spam%"),
|
|
165
|
+
)
|
|
153
166
|
```
|
|
154
167
|
|
|
155
168
|
### Range & List
|
|
@@ -159,6 +172,15 @@ db.selectFrom("users").crossJoin("posts").compile(db.printer())
|
|
|
159
172
|
age.between(18, 65),
|
|
160
173
|
)
|
|
161
174
|
|
|
175
|
+
.where(({ age }) =>
|
|
176
|
+
age.notBetween(18, 65),
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
// Order-independent (PG)
|
|
180
|
+
.where(({ age }) =>
|
|
181
|
+
age.betweenSymmetric(65, 18),
|
|
182
|
+
)
|
|
183
|
+
|
|
162
184
|
.where(({ id }) =>
|
|
163
185
|
id.in([1, 2, 3]),
|
|
164
186
|
)
|
|
@@ -205,13 +227,30 @@ db.selectFrom("users").crossJoin("posts").compile(db.printer())
|
|
|
205
227
|
)
|
|
206
228
|
```
|
|
207
229
|
|
|
230
|
+
### Null-Safe Comparisons
|
|
231
|
+
|
|
232
|
+
```ts
|
|
233
|
+
// IS DISTINCT FROM — null-safe inequality
|
|
234
|
+
.where(({ age }) =>
|
|
235
|
+
age.isDistinctFrom(null),
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
// IS NOT DISTINCT FROM — null-safe equality
|
|
239
|
+
.where(({ age }) =>
|
|
240
|
+
age.isNotDistinctFrom(25),
|
|
241
|
+
)
|
|
242
|
+
```
|
|
243
|
+
|
|
208
244
|
### Aggregates
|
|
209
245
|
|
|
210
246
|
```ts
|
|
211
|
-
import { count, sum, avg, min, max, coalesce } from "sumak"
|
|
247
|
+
import { count, countDistinct, sumDistinct, avgDistinct, sum, avg, min, max, coalesce } from "sumak"
|
|
212
248
|
|
|
213
249
|
db.selectFrom("users").selectExpr(count(), "total").compile(db.printer())
|
|
214
250
|
|
|
251
|
+
db.selectFrom("users").selectExpr(countDistinct(col.dept), "uniqueDepts").compile(db.printer())
|
|
252
|
+
// SELECT COUNT(DISTINCT "dept") AS "uniqueDepts" FROM "users"
|
|
253
|
+
|
|
215
254
|
db.selectFrom("orders").selectExpr(sum(col.amount), "totalAmount").compile(db.printer())
|
|
216
255
|
|
|
217
256
|
db.selectFrom("orders").selectExpr(avg(col.amount), "avgAmount").compile(db.printer())
|
|
@@ -219,6 +258,50 @@ db.selectFrom("orders").selectExpr(avg(col.amount), "avgAmount").compile(db.prin
|
|
|
219
258
|
db.selectFrom("orders")
|
|
220
259
|
.selectExpr(coalesce(col.discount, val(0)), "safeDiscount")
|
|
221
260
|
.compile(db.printer())
|
|
261
|
+
|
|
262
|
+
// SUM(DISTINCT), AVG(DISTINCT)
|
|
263
|
+
db.selectFrom("orders").selectExpr(sumDistinct(col.amount), "uniqueSum").compile(db.printer())
|
|
264
|
+
db.selectFrom("orders").selectExpr(avgDistinct(col.amount), "uniqueAvg").compile(db.printer())
|
|
265
|
+
|
|
266
|
+
// COALESCE with multiple fallbacks
|
|
267
|
+
db.selectFrom("users")
|
|
268
|
+
.selectExpr(coalesce(col.nick, col.name, val("Anonymous")), "displayName")
|
|
269
|
+
.compile(db.printer())
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### String & JSON Aggregates
|
|
273
|
+
|
|
274
|
+
```ts
|
|
275
|
+
import { stringAgg, arrayAgg, jsonAgg, jsonBuildObject } from "sumak"
|
|
276
|
+
|
|
277
|
+
// STRING_AGG with ORDER BY
|
|
278
|
+
db.selectFrom("users")
|
|
279
|
+
.selectExpr(stringAgg(col.name, ", ", [{ expr: col.name, direction: "ASC" }]), "names")
|
|
280
|
+
.compile(db.printer())
|
|
281
|
+
// STRING_AGG("name", ', ' ORDER BY "name" ASC)
|
|
282
|
+
|
|
283
|
+
// ARRAY_AGG
|
|
284
|
+
db.selectFrom("users").selectExpr(arrayAgg(col.id), "ids").compile(db.printer())
|
|
285
|
+
|
|
286
|
+
// JSON_AGG / JSON_BUILD_OBJECT
|
|
287
|
+
db.selectFrom("users").selectExpr(jsonAgg(col.name), "namesJson").compile(db.printer())
|
|
288
|
+
|
|
289
|
+
db.selectFrom("users")
|
|
290
|
+
.selectExpr(jsonBuildObject(["name", col.name], ["age", col.age]), "obj")
|
|
291
|
+
.compile(db.printer())
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Arithmetic
|
|
295
|
+
|
|
296
|
+
```ts
|
|
297
|
+
import { add, sub, mul, div, mod, neg } from "sumak"
|
|
298
|
+
|
|
299
|
+
db.selectFrom("orders").selectExpr(mul(col.price, col.qty), "total").compile(db.printer())
|
|
300
|
+
// ("price" * "qty") AS "total"
|
|
301
|
+
|
|
302
|
+
db.selectFrom("orders")
|
|
303
|
+
.selectExpr(add(col.price, val(10)), "adjusted")
|
|
304
|
+
.compile(db.printer())
|
|
222
305
|
```
|
|
223
306
|
|
|
224
307
|
### EXISTS / NOT EXISTS
|
|
@@ -292,6 +375,122 @@ db.selectFrom("users")
|
|
|
292
375
|
.compile(db.printer())
|
|
293
376
|
```
|
|
294
377
|
|
|
378
|
+
## Window Functions
|
|
379
|
+
|
|
380
|
+
```ts
|
|
381
|
+
import { over, rowNumber, rank, denseRank, lag, lead, ntile, count, sum } from "sumak"
|
|
382
|
+
|
|
383
|
+
// ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC)
|
|
384
|
+
db.selectFrom("employees")
|
|
385
|
+
.selectExpr(
|
|
386
|
+
over(rowNumber(), (w) => w.partitionBy("dept").orderBy("salary", "DESC")),
|
|
387
|
+
"rn",
|
|
388
|
+
)
|
|
389
|
+
.compile(db.printer())
|
|
390
|
+
|
|
391
|
+
// RANK() OVER (ORDER BY score DESC)
|
|
392
|
+
db.selectFrom("students")
|
|
393
|
+
.selectExpr(
|
|
394
|
+
over(rank(), (w) => w.orderBy("score", "DESC")),
|
|
395
|
+
"rnk",
|
|
396
|
+
)
|
|
397
|
+
.compile(db.printer())
|
|
398
|
+
|
|
399
|
+
// Running total with frame
|
|
400
|
+
db.selectFrom("orders")
|
|
401
|
+
.selectExpr(
|
|
402
|
+
over(sum(col.amount), (w) =>
|
|
403
|
+
w
|
|
404
|
+
.partitionBy("userId")
|
|
405
|
+
.orderBy("createdAt")
|
|
406
|
+
.rows({ type: "unbounded_preceding" }, { type: "current_row" }),
|
|
407
|
+
),
|
|
408
|
+
"runningTotal",
|
|
409
|
+
)
|
|
410
|
+
.compile(db.printer())
|
|
411
|
+
|
|
412
|
+
// LAG / LEAD
|
|
413
|
+
db.selectFrom("prices")
|
|
414
|
+
.selectExpr(
|
|
415
|
+
over(lag(col.price, 1), (w) => w.orderBy("date")),
|
|
416
|
+
"prevPrice",
|
|
417
|
+
)
|
|
418
|
+
.compile(db.printer())
|
|
419
|
+
|
|
420
|
+
// NTILE(4)
|
|
421
|
+
db.selectFrom("employees")
|
|
422
|
+
.selectExpr(
|
|
423
|
+
over(ntile(4), (w) => w.orderBy("salary", "DESC")),
|
|
424
|
+
"quartile",
|
|
425
|
+
)
|
|
426
|
+
.compile(db.printer())
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
## SQL Functions
|
|
430
|
+
|
|
431
|
+
### String Functions
|
|
432
|
+
|
|
433
|
+
```ts
|
|
434
|
+
import { upper, lower, concat, substring, trim, length } from "sumak"
|
|
435
|
+
|
|
436
|
+
db.selectFrom("users").selectExpr(upper(col.name), "upperName").compile(db.printer())
|
|
437
|
+
// SELECT UPPER("name") AS "upperName" FROM "users"
|
|
438
|
+
|
|
439
|
+
db.selectFrom("users").selectExpr(lower(col.email), "lowerEmail").compile(db.printer())
|
|
440
|
+
|
|
441
|
+
db.selectFrom("users")
|
|
442
|
+
.selectExpr(concat(col.firstName, val(" "), col.lastName), "fullName")
|
|
443
|
+
.compile(db.printer())
|
|
444
|
+
|
|
445
|
+
db.selectFrom("users")
|
|
446
|
+
.selectExpr(substring(col.name, 1, 3), "prefix")
|
|
447
|
+
.compile(db.printer())
|
|
448
|
+
|
|
449
|
+
db.selectFrom("users").selectExpr(trim(col.name), "trimmed").compile(db.printer())
|
|
450
|
+
|
|
451
|
+
db.selectFrom("users").selectExpr(length(col.name), "nameLen").compile(db.printer())
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
### Numeric Functions
|
|
455
|
+
|
|
456
|
+
```ts
|
|
457
|
+
import { abs, round, ceil, floor } from "sumak"
|
|
458
|
+
|
|
459
|
+
db.selectFrom("orders").selectExpr(abs(col.balance), "absBalance").compile(db.printer())
|
|
460
|
+
|
|
461
|
+
db.selectFrom("orders").selectExpr(round(col.price, 2), "rounded").compile(db.printer())
|
|
462
|
+
|
|
463
|
+
db.selectFrom("orders").selectExpr(ceil(col.amount), "ceiling").compile(db.printer())
|
|
464
|
+
|
|
465
|
+
db.selectFrom("orders").selectExpr(floor(col.amount), "floored").compile(db.printer())
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
### Conditional Functions
|
|
469
|
+
|
|
470
|
+
```ts
|
|
471
|
+
import { nullif, greatest, least } from "sumak"
|
|
472
|
+
|
|
473
|
+
db.selectFrom("users")
|
|
474
|
+
.selectExpr(nullif(col.age, val(0)), "ageOrNull")
|
|
475
|
+
.compile(db.printer())
|
|
476
|
+
|
|
477
|
+
db.selectFrom("products")
|
|
478
|
+
.selectExpr(greatest(col.price, col.minPrice), "effectivePrice")
|
|
479
|
+
.compile(db.printer())
|
|
480
|
+
|
|
481
|
+
db.selectFrom("products")
|
|
482
|
+
.selectExpr(least(col.price, col.maxPrice), "cappedPrice")
|
|
483
|
+
.compile(db.printer())
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
### Date/Time Functions
|
|
487
|
+
|
|
488
|
+
```ts
|
|
489
|
+
import { now, currentTimestamp } from "sumak"
|
|
490
|
+
|
|
491
|
+
db.selectFrom("users").selectExpr(now(), "currentTime").compile(db.printer())
|
|
492
|
+
```
|
|
493
|
+
|
|
295
494
|
## Set Operations
|
|
296
495
|
|
|
297
496
|
```ts
|
|
@@ -311,11 +510,13 @@ active.union(premium).compile(db.printer())
|
|
|
311
510
|
// UNION ALL
|
|
312
511
|
active.unionAll(premium).compile(db.printer())
|
|
313
512
|
|
|
314
|
-
// INTERSECT
|
|
513
|
+
// INTERSECT / INTERSECT ALL
|
|
315
514
|
active.intersect(premium).compile(db.printer())
|
|
515
|
+
active.intersectAll(premium).compile(db.printer())
|
|
316
516
|
|
|
317
|
-
// EXCEPT
|
|
517
|
+
// EXCEPT / EXCEPT ALL
|
|
318
518
|
active.except(premium).compile(db.printer())
|
|
519
|
+
active.exceptAll(premium).compile(db.printer())
|
|
319
520
|
```
|
|
320
521
|
|
|
321
522
|
## CTEs (WITH)
|
|
@@ -362,20 +563,95 @@ db.update("users")
|
|
|
362
563
|
// UPDATE "users" SET "name" = $1 FROM "posts" WHERE ("id" = $2)
|
|
363
564
|
```
|
|
364
565
|
|
|
566
|
+
## Conditional Query Building
|
|
567
|
+
|
|
568
|
+
```ts
|
|
569
|
+
const withFilter = true
|
|
570
|
+
const withOrder = false
|
|
571
|
+
|
|
572
|
+
db.selectFrom("users")
|
|
573
|
+
.select("id", "name")
|
|
574
|
+
.$if(withFilter, (qb) => qb.where(({ age }) => age.gt(18)))
|
|
575
|
+
.$if(withOrder, (qb) => qb.orderBy("name"))
|
|
576
|
+
.compile(db.printer())
|
|
577
|
+
// WHERE applied, ORDER BY skipped
|
|
578
|
+
|
|
579
|
+
// Multiple .where() calls are AND'd together
|
|
580
|
+
db.selectFrom("users")
|
|
581
|
+
.select("id")
|
|
582
|
+
.where(({ age }) => age.gt(18))
|
|
583
|
+
.where(({ active }) => active.eq(true))
|
|
584
|
+
.compile(db.printer())
|
|
585
|
+
// WHERE ("age" > $1) AND ("active" = $2)
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
## Reusable Query Fragments
|
|
589
|
+
|
|
590
|
+
```ts
|
|
591
|
+
// $call — pipe builder through a function
|
|
592
|
+
const withPagination = (qb) => qb.limit(10).offset(20)
|
|
593
|
+
const withActiveFilter = (qb) => qb.where(({ active }) => active.eq(true))
|
|
594
|
+
|
|
595
|
+
db.selectFrom("users")
|
|
596
|
+
.select("id", "name")
|
|
597
|
+
.$call(withActiveFilter)
|
|
598
|
+
.$call(withPagination)
|
|
599
|
+
.compile(db.printer())
|
|
600
|
+
|
|
601
|
+
// selectExprs — multiple aliased expressions at once
|
|
602
|
+
db.selectFrom("users")
|
|
603
|
+
.selectExprs({
|
|
604
|
+
total: count(),
|
|
605
|
+
greeting: val("hello"),
|
|
606
|
+
})
|
|
607
|
+
.compile(db.printer())
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
## INSERT Advanced
|
|
611
|
+
|
|
612
|
+
```ts
|
|
613
|
+
// INSERT ... SELECT
|
|
614
|
+
const selectQuery = db.selectFrom("users").select("name", "age").build()
|
|
615
|
+
db.insertInto("archive").fromSelect(selectQuery).compile(db.printer())
|
|
616
|
+
|
|
617
|
+
// INSERT ... DEFAULT VALUES
|
|
618
|
+
db.insertInto("users").defaultValues().compile(db.printer())
|
|
619
|
+
|
|
620
|
+
// SQLite: INSERT OR IGNORE / INSERT OR REPLACE
|
|
621
|
+
db.insertInto("users").values({ name: "Alice" }).orIgnore().compile(db.printer())
|
|
622
|
+
// INSERT OR IGNORE INTO "users" ...
|
|
623
|
+
|
|
624
|
+
db.insertInto("users").values({ name: "Alice" }).orReplace().compile(db.printer())
|
|
625
|
+
// INSERT OR REPLACE INTO "users" ...
|
|
626
|
+
```
|
|
627
|
+
|
|
365
628
|
## ON CONFLICT
|
|
366
629
|
|
|
367
630
|
```ts
|
|
368
|
-
// DO NOTHING
|
|
631
|
+
// DO NOTHING (by columns)
|
|
369
632
|
db.insertInto("users")
|
|
370
633
|
.values({ name: "Alice", email: "a@b.com" })
|
|
371
634
|
.onConflictDoNothing("email")
|
|
372
635
|
.compile(db.printer())
|
|
373
636
|
|
|
374
|
-
// DO UPDATE
|
|
637
|
+
// DO UPDATE (by columns)
|
|
375
638
|
db.insertInto("users")
|
|
376
639
|
.values({ name: "Alice", email: "a@b.com" })
|
|
377
640
|
.onConflictDoUpdate(["email"], [{ column: "name", value: val("Alice") }])
|
|
378
641
|
.compile(db.printer())
|
|
642
|
+
|
|
643
|
+
// DO NOTHING (by constraint name)
|
|
644
|
+
db.insertInto("users")
|
|
645
|
+
.values({ name: "Alice", email: "a@b.com" })
|
|
646
|
+
.onConflictConstraintDoNothing("users_email_key")
|
|
647
|
+
.compile(db.printer())
|
|
648
|
+
// ON CONFLICT ON CONSTRAINT "users_email_key" DO NOTHING
|
|
649
|
+
|
|
650
|
+
// MySQL: ON DUPLICATE KEY UPDATE
|
|
651
|
+
db.insertInto("users")
|
|
652
|
+
.values({ name: "Alice" })
|
|
653
|
+
.onDuplicateKeyUpdate([{ column: "name", value: val("Alice") }])
|
|
654
|
+
.compile(db.printer())
|
|
379
655
|
```
|
|
380
656
|
|
|
381
657
|
## MERGE (SQL:2003)
|
|
@@ -395,6 +671,134 @@ db.mergeInto("users", "staging", "s", ({ target, source }) => target.id.eqCol(so
|
|
|
395
671
|
.compile(db.printer())
|
|
396
672
|
```
|
|
397
673
|
|
|
674
|
+
## Row Locking
|
|
675
|
+
|
|
676
|
+
```ts
|
|
677
|
+
// FOR UPDATE
|
|
678
|
+
db.selectFrom("users").select("id").forUpdate().compile(db.printer())
|
|
679
|
+
|
|
680
|
+
// FOR SHARE
|
|
681
|
+
db.selectFrom("users").select("id").forShare().compile(db.printer())
|
|
682
|
+
|
|
683
|
+
// FOR NO KEY UPDATE / FOR KEY SHARE (PG)
|
|
684
|
+
db.selectFrom("users").select("id").forNoKeyUpdate().compile(db.printer())
|
|
685
|
+
db.selectFrom("users").select("id").forKeyShare().compile(db.printer())
|
|
686
|
+
|
|
687
|
+
// SKIP LOCKED / NOWAIT
|
|
688
|
+
db.selectFrom("users").select("id").forUpdate().skipLocked().compile(db.printer())
|
|
689
|
+
db.selectFrom("users").select("id").forUpdate().noWait().compile(db.printer())
|
|
690
|
+
```
|
|
691
|
+
|
|
692
|
+
## DISTINCT ON (PG)
|
|
693
|
+
|
|
694
|
+
```ts
|
|
695
|
+
db.selectFrom("users")
|
|
696
|
+
.selectAll()
|
|
697
|
+
.distinctOn("dept")
|
|
698
|
+
.orderBy("dept")
|
|
699
|
+
.orderBy("salary", "DESC")
|
|
700
|
+
.compile(db.printer())
|
|
701
|
+
// SELECT DISTINCT ON ("dept") * FROM "users" ORDER BY "dept" ASC, "salary" DESC
|
|
702
|
+
```
|
|
703
|
+
|
|
704
|
+
## DELETE USING / JOIN in UPDATE & DELETE
|
|
705
|
+
|
|
706
|
+
```ts
|
|
707
|
+
// PG: DELETE ... USING
|
|
708
|
+
db.deleteFrom("orders")
|
|
709
|
+
.using("users")
|
|
710
|
+
.where(eq(col("orders.user_id"), col("users.id")))
|
|
711
|
+
.compile(db.printer())
|
|
712
|
+
|
|
713
|
+
// MySQL: DELETE with JOIN
|
|
714
|
+
db.deleteFrom("orders")
|
|
715
|
+
.innerJoin("users", eq(col("user_id", "orders"), col("id", "users")))
|
|
716
|
+
.where(eq(col("name", "users"), lit("Alice")))
|
|
717
|
+
.compile(db.printer())
|
|
718
|
+
|
|
719
|
+
// MySQL: UPDATE with JOIN
|
|
720
|
+
db.update("orders")
|
|
721
|
+
.set({ total: 0 })
|
|
722
|
+
.innerJoin("users", eq(col("user_id", "orders"), col("id", "users")))
|
|
723
|
+
.compile(db.printer())
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
## Lateral JOIN
|
|
727
|
+
|
|
728
|
+
```ts
|
|
729
|
+
// INNER JOIN LATERAL — correlated subquery join
|
|
730
|
+
const recentPosts = db
|
|
731
|
+
.selectFrom("posts")
|
|
732
|
+
.select("id", "title")
|
|
733
|
+
.where(({ userId }) => userId.eq(1))
|
|
734
|
+
.limit(3)
|
|
735
|
+
|
|
736
|
+
db.selectFrom("users").innerJoinLateral(recentPosts, "rp", onExpr).compile(db.printer())
|
|
737
|
+
// SELECT * FROM "users" INNER JOIN LATERAL (SELECT ...) AS "rp" ON ...
|
|
738
|
+
|
|
739
|
+
// LEFT JOIN LATERAL
|
|
740
|
+
db.selectFrom("users").leftJoinLateral(recentPosts, "rp", onExpr).compile(db.printer())
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
## Tuple Comparisons
|
|
744
|
+
|
|
745
|
+
```ts
|
|
746
|
+
import { tuple, val } from "sumak"
|
|
747
|
+
|
|
748
|
+
// Row-value comparison: (id, age) = (1, 25)
|
|
749
|
+
db.selectFrom("users")
|
|
750
|
+
.selectExpr(tuple(val(1), val(2), val(3)), "triple")
|
|
751
|
+
.compile(db.printer())
|
|
752
|
+
// (1, 2, 3)
|
|
753
|
+
```
|
|
754
|
+
|
|
755
|
+
## SQL Template Literal
|
|
756
|
+
|
|
757
|
+
```ts
|
|
758
|
+
import { sql, val } from "sumak"
|
|
759
|
+
|
|
760
|
+
// Tagged template with auto-parameterization
|
|
761
|
+
sql`SELECT * FROM users WHERE name = ${"Alice"}`
|
|
762
|
+
// params: ["Alice"]
|
|
763
|
+
|
|
764
|
+
// Inline Expression values
|
|
765
|
+
sql`SELECT * FROM users WHERE active = ${val(true)}`
|
|
766
|
+
// → SELECT * FROM users WHERE active = TRUE
|
|
767
|
+
|
|
768
|
+
// Helpers
|
|
769
|
+
sql`SELECT ${sql.ref("id")} FROM ${sql.table("users", "public")}`
|
|
770
|
+
// → SELECT "id" FROM "public"."users"
|
|
771
|
+
|
|
772
|
+
// Use in selectExpr
|
|
773
|
+
db.selectFrom("users")
|
|
774
|
+
.selectExpr(sql`CURRENT_DATE`, "today")
|
|
775
|
+
.compile(db.printer())
|
|
776
|
+
```
|
|
777
|
+
|
|
778
|
+
## Aggregate FILTER (WHERE)
|
|
779
|
+
|
|
780
|
+
```ts
|
|
781
|
+
import { filter, count, sum } from "sumak"
|
|
782
|
+
|
|
783
|
+
// COUNT(*) FILTER (WHERE active = true)
|
|
784
|
+
db.selectFrom("users").selectExpr(filter(count(), activeExpr), "activeCount").compile(db.printer())
|
|
785
|
+
```
|
|
786
|
+
|
|
787
|
+
## EXPLAIN
|
|
788
|
+
|
|
789
|
+
```ts
|
|
790
|
+
// EXPLAIN
|
|
791
|
+
db.selectFrom("users").select("id").explain().compile(db.printer())
|
|
792
|
+
// EXPLAIN SELECT "id" FROM "users"
|
|
793
|
+
|
|
794
|
+
// EXPLAIN ANALYZE
|
|
795
|
+
db.selectFrom("users").select("id").explain({ analyze: true }).compile(db.printer())
|
|
796
|
+
|
|
797
|
+
// EXPLAIN with format
|
|
798
|
+
db.selectFrom("users").select("id").explain({ format: "JSON" }).compile(db.printer())
|
|
799
|
+
// EXPLAIN (FORMAT JSON) SELECT "id" FROM "users"
|
|
800
|
+
```
|
|
801
|
+
|
|
398
802
|
## Full-Text Search
|
|
399
803
|
|
|
400
804
|
Dialect-aware FTS — same API, different SQL per dialect:
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { BetweenNode, BinaryOpNode, CastNode, ColumnRefNode, ExistsNode, ExpressionNode, FunctionCallNode, InNode, IsNullNode, LiteralNode, ParamNode, RawNode, SelectNode, StarNode, SubqueryNode, UnaryOpNode } from "./nodes.mjs";
|
|
2
|
+
export declare function col(column: string, table?: string): ColumnRefNode;
|
|
3
|
+
export declare function colAs(column: string, alias: string, table?: string): ColumnRefNode;
|
|
4
|
+
export declare function lit(value: string | number | boolean | null): LiteralNode;
|
|
5
|
+
export declare function star(table?: string): StarNode;
|
|
6
|
+
export declare function param(index: number, value: unknown): ParamNode;
|
|
7
|
+
export declare function raw(sql: string, params?: unknown[]): RawNode;
|
|
8
|
+
export declare function subquery(query: SelectNode, alias?: string): SubqueryNode;
|
|
9
|
+
export declare function fn(name: string, args: ExpressionNode[], alias?: string): FunctionCallNode;
|
|
10
|
+
export declare function binOp(op: string, left: ExpressionNode, right: ExpressionNode): BinaryOpNode;
|
|
11
|
+
export declare function unaryOp(op: string, operand: ExpressionNode, position?: "prefix" | "postfix"): UnaryOpNode;
|
|
12
|
+
export declare function and(left: ExpressionNode, right: ExpressionNode): BinaryOpNode;
|
|
13
|
+
export declare function or(left: ExpressionNode, right: ExpressionNode): BinaryOpNode;
|
|
14
|
+
export declare function eq(left: ExpressionNode, right: ExpressionNode): BinaryOpNode;
|
|
15
|
+
export declare function neq(left: ExpressionNode, right: ExpressionNode): BinaryOpNode;
|
|
16
|
+
export declare function gt(left: ExpressionNode, right: ExpressionNode): BinaryOpNode;
|
|
17
|
+
export declare function gte(left: ExpressionNode, right: ExpressionNode): BinaryOpNode;
|
|
18
|
+
export declare function lt(left: ExpressionNode, right: ExpressionNode): BinaryOpNode;
|
|
19
|
+
export declare function lte(left: ExpressionNode, right: ExpressionNode): BinaryOpNode;
|
|
20
|
+
export declare function like(expr: ExpressionNode, pattern: ExpressionNode): BinaryOpNode;
|
|
21
|
+
export declare function between(expr: ExpressionNode, low: ExpressionNode, high: ExpressionNode, negated?: boolean): BetweenNode;
|
|
22
|
+
export declare function inList(expr: ExpressionNode, values: ExpressionNode[] | SelectNode, negated?: boolean): InNode;
|
|
23
|
+
export declare function isNull(expr: ExpressionNode, negated?: boolean): IsNullNode;
|
|
24
|
+
export declare function cast(expr: ExpressionNode, dataType: string): CastNode;
|
|
25
|
+
export declare function exists(query: SelectNode, negated?: boolean): ExistsNode;
|
|
26
|
+
export declare function not(operand: ExpressionNode): UnaryOpNode;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
export function col(column, table) {
|
|
2
|
+
return {
|
|
3
|
+
type: "column_ref",
|
|
4
|
+
column,
|
|
5
|
+
table
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
export function colAs(column, alias, table) {
|
|
9
|
+
return {
|
|
10
|
+
type: "column_ref",
|
|
11
|
+
column,
|
|
12
|
+
table,
|
|
13
|
+
alias
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export function lit(value) {
|
|
17
|
+
return {
|
|
18
|
+
type: "literal",
|
|
19
|
+
value
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export function star(table) {
|
|
23
|
+
return {
|
|
24
|
+
type: "star",
|
|
25
|
+
table
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export function param(index, value) {
|
|
29
|
+
return {
|
|
30
|
+
type: "param",
|
|
31
|
+
index,
|
|
32
|
+
value
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export function raw(sql, params = []) {
|
|
36
|
+
return {
|
|
37
|
+
type: "raw",
|
|
38
|
+
sql,
|
|
39
|
+
params
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export function subquery(query, alias) {
|
|
43
|
+
return {
|
|
44
|
+
type: "subquery",
|
|
45
|
+
query,
|
|
46
|
+
alias
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
export function fn(name, args, alias) {
|
|
50
|
+
return {
|
|
51
|
+
type: "function_call",
|
|
52
|
+
name,
|
|
53
|
+
args,
|
|
54
|
+
alias
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export function binOp(op, left, right) {
|
|
58
|
+
return {
|
|
59
|
+
type: "binary_op",
|
|
60
|
+
op,
|
|
61
|
+
left,
|
|
62
|
+
right
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
export function unaryOp(op, operand, position = "prefix") {
|
|
66
|
+
return {
|
|
67
|
+
type: "unary_op",
|
|
68
|
+
op,
|
|
69
|
+
operand,
|
|
70
|
+
position
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
export function and(left, right) {
|
|
74
|
+
return binOp("AND", left, right);
|
|
75
|
+
}
|
|
76
|
+
export function or(left, right) {
|
|
77
|
+
return binOp("OR", left, right);
|
|
78
|
+
}
|
|
79
|
+
export function eq(left, right) {
|
|
80
|
+
return binOp("=", left, right);
|
|
81
|
+
}
|
|
82
|
+
export function neq(left, right) {
|
|
83
|
+
return binOp("!=", left, right);
|
|
84
|
+
}
|
|
85
|
+
export function gt(left, right) {
|
|
86
|
+
return binOp(">", left, right);
|
|
87
|
+
}
|
|
88
|
+
export function gte(left, right) {
|
|
89
|
+
return binOp(">=", left, right);
|
|
90
|
+
}
|
|
91
|
+
export function lt(left, right) {
|
|
92
|
+
return binOp("<", left, right);
|
|
93
|
+
}
|
|
94
|
+
export function lte(left, right) {
|
|
95
|
+
return binOp("<=", left, right);
|
|
96
|
+
}
|
|
97
|
+
export function like(expr, pattern) {
|
|
98
|
+
return binOp("LIKE", expr, pattern);
|
|
99
|
+
}
|
|
100
|
+
export function between(expr, low, high, negated = false) {
|
|
101
|
+
return {
|
|
102
|
+
type: "between",
|
|
103
|
+
expr,
|
|
104
|
+
low,
|
|
105
|
+
high,
|
|
106
|
+
negated
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
export function inList(expr, values, negated = false) {
|
|
110
|
+
return {
|
|
111
|
+
type: "in",
|
|
112
|
+
expr,
|
|
113
|
+
values,
|
|
114
|
+
negated
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
export function isNull(expr, negated = false) {
|
|
118
|
+
return {
|
|
119
|
+
type: "is_null",
|
|
120
|
+
expr,
|
|
121
|
+
negated
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
export function cast(expr, dataType) {
|
|
125
|
+
return {
|
|
126
|
+
type: "cast",
|
|
127
|
+
expr,
|
|
128
|
+
dataType
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
export function exists(query, negated = false) {
|
|
132
|
+
return {
|
|
133
|
+
type: "exists",
|
|
134
|
+
query,
|
|
135
|
+
negated
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
export function not(operand) {
|
|
139
|
+
return unaryOp("NOT", operand);
|
|
140
|
+
}
|