@vibeorm/sql 4.0.0 → 4.2.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/dist/index.js CHANGED
@@ -1,4 +1,76 @@
1
1
  // @bun
2
+ // packages/sql/src/timestamp-identity.ts
3
+ var identities = new WeakMap;
4
+ function rememberTimestampIdentity(params) {
5
+ const { date, text } = params;
6
+ const fraction = /\.(\d+)/.exec(text)?.[1] ?? "";
7
+ const remainder = fraction.slice(3).replace(/0+$/, "");
8
+ const canonical = Date.prototype.toISOString.call(date).replace(/(\.\d{3})Z$/, `$1${remainder}Z`);
9
+ identities.set(date, {
10
+ milliseconds: Date.prototype.getTime.call(date),
11
+ text,
12
+ ...params.numeric === undefined ? {} : { numeric: params.numeric },
13
+ key: params.key ?? `date:${canonical}`,
14
+ kind: params.kind ?? "timestamp"
15
+ });
16
+ return date;
17
+ }
18
+ function identityOf(params) {
19
+ const stored = identities.get(params.date);
20
+ if (stored !== undefined && stored.milliseconds !== Date.prototype.getTime.call(params.date)) {
21
+ identities.delete(params.date);
22
+ return;
23
+ }
24
+ return stored;
25
+ }
26
+ function timestampIdentityKey(params) {
27
+ return identityOf(params)?.key ?? `date:${Number.isFinite(Date.prototype.getTime.call(params.date)) ? Date.prototype.toISOString.call(params.date) : "invalid"}`;
28
+ }
29
+ function timestampIdentityText(params) {
30
+ const identity = identityOf(params);
31
+ return identity !== undefined && (params.kind === undefined || identity.kind === params.kind) ? identity.text : undefined;
32
+ }
33
+ function sqliteTimestampIdentityValue(params) {
34
+ const identity = identityOf(params);
35
+ return identity?.kind === "sqlite" ? identity.numeric ?? identity.text : undefined;
36
+ }
37
+ function compareTimestampDates(params) {
38
+ const left = identityOf({ date: params.left });
39
+ const right = identityOf({ date: params.right });
40
+ if (timestampIdentityKey({ date: params.left }) === timestampIdentityKey({ date: params.right }))
41
+ return 0;
42
+ const rank = (a, b) => a < b ? -1 : a > b ? 1 : 0;
43
+ if (left?.kind === "sqlite" || right?.kind === "sqlite") {
44
+ return left?.numeric !== undefined && right?.numeric !== undefined ? rank(left.numeric, right.numeric) : null;
45
+ }
46
+ const clock = (identity) => {
47
+ const [, hours, minutes, seconds, offset] = identity.key.split(":");
48
+ const [whole, fraction = ""] = seconds.split(".");
49
+ const local = BigInt(Number(hours) * 3600 + Number(minutes) * 60 + Number(whole)) * 1000000000n + BigInt(fraction.padEnd(9, "0"));
50
+ const zone = Number(offset);
51
+ return [local - BigInt(zone) * 1000000n, -zone];
52
+ };
53
+ if (left?.kind !== undefined && left.kind !== "timestamp" || right?.kind !== undefined && right.kind !== "timestamp") {
54
+ if (left === undefined || right === undefined || left.kind !== right.kind)
55
+ return null;
56
+ const a = clock(left);
57
+ const b = clock(right);
58
+ return rank(a[0], b[0]) || rank(a[1], b[1]);
59
+ }
60
+ const milliseconds = rank(Date.prototype.getTime.call(params.left), Date.prototype.getTime.call(params.right));
61
+ if (milliseconds !== 0)
62
+ return milliseconds;
63
+ const a = /\.(\d+)/.exec(left?.text ?? "")?.[1]?.slice(3) ?? "";
64
+ const b = /\.(\d+)/.exec(right?.text ?? "")?.[1]?.slice(3) ?? "";
65
+ return rank(a.padEnd(Math.max(a.length, b.length), "0"), b.padEnd(Math.max(a.length, b.length), "0"));
66
+ }
67
+ function cloneTimestampDate(params) {
68
+ const date = new Date(Date.prototype.getTime.call(params.date));
69
+ const identity = identityOf(params);
70
+ if (identity !== undefined)
71
+ identities.set(date, identity);
72
+ return date;
73
+ }
2
74
  // packages/sql/src/native-enum-parameter.ts
3
75
  var NATIVE_ENUM_ARRAY_PARAMETER = Symbol("vibeorm.nativeEnumArrayParameter");
