@vibeorm/sql 2.0.2 → 2.1.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.
Files changed (41) hide show
  1. package/dist/ast.d.ts +54 -7
  2. package/dist/ast.d.ts.map +1 -1
  3. package/dist/codecs.d.ts +2 -3
  4. package/dist/codecs.d.ts.map +1 -1
  5. package/dist/composable.d.ts +70 -0
  6. package/dist/composable.d.ts.map +1 -0
  7. package/dist/dialect.d.ts +82 -6
  8. package/dist/dialect.d.ts.map +1 -1
  9. package/dist/dialects/constraint-owned-index.d.ts +9 -0
  10. package/dist/dialects/constraint-owned-index.d.ts.map +1 -0
  11. package/dist/dialects/migration-history.d.ts +8 -0
  12. package/dist/dialects/migration-history.d.ts.map +1 -0
  13. package/dist/dialects/migration-inventory.d.ts +24 -0
  14. package/dist/dialects/migration-inventory.d.ts.map +1 -0
  15. package/dist/dialects/migration-objects.d.ts +37 -0
  16. package/dist/dialects/migration-objects.d.ts.map +1 -0
  17. package/dist/dialects/migration-visibility.d.ts +8 -0
  18. package/dist/dialects/migration-visibility.d.ts.map +1 -0
  19. package/dist/dialects/mysql-check-probe.d.ts +16 -0
  20. package/dist/dialects/mysql-check-probe.d.ts.map +1 -0
  21. package/dist/dialects/mysql.d.ts.map +1 -1
  22. package/dist/dialects/postgres.d.ts.map +1 -1
  23. package/dist/dialects/sqlite-constraints.d.ts +16 -0
  24. package/dist/dialects/sqlite-constraints.d.ts.map +1 -0
  25. package/dist/dialects/sqlite-table-definition.d.ts +19 -0
  26. package/dist/dialects/sqlite-table-definition.d.ts.map +1 -0
  27. package/dist/dialects/sqlite.d.ts.map +1 -1
  28. package/dist/exclusion.d.ts +32 -0
  29. package/dist/exclusion.d.ts.map +1 -0
  30. package/dist/index.d.ts +21 -3
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +1544 -379
  33. package/dist/index.js.map +25 -12
  34. package/dist/locks.d.ts +57 -0
  35. package/dist/locks.d.ts.map +1 -0
  36. package/dist/predicate.d.ts +53 -0
  37. package/dist/predicate.d.ts.map +1 -0
  38. package/dist/render.d.ts.map +1 -1
  39. package/dist/result-decoder.d.ts +49 -0
  40. package/dist/result-decoder.d.ts.map +1 -0
  41. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -1,4 +1,878 @@
1
1
  // @bun
