@vibeorm/sql 2.3.1 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ast.d.ts +5 -2
- package/dist/ast.d.ts.map +1 -1
- package/dist/dialects/external-dependents.d.ts +28 -0
- package/dist/dialects/external-dependents.d.ts.map +1 -0
- package/dist/dialects/external-ownership.d.ts +8 -0
- package/dist/dialects/external-ownership.d.ts.map +1 -0
- package/dist/dialects/migration-inventory.d.ts.map +1 -1
- package/dist/dialects/migration-objects.d.ts.map +1 -1
- package/dist/dialects/migration-replay-coverage.d.ts.map +1 -1
- package/dist/dialects/migration-shadow.d.ts +5 -0
- package/dist/dialects/migration-shadow.d.ts.map +1 -1
- package/dist/dialects/postgres-index-keys.d.ts +9 -0
- package/dist/dialects/postgres-index-keys.d.ts.map +1 -0
- package/dist/dialects/postgres-physical-catalog.d.ts +11 -0
- package/dist/dialects/postgres-physical-catalog.d.ts.map +1 -0
- package/dist/exclusion.d.ts +2 -0
- package/dist/exclusion.d.ts.map +1 -1
- package/dist/index-columns.d.ts +15 -0
- package/dist/index-columns.d.ts.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +378 -190
- package/dist/index.js.map +18 -12
- package/dist/native-enum-parameter.d.ts +17 -0
- package/dist/native-enum-parameter.d.ts.map +1 -0
- package/dist/params.d.ts.map +1 -1
- package/dist/render.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,5 +1,40 @@
|
|
|
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/dialects/external-ownership.ts
|
|
11
|
+
import { unsupportedCapability } from "@vibeorm/schema";
|
|
12
|
+
function renderExternalOwnershipCatalog(params) {
|
|
13
|
+
if (params.dialect.name !== "postgres")
|
|
14
|
+
throw unsupportedCapability({ dialect: params.dialect.name, feature: "externalObjects" });
|
|
15
|
+
const scope = "n.nspname <> 'information_schema' AND n.nspname NOT LIKE 'pg\\_%' ESCAPE '\\' AND n.nspname NOT IN ('_vibeorm_shadow', '_vibe_shadow')";
|
|
16
|
+
let text;
|
|
17
|
+
switch (params.operation) {
|
|
18
|
+
case "namespaces":
|
|
19
|
+
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`;
|
|
20
|
+
break;
|
|
21
|
+
case "inheritance":
|
|
22
|
+
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`;
|
|
23
|
+
break;
|
|
24
|
+
case "ownedSequences":
|
|
25
|
+
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`;
|
|
26
|
+
break;
|
|
27
|
+
case "foreignKeys":
|
|
28
|
+
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`;
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
return { text, values: [], returning: "none" };
|
|
32
|
+
}
|
|
33
|
+
// packages/sql/src/dialects/external-dependents.ts
|
|
34
|
+
import { unsupportedCapability as unsupportedCapability3 } from "@vibeorm/schema";
|
|
35
|
+
|
|
2
36
|
// packages/sql/src/params.ts
|
|
37
|
+
import { unsupportedCapability as unsupportedCapability2 } from "@vibeorm/schema";
|
|
3
38
|
class ParamCollector {
|
|
4
39
|
dialect;
|
|
5
40
|
values;
|
|
@@ -8,6 +43,12 @@ class ParamCollector {
|
|
|
8
43
|
this.values = [];
|
|
9
44
|
}
|
|
10
45
|
add(value) {
|
|
46
|
+
if (isNativeEnumArrayParameter(value)) {
|
|
47
|
+
if (!this.dialect.capabilities.scalarArrays)
|
|
48
|
+
throw unsupportedCapability2({ dialect: this.dialect.name, feature: "scalarArrays" });
|
|
49
|
+
this.values.push(value.value);
|
|
50
|
+
return `${this.dialect.placeholder({ index: this.values.length })}::${this.dialect.quoteIdent({ name: value.type.namespace })}.${this.dialect.quoteIdent({ name: value.type.name })}[]`;
|
|
51
|
+
}
|
|
11
52
|
this.values.push(value);
|
|
12
53
|
return this.dialect.placeholder({ index: this.values.length });
|
|
13
54
|
}
|
|
@@ -16,6 +57,121 @@ class ParamCollector {
|
|
|
16
57
|
}
|
|
17
58
|
}
|
|
18
59
|
|
|
60
|
+
// packages/sql/src/dialects/external-dependents.ts
|
|
61
|
+
function renderExternalDependents(params) {
|
|
62
|
+
if (params.dialect.name !== "postgres")
|
|
63
|
+
throw unsupportedCapability3({ dialect: params.dialect.name, feature: "externalObjects" });
|
|
64
|
+
const collector = new ParamCollector(params.dialect);
|
|
65
|
+
const requests = params.targets.map((target, position) => ({ position, kind: target.kind, namespace: target.namespace, name: target.name, column: target.kind === "column" ? target.column : null }));
|
|
66
|
+
const request = collector.add(JSON.stringify(requests));
|
|
67
|
+
const text = `WITH request AS (
|
|
68
|
+
SELECT * FROM jsonb_to_recordset(${request}::text::jsonb) AS request(position integer, kind text, namespace text, name text, "column" text)
|
|
69
|
+
), target AS (
|
|
70
|
+
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'
|
|
71
|
+
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
|
|
72
|
+
), owned AS (
|
|
73
|
+
SELECT position, classid, objid, objsubid FROM target
|
|
74
|
+
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
|
|
75
|
+
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'
|
|
76
|
+
), referenced AS (
|
|
77
|
+
SELECT position, classid, objid, objsubid FROM owned
|
|
78
|
+
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
|
|
79
|
+
)
|
|
80
|
+
SELECT DISTINCT referenced.position, dependent.type AS "dependentType", dependent.identity AS "dependentIdentity", ref.identity AS referenced, d.classid::regclass::text AS "dependentClass",
|
|
81
|
+
rn.nspname::text AS "relationNamespace", rel.relname::text AS "relationName", rel.relkind::text AS "relationKind", col.attname::text AS "columnName",
|
|
82
|
+
COALESCE(pn.nspname, tn.nspname, dependent.schema)::text AS "objectNamespace", COALESCE(p.proname, ty.typname)::text AS "objectName"
|
|
83
|
+
FROM referenced
|
|
84
|
+
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'
|
|
85
|
+
CROSS JOIN LATERAL pg_catalog.pg_identify_object(d.classid, d.objid, d.objsubid) dependent
|
|
86
|
+
CROSS JOIN LATERAL pg_catalog.pg_identify_object(d.refclassid, d.refobjid, d.refobjsubid) ref
|
|
87
|
+
LEFT JOIN pg_catalog.pg_class self ON d.classid = 'pg_class'::regclass AND self.oid = d.objid
|
|
88
|
+
LEFT JOIN pg_catalog.pg_index ix ON ix.indexrelid = self.oid
|
|
89
|
+
LEFT JOIN pg_catalog.pg_attrdef ad ON d.classid = 'pg_attrdef'::regclass AND ad.oid = d.objid
|
|
90
|
+
LEFT JOIN pg_catalog.pg_rewrite rw ON d.classid = 'pg_rewrite'::regclass AND rw.oid = d.objid
|
|
91
|
+
LEFT JOIN pg_catalog.pg_trigger tg ON d.classid = 'pg_trigger'::regclass AND tg.oid = d.objid
|
|
92
|
+
LEFT JOIN pg_catalog.pg_constraint con ON d.classid = 'pg_constraint'::regclass AND con.oid = d.objid
|
|
93
|
+
LEFT JOIN pg_catalog.pg_policy pol ON d.classid = 'pg_policy'::regclass AND pol.oid = d.objid
|
|
94
|
+
LEFT JOIN pg_catalog.pg_type ty ON d.classid = 'pg_type'::regclass AND ty.oid = d.objid
|
|
95
|
+
LEFT JOIN pg_catalog.pg_namespace tn ON tn.oid = ty.typnamespace
|
|
96
|
+
LEFT JOIN pg_catalog.pg_proc p ON d.classid = 'pg_proc'::regclass AND p.oid = d.objid
|
|
97
|
+
LEFT JOIN pg_catalog.pg_namespace pn ON pn.oid = p.pronamespace
|
|
98
|
+
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))
|
|
99
|
+
LEFT JOIN pg_catalog.pg_namespace rn ON rn.oid = rel.relnamespace
|
|
100
|
+
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
|
|
101
|
+
ORDER BY 1, 2, 3, 4`;
|
|
102
|
+
return { text, values: collector.values, returning: "none" };
|
|
103
|
+
}
|
|
104
|
+
// packages/sql/src/dialects/postgres-physical-catalog.ts
|
|
105
|
+
import { unsupportedCapability as unsupportedCapability4 } from "@vibeorm/schema";
|
|
106
|
+
function renderPostgresPhysicalCatalog(params) {
|
|
107
|
+
const { dialect } = params;
|
|
108
|
+
if (dialect.name !== "postgres")
|
|
109
|
+
throw unsupportedCapability4({ dialect: dialect.name, feature: "postgresPhysicalCatalog" });
|
|
110
|
+
const collector = new ParamCollector(dialect);
|
|
111
|
+
const namespace = collector.add(params.namespace);
|
|
112
|
+
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)`;
|
|
113
|
+
let text;
|
|
114
|
+
switch (params.query) {
|
|
115
|
+
case "tables":
|
|
116
|
+
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`;
|
|
117
|
+
break;
|
|
118
|
+
case "views":
|
|
119
|
+
text = `SELECT table_name FROM information_schema.views WHERE table_schema = ${namespace} AND ${extensionOwnedFilter} ORDER BY table_name`;
|
|
120
|
+
break;
|
|
121
|
+
case "columns":
|
|
122
|
+
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`;
|
|
123
|
+
break;
|
|
124
|
+
case "constraints":
|
|
125
|
+
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`;
|
|
126
|
+
break;
|
|
127
|
+
case "checks":
|
|
128
|
+
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`;
|
|
129
|
+
break;
|
|
130
|
+
case "foreignKeys":
|
|
131
|
+
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`;
|
|
132
|
+
break;
|
|
133
|
+
case "enums":
|
|
134
|
+
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} ORDER BY t.typname, e.enumsortorder`;
|
|
135
|
+
break;
|
|
136
|
+
case "indexes":
|
|
137
|
+
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`;
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
return { text, values: collector.values, returning: "none" };
|
|
141
|
+
}
|
|
142
|
+
// packages/sql/src/dialects/postgres-index-keys.ts
|
|
143
|
+
import { unsupportedCapability as unsupportedCapability5 } from "@vibeorm/schema";
|
|
144
|
+
function renderPostgresIndexKeys(params) {
|
|
145
|
+
const { dialect } = params;
|
|
146
|
+
if (dialect.name !== "postgres")
|
|
147
|
+
throw unsupportedCapability5({ dialect: dialect.name, feature: "indexOperatorClasses" });
|
|
148
|
+
const collector = new ParamCollector(dialect);
|
|
149
|
+
const scope = params.table === undefined ? "n.nspname <> 'information_schema' AND n.nspname NOT LIKE 'pg\\_%' ESCAPE '\\'" : `n.nspname = ${collector.add(params.table.namespace)} AND c.relname = ${collector.add(params.table.name)}`;
|
|
150
|
+
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`;
|
|
151
|
+
return {
|
|
152
|
+
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`,
|
|
153
|
+
values: collector.values,
|
|
154
|
+
returning: "none"
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
// packages/sql/src/index-columns.ts
|
|
158
|
+
import { VibeError, isSqlObjectIdentity, unsupportedCapability as unsupportedCapability6 } from "@vibeorm/schema";
|
|
159
|
+
function renderIndexColumns(params) {
|
|
160
|
+
const { dialect, columns, unique } = params;
|
|
161
|
+
const hasOperatorClass = columns.some((column) => column.operatorClass !== undefined);
|
|
162
|
+
if (hasOperatorClass && !dialect.capabilities.indexOperatorClasses)
|
|
163
|
+
throw unsupportedCapability6({ dialect: dialect.name, feature: "indexOperatorClasses" });
|
|
164
|
+
if (hasOperatorClass && unique)
|
|
165
|
+
throw unsupportedCapability6({ dialect: dialect.name, feature: "uniqueIndexOperatorClasses", hint: "custom unique operator classes can change client selector equality" });
|
|
166
|
+
return columns.map((column) => {
|
|
167
|
+
const operatorClass = column.operatorClass;
|
|
168
|
+
if (operatorClass !== undefined && !isSqlObjectIdentity({ kind: "table", name: operatorClass })) {
|
|
169
|
+
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 } });
|
|
170
|
+
}
|
|
171
|
+
const ops = operatorClass === undefined ? "" : ` ${dialect.quoteIdent({ name: operatorClass.namespace })}.${dialect.quoteIdent({ name: operatorClass.name })}`;
|
|
172
|
+
return `${dialect.quoteIdent({ name: column.name })}${ops}${column.desc ? " DESC" : ""}`;
|
|
173
|
+
});
|
|
174
|
+
}
|
|
19
175
|
// packages/sql/src/dialects/module-dependency.ts
|
|
20
176
|
function renderModuleDependencyQuery(params) {
|
|
21
177
|
const { dialect, operation } = params;
|
|
@@ -43,24 +199,26 @@ ORDER BY source_namespace, source_table, constraint_name`,
|
|
|
43
199
|
};
|
|
44
200
|
}
|
|
45
201
|
// packages/sql/src/dialects/migration-replay-coverage.ts
|
|
202
|
+
var extensionMember = (classid, objid) => `EXISTS (SELECT 1 FROM pg_catalog.pg_depend d WHERE d.classid = '${classid}'::regclass AND d.objid = ${objid} AND d.deptype = 'e')`;
|
|
203
|
+
var extensionOwnedNamespace = `(EXISTS (SELECT 1 FROM pg_catalog.pg_extension e WHERE e.extnamespace = n.oid) OR ${extensionMember("pg_namespace", "n.oid")}) AND NOT EXISTS (SELECT 1 FROM pg_catalog.pg_class c WHERE c.relnamespace = n.oid AND c.relkind IN ('r', 'p', 'v', 'm', 'f', 'S') AND NOT ${extensionMember("pg_class", "c.oid")}) AND NOT EXISTS (SELECT 1 FROM pg_catalog.pg_proc p WHERE p.pronamespace = n.oid AND NOT ${extensionMember("pg_proc", "p.oid")}) AND NOT EXISTS (SELECT 1 FROM pg_catalog.pg_type t WHERE t.typnamespace = n.oid AND (t.typrelid = 0 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 ${extensionMember("pg_type", "t.oid")})`;
|
|
46
204
|
function renderReplayCoverageGaps(params) {
|
|
47
205
|
const { dialect } = params;
|
|
48
206
|
const collector = new ParamCollector(dialect);
|
|
49
207
|
const namespace = collector.add(params.namespace);
|
|
50
208
|
if (dialect.name === "postgres")
|
|
51
|
-
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')
|
|
52
|
-
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')
|
|
53
|
-
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')
|
|
54
|
-
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'
|
|
55
|
-
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 '\\'${params.privateNamespace === undefined ? "" : ` AND n.nspname <> ${collector.add(params.privateNamespace)}`}`, values: collector.values, returning: "none" };
|
|
209
|
+
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')
|
|
210
|
+
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')
|
|
211
|
+
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')
|
|
212
|
+
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")}
|
|
213
|
+
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" };
|
|
56
214
|
if (dialect.name === "mysql")
|
|
57
|
-
return { text: `SELECT 'procedure' AS kind, ROUTINE_NAME AS name FROM information_schema.ROUTINES WHERE ROUTINE_SCHEMA = ${namespace} AND ROUTINE_TYPE = 'PROCEDURE'
|
|
58
|
-
UNION ALL SELECT 'event', EVENT_NAME FROM information_schema.EVENTS WHERE EVENT_SCHEMA = ${collector.add(params.namespace)}
|
|
59
|
-
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" };
|
|
60
|
-
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" };
|
|
215
|
+
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'
|
|
216
|
+
UNION ALL SELECT 'event', EVENT_NAME, EVENT_SCHEMA, NULL FROM information_schema.EVENTS WHERE EVENT_SCHEMA = ${collector.add(params.namespace)}
|
|
217
|
+
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" };
|
|
218
|
+
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" };
|
|
61
219
|
}
|
|
62
220
|
// packages/sql/src/dialects/migration-shadow.ts
|
|
63
|
-
import { unsupportedCapability } from "@vibeorm/schema";
|
|
221
|
+
import { assertSqlNamespace, unsupportedCapability as unsupportedCapability7 } from "@vibeorm/schema";
|
|
64
222
|
var SHADOW_DATABASE = Object.freeze({ schema: "_vibeorm_shadow", table: "_vibe_shadow", lock: 1447645267, timeoutSeconds: 60 });
|
|
65
223
|
var SHADOW_CAPABILITIES = Object.freeze({
|
|
66
224
|
postgres: Object.freeze({ serverProvisioning: true, dedicated: true }),
|
|
@@ -70,14 +228,14 @@ var SHADOW_CAPABILITIES = Object.freeze({
|
|
|
70
228
|
function renderShadowOperation(params) {
|
|
71
229
|
const { dialect, operation } = params;
|
|
72
230
|
if (!SHADOW_CAPABILITIES[dialect.name].serverProvisioning)
|
|
73
|
-
throw
|
|
231
|
+
throw unsupportedCapability7({ dialect: dialect.name, feature: "shadowDatabaseProvisioning" });
|
|
74
232
|
const pg = dialect.name === "postgres";
|
|
75
233
|
const collector = new ParamCollector(dialect);
|
|
76
234
|
const marker = pg ? `${dialect.quoteIdent({ name: SHADOW_DATABASE.schema })}.${dialect.quoteIdent({ name: SHADOW_DATABASE.table })}` : dialect.quoteIdent({ name: SHADOW_DATABASE.table });
|
|
77
235
|
const result = (text) => [{ text, values: collector.values, returning: "none" }];
|
|
78
236
|
switch (operation.kind) {
|
|
79
237
|
case "createDatabase":
|
|
80
|
-
return result(`CREATE DATABASE ${dialect.quoteIdent({ name: operation.name })}`);
|
|
238
|
+
return result(`CREATE DATABASE ${dialect.quoteIdent({ name: operation.name })}${pg ? " TEMPLATE template0" : ""}`);
|
|
81
239
|
case "dropDatabase":
|
|
82
240
|
return result(`DROP DATABASE ${dialect.quoteIdent({ name: operation.name })}`);
|
|
83
241
|
case "identity":
|
|
@@ -95,7 +253,7 @@ function renderShadowOperation(params) {
|
|
|
95
253
|
ORDER BY ordinal_position`);
|
|
96
254
|
case "grants": {
|
|
97
255
|
if (pg)
|
|
98
|
-
throw
|
|
256
|
+
throw unsupportedCapability7({ dialect: dialect.name, feature: "mysqlShadowPrivileges" });
|
|
99
257
|
return result("SHOW GRANTS FOR CURRENT_USER");
|
|
100
258
|
}
|
|
101
259
|
case "markerCreate":
|
|
@@ -123,19 +281,27 @@ UNION ALL SELECT ROUTINE_TYPE, ROUTINE_NAME, ROUTINE_SCHEMA FROM information_sch
|
|
|
123
281
|
UNION ALL SELECT 'EVENT', EVENT_NAME, EVENT_SCHEMA FROM information_schema.EVENTS WHERE EVENT_SCHEMA = DATABASE()
|
|
124
282
|
UNION ALL SELECT 'TRIGGER', TRIGGER_NAME, TRIGGER_SCHEMA FROM information_schema.TRIGGERS WHERE TRIGGER_SCHEMA = DATABASE()
|
|
125
283
|
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)`);
|
|
126
|
-
case "resetPostgres":
|
|
284
|
+
case "resetPostgres":
|
|
285
|
+
case "resetDedicatedPostgres": {
|
|
127
286
|
if (!pg)
|
|
128
|
-
throw
|
|
129
|
-
|
|
287
|
+
throw unsupportedCapability7({ dialect: dialect.name, feature: "postgresShadowReset" });
|
|
288
|
+
const owned = operation.kind === "resetDedicatedPostgres" ? operation.ownedNamespaces : [];
|
|
289
|
+
for (const namespace of owned)
|
|
290
|
+
assertSqlNamespace({ namespace, dialect: "postgres", allowPublic: false });
|
|
291
|
+
return [
|
|
292
|
+
...owned.map((namespace) => ({ text: `DROP SCHEMA IF EXISTS ${dialect.quoteIdent({ name: namespace })} CASCADE`, values: [], returning: "none" })),
|
|
293
|
+
{ text: 'DROP SCHEMA "public" CASCADE', values: [], returning: "none" },
|
|
294
|
+
{ text: 'CREATE SCHEMA "public"', values: [], returning: "none" }
|
|
295
|
+
];
|
|
130
296
|
}
|
|
131
297
|
case "mysqlForeignKeys": {
|
|
132
298
|
if (pg)
|
|
133
|
-
throw
|
|
299
|
+
throw unsupportedCapability7({ dialect: dialect.name, feature: "mysqlShadowReset" });
|
|
134
300
|
return result(`SET FOREIGN_KEY_CHECKS = ${operation.enabled ? "1" : "0"}`);
|
|
135
301
|
}
|
|
136
302
|
case "dropMysqlObject": {
|
|
137
303
|
if (pg)
|
|
138
|
-
throw
|
|
304
|
+
throw unsupportedCapability7({ dialect: dialect.name, feature: "mysqlShadowReset" });
|
|
139
305
|
return result(`DROP ${operation.object} ${dialect.quoteIdent({ name: operation.name })}`);
|
|
140
306
|
}
|
|
141
307
|
}
|
|
@@ -180,10 +346,10 @@ var SQLITE_SESSION_SQL = Object.freeze({
|
|
|
180
346
|
foreignKeysOn: "PRAGMA foreign_keys = ON"
|
|
181
347
|
});
|
|
182
348
|
// packages/sql/src/composable.ts
|
|
183
|
-
import { VibeError } from "@vibeorm/schema";
|
|
349
|
+
import { VibeError as VibeError2 } from "@vibeorm/schema";
|
|
184
350
|
var fragments = new WeakMap;
|
|
185
351
|
function invalid(message) {
|
|
186
|
-
throw new
|
|
352
|
+
throw new VibeError2({ code: "VIBE_VALIDATION", message });
|
|
187
353
|
}
|
|
188
354
|
function construct(parts) {
|
|
189
355
|
const snapshot = Object.freeze([...parts]);
|
|
@@ -308,7 +474,7 @@ function sqlUsesIncoming(params) {
|
|
|
308
474
|
return partsOf(params.fragment).some((part) => typeof part !== "string" && (("row" in part) && part.row === "incoming" || ("greatest" in part) && part.greatest.some((fragment) => sqlUsesIncoming({ fragment }))));
|
|
309
475
|
}
|
|
310
476
|
// packages/sql/src/result-decoder.ts
|
|
311
|
-
import { VibeError as
|
|
477
|
+
import { VibeError as VibeError4 } from "@vibeorm/schema";
|
|
312
478
|
|
|
313
479
|
// packages/sql/src/pg-array.ts
|
|
314
480
|
var PG_ARRAY_QUOTE = /^$|^NULL$|[{},"\\\s]/i;
|
|
@@ -363,7 +529,7 @@ function parsePgArrayLiteral(params) {
|
|
|
363
529
|
}
|
|
364
530
|
|
|
365
531
|
// packages/sql/src/codecs.ts
|
|
366
|
-
import { VibeError as
|
|
532
|
+
import { VibeError as VibeError3, invalidStoredData } from "@vibeorm/schema";
|
|
367
533
|
function enumMembershipCodec(params) {
|
|
368
534
|
const { model, field, enum: enumName } = params;
|
|
369
535
|
const members = new Set(params.members);
|
|
@@ -371,7 +537,7 @@ function enumMembershipCodec(params) {
|
|
|
371
537
|
encode: (value) => {
|
|
372
538
|
if (typeof value === "string" && members.has(value))
|
|
373
539
|
return value;
|
|
374
|
-
throw new
|
|
540
|
+
throw new VibeError3({
|
|
375
541
|
code: "VIBE_VALIDATION",
|
|
376
542
|
message: `Invalid value for enum ${enumName} on ${model}.${field}`,
|
|
377
543
|
meta: { model, field, enum: enumName }
|
|
@@ -411,7 +577,7 @@ var bigIntCodec = {
|
|
|
411
577
|
return BigInt(value.trim());
|
|
412
578
|
if (typeof value === "number" && Number.isSafeInteger(value))
|
|
413
579
|
return BigInt(value);
|
|
414
|
-
throw new
|
|
580
|
+
throw new VibeError3({ code: "VIBE_DATA_INVALID", message: "BigInt requires an integer string or a safe integer", meta: { value } });
|
|
415
581
|
}
|
|
416
582
|
};
|
|
417
583
|
var decimalCodec = {
|
|
@@ -441,7 +607,7 @@ function parseDateTimeText(params) {
|
|
|
441
607
|
var dateNativeCodec = {
|
|
442
608
|
encode: (value) => {
|
|
443
609
|
if (value instanceof Date && !Number.isFinite(value.getTime()) || typeof value === "number" && !Number.isFinite(value) || typeof value === "string" && /^[+-]?infinity$/i.test(value.trim())) {
|
|
444
|
-
throw new
|
|
610
|
+
throw new VibeError3({ code: "VIBE_VALIDATION", message: "DateTime parameters require finite timestamps; use an extended timestamp encoder for infinity" });
|
|
445
611
|
}
|
|
446
612
|
return value;
|
|
447
613
|
},
|
|
@@ -449,7 +615,7 @@ var dateNativeCodec = {
|
|
|
449
615
|
const date = value instanceof Date ? value : typeof value === "string" ? parseDateTimeText({ text: value }) : typeof value === "number" ? new Date(value) : undefined;
|
|
450
616
|
if (date !== undefined && Number.isFinite(date.getTime()))
|
|
451
617
|
return date;
|
|
452
|
-
throw new
|
|
618
|
+
throw new VibeError3({ code: "VIBE_DATA_INVALID", message: "DateTime requires a finite timestamp; use an explicit extended timestamp decoder for infinity" });
|
|
453
619
|
}
|
|
454
620
|
};
|
|
455
621
|
var dateTextCodec = {
|
|
@@ -458,7 +624,7 @@ var dateTextCodec = {
|
|
|
458
624
|
const date = value instanceof Date ? value : typeof value === "string" ? parseDateTimeText({ text: value }) : typeof value === "number" ? new Date(value) : undefined;
|
|
459
625
|
if (date !== undefined && Number.isFinite(date.getTime()))
|
|
460
626
|
return date.toISOString();
|
|
461
|
-
throw new
|
|
627
|
+
throw new VibeError3({ code: "VIBE_VALIDATION", message: "DateTime parameters require a valid finite timestamp" });
|
|
462
628
|
},
|
|
463
629
|
decode: dateNativeCodec.decode
|
|
464
630
|
};
|
|
@@ -495,7 +661,7 @@ var jsonCodec = {
|
|
|
495
661
|
try {
|
|
496
662
|
return JSON.parse(value);
|
|
497
663
|
} catch (cause) {
|
|
498
|
-
throw new
|
|
664
|
+
throw new VibeError3({
|
|
499
665
|
code: "VIBE_INTERNAL",
|
|
500
666
|
message: "a Json column returned text that is not valid JSON",
|
|
501
667
|
meta: { value },
|
|
@@ -545,7 +711,7 @@ var DIALECT_CODECS = {
|
|
|
545
711
|
|
|
546
712
|
// packages/sql/src/result-decoder.ts
|
|
547
713
|
function refuse(encoding, message) {
|
|
548
|
-
throw new
|
|
714
|
+
throw new VibeError4({ code: encoding ? "VIBE_VALIDATION" : "VIBE_DATA_INVALID", message });
|
|
549
715
|
}
|
|
550
716
|
function scalar(type, valid) {
|
|
551
717
|
const convert = (params, encoding) => {
|
|
@@ -591,7 +757,7 @@ var extendedDecoder = Object.freeze({
|
|
|
591
757
|
encode: (params) => {
|
|
592
758
|
if (params.value === "infinity" || params.value === "-infinity") {
|
|
593
759
|
if (params.dialect !== "postgres")
|
|
594
|
-
throw new
|
|
760
|
+
throw new VibeError4({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Timestamp infinity parameters require PostgreSQL" });
|
|
595
761
|
return params.value;
|
|
596
762
|
}
|
|
597
763
|
return dateDecoder.encode(params);
|
|
@@ -649,15 +815,15 @@ var decode = Object.freeze({
|
|
|
649
815
|
}
|
|
650
816
|
});
|
|
651
817
|
// packages/sql/src/exclusion.ts
|
|
652
|
-
import { VibeError as
|
|
818
|
+
import { VibeError as VibeError6, fieldTypeIdentity as fieldTypeIdentity2, isEnumType as isEnumType2, unsupportedCapability as unsupportedCapability9 } from "@vibeorm/schema";
|
|
653
819
|
|
|
654
820
|
// packages/sql/src/predicate.ts
|
|
655
|
-
import { VibeError as
|
|
821
|
+
import { VibeError as VibeError5, fieldTypeIdentity, isEnumType, unsupportedCapability as unsupportedCapability8 } from "@vibeorm/schema";
|
|
656
822
|
function renderPredicate(params) {
|
|
657
823
|
if (params.usage === "exclusionPredicate" && !params.dialect.capabilities.exclusionConstraints)
|
|
658
|
-
throw
|
|
824
|
+
throw unsupportedCapability8({ dialect: params.dialect.name, feature: "exclusionConstraints" });
|
|
659
825
|
if (params.usage === "indexPredicate" && !params.dialect.capabilities.partialIndexes) {
|
|
660
|
-
throw
|
|
826
|
+
throw unsupportedCapability8({ dialect: params.dialect.name, feature: "partialIndexes" });
|
|
661
827
|
}
|
|
662
828
|
const context = {
|
|
663
829
|
model: params.model,
|
|
@@ -692,7 +858,7 @@ var INT64_MIN = -(2n ** 63n);
|
|
|
692
858
|
var INT64_MAX = 2n ** 63n - 1n;
|
|
693
859
|
function refuse2(params) {
|
|
694
860
|
const { context, rule, message } = params;
|
|
695
|
-
throw new
|
|
861
|
+
throw new VibeError5({
|
|
696
862
|
code: "VIBE_SCHEMA_INVALID",
|
|
697
863
|
message: `${context.site} on "${context.model.name}": ${message}`,
|
|
698
864
|
meta: { rule, model: context.model.name, site: context.site, ...params.meta ?? {} }
|
|
@@ -785,7 +951,7 @@ function resolveField(params) {
|
|
|
785
951
|
});
|
|
786
952
|
}
|
|
787
953
|
if (context.usage === "check" && context.dialect.name === "mysql" && field.default?.kind === "autoincrement") {
|
|
788
|
-
throw
|
|
954
|
+
throw unsupportedCapability8({
|
|
789
955
|
dialect: "mysql",
|
|
790
956
|
feature: `CHECK constraint referencing auto-increment column "${context.model.name}.${name}"`,
|
|
791
957
|
hint: "mysql refuses this with error 3818 \u2014 enforce the rule in application code or on a non-generated column"
|
|
@@ -956,12 +1122,12 @@ function describe(params) {
|
|
|
956
1122
|
|
|
957
1123
|
// packages/sql/src/exclusion.ts
|
|
958
1124
|
function invalid2(rule) {
|
|
959
|
-
throw new
|
|
1125
|
+
throw new VibeError6({ code: "VIBE_SCHEMA_INVALID", message: `Invalid exclusion constraint: ${rule}`, meta: { rule: `exclusion-${rule}` } });
|
|
960
1126
|
}
|
|
961
1127
|
function compileExclusion(params) {
|
|
962
1128
|
const { exclusion: spec, model, dialect } = params;
|
|
963
1129
|
if (!dialect.capabilities.exclusionConstraints)
|
|
964
|
-
throw
|
|
1130
|
+
throw unsupportedCapability9({ dialect: dialect.name, feature: "exclusionConstraints" });
|
|
965
1131
|
if (!spec.name || spec.name.includes("\x00"))
|
|
966
1132
|
invalid2("name");
|
|
967
1133
|
if (!["int8range", "tsrange", "tstzrange"].includes(spec.range.type))
|
|
@@ -1004,7 +1170,7 @@ function compileExclusion(params) {
|
|
|
1004
1170
|
const column = field(key.field);
|
|
1005
1171
|
if (column.isOptional)
|
|
1006
1172
|
invalid2("nullable-key-requires-expression");
|
|
1007
|
-
if (!["String", "Int", "BigInt", "Float", "Decimal", "Boolean", "DateTime"].includes(fieldTypeIdentity2({ type: column.type })))
|
|
1173
|
+
if (!isEnumType2(column.type) && !["String", "Int", "BigInt", "Float", "Decimal", "Boolean", "DateTime"].includes(fieldTypeIdentity2({ type: column.type })))
|
|
1008
1174
|
invalid2("key-type");
|
|
1009
1175
|
return `${dialect.quoteIdent({ name: column.dbName ?? column.name })} WITH =`;
|
|
1010
1176
|
});
|
|
@@ -1014,13 +1180,13 @@ function compileExclusion(params) {
|
|
|
1014
1180
|
}
|
|
1015
1181
|
function renderExclusionSetup(params) {
|
|
1016
1182
|
if (!params.dialect.capabilities.exclusionConstraints)
|
|
1017
|
-
throw
|
|
1183
|
+
throw unsupportedCapability9({ dialect: params.dialect.name, feature: "exclusionConstraints" });
|
|
1018
1184
|
return params.exclusion.requiredExtensions.map((name) => `CREATE EXTENSION IF NOT EXISTS ${params.dialect.quoteIdent({ name })};`);
|
|
1019
1185
|
}
|
|
1020
1186
|
function renderExclusionDdl(params) {
|
|
1021
1187
|
const { dialect, table, exclusion, operation } = params;
|
|
1022
1188
|
if (!dialect.capabilities.exclusionConstraints)
|
|
1023
|
-
throw
|
|
1189
|
+
throw unsupportedCapability9({ dialect: dialect.name, feature: "exclusionConstraints" });
|
|
1024
1190
|
const target = `ALTER TABLE ${dialect.quoteIdent({ name: table })}`;
|
|
1025
1191
|
if (operation === "drop")
|
|
1026
1192
|
return [`${target} DROP CONSTRAINT ${dialect.quoteIdent({ name: exclusion.name })};`];
|
|
@@ -1029,15 +1195,15 @@ function renderExclusionDdl(params) {
|
|
|
1029
1195
|
function renderExclusionCatalog(params) {
|
|
1030
1196
|
const { dialect, probe } = params;
|
|
1031
1197
|
if (!dialect.capabilities.exclusionConstraints)
|
|
1032
|
-
throw
|
|
1033
|
-
const scope = probe === undefined ?
|
|
1198
|
+
throw unsupportedCapability9({ dialect: dialect.name, feature: "exclusionConstraints" });
|
|
1199
|
+
const scope = probe === undefined ? `n.nspname = ${dialect.quoteStringLiteral({ value: params.namespace ?? "public" })}` : `t.oid = ${dialect.quoteStringLiteral({ value: probe })}::regclass`;
|
|
1034
1200
|
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`;
|
|
1035
1201
|
}
|
|
1036
1202
|
function renderExclusionProbe(params) {
|
|
1037
1203
|
const { dialect, table, probe, definition } = params;
|
|
1038
1204
|
if (!dialect.capabilities.exclusionConstraints)
|
|
1039
|
-
throw
|
|
1040
|
-
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}`];
|
|
1205
|
+
throw unsupportedCapability9({ dialect: dialect.name, feature: "exclusionConstraints" });
|
|
1206
|
+
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}`];
|
|
1041
1207
|
}
|
|
1042
1208
|
// packages/sql/src/ast.ts
|
|
1043
1209
|
var SET_OPERATORS = ["increment", "decrement", "multiply", "divide"];
|
|
@@ -1092,7 +1258,7 @@ function isExcludedRef(value) {
|
|
|
1092
1258
|
}
|
|
1093
1259
|
var ADVISORY_LOCK_RESULT_COLUMN = "acquired";
|
|
1094
1260
|
// packages/sql/src/locks.ts
|
|
1095
|
-
import { internalError, unsupportedCapability as
|
|
1261
|
+
import { internalError, unsupportedCapability as unsupportedCapability10 } from "@vibeorm/schema";
|
|
1096
1262
|
var ROW_LOCK_PROSE = Object.freeze({
|
|
1097
1263
|
strengths: Object.freeze({
|
|
1098
1264
|
update: "FOR UPDATE",
|
|
@@ -1112,7 +1278,7 @@ function renderRowLockClause(params) {
|
|
|
1112
1278
|
if (syntax === null) {
|
|
1113
1279
|
if (lock.strength === "update" && lock.wait === "wait")
|
|
1114
1280
|
return null;
|
|
1115
|
-
throw
|
|
1281
|
+
throw unsupportedCapability10({
|
|
1116
1282
|
dialect: dialect.name,
|
|
1117
1283
|
feature: `a ${describeRowLock({ lock })} locking read`,
|
|
1118
1284
|
hint: `${dialect.name} has no locking-read clause at all`
|
|
@@ -1120,7 +1286,7 @@ function renderRowLockClause(params) {
|
|
|
1120
1286
|
}
|
|
1121
1287
|
const strengthText = syntax.strengths[lock.strength];
|
|
1122
1288
|
if (strengthText === undefined) {
|
|
1123
|
-
throw
|
|
1289
|
+
throw unsupportedCapability10({
|
|
1124
1290
|
dialect: dialect.name,
|
|
1125
1291
|
feature: `the ${describeRowLock({ lock: { ...lock, wait: "wait" } })} row-lock strength`,
|
|
1126
1292
|
hint: `${dialect.name} can express: ${Object.keys(syntax.strengths).join(", ")}`
|
|
@@ -1128,7 +1294,7 @@ function renderRowLockClause(params) {
|
|
|
1128
1294
|
}
|
|
1129
1295
|
const waitText = syntax.wait[lock.wait];
|
|
1130
1296
|
if (waitText === undefined) {
|
|
1131
|
-
throw
|
|
1297
|
+
throw unsupportedCapability10({
|
|
1132
1298
|
dialect: dialect.name,
|
|
1133
1299
|
feature: `the "${lock.wait}" lock wait policy`,
|
|
1134
1300
|
hint: `${dialect.name} can express: ${Object.keys(syntax.wait).join(", ")}`
|
|
@@ -1147,7 +1313,7 @@ function renderAdvisoryLock(params) {
|
|
|
1147
1313
|
const { dialect, statement, collector } = params;
|
|
1148
1314
|
const functions = dialect.advisoryLockFunctions;
|
|
1149
1315
|
if (functions === null) {
|
|
1150
|
-
throw
|
|
1316
|
+
throw unsupportedCapability10({
|
|
1151
1317
|
dialect: dialect.name,
|
|
1152
1318
|
feature: "advisory locks",
|
|
1153
1319
|
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`
|
|
@@ -1189,10 +1355,10 @@ import {
|
|
|
1189
1355
|
} from "@vibeorm/schema";
|
|
1190
1356
|
|
|
1191
1357
|
// packages/sql/src/identifiers.ts
|
|
1192
|
-
import { VibeError as
|
|
1358
|
+
import { VibeError as VibeError7 } from "@vibeorm/schema";
|
|
1193
1359
|
function assertNoNul(params) {
|
|
1194
1360
|
if (params.name.includes("\x00")) {
|
|
1195
|
-
throw new
|
|
1361
|
+
throw new VibeError7({
|
|
1196
1362
|
code: "VIBE_VALIDATION",
|
|
1197
1363
|
message: "an SQL identifier cannot contain a NUL character \u2014 the server would stop reading it there and address a different object",
|
|
1198
1364
|
meta: { name: params.name.replaceAll("\x00", "\\0") }
|
|
@@ -1385,8 +1551,85 @@ var SQL_DIALECTS = {
|
|
|
1385
1551
|
function getSqlDialect(params) {
|
|
1386
1552
|
return SQL_DIALECTS[params.dialect];
|
|
1387
1553
|
}
|
|
1554
|
+
// packages/sql/src/dialects/sqlite-table-definition.ts
|
|
1555
|
+
import { VibeError as VibeError8 } from "@vibeorm/schema";
|
|
1556
|
+
function tableDefinitionParts(params) {
|
|
1557
|
+
const sql = params.sql;
|
|
1558
|
+
if (!/^\s*CREATE\s+(?:TEMP(?:ORARY)?\s+)?TABLE\b/i.test(sql))
|
|
1559
|
+
throw unavailable();
|
|
1560
|
+
const parts = [];
|
|
1561
|
+
let start = -1;
|
|
1562
|
+
let depth = 0;
|
|
1563
|
+
let quote;
|
|
1564
|
+
for (let index = 0;index < sql.length; index++) {
|
|
1565
|
+
const char = sql[index];
|
|
1566
|
+
if (quote !== undefined) {
|
|
1567
|
+
if (char === quote) {
|
|
1568
|
+
if (quote !== "]" && sql[index + 1] === quote)
|
|
1569
|
+
index++;
|
|
1570
|
+
else
|
|
1571
|
+
quote = undefined;
|
|
1572
|
+
}
|
|
1573
|
+
continue;
|
|
1574
|
+
}
|
|
1575
|
+
if (char === "'" || char === '"' || char === "`") {
|
|
1576
|
+
quote = char;
|
|
1577
|
+
continue;
|
|
1578
|
+
}
|
|
1579
|
+
if (char === "[") {
|
|
1580
|
+
quote = "]";
|
|
1581
|
+
continue;
|
|
1582
|
+
}
|
|
1583
|
+
if (char === "-" && sql[index + 1] === "-") {
|
|
1584
|
+
const end = sql.indexOf(`
|
|
1585
|
+
`, index + 2);
|
|
1586
|
+
if (end < 0)
|
|
1587
|
+
break;
|
|
1588
|
+
index = end;
|
|
1589
|
+
continue;
|
|
1590
|
+
}
|
|
1591
|
+
if (char === "/" && sql[index + 1] === "*") {
|
|
1592
|
+
const end = sql.indexOf("*/", index + 2);
|
|
1593
|
+
if (end < 0)
|
|
1594
|
+
break;
|
|
1595
|
+
index = end + 1;
|
|
1596
|
+
continue;
|
|
1597
|
+
}
|
|
1598
|
+
if (char === "(") {
|
|
1599
|
+
if (depth === 0)
|
|
1600
|
+
start = index + 1;
|
|
1601
|
+
depth++;
|
|
1602
|
+
} else if (char === ")") {
|
|
1603
|
+
depth--;
|
|
1604
|
+
if (depth === 0 && start >= 0) {
|
|
1605
|
+
parts.push(sql.slice(start, index).trim());
|
|
1606
|
+
return { clauses: parts, suffix: sql.slice(index + 1) };
|
|
1607
|
+
}
|
|
1608
|
+
} else if (char === "," && depth === 1) {
|
|
1609
|
+
parts.push(sql.slice(start, index).trim());
|
|
1610
|
+
start = index + 1;
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
throw unavailable();
|
|
1614
|
+
}
|
|
1615
|
+
function unavailable() {
|
|
1616
|
+
return new VibeError8({ code: "VIBE_MIGRATION", message: "Cannot preserve table definition", meta: { reason: "table-definition-unavailable" } });
|
|
1617
|
+
}
|
|
1618
|
+
function sqliteColumnClauses(params) {
|
|
1619
|
+
const columns = [];
|
|
1620
|
+
for (const part of tableDefinitionParts(params).clauses) {
|
|
1621
|
+
const token = /^(?:"((?:[^"]|"")+)"|`((?:[^`]|``)+)`|\[([^\]]+)\]|([A-Za-z_][A-Za-z_0-9]*))/.exec(part);
|
|
1622
|
+
if (token === null)
|
|
1623
|
+
throw unavailable();
|
|
1624
|
+
if (token[4] !== undefined && /^(?:CONSTRAINT|PRIMARY|UNIQUE|CHECK|FOREIGN)$/i.test(token[4]))
|
|
1625
|
+
continue;
|
|
1626
|
+
const name = token[1]?.replaceAll('""', '"') ?? token[2]?.replaceAll("``", "`") ?? token[3] ?? token[4];
|
|
1627
|
+
columns.push({ name, sql: part });
|
|
1628
|
+
}
|
|
1629
|
+
return columns;
|
|
1630
|
+
}
|
|
1388
1631
|
// packages/sql/src/dialects/diagnostics.ts
|
|
1389
|
-
import { unsupportedCapability as
|
|
1632
|
+
import { unsupportedCapability as unsupportedCapability11 } from "@vibeorm/schema";
|
|
1390
1633
|
var QUERIES = Object.freeze({
|
|
1391
1634
|
postgresJsonWire: { dialect: "postgres", text: `SELECT '{"__vibeorm":1}'::jsonb AS "probe"` },
|
|
1392
1635
|
sqliteTriggers: { dialect: "sqlite", text: "SELECT name, tbl_name, sql FROM sqlite_master WHERE type = 'trigger'" }
|
|
@@ -1394,7 +1637,7 @@ var QUERIES = Object.freeze({
|
|
|
1394
1637
|
function renderDiagnostic(params) {
|
|
1395
1638
|
const query = QUERIES[params.query];
|
|
1396
1639
|
if (params.dialect.name !== query.dialect) {
|
|
1397
|
-
throw
|
|
1640
|
+
throw unsupportedCapability11({ dialect: params.dialect.name, feature: params.query, hint: `this diagnostic requires ${query.dialect}` });
|
|
1398
1641
|
}
|
|
1399
1642
|
return { text: query.text, values: [], returning: "none" };
|
|
1400
1643
|
}
|
|
@@ -1403,7 +1646,7 @@ function isFixedDiagnostic(params) {
|
|
|
1403
1646
|
}
|
|
1404
1647
|
|
|
1405
1648
|
// packages/sql/src/dialects/postgres-diagnostics.ts
|
|
1406
|
-
import { VibeError as
|
|
1649
|
+
import { VibeError as VibeError9, unsupportedCapability as unsupportedCapability12 } from "@vibeorm/schema";
|
|
1407
1650
|
function renderPostgresExplain(params) {
|
|
1408
1651
|
const { dialect, query, options } = params;
|
|
1409
1652
|
requirePostgres({ dialect, feature: "query plan inspection (EXPLAIN)" });
|
|
@@ -1438,7 +1681,7 @@ var POSTGRES_WORKLOAD_LIMITS = Object.freeze({
|
|
|
1438
1681
|
});
|
|
1439
1682
|
function requirePostgres(params) {
|
|
1440
1683
|
if (params.dialect.name !== "postgres") {
|
|
1441
|
-
throw
|
|
1684
|
+
throw unsupportedCapability12({
|
|
1442
1685
|
dialect: params.dialect.name,
|
|
1443
1686
|
feature: params.feature,
|
|
1444
1687
|
hint: "plan inspection and workload statistics are PostgreSQL-shaped; portable operation tracing works on every dialect"
|
|
@@ -1456,7 +1699,7 @@ function renderPostgresWorkload(params) {
|
|
|
1456
1699
|
if (query === "statementSnapshot") {
|
|
1457
1700
|
const bounded = limit ?? 20;
|
|
1458
1701
|
if (!Number.isInteger(bounded) || bounded < POSTGRES_WORKLOAD_LIMITS.minLimit || bounded > POSTGRES_WORKLOAD_LIMITS.maxLimit) {
|
|
1459
|
-
throw new
|
|
1702
|
+
throw new VibeError9({
|
|
1460
1703
|
code: "VIBE_CONFIG",
|
|
1461
1704
|
message: `workload snapshot limit must be an integer between ${POSTGRES_WORKLOAD_LIMITS.minLimit} and ${POSTGRES_WORKLOAD_LIMITS.maxLimit}, got ${String(limit)}`,
|
|
1462
1705
|
meta: { limit }
|
|
@@ -1475,7 +1718,7 @@ function renderPostgresWorkload(params) {
|
|
|
1475
1718
|
function requireArg(params) {
|
|
1476
1719
|
const { value, name, query } = params;
|
|
1477
1720
|
if (value === undefined || value.length === 0) {
|
|
1478
|
-
throw new
|
|
1721
|
+
throw new VibeError9({
|
|
1479
1722
|
code: "VIBE_CONFIG",
|
|
1480
1723
|
message: `the "${query}" diagnostic read requires a ${name}`,
|
|
1481
1724
|
meta: { query, argument: name }
|
|
@@ -1485,10 +1728,10 @@ function requireArg(params) {
|
|
|
1485
1728
|
}
|
|
1486
1729
|
|
|
1487
1730
|
// packages/sql/src/dialects/postgres-rls-readiness.ts
|
|
1488
|
-
import { unsupportedCapability as
|
|
1731
|
+
import { unsupportedCapability as unsupportedCapability14 } from "@vibeorm/schema";
|
|
1489
1732
|
|
|
1490
1733
|
// packages/sql/src/dialects/postgres-rls.ts
|
|
1491
|
-
import { RlsContextError, VibeError as
|
|
1734
|
+
import { RlsContextError, VibeError as VibeError10, assertValidSchemaIR, unsupportedCapability as unsupportedCapability13, validateRlsContextIR } from "@vibeorm/schema";
|
|
1492
1735
|
var NATIVE_RLS = Object.freeze({ policyName: "vibeorm_managed", tableSchema: "public", settingPrefix: "vibeorm.", parentAlias: "parent" });
|
|
1493
1736
|
var COLUMN_TYPES = {
|
|
1494
1737
|
String: { default: "text", native: { text: "text", varchar: "varchar", charactervarying: "varchar", uuid: "uuid" } },
|
|
@@ -1501,18 +1744,18 @@ var COLUMN_TYPES = {
|
|
|
1501
1744
|
};
|
|
1502
1745
|
function requireRls(params) {
|
|
1503
1746
|
if (!params.dialect.capabilities.nativeRowLevelSecurity) {
|
|
1504
|
-
throw
|
|
1747
|
+
throw unsupportedCapability13({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity" });
|
|
1505
1748
|
}
|
|
1506
1749
|
}
|
|
1507
1750
|
function identifier2(params) {
|
|
1508
1751
|
if (typeof params.name !== "string" || params.name.length === 0 || params.name.includes("\x00") || new TextEncoder().encode(params.name).length > 63) {
|
|
1509
|
-
throw new
|
|
1752
|
+
throw new VibeError10({ code: "VIBE_SCHEMA_INVALID", message: "Native RLS identifiers must be nonempty PostgreSQL identifiers of at most 63 bytes" });
|
|
1510
1753
|
}
|
|
1511
1754
|
return params.dialect.quoteIdent({ name: params.name });
|
|
1512
1755
|
}
|
|
1513
1756
|
function tableRef(params) {
|
|
1514
1757
|
if (params.schema !== undefined && params.schema !== "public" && params.schema !== "pg_temp") {
|
|
1515
|
-
throw
|
|
1758
|
+
throw unsupportedCapability13({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity", hint: "Only public tables and temporary policy probes are supported" });
|
|
1516
1759
|
}
|
|
1517
1760
|
return `${identifier2({ dialect: params.dialect, name: params.schema ?? NATIVE_RLS.tableSchema })}.${identifier2({ dialect: params.dialect, name: params.table })}`;
|
|
1518
1761
|
}
|
|
@@ -1553,14 +1796,14 @@ function renderRlsColumnType(params) {
|
|
|
1553
1796
|
if (native !== undefined)
|
|
1554
1797
|
return native;
|
|
1555
1798
|
}
|
|
1556
|
-
throw
|
|
1799
|
+
throw unsupportedCapability13({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity", hint: `Unsupported physical ownership type on field ${field.name}` });
|
|
1557
1800
|
}
|
|
1558
1801
|
function protectedModel(params) {
|
|
1559
1802
|
requireRls(params);
|
|
1560
1803
|
assertValidSchemaIR({ schema: params.schema });
|
|
1561
1804
|
const model = params.schema.models.find((entry) => entry.name === params.model);
|
|
1562
1805
|
if (model === undefined || model.rls === undefined || model.rls === "unprotected") {
|
|
1563
|
-
throw new
|
|
1806
|
+
throw new VibeError10({ code: "VIBE_SCHEMA_INVALID", message: "Native RLS policy rendering requires a protected model", meta: { model: params.model } });
|
|
1564
1807
|
}
|
|
1565
1808
|
return model;
|
|
1566
1809
|
}
|
|
@@ -1593,7 +1836,7 @@ function predicate(params) {
|
|
|
1593
1836
|
}).join(" AND ");
|
|
1594
1837
|
return `EXISTS (SELECT 1 FROM ${tableRef({ dialect, table: parent.dbName ?? parent.name })} AS ${alias} WHERE ${join})`;
|
|
1595
1838
|
}
|
|
1596
|
-
throw new
|
|
1839
|
+
throw new VibeError10({ code: "VIBE_SCHEMA_INVALID", message: "Native RLS requires a protected policy rule" });
|
|
1597
1840
|
}
|
|
1598
1841
|
function renderRlsPolicy(params) {
|
|
1599
1842
|
const model = protectedModel(params);
|
|
@@ -1627,7 +1870,7 @@ function renderRlsContext(params) {
|
|
|
1627
1870
|
function renderRlsRole(params) {
|
|
1628
1871
|
requireRls(params);
|
|
1629
1872
|
if (typeof params.role !== "string" || params.role.trim().length === 0 || params.role.includes("\x00")) {
|
|
1630
|
-
throw new
|
|
1873
|
+
throw new VibeError10({ code: "VIBE_CONFIG", message: "PGlite native RLS requires a trusted nonempty runtime role name" });
|
|
1631
1874
|
}
|
|
1632
1875
|
const collector = new ParamCollector(params.dialect);
|
|
1633
1876
|
return query({ text: `SELECT set_config('role', ${collector.add(params.role)}, true)`, values: collector.values });
|
|
@@ -1779,7 +2022,7 @@ ORDER BY c.relname, p.polname`,
|
|
|
1779
2022
|
});
|
|
1780
2023
|
function requireRls2(params) {
|
|
1781
2024
|
if (!params.dialect.capabilities.nativeRowLevelSecurity) {
|
|
1782
|
-
throw
|
|
2025
|
+
throw unsupportedCapability14({ dialect: params.dialect.name, feature: "nativeRowLevelSecurity" });
|
|
1783
2026
|
}
|
|
1784
2027
|
}
|
|
1785
2028
|
function renderRlsReadinessQuery(params) {
|
|
@@ -1811,7 +2054,7 @@ function renderRlsPolicyPath(params) {
|
|
|
1811
2054
|
}
|
|
1812
2055
|
|
|
1813
2056
|
// packages/sql/src/render.ts
|
|
1814
|
-
import { internalError as internalError2, unsupportedCapability as
|
|
2057
|
+
import { assertSqlNamespace as assertSqlNamespace2, internalError as internalError2, unsupportedCapability as unsupportedCapability15 } from "@vibeorm/schema";
|
|
1815
2058
|
|
|
1816
2059
|
// packages/sql/src/like.ts
|
|
1817
2060
|
function escapeLikePattern(params) {
|
|
@@ -1917,7 +2160,10 @@ function quoteProjection(dialect, column) {
|
|
|
1917
2160
|
return `encode(${ref}, 'base64') AS ${alias}`;
|
|
1918
2161
|
}
|
|
1919
2162
|
function renderTable(dialect, table) {
|
|
1920
|
-
|
|
2163
|
+
if (table.namespace !== undefined)
|
|
2164
|
+
assertSqlNamespace2({ namespace: table.namespace, dialect: dialect.name });
|
|
2165
|
+
const relation = dialect.quoteIdent({ name: table.name });
|
|
2166
|
+
const name = table.namespace === undefined ? relation : `${dialect.quoteIdent({ name: table.namespace })}.${relation}`;
|
|
1921
2167
|
if (table.alias === undefined)
|
|
1922
2168
|
return name;
|
|
1923
2169
|
return `${name} AS ${dialect.quoteIdent({ name: table.alias })}`;
|
|
@@ -1956,7 +2202,7 @@ function renderAggRef(dialect, agg) {
|
|
|
1956
2202
|
function assertScalarArrays(dialect, feature) {
|
|
1957
2203
|
if (dialect.capabilities.scalarArrays)
|
|
1958
2204
|
return;
|
|
1959
|
-
throw
|
|
2205
|
+
throw unsupportedCapability15({
|
|
1960
2206
|
dialect: dialect.name,
|
|
1961
2207
|
feature,
|
|
1962
2208
|
hint: "only postgres has native array columns \u2014 model the list as a related table instead"
|
|
@@ -1990,7 +2236,7 @@ function renderExpr(params) {
|
|
|
1990
2236
|
}
|
|
1991
2237
|
const column = quoteCol(dialect, expr.column);
|
|
1992
2238
|
if (dialect.inArrayStrategy === "any") {
|
|
1993
|
-
const base = `${column} = ANY(${collector.add([...expr.values])})`;
|
|
2239
|
+
const base = `${column} = ANY(${collector.add(Array.isArray(expr.values) ? [...expr.values] : expr.values)})`;
|
|
1994
2240
|
return expr.negate === true ? `NOT (${base})` : base;
|
|
1995
2241
|
}
|
|
1996
2242
|
const placeholders = collector.addMany(expr.values);
|
|
@@ -2087,11 +2333,11 @@ function renderExpr(params) {
|
|
|
2087
2333
|
}
|
|
2088
2334
|
case "arrayContainsAll": {
|
|
2089
2335
|
assertScalarArrays(dialect, 'the scalar-list filter "hasEvery"');
|
|
2090
|
-
return `${quoteCol(dialect, expr.column)} @> ${collector.add([...expr.values])}`;
|
|
2336
|
+
return `${quoteCol(dialect, expr.column)} @> ${collector.add(Array.isArray(expr.values) ? [...expr.values] : expr.values)}`;
|
|
2091
2337
|
}
|
|
2092
2338
|
case "arrayOverlaps": {
|
|
2093
2339
|
assertScalarArrays(dialect, 'the scalar-list filter "hasSome"');
|
|
2094
|
-
return `${quoteCol(dialect, expr.column)} && ${collector.add([...expr.values])}`;
|
|
2340
|
+
return `${quoteCol(dialect, expr.column)} && ${collector.add(Array.isArray(expr.values) ? [...expr.values] : expr.values)}`;
|
|
2095
2341
|
}
|
|
2096
2342
|
case "arrayIsEmpty": {
|
|
2097
2343
|
assertScalarArrays(dialect, 'the scalar-list filter "isEmpty"');
|
|
@@ -2166,7 +2412,7 @@ function renderJsonLike(params) {
|
|
|
2166
2412
|
function renderJsonFilter(params) {
|
|
2167
2413
|
const { dialect, expr, collector } = params;
|
|
2168
2414
|
if (!dialect.supportedJsonOps.includes(expr.op)) {
|
|
2169
|
-
throw
|
|
2415
|
+
throw unsupportedCapability15({
|
|
2170
2416
|
dialect: dialect.name,
|
|
2171
2417
|
feature: `the json filter "${expr.op}"`,
|
|
2172
2418
|
hint: `this dialect can express: ${dialect.supportedJsonOps.join(", ")}`
|
|
@@ -2230,7 +2476,7 @@ function renderJsonFilter(params) {
|
|
|
2230
2476
|
const elements = Array.isArray(expr.value) ? expr.value : [expr.value];
|
|
2231
2477
|
const tests = elements.map((element) => {
|
|
2232
2478
|
if (element === null || typeof element === "object" && element !== null) {
|
|
2233
|
-
throw
|
|
2479
|
+
throw unsupportedCapability15({
|
|
2234
2480
|
dialect: dialect.name,
|
|
2235
2481
|
feature: "json array_contains with object/array/null elements",
|
|
2236
2482
|
hint: "sqlite expresses scalar-element containment only \u2014 deep structural containment needs postgres or mysql"
|
|
@@ -2381,7 +2627,7 @@ function renderSelect(params) {
|
|
|
2381
2627
|
let distinctPrefix = "";
|
|
2382
2628
|
if (statement.distinctOn !== undefined && statement.distinctOn.length > 0) {
|
|
2383
2629
|
if (!dialect.capabilities.distinctOn) {
|
|
2384
|
-
throw
|
|
2630
|
+
throw unsupportedCapability15({
|
|
2385
2631
|
dialect: dialect.name,
|
|
2386
2632
|
feature: "SELECT DISTINCT ON",
|
|
2387
2633
|
hint: "postgres-only \u2014 the runtime emulates distinct client-side on this dialect"
|
|
@@ -2412,7 +2658,7 @@ function renderSelect(params) {
|
|
|
2412
2658
|
}
|
|
2413
2659
|
if (statement.lateralJoins !== undefined && statement.lateralJoins.length > 0) {
|
|
2414
2660
|
if (!dialect.capabilities.lateralJoin) {
|
|
2415
|
-
throw
|
|
2661
|
+
throw unsupportedCapability15({
|
|
2416
2662
|
dialect: dialect.name,
|
|
2417
2663
|
feature: "LATERAL JOIN relation loading",
|
|
2418
2664
|
hint: 'use the portable "query" relation strategy on this dialect'
|
|
@@ -2497,7 +2743,7 @@ function renderInsert(params) {
|
|
|
2497
2743
|
let target = "";
|
|
2498
2744
|
if (onConflict.constraint !== undefined) {
|
|
2499
2745
|
if (dialect.name !== "postgres") {
|
|
2500
|
-
throw
|
|
2746
|
+
throw unsupportedCapability15({
|
|
2501
2747
|
dialect: dialect.name,
|
|
2502
2748
|
feature: "a named-constraint conflict target (ON CONFLICT ON CONSTRAINT)",
|
|
2503
2749
|
hint: "only postgres names constraints in conflict targets \u2014 use a column-list target"
|
|
@@ -2512,7 +2758,7 @@ function renderInsert(params) {
|
|
|
2512
2758
|
}
|
|
2513
2759
|
text += ` ON CONFLICT${target} DO NOTHING`;
|
|
2514
2760
|
} else if (onConflict.constraint !== undefined || onConflict.conflictWhere !== undefined) {
|
|
2515
|
-
throw
|
|
2761
|
+
throw unsupportedCapability15({
|
|
2516
2762
|
dialect: dialect.name,
|
|
2517
2763
|
feature: "a refined conflict target (constraint / partial-index predicate) on INSERT IGNORE",
|
|
2518
2764
|
hint: "mysql skips on any duplicate key \u2014 drop the refinement or gate the call per dialect"
|
|
@@ -2530,7 +2776,7 @@ function renderInsert(params) {
|
|
|
2530
2776
|
throw internalError2({ message: "lastInsertIdColumn is a mysql (onDuplicateKey) construct" });
|
|
2531
2777
|
}
|
|
2532
2778
|
if (onConflict.constraint !== undefined && dialect.name !== "postgres") {
|
|
2533
|
-
throw
|
|
2779
|
+
throw unsupportedCapability15({
|
|
2534
2780
|
dialect: dialect.name,
|
|
2535
2781
|
feature: "a named-constraint conflict target (ON CONFLICT ON CONSTRAINT)",
|
|
2536
2782
|
hint: "sqlite conflict targets are column lists \u2014 name the indexed columns instead"
|
|
@@ -2546,14 +2792,14 @@ function renderInsert(params) {
|
|
|
2546
2792
|
text += ` ON CONFLICT${target} DO UPDATE SET ${assignments}`;
|
|
2547
2793
|
} else {
|
|
2548
2794
|
if (onConflict.constraint !== undefined) {
|
|
2549
|
-
throw
|
|
2795
|
+
throw unsupportedCapability15({
|
|
2550
2796
|
dialect: dialect.name,
|
|
2551
2797
|
feature: "a named-constraint conflict target on ON DUPLICATE KEY UPDATE",
|
|
2552
2798
|
hint: "mysql's ODKU fires on any duplicate key \u2014 no target can be named"
|
|
2553
2799
|
});
|
|
2554
2800
|
}
|
|
2555
2801
|
if (onConflict.conflictWhere !== undefined) {
|
|
2556
|
-
throw
|
|
2802
|
+
throw unsupportedCapability15({
|
|
2557
2803
|
dialect: dialect.name,
|
|
2558
2804
|
feature: "a partial-index conflict target on ON DUPLICATE KEY UPDATE",
|
|
2559
2805
|
hint: "mysql's ODKU fires on any duplicate key \u2014 a partial arbiter cannot be selected; gate the call per dialect"
|
|
@@ -2635,85 +2881,8 @@ function renderDelete(params) {
|
|
|
2635
2881
|
const returning = applyReturning({ dialect, requested: statement.returning });
|
|
2636
2882
|
return { text: text + returning.suffix, values: collector.values, returning: returning.status };
|
|
2637
2883
|
}
|
|
2638
|
-
// packages/sql/src/dialects/sqlite-table-definition.ts
|
|
2639
|
-
import { VibeError as VibeError9 } from "@vibeorm/schema";
|
|
2640
|
-
function tableDefinitionParts(params) {
|
|
2641
|
-
const sql = params.sql;
|
|
2642
|
-
if (!/^\s*CREATE\s+(?:TEMP(?:ORARY)?\s+)?TABLE\b/i.test(sql))
|
|
2643
|
-
throw unavailable();
|
|
2644
|
-
const parts = [];
|
|
2645
|
-
let start = -1;
|
|
2646
|
-
let depth = 0;
|
|
2647
|
-
let quote;
|
|
2648
|
-
for (let index = 0;index < sql.length; index++) {
|
|
2649
|
-
const char = sql[index];
|
|
2650
|
-
if (quote !== undefined) {
|
|
2651
|
-
if (char === quote) {
|
|
2652
|
-
if (quote !== "]" && sql[index + 1] === quote)
|
|
2653
|
-
index++;
|
|
2654
|
-
else
|
|
2655
|
-
quote = undefined;
|
|
2656
|
-
}
|
|
2657
|
-
continue;
|
|
2658
|
-
}
|
|
2659
|
-
if (char === "'" || char === '"' || char === "`") {
|
|
2660
|
-
quote = char;
|
|
2661
|
-
continue;
|
|
2662
|
-
}
|
|
2663
|
-
if (char === "[") {
|
|
2664
|
-
quote = "]";
|
|
2665
|
-
continue;
|
|
2666
|
-
}
|
|
2667
|
-
if (char === "-" && sql[index + 1] === "-") {
|
|
2668
|
-
const end = sql.indexOf(`
|
|
2669
|
-
`, index + 2);
|
|
2670
|
-
if (end < 0)
|
|
2671
|
-
break;
|
|
2672
|
-
index = end;
|
|
2673
|
-
continue;
|
|
2674
|
-
}
|
|
2675
|
-
if (char === "/" && sql[index + 1] === "*") {
|
|
2676
|
-
const end = sql.indexOf("*/", index + 2);
|
|
2677
|
-
if (end < 0)
|
|
2678
|
-
break;
|
|
2679
|
-
index = end + 1;
|
|
2680
|
-
continue;
|
|
2681
|
-
}
|
|
2682
|
-
if (char === "(") {
|
|
2683
|
-
if (depth === 0)
|
|
2684
|
-
start = index + 1;
|
|
2685
|
-
depth++;
|
|
2686
|
-
} else if (char === ")") {
|
|
2687
|
-
depth--;
|
|
2688
|
-
if (depth === 0 && start >= 0) {
|
|
2689
|
-
parts.push(sql.slice(start, index).trim());
|
|
2690
|
-
return { clauses: parts, suffix: sql.slice(index + 1) };
|
|
2691
|
-
}
|
|
2692
|
-
} else if (char === "," && depth === 1) {
|
|
2693
|
-
parts.push(sql.slice(start, index).trim());
|
|
2694
|
-
start = index + 1;
|
|
2695
|
-
}
|
|
2696
|
-
}
|
|
2697
|
-
throw unavailable();
|
|
2698
|
-
}
|
|
2699
|
-
function unavailable() {
|
|
2700
|
-
return new VibeError9({ code: "VIBE_MIGRATION", message: "Cannot preserve table definition", meta: { reason: "table-definition-unavailable" } });
|
|
2701
|
-
}
|
|
2702
|
-
function sqliteColumnClauses(params) {
|
|
2703
|
-
const columns = [];
|
|
2704
|
-
for (const part of tableDefinitionParts(params).clauses) {
|
|
2705
|
-
const token = /^(?:"((?:[^"]|"")+)"|`((?:[^`]|``)+)`|\[([^\]]+)\]|([A-Za-z_][A-Za-z_0-9]*))/.exec(part);
|
|
2706
|
-
if (token === null)
|
|
2707
|
-
throw unavailable();
|
|
2708
|
-
if (token[4] !== undefined && /^(?:CONSTRAINT|PRIMARY|UNIQUE|CHECK|FOREIGN)$/i.test(token[4]))
|
|
2709
|
-
continue;
|
|
2710
|
-
const name = token[1]?.replaceAll('""', '"') ?? token[2]?.replaceAll("``", "`") ?? token[3] ?? token[4];
|
|
2711
|
-
columns.push({ name, sql: part });
|
|
2712
|
-
}
|
|
2713
|
-
return columns;
|
|
2714
|
-
}
|
|
2715
2884
|
// packages/sql/src/dialects/postgres-type-identity.ts
|
|
2716
|
-
import { unsupportedCapability as
|
|
2885
|
+
import { unsupportedCapability as unsupportedCapability16 } from "@vibeorm/schema";
|
|
2717
2886
|
var IDENTIFIER = '(?:"(?:[^"\\x00]|"")+"|[a-zA-Z_][a-zA-Z_0-9$]*)';
|
|
2718
2887
|
var MODIFIER_VALUE = `(?:[+-]?\\d+|${IDENTIFIER}|'(?:[^'\\x00]|'')*')`;
|
|
2719
2888
|
var TYPE_NAME = new RegExp(`^\\s*(${IDENTIFIER})(?:\\s*\\.\\s*(${IDENTIFIER}))?\\s*(\\(\\s*${MODIFIER_VALUE}(?:\\s*,\\s*${MODIFIER_VALUE})*\\s*\\))?\\s*((?:\\[\\s*\\]\\s*)*)$`);
|
|
@@ -2740,13 +2909,13 @@ function parsePostgresTypeName(params) {
|
|
|
2740
2909
|
}
|
|
2741
2910
|
function renderPostgresTypeName(params) {
|
|
2742
2911
|
if (params.dialect.name !== "postgres")
|
|
2743
|
-
throw
|
|
2912
|
+
throw unsupportedCapability16({ dialect: params.dialect.name, feature: "postgresTypeIdentity" });
|
|
2744
2913
|
const { dialect, type } = params;
|
|
2745
2914
|
return `${type.namespace === undefined ? "" : `${dialect.quoteIdent({ name: type.namespace })}.`}${dialect.quoteIdent({ name: type.name })}${type.modifier}${type.array ? "[]" : ""}`;
|
|
2746
2915
|
}
|
|
2747
2916
|
function renderPostgresTypeIdentity(params) {
|
|
2748
2917
|
if (params.dialect.name !== "postgres")
|
|
2749
|
-
throw
|
|
2918
|
+
throw unsupportedCapability16({ dialect: params.dialect.name, feature: "postgresTypeIdentity" });
|
|
2750
2919
|
const collector = new ParamCollector(params.dialect);
|
|
2751
2920
|
const { source } = params;
|
|
2752
2921
|
let text;
|
|
@@ -2759,11 +2928,11 @@ function renderPostgresTypeIdentity(params) {
|
|
|
2759
2928
|
return { text, values: collector.values, returning: "none" };
|
|
2760
2929
|
}
|
|
2761
2930
|
// packages/sql/src/dialects/postgres-default-identity.ts
|
|
2762
|
-
import { unsupportedCapability as
|
|
2931
|
+
import { unsupportedCapability as unsupportedCapability17 } from "@vibeorm/schema";
|
|
2763
2932
|
function renderPostgresDefaultFunctionIdentity(params) {
|
|
2764
2933
|
const { dialect, table, column } = params;
|
|
2765
2934
|
if (dialect.name !== "postgres")
|
|
2766
|
-
throw
|
|
2935
|
+
throw unsupportedCapability17({ dialect: dialect.name, feature: "defaultFunctionIdentity" });
|
|
2767
2936
|
const collector = new ParamCollector(dialect);
|
|
2768
2937
|
const namespace = collector.add(table.namespace);
|
|
2769
2938
|
const name = collector.add(table.name);
|
|
@@ -2787,28 +2956,28 @@ WHERE n.nspname = ${namespace} AND c.relname = ${name} AND a.attname = ${field}
|
|
|
2787
2956
|
};
|
|
2788
2957
|
}
|
|
2789
2958
|
// packages/sql/src/dialects/constraint-owned-index.ts
|
|
2790
|
-
import { unsupportedCapability as
|
|
2959
|
+
import { unsupportedCapability as unsupportedCapability18 } from "@vibeorm/schema";
|
|
2791
2960
|
function renderConstraintOwnedIndexDrop(params) {
|
|
2792
2961
|
if (params.dialect.name !== "postgres")
|
|
2793
|
-
throw
|
|
2962
|
+
throw unsupportedCapability18({ dialect: params.dialect.name, feature: "constraintOwnedIndexDrop" });
|
|
2794
2963
|
const quote = (name) => params.dialect.quoteIdent({ name });
|
|
2795
2964
|
return { text: `ALTER TABLE ${quote(params.table)} DROP CONSTRAINT ${quote(params.constraint)};`, values: [], returning: "none" };
|
|
2796
2965
|
}
|
|
2797
2966
|
// packages/sql/src/dialects/mysql-check-probe.ts
|
|
2798
|
-
import { unsupportedCapability as
|
|
2967
|
+
import { unsupportedCapability as unsupportedCapability19 } from "@vibeorm/schema";
|
|
2799
2968
|
function renderMysqlCheckProbe(params) {
|
|
2800
2969
|
if (params.dialect.name !== "mysql")
|
|
2801
|
-
throw
|
|
2970
|
+
throw unsupportedCapability19({ dialect: params.dialect.name, feature: "temporaryCheckProbe" });
|
|
2802
2971
|
const q = (name) => params.dialect.quoteIdent({ name });
|
|
2803
2972
|
const probe = q(params.probe);
|
|
2804
2973
|
switch (params.operation) {
|
|
2805
2974
|
case "clone":
|
|
2806
2975
|
if (params.table === undefined)
|
|
2807
|
-
throw
|
|
2976
|
+
throw unsupportedCapability19({ dialect: "mysql", feature: "temporaryCheckProbe", hint: "A source table is required" });
|
|
2808
2977
|
return { text: `CREATE TEMPORARY TABLE ${probe} LIKE ${q(params.table.namespace)}.${q(params.table.name)}`, values: [], returning: "none" };
|
|
2809
2978
|
case "add": {
|
|
2810
2979
|
if (params.expression === undefined || params.definition === undefined)
|
|
2811
|
-
throw
|
|
2980
|
+
throw unsupportedCapability19({ dialect: "mysql", feature: "temporaryCheckProbe", hint: "An authored expression and temporary clone definition are required" });
|
|
2812
2981
|
const parts = tableDefinitionParts({ sql: params.definition });
|
|
2813
2982
|
return { text: `CREATE TEMPORARY TABLE ${probe} (
|
|
2814
2983
|
${[...parts.clauses, `CONSTRAINT ${probe} CHECK (${params.expression})`].join(`,
|
|
@@ -2854,31 +3023,31 @@ function sqliteTableConstraints(params) {
|
|
|
2854
3023
|
return result;
|
|
2855
3024
|
}
|
|
2856
3025
|
// packages/sql/src/dialects/migration-visibility.ts
|
|
2857
|
-
import { unsupportedCapability as
|
|
3026
|
+
import { unsupportedCapability as unsupportedCapability20 } from "@vibeorm/schema";
|
|
2858
3027
|
function renderMigrationVisibility(params) {
|
|
2859
3028
|
if (params.dialect.name !== "mysql")
|
|
2860
|
-
throw
|
|
3029
|
+
throw unsupportedCapability20({ dialect: params.dialect.name, feature: "mysqlCatalogVisibility" });
|
|
2861
3030
|
const collector = new ParamCollector(params.dialect);
|
|
2862
3031
|
const grantee = "CONCAT(QUOTE(SUBSTRING_INDEX(CURRENT_USER(), '@', 1)), '@', QUOTE(SUBSTRING_INDEX(CURRENT_USER(), '@', -1)))";
|
|
2863
3032
|
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" };
|
|
2864
3033
|
}
|
|
2865
3034
|
// packages/sql/src/dialects/migration-inventory.ts
|
|
2866
|
-
import { unsupportedCapability as
|
|
3035
|
+
import { unsupportedCapability as unsupportedCapability21 } from "@vibeorm/schema";
|
|
2867
3036
|
function renderSqliteColumnInventory(params) {
|
|
2868
3037
|
if (params.dialect.name !== "sqlite")
|
|
2869
|
-
throw
|
|
3038
|
+
throw unsupportedCapability21({ dialect: params.dialect.name, feature: "sqliteColumnInventory" });
|
|
2870
3039
|
const collector = new ParamCollector(params.dialect);
|
|
2871
3040
|
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" };
|
|
2872
3041
|
}
|
|
2873
3042
|
function renderSqliteForeignKeyInventory(params) {
|
|
2874
3043
|
if (params.dialect.name !== "sqlite")
|
|
2875
|
-
throw
|
|
3044
|
+
throw unsupportedCapability21({ dialect: params.dialect.name, feature: "sqliteForeignKeyInventory" });
|
|
2876
3045
|
const collector = new ParamCollector(params.dialect);
|
|
2877
3046
|
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" };
|
|
2878
3047
|
}
|
|
2879
3048
|
function renderSqliteRebuildObjects(params) {
|
|
2880
3049
|
if (params.dialect.name !== "sqlite")
|
|
2881
|
-
throw
|
|
3050
|
+
throw unsupportedCapability21({ dialect: params.dialect.name, feature: "sqliteRebuildObjects" });
|
|
2882
3051
|
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" };
|
|
2883
3052
|
}
|
|
2884
3053
|
function renderMigrationInventory(params) {
|
|
@@ -2898,6 +3067,8 @@ UNION ALL SELECT 'trigger', t.tgname::text, r.relname::text, NULL, NULL, NULL FR
|
|
|
2898
3067
|
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')
|
|
2899
3068
|
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
|
|
2900
3069
|
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')
|
|
3070
|
+
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')
|
|
3071
|
+
${params.namespace === "public" ? "" : `UNION ALL SELECT 'namespace', n.nspname::text, NULL, NULL, NULL, NULL FROM pg_catalog.pg_namespace n WHERE n.nspname = ${namespace}`}
|
|
2901
3072
|
ORDER BY kind, parent, name`, values: collector.values, returning: "none" };
|
|
2902
3073
|
}
|
|
2903
3074
|
if (params.dialect.name === "mysql") {
|
|
@@ -2915,12 +3086,17 @@ ORDER BY kind, parent, name`, values: collector.values, returning: "none" };
|
|
|
2915
3086
|
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" };
|
|
2916
3087
|
}
|
|
2917
3088
|
// packages/sql/src/dialects/migration-objects.ts
|
|
2918
|
-
import { internalError as internalError3, unsupportedCapability as
|
|
3089
|
+
import { internalError as internalError3, unsupportedCapability as unsupportedCapability22 } from "@vibeorm/schema";
|
|
2919
3090
|
function renderMigrationObjectQuery(params) {
|
|
2920
3091
|
return objectQuery({ ...params, collector: new ParamCollector(params.dialect) });
|
|
2921
3092
|
}
|
|
2922
3093
|
function objectQuery(params) {
|
|
2923
3094
|
const { dialect, identity: object, collector } = params;
|
|
3095
|
+
if (object.kind === "namespace") {
|
|
3096
|
+
if (dialect.name !== "postgres")
|
|
3097
|
+
throw unsupportedCapability22({ dialect: dialect.name, feature: "fixedNamespaces" });
|
|
3098
|
+
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" };
|
|
3099
|
+
}
|
|
2924
3100
|
const physical = "table" in object ? object.table : object.name;
|
|
2925
3101
|
const namespace = collector.add(physical.namespace);
|
|
2926
3102
|
const name = collector.add(physical.name);
|
|
@@ -2982,7 +3158,7 @@ function objectQuery(params) {
|
|
|
2982
3158
|
break;
|
|
2983
3159
|
case "constraint":
|
|
2984
3160
|
if (object.constraintType === "exclusion")
|
|
2985
|
-
throw
|
|
3161
|
+
throw unsupportedCapability22({ dialect: "mysql", feature: "exclusionConstraints" });
|
|
2986
3162
|
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)}`;
|
|
2987
3163
|
break;
|
|
2988
3164
|
case "index":
|
|
@@ -2992,15 +3168,15 @@ function objectQuery(params) {
|
|
|
2992
3168
|
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}`;
|
|
2993
3169
|
break;
|
|
2994
3170
|
default:
|
|
2995
|
-
throw
|
|
3171
|
+
throw unsupportedCapability22({ dialect: "mysql", feature: `inspect:${object.kind}` });
|
|
2996
3172
|
}
|
|
2997
3173
|
} else {
|
|
2998
3174
|
if (physical.namespace !== "main" && physical.namespace !== "temp")
|
|
2999
|
-
throw
|
|
3175
|
+
throw unsupportedCapability22({ dialect: "sqlite", feature: "attachedSchemaInspection" });
|
|
3000
3176
|
const catalog = `${dialect.quoteIdent({ name: physical.namespace })}.sqlite_schema`;
|
|
3001
3177
|
const kind = object.kind === "table" || object.kind === "view" ? object.kind : object.kind === "trigger" ? "trigger" : object.kind === "index" ? "index" : "table";
|
|
3002
3178
|
if (["function", "sequence", "postgresExtension", "enum"].includes(object.kind) || object.kind === "constraint" && object.constraintType === "exclusion")
|
|
3003
|
-
throw
|
|
3179
|
+
throw unsupportedCapability22({ dialect: "sqlite", feature: `inspect:${object.kind}` });
|
|
3004
3180
|
const sqliteParams = new ParamCollector(dialect);
|
|
3005
3181
|
if (object.kind === "column" || object.kind === "columnDefault" || object.kind === "generatedColumn") {
|
|
3006
3182
|
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)}`;
|
|
@@ -3030,10 +3206,14 @@ function renderMigrationDependencies(params) {
|
|
|
3030
3206
|
}
|
|
3031
3207
|
function dependencyQuery(params) {
|
|
3032
3208
|
if (params.dialect.name !== "postgres")
|
|
3033
|
-
throw
|
|
3209
|
+
throw unsupportedCapability22({ dialect: params.dialect.name, feature: "catalogDependencies" });
|
|
3034
3210
|
const object = params.identity;
|
|
3035
|
-
const physical = "table" in object ? object.table : object.name;
|
|
3036
3211
|
const collector = params.collector;
|
|
3212
|
+
if (object.kind === "namespace") {
|
|
3213
|
+
const namespace = collector.add(object.name);
|
|
3214
|
+
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" };
|
|
3215
|
+
}
|
|
3216
|
+
const physical = "table" in object ? object.table : object.name;
|
|
3037
3217
|
const namespace = collector.add(physical.namespace);
|
|
3038
3218
|
const name = collector.add(physical.name);
|
|
3039
3219
|
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}`;
|
|
@@ -3083,7 +3263,7 @@ function inspectionReferences(params) {
|
|
|
3083
3263
|
function renderMigrationObjectBatch(params) {
|
|
3084
3264
|
const { dialect } = params;
|
|
3085
3265
|
if (dialect.name !== "postgres")
|
|
3086
|
-
throw
|
|
3266
|
+
throw unsupportedCapability22({ dialect: dialect.name, feature: "batchedMigrationInspection" });
|
|
3087
3267
|
const groups = new Map;
|
|
3088
3268
|
for (const [position, identity] of params.identities.entries()) {
|
|
3089
3269
|
const kind = identity.kind === "constraint" ? `${identity.kind}:${identity.constraintType}` : identity.kind === "generatedColumn" ? "columnDefault" : identity.kind;
|
|
@@ -3116,16 +3296,16 @@ function renderMigrationNamespace(params) {
|
|
|
3116
3296
|
}
|
|
3117
3297
|
function renderCheckProbe(params) {
|
|
3118
3298
|
if (params.dialect.name !== "postgres")
|
|
3119
|
-
throw
|
|
3299
|
+
throw unsupportedCapability22({ dialect: params.dialect.name, feature: "rollbackCheckProbe" });
|
|
3120
3300
|
const q = (name) => params.dialect.quoteIdent({ name });
|
|
3121
3301
|
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" };
|
|
3122
3302
|
}
|
|
3123
3303
|
// packages/sql/src/dialects/postgres-migration.ts
|
|
3124
|
-
import { VibeError as
|
|
3304
|
+
import { VibeError as VibeError11, unsupportedCapability as unsupportedCapability23 } from "@vibeorm/schema";
|
|
3125
3305
|
var SETTING_VALUE = /^[0-9]+(?:\.[0-9]+)?\s*(?:us|ms|s|min|h|d)?$/;
|
|
3126
3306
|
function requirePostgres2(params) {
|
|
3127
3307
|
if (params.dialect.name !== "postgres") {
|
|
3128
|
-
throw
|
|
3308
|
+
throw unsupportedCapability23({ dialect: params.dialect.name, feature: params.feature });
|
|
3129
3309
|
}
|
|
3130
3310
|
}
|
|
3131
3311
|
function renderMigrationSessionRead(params) {
|
|
@@ -3140,7 +3320,7 @@ function renderMigrationSessionSet(params) {
|
|
|
3140
3320
|
requirePostgres2({ dialect: params.dialect, feature: "migrationSessionSettings" });
|
|
3141
3321
|
const { milliseconds, setting } = params;
|
|
3142
3322
|
if (!Number.isSafeInteger(milliseconds) || milliseconds < 0) {
|
|
3143
|
-
throw new
|
|
3323
|
+
throw new VibeError11({
|
|
3144
3324
|
code: "VIBE_MIGRATION",
|
|
3145
3325
|
message: `${setting} must be a non-negative integer number of milliseconds (got ${String(milliseconds)})`,
|
|
3146
3326
|
meta: { reason: "execution-metadata-invalid", setting, value: milliseconds }
|
|
@@ -3269,12 +3449,14 @@ export {
|
|
|
3269
3449
|
isFixedDiagnostic,
|
|
3270
3450
|
isJsonParamCell,
|
|
3271
3451
|
isListSetOperation,
|
|
3452
|
+
isNativeEnumArrayParameter,
|
|
3272
3453
|
isSetOperation,
|
|
3273
3454
|
isSqlDefaultCell,
|
|
3274
3455
|
isSqlFragment,
|
|
3275
3456
|
jsonParamCell,
|
|
3276
3457
|
mapConflictColumns,
|
|
3277
3458
|
mysqlDialect,
|
|
3459
|
+
nativeEnumArrayParameter,
|
|
3278
3460
|
parseDateTimeText,
|
|
3279
3461
|
parsePgArrayLiteral,
|
|
3280
3462
|
parsePostgresTypeName,
|
|
@@ -3291,7 +3473,10 @@ export {
|
|
|
3291
3473
|
renderExclusionProbe,
|
|
3292
3474
|
renderExclusionSetup,
|
|
3293
3475
|
renderExpr,
|
|
3476
|
+
renderExternalDependents,
|
|
3477
|
+
renderExternalOwnershipCatalog,
|
|
3294
3478
|
renderFragment,
|
|
3479
|
+
renderIndexColumns,
|
|
3295
3480
|
renderIndexStateQuery,
|
|
3296
3481
|
renderMigrationDependencies,
|
|
3297
3482
|
renderMigrationHistoryQuery,
|
|
@@ -3310,6 +3495,8 @@ export {
|
|
|
3310
3495
|
renderMysqlObjectMembers,
|
|
3311
3496
|
renderPostgresDefaultFunctionIdentity,
|
|
3312
3497
|
renderPostgresExplain,
|
|
3498
|
+
renderPostgresIndexKeys,
|
|
3499
|
+
renderPostgresPhysicalCatalog,
|
|
3313
3500
|
renderPostgresTypeIdentity,
|
|
3314
3501
|
renderPostgresTypeName,
|
|
3315
3502
|
renderPostgresWorkload,
|
|
@@ -3343,7 +3530,8 @@ export {
|
|
|
3343
3530
|
sqliteTableConstraints,
|
|
3344
3531
|
startsWithPattern,
|
|
3345
3532
|
strictMatchPattern,
|
|
3533
|
+
tableDefinitionParts,
|
|
3346
3534
|
toPgArrayLiteral
|
|
3347
3535
|
};
|
|
3348
3536
|
|
|
3349
|
-
//# debugId=
|
|
3537
|
+
//# debugId=55E18657169AA23764756E2164756E21
|