4
76
  function nativeEnumArrayParameter(params) {
@@ -234,24 +306,24 @@ function parseDateTimeText(params) {
234
306
  if (ZONE_LESS_DATETIME.test(trimmed)) {
235
307
  const asUtc = new Date(`${trimmed.replace(" ", "T").replace(/(\.\d{3})\d+$/, "$1")}Z`);
236
308
  if (!Number.isNaN(asUtc.getTime()))
237
- return asUtc;
309
+ return rememberTimestampIdentity({ date: asUtc, text: trimmed });
238
310
  }
239
311
  const zoned = PG_ZONED_DATETIME.exec(trimmed);
240
312
  if (zoned !== null) {
241
313
  const [, date, time, fraction, hours, minutes] = zoned;
242
314
  const iso = new Date(`${date}T${time}${(fraction ?? "").slice(0, 4)}${hours}:${minutes ?? "00"}`);
243
315
  if (!Number.isNaN(iso.getTime()))
244
- return iso;
316
+ return rememberTimestampIdentity({ date: iso, text: trimmed });
245
317
  }
246
318
  const parsed = new Date(trimmed);
247
- return Number.isNaN(parsed.getTime()) ? undefined : parsed;
319
+ return Number.isNaN(parsed.getTime()) ? undefined : rememberTimestampIdentity({ date: parsed, text: trimmed });
248
320
  }
249
321
  var dateNativeCodec = {
250
322
  encode: (value) => {
251
323
  if (value instanceof Date && !Number.isFinite(value.getTime()) || typeof value === "number" && !Number.isFinite(value) || typeof value === "string" && /^[+-]?infinity$/i.test(value.trim())) {
252
324
  throw new VibeError({ code: "VIBE_VALIDATION", message: "DateTime parameters require finite timestamps; use an extended timestamp encoder for infinity" });
253
325
  }
254
- return value;
326
+ return value instanceof Date ? timestampIdentityText({ date: value }) ?? value : value;
255
327
  },
256
328
  decode: (value) => {
257
329
  const date = value instanceof Date ? value : typeof value === "string" ? parseDateTimeText({ text: value }) : typeof value === "number" ? new Date(value) : undefined;
@@ -265,16 +337,27 @@ var dateTextCodec = {
265
337
  dateNativeCodec.encode(value);
266
338
  const date = value instanceof Date ? value : typeof value === "string" ? parseDateTimeText({ text: value }) : typeof value === "number" ? new Date(value) : undefined;
267
339
  if (date !== undefined && Number.isFinite(date.getTime()))
268
- return date.toISOString();
340
+ return value instanceof Date ? sqliteTimestampIdentityValue({ date: value }) ?? date.toISOString() : date.toISOString();
269
341
  throw new VibeError({ code: "VIBE_VALIDATION", message: "DateTime parameters require a valid finite timestamp" });
270
342
  },
271
- decode: dateNativeCodec.decode
343
+ decode: (value) => {
344
+ const date = dateNativeCodec.decode(value);
345
+ if (typeof value === "number")
346
+ return rememberTimestampIdentity({ date, text: date.toISOString(), numeric: value, key: `epoch:${value}`, kind: "sqlite" });
347
+ return typeof value === "string" ? rememberTimestampIdentity({ date, text: value, key: `date:${value}`, kind: "sqlite" }) : date;
348
+ }
272
349
  };
273
350
  var mysqlDateCodec = {
274
351
  encode: (value) => {
275
352
  dateNativeCodec.encode(value);
276
- if (value instanceof Date)
353
+ if (value instanceof Date) {
354
+ const text = timestampIdentityText({ date: value, kind: "timestamp" });
355
+ if (text !== undefined) {
356
+ const fraction = /\.(\d+)/.exec(text)?.[1] ?? "";
357
+ return value.toISOString().replace("T", " ").replace(/\.\d{3}Z$/, `.${fraction.padEnd(3, "0")}`);
358
+ }
277
359
  return value;
360
+ }
278
361
  if (typeof value === "number") {
279
362
  const parsed = new Date(value);
280
363
  if (!Number.isNaN(parsed.getTime()))
@@ -301,18 +384,26 @@ function makeTimeOfDayCodec(params) {
301
384
  if (date === undefined || !Number.isFinite(date.getTime())) {
302
385
  throw new VibeError({ code: "VIBE_VALIDATION", message: "a time-of-day DateTime parameter requires a valid finite Date", meta: { value } });
303
386
  }
304
- return `${timeOfDayText(utcMillisecondOfDay(date))}${suffix}`;
387
+ return timestampIdentityText({ date, kind: params.zoned ? "timetz" : "time" }) ?? `${timeOfDayText(utcMillisecondOfDay(date))}${suffix}`;
305
388
  },
306
389
  decode: (value) => {
307
- if (value instanceof Date && Number.isFinite(value.getTime()))
308
- return new Date(utcMillisecondOfDay(value));
390
+ if (value instanceof Date && Number.isFinite(value.getTime())) {
391
+ if (timestampIdentityText({ date: value, kind: params.zoned ? "timetz" : "time" }) !== undefined)
392
+ return value;
393
+ const milliseconds = utcMillisecondOfDay(value);
394
+ const text = timeOfDayText(milliseconds);
395
+ return rememberTimestampIdentity({ date: new Date(milliseconds), text: `${text}${suffix}`, key: `${params.zoned ? "timetz" : "time"}:${text}:0`, kind: params.zoned ? "timetz" : "time" });
396
+ }
309
397
  const match = typeof value === "string" ? TIME_OF_DAY_TEXT.exec(value.trim()) : null;
310
398
  if (match !== null) {
311
399
  const [, hours, minutes, seconds, fraction, sign, offsetHours, offsetMinutes, offsetSeconds] = match;
312
400
  const millisecond = Number(hours) * 3600000 + Number(minutes) * 60000 + Number(seconds ?? 0) * 1000 + Number((fraction ?? "").slice(0, 3).padEnd(3, "0"));
313
401
  const offset = sign === undefined ? 0 : (sign === "-" ? -1 : 1) * (Number(offsetHours) * 3600000 + Number(offsetMinutes ?? 0) * 60000 + Number(offsetSeconds ?? 0) * 1000);
314
402
  if (millisecond <= MILLISECONDS_PER_DAY) {
315
- return new Date(((millisecond - offset) % MILLISECONDS_PER_DAY + MILLISECONDS_PER_DAY) % MILLISECONDS_PER_DAY);
403
+ const date = new Date(((millisecond - offset) % MILLISECONDS_PER_DAY + MILLISECONDS_PER_DAY) % MILLISECONDS_PER_DAY);
404
+ const exactFraction = (fraction ?? "").padEnd(3, "0").replace(/(?<=\d{3})0+$/, "");
405
+ const key = `${params.zoned ? "timetz" : "time"}:${hours}:${minutes}:${seconds ?? "00"}.${exactFraction}:${offset}`;
406
+ return rememberTimestampIdentity({ date, text: value.trim(), key, kind: params.zoned ? "timetz" : "time" });
316
407
  }
317
408
  }
318
409
  throw new VibeError({ code: "VIBE_DATA_INVALID", message: "a time-of-day DateTime column returned a value that is not a time of day", meta: { value } });
@@ -435,12 +526,27 @@ var DIALECT_CODECS = {
435
526
 
436
527
  // packages/sql/src/result-decoder.ts
437
528
  var scalarContracts = new WeakMap;
529
+ var decoderIdentities = new WeakMap;
530
+ var nullableDecoders = new WeakMap;
531
+ var arrayDecoders = new WeakMap;
532
+ function sameResultDecoder(params) {
533
+ if (params.left === params.right)
534
+ return true;
535
+ const identity = decoderIdentities.get(params.left);
536
+ return identity !== undefined && identity === decoderIdentities.get(params.right);
537
+ }
538
+ function wrappedIdentity(params) {
539
+ const identity = decoderIdentities.get(params.decoder);
540
+ return identity === undefined ? undefined : JSON.stringify([params.kind, identity]);
541
+ }
438
542
  function resultScalarContract(params) {
439
543
  return scalarContracts.get(params.decoder);
440
544
  }
441
545
  function registerScalar(params) {
442
546
  if (params.contract !== undefined)
443
547
  scalarContracts.set(params.decoder, Object.freeze(params.contract));
548
+ if (params.identity !== undefined)
549
+ decoderIdentities.set(params.decoder, params.identity);
444
550
  return params.decoder;
445
551
  }
446
552
  function wrappedContract(params) {
@@ -473,7 +579,7 @@ function scalar(type, valid) {
473
579
  refuse(encoding, `Invalid ${type} projection value`);
474
580
  return result;
475
581
  };
476
- return registerScalar({ contract: { scalar: type, nullable: false, list: false }, decoder: Object.freeze({
582
+ return registerScalar({ identity: JSON.stringify(["scalar", type]), contract: { scalar: type, nullable: false, list: false }, decoder: Object.freeze({
477
583
  decode: (params) => convert(params, false),
478
584
  encode: (params) => {
479
585
  const value = type === "Json" ? params.value : convert(params, true);
@@ -485,7 +591,7 @@ function scalar(type, valid) {
485
591
  }
486
592
  var stringDecoder = scalar("String", (value) => typeof value === "string");
487
593
  var dateDecoder = scalar("DateTime", (value) => value instanceof Date && Number.isFinite(value.getTime()));
488
- var extendedDecoder = registerScalar({ contract: { scalar: "DateTime", nullable: false, list: false }, decoder: Object.freeze({
594
+ var extendedDecoder = registerScalar({ identity: "extended-date-time", contract: { scalar: "DateTime", nullable: false, list: false }, decoder: Object.freeze({
489
595
  decode: (params) => {
490
596
  if (params.value === "infinity" || params.value === Infinity)
491
597
  return "infinity";
@@ -506,15 +612,21 @@ function hasEncoder(decoder) {
506
612
  return "encode" in decoder && typeof decoder.encode === "function";
507
613
  }
508
614
  function nullable({ decoder }) {
615
+ const cached = nullableDecoders.get(decoder);
616
+ if (cached !== undefined)
617
+ return cached;
509
618
  const read = (params) => params.value === null ? null : decoder.decode(params);
510
- if (!hasEncoder(decoder))
511
- return Object.freeze({ decode: read });
512
- return registerScalar({ contract: wrappedContract({ decoder, nullable: true }), decoder: Object.freeze({
619
+ const wrapped = hasEncoder(decoder) ? registerScalar({ identity: wrappedIdentity({ decoder, kind: "nullable" }), contract: wrappedContract({ decoder, nullable: true }), decoder: Object.freeze({
513
620
  decode: read,
514
621
  encode: (params) => params.value === null ? null : decoder.encode(params)
515
- }) });
622
+ }) }) : Object.freeze({ decode: read });
623
+ nullableDecoders.set(decoder, wrapped);
624
+ return wrapped;
516
625
  }
517
626
  function array({ decoder }) {
627
+ const cached = arrayDecoders.get(decoder);
628
+ if (cached !== undefined)
629
+ return cached;
518
630
  const bidirectional = hasEncoder(decoder);
519
631
  const read = (params) => {
520
632
  const raw = params.value;
@@ -534,16 +646,16 @@ function array({ decoder }) {
534
646
  refuse(false, "Expected an array projection");
535
647
  return value.map((element) => decoder.decode({ ...params, value: element }));
536
648
  };
537
- if (!hasEncoder(decoder))
538
- return Object.freeze({ decode: read });
539
- return registerScalar({ contract: wrappedContract({ decoder, list: true }), decoder: Object.freeze({
649
+ const wrapped = hasEncoder(decoder) ? registerScalar({ identity: wrappedIdentity({ decoder, kind: "array" }), contract: wrappedContract({ decoder, list: true }), decoder: Object.freeze({
540
650
  decode: read,
541
651
  encode: (params) => {
542
652
  if (!Array.isArray(params.value))
543
653
  refuse(true, "Expected an array parameter");
544
654
  return params.value.map((value) => decoder.encode({ ...params, value }));
545
655
  }
546
- }) });
656
+ }) }) : Object.freeze({ decode: read });
657
+ arrayDecoders.set(decoder, wrapped);
658
+ return wrapped;
547
659
  }
548
660
  var encode = Object.freeze({
549
661
  array: ({ encoder }) => Object.freeze({
@@ -569,7 +681,7 @@ var decode = Object.freeze({
569
681
  array,
570
682
  enum: ({ values }) => {
571
683
  const members = new Set(values);
572
- return registerScalar({ contract: { scalar: "String", nullable: false, list: false }, decoder: Object.freeze({
684
+ return registerScalar({ identity: JSON.stringify(["enum", [...members].sort()]), contract: { scalar: "String", nullable: false, list: false }, decoder: Object.freeze({
573
685
  decode: ({ value }) => typeof value === "string" && members.has(value) ? value : refuse(false, "Unknown enum projection member"),
574
686
  encode: ({ value }) => typeof value === "string" && members.has(value) ? value : refuse(true, "Unknown enum parameter member")
575
687
  }) });
@@ -590,10 +702,23 @@ var decode = Object.freeze({
590
702
  }
591
703
  });
592
704
 
705
+ // packages/sql/src/sql-handles.ts
706
+ var checks = new Set;
707
+ function registerSqlHandleCheck(params) {
708
+ checks.add(params.check);
709
+ }
710
+ function isSqlHandle(value) {
711
+ for (const check of checks)
712
+ if (check(value))
713
+ return true;
714
+ return false;
715
+ }
716
+
593
717
  // packages/sql/src/sql-input.ts
594
718
  var inputs = new WeakMap;
595
719
  var tokens = new WeakMap;
596
720
  var scopes = new WeakMap;
721
+ registerSqlHandleCheck({ check: (value) => inputs.has(value) || tokens.has(value) || scopes.has(value) });
597
722
  function invalid(message) {
598
723
  throw new VibeError3({ code: "VIBE_VALIDATION", message });
599
724
  }
@@ -691,7 +816,7 @@ function snapshotSqlValue(value, ancestors = new Set, isHandle) {
691
816
  };
692
817
  if (isSqlParam(value))
693
818
  return invalid("SQL input tokens are valid only in admitted reusable builder positions");
694
- if (isHandle !== undefined && typeof value === "object" && value !== null && isHandle(value))
819
+ if (typeof value === "object" && value !== null && (isSqlHandle(value) || isHandle?.(value)))
695
820
  return invalid("SQL builder, plan, template, input and fragment handles are not execution values");
696
821
  if (value === undefined)
697
822
  return invalid("SQL values cannot be undefined; omit a defaulted column or use null");
@@ -700,7 +825,7 @@ function snapshotSqlValue(value, ancestors = new Set, isHandle) {
700
825
  if (typeof value === "number")
701
826
  return value;
702
827
  if (value instanceof Date)
703
- return Number.isFinite(value.getTime()) ? new Date(value.getTime()) : invalid("SQL dates must be finite");
828
+ return Number.isFinite(value.getTime()) ? cloneTimestampDate({ date: value }) : invalid("SQL dates must be finite");
704
829
  if (value instanceof Uint8Array)
705
830
  return new Uint8Array(value);
706
831
  if (typeof value !== "object")
@@ -1244,6 +1369,7 @@ var sqliteDialect = {
1244
1369
  rowLockSyntax: null,
1245
1370
  advisoryLockFunctions: null,
1246
1371
  rowDefaultKeyword: false,
1372
+ emptyInsert: { form: "defaultValues", conflictClause: false },
1247
1373
  statementChain: null,
1248
1374
  paramBudget: 900,
1249
1375
  booleanLiteral: { true: "1", false: "0" },
@@ -1407,6 +1533,7 @@ var SQLITE_SESSION_SQL = Object.freeze({
1407
1533
  // packages/sql/src/composable.ts
1408
1534
  import { VibeError as VibeError9 } from "@vibeorm/schema";
1409
1535
  var fragments = new WeakMap;
1536
+ registerSqlHandleCheck({ check: (value) => fragments.has(value) });
1410
1537
  function invalid3(message) {
1411
1538
  throw new VibeError9({ code: "VIBE_VALIDATION", message });
1412
1539
  }
@@ -1492,7 +1619,7 @@ function renderSqlFragment(params) {
1492
1619
  const encode = (part) => {
1493
1620
  if (part.value === undefined)
1494
1621
  invalid3("SQL parameters cannot be undefined; use null explicitly");
1495
- return snapshotSqlValue(part.decoder === undefined ? part.value : part.decoder.encode({ value: part.value, dialect: dialect.name }));
1622
+ return snapshotSqlValue(part.decoder === undefined ? part.value : part.decoder.encode({ value: snapshotSqlValue(part.value), dialect: dialect.name }));
1496
1623
  };
1497
1624
  const recording = isSqlBindingCollector({ collector });
1498
1625
  const recordValue = (part) => recordSqlBinding({
@@ -1580,6 +1707,7 @@ var queries = new WeakMap;
1580
1707
  var commands = new WeakMap;
1581
1708
  var reusableQueries = new WeakMap;
1582
1709
  var reusableCommands = new WeakMap;
1710
+ registerSqlHandleCheck({ check: (value) => queries.has(value) || commands.has(value) || reusableQueries.has(value) || reusableCommands.has(value) });
1583
1711
  function invalid4(message) {
1584
1712
  throw new VibeError10({ code: "VIBE_VALIDATION", message });
1585
1713
  }
@@ -2249,7 +2377,7 @@ function assertInt32(params) {
2249
2377
  var JSON_TRANSPORT = Object.freeze({ chunkSize: 100 });
2250
2378
  function projectionOutputName(params) {
2251
2379
  const { column } = params;
2252
- return column.transform === undefined || column.transform === "jsonText" ? column.alias ?? column.name : column.name;
2380
+ return column.alias ?? column.name;
2253
2381
  }
2254
2382
  // packages/sql/src/dialects/postgres.ts
2255
2383
  var postgresDialect = {
@@ -2306,6 +2434,7 @@ var postgresDialect = {
2306
2434
  keyCast: Object.freeze({ single: "::bigint", pair: "::int4" })
2307
2435
  },
2308
2436
  rowDefaultKeyword: true,
2437
+ emptyInsert: { form: "defaultValues", conflictClause: true },
2309
2438
  statementChain: "dataModifyingCte",
2310
2439
  paramBudget: 30000,
2311
2440
  booleanLiteral: { true: "true", false: "false" },
@@ -2368,6 +2497,7 @@ var mysqlDialect = {
2368
2497
  },
2369
2498
  advisoryLockFunctions: null,
2370
2499
  rowDefaultKeyword: true,
2500
+ emptyInsert: { form: "emptyRow" },
2371
2501
  statementChain: null,
2372
2502
  paramBudget: 1e4,
2373
2503
  booleanLiteral: { true: "1", false: "0" },
@@ -2398,6 +2528,20 @@ var SQL_DIALECTS = {
2398
2528
  function getSqlDialect(params) {
2399
2529
  return SQL_DIALECTS[params.dialect];
2400
2530
  }
2531
+ // packages/sql/src/dialects/sqlite-json-equality.ts
2532
+ function renderSqliteJsonEquality(params) {
2533
+ const q = (name) => params.dialect.quoteIdent({ name });
2534
+ const values = q("__vibe_json_values");
2535
+ const tree = q("__vibe_json_tree");
2536
+ const paths = q("__vibe_json_paths");
2537
+ const child = q("__vibe_json_child");
2538
+ const parent = q("__vibe_json_parent");
2539
+ const [side, id, parentId, key, type, atom, path] = ["side", "id", "parent", "key", "type", "atom", "path"].map(q);
2540
+ const left = `${values}.${q("left")}`;
2541
+ const right = `${values}.${q("right")}`;
2542
+ return `(SELECT CASE WHEN ${left} IS NULL THEN NULL ELSE NOT EXISTS (` + `WITH RECURSIVE ${tree} AS (` + `SELECT 1 AS ${side}, ${id}, ${parentId}, ${key}, ${type}, ${atom} FROM json_tree(CASE WHEN json_valid(${left}) THEN ${left} ELSE 'null' END) ` + `UNION ALL SELECT -1 AS ${side}, ${id}, ${parentId}, ${key}, ${type}, ${atom} FROM json_tree(${right})` + `), ${paths} (${side}, ${id}, ${path}, ${type}, ${atom}) AS (` + `SELECT ${side}, ${id}, json_array(), ${type}, ${atom} FROM ${tree} WHERE ${parentId} IS NULL ` + `UNION ALL SELECT ${child}.${side}, ${child}.${id}, json_insert(${parent}.${path}, '$[#]', ${child}.${key}), ${child}.${type}, ${child}.${atom} ` + `FROM ${tree} AS ${child} JOIN ${paths} AS ${parent} ON ${child}.${side} = ${parent}.${side} AND ${child}.${parentId} = ${parent}.${id}` + `) SELECT 1 FROM ${paths} GROUP BY ${path}, CASE WHEN ${type} IN ('integer', 'real') THEN 'number' ELSE ${type} END, ${atom} HAVING SUM(${side}) <> 0` + `) END FROM (SELECT ${params.left} AS ${q("left")}, ${params.right} AS ${q("right")}) AS ${values})`;
2543
+ }
2544
+
2401
2545
  // packages/sql/src/dialects/diagnostics.ts
2402
2546
  import { unsupportedCapability as unsupportedCapability12 } from "@vibeorm/schema";
2403
2547
  var QUERIES = Object.freeze({
@@ -2501,7 +2645,7 @@ function requireArg(params) {
2501
2645
  import { unsupportedCapability as unsupportedCapability15 } from "@vibeorm/schema";
2502
2646
 
2503
2647
  // packages/sql/src/dialects/postgres-rls.ts
2504
- import { RlsContextError, VibeError as VibeError14, assertValidSchemaIR, unsupportedCapability as unsupportedCapability14, validateRlsContextIR } from "@vibeorm/schema";
2648
+ import { RlsContextError, VibeError as VibeError14, assertNativeRlsAdmission, assertValidSchemaIR, unsupportedCapability as unsupportedCapability14, validateRlsContextIR } from "@vibeorm/schema";
2505
2649
  var NATIVE_RLS = Object.freeze({ policyName: "vibeorm_managed", tableSchema: "public", settingPrefix: "vibeorm.", parentAlias: "parent" });
2506
2650
  var COLUMN_TYPES = {
2507
2651
  String: { default: "text", native: { text: "text", varchar: "varchar", charactervarying: "varchar", uuid: "uuid" } },
@@ -2570,6 +2714,7 @@ function renderRlsColumnType(params) {
2570
2714
  }
2571
2715
  function protectedModel(params) {
2572
2716
  requireRls(params);
2717
+ assertNativeRlsAdmission({ schema: params.schema, dialect: params.dialect.name });
2573
2718
  assertValidSchemaIR({ schema: params.schema });
2574
2719
  const model = params.schema.models.find((entry) => entry.name === params.model);
2575
2720
  if (model === undefined || model.rls === undefined || model.rls === "unprotected") {
@@ -2948,15 +3093,18 @@ function quoteProjection(dialect, column) {
2948
3093
  const alias = dialect.quoteIdent({ name: column.alias ?? column.name });
2949
3094
  return `${dialect.jsonTextProjection({ expression: ref })} AS ${alias}`;
2950
3095
  }
2951
- if (dialect.name !== "postgres") {
2952
- throw internalError2({ message: "ColRef.transform is a postgres construct" });
2953
- }
2954
3096
  const ref = quoteCol(dialect, column);
2955
- const alias = dialect.quoteIdent({ name: column.name });
3097
+ const alias = dialect.quoteIdent({ name: column.alias ?? column.name });
2956
3098
  if (column.transform === "castText")
2957
- return `${ref}::text AS ${alias}`;
3099
+ return `${textProjection({ dialect, expression: ref })} AS ${alias}`;
3100
+ if (dialect.name !== "postgres") {
3101
+ throw internalError2({ message: "encodeBase64 is a postgres construct" });
3102
+ }
2958
3103
  return `encode(${ref}, 'base64') AS ${alias}`;
2959
3104
  }
3105
+ function textProjection(params) {
3106
+ return params.dialect.name === "postgres" ? `${params.expression}::text` : `CAST(${params.expression} AS ${params.dialect.name === "mysql" ? "CHAR" : "TEXT"})`;
3107
+ }
2960
3108
  function renderProjection(params) {
2961
3109
  const { dialect, columns } = params;
2962
3110
  return columns.map((column) => quoteProjection(dialect, column)).join(", ");
@@ -3277,8 +3425,8 @@ function renderJsonFilter(params) {
3277
3425
  if (value !== null) {
3278
3426
  const target = `json_extract(${column}, ${collector.add(path)})`;
3279
3427
  const bound = typeof value === "boolean" ? value ? 1 : 0 : value;
3280
- const operand = typeof value === "object" ? `json(${collector.add(JSON.stringify(value))})` : collector.add(bound);
3281
- base = `(${guard} AND ${target} = ${operand})`;
3428
+ const comparison = typeof value === "object" ? renderSqliteJsonEquality({ dialect, left: target, right: collector.add(JSON.stringify(value)) }) : `${target} = ${collector.add(bound)}`;
3429
+ base = `(${guard} AND ${comparison})`;
3282
3430
  }
3283
3431
  return expr.negate === true ? `NOT (${base})` : base;
3284
3432
  };
@@ -3443,11 +3591,8 @@ function applyReturning(params) {
3443
3591
  const name = dialect.quoteIdent({ name: column.name });
3444
3592
  return `${dialect.jsonTextProjection({ expression: name })} AS ${name}`;
3445
3593
  }
3446
- if (dialect.name !== "postgres") {
3447
- throw internalError2({ message: "a castText RETURNING column is a postgres construct" });
3448
- }
3449
3594
  const name = dialect.quoteIdent({ name: column.name });
3450
- return `${name}::text AS ${name}`;
3595
+ return `${textProjection({ dialect, expression: name })} AS ${name}`;
3451
3596
  }).join(", ");
3452
3597
  return { suffix: ` RETURNING ${columns}`, status: "native" };
3453
3598
  }
@@ -3484,6 +3629,10 @@ function renderKeyTableJoin(params) {
3484
3629
  switch (member.kind) {
3485
3630
  case "integer":
3486
3631
  return dialect.name === "mysql" ? `CAST(${placeholder} AS SIGNED)` : `CAST(${placeholder} AS bigint)`;
3632
+ case "timestamp":
3633
+ return `CAST(${placeholder} AS DATETIME(6))`;
3634
+ case "time":
3635
+ return `CAST(${placeholder} AS TIME(6))`;
3487
3636
  case "decimal":
3488
3637
  return `CAST(${placeholder} AS DECIMAL(65,${member.scale}))`;
3489
3638
  case "citext":
@@ -3513,7 +3662,9 @@ function renderSelect(params) {
3513
3662
  }
3514
3663
  if (statement.aggregates !== undefined) {
3515
3664
  for (const selection of statement.aggregates) {
3516
- columnList.push(`${renderAggRef(dialect, selection.agg)} AS ${dialect.quoteIdent({ name: selection.alias })}`);
3665
+ const expression = renderAggRef(dialect, selection.agg);
3666
+ const projected = selection.castText === true ? textProjection({ dialect, expression }) : expression;
3667
+ columnList.push(`${projected} AS ${dialect.quoteIdent({ name: selection.alias })}`);
3517
3668
  }
3518
3669
  }
3519
3670
  if (statement.countSubqueries !== undefined) {
@@ -3610,12 +3761,20 @@ function renderSelect(params) {
3610
3761
  }
3611
3762
  function renderInsert(params) {
3612
3763
  const { dialect, statement, collector } = params;
3613
- if (statement.columns.length === 0) {
3614
- throw internalError2({ message: "insert requires at least one column" });
3615
- }
3616
3764
  if (statement.rows.length === 0) {
3617
3765
  throw internalError2({ message: "insert requires at least one row" });
3618
3766
  }
3767
+ const defaultValues = statement.columns.length === 0 && dialect.emptyInsert.form === "defaultValues";
3768
+ if (defaultValues && statement.rows.length !== 1) {
3769
+ throw internalError2({ message: `${dialect.name} inserts an all-default row as DEFAULT VALUES, one row per statement` });
3770
+ }
3771
+ if (defaultValues && statement.onConflict !== undefined && !(dialect.emptyInsert.form === "defaultValues" && dialect.emptyInsert.conflictClause)) {
3772
+ throw unsupportedCapability16({
3773
+ dialect: dialect.name,
3774
+ feature: "a conflict clause on an all-default row (DEFAULT VALUES)",
3775
+ hint: "supply at least one column value, or drop the conflict clause"
3776
+ });
3777
+ }
3619
3778
  for (const row of statement.rows) {
3620
3779
  if (row.length !== statement.columns.length) {
3621
3780
  throw internalError2({
@@ -3642,7 +3801,7 @@ function renderInsert(params) {
3642
3801
  }
3643
3802
  return "DEFAULT";
3644
3803
  }).join(", ")})`).join(", ");
3645
- let text = `${insertKeyword} ${renderTable(dialect, statement.table)} (${columnList}) VALUES ${rowsSql}`;
3804
+ let text = defaultValues ? `${insertKeyword} ${renderTable(dialect, statement.table)} DEFAULT VALUES` : `${insertKeyword} ${renderTable(dialect, statement.table)} (${columnList}) VALUES ${rowsSql}`;
3646
3805
  if (onConflict !== undefined && !("doNothing" in onConflict) && dialect.excludedRowRef.form === "rowAlias" && Object.values(onConflict.update).some((value) => isExcludedRef(value) || isSqlFragment(value) && sqlUsesIncoming({ fragment: value }))) {
3647
3806
  text += ` AS ${dialect.quoteIdent({ name: dialect.excludedRowRef.alias })}`;
3648
3807
  }
@@ -4417,8 +4576,13 @@ function renderIndexStateQuery(params) {
4417
4576
  text: `SELECT c.relname::text AS "index",
4418
4577
  t.relname::text AS "table",
4419
4578
  am.amname::text AS "method",
4420
- i.indisunique AS "unique",
4421
- i.indisvalid AS "valid",
4579
+ i.indisunique AS "unique",
4580
+ i.indnullsnotdistinct AS "nullsNotDistinct",
4581
+ pg_catalog.array_to_json(ARRAY(SELECT pg_catalog.pg_get_indexdef(i.indexrelid, k.k, true)
4582
+ FROM pg_catalog.generate_series(1, i.indnkeyatts) AS k(k) ORDER BY k.k))::text AS "keyColumns",
4583
+ pg_catalog.array_to_json(ARRAY(SELECT pg_catalog.pg_get_indexdef(i.indexrelid, k.k, true)
4584
+ FROM pg_catalog.generate_series(i.indnkeyatts + 1, i.indnatts) AS k(k) ORDER BY k.k))::text AS "includedColumns",
4585
+ i.indisvalid AS "valid",
4422
4586
  i.indisready AS "ready",
4423
4587
  pg_catalog.pg_get_expr(i.indpred, i.indrelid) AS "predicate",
4424
4588
  pg_catalog.pg_get_indexdef(i.indexrelid) AS "definition",
@@ -4510,6 +4674,8 @@ export {
4510
4674
  SQL_DIALECTS,
4511
4675
  assertSqlParamBuilding,
4512
4676
  chainVetoToken,
4677
+ cloneTimestampDate,
4678
+ compareTimestampDates,
4513
4679
  compileExclusion,
4514
4680
  compileReusableSqlCommand,
4515
4681
  compileReusableSqlQuery,
@@ -4567,6 +4733,7 @@ export {
4567
4733
  projectionOutputName,
4568
4734
  recordSqlBinding,
4569
4735
  recordSqlBindingCheck,
4736
+ registerSqlHandleCheck,
4570
4737
  renderAdvisoryLock,
4571
4738
  renderCheckProbe,
4572
4739
  renderColumnStateQuery,
@@ -4632,6 +4799,7 @@ export {
4632
4799
  renderStatement,
4633
4800
  resultScalarContract,
4634
4801
  returningColumnName,
4802
+ sameResultDecoder,
4635
4803
  sealSqlInputScope,
4636
4804
  shortestFloat32,
4637
4805
  snapshotSqlValue,
@@ -4648,7 +4816,9 @@ export {
4648
4816
  strictMatchPattern,
4649
4817
  tableDefinitionParts,
4650
4818
  timeOfDayCodec,
4819
+ timestampIdentityKey,
4820
+ timestampIdentityText,
4651
4821
  toPgArrayLiteral
4652
4822
  };
4653
4823
 
4654
- //# debugId=B8D70B32D9F5E99064756E2164756E21
4824
+ //# debugId=71705578A45D011664756E2164756E21