@vibeorm/sql 2.4.0 → 3.0.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 (60) hide show
  1. package/LICENSE +21 -0
  2. package/dist/ast.d.ts +334 -11
  3. package/dist/codecs.d.ts +48 -0
  4. package/dist/dialect.d.ts +44 -0
  5. package/dist/dialects/external-dependents.d.ts +28 -0
  6. package/dist/dialects/external-ownership.d.ts +8 -0
  7. package/dist/dialects/migration-shadow.d.ts +5 -0
  8. package/dist/dialects/postgres-index-keys.d.ts +10 -0
  9. package/dist/dialects/postgres-physical-catalog.d.ts +17 -0
  10. package/dist/dialects/sqlite-fts.d.ts +28 -0
  11. package/dist/exclusion.d.ts +2 -0
  12. package/dist/index-columns.d.ts +15 -0
  13. package/dist/index.d.ts +20 -5
  14. package/dist/index.js +1023 -273
  15. package/dist/index.js.map +28 -20
  16. package/dist/json-list-parameter.d.ts +37 -0
  17. package/dist/native-enum-parameter.d.ts +17 -0
  18. package/dist/pg-array.d.ts +13 -0
  19. package/dist/render.d.ts +11 -1
  20. package/package.json +5 -3
  21. package/dist/ast.d.ts.map +0 -1
  22. package/dist/capabilities.d.ts.map +0 -1
  23. package/dist/codecs.d.ts.map +0 -1
  24. package/dist/composable.d.ts.map +0 -1
  25. package/dist/dialect.d.ts.map +0 -1
  26. package/dist/dialects/constraint-owned-index.d.ts.map +0 -1
  27. package/dist/dialects/diagnostics.d.ts.map +0 -1
  28. package/dist/dialects/migration-history.d.ts.map +0 -1
  29. package/dist/dialects/migration-inventory.d.ts.map +0 -1
  30. package/dist/dialects/migration-objects.d.ts.map +0 -1
  31. package/dist/dialects/migration-replay-coverage.d.ts.map +0 -1
  32. package/dist/dialects/migration-shadow.d.ts.map +0 -1
  33. package/dist/dialects/migration-visibility.d.ts.map +0 -1
  34. package/dist/dialects/module-dependency.d.ts.map +0 -1
  35. package/dist/dialects/mysql-check-probe.d.ts.map +0 -1
  36. package/dist/dialects/mysql.d.ts.map +0 -1
  37. package/dist/dialects/postgres-default-identity.d.ts.map +0 -1
  38. package/dist/dialects/postgres-diagnostics.d.ts.map +0 -1
  39. package/dist/dialects/postgres-migration.d.ts.map +0 -1
  40. package/dist/dialects/postgres-rls-readiness.d.ts.map +0 -1
  41. package/dist/dialects/postgres-rls.d.ts.map +0 -1
  42. package/dist/dialects/postgres-type-identity.d.ts.map +0 -1
  43. package/dist/dialects/postgres.d.ts.map +0 -1
  44. package/dist/dialects/registry.d.ts.map +0 -1
  45. package/dist/dialects/sqlite-constraints.d.ts.map +0 -1
  46. package/dist/dialects/sqlite-session.d.ts.map +0 -1
  47. package/dist/dialects/sqlite-table-definition.d.ts.map +0 -1
  48. package/dist/dialects/sqlite.d.ts.map +0 -1
  49. package/dist/exclusion.d.ts.map +0 -1
  50. package/dist/identifiers.d.ts.map +0 -1
  51. package/dist/index.d.ts.map +0 -1
  52. package/dist/json-transport.d.ts.map +0 -1
  53. package/dist/like.d.ts.map +0 -1
  54. package/dist/locks.d.ts.map +0 -1
  55. package/dist/module-journal.d.ts.map +0 -1
  56. package/dist/params.d.ts.map +0 -1
  57. package/dist/pg-array.d.ts.map +0 -1
  58. package/dist/predicate.d.ts.map +0 -1
  59. package/dist/render.d.ts.map +0 -1
  60. package/dist/result-decoder.d.ts.map +0 -1
package/dist/index.js CHANGED
@@ -1,5 +1,48 @@
1
1
  // @bun
2
+ // packages/sql/src/native-enum-parameter.ts
3
+ var NATIVE_ENUM_ARRAY_PARAMETER = Symbol("vibeorm.nativeEnumArrayParameter");
4
+ function nativeEnumArrayParameter(params) {
5
+ return { [NATIVE_ENUM_ARRAY_PARAMETER]: true, value: params.value, type: params.type };
6
+ }
7
+ function isNativeEnumArrayParameter(value) {
8
+ return typeof value === "object" && value !== null && NATIVE_ENUM_ARRAY_PARAMETER in value;
9
+ }
10
+ // packages/sql/src/json-list-parameter.ts
11
+ var JSON_LIST_PARAMETER = Symbol("vibeorm.jsonListParameter");
12
+ function jsonListParameter(params) {
13
+ return { [JSON_LIST_PARAMETER]: true, value: params.value, storage: params.storage };
14
+ }
15
+ function isJsonListParameter(value) {
16
+ return typeof value === "object" && value !== null && JSON_LIST_PARAMETER in value;
17
+ }
18
+ // packages/sql/src/dialects/external-ownership.ts
19
+ import { unsupportedCapability } from "@vibeorm/schema";
20
+ function renderExternalOwnershipCatalog(params) {
21
+ if (params.dialect.name !== "postgres")
22
+ throw unsupportedCapability({ dialect: params.dialect.name, feature: "externalObjects" });
23
+ const scope = "n.nspname <> 'information_schema' AND n.nspname NOT LIKE 'pg\\_%' ESCAPE '\\' AND n.nspname NOT IN ('_vibeorm_shadow', '_vibe_shadow')";
24
+ let text;
25
+ switch (params.operation) {
26
+ case "namespaces":
27
+ text = `SELECT n.nspname::text AS namespace, has_schema_privilege(n.oid, 'USAGE') AS visible FROM pg_catalog.pg_namespace n WHERE ${scope} ORDER BY n.nspname`;
28
+ break;
29
+ case "inheritance":
30
+ text = `SELECT n.nspname::text AS namespace, c.relname::text AS name, pn.nspname::text AS "parentNamespace", p.relname::text AS "parentName" FROM pg_catalog.pg_inherits i JOIN pg_catalog.pg_class c ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace JOIN pg_catalog.pg_class p ON p.oid = i.inhparent JOIN pg_catalog.pg_namespace pn ON pn.oid = p.relnamespace WHERE ${scope} ORDER BY n.nspname, c.relname, pn.nspname, p.relname`;
31
+ break;
32
+ case "ownedSequences":
33
+ text = `SELECT n.nspname::text AS namespace, s.relname::text AS name, tn.nspname::text AS "tableNamespace", t.relname::text AS "tableName" FROM pg_catalog.pg_depend d JOIN pg_catalog.pg_class s ON s.oid = d.objid AND s.relkind = 'S' JOIN pg_catalog.pg_namespace n ON n.oid = s.relnamespace JOIN pg_catalog.pg_class t ON t.oid = d.refobjid JOIN pg_catalog.pg_namespace tn ON tn.oid = t.relnamespace WHERE d.classid = 'pg_class'::regclass AND d.refclassid = 'pg_class'::regclass AND d.deptype IN ('a', 'i') AND d.refobjsubid > 0 AND ${scope} ORDER BY n.nspname, s.relname`;
34
+ break;
35
+ case "foreignKeys":
36
+ text = `SELECT n.nspname::text AS namespace, t.relname::text AS table, rn.nspname::text AS "refNamespace", r.relname::text AS "refTable", c.conname::text AS name 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_class r ON r.oid = c.confrelid JOIN pg_catalog.pg_namespace rn ON rn.oid = r.relnamespace WHERE c.contype = 'f' AND ${scope} ORDER BY n.nspname, t.relname, c.conname`;
37
+ break;
38
+ }
39
+ return { text, values: [], returning: "none" };
40
+ }
41
+ // packages/sql/src/dialects/external-dependents.ts
42
+ import { unsupportedCapability as unsupportedCapability3 } from "@vibeorm/schema";
43
+
2
44
  // packages/sql/src/params.ts
45
+ import { unsupportedCapability as unsupportedCapability2 } from "@vibeorm/schema";
3
46
  class ParamCollector {
4
47
  dialect;
5
48
  values;
@@ -8,6 +51,19 @@ class ParamCollector {
8
51
  this.values = [];
9
52
  }
10
53
  add(value) {
54
+ if (isNativeEnumArrayParameter(value)) {
55
+ if (!this.dialect.capabilities.scalarArrays)
56
+ throw unsupportedCapability2({ dialect: this.dialect.name, feature: "scalarArrays" });
57
+ this.values.push(value.value);
58
+ return `${this.dialect.placeholder({ index: this.values.length })}::${this.dialect.quoteIdent({ name: value.type.namespace })}.${this.dialect.quoteIdent({ name: value.type.name })}[]`;
59
+ }
60
+ if (isJsonListParameter(value)) {
61
+ if (!this.dialect.capabilities.scalarArrays)
62
+ throw unsupportedCapability2({ dialect: this.dialect.name, feature: "scalarArrays" });
63
+ const whole = typeof value.value !== "string";
64
+ this.values.push(whole ? [...value.value] : value.value);
65
+ return `${this.dialect.placeholder({ index: this.values.length })}::text::${value.storage}${whole ? "[]" : ""}`;
66
+ }
11
67
  this.values.push(value);
12
68
  return this.dialect.placeholder({ index: this.values.length });
13
69
  }
@@ -16,6 +72,121 @@ class ParamCollector {
16
72
  }
17
73
  }
18
74
 
75
+ // packages/sql/src/dialects/external-dependents.ts
76
+ function renderExternalDependents(params) {
77
+ if (params.dialect.name !== "postgres")
78
+ throw unsupportedCapability3({ dialect: params.dialect.name, feature: "externalObjects" });
79
+ const collector = new ParamCollector(params.dialect);
80
+ const requests = params.targets.map((target, position) => ({ position, kind: target.kind, namespace: target.namespace, name: target.name, column: target.kind === "column" ? target.column : null }));
81
+ const request = collector.add(JSON.stringify(requests));
82
+ const text = `WITH request AS (
83
+ SELECT * FROM jsonb_to_recordset(${request}::text::jsonb) AS request(position integer, kind text, namespace text, name text, "column" text)
84
+ ), target AS (
85
+ SELECT request.position, 'pg_type'::regclass AS classid, t.oid AS objid, NULL::integer AS objsubid FROM request JOIN pg_catalog.pg_namespace n ON n.nspname = request.namespace JOIN pg_catalog.pg_type t ON t.typnamespace = n.oid AND t.typname = request.name WHERE request.kind = 'type'
86
+ UNION ALL SELECT request.position, 'pg_class'::regclass, c.oid, a.attnum::integer FROM request JOIN pg_catalog.pg_namespace n ON n.nspname = request.namespace JOIN pg_catalog.pg_class c ON c.relnamespace = n.oid AND c.relname = request.name LEFT JOIN pg_catalog.pg_attribute a ON request.kind = 'column' AND a.attrelid = c.oid AND a.attname = request."column" AND NOT a.attisdropped WHERE request.kind = 'relation' OR request.kind = 'column' AND a.attnum IS NOT NULL
87
+ ), owned AS (
88
+ SELECT position, classid, objid, objsubid FROM target
89
+ UNION SELECT target.position, 'pg_type'::regclass, c.reltype, NULL FROM target JOIN pg_catalog.pg_class c ON c.oid = target.objid WHERE target.classid = 'pg_class'::regclass AND target.objsubid IS NULL AND c.reltype <> 0
90
+ UNION SELECT target.position, 'pg_class'::regclass, s.oid, NULL FROM target JOIN pg_catalog.pg_depend sd ON sd.refclassid = target.classid AND sd.refobjid = target.objid AND (target.objsubid IS NULL OR sd.refobjsubid = target.objsubid) AND sd.classid = 'pg_class'::regclass AND sd.deptype IN ('a', 'i') JOIN pg_catalog.pg_class s ON s.oid = sd.objid AND s.relkind = 'S'
91
+ ), referenced AS (
92
+ SELECT position, classid, objid, objsubid FROM owned
93
+ UNION SELECT owned.position, 'pg_type'::regclass, t.typarray, NULL FROM owned JOIN pg_catalog.pg_type t ON t.oid = owned.objid WHERE owned.classid = 'pg_type'::regclass AND t.typarray <> 0
94
+ )
95
+ SELECT DISTINCT referenced.position, dependent.type AS "dependentType", dependent.identity AS "dependentIdentity", ref.identity AS referenced, d.classid::regclass::text AS "dependentClass",
96
+ rn.nspname::text AS "relationNamespace", rel.relname::text AS "relationName", rel.relkind::text AS "relationKind", col.attname::text AS "columnName",
97
+ COALESCE(pn.nspname, tn.nspname, dependent.schema)::text AS "objectNamespace", COALESCE(p.proname, ty.typname)::text AS "objectName"
98
+ FROM referenced
99
+ JOIN pg_catalog.pg_depend d ON d.refclassid = referenced.classid AND d.refobjid = referenced.objid AND (referenced.objsubid IS NULL OR d.refobjsubid = referenced.objsubid) AND d.deptype = 'n'
100
+ CROSS JOIN LATERAL pg_catalog.pg_identify_object(d.classid, d.objid, d.objsubid) dependent
101
+ CROSS JOIN LATERAL pg_catalog.pg_identify_object(d.refclassid, d.refobjid, d.refobjsubid) ref
102
+ LEFT JOIN pg_catalog.pg_class self ON d.classid = 'pg_class'::regclass AND self.oid = d.objid
103
+ LEFT JOIN pg_catalog.pg_index ix ON ix.indexrelid = self.oid
104
+ LEFT JOIN pg_catalog.pg_attrdef ad ON d.classid = 'pg_attrdef'::regclass AND ad.oid = d.objid
105
+ LEFT JOIN pg_catalog.pg_rewrite rw ON d.classid = 'pg_rewrite'::regclass AND rw.oid = d.objid
106
+ LEFT JOIN pg_catalog.pg_trigger tg ON d.classid = 'pg_trigger'::regclass AND tg.oid = d.objid
107
+ LEFT JOIN pg_catalog.pg_constraint con ON d.classid = 'pg_constraint'::regclass AND con.oid = d.objid
108
+ LEFT JOIN pg_catalog.pg_policy pol ON d.classid = 'pg_policy'::regclass AND pol.oid = d.objid
109
+ LEFT JOIN pg_catalog.pg_type ty ON d.classid = 'pg_type'::regclass AND ty.oid = d.objid
110
+ LEFT JOIN pg_catalog.pg_namespace tn ON tn.oid = ty.typnamespace
111
+ LEFT JOIN pg_catalog.pg_proc p ON d.classid = 'pg_proc'::regclass AND p.oid = d.objid
112
+ LEFT JOIN pg_catalog.pg_namespace pn ON pn.oid = p.pronamespace
113
+ LEFT JOIN pg_catalog.pg_class rel ON rel.oid = COALESCE(ix.indrelid, self.oid, ad.adrelid, rw.ev_class, tg.tgrelid, NULLIF(con.conrelid, 0), pol.polrelid, NULLIF(ty.typrelid, 0))
114
+ LEFT JOIN pg_catalog.pg_namespace rn ON rn.oid = rel.relnamespace
115
+ LEFT JOIN pg_catalog.pg_attribute col ON col.attrelid = rel.oid AND col.attnum = CASE WHEN self.oid IS NOT NULL AND d.objsubid > 0 THEN d.objsubid WHEN ad.oid IS NOT NULL THEN ad.adnum END
116
+ ORDER BY 1, 2, 3, 4`;
117
+ return { text, values: collector.values, returning: "none" };
118
+ }
119
+ // packages/sql/src/dialects/postgres-physical-catalog.ts
120
+ import { unsupportedCapability as unsupportedCapability4 } from "@vibeorm/schema";
121
+ function renderPostgresPhysicalCatalog(params) {
122
+ const { dialect } = params;
123
+ if (dialect.name !== "postgres")
124
+ throw unsupportedCapability4({ dialect: dialect.name, feature: "postgresPhysicalCatalog" });
125
+ const collector = new ParamCollector(dialect);
126
+ const namespace = collector.add(params.namespace);
127
+ const extensionOwnedFilter = `NOT EXISTS (SELECT 1 FROM pg_catalog.pg_depend d JOIN pg_catalog.pg_class c ON c.oid = d.objid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE d.classid = 'pg_class'::regclass AND d.deptype = 'e' AND n.nspname = ${namespace} AND c.relname = table_name)`;
128
+ let text;
129
+ switch (params.query) {
130
+ case "tables":
131
+ text = `SELECT table_name FROM information_schema.tables WHERE table_schema = ${namespace} AND table_type = 'BASE TABLE' AND table_name <> '_vibe_migrations' AND ${extensionOwnedFilter} ORDER BY table_name`;
132
+ break;
133
+ case "views":
134
+ text = `SELECT table_name FROM information_schema.views WHERE table_schema = ${namespace} AND ${extensionOwnedFilter} ORDER BY table_name`;
135
+ break;
136
+ case "columns":
137
+ text = `SELECT table_name, column_name, data_type, udt_name, udt_schema, is_nullable, is_identity, is_generated, collation_name, column_default, datetime_precision, character_maximum_length, numeric_precision, numeric_scale, ordinal_position, (SELECT format_type(a.atttypid, a.atttypmod) FROM pg_attribute a JOIN pg_class c ON c.oid = a.attrelid JOIN pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname = ${namespace} AND c.relname = columns.table_name AND a.attname = columns.column_name) AS full_type FROM information_schema.columns AS columns WHERE table_schema = ${namespace} ORDER BY table_name, ordinal_position`;
138
+ break;
139
+ case "constraints":
140
+ text = `SELECT c.conname AS constraint_name, CASE c.contype WHEN 'p' THEN 'PRIMARY KEY' ELSE 'UNIQUE' END AS constraint_type, cl.relname AS table_name, att.attname AS column_name FROM pg_catalog.pg_constraint c JOIN pg_catalog.pg_class cl ON cl.oid = c.conrelid JOIN pg_catalog.pg_namespace n ON n.oid = cl.relnamespace JOIN unnest(c.conkey) WITH ORDINALITY AS k(attnum, ord) ON true JOIN pg_catalog.pg_attribute att ON att.attrelid = c.conrelid AND att.attnum = k.attnum WHERE c.contype IN ('p', 'u') AND n.nspname = ${namespace} ORDER BY cl.relname, c.conname, k.ord`;
141
+ break;
142
+ case "checks":
143
+ text = `SELECT cl.relname AS table_name, c.conname AS constraint_name, pg_get_constraintdef(c.oid) AS definition FROM pg_catalog.pg_constraint c JOIN pg_catalog.pg_class cl ON cl.oid = c.conrelid JOIN pg_catalog.pg_namespace n ON n.oid = cl.relnamespace WHERE c.contype = 'c' AND n.nspname = ${namespace} ORDER BY cl.relname, c.conname`;
144
+ break;
145
+ case "foreignKeys":
146
+ text = `SELECT c.conname AS constraint_name, cl.relname AS table_name, att.attname AS column_name, fn.nspname AS foreign_namespace, fcl.relname AS foreign_table_name, fatt.attname AS foreign_column_name, c.confdeltype AS on_delete_char, c.confupdtype AS on_update_char, c.convalidated AS validated, c.condeferrable AS deferrable, (SELECT bool_and(t.tgenabled IN ('O', 'A')) FROM pg_catalog.pg_trigger t WHERE t.tgconstraint = c.oid) AS triggers_enabled FROM pg_catalog.pg_constraint c JOIN pg_catalog.pg_class cl ON cl.oid = c.conrelid JOIN pg_catalog.pg_namespace n ON n.oid = cl.relnamespace JOIN pg_catalog.pg_class fcl ON fcl.oid = c.confrelid JOIN pg_catalog.pg_namespace fn ON fn.oid = fcl.relnamespace JOIN unnest(c.conkey) WITH ORDINALITY AS lk(attnum, ord) ON true JOIN unnest(c.confkey) WITH ORDINALITY AS rk(attnum, ord) ON lk.ord = rk.ord JOIN pg_catalog.pg_attribute att ON att.attrelid = c.conrelid AND att.attnum = lk.attnum JOIN pg_catalog.pg_attribute fatt ON fatt.attrelid = c.confrelid AND fatt.attnum = rk.attnum WHERE c.contype = 'f' AND n.nspname = ${namespace} ORDER BY cl.relname, c.conname, lk.ord`;
147
+ break;
148
+ case "enums":
149
+ text = `SELECT t.typname AS type_name, n.nspname AS type_namespace, e.enumlabel AS enum_value, e.enumsortorder AS sort_order FROM pg_type t JOIN pg_enum e ON t.oid = e.enumtypid JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace WHERE n.nspname = ${namespace} AND NOT EXISTS (SELECT 1 FROM pg_catalog.pg_depend d WHERE d.classid = 'pg_type'::regclass AND d.objid = t.oid AND d.deptype = 'e') ORDER BY t.typname, e.enumsortorder`;
150
+ break;
151
+ case "indexes":
152
+ text = `SELECT tablename AS table_name, indexname AS index_name, indexdef AS index_def, con.conname AS backing_constraint, con.contype AS backing_constraint_type, health.indisvalid AS valid, health.indisready AS ready, health.indimmediate AS immediate, health.indexprs IS NULL AS plain_columns, health.indnkeyatts AS key_count, health.indnatts AS attribute_count FROM pg_indexes ix JOIN pg_catalog.pg_namespace ns ON ns.nspname = ix.schemaname JOIN pg_catalog.pg_class idx ON idx.relnamespace = ns.oid AND idx.relname = ix.indexname JOIN pg_catalog.pg_index health ON health.indexrelid = idx.oid LEFT JOIN pg_catalog.pg_constraint con ON con.conindid = idx.oid AND con.contype IN ('p', 'u', 'x') WHERE schemaname = ${namespace} ORDER BY tablename, indexname`;
153
+ break;
154
+ }
155
+ return { text, values: collector.values, returning: "none" };
156
+ }
157
+ // packages/sql/src/dialects/postgres-index-keys.ts
158
+ import { unsupportedCapability as unsupportedCapability5 } from "@vibeorm/schema";
159
+ function renderPostgresIndexKeys(params) {
160
+ const { dialect } = params;
161
+ if (dialect.name !== "postgres")
162
+ throw unsupportedCapability5({ dialect: dialect.name, feature: "indexOperatorClasses" });
163
+ const collector = new ParamCollector(dialect);
164
+ const scope = params.table !== undefined ? `n.nspname = ${collector.add(params.table.namespace)} AND c.relname = ${collector.add(params.table.name)}` : params.namespaces === undefined ? "n.nspname <> 'information_schema' AND n.nspname NOT LIKE 'pg\\_%' ESCAPE '\\'" : params.namespaces.length === 0 ? "false" : `n.nspname IN (${[...new Set(params.namespaces)].map((namespace) => collector.add(namespace)).join(", ")})`;
165
+ const defaultResolution = `LEFT JOIN LATERAL (WITH RECURSIVE input_types AS (SELECT t.oid, t.typbasetype, t.typtype, t.typelem, t.typcategory FROM pg_catalog.pg_type t WHERE t.oid = a.atttypid UNION ALL SELECT t.oid, t.typbasetype, t.typtype, t.typelem, t.typcategory FROM pg_catalog.pg_type t JOIN input_types previous ON t.oid = previous.typbasetype WHERE previous.typtype = 'd'), candidates AS (SELECT candidate.oid, CASE WHEN candidate.opcintype = input.oid THEN 0 WHEN class_input.typispreferred AND class_input.typcategory = input.typcategory THEN 1 ELSE 2 END AS priority FROM pg_catalog.pg_opclass candidate JOIN pg_catalog.pg_type class_input ON class_input.oid = candidate.opcintype CROSS JOIN input_types input WHERE input.typtype <> 'd' AND candidate.opcdefault AND candidate.opcmethod = idx.relam AND (candidate.opcintype = input.oid OR EXISTS (SELECT 1 FROM pg_catalog.pg_cast cast_entry WHERE cast_entry.castsource = input.oid AND cast_entry.casttarget = candidate.opcintype AND cast_entry.castmethod = 'b' AND cast_entry.castcontext = 'i') OR input.typtype = 'e' AND candidate.opcintype = 'pg_catalog.anyenum'::regtype OR input.typelem <> 0 AND candidate.opcintype = 'pg_catalog.anyarray'::regtype)) SELECT (array_agg(candidate.oid))[1] AS oid FROM candidates candidate WHERE candidate.priority = (SELECT min(priority) FROM candidates) HAVING count(*) = 1) resolved_default ON true`;
166
+ return {
167
+ text: `SELECT n.nspname AS table_namespace, c.relname AS table_name, idx.relname AS index_name, am.amname AS method, i.indisunique AS unique, pg_catalog.pg_get_expr(i.indpred, i.indrelid, false) AS predicate, i.indnullsnotdistinct AS nulls_not_distinct, k.ordinality AS ordinal, a.attname AS column_name, (i.indoption[k.ordinality - 1] & 1) <> 0 AS descending, opn.nspname AS operator_namespace, op.opcname AS operator_name, COALESCE(op.oid = resolved_default.oid, false) AS exact_default FROM pg_catalog.pg_index i JOIN pg_catalog.pg_class c ON c.oid = i.indrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace JOIN pg_catalog.pg_class idx ON idx.oid = i.indexrelid JOIN pg_catalog.pg_am am ON am.oid = idx.relam CROSS JOIN LATERAL unnest(i.indkey) WITH ORDINALITY k(attnum, ordinality) LEFT JOIN pg_catalog.pg_attribute a ON a.attrelid = c.oid AND a.attnum = k.attnum JOIN pg_catalog.pg_opclass op ON op.oid = i.indclass[k.ordinality - 1] JOIN pg_catalog.pg_namespace opn ON opn.oid = op.opcnamespace ${defaultResolution} WHERE ${scope} AND k.ordinality <= i.indnkeyatts ORDER BY n.nspname, c.relname, idx.relname, k.ordinality`,
168
+ values: collector.values,
169
+ returning: "none"
170
+ };
171
+ }
172
+ // packages/sql/src/index-columns.ts
173
+ import { VibeError, isSqlObjectIdentity, unsupportedCapability as unsupportedCapability6 } from "@vibeorm/schema";
174
+ function renderIndexColumns(params) {
175
+ const { dialect, columns, unique } = params;
176
+ const hasOperatorClass = columns.some((column) => column.operatorClass !== undefined);
177
+ if (hasOperatorClass && !dialect.capabilities.indexOperatorClasses)
178
+ throw unsupportedCapability6({ dialect: dialect.name, feature: "indexOperatorClasses" });
179
+ if (hasOperatorClass && unique)
180
+ throw unsupportedCapability6({ dialect: dialect.name, feature: "uniqueIndexOperatorClasses", hint: "custom unique operator classes can change client selector equality" });
181
+ return columns.map((column) => {
182
+ const operatorClass = column.operatorClass;
183
+ if (operatorClass !== undefined && !isSqlObjectIdentity({ kind: "table", name: operatorClass })) {
184
+ throw new VibeError({ code: "VIBE_SCHEMA_INVALID", message: "Index operator class must be a qualified SQL name { namespace, name }", meta: { rule: "index-operator-class-identity", column: column.name } });
185
+ }
186
+ const ops = operatorClass === undefined ? "" : ` ${dialect.quoteIdent({ name: operatorClass.namespace })}.${dialect.quoteIdent({ name: operatorClass.name })}`;
187
+ return `${dialect.quoteIdent({ name: column.name })}${ops}${column.desc ? " DESC" : ""}`;
188
+ });
189
+ }
19
190
  // packages/sql/src/dialects/module-dependency.ts