2
+ // packages/sql/src/composable.ts
3
+ import { VibeError } from "@vibeorm/schema";
4
+
5
+ // packages/sql/src/params.ts
6
+ class ParamCollector {
7
+ dialect;
8
+ values;
9
+ constructor(dialect) {
10
+ this.dialect = dialect;
11
+ this.values = [];
12
+ }
13
+ add(value) {
14
+ this.values.push(value);
15
+ return this.dialect.placeholder({ index: this.values.length });
16
+ }
17
+ addMany(values) {
18
+ return values.map((value) => this.add(value));
19
+ }
20
+ }
21
+
22
+ // packages/sql/src/composable.ts
23
+ var fragments = new WeakMap;
24
+ function invalid(message) {
25
+ throw new VibeError({ code: "VIBE_VALIDATION", message });
26
+ }
27
+ function construct(parts) {
28
+ const snapshot = Object.freeze([...parts]);
29
+ const fragment = Object.freeze(Object.defineProperty(Object.create(null), "toJSON", {
30
+ value: () => ({ parts: snapshot })
31
+ }));
32
+ fragments.set(fragment, snapshot);
33
+ return fragment;
34
+ }
35
+ function isSqlFragment(value) {
36
+ return typeof value === "object" && value !== null && fragments.has(value);
37
+ }
38
+ function partsOf(value) {
39
+ return fragments.get(value) ?? invalid("Expected a constructed SQL fragment");
40
+ }
41
+ function identifier(parts) {
42
+ if (!Array.isArray(parts) || parts.length === 0 || parts.some((part) => typeof part !== "string" || part.length === 0 || part.includes("\x00"))) {
43
+ invalid("SQL identifiers require non-empty, NUL-free name components");
44
+ }
45
+ }
46
+ function template(strings, ...values) {
47
+ if (!Array.isArray(strings) || !Array.isArray(strings.raw) || strings.length !== values.length + 1 || strings.some((text) => typeof text !== "string")) {
48
+ invalid("Use sql as a template tag");
49
+ }
50
+ const parts = [];
51
+ for (let index = 0;index < strings.length; index++) {
52
+ parts.push(strings[index]);
53
+ if (index < values.length) {
54
+ const value = values[index];
55
+ if (isSqlFragment(value))
56
+ parts.push(...partsOf(value));
57
+ else
58
+ parts.push(Object.freeze({ value }));
59
+ }
60
+ }
61
+ return construct(parts);
62
+ }
63
+ var sql = Object.freeze(Object.assign(template, {
64
+ text: ({ text }) => {
65
+ if (typeof text !== "string" || text.includes("\x00"))
66
+ invalid("SQL text must be a NUL-free string");
67
+ return construct([text]);
68
+ },
69
+ ident: ({ parts }) => {
70
+ identifier(parts);
71
+ return construct([Object.freeze({ ident: Object.freeze([...parts]) })]);
72
+ },
73
+ value: ({ value, decoder }) => construct([Object.freeze({ value, ...decoder === undefined ? {} : { decoder } })]),
74
+ join: ({ items, separator }) => {
75
+ if (!Array.isArray(items))
76
+ invalid("sql.join requires an array");
77
+ const between = separator === undefined ? [", "] : partsOf(separator);
78
+ const parts = [];
79
+ items.forEach((value, index) => {
80
+ if (index > 0)
81
+ parts.push(...between);
82
+ parts.push(...isSqlFragment(value) ? partsOf(value) : [Object.freeze({ value })]);
83
+ });
84
+ return construct(parts);
85
+ },
86
+ existing: ({ column }) => {
87
+ identifier([column]);
88
+ return construct([Object.freeze({ row: "existing", column })]);
89
+ },
90
+ incoming: ({ column }) => {
91
+ identifier([column]);
92
+ return construct([Object.freeze({ row: "incoming", column })]);
93
+ },
94
+ greatest: ({ values }) => {
95
+ if (!Array.isArray(values) || values.length !== 2 || values.some((value) => !isSqlFragment(value)))
96
+ invalid("sql.greatest requires two SQL fragments");
97
+ return construct([Object.freeze({ greatest: Object.freeze([...values]) })]);
98
+ }
99
+ }));
100
+ function renderSqlFragment(params) {
101
+ const { dialect, collector, conflict } = params;
102
+ const render = (fragment) => renderSqlFragment({ ...params, fragment });
103
+ return partsOf(params.fragment).map((part) => {
104
+ if (typeof part === "string")
105
+ return part;
106
+ if ("value" in part) {
107
+ if (part.value === undefined)
108
+ invalid("SQL parameters cannot be undefined; use null explicitly");
109
+ return collector.add(part.decoder === undefined ? part.value : part.decoder.encode({ value: part.value, dialect: dialect.name }));
110
+ }
111
+ if ("ident" in part)
112
+ return part.ident.map((name) => dialect.quoteIdent({ name })).join(".");
113
+ if ("row" in part) {
114
+ if (conflict === undefined)
115
+ invalid("Existing/incoming row references require a conflict update");
116
+ const name = conflict.columns === undefined ? part.column : conflict.columns[part.column];
117
+ if (name === undefined)
118
+ invalid(`Unknown conflict-expression field "${part.column}"`);
119
+ const ref = dialect.excludedRowRef;
120
+ const qualifier = part.row === "existing" ? dialect.quoteIdent({ name: conflict.table }) : ref.form === "keyword" ? ref.name : dialect.quoteIdent({ name: ref.alias });
121
+ return `${qualifier}.${dialect.quoteIdent({ name })}`;
122
+ }
123
+ return render(part.greatest.reduce((left, right) => sql`(CASE WHEN ${left} IS NULL THEN ${right} WHEN ${right} IS NULL THEN ${left} WHEN ${left} >= ${right} THEN ${left} ELSE ${right} END)`));
124
+ }).join("");
125
+ }
126
+ function mapConflictColumns(params) {
127
+ return construct(partsOf(params.fragment).map((part) => {
128
+ if (typeof part === "string")
129
+ return part;
130
+ if ("row" in part) {
131
+ const column = Object.hasOwn(params.columns, part.column) ? params.columns[part.column] : undefined;
132
+ if (column === undefined)
133
+ invalid(`Unknown conflict-expression field "${part.column}"`);
134
+ return Object.freeze({ ...part, column });
135
+ }
136
+ if ("greatest" in part)
137
+ return Object.freeze({ greatest: Object.freeze(part.greatest.map((fragment) => mapConflictColumns({ ...params, fragment }))) });
138
+ return part;
139
+ }));
140
+ }
141
+ function compileSql(params) {
142
+ const collector = new ParamCollector(params.dialect);
143
+ const text = renderSqlFragment({ dialect: params.dialect, fragment: params.query, collector });
144
+ return { text, values: collector.values };
145
+ }
146
+ function sqlUsesIncoming(params) {
147
+ return partsOf(params.fragment).some((part) => typeof part !== "string" && (("row" in part) && part.row === "incoming" || ("greatest" in part) && part.greatest.some((fragment) => sqlUsesIncoming({ fragment }))));
148
+ }
149
+ // packages/sql/src/result-decoder.ts
150
+ import { VibeError as VibeError3 } from "@vibeorm/schema";
151
+
152
+ // packages/sql/src/pg-array.ts
153
+ var PG_ARRAY_QUOTE = /^$|^NULL$|[{},"\\\s]/i;
154
+ function toPgArrayLiteral(params) {
155
+ const { values } = params;
156
+ const parts = values.map((value) => {
157
+ if (value === null || value === undefined)
158
+ return "NULL";
159
+ const text = String(value);
160
+ return PG_ARRAY_QUOTE.test(text) ? `"${text.replace(/["\\]/g, (char) => `\\${char}`)}"` : text;
161
+ });
162
+ return `{${parts.join(",")}}`;
163
+ }
164
+ function parsePgArrayLiteral(params) {
165
+ const { literal } = params;
166
+ if (literal.length < 2 || !literal.startsWith("{") || !literal.endsWith("}"))
167
+ return null;
168
+ const inner = literal.slice(1, -1);
169
+ if (inner.length === 0)
170
+ return [];
171
+ const out = [];
172
+ let index = 0;
173
+ while (index < inner.length) {
174
+ if (inner[index] === '"') {
175
+ let text = "";
176
+ index++;
177
+ while (index < inner.length) {
178
+ const char = inner[index];
179
+ if (char === "\\") {
180
+ text += inner[index + 1] ?? "";
181
+ index += 2;
182
+ continue;
183
+ }
184
+ index++;
185
+ if (char === '"')
186
+ break;
187
+ text += char;
188
+ }
189
+ out.push(text);
190
+ } else {
191
+ let text = "";
192
+ while (index < inner.length && inner[index] !== ",") {
193
+ text += inner[index];
194
+ index++;
195
+ }
196
+ out.push(text === "NULL" ? null : text);
197
+ }
198
+ if (inner[index] === ",")
199
+ index++;
200
+ }
201
+ return out;
202
+ }
203
+
204
+ // packages/sql/src/codecs.ts
205
+ import { VibeError as VibeError2, invalidStoredData } from "@vibeorm/schema";
206
+ function enumMembershipCodec(params) {
207
+ const { model, field, enum: enumName } = params;
208
+ const members = new Set(params.members);
209
+ return {
210
+ encode: (value) => {
211
+ if (typeof value === "string" && members.has(value))
212
+ return value;
213
+ throw new VibeError2({
214
+ code: "VIBE_VALIDATION",
215
+ message: `Invalid value for enum ${enumName} on ${model}.${field}`,
216
+ meta: { model, field, enum: enumName }
217
+ });
218
+ },
219
+ decode: (value) => {
220
+ if (typeof value === "string" && members.has(value))
221
+ return value;
222
+ throw invalidStoredData({ model, field, enum: enumName, value });
223
+ }
224
+ };
225
+ }
226
+ function decodeIsNoop(params) {
227
+ const { scalarType, wire } = params;
228
+ if (wire === undefined)
229
+ return false;
230
+ if (scalarType === "DateTime")
231
+ return false;
232
+ if (scalarType === "Json")
233
+ return wire.json === "parsed";
234
+ if (scalarType === "Int" || scalarType === "Float")
235
+ return wire.numbers === "native";
236
+ return false;
237
+ }
238
+ var identity = (value) => value;
239
+ var passthroughCodec = { encode: identity, decode: identity };
240
+ var numberCodec = {
241
+ encode: identity,
242
+ decode: (value) => typeof value === "string" ? Number(value) : value
243
+ };
244
+ var bigIntCodec = {
245
+ encode: (value) => typeof value === "bigint" ? value.toString() : value,
246
+ decode: (value) => {
247
+ if (typeof value === "bigint")
248
+ return value;
249
+ if (typeof value === "string" || typeof value === "number")
250
+ return BigInt(value);
251
+ return value;
252
+ }
253
+ };
254
+ var decimalCodec = {
255
+ encode: (value) => typeof value === "number" || typeof value === "bigint" ? String(value) : value,
256
+ decode: identity
257
+ };
258
+ var mysqlDecimalCodec = {
259
+ encode: decimalCodec.encode,
260
+ decode: (value) => {
261
+ if (typeof value !== "string" || !value.includes("."))
262
+ return value;
263
+ const trimmed = value.replace(/0+$/, "").replace(/\.$/, "");
264
+ return trimmed === "" || trimmed === "-" ? "0" : trimmed;
265
+ }
266
+ };
267
+ var ZONE_LESS_DATETIME = /^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}(?::\d{2}(?:\.\d{1,9})?)?$/;
268
+ function parseDateTimeText(params) {
269
+ const trimmed = params.text.trim();
270
+ if (ZONE_LESS_DATETIME.test(trimmed)) {
271
+ const asUtc = new Date(`${trimmed.replace(" ", "T").replace(/(\.\d{3})\d+$/, "$1")}Z`);
272
+ if (!Number.isNaN(asUtc.getTime()))
273
+ return asUtc;
274
+ }
275
+ const parsed = new Date(trimmed);
276
+ return Number.isNaN(parsed.getTime()) ? undefined : parsed;
277
+ }
278
+ var dateNativeCodec = {
279
+ encode: (value) => {
280
+ if (value instanceof Date && !Number.isFinite(value.getTime()) || typeof value === "number" && !Number.isFinite(value) || typeof value === "string" && /^[+-]?infinity$/i.test(value.trim())) {
281
+ throw new VibeError2({ code: "VIBE_VALIDATION", message: "DateTime parameters require finite timestamps; use an extended timestamp encoder for infinity" });
282
+ }
283
+ return value;
284
+ },
285
+ decode: (value) => {
286
+ const date = value instanceof Date ? value : typeof value === "string" ? parseDateTimeText({ text: value }) : typeof value === "number" ? new Date(value) : undefined;
287
+ if (date !== undefined && Number.isFinite(date.getTime()))
288
+ return date;
289
+ throw new VibeError2({ code: "VIBE_DATA_INVALID", message: "DateTime requires a finite timestamp; use an explicit extended timestamp decoder for infinity" });
290
+ }
291
+ };
292
+ var dateTextCodec = {
293
+ encode: (value) => {
294
+ dateNativeCodec.encode(value);
295
+ return value instanceof Date ? value.toISOString() : value;
296
+ },
297
+ decode: dateNativeCodec.decode
298
+ };
299
+ var mysqlDateCodec = {
300
+ encode: (value) => {
301
+ dateNativeCodec.encode(value);
302
+ if (value instanceof Date)
303
+ return value;
304
+ if (typeof value === "number") {
305
+ const parsed = new Date(value);
306
+ if (!Number.isNaN(parsed.getTime()))
307
+ return parsed;
308
+ return value;
309
+ }
310
+ if (typeof value === "string")
311
+ return parseDateTimeText({ text: value }) ?? value;
312
+ return value;
313
+ },
314
+ decode: dateNativeCodec.decode
315
+ };
316
+ var booleanIntCodec = {
317
+ encode: (value) => typeof value === "boolean" ? value ? 1 : 0 : value,
318
+ decode: (value) => typeof value === "number" ? value !== 0 : value
319
+ };
320
+ var booleanNativeInCodec = {
321
+ encode: identity,
322
+ decode: booleanIntCodec.decode
323
+ };
324
+ var jsonCodec = {
325
+ encode: (value) => JSON.stringify(value),
326
+ decode: (value) => {
327
+ if (typeof value !== "string")
328
+ return value;
329
+ try {
330
+ return JSON.parse(value);
331
+ } catch (cause) {
332
+ throw new VibeError2({
333
+ code: "VIBE_INTERNAL",
334
+ message: "a Json column returned text that is not valid JSON",
335
+ meta: { value },
336
+ cause
337
+ });
338
+ }
339
+ }
340
+ };
341
+ var POSTGRES_CODECS = {
342
+ String: passthroughCodec,
343
+ Boolean: passthroughCodec,
344
+ Int: numberCodec,
345
+ BigInt: bigIntCodec,
346
+ Float: numberCodec,
347
+ Decimal: decimalCodec,
348
+ DateTime: dateNativeCodec,
349
+ Json: jsonCodec,
350
+ Bytes: passthroughCodec
351
+ };
352
+ var SQLITE_CODECS = {
353
+ String: passthroughCodec,
354
+ Boolean: booleanIntCodec,
355
+ Int: numberCodec,
356
+ BigInt: bigIntCodec,
357
+ Float: numberCodec,
358
+ Decimal: decimalCodec,
359
+ DateTime: dateTextCodec,
360
+ Json: jsonCodec,
361
+ Bytes: passthroughCodec
362
+ };
363
+ var MYSQL_CODECS = {
364
+ String: passthroughCodec,
365
+ Boolean: booleanNativeInCodec,
366
+ Int: numberCodec,
367
+ BigInt: bigIntCodec,
368
+ Float: numberCodec,
369
+ Decimal: mysqlDecimalCodec,
370
+ DateTime: mysqlDateCodec,
371
+ Json: jsonCodec,
372
+ Bytes: passthroughCodec
373
+ };
374
+ var DIALECT_CODECS = {
375
+ postgres: POSTGRES_CODECS,
376
+ sqlite: SQLITE_CODECS,
377
+ mysql: MYSQL_CODECS
378
+ };
379
+
380
+ // packages/sql/src/result-decoder.ts
381
+ function refuse(encoding, message) {
382
+ throw new VibeError3({ code: encoding ? "VIBE_VALIDATION" : "VIBE_DATA_INVALID", message });
383
+ }
384
+ function scalar(type, valid) {
385
+ const convert = (params, encoding) => {
386
+ const { value, dialect } = params;
387
+ if (value === null || value === undefined)
388
+ refuse(encoding, `A non-null ${type} projection received null or a missing column`);
389
+ if (type === "BigInt" && !(typeof value === "bigint" || typeof value === "string" && /^[+-]?\d+$/.test(value) || typeof value === "number" && Number.isSafeInteger(value))) {
390
+ refuse(encoding, "BigInt requires exact integer text, bigint, or a safe integer");
391
+ }
392
+ if (type === "Boolean" && value !== true && value !== false && value !== 0 && value !== 1)
393
+ refuse(encoding, "Boolean requires true, false, 0, or 1");
394
+ if (type === "Decimal" && typeof value === "number")
395
+ refuse(encoding, "Decimal requires exact decimal text, not a floating-point number");
396
+ if ((type === "Int" || type === "Float") && typeof value === "string" && !/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/.test(value)) {
397
+ refuse(encoding, `Invalid ${type} text`);
398
+ }
399
+ const codec = DIALECT_CODECS[dialect][type];
400
+ const result = type === "Json" && params.wire?.json === "parsed" ? value : codec.decode(value);
401
+ if (!valid(result))
402
+ refuse(encoding, `Invalid ${type} projection value`);
403
+ return result;
404
+ };
405
+ return Object.freeze({
406
+ decode: (params) => convert(params, false),
407
+ encode: (params) => {
408
+ const value = type === "Json" ? params.value : convert(params, true);
409
+ if (value === undefined || value === null)
410
+ refuse(true, `A non-null ${type} parameter cannot be null or undefined`);
411
+ return DIALECT_CODECS[params.dialect][type].encode(value);
412
+ }
413
+ });
414
+ }
415
+ var stringDecoder = scalar("String", (value) => typeof value === "string");
416
+ var dateDecoder = scalar("DateTime", (value) => value instanceof Date && Number.isFinite(value.getTime()));
417
+ var extendedDecoder = Object.freeze({
418
+ decode: (params) => {
419
+ if (params.value === "infinity" || params.value === Infinity)
420
+ return "infinity";
421
+ if (params.value === "-infinity" || params.value === -Infinity)
422
+ return "-infinity";
423
+ return dateDecoder.decode(params);
424
+ },
425
+ encode: (params) => {
426
+ if (params.value === "infinity" || params.value === "-infinity") {
427
+ if (params.dialect !== "postgres")
428
+ throw new VibeError3({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Timestamp infinity parameters require PostgreSQL" });
429
+ return params.value;
430
+ }
431
+ return dateDecoder.encode(params);
432
+ }
433
+ });
434
+ var decode = Object.freeze({
435
+ string: stringDecoder,
436
+ boolean: scalar("Boolean", (value) => typeof value === "boolean"),
437
+ int: scalar("Int", (value) => typeof value === "number" && Number.isSafeInteger(value)),
438
+ float: scalar("Float", (value) => typeof value === "number" && Number.isFinite(value)),
439
+ bigInt: scalar("BigInt", (value) => typeof value === "bigint"),
440
+ decimal: scalar("Decimal", (value) => typeof value === "string" && /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/.test(value)),
441
+ dateTime: dateDecoder,
442
+ extendedDateTime: extendedDecoder,
443
+ json: scalar("Json", (value) => value !== undefined),
444
+ bytes: scalar("Bytes", (value) => value instanceof Uint8Array),
445
+ nullable: ({ decoder }) => Object.freeze({
446
+ decode: (params) => params.value === null ? null : decoder.decode(params),
447
+ encode: (params) => params.value === null ? null : decoder.encode(params)
448
+ }),
449
+ array: ({ decoder }) => Object.freeze({
450
+ decode: (params) => {
451
+ const raw = params.value;
452
+ const value = params.dialect === "postgres" && typeof raw === "string" ? parsePgArrayLiteral({ literal: raw }) : ArrayBuffer.isView(raw) && ("length" in raw) ? Array.from(raw) : raw;
453
+ if (!Array.isArray(value))
454
+ refuse(false, "Expected an array projection");
455
+ return value.map((element) => decoder.decode({ ...params, value: element }));
456
+ },
457
+ encode: (params) => {
458
+ if (!Array.isArray(params.value))
459
+ refuse(true, "Expected an array parameter");
460
+ return params.value.map((value) => decoder.encode({ ...params, value }));
461
+ }
462
+ }),
463
+ enum: ({ values }) => {
464
+ const members = new Set(values);
465
+ return Object.freeze({
466
+ decode: ({ value }) => typeof value === "string" && members.has(value) ? value : refuse(false, "Unknown enum projection member"),
467
+ encode: ({ value }) => typeof value === "string" && members.has(value) ? value : refuse(true, "Unknown enum parameter member")
468
+ });
469
+ },
470
+ object: ({ fields }) => {
471
+ const entries = Object.entries(fields);
472
+ const convert = (params, encoding) => {
473
+ if (typeof params.value !== "object" || params.value === null || Array.isArray(params.value))
474
+ refuse(encoding, "Expected a result row object");
475
+ const row = params.value;
476
+ return Object.fromEntries(entries.map(([name, decoder]) => {
477
+ if (!Object.hasOwn(row, name))
478
+ refuse(encoding, `Missing projection column "${name}"`);
479
+ return [name, encoding ? decoder.encode({ ...params, value: row[name] }) : decoder.decode({ ...params, value: row[name] })];
480
+ }));
481
+ };
482
+ return Object.freeze({ decode: (params) => convert(params, false), encode: (params) => convert(params, true) });
483
+ }
484
+ });
485
+ // packages/sql/src/exclusion.ts
486
+ import { VibeError as VibeError5, fieldTypeIdentity as fieldTypeIdentity2, unsupportedCapability as unsupportedCapability2 } from "@vibeorm/schema";
487
+
488
+ // packages/sql/src/predicate.ts
489
+ import { VibeError as VibeError4, fieldTypeIdentity, isEnumType, unsupportedCapability } from "@vibeorm/schema";
490
+ function renderPredicate(params) {
491
+ if (params.usage === "exclusionPredicate" && !params.dialect.capabilities.exclusionConstraints)
492
+ throw unsupportedCapability({ dialect: params.dialect.name, feature: "exclusionConstraints" });
493
+ if (params.usage === "indexPredicate" && !params.dialect.capabilities.partialIndexes) {
494
+ throw unsupportedCapability({ dialect: params.dialect.name, feature: "partialIndexes" });
495
+ }
496
+ const context = {
497
+ model: params.model,
498
+ dialect: params.dialect,
499
+ enums: params.enums ?? [],
500
+ usage: params.usage,
501
+ site: params.site
502
+ };
503
+ return renderNode({ node: params.predicate, context });
504
+ }
505
+ var PRECEDENCE = {
506
+ or: 1,
507
+ and: 2,
508
+ not: 3,
509
+ compare: 4,
510
+ inList: 4,
511
+ isNull: 4
512
+ };
513
+ var COMPARISON_SQL = {
514
+ eq: "=",
515
+ ne: "<>",
516
+ gt: ">",
517
+ gte: ">=",
518
+ lt: "<",
519
+ lte: "<="
520
+ };
521
+ var EXACT_INTEGER = /^-?(0|[1-9]\d*)$/;
522
+ var EXACT_DECIMAL = /^-?(0|[1-9]\d*)(\.\d+)?$/;
523
+ var INT32_MIN = -2147483648n;
524
+ var INT32_MAX = 2147483647n;
525
+ var INT64_MIN = -(2n ** 63n);
526
+ var INT64_MAX = 2n ** 63n - 1n;
527
+ function refuse2(params) {
528
+ const { context, rule, message } = params;
529
+ throw new VibeError4({
530
+ code: "VIBE_SCHEMA_INVALID",
531
+ message: `${context.site} on "${context.model.name}": ${message}`,
532
+ meta: { rule, model: context.model.name, site: context.site, ...params.meta ?? {} }
533
+ });
534
+ }
535
+ function renderChild(params) {
536
+ const { node, parent, context } = params;
537
+ if ((node.kind === "and" || node.kind === "or") && node.operands.length === 1) {
538
+ return renderChild({ node: node.operands[0], parent, context });
539
+ }
540
+ const text = renderNode({ node, context });
541
+ return PRECEDENCE[node.kind] < PRECEDENCE[parent] ? `(${text})` : text;
542
+ }
543
+ function renderNode(params) {
544
+ const { node, context } = params;
545
+ switch (node.kind) {
546
+ case "and":
547
+ case "or": {
548
+ if (node.operands.length === 0) {
549
+ refuse2({
550
+ context,
551
+ rule: "predicate-empty",
552
+ message: `${node.kind === "and" ? "and()" : "or()"} has no operands \u2014 an empty boolean group has no honest SQL spelling`
553
+ });
554
+ }
555
+ if (node.operands.length === 1)
556
+ return renderNode({ node: node.operands[0], context });
557
+ const separator = node.kind === "and" ? " AND " : " OR ";
558
+ return node.operands.map((operand) => renderChild({ node: operand, parent: node.kind, context })).join(separator);
559
+ }
560
+ case "not":
561
+ return `NOT ${renderChild({ node: node.operand, parent: "not", context })}`;
562
+ case "isNull": {
563
+ const field = resolveField({ name: node.field.name, context });
564
+ return `${quoteColumn({ field, context })} IS ${node.negate === true ? "NOT NULL" : "NULL"}`;
565
+ }
566
+ case "compare": {
567
+ const left = resolveField({ name: node.left.name, context });
568
+ const right = renderOperand({ operand: node.right, field: left, context });
569
+ return `${compareColumn({ field: left, context })} ${COMPARISON_SQL[node.op]} ${right}`;
570
+ }
571
+ case "inList": {
572
+ if (node.values.length === 0) {
573
+ refuse2({
574
+ context,
575
+ rule: "predicate-empty",
576
+ message: `inList("${node.field.name}") has no values \u2014 an empty IN list is UNKNOWN on every row and never what an author means`,
577
+ meta: { field: node.field.name }
578
+ });
579
+ }
580
+ const field = resolveField({ name: node.field.name, context });
581
+ const values = node.values.map((value) => renderLiteral({ value, field, context })).join(", ");
582
+ return `${compareColumn({ field, context })} ${node.negate === true ? "NOT IN" : "IN"} (${values})`;
583
+ }
584
+ }
585
+ }
586
+ function resolveField(params) {
587
+ const { name, context } = params;
588
+ const field = context.model.fields.find((candidate) => candidate.name === name);
589
+ if (field === undefined) {
590
+ refuse2({
591
+ context,
592
+ rule: "predicate-unknown-field",
593
+ message: `references field "${name}", which the model does not declare`,
594
+ meta: { field: name }
595
+ });
596
+ }
597
+ if (field.isList) {
598
+ refuse2({
599
+ context,
600
+ rule: "predicate-list-field",
601
+ message: `compares scalar list "${name}" \u2014 array operators differ per dialect; use a raw expression for them`,
602
+ meta: { field: name }
603
+ });
604
+ }
605
+ if (field.isUnsupported === true) {
606
+ refuse2({
607
+ context,
608
+ rule: "predicate-unsupported-type",
609
+ message: `references "${name}", an Unsupported("${field.nativeType ?? "?"}") column whose operators VibeORM does not model \u2014 use a raw expression`,
610
+ meta: { field: name }
611
+ });
612
+ }
613
+ if (field.type === "Json" || field.type === "Bytes") {
614
+ refuse2({
615
+ context,
616
+ rule: "predicate-unsupported-type",
617
+ message: `references ${field.type} column "${name}" \u2014 ${field.type} comparison operators differ per dialect; use a raw expression`,
618
+ meta: { field: name, type: field.type }
619
+ });
620
+ }
621
+ if (context.usage === "check" && context.dialect.name === "mysql" && field.default?.kind === "autoincrement") {
622
+ throw unsupportedCapability({
623
+ dialect: "mysql",
624
+ feature: `CHECK constraint referencing auto-increment column "${context.model.name}.${name}"`,
625
+ hint: "mysql refuses this with error 3818 \u2014 enforce the rule in application code or on a non-generated column"
626
+ });
627
+ }
628
+ return field;
629
+ }
630
+ function quoteColumn(params) {
631
+ const { field, context } = params;
632
+ return context.dialect.quoteIdent({ name: field.dbName ?? field.name });
633
+ }
634
+ function compareColumn(params) {
635
+ const { field, context } = params;
636
+ const quoted = quoteColumn({ field, context });
637
+ return context.dialect.name === "sqlite" && field.type === "Decimal" ? `CAST(${quoted} AS REAL)` : quoted;
638
+ }
639
+ function renderOperand(params) {
640
+ const { operand, field, context } = params;
641
+ if (operand.kind === "value")
642
+ return renderLiteral({ value: operand, field, context });
643
+ const other = resolveField({ name: operand.name, context });
644
+ const leftType = fieldTypeIdentity({ type: field.type });
645
+ const rightType = fieldTypeIdentity({ type: other.type });
646
+ if (leftType !== rightType) {
647
+ refuse2({
648
+ context,
649
+ rule: "predicate-type-mismatch",
650
+ message: `compares "${field.name}" (${leftType}) with "${other.name}" (${rightType}) \u2014 both sides of a comparison need the same scalar type`,
651
+ meta: { field: field.name, other: other.name, fieldType: leftType, otherType: rightType }
652
+ });
653
+ }
654
+ return compareColumn({ field: other, context });
655
+ }
656
+ function renderLiteral(params) {
657
+ const { field, context } = params;
658
+ const { value } = params.value;
659
+ const type = field.type;
660
+ const wrongCarrier = (expected) => refuse2({
661
+ context,
662
+ rule: "predicate-type-mismatch",
663
+ message: `compares ${fieldTypeIdentity({ type })} field "${field.name}" with ${describe({ value })} \u2014 ${expected}`,
664
+ meta: { field: field.name, fieldType: fieldTypeIdentity({ type }), value }
665
+ });
666
+ if (isEnumType(type)) {
667
+ if (typeof value !== "string")
668
+ return wrongCarrier(`enum ${type.enum} takes one of its member names as a string`);
669
+ const declared = context.enums.find((entry) => entry.name === type.enum);
670
+ if (declared !== undefined && !declared.values.includes(value)) {
671
+ refuse2({
672
+ context,
673
+ rule: "predicate-enum-member",
674
+ message: `compares "${field.name}" with "${value}", which is not a member of enum ${type.enum} (${declared.values.join(", ")})`,
675
+ meta: { field: field.name, enum: type.enum, value, members: [...declared.values] }
676
+ });
677
+ }
678
+ return stringLiteral({ value, context, field });
679
+ }
680
+ switch (type) {
681
+ case "String":
682
+ if (typeof value !== "string")
683
+ return wrongCarrier("a String column takes a string literal");
684
+ return stringLiteral({ value, context, field });
685
+ case "Boolean":
686
+ if (typeof value !== "boolean")
687
+ return wrongCarrier("a Boolean column takes `true` or `false`");
688
+ return value ? context.dialect.booleanLiteral.true : context.dialect.booleanLiteral.false;
689
+ case "Int":
690
+ return integerLiteral({ value, field, context, min: INT32_MIN, max: INT32_MAX, label: "Int" });
691
+ case "BigInt":
692
+ return integerLiteral({ value, field, context, min: INT64_MIN, max: INT64_MAX, label: "BigInt" });
693
+ case "Float": {
694
+ if (typeof value !== "number")
695
+ return wrongCarrier("a Float column takes a number");
696
+ if (!Number.isFinite(value)) {
697
+ refuse2({
698
+ context,
699
+ rule: "predicate-literal-invalid",
700
+ message: `compares "${field.name}" with ${String(value)} \u2014 a Float literal must be finite`,
701
+ meta: { field: field.name, value: String(value) }
702
+ });
703
+ }
704
+ return String(value);
705
+ }
706
+ case "Decimal": {
707
+ if (typeof value !== "string") {
708
+ return wrongCarrier('a Decimal column takes exact decimal TEXT ("0", "19.99") \u2014 a JS number cannot carry an exact decimal');
709
+ }
710
+ if (!EXACT_DECIMAL.test(value)) {
711
+ refuse2({
712
+ context,
713
+ rule: "predicate-literal-invalid",
714
+ message: `compares "${field.name}" with "${value}", which is not an exact decimal literal`,
715
+ meta: { field: field.name, value }
716
+ });
717
+ }
718
+ return value;
719
+ }
720
+ case "DateTime":
721
+ refuse2({
722
+ context,
723
+ rule: "predicate-unsupported-literal",
724
+ message: `compares DateTime field "${field.name}" with a literal \u2014 DateTime literals are spelled differently on postgres, mysql and sqlite, so VibeORM does not guess one. Compare two DateTime columns, or write a raw expression for a fixed instant`,
725
+ meta: { field: field.name }
726
+ });
727
+ case "Json":
728
+ case "Bytes":
729
+ return wrongCarrier(`${type} columns take no predicate literal`);
730
+ }
731
+ }
732
+ function integerLiteral(params) {
733
+ const { value, field, context, min, max, label } = params;
734
+ let text;
735
+ if (typeof value === "number") {
736
+ if (!Number.isSafeInteger(value)) {
737
+ refuse2({
738
+ context,
739
+ rule: "predicate-literal-invalid",
740
+ message: `compares "${field.name}" with ${String(value)} \u2014 an ${label} literal must be an exact integer JavaScript can represent; pass a bigint or a decimal string for larger values`,
741
+ meta: { field: field.name, value: String(value) }
742
+ });
743
+ }
744
+ text = String(value);
745
+ } else if (typeof value === "string") {
746
+ if (!EXACT_INTEGER.test(value)) {
747
+ refuse2({
748
+ context,
749
+ rule: "predicate-literal-invalid",
750
+ message: `compares "${field.name}" with "${value}", which is not an exact integer literal`,
751
+ meta: { field: field.name, value }
752
+ });
753
+ }
754
+ text = value;
755
+ } else {
756
+ refuse2({
757
+ context,
758
+ rule: "predicate-type-mismatch",
759
+ message: `compares ${label} field "${field.name}" with ${describe({ value })} \u2014 an ${label} column takes an integer`,
760
+ meta: { field: field.name, value }
761
+ });
762
+ }
763
+ const parsed = BigInt(text);
764
+ if (parsed < min || parsed > max) {
765
+ refuse2({
766
+ context,
767
+ rule: "predicate-literal-invalid",
768
+ message: `compares "${field.name}" with ${text}, which is outside the range an ${label} column can hold (${min}\u2026${max})`,
769
+ meta: { field: field.name, value: text }
770
+ });
771
+ }
772
+ return text;
773
+ }
774
+ function stringLiteral(params) {
775
+ const { value, context, field } = params;
776
+ if (value.includes("\x00")) {
777
+ refuse2({
778
+ context,
779
+ rule: "predicate-literal-invalid",
780
+ message: `compares "${field.name}" with a string containing a NUL byte, which no dialect here can store`,
781
+ meta: { field: field.name }
782
+ });
783
+ }
784
+ return context.dialect.quoteStringLiteral({ value });
785
+ }
786
+ function describe(params) {
787
+ const { value } = params;
788
+ return typeof value === "string" ? `the string ${JSON.stringify(value)}` : `the ${typeof value} ${String(value)}`;
789
+ }
790
+
791
+ // packages/sql/src/exclusion.ts
792
+ function invalid2(rule) {
793
+ throw new VibeError5({ code: "VIBE_SCHEMA_INVALID", message: `Invalid exclusion constraint: ${rule}`, meta: { rule: `exclusion-${rule}` } });
794
+ }
795
+ function compileExclusion(params) {
796
+ const { exclusion: spec, model, dialect } = params;
797
+ if (!dialect.capabilities.exclusionConstraints)
798
+ throw unsupportedCapability2({ dialect: dialect.name, feature: "exclusionConstraints" });
799
+ if (!spec.name || spec.name.includes("\x00"))
800
+ invalid2("name");
801
+ if (!["int8range", "tsrange", "tstzrange"].includes(spec.range.type))
802
+ invalid2("range-type");
803
+ if (!["[)", "[]", "()", "(]"].includes(spec.range.bounds))
804
+ invalid2("bounds");
805
+ const field = (name) => {
806
+ const value = model.fields.find((entry) => entry.name === name);
807
+ if (value === undefined || value.isList || value.isUnsupported)
808
+ invalid2("field");
809
+ return value;
810
+ };
811
+ const endpoint = (value) => {
812
+ if ("unbounded" in value)
813
+ return "NULL";
814
+ const infinity = "infinity" in value ? value.infinity : value.nullAs;
815
+ if (infinity !== undefined && (spec.range.type === "int8range" || !["infinity", "-infinity"].includes(infinity)))
816
+ invalid2("infinity");
817
+ const literal = infinity === undefined ? undefined : `${dialect.quoteStringLiteral({ value: infinity })}::${spec.range.type === "tsrange" ? "timestamp" : "timestamptz"}`;
818
+ if ("infinity" in value)
819
+ return literal;
820
+ const column = field(value.field);
821
+ if (fieldTypeIdentity2({ type: column.type }) !== (spec.range.type === "int8range" ? "BigInt" : "DateTime"))
822
+ invalid2("range-field-type");
823
+ if (column.type === "DateTime") {
824
+ const timezone = /timestamptz|timestamp.*with time zone/i.test(column.nativeType ?? "timestamptz");
825
+ if (timezone !== (spec.range.type === "tstzrange"))
826
+ invalid2("range-timezone");
827
+ }
828
+ const sql2 = dialect.quoteIdent({ name: column.dbName ?? column.name });
829
+ return literal === undefined ? sql2 : `COALESCE(${sql2}, ${literal})`;
830
+ };
831
+ const keys = spec.keys.map((key) => {
832
+ if ("expression" in key) {
833
+ if (!key.expression.trim() || key.expression.includes("\x00") || key.fields.length === 0)
834
+ invalid2("expression");
835
+ key.fields.forEach(field);
836
+ return `(${key.expression}) WITH =`;
837
+ }
838
+ const column = field(key.field);
839
+ if (column.isOptional)
840
+ invalid2("nullable-key-requires-expression");
841
+ if (!["String", "Int", "BigInt", "Float", "Decimal", "Boolean", "DateTime"].includes(fieldTypeIdentity2({ type: column.type })))
842
+ invalid2("key-type");
843
+ return `${dialect.quoteIdent({ name: column.dbName ?? column.name })} WITH =`;
844
+ });
845
+ keys.push(`${spec.range.type}(${endpoint(spec.range.lower)}, ${endpoint(spec.range.upper)}, ${dialect.quoteStringLiteral({ value: spec.range.bounds })}) WITH &&`);
846
+ const where = spec.where === undefined ? "" : ` WHERE (${renderPredicate({ predicate: spec.where, model, dialect, usage: "exclusionPredicate", site: spec.name })})`;
847
+ return { name: spec.name, definition: `EXCLUDE USING gist (${keys.join(", ")})${where}`, source: spec, requiredExtensions: spec.keys.length === 0 ? [] : dialect.capabilities.exclusionExtensions };
848
+ }
849
+ function renderExclusionSetup(params) {
850
+ if (!params.dialect.capabilities.exclusionConstraints)
851
+ throw unsupportedCapability2({ dialect: params.dialect.name, feature: "exclusionConstraints" });
852
+ return params.exclusion.requiredExtensions.map((name) => `CREATE EXTENSION IF NOT EXISTS ${params.dialect.quoteIdent({ name })};`);
853
+ }
854
+ function renderExclusionDdl(params) {
855
+ const { dialect, table, exclusion, operation } = params;
856
+ if (!dialect.capabilities.exclusionConstraints)
857
+ throw unsupportedCapability2({ dialect: dialect.name, feature: "exclusionConstraints" });
858
+ const target = `ALTER TABLE ${dialect.quoteIdent({ name: table })}`;
859
+ if (operation === "drop")
860
+ return [`${target} DROP CONSTRAINT ${dialect.quoteIdent({ name: exclusion.name })};`];
861
+ return [...renderExclusionSetup({ dialect, exclusion }), `${target} ADD CONSTRAINT ${dialect.quoteIdent({ name: exclusion.name })} ${exclusion.definition};`];
862
+ }
863
+ function renderExclusionCatalog(params) {
864
+ const { dialect, probe } = params;
865
+ if (!dialect.capabilities.exclusionConstraints)
866
+ throw unsupportedCapability2({ dialect: dialect.name, feature: "exclusionConstraints" });
867
+ const scope = probe === undefined ? "n.nspname = 'public'" : `t.oid = ${dialect.quoteStringLiteral({ value: probe })}::regclass`;
868
+ return `SELECT t.relname AS table_name, c.conname AS constraint_name, pg_catalog.pg_get_constraintdef(c.oid, true) AS definition, c.convalidated AS validated, i.indisvalid AS valid, i.indisready AS ready FROM pg_catalog.pg_constraint c JOIN pg_catalog.pg_class t ON t.oid = c.conrelid JOIN pg_catalog.pg_namespace n ON n.oid = t.relnamespace JOIN pg_catalog.pg_index i ON i.indexrelid = c.conindid WHERE c.contype = 'x' AND ${scope} ORDER BY t.relname, c.conname`;
869
+ }
870
+ function renderExclusionProbe(params) {
871
+ const { dialect, table, probe, definition } = params;
872
+ if (!dialect.capabilities.exclusionConstraints)
873
+ throw unsupportedCapability2({ dialect: dialect.name, feature: "exclusionConstraints" });
874
+ return [`CREATE TEMP TABLE ${dialect.quoteIdent({ name: probe })} (LIKE ${dialect.quoteIdent({ name: "public" })}.${dialect.quoteIdent({ name: table })})`, `ALTER TABLE ${dialect.quoteIdent({ name: probe })} ADD CONSTRAINT ${dialect.quoteIdent({ name: `${probe}_x` })} ${definition}`];
875
+ }
2
876
  // packages/sql/src/ast.ts
3
877
  var SET_OPERATORS = ["increment", "decrement", "multiply", "divide"];
4
878
  function isSetOperation(value) {
@@ -48,6 +922,90 @@ function excludedRef(params) {
48
922
  function isExcludedRef(value) {
49
923
  return typeof value === "object" && value !== null && value.__excluded === true;
50
924
  }
925
+ var ADVISORY_LOCK_RESULT_COLUMN = "acquired";
926
+ // packages/sql/src/locks.ts
927
+ import { internalError, unsupportedCapability as unsupportedCapability3 } from "@vibeorm/schema";
928
+ var ROW_LOCK_PROSE = Object.freeze({
929
+ strengths: Object.freeze({
930
+ update: "FOR UPDATE",
931
+ noKeyUpdate: "FOR NO KEY UPDATE",
932
+ share: "FOR SHARE",
933
+ keyShare: "FOR KEY SHARE"
934
+ }),
935
+ wait: Object.freeze({ wait: "", nowait: " NOWAIT", skipLocked: " SKIP LOCKED" })
936
+ });
937
+ function describeRowLock(params) {
938
+ const { strength, wait } = params.lock;
939
+ return `${ROW_LOCK_PROSE.strengths[strength]}${ROW_LOCK_PROSE.wait[wait]}`;
940
+ }
941
+ function renderRowLockClause(params) {
942
+ const { dialect, lock } = params;
943
+ const syntax = dialect.rowLockSyntax;
944
+ if (syntax === null) {
945
+ if (lock.strength === "update" && lock.wait === "wait")
946
+ return null;
947
+ throw unsupportedCapability3({
948
+ dialect: dialect.name,
949
+ feature: `a ${describeRowLock({ lock })} locking read`,
950
+ hint: `${dialect.name} has no locking-read clause at all`
951
+ });
952
+ }
953
+ const strengthText = syntax.strengths[lock.strength];
954
+ if (strengthText === undefined) {
955
+ throw unsupportedCapability3({
956
+ dialect: dialect.name,
957
+ feature: `the ${describeRowLock({ lock: { ...lock, wait: "wait" } })} row-lock strength`,
958
+ hint: `${dialect.name} can express: ${Object.keys(syntax.strengths).join(", ")}`
959
+ });
960
+ }
961
+ const waitText = syntax.wait[lock.wait];
962
+ if (waitText === undefined) {
963
+ throw unsupportedCapability3({
964
+ dialect: dialect.name,
965
+ feature: `the "${lock.wait}" lock wait policy`,
966
+ hint: `${dialect.name} can express: ${Object.keys(syntax.wait).join(", ")}`
967
+ });
968
+ }
969
+ return `${strengthText}${waitText}`;
970
+ }
971
+ var ADVISORY_KEY_BOUNDS = Object.freeze({
972
+ single: Object.freeze({ min: -(2n ** 63n), max: 2n ** 63n - 1n }),
973
+ pair: Object.freeze({ min: -(2 ** 31), max: 2 ** 31 - 1 })
974
+ });
975
+ function isSingleKey(key) {
976
+ return "key" in key;
977
+ }
978
+ function renderAdvisoryLock(params) {
979
+ const { dialect, statement, collector } = params;
980
+ const functions = dialect.advisoryLockFunctions;
981
+ if (functions === null) {
982
+ throw unsupportedCapability3({
983
+ dialect: dialect.name,
984
+ feature: "advisory locks",
985
+ hint: dialect.name === "mysql" ? "mysql's GET_LOCK is SESSION-scoped \u2014 it survives a rollback, so it cannot be released with the transaction" : `${dialect.name} has no advisory-lock functions`
986
+ });
987
+ }
988
+ const mode = statement.mode;
989
+ const fn = (mode === "exclusive" ? functions.exclusive : functions.shared)[statement.wait];
990
+ const key = statement.key;
991
+ const args = isSingleKey(key) ? `${collector.add(key.key.toString())}${functions.keyCast.single}` : `${collector.add(String(assertInt32({ value: key.classId, part: "classId" })))}${functions.keyCast.pair}, ` + `${collector.add(String(assertInt32({ value: key.objectId, part: "objectId" })))}${functions.keyCast.pair}`;
992
+ const projection = statement.wait === "nowait" ? ` AS ${dialect.quoteIdent({ name: ADVISORY_LOCK_RESULT_COLUMN })}` : "";
993
+ return {
994
+ text: `SELECT ${fn}(${args})${projection}`,
995
+ values: collector.values,
996
+ returning: "none"
997
+ };
998
+ }
999
+ function assertInt32(params) {
1000
+ const { value, part } = params;
1001
+ if (!Number.isInteger(value) || value < ADVISORY_KEY_BOUNDS.pair.min || value > ADVISORY_KEY_BOUNDS.pair.max) {
1002
+ throw internalError({
1003
+ message: `advisory lock ${part} must be a 32-bit integer, got ${String(value)}`,
1004
+ meta: { part, value }
1005
+ });
1006
+ }
1007
+ return value;
1008
+ }
51
1009
  // packages/sql/src/json-transport.ts
52
1010
  var JSON_TRANSPORT = Object.freeze({ chunkSize: 100 });
53
1011
  function projectionOutputName(params) {
@@ -93,12 +1051,35 @@ var postgresDialect = {
93
1051
  ],
94
1052
  jsonParamCast: "::text::jsonb",
95
1053
  lastInsertIdSql: null,
96
- selectForUpdate: " FOR UPDATE",
1054
+ rowLockSyntax: {
1055
+ strengths: Object.freeze({
1056
+ update: "FOR UPDATE",
1057
+ noKeyUpdate: "FOR NO KEY UPDATE",
1058
+ share: "FOR SHARE",
1059
+ keyShare: "FOR KEY SHARE"
1060
+ }),
1061
+ wait: Object.freeze({ wait: "", nowait: " NOWAIT", skipLocked: " SKIP LOCKED" })
1062
+ },
1063
+ advisoryLockFunctions: {
1064
+ exclusive: Object.freeze({
1065
+ wait: "pg_catalog.pg_advisory_xact_lock",
1066
+ nowait: "pg_catalog.pg_try_advisory_xact_lock"
1067
+ }),
1068
+ shared: Object.freeze({
1069
+ wait: "pg_catalog.pg_advisory_xact_lock_shared",
1070
+ nowait: "pg_catalog.pg_try_advisory_xact_lock_shared"
1071
+ }),
1072
+ keyCast: Object.freeze({ single: "::bigint", pair: "::int4" })
1073
+ },
97
1074
  rowDefaultKeyword: true,
98
1075
  paramBudget: 30000,
1076
+ booleanLiteral: { true: "true", false: "false" },
99
1077
  quoteIdent({ name }) {
100
1078
  return `"${name.replaceAll('"', '""')}"`;
101
1079
  },
1080
+ quoteStringLiteral({ value }) {
1081
+ return `'${value.replaceAll("'", "''")}'`;
1082
+ },
102
1083
  placeholder({ index }) {
103
1084
  return `$${index}`;
104
1085
  },
@@ -139,248 +1120,92 @@ var sqliteDialect = {
139
1120
  ],
140
1121
  jsonParamCast: "",
141
1122
  lastInsertIdSql: null,
142
- selectForUpdate: null,
1123
+ rowLockSyntax: null,
1124
+ advisoryLockFunctions: null,
143
1125
  rowDefaultKeyword: false,
144
1126
  paramBudget: 900,
145
- quoteIdent({ name }) {
146
- return `"${name.replaceAll('"', '""')}"`;
147
- },
148
- placeholder() {
149
- return "?";
150
- },
151
- renderDbNow({ offsetSeconds, collector }) {
152
- if (offsetSeconds === 0)
153
- return `strftime('%Y-%m-%dT%H:%M:%fZ', 'now')`;
154
- return `strftime('%Y-%m-%dT%H:%M:%fZ', 'now', ${collector.add(offsetSeconds)} || ' seconds')`;
155
- }
156
- };
157
- // packages/sql/src/dialects/mysql.ts
158
- var mysqlDialect = {
159
- name: "mysql",
160
- capabilities: MYSQL_CAPABILITIES,
161
- inArrayStrategy: "expand",
162
- tupleInStrategy: "rowValue",
163
- upsertForm: "onDuplicateKey",
164
- conflictKeyEquality: {
165
- foldingScalars: ["Decimal"],
166
- defaultStringMaxLength: 191,
167
- textIsBinaryByDefault: false,
168
- duplicatesInOneStatement: "lastWriteWins"
169
- },
170
- excludedRowRef: { form: "rowAlias", alias: "new" },
171
- doNothingForm: "insertIgnore",
172
- insensitiveLike: "lower",
173
- strictMatchStrategy: "binary",
174
- orderByNullsMode: "emulate",
175
- offsetRequiresLimit: { limitText: "18446744073709551615" },
176
- likeEscapeClause: null,
177
- jsonPathMode: "mysqlExtract",
178
- supportedJsonOps: [
179
- "equals",
180
- "string_contains",
181
- "string_starts_with",
182
- "string_ends_with",
183
- "array_contains",
184
- "array_starts_with",
185
- "array_ends_with"
186
- ],
187
- jsonParamCast: "",
188
- lastInsertIdSql: "SELECT LAST_INSERT_ID() AS `id`",
189
- selectForUpdate: " FOR UPDATE",
190
- rowDefaultKeyword: true,
191
- paramBudget: 1e4,
192
- quoteIdent({ name }) {
193
- return `\`${name.replaceAll("`", "``")}\``;
194
- },
195
- placeholder() {
196
- return "?";
197
- },
198
- renderDbNow({ offsetSeconds, collector }) {
199
- if (offsetSeconds === 0)
200
- return "NOW(6)";
201
- return `DATE_ADD(NOW(6), INTERVAL ${collector.add(offsetSeconds)} SECOND)`;
202
- }
203
- };
204
- // packages/sql/src/dialects/registry.ts
205
- var SQL_DIALECTS = {
206
- postgres: postgresDialect,
207
- sqlite: sqliteDialect,
208
- mysql: mysqlDialect
209
- };
210
- function getSqlDialect(params) {
211
- return SQL_DIALECTS[params.dialect];
212
- }
213
- // packages/sql/src/codecs.ts
214
- import { VibeError, invalidStoredData } from "@vibeorm/schema";
215
- function enumMembershipCodec(params) {
216
- const { model, field, enum: enumName } = params;
217
- const members = new Set(params.members);
218
- return {
219
- encode: (value) => {
220
- if (typeof value === "string" && members.has(value))
221
- return value;
222
- throw new VibeError({
223
- code: "VIBE_VALIDATION",
224
- message: `Invalid value for enum ${enumName} on ${model}.${field}`,
225
- meta: { model, field, enum: enumName }
226
- });
227
- },
228
- decode: (value) => {
229
- if (typeof value === "string" && members.has(value))
230
- return value;
231
- throw invalidStoredData({ model, field, enum: enumName, value });
232
- }
233
- };
234
- }
235
- function decodeIsNoop(params) {
236
- const { scalarType, wire } = params;
237
- if (wire === undefined)
238
- return false;
239
- if (scalarType === "DateTime")
240
- return wire.dateTime === "native";
241
- if (scalarType === "Json")
242
- return wire.json === "parsed";
243
- if (scalarType === "Int" || scalarType === "Float")
244
- return wire.numbers === "native";
245
- return false;
246
- }
247
- var identity = (value) => value;
248
- var passthroughCodec = { encode: identity, decode: identity };
249
- var numberCodec = {
250
- encode: identity,
251
- decode: (value) => typeof value === "string" ? Number(value) : value
252
- };
253
- var bigIntCodec = {
254
- encode: (value) => typeof value === "bigint" ? value.toString() : value,
255
- decode: (value) => {
256
- if (typeof value === "bigint")
257
- return value;
258
- if (typeof value === "string" || typeof value === "number")
259
- return BigInt(value);
260
- return value;
261
- }
262
- };
263
- var decimalCodec = {
264
- encode: (value) => typeof value === "number" || typeof value === "bigint" ? String(value) : value,
265
- decode: identity
266
- };
267
- var mysqlDecimalCodec = {
268
- encode: decimalCodec.encode,
269
- decode: (value) => {
270
- if (typeof value !== "string" || !value.includes("."))
271
- return value;
272
- const trimmed = value.replace(/0+$/, "").replace(/\.$/, "");
273
- return trimmed === "" || trimmed === "-" ? "0" : trimmed;
274
- }
275
- };
276
- var ZONE_LESS_DATETIME = /^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}(?::\d{2}(?:\.\d{1,9})?)?$/;
277
- function parseDateTimeText(params) {
278
- const trimmed = params.text.trim();
279
- if (ZONE_LESS_DATETIME.test(trimmed)) {
280
- const asUtc = new Date(`${trimmed.replace(" ", "T").replace(/(\.\d{3})\d+$/, "$1")}Z`);
281
- if (!Number.isNaN(asUtc.getTime()))
282
- return asUtc;
283
- }
284
- const parsed = new Date(trimmed);
285
- return Number.isNaN(parsed.getTime()) ? undefined : parsed;
286
- }
287
- var dateNativeCodec = {
288
- encode: identity,
289
- decode: (value) => {
290
- if (value instanceof Date)
291
- return value;
292
- if (typeof value === "string")
293
- return parseDateTimeText({ text: value }) ?? new Date(value);
294
- if (typeof value === "number")
295
- return new Date(value);
296
- return value;
1127
+ booleanLiteral: { true: "1", false: "0" },
1128
+ quoteIdent({ name }) {
1129
+ return `"${name.replaceAll('"', '""')}"`;
1130
+ },
1131
+ quoteStringLiteral({ value }) {
1132
+ return `'${value.replaceAll("'", "''")}'`;
1133
+ },
1134
+ placeholder() {
1135
+ return "?";
1136
+ },
1137
+ renderDbNow({ offsetSeconds, collector }) {
1138
+ if (offsetSeconds === 0)
1139
+ return `strftime('%Y-%m-%dT%H:%M:%fZ', 'now')`;
1140
+ return `strftime('%Y-%m-%dT%H:%M:%fZ', 'now', ${collector.add(offsetSeconds)} || ' seconds')`;
297
1141
  }
298
1142
  };
299
- var dateTextCodec = {
300
- encode: (value) => value instanceof Date ? value.toISOString() : value,
301
- decode: dateNativeCodec.decode
302
- };
303
- var mysqlDateCodec = {
304
- encode: (value) => {
305
- if (value instanceof Date)
306
- return value;
307
- if (typeof value === "number") {
308
- const parsed = new Date(value);
309
- if (!Number.isNaN(parsed.getTime()))
310
- return parsed;
311
- return value;
312
- }
313
- if (typeof value === "string")
314
- return parseDateTimeText({ text: value }) ?? value;
315
- return value;
1143
+ // packages/sql/src/dialects/mysql.ts
1144
+ var mysqlDialect = {
1145
+ name: "mysql",
1146
+ capabilities: MYSQL_CAPABILITIES,
1147
+ inArrayStrategy: "expand",
1148
+ tupleInStrategy: "rowValue",
1149
+ upsertForm: "onDuplicateKey",
1150
+ conflictKeyEquality: {
1151
+ foldingScalars: ["Decimal"],
1152
+ defaultStringMaxLength: 191,
1153
+ textIsBinaryByDefault: false,
1154
+ duplicatesInOneStatement: "lastWriteWins"
316
1155
  },
317
- decode: dateNativeCodec.decode
318
- };
319
- var booleanIntCodec = {
320
- encode: (value) => typeof value === "boolean" ? value ? 1 : 0 : value,
321
- decode: (value) => typeof value === "number" ? value !== 0 : value
322
- };
323
- var booleanNativeInCodec = {
324
- encode: identity,
325
- decode: booleanIntCodec.decode
326
- };
327
- var jsonCodec = {
328
- encode: (value) => JSON.stringify(value),
329
- decode: (value) => {
330
- if (typeof value !== "string")
331
- return value;
332
- try {
333
- return JSON.parse(value);
334
- } catch (cause) {
335
- throw new VibeError({
336
- code: "VIBE_INTERNAL",
337
- message: "a Json column returned text that is not valid JSON",
338
- meta: { value },
339
- cause
340
- });
341
- }
1156
+ excludedRowRef: { form: "rowAlias", alias: "new" },
1157
+ doNothingForm: "insertIgnore",
1158
+ insensitiveLike: "lower",
1159
+ strictMatchStrategy: "binary",
1160
+ orderByNullsMode: "emulate",
1161
+ offsetRequiresLimit: { limitText: "18446744073709551615" },
1162
+ likeEscapeClause: null,
1163
+ jsonPathMode: "mysqlExtract",
1164
+ supportedJsonOps: [
1165
+ "equals",
1166
+ "string_contains",
1167
+ "string_starts_with",
1168
+ "string_ends_with",
1169
+ "array_contains",
1170
+ "array_starts_with",
1171
+ "array_ends_with"
1172
+ ],
1173
+ jsonParamCast: "",
1174
+ lastInsertIdSql: "SELECT LAST_INSERT_ID() AS `id`",
1175
+ rowLockSyntax: {
1176
+ strengths: Object.freeze({ update: "FOR UPDATE", share: "FOR SHARE" }),
1177
+ wait: Object.freeze({ wait: "", nowait: " NOWAIT", skipLocked: " SKIP LOCKED" })
1178
+ },
1179
+ advisoryLockFunctions: null,
1180
+ rowDefaultKeyword: true,
1181
+ paramBudget: 1e4,
1182
+ booleanLiteral: { true: "1", false: "0" },
1183
+ quoteIdent({ name }) {
1184
+ return `\`${name.replaceAll("`", "``")}\``;
1185
+ },
1186
+ quoteStringLiteral({ value }) {
1187
+ return `'${value.replaceAll("\\", "\\\\").replaceAll("'", "''")}'`;
1188
+ },
1189
+ placeholder() {
1190
+ return "?";
1191
+ },
1192
+ renderDbNow({ offsetSeconds, collector }) {
1193
+ if (offsetSeconds === 0)
1194
+ return "NOW(6)";
1195
+ return `DATE_ADD(NOW(6), INTERVAL ${collector.add(offsetSeconds)} SECOND)`;
342
1196
  }
343
1197
  };
344
- var POSTGRES_CODECS = {
345
- String: passthroughCodec,
346
- Boolean: passthroughCodec,
347
- Int: numberCodec,
348
- BigInt: bigIntCodec,
349
- Float: numberCodec,
350
- Decimal: decimalCodec,
351
- DateTime: dateNativeCodec,
352
- Json: jsonCodec,
353
- Bytes: passthroughCodec
354
- };
355
- var SQLITE_CODECS = {
356
- String: passthroughCodec,
357
- Boolean: booleanIntCodec,
358
- Int: numberCodec,
359
- BigInt: bigIntCodec,
360
- Float: numberCodec,
361
- Decimal: decimalCodec,
362
- DateTime: dateTextCodec,
363
- Json: jsonCodec,
364
- Bytes: passthroughCodec
365
- };
366
- var MYSQL_CODECS = {
367
- String: passthroughCodec,
368
- Boolean: booleanNativeInCodec,
369
- Int: numberCodec,
370
- BigInt: bigIntCodec,
371
- Float: numberCodec,
372
- Decimal: mysqlDecimalCodec,
373
- DateTime: mysqlDateCodec,
374
- Json: jsonCodec,
375
- Bytes: passthroughCodec
376
- };
377
- var DIALECT_CODECS = {
378
- postgres: POSTGRES_CODECS,
379
- sqlite: SQLITE_CODECS,
380
- mysql: MYSQL_CODECS
1198
+ // packages/sql/src/dialects/registry.ts
1199
+ var SQL_DIALECTS = {
1200
+ postgres: postgresDialect,
1201
+ sqlite: sqliteDialect,
1202
+ mysql: mysqlDialect
381
1203
  };
1204
+ function getSqlDialect(params) {
1205
+ return SQL_DIALECTS[params.dialect];
1206
+ }
382
1207
  // packages/sql/src/dialects/diagnostics.ts
383
- import { unsupportedCapability } from "@vibeorm/schema";
1208
+ import { unsupportedCapability as unsupportedCapability4 } from "@vibeorm/schema";
384
1209
  var QUERIES = Object.freeze({
385
1210
  postgresJsonWire: { dialect: "postgres", text: `SELECT '{"__vibeorm":1}'::jsonb AS "probe"` },
386
1211
  sqliteTriggers: { dialect: "sqlite", text: "SELECT name, tbl_name, sql FROM sqlite_master WHERE type = 'trigger'" }
@@ -388,7 +1213,7 @@ var QUERIES = Object.freeze({
388
1213
  function renderDiagnostic(params) {
389
1214
  const query = QUERIES[params.query];
390
1215
  if (params.dialect.name !== query.dialect) {
391
- throw unsupportedCapability({ dialect: params.dialect.name, feature: params.query, hint: `this diagnostic requires ${query.dialect}` });
1216
+ throw unsupportedCapability4({ dialect: params.dialect.name, feature: params.query, hint: `this diagnostic requires ${query.dialect}` });
392
1217
  }
393
1218
  return { text: query.text, values: [], returning: "none" };
394
1219
  }
@@ -397,26 +1222,7 @@ function isFixedDiagnostic(params) {
397
1222
  }
398
1223
 
399
1224
  // packages/sql/src/dialects/postgres-diagnostics.ts
400
- import { VibeError as VibeError2, unsupportedCapability as unsupportedCapability2 } from "@vibeorm/schema";
401
-
402
- // packages/sql/src/params.ts
403
- class ParamCollector {
404
- dialect;
405
- values;
406
- constructor(dialect) {
407
- this.dialect = dialect;
408
- this.values = [];
409
- }
410
- add(value) {
411
- this.values.push(value);
412
- return this.dialect.placeholder({ index: this.values.length });
413
- }
414
- addMany(values) {
415
- return values.map((value) => this.add(value));
416
- }
417
- }
418
-
419
- // packages/sql/src/dialects/postgres-diagnostics.ts
1225
+ import { VibeError as VibeError6, unsupportedCapability as unsupportedCapability5 } from "@vibeorm/schema";
420
1226
  function renderPostgresExplain(params) {
421
1227
  const { dialect, query, options } = params;
422
1228
  requirePostgres({ dialect, feature: "query plan inspection (EXPLAIN)" });
@@ -451,7 +1257,7 @@ var POSTGRES_WORKLOAD_LIMITS = Object.freeze({
451
1257
  });
452
1258
  function requirePostgres(params) {
453
1259
  if (params.dialect.name !== "postgres") {
454
- throw unsupportedCapability2({
1260
+ throw unsupportedCapability5({
455
1261
  dialect: params.dialect.name,
456
1262
  feature: params.feature,
457
1263
  hint: "plan inspection and workload statistics are PostgreSQL-shaped; portable operation tracing works on every dialect"
@@ -469,7 +1275,7 @@ function renderPostgresWorkload(params) {
469
1275
  if (query === "statementSnapshot") {
470
1276
  const bounded = limit ?? 20;
471
1277
  if (!Number.isInteger(bounded) || bounded < POSTGRES_WORKLOAD_LIMITS.minLimit || bounded > POSTGRES_WORKLOAD_LIMITS.maxLimit) {
472
- throw new VibeError2({
1278
+ throw new VibeError6({
473
1279
  code: "VIBE_CONFIG",
474
1280
  message: `workload snapshot limit must be an integer between ${POSTGRES_WORKLOAD_LIMITS.minLimit} and ${POSTGRES_WORKLOAD_LIMITS.maxLimit}, got ${String(limit)}`,
475
1281
  meta: { limit }
@@ -488,7 +1294,7 @@ function renderPostgresWorkload(params) {
488
1294
  function requireArg(params) {
489
1295
  const { value, name, query } = params;
490
1296
  if (value === undefined || value.length === 0) {
491
- throw new VibeError2({
1297
+ throw new VibeError6({
492
1298
  code: "VIBE_CONFIG",
493
1299
  message: `the "${query}" diagnostic read requires a ${name}`,
494
1300
  meta: { query, argument: name }
@@ -498,10 +1304,10 @@ function requireArg(params) {
498
1304
  }
499
1305
 
500
1306
  // packages/sql/src/dialects/postgres-rls-readiness.ts
501
- import { unsupportedCapability as unsupportedCapability4 } from "@vibeorm/schema";
1307
+ import { unsupportedCapability as unsupportedCapability7 } from "@vibeorm/schema";
502
1308
 
503
1309
  // packages/sql/src/dialects/postgres-rls.ts
504
- import { RlsContextError, VibeError as VibeError3, assertValidSchemaIR, unsupportedCapability as unsupportedCapability3, validateRlsContextIR } from "@vibeorm/schema";
1310
+ import { RlsContextError, VibeError as VibeError7, assertValidSchemaIR, unsupportedCapability as unsupportedCapability6, validateRlsContextIR } from "@vibeorm/schema";
505
1311
  var NATIVE_RLS = Object.freeze({ policyName: "vibeorm_managed", tableSchema: "public", settingPrefix: "vibeorm.", parentAlias: "parent" });
506
1312
  var COLUMN_TYPES = {
507
1313
  String: { default: "text", native: { text: "text", varchar: "varchar", charactervarying: "varchar", uuid: "uuid" } },
@@ -514,20 +1320,20 @@ var COLUMN_TYPES = {
514
1320
  };
515
1321
  function requireRls(params) {
516
1322
  if (!params.dialect.capabilities.nativeRowLevelSecurity) {
517
- throw unsupportedCapability3({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity" });
1323
+ throw unsupportedCapability6({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity" });
518
1324
  }
519
1325
  }
520
- function identifier(params) {
1326
+ function identifier2(params) {
521
1327
  if (typeof params.name !== "string" || params.name.length === 0 || params.name.includes("\x00") || new TextEncoder().encode(params.name).length > 63) {
522
- throw new VibeError3({ code: "VIBE_SCHEMA_INVALID", message: "Native RLS identifiers must be nonempty PostgreSQL identifiers of at most 63 bytes" });
1328
+ throw new VibeError7({ code: "VIBE_SCHEMA_INVALID", message: "Native RLS identifiers must be nonempty PostgreSQL identifiers of at most 63 bytes" });
523
1329
  }
524
1330
  return params.dialect.quoteIdent({ name: params.name });
525
1331
  }
526
1332
  function tableRef(params) {
527
1333
  if (params.schema !== undefined && params.schema !== "public" && params.schema !== "pg_temp") {
528
- throw unsupportedCapability3({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity", hint: "Only public tables and temporary policy probes are supported" });
1334
+ throw unsupportedCapability6({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity", hint: "Only public tables and temporary policy probes are supported" });
529
1335
  }
530
- return `${identifier({ dialect: params.dialect, name: params.schema ?? NATIVE_RLS.tableSchema })}.${identifier({ dialect: params.dialect, name: params.table })}`;
1336
+ return `${identifier2({ dialect: params.dialect, name: params.schema ?? NATIVE_RLS.tableSchema })}.${identifier2({ dialect: params.dialect, name: params.table })}`;
531
1337
  }
532
1338
  function query(params) {
533
1339
  return { text: params.text, values: params.values ?? [], returning: "none" };
@@ -550,7 +1356,7 @@ function renderRlsNoForce({ dialect, table }) {
550
1356
  }
551
1357
  function renderRlsDropPolicy({ dialect, table, policyName }) {
552
1358
  requireRls({ dialect });
553
- const name = identifier({ dialect, name: policyName ?? NATIVE_RLS.policyName });
1359
+ const name = identifier2({ dialect, name: policyName ?? NATIVE_RLS.policyName });
554
1360
  return query({ text: `DROP POLICY ${name} ON ${tableRef({ dialect, table })}` });
555
1361
  }
556
1362
  function renderRlsColumnType(params) {
@@ -566,14 +1372,14 @@ function renderRlsColumnType(params) {
566
1372
  if (native !== undefined)
567
1373
  return native;
568
1374
  }
569
- throw unsupportedCapability3({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity", hint: `Unsupported physical ownership type on field ${field.name}` });
1375
+ throw unsupportedCapability6({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity", hint: `Unsupported physical ownership type on field ${field.name}` });
570
1376
  }
571
1377
  function protectedModel(params) {
572
1378
  requireRls(params);
573
1379
  assertValidSchemaIR({ schema: params.schema });
574
1380
  const model = params.schema.models.find((entry) => entry.name === params.model);
575
1381
  if (model === undefined || model.rls === undefined || model.rls === "unprotected") {
576
- throw new VibeError3({ code: "VIBE_SCHEMA_INVALID", message: "Native RLS policy rendering requires a protected model", meta: { model: params.model } });
1382
+ throw new VibeError7({ code: "VIBE_SCHEMA_INVALID", message: "Native RLS policy rendering requires a protected model", meta: { model: params.model } });
577
1383
  }
578
1384
  return model;
579
1385
  }
@@ -589,7 +1395,7 @@ function predicate(params) {
589
1395
  if (typeof rule === "object" && "where" in rule) {
590
1396
  return Object.entries(rule.where).map(([name, ref]) => {
591
1397
  const field = model.fields.find((entry) => entry.name === name);
592
- const column = identifier({ dialect, name: field.dbName ?? field.name });
1398
+ const column = identifier2({ dialect, name: field.dbName ?? field.name });
593
1399
  const physicalType = renderRlsColumnType({ dialect, field });
594
1400
  return `(${column} = NULLIF(current_setting('${NATIVE_RLS.settingPrefix}${ref.context}', true), '')::${physicalType})`;
595
1401
  }).join(" AND ");
@@ -597,21 +1403,21 @@ function predicate(params) {
597
1403
  if (typeof rule === "object" && "through" in rule) {
598
1404
  const relation = model.relations.find((entry) => entry.name === rule.through);
599
1405
  const parent = schema.models.find((entry) => entry.name === relation.targetModel);
600
- const alias = identifier({ dialect, name: NATIVE_RLS.parentAlias });
601
- const child = identifier({ dialect, name: model.dbName ?? model.name });
1406
+ const alias = identifier2({ dialect, name: NATIVE_RLS.parentAlias });
1407
+ const child = identifier2({ dialect, name: model.dbName ?? model.name });
602
1408
  const join = relation.fields.map((name, index) => {
603
1409
  const childField = model.fields.find((field) => field.name === name);
604
1410
  const parentField = parent.fields.find((field) => field.name === relation.references[index]);
605
- return `(${alias}.${identifier({ dialect, name: parentField.dbName ?? parentField.name })} = ${child}.${identifier({ dialect, name: childField.dbName ?? childField.name })})`;
1411
+ return `(${alias}.${identifier2({ dialect, name: parentField.dbName ?? parentField.name })} = ${child}.${identifier2({ dialect, name: childField.dbName ?? childField.name })})`;
606
1412
  }).join(" AND ");
607
1413
  return `EXISTS (SELECT 1 FROM ${tableRef({ dialect, table: parent.dbName ?? parent.name })} AS ${alias} WHERE ${join})`;
608
1414
  }
609
- throw new VibeError3({ code: "VIBE_SCHEMA_INVALID", message: "Native RLS requires a protected policy rule" });
1415
+ throw new VibeError7({ code: "VIBE_SCHEMA_INVALID", message: "Native RLS requires a protected policy rule" });
610
1416
  }
611
1417
  function renderRlsPolicy(params) {
612
1418
  const model = protectedModel(params);
613
1419
  const expression = predicate({ dialect: params.dialect, schema: params.schema, model });
614
- const name = identifier({ dialect: params.dialect, name: params.policyName ?? NATIVE_RLS.policyName });
1420
+ const name = identifier2({ dialect: params.dialect, name: params.policyName ?? NATIVE_RLS.policyName });
615
1421
  const table = tableRef({ dialect: params.dialect, table: model.dbName ?? model.name, schema: params.targetSchema });
616
1422
  return query({ text: `CREATE POLICY ${name} ON ${table} AS PERMISSIVE FOR ALL TO PUBLIC USING (${expression}) WITH CHECK (${expression})` });
617
1423
  }
@@ -640,7 +1446,7 @@ function renderRlsContext(params) {
640
1446
  function renderRlsRole(params) {
641
1447
  requireRls(params);
642
1448
  if (typeof params.role !== "string" || params.role.trim().length === 0 || params.role.includes("\x00")) {
643
- throw new VibeError3({ code: "VIBE_CONFIG", message: "PGlite native RLS requires a trusted nonempty runtime role name" });
1449
+ throw new VibeError7({ code: "VIBE_CONFIG", message: "PGlite native RLS requires a trusted nonempty runtime role name" });
644
1450
  }
645
1451
  const collector = new ParamCollector(params.dialect);
646
1452
  return query({ text: `SELECT set_config('role', ${collector.add(params.role)}, true)`, values: collector.values });
@@ -775,7 +1581,7 @@ ORDER BY c.relname, t.tgname`,
775
1581
  });
776
1582
  function requireRls2(params) {
777
1583
  if (!params.dialect.capabilities.nativeRowLevelSecurity) {
778
- throw unsupportedCapability4({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity" });
1584
+ throw unsupportedCapability7({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity" });
779
1585
  }
780
1586
  }
781
1587
  function renderRlsReadinessQuery(params) {
@@ -807,7 +1613,7 @@ function renderRlsPolicyPath(params) {
807
1613
  }
808
1614
 
809
1615
  // packages/sql/src/render.ts
810
- import { internalError, unsupportedCapability as unsupportedCapability5 } from "@vibeorm/schema";
1616
+ import { internalError as internalError2, unsupportedCapability as unsupportedCapability8 } from "@vibeorm/schema";
811
1617
 
812
1618
  // packages/sql/src/like.ts
813
1619
  function escapeLikePattern(params) {
@@ -853,6 +1659,8 @@ function renderStatement(params) {
853
1659
  }
854
1660
  case "select":
855
1661
  return renderSelect({ dialect, statement, collector });
1662
+ case "advisoryLock":
1663
+ return renderAdvisoryLock({ dialect, statement, collector });
856
1664
  case "insert":
857
1665
  return renderInsert({ dialect, statement, collector });
858
1666
  case "update":
@@ -888,7 +1696,7 @@ function quoteCol(dialect, column) {
888
1696
  const ref = column.table === undefined ? name : `${dialect.quoteIdent({ name: column.table })}.${name}`;
889
1697
  if (column.castReal === true) {
890
1698
  if (dialect.name !== "sqlite") {
891
- throw internalError({ message: "ColRef.castReal is a sqlite Decimal construct" });
1699
+ throw internalError2({ message: "ColRef.castReal is a sqlite Decimal construct" });
892
1700
  }
893
1701
  return `CAST(${ref} AS REAL)`;
894
1702
  }
@@ -902,7 +1710,7 @@ function quoteProjection(dialect, column) {
902
1710
  return `${base} AS ${dialect.quoteIdent({ name: column.alias })}`;
903
1711
  }
904
1712
  if (dialect.name !== "postgres") {
905
- throw internalError({ message: "ColRef.transform is a postgres lateral-join construct" });
1713
+ throw internalError2({ message: "ColRef.transform is a postgres lateral-join construct" });
906
1714
  }
907
1715
  const ref = quoteCol(dialect, column);
908
1716
  const alias = dialect.quoteIdent({ name: column.name });
@@ -918,10 +1726,10 @@ function renderTable(dialect, table) {
918
1726
  }
919
1727
  function assertCount(value, what) {
920
1728
  if (!Number.isSafeInteger(value) || value < 0) {
921
- throw internalError({ message: `${what} must be a non-negative integer, got ${value}` });
1729
+ throw internalError2({ message: `${what} must be a non-negative integer, got ${value}` });
922
1730
  }
923
1731
  }
924
- var COMPARISON_SQL = {
1732
+ var COMPARISON_SQL2 = {
925
1733
  eq: "=",
926
1734
  ne: "<>",
927
1735
  gt: ">",
@@ -950,7 +1758,7 @@ function renderAggRef(dialect, agg) {
950
1758
  function assertScalarArrays(dialect, feature) {
951
1759
  if (dialect.capabilities.scalarArrays)
952
1760
  return;
953
- throw unsupportedCapability5({
1761
+ throw unsupportedCapability8({
954
1762
  dialect: dialect.name,
955
1763
  feature,
956
1764
  hint: "only postgres has native array columns \u2014 model the list as a related table instead"
@@ -960,9 +1768,9 @@ function renderExpr(params) {
960
1768
  const { dialect, expr, collector } = params;
961
1769
  switch (expr.kind) {
962
1770
  case "cmp": {
963
- const op = COMPARISON_SQL[expr.op];
1771
+ const op = COMPARISON_SQL2[expr.op];
964
1772
  if (op === undefined) {
965
- throw internalError({ message: `unknown comparison operator "${expr.op}"` });
1773
+ throw internalError2({ message: `unknown comparison operator "${expr.op}"` });
966
1774
  }
967
1775
  if (isDbNowMarker(expr.value)) {
968
1776
  return `${quoteCol(dialect, expr.column)} ${op} ${dialect.renderDbNow({ offsetSeconds: expr.value.offsetSeconds, collector })}`;
@@ -970,9 +1778,9 @@ function renderExpr(params) {
970
1778
  return `${quoteCol(dialect, expr.column)} ${op} ${collector.add(expr.value)}`;
971
1779
  }
972
1780
  case "cmpCol": {
973
- const op = COMPARISON_SQL[expr.op];
1781
+ const op = COMPARISON_SQL2[expr.op];
974
1782
  if (op === undefined) {
975
- throw internalError({ message: `unknown comparison operator "${expr.op}"` });
1783
+ throw internalError2({ message: `unknown comparison operator "${expr.op}"` });
976
1784
  }
977
1785
  return `${quoteCol(dialect, expr.left)} ${op} ${quoteCol(dialect, expr.right)}`;
978
1786
  }
@@ -992,11 +1800,11 @@ function renderExpr(params) {
992
1800
  }
993
1801
  case "tupleIn": {
994
1802
  if (expr.columns.length === 0) {
995
- throw internalError({ message: "tupleIn needs at least one column" });
1803
+ throw internalError2({ message: "tupleIn needs at least one column" });
996
1804
  }
997
1805
  for (const row of expr.rows) {
998
1806
  if (row.length !== expr.columns.length) {
999
- throw internalError({
1807
+ throw internalError2({
1000
1808
  message: `tupleIn row has ${row.length} values for ${expr.columns.length} columns`
1001
1809
  });
1002
1810
  }
@@ -1068,9 +1876,9 @@ function renderExpr(params) {
1068
1876
  case "not":
1069
1877
  return `NOT (${renderExpr({ dialect, expr: expr.operand, collector })})`;
1070
1878
  case "aggCmp": {
1071
- const op = COMPARISON_SQL[expr.op];
1879
+ const op = COMPARISON_SQL2[expr.op];
1072
1880
  if (op === undefined) {
1073
- throw internalError({ message: `unknown comparison operator "${expr.op}"` });
1881
+ throw internalError2({ message: `unknown comparison operator "${expr.op}"` });
1074
1882
  }
1075
1883
  return `${renderAggRef(dialect, expr.agg)} ${op} ${collector.add(expr.value)}`;
1076
1884
  }
@@ -1098,6 +1906,8 @@ function renderExpr(params) {
1098
1906
  const sub = renderSelect({ dialect, statement: expr.select, collector });
1099
1907
  return `${expr.negate === true ? "NOT " : ""}EXISTS (${sub.text})`;
1100
1908
  }
1909
+ case "composed":
1910
+ return `(${renderSqlFragment({ dialect, fragment: expr.fragment, collector })})`;
1101
1911
  case "fragment":
1102
1912
  return renderFragment({ dialect, fragment: expr.fragment, collector });
1103
1913
  case "raw":
@@ -1134,7 +1944,7 @@ function jsonPathString(path) {
1134
1944
  function jsonStringPattern(params) {
1135
1945
  const { expr } = params;
1136
1946
  if (typeof expr.value !== "string") {
1137
- throw internalError({ message: `json ${expr.op} expects a string operand` });
1947
+ throw internalError2({ message: `json ${expr.op} expects a string operand` });
1138
1948
  }
1139
1949
  if (expr.op === "string_contains")
1140
1950
  return containsPattern({ raw: expr.value });
@@ -1158,7 +1968,7 @@ function renderJsonLike(params) {
1158
1968
  function renderJsonFilter(params) {
1159
1969
  const { dialect, expr, collector } = params;
1160
1970
  if (!dialect.supportedJsonOps.includes(expr.op)) {
1161
- throw unsupportedCapability5({
1971
+ throw unsupportedCapability8({
1162
1972
  dialect: dialect.name,
1163
1973
  feature: `the json filter "${expr.op}"`,
1164
1974
  hint: `this dialect can express: ${dialect.supportedJsonOps.join(", ")}`
@@ -1213,7 +2023,7 @@ function renderJsonFilter(params) {
1213
2023
  const elements = Array.isArray(expr.value) ? expr.value : [expr.value];
1214
2024
  const tests = elements.map((element) => {
1215
2025
  if (element === null || typeof element === "object" && element !== null) {
1216
- throw unsupportedCapability5({
2026
+ throw unsupportedCapability8({
1217
2027
  dialect: dialect.name,
1218
2028
  feature: "json array_contains with object/array/null elements",
1219
2029
  hint: "sqlite expresses scalar-element containment only \u2014 deep structural containment needs postgres or mysql"
@@ -1225,7 +2035,7 @@ function renderJsonFilter(params) {
1225
2035
  return tests.length === 0 ? guard : `(${[guard, ...tests].join(" AND ")})`;
1226
2036
  }
1227
2037
  default:
1228
- throw internalError({ message: `json op "${expr.op}" leaked past supportedJsonOps on sqlite` });
2038
+ throw internalError2({ message: `json op "${expr.op}" leaked past supportedJsonOps on sqlite` });
1229
2039
  }
1230
2040
  }
1231
2041
  const column = quoteCol(dialect, expr.column);
@@ -1280,11 +2090,11 @@ function renderSelectExpr(params) {
1280
2090
  function renderPositionalRow(params) {
1281
2091
  const { dialect, join, subAlias } = params;
1282
2092
  if (join.select.columns.length === 0 || join.select.selectExpr !== undefined || (join.select.aggregates?.length ?? 0) > 0 || join.select.rowNumber !== undefined) {
1283
- throw internalError({ message: "positional lateral rows require an explicit column-only projection" });
2093
+ throw internalError2({ message: "positional lateral rows require an explicit column-only projection" });
1284
2094
  }
1285
2095
  const names = join.select.columns.map((column) => projectionOutputName({ column }));
1286
2096
  if (new Set(names).size !== names.length) {
1287
- throw internalError({ message: "positional lateral projection has duplicate output names" });
2097
+ throw internalError2({ message: "positional lateral projection has duplicate output names" });
1288
2098
  }
1289
2099
  const values = names.map((name) => `${subAlias}.${dialect.quoteIdent({ name })}`);
1290
2100
  const chunks = [];
@@ -1292,7 +2102,7 @@ function renderPositionalRow(params) {
1292
2102
  chunks.push(`json_build_array(${values.slice(index, index + JSON_TRANSPORT.chunkSize).join(", ")})`);
1293
2103
  }
1294
2104
  if (chunks.length > JSON_TRANSPORT.chunkSize) {
1295
- throw internalError({ message: "positional lateral projection exceeds the transport width limit" });
2105
+ throw internalError2({ message: "positional lateral projection exceeds the transport width limit" });
1296
2106
  }
1297
2107
  return chunks.length === 1 ? chunks[0] : `json_build_array(${chunks.join(", ")})`;
1298
2108
  }
@@ -1301,7 +2111,7 @@ function renderLateralJoin(params) {
1301
2111
  const inner = renderSelect({ dialect, statement: join.select, collector });
1302
2112
  if (join.agg === "count") {
1303
2113
  if (join.rowFormat === "positional" || join.select.columns.length !== 0 || join.select.selectExpr?.kind !== "countAll" || join.select.selectExpr.alias !== join.columnAlias || join.select.groupBy !== undefined || join.select.having !== undefined || join.select.limit !== undefined || join.select.offset !== undefined || join.select.distinctOn !== undefined) {
1304
- throw internalError({ message: "count lateral must return one ungrouped COUNT(*) under its column alias" });
2114
+ throw internalError2({ message: "count lateral must return one ungrouped COUNT(*) under its column alias" });
1305
2115
  }
1306
2116
  return `LEFT JOIN LATERAL (${inner.text}) AS ${dialect.quoteIdent({ name: join.alias })} ON true`;
1307
2117
  }
@@ -1336,10 +2146,10 @@ function applyReturning(params) {
1336
2146
  function renderSelect(params) {
1337
2147
  const { dialect, statement, collector } = params;
1338
2148
  if (statement.selectExpr !== undefined && statement.columns.length > 0 && statement.selectExpr.kind !== "countAll") {
1339
- throw internalError({ message: "only countAll can combine with a column list" });
2149
+ throw internalError2({ message: "only countAll can combine with a column list" });
1340
2150
  }
1341
2151
  if (statement.selectExpr !== undefined && statement.aggregates !== undefined && statement.aggregates.length > 0) {
1342
- throw internalError({ message: "selectExpr and aggregates cannot combine" });
2152
+ throw internalError2({ message: "selectExpr and aggregates cannot combine" });
1343
2153
  }
1344
2154
  const columnList = statement.columns.map((column) => quoteProjection(dialect, column));
1345
2155
  if (statement.selectExpr !== undefined) {
@@ -1353,7 +2163,7 @@ function renderSelect(params) {
1353
2163
  if (statement.rowNumber !== undefined) {
1354
2164
  const rowNumber = statement.rowNumber;
1355
2165
  if (rowNumber.partitionBy.length === 0) {
1356
- throw internalError({ message: "rowNumber needs at least one partition column" });
2166
+ throw internalError2({ message: "rowNumber needs at least one partition column" });
1357
2167
  }
1358
2168
  const partition = rowNumber.partitionBy.map((column) => quoteCol(dialect, column)).join(", ");
1359
2169
  const order = rowNumber.orderBy.length === 0 ? "" : ` ORDER BY ${rowNumber.orderBy.map((term) => renderOrderTerm({ dialect, term, collector })).join(", ")}`;
@@ -1362,7 +2172,7 @@ function renderSelect(params) {
1362
2172
  let distinctPrefix = "";
1363
2173
  if (statement.distinctOn !== undefined && statement.distinctOn.length > 0) {
1364
2174
  if (!dialect.capabilities.distinctOn) {
1365
- throw unsupportedCapability5({
2175
+ throw unsupportedCapability8({
1366
2176
  dialect: dialect.name,
1367
2177
  feature: "SELECT DISTINCT ON",
1368
2178
  hint: "postgres-only \u2014 the runtime emulates distinct client-side on this dialect"
@@ -1374,7 +2184,7 @@ function renderSelect(params) {
1374
2184
  let from;
1375
2185
  if (statement.fromSelect !== undefined) {
1376
2186
  if (statement.table.alias !== undefined) {
1377
- throw internalError({ message: "fromSelect uses table.name as the subquery alias \u2014 table.alias must be unset" });
2187
+ throw internalError2({ message: "fromSelect uses table.name as the subquery alias \u2014 table.alias must be unset" });
1378
2188
  }
1379
2189
  const inner = renderSelect({ dialect, statement: statement.fromSelect, collector });
1380
2190
  from = `(${inner.text}) AS ${dialect.quoteIdent({ name: statement.table.name })}`;
@@ -1385,7 +2195,7 @@ function renderSelect(params) {
1385
2195
  if (statement.innerJoins !== undefined && statement.innerJoins.length > 0) {
1386
2196
  for (const join of statement.innerJoins) {
1387
2197
  if (join.on.length === 0) {
1388
- throw internalError({ message: "inner join needs at least one ON pair" });
2198
+ throw internalError2({ message: "inner join needs at least one ON pair" });
1389
2199
  }
1390
2200
  const on = join.on.map((pair) => `${quoteCol(dialect, pair.left)} = ${quoteCol(dialect, pair.right)}`).join(" AND ");
1391
2201
  text += ` INNER JOIN ${renderTable(dialect, join.table)} ON ${on}`;
@@ -1393,7 +2203,7 @@ function renderSelect(params) {
1393
2203
  }
1394
2204
  if (statement.lateralJoins !== undefined && statement.lateralJoins.length > 0) {
1395
2205
  if (!dialect.capabilities.lateralJoin) {
1396
- throw unsupportedCapability5({
2206
+ throw unsupportedCapability8({
1397
2207
  dialect: dialect.name,
1398
2208
  feature: "LATERAL JOIN relation loading",
1399
2209
  hint: 'use the portable "query" relation strategy on this dialect'
@@ -1427,22 +2237,24 @@ function renderSelect(params) {
1427
2237
  }
1428
2238
  text += ` OFFSET ${statement.offset}`;
1429
2239
  }
1430
- if (statement.lockForUpdate === true && dialect.selectForUpdate !== null) {
1431
- text += dialect.selectForUpdate;
2240
+ if (statement.rowLock !== undefined) {
2241
+ const clause = renderRowLockClause({ dialect, lock: statement.rowLock });
2242
+ if (clause !== null)
2243
+ text += ` ${clause}`;
1432
2244
  }
1433
2245
  return { text, values: collector.values, returning: "none" };
1434
2246
  }
1435
2247
  function renderInsert(params) {
1436
2248
  const { dialect, statement, collector } = params;
1437
2249
  if (statement.columns.length === 0) {
1438
- throw internalError({ message: "insert requires at least one column" });
2250
+ throw internalError2({ message: "insert requires at least one column" });
1439
2251
  }
1440
2252
  if (statement.rows.length === 0) {
1441
- throw internalError({ message: "insert requires at least one row" });
2253
+ throw internalError2({ message: "insert requires at least one row" });
1442
2254
  }
1443
2255
  for (const row of statement.rows) {
1444
2256
  if (row.length !== statement.columns.length) {
1445
- throw internalError({
2257
+ throw internalError2({
1446
2258
  message: `insert row has ${row.length} values but ${statement.columns.length} columns`
1447
2259
  });
1448
2260
  }
@@ -1460,14 +2272,14 @@ function renderInsert(params) {
1460
2272
  if (!isSqlDefaultCell(value))
1461
2273
  return collector.add(value);
1462
2274
  if (!dialect.rowDefaultKeyword) {
1463
- throw internalError({
2275
+ throw internalError2({
1464
2276
  message: `${dialect.name} cannot render a per-row DEFAULT cell \u2014 rows must be grouped by column set`
1465
2277
  });
1466
2278
  }
1467
2279
  return "DEFAULT";
1468
2280
  }).join(", ")})`).join(", ");
1469
2281
  let text = `${insertKeyword} ${renderTable(dialect, statement.table)} (${columnList}) VALUES ${rowsSql}`;
1470
- if (onConflict !== undefined && !("doNothing" in onConflict) && dialect.excludedRowRef.form === "rowAlias" && Object.values(onConflict.update).some((value) => isExcludedRef(value))) {
2282
+ if (onConflict !== undefined && !("doNothing" in onConflict) && dialect.excludedRowRef.form === "rowAlias" && Object.values(onConflict.update).some((value) => isExcludedRef(value) || isSqlFragment(value) && sqlUsesIncoming({ fragment: value }))) {
1471
2283
  text += ` AS ${dialect.quoteIdent({ name: dialect.excludedRowRef.alias })}`;
1472
2284
  }
1473
2285
  if (onConflict !== undefined) {
@@ -1476,7 +2288,7 @@ function renderInsert(params) {
1476
2288
  let target = "";
1477
2289
  if (onConflict.constraint !== undefined) {
1478
2290
  if (dialect.name !== "postgres") {
1479
- throw unsupportedCapability5({
2291
+ throw unsupportedCapability8({
1480
2292
  dialect: dialect.name,
1481
2293
  feature: "a named-constraint conflict target (ON CONFLICT ON CONSTRAINT)",
1482
2294
  hint: "only postgres names constraints in conflict targets \u2014 use a column-list target"
@@ -1491,7 +2303,7 @@ function renderInsert(params) {
1491
2303
  }
1492
2304
  text += ` ON CONFLICT${target} DO NOTHING`;
1493
2305
  } else if (onConflict.constraint !== undefined || onConflict.conflictWhere !== undefined) {
1494
- throw unsupportedCapability5({
2306
+ throw unsupportedCapability8({
1495
2307
  dialect: dialect.name,
1496
2308
  feature: "a refined conflict target (constraint / partial-index predicate) on INSERT IGNORE",
1497
2309
  hint: "mysql skips on any duplicate key \u2014 drop the refinement or gate the call per dialect"
@@ -1500,23 +2312,23 @@ function renderInsert(params) {
1500
2312
  } else {
1501
2313
  const updates = Object.entries(onConflict.update);
1502
2314
  if (updates.length === 0) {
1503
- throw internalError({ message: "upsert update must set at least one column" });
2315
+ throw internalError2({ message: "upsert update must set at least one column" });
1504
2316
  }
1505
2317
  const selfRef = dialect.quoteIdent({ name: statement.table.name });
1506
- const assignments = renderAssignments({ dialect, entries: updates, collector, selfRef, allowExcluded: true });
2318
+ const assignments = renderAssignments({ dialect, entries: updates, collector, selfRef, allowExcluded: true, tableName: statement.table.name });
1507
2319
  if (dialect.upsertForm === "onConflict") {
1508
2320
  if (onConflict.lastInsertIdColumn !== undefined) {
1509
- throw internalError({ message: "lastInsertIdColumn is a mysql (onDuplicateKey) construct" });
2321
+ throw internalError2({ message: "lastInsertIdColumn is a mysql (onDuplicateKey) construct" });
1510
2322
  }
1511
2323
  if (onConflict.constraint !== undefined && dialect.name !== "postgres") {
1512
- throw unsupportedCapability5({
2324
+ throw unsupportedCapability8({
1513
2325
  dialect: dialect.name,
1514
2326
  feature: "a named-constraint conflict target (ON CONFLICT ON CONSTRAINT)",
1515
2327
  hint: "sqlite conflict targets are column lists \u2014 name the indexed columns instead"
1516
2328
  });
1517
2329
  }
1518
2330
  if (onConflict.constraint !== undefined && onConflict.conflictWhere !== undefined) {
1519
- throw internalError({
2331
+ throw internalError2({
1520
2332
  message: "a named-constraint conflict target already identifies the index \u2014 it cannot carry a partial predicate too"
1521
2333
  });
1522
2334
  }
@@ -1525,14 +2337,14 @@ function renderInsert(params) {
1525
2337
  text += ` ON CONFLICT${target} DO UPDATE SET ${assignments}`;
1526
2338
  } else {
1527
2339
  if (onConflict.constraint !== undefined) {
1528
- throw unsupportedCapability5({
2340
+ throw unsupportedCapability8({
1529
2341
  dialect: dialect.name,
1530
2342
  feature: "a named-constraint conflict target on ON DUPLICATE KEY UPDATE",
1531
2343
  hint: "mysql's ODKU fires on any duplicate key \u2014 no target can be named"
1532
2344
  });
1533
2345
  }
1534
2346
  if (onConflict.conflictWhere !== undefined) {
1535
- throw unsupportedCapability5({
2347
+ throw unsupportedCapability8({
1536
2348
  dialect: dialect.name,
1537
2349
  feature: "a partial-index conflict target on ON DUPLICATE KEY UPDATE",
1538
2350
  hint: "mysql's ODKU fires on any duplicate key \u2014 a partial arbiter cannot be selected; gate the call per dialect"
@@ -1550,7 +2362,7 @@ function renderUpdate(params) {
1550
2362
  const { dialect, statement, collector } = params;
1551
2363
  const assignments = Object.entries(statement.set);
1552
2364
  if (assignments.length === 0) {
1553
- throw internalError({ message: "update must set at least one column" });
2365
+ throw internalError2({ message: "update must set at least one column" });
1554
2366
  }
1555
2367
  const setSql = renderAssignments({ dialect, entries: assignments, collector });
1556
2368
  let text = `UPDATE ${renderTable(dialect, statement.table)} SET ${setSql}`;
@@ -1571,9 +2383,14 @@ function renderAssignments(params) {
1571
2383
  return entries.map(([name, value]) => {
1572
2384
  const column = dialect.quoteIdent({ name });
1573
2385
  const read = selfRef === undefined ? column : `${selfRef}.${column}`;
2386
+ if (isSqlFragment(value)) {
2387
+ if (params.allowExcluded !== true || selfRef === undefined)
2388
+ throw internalError2({ message: "SQL assignment fragments require a conflict update" });
2389
+ return `${column} = ${renderSqlFragment({ dialect, fragment: value, collector, conflict: { table: params.tableName } })}`;
2390
+ }
1574
2391
  if (isExcludedRef(value)) {
1575
2392
  if (params.allowExcluded !== true) {
1576
- throw internalError({
2393
+ throw internalError2({
1577
2394
  message: `an incoming-row reference (${value.column}) is only meaningful inside an upsert's update arm`
1578
2395
  });
1579
2396
  }
@@ -1607,12 +2424,377 @@ function renderDelete(params) {
1607
2424
  const returning = applyReturning({ dialect, requested: statement.returning });
1608
2425
  return { text: text + returning.suffix, values: collector.values, returning: returning.status };
1609
2426
  }
2427
+ // packages/sql/src/dialects/sqlite-table-definition.ts
2428
+ import { VibeError as VibeError8 } from "@vibeorm/schema";
2429
+ function tableDefinitionParts(params) {
2430
+ const sql2 = params.sql;
2431
+ if (!/^\s*CREATE\s+(?:TEMP(?:ORARY)?\s+)?TABLE\b/i.test(sql2))
2432
+ throw unavailable();
2433
+ const parts = [];
2434
+ let start = -1;
2435
+ let depth = 0;
2436
+ let quote;
2437
+ for (let index = 0;index < sql2.length; index++) {
2438
+ const char = sql2[index];
2439
+ if (quote !== undefined) {
2440
+ if (char === quote) {
2441
+ if (quote !== "]" && sql2[index + 1] === quote)
2442
+ index++;
2443
+ else
2444
+ quote = undefined;
2445
+ }
2446
+ continue;
2447
+ }
2448
+ if (char === "'" || char === '"' || char === "`") {
2449
+ quote = char;
2450
+ continue;
2451
+ }
2452
+ if (char === "[") {
2453
+ quote = "]";
2454
+ continue;
2455
+ }
2456
+ if (char === "-" && sql2[index + 1] === "-") {
2457
+ const end = sql2.indexOf(`
2458
+ `, index + 2);
2459
+ if (end < 0)
2460
+ break;
2461
+ index = end;
2462
+ continue;
2463
+ }
2464
+ if (char === "/" && sql2[index + 1] === "*") {
2465
+ const end = sql2.indexOf("*/", index + 2);
2466
+ if (end < 0)
2467
+ break;
2468
+ index = end + 1;
2469
+ continue;
2470
+ }
2471
+ if (char === "(") {
2472
+ if (depth === 0)
2473
+ start = index + 1;
2474
+ depth++;
2475
+ } else if (char === ")") {
2476
+ depth--;
2477
+ if (depth === 0 && start >= 0) {
2478
+ parts.push(sql2.slice(start, index).trim());
2479
+ return { clauses: parts, suffix: sql2.slice(index + 1) };
2480
+ }
2481
+ } else if (char === "," && depth === 1) {
2482
+ parts.push(sql2.slice(start, index).trim());
2483
+ start = index + 1;
2484
+ }
2485
+ }
2486
+ throw unavailable();
2487
+ }
2488
+ function unavailable() {
2489
+ return new VibeError8({ code: "VIBE_MIGRATION", message: "Cannot preserve table definition", meta: { reason: "table-definition-unavailable" } });
2490
+ }
2491
+ function sqliteColumnClauses(params) {
2492
+ const columns = [];
2493
+ for (const part of tableDefinitionParts(params).clauses) {
2494
+ const token = /^(?:"((?:[^"]|"")+)"|`((?:[^`]|``)+)`|\[([^\]]+)\]|([A-Za-z_][A-Za-z_0-9]*))/.exec(part);
2495
+ if (token === null)
2496
+ throw unavailable();
2497
+ if (token[4] !== undefined && /^(?:CONSTRAINT|PRIMARY|UNIQUE|CHECK|FOREIGN)$/i.test(token[4]))
2498
+ continue;
2499
+ const name = token[1]?.replaceAll('""', '"') ?? token[2]?.replaceAll("``", "`") ?? token[3] ?? token[4];
2500
+ columns.push({ name, sql: part });
2501
+ }
2502
+ return columns;
2503
+ }
2504
+ // packages/sql/src/dialects/constraint-owned-index.ts
2505
+ import { unsupportedCapability as unsupportedCapability9 } from "@vibeorm/schema";
2506
+ function renderConstraintOwnedIndexDrop(params) {
2507
+ if (params.dialect.name !== "postgres")
2508
+ throw unsupportedCapability9({ dialect: params.dialect.name, feature: "constraintOwnedIndexDrop" });
2509
+ const quote = (name) => params.dialect.quoteIdent({ name });
2510
+ return { text: `ALTER TABLE ${quote(params.table)} DROP CONSTRAINT ${quote(params.constraint)};`, values: [], returning: "none" };
2511
+ }
2512
+ // packages/sql/src/dialects/mysql-check-probe.ts
2513
+ import { unsupportedCapability as unsupportedCapability10 } from "@vibeorm/schema";
2514
+ function renderMysqlCheckProbe(params) {
2515
+ if (params.dialect.name !== "mysql")
2516
+ throw unsupportedCapability10({ dialect: params.dialect.name, feature: "temporaryCheckProbe" });
2517
+ const q = (name) => params.dialect.quoteIdent({ name });
2518
+ const probe = q(params.probe);
2519
+ switch (params.operation) {
2520
+ case "clone":
2521
+ if (params.table === undefined)
2522
+ throw unsupportedCapability10({ dialect: "mysql", feature: "temporaryCheckProbe", hint: "A source table is required" });
2523
+ return { text: `CREATE TEMPORARY TABLE ${probe} LIKE ${q(params.table.namespace)}.${q(params.table.name)}`, values: [], returning: "none" };
2524
+ case "add": {
2525
+ if (params.expression === undefined || params.definition === undefined)
2526
+ throw unsupportedCapability10({ dialect: "mysql", feature: "temporaryCheckProbe", hint: "An authored expression and temporary clone definition are required" });
2527
+ const parts = tableDefinitionParts({ sql: params.definition });
2528
+ return { text: `CREATE TEMPORARY TABLE ${probe} (
2529
+ ${[...parts.clauses, `CONSTRAINT ${probe} CHECK (${params.expression})`].join(`,
2530
+ `)}
2531
+ )${parts.suffix}`, values: [], returning: "none" };
2532
+ }
2533
+ case "read":
2534
+ return { text: `SHOW CREATE TABLE ${probe}`, values: [], returning: "none" };
2535
+ case "drop":
2536
+ return { text: `DROP TEMPORARY TABLE ${probe}`, values: [], returning: "none" };
2537
+ }
2538
+ }
2539
+ // packages/sql/src/dialects/migration-history.ts
2540
+ function renderMigrationHistoryQuery(params) {
2541
+ const q = (name) => params.dialect.quoteIdent({ name });
2542
+ return { text: `SELECT ${q("name")}, ${q("checksum")}, ${q("statement_index")}, ${q("error")} FROM ${q(params.namespace)}.${q("_vibe_migrations")} ORDER BY ${q("id")}`, values: [], returning: "none" };
2543
+ }
2544
+ // packages/sql/src/dialects/sqlite-constraints.ts
2545
+ var IDENTIFIER = '(?:"(?:[^" ]| |"")+"|`(?:[^`]|``)+`|\\[[^\\]]+\\]|[A-Za-z_][A-Za-z_0-9]*)';
2546
+ function identifier3(value) {
2547
+ const text = value.trim();
2548
+ if (text.startsWith('"'))
2549
+ return text.slice(1, -1).replaceAll('""', '"');
2550
+ if (text.startsWith("`"))
2551
+ return text.slice(1, -1).replaceAll("``", "`");
2552
+ if (text.startsWith("["))
2553
+ return text.slice(1, -1);
2554
+ return text;
2555
+ }
2556
+ function sqliteTableConstraints(params) {
2557
+ const result = [];
2558
+ for (const clause of tableDefinitionParts(params).clauses) {
2559
+ const named = new RegExp(`^CONSTRAINT\\s+(${IDENTIFIER})\\s+`, "i").exec(clause);
2560
+ const body = named === null ? clause : clause.slice(named[0].length);
2561
+ const kind = /^(CHECK|FOREIGN\s+KEY|UNIQUE|PRIMARY\s+KEY)\s*\(/i.exec(body);
2562
+ if (kind === null)
2563
+ continue;
2564
+ const type = /^CHECK$/i.test(kind[1]) ? "check" : /^FOREIGN/i.test(kind[1]) ? "foreignKey" : /^PRIMARY/i.test(kind[1]) ? "primaryKey" : "unique";
2565
+ const parts = tableDefinitionParts({ sql: `CREATE TABLE x ${body.slice(kind[0].indexOf("("))}` });
2566
+ const columns = type === "check" ? [] : parts.clauses.map(identifier3);
2567
+ result.push({ name: named === null ? `@sqlite:${type}:${result.length}` : identifier3(named[1]), named: named !== null, type, sql: body, columns });
2568
+ }
2569
+ return result;
2570
+ }
2571
+ // packages/sql/src/dialects/migration-visibility.ts
2572
+ import { unsupportedCapability as unsupportedCapability11 } from "@vibeorm/schema";
2573
+ function renderMigrationVisibility(params) {
2574
+ if (params.dialect.name !== "mysql")
2575
+ throw unsupportedCapability11({ dialect: params.dialect.name, feature: "mysqlCatalogVisibility" });
2576
+ const collector = new ParamCollector(params.dialect);
2577
+ const grantee = "CONCAT(QUOTE(SUBSTRING_INDEX(CURRENT_USER(), '@', 1)), '@', QUOTE(SUBSTRING_INDEX(CURRENT_USER(), '@', -1)))";
2578
+ return { text: `SELECT PRIVILEGE_TYPE AS privilege FROM information_schema.USER_PRIVILEGES WHERE GRANTEE = ${grantee} UNION SELECT PRIVILEGE_TYPE AS privilege FROM information_schema.SCHEMA_PRIVILEGES WHERE GRANTEE = ${grantee} AND ${collector.add(params.namespace)} LIKE TABLE_SCHEMA`, values: collector.values, returning: "none" };
2579
+ }
2580
+ // packages/sql/src/dialects/migration-inventory.ts
2581
+ import { unsupportedCapability as unsupportedCapability12 } from "@vibeorm/schema";
2582
+ function renderSqliteColumnInventory(params) {
2583
+ if (params.dialect.name !== "sqlite")
2584
+ throw unsupportedCapability12({ dialect: params.dialect.name, feature: "sqliteColumnInventory" });
2585
+ const collector = new ParamCollector(params.dialect);
2586
+ return { text: `SELECT name, hidden, pk, dflt_value AS definition FROM pragma_table_xinfo(${collector.add(params.table)}, ${collector.add(params.namespace)}) ORDER BY cid`, values: collector.values, returning: "none" };
2587
+ }
2588
+ function renderSqliteForeignKeyInventory(params) {
2589
+ if (params.dialect.name !== "sqlite")
2590
+ throw unsupportedCapability12({ dialect: params.dialect.name, feature: "sqliteForeignKeyInventory" });
2591
+ const collector = new ParamCollector(params.dialect);
2592
+ return { text: `SELECT id, seq, "from", "to", "table", on_update, on_delete, match FROM pragma_foreign_key_list(${collector.add(params.table)}, ${collector.add(params.namespace)}) ORDER BY id, seq`, values: collector.values, returning: "none" };
2593
+ }
2594
+ function renderSqliteRebuildObjects(params) {
2595
+ if (params.dialect.name !== "sqlite")
2596
+ throw unsupportedCapability12({ dialect: params.dialect.name, feature: "sqliteRebuildObjects" });
2597
+ return { text: "SELECT name, tbl_name, type, sql FROM main.sqlite_schema WHERE type IN ('index', 'trigger') AND sql IS NOT NULL ORDER BY type, name", values: [], returning: "none" };
2598
+ }
2599
+ function renderMigrationInventory(params) {
2600
+ const collector = new ParamCollector(params.dialect);
2601
+ const namespace = collector.add(params.namespace);
2602
+ if (params.dialect.name === "postgres") {
2603
+ return { text: `WITH relations AS (
2604
+ SELECT c.oid, c.relname, c.relkind FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
2605
+ WHERE n.nspname = ${namespace} AND c.relname <> '_vibe_migrations'
2606
+ AND NOT EXISTS (SELECT 1 FROM pg_catalog.pg_depend jd JOIN pg_catalog.pg_class jt ON jt.oid = jd.refobjid WHERE jd.classid = 'pg_class'::regclass AND jd.objid = c.oid AND jd.refclassid = 'pg_class'::regclass AND jt.relname = '_vibe_migrations' AND jt.relnamespace = c.relnamespace)
2607
+ AND NOT EXISTS (SELECT 1 FROM pg_catalog.pg_depend d WHERE d.classid = 'pg_class'::regclass AND d.objid = c.oid AND d.deptype = 'e')
2608
+ )
2609
+ SELECT CASE WHEN r.relkind IN ('v','m') THEN 'view' WHEN r.relkind = 'S' THEN 'sequence' ELSE 'table' END AS kind, r.relname::text AS name, NULL::text AS parent, NULL::text AS subtype, NULL::text AS signature, NULL::text AS backing FROM relations r WHERE r.relkind IN ('r','p','v','m','S')
2610
+ UNION ALL SELECT 'constraint', con.conname::text, r.relname::text, con.contype::text, NULL, NULL FROM relations r JOIN pg_catalog.pg_constraint con ON con.conrelid = r.oid WHERE con.contype IN ('c','f','x','u','p')
2611
+ UNION ALL SELECT 'index', ix.relname::text, r.relname::text, NULL, NULL, con.conname::text FROM relations r JOIN pg_catalog.pg_index i ON i.indrelid = r.oid JOIN pg_catalog.pg_class ix ON ix.oid = i.indexrelid LEFT JOIN pg_catalog.pg_constraint con ON con.conindid = i.indexrelid AND con.contype IN ('p','u','x')
2612
+ UNION ALL SELECT 'trigger', t.tgname::text, r.relname::text, NULL, NULL, NULL FROM relations r JOIN pg_catalog.pg_trigger t ON t.tgrelid = r.oid WHERE NOT t.tgisinternal
2613
+ UNION ALL SELECT 'column', a.attname::text, r.relname::text, NULL, NULL, NULL FROM relations r JOIN pg_catalog.pg_attribute a ON a.attrelid = r.oid WHERE a.attnum > 0 AND NOT a.attisdropped AND r.relkind IN ('r','p')
2614
+ UNION ALL SELECT CASE WHEN a.attgenerated <> '' THEN 'generatedColumn' ELSE 'columnDefault' END, a.attname::text, r.relname::text, NULL, NULL, NULL FROM relations r JOIN pg_catalog.pg_attribute a ON a.attrelid = r.oid JOIN pg_catalog.pg_attrdef d ON d.adrelid = r.oid AND d.adnum = a.attnum WHERE a.attnum > 0 AND NOT a.attisdropped
2615
+ UNION ALL SELECT 'function', p.proname::text, NULL, NULL, COALESCE((SELECT json_agg(json_build_object('namespace', tn.nspname, 'name', t.typname) ORDER BY a.ord)::text FROM unnest(p.proargtypes) WITH ORDINALITY a(oid, ord) JOIN pg_catalog.pg_type t ON t.oid = a.oid JOIN pg_catalog.pg_namespace tn ON tn.oid = t.typnamespace), '[]'), NULL FROM pg_catalog.pg_proc p JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace WHERE n.nspname = ${namespace} AND p.prokind = 'f' AND NOT EXISTS (SELECT 1 FROM pg_catalog.pg_depend d WHERE d.classid = 'pg_proc'::regclass AND d.objid = p.oid AND d.deptype = 'e')
2616
+ ORDER BY kind, parent, name`, values: collector.values, returning: "none" };
2617
+ }
2618
+ if (params.dialect.name === "mysql") {
2619
+ const n = () => collector.add(params.namespace);
2620
+ return { text: `SELECT CASE WHEN TABLE_TYPE = 'VIEW' THEN 'view' ELSE 'table' END AS kind, TABLE_NAME AS name, NULL AS parent, NULL AS subtype, NULL AS signature, NULL AS backing FROM information_schema.TABLES WHERE TABLE_SCHEMA = ${namespace} AND TABLE_NAME <> '_vibe_migrations'
2621
+ UNION ALL SELECT 'constraint', CONSTRAINT_NAME, TABLE_NAME, CONSTRAINT_TYPE, NULL, NULL FROM information_schema.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = ${n()} AND TABLE_NAME <> '_vibe_migrations'
2622
+ UNION ALL SELECT 'index', s.INDEX_NAME, s.TABLE_NAME, NULL, NULL, tc.CONSTRAINT_NAME FROM information_schema.STATISTICS s LEFT JOIN information_schema.TABLE_CONSTRAINTS tc ON tc.TABLE_SCHEMA = s.TABLE_SCHEMA AND tc.TABLE_NAME = s.TABLE_NAME AND tc.CONSTRAINT_NAME = s.INDEX_NAME AND tc.CONSTRAINT_TYPE IN ('UNIQUE', 'PRIMARY KEY') WHERE s.TABLE_SCHEMA = ${n()} AND s.TABLE_NAME <> '_vibe_migrations' GROUP BY s.INDEX_NAME, s.TABLE_NAME, tc.CONSTRAINT_NAME
2623
+ UNION ALL SELECT 'trigger', TRIGGER_NAME, EVENT_OBJECT_TABLE, NULL, NULL, NULL FROM information_schema.TRIGGERS WHERE TRIGGER_SCHEMA = ${n()}
2624
+ UNION ALL SELECT 'column', COLUMN_NAME, TABLE_NAME, NULL, NULL, NULL FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = ${n()} AND TABLE_NAME <> '_vibe_migrations'
2625
+ UNION ALL SELECT CASE WHEN GENERATION_EXPRESSION <> '' THEN 'generatedColumn' ELSE 'columnDefault' END, COLUMN_NAME, TABLE_NAME, NULL, NULL, NULL FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = ${n()} AND TABLE_NAME <> '_vibe_migrations' AND (COLUMN_DEFAULT IS NOT NULL OR GENERATION_EXPRESSION <> '')
2626
+ UNION ALL SELECT 'function', ROUTINE_NAME, NULL, NULL, '[]', NULL FROM information_schema.ROUTINES WHERE ROUTINE_SCHEMA = ${n()} AND ROUTINE_TYPE = 'FUNCTION'
2627
+ ORDER BY kind, parent, name`, values: collector.values, returning: "none" };
2628
+ }
2629
+ const schema = params.dialect.quoteIdent({ name: params.namespace });
2630
+ return { text: `SELECT type AS kind, name, tbl_name AS parent, sql AS definition FROM ${schema}.sqlite_schema WHERE name NOT LIKE 'sqlite_%' AND name <> '_vibe_migrations' ORDER BY type, tbl_name, name`, values: [], returning: "none" };
2631
+ }
2632
+ // packages/sql/src/dialects/migration-objects.ts
2633
+ import { unsupportedCapability as unsupportedCapability13 } from "@vibeorm/schema";
2634
+ function renderMigrationObjectQuery(params) {
2635
+ const { dialect, identity: object } = params;
2636
+ const collector = new ParamCollector(dialect);
2637
+ const physical = "table" in object ? object.table : object.name;
2638
+ const namespace = collector.add(physical.namespace);
2639
+ const name = collector.add(physical.name);
2640
+ let text;
2641
+ if (dialect.name === "postgres") {
2642
+ const relation = `FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace`;
2643
+ const where = `n.nspname = ${namespace} AND c.relname = ${name}`;
2644
+ switch (object.kind) {
2645
+ case "constraint": {
2646
+ const enforcement = object.constraintType === "foreignKey" ? `, (SELECT bool_and(t.tgenabled IN ('O', 'A')) FROM pg_catalog.pg_trigger t WHERE t.tgconstraint = con.oid) AS "triggersEnabled"` : "";
2647
+ text = `SELECT con.contype::text AS "type", pg_catalog.pg_get_constraintdef(con.oid, false) AS "definition", con.convalidated AS "validated", con.condeferrable AS "deferrable", con.condeferred AS "deferred"${enforcement} ${relation} JOIN pg_catalog.pg_constraint con ON con.conrelid = c.oid WHERE ${where} AND con.conname = ${collector.add(object.name)}`;
2648
+ break;
2649
+ }
2650
+ case "trigger":
2651
+ text = `SELECT pg_catalog.pg_get_triggerdef(t.oid, false) AS "definition", t.tgenabled::text AS "enabled", pn.nspname::text AS "functionNamespace", p.proname::text AS "functionName", pg_catalog.pg_get_function_identity_arguments(p.oid) AS "functionArguments" ${relation} JOIN pg_catalog.pg_trigger t ON t.tgrelid = c.oid JOIN pg_catalog.pg_proc p ON p.oid = t.tgfoid JOIN pg_catalog.pg_namespace pn ON pn.oid = p.pronamespace WHERE ${where} AND t.tgname = ${collector.add(object.name)} AND NOT t.tgisinternal`;
2652
+ break;
2653
+ case "function":
2654
+ text = `SELECT pg_catalog.pg_get_functiondef(p.oid) AS "definition", p.provolatile::text AS "volatility", p.prosecdef AS "securityDefiner", p.proisstrict AS "strict", p.proconfig::text AS "config", pg_catalog.pg_get_function_result(p.oid) AS "result" FROM pg_catalog.pg_proc p JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace WHERE n.nspname = ${namespace} AND p.proname = ${name} AND p.prokind = 'f' AND ARRAY(SELECT tn.nspname || '.' || t.typname FROM unnest(p.proargtypes) WITH ORDINALITY a(oid, ord) JOIN pg_catalog.pg_type t ON t.oid = a.oid JOIN pg_catalog.pg_namespace tn ON tn.oid = t.typnamespace ORDER BY a.ord) = ${collector.add(object.argumentTypes.map((type) => `${type.namespace}.${type.name}`))}::text[]`;
2655
+ break;
2656
+ case "view":
2657
+ text = `SELECT pg_catalog.pg_get_viewdef(c.oid, false) AS "definition", c.relkind::text AS "type", c.reloptions::text AS "options" ${relation} WHERE ${where} AND c.relkind IN ('v', 'm')`;
2658
+ break;
2659
+ case "sequence":
2660
+ text = `SELECT pg_catalog.format_type(s.seqtypid, NULL) AS "type", s.seqstart::text AS "start", s.seqincrement::text AS "increment", s.seqmax::text AS "max", s.seqmin::text AS "min", s.seqcache::text AS "cache", s.seqcycle AS "cycle" ${relation} JOIN pg_catalog.pg_sequence s ON s.seqrelid = c.oid WHERE ${where}`;
2661
+ break;
2662
+ case "column":
2663
+ case "columnDefault":
2664
+ case "generatedColumn":
2665
+ text = `SELECT pg_catalog.format_type(a.atttypid, a.atttypmod) AS "type", a.attnotnull AS "notNull", a.attgenerated::text AS "generated", a.attidentity::text AS "identity", pg_catalog.pg_get_expr(d.adbin, d.adrelid, false) AS "definition" ${relation} JOIN pg_catalog.pg_attribute a ON a.attrelid = c.oid LEFT JOIN pg_catalog.pg_attrdef d ON d.adrelid = c.oid AND d.adnum = a.attnum WHERE ${where} AND a.attname = ${collector.add(object.column)} AND a.attnum > 0 AND NOT a.attisdropped`;
2666
+ break;
2667
+ case "index":
2668
+ text = `SELECT pg_catalog.pg_get_indexdef(i.indexrelid, 0, false) AS "definition", i.indisunique AS "unique", i.indisvalid AS "valid", i.indisready AS "ready", con.conname::text AS "constraint", con.contype::text AS "constraintType" ${relation} JOIN pg_catalog.pg_index i ON i.indrelid = c.oid JOIN pg_catalog.pg_class ix ON ix.oid = i.indexrelid LEFT JOIN pg_catalog.pg_constraint con ON con.conindid = i.indexrelid AND con.contype IN ('p', 'u', 'x') WHERE ${where} AND ix.relname = ${collector.add(object.name)}`;
2669
+ break;
2670
+ case "table":
2671
+ text = `SELECT c.relkind::text AS "type", c.relrowsecurity AS "rls", c.relforcerowsecurity AS "forceRls" ${relation} WHERE ${where} AND c.relkind IN ('r', 'p')`;
2672
+ break;
2673
+ case "postgresExtension":
2674
+ text = `SELECT e.extversion::text AS "version", e.extrelocatable AS "relocatable" FROM pg_catalog.pg_extension e JOIN pg_catalog.pg_namespace n ON n.oid = e.extnamespace WHERE n.nspname = ${namespace} AND e.extname = ${name}`;
2675
+ break;
2676
+ }
2677
+ } else if (dialect.name === "mysql") {
2678
+ switch (object.kind) {
2679
+ case "view":
2680
+ text = `SELECT VIEW_DEFINITION AS definition, CHECK_OPTION AS checkOption, SECURITY_TYPE AS securityType, DEFINER AS definer FROM information_schema.VIEWS WHERE TABLE_SCHEMA = ${namespace} AND TABLE_NAME = ${name}`;
2681
+ break;
2682
+ case "trigger":
2683
+ text = `SELECT ACTION_STATEMENT AS definition, ACTION_TIMING AS timing, EVENT_MANIPULATION AS event, ACTION_ORDER AS actionOrder, SQL_MODE AS sqlMode, DEFINER AS definer FROM information_schema.TRIGGERS WHERE TRIGGER_SCHEMA = ${namespace} AND EVENT_OBJECT_TABLE = ${name} AND TRIGGER_NAME = ${collector.add(object.name)}`;
2684
+ break;
2685
+ case "function":
2686
+ text = `SELECT ROUTINE_DEFINITION AS definition, DTD_IDENTIFIER AS result, IS_DETERMINISTIC AS deterministic, SQL_DATA_ACCESS AS dataAccess, SECURITY_TYPE AS securityType, SQL_MODE AS sqlMode, DEFINER AS definer FROM information_schema.ROUTINES WHERE ROUTINE_SCHEMA = ${namespace} AND ROUTINE_NAME = ${name} AND ROUTINE_TYPE = 'FUNCTION'`;
2687
+ break;
2688
+ case "column":
2689
+ case "columnDefault":
2690
+ case "generatedColumn":
2691
+ text = `SELECT COLUMN_TYPE AS type, IS_NULLABLE AS nullable, COLUMN_DEFAULT AS definition, GENERATION_EXPRESSION AS generated, EXTRA AS extra, COLLATION_NAME AS collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = ${namespace} AND TABLE_NAME = ${name} AND COLUMN_NAME = ${collector.add(object.column)}`;
2692
+ break;
2693
+ case "constraint":
2694
+ if (object.constraintType === "exclusion")
2695
+ throw unsupportedCapability13({ dialect: "mysql", feature: "exclusionConstraints" });
2696
+ text = `SELECT tc.CONSTRAINT_TYPE AS type, tc.ENFORCED AS enforced, cc.CHECK_CLAUSE AS definition, rc.UPDATE_RULE AS onUpdate, rc.DELETE_RULE AS onDelete, rc.MATCH_OPTION AS matchOption FROM information_schema.TABLE_CONSTRAINTS tc LEFT JOIN information_schema.CHECK_CONSTRAINTS cc ON cc.CONSTRAINT_SCHEMA = tc.CONSTRAINT_SCHEMA AND cc.CONSTRAINT_NAME = tc.CONSTRAINT_NAME LEFT JOIN information_schema.REFERENTIAL_CONSTRAINTS rc ON rc.CONSTRAINT_SCHEMA = tc.CONSTRAINT_SCHEMA AND rc.TABLE_NAME = tc.TABLE_NAME AND rc.CONSTRAINT_NAME = tc.CONSTRAINT_NAME WHERE tc.TABLE_SCHEMA = ${namespace} AND tc.TABLE_NAME = ${name} AND tc.CONSTRAINT_NAME = ${collector.add(object.name)}`;
2697
+ break;
2698
+ case "index":
2699
+ text = `SELECT INDEX_NAME AS name, NON_UNIQUE AS nonUnique, INDEX_TYPE AS method, IS_VISIBLE AS visible FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = ${namespace} AND TABLE_NAME = ${name} AND INDEX_NAME = ${collector.add(object.name)} GROUP BY INDEX_NAME, NON_UNIQUE, INDEX_TYPE, IS_VISIBLE`;
2700
+ break;
2701
+ case "table":
2702
+ text = `SELECT TABLE_TYPE AS type, ENGINE AS engine, TABLE_COLLATION AS collation FROM information_schema.TABLES WHERE TABLE_SCHEMA = ${namespace} AND TABLE_NAME = ${name}`;
2703
+ break;
2704
+ default:
2705
+ throw unsupportedCapability13({ dialect: "mysql", feature: `inspect:${object.kind}` });
2706
+ }
2707
+ } else {
2708
+ if (physical.namespace !== "main" && physical.namespace !== "temp")
2709
+ throw unsupportedCapability13({ dialect: "sqlite", feature: "attachedSchemaInspection" });
2710
+ const catalog = `${dialect.quoteIdent({ name: physical.namespace })}.sqlite_schema`;
2711
+ const kind = object.kind === "table" || object.kind === "view" ? object.kind : object.kind === "trigger" ? "trigger" : object.kind === "index" ? "index" : "table";
2712
+ if (["function", "sequence", "postgresExtension"].includes(object.kind) || object.kind === "constraint" && object.constraintType === "exclusion")
2713
+ throw unsupportedCapability13({ dialect: "sqlite", feature: `inspect:${object.kind}` });
2714
+ const sqliteParams = new ParamCollector(dialect);
2715
+ if (object.kind === "column" || object.kind === "columnDefault" || object.kind === "generatedColumn") {
2716
+ text = `SELECT x.type AS type, x."notnull" AS "notNull", x.pk AS primaryKeyPosition, x.hidden AS generated, x.dflt_value AS definition, (SELECT sql FROM ${catalog} WHERE name = ${sqliteParams.add(physical.name)} AND type = 'table') AS tableDefinition FROM pragma_table_xinfo(${sqliteParams.add(physical.name)}, ${sqliteParams.add(physical.namespace)}) x WHERE x.name = ${sqliteParams.add(object.column)}`;
2717
+ return { text, values: sqliteParams.values, returning: "none" };
2718
+ }
2719
+ const objectName = object.kind === "trigger" || object.kind === "index" ? object.name : physical.name;
2720
+ text = `SELECT sql AS definition, type AS type FROM ${catalog} WHERE name = ${sqliteParams.add(objectName)} AND type = ${sqliteParams.add(kind)}`;
2721
+ return { text, values: sqliteParams.values, returning: "none" };
2722
+ }
2723
+ return { text, values: collector.values, returning: "none" };
2724
+ }
2725
+ function renderMysqlObjectMembers(params) {
2726
+ if (params.dialect.name !== "mysql")
2727
+ return;
2728
+ const object = params.identity;
2729
+ if (object.kind !== "function" && object.kind !== "index" && object.kind !== "constraint")
2730
+ return;
2731
+ const collector = new ParamCollector(params.dialect);
2732
+ const physical = "table" in object ? object.table : object.name;
2733
+ const namespace = collector.add(physical.namespace);
2734
+ const name = collector.add(physical.name);
2735
+ const text = object.kind === "function" ? `SELECT PARAMETER_MODE AS mode, PARAMETER_NAME AS name, DTD_IDENTIFIER AS type, CHARACTER_SET_NAME AS characterSet, COLLATION_NAME AS collation FROM information_schema.PARAMETERS WHERE SPECIFIC_SCHEMA = ${namespace} AND SPECIFIC_NAME = ${name} ORDER BY ORDINAL_POSITION` : object.kind === "index" ? `SELECT COLUMN_NAME AS name, EXPRESSION AS expression, SUB_PART AS prefixLength, COLLATION AS collation FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = ${namespace} AND TABLE_NAME = ${name} AND INDEX_NAME = ${collector.add(object.name)} ORDER BY SEQ_IN_INDEX` : `SELECT COLUMN_NAME AS name, REFERENCED_TABLE_SCHEMA AS referenceNamespace, REFERENCED_TABLE_NAME AS referenceTable, REFERENCED_COLUMN_NAME AS referenceColumn FROM information_schema.KEY_COLUMN_USAGE WHERE CONSTRAINT_SCHEMA = ${namespace} AND TABLE_NAME = ${name} AND CONSTRAINT_NAME = ${collector.add(object.name)} ORDER BY ORDINAL_POSITION`;
2736
+ return { text, values: collector.values, returning: "none" };
2737
+ }
2738
+ function renderMigrationDependencies(params) {
2739
+ if (params.dialect.name !== "postgres")
2740
+ throw unsupportedCapability13({ dialect: params.dialect.name, feature: "catalogDependencies" });
2741
+ const object = params.identity;
2742
+ const physical = "table" in object ? object.table : object.name;
2743
+ const collector = new ParamCollector(params.dialect);
2744
+ const namespace = collector.add(physical.namespace);
2745
+ const name = collector.add(physical.name);
2746
+ const relation = `SELECT c.oid FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname = ${namespace} AND c.relname = ${name}`;
2747
+ let source;
2748
+ switch (object.kind) {
2749
+ case "constraint":
2750
+ source = `SELECT 'pg_constraint'::regclass AS classid, con.oid AS objid, 0 AS objsubid FROM pg_catalog.pg_constraint con WHERE con.conrelid = (${relation}) AND con.conname = ${collector.add(object.name)}`;
2751
+ break;
2752
+ case "trigger":
2753
+ source = `SELECT 'pg_trigger'::regclass AS classid, t.oid AS objid, 0 AS objsubid FROM pg_catalog.pg_trigger t WHERE t.tgrelid = (${relation}) AND t.tgname = ${collector.add(object.name)}`;
2754
+ break;
2755
+ case "columnDefault":
2756
+ case "generatedColumn":
2757
+ source = `SELECT 'pg_attrdef'::regclass AS classid, d.oid AS objid, 0 AS objsubid FROM pg_catalog.pg_attrdef d JOIN pg_catalog.pg_attribute a ON a.attrelid = d.adrelid AND a.attnum = d.adnum WHERE a.attrelid = (${relation}) AND a.attname = ${collector.add(object.column)}`;
2758
+ break;
2759
+ case "column":
2760
+ source = `SELECT 'pg_class'::regclass AS classid, a.attrelid AS objid, a.attnum AS objsubid FROM pg_catalog.pg_attribute a WHERE a.attrelid = (${relation}) AND a.attname = ${collector.add(object.column)}`;
2761
+ break;
2762
+ case "view":
2763
+ source = `SELECT 'pg_rewrite'::regclass AS classid, r.oid AS objid, 0 AS objsubid FROM pg_catalog.pg_rewrite r WHERE r.ev_class = (${relation})`;
2764
+ break;
2765
+ case "index":
2766
+ source = `SELECT 'pg_class'::regclass AS classid, i.indexrelid AS objid, 0 AS objsubid FROM pg_catalog.pg_index i JOIN pg_catalog.pg_class c ON c.oid = i.indexrelid WHERE i.indrelid = (${relation}) AND c.relname = ${collector.add(object.name)}`;
2767
+ break;
2768
+ case "function":
2769
+ source = `SELECT 'pg_proc'::regclass AS classid, p.oid AS objid, 0 AS objsubid FROM pg_catalog.pg_proc p JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace WHERE n.nspname = ${namespace} AND p.proname = ${name} AND ARRAY(SELECT tn.nspname || '.' || t.typname FROM unnest(p.proargtypes) WITH ORDINALITY a(oid, ord) JOIN pg_catalog.pg_type t ON t.oid = a.oid JOIN pg_catalog.pg_namespace tn ON tn.oid = t.typnamespace ORDER BY a.ord) = ${collector.add(object.argumentTypes.map((type) => `${type.namespace}.${type.name}`))}::text[]`;
2770
+ break;
2771
+ case "postgresExtension":
2772
+ source = `SELECT 'pg_extension'::regclass AS classid, e.oid AS objid, 0 AS objsubid FROM pg_catalog.pg_extension e JOIN pg_catalog.pg_namespace n ON n.oid = e.extnamespace WHERE n.nspname = ${namespace} AND e.extname = ${name}`;
2773
+ break;
2774
+ default:
2775
+ source = `SELECT 'pg_class'::regclass AS classid, c.oid AS objid, 0 AS objsubid FROM pg_catalog.pg_class c WHERE c.oid = (${relation})`;
2776
+ break;
2777
+ }
2778
+ return { text: `SELECT DISTINCT json_build_array(d.deptype, a.type, a.object_names, a.object_args)::text AS address FROM (${source}) s JOIN pg_catalog.pg_depend d ON d.classid = s.classid AND d.objid = s.objid AND d.objsubid = s.objsubid CROSS JOIN LATERAL pg_catalog.pg_identify_object_as_address(d.refclassid, d.refobjid, d.refobjsubid) a ORDER BY address`, values: collector.values, returning: "none" };
2779
+ }
2780
+ function renderMigrationServerVersion(params) {
2781
+ return { text: params.dialect.name === "sqlite" ? "SELECT sqlite_version() AS version" : "SELECT version() AS version", values: [], returning: "none" };
2782
+ }
2783
+ function renderMigrationNamespace(params) {
2784
+ return { text: params.dialect.name === "mysql" ? "SELECT DATABASE() AS namespace" : params.dialect.name === "sqlite" ? "SELECT 'main' AS namespace" : "SELECT 'public' AS namespace", values: [], returning: "none" };
2785
+ }
2786
+ function renderCheckProbe(params) {
2787
+ if (params.dialect.name !== "postgres")
2788
+ throw unsupportedCapability13({ dialect: params.dialect.name, feature: "rollbackCheckProbe" });
2789
+ const q = (name) => params.dialect.quoteIdent({ name });
2790
+ return { text: `ALTER TABLE ${q(params.table.namespace)}.${q(params.table.name)} ADD CONSTRAINT ${q(params.name)} CHECK (${params.expression}) NOT VALID`, values: [], returning: "none" };
2791
+ }
1610
2792
  // packages/sql/src/dialects/postgres-migration.ts
1611
- import { VibeError as VibeError4, unsupportedCapability as unsupportedCapability6 } from "@vibeorm/schema";
2793
+ import { VibeError as VibeError9, unsupportedCapability as unsupportedCapability14 } from "@vibeorm/schema";
1612
2794
  var SETTING_VALUE = /^[0-9]+(?:\.[0-9]+)?\s*(?:us|ms|s|min|h|d)?$/;
1613
2795
  function requirePostgres2(params) {
1614
2796
  if (params.dialect.name !== "postgres") {
1615
- throw unsupportedCapability6({ dialect: params.dialect.name, feature: params.feature });
2797
+ throw unsupportedCapability14({ dialect: params.dialect.name, feature: params.feature });
1616
2798
  }
1617
2799
  }
1618
2800
  function renderMigrationSessionRead(params) {
@@ -1627,7 +2809,7 @@ function renderMigrationSessionSet(params) {
1627
2809
  requirePostgres2({ dialect: params.dialect, feature: "migrationSessionSettings" });
1628
2810
  const { milliseconds, setting } = params;
1629
2811
  if (!Number.isSafeInteger(milliseconds) || milliseconds < 0) {
1630
- throw new VibeError4({
2812
+ throw new VibeError9({
1631
2813
  code: "VIBE_MIGRATION",
1632
2814
  message: `${setting} must be a non-negative integer number of milliseconds (got ${String(milliseconds)})`,
1633
2815
  meta: { reason: "execution-metadata-invalid", setting, value: milliseconds }
@@ -1714,63 +2896,21 @@ WHERE n.nspname = pg_catalog.current_schema() AND c.relname = ${table} AND a.att
1714
2896
  returning: "none"
1715
2897
  };
1716
2898
  }
1717
- // packages/sql/src/pg-array.ts
1718
- var PG_ARRAY_QUOTE = /^$|^NULL$|[{},"\\\s]/i;
1719
- function toPgArrayLiteral(params) {
1720
- const { values } = params;
1721
- const parts = values.map((value) => {
1722
- if (value === null || value === undefined)
1723
- return "NULL";
1724
- const text = String(value);
1725
- return PG_ARRAY_QUOTE.test(text) ? `"${text.replace(/["\\]/g, (char) => `\\${char}`)}"` : text;
1726
- });
1727
- return `{${parts.join(",")}}`;
1728
- }
1729
- function parsePgArrayLiteral(params) {
1730
- const { literal } = params;
1731
- if (literal.length < 2 || !literal.startsWith("{") || !literal.endsWith("}"))
1732
- return null;
1733
- const inner = literal.slice(1, -1);
1734
- if (inner.length === 0)
1735
- return [];
1736
- const out = [];
1737
- let index = 0;
1738
- while (index < inner.length) {
1739
- if (inner[index] === '"') {
1740
- let text = "";
1741
- index++;
1742
- while (index < inner.length) {
1743
- const char = inner[index];
1744
- if (char === "\\") {
1745
- text += inner[index + 1] ?? "";
1746
- index += 2;
1747
- continue;
1748
- }
1749
- index++;
1750
- if (char === '"')
1751
- break;
1752
- text += char;
1753
- }
1754
- out.push(text);
1755
- } else {
1756
- let text = "";
1757
- while (index < inner.length && inner[index] !== ",") {
1758
- text += inner[index];
1759
- index++;
1760
- }
1761
- out.push(text === "NULL" ? null : text);
1762
- }
1763
- if (inner[index] === ",")
1764
- index++;
1765
- }
1766
- return out;
1767
- }
1768
2899
  export {
1769
2900
  toPgArrayLiteral,
1770
2901
  strictMatchPattern,
1771
2902
  startsWithPattern,
2903
+ sqliteTableConstraints,
1772
2904
  sqliteDialect,
2905
+ sqliteColumnClauses,
2906
+ sqlUsesIncoming,
2907
+ sql,
1773
2908
  renderStatement,
2909
+ renderSqliteRebuildObjects,
2910
+ renderSqliteForeignKeyInventory,
2911
+ renderSqliteColumnInventory,
2912
+ renderSqlFragment,
2913
+ renderRowLockClause,
1774
2914
  renderRlsRole,
1775
2915
  renderRlsReadinessQuery,
1776
2916
  renderRlsProbeTable,
@@ -1785,23 +2925,42 @@ export {
1785
2925
  renderRlsContext,
1786
2926
  renderRlsColumnType,
1787
2927
  renderRelationStateQuery,
2928
+ renderPredicate,
1788
2929
  renderPostgresWorkload,
1789
2930
  renderPostgresExplain,
2931
+ renderMysqlObjectMembers,
2932
+ renderMysqlCheckProbe,
2933
+ renderMigrationVisibility,
1790
2934
  renderMigrationSessionSet,
1791
2935
  renderMigrationSessionRestore,
1792
2936
  renderMigrationSessionRead,
2937
+ renderMigrationServerVersion,
2938
+ renderMigrationObjectQuery,
2939
+ renderMigrationNamespace,
2940
+ renderMigrationInventory,
2941
+ renderMigrationHistoryQuery,
2942
+ renderMigrationDependencies,
1793
2943
  renderIndexStateQuery,
1794
2944
  renderFragment,
1795
2945
  renderExpr,
2946
+ renderExclusionSetup,
2947
+ renderExclusionProbe,
2948
+ renderExclusionDdl,
2949
+ renderExclusionCatalog,
1796
2950
  renderDiagnostic,
1797
2951
  renderConstraintStateQuery,
2952
+ renderConstraintOwnedIndexDrop,
1798
2953
  renderColumnStateQuery,
2954
+ renderCheckProbe,
2955
+ renderAdvisoryLock,
1799
2956
  projectionOutputName,
1800
2957
  postgresDialect,
1801
2958
  parsePgArrayLiteral,
1802
2959
  parseDateTimeText,
1803
2960
  mysqlDialect,
2961
+ mapConflictColumns,
1804
2962
  jsonParamCell,
2963
+ isSqlFragment,
1805
2964
  isSqlDefaultCell,
1806
2965
  isSetOperation,
1807
2966
  isListSetOperation,
@@ -1815,9 +2974,13 @@ export {
1815
2974
  escapeGlobPattern,
1816
2975
  enumMembershipCodec,
1817
2976
  endsWithPattern,
2977
+ describeRowLock,
1818
2978
  decodeIsNoop,
2979
+ decode,
1819
2980
  dbNowMarker,
1820
2981
  containsPattern,
2982
+ compileSql,
2983
+ compileExclusion,
1821
2984
  SQL_DIALECTS,
1822
2985
  SQL_DEFAULT_CELL,
1823
2986
  SQLITE_CODECS,
@@ -1834,7 +2997,9 @@ export {
1834
2997
  MYSQL_CAPABILITIES,
1835
2998
  JSON_TRANSPORT,
1836
2999
  DIALECT_CODECS,
1837
- DIALECT_CAPABILITIES
3000
+ DIALECT_CAPABILITIES,
3001
+ ADVISORY_LOCK_RESULT_COLUMN,
3002
+ ADVISORY_KEY_BOUNDS
1838
3003
  };
1839
3004
 
1840
- //# debugId=2783254C4B92111564756E2164756E21
3005
+ //# debugId=F9D013AFDDCCA77864756E2164756E21