@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/ast.d.ts +15 -5
- package/dist/dialect.d.ts +15 -0
- package/dist/dialects/postgres-migration.d.ts +2 -0
- package/dist/dialects/sqlite-json-equality.d.ts +15 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +216 -46
- package/dist/index.js.map +20 -17
- package/dist/parameter-snapshot.d.ts +2 -1
- package/dist/query/index.js +5 -3
- package/dist/query/index.js.map +4 -4
- package/dist/result-decoder.d.ts +5 -0
- package/dist/sql-handles.d.ts +7 -0
- package/dist/timestamp-identity.d.ts +40 -0
- package/package.json +2 -2
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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()) ?
|
|
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.
|
|
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}
|
|
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
|
|
3281
|
-
base = `(${guard} AND ${
|
|
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}
|
|
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
|
-
|
|
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
|
-
|
|
4421
|
-
|
|
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=
|
|
4824
|
+
//# debugId=71705578A45D011664756E2164756E21
|