20
191
  function renderModuleDependencyQuery(params) {
21
192
  const { dialect, operation } = params;
@@ -50,19 +221,19 @@ function renderReplayCoverageGaps(params) {
50
221
  const collector = new ParamCollector(dialect);
51
222
  const namespace = collector.add(params.namespace);
52
223
  if (dialect.name === "postgres")
53
- return { text: `SELECT 'type' AS kind, t.typname::text AS name FROM pg_catalog.pg_type t JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace WHERE n.nspname = ${namespace} AND (t.typtype IN ('b', 'd', 'r', 'm') AND t.typelem = 0 OR t.typtype = 'c' AND EXISTS (SELECT 1 FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid AND c.relkind = 'c')) AND NOT EXISTS (SELECT 1 FROM pg_catalog.pg_depend d WHERE d.classid = 'pg_type'::regclass AND d.objid = t.oid AND d.deptype = 'e')
54
- UNION ALL SELECT 'routine', p.proname::text 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')
55
- UNION ALL SELECT 'relation-layout', c.relname::text FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname = ${namespace} AND (c.relkind IN ('p', 'f') OR c.relispartition OR EXISTS (SELECT 1 FROM pg_catalog.pg_inherits i WHERE i.inhrelid = c.oid)) 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')
56
- UNION ALL SELECT 'rule', r.rulename::text FROM pg_catalog.pg_rewrite r JOIN pg_catalog.pg_class c ON c.oid = r.ev_class JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname = ${namespace} AND r.rulename <> '_RETURN' AND NOT ${extensionMember("pg_class", "c.oid")}
57
- UNION ALL SELECT 'namespace', n.nspname::text FROM pg_catalog.pg_namespace n WHERE n.nspname <> ${namespace} AND n.nspname <> 'information_schema' AND n.nspname NOT LIKE 'pg\\_%' ESCAPE '\\' AND NOT (${extensionOwnedNamespace})${params.privateNamespace === undefined ? "" : ` AND n.nspname <> ${collector.add(params.privateNamespace)}`}`, values: collector.values, returning: "none" };
224
+ return { text: `SELECT 'type' AS kind, t.typname::text AS name, n.nspname::text AS namespace, NULL::text AS parent FROM pg_catalog.pg_type t JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace WHERE n.nspname = ${namespace} AND (t.typtype IN ('b', 'd', 'r', 'm') AND t.typelem = 0 OR t.typtype = 'c' AND EXISTS (SELECT 1 FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid AND c.relkind = 'c')) AND NOT EXISTS (SELECT 1 FROM pg_catalog.pg_depend d WHERE d.classid = 'pg_type'::regclass AND d.objid = t.oid AND d.deptype = 'e')
225
+ UNION ALL SELECT 'routine', p.proname::text, n.nspname::text, NULL::text 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')
226
+ UNION ALL SELECT 'relation-layout', c.relname::text, n.nspname::text, NULL::text FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname = ${namespace} AND (c.relkind IN ('p', 'f') OR c.relispartition OR EXISTS (SELECT 1 FROM pg_catalog.pg_inherits i WHERE i.inhrelid = c.oid)) 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')
227
+ UNION ALL SELECT 'rule', r.rulename::text, n.nspname::text, c.relname::text FROM pg_catalog.pg_rewrite r JOIN pg_catalog.pg_class c ON c.oid = r.ev_class JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname = ${namespace} AND r.rulename <> '_RETURN' AND NOT ${extensionMember("pg_class", "c.oid")}
228
+ UNION ALL SELECT 'namespace', n.nspname::text, n.nspname::text, NULL::text FROM pg_catalog.pg_namespace n WHERE n.nspname <> ${namespace} AND n.nspname <> 'information_schema' AND n.nspname NOT LIKE 'pg\\_%' ESCAPE '\\' AND NOT (${extensionOwnedNamespace})${params.privateNamespace === undefined ? "" : ` AND n.nspname <> ${collector.add(params.privateNamespace)}`}`, values: collector.values, returning: "none" };
58
229
  if (dialect.name === "mysql")
59
- return { text: `SELECT 'procedure' AS kind, ROUTINE_NAME AS name FROM information_schema.ROUTINES WHERE ROUTINE_SCHEMA = ${namespace} AND ROUTINE_TYPE = 'PROCEDURE'
60
- UNION ALL SELECT 'event', EVENT_NAME FROM information_schema.EVENTS WHERE EVENT_SCHEMA = ${collector.add(params.namespace)}
61
- UNION ALL SELECT DISTINCT 'partition', TABLE_NAME FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = ${collector.add(params.namespace)} AND PARTITION_NAME IS NOT NULL`, values: collector.values, returning: "none" };
62
- return { text: `SELECT 'table-options' AS kind, name FROM pragma_table_list WHERE schema = ${namespace} AND name NOT LIKE 'sqlite\\_%' ESCAPE '\\' AND (type = 'virtual' OR wr <> 0 OR strict <> 0)`, values: collector.values, returning: "none" };
230
+ return { text: `SELECT 'procedure' AS kind, ROUTINE_NAME AS name, ROUTINE_SCHEMA AS namespace, NULL AS parent FROM information_schema.ROUTINES WHERE ROUTINE_SCHEMA = ${namespace} AND ROUTINE_TYPE = 'PROCEDURE'
231
+ UNION ALL SELECT 'event', EVENT_NAME, EVENT_SCHEMA, NULL FROM information_schema.EVENTS WHERE EVENT_SCHEMA = ${collector.add(params.namespace)}
232
+ UNION ALL SELECT DISTINCT 'partition', TABLE_NAME, TABLE_SCHEMA, NULL FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = ${collector.add(params.namespace)} AND PARTITION_NAME IS NOT NULL`, values: collector.values, returning: "none" };
233
+ return { text: `SELECT 'table-options' AS kind, name, schema AS namespace, NULL AS parent FROM pragma_table_list WHERE schema = ${namespace} AND name NOT LIKE 'sqlite\\_%' ESCAPE '\\' AND (type = 'virtual' OR wr <> 0 OR strict <> 0)`, values: collector.values, returning: "none" };
63
234
  }
64
235
  // packages/sql/src/dialects/migration-shadow.ts
65
- import { unsupportedCapability } from "@vibeorm/schema";
236
+ import { assertSqlNamespace, unsupportedCapability as unsupportedCapability7 } from "@vibeorm/schema";
66
237
  var SHADOW_DATABASE = Object.freeze({ schema: "_vibeorm_shadow", table: "_vibe_shadow", lock: 1447645267, timeoutSeconds: 60 });
67
238
  var SHADOW_CAPABILITIES = Object.freeze({
68
239
  postgres: Object.freeze({ serverProvisioning: true, dedicated: true }),
@@ -72,7 +243,7 @@ var SHADOW_CAPABILITIES = Object.freeze({
72
243
  function renderShadowOperation(params) {
73
244
  const { dialect, operation } = params;
74
245
  if (!SHADOW_CAPABILITIES[dialect.name].serverProvisioning)
75
- throw unsupportedCapability({ dialect: dialect.name, feature: "shadowDatabaseProvisioning" });
246
+ throw unsupportedCapability7({ dialect: dialect.name, feature: "shadowDatabaseProvisioning" });
76
247
  const pg = dialect.name === "postgres";
77
248
  const collector = new ParamCollector(dialect);
78
249
  const marker = pg ? `${dialect.quoteIdent({ name: SHADOW_DATABASE.schema })}.${dialect.quoteIdent({ name: SHADOW_DATABASE.table })}` : dialect.quoteIdent({ name: SHADOW_DATABASE.table });
@@ -97,7 +268,7 @@ function renderShadowOperation(params) {
97
268
  ORDER BY ordinal_position`);
98
269
  case "grants": {
99
270
  if (pg)
100
- throw unsupportedCapability({ dialect: dialect.name, feature: "mysqlShadowPrivileges" });
271
+ throw unsupportedCapability7({ dialect: dialect.name, feature: "mysqlShadowPrivileges" });
101
272
  return result("SHOW GRANTS FOR CURRENT_USER");
102
273
  }
103
274
  case "markerCreate":
@@ -125,19 +296,27 @@ UNION ALL SELECT ROUTINE_TYPE, ROUTINE_NAME, ROUTINE_SCHEMA FROM information_sch
125
296
  UNION ALL SELECT 'EVENT', EVENT_NAME, EVENT_SCHEMA FROM information_schema.EVENTS WHERE EVENT_SCHEMA = DATABASE()
126
297
  UNION ALL SELECT 'TRIGGER', TRIGGER_NAME, TRIGGER_SCHEMA FROM information_schema.TRIGGERS WHERE TRIGGER_SCHEMA = DATABASE()
127
298
  UNION ALL SELECT 'EXTERNAL FOREIGN KEY', CONCAT(TABLE_NAME, '.', CONSTRAINT_NAME), CONSTRAINT_SCHEMA FROM information_schema.REFERENTIAL_CONSTRAINTS WHERE UNIQUE_CONSTRAINT_SCHEMA = DATABASE() AND CAST(CONSTRAINT_SCHEMA AS BINARY) <> CAST(DATABASE() AS BINARY)`);
128
- case "resetPostgres": {
299
+ case "resetPostgres":
300
+ case "resetDedicatedPostgres": {
129
301
  if (!pg)
130
- throw unsupportedCapability({ dialect: dialect.name, feature: "postgresShadowReset" });
131
- return [{ text: 'DROP SCHEMA "public" CASCADE', values: [], returning: "none" }, { text: 'CREATE SCHEMA "public"', values: [], returning: "none" }];
302
+ throw unsupportedCapability7({ dialect: dialect.name, feature: "postgresShadowReset" });
303
+ const owned = operation.kind === "resetDedicatedPostgres" ? operation.ownedNamespaces : [];
304
+ for (const namespace of owned)
305
+ assertSqlNamespace({ namespace, dialect: "postgres", allowPublic: false });
306
+ return [
307
+ ...owned.map((namespace) => ({ text: `DROP SCHEMA IF EXISTS ${dialect.quoteIdent({ name: namespace })} CASCADE`, values: [], returning: "none" })),
308
+ { text: 'DROP SCHEMA "public" CASCADE', values: [], returning: "none" },
309
+ { text: 'CREATE SCHEMA "public"', values: [], returning: "none" }
310
+ ];
132
311
  }
133
312
  case "mysqlForeignKeys": {
134
313
  if (pg)
135
- throw unsupportedCapability({ dialect: dialect.name, feature: "mysqlShadowReset" });
314
+ throw unsupportedCapability7({ dialect: dialect.name, feature: "mysqlShadowReset" });
136
315
  return result(`SET FOREIGN_KEY_CHECKS = ${operation.enabled ? "1" : "0"}`);
137
316
  }
138
317
  case "dropMysqlObject": {
139
318
  if (pg)
140
- throw unsupportedCapability({ dialect: dialect.name, feature: "mysqlShadowReset" });
319
+ throw unsupportedCapability7({ dialect: dialect.name, feature: "mysqlShadowReset" });
141
320
  return result(`DROP ${operation.object} ${dialect.quoteIdent({ name: operation.name })}`);
142
321
  }
143
322
  }
@@ -170,6 +349,209 @@ function renderModuleJournal(params) {
170
349
  }
171
350
  return { text, values: collector.values };
172
351
  }
352
+ // packages/sql/src/capabilities.ts
353
+ import {
354
+ DIALECT_CAPABILITIES,
355
+ POSTGRES_CAPABILITIES,
356
+ SQLITE_CAPABILITIES,
357
+ MYSQL_CAPABILITIES
358
+ } from "@vibeorm/schema";
359
+
360
+ // packages/sql/src/identifiers.ts
361
+ import { VibeError as VibeError2 } from "@vibeorm/schema";
362
+ function assertNoNul(params) {
363
+ if (params.name.includes("\x00")) {
364
+ throw new VibeError2({
365
+ code: "VIBE_VALIDATION",
366
+ message: "an SQL identifier cannot contain a NUL character \u2014 the server would stop reading it there and address a different object",
367
+ meta: { name: params.name.replaceAll("\x00", "\\0") }
368
+ });
369
+ }
370
+ return params.name;
371
+ }
372
+
373
+ // packages/sql/src/dialects/sqlite.ts
374
+ var sqliteDialect = {
375
+ name: "sqlite",
376
+ capabilities: SQLITE_CAPABILITIES,
377
+ inArrayStrategy: "expand",
378
+ tupleInStrategy: "rowValue",
379
+ timeOfDayInStrategy: "in",
380
+ upsertForm: "onConflict",
381
+ conflictKeyEquality: {
382
+ foldingScalars: [],
383
+ textIsBinaryByDefault: true,
384
+ duplicatesInOneStatement: "lastWriteWins"
385
+ },
386
+ excludedRowRef: { form: "keyword", name: "excluded" },
387
+ doNothingForm: "onConflictDoNothing",
388
+ insensitiveLike: "lower",
389
+ strictMatchStrategy: "glob",
390
+ greatestForm: "frame",
391
+ orderByNullsMode: "native",
392
+ offsetRequiresLimit: { limitText: "-1" },
393
+ likeEscapeClause: " ESCAPE '\\'",
394
+ jsonPathMode: "sqliteExtract",
395
+ supportedJsonOps: [
396
+ "equals",
397
+ "string_contains",
398
+ "string_starts_with",
399
+ "string_ends_with",
400
+ "array_contains",
401
+ "array_starts_with",
402
+ "array_ends_with"
403
+ ],
404
+ jsonParamCast: "",
405
+ lastInsertIdSql: null,
406
+ rowLockSyntax: null,
407
+ advisoryLockFunctions: null,
408
+ rowDefaultKeyword: false,
409
+ statementChain: null,
410
+ paramBudget: 900,
411
+ booleanLiteral: { true: "1", false: "0" },
412
+ quoteIdent({ name }) {
413
+ return `"${assertNoNul({ name }).replaceAll('"', '""')}"`;
414
+ },
415
+ quoteStringLiteral({ value }) {
416
+ return `'${value.replaceAll("'", "''")}'`;
417
+ },
418
+ placeholder() {
419
+ return "?";
420
+ },
421
+ renderDbNow({ offsetSeconds, collector }) {
422
+ if (offsetSeconds === 0)
423
+ return `strftime('%Y-%m-%dT%H:%M:%fZ', 'now')`;
424
+ return `strftime('%Y-%m-%dT%H:%M:%fZ', 'now', ${collector.add(offsetSeconds)} || ' seconds')`;
425
+ }
426
+ };
427
+
428
+ // packages/sql/src/dialects/sqlite-table-definition.ts
429
+ import { VibeError as VibeError3 } from "@vibeorm/schema";
430
+ function tableDefinitionParts(params) {
431
+ const sql = params.sql;
432
+ if (!/^\s*CREATE\s+(?:TEMP(?:ORARY)?\s+)?TABLE\b/i.test(sql))
433
+ throw unavailable();
434
+ const parts = [];
435
+ let start = -1;
436
+ let depth = 0;
437
+ let quote;
438
+ for (let index = 0;index < sql.length; index++) {
439
+ const char = sql[index];
440
+ if (quote !== undefined) {
441
+ if (char === quote) {
442
+ if (quote !== "]" && sql[index + 1] === quote)
443
+ index++;
444
+ else
445
+ quote = undefined;
446
+ }
447
+ continue;
448
+ }
449
+ if (char === "'" || char === '"' || char === "`") {
450
+ quote = char;
451
+ continue;
452
+ }
453
+ if (char === "[") {
454
+ quote = "]";
455
+ continue;
456
+ }
457
+ if (char === "-" && sql[index + 1] === "-") {
458
+ const end = sql.indexOf(`
459
+ `, index + 2);
460
+ if (end < 0)
461
+ break;
462
+ index = end;
463
+ continue;
464
+ }
465
+ if (char === "/" && sql[index + 1] === "*") {
466
+ const end = sql.indexOf("*/", index + 2);
467
+ if (end < 0)
468
+ break;
469
+ index = end + 1;
470
+ continue;
471
+ }
472
+ if (char === "(") {
473
+ if (depth === 0)
474
+ start = index + 1;
475
+ depth++;
476
+ } else if (char === ")") {
477
+ depth--;
478
+ if (depth === 0 && start >= 0) {
479
+ parts.push(sql.slice(start, index).trim());
480
+ return { clauses: parts, suffix: sql.slice(index + 1) };
481
+ }
482
+ } else if (char === "," && depth === 1) {
483
+ parts.push(sql.slice(start, index).trim());
484
+ start = index + 1;
485
+ }
486
+ }
487
+ throw unavailable();
488
+ }
489
+ function unavailable() {
490
+ return new VibeError3({ code: "VIBE_MIGRATION", message: "Cannot preserve table definition", meta: { reason: "table-definition-unavailable" } });
491
+ }
492
+ function sqliteColumnClauses(params) {
493
+ const columns = [];
494
+ for (const part of tableDefinitionParts(params).clauses) {
495
+ const token = /^(?:"((?:[^"]|"")+)"|`((?:[^`]|``)+)`|\[([^\]]+)\]|([A-Za-z_][A-Za-z_0-9]*))/.exec(part);
496
+ if (token === null)
497
+ throw unavailable();
498
+ if (token[4] !== undefined && /^(?:CONSTRAINT|PRIMARY|UNIQUE|CHECK|FOREIGN)$/i.test(token[4]))
499
+ continue;
500
+ const name = token[1]?.replaceAll('""', '"') ?? token[2]?.replaceAll("``", "`") ?? token[3] ?? token[4];
501
+ columns.push({ name, sql: part });
502
+ }
503
+ return columns;
504
+ }
505
+
506
+ // packages/sql/src/dialects/sqlite-fts.ts
507
+ function updateStatements(params) {
508
+ const quote = (name) => sqliteDialect.quoteIdent({ name });
509
+ const columns = params.columns.map((column) => quote(column)).join(", ");
510
+ const oldValues = params.columns.map((column) => `old.${quote(column)}`).join(", ");
511
+ const newValues = params.columns.map((column) => `new.${quote(column)}`).join(", ");
512
+ const changes = [
513
+ "old.rowid IS NOT new.rowid",
514
+ ...params.columns.map((column) => `old.${quote(column)} COLLATE BINARY IS NOT new.${quote(column)} COLLATE BINARY`)
515
+ ].join(" OR ");
516
+ const target = `${quote(`${params.ftsTable}_au`)} AFTER UPDATE ON ${quote(params.table)}`;
517
+ const body = `BEGIN
518
+ INSERT INTO ${quote(params.ftsTable)}(${quote(params.ftsTable)}, rowid, ${columns}) VALUES ('delete', old.rowid, ${oldValues});
519
+ INSERT INTO ${quote(params.ftsTable)}(rowid, ${columns}) VALUES (new.rowid, ${newValues});
520
+ END`;
521
+ const tail = `${target} WHEN (${changes}) ${body}`;
522
+ return {
523
+ create: `CREATE TRIGGER IF NOT EXISTS ${tail};`,
524
+ definition: `CREATE TRIGGER ${tail}`,
525
+ legacyDefinition: `CREATE TRIGGER ${target} ${body}`
526
+ };
527
+ }
528
+ function renderSqliteFtsUpdateTrigger(params) {
529
+ const { create, definition } = updateStatements(params);
530
+ return { create, definition };
531
+ }
532
+ function sqliteFtsUpdateUpgrade(params) {
533
+ const quoted = '"(?:[^"]|"")+"';
534
+ const body = new RegExp(`BEGIN\\n INSERT INTO (${quoted})\\((${quoted}), rowid, (${quoted}(?:, ${quoted})*)\\) VALUES \\('delete', old\\.rowid, `, "g");
535
+ for (const match of params.create.matchAll(body)) {
536
+ const ftsTable = sqliteColumnClauses({ sql: `CREATE TABLE x (${match[1]})` })[0].name;
537
+ const columns = sqliteColumnClauses({ sql: `CREATE TABLE x (${match[3]})` }).map((column) => column.name);
538
+ if (params.name !== `${ftsTable}_au`)
539
+ continue;
540
+ const statements = updateStatements({ table: params.table, ftsTable, columns });
541
+ if (params.create !== statements.create)
542
+ continue;
543
+ const installed = params.installed === statements.legacyDefinition ? "legacy" : params.installed === statements.definition ? "current" : undefined;
544
+ if (installed === undefined)
545
+ continue;
546
+ if (params.tableDefinition === undefined)
547
+ return { kind: "tableRequired", ftsTable };
548
+ const tableTail = `${sqliteDialect.quoteIdent({ name: ftsTable })} USING fts5(${columns.map((name) => sqliteDialect.quoteIdent({ name })).join(", ")}, content=${sqliteDialect.quoteStringLiteral({ value: params.table })})`;
549
+ if (params.tableDefinition !== `CREATE VIRTUAL TABLE ${tableTail}` && params.tableDefinition !== `CREATE VIRTUAL TABLE IF NOT EXISTS ${tableTail};`)
550
+ continue;
551
+ return { kind: "recognized", ftsTable, legacyDefinition: statements.legacyDefinition, currentDefinition: statements.definition, drop: `DROP TRIGGER IF EXISTS ${sqliteDialect.quoteIdent({ name: `${ftsTable}_au` })};`, installed };
552
+ }
553
+ return;
554
+ }
173
555
  // packages/sql/src/dialects/sqlite-session.ts
174
556
  var SQLITE_SESSION_SQL = Object.freeze({
175
557
  lockingMode: "PRAGMA main.locking_mode",
@@ -182,10 +564,10 @@ var SQLITE_SESSION_SQL = Object.freeze({
182
564
  foreignKeysOn: "PRAGMA foreign_keys = ON"
183
565
  });
184
566
  // packages/sql/src/composable.ts
185
- import { VibeError } from "@vibeorm/schema";
567
+ import { VibeError as VibeError4 } from "@vibeorm/schema";
186
568
  var fragments = new WeakMap;
187
569
  function invalid(message) {
188
- throw new VibeError({ code: "VIBE_VALIDATION", message });
570
+ throw new VibeError4({ code: "VIBE_VALIDATION", message });
189
571
  }
190
572
  function construct(parts) {
191
573
  const snapshot = Object.freeze([...parts]);
@@ -201,6 +583,9 @@ function isSqlFragment(value) {
201
583
  function partsOf(value) {
202
584
  return fragments.get(value) ?? invalid("Expected a constructed SQL fragment");
203
585
  }
586
+ function containsGreatest(fragment) {
587
+ return partsOf(fragment).some((part) => typeof part !== "string" && ("greatest" in part));
588
+ }
204
589
  function identifier(parts) {
205
590
  if (!Array.isArray(parts) || parts.length === 0 || parts.some((part) => typeof part !== "string" || part.length === 0 || part.includes("\x00"))) {
206
591
  invalid("SQL identifiers require non-empty, NUL-free name components");
@@ -263,14 +648,25 @@ var sql = Object.freeze(Object.assign(template, {
263
648
  function renderSqlFragment(params) {
264
649
  const { dialect, collector, conflict } = params;
265
650
  const render = (fragment) => renderSqlFragment({ ...params, fragment });
651
+ const encode = (part) => {
652
+ if (part.value === undefined)
653
+ invalid("SQL parameters cannot be undefined; use null explicitly");
654
+ return part.decoder === undefined ? part.value : part.decoder.encode({ value: part.value, dialect: dialect.name });
655
+ };
656
+ const frameOperand = (fragment) => {
657
+ const parts = partsOf(fragment).filter((part) => part !== "");
658
+ const only = parts.length === 1 ? parts[0] : undefined;
659
+ const value = only !== undefined && typeof only !== "string" && "value" in only ? { encoded: encode(only) } : undefined;
660
+ if (value !== undefined && value.encoded === null)
661
+ return "NULL";
662
+ const text = value === undefined ? render(fragment) : collector.add(value.encoded);
663
+ return dialect.greatestForm === "wrapped-frame" ? `(SELECT ${text})` : text;
664
+ };
266
665
  return partsOf(params.fragment).map((part) => {
267
666
  if (typeof part === "string")
268
667
  return part;
269
- if ("value" in part) {
270
- if (part.value === undefined)
271
- invalid("SQL parameters cannot be undefined; use null explicitly");
272
- return collector.add(part.decoder === undefined ? part.value : part.decoder.encode({ value: part.value, dialect: dialect.name }));
273
- }
668
+ if ("value" in part)
669
+ return collector.add(encode(part));
274
670
  if ("ident" in part)
275
671
  return part.ident.map((name) => dialect.quoteIdent({ name })).join(".");
276
672
  if ("row" in part) {
@@ -283,7 +679,14 @@ function renderSqlFragment(params) {
283
679
  const qualifier = part.row === "existing" ? dialect.quoteIdent({ name: conflict.table }) : ref.form === "keyword" ? ref.name : dialect.quoteIdent({ name: ref.alias });
284
680
  return `${qualifier}.${dialect.quoteIdent({ name })}`;
285
681
  }
286
- 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)`));
682
+ if (dialect.greatestForm === "native")
683
+ return `GREATEST(${part.greatest.map(render).join(", ")})`;
684
+ if (!part.greatest.some(containsGreatest)) {
685
+ 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)`));
686
+ }
687
+ const [left, right] = part.greatest.map(frameOperand);
688
+ const [a, b, frame] = ["a", "b", "g"].map((name) => dialect.quoteIdent({ name }));
689
+ return `(SELECT CASE WHEN ${a} IS NULL THEN ${b} WHEN ${b} IS NULL THEN ${a} WHEN ${a} >= ${b} THEN ${a} ELSE ${b} END FROM (SELECT ${left} AS ${a}, ${right} AS ${b}) AS ${frame})`;
287
690
  }).join("");
288
691
  }
289
692
  function mapConflictColumns(params) {
@@ -310,7 +713,7 @@ function sqlUsesIncoming(params) {
310
713
  return partsOf(params.fragment).some((part) => typeof part !== "string" && (("row" in part) && part.row === "incoming" || ("greatest" in part) && part.greatest.some((fragment) => sqlUsesIncoming({ fragment }))));
311
714
  }
312
715
  // packages/sql/src/result-decoder.ts
313
- import { VibeError as VibeError3 } from "@vibeorm/schema";
716
+ import { VibeError as VibeError6 } from "@vibeorm/schema";
314
717
 
315
718
  // packages/sql/src/pg-array.ts
316
719
  var PG_ARRAY_QUOTE = /^$|^NULL$|[{},"\\\s]/i;
@@ -325,7 +728,7 @@ function toPgArrayLiteral(params) {
325
728
  return `{${parts.join(",")}}`;
326
729
  }
327
730
  function parsePgArrayLiteral(params) {
328
- const { literal } = params;
731
+ const literal = params.literal.startsWith("[") ? params.literal.replace(/^\[-?\d+:-?\d+\]=/, "") : params.literal;
329
732
  if (literal.length < 2 || !literal.startsWith("{") || !literal.endsWith("}"))
330
733
  return null;
331
734
  const inner = literal.slice(1, -1);
@@ -363,9 +766,63 @@ function parsePgArrayLiteral(params) {
363
766
  }
364
767
  return out;
365
768
  }
769
+ function parsePgArrayLiteralNested(params) {
770
+ const text = params.literal.replace(/^(?:\[-?\d+:-?\d+\])+=/, "");
771
+ const cursor = { index: 0 };
772
+ const parsed = parseDimension({ text, cursor });
773
+ return parsed !== null && cursor.index === text.length ? parsed : null;
774
+ }
775
+ function parseDimension(params) {
776
+ const { text, cursor } = params;
777
+ if (text[cursor.index] !== "{")
778
+ return null;
779
+ cursor.index++;
780
+ const out = [];
781
+ if (text[cursor.index] === "}") {
782
+ cursor.index++;
783
+ return out;
784
+ }
785
+ while (cursor.index < text.length) {
786
+ if (text[cursor.index] === "{") {
787
+ const nested = parseDimension({ text, cursor });
788
+ if (nested === null)
789
+ return null;
790
+ out.push(nested);
791
+ } else if (text[cursor.index] === '"') {
792
+ let element = "";
793
+ cursor.index++;
794
+ while (cursor.index < text.length && text[cursor.index] !== '"') {
795
+ if (text[cursor.index] === "\\")
796
+ cursor.index++;
797
+ element += text[cursor.index] ?? "";
798
+ cursor.index++;
799
+ }
800
+ if (text[cursor.index] !== '"')
801
+ return null;
802
+ cursor.index++;
803
+ out.push(element);
804
+ } else {
805
+ let element = "";
806
+ while (cursor.index < text.length && text[cursor.index] !== "," && text[cursor.index] !== "}") {
807
+ element += text[cursor.index];
808
+ cursor.index++;
809
+ }
810
+ out.push(element === "NULL" ? null : element);
811
+ }
812
+ if (text[cursor.index] === ",") {
813
+ cursor.index++;
814
+ continue;
815
+ }
816
+ if (text[cursor.index] !== "}")
817
+ return null;
818
+ cursor.index++;
819
+ return out;
820
+ }
821
+ return null;
822
+ }
366
823
 
367
824
  // packages/sql/src/codecs.ts
368
- import { VibeError as VibeError2, invalidStoredData } from "@vibeorm/schema";
825
+ import { VibeError as VibeError5, invalidStoredData, timeOfDayType } from "@vibeorm/schema";
369
826
  function enumMembershipCodec(params) {
370
827
  const { model, field, enum: enumName } = params;
371
828
  const members = new Set(params.members);
@@ -373,7 +830,7 @@ function enumMembershipCodec(params) {
373
830
  encode: (value) => {
374
831
  if (typeof value === "string" && members.has(value))
375
832
  return value;
376
- throw new VibeError2({
833
+ throw new VibeError5({
377
834
  code: "VIBE_VALIDATION",
378
835
  message: `Invalid value for enum ${enumName} on ${model}.${field}`,
379
836
  meta: { model, field, enum: enumName }
@@ -413,7 +870,7 @@ var bigIntCodec = {
413
870
  return BigInt(value.trim());
414
871
  if (typeof value === "number" && Number.isSafeInteger(value))
415
872
  return BigInt(value);
416
- throw new VibeError2({ code: "VIBE_DATA_INVALID", message: "BigInt requires an integer string or a safe integer", meta: { value } });
873
+ throw new VibeError5({ code: "VIBE_DATA_INVALID", message: "BigInt requires an integer string or a safe integer", meta: { value } });
417
874
  }
418
875
  };
419
876
  var decimalCodec = {
@@ -430,6 +887,7 @@ var mysqlDecimalCodec = {
430
887
  }
431
888
  };
432
889
  var ZONE_LESS_DATETIME = /^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}(?::\d{2}(?:\.\d{1,9})?)?$/;
890
+ var PG_ZONED_DATETIME = /^(\d{4}-\d{2}-\d{2})[T ](\d{2}:\d{2}(?::\d{2})?)(\.\d{1,9})?([+-]\d{2})(?::?(\d{2}))?$/;
433
891
  function parseDateTimeText(params) {
434
892
  const trimmed = params.text.trim();
435
893
  if (ZONE_LESS_DATETIME.test(trimmed)) {
@@ -437,13 +895,20 @@ function parseDateTimeText(params) {
437
895
  if (!Number.isNaN(asUtc.getTime()))
438
896
  return asUtc;
439
897
  }
898
+ const zoned = PG_ZONED_DATETIME.exec(trimmed);
899
+ if (zoned !== null) {
900
+ const [, date, time, fraction, hours, minutes] = zoned;
901
+ const iso = new Date(`${date}T${time}${(fraction ?? "").slice(0, 4)}${hours}:${minutes ?? "00"}`);
902
+ if (!Number.isNaN(iso.getTime()))
903
+ return iso;
904
+ }
440
905
  const parsed = new Date(trimmed);
441
906
  return Number.isNaN(parsed.getTime()) ? undefined : parsed;
442
907
  }
443
908
  var dateNativeCodec = {
444
909
  encode: (value) => {
445
910
  if (value instanceof Date && !Number.isFinite(value.getTime()) || typeof value === "number" && !Number.isFinite(value) || typeof value === "string" && /^[+-]?infinity$/i.test(value.trim())) {
446
- throw new VibeError2({ code: "VIBE_VALIDATION", message: "DateTime parameters require finite timestamps; use an extended timestamp encoder for infinity" });
911
+ throw new VibeError5({ code: "VIBE_VALIDATION", message: "DateTime parameters require finite timestamps; use an extended timestamp encoder for infinity" });
447
912
  }
448
913
  return value;
449
914
  },
@@ -451,7 +916,7 @@ var dateNativeCodec = {
451
916
  const date = value instanceof Date ? value : typeof value === "string" ? parseDateTimeText({ text: value }) : typeof value === "number" ? new Date(value) : undefined;
452
917
  if (date !== undefined && Number.isFinite(date.getTime()))
453
918
  return date;
454
- throw new VibeError2({ code: "VIBE_DATA_INVALID", message: "DateTime requires a finite timestamp; use an explicit extended timestamp decoder for infinity" });
919
+ throw new VibeError5({ code: "VIBE_DATA_INVALID", message: "DateTime requires a finite timestamp; use an explicit extended timestamp decoder for infinity" });
455
920
  }
456
921
  };
457
922
  var dateTextCodec = {
@@ -460,7 +925,7 @@ var dateTextCodec = {
460
925
  const date = value instanceof Date ? value : typeof value === "string" ? parseDateTimeText({ text: value }) : typeof value === "number" ? new Date(value) : undefined;
461
926
  if (date !== undefined && Number.isFinite(date.getTime()))
462
927
  return date.toISOString();
463
- throw new VibeError2({ code: "VIBE_VALIDATION", message: "DateTime parameters require a valid finite timestamp" });
928
+ throw new VibeError5({ code: "VIBE_VALIDATION", message: "DateTime parameters require a valid finite timestamp" });
464
929
  },
465
930
  decode: dateNativeCodec.decode
466
931
  };
@@ -481,6 +946,44 @@ var mysqlDateCodec = {
481
946
  },
482
947
  decode: dateNativeCodec.decode
483
948
  };
949
+ var MILLISECONDS_PER_DAY = 86400000;
950
+ var TIME_OF_DAY_TEXT = /^(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{1,9}))?)?(?:([+-])(\d{2})(?::?(\d{2}))?(?::?(\d{2}))?)?$/;
951
+ var pad = (value, width) => String(value).padStart(width, "0");
952
+ var utcMillisecondOfDay = (date) => (date.getTime() % MILLISECONDS_PER_DAY + MILLISECONDS_PER_DAY) % MILLISECONDS_PER_DAY;
953
+ var timeOfDayText = (millisecond) => `${pad(Math.floor(millisecond / 3600000), 2)}:${pad(Math.floor(millisecond / 60000) % 60, 2)}:${pad(Math.floor(millisecond / 1000) % 60, 2)}.${pad(millisecond % 1000, 3)}`;
954
+ function makeTimeOfDayCodec(params) {
955
+ const suffix = params.zoned ? "+00" : "";
956
+ return Object.freeze({
957
+ encode: (value) => {
958
+ dateNativeCodec.encode(value);
959
+ const date = value instanceof Date ? value : typeof value === "string" ? parseDateTimeText({ text: value }) : typeof value === "number" ? new Date(value) : undefined;
960
+ if (date === undefined || !Number.isFinite(date.getTime())) {
961
+ throw new VibeError5({ code: "VIBE_VALIDATION", message: "a time-of-day DateTime parameter requires a valid finite Date", meta: { value } });
962
+ }
963
+ return `${timeOfDayText(utcMillisecondOfDay(date))}${suffix}`;
964
+ },
965
+ decode: (value) => {
966
+ if (value instanceof Date && Number.isFinite(value.getTime()))
967
+ return new Date(utcMillisecondOfDay(value));
968
+ const match = typeof value === "string" ? TIME_OF_DAY_TEXT.exec(value.trim()) : null;
969
+ if (match !== null) {
970
+ const [, hours, minutes, seconds, fraction, sign, offsetHours, offsetMinutes, offsetSeconds] = match;
971
+ const millisecond = Number(hours) * 3600000 + Number(minutes) * 60000 + Number(seconds ?? 0) * 1000 + Number((fraction ?? "").slice(0, 3).padEnd(3, "0"));
972
+ const offset = sign === undefined ? 0 : (sign === "-" ? -1 : 1) * (Number(offsetHours) * 3600000 + Number(offsetMinutes ?? 0) * 60000 + Number(offsetSeconds ?? 0) * 1000);
973
+ if (millisecond <= MILLISECONDS_PER_DAY) {
974
+ return new Date(((millisecond - offset) % MILLISECONDS_PER_DAY + MILLISECONDS_PER_DAY) % MILLISECONDS_PER_DAY);
975
+ }
976
+ }
977
+ throw new VibeError5({ code: "VIBE_DATA_INVALID", message: "a time-of-day DateTime column returned a value that is not a time of day", meta: { value } });
978
+ }
979
+ });
980
+ }
981
+ var timeCodec = makeTimeOfDayCodec({ zoned: false });
982
+ var timetzCodec = makeTimeOfDayCodec({ zoned: true });
983
+ function timeOfDayCodec(params) {
984
+ const type = timeOfDayType({ nativeType: params.nativeType });
985
+ return type === "timetz" ? timetzCodec : type === "time" ? timeCodec : undefined;
986
+ }
484
987
  var booleanIntCodec = {
485
988
  encode: (value) => typeof value === "boolean" ? value ? 1 : 0 : value,
486
989
  decode: (value) => typeof value === "number" ? value !== 0 : value
@@ -497,7 +1000,7 @@ var jsonCodec = {
497
1000
  try {
498
1001
  return JSON.parse(value);
499
1002
  } catch (cause) {
500
- throw new VibeError2({
1003
+ throw new VibeError5({
501
1004
  code: "VIBE_INTERNAL",
502
1005
  message: "a Json column returned text that is not valid JSON",
503
1006
  meta: { value },
@@ -506,6 +1009,39 @@ var jsonCodec = {
506
1009
  }
507
1010
  }
508
1011
  };
1012
+ function shortestFloat32(params) {
1013
+ const { value } = params;
1014
+ if (value === 0 || !Number.isFinite(value))
1015
+ return value;
1016
+ const target = Math.fround(value);
1017
+ for (let precision = 1;precision <= 9; precision++) {
1018
+ const candidate = Number(value.toPrecision(precision));
1019
+ if (Math.fround(candidate) === target)
1020
+ return candidate;
1021
+ }
1022
+ return value;
1023
+ }
1024
+ var float32Codec = {
1025
+ encode: identity,
1026
+ decode: (value) => {
1027
+ const number = typeof value === "string" ? Number(value) : value;
1028
+ return typeof number === "number" ? shortestFloat32({ value: number }) : number;
1029
+ }
1030
+ };
1031
+ var booleanFromText = (text) => text === "t" ? true : text === "f" ? false : text;
1032
+ var bytesFromText = (text) => /^\\x(?:[0-9a-fA-F]{2})*$/.test(text) ? new Uint8Array(Buffer.from(text.slice(2), "hex")) : text;
1033
+ function postgresArrayElementFromText(params) {
1034
+ switch (params.scalarType) {
1035
+ case "Boolean":
1036
+ return booleanFromText;
1037
+ case "Bytes":
1038
+ return bytesFromText;
1039
+ case "Json":
1040
+ return jsonCodec.decode;
1041
+ default:
1042
+ return null;
1043
+ }
1044
+ }
509
1045
  var POSTGRES_CODECS = {
510
1046
  String: passthroughCodec,
511
1047
  Boolean: passthroughCodec,
@@ -547,7 +1083,7 @@ var DIALECT_CODECS = {
547
1083
 
548
1084
  // packages/sql/src/result-decoder.ts
549
1085
  function refuse(encoding, message) {
550
- throw new VibeError3({ code: encoding ? "VIBE_VALIDATION" : "VIBE_DATA_INVALID", message });
1086
+ throw new VibeError6({ code: encoding ? "VIBE_VALIDATION" : "VIBE_DATA_INVALID", message });
551
1087
  }
552
1088
  function scalar(type, valid) {
553
1089
  const convert = (params, encoding) => {
@@ -593,7 +1129,7 @@ var extendedDecoder = Object.freeze({
593
1129
  encode: (params) => {
594
1130
  if (params.value === "infinity" || params.value === "-infinity") {
595
1131
  if (params.dialect !== "postgres")
596
- throw new VibeError3({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Timestamp infinity parameters require PostgreSQL" });
1132
+ throw new VibeError6({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Timestamp infinity parameters require PostgreSQL" });
597
1133
  return params.value;
598
1134
  }
599
1135
  return dateDecoder.encode(params);
@@ -651,15 +1187,15 @@ var decode = Object.freeze({
651
1187
  }
652
1188
  });
653
1189
  // packages/sql/src/exclusion.ts
654
- import { VibeError as VibeError5, fieldTypeIdentity as fieldTypeIdentity2, unsupportedCapability as unsupportedCapability3 } from "@vibeorm/schema";
1190
+ import { VibeError as VibeError8, fieldTypeIdentity as fieldTypeIdentity2, isEnumType as isEnumType2, unsupportedCapability as unsupportedCapability9 } from "@vibeorm/schema";
655
1191
 
656
1192
  // packages/sql/src/predicate.ts
657
- import { VibeError as VibeError4, fieldTypeIdentity, isEnumType, unsupportedCapability as unsupportedCapability2 } from "@vibeorm/schema";
1193
+ import { VibeError as VibeError7, fieldTypeIdentity, isEnumType, unsupportedCapability as unsupportedCapability8 } from "@vibeorm/schema";
658
1194
  function renderPredicate(params) {
659
1195
  if (params.usage === "exclusionPredicate" && !params.dialect.capabilities.exclusionConstraints)
660
- throw unsupportedCapability2({ dialect: params.dialect.name, feature: "exclusionConstraints" });
1196
+ throw unsupportedCapability8({ dialect: params.dialect.name, feature: "exclusionConstraints" });
661
1197
  if (params.usage === "indexPredicate" && !params.dialect.capabilities.partialIndexes) {
662
- throw unsupportedCapability2({ dialect: params.dialect.name, feature: "partialIndexes" });
1198
+ throw unsupportedCapability8({ dialect: params.dialect.name, feature: "partialIndexes" });
663
1199
  }
664
1200
  const context = {
665
1201
  model: params.model,
@@ -694,7 +1230,7 @@ var INT64_MIN = -(2n ** 63n);
694
1230
  var INT64_MAX = 2n ** 63n - 1n;
695
1231
  function refuse2(params) {
696
1232
  const { context, rule, message } = params;
697
- throw new VibeError4({
1233
+ throw new VibeError7({
698
1234
  code: "VIBE_SCHEMA_INVALID",
699
1235
  message: `${context.site} on "${context.model.name}": ${message}`,
700
1236
  meta: { rule, model: context.model.name, site: context.site, ...params.meta ?? {} }
@@ -787,7 +1323,7 @@ function resolveField(params) {
787
1323
  });
788
1324
  }
789
1325
  if (context.usage === "check" && context.dialect.name === "mysql" && field.default?.kind === "autoincrement") {
790
- throw unsupportedCapability2({
1326
+ throw unsupportedCapability8({
791
1327
  dialect: "mysql",
792
1328
  feature: `CHECK constraint referencing auto-increment column "${context.model.name}.${name}"`,
793
1329
  hint: "mysql refuses this with error 3818 \u2014 enforce the rule in application code or on a non-generated column"
@@ -958,12 +1494,12 @@ function describe(params) {
958
1494
 
959
1495
  // packages/sql/src/exclusion.ts
960
1496
  function invalid2(rule) {
961
- throw new VibeError5({ code: "VIBE_SCHEMA_INVALID", message: `Invalid exclusion constraint: ${rule}`, meta: { rule: `exclusion-${rule}` } });
1497
+ throw new VibeError8({ code: "VIBE_SCHEMA_INVALID", message: `Invalid exclusion constraint: ${rule}`, meta: { rule: `exclusion-${rule}` } });
962
1498
  }
963
1499
  function compileExclusion(params) {
964
1500
  const { exclusion: spec, model, dialect } = params;
965
1501
  if (!dialect.capabilities.exclusionConstraints)
966
- throw unsupportedCapability3({ dialect: dialect.name, feature: "exclusionConstraints" });
1502
+ throw unsupportedCapability9({ dialect: dialect.name, feature: "exclusionConstraints" });
967
1503
  if (!spec.name || spec.name.includes("\x00"))
968
1504
  invalid2("name");
969
1505
  if (!["int8range", "tsrange", "tstzrange"].includes(spec.range.type))
@@ -1006,7 +1542,7 @@ function compileExclusion(params) {
1006
1542
  const column = field(key.field);
1007
1543
  if (column.isOptional)
1008
1544
  invalid2("nullable-key-requires-expression");
1009
- if (!["String", "Int", "BigInt", "Float", "Decimal", "Boolean", "DateTime"].includes(fieldTypeIdentity2({ type: column.type })))
1545
+ if (!isEnumType2(column.type) && !["String", "Int", "BigInt", "Float", "Decimal", "Boolean", "DateTime"].includes(fieldTypeIdentity2({ type: column.type })))
1010
1546
  invalid2("key-type");
1011
1547
  return `${dialect.quoteIdent({ name: column.dbName ?? column.name })} WITH =`;
1012
1548
  });
@@ -1016,13 +1552,13 @@ function compileExclusion(params) {
1016
1552
  }
1017
1553
  function renderExclusionSetup(params) {
1018
1554
  if (!params.dialect.capabilities.exclusionConstraints)
1019
- throw unsupportedCapability3({ dialect: params.dialect.name, feature: "exclusionConstraints" });
1555
+ throw unsupportedCapability9({ dialect: params.dialect.name, feature: "exclusionConstraints" });
1020
1556
  return params.exclusion.requiredExtensions.map((name) => `CREATE EXTENSION IF NOT EXISTS ${params.dialect.quoteIdent({ name })};`);
1021
1557
  }
1022
1558
  function renderExclusionDdl(params) {
1023
1559
  const { dialect, table, exclusion, operation } = params;
1024
1560
  if (!dialect.capabilities.exclusionConstraints)
1025
- throw unsupportedCapability3({ dialect: dialect.name, feature: "exclusionConstraints" });
1561
+ throw unsupportedCapability9({ dialect: dialect.name, feature: "exclusionConstraints" });
1026
1562
  const target = `ALTER TABLE ${dialect.quoteIdent({ name: table })}`;
1027
1563
  if (operation === "drop")
1028
1564
  return [`${target} DROP CONSTRAINT ${dialect.quoteIdent({ name: exclusion.name })};`];
@@ -1031,15 +1567,15 @@ function renderExclusionDdl(params) {
1031
1567
  function renderExclusionCatalog(params) {
1032
1568
  const { dialect, probe } = params;
1033
1569
  if (!dialect.capabilities.exclusionConstraints)
1034
- throw unsupportedCapability3({ dialect: dialect.name, feature: "exclusionConstraints" });
1035
- const scope = probe === undefined ? "n.nspname = 'public'" : `t.oid = ${dialect.quoteStringLiteral({ value: probe })}::regclass`;
1570
+ throw unsupportedCapability9({ dialect: dialect.name, feature: "exclusionConstraints" });
1571
+ const scope = probe === undefined ? `n.nspname = ${dialect.quoteStringLiteral({ value: params.namespace ?? "public" })}` : `t.oid = ${dialect.quoteStringLiteral({ value: probe })}::regclass`;
1036
1572
  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`;
1037
1573
  }
1038
1574
  function renderExclusionProbe(params) {
1039
1575
  const { dialect, table, probe, definition } = params;
1040
1576
  if (!dialect.capabilities.exclusionConstraints)
1041
- throw unsupportedCapability3({ dialect: dialect.name, feature: "exclusionConstraints" });
1042
- 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}`];
1577
+ throw unsupportedCapability9({ dialect: dialect.name, feature: "exclusionConstraints" });
1578
+ return [`CREATE TEMP TABLE ${dialect.quoteIdent({ name: probe })} (LIKE ${dialect.quoteIdent({ name: params.namespace ?? "public" })}.${dialect.quoteIdent({ name: table })})`, `ALTER TABLE ${dialect.quoteIdent({ name: probe })} ADD CONSTRAINT ${dialect.quoteIdent({ name: `${probe}_x` })} ${definition}`];
1043
1579
  }
1044
1580
  // packages/sql/src/ast.ts
1045
1581
  var SET_OPERATORS = ["increment", "decrement", "multiply", "divide"];
@@ -1081,7 +1617,7 @@ function isSqlDefaultCell(value) {
1081
1617
  return value === SQL_DEFAULT_CELL;
1082
1618
  }
1083
1619
  function jsonParamCell(params) {
1084
- return { __jsonParam: true, text: params.text };
1620
+ return params.storage === "json" ? { __jsonParam: true, text: params.text, storage: "json" } : { __jsonParam: true, text: params.text };
1085
1621
  }
1086
1622
  function isJsonParamCell(value) {
1087
1623
  return typeof value === "object" && value !== null && value.__jsonParam === true;
@@ -1092,9 +1628,32 @@ function excludedRef(params) {
1092
1628
  function isExcludedRef(value) {
1093
1629
  return typeof value === "object" && value !== null && value.__excluded === true;
1094
1630
  }
1631
+ function returningColumnName(column) {
1632
+ return typeof column === "string" ? column : column.name;
1633
+ }
1095
1634
  var ADVISORY_LOCK_RESULT_COLUMN = "acquired";
1635
+ function stepColumnRef(params) {
1636
+ return { __stepColumn: true, step: params.step, column: params.column };
1637
+ }
1638
+ function isStepColumnRef(value) {
1639
+ return typeof value === "object" && value !== null && value.__stepColumn === true;
1640
+ }
1641
+ var CHAIN_VETO_PREFIX = "__vibe_veto:";
1642
+ var CHAIN_VETO_TOKEN_PATTERN = /^[a-z][a-z0-9_]*$/;
1643
+ function chainVetoToken(params) {
1644
+ const { message, nonce } = params;
1645
+ if (nonce === "")
1646
+ return;
1647
+ for (let start = message.indexOf(CHAIN_VETO_PREFIX);start >= 0; start = message.indexOf(CHAIN_VETO_PREFIX, start + 1)) {
1648
+ const rest = message.slice(start + CHAIN_VETO_PREFIX.length);
1649
+ const match = /^([a-z][a-z0-9_]*):/.exec(rest);
1650
+ if (match !== null && rest.startsWith(`${nonce}:`, match[0].length))
1651
+ return match[1];
1652
+ }
1653
+ return;
1654
+ }
1096
1655
  // packages/sql/src/locks.ts
1097
- import { internalError, unsupportedCapability as unsupportedCapability4 } from "@vibeorm/schema";
1656
+ import { internalError, unsupportedCapability as unsupportedCapability10 } from "@vibeorm/schema";
1098
1657
  var ROW_LOCK_PROSE = Object.freeze({
1099
1658
  strengths: Object.freeze({
1100
1659
  update: "FOR UPDATE",
@@ -1114,7 +1673,7 @@ function renderRowLockClause(params) {
1114
1673
  if (syntax === null) {
1115
1674
  if (lock.strength === "update" && lock.wait === "wait")
1116
1675
  return null;
1117
- throw unsupportedCapability4({
1676
+ throw unsupportedCapability10({
1118
1677
  dialect: dialect.name,
1119
1678
  feature: `a ${describeRowLock({ lock })} locking read`,
1120
1679
  hint: `${dialect.name} has no locking-read clause at all`
@@ -1122,7 +1681,7 @@ function renderRowLockClause(params) {
1122
1681
  }
1123
1682
  const strengthText = syntax.strengths[lock.strength];
1124
1683
  if (strengthText === undefined) {
1125
- throw unsupportedCapability4({
1684
+ throw unsupportedCapability10({
1126
1685
  dialect: dialect.name,
1127
1686
  feature: `the ${describeRowLock({ lock: { ...lock, wait: "wait" } })} row-lock strength`,
1128
1687
  hint: `${dialect.name} can express: ${Object.keys(syntax.strengths).join(", ")}`
@@ -1130,7 +1689,7 @@ function renderRowLockClause(params) {
1130
1689
  }
1131
1690
  const waitText = syntax.wait[lock.wait];
1132
1691
  if (waitText === undefined) {
1133
- throw unsupportedCapability4({
1692
+ throw unsupportedCapability10({
1134
1693
  dialect: dialect.name,
1135
1694
  feature: `the "${lock.wait}" lock wait policy`,
1136
1695
  hint: `${dialect.name} can express: ${Object.keys(syntax.wait).join(", ")}`
@@ -1149,7 +1708,7 @@ function renderAdvisoryLock(params) {
1149
1708
  const { dialect, statement, collector } = params;
1150
1709
  const functions = dialect.advisoryLockFunctions;
1151
1710
  if (functions === null) {
1152
- throw unsupportedCapability4({
1711
+ throw unsupportedCapability10({
1153
1712
  dialect: dialect.name,
1154
1713
  feature: "advisory locks",
1155
1714
  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`
@@ -1182,33 +1741,13 @@ function projectionOutputName(params) {
1182
1741
  const { column } = params;
1183
1742
  return column.transform === undefined ? column.alias ?? column.name : column.name;
1184
1743
  }
1185
- // packages/sql/src/capabilities.ts
1186
- import {
1187
- DIALECT_CAPABILITIES,
1188
- POSTGRES_CAPABILITIES,
1189
- SQLITE_CAPABILITIES,
1190
- MYSQL_CAPABILITIES
1191
- } from "@vibeorm/schema";
1192
-
1193
- // packages/sql/src/identifiers.ts
1194
- import { VibeError as VibeError6 } from "@vibeorm/schema";
1195
- function assertNoNul(params) {
1196
- if (params.name.includes("\x00")) {
1197
- throw new VibeError6({
1198
- code: "VIBE_VALIDATION",
1199
- message: "an SQL identifier cannot contain a NUL character \u2014 the server would stop reading it there and address a different object",
1200
- meta: { name: params.name.replaceAll("\x00", "\\0") }
1201
- });
1202
- }
1203
- return params.name;
1204
- }
1205
-
1206
1744
  // packages/sql/src/dialects/postgres.ts
1207
1745
  var postgresDialect = {
1208
1746
  name: "postgres",
1209
1747
  capabilities: POSTGRES_CAPABILITIES,
1210
1748
  inArrayStrategy: "any",
1211
1749
  tupleInStrategy: "rowValue",
1750
+ timeOfDayInStrategy: "in",
1212
1751
  upsertForm: "onConflict",
1213
1752
  conflictKeyEquality: {
1214
1753
  foldingScalars: ["Decimal"],
@@ -1219,6 +1758,7 @@ var postgresDialect = {
1219
1758
  doNothingForm: "onConflictDoNothing",
1220
1759
  insensitiveLike: "ilike",
1221
1760
  strictMatchStrategy: "native",
1761
+ greatestForm: "native",
1222
1762
  orderByNullsMode: "native",
1223
1763
  offsetRequiresLimit: null,
1224
1764
  likeEscapeClause: null,
@@ -1255,6 +1795,7 @@ var postgresDialect = {
1255
1795
  keyCast: Object.freeze({ single: "::bigint", pair: "::int4" })
1256
1796
  },
1257
1797
  rowDefaultKeyword: true,
1798
+ statementChain: "dataModifyingCte",
1258
1799
  paramBudget: 30000,
1259
1800
  booleanLiteral: { true: "true", false: "false" },
1260
1801
  quoteIdent({ name }) {
@@ -1272,63 +1813,13 @@ var postgresDialect = {
1272
1813
  return `(now() + make_interval(secs => ${collector.add(offsetSeconds)}))`;
1273
1814
  }
1274
1815
  };
1275
- // packages/sql/src/dialects/sqlite.ts
1276
- var sqliteDialect = {
1277
- name: "sqlite",
1278
- capabilities: SQLITE_CAPABILITIES,
1279
- inArrayStrategy: "expand",
1280
- tupleInStrategy: "rowValue",
1281
- upsertForm: "onConflict",
1282
- conflictKeyEquality: {
1283
- foldingScalars: [],
1284
- textIsBinaryByDefault: true,
1285
- duplicatesInOneStatement: "lastWriteWins"
1286
- },
1287
- excludedRowRef: { form: "keyword", name: "excluded" },
1288
- doNothingForm: "onConflictDoNothing",
1289
- insensitiveLike: "lower",
1290
- strictMatchStrategy: "glob",
1291
- orderByNullsMode: "native",
1292
- offsetRequiresLimit: { limitText: "-1" },
1293
- likeEscapeClause: " ESCAPE '\\'",
1294
- jsonPathMode: "sqliteExtract",
1295
- supportedJsonOps: [
1296
- "equals",
1297
- "string_contains",
1298
- "string_starts_with",
1299
- "string_ends_with",
1300
- "array_contains",
1301
- "array_starts_with",
1302
- "array_ends_with"
1303
- ],
1304
- jsonParamCast: "",
1305
- lastInsertIdSql: null,
1306
- rowLockSyntax: null,
1307
- advisoryLockFunctions: null,
1308
- rowDefaultKeyword: false,
1309
- paramBudget: 900,
1310
- booleanLiteral: { true: "1", false: "0" },
1311
- quoteIdent({ name }) {
1312
- return `"${assertNoNul({ name }).replaceAll('"', '""')}"`;
1313
- },
1314
- quoteStringLiteral({ value }) {
1315
- return `'${value.replaceAll("'", "''")}'`;
1316
- },
1317
- placeholder() {
1318
- return "?";
1319
- },
1320
- renderDbNow({ offsetSeconds, collector }) {
1321
- if (offsetSeconds === 0)
1322
- return `strftime('%Y-%m-%dT%H:%M:%fZ', 'now')`;
1323
- return `strftime('%Y-%m-%dT%H:%M:%fZ', 'now', ${collector.add(offsetSeconds)} || ' seconds')`;
1324
- }
1325
- };
1326
1816
  // packages/sql/src/dialects/mysql.ts
1327
1817
  var mysqlDialect = {
1328
1818
  name: "mysql",
1329
1819
  capabilities: MYSQL_CAPABILITIES,
1330
1820
  inArrayStrategy: "expand",
1331
1821
  tupleInStrategy: "rowValue",
1822
+ timeOfDayInStrategy: "equalities",
1332
1823
  upsertForm: "onDuplicateKey",
1333
1824
  conflictKeyEquality: {
1334
1825
  foldingScalars: ["Decimal"],
@@ -1340,6 +1831,7 @@ var mysqlDialect = {
1340
1831
  doNothingForm: "insertIgnore",
1341
1832
  insensitiveLike: "lower",
1342
1833
  strictMatchStrategy: "binary",
1834
+ greatestForm: "wrapped-frame",
1343
1835
  orderByNullsMode: "emulate",
1344
1836
  offsetRequiresLimit: { limitText: "18446744073709551615" },
1345
1837
  likeEscapeClause: null,
@@ -1361,6 +1853,7 @@ var mysqlDialect = {
1361
1853
  },
1362
1854
  advisoryLockFunctions: null,
1363
1855
  rowDefaultKeyword: true,
1856
+ statementChain: null,
1364
1857
  paramBudget: 1e4,
1365
1858
  booleanLiteral: { true: "1", false: "0" },
1366
1859
  quoteIdent({ name }) {
@@ -1388,7 +1881,7 @@ function getSqlDialect(params) {
1388
1881
  return SQL_DIALECTS[params.dialect];
1389
1882
  }
1390
1883
  // packages/sql/src/dialects/diagnostics.ts
1391
- import { unsupportedCapability as unsupportedCapability5 } from "@vibeorm/schema";
1884
+ import { unsupportedCapability as unsupportedCapability11 } from "@vibeorm/schema";
1392
1885
  var QUERIES = Object.freeze({
1393
1886
  postgresJsonWire: { dialect: "postgres", text: `SELECT '{"__vibeorm":1}'::jsonb AS "probe"` },
1394
1887
  sqliteTriggers: { dialect: "sqlite", text: "SELECT name, tbl_name, sql FROM sqlite_master WHERE type = 'trigger'" }
@@ -1396,7 +1889,7 @@ var QUERIES = Object.freeze({
1396
1889
  function renderDiagnostic(params) {
1397
1890
  const query = QUERIES[params.query];
1398
1891
  if (params.dialect.name !== query.dialect) {
1399
- throw unsupportedCapability5({ dialect: params.dialect.name, feature: params.query, hint: `this diagnostic requires ${query.dialect}` });
1892
+ throw unsupportedCapability11({ dialect: params.dialect.name, feature: params.query, hint: `this diagnostic requires ${query.dialect}` });
1400
1893
  }
1401
1894
  return { text: query.text, values: [], returning: "none" };
1402
1895
  }
@@ -1405,7 +1898,7 @@ function isFixedDiagnostic(params) {
1405
1898
  }
1406
1899
 
1407
1900
  // packages/sql/src/dialects/postgres-diagnostics.ts
1408
- import { VibeError as VibeError7, unsupportedCapability as unsupportedCapability6 } from "@vibeorm/schema";
1901
+ import { VibeError as VibeError9, unsupportedCapability as unsupportedCapability12 } from "@vibeorm/schema";
1409
1902
  function renderPostgresExplain(params) {
1410
1903
  const { dialect, query, options } = params;
1411
1904
  requirePostgres({ dialect, feature: "query plan inspection (EXPLAIN)" });
@@ -1440,7 +1933,7 @@ var POSTGRES_WORKLOAD_LIMITS = Object.freeze({
1440
1933
  });
1441
1934
  function requirePostgres(params) {
1442
1935
  if (params.dialect.name !== "postgres") {
1443
- throw unsupportedCapability6({
1936
+ throw unsupportedCapability12({
1444
1937
  dialect: params.dialect.name,
1445
1938
  feature: params.feature,
1446
1939
  hint: "plan inspection and workload statistics are PostgreSQL-shaped; portable operation tracing works on every dialect"
@@ -1458,7 +1951,7 @@ function renderPostgresWorkload(params) {
1458
1951
  if (query === "statementSnapshot") {
1459
1952
  const bounded = limit ?? 20;
1460
1953
  if (!Number.isInteger(bounded) || bounded < POSTGRES_WORKLOAD_LIMITS.minLimit || bounded > POSTGRES_WORKLOAD_LIMITS.maxLimit) {
1461
- throw new VibeError7({
1954
+ throw new VibeError9({
1462
1955
  code: "VIBE_CONFIG",
1463
1956
  message: `workload snapshot limit must be an integer between ${POSTGRES_WORKLOAD_LIMITS.minLimit} and ${POSTGRES_WORKLOAD_LIMITS.maxLimit}, got ${String(limit)}`,
1464
1957
  meta: { limit }
@@ -1477,7 +1970,7 @@ function renderPostgresWorkload(params) {
1477
1970
  function requireArg(params) {
1478
1971
  const { value, name, query } = params;
1479
1972
  if (value === undefined || value.length === 0) {
1480
- throw new VibeError7({
1973
+ throw new VibeError9({
1481
1974
  code: "VIBE_CONFIG",
1482
1975
  message: `the "${query}" diagnostic read requires a ${name}`,
1483
1976
  meta: { query, argument: name }
@@ -1487,10 +1980,10 @@ function requireArg(params) {
1487
1980
  }
1488
1981
 
1489
1982
  // packages/sql/src/dialects/postgres-rls-readiness.ts
1490
- import { unsupportedCapability as unsupportedCapability8 } from "@vibeorm/schema";
1983
+ import { unsupportedCapability as unsupportedCapability14 } from "@vibeorm/schema";
1491
1984
 
1492
1985
  // packages/sql/src/dialects/postgres-rls.ts
1493
- import { RlsContextError, VibeError as VibeError8, assertValidSchemaIR, unsupportedCapability as unsupportedCapability7, validateRlsContextIR } from "@vibeorm/schema";
1986
+ import { RlsContextError, VibeError as VibeError10, assertValidSchemaIR, unsupportedCapability as unsupportedCapability13, validateRlsContextIR } from "@vibeorm/schema";
1494
1987
  var NATIVE_RLS = Object.freeze({ policyName: "vibeorm_managed", tableSchema: "public", settingPrefix: "vibeorm.", parentAlias: "parent" });
1495
1988
  var COLUMN_TYPES = {
1496
1989
  String: { default: "text", native: { text: "text", varchar: "varchar", charactervarying: "varchar", uuid: "uuid" } },
@@ -1503,18 +1996,18 @@ var COLUMN_TYPES = {
1503
1996
  };
1504
1997
  function requireRls(params) {
1505
1998
  if (!params.dialect.capabilities.nativeRowLevelSecurity) {
1506
- throw unsupportedCapability7({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity" });
1999
+ throw unsupportedCapability13({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity" });
1507
2000
  }
1508
2001
  }
1509
2002
  function identifier2(params) {
1510
2003
  if (typeof params.name !== "string" || params.name.length === 0 || params.name.includes("\x00") || new TextEncoder().encode(params.name).length > 63) {
1511
- throw new VibeError8({ code: "VIBE_SCHEMA_INVALID", message: "Native RLS identifiers must be nonempty PostgreSQL identifiers of at most 63 bytes" });
2004
+ throw new VibeError10({ code: "VIBE_SCHEMA_INVALID", message: "Native RLS identifiers must be nonempty PostgreSQL identifiers of at most 63 bytes" });
1512
2005
  }
1513
2006
  return params.dialect.quoteIdent({ name: params.name });
1514
2007
  }
1515
2008
  function tableRef(params) {
1516
2009
  if (params.schema !== undefined && params.schema !== "public" && params.schema !== "pg_temp") {
1517
- throw unsupportedCapability7({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity", hint: "Only public tables and temporary policy probes are supported" });
2010
+ throw unsupportedCapability13({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity", hint: "Only public tables and temporary policy probes are supported" });
1518
2011
  }
1519
2012
  return `${identifier2({ dialect: params.dialect, name: params.schema ?? NATIVE_RLS.tableSchema })}.${identifier2({ dialect: params.dialect, name: params.table })}`;
1520
2013
  }
@@ -1555,14 +2048,14 @@ function renderRlsColumnType(params) {
1555
2048
  if (native !== undefined)
1556
2049
  return native;
1557
2050
  }
1558
- throw unsupportedCapability7({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity", hint: `Unsupported physical ownership type on field ${field.name}` });
2051
+ throw unsupportedCapability13({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity", hint: `Unsupported physical ownership type on field ${field.name}` });
1559
2052
  }
1560
2053
  function protectedModel(params) {
1561
2054
  requireRls(params);
1562
2055
  assertValidSchemaIR({ schema: params.schema });
1563
2056
  const model = params.schema.models.find((entry) => entry.name === params.model);
1564
2057
  if (model === undefined || model.rls === undefined || model.rls === "unprotected") {
1565
- throw new VibeError8({ code: "VIBE_SCHEMA_INVALID", message: "Native RLS policy rendering requires a protected model", meta: { model: params.model } });
2058
+ throw new VibeError10({ code: "VIBE_SCHEMA_INVALID", message: "Native RLS policy rendering requires a protected model", meta: { model: params.model } });
1566
2059
  }
1567
2060
  return model;
1568
2061
  }
@@ -1595,7 +2088,7 @@ function predicate(params) {
1595
2088
  }).join(" AND ");
1596
2089
  return `EXISTS (SELECT 1 FROM ${tableRef({ dialect, table: parent.dbName ?? parent.name })} AS ${alias} WHERE ${join})`;
1597
2090
  }
1598
- throw new VibeError8({ code: "VIBE_SCHEMA_INVALID", message: "Native RLS requires a protected policy rule" });
2091
+ throw new VibeError10({ code: "VIBE_SCHEMA_INVALID", message: "Native RLS requires a protected policy rule" });
1599
2092
  }
1600
2093
  function renderRlsPolicy(params) {
1601
2094
  const model = protectedModel(params);
@@ -1629,7 +2122,7 @@ function renderRlsContext(params) {
1629
2122
  function renderRlsRole(params) {
1630
2123
  requireRls(params);
1631
2124
  if (typeof params.role !== "string" || params.role.trim().length === 0 || params.role.includes("\x00")) {
1632
- throw new VibeError8({ code: "VIBE_CONFIG", message: "PGlite native RLS requires a trusted nonempty runtime role name" });
2125
+ throw new VibeError10({ code: "VIBE_CONFIG", message: "PGlite native RLS requires a trusted nonempty runtime role name" });
1633
2126
  }
1634
2127
  const collector = new ParamCollector(params.dialect);
1635
2128
  return query({ text: `SELECT set_config('role', ${collector.add(params.role)}, true)`, values: collector.values });
@@ -1781,7 +2274,7 @@ ORDER BY c.relname, p.polname`,
1781
2274
  });
1782
2275
  function requireRls2(params) {
1783
2276
  if (!params.dialect.capabilities.nativeRowLevelSecurity) {
1784
- throw unsupportedCapability8({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity" });
2277
+ throw unsupportedCapability14({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity" });
1785
2278
  }
1786
2279
  }
1787
2280
  function renderRlsReadinessQuery(params) {
@@ -1813,7 +2306,7 @@ function renderRlsPolicyPath(params) {
1813
2306
  }
1814
2307
 
1815
2308
  // packages/sql/src/render.ts
1816
- import { internalError as internalError2, unsupportedCapability as unsupportedCapability9 } from "@vibeorm/schema";
2309
+ import { assertSqlNamespace as assertSqlNamespace2, internalError as internalError2, unsupportedCapability as unsupportedCapability15 } from "@vibeorm/schema";
1817
2310
 
1818
2311
  // packages/sql/src/like.ts
1819
2312
  function escapeLikePattern(params) {
@@ -1867,6 +2360,8 @@ function renderStatement(params) {
1867
2360
  return renderUpdate({ dialect, statement, collector });
1868
2361
  case "delete":
1869
2362
  return renderDelete({ dialect, statement, collector });
2363
+ case "chain":
2364
+ return renderChain({ dialect, statement, collector });
1870
2365
  case "rlsReadiness":
1871
2366
  return renderRlsReadinessQuery({ dialect, ...statement });
1872
2367
  case "rlsProbeTable":
@@ -1900,8 +2395,29 @@ function quoteCol(dialect, column) {
1900
2395
  }
1901
2396
  return `CAST(${ref} AS REAL)`;
1902
2397
  }
2398
+ if (column.castJsonb !== undefined) {
2399
+ if (dialect.name !== "postgres") {
2400
+ throw internalError2({ message: "ColRef.castJsonb is a postgres json construct" });
2401
+ }
2402
+ return `${ref}::${column.castJsonb}`;
2403
+ }
1903
2404
  return ref;
1904
2405
  }
2406
+ function typedOrderRef(params) {
2407
+ const { statement, column } = params;
2408
+ if (column.table !== undefined || column.castReal === true || column.castJsonb !== undefined)
2409
+ return column;
2410
+ const renamed = statement.columns.some((projected) => (projected.alias ?? projected.name) === column.name && (projected.transform !== undefined || projected.alias !== undefined));
2411
+ return renamed ? { ...column, table: statement.table.alias ?? statement.table.name } : column;
2412
+ }
2413
+ function jsonCellCast(dialect, cell) {
2414
+ if (cell.storage !== "json" || dialect.jsonParamCast === "")
2415
+ return dialect.jsonParamCast;
2416
+ if (dialect.name !== "postgres") {
2417
+ throw internalError2({ message: "JsonParamCell storage json is a postgres construct" });
2418
+ }
2419
+ return "::text::json";
2420
+ }
1905
2421
  function quoteProjection(dialect, column) {
1906
2422
  if (column.transform === undefined) {
1907
2423
  const base = quoteCol(dialect, column);
@@ -1910,7 +2426,7 @@ function quoteProjection(dialect, column) {
1910
2426
  return `${base} AS ${dialect.quoteIdent({ name: column.alias })}`;
1911
2427
  }
1912
2428
  if (dialect.name !== "postgres") {
1913
- throw internalError2({ message: "ColRef.transform is a postgres lateral-join construct" });
2429
+ throw internalError2({ message: "ColRef.transform is a postgres construct" });
1914
2430
  }
1915
2431
  const ref = quoteCol(dialect, column);
1916
2432
  const alias = dialect.quoteIdent({ name: column.name });
@@ -1918,8 +2434,15 @@ function quoteProjection(dialect, column) {
1918
2434
  return `${ref}::text AS ${alias}`;
1919
2435
  return `encode(${ref}, 'base64') AS ${alias}`;
1920
2436
  }
2437
+ function renderProjection(params) {
2438
+ const { dialect, columns } = params;
2439
+ return columns.map((column) => quoteProjection(dialect, column)).join(", ");
2440
+ }
1921
2441
  function renderTable(dialect, table) {
1922
- const name = dialect.quoteIdent({ name: table.name });
2442
+ if (table.namespace !== undefined)
2443
+ assertSqlNamespace2({ namespace: table.namespace, dialect: dialect.name });
2444
+ const relation = dialect.quoteIdent({ name: table.name });
2445
+ const name = table.namespace === undefined ? relation : `${dialect.quoteIdent({ name: table.namespace })}.${relation}`;
1923
2446
  if (table.alias === undefined)
1924
2447
  return name;
1925
2448
  return `${name} AS ${dialect.quoteIdent({ name: table.alias })}`;
@@ -1958,7 +2481,7 @@ function renderAggRef(dialect, agg) {
1958
2481
  function assertScalarArrays(dialect, feature) {
1959
2482
  if (dialect.capabilities.scalarArrays)
1960
2483
  return;
1961
- throw unsupportedCapability9({
2484
+ throw unsupportedCapability15({
1962
2485
  dialect: dialect.name,
1963
2486
  feature,
1964
2487
  hint: "only postgres has native array columns \u2014 model the list as a related table instead"
@@ -1975,7 +2498,8 @@ function renderExpr(params) {
1975
2498
  if (isDbNowMarker(expr.value)) {
1976
2499
  return `${quoteCol(dialect, expr.column)} ${op} ${dialect.renderDbNow({ offsetSeconds: expr.value.offsetSeconds, collector })}`;
1977
2500
  }
1978
- return `${quoteCol(dialect, expr.column)} ${op} ${collector.add(expr.value)}`;
2501
+ const compared = listComparison({ dialect, column: expr.column, value: expr.value, collector });
2502
+ return `${compared.column} ${op} ${compared.operand}`;
1979
2503
  }
1980
2504
  case "cmpCol": {
1981
2505
  const op = COMPARISON_SQL2[expr.op];
@@ -1991,8 +2515,12 @@ function renderExpr(params) {
1991
2515
  return expr.negate === true ? "1 = 1" : "1 = 0";
1992
2516
  }
1993
2517
  const column = quoteCol(dialect, expr.column);
2518
+ if (expr.timeOfDay === true && dialect.timeOfDayInStrategy === "equalities") {
2519
+ const equalities = collector.addMany(expr.values).map((placeholder) => `${column} = ${placeholder}`);
2520
+ return `${expr.negate === true ? "NOT " : ""}(${equalities.join(" OR ")})`;
2521
+ }
1994
2522
  if (dialect.inArrayStrategy === "any") {
1995
- const base = `${column} = ANY(${collector.add([...expr.values])})`;
2523
+ const base = `${column} = ANY(${collector.add(Array.isArray(expr.values) ? [...expr.values] : expr.values)})`;
1996
2524
  return expr.negate === true ? `NOT (${base})` : base;
1997
2525
  }
1998
2526
  const placeholders = collector.addMany(expr.values);
@@ -2084,16 +2612,21 @@ function renderExpr(params) {
2084
2612
  }
2085
2613
  case "arrayHas": {
2086
2614
  assertScalarArrays(dialect, 'the scalar-list filter "has"');
2087
- const base = `${collector.add(expr.value)} = ANY(${quoteCol(dialect, expr.column)})`;
2615
+ const compared = listComparison({ dialect, column: expr.column, value: expr.value, collector });
2616
+ const base = `${compared.operand} = ANY(${compared.column})`;
2088
2617
  return expr.negate === true ? `NOT (${base})` : base;
2089
2618
  }
2090
2619
  case "arrayContainsAll": {
2091
2620
  assertScalarArrays(dialect, 'the scalar-list filter "hasEvery"');
2092
- return `${quoteCol(dialect, expr.column)} @> ${collector.add([...expr.values])}`;
2621
+ const values = Array.isArray(expr.values) ? [...expr.values] : expr.values;
2622
+ const compared = listComparison({ dialect, column: expr.column, value: values, collector });
2623
+ return `${compared.column} @> ${compared.operand}`;
2093
2624
  }
2094
2625
  case "arrayOverlaps": {
2095
2626
  assertScalarArrays(dialect, 'the scalar-list filter "hasSome"');
2096
- return `${quoteCol(dialect, expr.column)} && ${collector.add([...expr.values])}`;
2627
+ const values = Array.isArray(expr.values) ? [...expr.values] : expr.values;
2628
+ const compared = listComparison({ dialect, column: expr.column, value: values, collector });
2629
+ return `${compared.column} && ${compared.operand}`;
2097
2630
  }
2098
2631
  case "arrayIsEmpty": {
2099
2632
  assertScalarArrays(dialect, 'the scalar-list filter "isEmpty"');
@@ -2141,6 +2674,16 @@ function jsonPathString(path) {
2141
2674
  }
2142
2675
  return out;
2143
2676
  }
2677
+ function listComparison(params) {
2678
+ const { dialect, column, value, collector } = params;
2679
+ if (isJsonListParameter(value) && value.storage === "json") {
2680
+ return {
2681
+ column: `${quoteCol(dialect, column)}::jsonb[]`,
2682
+ operand: collector.add(jsonListParameter({ value: value.value, storage: "jsonb" }))
2683
+ };
2684
+ }
2685
+ return { column: quoteCol(dialect, column), operand: collector.add(value) };
2686
+ }
2144
2687
  function jsonStringPattern(params) {
2145
2688
  const { expr } = params;
2146
2689
  if (typeof expr.value !== "string") {
@@ -2168,7 +2711,7 @@ function renderJsonLike(params) {
2168
2711
  function renderJsonFilter(params) {
2169
2712
  const { dialect, expr, collector } = params;
2170
2713
  if (!dialect.supportedJsonOps.includes(expr.op)) {
2171
- throw unsupportedCapability9({
2714
+ throw unsupportedCapability15({
2172
2715
  dialect: dialect.name,
2173
2716
  feature: `the json filter "${expr.op}"`,
2174
2717
  hint: `this dialect can express: ${dialect.supportedJsonOps.join(", ")}`
@@ -2178,19 +2721,22 @@ function renderJsonFilter(params) {
2178
2721
  if (dialect.jsonPathMode === "operators") {
2179
2722
  const column = quoteCol(dialect, expr.column);
2180
2723
  const pathParam = `${collector.add(expr.path.map(String))}::text[]`;
2724
+ const extracted = expr.storage === "json" ? `(${column} #> ${pathParam})::jsonb` : `(${column} #> ${pathParam})`;
2181
2725
  switch (expr.op) {
2182
2726
  case "equals":
2183
- return `(${column} #> ${pathParam}) ${cmp} ${collector.add(JSON.stringify(expr.value))}${dialect.jsonParamCast}`;
2727
+ return `${extracted} ${cmp} ${collector.add(JSON.stringify(expr.value))}${dialect.jsonParamCast}`;
2184
2728
  case "string_contains":
2185
2729
  case "string_starts_with":
2186
- case "string_ends_with":
2187
- return renderJsonLike({ dialect, target: `(${column} #>> ${pathParam})`, expr, collector });
2730
+ case "string_ends_with": {
2731
+ const target = expr.storage === "json" ? `(${extracted} #>> '{}')` : `(${column} #>> ${pathParam})`;
2732
+ return renderJsonLike({ dialect, target, expr, collector });
2733
+ }
2188
2734
  case "array_contains":
2189
- return `(${column} #> ${pathParam}) @> ${collector.add(JSON.stringify(expr.value))}${dialect.jsonParamCast}`;
2735
+ return `${extracted} @> ${collector.add(JSON.stringify(expr.value))}${dialect.jsonParamCast}`;
2190
2736
  case "array_starts_with":
2191
- return `(${column} #> ${pathParam}) -> 0 = ${collector.add(JSON.stringify(expr.value))}${dialect.jsonParamCast}`;
2737
+ return `${extracted} -> 0 = ${collector.add(JSON.stringify(expr.value))}${dialect.jsonParamCast}`;
2192
2738
  case "array_ends_with":
2193
- return `(${column} #> ${pathParam}) -> -1 = ${collector.add(JSON.stringify(expr.value))}${dialect.jsonParamCast}`;
2739
+ return `${extracted} -> -1 = ${collector.add(JSON.stringify(expr.value))}${dialect.jsonParamCast}`;
2194
2740
  }
2195
2741
  }
2196
2742
  if (dialect.jsonPathMode === "sqliteExtract") {
@@ -2232,7 +2778,7 @@ function renderJsonFilter(params) {
2232
2778
  const elements = Array.isArray(expr.value) ? expr.value : [expr.value];
2233
2779
  const tests = elements.map((element) => {
2234
2780
  if (element === null || typeof element === "object" && element !== null) {
2235
- throw unsupportedCapability9({
2781
+ throw unsupportedCapability15({
2236
2782
  dialect: dialect.name,
2237
2783
  feature: "json array_contains with object/array/null elements",
2238
2784
  hint: "sqlite expresses scalar-element containment only \u2014 deep structural containment needs postgres or mysql"
@@ -2334,7 +2880,14 @@ function renderLateralJoin(params) {
2334
2880
  const terms = join.select.orderBy;
2335
2881
  const columnTerms = terms.filter((term) => ("column" in term));
2336
2882
  if (columnTerms.length === terms.length && columnTerms.every((term) => projected.has(term.column.name))) {
2337
- const rendered = columnTerms.map((term) => renderOrderTerm({ dialect, term: { ...term, column: { table: "__sub", name: term.column.name } }, collector }));
2883
+ const rendered = columnTerms.map((term) => renderOrderTerm({
2884
+ dialect,
2885
+ term: {
2886
+ ...term,
2887
+ column: { table: "__sub", name: term.column.name, ...term.column.castJsonb === undefined ? {} : { castJsonb: term.column.castJsonb } }
2888
+ },
2889
+ collector
2890
+ }));
2338
2891
  aggOrder = ` ORDER BY ${rendered.join(", ")}`;
2339
2892
  }
2340
2893
  }
@@ -2343,6 +2896,15 @@ function renderLateralJoin(params) {
2343
2896
  const alias = dialect.quoteIdent({ name: join.alias });
2344
2897
  return `LEFT JOIN LATERAL (SELECT ${projection} FROM (${inner.text}) AS ${subAlias}) AS ${alias} ON true`;
2345
2898
  }
2899
+ function renderCountSubquery(params) {
2900
+ const { dialect, subquery, collector } = params;
2901
+ const select = subquery.select;
2902
+ if (select.columns.length !== 0 || select.selectExpr?.kind !== "countAll" || (select.aggregates?.length ?? 0) > 0 || select.rowNumber !== undefined || select.groupBy !== undefined || select.having !== undefined || select.limit !== undefined || select.offset !== undefined || select.distinctOn !== undefined || select.rowLock !== undefined || select.countSubqueries !== undefined) {
2903
+ throw internalError2({ message: "count subquery must be one ungrouped COUNT(*)" });
2904
+ }
2905
+ const inner = renderSelect({ dialect, statement: select, collector });
2906
+ return `(${inner.text}) AS ${dialect.quoteIdent({ name: subquery.alias })}`;
2907
+ }
2346
2908
  function applyReturning(params) {
2347
2909
  const { dialect, requested } = params;
2348
2910
  if (requested === undefined || requested.length === 0) {
@@ -2351,9 +2913,65 @@ function applyReturning(params) {
2351
2913
  if (!dialect.capabilities.returning) {
2352
2914
  return { suffix: "", status: "omitted" };
2353
2915
  }
2354
- const columns = requested.map((name) => dialect.quoteIdent({ name })).join(", ");
2916
+ const columns = requested.map((column) => {
2917
+ if (typeof column === "string")
2918
+ return dialect.quoteIdent({ name: column });
2919
+ if (dialect.name !== "postgres") {
2920
+ throw internalError2({ message: "a castText RETURNING column is a postgres construct" });
2921
+ }
2922
+ const name = dialect.quoteIdent({ name: column.name });
2923
+ return `${name}::text AS ${name}`;
2924
+ }).join(", ");
2355
2925
  return { suffix: ` RETURNING ${columns}`, status: "native" };
2356
2926
  }
2927
+ function renderKeyTableJoin(params) {
2928
+ const { dialect, join, collector } = params;
2929
+ const postgresMember = (member) => member.kind === "citext" || member.kind === "text" || member.kind === "integer";
2930
+ const admitted = dialect.name === "mysql" ? join.members.every((member) => member.kind !== "citext" && member.kind !== "text") : dialect.name === "postgres" && join.members.every(postgresMember);
2931
+ if (!admitted) {
2932
+ throw unsupportedCapability15({
2933
+ dialect: dialect.name,
2934
+ feature: "relation key table",
2935
+ hint: "the key-table join is mysql's relation association \u2014 this dialect compares relation keys by value"
2936
+ });
2937
+ }
2938
+ if (join.columns.length === 0 || join.columns.length !== join.members.length) {
2939
+ throw internalError2({ message: `key table needs one member per column, got ${join.members.length} for ${join.columns.length}` });
2940
+ }
2941
+ if (join.rows.length === 0)
2942
+ throw internalError2({ message: "key table needs at least one key row" });
2943
+ for (const member of join.members) {
2944
+ if (member.kind === "decimal" && !(Number.isInteger(member.scale) && member.scale >= 0 && member.scale <= 30)) {
2945
+ throw internalError2({ message: `key table decimal scale must be an integer 0-30, got ${String(member.scale)}` });
2946
+ }
2947
+ }
2948
+ const alias = dialect.quoteIdent({ name: join.alias });
2949
+ const memberNames = join.members.map((_, index) => dialect.quoteIdent({ name: `__vibe_k${index}` }));
2950
+ const rows = join.rows.map((row, ordinal) => {
2951
+ if (row.length !== join.members.length) {
2952
+ throw internalError2({ message: `key table row has ${row.length} values for ${join.members.length} members` });
2953
+ }
2954
+ const cells = row.map((value, index) => {
2955
+ const placeholder = collector.add(value);
2956
+ const member = join.members[index];
2957
+ switch (member.kind) {
2958
+ case "integer":
2959
+ return dialect.name === "mysql" ? `CAST(${placeholder} AS SIGNED)` : `CAST(${placeholder} AS bigint)`;
2960
+ case "decimal":
2961
+ return `CAST(${placeholder} AS DECIMAL(65,${member.scale}))`;
2962
+ case "citext":
2963
+ return `CAST(${placeholder} AS citext)`;
2964
+ case "text":
2965
+ return `CAST(${placeholder} AS text)`;
2966
+ default:
2967
+ return placeholder;
2968
+ }
2969
+ });
2970
+ return dialect.name === "mysql" ? `ROW(${cells.join(", ")}, ${ordinal})` : `(${cells.join(", ")}, ${ordinal})`;
2971
+ });
2972
+ const on = join.columns.map((column, index) => `${quoteCol(dialect, column)} = ${alias}.${memberNames[index]}`).join(" AND ");
2973
+ return `INNER JOIN (VALUES ${rows.join(", ")}) AS ${alias} (${[...memberNames, dialect.quoteIdent({ name: join.ordinalAlias })].join(", ")}) ON ${on}`;
2974
+ }
2357
2975
  function renderSelect(params) {
2358
2976
  const { dialect, statement, collector } = params;
2359
2977
  if (statement.selectExpr !== undefined && statement.columns.length > 0 && statement.selectExpr.kind !== "countAll") {
@@ -2371,6 +2989,11 @@ function renderSelect(params) {
2371
2989
  columnList.push(`${renderAggRef(dialect, selection.agg)} AS ${dialect.quoteIdent({ name: selection.alias })}`);
2372
2990
  }
2373
2991
  }
2992
+ if (statement.countSubqueries !== undefined) {
2993
+ for (const subquery of statement.countSubqueries) {
2994
+ columnList.push(renderCountSubquery({ dialect, subquery, collector }));
2995
+ }
2996
+ }
2374
2997
  if (statement.rowNumber !== undefined) {
2375
2998
  const rowNumber = statement.rowNumber;
2376
2999
  if (rowNumber.partitionBy.length === 0) {
@@ -2383,13 +3006,13 @@ function renderSelect(params) {
2383
3006
  let distinctPrefix = "";
2384
3007
  if (statement.distinctOn !== undefined && statement.distinctOn.length > 0) {
2385
3008
  if (!dialect.capabilities.distinctOn) {
2386
- throw unsupportedCapability9({
3009
+ throw unsupportedCapability15({
2387
3010
  dialect: dialect.name,
2388
3011
  feature: "SELECT DISTINCT ON",
2389
3012
  hint: "postgres-only \u2014 the runtime emulates distinct client-side on this dialect"
2390
3013
  });
2391
3014
  }
2392
- distinctPrefix = `DISTINCT ON (${statement.distinctOn.map((column) => quoteCol(dialect, column)).join(", ")}) `;
3015
+ distinctPrefix = `DISTINCT ON (${statement.distinctOn.map((column) => quoteCol(dialect, typedOrderRef({ statement, column }))).join(", ")}) `;
2393
3016
  }
2394
3017
  const columns = columnList.length === 0 ? "*" : columnList.join(", ");
2395
3018
  let from;
@@ -2412,9 +3035,12 @@ function renderSelect(params) {
2412
3035
  text += ` INNER JOIN ${renderTable(dialect, join.table)} ON ${on}`;
2413
3036
  }
2414
3037
  }
3038
+ if (statement.keyTable !== undefined) {
3039
+ text += ` ${renderKeyTableJoin({ dialect, join: statement.keyTable, collector })}`;
3040
+ }
2415
3041
  if (statement.lateralJoins !== undefined && statement.lateralJoins.length > 0) {
2416
3042
  if (!dialect.capabilities.lateralJoin) {
2417
- throw unsupportedCapability9({
3043
+ throw unsupportedCapability15({
2418
3044
  dialect: dialect.name,
2419
3045
  feature: "LATERAL JOIN relation loading",
2420
3046
  hint: 'use the portable "query" relation strategy on this dialect'
@@ -2434,7 +3060,7 @@ function renderSelect(params) {
2434
3060
  text += ` HAVING ${renderExpr({ dialect, expr: statement.having, collector })}`;
2435
3061
  }
2436
3062
  if (statement.orderBy !== undefined && statement.orderBy.length > 0) {
2437
- const terms = statement.orderBy.map((term) => renderOrderTerm({ dialect, term, collector }));
3063
+ const terms = statement.orderBy.map((term) => renderOrderTerm({ dialect, term: "column" in term ? { ...term, column: typedOrderRef({ statement, column: term.column }) } : term, collector }));
2438
3064
  text += ` ORDER BY ${terms.join(", ")}`;
2439
3065
  }
2440
3066
  if (statement.limit !== undefined) {
@@ -2476,7 +3102,7 @@ function renderInsert(params) {
2476
3102
  const columnList = statement.columns.map((name) => dialect.quoteIdent({ name })).join(", ");
2477
3103
  const rowsSql = statement.rows.map((row) => `(${row.map((value) => {
2478
3104
  if (isJsonParamCell(value))
2479
- return `${collector.add(value.text)}${dialect.jsonParamCast}`;
3105
+ return `${collector.add(value.text)}${jsonCellCast(dialect, value)}`;
2480
3106
  if (isDbNowMarker(value)) {
2481
3107
  return dialect.renderDbNow({ offsetSeconds: value.offsetSeconds, collector });
2482
3108
  }
@@ -2499,7 +3125,7 @@ function renderInsert(params) {
2499
3125
  let target = "";
2500
3126
  if (onConflict.constraint !== undefined) {
2501
3127
  if (dialect.name !== "postgres") {
2502
- throw unsupportedCapability9({
3128
+ throw unsupportedCapability15({
2503
3129
  dialect: dialect.name,
2504
3130
  feature: "a named-constraint conflict target (ON CONFLICT ON CONSTRAINT)",
2505
3131
  hint: "only postgres names constraints in conflict targets \u2014 use a column-list target"
@@ -2514,7 +3140,7 @@ function renderInsert(params) {
2514
3140
  }
2515
3141
  text += ` ON CONFLICT${target} DO NOTHING`;
2516
3142
  } else if (onConflict.constraint !== undefined || onConflict.conflictWhere !== undefined) {
2517
- throw unsupportedCapability9({
3143
+ throw unsupportedCapability15({
2518
3144
  dialect: dialect.name,
2519
3145
  feature: "a refined conflict target (constraint / partial-index predicate) on INSERT IGNORE",
2520
3146
  hint: "mysql skips on any duplicate key \u2014 drop the refinement or gate the call per dialect"
@@ -2532,7 +3158,7 @@ function renderInsert(params) {
2532
3158
  throw internalError2({ message: "lastInsertIdColumn is a mysql (onDuplicateKey) construct" });
2533
3159
  }
2534
3160
  if (onConflict.constraint !== undefined && dialect.name !== "postgres") {
2535
- throw unsupportedCapability9({
3161
+ throw unsupportedCapability15({
2536
3162
  dialect: dialect.name,
2537
3163
  feature: "a named-constraint conflict target (ON CONFLICT ON CONSTRAINT)",
2538
3164
  hint: "sqlite conflict targets are column lists \u2014 name the indexed columns instead"
@@ -2548,14 +3174,14 @@ function renderInsert(params) {
2548
3174
  text += ` ON CONFLICT${target} DO UPDATE SET ${assignments}`;
2549
3175
  } else {
2550
3176
  if (onConflict.constraint !== undefined) {
2551
- throw unsupportedCapability9({
3177
+ throw unsupportedCapability15({
2552
3178
  dialect: dialect.name,
2553
3179
  feature: "a named-constraint conflict target on ON DUPLICATE KEY UPDATE",
2554
3180
  hint: "mysql's ODKU fires on any duplicate key \u2014 no target can be named"
2555
3181
  });
2556
3182
  }
2557
3183
  if (onConflict.conflictWhere !== undefined) {
2558
- throw unsupportedCapability9({
3184
+ throw unsupportedCapability15({
2559
3185
  dialect: dialect.name,
2560
3186
  feature: "a partial-index conflict target on ON DUPLICATE KEY UPDATE",
2561
3187
  hint: "mysql's ODKU fires on any duplicate key \u2014 a partial arbiter cannot be selected; gate the call per dialect"
@@ -2608,7 +3234,7 @@ function renderAssignments(params) {
2608
3234
  return `${column} = ${renderIncomingRowColumn({ dialect, column: value.column })}`;
2609
3235
  }
2610
3236
  if (isJsonParamCell(value)) {
2611
- return `${column} = ${collector.add(value.text)}${dialect.jsonParamCast}`;
3237
+ return `${column} = ${collector.add(value.text)}${jsonCellCast(dialect, value)}`;
2612
3238
  }
2613
3239
  if (isSetOperation(value)) {
2614
3240
  const operand = collector.add(value.value);
@@ -2637,85 +3263,174 @@ function renderDelete(params) {
2637
3263
  const returning = applyReturning({ dialect, requested: statement.returning });
2638
3264
  return { text: text + returning.suffix, values: collector.values, returning: returning.status };
2639
3265
  }
2640
- // packages/sql/src/dialects/sqlite-table-definition.ts
2641
- import { VibeError as VibeError9 } from "@vibeorm/schema";
2642
- function tableDefinitionParts(params) {
2643
- const sql = params.sql;
2644
- if (!/^\s*CREATE\s+(?:TEMP(?:ORARY)?\s+)?TABLE\b/i.test(sql))
2645
- throw unavailable();
2646
- const parts = [];
2647
- let start = -1;
2648
- let depth = 0;
2649
- let quote;
2650
- for (let index = 0;index < sql.length; index++) {
2651
- const char = sql[index];
2652
- if (quote !== undefined) {
2653
- if (char === quote) {
2654
- if (quote !== "]" && sql[index + 1] === quote)
2655
- index++;
2656
- else
2657
- quote = undefined;
2658
- }
2659
- continue;
2660
- }
2661
- if (char === "'" || char === '"' || char === "`") {
2662
- quote = char;
2663
- continue;
2664
- }
2665
- if (char === "[") {
2666
- quote = "]";
2667
- continue;
3266
+ var CHAIN_ROWS_ALIAS = "__vibe_rows";
3267
+ var CHAIN_ONE_ALIAS = "__vibe_one";
3268
+ function renderChain(params) {
3269
+ const { dialect, statement, collector } = params;
3270
+ if (dialect.statementChain === null) {
3271
+ throw unsupportedCapability15({
3272
+ dialect: dialect.name,
3273
+ feature: "a statement chain (data-modifying WITH steps)",
3274
+ hint: "this dialect accepts only SELECT inside WITH \u2014 gate on SqlDialect.statementChain and keep the multi-statement path"
3275
+ });
3276
+ }
3277
+ if (statement.steps.length === 0)
3278
+ throw internalError2({ message: "a statement chain needs at least one step" });
3279
+ const defined = new Set;
3280
+ const steps = [];
3281
+ for (const step of statement.steps) {
3282
+ if (step.name === CHAIN_ROWS_ALIAS || step.name === CHAIN_ONE_ALIAS || defined.has(step.name)) {
3283
+ throw internalError2({ message: `chain step name "${step.name}" is reserved or repeated` });
2668
3284
  }
2669
- if (char === "-" && sql[index + 1] === "-") {
2670
- const end = sql.indexOf(`
2671
- `, index + 2);
2672
- if (end < 0)
2673
- break;
2674
- index = end;
2675
- continue;
3285
+ const body = renderChainStep({ dialect, statement: step.statement, collector, earlier: defined });
3286
+ steps.push(`${dialect.quoteIdent({ name: step.name })} AS (${body})`);
3287
+ defined.add(step.name);
3288
+ }
3289
+ const result = statement.result;
3290
+ if (result.rowsFrom.length === 0 || result.columns.length === 0) {
3291
+ throw internalError2({ message: "a chain result needs at least one source step and one column" });
3292
+ }
3293
+ for (const name of result.rowsFrom) {
3294
+ if (!defined.has(name))
3295
+ throw internalError2({ message: `chain result reads undefined step "${name}"` });
3296
+ }
3297
+ const columnList = result.columns.map((name) => dialect.quoteIdent({ name })).join(", ");
3298
+ const union = result.rowsFrom.map((name) => `SELECT ${columnList} FROM ${dialect.quoteIdent({ name })}`).join(" UNION ALL ");
3299
+ let select = union;
3300
+ if (result.verdict !== undefined || result.tally !== undefined || result.veto !== undefined && result.veto.length > 0) {
3301
+ const extra = [];
3302
+ for (const alias of [result.verdict?.alias, result.tally?.alias]) {
3303
+ if (alias !== undefined && result.columns.includes(alias)) {
3304
+ throw internalError2({ message: `chain result alias "${alias}" collides with a result column` });
3305
+ }
2676
3306
  }
2677
- if (char === "/" && sql[index + 1] === "*") {
2678
- const end = sql.indexOf("*/", index + 2);
2679
- if (end < 0)
2680
- break;
2681
- index = end + 1;
2682
- continue;
3307
+ const rows = dialect.quoteIdent({ name: CHAIN_ROWS_ALIAS });
3308
+ const projected = result.columns.map((name) => `${rows}.${dialect.quoteIdent({ name })}`);
3309
+ if (result.verdict !== undefined) {
3310
+ extra.push(`${renderExpr({ dialect, expr: result.verdict.expr, collector })} AS ${dialect.quoteIdent({ name: result.verdict.alias })}`);
2683
3311
  }
2684
- if (char === "(") {
2685
- if (depth === 0)
2686
- start = index + 1;
2687
- depth++;
2688
- } else if (char === ")") {
2689
- depth--;
2690
- if (depth === 0 && start >= 0) {
2691
- parts.push(sql.slice(start, index).trim());
2692
- return { clauses: parts, suffix: sql.slice(index + 1) };
2693
- }
2694
- } else if (char === "," && depth === 1) {
2695
- parts.push(sql.slice(start, index).trim());
2696
- start = index + 1;
3312
+ if (result.tally !== undefined || result.veto !== undefined && result.veto.length > 0) {
3313
+ extra.push(renderTallyColumn({ dialect, result, defined, collector }));
2697
3314
  }
3315
+ select = `SELECT ${[...projected, ...extra].join(", ")} FROM (SELECT 1) AS ${dialect.quoteIdent({ name: CHAIN_ONE_ALIAS })}` + ` LEFT JOIN (${union}) AS ${rows} ON TRUE`;
2698
3316
  }
2699
- throw unavailable();
3317
+ return { text: `WITH ${steps.join(", ")} ${select}`, values: collector.values, returning: "none" };
2700
3318
  }
2701
- function unavailable() {
2702
- return new VibeError9({ code: "VIBE_MIGRATION", message: "Cannot preserve table definition", meta: { reason: "table-definition-unavailable" } });
3319
+ function renderChainStep(params) {
3320
+ const { dialect, statement, collector } = params;
3321
+ switch (statement.kind) {
3322
+ case "select":
3323
+ return renderSelect({ dialect, statement, collector }).text;
3324
+ case "insert":
3325
+ return renderInsert({ dialect, statement, collector }).text;
3326
+ case "insertSelect":
3327
+ return renderInsertSelect({ dialect, statement, collector, earlier: params.earlier });
3328
+ case "update":
3329
+ return renderUpdate({ dialect, statement, collector }).text;
3330
+ case "delete":
3331
+ return renderDelete({ dialect, statement, collector }).text;
3332
+ }
2703
3333
  }
2704
- function sqliteColumnClauses(params) {
2705
- const columns = [];
2706
- for (const part of tableDefinitionParts(params).clauses) {
2707
- const token = /^(?:"((?:[^"]|"")+)"|`((?:[^`]|``)+)`|\[([^\]]+)\]|([A-Za-z_][A-Za-z_0-9]*))/.exec(part);
2708
- if (token === null)
2709
- throw unavailable();
2710
- if (token[4] !== undefined && /^(?:CONSTRAINT|PRIMARY|UNIQUE|CHECK|FOREIGN)$/i.test(token[4]))
2711
- continue;
2712
- const name = token[1]?.replaceAll('""', '"') ?? token[2]?.replaceAll("``", "`") ?? token[3] ?? token[4];
2713
- columns.push({ name, sql: part });
3334
+ function renderSelectCell(params) {
3335
+ const { dialect, value, collector } = params;
3336
+ if (isJsonParamCell(value))
3337
+ return `${collector.add(value.text)}${jsonCellCast(dialect, value)}`;
3338
+ if (isDbNowMarker(value))
3339
+ return dialect.renderDbNow({ offsetSeconds: value.offsetSeconds, collector });
3340
+ if (isSqlDefaultCell(value)) {
3341
+ throw internalError2({ message: "DEFAULT is not allowed in an INSERT \u2026 SELECT list \u2014 omit the column instead" });
2714
3342
  }
2715
- return columns;
3343
+ return collector.add(value);
3344
+ }
3345
+ function renderRowSource(params) {
3346
+ const { dialect, source, collector } = params;
3347
+ if (source.columns.length === 0 || source.rows.length === 0) {
3348
+ throw internalError2({ message: "a row source needs at least one column and one row" });
3349
+ }
3350
+ const typedBy = renderTable(dialect, source.typedBy);
3351
+ const columns = source.columns.map((name) => dialect.quoteIdent({ name }));
3352
+ const rows = [`(${columns.map((column) => `(SELECT ${column} FROM ${typedBy} WHERE FALSE)`).join(", ")})`];
3353
+ for (const row of source.rows) {
3354
+ if (row.length !== source.columns.length) {
3355
+ throw internalError2({ message: `row source row has ${row.length} values but ${source.columns.length} columns` });
3356
+ }
3357
+ rows.push(`(${row.map((value) => renderSelectCell({ dialect, value, collector })).join(", ")})`);
3358
+ }
3359
+ const alias = dialect.quoteIdent({ name: source.alias });
3360
+ const columnList = columns.join(", ");
3361
+ return `(SELECT ${columnList} FROM (VALUES ${rows.join(", ")}) AS ${alias} (${columnList}) OFFSET 1) AS ${alias}`;
3362
+ }
3363
+ function renderTallyColumn(params) {
3364
+ const { dialect, result, defined, collector } = params;
3365
+ const tally = result.tally;
3366
+ if (tally === undefined)
3367
+ throw internalError2({ message: "a chain veto needs a tally column to guard" });
3368
+ if (tally.steps.length === 0)
3369
+ throw internalError2({ message: "a chain tally needs at least one step" });
3370
+ const step = (name) => {
3371
+ if (!defined.has(name))
3372
+ throw internalError2({ message: `chain tally or veto reads undefined step "${name}"` });
3373
+ return dialect.quoteIdent({ name });
3374
+ };
3375
+ const count = (name) => `(SELECT count(*) FROM ${step(name)})`;
3376
+ const sum = tally.steps.map(count).join(" + ");
3377
+ const vetoes = result.veto ?? [];
3378
+ if (vetoes.length === 0)
3379
+ return `${sum} AS ${dialect.quoteIdent({ name: tally.alias })}`;
3380
+ const arms = vetoes.map((veto) => {
3381
+ if (!CHAIN_VETO_TOKEN_PATTERN.test(veto.token))
3382
+ throw internalError2({ message: `chain veto token "${veto.token}" is not a lower-case word` });
3383
+ if (typeof veto.nonce !== "string" || veto.nonce === "")
3384
+ throw internalError2({ message: `chain veto "${veto.token}" needs a non-empty nonce` });
3385
+ const minRows = veto.minRows ?? 1;
3386
+ if (!Number.isSafeInteger(minRows) || minRows < 1)
3387
+ throw internalError2({ message: `chain veto "${veto.token}" needs a positive integer minRows` });
3388
+ const when = minRows === 1 ? `WHEN NOT EXISTS (SELECT 1 FROM ${step(veto.step)})` : `WHEN ${count(veto.step)} < ${minRows}`;
3389
+ return `${when} THEN CAST('${CHAIN_VETO_PREFIX}${veto.token}:' || ${collector.add(veto.nonce)} || ':' || ${count(veto.step)} AS integer)`;
3390
+ });
3391
+ return `CASE ${arms.join(" ")} ELSE ${sum} END AS ${dialect.quoteIdent({ name: tally.alias })}`;
3392
+ }
3393
+ function renderInsertSelect(params) {
3394
+ const { dialect, statement, collector } = params;
3395
+ if (statement.columns.length === 0)
3396
+ throw internalError2({ message: "insert requires at least one column" });
3397
+ if (statement.cells.length !== statement.columns.length) {
3398
+ throw internalError2({
3399
+ message: `insert row has ${statement.cells.length} values but ${statement.columns.length} columns`
3400
+ });
3401
+ }
3402
+ const from = statement.from ?? [];
3403
+ for (const name of from) {
3404
+ if (!params.earlier.has(name))
3405
+ throw internalError2({ message: `INSERT \u2026 SELECT reads step "${name}" before it is defined` });
3406
+ }
3407
+ const source = statement.source;
3408
+ if (source !== undefined && (params.earlier.has(source.alias) || from.includes(source.alias))) {
3409
+ throw internalError2({ message: `row source alias "${source.alias}" collides with a chain step` });
3410
+ }
3411
+ const cells = statement.cells.map((value) => {
3412
+ if (isStepColumnRef(value)) {
3413
+ if (!from.includes(value.step) && value.step !== source?.alias) {
3414
+ throw internalError2({ message: `cell reads step "${value.step}", which the INSERT \u2026 SELECT does not name in FROM` });
3415
+ }
3416
+ return `${dialect.quoteIdent({ name: value.step })}.${dialect.quoteIdent({ name: value.column })}`;
3417
+ }
3418
+ return renderSelectCell({ dialect, value, collector });
3419
+ });
3420
+ const columnList = statement.columns.map((name) => dialect.quoteIdent({ name })).join(", ");
3421
+ let text = `INSERT INTO ${renderTable(dialect, statement.table)} (${columnList}) SELECT ${cells.join(", ")}`;
3422
+ const relations = from.map((name) => dialect.quoteIdent({ name }));
3423
+ if (source !== undefined)
3424
+ relations.push(renderRowSource({ dialect, source, collector }));
3425
+ if (relations.length > 0)
3426
+ text += ` FROM ${relations.join(" CROSS JOIN ")}`;
3427
+ if (statement.where !== undefined) {
3428
+ text += ` WHERE ${renderExpr({ dialect, expr: statement.where, collector })}`;
3429
+ }
3430
+ return text + applyReturning({ dialect, requested: statement.returning }).suffix;
2716
3431
  }
2717
3432
  // packages/sql/src/dialects/postgres-type-identity.ts
2718
- import { unsupportedCapability as unsupportedCapability10 } from "@vibeorm/schema";
3433
+ import { unsupportedCapability as unsupportedCapability16 } from "@vibeorm/schema";
2719
3434
  var IDENTIFIER = '(?:"(?:[^"\\x00]|"")+"|[a-zA-Z_][a-zA-Z_0-9$]*)';
2720
3435
  var MODIFIER_VALUE = `(?:[+-]?\\d+|${IDENTIFIER}|'(?:[^'\\x00]|'')*')`;
2721
3436
  var TYPE_NAME = new RegExp(`^\\s*(${IDENTIFIER})(?:\\s*\\.\\s*(${IDENTIFIER}))?\\s*(\\(\\s*${MODIFIER_VALUE}(?:\\s*,\\s*${MODIFIER_VALUE})*\\s*\\))?\\s*((?:\\[\\s*\\]\\s*)*)$`);
@@ -2742,13 +3457,13 @@ function parsePostgresTypeName(params) {
2742
3457
  }
2743
3458
  function renderPostgresTypeName(params) {
2744
3459
  if (params.dialect.name !== "postgres")
2745
- throw unsupportedCapability10({ dialect: params.dialect.name, feature: "postgresTypeIdentity" });
3460
+ throw unsupportedCapability16({ dialect: params.dialect.name, feature: "postgresTypeIdentity" });
2746
3461
  const { dialect, type } = params;
2747
3462
  return `${type.namespace === undefined ? "" : `${dialect.quoteIdent({ name: type.namespace })}.`}${dialect.quoteIdent({ name: type.name })}${type.modifier}${type.array ? "[]" : ""}`;
2748
3463
  }
2749
3464
  function renderPostgresTypeIdentity(params) {
2750
3465
  if (params.dialect.name !== "postgres")
2751
- throw unsupportedCapability10({ dialect: params.dialect.name, feature: "postgresTypeIdentity" });
3466
+ throw unsupportedCapability16({ dialect: params.dialect.name, feature: "postgresTypeIdentity" });
2752
3467
  const collector = new ParamCollector(params.dialect);
2753
3468
  const { source } = params;
2754
3469
  let text;
@@ -2761,11 +3476,11 @@ function renderPostgresTypeIdentity(params) {
2761
3476
  return { text, values: collector.values, returning: "none" };
2762
3477
  }
2763
3478
  // packages/sql/src/dialects/postgres-default-identity.ts
2764
- import { unsupportedCapability as unsupportedCapability11 } from "@vibeorm/schema";
3479
+ import { unsupportedCapability as unsupportedCapability17 } from "@vibeorm/schema";
2765
3480
  function renderPostgresDefaultFunctionIdentity(params) {
2766
3481
  const { dialect, table, column } = params;
2767
3482
  if (dialect.name !== "postgres")
2768
- throw unsupportedCapability11({ dialect: dialect.name, feature: "defaultFunctionIdentity" });
3483
+ throw unsupportedCapability17({ dialect: dialect.name, feature: "defaultFunctionIdentity" });
2769
3484
  const collector = new ParamCollector(dialect);
2770
3485
  const namespace = collector.add(table.namespace);
2771
3486
  const name = collector.add(table.name);
@@ -2789,28 +3504,28 @@ WHERE n.nspname = ${namespace} AND c.relname = ${name} AND a.attname = ${field}
2789
3504
  };
2790
3505
  }
2791
3506
  // packages/sql/src/dialects/constraint-owned-index.ts
2792
- import { unsupportedCapability as unsupportedCapability12 } from "@vibeorm/schema";
3507
+ import { unsupportedCapability as unsupportedCapability18 } from "@vibeorm/schema";
2793
3508
  function renderConstraintOwnedIndexDrop(params) {
2794
3509
  if (params.dialect.name !== "postgres")
2795
- throw unsupportedCapability12({ dialect: params.dialect.name, feature: "constraintOwnedIndexDrop" });
3510
+ throw unsupportedCapability18({ dialect: params.dialect.name, feature: "constraintOwnedIndexDrop" });
2796
3511
  const quote = (name) => params.dialect.quoteIdent({ name });
2797
3512
  return { text: `ALTER TABLE ${quote(params.table)} DROP CONSTRAINT ${quote(params.constraint)};`, values: [], returning: "none" };
2798
3513
  }
2799
3514
  // packages/sql/src/dialects/mysql-check-probe.ts
2800
- import { unsupportedCapability as unsupportedCapability13 } from "@vibeorm/schema";
3515
+ import { unsupportedCapability as unsupportedCapability19 } from "@vibeorm/schema";
2801
3516
  function renderMysqlCheckProbe(params) {
2802
3517
  if (params.dialect.name !== "mysql")
2803
- throw unsupportedCapability13({ dialect: params.dialect.name, feature: "temporaryCheckProbe" });
3518
+ throw unsupportedCapability19({ dialect: params.dialect.name, feature: "temporaryCheckProbe" });
2804
3519
  const q = (name) => params.dialect.quoteIdent({ name });
2805
3520
  const probe = q(params.probe);
2806
3521
  switch (params.operation) {
2807
3522
  case "clone":
2808
3523
  if (params.table === undefined)
2809
- throw unsupportedCapability13({ dialect: "mysql", feature: "temporaryCheckProbe", hint: "A source table is required" });
3524
+ throw unsupportedCapability19({ dialect: "mysql", feature: "temporaryCheckProbe", hint: "A source table is required" });
2810
3525
  return { text: `CREATE TEMPORARY TABLE ${probe} LIKE ${q(params.table.namespace)}.${q(params.table.name)}`, values: [], returning: "none" };
2811
3526
  case "add": {
2812
3527
  if (params.expression === undefined || params.definition === undefined)
2813
- throw unsupportedCapability13({ dialect: "mysql", feature: "temporaryCheckProbe", hint: "An authored expression and temporary clone definition are required" });
3528
+ throw unsupportedCapability19({ dialect: "mysql", feature: "temporaryCheckProbe", hint: "An authored expression and temporary clone definition are required" });
2814
3529
  const parts = tableDefinitionParts({ sql: params.definition });
2815
3530
  return { text: `CREATE TEMPORARY TABLE ${probe} (
2816
3531
  ${[...parts.clauses, `CONSTRAINT ${probe} CHECK (${params.expression})`].join(`,
@@ -2856,31 +3571,31 @@ function sqliteTableConstraints(params) {
2856
3571
  return result;
2857
3572
  }
2858
3573
  // packages/sql/src/dialects/migration-visibility.ts
2859
- import { unsupportedCapability as unsupportedCapability14 } from "@vibeorm/schema";
3574
+ import { unsupportedCapability as unsupportedCapability20 } from "@vibeorm/schema";
2860
3575
  function renderMigrationVisibility(params) {
2861
3576
  if (params.dialect.name !== "mysql")
2862
- throw unsupportedCapability14({ dialect: params.dialect.name, feature: "mysqlCatalogVisibility" });
3577
+ throw unsupportedCapability20({ dialect: params.dialect.name, feature: "mysqlCatalogVisibility" });
2863
3578
  const collector = new ParamCollector(params.dialect);
2864
3579
  const grantee = "CONCAT(QUOTE(SUBSTRING_INDEX(CURRENT_USER(), '@', 1)), '@', QUOTE(SUBSTRING_INDEX(CURRENT_USER(), '@', -1)))";
2865
3580
  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" };
2866
3581
  }
2867
3582
  // packages/sql/src/dialects/migration-inventory.ts
2868
- import { unsupportedCapability as unsupportedCapability15 } from "@vibeorm/schema";
3583
+ import { unsupportedCapability as unsupportedCapability21 } from "@vibeorm/schema";
2869
3584
  function renderSqliteColumnInventory(params) {
2870
3585
  if (params.dialect.name !== "sqlite")
2871
- throw unsupportedCapability15({ dialect: params.dialect.name, feature: "sqliteColumnInventory" });
3586
+ throw unsupportedCapability21({ dialect: params.dialect.name, feature: "sqliteColumnInventory" });
2872
3587
  const collector = new ParamCollector(params.dialect);
2873
3588
  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" };
2874
3589
  }
2875
3590
  function renderSqliteForeignKeyInventory(params) {
2876
3591
  if (params.dialect.name !== "sqlite")
2877
- throw unsupportedCapability15({ dialect: params.dialect.name, feature: "sqliteForeignKeyInventory" });
3592
+ throw unsupportedCapability21({ dialect: params.dialect.name, feature: "sqliteForeignKeyInventory" });
2878
3593
  const collector = new ParamCollector(params.dialect);
2879
3594
  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" };
2880
3595
  }
2881
3596
  function renderSqliteRebuildObjects(params) {
2882
3597
  if (params.dialect.name !== "sqlite")
2883
- throw unsupportedCapability15({ dialect: params.dialect.name, feature: "sqliteRebuildObjects" });
3598
+ throw unsupportedCapability21({ dialect: params.dialect.name, feature: "sqliteRebuildObjects" });
2884
3599
  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" };
2885
3600
  }
2886
3601
  function renderMigrationInventory(params) {
@@ -2900,6 +3615,8 @@ UNION ALL SELECT 'trigger', t.tgname::text, r.relname::text, NULL, NULL, NULL FR
2900
3615
  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')
2901
3616
  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
2902
3617
  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')
3618
+ UNION ALL SELECT 'enum', t.typname::text, NULL, NULL, NULL, NULL FROM pg_catalog.pg_type t JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace WHERE n.nspname = ${namespace} AND t.typtype = 'e' AND NOT EXISTS (SELECT 1 FROM pg_catalog.pg_depend d WHERE d.classid = 'pg_type'::regclass AND d.objid = t.oid AND d.deptype = 'e')
3619
+ ${params.namespace === "public" ? "" : `UNION ALL SELECT 'namespace', n.nspname::text, NULL, NULL, NULL, NULL FROM pg_catalog.pg_namespace n WHERE n.nspname = ${namespace}`}
2903
3620
  ORDER BY kind, parent, name`, values: collector.values, returning: "none" };
2904
3621
  }
2905
3622
  if (params.dialect.name === "mysql") {
@@ -2917,12 +3634,17 @@ ORDER BY kind, parent, name`, values: collector.values, returning: "none" };
2917
3634
  return { text: `SELECT type AS kind, name, tbl_name AS parent, sql AS definition FROM ${schema}.sqlite_schema WHERE name NOT LIKE 'sqlite\\_%' ESCAPE '\\' AND name <> '_vibe_migrations' ORDER BY type, tbl_name, name`, values: [], returning: "none" };
2918
3635
  }
2919
3636
  // packages/sql/src/dialects/migration-objects.ts
2920
- import { internalError as internalError3, unsupportedCapability as unsupportedCapability16 } from "@vibeorm/schema";
3637
+ import { internalError as internalError3, unsupportedCapability as unsupportedCapability22 } from "@vibeorm/schema";
2921
3638
  function renderMigrationObjectQuery(params) {
2922
3639
  return objectQuery({ ...params, collector: new ParamCollector(params.dialect) });
2923
3640
  }
2924
3641
  function objectQuery(params) {
2925
3642
  const { dialect, identity: object, collector } = params;
3643
+ if (object.kind === "namespace") {
3644
+ if (dialect.name !== "postgres")
3645
+ throw unsupportedCapability22({ dialect: dialect.name, feature: "fixedNamespaces" });
3646
+ return { text: `SELECT n.nspname::text AS "name" FROM pg_catalog.pg_namespace n WHERE n.nspname = ${collector.add(object.name)}`, values: collector.values, returning: "none" };
3647
+ }
2926
3648
  const physical = "table" in object ? object.table : object.name;
2927
3649
  const namespace = collector.add(physical.namespace);
2928
3650
  const name = collector.add(physical.name);
@@ -2984,7 +3706,7 @@ function objectQuery(params) {
2984
3706
  break;
2985
3707
  case "constraint":
2986
3708
  if (object.constraintType === "exclusion")
2987
- throw unsupportedCapability16({ dialect: "mysql", feature: "exclusionConstraints" });
3709
+ throw unsupportedCapability22({ dialect: "mysql", feature: "exclusionConstraints" });
2988
3710
  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)}`;
2989
3711
  break;
2990
3712
  case "index":
@@ -2994,15 +3716,15 @@ function objectQuery(params) {
2994
3716
  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}`;
2995
3717
  break;
2996
3718
  default:
2997
- throw unsupportedCapability16({ dialect: "mysql", feature: `inspect:${object.kind}` });
3719
+ throw unsupportedCapability22({ dialect: "mysql", feature: `inspect:${object.kind}` });
2998
3720
  }
2999
3721
  } else {
3000
3722
  if (physical.namespace !== "main" && physical.namespace !== "temp")
3001
- throw unsupportedCapability16({ dialect: "sqlite", feature: "attachedSchemaInspection" });
3723
+ throw unsupportedCapability22({ dialect: "sqlite", feature: "attachedSchemaInspection" });
3002
3724
  const catalog = `${dialect.quoteIdent({ name: physical.namespace })}.sqlite_schema`;
3003
3725
  const kind = object.kind === "table" || object.kind === "view" ? object.kind : object.kind === "trigger" ? "trigger" : object.kind === "index" ? "index" : "table";
3004
3726
  if (["function", "sequence", "postgresExtension", "enum"].includes(object.kind) || object.kind === "constraint" && object.constraintType === "exclusion")
3005
- throw unsupportedCapability16({ dialect: "sqlite", feature: `inspect:${object.kind}` });
3727
+ throw unsupportedCapability22({ dialect: "sqlite", feature: `inspect:${object.kind}` });
3006
3728
  const sqliteParams = new ParamCollector(dialect);
3007
3729
  if (object.kind === "column" || object.kind === "columnDefault" || object.kind === "generatedColumn") {
3008
3730
  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)}`;
@@ -3032,10 +3754,14 @@ function renderMigrationDependencies(params) {
3032
3754
  }
3033
3755
  function dependencyQuery(params) {
3034
3756
  if (params.dialect.name !== "postgres")
3035
- throw unsupportedCapability16({ dialect: params.dialect.name, feature: "catalogDependencies" });
3757
+ throw unsupportedCapability22({ dialect: params.dialect.name, feature: "catalogDependencies" });
3036
3758
  const object = params.identity;
3037
- const physical = "table" in object ? object.table : object.name;
3038
3759
  const collector = params.collector;
3760
+ if (object.kind === "namespace") {
3761
+ const namespace = collector.add(object.name);
3762
+ return { text: `SELECT DISTINCT json_build_array(d.deptype, a.type, a.object_names, a.object_args)::text AS address FROM pg_catalog.pg_namespace n JOIN pg_catalog.pg_depend d ON d.classid = 'pg_namespace'::regclass AND d.objid = n.oid AND d.objsubid = 0 CROSS JOIN LATERAL pg_catalog.pg_identify_object_as_address(d.refclassid, d.refobjid, d.refobjsubid) a WHERE n.nspname = ${namespace} ORDER BY address`, values: collector.values, returning: "none" };
3763
+ }
3764
+ const physical = "table" in object ? object.table : object.name;
3039
3765
  const namespace = collector.add(physical.namespace);
3040
3766
  const name = collector.add(physical.name);
3041
3767
  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}`;
@@ -3085,7 +3811,7 @@ function inspectionReferences(params) {
3085
3811
  function renderMigrationObjectBatch(params) {
3086
3812
  const { dialect } = params;
3087
3813
  if (dialect.name !== "postgres")
3088
- throw unsupportedCapability16({ dialect: dialect.name, feature: "batchedMigrationInspection" });
3814
+ throw unsupportedCapability22({ dialect: dialect.name, feature: "batchedMigrationInspection" });
3089
3815
  const groups = new Map;
3090
3816
  for (const [position, identity] of params.identities.entries()) {
3091
3817
  const kind = identity.kind === "constraint" ? `${identity.kind}:${identity.constraintType}` : identity.kind === "generatedColumn" ? "columnDefault" : identity.kind;
@@ -3118,16 +3844,16 @@ function renderMigrationNamespace(params) {
3118
3844
  }
3119
3845
  function renderCheckProbe(params) {
3120
3846
  if (params.dialect.name !== "postgres")
3121
- throw unsupportedCapability16({ dialect: params.dialect.name, feature: "rollbackCheckProbe" });
3847
+ throw unsupportedCapability22({ dialect: params.dialect.name, feature: "rollbackCheckProbe" });
3122
3848
  const q = (name) => params.dialect.quoteIdent({ name });
3123
3849
  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" };
3124
3850
  }
3125
3851
  // packages/sql/src/dialects/postgres-migration.ts
3126
- import { VibeError as VibeError10, unsupportedCapability as unsupportedCapability17 } from "@vibeorm/schema";
3852
+ import { VibeError as VibeError11, unsupportedCapability as unsupportedCapability23 } from "@vibeorm/schema";
3127
3853
  var SETTING_VALUE = /^[0-9]+(?:\.[0-9]+)?\s*(?:us|ms|s|min|h|d)?$/;
3128
3854
  function requirePostgres2(params) {
3129
3855
  if (params.dialect.name !== "postgres") {
3130
- throw unsupportedCapability17({ dialect: params.dialect.name, feature: params.feature });
3856
+ throw unsupportedCapability23({ dialect: params.dialect.name, feature: params.feature });
3131
3857
  }
3132
3858
  }
3133
3859
  function renderMigrationSessionRead(params) {
@@ -3142,7 +3868,7 @@ function renderMigrationSessionSet(params) {
3142
3868
  requirePostgres2({ dialect: params.dialect, feature: "migrationSessionSettings" });
3143
3869
  const { milliseconds, setting } = params;
3144
3870
  if (!Number.isSafeInteger(milliseconds) || milliseconds < 0) {
3145
- throw new VibeError10({
3871
+ throw new VibeError11({
3146
3872
  code: "VIBE_MIGRATION",
3147
3873
  message: `${setting} must be a non-negative integer number of milliseconds (got ${String(milliseconds)})`,
3148
3874
  meta: { reason: "execution-metadata-invalid", setting, value: milliseconds }
@@ -3232,6 +3958,8 @@ WHERE n.nspname = pg_catalog.current_schema() AND c.relname = ${table} AND a.att
3232
3958
  export {
3233
3959
  ADVISORY_KEY_BOUNDS,
3234
3960
  ADVISORY_LOCK_RESULT_COLUMN,
3961
+ CHAIN_VETO_PREFIX,
3962
+ CHAIN_VETO_TOKEN_PATTERN,
3235
3963
  DIALECT_CAPABILITIES,
3236
3964
  DIALECT_CODECS,
3237
3965
  JSON_TRANSPORT,
@@ -3253,6 +3981,7 @@ export {
3253
3981
  SQLITE_SESSION_SQL,
3254
3982
  SQL_DEFAULT_CELL,
3255
3983
  SQL_DIALECTS,
3984
+ chainVetoToken,
3256
3985
  compileExclusion,
3257
3986
  compileSql,
3258
3987
  containsPattern,
@@ -3265,21 +3994,29 @@ export {
3265
3994
  escapeGlobPattern,
3266
3995
  escapeLikePattern,
3267
3996
  excludedRef,
3997
+ float32Codec,
3268
3998
  getSqlDialect,
3269
3999
  isDbNowMarker,
3270
4000
  isExcludedRef,
3271
4001
  isFixedDiagnostic,
4002
+ isJsonListParameter,
3272
4003
  isJsonParamCell,
3273
4004
  isListSetOperation,
4005
+ isNativeEnumArrayParameter,
3274
4006
  isSetOperation,
3275
4007
  isSqlDefaultCell,
3276
4008
  isSqlFragment,
4009
+ isStepColumnRef,
4010
+ jsonListParameter,
3277
4011
  jsonParamCell,
3278
4012
  mapConflictColumns,
3279
4013
  mysqlDialect,
4014
+ nativeEnumArrayParameter,
3280
4015
  parseDateTimeText,
3281
4016
  parsePgArrayLiteral,
4017
+ parsePgArrayLiteralNested,
3282
4018
  parsePostgresTypeName,
4019
+ postgresArrayElementFromText,
3283
4020
  postgresDialect,
3284
4021
  projectionOutputName,
3285
4022
  renderAdvisoryLock,
@@ -3293,7 +4030,10 @@ export {
3293
4030
  renderExclusionProbe,
3294
4031
  renderExclusionSetup,
3295
4032
  renderExpr,
4033
+ renderExternalDependents,
4034
+ renderExternalOwnershipCatalog,
3296
4035
  renderFragment,
4036
+ renderIndexColumns,
3297
4037
  renderIndexStateQuery,
3298
4038
  renderMigrationDependencies,
3299
4039
  renderMigrationHistoryQuery,
@@ -3312,10 +4052,13 @@ export {
3312
4052
  renderMysqlObjectMembers,
3313
4053
  renderPostgresDefaultFunctionIdentity,
3314
4054
  renderPostgresExplain,
4055
+ renderPostgresIndexKeys,
4056
+ renderPostgresPhysicalCatalog,
3315
4057
  renderPostgresTypeIdentity,
3316
4058
  renderPostgresTypeName,
3317
4059
  renderPostgresWorkload,
3318
4060
  renderPredicate,
4061
+ renderProjection,
3319
4062
  renderRelationStateQuery,
3320
4063
  renderReplayCoverageGaps,
3321
4064
  renderRlsColumnType,
@@ -3336,16 +4079,23 @@ export {
3336
4079
  renderSqlFragment,
3337
4080
  renderSqliteColumnInventory,
3338
4081
  renderSqliteForeignKeyInventory,
4082
+ renderSqliteFtsUpdateTrigger,
3339
4083
  renderSqliteRebuildObjects,
3340
4084
  renderStatement,
4085
+ returningColumnName,
4086
+ shortestFloat32,
3341
4087
  sql,
3342
4088
  sqlUsesIncoming,
3343
4089
  sqliteColumnClauses,
3344
4090
  sqliteDialect,
4091
+ sqliteFtsUpdateUpgrade,
3345
4092
  sqliteTableConstraints,
3346
4093
  startsWithPattern,
4094
+ stepColumnRef,
3347
4095
  strictMatchPattern,
4096
+ tableDefinitionParts,
4097
+ timeOfDayCodec,
3348
4098
  toPgArrayLiteral
3349
4099
  };
3350
4100
 
3351
- //# debugId=B1F6683E5EF3B96D64756E2164756E21
4101
+ //# debugId=67799F9417158D9264756E2164756E21