@voltro/database 0.22.1 → 0.23.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/CHANGELOG.md +316 -0
- package/dist/{fileBased-CDnutVVk.js → frameworkLiveTables-BvPIm2cD.js} +174 -174
- package/dist/index.d.ts +15 -0
- package/dist/index.js +651 -651
- package/dist/sql.d.ts +20 -3
- package/dist/sql.js +917 -884
- package/package.json +2 -2
package/dist/sql.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Effect as
|
|
3
|
-
import { createLogger as
|
|
4
|
-
import { SqlClient as
|
|
5
|
-
import { createHash as
|
|
6
|
-
import { promises as
|
|
7
|
-
import { join as
|
|
8
|
-
import { pathToFileURL as
|
|
1
|
+
import { H as e, U as t, a as n, c as r, d as i, ft as a, i as o, n as s, o as c, ot as l, q as u, r as d, s as f } from "./frameworkLiveTables-BvPIm2cD.js";
|
|
2
|
+
import { Effect as p } from "effect";
|
|
3
|
+
import { createLogger as m } from "@voltro/logger";
|
|
4
|
+
import { SqlClient as h } from "@effect/sql";
|
|
5
|
+
import { createHash as g } from "node:crypto";
|
|
6
|
+
import { promises as _ } from "node:fs";
|
|
7
|
+
import { join as v } from "node:path";
|
|
8
|
+
import { pathToFileURL as y } from "node:url";
|
|
9
9
|
//#region src/migrations/ddlError.ts
|
|
10
|
-
var
|
|
10
|
+
var b = (e, t, n, r = 400) => {
|
|
11
11
|
let i = n, a = i.cause ?? {}, o = (e) => {
|
|
12
12
|
let t = a[e];
|
|
13
13
|
return t == null || t === "" ? void 0 : String(t);
|
|
@@ -39,17 +39,17 @@ var _ = (e, t, n, r = 400) => {
|
|
|
39
39
|
t !== void 0 && s.push(`db.${e}: ${t}`);
|
|
40
40
|
}
|
|
41
41
|
return s.push(""), s.join("\n");
|
|
42
|
-
},
|
|
43
|
-
if (t !== "mysql" && t !== "mariadb" || !e.appliedIndexes.some(
|
|
42
|
+
}, x = (e) => e.unique === !0 && typeof e.where == "string" && e.where.length > 0, S = (e) => e.replace(/"/g, "`"), ee = (e) => typeof e == "string" ? e : e.name ?? String(e), te = (e, t) => `_ua_${t}_${e}`.slice(0, 64), ne = (e, t) => {
|
|
43
|
+
if (t !== "mysql" && t !== "mariadb" || !e.appliedIndexes.some(x)) return e;
|
|
44
44
|
let n = { ...e.fields }, r = [], i = [];
|
|
45
45
|
for (let t of e.appliedIndexes) {
|
|
46
|
-
if (!
|
|
46
|
+
if (!x(t)) {
|
|
47
47
|
r.push(t);
|
|
48
48
|
continue;
|
|
49
49
|
}
|
|
50
|
-
let a =
|
|
50
|
+
let a = S(t.where), o = [];
|
|
51
51
|
for (let r of t.fields) {
|
|
52
|
-
let i =
|
|
52
|
+
let i = ee(r);
|
|
53
53
|
if (!(i in e.fields)) throw Error(`@voltro/migrate: uniqueActive '${t.name}' on '${e.tableName}' references unknown column '${i}'.`);
|
|
54
54
|
let s = te(t.name, i);
|
|
55
55
|
o.push(s), n[s] = {
|
|
@@ -74,101 +74,101 @@ var _ = (e, t, n, r = 400) => {
|
|
|
74
74
|
appliedIndexes: r,
|
|
75
75
|
appliedUniques: [...e.appliedUniques ?? [], ...i]
|
|
76
76
|
};
|
|
77
|
-
},
|
|
77
|
+
}, C = (e, t) => `${e}::${t}`, re = (e, t) => {
|
|
78
78
|
let n = /* @__PURE__ */ new Set(), r = /* @__PURE__ */ new Set(), i = /* @__PURE__ */ new Set(), a = (e) => {
|
|
79
79
|
n.add(e);
|
|
80
80
|
let o = [...t(e)].filter((t) => t.target !== e).sort((e, t) => e.column.localeCompare(t.column));
|
|
81
|
-
for (let t of o) n.has(t.target) ? i.add(
|
|
81
|
+
for (let t of o) n.has(t.target) ? i.add(C(e, t.column)) : r.has(t.target) || a(t.target);
|
|
82
82
|
n.delete(e), r.add(e);
|
|
83
83
|
};
|
|
84
84
|
for (let t of [...e].sort()) r.has(t) || a(t);
|
|
85
85
|
return i;
|
|
86
|
-
},
|
|
86
|
+
}, ie = 6322741009312437n, ae = 250, oe = () => {
|
|
87
87
|
let e = Number(process.env.VOLTRO_MIGRATION_LOCK_TIMEOUT_MS);
|
|
88
88
|
return Number.isFinite(e) && e > 0 ? e : 3e4;
|
|
89
|
-
},
|
|
90
|
-
let t =
|
|
89
|
+
}, se = (e) => p.gen(function* () {
|
|
90
|
+
let t = oe(), n = Date.now();
|
|
91
91
|
for (;;) {
|
|
92
92
|
if ((yield* e`
|
|
93
93
|
SELECT pg_try_advisory_lock(${"6322741009312437"}::bigint) AS got`)[0]?.got) return;
|
|
94
|
-
Date.now() - n >= t && (yield*
|
|
94
|
+
Date.now() - n >= t && (yield* p.die(/* @__PURE__ */ Error(`could not acquire the postgres migration advisory lock within ${Math.round(t / 1e3)}s. Another migration is in progress, or a prior boot crashed while holding it. (A crashed process's session-level advisory lock is released once its DB connection closes; if this persists, find + terminate the holder via pg_stat_activity, or raise VOLTRO_MIGRATION_LOCK_TIMEOUT_MS.)`))), yield* p.sleep(`${ae} millis`);
|
|
95
95
|
}
|
|
96
|
-
}),
|
|
97
|
-
yield* e`SELECT pg_advisory_unlock(${
|
|
98
|
-
}),
|
|
96
|
+
}), ce = (e) => p.gen(function* () {
|
|
97
|
+
yield* e`SELECT pg_advisory_unlock(${ie.toString()}::bigint)`;
|
|
98
|
+
}), le = `voltro_migration_${ie.toString(36)}`, ue = 30, de = (e) => p.gen(function* () {
|
|
99
99
|
(yield* e`
|
|
100
|
-
SELECT GET_LOCK(${
|
|
101
|
-
}),
|
|
102
|
-
yield* e`SELECT RELEASE_LOCK(${
|
|
103
|
-
}),
|
|
100
|
+
SELECT GET_LOCK(${le}, ${ue}) AS got`)[0]?.got !== 1 && (yield* p.die(/* @__PURE__ */ Error(`could not acquire the migration lock '${le}' within ${ue}s (another migration is in progress, or a prior one left it held).`)));
|
|
101
|
+
}), fe = (e) => p.gen(function* () {
|
|
102
|
+
yield* e`SELECT RELEASE_LOCK(${le})`;
|
|
103
|
+
}), pe = `voltro_migration_${ie.toString(36)}`, me = 3e4, he = (e) => p.gen(function* () {
|
|
104
104
|
let t = yield* e`
|
|
105
105
|
DECLARE @res int;
|
|
106
|
-
EXEC @res = sp_getapplock @Resource = ${
|
|
107
|
-
@LockOwner = 'Session', @LockTimeout = ${
|
|
106
|
+
EXEC @res = sp_getapplock @Resource = ${pe}, @LockMode = 'Exclusive',
|
|
107
|
+
@LockOwner = 'Session', @LockTimeout = ${me};
|
|
108
108
|
SELECT @res AS res`;
|
|
109
|
-
(t[0]?.res ?? -999) < 0 && (yield*
|
|
110
|
-
}),
|
|
111
|
-
yield* e`EXEC sp_releaseapplock @Resource = ${
|
|
112
|
-
}),
|
|
113
|
-
mysql: () => ue(e),
|
|
114
|
-
mssql: () => me(e),
|
|
115
|
-
sqlite: () => ge,
|
|
116
|
-
orElse: () => oe(e)
|
|
117
|
-
}), w = (e) => e.onDialectOrElse({
|
|
109
|
+
(t[0]?.res ?? -999) < 0 && (yield* p.die(/* @__PURE__ */ Error(`could not acquire the migration lock '${pe}' (sp_getapplock returned ${t[0]?.res ?? "no row"}; another migration is in progress).`)));
|
|
110
|
+
}), ge = (e) => p.gen(function* () {
|
|
111
|
+
yield* e`EXEC sp_releaseapplock @Resource = ${pe}, @LockOwner = 'Session'`;
|
|
112
|
+
}), _e = p.void, w = (e) => e.onDialectOrElse({
|
|
118
113
|
mysql: () => de(e),
|
|
119
114
|
mssql: () => he(e),
|
|
120
|
-
sqlite: () =>
|
|
115
|
+
sqlite: () => _e,
|
|
121
116
|
orElse: () => se(e)
|
|
122
|
-
}),
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
return
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
117
|
+
}), T = (e) => e.onDialectOrElse({
|
|
118
|
+
mysql: () => fe(e),
|
|
119
|
+
mssql: () => ge(e),
|
|
120
|
+
sqlite: () => _e,
|
|
121
|
+
orElse: () => ce(e)
|
|
122
|
+
}), ve = (e, t) => p.acquireUseRelease(w(e), () => t, () => T(e).pipe(p.orDie)), ye = m({ scope: "voltro:migrate" }), E = (e) => {
|
|
123
|
+
ye.warn(e.replace(/^\[voltro:migrate\]\s*/, ""));
|
|
124
|
+
}, D = (t, n) => {
|
|
125
|
+
let r = t.type;
|
|
126
|
+
if (t.spatial) {
|
|
127
|
+
if (n === "postgres") return `${t.spatial.kind}(${t.spatial.geomKind},${t.spatial.srid})`;
|
|
128
|
+
throw Error(`@voltro/plugin-postgis: ${t.spatial.kind}('${t.spatial.geomKind}', ${t.spatial.srid}) is postgres-only — the active dialect is '${n}'. PostGIS has no portable equivalent; use store: 'postgres' (or DB_DIALECT=postgres) for spatial columns, or model the value as plain text/json.`);
|
|
129
|
+
}
|
|
130
|
+
if (r === "raw") {
|
|
131
|
+
if (t.rawDdl === void 0) throw Error("raw() column is missing its DDL fragment — this is a framework bug.");
|
|
132
|
+
return t.rawDdl;
|
|
133
|
+
}
|
|
134
|
+
if (r === "array" && t.arrayElement) {
|
|
135
|
+
if (n === "postgres") return `${D({
|
|
136
|
+
...t,
|
|
137
|
+
type: t.arrayElement,
|
|
138
138
|
arrayElement: void 0
|
|
139
|
-
},
|
|
140
|
-
if (
|
|
141
|
-
if (
|
|
142
|
-
if (
|
|
143
|
-
}
|
|
144
|
-
if (
|
|
145
|
-
let
|
|
146
|
-
if (
|
|
147
|
-
let
|
|
148
|
-
if (
|
|
149
|
-
if (
|
|
150
|
-
if (
|
|
151
|
-
if (
|
|
152
|
-
}
|
|
153
|
-
if (
|
|
154
|
-
if (
|
|
155
|
-
if (
|
|
156
|
-
if (
|
|
157
|
-
}
|
|
158
|
-
if (
|
|
159
|
-
let
|
|
160
|
-
if (
|
|
161
|
-
return
|
|
162
|
-
}
|
|
163
|
-
if (
|
|
164
|
-
if (
|
|
165
|
-
if (
|
|
166
|
-
if (
|
|
167
|
-
if (
|
|
168
|
-
if (
|
|
169
|
-
}
|
|
170
|
-
if (
|
|
171
|
-
if (
|
|
139
|
+
}, n)}[]`;
|
|
140
|
+
if (n === "mysql" || n === "mariadb") return "JSON";
|
|
141
|
+
if (n === "mssql") return "NVARCHAR(MAX)";
|
|
142
|
+
if (e(n)) return "TEXT";
|
|
143
|
+
}
|
|
144
|
+
if (r === "vector") {
|
|
145
|
+
let r = t.vectorDim;
|
|
146
|
+
if (r === void 0) throw Error("vector column is missing its dimension — this is a framework bug.");
|
|
147
|
+
let i = t.vectorPrecision === "half";
|
|
148
|
+
if (n === "postgres") return i ? `HALFVEC(${r})` : `VECTOR(${r})`;
|
|
149
|
+
if (n === "mysql" || n === "mariadb") return `VECTOR(${r})`;
|
|
150
|
+
if (n === "mssql") return "VARBINARY(MAX)";
|
|
151
|
+
if (e(n)) return "BLOB";
|
|
152
|
+
}
|
|
153
|
+
if (r === "interval") {
|
|
154
|
+
if (n === "postgres") return "INTERVAL";
|
|
155
|
+
if (n === "mysql" || n === "mariadb" || n === "mssql") return "BIGINT";
|
|
156
|
+
if (e(n)) return "INTEGER";
|
|
157
|
+
}
|
|
158
|
+
if (r === "decimal") {
|
|
159
|
+
let r = t.precision, i = t.scale ?? 0;
|
|
160
|
+
if (r === void 0) throw Error("decimal column is missing its precision — this is a framework bug.");
|
|
161
|
+
return e(n) ? "NUMERIC" : n === "postgres" ? `NUMERIC(${r}, ${i})` : `DECIMAL(${r}, ${i})`;
|
|
162
|
+
}
|
|
163
|
+
if (r === "bigint") return e(n) ? "INTEGER" : "BIGINT";
|
|
164
|
+
if (r === "enum") {
|
|
165
|
+
if (n === "postgres") return t.enumName ?? "TEXT";
|
|
166
|
+
if (n === "mysql" || n === "mariadb") return `ENUM(${(t.enumValues ?? []).map((e) => `'${e.replace(/'/g, "''")}'`).join(", ")})`;
|
|
167
|
+
if (n === "mssql") return "NVARCHAR(255)";
|
|
168
|
+
if (e(n)) return "TEXT";
|
|
169
|
+
}
|
|
170
|
+
if (r === "text" && t.maxLength !== void 0 && t.generatedAs === void 0) return n === "mssql" ? `NVARCHAR(${t.maxLength})` : e(n) ? "TEXT" : `VARCHAR(${t.maxLength})`;
|
|
171
|
+
if (e(n)) switch (r) {
|
|
172
172
|
case "id": return "TEXT";
|
|
173
173
|
case "text": return "TEXT";
|
|
174
174
|
case "integer": return "INTEGER";
|
|
@@ -184,9 +184,9 @@ var _ = (e, t, n, r = 400) => {
|
|
|
184
184
|
case "array": return "TEXT";
|
|
185
185
|
case "interval": return "INTEGER";
|
|
186
186
|
}
|
|
187
|
-
if (
|
|
187
|
+
if (n === "mysql" || n === "mariadb") switch (r) {
|
|
188
188
|
case "id": return "VARCHAR(64)";
|
|
189
|
-
case "text": return
|
|
189
|
+
case "text": return t.hasDefault && t.defaultFactory === void 0 || t.oneOf && t.oneOf.length > 0 ? "VARCHAR(255)" : t.generatedAs ? "TEXT" : "LONGTEXT";
|
|
190
190
|
case "integer": return "INT";
|
|
191
191
|
case "real": return "DOUBLE";
|
|
192
192
|
case "boolean": return "TINYINT(1)";
|
|
@@ -200,9 +200,9 @@ var _ = (e, t, n, r = 400) => {
|
|
|
200
200
|
case "array": return "JSON";
|
|
201
201
|
case "interval": return "BIGINT";
|
|
202
202
|
}
|
|
203
|
-
if (
|
|
203
|
+
if (n === "mssql") switch (r) {
|
|
204
204
|
case "id": return "NVARCHAR(64)";
|
|
205
|
-
case "text": return
|
|
205
|
+
case "text": return t.hasDefault && t.defaultFactory === void 0 || t.oneOf && t.oneOf.length > 0 ? "NVARCHAR(450)" : "NVARCHAR(MAX)";
|
|
206
206
|
case "integer": return "INT";
|
|
207
207
|
case "real": return "FLOAT";
|
|
208
208
|
case "boolean": return "BIT";
|
|
@@ -216,7 +216,7 @@ var _ = (e, t, n, r = 400) => {
|
|
|
216
216
|
case "array": return "NVARCHAR(MAX)";
|
|
217
217
|
case "interval": return "BIGINT";
|
|
218
218
|
}
|
|
219
|
-
switch (
|
|
219
|
+
switch (r) {
|
|
220
220
|
case "id": return "TEXT";
|
|
221
221
|
case "text": return "TEXT";
|
|
222
222
|
case "integer": return "INTEGER";
|
|
@@ -228,26 +228,26 @@ var _ = (e, t, n, r = 400) => {
|
|
|
228
228
|
case "bytes": return "BYTEA";
|
|
229
229
|
case "reference": return "TEXT";
|
|
230
230
|
case "vector": return "VECTOR";
|
|
231
|
-
case "enum": return
|
|
231
|
+
case "enum": return t.enumName ?? "TEXT";
|
|
232
232
|
case "array": return "TEXT[]";
|
|
233
233
|
case "interval": return "INTERVAL";
|
|
234
234
|
}
|
|
235
|
-
},
|
|
236
|
-
if (!
|
|
237
|
-
let
|
|
238
|
-
return
|
|
239
|
-
},
|
|
240
|
-
if (n !== "postgres" || t.type !== "array" || t.arrayElement === void 0) return
|
|
241
|
-
let r =
|
|
235
|
+
}, be = (t, n) => {
|
|
236
|
+
if (!t.hasDefault || t.defaultFactory !== void 0) return null;
|
|
237
|
+
let r = t.defaultValue;
|
|
238
|
+
return r === "now" ? e(n) ? "DEFAULT CURRENT_TIMESTAMP" : n === "mssql" ? "DEFAULT SYSUTCDATETIME()" : n === "mysql" || n === "mariadb" ? "DEFAULT CURRENT_TIMESTAMP(6)" : "DEFAULT now()" : typeof r == "boolean" ? n === "postgres" ? `DEFAULT ${r}` : `DEFAULT ${+!!r}` : typeof r == "number" ? `DEFAULT ${r}` : typeof r == "string" ? `DEFAULT '${r.replace(/'/g, "''")}'` : Array.isArray(r) ? xe(r, t, n) : typeof r == "object" && r ? O(r, n) : null;
|
|
239
|
+
}, xe = (e, t, n) => {
|
|
240
|
+
if (n !== "postgres" || t.type !== "array" || t.arrayElement === void 0) return O(e, n);
|
|
241
|
+
let r = D({
|
|
242
242
|
...t,
|
|
243
243
|
type: t.arrayElement,
|
|
244
244
|
arrayElement: void 0
|
|
245
245
|
}, n);
|
|
246
246
|
return `DEFAULT '{${e.map((e) => typeof e == "number" || typeof e == "boolean" ? String(e) : `"${String(e).replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`).join(",").replace(/'/g, "''")}}'::${r}[]`;
|
|
247
|
-
},
|
|
247
|
+
}, O = (e, t) => {
|
|
248
248
|
let n = JSON.stringify(e).replace(/'/g, "''");
|
|
249
249
|
return t === "postgres" ? `DEFAULT '${n}'::jsonb` : t === "mysql" || t === "mariadb" ? `DEFAULT (CAST('${n}' AS JSON))` : `DEFAULT '${n}'`;
|
|
250
|
-
},
|
|
250
|
+
}, k = (e, t) => {
|
|
251
251
|
switch (t) {
|
|
252
252
|
case "postgres": return `"${e}" BIGSERIAL PRIMARY KEY`;
|
|
253
253
|
case "mysql":
|
|
@@ -256,7 +256,7 @@ var _ = (e, t, n, r = 400) => {
|
|
|
256
256
|
case "sqlite": return `"${e}" INTEGER PRIMARY KEY AUTOINCREMENT`;
|
|
257
257
|
case "turso": return `"${e}" INTEGER PRIMARY KEY`;
|
|
258
258
|
}
|
|
259
|
-
},
|
|
259
|
+
}, A = (e, t) => {
|
|
260
260
|
if (e === "restrict" && t === "mssql") return "NO ACTION";
|
|
261
261
|
switch (e) {
|
|
262
262
|
case "cascade": return "CASCADE";
|
|
@@ -264,174 +264,216 @@ var _ = (e, t, n, r = 400) => {
|
|
|
264
264
|
case "setNull": return "SET NULL";
|
|
265
265
|
case "noAction": return "NO ACTION";
|
|
266
266
|
}
|
|
267
|
-
},
|
|
268
|
-
let i = e
|
|
267
|
+
}, Se = (e, n, r) => r === null ? t(e, n) : `${t(r, n)}.${t(e, n)}`, Ce = 191, we = (e, n, r) => {
|
|
268
|
+
let i = t(e, r);
|
|
269
269
|
if (r !== "mysql" && r !== "mariadb") return i;
|
|
270
|
-
let a = n[
|
|
270
|
+
let a = n[e];
|
|
271
271
|
if (!a) return i;
|
|
272
|
-
let o =
|
|
273
|
-
return o.endsWith("TEXT") || o.endsWith("BLOB") ? `${i}(${
|
|
274
|
-
},
|
|
272
|
+
let o = D(a, r).toUpperCase();
|
|
273
|
+
return o.endsWith("TEXT") || o.endsWith("BLOB") ? `${i}(${Ce})` : i;
|
|
274
|
+
}, Te = (e, t) => {
|
|
275
275
|
if (!e.generatedAs) return null;
|
|
276
276
|
let { expr: n, stored: r } = e.generatedAs;
|
|
277
|
-
if (t === "postgres") return r ||
|
|
277
|
+
if (t === "postgres") return r || E("[voltro:migrate] generated column requested as VIRTUAL but postgres only supports STORED — promoting"), `GENERATED ALWAYS AS (${n}) STORED`;
|
|
278
278
|
if (t === "mysql" || t === "mariadb") return `GENERATED ALWAYS AS (${n}) ${r ? "STORED" : "VIRTUAL"}`;
|
|
279
279
|
if (t === "mssql") return `AS (${n})${r ? " PERSISTED" : ""}`;
|
|
280
280
|
if (t === "turso") throw Error("@voltro/sql-turso: generated columns (.generatedAs(...)) are not supported — Turso's MVCC mode (journal_mode=experimental_mvcc) rejects them. Drop the generated column, or use the 'sqlite' / 'postgres' dialect for this table.");
|
|
281
281
|
return `GENERATED ALWAYS AS (${n}) ${r ? "STORED" : "VIRTUAL"}`;
|
|
282
|
-
},
|
|
283
|
-
if (
|
|
284
|
-
let
|
|
285
|
-
|
|
286
|
-
let
|
|
287
|
-
if (
|
|
282
|
+
}, Ee = (n, r, i, a, o = !1, s = !1) => {
|
|
283
|
+
if (r.type === "id" && r.idScheme?.kind === "numeric") return k(n, i);
|
|
284
|
+
let c = i === "mssql" && r.type === "text" && !r.generatedAs && r.maxLength === void 0 && a?.has(n) ? "NVARCHAR(450)" : D(r, i), l = [t(n, i), c];
|
|
285
|
+
r.type === "id" && !s && l.push("PRIMARY KEY");
|
|
286
|
+
let u = Te(r, i);
|
|
287
|
+
if (u) l.push(u);
|
|
288
288
|
else {
|
|
289
|
-
|
|
290
|
-
let e =
|
|
291
|
-
e &&
|
|
292
|
-
}
|
|
293
|
-
if (
|
|
294
|
-
let
|
|
295
|
-
|
|
296
|
-
let
|
|
297
|
-
|
|
298
|
-
let a =
|
|
299
|
-
a !== "NO ACTION" &&
|
|
300
|
-
}
|
|
301
|
-
if (
|
|
302
|
-
let
|
|
303
|
-
|
|
304
|
-
}
|
|
305
|
-
if (
|
|
306
|
-
if (
|
|
307
|
-
let
|
|
308
|
-
|
|
309
|
-
}
|
|
310
|
-
if (
|
|
311
|
-
let
|
|
312
|
-
|
|
313
|
-
}
|
|
314
|
-
return
|
|
315
|
-
},
|
|
289
|
+
r.nullable || l.push("NOT NULL"), r.unique && r.type !== "id" && l.push("UNIQUE");
|
|
290
|
+
let e = be(r, i);
|
|
291
|
+
e && l.push(e);
|
|
292
|
+
}
|
|
293
|
+
if (r.type === "reference" && r.references && !o) {
|
|
294
|
+
let e = r.references();
|
|
295
|
+
l.push(`REFERENCES ${t(e.tableName, i)} (${t("id", i)})`);
|
|
296
|
+
let n = A(r.onDelete ?? "restrict", i);
|
|
297
|
+
l.push(`ON DELETE ${n}`);
|
|
298
|
+
let a = A(r.refOnUpdate ?? "noAction", i);
|
|
299
|
+
a !== "NO ACTION" && l.push(`ON UPDATE ${a}`);
|
|
300
|
+
}
|
|
301
|
+
if (r.oneOf && r.oneOf.length > 0) {
|
|
302
|
+
let e = r.oneOf.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ");
|
|
303
|
+
l.push(`CHECK (${t(n, i)} IN (${e}))`);
|
|
304
|
+
}
|
|
305
|
+
if (r.checks && r.checks.length > 0) for (let e of r.checks) l.push(`CHECK (${e})`);
|
|
306
|
+
if (r.type === "enum" && r.enumValues && r.enumValues.length > 0 && (i === "mssql" || e(i))) {
|
|
307
|
+
let e = r.enumValues.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ");
|
|
308
|
+
l.push(`CHECK (${t(n, i)} IN (${e}))`);
|
|
309
|
+
}
|
|
310
|
+
if (r.type === "json" || r.type === "array") {
|
|
311
|
+
let r = t(n, i);
|
|
312
|
+
e(i) ? l.push(`CHECK (${r} IS NULL OR json_valid(${r}))`) : i === "mssql" && l.push(`CHECK (${r} IS NULL OR ISJSON(${r}) = 1)`);
|
|
313
|
+
}
|
|
314
|
+
return l.join(" ");
|
|
315
|
+
}, De = (e, n, r = null, i) => {
|
|
316
316
|
let a = /* @__PURE__ */ new Set();
|
|
317
|
-
for (let
|
|
318
|
-
for (let
|
|
319
|
-
let o =
|
|
320
|
-
let i = r.fields.map((
|
|
321
|
-
return ` CONSTRAINT ${
|
|
322
|
-
}), u = (
|
|
323
|
-
...s ? [` PRIMARY KEY (${o.map((
|
|
317
|
+
for (let t of e.appliedIndexes ?? []) for (let e of t.fields) typeof e == "string" && a.add(e);
|
|
318
|
+
for (let t of e.appliedUniques ?? []) for (let e of t.fields) typeof e == "string" && a.add(e);
|
|
319
|
+
let o = e.appliedPrimaryKey ?? [], s = o.length > 0, c = Object.entries(e.fields).map(([e, t]) => ` ${Ee(e, t, n, a, i?.has(e) ?? !1, s)}`).join(",\n"), l = (e.appliedUniques ?? []).map((r) => {
|
|
320
|
+
let i = r.fields.map((t) => we(t, e.fields, n)).join(", ");
|
|
321
|
+
return ` CONSTRAINT ${t(r.name, n)} UNIQUE (${i})`;
|
|
322
|
+
}), u = (e.appliedChecks ?? []).map((e) => ` CONSTRAINT ${t(e.name, n)} CHECK (${e.expr})`), d = [
|
|
323
|
+
...s ? [` PRIMARY KEY (${o.map((t) => we(t, e.fields, n)).join(", ")})`] : [],
|
|
324
324
|
...l,
|
|
325
325
|
...u
|
|
326
|
-
], f = d.length > 0 ? `${c},\n${d.join(",\n")}` : c, p =
|
|
326
|
+
], f = d.length > 0 ? `${c},\n${d.join(",\n")}` : c, p = Se(e.tableName, n, r);
|
|
327
327
|
if (n === "mssql") {
|
|
328
|
-
let
|
|
329
|
-
return `IF OBJECT_ID('${
|
|
328
|
+
let t = r === null ? e.tableName : `${r}.${e.tableName}`, n = `CREATE TABLE ${p} (\n${f}\n)`;
|
|
329
|
+
return `IF OBJECT_ID('${t.replace(/'/g, "''")}', 'U') IS NULL EXEC(N'${n.replace(/'/g, "''")}');`;
|
|
330
330
|
}
|
|
331
331
|
return [
|
|
332
332
|
`CREATE TABLE IF NOT EXISTS ${p} (`,
|
|
333
333
|
f,
|
|
334
334
|
");"
|
|
335
335
|
].join("\n");
|
|
336
|
-
},
|
|
337
|
-
let i = `${
|
|
338
|
-
return c.push(`CREATE VIRTUAL TABLE IF NOT EXISTS ${a(i)} USING fts5(${o}, row_id UNINDEXED, tokenize='porter');`), c.push(`CREATE TRIGGER IF NOT EXISTS ${a(`${i}_ai`)} AFTER INSERT ON ${a(
|
|
339
|
-
},
|
|
340
|
-
let
|
|
341
|
-
let
|
|
336
|
+
}, Oe = (e, n, r) => {
|
|
337
|
+
let i = `${e}_${n.name}_fts`, a = (e) => t(e, r), o = n.columns.map(a).join(", "), s = n.columns.map((e) => `new.${a(e)}`).join(", "), c = [];
|
|
338
|
+
return c.push(`CREATE VIRTUAL TABLE IF NOT EXISTS ${a(i)} USING fts5(${o}, row_id UNINDEXED, tokenize='porter');`), c.push(`CREATE TRIGGER IF NOT EXISTS ${a(`${i}_ai`)} AFTER INSERT ON ${a(e)} BEGIN\n INSERT INTO ${a(i)} (${o}, row_id) VALUES (${s}, new.${a("id")});\nEND;`), c.push(`CREATE TRIGGER IF NOT EXISTS ${a(`${i}_ad`)} AFTER DELETE ON ${a(e)} BEGIN\n DELETE FROM ${a(i)} WHERE row_id = old.${a("id")};\nEND;`), c.push(`CREATE TRIGGER IF NOT EXISTS ${a(`${i}_au`)} AFTER UPDATE ON ${a(e)} BEGIN\n DELETE FROM ${a(i)} WHERE row_id = old.${a("id")};\n INSERT INTO ${a(i)} (${o}, row_id) VALUES (${s}, new.${a("id")});\nEND;`), c;
|
|
339
|
+
}, ke = (e, n, r = null) => {
|
|
340
|
+
let i = [], a = /* @__PURE__ */ new Set(), o = (e) => e.join("\0"), s = (e, t, r, i) => {
|
|
341
|
+
let a = {
|
|
342
342
|
using: "",
|
|
343
343
|
withSuffix: "",
|
|
344
344
|
skip: !1
|
|
345
345
|
};
|
|
346
|
-
if (
|
|
347
|
-
if (
|
|
346
|
+
if (r === void 0 || r === "btree") return a;
|
|
347
|
+
if (r === "gist") return n === "postgres" ? {
|
|
348
348
|
using: " USING GIST",
|
|
349
349
|
withSuffix: "",
|
|
350
350
|
skip: !1
|
|
351
|
-
} : (
|
|
352
|
-
if (
|
|
351
|
+
} : (E(`[voltro:migrate] index '${e}' on '${t}': ${n} doesn't support GiST — falling back to btree. Spatial queries on this column will fall back to sequential scan.`), a);
|
|
352
|
+
if (r === "gin") return n === "postgres" ? {
|
|
353
353
|
using: " USING GIN",
|
|
354
354
|
withSuffix: "",
|
|
355
355
|
skip: !1
|
|
356
|
-
} :
|
|
356
|
+
} : n === "mysql" || n === "mariadb" ? (E(`[voltro:migrate] gin index '${e}' on '${t}': ${n} has no GIN access method — skipping (a btree on a JSON/array column can't serve containment/membership queries). Add a generated column for the specific path you query, then index that with .expressionIndex(...).`), {
|
|
357
357
|
using: "",
|
|
358
358
|
withSuffix: "",
|
|
359
359
|
skip: !0
|
|
360
|
-
}) : (
|
|
361
|
-
if (
|
|
360
|
+
}) : (E(`[voltro:migrate] gin index '${e}' on '${t}': ${n} has no GIN access method — falling back to btree. Containment/membership queries won't use this index efficiently.`), a);
|
|
361
|
+
if (r === "brin") return n === "postgres" ? {
|
|
362
362
|
using: " USING BRIN",
|
|
363
363
|
withSuffix: "",
|
|
364
364
|
skip: !1
|
|
365
|
-
} : (
|
|
366
|
-
if (
|
|
367
|
-
if (
|
|
368
|
-
let e =
|
|
365
|
+
} : (E(`[voltro:migrate] brin index '${e}' on '${t}': ${n} has no BRIN access method — falling back to btree (larger on disk, but the range scan still works).`), a);
|
|
366
|
+
if (r === "hnsw") {
|
|
367
|
+
if (n === "postgres") {
|
|
368
|
+
let e = i?.hnsw?.m ?? 16, t = i?.hnsw?.efConstruction ?? 64, n = i?.hnsw?.opclass ?? u(i?.hnsw?.distance);
|
|
369
369
|
return {
|
|
370
370
|
using: " USING hnsw",
|
|
371
|
-
withSuffix: ` WITH (m = ${e}, ef_construction = ${
|
|
372
|
-
opclass:
|
|
371
|
+
withSuffix: ` WITH (m = ${e}, ef_construction = ${t})`,
|
|
372
|
+
opclass: n,
|
|
373
373
|
skip: !1
|
|
374
374
|
};
|
|
375
375
|
}
|
|
376
|
-
return
|
|
376
|
+
return E(`[voltro:migrate] hnsw index '${e}' on '${t}': ${n} has no pgvector / HNSW access method — skipping. Nearest-neighbour queries on this column fall back to a sequential scan.`), {
|
|
377
377
|
using: "",
|
|
378
378
|
withSuffix: "",
|
|
379
379
|
skip: !0
|
|
380
380
|
};
|
|
381
381
|
}
|
|
382
|
-
return
|
|
383
|
-
},
|
|
384
|
-
let
|
|
385
|
-
if (typeof
|
|
386
|
-
if ("jsonPath" in
|
|
387
|
-
let { column:
|
|
388
|
-
return `(${
|
|
382
|
+
return a;
|
|
383
|
+
}, c = (t, r) => {
|
|
384
|
+
let i = t.map((t) => {
|
|
385
|
+
if (typeof t == "string") return we(t, e.fields, n);
|
|
386
|
+
if ("jsonPath" in t) {
|
|
387
|
+
let { column: e, path: r, numeric: i } = t.jsonPath;
|
|
388
|
+
return `(${f(e, r, n, { numeric: i })})`;
|
|
389
389
|
}
|
|
390
|
-
return `(${
|
|
390
|
+
return `(${t.expr})`;
|
|
391
391
|
});
|
|
392
|
-
return
|
|
393
|
-
},
|
|
394
|
-
let f =
|
|
392
|
+
return r !== void 0 && i.length === 1 ? `${i[0]} ${r}` : i.join(", ");
|
|
393
|
+
}, l = (e, i, a, o, l, u = "", d = !1) => {
|
|
394
|
+
let f = s(e, i, o, l);
|
|
395
395
|
if (f.skip) return "";
|
|
396
|
-
let p =
|
|
397
|
-
return
|
|
396
|
+
let p = c(a, f.opclass), m = `${f.withSuffix}${u}`, h = Se(i, n, r), g = d ? "UNIQUE " : "";
|
|
397
|
+
return n === "mssql" ? `IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = '${e}') CREATE ${g}INDEX ${t(e, n)} ON ${h}${f.using} (${p})${m};` : n === "mysql" ? `CREATE ${g}INDEX ${t(e, n)} ON ${h}${f.using} (${p})${m};` : `CREATE ${g}INDEX IF NOT EXISTS ${t(e, n)} ON ${h}${f.using} (${p})${m};`;
|
|
398
398
|
};
|
|
399
|
-
for (let
|
|
400
|
-
let
|
|
401
|
-
let
|
|
402
|
-
return `setweight(to_tsvector('${
|
|
399
|
+
for (let r of e.appliedFullText ?? []) if (n === "postgres") {
|
|
400
|
+
let a = r.config ?? "english", o = `${r.name}_tsv`, s = r.columns.map((e) => {
|
|
401
|
+
let i = r.weights?.[e] ?? "A";
|
|
402
|
+
return `setweight(to_tsvector('${a}', coalesce(${t(e, n)}, '')), '${i}')`;
|
|
403
403
|
}).join(" || ");
|
|
404
|
-
|
|
405
|
-
} else if (
|
|
406
|
-
let
|
|
407
|
-
|
|
408
|
-
} else if (
|
|
409
|
-
else
|
|
410
|
-
for (let
|
|
411
|
-
if ((
|
|
412
|
-
|
|
404
|
+
i.push(`ALTER TABLE ${t(e.tableName, n)} ADD COLUMN IF NOT EXISTS ${t(o, n)} tsvector GENERATED ALWAYS AS (${s}) STORED;`), i.push(`CREATE INDEX IF NOT EXISTS ${t(r.name, n)} ON ${t(e.tableName, n)} USING GIN (${t(o, n)});`);
|
|
405
|
+
} else if (n === "mysql" || n === "mariadb") {
|
|
406
|
+
let a = r.columns.map((e) => t(e, n)).join(", ");
|
|
407
|
+
i.push(`CREATE FULLTEXT INDEX ${t(r.name, n)} ON ${t(e.tableName, n)} (${a});`);
|
|
408
|
+
} else if (n === "turso") throw Error(`@voltro/sql-turso: full-text index '${r.name}' on '${e.tableName}' is not supported — Turso's MVCC mode has no FTS5 virtual tables. Drop .fullTextIndex(), or use the 'sqlite' / 'postgres' dialect for this table.`);
|
|
409
|
+
else n === "sqlite" ? i.push(...Oe(e.tableName, r, n)) : E(`[voltro:migrate] full-text index '${r.name}' on '${e.tableName}': ${n} uses a ranked LIKE search at query time (no native FTS catalog required).`);
|
|
410
|
+
for (let t of e.appliedIndexes) {
|
|
411
|
+
if ((n === "mariadb" || n === "mssql") && t.fields.some((e) => typeof e != "string" && "jsonPath" in e)) {
|
|
412
|
+
E(`[voltro:migrate] json-path index '${t.name}' on '${e.tableName}': ${n} cannot index a JSON expression directly — skipping. For an indexed JSON path on ${n}, add a generated column for the path (text().generatedAs(...) / .stored()) and .index([...]) it, then filter on that column instead of jsonField(...).`);
|
|
413
413
|
continue;
|
|
414
414
|
}
|
|
415
|
-
if (
|
|
416
|
-
let
|
|
417
|
-
|
|
418
|
-
let
|
|
419
|
-
|
|
420
|
-
}
|
|
421
|
-
for (let [
|
|
422
|
-
if (
|
|
423
|
-
let r = `${
|
|
424
|
-
|
|
425
|
-
}
|
|
426
|
-
return
|
|
427
|
-
},
|
|
415
|
+
if (t.unique && t.where !== void 0 && t.where.length > 0 && (n === "mysql" || n === "mariadb")) throw Error(`@voltro/migrate: uniqueActive index '${t.name}' on '${e.tableName}' cannot be expressed on ${n} — it has no partial (WHERE) index support, and a full UNIQUE index would forbid re-creating a soft-deleted row. Use a generated STORED column (text().generatedAs("CASE WHEN \`deletedAt\` IS NULL THEN <key> ELSE NULL END", { stored: true })) + .unique([...]) on ${n}, or move this table to postgres/sqlite/mssql.`);
|
|
416
|
+
let r = "";
|
|
417
|
+
t.where !== void 0 && t.where.length > 0 && (n === "mysql" || n === "mariadb" ? E(`[voltro:migrate] partial index '${t.name}' on '${e.tableName}': ${n} does not support WHERE clauses on indexes — emitting a full index`) : r = ` WHERE ${t.where}`);
|
|
418
|
+
let s = l(t.name, e.tableName, t.fields, t.kind, t.kindOptions, r, t.unique === !0);
|
|
419
|
+
s !== "" && i.push(s), t.fields.every((e) => typeof e == "string") && a.add(o(t.fields));
|
|
420
|
+
}
|
|
421
|
+
for (let [t, n] of Object.entries(e.fields)) {
|
|
422
|
+
if (n.type !== "reference" || n.fkAutoIndex === !1 || a.has(o([t]))) continue;
|
|
423
|
+
let r = `${e.tableName}_${t}_idx`;
|
|
424
|
+
i.push(l(r, e.tableName, [t])), a.add(o([t]));
|
|
425
|
+
}
|
|
426
|
+
return i.join("\n");
|
|
427
|
+
}, j = "framework_changes", Ae = (e, t, n = j) => {
|
|
428
|
+
if (t !== "postgres") return [];
|
|
429
|
+
let r = e.filter((e) => e.isReactive !== !1);
|
|
430
|
+
return r.length === 0 ? [] : [Me(n), ...r.map((e) => Ne(e, n))];
|
|
431
|
+
}, je = (e) => e === "framework_changes" ? "framework_notify_change" : `framework_notify_change_${e}`, Me = (e) => `
|
|
432
|
+
CREATE OR REPLACE FUNCTION ${je(e)}() RETURNS trigger AS $$
|
|
433
|
+
DECLARE
|
|
434
|
+
payload TEXT;
|
|
435
|
+
BEGIN
|
|
436
|
+
payload := json_build_object(
|
|
437
|
+
'table', TG_TABLE_NAME,
|
|
438
|
+
-- The schema the write landed in. Under namespace isolation this is
|
|
439
|
+
-- the tenant schema (tenant_<id>); the CDC consumer uses it so a
|
|
440
|
+
-- write in tenant A namespace does not spuriously wake tenant B
|
|
441
|
+
-- subscriptions on the same-named table. public/absent = shared.
|
|
442
|
+
'schema', TG_TABLE_SCHEMA,
|
|
443
|
+
'op', lower(TG_OP),
|
|
444
|
+
'old', CASE WHEN TG_OP = 'INSERT' THEN NULL ELSE row_to_json(OLD) END,
|
|
445
|
+
'new', CASE WHEN TG_OP = 'DELETE' THEN NULL ELSE row_to_json(NEW) END
|
|
446
|
+
)::text;
|
|
447
|
+
-- pg_notify hard-caps the payload at 8000 bytes; a wide reactive row
|
|
448
|
+
-- (big text / json column) would otherwise abort the write itself.
|
|
449
|
+
-- Fall back to a body-less notice — the CDC consumer treats absent
|
|
450
|
+
-- old/new as "all columns changed" and re-queries, so correctness is
|
|
451
|
+
-- preserved; only the delta-diff fast path is skipped for that write.
|
|
452
|
+
IF octet_length(payload) >= 7999 THEN
|
|
453
|
+
payload := json_build_object(
|
|
454
|
+
'table', TG_TABLE_NAME,
|
|
455
|
+
'schema', TG_TABLE_SCHEMA,
|
|
456
|
+
'op', lower(TG_OP),
|
|
457
|
+
'old', NULL,
|
|
458
|
+
'new', NULL,
|
|
459
|
+
'oversized', true
|
|
460
|
+
)::text;
|
|
461
|
+
END IF;
|
|
462
|
+
PERFORM pg_notify('${e}', payload);
|
|
463
|
+
RETURN COALESCE(NEW, OLD);
|
|
464
|
+
END;
|
|
465
|
+
$$ LANGUAGE plpgsql;
|
|
466
|
+
`.trim(), Ne = (e, t) => {
|
|
467
|
+
let n = t === "framework_changes" ? `framework_changes_${e.tableName}` : `framework_changes_${t}_${e.tableName}`;
|
|
468
|
+
return `
|
|
428
469
|
ALTER TABLE "${e.tableName}" REPLICA IDENTITY FULL;
|
|
429
|
-
DROP TRIGGER IF EXISTS
|
|
430
|
-
CREATE TRIGGER
|
|
470
|
+
DROP TRIGGER IF EXISTS ${n} ON "${e.tableName}";
|
|
471
|
+
CREATE TRIGGER ${n}
|
|
431
472
|
AFTER INSERT OR UPDATE OR DELETE ON "${e.tableName}"
|
|
432
|
-
FOR EACH ROW EXECUTE FUNCTION
|
|
433
|
-
`.trim()
|
|
434
|
-
|
|
473
|
+
FOR EACH ROW EXECUTE FUNCTION ${je(t)}();
|
|
474
|
+
`.trim();
|
|
475
|
+
}, Pe = (e) => e.some((e) => Object.values(e.fields).some((e) => e.type === "vector")), Fe = (e) => e.some((e) => Object.values(e.fields).some((e) => e.spatial !== void 0)), Ie = (e) => {
|
|
476
|
+
let t = new Map(e.map((e) => [e.tableName, e])), n = re(e.map((e) => e.tableName), (e) => {
|
|
435
477
|
let n = t.get(e);
|
|
436
478
|
return n ? Object.entries(n.fields).flatMap(([e, n]) => {
|
|
437
479
|
if (n.type !== "reference" || !n.references) return [];
|
|
@@ -442,7 +484,7 @@ FOR EACH ROW EXECUTE FUNCTION framework_notify_change();
|
|
|
442
484
|
}] : [];
|
|
443
485
|
}) : [];
|
|
444
486
|
}), r = [];
|
|
445
|
-
for (let t of e) for (let [e, i] of Object.entries(t.fields)) i.type !== "reference" || !i.references || n.has(
|
|
487
|
+
for (let t of e) for (let [e, i] of Object.entries(t.fields)) i.type !== "reference" || !i.references || n.has(C(t.tableName, e)) && r.push({
|
|
446
488
|
table: t.tableName,
|
|
447
489
|
column: e,
|
|
448
490
|
targetTable: i.references().tableName,
|
|
@@ -453,7 +495,7 @@ FOR EACH ROW EXECUTE FUNCTION framework_notify_change();
|
|
|
453
495
|
if (!(i.has(e.tableName) || a.has(e.tableName))) {
|
|
454
496
|
a.add(e.tableName);
|
|
455
497
|
for (let [r, i] of Object.entries(e.fields)) {
|
|
456
|
-
if (i.type !== "reference" || !i.references || n.has(
|
|
498
|
+
if (i.type !== "reference" || !i.references || n.has(C(e.tableName, r))) continue;
|
|
457
499
|
let a = i.references();
|
|
458
500
|
if (a.tableName === e.tableName) continue;
|
|
459
501
|
let o = t.get(a.tableName);
|
|
@@ -467,30 +509,30 @@ FOR EACH ROW EXECUTE FUNCTION framework_notify_change();
|
|
|
467
509
|
sorted: o,
|
|
468
510
|
cyclic: r
|
|
469
511
|
};
|
|
470
|
-
},
|
|
471
|
-
let i = (
|
|
512
|
+
}, Le = (e, n, r = null) => {
|
|
513
|
+
let i = (e) => r ? `${t(r, n)}.${t(e, n)}` : t(e, n), a = `${e.table}_${e.column}_fkey`, o = A(e.onDelete, n), s = A(e.onUpdate, n), c = ` ON DELETE ${o}${s === "NO ACTION" ? "" : ` ON UPDATE ${s}`}`, l = (r) => `ADD CONSTRAINT ${t(a, n)} FOREIGN KEY ${r ? "IF NOT EXISTS " : ""}(${t(e.column, n)}) REFERENCES ${i(e.targetTable)} (${t("id", n)})${c}`;
|
|
472
514
|
switch (n) {
|
|
473
515
|
case "postgres": return [
|
|
474
516
|
"DO $$ BEGIN",
|
|
475
517
|
" IF NOT EXISTS (SELECT 1 FROM pg_constraint",
|
|
476
|
-
` WHERE conname = '${a}' AND conrelid = '${r ? `${r}.` : ""}${
|
|
477
|
-
` ALTER TABLE ${i(
|
|
518
|
+
` WHERE conname = '${a}' AND conrelid = '${r ? `${r}.` : ""}${e.table}'::regclass) THEN`,
|
|
519
|
+
` ALTER TABLE ${i(e.table)} ${l(!1)};`,
|
|
478
520
|
" END IF;",
|
|
479
521
|
"END $$;"
|
|
480
522
|
].join("\n");
|
|
481
|
-
case "mssql": return `IF NOT EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = '${a}') ALTER TABLE ${i(
|
|
482
|
-
case "mariadb": return `ALTER TABLE ${i(
|
|
483
|
-
default: return `ALTER TABLE ${i(
|
|
523
|
+
case "mssql": return `IF NOT EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = '${a}') ALTER TABLE ${i(e.table)} ${l(!1)};`;
|
|
524
|
+
case "mariadb": return `ALTER TABLE ${i(e.table)} ${l(!0)};`;
|
|
525
|
+
default: return `ALTER TABLE ${i(e.table)} ${l(!1)};`;
|
|
484
526
|
}
|
|
485
|
-
},
|
|
486
|
-
let
|
|
487
|
-
if (
|
|
488
|
-
for (let
|
|
489
|
-
let
|
|
490
|
-
|
|
527
|
+
}, Re = (t, n) => {
|
|
528
|
+
let r = /* @__PURE__ */ new Map();
|
|
529
|
+
if (e(n)) return r;
|
|
530
|
+
for (let e of t) {
|
|
531
|
+
let t = r.get(e.table) ?? /* @__PURE__ */ new Set();
|
|
532
|
+
t.add(e.column), r.set(e.table, t);
|
|
491
533
|
}
|
|
492
|
-
return
|
|
493
|
-
},
|
|
534
|
+
return r;
|
|
535
|
+
}, ze = (e, t) => {
|
|
494
536
|
if (t !== "postgres") return "";
|
|
495
537
|
let n = /* @__PURE__ */ new Map();
|
|
496
538
|
for (let t of e) for (let e of Object.values(t.fields)) {
|
|
@@ -506,7 +548,7 @@ FOR EACH ROW EXECUTE FUNCTION framework_notify_change();
|
|
|
506
548
|
r.push(`DO $$ BEGIN\n IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = '${e.replace(/'/g, "''")}') THEN\n CREATE TYPE "${e}" AS ENUM (${n});\n END IF;\nEND $$;`);
|
|
507
549
|
}
|
|
508
550
|
return r.join("\n\n");
|
|
509
|
-
},
|
|
551
|
+
}, Be = (e, t) => {
|
|
510
552
|
if (t !== "postgres") return "";
|
|
511
553
|
let n = /* @__PURE__ */ new Set();
|
|
512
554
|
for (let t of e) for (let e of Object.values(t.fields)) e.type === "enum" && e.enumName && n.add(e.enumName);
|
|
@@ -520,80 +562,71 @@ BEGIN
|
|
|
520
562
|
SELECT oid INTO type_oid FROM pg_type WHERE typname = '${i}';\n IF type_oid IS NOT NULL\n AND EXISTS (SELECT 1 FROM pg_enum WHERE enumtypid = type_oid AND enumlabel = '${a}')\n AND NOT EXISTS (SELECT 1 FROM pg_enum WHERE enumtypid = type_oid AND enumlabel = '${o}') THEN\n IF current_setting('server_version_num')::int >= 140000 THEN\n EXECUTE format('ALTER TYPE %I RENAME VALUE %L TO %L', '${i}', '${a}', '${o}');\n ELSE\n UPDATE pg_enum SET enumlabel = '${o}' WHERE enumtypid = type_oid AND enumlabel = '${a}';\n END IF;\n END IF;\nEND $$;`);
|
|
521
563
|
}
|
|
522
564
|
return r.join("\n\n");
|
|
523
|
-
},
|
|
524
|
-
let t = [],
|
|
525
|
-
for (let i of e)
|
|
565
|
+
}, Ve = (e) => {
|
|
566
|
+
let t = [], n = [];
|
|
567
|
+
for (let i of e) r(i) ? n.push(i) : t.push(i);
|
|
526
568
|
return {
|
|
527
569
|
tables: t,
|
|
528
|
-
views:
|
|
570
|
+
views: n
|
|
529
571
|
};
|
|
530
|
-
},
|
|
531
|
-
let { tables:
|
|
532
|
-
|
|
533
|
-
let
|
|
534
|
-
|
|
535
|
-
let
|
|
536
|
-
|
|
537
|
-
for (let e of
|
|
538
|
-
if (!
|
|
539
|
-
for (let e of
|
|
540
|
-
let
|
|
541
|
-
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
for (let e of i) c.push(r(e, t));
|
|
549
|
-
return c.join("\n\n") + "\n";
|
|
550
|
-
}, ze = (t, n, r) => {
|
|
551
|
-
let i = e(t, n);
|
|
572
|
+
}, M = (t, n, r = j) => {
|
|
573
|
+
let { tables: a, views: o } = Ve(t), { sorted: s, cyclic: c } = Ie(a.map((e) => ne(e, n))), l = Re(c, n), u = [];
|
|
574
|
+
n === "postgres" && Pe(s) && u.push("CREATE EXTENSION IF NOT EXISTS vector;"), n === "postgres" && Fe(s) && u.push("CREATE EXTENSION IF NOT EXISTS postgis;");
|
|
575
|
+
let d = Be(s, n);
|
|
576
|
+
d && u.push(d);
|
|
577
|
+
let f = ze(s, n);
|
|
578
|
+
f && u.push(f);
|
|
579
|
+
for (let e of s) u.push(De(e, n, null, l.get(e.tableName)));
|
|
580
|
+
if (!e(n)) for (let e of c) u.push(Le(e, n));
|
|
581
|
+
for (let e of s) {
|
|
582
|
+
let t = ke(e, n);
|
|
583
|
+
t && u.push(t);
|
|
584
|
+
}
|
|
585
|
+
u.push(...Ae(s, n, r));
|
|
586
|
+
for (let e of o) u.push(i(e, n));
|
|
587
|
+
return u.join("\n\n") + "\n";
|
|
588
|
+
}, He = (e, n, r) => {
|
|
589
|
+
let i = t(e, n);
|
|
552
590
|
switch (n) {
|
|
553
591
|
case "postgres": return `CREATE SCHEMA IF NOT EXISTS ${i};`;
|
|
554
592
|
case "mysql":
|
|
555
593
|
case "mariadb": return `CREATE DATABASE IF NOT EXISTS ${i};`;
|
|
556
|
-
case "mssql": return `IF SCHEMA_ID('${
|
|
594
|
+
case "mssql": return `IF SCHEMA_ID('${e.replace(/'/g, "''")}') IS NULL EXEC(N'CREATE SCHEMA ${i.replace(/'/g, "''")}');`;
|
|
557
595
|
case "turso":
|
|
558
|
-
case "sqlite": return `ATTACH DATABASE '${(r ?? `${
|
|
559
|
-
}
|
|
560
|
-
},
|
|
561
|
-
let
|
|
562
|
-
if (
|
|
563
|
-
if (
|
|
564
|
-
let { sorted:
|
|
565
|
-
for (let e of
|
|
566
|
-
if (!
|
|
567
|
-
for (let e of
|
|
568
|
-
let t =
|
|
596
|
+
case "sqlite": return `ATTACH DATABASE '${(r ?? `${e}.db`).replace(/'/g, "''")}' AS ${i};`;
|
|
597
|
+
}
|
|
598
|
+
}, Ue = (n, r, i, a) => {
|
|
599
|
+
let o = He(i, r, a), s = t(i, r);
|
|
600
|
+
if (r === "postgres") return `${o}\n\nSET search_path TO ${s};\n\n${M(n, r)}`;
|
|
601
|
+
if (r === "mysql" || r === "mariadb") return `${o}\n\nUSE ${s};\n\n${M(n, r)}`;
|
|
602
|
+
let { sorted: c, cyclic: l } = Ie(n), u = Re(l, r), d = [o];
|
|
603
|
+
for (let e of c) d.push(De(e, r, i, u.get(e.tableName)));
|
|
604
|
+
if (!e(r)) for (let e of l) d.push(Le(e, r, i));
|
|
605
|
+
for (let e of c) {
|
|
606
|
+
let t = ke(e, r, i);
|
|
569
607
|
t && d.push(t);
|
|
570
608
|
}
|
|
571
609
|
return d.join("\n\n") + "\n";
|
|
572
|
-
},
|
|
573
|
-
let { sorted:
|
|
574
|
-
|
|
575
|
-
let
|
|
576
|
-
|
|
577
|
-
for (let e of
|
|
578
|
-
for (let e of
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
return a.join("\n\n") + "\n";
|
|
585
|
-
}, He = (e, t = "postgres") => d.gen(function* () {
|
|
586
|
-
let n = yield* p.SqlClient;
|
|
587
|
-
yield* _e(n, Ge(n, A(e, t), t)).pipe(d.orDie);
|
|
588
|
-
}), Ue = (e, t) => {
|
|
610
|
+
}, We = (e, t, n = j) => {
|
|
611
|
+
let { sorted: r, cyclic: i } = Ie(e), a = Re(i, t), o = [], s = Be(r, t);
|
|
612
|
+
s && o.push(s);
|
|
613
|
+
let c = ze(r, t);
|
|
614
|
+
c && o.push(c);
|
|
615
|
+
for (let e of r) o.push(De(e, t, null, a.get(e.tableName)));
|
|
616
|
+
for (let e of i) o.push(Le(e, t));
|
|
617
|
+
return o.push(...Ae(r, t, n)), o.join("\n\n") + "\n";
|
|
618
|
+
}, Ge = (e, t = "postgres", n = j) => p.gen(function* () {
|
|
619
|
+
let r = yield* h.SqlClient;
|
|
620
|
+
yield* ve(r, Je(r, M(e, t, n), t)).pipe(p.orDie);
|
|
621
|
+
}), Ke = (e, t) => {
|
|
589
622
|
let n = t.cause ?? {};
|
|
590
623
|
return (n.errno === 1061 || n.code === "ER_DUP_KEYNAME") && /^\s*CREATE\s+(UNIQUE\s+|FULLTEXT\s+)?INDEX\b/i.test(e);
|
|
591
|
-
},
|
|
592
|
-
let t = yield*
|
|
593
|
-
for (let n of
|
|
594
|
-
|
|
595
|
-
})),
|
|
596
|
-
}),
|
|
624
|
+
}, qe = (e) => p.gen(function* () {
|
|
625
|
+
let t = yield* h.SqlClient;
|
|
626
|
+
for (let n of Ye(e)) process.env.VOLTRO_MIGRATE_DEBUG === "1" && ye.debug(`EXEC: ${n.replace(/\s+/g, " ").slice(0, 180)}`), yield* t.unsafe(n).pipe(p.catchIf((e) => Ke(n, e), () => p.void), p.tapError((e) => p.sync(() => {
|
|
627
|
+
ye.error(b("migrate: statement failed", n, e, 200));
|
|
628
|
+
})), p.orDieWith((e) => Error(b("migrate: statement failed", n, e, 400).trim())));
|
|
629
|
+
}), Je = (e, t, n) => (n === "turso" ? qe(t) : e.withTransaction(qe(t))).pipe(p.orDie), Ye = (e) => {
|
|
597
630
|
let t = [], n = "", r = !1;
|
|
598
631
|
for (let i = 0; i < e.length; i++) {
|
|
599
632
|
if (e.slice(i, i + 2) === "$$") {
|
|
@@ -608,40 +641,40 @@ BEGIN
|
|
|
608
641
|
}
|
|
609
642
|
let i = n.trim();
|
|
610
643
|
return i.length > 0 && t.push(i), t;
|
|
611
|
-
}, qe = async (e, t, n = "postgres") => {
|
|
612
|
-
await d.runPromise(He(e, n).pipe(d.provide(t)));
|
|
613
|
-
}, Je = (e, t, n = "postgres") => d.gen(function* () {
|
|
614
|
-
let r = Be(e, n, t), i = yield* p.SqlClient;
|
|
615
|
-
yield* _e(i, Ge(i, r, n)).pipe(d.orDie);
|
|
616
|
-
}), Ye = async (e, t, n, r = "postgres") => {
|
|
617
|
-
await d.runPromise(Je(e, t, r).pipe(d.provide(n)));
|
|
618
644
|
}, Xe = async (e, t, n = "postgres") => {
|
|
619
|
-
await
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
},
|
|
645
|
+
await p.runPromise(Ge(e, n).pipe(p.provide(t)));
|
|
646
|
+
}, Ze = (e, t, n = "postgres") => p.gen(function* () {
|
|
647
|
+
let r = Ue(e, n, t), i = yield* h.SqlClient;
|
|
648
|
+
yield* ve(i, Je(i, r, n)).pipe(p.orDie);
|
|
649
|
+
}), Qe = async (e, t, n, r = "postgres") => {
|
|
650
|
+
await p.runPromise(Ze(e, t, r).pipe(p.provide(n)));
|
|
651
|
+
}, $e = async (e, t, n = "postgres") => {
|
|
652
|
+
await p.runPromise(p.gen(function* () {
|
|
653
|
+
let t = We(e, n), r = yield* h.SqlClient;
|
|
654
|
+
yield* ve(r, Je(r, t, n)).pipe(p.orDie);
|
|
655
|
+
}).pipe(p.provide(t)));
|
|
656
|
+
}, et = (e) => {
|
|
624
657
|
if (e == null) return e;
|
|
625
|
-
if (Array.isArray(e)) return e.map(
|
|
658
|
+
if (Array.isArray(e)) return e.map(et);
|
|
626
659
|
if (typeof e != "object") return e;
|
|
627
660
|
let t = {};
|
|
628
661
|
for (let n of Object.keys(e).sort()) {
|
|
629
662
|
let r = e[n];
|
|
630
|
-
r !== void 0 && (t[n] =
|
|
663
|
+
r !== void 0 && (t[n] = et(r));
|
|
631
664
|
}
|
|
632
665
|
return t;
|
|
633
|
-
},
|
|
666
|
+
}, tt = (e) => {
|
|
634
667
|
if (!e.idScheme && !e.sensitive && !e.safe) return e;
|
|
635
668
|
let { idScheme: t, sensitive: n, safe: r, ...i } = e;
|
|
636
669
|
return i;
|
|
637
|
-
},
|
|
670
|
+
}, nt = (e) => [...e].map(tt).sort((e, t) => e.name.localeCompare(t.name)), rt = (e) => [...e].sort((e, t) => e.name.localeCompare(t.name)), it = (e) => [...e].map((e) => ({
|
|
638
671
|
name: e.name,
|
|
639
|
-
columns:
|
|
640
|
-
indexes:
|
|
641
|
-
})).sort((e, t) => e.name.localeCompare(t.name)),
|
|
642
|
-
let t = { tables:
|
|
643
|
-
return
|
|
644
|
-
},
|
|
672
|
+
columns: nt(e.columns),
|
|
673
|
+
indexes: rt(e.indexes)
|
|
674
|
+
})).sort((e, t) => e.name.localeCompare(t.name)), N = (e) => {
|
|
675
|
+
let t = { tables: it(e.tables) }, n = JSON.stringify(et(t));
|
|
676
|
+
return g("sha256").update(n).digest("hex");
|
|
677
|
+
}, P = (e) => e.slice(0, 16), at = (e, t) => {
|
|
645
678
|
let n = t.references ? {
|
|
646
679
|
table: t.references().tableName,
|
|
647
680
|
column: "id",
|
|
@@ -680,7 +713,7 @@ BEGIN
|
|
|
680
713
|
...t.enumValues === void 0 ? {} : { enumValues: t.enumValues },
|
|
681
714
|
...t.spatial === void 0 ? {} : { spatial: t.spatial }
|
|
682
715
|
};
|
|
683
|
-
},
|
|
716
|
+
}, ot = (e) => {
|
|
684
717
|
let t = [], n = /* @__PURE__ */ new Set(), r = (e) => {
|
|
685
718
|
n.has(e.name) || (n.add(e.name), t.push(e));
|
|
686
719
|
}, i = e.appliedPrimaryKey ?? [];
|
|
@@ -720,7 +753,7 @@ BEGIN
|
|
|
720
753
|
let i = n;
|
|
721
754
|
if (i.dropped || !i.references || i.fkAutoIndex === !1 || s.has(t)) continue;
|
|
722
755
|
let a = `${e.tableName}_${t}_idx`;
|
|
723
|
-
|
|
756
|
+
l(e.tableName, a, !0, [t]), r({
|
|
724
757
|
name: a,
|
|
725
758
|
table: e.tableName,
|
|
726
759
|
columns: [t],
|
|
@@ -728,11 +761,11 @@ BEGIN
|
|
|
728
761
|
});
|
|
729
762
|
}
|
|
730
763
|
return t;
|
|
731
|
-
},
|
|
764
|
+
}, st = (e) => {
|
|
732
765
|
let t = new Set((e.appliedUniques ?? []).filter((e) => e.fields.length === 1).map((e) => e.fields[0])), n = [];
|
|
733
766
|
for (let [r, i] of Object.entries(e.fields)) {
|
|
734
767
|
if (i.dropped) continue;
|
|
735
|
-
let e =
|
|
768
|
+
let e = at(r, i);
|
|
736
769
|
n.push(t.has(r) ? {
|
|
737
770
|
...e,
|
|
738
771
|
unique: !0
|
|
@@ -742,21 +775,21 @@ BEGIN
|
|
|
742
775
|
return {
|
|
743
776
|
name: e.tableName,
|
|
744
777
|
columns: n,
|
|
745
|
-
indexes:
|
|
778
|
+
indexes: ot(e),
|
|
746
779
|
...r.length > 0 ? { primaryKey: [...r] } : {},
|
|
747
780
|
...i === void 0 ? {} : { renamedFrom: i }
|
|
748
781
|
};
|
|
749
|
-
},
|
|
782
|
+
}, ct = (e) => {
|
|
750
783
|
let t = /* @__PURE__ */ new Map();
|
|
751
784
|
for (let n of e) for (let e of n.indexes) {
|
|
752
785
|
let r = t.get(e.name);
|
|
753
786
|
if (r !== void 0 && r !== n.name) throw Error(`duplicate index name '${e.name}' across tables '${r}' and '${n.name}'. Index names must be unique per SCHEMA, not just per table — the DB creates only one, so the other never applies and 'db plan' re-emits it forever. Give each an explicit, table-prefixed name (e.g. '${n.name}_${e.name}').`);
|
|
754
787
|
t.set(e.name, n.name);
|
|
755
788
|
}
|
|
756
|
-
},
|
|
757
|
-
let n = e.map((e) =>
|
|
758
|
-
return
|
|
759
|
-
},
|
|
789
|
+
}, F = (e, t) => {
|
|
790
|
+
let n = e.map((e) => st(ne(e, t)));
|
|
791
|
+
return ct(n), { tables: n };
|
|
792
|
+
}, lt = "framework_changes_", ut = (e, t) => p.gen(function* () {
|
|
760
793
|
let n = t.filter((e) => !e.tableName.startsWith("_voltro_"));
|
|
761
794
|
if (n.length === 0) return {
|
|
762
795
|
missing: [],
|
|
@@ -769,17 +802,17 @@ BEGIN
|
|
|
769
802
|
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
770
803
|
WHERE NOT t.tgisinternal
|
|
771
804
|
AND n.nspname = current_schema()
|
|
772
|
-
AND t.tgname LIKE ${`${
|
|
805
|
+
AND t.tgname LIKE ${`${lt}%`}
|
|
773
806
|
`, i = new Set(r.map((e) => e.tgname)), a = [], o = [];
|
|
774
807
|
for (let e of n) {
|
|
775
|
-
let t = i.has(`${
|
|
808
|
+
let t = i.has(`${lt}${e.tableName}`), n = e.isReactive !== !1;
|
|
776
809
|
n && !t && a.push(e.tableName), !n && t && o.push(e.tableName);
|
|
777
810
|
}
|
|
778
811
|
return {
|
|
779
812
|
missing: a.sort(),
|
|
780
813
|
stale: o.sort()
|
|
781
814
|
};
|
|
782
|
-
}),
|
|
815
|
+
}), dt = (e) => {
|
|
783
816
|
let t = [];
|
|
784
817
|
if (e.missing.length > 0) {
|
|
785
818
|
let n = e.missing.slice(0, 8).join(", "), r = e.missing.length > 8 ? `, … (+${e.missing.length - 8})` : "";
|
|
@@ -790,19 +823,19 @@ BEGIN
|
|
|
790
823
|
t.push(`${e.stale.length} .nonReactive() table(s) still carry a change trigger — ${n}. They keep paying REPLICA IDENTITY FULL and a NOTIFY on every write for a subscription nobody receives. \`voltro db apply\` removes them.`);
|
|
791
824
|
}
|
|
792
825
|
return t.length > 0 ? t.join("\n") : void 0;
|
|
793
|
-
},
|
|
826
|
+
}, ft = {
|
|
794
827
|
postgres: !0,
|
|
795
828
|
mysql: !0,
|
|
796
829
|
mariadb: !0,
|
|
797
830
|
mssql: !0,
|
|
798
831
|
sqlite: !0
|
|
799
|
-
},
|
|
832
|
+
}, pt = {
|
|
800
833
|
postgres: !0,
|
|
801
834
|
mysql: !1,
|
|
802
835
|
mariadb: !1,
|
|
803
836
|
mssql: !0,
|
|
804
837
|
sqlite: !0
|
|
805
|
-
},
|
|
838
|
+
}, mt = (e) => {
|
|
806
839
|
switch (e) {
|
|
807
840
|
case "add-column":
|
|
808
841
|
case "drop-column":
|
|
@@ -812,43 +845,43 @@ BEGIN
|
|
|
812
845
|
case "add-foreign-key":
|
|
813
846
|
case "add-unique":
|
|
814
847
|
case "add-unique-composite":
|
|
815
|
-
case "add-check": return
|
|
816
|
-
default: return
|
|
848
|
+
case "add-check": return pt;
|
|
849
|
+
default: return ft;
|
|
817
850
|
}
|
|
818
|
-
},
|
|
851
|
+
}, ht = (e, t) => {
|
|
819
852
|
let n = new Map(t.tables.map((e) => [e.name, e]));
|
|
820
853
|
return new Map(e.map((e) => [e.tableName, {
|
|
821
854
|
table: e,
|
|
822
855
|
snapshot: n.get(e.tableName),
|
|
823
856
|
columnDefs: e.fields
|
|
824
857
|
}]));
|
|
825
|
-
},
|
|
858
|
+
}, I = (e) => {
|
|
826
859
|
if (typeof e != "object" || !e) return JSON.stringify(e);
|
|
827
|
-
if (Array.isArray(e)) return `[${e.map(
|
|
860
|
+
if (Array.isArray(e)) return `[${e.map(I).join(",")}]`;
|
|
828
861
|
let t = e;
|
|
829
|
-
return `{${Object.keys(t).sort().map((e) => `${JSON.stringify(e)}:${
|
|
830
|
-
},
|
|
862
|
+
return `{${Object.keys(t).sort().map((e) => `${JSON.stringify(e)}:${I(t[e])}`).join(",")}}`;
|
|
863
|
+
}, gt = (e) => {
|
|
831
864
|
if (!e.hasDefault || e.defaultValue === void 0 || e.defaultValue === null) return null;
|
|
832
865
|
let t = e.defaultValue;
|
|
833
866
|
if (typeof t == "boolean") return t ? "__BOOL_TRUE__" : "__BOOL_FALSE__";
|
|
834
867
|
if (typeof t == "number") return String(t);
|
|
835
|
-
if (typeof t != "string") return
|
|
868
|
+
if (typeof t != "string") return I(t);
|
|
836
869
|
let n = t.trim(), r = n.toLowerCase().replace(/\s+/g, "");
|
|
837
870
|
if (r === "now" || r === "now()" || /^current_timestamp(\(\d*\))?$/.test(r) || r === "sysutcdatetime()" || r === "sysdatetime()" || r === "getutcdate()" || r === "getdate()") return "__NOW__";
|
|
838
871
|
let i = n.replace(/::[a-zA-Z0-9_ "]+$/, "").trim(), a = /^'(.*)'$/.test(i) ? i.slice(1, -1).replace(/''/g, "'") : i, o = /^cast\(\s*(?:_\w+\s*)?\\?'(.*?)\\?'\s+as\s+json\s*\)$/i.exec(a);
|
|
839
872
|
if (o) try {
|
|
840
|
-
return
|
|
873
|
+
return I(JSON.parse(o[1]));
|
|
841
874
|
} catch {}
|
|
842
875
|
if (a.startsWith("{") || a.startsWith("[")) try {
|
|
843
|
-
return
|
|
876
|
+
return I(JSON.parse(a));
|
|
844
877
|
} catch {}
|
|
845
878
|
return /^-?\d+(\.\d+)?$/.test(a) ? String(Number(a)) : a;
|
|
846
|
-
},
|
|
847
|
-
let n =
|
|
879
|
+
}, _t = (e, t) => {
|
|
880
|
+
let n = gt(e), r = gt(t);
|
|
848
881
|
if (n === r) return !0;
|
|
849
882
|
let i = (e) => e === "1" || e === "true" ? "__BOOL_TRUE__" : e === "0" || e === "false" ? "__BOOL_FALSE__" : e;
|
|
850
883
|
return i(n) === i(r);
|
|
851
|
-
},
|
|
884
|
+
}, L = (e) => {
|
|
852
885
|
if (!e) return null;
|
|
853
886
|
let t = (e) => e === "restrict" ? "noAction" : e;
|
|
854
887
|
return {
|
|
@@ -857,25 +890,25 @@ BEGIN
|
|
|
857
890
|
onDelete: t(e.onDelete),
|
|
858
891
|
onUpdate: t(e.onUpdate)
|
|
859
892
|
};
|
|
860
|
-
},
|
|
893
|
+
}, vt = (e, t) => {
|
|
861
894
|
let n = e && e.length > 0 ? e : void 0, r = t && t.length > 0 ? t : void 0;
|
|
862
895
|
if (!n && !r) return !0;
|
|
863
896
|
if (!n || !r || n.length !== r.length) return !1;
|
|
864
897
|
let i = [...n].sort(), a = [...r].sort();
|
|
865
898
|
return i.every((e, t) => e === a[t]);
|
|
866
|
-
},
|
|
899
|
+
}, yt = (e, t) => {
|
|
867
900
|
try {
|
|
868
|
-
let n = /\((\d+)\)/.exec(
|
|
901
|
+
let n = /\((\d+)\)/.exec(D(e, t));
|
|
869
902
|
return n ? Number(n[1]) : void 0;
|
|
870
903
|
} catch {
|
|
871
904
|
return;
|
|
872
905
|
}
|
|
873
|
-
},
|
|
906
|
+
}, bt = (t, n, r) => r === void 0 || e(r) ? !0 : t.type !== "text" || n.type !== "text" || yt(t, r) === n.maxLength, xt = (e, t, n) => e.type === t.type && e.nullable === t.nullable && e.unique === t.unique && _t(e, t) && bt(e, t, n) && JSON.stringify(L(e.references)) === JSON.stringify(L(t.references)) && vt(e.oneOf, t.oneOf), St = (e, t) => t.oneOf && t.oneOf.length > 0 ? R({
|
|
874
907
|
kind: "add-check",
|
|
875
908
|
table: e,
|
|
876
909
|
column: t.name,
|
|
877
910
|
values: t.oneOf
|
|
878
|
-
}, "safe", `enum-membership CHECK on ${e}.${t.name}`) : void 0,
|
|
911
|
+
}, "safe", `enum-membership CHECK on ${e}.${t.name}`) : void 0, Ct = (e, t) => e.unique === t.unique && (e.expression || t.expression ? !0 : e.columns.length === t.columns.length && e.columns.every((e, n) => e === t.columns[n])), wt = (e, t, n) => {
|
|
879
912
|
let r = new Map(t.columns.map((e) => [e.name, e])), i = new Map(e.columns.map((e) => [e.name, e])), a = [], o = [], s = [];
|
|
880
913
|
for (let t of e.columns) {
|
|
881
914
|
let e = r.get(t.name);
|
|
@@ -883,7 +916,7 @@ BEGIN
|
|
|
883
916
|
a.push(t);
|
|
884
917
|
continue;
|
|
885
918
|
}
|
|
886
|
-
|
|
919
|
+
xt(t, e, n) || s.push({
|
|
887
920
|
declared: t,
|
|
888
921
|
live: e
|
|
889
922
|
});
|
|
@@ -894,47 +927,47 @@ BEGIN
|
|
|
894
927
|
removed: o,
|
|
895
928
|
changed: s
|
|
896
929
|
};
|
|
897
|
-
},
|
|
930
|
+
}, Tt = (e, t) => {
|
|
898
931
|
let n = new Map(t.indexes.map((e) => [e.name, e])), r = new Map(e.indexes.map((e) => [e.name, e])), i = [], a = [];
|
|
899
932
|
for (let t of e.indexes) {
|
|
900
933
|
let e = n.get(t.name);
|
|
901
|
-
(!e || !
|
|
934
|
+
(!e || !Ct(t, e)) && i.push(t);
|
|
902
935
|
}
|
|
903
936
|
for (let e of t.indexes) r.has(e.name) || a.push(e);
|
|
904
937
|
return {
|
|
905
938
|
added: i,
|
|
906
939
|
removed: a
|
|
907
940
|
};
|
|
908
|
-
},
|
|
941
|
+
}, R = (e, t, n, r) => ({
|
|
909
942
|
op: e,
|
|
910
943
|
classification: t,
|
|
911
|
-
atomicByDialect:
|
|
944
|
+
atomicByDialect: mt(e.kind),
|
|
912
945
|
...n ? { reason: n } : {},
|
|
913
946
|
...r ? { blocked: r } : {}
|
|
914
|
-
}),
|
|
947
|
+
}), Et = (e, t, n) => {
|
|
915
948
|
let r = {
|
|
916
949
|
kind: "add-column",
|
|
917
950
|
table: e,
|
|
918
951
|
column: t
|
|
919
952
|
};
|
|
920
|
-
return t.nullable ?
|
|
921
|
-
},
|
|
953
|
+
return t.nullable ? R(r, "safe", "nullable column add — no backfill needed") : t.hasDefault && t.defaultValue !== void 0 ? R(r, "needs-default", "NOT NULL with literal default — DDL DEFAULT will populate existing rows") : n?.backfill ? R(r, "needs-backfill", `backfill declared (kind=${n.backfill.kind}) — applier will run 3-step add/update/set-not-null`) : R(r, "needs-backfill", "NOT NULL column on a table whose row count is unknown", { fix: `declare \`${t.name}: <type>().backfill(sql\`...\`)\` OR \`.default(value)\` so existing rows survive the migration` });
|
|
954
|
+
}, Dt = (e, t, n) => {
|
|
922
955
|
let r = {
|
|
923
956
|
kind: "drop-column",
|
|
924
957
|
table: e,
|
|
925
958
|
column: t.name
|
|
926
959
|
};
|
|
927
|
-
return n?.dropped ?
|
|
928
|
-
},
|
|
960
|
+
return n?.dropped ? R(r, "lossy", `column dropped via \`${t.name}: dropped()\` marker — intentional`) : R(r, "lossy", "column missing from declared schema", { fix: `if intentional, add \`${t.name}: dropped()\` to the schema. If a typo, restore the field` });
|
|
961
|
+
}, Ot = (e, t, n, r, i) => R({
|
|
929
962
|
kind: "create-table",
|
|
930
963
|
table: e,
|
|
931
964
|
columns: t,
|
|
932
965
|
indexes: n,
|
|
933
966
|
...r && r.length > 0 ? { primaryKey: r } : {}
|
|
934
|
-
}, "safe", i ?? "new table — no data to preserve"),
|
|
967
|
+
}, "safe", i ?? "new table — no data to preserve"), kt = (e) => R({
|
|
935
968
|
kind: "drop-table",
|
|
936
969
|
table: e
|
|
937
|
-
}, "lossy", "table missing from declared schema", { fix: "if intentional, set VOLTRO_DESTRUCTIVE_OK=1 OR add a file-based migration. If a typo, restore the table declaration" }),
|
|
970
|
+
}, "lossy", "table missing from declared schema", { fix: "if intentional, set VOLTRO_DESTRUCTIVE_OK=1 OR add a file-based migration. If a typo, restore the table declaration" }), At = 5e4, jt = (e, t, n, r) => {
|
|
938
971
|
let i = (n ?? 0) >= r, a;
|
|
939
972
|
return a = t.unique ? t.columns.length === 1 ? {
|
|
940
973
|
kind: "add-unique",
|
|
@@ -949,16 +982,16 @@ BEGIN
|
|
|
949
982
|
kind: "add-index",
|
|
950
983
|
table: e,
|
|
951
984
|
index: t
|
|
952
|
-
}, i ?
|
|
953
|
-
},
|
|
985
|
+
}, i ? R(a, "online-required", `online build on ${n} rows — CONCURRENTLY / INPLACE / WITH(ONLINE=ON) depending on dialect`) : R(a, "safe", t.unique ? t.columns.length === 1 ? "add unique constraint" : "add composite UNIQUE constraint" : "add B-tree index");
|
|
986
|
+
}, Mt = (e, t) => t.unique && t.columns.length > 1 ? R({
|
|
954
987
|
kind: "drop-unique-composite",
|
|
955
988
|
table: e,
|
|
956
989
|
name: t.name
|
|
957
|
-
}, "safe", "drop composite UNIQUE constraint (no data loss — constraint is derived metadata)") :
|
|
990
|
+
}, "safe", "drop composite UNIQUE constraint (no data loss — constraint is derived metadata)") : R({
|
|
958
991
|
kind: "drop-index",
|
|
959
992
|
table: e,
|
|
960
993
|
index: t.name
|
|
961
|
-
}, "safe", "drop index (no data loss — index is derived data)"),
|
|
994
|
+
}, "safe", "drop index (no data loss — index is derived data)"), Nt = (e, t) => {
|
|
962
995
|
if (e === "reference") return "text";
|
|
963
996
|
switch (t) {
|
|
964
997
|
case "sqlite":
|
|
@@ -966,106 +999,106 @@ BEGIN
|
|
|
966
999
|
case "mssql": return e === "json" ? "text" : e;
|
|
967
1000
|
default: return e;
|
|
968
1001
|
}
|
|
969
|
-
},
|
|
970
|
-
let
|
|
971
|
-
if (
|
|
972
|
-
let
|
|
1002
|
+
}, Pt = (t, n, r, i, a, o) => {
|
|
1003
|
+
let s = [];
|
|
1004
|
+
if (n.nullable !== r.nullable) {
|
|
1005
|
+
let e = {
|
|
973
1006
|
kind: "alter-column-nullability",
|
|
974
|
-
table:
|
|
975
|
-
column:
|
|
976
|
-
toNullable:
|
|
1007
|
+
table: t,
|
|
1008
|
+
column: n.name,
|
|
1009
|
+
toNullable: n.nullable
|
|
977
1010
|
};
|
|
978
|
-
|
|
1011
|
+
s.push(n.nullable ? R(e, "safe", `loosen ${t}.${n.name} to nullable`) : R(e, "needs-backfill", `tighten ${t}.${n.name} to NOT NULL — existing rows must already be non-null`));
|
|
979
1012
|
}
|
|
980
|
-
if (
|
|
981
|
-
let
|
|
1013
|
+
if (n.unique !== r.unique) if (n.unique) {
|
|
1014
|
+
let e = {
|
|
982
1015
|
kind: "add-unique",
|
|
983
|
-
table:
|
|
984
|
-
column:
|
|
985
|
-
}, r = (
|
|
986
|
-
|
|
987
|
-
} else
|
|
1016
|
+
table: t,
|
|
1017
|
+
column: n.name
|
|
1018
|
+
}, r = (a ?? 0) >= o;
|
|
1019
|
+
s.push(r ? R(e, "online-required", `online unique build on ${a} rows — CONCURRENTLY / INPLACE / WITH(ONLINE=ON) depending on dialect`) : R(e, "safe", `add unique constraint on ${t}.${n.name} — existing rows must already be distinct (else the DB rejects at migrate)`));
|
|
1020
|
+
} else s.push(R({
|
|
988
1021
|
kind: "drop-unique",
|
|
989
|
-
table:
|
|
990
|
-
column:
|
|
991
|
-
}, "safe", `drop unique constraint on ${
|
|
992
|
-
if (
|
|
993
|
-
let
|
|
994
|
-
if (
|
|
1022
|
+
table: t,
|
|
1023
|
+
column: n.name
|
|
1024
|
+
}, "safe", `drop unique constraint on ${t}.${n.name} — constraint is derived metadata, no data loss`));
|
|
1025
|
+
if (Nt(n.type, i) !== Nt(r.type, i)) {
|
|
1026
|
+
let e = n.narrowedFrom;
|
|
1027
|
+
if (e && e.from === r.type) {
|
|
995
1028
|
let i = {
|
|
996
1029
|
kind: "alter-column-type",
|
|
997
|
-
table:
|
|
998
|
-
column:
|
|
999
|
-
from:
|
|
1000
|
-
to:
|
|
1001
|
-
...
|
|
1030
|
+
table: t,
|
|
1031
|
+
column: n.name,
|
|
1032
|
+
from: r.type,
|
|
1033
|
+
to: n.type,
|
|
1034
|
+
...e.using === void 0 ? {} : { using: e.using }
|
|
1002
1035
|
};
|
|
1003
|
-
|
|
1036
|
+
s.push(R(i, "needs-backfill", `type change ${r.type} → ${n.type} on ${t}.${n.name} — narrowedFrom('${e.from}') declared` + (e.using ? ` with USING ${e.using}` : " (implicit cast)")));
|
|
1004
1037
|
} else {
|
|
1005
|
-
let
|
|
1038
|
+
let e = {
|
|
1006
1039
|
kind: "alter-column-type",
|
|
1007
|
-
table:
|
|
1008
|
-
column:
|
|
1009
|
-
from:
|
|
1010
|
-
to:
|
|
1040
|
+
table: t,
|
|
1041
|
+
column: n.name,
|
|
1042
|
+
from: r.type,
|
|
1043
|
+
to: n.type
|
|
1011
1044
|
};
|
|
1012
|
-
|
|
1045
|
+
s.push(R(e, "lossy", `type change ${r.type} → ${n.type} on ${t}.${n.name} may not be value-preserving`, { fix: `acknowledge it on the column: \`.narrowedFrom('${r.type}', { using: '${n.name}::${n.type}' })\` (drop \`using\` if the cast is implicit), or use a file-based migration` }));
|
|
1013
1046
|
}
|
|
1014
1047
|
}
|
|
1015
|
-
if (
|
|
1016
|
-
let
|
|
1017
|
-
|
|
1048
|
+
if (n.type === "text" && r.type === "text" && !e(i ?? "postgres") && n.maxLength !== r.maxLength) {
|
|
1049
|
+
let e = n.maxLength === void 0 || r.maxLength !== void 0 && n.maxLength > r.maxLength, i = r.maxLength === void 0 ? "unbounded" : `varchar(${r.maxLength})`, a = n.maxLength === void 0 ? "unbounded" : `varchar(${n.maxLength})`;
|
|
1050
|
+
s.push(R({
|
|
1018
1051
|
kind: "alter-column-type",
|
|
1019
|
-
table:
|
|
1020
|
-
column:
|
|
1052
|
+
table: t,
|
|
1053
|
+
column: n.name,
|
|
1021
1054
|
from: "text",
|
|
1022
1055
|
to: "text"
|
|
1023
|
-
},
|
|
1056
|
+
}, e ? "safe" : "needs-backfill", e ? `text length ${i} → ${a} on ${t}.${n.name} — widening, no data can be lost` : `text length ${i} → ${a} on ${t}.${n.name} — NARROWING; the ALTER fails if any existing value is longer than ${String(n.maxLength)}. Check first: SELECT COUNT(*) FROM ${t} WHERE LENGTH(${n.name}) > ${String(n.maxLength)}`));
|
|
1024
1057
|
}
|
|
1025
|
-
if (!
|
|
1026
|
-
let
|
|
1058
|
+
if (!_t(n, r)) {
|
|
1059
|
+
let e = {
|
|
1027
1060
|
kind: "alter-column-default",
|
|
1028
|
-
table:
|
|
1029
|
-
column:
|
|
1030
|
-
hasDefault:
|
|
1031
|
-
...
|
|
1061
|
+
table: t,
|
|
1062
|
+
column: n.name,
|
|
1063
|
+
hasDefault: n.hasDefault,
|
|
1064
|
+
...n.defaultValue === void 0 ? {} : { defaultValue: n.defaultValue }
|
|
1032
1065
|
};
|
|
1033
|
-
|
|
1066
|
+
s.push(R(e, "safe", `default change on ${t}.${n.name} affects only future inserts`));
|
|
1034
1067
|
}
|
|
1035
|
-
if (!
|
|
1036
|
-
|
|
1068
|
+
if (!vt(n.oneOf, r.oneOf)) {
|
|
1069
|
+
r.oneOf && r.oneOf.length > 0 && s.push(R({
|
|
1037
1070
|
kind: "drop-check",
|
|
1038
|
-
table:
|
|
1039
|
-
column:
|
|
1040
|
-
}, "safe", `drop stale enum CHECK on ${
|
|
1041
|
-
let
|
|
1042
|
-
|
|
1071
|
+
table: t,
|
|
1072
|
+
column: n.name
|
|
1073
|
+
}, "safe", `drop stale enum CHECK on ${t}.${n.name}`));
|
|
1074
|
+
let e = St(t, n);
|
|
1075
|
+
e && s.push(e);
|
|
1043
1076
|
}
|
|
1044
|
-
if (JSON.stringify(
|
|
1077
|
+
if (JSON.stringify(L(n.references)) !== JSON.stringify(L(r.references)) && (r.references && s.push(R({
|
|
1045
1078
|
kind: "drop-foreign-key",
|
|
1046
|
-
table:
|
|
1047
|
-
column:
|
|
1048
|
-
}, "safe", `drop FK on ${
|
|
1049
|
-
let
|
|
1079
|
+
table: t,
|
|
1080
|
+
column: n.name
|
|
1081
|
+
}, "safe", `drop FK on ${t}.${n.name} (→ ${r.references.table})`)), n.references)) {
|
|
1082
|
+
let e = n.references.table, r = n.references.orphanPolicy ?? "fail", i = {
|
|
1050
1083
|
kind: "add-foreign-key",
|
|
1051
|
-
table:
|
|
1052
|
-
column:
|
|
1053
|
-
targetTable:
|
|
1054
|
-
targetColumn:
|
|
1055
|
-
...
|
|
1056
|
-
...
|
|
1084
|
+
table: t,
|
|
1085
|
+
column: n.name,
|
|
1086
|
+
targetTable: e,
|
|
1087
|
+
targetColumn: n.references.column,
|
|
1088
|
+
...n.references.onDelete ? { onDelete: n.references.onDelete } : {},
|
|
1089
|
+
...n.references.onUpdate ? { onUpdate: n.references.onUpdate } : {}
|
|
1057
1090
|
};
|
|
1058
|
-
r === "null" && !
|
|
1091
|
+
r === "null" && !n.nullable ? s.push(R(i, "lossy", `add FK ${t}.${n.name} → ${e}: orphanPolicy 'null' needs a nullable column (the NULL-out would violate NOT NULL)`, { fix: `make ${n.name} \`.nullable()\`, OR use \`orphanPolicy: 'delete'\`, OR clean the orphans and drop the policy` })) : r === "null" || r === "delete" ? s.push(R({
|
|
1059
1092
|
...i,
|
|
1060
1093
|
orphanPolicy: r
|
|
1061
|
-
}, "lossy", r === "delete" ? `add FK ${
|
|
1094
|
+
}, "lossy", r === "delete" ? `add FK ${t}.${n.name} → ${e}: orphanPolicy 'delete' first DELETES rows whose ${n.name} points at a missing ${e}` : `add FK ${t}.${n.name} → ${e}: orphanPolicy 'null' first NULLs ${n.name} where it points at a missing ${e}`)) : s.push(R(i, "needs-backfill", `add FK ${t}.${n.name} → ${e} — existing rows must already reference a valid ${e} (else set orphanPolicy:'null'/'delete')`));
|
|
1062
1095
|
}
|
|
1063
|
-
return
|
|
1064
|
-
},
|
|
1096
|
+
return s;
|
|
1097
|
+
}, Ft = (e, t, n) => {
|
|
1065
1098
|
let r = [], i = /* @__PURE__ */ new Set(), a = /* @__PURE__ */ new Set(), o = new Set(n.removed.map((e) => e.name)), s = new Set(n.added.map((e) => e.name));
|
|
1066
1099
|
for (let [n, c] of Object.entries(t)) {
|
|
1067
1100
|
let t = c.renamedFrom;
|
|
1068
|
-
t && o.has(t) && s.has(n) && (i.add(n), a.add(t), r.push(
|
|
1101
|
+
t && o.has(t) && s.has(n) && (i.add(n), a.add(t), r.push(R({
|
|
1069
1102
|
kind: "rename-column",
|
|
1070
1103
|
table: e.tableName,
|
|
1071
1104
|
from: t,
|
|
@@ -1077,26 +1110,26 @@ BEGIN
|
|
|
1077
1110
|
consumedAddedNames: i,
|
|
1078
1111
|
consumedRemovedNames: a
|
|
1079
1112
|
};
|
|
1080
|
-
},
|
|
1113
|
+
}, It = /* @__PURE__ */ new Set([
|
|
1081
1114
|
"postgres",
|
|
1082
1115
|
"mysql",
|
|
1083
1116
|
"mariadb",
|
|
1084
1117
|
"mssql"
|
|
1085
|
-
]),
|
|
1118
|
+
]), Lt = {
|
|
1086
1119
|
ops: [],
|
|
1087
1120
|
consumedAddedNames: /* @__PURE__ */ new Set(),
|
|
1088
1121
|
consumedRemovedNames: /* @__PURE__ */ new Set()
|
|
1089
|
-
},
|
|
1090
|
-
if (i === void 0 || !
|
|
1091
|
-
let a = t.added.filter((e) =>
|
|
1092
|
-
if (a.length === 0) return
|
|
1122
|
+
}, Rt = (e) => !e.unique && !e.expression, zt = (e, t, n, r, i) => {
|
|
1123
|
+
if (i === void 0 || !It.has(i)) return Lt;
|
|
1124
|
+
let a = t.added.filter((e) => Rt(e) && !r.has(e.name));
|
|
1125
|
+
if (a.length === 0) return Lt;
|
|
1093
1126
|
let o = (e) => e.columns.map((e) => n.get(e) ?? e).join("\0"), s = (e) => e.columns.join("\0"), c = [], l = /* @__PURE__ */ new Set(), u = /* @__PURE__ */ new Set();
|
|
1094
1127
|
for (let n of t.removed) {
|
|
1095
|
-
if (!
|
|
1128
|
+
if (!Rt(n)) continue;
|
|
1096
1129
|
let t = o(n), r = a.filter((e) => !l.has(e.name) && s(e) === t);
|
|
1097
1130
|
if (r.length !== 1) continue;
|
|
1098
1131
|
let i = r[0];
|
|
1099
|
-
i.name !== n.name && (l.add(i.name), u.add(n.name), c.push(
|
|
1132
|
+
i.name !== n.name && (l.add(i.name), u.add(n.name), c.push(R({
|
|
1100
1133
|
kind: "rename-index",
|
|
1101
1134
|
table: e,
|
|
1102
1135
|
from: n.name,
|
|
@@ -1108,33 +1141,33 @@ BEGIN
|
|
|
1108
1141
|
consumedAddedNames: l,
|
|
1109
1142
|
consumedRemovedNames: u
|
|
1110
1143
|
};
|
|
1111
|
-
},
|
|
1144
|
+
}, Bt = (e, t, n) => {
|
|
1112
1145
|
let r = [], i = /* @__PURE__ */ new Map(), a = /* @__PURE__ */ new Set(), o = /* @__PURE__ */ new Map(), s = /* @__PURE__ */ new Map(), c = /* @__PURE__ */ new Set();
|
|
1113
1146
|
for (let t of e.tables) t.renamedFrom !== void 0 && (s.set(t.renamedFrom, (s.get(t.renamedFrom) ?? 0) + 1), c.add(t.renamedFrom));
|
|
1114
1147
|
for (let c of e.tables) {
|
|
1115
1148
|
let e = c.renamedFrom;
|
|
1116
1149
|
if (e !== void 0 && e !== c.name && t.has(e)) {
|
|
1150
|
+
if (n.has(e)) {
|
|
1151
|
+
o.set(c.name, `.renamedFrom('${e}') NOT applied — '${e}' is still declared by this schema, so it belongs to that declaration. This table is created empty; that is the intended outcome when an app owns a table of the same name.`);
|
|
1152
|
+
continue;
|
|
1153
|
+
}
|
|
1117
1154
|
if (t.has(c.name)) {
|
|
1118
|
-
r.push(
|
|
1155
|
+
r.push(R({
|
|
1119
1156
|
kind: "rename-table",
|
|
1120
1157
|
from: e,
|
|
1121
1158
|
to: c.name
|
|
1122
1159
|
}, "needs-rename-annotation", `.renamedFrom('${e}') cannot be applied — both '${e}' and '${c.name}' exist in the database`, { fix: `decide which holds the real rows: move them into '${c.name}' and drop '${e}', or drop the empty '${c.name}' so the rename can run. '${e}' is left untouched until you do.` }));
|
|
1123
1160
|
continue;
|
|
1124
1161
|
}
|
|
1125
|
-
if (n.has(e)) {
|
|
1126
|
-
o.set(c.name, `.renamedFrom('${e}') NOT applied — '${e}' is still declared by this schema, so it belongs to that declaration. This table is created empty; that is the intended outcome when an app owns a table of the same name.`);
|
|
1127
|
-
continue;
|
|
1128
|
-
}
|
|
1129
1162
|
if ((s.get(e) ?? 0) !== 1) {
|
|
1130
|
-
r.push(
|
|
1163
|
+
r.push(R({
|
|
1131
1164
|
kind: "rename-table",
|
|
1132
1165
|
from: e,
|
|
1133
1166
|
to: c.name
|
|
1134
1167
|
}, "needs-rename-annotation", `.renamedFrom('${e}') is claimed by ${s.get(e)} tables — nothing says which receives its rows`, { fix: `remove the \`.renamedFrom('${e}')\` marker from all but one of them.` }));
|
|
1135
1168
|
continue;
|
|
1136
1169
|
}
|
|
1137
|
-
i.set(c.name, e), a.add(e), r.push(
|
|
1170
|
+
i.set(c.name, e), a.add(e), r.push(R({
|
|
1138
1171
|
kind: "rename-table",
|
|
1139
1172
|
from: e,
|
|
1140
1173
|
to: c.name
|
|
@@ -1148,19 +1181,19 @@ BEGIN
|
|
|
1148
1181
|
claimedOldNames: c,
|
|
1149
1182
|
refusals: o
|
|
1150
1183
|
};
|
|
1151
|
-
},
|
|
1184
|
+
}, Vt = (e, t, n, r) => {
|
|
1152
1185
|
if (t === void 0) return {
|
|
1153
1186
|
live: e,
|
|
1154
1187
|
ops: []
|
|
1155
1188
|
};
|
|
1156
|
-
let i = r !== void 0 &&
|
|
1189
|
+
let i = r !== void 0 && It.has(r), a = [], o = e.indexes.map((e) => {
|
|
1157
1190
|
if (!e.name.startsWith(t)) return e;
|
|
1158
1191
|
let r = `${n}${e.name.slice(t.length)}`;
|
|
1159
1192
|
return r === e.name ? e : e.name === `${t}_pkey` ? {
|
|
1160
1193
|
...e,
|
|
1161
1194
|
name: r,
|
|
1162
1195
|
table: n
|
|
1163
|
-
} : i ? (a.push(
|
|
1196
|
+
} : i ? (a.push(R({
|
|
1164
1197
|
kind: "rename-index",
|
|
1165
1198
|
table: n,
|
|
1166
1199
|
from: e.name,
|
|
@@ -1179,7 +1212,7 @@ BEGIN
|
|
|
1179
1212
|
},
|
|
1180
1213
|
ops: a
|
|
1181
1214
|
};
|
|
1182
|
-
},
|
|
1215
|
+
}, Ht = (e) => {
|
|
1183
1216
|
let t = {
|
|
1184
1217
|
safe: 0,
|
|
1185
1218
|
needsDefault: 0,
|
|
@@ -1214,51 +1247,51 @@ BEGIN
|
|
|
1214
1247
|
break;
|
|
1215
1248
|
}
|
|
1216
1249
|
return t;
|
|
1217
|
-
},
|
|
1218
|
-
let t =
|
|
1250
|
+
}, Ut = (e) => {
|
|
1251
|
+
let t = F(e.declared, e.dialect), n = ht(e.declared, t), r = new Map(e.live.tables.map((e) => [e.name, e])), i = e.onlineAfter ?? At, a = [], o = Bt(t, r, n);
|
|
1219
1252
|
a.push(...o.ops);
|
|
1220
1253
|
for (let e of t.tables) if (!o.renamedInto.has(e.name) && !r.has(e.name)) {
|
|
1221
|
-
a.push(
|
|
1254
|
+
a.push(Ot(e.name, e.columns, e.indexes, e.primaryKey, o.refusals.get(e.name)));
|
|
1222
1255
|
for (let t of e.columns) {
|
|
1223
|
-
let n =
|
|
1256
|
+
let n = St(e.name, t);
|
|
1224
1257
|
n && a.push(n);
|
|
1225
1258
|
}
|
|
1226
1259
|
}
|
|
1227
|
-
let
|
|
1228
|
-
for (let t of e.live.tables) n.has(t.name) || o.claimedOldNames.has(t.name) ||
|
|
1260
|
+
let c = new Set(e.ignoreTables ?? []);
|
|
1261
|
+
for (let t of e.live.tables) n.has(t.name) || o.claimedOldNames.has(t.name) || s(t.name) || d(t.name) || c.has(t.name) || a.push(kt(t.name));
|
|
1229
1262
|
for (let s of t.tables) {
|
|
1230
1263
|
let t = o.renamedInto.get(s.name), c = r.get(s.name) ?? (t === void 0 ? void 0 : r.get(t));
|
|
1231
1264
|
if (!c) continue;
|
|
1232
|
-
let l =
|
|
1265
|
+
let l = Vt(c, t, s.name, e.dialect);
|
|
1233
1266
|
a.push(...l.ops);
|
|
1234
|
-
let u = l.live,
|
|
1235
|
-
a.push(...
|
|
1236
|
-
for (let e of
|
|
1237
|
-
if (
|
|
1238
|
-
let t =
|
|
1239
|
-
a.push(Ot(s.name, e, t));
|
|
1240
|
-
}
|
|
1241
|
-
for (let e of f.added) {
|
|
1242
|
-
if (m.consumedAddedNames.has(e.name)) continue;
|
|
1243
|
-
let t = d.columnDefs[e.name];
|
|
1267
|
+
let u = l.live, f = n.get(s.name), p = wt(s, u, e.dialect), m = Tt(s, u), h = Ft(f.table, f.columnDefs, p);
|
|
1268
|
+
a.push(...h.ops);
|
|
1269
|
+
for (let e of p.removed) {
|
|
1270
|
+
if (h.consumedRemovedNames.has(e.name) || d(e.name)) continue;
|
|
1271
|
+
let t = f.columnDefs[e.name];
|
|
1244
1272
|
a.push(Dt(s.name, e, t));
|
|
1245
|
-
|
|
1273
|
+
}
|
|
1274
|
+
for (let e of p.added) {
|
|
1275
|
+
if (h.consumedAddedNames.has(e.name)) continue;
|
|
1276
|
+
let t = f.columnDefs[e.name];
|
|
1277
|
+
a.push(Et(s.name, e, t));
|
|
1278
|
+
let n = St(s.name, e);
|
|
1246
1279
|
n && a.push(n);
|
|
1247
1280
|
}
|
|
1248
|
-
let
|
|
1249
|
-
a.push(...
|
|
1250
|
-
for (let e of
|
|
1251
|
-
for (let e of
|
|
1252
|
-
for (let { declared: t, live: n } of
|
|
1253
|
-
}
|
|
1254
|
-
let
|
|
1255
|
-
let t =
|
|
1256
|
-
return t ? t.columns.flatMap((e) => e.references &&
|
|
1281
|
+
let g = new Map(h.ops.flatMap((e) => e.op.kind === "rename-column" ? [[e.op.from, e.op.to]] : [])), _ = zt(s.name, m, g, new Set(u.indexes.map((e) => e.name)), e.dialect);
|
|
1282
|
+
a.push(..._.ops);
|
|
1283
|
+
for (let e of m.added) _.consumedAddedNames.has(e.name) || a.push(jt(s.name, e, u.rowCount, i));
|
|
1284
|
+
for (let e of m.removed) _.consumedRemovedNames.has(e.name) || a.push(Mt(s.name, e));
|
|
1285
|
+
for (let { declared: t, live: n } of p.changed) a.push(...Pt(s.name, t, n, e.dialect, u.rowCount, i));
|
|
1286
|
+
}
|
|
1287
|
+
let l = new Set(a.flatMap((e) => e.op.kind === "create-table" ? [e.op.table] : [])), u = new Map(t.tables.map((e) => [e.name, e])), f = (e) => {
|
|
1288
|
+
let t = u.get(e);
|
|
1289
|
+
return t ? t.columns.flatMap((e) => e.references && l.has(e.references.table) ? [{
|
|
1257
1290
|
column: e.name,
|
|
1258
1291
|
target: e.references.table
|
|
1259
1292
|
}] : []) : [];
|
|
1260
|
-
},
|
|
1261
|
-
for (let e of t.tables) if (
|
|
1293
|
+
}, p = re([...l], f), m = [];
|
|
1294
|
+
for (let e of t.tables) if (l.has(e.name)) for (let t of e.columns) !t.references || !p.has(C(e.name, t.name)) || m.push(R({
|
|
1262
1295
|
kind: "add-foreign-key",
|
|
1263
1296
|
table: e.name,
|
|
1264
1297
|
column: t.name,
|
|
@@ -1267,14 +1300,14 @@ BEGIN
|
|
|
1267
1300
|
...t.references.onDelete ? { onDelete: t.references.onDelete } : {},
|
|
1268
1301
|
...t.references.onUpdate ? { onUpdate: t.references.onUpdate } : {}
|
|
1269
1302
|
}, "safe", `deferred FK ${e.name}.${t.name} → ${t.references.table} — breaks a create-time table cycle`));
|
|
1270
|
-
if (
|
|
1303
|
+
if (p.size > 0) {
|
|
1271
1304
|
let e = (e) => e.references ? Object.fromEntries(Object.entries(e).filter(([e]) => e !== "references")) : e;
|
|
1272
1305
|
for (let t = 0; t < a.length; t += 1) {
|
|
1273
1306
|
let n = a[t];
|
|
1274
1307
|
if (n.op.kind !== "create-table") continue;
|
|
1275
1308
|
let r = n.op;
|
|
1276
|
-
if (!r.columns.some((e) =>
|
|
1277
|
-
let i = r.columns.map((t) =>
|
|
1309
|
+
if (!r.columns.some((e) => p.has(C(r.table, e.name)))) continue;
|
|
1310
|
+
let i = r.columns.map((t) => p.has(C(r.table, t.name)) ? e(t) : t);
|
|
1278
1311
|
a[t] = {
|
|
1279
1312
|
...n,
|
|
1280
1313
|
op: {
|
|
@@ -1283,24 +1316,24 @@ BEGIN
|
|
|
1283
1316
|
}
|
|
1284
1317
|
};
|
|
1285
1318
|
}
|
|
1286
|
-
a.push(...
|
|
1319
|
+
a.push(...m);
|
|
1287
1320
|
}
|
|
1288
|
-
let
|
|
1289
|
-
let n =
|
|
1321
|
+
let h = new Map(t.tables.map((e) => [e.name, e])), g = /* @__PURE__ */ new Map(), _ = (e, t = /* @__PURE__ */ new Set()) => {
|
|
1322
|
+
let n = g.get(e);
|
|
1290
1323
|
if (n !== void 0) return n;
|
|
1291
1324
|
if (t.has(e)) return 0;
|
|
1292
|
-
let r =
|
|
1325
|
+
let r = h.get(e);
|
|
1293
1326
|
if (!r) return 0;
|
|
1294
1327
|
let i = new Set(t).add(e), a = 0;
|
|
1295
1328
|
for (let t of r.columns) {
|
|
1296
1329
|
let n = t.references?.table;
|
|
1297
|
-
n && n !== e && !
|
|
1330
|
+
n && n !== e && !p.has(C(e, t.name)) && (a = Math.max(a, _(n, i) + 1));
|
|
1298
1331
|
}
|
|
1299
|
-
return
|
|
1300
|
-
},
|
|
1332
|
+
return g.set(e, a), a;
|
|
1333
|
+
}, v = (e) => e === "rename-table" ? -1 : e === "create-table" ? 0 : e === "drop-table" ? 2 : 1, y = (e) => {
|
|
1301
1334
|
let t = "table" in e ? e.table : "";
|
|
1302
|
-
return e.kind === "create-table" ?
|
|
1303
|
-
},
|
|
1335
|
+
return e.kind === "create-table" ? _(t) : e.kind === "drop-table" ? -_(t) : 0;
|
|
1336
|
+
}, b = {
|
|
1304
1337
|
"add-column": 0,
|
|
1305
1338
|
"rename-column": 1,
|
|
1306
1339
|
"rename-index": 1,
|
|
@@ -1318,23 +1351,23 @@ BEGIN
|
|
|
1318
1351
|
"add-unique-composite": 5,
|
|
1319
1352
|
"add-foreign-key": 5,
|
|
1320
1353
|
"add-check": 5
|
|
1321
|
-
},
|
|
1322
|
-
let n =
|
|
1354
|
+
}, x = (e) => b[e] ?? 9, S = [...a].sort((e, t) => {
|
|
1355
|
+
let n = v(e.op.kind), r = v(t.op.kind);
|
|
1323
1356
|
if (n !== r) return n - r;
|
|
1324
|
-
let i =
|
|
1357
|
+
let i = y(e.op), a = y(t.op);
|
|
1325
1358
|
if (i !== a) return i - a;
|
|
1326
1359
|
let o = "table" in e.op ? e.op.table : "", s = "table" in t.op ? t.op.table : "";
|
|
1327
1360
|
if (o !== s) return o.localeCompare(s);
|
|
1328
|
-
let c =
|
|
1361
|
+
let c = x(e.op.kind), l = x(t.op.kind);
|
|
1329
1362
|
return c === l ? e.op.kind.localeCompare(t.op.kind) : c - l;
|
|
1330
1363
|
});
|
|
1331
1364
|
return {
|
|
1332
|
-
operations:
|
|
1333
|
-
fromFingerprint:
|
|
1334
|
-
toFingerprint:
|
|
1335
|
-
summary:
|
|
1365
|
+
operations: S,
|
|
1366
|
+
fromFingerprint: N(e.live),
|
|
1367
|
+
toFingerprint: N(t),
|
|
1368
|
+
summary: Ht(S)
|
|
1336
1369
|
};
|
|
1337
|
-
},
|
|
1370
|
+
}, Wt = (e) => {
|
|
1338
1371
|
let t = /[`"[]?([A-Za-z_][A-Za-z0-9_]*)[`"\]]?\s*(?:in\b|=)/i.exec(e);
|
|
1339
1372
|
if (!t) return null;
|
|
1340
1373
|
let n = [...e.matchAll(/'((?:[^']|'')*)'/g)].map((e) => e[1].replace(/''/g, "'"));
|
|
@@ -1342,7 +1375,7 @@ BEGIN
|
|
|
1342
1375
|
column: t[1],
|
|
1343
1376
|
values: n
|
|
1344
1377
|
};
|
|
1345
|
-
},
|
|
1378
|
+
}, Gt = (e, t) => {
|
|
1346
1379
|
if (t === "vector") return "vector";
|
|
1347
1380
|
switch (e) {
|
|
1348
1381
|
case "text":
|
|
@@ -1369,7 +1402,7 @@ BEGIN
|
|
|
1369
1402
|
case "USER-DEFINED": return "text";
|
|
1370
1403
|
default: return "text";
|
|
1371
1404
|
}
|
|
1372
|
-
},
|
|
1405
|
+
}, z = (e) => {
|
|
1373
1406
|
switch (e.toUpperCase()) {
|
|
1374
1407
|
case "CASCADE": return "cascade";
|
|
1375
1408
|
case "RESTRICT": return "restrict";
|
|
@@ -1377,25 +1410,25 @@ BEGIN
|
|
|
1377
1410
|
case "SET DEFAULT": return "noAction";
|
|
1378
1411
|
default: return "noAction";
|
|
1379
1412
|
}
|
|
1380
|
-
},
|
|
1413
|
+
}, Kt = (e, t) => {
|
|
1381
1414
|
let n = e !== void 0 && e !== "" ? Number(e) : NaN;
|
|
1382
1415
|
return Number.isInteger(n) && n > 0 ? n : t;
|
|
1383
|
-
},
|
|
1416
|
+
}, qt = () => Kt(process.env.VOLTRO_INTROSPECT_BATCH, 20), Jt = 300, Yt = () => {
|
|
1384
1417
|
let e = process.env.VOLTRO_INTROSPECT_TIMEOUT_MS;
|
|
1385
|
-
return e !== void 0 && e.trim() === "0" ? 0 :
|
|
1386
|
-
},
|
|
1418
|
+
return e !== void 0 && e.trim() === "0" ? 0 : Kt(e, 3e4);
|
|
1419
|
+
}, Xt = (e, t) => {
|
|
1387
1420
|
let n = Number.isFinite(t) && t >= 1 ? Math.floor(t) : 1, r = [];
|
|
1388
1421
|
for (let t = 0; t < e.length; t += n) r.push(e.slice(t, t + n));
|
|
1389
1422
|
return r;
|
|
1390
|
-
},
|
|
1391
|
-
let t =
|
|
1423
|
+
}, Zt = (e) => {
|
|
1424
|
+
let t = Yt(), n = p.gen(function* () {
|
|
1392
1425
|
t > 0 && (yield* e.unsafe(`SET LOCAL statement_timeout = ${t}`));
|
|
1393
|
-
let n = (t) => e.unsafe("(" + t.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ") + ")"), r = (t) => e.unsafe(String(Math.max(0, Math.trunc(t)))), i =
|
|
1426
|
+
let n = (t) => e.unsafe("(" + t.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ") + ")"), r = (t) => e.unsafe(String(Math.max(0, Math.trunc(t)))), i = qt(), a = (e, t) => p.gen(function* () {
|
|
1394
1427
|
let r = [];
|
|
1395
|
-
for (let a of
|
|
1428
|
+
for (let a of Xt(e, i)) a.length !== 0 && r.push(...yield* t(n(a)));
|
|
1396
1429
|
return r;
|
|
1397
1430
|
}), o = [];
|
|
1398
|
-
for (let t = 0;; t +=
|
|
1431
|
+
for (let t = 0;; t += Jt) {
|
|
1399
1432
|
let n = yield* e`
|
|
1400
1433
|
SELECT c.relname AS table_name,
|
|
1401
1434
|
COALESCE(c.reltuples::bigint, 0) AS row_count
|
|
@@ -1403,9 +1436,9 @@ BEGIN
|
|
|
1403
1436
|
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
1404
1437
|
WHERE n.nspname = current_schema() AND c.relkind = 'r'
|
|
1405
1438
|
ORDER BY c.relname
|
|
1406
|
-
LIMIT ${r(
|
|
1439
|
+
LIMIT ${r(Jt)} OFFSET ${r(t)}
|
|
1407
1440
|
`;
|
|
1408
|
-
if (o.push(...n), n.length <
|
|
1441
|
+
if (o.push(...n), n.length < Jt) break;
|
|
1409
1442
|
}
|
|
1410
1443
|
let s = /* @__PURE__ */ new Set(), c = [];
|
|
1411
1444
|
for (let e of o) s.has(e.table_name) || (s.add(e.table_name), c.push(e));
|
|
@@ -1415,7 +1448,7 @@ BEGIN
|
|
|
1415
1448
|
FROM information_schema.columns
|
|
1416
1449
|
WHERE table_schema = current_schema() AND table_name IN ${t}
|
|
1417
1450
|
ORDER BY table_name, ordinal_position
|
|
1418
|
-
`),
|
|
1451
|
+
`), d = yield* a(l, (t) => e`
|
|
1419
1452
|
SELECT rel.relname AS table_name, att.attname AS column_name
|
|
1420
1453
|
FROM pg_constraint con
|
|
1421
1454
|
JOIN pg_class rel ON rel.oid = con.conrelid
|
|
@@ -1424,7 +1457,7 @@ BEGIN
|
|
|
1424
1457
|
WHERE con.contype = 'p' AND nsp.nspname = current_schema()
|
|
1425
1458
|
AND rel.relname IN ${t}
|
|
1426
1459
|
ORDER BY rel.relname, array_position(con.conkey, att.attnum)
|
|
1427
|
-
`),
|
|
1460
|
+
`), f = yield* a(l, (t) => e`
|
|
1428
1461
|
SELECT rel.relname AS table_name, con.conname AS constraint_name,
|
|
1429
1462
|
att.attname AS column_name, array_position(con.conkey, att.attnum) AS position
|
|
1430
1463
|
FROM pg_constraint con
|
|
@@ -1470,7 +1503,7 @@ BEGIN
|
|
|
1470
1503
|
WHERE con.contype = 'f' AND nsp.nspname = current_schema()
|
|
1471
1504
|
AND rel.relname IN ${t}
|
|
1472
1505
|
ORDER BY rel.relname, con.conname, ck.ord
|
|
1473
|
-
`),
|
|
1506
|
+
`), _ = yield* a(l, (t) => e`
|
|
1474
1507
|
WITH ix AS (
|
|
1475
1508
|
SELECT
|
|
1476
1509
|
c.relname AS table_name,
|
|
@@ -1491,34 +1524,34 @@ BEGIN
|
|
|
1491
1524
|
AND c.relname IN ${t}
|
|
1492
1525
|
)
|
|
1493
1526
|
SELECT * FROM ix ORDER BY table_name, index_name, position
|
|
1494
|
-
`),
|
|
1527
|
+
`), v = /* @__PURE__ */ new Map(), y = /* @__PURE__ */ new Map(), b = /* @__PURE__ */ new Map(), x = /* @__PURE__ */ new Map(), S = /* @__PURE__ */ new Map(), ee = /* @__PURE__ */ new Map();
|
|
1495
1528
|
for (let e of u) {
|
|
1496
|
-
let t = _.get(e.table_name);
|
|
1497
|
-
t || (t = [], _.set(e.table_name, t)), t.push(e);
|
|
1498
|
-
}
|
|
1499
|
-
for (let e of f) {
|
|
1500
1529
|
let t = v.get(e.table_name);
|
|
1501
|
-
t || (t =
|
|
1530
|
+
t || (t = [], v.set(e.table_name, t)), t.push(e);
|
|
1531
|
+
}
|
|
1532
|
+
for (let e of d) {
|
|
1533
|
+
let t = y.get(e.table_name);
|
|
1534
|
+
t || (t = /* @__PURE__ */ new Set(), y.set(e.table_name, t)), t.add(e.column_name);
|
|
1502
1535
|
}
|
|
1503
|
-
let
|
|
1504
|
-
for (let e of
|
|
1505
|
-
let t = `${e.table_name}\u0000${e.constraint_name}`, n =
|
|
1536
|
+
let te = /* @__PURE__ */ new Map();
|
|
1537
|
+
for (let e of f) {
|
|
1538
|
+
let t = `${e.table_name}\u0000${e.constraint_name}`, n = te.get(t);
|
|
1506
1539
|
n || (n = {
|
|
1507
1540
|
table: e.table_name,
|
|
1508
1541
|
columns: []
|
|
1509
|
-
},
|
|
1542
|
+
}, te.set(t, n)), n.columns.push({
|
|
1510
1543
|
name: e.column_name,
|
|
1511
1544
|
position: e.position
|
|
1512
1545
|
});
|
|
1513
1546
|
}
|
|
1514
|
-
for (let [e, t] of
|
|
1547
|
+
for (let [e, t] of te) {
|
|
1515
1548
|
let n = [...t.columns].sort((e, t) => e.position - t.position).map((e) => e.name);
|
|
1516
1549
|
if (n.length === 1) {
|
|
1517
|
-
let e =
|
|
1518
|
-
e || (e = /* @__PURE__ */ new Set(),
|
|
1550
|
+
let e = b.get(t.table);
|
|
1551
|
+
e || (e = /* @__PURE__ */ new Set(), b.set(t.table, e)), e.add(n[0]);
|
|
1519
1552
|
} else {
|
|
1520
|
-
let r =
|
|
1521
|
-
r || (r = [],
|
|
1553
|
+
let r = x.get(t.table);
|
|
1554
|
+
r || (r = [], x.set(t.table, r)), r.push({
|
|
1522
1555
|
name: e.slice(e.indexOf("\0") + 1),
|
|
1523
1556
|
table: t.table,
|
|
1524
1557
|
columns: n,
|
|
@@ -1527,28 +1560,28 @@ BEGIN
|
|
|
1527
1560
|
}
|
|
1528
1561
|
}
|
|
1529
1562
|
for (let e of g) {
|
|
1530
|
-
let t =
|
|
1531
|
-
t || (t = [],
|
|
1563
|
+
let t = S.get(e.source_table);
|
|
1564
|
+
t || (t = [], S.set(e.source_table, t)), t.push(e);
|
|
1532
1565
|
}
|
|
1533
|
-
for (let e of
|
|
1534
|
-
let t =
|
|
1535
|
-
t || (t = /* @__PURE__ */ new Map(),
|
|
1566
|
+
for (let e of _) {
|
|
1567
|
+
let t = ee.get(e.table_name);
|
|
1568
|
+
t || (t = /* @__PURE__ */ new Map(), ee.set(e.table_name, t));
|
|
1536
1569
|
let n = t.get(e.index_name);
|
|
1537
1570
|
n || (n = [], t.set(e.index_name, n)), n.push(e);
|
|
1538
1571
|
}
|
|
1539
1572
|
let ne = [];
|
|
1540
1573
|
for (let e of c) {
|
|
1541
|
-
let t = /* @__PURE__ */ new Set(), n = (
|
|
1542
|
-
let n = r.has(t.column_name) && r.size === 1, a = o.get(t.column_name), s = a ? "reference" : n && t.column_name === "id" ? "id" :
|
|
1574
|
+
let t = /* @__PURE__ */ new Set(), n = (v.get(e.table_name) ?? []).filter((e) => t.has(e.column_name) ? !1 : (t.add(e.column_name), !0)), r = y.get(e.table_name) ?? /* @__PURE__ */ new Set(), i = b.get(e.table_name) ?? /* @__PURE__ */ new Set(), a = S.get(e.table_name) ?? [], o = new Map(a.map((e) => [e.source_column, e])), s = new Set(n.filter((e) => e.udt_name === "tsvector").map((e) => e.column_name)), c = n.filter((e) => !s.has(e.column_name)).map((t) => {
|
|
1575
|
+
let n = r.has(t.column_name) && r.size === 1, a = o.get(t.column_name), s = a ? "reference" : n && t.column_name === "id" ? "id" : Gt(t.data_type, t.udt_name), c = a ? {
|
|
1543
1576
|
table: a.target_table,
|
|
1544
1577
|
column: a.target_column,
|
|
1545
|
-
onDelete:
|
|
1546
|
-
onUpdate:
|
|
1578
|
+
onDelete: z(a.delete_rule),
|
|
1579
|
+
onUpdate: z(a.update_rule)
|
|
1547
1580
|
} : void 0, l = h.get(`${e.table_name}.${t.column_name}`);
|
|
1548
1581
|
return {
|
|
1549
1582
|
name: t.column_name,
|
|
1550
1583
|
type: s,
|
|
1551
|
-
...
|
|
1584
|
+
...H(t.data_type, t.character_maximum_length) === void 0 ? {} : { maxLength: H(t.data_type, t.character_maximum_length) },
|
|
1552
1585
|
nullable: t.is_nullable === "YES",
|
|
1553
1586
|
unique: i.has(t.column_name) || n && t.column_name === "id",
|
|
1554
1587
|
hasDefault: t.column_default !== null,
|
|
@@ -1556,7 +1589,7 @@ BEGIN
|
|
|
1556
1589
|
...c ? { references: c } : {},
|
|
1557
1590
|
...l ? { oneOf: l } : {}
|
|
1558
1591
|
};
|
|
1559
|
-
}), l = [], u =
|
|
1592
|
+
}), l = [], u = ee.get(e.table_name);
|
|
1560
1593
|
if (u) for (let [t, n] of u) {
|
|
1561
1594
|
let r = /* @__PURE__ */ new Set(), i = [...n].filter((e) => e.column_name !== null && e.position !== null).sort((e, t) => (e.position ?? 0) - (t.position ?? 0)).filter((e) => {
|
|
1562
1595
|
let t = e.position;
|
|
@@ -1572,7 +1605,7 @@ BEGIN
|
|
|
1572
1605
|
...a ? { expression: !0 } : {}
|
|
1573
1606
|
});
|
|
1574
1607
|
}
|
|
1575
|
-
for (let t of
|
|
1608
|
+
for (let t of x.get(e.table_name) ?? []) l.push(t);
|
|
1576
1609
|
r.size > 0 && l.push({
|
|
1577
1610
|
name: `${e.table_name}_pkey`,
|
|
1578
1611
|
table: e.table_name,
|
|
@@ -1589,11 +1622,11 @@ BEGIN
|
|
|
1589
1622
|
}
|
|
1590
1623
|
return { tables: ne };
|
|
1591
1624
|
});
|
|
1592
|
-
return (t > 0 ? e.withTransaction(n) : n).pipe(
|
|
1625
|
+
return (t > 0 ? e.withTransaction(n) : n).pipe(p.catchAll((e) => {
|
|
1593
1626
|
let n = `${String(e)} ${String(e.cause ?? "")}`.toLowerCase();
|
|
1594
|
-
return n.includes("statement timeout") || n.includes("57014") ?
|
|
1627
|
+
return n.includes("statement timeout") || n.includes("57014") ? p.die(/* @__PURE__ */ Error(`schema introspection exceeded VOLTRO_INTROSPECT_TIMEOUT_MS (${t}ms) — the schema is very large / FK-dense or the database is slow. Raise VOLTRO_INTROSPECT_TIMEOUT_MS, set it to 0 to disable the ceiling, or use DB_DIRECT_URL for a non-pooler connection.`)) : p.fail(e);
|
|
1595
1628
|
}));
|
|
1596
|
-
},
|
|
1629
|
+
}, Qt = (e, t) => {
|
|
1597
1630
|
switch (e.toLowerCase()) {
|
|
1598
1631
|
case "varchar":
|
|
1599
1632
|
case "char":
|
|
@@ -1625,21 +1658,21 @@ BEGIN
|
|
|
1625
1658
|
case "json": return "json";
|
|
1626
1659
|
default: return "text";
|
|
1627
1660
|
}
|
|
1628
|
-
},
|
|
1661
|
+
}, B = (e) => e.map((e) => {
|
|
1629
1662
|
let t = e, n = {};
|
|
1630
1663
|
for (let e of Object.keys(t)) n[e.toLowerCase()] = t[e];
|
|
1631
1664
|
return n;
|
|
1632
|
-
}),
|
|
1633
|
-
let t =
|
|
1665
|
+
}), $t = (e) => p.gen(function* () {
|
|
1666
|
+
let t = B(yield* e`
|
|
1634
1667
|
SELECT table_name, COALESCE(table_rows, 0) AS table_rows
|
|
1635
1668
|
FROM information_schema.tables
|
|
1636
1669
|
WHERE table_schema = DATABASE() AND table_type = 'BASE TABLE'
|
|
1637
|
-
ORDER BY table_name`), n =
|
|
1670
|
+
ORDER BY table_name`), n = B(yield* e`
|
|
1638
1671
|
SELECT table_name, column_name, data_type, column_type, is_nullable, column_default, column_key,
|
|
1639
1672
|
character_maximum_length
|
|
1640
1673
|
FROM information_schema.columns
|
|
1641
1674
|
WHERE table_schema = DATABASE()
|
|
1642
|
-
ORDER BY table_name, ordinal_position`), r =
|
|
1675
|
+
ORDER BY table_name, ordinal_position`), r = B(yield* e`
|
|
1643
1676
|
SELECT k.table_name AS source_table,
|
|
1644
1677
|
k.column_name AS source_column,
|
|
1645
1678
|
k.referenced_table_name AS target_table,
|
|
@@ -1649,21 +1682,21 @@ BEGIN
|
|
|
1649
1682
|
JOIN information_schema.referential_constraints r
|
|
1650
1683
|
ON k.constraint_name = r.constraint_name
|
|
1651
1684
|
AND k.table_schema = r.constraint_schema
|
|
1652
|
-
WHERE k.table_schema = DATABASE() AND k.referenced_table_name IS NOT NULL`), i =
|
|
1685
|
+
WHERE k.table_schema = DATABASE() AND k.referenced_table_name IS NOT NULL`), i = B(yield* e`
|
|
1653
1686
|
SELECT table_name, index_name, non_unique, column_name, seq_in_index
|
|
1654
1687
|
FROM information_schema.statistics
|
|
1655
1688
|
WHERE table_schema = DATABASE() AND index_name <> 'PRIMARY'
|
|
1656
|
-
ORDER BY table_name, index_name, seq_in_index`), a =
|
|
1689
|
+
ORDER BY table_name, index_name, seq_in_index`), a = B(yield* e`
|
|
1657
1690
|
SELECT table_name, check_clause
|
|
1658
1691
|
FROM information_schema.check_constraints
|
|
1659
|
-
WHERE constraint_schema = DATABASE()`.unprepared.pipe(
|
|
1692
|
+
WHERE constraint_schema = DATABASE()`.unprepared.pipe(p.orElseSucceed(() => []))), o = /* @__PURE__ */ new Set(), s = /* @__PURE__ */ new Map();
|
|
1660
1693
|
for (let e of a) {
|
|
1661
1694
|
let t = /json_valid\(`?([^`)]+)`?\)/i.exec(e.check_clause);
|
|
1662
1695
|
if (t) {
|
|
1663
1696
|
o.add(`${e.table_name}.${t[1]}`);
|
|
1664
1697
|
continue;
|
|
1665
1698
|
}
|
|
1666
|
-
let n =
|
|
1699
|
+
let n = Wt(e.check_clause);
|
|
1667
1700
|
n && s.set(`${e.table_name}.${n.column}`, n.values);
|
|
1668
1701
|
}
|
|
1669
1702
|
let c = /* @__PURE__ */ new Map(), l = /* @__PURE__ */ new Map(), u = /* @__PURE__ */ new Map();
|
|
@@ -1681,22 +1714,22 @@ BEGIN
|
|
|
1681
1714
|
let n = t.get(e.index_name) ?? [];
|
|
1682
1715
|
n.push(e), t.set(e.index_name, n);
|
|
1683
1716
|
}
|
|
1684
|
-
let
|
|
1717
|
+
let d = [];
|
|
1685
1718
|
for (let e of t) {
|
|
1686
|
-
let t = c.get(e.table_name) ?? [], n = l.get(e.table_name) ?? [], r = new Map(n.map((e) => [e.source_column, e])), i = t.filter((e) => e.column_key === "PRI").map((e) => e.column_name), a = /* @__PURE__ */ new Set(),
|
|
1687
|
-
if (
|
|
1719
|
+
let t = c.get(e.table_name) ?? [], n = l.get(e.table_name) ?? [], r = new Map(n.map((e) => [e.source_column, e])), i = t.filter((e) => e.column_key === "PRI").map((e) => e.column_name), a = /* @__PURE__ */ new Set(), f = u.get(e.table_name);
|
|
1720
|
+
if (f) for (let e of f.values()) e.length === 1 && e[0].non_unique === 0 && a.add(e[0].column_name);
|
|
1688
1721
|
let p = t.map((t) => {
|
|
1689
|
-
let n = i.includes(t.column_name) && i.length === 1, c = r.get(t.column_name), l = c ? "reference" : n && t.column_name === "id" ? "id" : o.has(`${e.table_name}.${t.column_name}`) ? "json" :
|
|
1722
|
+
let n = i.includes(t.column_name) && i.length === 1, c = r.get(t.column_name), l = c ? "reference" : n && t.column_name === "id" ? "id" : o.has(`${e.table_name}.${t.column_name}`) ? "json" : Qt(t.data_type, t.column_type), u = c ? {
|
|
1690
1723
|
table: c.target_table,
|
|
1691
1724
|
column: c.target_column,
|
|
1692
|
-
onDelete:
|
|
1693
|
-
onUpdate:
|
|
1725
|
+
onDelete: z(c.delete_rule),
|
|
1726
|
+
onUpdate: z(c.update_rule)
|
|
1694
1727
|
} : void 0, d = a.has(t.column_name) || n && t.column_name === "id", f = t.column_default === "NULL" ? null : t.column_default, p = s.get(`${e.table_name}.${t.column_name}`);
|
|
1695
1728
|
return {
|
|
1696
1729
|
name: t.column_name,
|
|
1697
1730
|
type: l,
|
|
1698
1731
|
nullable: t.is_nullable === "YES",
|
|
1699
|
-
...
|
|
1732
|
+
...H(t.data_type, t.character_maximum_length) === void 0 ? {} : { maxLength: H(t.data_type, t.character_maximum_length) },
|
|
1700
1733
|
unique: d,
|
|
1701
1734
|
hasDefault: f !== null,
|
|
1702
1735
|
...f === null ? {} : { defaultValue: f },
|
|
@@ -1704,7 +1737,7 @@ BEGIN
|
|
|
1704
1737
|
...p ? { oneOf: p } : {}
|
|
1705
1738
|
};
|
|
1706
1739
|
}), m = new Set(n.map((e) => e.source_column)), h = [];
|
|
1707
|
-
if (
|
|
1740
|
+
if (f) for (let [t, n] of f) {
|
|
1708
1741
|
let r = [...n].sort((e, t) => e.seq_in_index - t.seq_in_index);
|
|
1709
1742
|
r.length === 1 && r[0].non_unique === 0 || r.length === 1 && r[0].non_unique !== 0 && m.has(r[0].column_name) && t !== `${e.table_name}_${r[0].column_name}_idx` || h.push({
|
|
1710
1743
|
name: t,
|
|
@@ -1720,18 +1753,18 @@ BEGIN
|
|
|
1720
1753
|
unique: !0
|
|
1721
1754
|
});
|
|
1722
1755
|
let g = Number(e.table_rows ?? 0);
|
|
1723
|
-
|
|
1756
|
+
d.push({
|
|
1724
1757
|
name: e.table_name,
|
|
1725
1758
|
columns: p,
|
|
1726
1759
|
indexes: h,
|
|
1727
1760
|
...g > 0 ? { rowCount: g } : {}
|
|
1728
1761
|
});
|
|
1729
1762
|
}
|
|
1730
|
-
return { tables:
|
|
1731
|
-
}),
|
|
1763
|
+
return { tables: d };
|
|
1764
|
+
}), en = (e) => {
|
|
1732
1765
|
let t = e.toUpperCase();
|
|
1733
1766
|
return t.includes("INT") ? "integer" : t.includes("REAL") || t.includes("FLOA") || t.includes("DOUB") ? "real" : t.includes("BLOB") ? "bytes" : "text";
|
|
1734
|
-
},
|
|
1767
|
+
}, tn = (e) => p.gen(function* () {
|
|
1735
1768
|
let t = yield* e`
|
|
1736
1769
|
SELECT name, sql FROM sqlite_master
|
|
1737
1770
|
WHERE type = 'table' AND name NOT LIKE 'sqlite_%'
|
|
@@ -1748,11 +1781,11 @@ BEGIN
|
|
|
1748
1781
|
d.set(t.name, r), t.unique === 1 && r.length === 1 && f.add(r[0]);
|
|
1749
1782
|
}
|
|
1750
1783
|
let p = o.map((e) => {
|
|
1751
|
-
let n = u.length === 1 && u[0] === e.name, r = l.get(e.name), i = r ? "reference" : n && e.name === "id" ? "id" :
|
|
1784
|
+
let n = u.length === 1 && u[0] === e.name, r = l.get(e.name), i = r ? "reference" : n && e.name === "id" ? "id" : en(e.type), a = r ? {
|
|
1752
1785
|
table: r.table,
|
|
1753
1786
|
column: r.to,
|
|
1754
|
-
onDelete:
|
|
1755
|
-
onUpdate:
|
|
1787
|
+
onDelete: z(r.on_delete),
|
|
1788
|
+
onUpdate: z(r.on_update)
|
|
1756
1789
|
} : void 0, o = t.get(e.name);
|
|
1757
1790
|
return {
|
|
1758
1791
|
name: e.name,
|
|
@@ -1787,7 +1820,7 @@ BEGIN
|
|
|
1787
1820
|
});
|
|
1788
1821
|
}
|
|
1789
1822
|
return { tables: r };
|
|
1790
|
-
}),
|
|
1823
|
+
}), nn = (e) => {
|
|
1791
1824
|
switch (e.toLowerCase()) {
|
|
1792
1825
|
case "int":
|
|
1793
1826
|
case "smallint":
|
|
@@ -1809,7 +1842,7 @@ BEGIN
|
|
|
1809
1842
|
case "image": return "bytes";
|
|
1810
1843
|
default: return "text";
|
|
1811
1844
|
}
|
|
1812
|
-
},
|
|
1845
|
+
}, V = (e) => e === !0 || e === 1, rn = (e) => p.gen(function* () {
|
|
1813
1846
|
let t = yield* e`
|
|
1814
1847
|
SELECT TABLE_NAME AS table_name FROM INFORMATION_SCHEMA.TABLES
|
|
1815
1848
|
WHERE TABLE_TYPE = 'BASE TABLE' ORDER BY TABLE_NAME`, n = yield* e`
|
|
@@ -1866,21 +1899,21 @@ BEGIN
|
|
|
1866
1899
|
let t = s.get(e) ?? [], n = c.get(e) ?? [], r = new Map(n.map((e) => [e.source_column, e])), i = new Set(n.map((e) => e.source_column)), a = l.get(e), d = [], f = /* @__PURE__ */ new Set();
|
|
1867
1900
|
if (a) for (let [, e] of a) {
|
|
1868
1901
|
let t = [...e].sort((e, t) => e.key_ordinal - t.key_ordinal);
|
|
1869
|
-
if (
|
|
1870
|
-
else
|
|
1902
|
+
if (V(t[0].is_primary)) for (let e of t) d.push(e.column_name);
|
|
1903
|
+
else V(t[0].is_unique) && t.length === 1 && f.add(t[0].column_name);
|
|
1871
1904
|
}
|
|
1872
1905
|
let p = t.map((t) => {
|
|
1873
|
-
let n = d.length === 1 && d[0] === t.column_name, i = r.get(t.column_name), a = i ? "reference" : n && t.column_name === "id" ? "id" :
|
|
1906
|
+
let n = d.length === 1 && d[0] === t.column_name, i = r.get(t.column_name), a = i ? "reference" : n && t.column_name === "id" ? "id" : nn(t.data_type), s = i ? {
|
|
1874
1907
|
table: i.target_table,
|
|
1875
1908
|
column: i.target_column,
|
|
1876
|
-
onDelete:
|
|
1877
|
-
onUpdate:
|
|
1878
|
-
} : void 0, c = t.column_default === null ? null :
|
|
1909
|
+
onDelete: an(i.delete_rule),
|
|
1910
|
+
onUpdate: an(i.update_rule)
|
|
1911
|
+
} : void 0, c = t.column_default === null ? null : on(t.column_default), l = o.get(`${e}.${t.column_name}`);
|
|
1879
1912
|
return {
|
|
1880
1913
|
name: t.column_name,
|
|
1881
1914
|
type: a,
|
|
1882
1915
|
nullable: t.is_nullable === "YES",
|
|
1883
|
-
...
|
|
1916
|
+
...H(t.data_type, t.character_maximum_length) === void 0 ? {} : { maxLength: H(t.data_type, t.character_maximum_length) },
|
|
1884
1917
|
unique: f.has(t.column_name) || n && t.column_name === "id",
|
|
1885
1918
|
hasDefault: c !== null,
|
|
1886
1919
|
...c === null ? {} : { defaultValue: c },
|
|
@@ -1890,11 +1923,11 @@ BEGIN
|
|
|
1890
1923
|
}), m = [];
|
|
1891
1924
|
if (a) for (let [t, n] of a) {
|
|
1892
1925
|
let r = [...n].sort((e, t) => e.key_ordinal - t.key_ordinal);
|
|
1893
|
-
|
|
1926
|
+
V(r[0].is_primary) || r.length === 1 && V(r[0].is_unique) || r.length === 1 && !V(r[0].is_unique) && i.has(r[0].column_name) && t !== `${e}_${r[0].column_name}_idx` || m.push({
|
|
1894
1927
|
name: t,
|
|
1895
1928
|
table: e,
|
|
1896
1929
|
columns: r.map((e) => e.column_name),
|
|
1897
|
-
unique:
|
|
1930
|
+
unique: V(r[0].is_unique)
|
|
1898
1931
|
});
|
|
1899
1932
|
}
|
|
1900
1933
|
d.length > 0 && m.push({
|
|
@@ -1909,48 +1942,48 @@ BEGIN
|
|
|
1909
1942
|
});
|
|
1910
1943
|
}
|
|
1911
1944
|
return { tables: u };
|
|
1912
|
-
}),
|
|
1945
|
+
}), an = (e) => {
|
|
1913
1946
|
switch (e.toUpperCase().replace(/_/g, " ")) {
|
|
1914
1947
|
case "CASCADE": return "cascade";
|
|
1915
1948
|
case "SET NULL": return "setNull";
|
|
1916
1949
|
case "SET DEFAULT": return "noAction";
|
|
1917
1950
|
default: return "noAction";
|
|
1918
1951
|
}
|
|
1919
|
-
},
|
|
1952
|
+
}, on = (e) => {
|
|
1920
1953
|
let t = e.trim();
|
|
1921
1954
|
for (; t.startsWith("(") && t.endsWith(")");) t = t.slice(1, -1).trim();
|
|
1922
1955
|
return t.startsWith("N'") && t.endsWith("'") ? t = t.slice(2, -1) : t.startsWith("'") && t.endsWith("'") && (t = t.slice(1, -1)), t;
|
|
1923
|
-
},
|
|
1956
|
+
}, sn = /* @__PURE__ */ new Set([
|
|
1924
1957
|
"character varying",
|
|
1925
1958
|
"varchar",
|
|
1926
1959
|
"nvarchar",
|
|
1927
1960
|
"char",
|
|
1928
1961
|
"character",
|
|
1929
1962
|
"nchar"
|
|
1930
|
-
]),
|
|
1931
|
-
if (!
|
|
1963
|
+
]), H = (e, t) => {
|
|
1964
|
+
if (!sn.has(e.toLowerCase())) return;
|
|
1932
1965
|
let n = Number(t);
|
|
1933
1966
|
if (!(!Number.isFinite(n) || n <= 0)) return n;
|
|
1934
|
-
},
|
|
1935
|
-
mysql: () =>
|
|
1936
|
-
mssql: () =>
|
|
1937
|
-
sqlite: () =>
|
|
1938
|
-
orElse: () =>
|
|
1939
|
-
}),
|
|
1967
|
+
}, U = (e) => e.onDialectOrElse({
|
|
1968
|
+
mysql: () => $t(e),
|
|
1969
|
+
mssql: () => rn(e),
|
|
1970
|
+
sqlite: () => tn(e),
|
|
1971
|
+
orElse: () => Zt(e)
|
|
1972
|
+
}), cn = (e, t) => `${t}(${e.precision ?? 38}, ${e.scale ?? 0})`, ln = (e, t) => {
|
|
1940
1973
|
if (!(e.type === "text" && e.maxLength !== void 0) && !e.spatial && e.type !== "vector" && e.type !== "array" && e.type !== "enum") return;
|
|
1941
1974
|
let n = e.type === "vector" && e.vectorDim === void 0 ? "vector dimension" : e.type === "array" && e.arrayElement === void 0 ? "array element type" : e.type === "enum" && e.enumName === void 0 && e.enumValues === void 0 ? "enum name/values" : void 0;
|
|
1942
1975
|
if (n !== void 0) throw Error(`migration applier: cannot render '${e.name}' (${e.type}) — the schema snapshot carries no ${n}. Rendering it as 'text' would emit DDL that applies successfully and changes nothing, so this fails instead. This is a framework bug: declaredSchema.ts must carry the parameter into the ColumnSnapshot.`);
|
|
1943
|
-
return
|
|
1944
|
-
}, U = (e) => {
|
|
1945
|
-
let t = H(e, "postgres");
|
|
1946
|
-
return t === void 0 ? un(e) : t;
|
|
1976
|
+
return D(e, t);
|
|
1947
1977
|
}, un = (e) => {
|
|
1978
|
+
let t = ln(e, "postgres");
|
|
1979
|
+
return t === void 0 ? dn(e) : t;
|
|
1980
|
+
}, dn = (e) => {
|
|
1948
1981
|
switch (e.type) {
|
|
1949
1982
|
case "id": return "text";
|
|
1950
1983
|
case "text": return "text";
|
|
1951
1984
|
case "integer": return "integer";
|
|
1952
1985
|
case "real": return "double precision";
|
|
1953
|
-
case "decimal": return
|
|
1986
|
+
case "decimal": return cn(e, "NUMERIC");
|
|
1954
1987
|
case "bigint": return "bigint";
|
|
1955
1988
|
case "boolean": return "boolean";
|
|
1956
1989
|
case "timestamp": return "timestamptz";
|
|
@@ -1964,15 +1997,15 @@ BEGIN
|
|
|
1964
1997
|
case "interval": return "interval";
|
|
1965
1998
|
case "raw": return e.rawDdl ?? "text";
|
|
1966
1999
|
}
|
|
1967
|
-
},
|
|
1968
|
-
let t =
|
|
2000
|
+
}, fn = (e) => {
|
|
2001
|
+
let t = ln(e, "mariadb");
|
|
1969
2002
|
if (t !== void 0) return t;
|
|
1970
2003
|
switch (e.type) {
|
|
1971
2004
|
case "id": return "VARCHAR(64)";
|
|
1972
2005
|
case "text": return e.hasDefault && e.defaultValue !== void 0 || e.oneOf && e.oneOf.length > 0 ? "VARCHAR(255)" : "LONGTEXT";
|
|
1973
2006
|
case "integer": return "INT";
|
|
1974
2007
|
case "real": return "DOUBLE";
|
|
1975
|
-
case "decimal": return
|
|
2008
|
+
case "decimal": return cn(e, "DECIMAL");
|
|
1976
2009
|
case "bigint": return "BIGINT";
|
|
1977
2010
|
case "boolean": return "TINYINT(1)";
|
|
1978
2011
|
case "timestamp": return "DATETIME(6)";
|
|
@@ -1986,82 +2019,82 @@ BEGIN
|
|
|
1986
2019
|
case "interval": return "BIGINT";
|
|
1987
2020
|
case "raw": return e.rawDdl ?? "LONGTEXT";
|
|
1988
2021
|
}
|
|
1989
|
-
},
|
|
2022
|
+
}, W = (e) => `"${e}"`, G = (e) => `\`${e.replace(/`/g, "``")}\``, pn = (e, t) => `ALTER TABLE ${t(e.table)} DROP COLUMN IF EXISTS ${t(e.column)};`, mn = (e) => `ALTER TABLE ${G(e.table)} DROP COLUMN IF EXISTS ${G(e.column)}, ALGORITHM=COPY;`, hn = (e, t) => `ALTER TABLE ${t(e.table)} RENAME COLUMN ${t(e.from)} TO ${t(e.to)};`, gn = (e, t, n) => `ALTER TABLE ${n(e.table)} RENAME COLUMN ${n(e.column)} TO ${n(`${e.column}${Bn(t)}`)};`, _n = (e, t, n) => e.hasDefault && e.defaultValue !== void 0 ? `ALTER TABLE ${t(e.table)} ALTER COLUMN ${t(e.column)} SET ${vn(e, n)};` : `ALTER TABLE ${t(e.table)} ALTER COLUMN ${t(e.column)} DROP DEFAULT;`, vn = (e, t) => {
|
|
1990
2023
|
let n = t(e.defaultValue);
|
|
1991
2024
|
if (n === null) throw Error(`migration applier: cannot render the declared default for '${e.table}.${e.column}' (${JSON.stringify(e.defaultValue)}). Emitting 'DEFAULT NULL' would apply cleanly and drop the default silently, so this fails instead. This is a framework bug — report the column type + default.`);
|
|
1992
2025
|
return n;
|
|
1993
|
-
},
|
|
1994
|
-
let r =
|
|
1995
|
-
return r ?
|
|
1996
|
-
},
|
|
1997
|
-
let r = n ?? e.nullable, i =
|
|
2026
|
+
}, K = (e, t, n) => e.find((e) => e.tableName === t)?.fields[n], yn = (e, t, n) => {
|
|
2027
|
+
let r = K(e, t, n);
|
|
2028
|
+
return r ? at(n, r) : void 0;
|
|
2029
|
+
}, bn = (e, t, n) => {
|
|
2030
|
+
let r = n ?? e.nullable, i = be(e, "mariadb");
|
|
1998
2031
|
return [
|
|
1999
|
-
|
|
2000
|
-
|
|
2032
|
+
G(t),
|
|
2033
|
+
D(e, "mariadb"),
|
|
2001
2034
|
r ? null : "NOT NULL",
|
|
2002
2035
|
i
|
|
2003
2036
|
].filter(Boolean).join(" ");
|
|
2004
|
-
},
|
|
2037
|
+
}, xn = (e, t) => e == null ? null : Array.isArray(e) ? Sn(e, t, "mariadb") : e === "now" ? "DEFAULT CURRENT_TIMESTAMP(6)" : typeof e == "boolean" ? `DEFAULT ${+!!e}` : typeof e == "number" ? `DEFAULT ${e}` : typeof e == "string" ? /[()]|^'/.test(e) ? `DEFAULT ${e}` : `DEFAULT '${e.replace(/'/g, "''")}'` : typeof e == "object" ? O(e, "mysql") : null, Sn = (e, t, n) => xe(e, t ?? { type: "json" }, n), Cn = (e, t) => e == null ? null : Array.isArray(e) ? Sn(e, t, "postgres") : e === "now" ? "DEFAULT now()" : typeof e == "boolean" || typeof e == "number" ? `DEFAULT ${e}` : typeof e == "string" ? /[()]|::|^'/.test(e) ? `DEFAULT ${e}` : `DEFAULT '${e.replace(/'/g, "''")}'` : typeof e == "object" ? O(e, "postgres") : null, q = (e, t) => {
|
|
2005
2038
|
if (!e.generatedAs) return null;
|
|
2006
2039
|
let { expr: n, stored: r } = e.generatedAs;
|
|
2007
2040
|
return t === "postgres" ? `GENERATED ALWAYS AS (${n}) STORED` : t === "mssql" ? `AS (${n})${r ? " PERSISTED" : ""}` : `GENERATED ALWAYS AS (${n}) ${r ? "STORED" : "VIRTUAL"}`;
|
|
2008
|
-
},
|
|
2009
|
-
if (e.type === "id" && e.idScheme?.kind === "numeric") return
|
|
2010
|
-
let t =
|
|
2011
|
-
if (t) return `${
|
|
2012
|
-
let n = [
|
|
2041
|
+
}, wn = (e) => {
|
|
2042
|
+
if (e.type === "id" && e.idScheme?.kind === "numeric") return k(e.name, "postgres");
|
|
2043
|
+
let t = q(e, "postgres");
|
|
2044
|
+
if (t) return `${W(e.name)} ${un(e)} ${t}`;
|
|
2045
|
+
let n = [W(e.name), un(e)];
|
|
2013
2046
|
if (e.type === "id" && n.push("PRIMARY KEY"), e.nullable || n.push("NOT NULL"), e.unique && e.type !== "id" && n.push("UNIQUE"), e.hasDefault) {
|
|
2014
|
-
let t =
|
|
2047
|
+
let t = Cn(e.defaultValue, e);
|
|
2015
2048
|
t && n.push(t);
|
|
2016
2049
|
}
|
|
2017
|
-
return e.references && (n.push(`REFERENCES ${
|
|
2050
|
+
return e.references && (n.push(`REFERENCES ${W(e.references.table)} (${W(e.references.column)})`), e.references.onDelete && n.push(`ON DELETE ${{
|
|
2018
2051
|
cascade: "CASCADE",
|
|
2019
2052
|
restrict: "RESTRICT",
|
|
2020
2053
|
setNull: "SET NULL",
|
|
2021
2054
|
noAction: "NO ACTION"
|
|
2022
2055
|
}[e.references.onDelete]}`)), n.join(" ");
|
|
2023
|
-
},
|
|
2024
|
-
let t = e.columns.map((e) => ` ${
|
|
2025
|
-
for (let t of e.indexes) t.name !== `${e.table}_pkey` && n.push(`CREATE ${t.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${
|
|
2056
|
+
}, Tn = (e, t) => e.primaryKey && e.primaryKey.length > 0 ? `,\n PRIMARY KEY (${e.primaryKey.map(t).join(", ")})` : "", En = (e) => {
|
|
2057
|
+
let t = e.columns.map((e) => ` ${wn(e)}`).join(",\n"), n = [`CREATE TABLE IF NOT EXISTS ${W(e.table)} (\n${t}${Tn(e, W)}\n)`];
|
|
2058
|
+
for (let t of e.indexes) t.name !== `${e.table}_pkey` && n.push(`CREATE ${t.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${W(t.name)} ON ${W(e.table)} (${t.columns.map(Gn).join(", ")})`);
|
|
2026
2059
|
return n;
|
|
2027
|
-
},
|
|
2028
|
-
let t =
|
|
2060
|
+
}, Dn = 191, J = (e) => {
|
|
2061
|
+
let t = fn(e).toUpperCase();
|
|
2029
2062
|
return t.endsWith("TEXT") || t.endsWith("BLOB");
|
|
2030
|
-
},
|
|
2031
|
-
if (e.type === "id" && e.idScheme?.kind === "numeric") return
|
|
2032
|
-
let t =
|
|
2033
|
-
if (t) return `${
|
|
2034
|
-
let n = [
|
|
2035
|
-
if (e.type === "id" && n.push("PRIMARY KEY"), e.nullable || n.push("NOT NULL"), e.unique && e.type !== "id" && !
|
|
2036
|
-
let t =
|
|
2063
|
+
}, On = (e) => {
|
|
2064
|
+
if (e.type === "id" && e.idScheme?.kind === "numeric") return k(e.name, "mariadb");
|
|
2065
|
+
let t = q(e, "mariadb");
|
|
2066
|
+
if (t) return `${G(e.name)} ${fn(e)} ${t}`;
|
|
2067
|
+
let n = [G(e.name), fn(e)];
|
|
2068
|
+
if (e.type === "id" && n.push("PRIMARY KEY"), e.nullable || n.push("NOT NULL"), e.unique && e.type !== "id" && !J(e) && n.push("UNIQUE"), e.hasDefault) {
|
|
2069
|
+
let t = xn(e.defaultValue, e);
|
|
2037
2070
|
t && n.push(t);
|
|
2038
2071
|
}
|
|
2039
|
-
return e.references && (n.push(`REFERENCES ${
|
|
2072
|
+
return e.references && (n.push(`REFERENCES ${G(e.references.table)} (${G(e.references.column)})`), e.references.onDelete && n.push(`ON DELETE ${{
|
|
2040
2073
|
cascade: "CASCADE",
|
|
2041
2074
|
restrict: "RESTRICT",
|
|
2042
2075
|
setNull: "SET NULL",
|
|
2043
2076
|
noAction: "NO ACTION"
|
|
2044
2077
|
}[e.references.onDelete]}`)), n.join(" ");
|
|
2045
|
-
},
|
|
2078
|
+
}, kn = (e) => {
|
|
2046
2079
|
let t = new Map(e.columns.map((e) => [e.name, e])), n = (e) => {
|
|
2047
2080
|
let n = t.get(e);
|
|
2048
|
-
return n &&
|
|
2049
|
-
}, r = e.columns.map((e) => ` ${
|
|
2050
|
-
for (let t of e.columns) t.unique && t.type !== "id" &&
|
|
2051
|
-
for (let t of e.indexes) t.name !== `${e.table}_pkey` && i.push(`CREATE ${t.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${
|
|
2081
|
+
return n && J(n) ? `${G(e)}(${Dn})` : G(e);
|
|
2082
|
+
}, r = e.columns.map((e) => ` ${On(e)}`).join(",\n"), i = [`CREATE TABLE IF NOT EXISTS ${G(e.table)} (\n${r}${Tn(e, n)}\n)`];
|
|
2083
|
+
for (let t of e.columns) t.unique && t.type !== "id" && J(t) && i.push(`CREATE UNIQUE INDEX IF NOT EXISTS ${G(`${e.table}_${t.name}_key`)} ON ${G(e.table)} (${n(t.name)})`);
|
|
2084
|
+
for (let t of e.indexes) t.name !== `${e.table}_pkey` && i.push(`CREATE ${t.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${G(t.name)} ON ${G(e.table)} (${t.columns.map(n).join(", ")})`);
|
|
2052
2085
|
return i;
|
|
2053
|
-
},
|
|
2086
|
+
}, An = {
|
|
2054
2087
|
cascade: "CASCADE",
|
|
2055
2088
|
restrict: "RESTRICT",
|
|
2056
2089
|
setNull: "SET NULL",
|
|
2057
2090
|
noAction: "NO ACTION"
|
|
2058
|
-
},
|
|
2091
|
+
}, jn = {
|
|
2059
2092
|
cascade: "CASCADE",
|
|
2060
2093
|
restrict: "NO ACTION",
|
|
2061
2094
|
setNull: "SET NULL",
|
|
2062
2095
|
noAction: "NO ACTION"
|
|
2063
|
-
},
|
|
2064
|
-
let t =
|
|
2096
|
+
}, Mn = (e) => {
|
|
2097
|
+
let t = ln(e, "sqlite");
|
|
2065
2098
|
if (t !== void 0) return t;
|
|
2066
2099
|
switch (e.type) {
|
|
2067
2100
|
case "integer":
|
|
@@ -2072,60 +2105,60 @@ BEGIN
|
|
|
2072
2105
|
case "bytes": return "BLOB";
|
|
2073
2106
|
default: return "TEXT";
|
|
2074
2107
|
}
|
|
2075
|
-
}, Y = (e, t) => e == null ? null : e === "now" ? `DEFAULT ${t}` : typeof e == "boolean" ? `DEFAULT ${+!!e}` : typeof e == "number" ? `DEFAULT ${e}` : typeof e == "string" ? /[()]|^'/.test(e) ? `DEFAULT ${e}` : `DEFAULT '${e.replace(/'/g, "''")}'` : typeof e == "object" ?
|
|
2076
|
-
if (e.type === "id" && e.idScheme?.kind === "numeric") return
|
|
2077
|
-
let t =
|
|
2078
|
-
if (t) return `${
|
|
2079
|
-
let n = [
|
|
2108
|
+
}, Y = (e, t) => e == null ? null : e === "now" ? `DEFAULT ${t}` : typeof e == "boolean" ? `DEFAULT ${+!!e}` : typeof e == "number" ? `DEFAULT ${e}` : typeof e == "string" ? /[()]|^'/.test(e) ? `DEFAULT ${e}` : `DEFAULT '${e.replace(/'/g, "''")}'` : typeof e == "object" ? O(e, "sqlite") : null, Nn = (e) => {
|
|
2109
|
+
if (e.type === "id" && e.idScheme?.kind === "numeric") return k(e.name, "sqlite");
|
|
2110
|
+
let t = q(e, "sqlite");
|
|
2111
|
+
if (t) return `${W(e.name)} ${Mn(e)} ${t}`;
|
|
2112
|
+
let n = [W(e.name), Mn(e)];
|
|
2080
2113
|
if (e.type === "id" && n.push("PRIMARY KEY"), e.nullable || n.push("NOT NULL"), e.unique && e.type !== "id" && n.push("UNIQUE"), e.hasDefault) {
|
|
2081
2114
|
let t = Y(e.defaultValue, "CURRENT_TIMESTAMP");
|
|
2082
2115
|
t && n.push(t);
|
|
2083
2116
|
}
|
|
2084
|
-
if (e.references && (n.push(`REFERENCES ${
|
|
2117
|
+
if (e.references && (n.push(`REFERENCES ${W(e.references.table)} (${W(e.references.column)})`), e.references.onDelete && n.push(`ON DELETE ${An[e.references.onDelete]}`)), e.oneOf && e.oneOf.length > 0) {
|
|
2085
2118
|
let t = e.oneOf.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ");
|
|
2086
|
-
n.push(`CHECK (${
|
|
2119
|
+
n.push(`CHECK (${W(e.name)} IN (${t}))`);
|
|
2087
2120
|
}
|
|
2088
2121
|
return n.join(" ");
|
|
2089
|
-
},
|
|
2090
|
-
let t = e.columns.map((e) => ` ${
|
|
2091
|
-
for (let t of e.indexes) t.name !== `${e.table}_pkey` && n.push(`CREATE ${t.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${
|
|
2122
|
+
}, Pn = (e) => {
|
|
2123
|
+
let t = e.columns.map((e) => ` ${Nn(e)}`).join(",\n"), n = [`CREATE TABLE IF NOT EXISTS ${W(e.table)} (\n${t}${Tn(e, W)}\n)`];
|
|
2124
|
+
for (let t of e.indexes) t.name !== `${e.table}_pkey` && n.push(`CREATE ${t.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${W(t.name)} ON ${W(e.table)} (${t.columns.map(W).join(", ")})`);
|
|
2092
2125
|
return n;
|
|
2093
2126
|
}, X = (e, t) => {
|
|
2094
2127
|
let n = t.find((t) => t.tableName === e);
|
|
2095
2128
|
if (!n) return [];
|
|
2096
|
-
let r =
|
|
2097
|
-
...
|
|
2129
|
+
let r = F([n]).tables[0], i = `${e}__voltro_rebuild`, a = r.columns.map((e) => W(e.name)).join(", "), o = [
|
|
2130
|
+
...Pn({
|
|
2098
2131
|
kind: "create-table",
|
|
2099
2132
|
table: i,
|
|
2100
2133
|
columns: r.columns,
|
|
2101
2134
|
indexes: [],
|
|
2102
2135
|
...r.primaryKey ? { primaryKey: r.primaryKey } : {}
|
|
2103
2136
|
}),
|
|
2104
|
-
`INSERT INTO ${
|
|
2105
|
-
`DROP TABLE ${
|
|
2106
|
-
`ALTER TABLE ${
|
|
2137
|
+
`INSERT INTO ${W(i)} (${a}) SELECT ${a} FROM ${W(e)}`,
|
|
2138
|
+
`DROP TABLE ${W(e)}`,
|
|
2139
|
+
`ALTER TABLE ${W(i)} RENAME TO ${W(e)}`
|
|
2107
2140
|
];
|
|
2108
|
-
for (let t of r.indexes) t.name !== `${e}_pkey` && o.push(`CREATE ${t.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${
|
|
2141
|
+
for (let t of r.indexes) t.name !== `${e}_pkey` && o.push(`CREATE ${t.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${W(t.name)} ON ${W(e)} (${t.columns.map(W).join(", ")})`);
|
|
2109
2142
|
return o;
|
|
2110
|
-
},
|
|
2143
|
+
}, Fn = (e, t, n, r) => {
|
|
2111
2144
|
let i = r ?? e.nullable;
|
|
2112
|
-
return `ALTER TABLE ${Z(n)} ALTER COLUMN ${Z(t)} ${
|
|
2113
|
-
},
|
|
2145
|
+
return `ALTER TABLE ${Z(n)} ALTER COLUMN ${Z(t)} ${D(e, "mssql")} ${i ? "NULL" : "NOT NULL"};`;
|
|
2146
|
+
}, In = (e) => {
|
|
2114
2147
|
let t = `DECLARE @dc sysname; SELECT @dc = dc.name FROM sys.default_constraints dc JOIN sys.columns col ON col.object_id = dc.parent_object_id AND col.column_id = dc.parent_column_id WHERE dc.parent_object_id = OBJECT_ID(N'${e.table.replace(/'/g, "''")}') AND col.name = N'${e.column.replace(/'/g, "''")}'; IF @dc IS NOT NULL EXEC(N'ALTER TABLE ${Z(e.table)} DROP CONSTRAINT [' + @dc + ']');`;
|
|
2115
2148
|
if (!e.hasDefault) return t;
|
|
2116
2149
|
let n = Y(e.defaultValue, "SYSUTCDATETIME()");
|
|
2117
2150
|
if (!n) return t;
|
|
2118
2151
|
let r = n.replace(/^DEFAULT\s+/i, "");
|
|
2119
2152
|
return `${t} ALTER TABLE ${Z(e.table)} ADD DEFAULT ${r} FOR ${Z(e.column)};`;
|
|
2120
|
-
}, Z = (e) => `[${e.replace(/]/g, "]]")}]`,
|
|
2121
|
-
let t =
|
|
2153
|
+
}, Z = (e) => `[${e.replace(/]/g, "]]")}]`, Ln = (e) => {
|
|
2154
|
+
let t = ln(e, "mssql");
|
|
2122
2155
|
if (t !== void 0) return t;
|
|
2123
2156
|
switch (e.type) {
|
|
2124
2157
|
case "id":
|
|
2125
2158
|
case "reference": return "NVARCHAR(64)";
|
|
2126
2159
|
case "integer": return "INT";
|
|
2127
2160
|
case "real": return "FLOAT";
|
|
2128
|
-
case "decimal": return
|
|
2161
|
+
case "decimal": return cn(e, "DECIMAL");
|
|
2129
2162
|
case "bigint": return "BIGINT";
|
|
2130
2163
|
case "boolean": return "BIT";
|
|
2131
2164
|
case "timestamp": return "DATETIME2(6)";
|
|
@@ -2133,40 +2166,40 @@ BEGIN
|
|
|
2133
2166
|
case "bytes": return "VARBINARY(MAX)";
|
|
2134
2167
|
default: return "NVARCHAR(MAX)";
|
|
2135
2168
|
}
|
|
2136
|
-
},
|
|
2137
|
-
if (e.type === "id" && e.idScheme?.kind === "numeric") return
|
|
2138
|
-
let n = e.type === "text" && t?.has(e.name) ? "NVARCHAR(450)" :
|
|
2169
|
+
}, Rn = (e, t) => {
|
|
2170
|
+
if (e.type === "id" && e.idScheme?.kind === "numeric") return k(e.name, "mssql");
|
|
2171
|
+
let n = e.type === "text" && t?.has(e.name) ? "NVARCHAR(450)" : Ln(e), r = q(e, "mssql");
|
|
2139
2172
|
if (r) return `${Z(e.name)} ${r}`;
|
|
2140
2173
|
let i = [Z(e.name), n];
|
|
2141
2174
|
if (e.type === "id" && i.push("PRIMARY KEY"), e.nullable || i.push("NOT NULL"), e.unique && e.type !== "id" && i.push("UNIQUE"), e.hasDefault) {
|
|
2142
2175
|
let t = Y(e.defaultValue, "SYSUTCDATETIME()");
|
|
2143
2176
|
t && i.push(t);
|
|
2144
2177
|
}
|
|
2145
|
-
return e.references && (i.push(`REFERENCES ${Z(e.references.table)} (${Z(e.references.column)})`), e.references.onDelete && i.push(`ON DELETE ${
|
|
2146
|
-
},
|
|
2147
|
-
let t = (e) => e.replace(/'/g, "''"), n = new Set(e.indexes.flatMap((e) => e.columns)), r = e.columns.map((e) => ` ${
|
|
2178
|
+
return e.references && (i.push(`REFERENCES ${Z(e.references.table)} (${Z(e.references.column)})`), e.references.onDelete && i.push(`ON DELETE ${jn[e.references.onDelete]}`)), i.join(" ");
|
|
2179
|
+
}, zn = (e) => {
|
|
2180
|
+
let t = (e) => e.replace(/'/g, "''"), n = new Set(e.indexes.flatMap((e) => e.columns)), r = e.columns.map((e) => ` ${Rn(e, n)}`).join(",\n"), i = `CREATE TABLE ${Z(e.table)} (\n${r}${Tn(e, Z)}\n)`, a = [`IF OBJECT_ID(N'${t(e.table)}', N'U') IS NULL EXEC(N'${t(i)}')`];
|
|
2148
2181
|
for (let n of e.indexes) {
|
|
2149
2182
|
if (n.name === `${e.table}_pkey`) continue;
|
|
2150
2183
|
let r = `CREATE ${n.unique ? "UNIQUE " : ""}INDEX ${Z(n.name)} ON ${Z(e.table)} (${n.columns.map(Z).join(", ")})`;
|
|
2151
2184
|
a.push(`IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'${t(n.name)}' AND object_id = OBJECT_ID(N'${t(e.table)}')) EXEC(N'${t(r)}')`);
|
|
2152
2185
|
}
|
|
2153
2186
|
return a;
|
|
2154
|
-
},
|
|
2155
|
-
let r = `${e.table}${
|
|
2187
|
+
}, Bn = (e) => `__dropped_${e.replace(/[-:T.Z]/g, "").slice(0, 14)}`, Vn = (e, t) => `DROP TABLE IF EXISTS ${t(e.table)};`, Hn = (e, t, n) => {
|
|
2188
|
+
let r = `${e.table}${Bn(t)}`;
|
|
2156
2189
|
return `ALTER TABLE ${n(e.table)} RENAME TO ${n(r)};`;
|
|
2157
|
-
},
|
|
2158
|
-
let r =
|
|
2159
|
-
return r ?
|
|
2160
|
-
},
|
|
2190
|
+
}, Un = (e, t, n) => {
|
|
2191
|
+
let r = K(e, t, n);
|
|
2192
|
+
return r ? J({ type: r.type }) : !1;
|
|
2193
|
+
}, Wn = (e, t, n) => Un(e, t, n) ? `${G(n)}(${Dn})` : G(n), Gn = (e) => e.startsWith("(") ? e : W(e), Kn = (e, t) => `CREATE ${e.index.unique ? "UNIQUE " : ""}INDEX ${t ? "CONCURRENTLY " : ""}IF NOT EXISTS ${W(e.index.name)} ON ${W(e.table)} (${e.index.columns.map(Gn).join(", ")});`, qn = (e, t) => `CREATE ${e.index.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${G(e.index.name)} ON ${G(e.table)} (${e.index.columns.map((n) => Wn(t, e.table, n)).join(", ")});`, Jn = (e) => `DROP INDEX IF EXISTS ${W(e.index)};`, Yn = (e) => `DROP INDEX ${G(e.index)} ON ${G(e.table)};`, Xn = (e) => `DROP INDEX IF EXISTS ${Z(e.index)} ON ${Z(e.table)};`, Zn = (e, t) => `ALTER TABLE ${t(e.from)} RENAME TO ${t(e.to)};`, Qn = (e) => {
|
|
2161
2194
|
let t = (e) => e.replace(/'/g, "''");
|
|
2162
2195
|
return `EXEC sp_rename N'${t(e.from)}', N'${t(e.to)}';`;
|
|
2163
|
-
},
|
|
2196
|
+
}, $n = (e) => `ALTER INDEX ${W(e.from)} RENAME TO ${W(e.to)};`, er = (e) => `ALTER TABLE ${G(e.table)} RENAME INDEX ${G(e.from)} TO ${G(e.to)};`, tr = (e) => {
|
|
2164
2197
|
let t = (e) => e.replace(/'/g, "''");
|
|
2165
2198
|
return `EXEC sp_rename N'${t(e.table)}.${t(e.from)}', N'${t(e.to)}', N'INDEX';`;
|
|
2166
|
-
},
|
|
2199
|
+
}, nr = (e) => {
|
|
2167
2200
|
let t = (e) => e.replace(/'/g, "''"), n = `CREATE ${e.index.unique ? "UNIQUE " : ""}INDEX ${Z(e.index.name)} ON ${Z(e.table)} (${e.index.columns.map(Z).join(", ")})`;
|
|
2168
2201
|
return `IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'${t(e.index.name)}' AND object_id = OBJECT_ID(N'${t(e.table)}')) EXEC(N'${t(n)}');`;
|
|
2169
|
-
},
|
|
2202
|
+
}, rr = (e, t, n) => {
|
|
2170
2203
|
let r = n.find((t) => t.tableName === e);
|
|
2171
2204
|
if (!r) return [];
|
|
2172
2205
|
let i = [];
|
|
@@ -2175,13 +2208,13 @@ BEGIN
|
|
|
2175
2208
|
t && t.type === "text" && !t.generatedAs && i.push(`ALTER TABLE ${Z(e)} ALTER COLUMN ${Z(n)} NVARCHAR(450) ${t.nullable ? "NULL" : "NOT NULL"};`);
|
|
2176
2209
|
}
|
|
2177
2210
|
return i;
|
|
2178
|
-
},
|
|
2179
|
-
let t = e.fields.map((e) =>
|
|
2180
|
-
return `ALTER TABLE ${
|
|
2181
|
-
},
|
|
2182
|
-
let n = e.fields.map((n) =>
|
|
2183
|
-
return `ALTER TABLE ${
|
|
2184
|
-
},
|
|
2211
|
+
}, ir = (e) => `ALTER TABLE ${W(e.table)} ADD CONSTRAINT ${W(`${e.table}_${e.column}_key`)} UNIQUE (${W(e.column)});`, ar = (e, t) => `ALTER TABLE ${G(e.table)} ADD CONSTRAINT ${G(`${e.table}_${e.column}_key`)} UNIQUE (${Wn(t, e.table, e.column)});`, or = (e) => {
|
|
2212
|
+
let t = e.fields.map((e) => W(e)).join(", ");
|
|
2213
|
+
return `ALTER TABLE ${W(e.table)} ADD CONSTRAINT ${W(e.name)} UNIQUE (${t});`;
|
|
2214
|
+
}, sr = (e, t) => {
|
|
2215
|
+
let n = e.fields.map((n) => Wn(t, e.table, n)).join(", ");
|
|
2216
|
+
return `ALTER TABLE ${G(e.table)} ADD CONSTRAINT ${G(e.name)} UNIQUE (${n});`;
|
|
2217
|
+
}, cr = (e) => `ALTER TABLE ${W(e.table)} DROP CONSTRAINT IF EXISTS ${W(e.name)};`, lr = (e) => `ALTER TABLE ${G(e.table)} DROP INDEX ${G(e.name)};`, ur = (e) => `ALTER TABLE ${W(e.table)} DROP CONSTRAINT IF EXISTS ${W(`${e.table}_${e.column}_key`)};`, dr = (e) => `ALTER TABLE ${G(e.table)} DROP INDEX ${G(`${e.table}_${e.column}_key`)};`, fr = (e, t) => {
|
|
2185
2218
|
let n = [], r = t(e.table), i = t(e.column), a = t(e.targetTable), o = t(e.targetColumn);
|
|
2186
2219
|
if (e.orphanPolicy) {
|
|
2187
2220
|
let t = `${i} IS NOT NULL AND ${i} NOT IN (SELECT ${o} FROM ${a})`;
|
|
@@ -2199,11 +2232,11 @@ BEGIN
|
|
|
2199
2232
|
setNull: "SET NULL",
|
|
2200
2233
|
noAction: "NO ACTION"
|
|
2201
2234
|
}[e.onDelete]}`), n.push(s.join(" ") + ";"), n;
|
|
2202
|
-
},
|
|
2235
|
+
}, pr = (e) => `ALTER TABLE ${W(e.table)} DROP CONSTRAINT IF EXISTS ${W(`${e.table}_${e.column}_fkey`)};`, mr = (e) => `ALTER TABLE ${G(e.table)} DROP FOREIGN KEY ${G(`${e.table}_${e.column}_fkey`)};`, hr = (e, t) => {
|
|
2203
2236
|
let n = e.values.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ");
|
|
2204
2237
|
return `ALTER TABLE ${t(e.table)} ADD CONSTRAINT ${t(`${e.table}_${e.column}_check`)} CHECK (${t(e.column)} IN (${n}));`;
|
|
2205
|
-
},
|
|
2206
|
-
let i = e.column, a = r === "mariadb", o = a ?
|
|
2238
|
+
}, gr = (e) => `ALTER TABLE ${W(e.table)} DROP CONSTRAINT IF EXISTS ${W(`${e.table}_${e.column}_check`)};`, _r = (e) => `ALTER TABLE ${G(e.table)} DROP CHECK ${G(`${e.table}_${e.column}_check`)};`, vr = (e, t, n, r) => {
|
|
2239
|
+
let i = e.column, a = r === "mariadb", o = a ? G : r === "mssql" ? Z : W, s = a ? fn(i) : r === "mssql" ? Ln(i) : r === "sqlite" ? Mn(i) : un(i), c = r === "mssql" ? "ADD" : "ADD COLUMN", l = (e) => a ? xn(e, i) : r === "mssql" ? Y(e, "SYSUTCDATETIME()") : r === "sqlite" ? Y(e, "CURRENT_TIMESTAMP") : Cn(e, i), u = q(i, r);
|
|
2207
2240
|
if (u) {
|
|
2208
2241
|
let t = r === "mssql" ? `${o(i.name)} ${u}` : `${o(i.name)} ${s} ${u}`;
|
|
2209
2242
|
return {
|
|
@@ -2231,7 +2264,7 @@ BEGIN
|
|
|
2231
2264
|
post: f ? [f] : []
|
|
2232
2265
|
};
|
|
2233
2266
|
return d?.sql && (p.backfillKind = "sql", p.backfillSql = d.sql), d?.js && (p.backfillKind = "js", p.backfillJs = d.js), d?.batchSize !== void 0 && (p.batchSize = d.batchSize), d?.sleepMs !== void 0 && (p.sleepMs = d.sleepMs), p;
|
|
2234
|
-
},
|
|
2267
|
+
}, yr = (e) => {
|
|
2235
2268
|
let t = new Map(e.map((e) => [e.tableName, e]));
|
|
2236
2269
|
return (e, n) => {
|
|
2237
2270
|
let r = t.get(e);
|
|
@@ -2251,118 +2284,118 @@ BEGIN
|
|
|
2251
2284
|
...o
|
|
2252
2285
|
};
|
|
2253
2286
|
};
|
|
2254
|
-
}, Q = (e, t, n) => e.unsafe(t).pipe(
|
|
2255
|
-
process.stderr.write(
|
|
2256
|
-
})),
|
|
2257
|
-
let r =
|
|
2287
|
+
}, Q = (e, t, n) => e.unsafe(t).pipe(p.tapError((e) => p.sync(() => {
|
|
2288
|
+
process.stderr.write(b(`applier: statement failed (op=${n})`, t, e, 400));
|
|
2289
|
+
})), p.mapError((e) => {
|
|
2290
|
+
let r = b(`applier: statement failed (op=${n})`, t, e, 400).trim();
|
|
2258
2291
|
return Object.assign(e, { message: r });
|
|
2259
|
-
}),
|
|
2260
|
-
let r = process.hrtime.bigint(), i =
|
|
2292
|
+
}), p.map(() => void 0)), $ = (e, t, n) => p.forEach(t, (t) => Q(e, t, n), { discard: !0 }), br = (e, t, n) => p.gen(function* () {
|
|
2293
|
+
let r = process.hrtime.bigint(), i = yr(n.declared), a = t.op;
|
|
2261
2294
|
switch (a.kind) {
|
|
2262
2295
|
case "create-table": {
|
|
2263
2296
|
let t = yield* e.onDialectOrElse({
|
|
2264
|
-
mysql: () =>
|
|
2265
|
-
mssql: () =>
|
|
2266
|
-
sqlite: () =>
|
|
2267
|
-
orElse: () =>
|
|
2297
|
+
mysql: () => p.succeed(kn(a)),
|
|
2298
|
+
mssql: () => p.succeed(zn(a)),
|
|
2299
|
+
sqlite: () => p.succeed(Pn(a)),
|
|
2300
|
+
orElse: () => p.succeed(En(a))
|
|
2268
2301
|
});
|
|
2269
2302
|
for (let n of t) yield* Q(e, n, a.kind);
|
|
2270
2303
|
break;
|
|
2271
2304
|
}
|
|
2272
2305
|
case "drop-table": {
|
|
2273
|
-
let t = process.env.VOLTRO_SOFT_DROP === "1", n = (/* @__PURE__ */ new Date()).toISOString(), r = (e) => t ?
|
|
2306
|
+
let t = process.env.VOLTRO_SOFT_DROP === "1", n = (/* @__PURE__ */ new Date()).toISOString(), r = (e) => t ? Hn(a, n, e) : Vn(a, e);
|
|
2274
2307
|
yield* e.onDialectOrElse({
|
|
2275
|
-
mysql: () => Q(e, r(
|
|
2276
|
-
orElse: () => Q(e, r(
|
|
2308
|
+
mysql: () => Q(e, r(G), a.kind),
|
|
2309
|
+
orElse: () => Q(e, r(W), a.kind)
|
|
2277
2310
|
});
|
|
2278
2311
|
break;
|
|
2279
2312
|
}
|
|
2280
2313
|
case "drop-column": {
|
|
2281
2314
|
let t = process.env.VOLTRO_SOFT_DROP === "1", n = (/* @__PURE__ */ new Date()).toISOString();
|
|
2282
2315
|
yield* e.onDialectOrElse({
|
|
2283
|
-
mysql: () => Q(e, t ?
|
|
2284
|
-
orElse: () => Q(e, t ?
|
|
2316
|
+
mysql: () => Q(e, t ? gn(a, n, G) : mn(a), a.kind),
|
|
2317
|
+
orElse: () => Q(e, t ? gn(a, n, W) : pn(a, W), a.kind)
|
|
2285
2318
|
});
|
|
2286
2319
|
break;
|
|
2287
2320
|
}
|
|
2288
2321
|
case "rename-column":
|
|
2289
2322
|
yield* e.onDialectOrElse({
|
|
2290
|
-
mysql: () => Q(e,
|
|
2323
|
+
mysql: () => Q(e, hn(a, G), a.kind),
|
|
2291
2324
|
mssql: () => Q(e, `EXEC sp_rename N'${a.table.replace(/'/g, "''")}.${a.from.replace(/'/g, "''")}', N'${a.to.replace(/'/g, "''")}', 'COLUMN';`, a.kind),
|
|
2292
|
-
orElse: () => Q(e,
|
|
2325
|
+
orElse: () => Q(e, hn(a, W), a.kind)
|
|
2293
2326
|
});
|
|
2294
2327
|
break;
|
|
2295
2328
|
case "add-index":
|
|
2296
2329
|
yield* e.onDialectOrElse({
|
|
2297
|
-
mysql: () => Q(e,
|
|
2298
|
-
mssql: () => $(e, [...
|
|
2299
|
-
orElse: () => Q(e,
|
|
2330
|
+
mysql: () => Q(e, qn(a, n.declared), a.kind),
|
|
2331
|
+
mssql: () => $(e, [...rr(a.table, a.index.columns, n.declared), nr(a)], a.kind),
|
|
2332
|
+
orElse: () => Q(e, Kn(a, t.classification === "online-required"), a.kind)
|
|
2300
2333
|
});
|
|
2301
2334
|
break;
|
|
2302
2335
|
case "drop-index":
|
|
2303
2336
|
yield* e.onDialectOrElse({
|
|
2304
|
-
mysql: () => Q(e,
|
|
2305
|
-
mssql: () => Q(e,
|
|
2306
|
-
orElse: () => Q(e,
|
|
2337
|
+
mysql: () => Q(e, Yn(a), a.kind),
|
|
2338
|
+
mssql: () => Q(e, Xn(a), a.kind),
|
|
2339
|
+
orElse: () => Q(e, Jn(a), a.kind)
|
|
2307
2340
|
});
|
|
2308
2341
|
break;
|
|
2309
2342
|
case "rename-table":
|
|
2310
2343
|
yield* e.onDialectOrElse({
|
|
2311
|
-
mysql: () => Q(e,
|
|
2312
|
-
mssql: () => Q(e,
|
|
2313
|
-
orElse: () => Q(e,
|
|
2344
|
+
mysql: () => Q(e, Zn(a, G), a.kind),
|
|
2345
|
+
mssql: () => Q(e, Qn(a), a.kind),
|
|
2346
|
+
orElse: () => Q(e, Zn(a, W), a.kind)
|
|
2314
2347
|
});
|
|
2315
2348
|
break;
|
|
2316
2349
|
case "rename-index":
|
|
2317
2350
|
yield* e.onDialectOrElse({
|
|
2318
|
-
mysql: () => Q(e,
|
|
2319
|
-
mssql: () => Q(e,
|
|
2320
|
-
orElse: () => Q(e,
|
|
2351
|
+
mysql: () => Q(e, er(a), a.kind),
|
|
2352
|
+
mssql: () => Q(e, tr(a), a.kind),
|
|
2353
|
+
orElse: () => Q(e, $n(a), a.kind)
|
|
2321
2354
|
});
|
|
2322
2355
|
break;
|
|
2323
2356
|
case "add-unique":
|
|
2324
2357
|
yield* e.onDialectOrElse({
|
|
2325
|
-
mysql: () => Q(e,
|
|
2358
|
+
mysql: () => Q(e, ar(a, n.declared), a.kind),
|
|
2326
2359
|
sqlite: () => $(e, X(a.table, n.declared), a.kind),
|
|
2327
|
-
mssql: () => $(e, [...
|
|
2328
|
-
orElse: () => Q(e,
|
|
2360
|
+
mssql: () => $(e, [...rr(a.table, [a.column], n.declared), ir(a)], a.kind),
|
|
2361
|
+
orElse: () => Q(e, ir(a), a.kind)
|
|
2329
2362
|
});
|
|
2330
2363
|
break;
|
|
2331
2364
|
case "drop-unique":
|
|
2332
2365
|
yield* e.onDialectOrElse({
|
|
2333
|
-
mysql: () => Q(e,
|
|
2366
|
+
mysql: () => Q(e, dr(a), a.kind),
|
|
2334
2367
|
sqlite: () => $(e, X(a.table, n.declared), a.kind),
|
|
2335
|
-
orElse: () => Q(e,
|
|
2368
|
+
orElse: () => Q(e, ur(a), a.kind)
|
|
2336
2369
|
});
|
|
2337
2370
|
break;
|
|
2338
2371
|
case "add-unique-composite":
|
|
2339
2372
|
yield* e.onDialectOrElse({
|
|
2340
|
-
mysql: () => Q(e,
|
|
2373
|
+
mysql: () => Q(e, sr(a, n.declared), a.kind),
|
|
2341
2374
|
sqlite: () => $(e, X(a.table, n.declared), a.kind),
|
|
2342
|
-
mssql: () => $(e, [...
|
|
2343
|
-
orElse: () => Q(e,
|
|
2375
|
+
mssql: () => $(e, [...rr(a.table, a.fields, n.declared), or(a)], a.kind),
|
|
2376
|
+
orElse: () => Q(e, or(a), a.kind)
|
|
2344
2377
|
});
|
|
2345
2378
|
break;
|
|
2346
2379
|
case "drop-unique-composite":
|
|
2347
2380
|
yield* e.onDialectOrElse({
|
|
2348
|
-
mysql: () => Q(e,
|
|
2381
|
+
mysql: () => Q(e, lr(a), a.kind),
|
|
2349
2382
|
sqlite: () => $(e, X(a.table, n.declared), a.kind),
|
|
2350
|
-
orElse: () => Q(e,
|
|
2383
|
+
orElse: () => Q(e, cr(a), a.kind)
|
|
2351
2384
|
});
|
|
2352
2385
|
break;
|
|
2353
2386
|
case "add-foreign-key":
|
|
2354
2387
|
yield* e.onDialectOrElse({
|
|
2355
|
-
mysql: () => $(e,
|
|
2388
|
+
mysql: () => $(e, fr(a, G), a.kind),
|
|
2356
2389
|
sqlite: () => $(e, X(a.table, n.declared), a.kind),
|
|
2357
|
-
orElse: () => $(e,
|
|
2390
|
+
orElse: () => $(e, fr(a, W), a.kind)
|
|
2358
2391
|
});
|
|
2359
2392
|
break;
|
|
2360
2393
|
case "add-column": {
|
|
2361
|
-
let n =
|
|
2362
|
-
mysql: () =>
|
|
2363
|
-
mssql: () =>
|
|
2364
|
-
sqlite: () =>
|
|
2365
|
-
orElse: () =>
|
|
2394
|
+
let n = vr(a, t, i, yield* e.onDialectOrElse({
|
|
2395
|
+
mysql: () => p.succeed("mariadb"),
|
|
2396
|
+
mssql: () => p.succeed("mssql"),
|
|
2397
|
+
sqlite: () => p.succeed("sqlite"),
|
|
2398
|
+
orElse: () => p.succeed("postgres")
|
|
2366
2399
|
}));
|
|
2367
2400
|
for (let t of n.pre) yield* Q(e, t, a.kind);
|
|
2368
2401
|
if (n.backfillKind === "sql" && n.backfillSql) if (!(t.classification === "online-required" || n.batchSize !== void 0)) yield* e`UPDATE ${e(a.table)} SET ${e(a.column.name)} = ${n.backfillSql}`;
|
|
@@ -2372,10 +2405,10 @@ BEGIN
|
|
|
2372
2405
|
let s = i === void 0 ? yield* e`SELECT ${e("id")} FROM ${e(a.table)} WHERE ${e(a.column.name)} IS NULL ORDER BY ${e("id")} LIMIT ${t}` : yield* e`SELECT ${e("id")} FROM ${e(a.table)} WHERE ${e(a.column.name)} IS NULL AND ${e("id")} > ${i} ORDER BY ${e("id")} LIMIT ${t}`;
|
|
2373
2406
|
if (s.length === 0) break;
|
|
2374
2407
|
let c = s.map((e) => e.id);
|
|
2375
|
-
yield* e`UPDATE ${e(a.table)} SET ${e(a.column.name)} = ${n.backfillSql} WHERE ${e("id")} IN ${e.in(c)}`, o += s.length, yield*
|
|
2408
|
+
yield* e`UPDATE ${e(a.table)} SET ${e(a.column.name)} = ${n.backfillSql} WHERE ${e("id")} IN ${e.in(c)}`, o += s.length, yield* p.logInfo(`backfill ${a.table}.${a.column.name}: ${o} rows`).pipe(p.annotateLogs({
|
|
2376
2409
|
scope: "voltro:migrate",
|
|
2377
2410
|
mode: "sql-batched"
|
|
2378
|
-
})), i = s[s.length - 1].id, r > 0 && (yield*
|
|
2411
|
+
})), i = s[s.length - 1].id, r > 0 && (yield* p.sleep(`${r} millis`));
|
|
2379
2412
|
}
|
|
2380
2413
|
}
|
|
2381
2414
|
else if (n.backfillKind === "js" && n.backfillJs) {
|
|
@@ -2384,17 +2417,17 @@ BEGIN
|
|
|
2384
2417
|
let s = i === void 0 ? yield* e`SELECT ${e("id")} FROM ${e(a.table)} WHERE ${e(a.column.name)} IS NULL ORDER BY ${e("id")} LIMIT ${t}` : yield* e`SELECT ${e("id")} FROM ${e(a.table)} WHERE ${e(a.column.name)} IS NULL AND ${e("id")} > ${i} ORDER BY ${e("id")} LIMIT ${t}`;
|
|
2385
2418
|
if (s.length === 0) break;
|
|
2386
2419
|
for (let t of s) {
|
|
2387
|
-
let r = yield*
|
|
2420
|
+
let r = yield* p.tryPromise({
|
|
2388
2421
|
try: () => Promise.resolve(n.backfillJs({ id: t.id })),
|
|
2389
2422
|
catch: (e) => e
|
|
2390
|
-
}).pipe(
|
|
2423
|
+
}).pipe(p.orDie);
|
|
2391
2424
|
yield* e`UPDATE ${e(a.table)} SET ${e(a.column.name)} = ${r} WHERE ${e("id")} = ${t.id}`;
|
|
2392
2425
|
}
|
|
2393
|
-
o += s.length, yield*
|
|
2426
|
+
o += s.length, yield* p.logInfo(`backfill ${a.table}.${a.column.name}: ${o} rows processed`).pipe(p.annotateLogs({
|
|
2394
2427
|
scope: "voltro:migrate",
|
|
2395
2428
|
table: a.table,
|
|
2396
2429
|
column: a.column.name
|
|
2397
|
-
})), i = s[s.length - 1].id, r > 0 && (yield*
|
|
2430
|
+
})), i = s[s.length - 1].id, r > 0 && (yield* p.sleep(`${r} millis`));
|
|
2398
2431
|
}
|
|
2399
2432
|
}
|
|
2400
2433
|
for (let t of n.post) yield* Q(e, t, a.kind);
|
|
@@ -2403,78 +2436,78 @@ BEGIN
|
|
|
2403
2436
|
case "alter-column-nullability":
|
|
2404
2437
|
yield* e.onDialectOrElse({
|
|
2405
2438
|
mysql: () => {
|
|
2406
|
-
let t =
|
|
2407
|
-
return t ? Q(e, `ALTER TABLE ${
|
|
2439
|
+
let t = K(n.declared, a.table, a.column);
|
|
2440
|
+
return t ? Q(e, `ALTER TABLE ${G(a.table)} MODIFY COLUMN ${bn(t, a.column, a.toNullable)};`, a.kind) : p.die(/* @__PURE__ */ Error(`alter-column-nullability on mariadb needs the declared column '${a.table}.${a.column}' (to render MODIFY COLUMN); it was not in ctx.declared.`));
|
|
2408
2441
|
},
|
|
2409
2442
|
mssql: () => {
|
|
2410
|
-
let t =
|
|
2411
|
-
return t ? Q(e,
|
|
2443
|
+
let t = K(n.declared, a.table, a.column);
|
|
2444
|
+
return t ? Q(e, Fn(t, a.column, a.table, a.toNullable), a.kind) : p.die(/* @__PURE__ */ Error(`alter-column-nullability on mssql needs the declared column '${a.table}.${a.column}'.`));
|
|
2412
2445
|
},
|
|
2413
2446
|
sqlite: () => $(e, X(a.table, n.declared), a.kind),
|
|
2414
|
-
orElse: () => Q(e, `ALTER TABLE ${
|
|
2447
|
+
orElse: () => Q(e, `ALTER TABLE ${W(a.table)} ALTER COLUMN ${W(a.column)} ${a.toNullable ? "DROP NOT NULL" : "SET NOT NULL"};`, a.kind)
|
|
2415
2448
|
});
|
|
2416
2449
|
break;
|
|
2417
2450
|
case "alter-column-default": {
|
|
2418
|
-
let t =
|
|
2451
|
+
let t = yn(n.declared, a.table, a.column);
|
|
2419
2452
|
yield* e.onDialectOrElse({
|
|
2420
|
-
mysql: () => Q(e,
|
|
2421
|
-
mssql: () => Q(e,
|
|
2453
|
+
mysql: () => Q(e, _n(a, G, (e) => xn(e, t)), a.kind),
|
|
2454
|
+
mssql: () => Q(e, In(a), a.kind),
|
|
2422
2455
|
sqlite: () => $(e, X(a.table, n.declared), a.kind),
|
|
2423
|
-
orElse: () => Q(e,
|
|
2456
|
+
orElse: () => Q(e, _n(a, W, (e) => Cn(e, t)), a.kind)
|
|
2424
2457
|
});
|
|
2425
2458
|
break;
|
|
2426
2459
|
}
|
|
2427
2460
|
case "alter-column-type": {
|
|
2428
|
-
let r =
|
|
2461
|
+
let r = un(yn(n.declared, a.table, a.column) ?? { type: a.to }), i = a.using ? ` USING ${a.using}` : "";
|
|
2429
2462
|
if (t.classification !== "online-required") {
|
|
2430
2463
|
yield* e.onDialectOrElse({
|
|
2431
2464
|
mysql: () => {
|
|
2432
|
-
let t =
|
|
2433
|
-
return t ? Q(e, `ALTER TABLE ${
|
|
2465
|
+
let t = K(n.declared, a.table, a.column);
|
|
2466
|
+
return t ? Q(e, `ALTER TABLE ${G(a.table)} MODIFY COLUMN ${bn(t, a.column)};`, a.kind) : p.die(/* @__PURE__ */ Error(`alter-column-type on mariadb needs the declared column '${a.table}.${a.column}' (to render MODIFY COLUMN); it was not in ctx.declared.`));
|
|
2434
2467
|
},
|
|
2435
2468
|
mssql: () => {
|
|
2436
|
-
let t =
|
|
2437
|
-
return t ? Q(e,
|
|
2469
|
+
let t = K(n.declared, a.table, a.column);
|
|
2470
|
+
return t ? Q(e, Fn(t, a.column, a.table), a.kind) : p.die(/* @__PURE__ */ Error(`alter-column-type on mssql needs the declared column '${a.table}.${a.column}'.`));
|
|
2438
2471
|
},
|
|
2439
2472
|
sqlite: () => $(e, X(a.table, n.declared), a.kind),
|
|
2440
|
-
orElse: () => Q(e, `ALTER TABLE ${
|
|
2473
|
+
orElse: () => Q(e, `ALTER TABLE ${W(a.table)} ALTER COLUMN ${W(a.column)} TYPE ${r}${i};`, a.kind)
|
|
2441
2474
|
});
|
|
2442
2475
|
break;
|
|
2443
2476
|
}
|
|
2444
2477
|
yield* e.onDialectOrElse({
|
|
2445
|
-
mysql: () =>
|
|
2446
|
-
orElse: () =>
|
|
2478
|
+
mysql: () => p.die(/* @__PURE__ */ Error(`online-required alter-column-type on ${a.table}.${a.column} is not supported on mariadb (the shadow-swap path is postgres-only). Use a smaller-table window or an external online-DDL tool.`)),
|
|
2479
|
+
orElse: () => p.void
|
|
2447
2480
|
});
|
|
2448
2481
|
let o = `${a.column}__shadow`, s = `${a.column}__old`;
|
|
2449
|
-
yield* e.unsafe(`ALTER TABLE ${
|
|
2450
|
-
let c = a.using ??
|
|
2482
|
+
yield* e.unsafe(`ALTER TABLE ${W(a.table)} ADD COLUMN ${W(o)} ${r}`);
|
|
2483
|
+
let c = a.using ?? W(a.column), l;
|
|
2451
2484
|
for (;;) {
|
|
2452
2485
|
let t = l === void 0 ? yield* e`SELECT ${e("id")} FROM ${e(a.table)} WHERE ${e(o)} IS NULL ORDER BY ${e("id")} LIMIT 5000` : yield* e`SELECT ${e("id")} FROM ${e(a.table)} WHERE ${e(o)} IS NULL AND ${e("id")} > ${l} ORDER BY ${e("id")} LIMIT 5000`;
|
|
2453
2486
|
if (t.length === 0) break;
|
|
2454
|
-
yield* e.unsafe(`UPDATE ${
|
|
2487
|
+
yield* e.unsafe(`UPDATE ${W(a.table)} SET ${W(o)} = ${c} WHERE ${W(a.column)} IS NOT NULL AND ${W(o)} IS NULL AND ${W("id")} IN (${t.map((e) => `'${e.id.replace(/'/g, "''")}'`).join(",")})`), l = t[t.length - 1].id;
|
|
2455
2488
|
}
|
|
2456
|
-
yield*
|
|
2489
|
+
yield* p.logInfo(`alter-column-type shadow ${a.table}.${a.column}: shadow populated`).pipe(p.annotateLogs({ scope: "voltro:migrate" })), yield* e.unsafe(`ALTER TABLE ${W(a.table)} RENAME COLUMN ${W(a.column)} TO ${W(s)}`), yield* e.unsafe(`ALTER TABLE ${W(a.table)} RENAME COLUMN ${W(o)} TO ${W(a.column)}`), yield* e.unsafe(`ALTER TABLE ${W(a.table)} DROP COLUMN ${W(s)}`);
|
|
2457
2490
|
break;
|
|
2458
2491
|
}
|
|
2459
2492
|
case "drop-foreign-key":
|
|
2460
2493
|
yield* e.onDialectOrElse({
|
|
2461
|
-
mysql: () => Q(e,
|
|
2494
|
+
mysql: () => Q(e, mr(a), a.kind),
|
|
2462
2495
|
sqlite: () => $(e, X(a.table, n.declared), a.kind),
|
|
2463
|
-
orElse: () => Q(e,
|
|
2496
|
+
orElse: () => Q(e, pr(a), a.kind)
|
|
2464
2497
|
});
|
|
2465
2498
|
break;
|
|
2466
2499
|
case "add-check":
|
|
2467
2500
|
yield* e.onDialectOrElse({
|
|
2468
|
-
mysql: () => Q(e,
|
|
2501
|
+
mysql: () => Q(e, hr(a, G), a.kind),
|
|
2469
2502
|
sqlite: () => $(e, X(a.table, n.declared), a.kind),
|
|
2470
|
-
orElse: () => Q(e,
|
|
2503
|
+
orElse: () => Q(e, hr(a, W), a.kind)
|
|
2471
2504
|
});
|
|
2472
2505
|
break;
|
|
2473
2506
|
case "drop-check":
|
|
2474
2507
|
yield* e.onDialectOrElse({
|
|
2475
|
-
mysql: () => Q(e,
|
|
2508
|
+
mysql: () => Q(e, _r(a), a.kind),
|
|
2476
2509
|
sqlite: () => $(e, X(a.table, n.declared), a.kind),
|
|
2477
|
-
orElse: () => Q(e,
|
|
2510
|
+
orElse: () => Q(e, gr(a), a.kind)
|
|
2478
2511
|
});
|
|
2479
2512
|
break;
|
|
2480
2513
|
}
|
|
@@ -2483,33 +2516,33 @@ BEGIN
|
|
|
2483
2516
|
status: "applied",
|
|
2484
2517
|
durationMs: Number((process.hrtime.bigint() - r) / 1000000n)
|
|
2485
2518
|
};
|
|
2486
|
-
}),
|
|
2519
|
+
}), xr = (e, t, n) => p.gen(function* () {
|
|
2487
2520
|
let r = t.operations.filter((e) => e.blocked);
|
|
2488
|
-
if (r.length > 0) return yield*
|
|
2489
|
-
yield*
|
|
2521
|
+
if (r.length > 0) return yield* p.die(/* @__PURE__ */ Error(`applyPlan: refusing to execute — ${r.length} blocked operations. Run \`voltro db plan\` to see the fixes.`));
|
|
2522
|
+
yield* w(e);
|
|
2490
2523
|
let i = Date.now(), a = [];
|
|
2491
2524
|
try {
|
|
2492
2525
|
let r = yield* e.onDialectOrElse({
|
|
2493
|
-
pg: () =>
|
|
2494
|
-
mssql: () =>
|
|
2495
|
-
orElse: () =>
|
|
2526
|
+
pg: () => p.succeed(!0),
|
|
2527
|
+
mssql: () => p.succeed(!0),
|
|
2528
|
+
orElse: () => p.succeed(!1)
|
|
2496
2529
|
}), i = r ? t.operations.filter((e) => e.classification !== "online-required") : [], o = r ? t.operations.filter((e) => e.classification === "online-required") : t.operations;
|
|
2497
|
-
i.length > 0 && (yield* e.withTransaction(
|
|
2498
|
-
let e = yield*
|
|
2499
|
-
for (let t of i) a.push(yield*
|
|
2530
|
+
i.length > 0 && (yield* e.withTransaction(p.gen(function* () {
|
|
2531
|
+
let e = yield* h.SqlClient;
|
|
2532
|
+
for (let t of i) a.push(yield* br(e, t, n));
|
|
2500
2533
|
})));
|
|
2501
|
-
for (let t of o) a.push(yield*
|
|
2534
|
+
for (let t of o) a.push(yield* br(e, t, n));
|
|
2502
2535
|
} finally {
|
|
2503
|
-
yield*
|
|
2536
|
+
yield* T(e).pipe(p.orDie);
|
|
2504
2537
|
}
|
|
2505
2538
|
let o = yield* n.replan(e);
|
|
2506
2539
|
if (o.operations.length > 0) {
|
|
2507
2540
|
let e = o.operations.slice(0, 10).map((e) => ` - ${e.op.kind} ${"table" in e.op ? e.op.table : ""}${"column" in e.op ? `.${String(e.op.column)}` : ""}`).join("\n");
|
|
2508
|
-
return yield*
|
|
2541
|
+
return yield* p.die(/* @__PURE__ */ Error(`applyPlan: the migration did not converge. ${t.operations.length} operation(s) were executed without error, but re-planning against the live schema still finds ${o.operations.length}:\n${e}${o.operations.length > 10 ? `\n … and ${o.operations.length - 10} more` : ""}\nNo fingerprint was recorded — recording one would make the next boot report "schema up to date" for a schema that was never applied. This is a framework bug; the schema is unchanged and safe.\nTwo causes look identical from here, so read the list before assuming either:\n - the DDL for those operations is a no-op (it succeeded and changed nothing), or\n - the planner cannot see what the DDL did, so it proposes the same work again — which is what an\n operation naming an object a PREVIOUS operation created or renamed usually means.\nReport the operation kinds above with the column types involved.`));
|
|
2509
2542
|
}
|
|
2510
|
-
let s = a.filter((e) => e.status === "applied").length, c = Date.now() - i, l = `plan_${Date.now().toString(36)}`, u = (/* @__PURE__ */ new Date()).toISOString(),
|
|
2511
|
-
mysql: () =>
|
|
2512
|
-
orElse: () =>
|
|
2543
|
+
let s = a.filter((e) => e.status === "applied").length, c = Date.now() - i, l = `plan_${Date.now().toString(36)}`, u = (/* @__PURE__ */ new Date()).toISOString(), d = yield* e.onDialectOrElse({
|
|
2544
|
+
mysql: () => p.succeed(u.replace("T", " ").replace("Z", "")),
|
|
2545
|
+
orElse: () => p.succeed(u)
|
|
2513
2546
|
});
|
|
2514
2547
|
return yield* e`
|
|
2515
2548
|
INSERT INTO ${e("_voltro_migration_plans")}
|
|
@@ -2519,7 +2552,7 @@ BEGIN
|
|
|
2519
2552
|
VALUES
|
|
2520
2553
|
(${l}, ${t.toFingerprint}, ${o.fromFingerprint}, ${JSON.stringify(t.operations)},
|
|
2521
2554
|
${n.appliedBy}, ${n.environment}, ${n.source},
|
|
2522
|
-
${c}, ${
|
|
2555
|
+
${c}, ${d}, ${n.notes ?? null})
|
|
2523
2556
|
`, {
|
|
2524
2557
|
id: l,
|
|
2525
2558
|
appliedAt: u,
|
|
@@ -2532,19 +2565,19 @@ BEGIN
|
|
|
2532
2565
|
source: n.source,
|
|
2533
2566
|
...n.notes ? { notes: n.notes } : {}
|
|
2534
2567
|
};
|
|
2535
|
-
}),
|
|
2568
|
+
}), Sr = (e) => e.operations.filter((e) => e.blocked).map((e) => ` - ${e.op.kind} ${"table" in e.op ? `[${e.op.table}]` : ""}: ${e.reason ?? "<no reason>"}\n fix: ${e.blocked?.fix ?? "<no fix>"}`).join("\n"), Cr = (e) => e`
|
|
2536
2569
|
SELECT fingerprint
|
|
2537
2570
|
FROM _voltro_migration_plans
|
|
2538
2571
|
ORDER BY ${e("appliedAt")} DESC
|
|
2539
2572
|
LIMIT 1
|
|
2540
|
-
`.pipe(
|
|
2573
|
+
`.pipe(p.map((e) => e.length > 0 ? e[0].fingerprint : void 0), p.catchAll(() => p.succeed(void 0))), wr = (e) => e.onDialectOrElse({
|
|
2541
2574
|
pg: () => "postgres",
|
|
2542
2575
|
mysql: () => "mysql",
|
|
2543
2576
|
mssql: () => "mssql",
|
|
2544
2577
|
sqlite: () => "sqlite",
|
|
2545
2578
|
orElse: () => "sqlite"
|
|
2546
|
-
}),
|
|
2547
|
-
let n =
|
|
2579
|
+
}), Tr = (e, t) => p.gen(function* () {
|
|
2580
|
+
let n = N(F(t, wr(e))), r = yield* Cr(e);
|
|
2548
2581
|
return r === n ? {
|
|
2549
2582
|
kind: "up-to-date",
|
|
2550
2583
|
fingerprint: n
|
|
@@ -2553,23 +2586,23 @@ BEGIN
|
|
|
2553
2586
|
expected: n,
|
|
2554
2587
|
...r === void 0 ? {} : { actual: r }
|
|
2555
2588
|
};
|
|
2556
|
-
}),
|
|
2589
|
+
}), Er = [
|
|
2557
2590
|
"pgeagertest_",
|
|
2558
2591
|
"mig_e2e_",
|
|
2559
2592
|
"mig_test_",
|
|
2560
2593
|
"mysqleagertest_",
|
|
2561
2594
|
"mssqleagertest_",
|
|
2562
2595
|
"sqliteeagertest_"
|
|
2563
|
-
],
|
|
2596
|
+
], Dr = (e) => Er.some((t) => e.startsWith(t)), Or = (e, t) => p.gen(function* () {
|
|
2564
2597
|
if (process.env.VOLTRO_TEST_FIXTURE_GC === "off") return [];
|
|
2565
|
-
let n = t.filter((e) =>
|
|
2598
|
+
let n = t.filter((e) => Dr(e.name)).map((e) => e.name);
|
|
2566
2599
|
if (n.length === 0) return [];
|
|
2567
|
-
for (let t of n) yield* e.unsafe(`DROP TABLE IF EXISTS "${t}" CASCADE`).pipe(
|
|
2600
|
+
for (let t of n) yield* e.unsafe(`DROP TABLE IF EXISTS "${t}" CASCADE`).pipe(p.catchAll(() => p.void));
|
|
2568
2601
|
return n;
|
|
2569
|
-
}),
|
|
2602
|
+
}), kr = () => {
|
|
2570
2603
|
let e = (process.env.VOLTRO_DB_IGNORE_TABLES ?? "").split(",").map((e) => e.trim()).filter((e) => e.length > 0);
|
|
2571
2604
|
return e.length > 0 ? { ignoreTables: e } : {};
|
|
2572
|
-
},
|
|
2605
|
+
}, Ar = (e) => {
|
|
2573
2606
|
if (e === void 0) return;
|
|
2574
2607
|
let t = e.trim();
|
|
2575
2608
|
if (t === "") return;
|
|
@@ -2579,83 +2612,83 @@ BEGIN
|
|
|
2579
2612
|
kind: "tables",
|
|
2580
2613
|
tables: new Set(n)
|
|
2581
2614
|
} : void 0;
|
|
2582
|
-
},
|
|
2615
|
+
}, jr = (e) => typeof e.table == "string" ? e.table : void 0, Mr = (e, t = { kind: "all" }) => ({
|
|
2583
2616
|
...e,
|
|
2584
2617
|
operations: e.operations.map((e) => {
|
|
2585
2618
|
if (!e.blocked || e.classification !== "lossy") return e;
|
|
2586
2619
|
if (t?.kind === "tables") {
|
|
2587
|
-
let n =
|
|
2620
|
+
let n = jr(e.op);
|
|
2588
2621
|
if (n === void 0 || !t.tables.has(n)) return e;
|
|
2589
2622
|
}
|
|
2590
2623
|
let { blocked: n, ...r } = e;
|
|
2591
2624
|
return r;
|
|
2592
2625
|
})
|
|
2593
|
-
}),
|
|
2594
|
-
let t =
|
|
2595
|
-
return t === void 0 ?
|
|
2596
|
-
}),
|
|
2597
|
-
let r =
|
|
2626
|
+
}), Nr = (e, t, n) => t === "postgres" ? ut(e, n).pipe(p.flatMap((e) => {
|
|
2627
|
+
let t = dt(e);
|
|
2628
|
+
return t === void 0 ? p.void : p.logWarning(`auto-migrate: ${t}`).pipe(p.annotateLogs({ scope: "voltro:migrate" }));
|
|
2629
|
+
}), p.catchAll(() => p.void)) : p.void, Pr = (e, t, n) => p.gen(function* () {
|
|
2630
|
+
let r = wr(e), i = t;
|
|
2598
2631
|
if (process.env.VOLTRO_MIGRATE_FORCE !== "1") {
|
|
2599
|
-
let t =
|
|
2600
|
-
if ((yield*
|
|
2632
|
+
let t = N(F(i, r));
|
|
2633
|
+
if ((yield* Cr(e)) === t) return yield* Nr(e, r, i), {
|
|
2601
2634
|
kind: "up-to-date",
|
|
2602
2635
|
fingerprint: t
|
|
2603
2636
|
};
|
|
2604
2637
|
}
|
|
2605
|
-
let a = yield*
|
|
2606
|
-
o.length > 0 && (yield*
|
|
2638
|
+
let a = yield* U(e), o = yield* Or(e, a.tables);
|
|
2639
|
+
o.length > 0 && (yield* p.logInfo(`auto-migrate: reaped ${o.length} test-fixture leftover(s)`).pipe(p.annotateLogs({
|
|
2607
2640
|
scope: "voltro:migrate",
|
|
2608
2641
|
count: o.length
|
|
2609
|
-
})), a = yield*
|
|
2610
|
-
let
|
|
2642
|
+
})), a = yield* U(e));
|
|
2643
|
+
let c = new Set(t.map((e) => e.tableName)), l = (e) => e.filter((e) => !s(e.name) || c.has(e.name)), u = Ut({
|
|
2611
2644
|
declared: i,
|
|
2612
|
-
live: { tables:
|
|
2645
|
+
live: { tables: l(a.tables) },
|
|
2613
2646
|
dialect: r,
|
|
2614
|
-
...
|
|
2647
|
+
...kr()
|
|
2615
2648
|
});
|
|
2616
|
-
if (
|
|
2617
|
-
let e =
|
|
2618
|
-
if (t === void 0 || !e || (
|
|
2649
|
+
if (u.summary.blocked > 0) {
|
|
2650
|
+
let e = u.operations.filter((e) => e.blocked).every((e) => e.classification === "lossy"), t = Ar(process.env.VOLTRO_DESTRUCTIVE_OK);
|
|
2651
|
+
if (t === void 0 || !e || (u = Mr(u, t), u.operations.some((e) => e.blocked))) return {
|
|
2619
2652
|
kind: "refused-blocked",
|
|
2620
|
-
plan:
|
|
2653
|
+
plan: u
|
|
2621
2654
|
};
|
|
2622
2655
|
}
|
|
2623
|
-
return
|
|
2656
|
+
return u.operations.length === 0 ? {
|
|
2624
2657
|
kind: "up-to-date",
|
|
2625
|
-
fingerprint:
|
|
2658
|
+
fingerprint: u.toFingerprint
|
|
2626
2659
|
} : {
|
|
2627
2660
|
kind: "applied",
|
|
2628
|
-
applied: yield*
|
|
2661
|
+
applied: yield* xr(e, u, {
|
|
2629
2662
|
declared: t,
|
|
2630
2663
|
appliedBy: n.appliedBy,
|
|
2631
2664
|
environment: n.environment === "prod" ? "dev" : n.environment,
|
|
2632
2665
|
source: "auto-diff",
|
|
2633
|
-
replan: (e) =>
|
|
2634
|
-
let t = yield*
|
|
2635
|
-
return
|
|
2666
|
+
replan: (e) => p.gen(function* () {
|
|
2667
|
+
let t = yield* U(e);
|
|
2668
|
+
return Ut({
|
|
2636
2669
|
declared: i,
|
|
2637
|
-
live: { tables:
|
|
2670
|
+
live: { tables: l(t.tables) },
|
|
2638
2671
|
dialect: r,
|
|
2639
|
-
...
|
|
2672
|
+
...kr()
|
|
2640
2673
|
});
|
|
2641
2674
|
})
|
|
2642
2675
|
}),
|
|
2643
|
-
plan:
|
|
2676
|
+
plan: u
|
|
2644
2677
|
};
|
|
2645
|
-
}),
|
|
2678
|
+
}), Fr = (e, t, n) => process.env.VOLTRO_AUTO_MIGRATE === "0" ? p.succeed({
|
|
2646
2679
|
kind: "skipped",
|
|
2647
2680
|
reason: "VOLTRO_AUTO_MIGRATE=0"
|
|
2648
|
-
}) : n.environment === "prod" ?
|
|
2681
|
+
}) : n.environment === "prod" ? Tr(e, t) : Pr(e, t, n), Ir = (e, t) => {
|
|
2649
2682
|
switch (e.kind) {
|
|
2650
2683
|
case "skipped": return `auto-migrate: skipped (${e.reason})`;
|
|
2651
|
-
case "up-to-date": return `auto-migrate: schema up to date (${t}, fingerprint=${
|
|
2684
|
+
case "up-to-date": return `auto-migrate: schema up to date (${t}, fingerprint=${P(e.fingerprint)})`;
|
|
2652
2685
|
case "applied": {
|
|
2653
2686
|
let t = e.plan.summary;
|
|
2654
|
-
return `auto-migrate: applied ${e.applied.appliedOps} op(s) in ${e.applied.durationMs}ms [safe=${t.safe} needs-default=${t.needsDefault} needs-backfill=${t.needsBackfill} rename=${t.needsRenameAnnotation} lossy=${t.lossy}] fingerprint=${
|
|
2687
|
+
return `auto-migrate: applied ${e.applied.appliedOps} op(s) in ${e.applied.durationMs}ms [safe=${t.safe} needs-default=${t.needsDefault} needs-backfill=${t.needsBackfill} rename=${t.needsRenameAnnotation} lossy=${t.lossy}] fingerprint=${P(e.applied.fingerprint)}`;
|
|
2655
2688
|
}
|
|
2656
|
-
case "prod-mismatch": return `auto-migrate: SCHEMA FINGERPRINT MISMATCH — declared=${
|
|
2689
|
+
case "prod-mismatch": return `auto-migrate: SCHEMA FINGERPRINT MISMATCH — declared=${P(e.expected)}` + (e.actual ? ` live=${P(e.actual)}` : " live=<no _voltro_migration_plans row>") + ". Run `voltro db apply --plan plan.json` from the deploy pipeline before serving.";
|
|
2657
2690
|
case "refused-blocked": {
|
|
2658
|
-
let t = e.plan.operations.filter((e) => e.blocked), n = t.filter((e) => e.op.kind === "drop-table").length, r = t.filter((e) => e.op.kind === "drop-table").map((e) => "table" in e.op ? e.op.table : "").filter(Boolean), i = t.filter((e) => e.op.kind === "add-column").length, a = t.filter((e) => e.op.kind === "drop-column").length, o = t.filter((e) => e.classification === "needs-rename-annotation").length, s = `auto-migrate: REFUSED — ${e.plan.summary.blocked} blocked operation(s):\n` +
|
|
2691
|
+
let t = e.plan.operations.filter((e) => e.blocked), n = t.filter((e) => e.op.kind === "drop-table").length, r = t.filter((e) => e.op.kind === "drop-table").map((e) => "table" in e.op ? e.op.table : "").filter(Boolean), i = t.filter((e) => e.op.kind === "add-column").length, a = t.filter((e) => e.op.kind === "drop-column").length, o = t.filter((e) => e.classification === "needs-rename-annotation").length, s = `auto-migrate: REFUSED — ${e.plan.summary.blocked} blocked operation(s):\n` + Sr(e.plan), c = [""];
|
|
2659
2692
|
if (n > 0 && c.push(`${n} table(s) exist in the live DB but aren't in your declared schema.`, "This is usually one of three things:", " (a) You're iterating on the schema — the table file got renamed, moved, or temporarily commented out.", " → Restore the declaration. Your data stays intact.", " (b) You want to KEEP the table but NOT manage it with Voltro (a leftover from another tool —", " a Strapi/Rails/legacy migration artifact, or an externally-owned table).", ` → Set \`VOLTRO_DB_IGNORE_TABLES=${r.join(",")}\` (comma-separated).`, " The planner excludes those tables from the diff entirely — it never drops them, and they", " stop blocking your OTHER (additive) changes. This is the fix when a leftover freezes your schema.", " (c) You actually want to drop the table.", ` → Set \`VOLTRO_DESTRUCTIVE_OK=${r.join(",")}\` for ONE run — it acknowledges`, " the data loss for THOSE tables only; every other lossy op in this plan stays blocked.", " (`VOLTRO_DESTRUCTIVE_OK=1` acknowledges all of them, which is rarely what you mean.)", " There is deliberately no `dropped()` marker for a TABLE, unlike for a column: a", " dropped column leaves a slot worth documenting in the declaration, a dropped table", " leaves nothing — the marker would be a dead entry you must remember to delete.", " Soft-drop: set `VOLTRO_SOFT_DROP=1` alongside DESTRUCTIVE_OK to RENAME instead of DROP", " (data survives as `<name>__dropped_<ts>` for ~7d; restorable via `voltro db restore-snapshot`)."), i > 0 && c.push(`${i} required column(s) need a backfill strategy before adding to a populated table.`, " → Annotate the column: `.backfill(sql`<expr>`)` or `.backfill(row => <fn>)` or `.default(<value>)`.", " Existing rows get the backfill value; the column then lands as NOT NULL."), a > 0) {
|
|
2660
2693
|
let t = /* @__PURE__ */ new Map(), n = /* @__PURE__ */ new Map();
|
|
2661
2694
|
for (let r of e.plan.operations) {
|
|
@@ -2672,7 +2705,7 @@ BEGIN
|
|
|
2672
2705
|
return o > 0 && c.push(`${o} column(s) look like renames (one column gone + a new one of same shape appeared).`, " → On the NEW column add `.renamedFrom('<oldName>')` so the planner emits RENAME instead of DROP+ADD."), c.push("", "See `voltro db plan` for the full machine-readable diff."), s + "\n" + c.join("\n");
|
|
2673
2706
|
}
|
|
2674
2707
|
}
|
|
2675
|
-
},
|
|
2708
|
+
}, Lr = /* @__PURE__ */ new Set([
|
|
2676
2709
|
"node_modules",
|
|
2677
2710
|
"dist",
|
|
2678
2711
|
"build",
|
|
@@ -2681,58 +2714,58 @@ BEGIN
|
|
|
2681
2714
|
".next",
|
|
2682
2715
|
".git",
|
|
2683
2716
|
".framework"
|
|
2684
|
-
]),
|
|
2717
|
+
]), Rr = async (e) => {
|
|
2685
2718
|
let t = [], n = async (e) => {
|
|
2686
|
-
let r = await
|
|
2719
|
+
let r = await _.readdir(e, { withFileTypes: !0 }).catch(() => []);
|
|
2687
2720
|
for (let i of r) {
|
|
2688
|
-
if (
|
|
2689
|
-
let r =
|
|
2690
|
-
i.isDirectory() ? await n(r) : i.isFile() &&
|
|
2721
|
+
if (Lr.has(i.name)) continue;
|
|
2722
|
+
let r = v(e, i.name);
|
|
2723
|
+
i.isDirectory() ? await n(r) : i.isFile() && o.test(i.name) && t.push(r);
|
|
2691
2724
|
}
|
|
2692
2725
|
};
|
|
2693
2726
|
return await n(e), t.sort((e, t) => {
|
|
2694
2727
|
let n = e.split("/").pop() ?? e, r = t.split("/").pop() ?? t;
|
|
2695
2728
|
return n.localeCompare(r);
|
|
2696
2729
|
});
|
|
2697
|
-
},
|
|
2730
|
+
}, zr = async (e) => {
|
|
2698
2731
|
let t = [];
|
|
2699
|
-
for (let
|
|
2700
|
-
let e = (await import(
|
|
2701
|
-
if (!
|
|
2702
|
-
process.stderr.write(`[voltro:migrate] ${
|
|
2732
|
+
for (let r of e) {
|
|
2733
|
+
let e = (await import(y(r).href)).default;
|
|
2734
|
+
if (!n(e)) {
|
|
2735
|
+
process.stderr.write(`[voltro:migrate] ${r} has no default-exported \`migration(...)\` — skipped\n`);
|
|
2703
2736
|
continue;
|
|
2704
2737
|
}
|
|
2705
2738
|
t.push({
|
|
2706
|
-
file:
|
|
2739
|
+
file: r,
|
|
2707
2740
|
migration: e
|
|
2708
2741
|
});
|
|
2709
2742
|
}
|
|
2710
2743
|
return t;
|
|
2711
|
-
},
|
|
2744
|
+
}, Br = (e) => e`
|
|
2712
2745
|
SELECT ${e("id")}
|
|
2713
2746
|
FROM ${e("_voltro_migration_plans")}
|
|
2714
2747
|
WHERE ${e("source")} = 'file'
|
|
2715
|
-
`.pipe(
|
|
2748
|
+
`.pipe(p.map((e) => e.map((e) => e.id)), p.catchAll(() => p.succeed([]))), Vr = (e, t, n, r) => p.gen(function* () {
|
|
2716
2749
|
let i = Date.now(), a = (/* @__PURE__ */ new Date()).toISOString(), o = {
|
|
2717
2750
|
sql: e,
|
|
2718
2751
|
log: {
|
|
2719
|
-
info: (e, n) =>
|
|
2752
|
+
info: (e, n) => p.logInfo(e).pipe(p.annotateLogs({
|
|
2720
2753
|
scope: `migrations:file:${t.migration.id}`,
|
|
2721
2754
|
...n ?? {}
|
|
2722
2755
|
})),
|
|
2723
|
-
warn: (e, n) =>
|
|
2756
|
+
warn: (e, n) => p.logWarning(e).pipe(p.annotateLogs({
|
|
2724
2757
|
scope: `migrations:file:${t.migration.id}`,
|
|
2725
2758
|
...n ?? {}
|
|
2726
2759
|
}))
|
|
2727
2760
|
},
|
|
2728
2761
|
appliedAt: a
|
|
2729
2762
|
}, s = t.migration.up(o);
|
|
2730
|
-
|
|
2763
|
+
p.isEffect(s) ? yield* s : yield* p.tryPromise({
|
|
2731
2764
|
try: () => s,
|
|
2732
2765
|
catch: (e) => e
|
|
2733
2766
|
});
|
|
2734
2767
|
let c = Date.now() - i;
|
|
2735
|
-
return !
|
|
2768
|
+
return !p.isEffect(s) && c < 2 && (yield* p.logWarning(`migration ${t.migration.id}: up() resolved in ${c}ms — did the body \`await\` an \`@effect/sql\` Effect by accident? \`sql.unsafe(...)\` returns an Effect, not a Promise; \`await\` on it is a silent no-op. Switch the body to \`Effect.gen(function* () { yield* sql.unsafe(...) })\` form.`).pipe(p.annotateLogs({ scope: `migrations:file:${t.migration.id}` }))), yield* e`
|
|
2736
2769
|
INSERT INTO ${e("_voltro_migration_plans")}
|
|
2737
2770
|
(${e("id")}, ${e("fingerprint")}, ${e("operations")},
|
|
2738
2771
|
${e("appliedBy")}, ${e("environment")}, ${e("source")},
|
|
@@ -2745,93 +2778,93 @@ BEGIN
|
|
|
2745
2778
|
${r}, ${n}, ${"file"},
|
|
2746
2779
|
${c}, ${a}, ${t.migration.description})
|
|
2747
2780
|
`, { durationMs: c };
|
|
2748
|
-
}),
|
|
2749
|
-
let n = yield*
|
|
2750
|
-
try: () =>
|
|
2781
|
+
}), Hr = (e, t) => p.gen(function* () {
|
|
2782
|
+
let n = yield* p.tryPromise({
|
|
2783
|
+
try: () => Rr(v(t, "migrations")),
|
|
2751
2784
|
catch: (e) => e
|
|
2752
2785
|
});
|
|
2753
2786
|
if (n.length === 0) return {
|
|
2754
2787
|
pending: [],
|
|
2755
2788
|
skipped: []
|
|
2756
2789
|
};
|
|
2757
|
-
let r = yield*
|
|
2758
|
-
try: () =>
|
|
2790
|
+
let r = yield* p.tryPromise({
|
|
2791
|
+
try: () => zr(n),
|
|
2759
2792
|
catch: (e) => e
|
|
2760
2793
|
});
|
|
2761
2794
|
if (r.length === 0) return {
|
|
2762
2795
|
pending: [],
|
|
2763
2796
|
skipped: []
|
|
2764
2797
|
};
|
|
2765
|
-
let i = new Set(yield*
|
|
2798
|
+
let i = new Set(yield* Br(e));
|
|
2766
2799
|
return {
|
|
2767
2800
|
pending: r.filter((e) => !i.has(e.migration.id)),
|
|
2768
2801
|
skipped: r.filter((e) => i.has(e.migration.id)).map((e) => e.migration.id)
|
|
2769
2802
|
};
|
|
2770
|
-
}),
|
|
2771
|
-
let { pending: n, skipped: r } = yield*
|
|
2803
|
+
}), Ur = (e, t) => Hr(e, t).pipe(p.map((e) => e.pending.map((e) => e.migration.id))), Wr = (e, t) => p.gen(function* () {
|
|
2804
|
+
let { pending: n, skipped: r } = yield* Hr(e, t.projectRoot);
|
|
2772
2805
|
if (n.length === 0) return {
|
|
2773
2806
|
applied: [],
|
|
2774
2807
|
skipped: r
|
|
2775
2808
|
};
|
|
2776
|
-
yield*
|
|
2809
|
+
yield* w(e);
|
|
2777
2810
|
let i = [];
|
|
2778
2811
|
try {
|
|
2779
2812
|
for (let r of n) {
|
|
2780
|
-
yield*
|
|
2813
|
+
yield* p.logInfo("file-based migration applying").pipe(p.annotateLogs({
|
|
2781
2814
|
scope: "voltro:migrate",
|
|
2782
2815
|
id: r.migration.id,
|
|
2783
2816
|
file: r.file
|
|
2784
2817
|
}));
|
|
2785
|
-
let { durationMs: n } = yield*
|
|
2818
|
+
let { durationMs: n } = yield* Vr(e, r, t.env, t.appliedBy);
|
|
2786
2819
|
i.push({
|
|
2787
2820
|
id: r.migration.id,
|
|
2788
2821
|
durationMs: n
|
|
2789
|
-
}), yield*
|
|
2822
|
+
}), yield* p.logInfo("file-based migration applied").pipe(p.annotateLogs({
|
|
2790
2823
|
scope: "voltro:migrate",
|
|
2791
2824
|
id: r.migration.id,
|
|
2792
2825
|
durationMs: n
|
|
2793
2826
|
}));
|
|
2794
2827
|
}
|
|
2795
2828
|
} finally {
|
|
2796
|
-
yield*
|
|
2829
|
+
yield* T(e).pipe(p.orDie);
|
|
2797
2830
|
}
|
|
2798
2831
|
return {
|
|
2799
2832
|
applied: i,
|
|
2800
2833
|
skipped: r
|
|
2801
2834
|
};
|
|
2802
|
-
}),
|
|
2835
|
+
}), Gr = (e, t) => e`
|
|
2803
2836
|
SELECT ${e("id")}
|
|
2804
2837
|
FROM ${e("_voltro_migration_plans")}
|
|
2805
2838
|
WHERE ${e("id")} = ${t} AND ${e("source")} = 'file'
|
|
2806
2839
|
LIMIT 1
|
|
2807
|
-
`,
|
|
2808
|
-
if (!(yield*
|
|
2809
|
-
let n = yield*
|
|
2810
|
-
try: () =>
|
|
2840
|
+
`, Kr = (e, t) => p.gen(function* () {
|
|
2841
|
+
if (!(yield* Gr(e, t.id))[0]) return yield* p.fail(/* @__PURE__ */ Error(`rollback: ${t.id} not found in _voltro_migration_plans (file source)`));
|
|
2842
|
+
let n = yield* p.tryPromise({
|
|
2843
|
+
try: () => Rr(v(t.projectRoot, "migrations")),
|
|
2811
2844
|
catch: (e) => e
|
|
2812
|
-
}), r = (yield*
|
|
2813
|
-
try: () =>
|
|
2845
|
+
}), r = (yield* p.tryPromise({
|
|
2846
|
+
try: () => zr(n),
|
|
2814
2847
|
catch: (e) => e
|
|
2815
2848
|
})).find((e) => e.migration.id === t.id);
|
|
2816
|
-
if (!r) return yield*
|
|
2817
|
-
yield*
|
|
2849
|
+
if (!r) return yield* p.fail(/* @__PURE__ */ Error(`rollback: file for migration ${t.id} not found on disk under ${t.projectRoot}/migrations/`));
|
|
2850
|
+
yield* w(e);
|
|
2818
2851
|
let i = Date.now();
|
|
2819
2852
|
try {
|
|
2820
2853
|
let n = {
|
|
2821
2854
|
sql: e,
|
|
2822
2855
|
log: {
|
|
2823
|
-
info: (e, n) =>
|
|
2856
|
+
info: (e, n) => p.logInfo(e).pipe(p.annotateLogs({
|
|
2824
2857
|
scope: `migrations:file:${t.id}`,
|
|
2825
2858
|
...n ?? {}
|
|
2826
2859
|
})),
|
|
2827
|
-
warn: (e, n) =>
|
|
2860
|
+
warn: (e, n) => p.logWarning(e).pipe(p.annotateLogs({
|
|
2828
2861
|
scope: `migrations:file:${t.id}`,
|
|
2829
2862
|
...n ?? {}
|
|
2830
2863
|
}))
|
|
2831
2864
|
},
|
|
2832
2865
|
appliedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2833
2866
|
}, a = r.migration.down(n);
|
|
2834
|
-
|
|
2867
|
+
p.isEffect(a) ? yield* a : yield* p.tryPromise({
|
|
2835
2868
|
try: () => a,
|
|
2836
2869
|
catch: (e) => e
|
|
2837
2870
|
});
|
|
@@ -2841,19 +2874,19 @@ BEGIN
|
|
|
2841
2874
|
durationMs: o
|
|
2842
2875
|
};
|
|
2843
2876
|
} finally {
|
|
2844
|
-
yield*
|
|
2877
|
+
yield* T(e).pipe(p.orDie);
|
|
2845
2878
|
}
|
|
2846
|
-
}),
|
|
2847
|
-
let t = yield*
|
|
2879
|
+
}), qr = (e) => p.gen(function* () {
|
|
2880
|
+
let t = yield* h.SqlClient, n = yield* t`
|
|
2848
2881
|
SELECT id, fingerprint, appliedAt FROM _voltro_migration_plans
|
|
2849
2882
|
WHERE source = 'auto-diff'
|
|
2850
2883
|
AND squashedAt IS NULL
|
|
2851
2884
|
AND appliedAt < ${e.before}
|
|
2852
2885
|
ORDER BY appliedAt DESC
|
|
2853
2886
|
`;
|
|
2854
|
-
if (n.length === 0) return yield*
|
|
2887
|
+
if (n.length === 0) return yield* p.fail(/* @__PURE__ */ Error(`squash: no rows applied before ${e.before}`));
|
|
2855
2888
|
let r = n[0];
|
|
2856
|
-
if (r.fingerprint !== e.fingerprint) return yield*
|
|
2889
|
+
if (r.fingerprint !== e.fingerprint) return yield* p.fail(/* @__PURE__ */ Error(`squash: latest pre-squash fingerprint (${r.fingerprint.slice(0, 12)}…) differs from current declared-schema fingerprint (${e.fingerprint.slice(0, 12)}…). This means the working tree carries un-applied changes — squashing now would lock the drift in.\n\nFix: run \`voltro db apply\` first to align the live schema with the working tree, then re-squash.`));
|
|
2857
2890
|
yield* t`
|
|
2858
2891
|
UPDATE _voltro_migration_plans
|
|
2859
2892
|
SET squashedAt = CURRENT_TIMESTAMP
|
|
@@ -2888,10 +2921,10 @@ BEGIN
|
|
|
2888
2921
|
snapshotFingerprint: e.fingerprint,
|
|
2889
2922
|
snapshotId: i
|
|
2890
2923
|
};
|
|
2891
|
-
}),
|
|
2924
|
+
}), Jr = (e, ...t) => ({
|
|
2892
2925
|
_tag: "RawSqlFragment",
|
|
2893
2926
|
strings: [...e],
|
|
2894
2927
|
values: [...t]
|
|
2895
|
-
}),
|
|
2928
|
+
}), Yr = (e) => typeof e == "object" && !!e && e._tag === "RawSqlFragment";
|
|
2896
2929
|
//#endregion
|
|
2897
|
-
export {
|
|
2930
|
+
export { j as DEFAULT_CDC_CHANNEL, o as FILE_MIGRATION_PATTERN, lt as REACTIVE_TRIGGER_PREFIX, ie as VOLTRO_MIGRATION_LOCK_KEY, w as acquireMigrationLock, Ze as applyNamespacedSchema, xr as applyPlan, Ge as applySchema, Xt as chunkTables, F as declaredSnapshot, xe as defaultArrayClause, be as defaultClause, O as defaultJsonClause, Ir as describeOutcome, Ar as destructiveScope, ut as detectReactiveTriggerDrift, pn as emitDropColumnDdl, mn as emitDropColumnDdlMysql, We as emitFrameworkBootstrapSql, He as emitNamespaceProvisionDdl, Ue as emitNamespacedSchemaSql, M as emitSchemaSql, N as fingerprintSchema, dt as formatReactiveTriggerDrift, kr as ignoreTablesFromEnv, U as introspectSchema, n as isFileMigration, Yr as isRawSqlFragment, Gt as mapPgType, c as migration, k as numericIdSql, Wt as parseEnumCheck, Ur as pendingFileMigrationIds, Ut as planMigrations, Qe as provisionTenantNamespace, T as releaseMigrationLock, On as renderColumnMysql, wn as renderColumnPg, Kr as rollbackFileBasedMigration, Wr as runFileBasedMigrations, $e as runFrameworkBootstrap, Xe as runMigrate, Fr as runPlannedMigrations, P as shortFingerprint, at as snapshotColumn, Jr as sql, D as sqlType, qr as squashMigrationPlans, Mr as unblockLossy, ve as withMigrationLock };
|