@voltro/database 0.32.0 → 0.34.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 +2006 -0
- package/dist/{frameworkLiveTables-DbAgeVOU.js → frameworkLiveTables-CeuTYhWm.js} +159 -130
- package/dist/index.d.ts +774 -11
- package/dist/index.js +1039 -710
- package/dist/sql.d.ts +247 -10
- package/dist/sql.js +1282 -924
- package/package.json +4 -3
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 { It as e, Jt as t, Lt as n, X as r, Z as i, _ as a, a as o, bt as s, c, et as l, i as u, l as d, m as f, mt as p, n as m, o as h, p as g, qt as _, r as v, tt as y } from "./frameworkLiveTables-CeuTYhWm.js";
|
|
2
|
+
import { Effect as b } from "effect";
|
|
3
|
+
import { createLogger as x } from "@voltro/logger";
|
|
4
|
+
import { SqlClient as S } from "@effect/sql";
|
|
5
|
+
import { createHash as C } from "node:crypto";
|
|
6
|
+
import { promises as ee } from "node:fs";
|
|
7
|
+
import { join as te } from "node:path";
|
|
8
|
+
import { pathToFileURL as ne } from "node:url";
|
|
9
9
|
//#region src/migrations/ddlError.ts
|
|
10
|
-
var
|
|
10
|
+
var re = (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,19 +39,19 @@ var b = (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
|
+
}, ie = (e) => e.unique === !0 && typeof e.where == "string" && e.where.length > 0, ae = (e) => e.replace(/"/g, "`"), oe = (e) => typeof e == "string" ? e : e.name ?? String(e), se = (e, t) => `_ua_${t}_${e}`.slice(0, 64), ce = (e, t) => {
|
|
43
|
+
if (t !== "mysql" && t !== "mariadb" || !e.appliedIndexes.some(ie)) return e;
|
|
44
44
|
let n = { ...e.fields }, r = [], i = [];
|
|
45
45
|
for (let t of e.appliedIndexes) {
|
|
46
|
-
if (!
|
|
46
|
+
if (!ie(t)) {
|
|
47
47
|
r.push(t);
|
|
48
48
|
continue;
|
|
49
49
|
}
|
|
50
|
-
let a =
|
|
50
|
+
let a = ae(t.where), o = [];
|
|
51
51
|
for (let r of t.fields) {
|
|
52
|
-
let i =
|
|
52
|
+
let i = oe(r);
|
|
53
53
|
if (!(i in e.fields)) throw Error(`@voltro/migrate: uniqueActive '${t.name}' on '${e.tableName}' references unknown column '${i}'.`);
|
|
54
|
-
let s =
|
|
54
|
+
let s = se(t.name, i);
|
|
55
55
|
o.push(s), n[s] = {
|
|
56
56
|
type: "text",
|
|
57
57
|
nullable: !0,
|
|
@@ -74,101 +74,101 @@ var b = (e, t, n, r = 400) => {
|
|
|
74
74
|
appliedIndexes: r,
|
|
75
75
|
appliedUniques: [...e.appliedUniques ?? [], ...i]
|
|
76
76
|
};
|
|
77
|
-
},
|
|
77
|
+
}, w = (e, t) => `${e}::${t}`, le = (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(w(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
|
+
}, ue = 6322741009312437n, de = 250, fe = () => {
|
|
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
|
+
}, pe = (e) => b.gen(function* () {
|
|
90
|
+
let t = fe(), 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* b.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* b.sleep(`${de} millis`);
|
|
95
95
|
}
|
|
96
|
-
}),
|
|
97
|
-
yield* e`SELECT pg_advisory_unlock(${
|
|
98
|
-
}),
|
|
96
|
+
}), me = (e) => b.gen(function* () {
|
|
97
|
+
yield* e`SELECT pg_advisory_unlock(${ue.toString()}::bigint)`;
|
|
98
|
+
}), he = `voltro_migration_${ue.toString(36)}`, ge = 30, _e = (e) => b.gen(function* () {
|
|
99
99
|
(yield* e`
|
|
100
|
-
SELECT GET_LOCK(${
|
|
101
|
-
}),
|
|
102
|
-
yield* e`SELECT RELEASE_LOCK(${
|
|
103
|
-
}),
|
|
100
|
+
SELECT GET_LOCK(${he}, ${ge}) AS got`)[0]?.got !== 1 && (yield* b.die(/* @__PURE__ */ Error(`could not acquire the migration lock '${he}' within ${ge}s (another migration is in progress, or a prior one left it held).`)));
|
|
101
|
+
}), ve = (e) => b.gen(function* () {
|
|
102
|
+
yield* e`SELECT RELEASE_LOCK(${he})`;
|
|
103
|
+
}), ye = `voltro_migration_${ue.toString(36)}`, be = 3e4, xe = (e) => b.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 = ${ye}, @LockMode = 'Exclusive',
|
|
107
|
+
@LockOwner = 'Session', @LockTimeout = ${be};
|
|
108
108
|
SELECT @res AS res`;
|
|
109
|
-
(t[0]?.res ?? -999) < 0 && (yield*
|
|
110
|
-
}),
|
|
111
|
-
yield* e`EXEC sp_releaseapplock @Resource = ${
|
|
112
|
-
}),
|
|
113
|
-
mysql: () =>
|
|
114
|
-
mssql: () =>
|
|
115
|
-
sqlite: () =>
|
|
116
|
-
orElse: () =>
|
|
117
|
-
}),
|
|
118
|
-
mysql: () =>
|
|
119
|
-
mssql: () =>
|
|
120
|
-
sqlite: () =>
|
|
121
|
-
orElse: () =>
|
|
122
|
-
}),
|
|
123
|
-
|
|
124
|
-
},
|
|
125
|
-
let
|
|
109
|
+
(t[0]?.res ?? -999) < 0 && (yield* b.die(/* @__PURE__ */ Error(`could not acquire the migration lock '${ye}' (sp_getapplock returned ${t[0]?.res ?? "no row"}; another migration is in progress).`)));
|
|
110
|
+
}), Se = (e) => b.gen(function* () {
|
|
111
|
+
yield* e`EXEC sp_releaseapplock @Resource = ${ye}, @LockOwner = 'Session'`;
|
|
112
|
+
}), Ce = b.void, T = (e) => e.onDialectOrElse({
|
|
113
|
+
mysql: () => _e(e),
|
|
114
|
+
mssql: () => xe(e),
|
|
115
|
+
sqlite: () => Ce,
|
|
116
|
+
orElse: () => pe(e)
|
|
117
|
+
}), E = (e) => e.onDialectOrElse({
|
|
118
|
+
mysql: () => ve(e),
|
|
119
|
+
mssql: () => Se(e),
|
|
120
|
+
sqlite: () => Ce,
|
|
121
|
+
orElse: () => me(e)
|
|
122
|
+
}), we = (e, t) => b.acquireUseRelease(T(e), () => t, () => E(e).pipe(b.orDie)), D = "framework_changes", Te = "framework_changes_", Ee = (e, t) => t === "framework_changes" ? `${Te}${e}` : `${Te}${t}_${e}`, De = (e) => e === "framework_changes" ? "framework_notify_change" : `framework_notify_change_${e}`, Oe = "voltro:notify:v2", ke = x({ scope: "voltro:migrate" }), O = (e) => {
|
|
123
|
+
ke.warn(e.replace(/^\[voltro:migrate\]\s*/, ""));
|
|
124
|
+
}, k = (e, t) => {
|
|
125
|
+
let n = e.type;
|
|
126
126
|
if (e.spatial) {
|
|
127
|
-
if (
|
|
128
|
-
throw Error(`@voltro/plugin-postgis: ${e.spatial.kind}('${e.spatial.geomKind}', ${e.spatial.srid}) is postgres-only — the active dialect is '${
|
|
127
|
+
if (t === "postgres") return `${e.spatial.kind}(${e.spatial.geomKind},${e.spatial.srid})`;
|
|
128
|
+
throw Error(`@voltro/plugin-postgis: ${e.spatial.kind}('${e.spatial.geomKind}', ${e.spatial.srid}) is postgres-only — the active dialect is '${t}'. PostGIS has no portable equivalent; use store: 'postgres' (or DB_DIALECT=postgres) for spatial columns, or model the value as plain text/json.`);
|
|
129
129
|
}
|
|
130
|
-
if (
|
|
130
|
+
if (n === "raw") {
|
|
131
131
|
if (e.rawDdl === void 0) throw Error("raw() column is missing its DDL fragment — this is a framework bug.");
|
|
132
132
|
return e.rawDdl;
|
|
133
133
|
}
|
|
134
|
-
if (
|
|
135
|
-
if (
|
|
134
|
+
if (n === "array" && e.arrayElement) {
|
|
135
|
+
if (t === "postgres") return `${k({
|
|
136
136
|
...e,
|
|
137
137
|
type: e.arrayElement,
|
|
138
138
|
arrayElement: void 0
|
|
139
|
-
},
|
|
140
|
-
if (
|
|
141
|
-
if (
|
|
142
|
-
if (t
|
|
143
|
-
}
|
|
144
|
-
if (
|
|
145
|
-
let
|
|
146
|
-
if (
|
|
139
|
+
}, t)}[]`;
|
|
140
|
+
if (t === "mysql" || t === "mariadb") return "JSON";
|
|
141
|
+
if (t === "mssql") return "NVARCHAR(MAX)";
|
|
142
|
+
if (r(t)) return "TEXT";
|
|
143
|
+
}
|
|
144
|
+
if (n === "vector") {
|
|
145
|
+
let n = e.vectorDim;
|
|
146
|
+
if (n === void 0) throw Error("vector column is missing its dimension — this is a framework bug.");
|
|
147
147
|
let i = e.vectorPrecision === "half";
|
|
148
|
-
if (
|
|
149
|
-
if (
|
|
150
|
-
if (
|
|
151
|
-
if (t
|
|
152
|
-
}
|
|
153
|
-
if (
|
|
154
|
-
if (
|
|
155
|
-
if (
|
|
156
|
-
if (t
|
|
157
|
-
}
|
|
158
|
-
if (
|
|
159
|
-
let
|
|
160
|
-
if (
|
|
161
|
-
return t
|
|
162
|
-
}
|
|
163
|
-
if (
|
|
164
|
-
if (
|
|
165
|
-
if (
|
|
166
|
-
if (
|
|
167
|
-
if (
|
|
168
|
-
if (t
|
|
169
|
-
}
|
|
170
|
-
if (
|
|
171
|
-
if (t
|
|
148
|
+
if (t === "postgres") return i ? `HALFVEC(${n})` : `VECTOR(${n})`;
|
|
149
|
+
if (t === "mysql" || t === "mariadb") return `VECTOR(${n})`;
|
|
150
|
+
if (t === "mssql") return "VARBINARY(MAX)";
|
|
151
|
+
if (r(t)) return "BLOB";
|
|
152
|
+
}
|
|
153
|
+
if (n === "interval") {
|
|
154
|
+
if (t === "postgres") return "INTERVAL";
|
|
155
|
+
if (t === "mysql" || t === "mariadb" || t === "mssql") return "BIGINT";
|
|
156
|
+
if (r(t)) return "INTEGER";
|
|
157
|
+
}
|
|
158
|
+
if (n === "decimal") {
|
|
159
|
+
let n = e.precision, i = e.scale ?? 0;
|
|
160
|
+
if (n === void 0) throw Error("decimal column is missing its precision — this is a framework bug.");
|
|
161
|
+
return r(t) ? "NUMERIC" : t === "postgres" ? `NUMERIC(${n}, ${i})` : `DECIMAL(${n}, ${i})`;
|
|
162
|
+
}
|
|
163
|
+
if (n === "bigint") return r(t) ? "INTEGER" : "BIGINT";
|
|
164
|
+
if (n === "enum") {
|
|
165
|
+
if (t === "postgres") return e.enumName ?? "TEXT";
|
|
166
|
+
if (t === "mysql" || t === "mariadb") return `ENUM(${(e.enumValues ?? []).map((e) => `'${e.replace(/'/g, "''")}'`).join(", ")})`;
|
|
167
|
+
if (t === "mssql") return "NVARCHAR(255)";
|
|
168
|
+
if (r(t)) return "TEXT";
|
|
169
|
+
}
|
|
170
|
+
if (n === "text" && e.maxLength !== void 0 && e.generatedAs === void 0) return t === "mssql" ? `NVARCHAR(${e.maxLength})` : r(t) ? "TEXT" : `VARCHAR(${e.maxLength})`;
|
|
171
|
+
if (r(t)) switch (n) {
|
|
172
172
|
case "id": return "TEXT";
|
|
173
173
|
case "text": return "TEXT";
|
|
174
174
|
case "integer": return "INTEGER";
|
|
@@ -184,7 +184,7 @@ var b = (e, t, n, r = 400) => {
|
|
|
184
184
|
case "array": return "TEXT";
|
|
185
185
|
case "interval": return "INTEGER";
|
|
186
186
|
}
|
|
187
|
-
if (
|
|
187
|
+
if (t === "mysql" || t === "mariadb") switch (n) {
|
|
188
188
|
case "id": return "VARCHAR(64)";
|
|
189
189
|
case "text": return e.hasDefault && e.defaultFactory === void 0 || e.oneOf && e.oneOf.length > 0 ? "VARCHAR(255)" : e.generatedAs ? "TEXT" : "LONGTEXT";
|
|
190
190
|
case "integer": return "INT";
|
|
@@ -200,7 +200,7 @@ var b = (e, t, n, r = 400) => {
|
|
|
200
200
|
case "array": return "JSON";
|
|
201
201
|
case "interval": return "BIGINT";
|
|
202
202
|
}
|
|
203
|
-
if (
|
|
203
|
+
if (t === "mssql") switch (n) {
|
|
204
204
|
case "id": return "NVARCHAR(64)";
|
|
205
205
|
case "text": return e.hasDefault && e.defaultFactory === void 0 || e.oneOf && e.oneOf.length > 0 ? "NVARCHAR(450)" : "NVARCHAR(MAX)";
|
|
206
206
|
case "integer": return "INT";
|
|
@@ -216,7 +216,7 @@ var b = (e, t, n, r = 400) => {
|
|
|
216
216
|
case "array": return "NVARCHAR(MAX)";
|
|
217
217
|
case "interval": return "BIGINT";
|
|
218
218
|
}
|
|
219
|
-
switch (
|
|
219
|
+
switch (n) {
|
|
220
220
|
case "id": return "TEXT";
|
|
221
221
|
case "text": return "TEXT";
|
|
222
222
|
case "integer": return "INTEGER";
|
|
@@ -232,22 +232,22 @@ var b = (e, t, n, r = 400) => {
|
|
|
232
232
|
case "array": return "TEXT[]";
|
|
233
233
|
case "interval": return "INTERVAL";
|
|
234
234
|
}
|
|
235
|
-
},
|
|
235
|
+
}, Ae = (e, t) => {
|
|
236
236
|
if (!e.hasDefault || e.defaultFactory !== void 0) return null;
|
|
237
|
-
let
|
|
238
|
-
return
|
|
239
|
-
},
|
|
240
|
-
if (n !== "postgres" || t.type !== "array" || t.arrayElement === void 0) return
|
|
241
|
-
let r =
|
|
237
|
+
let n = e.defaultValue;
|
|
238
|
+
return n === "now" ? r(t) ? "DEFAULT CURRENT_TIMESTAMP" : t === "mssql" ? "DEFAULT SYSUTCDATETIME()" : t === "mysql" || t === "mariadb" ? "DEFAULT CURRENT_TIMESTAMP(6)" : "DEFAULT now()" : typeof n == "boolean" ? t === "postgres" ? `DEFAULT ${n}` : `DEFAULT ${+!!n}` : typeof n == "number" ? `DEFAULT ${n}` : typeof n == "string" ? `DEFAULT '${n.replace(/'/g, "''")}'` : Array.isArray(n) ? je(n, e, t) : typeof n == "object" && n ? A(n, t) : null;
|
|
239
|
+
}, je = (e, t, n) => {
|
|
240
|
+
if (n !== "postgres" || t.type !== "array" || t.arrayElement === void 0) return A(e, n);
|
|
241
|
+
let r = k({
|
|
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
|
+
}, A = (e, t) => {
|
|
248
248
|
let n = JSON.stringify(e).replace(/'/g, "''");
|
|
249
249
|
return t === "postgres" ? `DEFAULT '${n}'::jsonb` : t === "mysql" || t === "mariadb" ? `DEFAULT (CONVERT('${n}' USING utf8mb4))` : `DEFAULT '${n}'`;
|
|
250
|
-
},
|
|
250
|
+
}, j = (e, t) => {
|
|
251
251
|
switch (t) {
|
|
252
252
|
case "postgres": return `"${e}" BIGSERIAL PRIMARY KEY`;
|
|
253
253
|
case "mysql":
|
|
@@ -256,7 +256,7 @@ var b = (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
|
+
}, Me = (e, t) => {
|
|
260
260
|
if (e === "restrict" && t === "mssql") return "NO ACTION";
|
|
261
261
|
switch (e) {
|
|
262
262
|
case "cascade": return "CASCADE";
|
|
@@ -264,175 +264,177 @@ var b = (e, t, n, r = 400) => {
|
|
|
264
264
|
case "setNull": return "SET NULL";
|
|
265
265
|
case "noAction": return "NO ACTION";
|
|
266
266
|
}
|
|
267
|
-
},
|
|
268
|
-
let
|
|
269
|
-
if (
|
|
267
|
+
}, Ne = (e, t, n) => n === null ? i(e, t) : `${i(n, t)}.${i(e, t)}`, Pe = 191, Fe = (e, t, n) => {
|
|
268
|
+
let r = i(e, n);
|
|
269
|
+
if (n !== "mysql" && n !== "mariadb") return r;
|
|
270
270
|
let a = t[e];
|
|
271
|
-
if (!a) return
|
|
272
|
-
let o =
|
|
273
|
-
return o.endsWith("TEXT") || o.endsWith("BLOB") ? `${
|
|
274
|
-
},
|
|
271
|
+
if (!a) return r;
|
|
272
|
+
let o = k(a, n).toUpperCase();
|
|
273
|
+
return o.endsWith("TEXT") || o.endsWith("BLOB") ? `${r}(${Pe})` : r;
|
|
274
|
+
}, Ie = (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 || O("[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 c =
|
|
285
|
-
|
|
286
|
-
let u =
|
|
282
|
+
}, Le = (e, t, n, a, o = !1, s = !1) => {
|
|
283
|
+
if (t.type === "id" && t.idScheme?.kind === "numeric") return j(e, n);
|
|
284
|
+
let c = n === "mssql" && t.type === "text" && !t.generatedAs && t.maxLength === void 0 && a?.has(e) ? "NVARCHAR(450)" : k(t, n), l = [i(e, n), c];
|
|
285
|
+
t.type === "id" && !s && l.push("PRIMARY KEY");
|
|
286
|
+
let u = Ie(t, n);
|
|
287
287
|
if (u) l.push(u);
|
|
288
288
|
else {
|
|
289
|
-
|
|
290
|
-
let e =
|
|
289
|
+
t.nullable || l.push("NOT NULL"), t.unique && t.type !== "id" && l.push("UNIQUE");
|
|
290
|
+
let e = Ae(t, n);
|
|
291
291
|
e && l.push(e);
|
|
292
292
|
}
|
|
293
|
-
if (
|
|
294
|
-
let e =
|
|
295
|
-
l.push(`REFERENCES ${
|
|
296
|
-
let
|
|
297
|
-
l.push(`ON DELETE ${
|
|
298
|
-
let a =
|
|
293
|
+
if (t.type === "reference" && t.references && !o) {
|
|
294
|
+
let e = t.references();
|
|
295
|
+
l.push(`REFERENCES ${i(e.tableName, n)} (${i("id", n)})`);
|
|
296
|
+
let r = Me(t.onDelete ?? "restrict", n);
|
|
297
|
+
l.push(`ON DELETE ${r}`);
|
|
298
|
+
let a = Me(t.refOnUpdate ?? "noAction", n);
|
|
299
299
|
a !== "NO ACTION" && l.push(`ON UPDATE ${a}`);
|
|
300
300
|
}
|
|
301
|
-
if (
|
|
302
|
-
let
|
|
303
|
-
l.push(`CHECK (${
|
|
301
|
+
if (t.oneOf && t.oneOf.length > 0) {
|
|
302
|
+
let r = t.oneOf.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ");
|
|
303
|
+
l.push(`CHECK (${i(e, n)} IN (${r}))`);
|
|
304
304
|
}
|
|
305
|
-
if (
|
|
306
|
-
if (
|
|
307
|
-
let
|
|
308
|
-
l.push(`CHECK (${
|
|
305
|
+
if (t.checks && t.checks.length > 0) for (let e of t.checks) l.push(`CHECK (${e})`);
|
|
306
|
+
if (t.type === "enum" && t.enumValues && t.enumValues.length > 0 && (n === "mssql" || r(n))) {
|
|
307
|
+
let r = t.enumValues.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ");
|
|
308
|
+
l.push(`CHECK (${i(e, n)} IN (${r}))`);
|
|
309
309
|
}
|
|
310
|
-
if (
|
|
311
|
-
let
|
|
312
|
-
|
|
310
|
+
if (t.type === "json" || t.type === "array") {
|
|
311
|
+
let t = i(e, n);
|
|
312
|
+
r(n) ? l.push(`CHECK (${t} IS NULL OR json_valid(${t}))`) : n === "mssql" && l.push(`CHECK (${t} IS NULL OR ISJSON(${t}) = 1)`);
|
|
313
313
|
}
|
|
314
314
|
return l.join(" ");
|
|
315
|
-
},
|
|
315
|
+
}, Re = (e, t, n = null, r) => {
|
|
316
316
|
let a = /* @__PURE__ */ new Set();
|
|
317
317
|
for (let t of e.appliedIndexes ?? []) for (let e of t.fields) typeof e == "string" && a.add(e);
|
|
318
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, n]) => ` ${
|
|
320
|
-
let
|
|
321
|
-
return ` CONSTRAINT ${n
|
|
322
|
-
}), u = (e.appliedChecks ?? []).map((e) => ` CONSTRAINT ${
|
|
323
|
-
...s ? [` PRIMARY KEY (${o.map((n) =>
|
|
319
|
+
let o = e.appliedPrimaryKey ?? [], s = o.length > 0, c = Object.entries(e.fields).map(([e, n]) => ` ${Le(e, n, t, a, r?.has(e) ?? !1, s)}`).join(",\n"), l = (e.appliedUniques ?? []).map((n) => {
|
|
320
|
+
let r = n.fields.map((n) => Fe(n, e.fields, t)).join(", ");
|
|
321
|
+
return ` CONSTRAINT ${i(n.name, t)} UNIQUE (${r})`;
|
|
322
|
+
}), u = (e.appliedChecks ?? []).map((e) => ` CONSTRAINT ${i(e.name, t)} CHECK (${e.expr})`), d = [
|
|
323
|
+
...s ? [` PRIMARY KEY (${o.map((n) => Fe(n, e.fields, t)).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 = Ne(e.tableName, t, n);
|
|
327
327
|
if (t === "mssql") {
|
|
328
|
-
let t =
|
|
329
|
-
return `IF OBJECT_ID('${t.replace(/'/g, "''")}', 'U') IS NULL EXEC(N'${
|
|
328
|
+
let t = n === null ? e.tableName : `${n}.${e.tableName}`, r = `CREATE TABLE ${p} (\n${f}\n)`;
|
|
329
|
+
return `IF OBJECT_ID('${t.replace(/'/g, "''")}', 'U') IS NULL EXEC(N'${r.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
|
|
338
|
-
return c.push(`CREATE VIRTUAL TABLE IF NOT EXISTS ${a(
|
|
339
|
-
},
|
|
340
|
-
let
|
|
341
|
-
let
|
|
336
|
+
}, ze = (e, t, n) => {
|
|
337
|
+
let r = `${e}_${t.name}_fts`, a = (e) => i(e, n), o = t.columns.map(a).join(", "), s = t.columns.map((e) => `new.${a(e)}`).join(", "), c = [];
|
|
338
|
+
return c.push(`CREATE VIRTUAL TABLE IF NOT EXISTS ${a(r)} USING fts5(${o}, row_id UNINDEXED, tokenize='porter');`), c.push(`CREATE TRIGGER IF NOT EXISTS ${a(`${r}_ai`)} AFTER INSERT ON ${a(e)} BEGIN\n INSERT INTO ${a(r)} (${o}, row_id) VALUES (${s}, new.${a("id")});\nEND;`), c.push(`CREATE TRIGGER IF NOT EXISTS ${a(`${r}_ad`)} AFTER DELETE ON ${a(e)} BEGIN\n DELETE FROM ${a(r)} WHERE row_id = old.${a("id")};\nEND;`), c.push(`CREATE TRIGGER IF NOT EXISTS ${a(`${r}_au`)} AFTER UPDATE ON ${a(e)} BEGIN\n DELETE FROM ${a(r)} WHERE row_id = old.${a("id")};\n INSERT INTO ${a(r)} (${o}, row_id) VALUES (${s}, new.${a("id")});\nEND;`), c;
|
|
339
|
+
}, Be = (e, t, n = null) => {
|
|
340
|
+
let r = [], a = /* @__PURE__ */ new Set(), o = (e) => e.join("\0"), s = (e, n, 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 t === "postgres" ? {
|
|
348
348
|
using: " USING GIST",
|
|
349
349
|
withSuffix: "",
|
|
350
350
|
skip: !1
|
|
351
|
-
} : (
|
|
352
|
-
if (
|
|
351
|
+
} : (O(`[voltro:migrate] index '${e}' on '${n}': ${t} 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 t === "postgres" ? {
|
|
353
353
|
using: " USING GIN",
|
|
354
354
|
withSuffix: "",
|
|
355
355
|
skip: !1
|
|
356
|
-
} :
|
|
356
|
+
} : t === "mysql" || t === "mariadb" ? (O(`[voltro:migrate] gin index '${e}' on '${n}': ${t} 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
|
+
}) : (O(`[voltro:migrate] gin index '${e}' on '${n}': ${t} has no GIN access method — falling back to btree. Containment/membership queries won't use this index efficiently.`), a);
|
|
361
|
+
if (r === "brin") return t === "postgres" ? {
|
|
362
362
|
using: " USING BRIN",
|
|
363
363
|
withSuffix: "",
|
|
364
364
|
skip: !1
|
|
365
|
-
} : (
|
|
366
|
-
if (
|
|
367
|
-
if (
|
|
368
|
-
let
|
|
365
|
+
} : (O(`[voltro:migrate] brin index '${e}' on '${n}': ${t} 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 (t === "postgres") {
|
|
368
|
+
let e = i?.hnsw?.m ?? 16, t = i?.hnsw?.efConstruction ?? 64, n = i?.hnsw?.opclass ?? y(i?.hnsw?.distance);
|
|
369
369
|
return {
|
|
370
370
|
using: " USING hnsw",
|
|
371
|
-
withSuffix: ` WITH (m = ${
|
|
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 O(`[voltro:migrate] hnsw index '${e}' on '${n}': ${t} 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 i =
|
|
385
|
-
if (typeof
|
|
386
|
-
if ("jsonPath" in
|
|
387
|
-
let { column:
|
|
388
|
-
return `(${
|
|
382
|
+
return a;
|
|
383
|
+
}, c = (n, r) => {
|
|
384
|
+
let i = n.map((n) => {
|
|
385
|
+
if (typeof n == "string") return Fe(n, e.fields, t);
|
|
386
|
+
if ("jsonPath" in n) {
|
|
387
|
+
let { column: e, path: r, numeric: i } = n.jsonPath;
|
|
388
|
+
return `(${g(e, r, t, { numeric: i })})`;
|
|
389
389
|
}
|
|
390
|
-
return `(${
|
|
390
|
+
return `(${n.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, r, a, o, l, u = "", d = !1) => {
|
|
394
|
+
let f = s(e, r, 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 = Ne(r, t, n), g = d ? "UNIQUE " : "";
|
|
397
|
+
return t === "mssql" ? `IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = '${e}') CREATE ${g}INDEX ${i(e, t)} ON ${h}${f.using} (${p})${m};` : t === "mysql" ? `CREATE ${g}INDEX ${i(e, t)} ON ${h}${f.using} (${p})${m};` : `CREATE ${g}INDEX IF NOT EXISTS ${i(e, t)} ON ${h}${f.using} (${p})${m};`;
|
|
398
398
|
};
|
|
399
|
-
for (let
|
|
400
|
-
let
|
|
401
|
-
let
|
|
402
|
-
return `setweight(to_tsvector('${
|
|
399
|
+
for (let n of e.appliedFullText ?? []) if (t === "postgres") {
|
|
400
|
+
let a = n.config ?? "english", o = `${n.name}_tsv`, s = n.columns.map((e) => {
|
|
401
|
+
let r = n.weights?.[e] ?? "A";
|
|
402
|
+
return `setweight(to_tsvector('${a}', coalesce(${i(e, t)}, '')), '${r}')`;
|
|
403
403
|
}).join(" || ");
|
|
404
|
-
|
|
405
|
-
} else if (
|
|
406
|
-
let
|
|
407
|
-
|
|
408
|
-
} else if (
|
|
409
|
-
else
|
|
410
|
-
for (let
|
|
411
|
-
if ((
|
|
412
|
-
|
|
404
|
+
r.push(`ALTER TABLE ${i(e.tableName, t)} ADD COLUMN IF NOT EXISTS ${i(o, t)} tsvector GENERATED ALWAYS AS (${s}) STORED;`), r.push(`CREATE INDEX IF NOT EXISTS ${i(n.name, t)} ON ${i(e.tableName, t)} USING GIN (${i(o, t)});`);
|
|
405
|
+
} else if (t === "mysql" || t === "mariadb") {
|
|
406
|
+
let a = n.columns.map((e) => i(e, t)).join(", ");
|
|
407
|
+
r.push(`CREATE FULLTEXT INDEX ${i(n.name, t)} ON ${i(e.tableName, t)} (${a});`);
|
|
408
|
+
} else if (t === "turso") throw Error(`@voltro/sql-turso: full-text index '${n.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 t === "sqlite" ? r.push(...ze(e.tableName, n, t)) : O(`[voltro:migrate] full-text index '${n.name}' on '${e.tableName}': ${t} uses a ranked LIKE search at query time (no native FTS catalog required).`);
|
|
410
|
+
for (let n of e.appliedIndexes) {
|
|
411
|
+
if ((t === "mariadb" || t === "mssql") && n.fields.some((e) => typeof e != "string" && "jsonPath" in e)) {
|
|
412
|
+
O(`[voltro:migrate] json-path index '${n.name}' on '${e.tableName}': ${t} cannot index a JSON expression directly — skipping. For an indexed JSON path on ${t}, 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 (n.type !== "reference" || n.fkAutoIndex === !1 ||
|
|
423
|
-
let
|
|
424
|
-
|
|
425
|
-
}
|
|
426
|
-
return
|
|
427
|
-
},
|
|
415
|
+
if (n.unique && n.where !== void 0 && n.where.length > 0 && (t === "mysql" || t === "mariadb")) throw Error(`@voltro/migrate: uniqueActive index '${n.name}' on '${e.tableName}' cannot be expressed on ${t} — 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 ${t}, or move this table to postgres/sqlite/mssql.`);
|
|
416
|
+
let i = "";
|
|
417
|
+
n.where !== void 0 && n.where.length > 0 && (t === "mysql" || t === "mariadb" ? O(`[voltro:migrate] partial index '${n.name}' on '${e.tableName}': ${t} does not support WHERE clauses on indexes — emitting a full index`) : i = ` WHERE ${n.where}`);
|
|
418
|
+
let s = l(n.name, e.tableName, n.fields, n.kind, n.kindOptions, i, n.unique === !0);
|
|
419
|
+
s !== "" && r.push(s), n.fields.every((e) => typeof e == "string") && a.add(o(n.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 i = `${e.tableName}_${t}_idx`;
|
|
424
|
+
r.push(l(i, e.tableName, [t])), a.add(o([t]));
|
|
425
|
+
}
|
|
426
|
+
return r.join("\n");
|
|
427
|
+
}, Ve = (e, t, n = D) => {
|
|
428
428
|
if (t !== "postgres") return [];
|
|
429
429
|
let r = e.filter((e) => e.isReactive !== !1);
|
|
430
|
-
return r.length === 0 ? [] : [
|
|
431
|
-
},
|
|
432
|
-
CREATE OR REPLACE FUNCTION ${
|
|
430
|
+
return r.length === 0 ? [] : [He(n), ...r.map((e) => We(e, n))];
|
|
431
|
+
}, He = (e) => `
|
|
432
|
+
CREATE OR REPLACE FUNCTION ${De(e)}() RETURNS trigger AS $$
|
|
433
433
|
DECLARE
|
|
434
434
|
payload TEXT;
|
|
435
|
+
pk JSONB;
|
|
435
436
|
BEGIN
|
|
437
|
+
-- ${Oe}
|
|
436
438
|
payload := json_build_object(
|
|
437
439
|
'table', TG_TABLE_NAME,
|
|
438
440
|
-- The schema the write landed in. Under namespace isolation this is
|
|
@@ -445,42 +447,63 @@ BEGIN
|
|
|
445
447
|
'new', CASE WHEN TG_OP = 'DELETE' THEN NULL ELSE row_to_json(NEW) END
|
|
446
448
|
)::text;
|
|
447
449
|
-- 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
|
-
--
|
|
450
|
-
--
|
|
451
|
-
--
|
|
450
|
+
-- (big text / json column) would otherwise abort the write itself. So the
|
|
451
|
+
-- images are dropped — but the PRIMARY KEY is kept, and that is the whole
|
|
452
|
+
-- difference between a slow path and data loss. A subscription re-queries on
|
|
453
|
+
-- an image-less event and is fine either way; a TAP (search index, analytics
|
|
454
|
+
-- mirror, cdc-out, row history) has nothing to apply and used to drop the
|
|
455
|
+
-- change silently and forever. With the key, the CDC consumer re-reads the
|
|
456
|
+
-- row before anyone sees the event (sql-postgres/src/cdcRehydrate.ts).
|
|
457
|
+
--
|
|
458
|
+
-- to_jsonb(REC) -> 'id' rather than REC.id: a reactive table with no id
|
|
459
|
+
-- column would make the field reference a runtime error inside the trigger,
|
|
460
|
+
-- which aborts the user's WRITE. The json operator answers NULL instead.
|
|
452
461
|
IF octet_length(payload) >= 7999 THEN
|
|
462
|
+
pk := CASE WHEN TG_OP = 'DELETE' THEN to_jsonb(OLD) -> 'id' ELSE to_jsonb(NEW) -> 'id' END;
|
|
453
463
|
payload := json_build_object(
|
|
454
464
|
'table', TG_TABLE_NAME,
|
|
455
465
|
'schema', TG_TABLE_SCHEMA,
|
|
456
466
|
'op', lower(TG_OP),
|
|
457
467
|
'old', NULL,
|
|
458
468
|
'new', NULL,
|
|
469
|
+
'id', pk,
|
|
459
470
|
'oversized', true
|
|
460
471
|
)::text;
|
|
472
|
+
-- A key can itself be oversized (a pathological text primary key). Carrying
|
|
473
|
+
-- it would put us back where the cap check started: pg_notify raises, and
|
|
474
|
+
-- the trigger takes the user's write down with it. Dropping it costs the
|
|
475
|
+
-- re-read and is counted as unrecovered — never the write.
|
|
476
|
+
IF octet_length(payload) >= 7999 THEN
|
|
477
|
+
payload := json_build_object(
|
|
478
|
+
'table', TG_TABLE_NAME,
|
|
479
|
+
'schema', TG_TABLE_SCHEMA,
|
|
480
|
+
'op', lower(TG_OP),
|
|
481
|
+
'old', NULL,
|
|
482
|
+
'new', NULL,
|
|
483
|
+
'oversized', true
|
|
484
|
+
)::text;
|
|
485
|
+
END IF;
|
|
461
486
|
END IF;
|
|
462
487
|
PERFORM pg_notify('${e}', payload);
|
|
463
488
|
RETURN COALESCE(NEW, OLD);
|
|
464
489
|
END;
|
|
465
490
|
$$ LANGUAGE plpgsql;
|
|
466
|
-
`.trim(),
|
|
467
|
-
let n =
|
|
491
|
+
`.trim(), Ue = (e, t) => {
|
|
492
|
+
let n = Ee(e.tableName, t);
|
|
468
493
|
return [
|
|
469
494
|
`ALTER TABLE "${e.tableName}" REPLICA IDENTITY FULL`,
|
|
470
495
|
`DROP TRIGGER IF EXISTS ${n} ON "${e.tableName}"`,
|
|
471
|
-
`CREATE TRIGGER ${n}\nAFTER INSERT OR UPDATE OR DELETE ON "${e.tableName}"\nFOR EACH ROW EXECUTE FUNCTION ${
|
|
496
|
+
`CREATE TRIGGER ${n}\nAFTER INSERT OR UPDATE OR DELETE ON "${e.tableName}"\nFOR EACH ROW EXECUTE FUNCTION ${De(t)}()`
|
|
472
497
|
];
|
|
473
|
-
},
|
|
498
|
+
}, We = (e, t) => Ue(e, t).map((e) => `${e};`).join("\n"), Ge = (e) => {
|
|
474
499
|
if (e.dialect !== "postgres") return [];
|
|
475
500
|
let t = e.channel ?? "framework_changes", n = new Map(e.tables.map((e) => [e.tableName, e])), r = [], i = e.missing.filter((e) => n.has(e));
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
}
|
|
480
|
-
for (let n of e.stale) r.push(`DROP TRIGGER IF EXISTS ${_e(n, t)} ON "${n}"`), r.push(`ALTER TABLE "${n}" REPLICA IDENTITY DEFAULT`);
|
|
501
|
+
(i.length > 0 || e.functionOutdated === !0) && r.push(He(t));
|
|
502
|
+
for (let e of i) r.push(...Ue(n.get(e), t));
|
|
503
|
+
for (let n of e.stale) r.push(`DROP TRIGGER IF EXISTS ${Ee(n, t)} ON "${n}"`), r.push(`ALTER TABLE "${n}" REPLICA IDENTITY DEFAULT`);
|
|
481
504
|
return r;
|
|
482
|
-
},
|
|
483
|
-
let t = new Map(e.map((e) => [e.tableName, e])), n =
|
|
505
|
+
}, Ke = (e) => e.some((e) => Object.values(e.fields).some((e) => e.type === "vector")), qe = (e) => e.some((e) => Object.values(e.fields).some((e) => e.spatial !== void 0)), Je = (e) => {
|
|
506
|
+
let t = new Map(e.map((e) => [e.tableName, e])), n = le(e.map((e) => e.tableName), (e) => {
|
|
484
507
|
let n = t.get(e);
|
|
485
508
|
return n ? Object.entries(n.fields).flatMap(([e, n]) => {
|
|
486
509
|
if (n.type !== "reference" || !n.references) return [];
|
|
@@ -491,7 +514,7 @@ $$ LANGUAGE plpgsql;
|
|
|
491
514
|
}] : [];
|
|
492
515
|
}) : [];
|
|
493
516
|
}), r = [];
|
|
494
|
-
for (let t of e) for (let [e, i] of Object.entries(t.fields)) i.type !== "reference" || !i.references || n.has(
|
|
517
|
+
for (let t of e) for (let [e, i] of Object.entries(t.fields)) i.type !== "reference" || !i.references || n.has(w(t.tableName, e)) && r.push({
|
|
495
518
|
table: t.tableName,
|
|
496
519
|
column: e,
|
|
497
520
|
targetTable: i.references().tableName,
|
|
@@ -502,7 +525,7 @@ $$ LANGUAGE plpgsql;
|
|
|
502
525
|
if (!(i.has(e.tableName) || a.has(e.tableName))) {
|
|
503
526
|
a.add(e.tableName);
|
|
504
527
|
for (let [r, i] of Object.entries(e.fields)) {
|
|
505
|
-
if (i.type !== "reference" || !i.references || n.has(
|
|
528
|
+
if (i.type !== "reference" || !i.references || n.has(w(e.tableName, r))) continue;
|
|
506
529
|
let a = i.references();
|
|
507
530
|
if (a.tableName === e.tableName) continue;
|
|
508
531
|
let o = t.get(a.tableName);
|
|
@@ -516,30 +539,30 @@ $$ LANGUAGE plpgsql;
|
|
|
516
539
|
sorted: o,
|
|
517
540
|
cyclic: r
|
|
518
541
|
};
|
|
519
|
-
},
|
|
520
|
-
let
|
|
542
|
+
}, Ye = (e, t, n = null) => {
|
|
543
|
+
let r = (e) => n ? `${i(n, t)}.${i(e, t)}` : i(e, t), a = `${e.table}_${e.column}_fkey`, o = Me(e.onDelete, t), s = Me(e.onUpdate, t), c = ` ON DELETE ${o}${s === "NO ACTION" ? "" : ` ON UPDATE ${s}`}`, l = (n) => `ADD CONSTRAINT ${i(a, t)} FOREIGN KEY ${n ? "IF NOT EXISTS " : ""}(${i(e.column, t)}) REFERENCES ${r(e.targetTable)} (${i("id", t)})${c}`;
|
|
521
544
|
switch (t) {
|
|
522
545
|
case "postgres": return [
|
|
523
546
|
"DO $$ BEGIN",
|
|
524
547
|
" IF NOT EXISTS (SELECT 1 FROM pg_constraint",
|
|
525
|
-
` WHERE conname = '${a}' AND conrelid = '${
|
|
526
|
-
` ALTER TABLE ${
|
|
548
|
+
` WHERE conname = '${a}' AND conrelid = '${n ? `${n}.` : ""}${e.table}'::regclass) THEN`,
|
|
549
|
+
` ALTER TABLE ${r(e.table)} ${l(!1)};`,
|
|
527
550
|
" END IF;",
|
|
528
551
|
"END $$;"
|
|
529
552
|
].join("\n");
|
|
530
|
-
case "mssql": return `IF NOT EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = '${a}') ALTER TABLE ${
|
|
531
|
-
case "mariadb": return `ALTER TABLE ${
|
|
532
|
-
default: return `ALTER TABLE ${
|
|
553
|
+
case "mssql": return `IF NOT EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = '${a}') ALTER TABLE ${r(e.table)} ${l(!1)};`;
|
|
554
|
+
case "mariadb": return `ALTER TABLE ${r(e.table)} ${l(!0)};`;
|
|
555
|
+
default: return `ALTER TABLE ${r(e.table)} ${l(!1)};`;
|
|
533
556
|
}
|
|
534
|
-
},
|
|
535
|
-
let
|
|
536
|
-
if (t
|
|
557
|
+
}, Xe = (e, t) => {
|
|
558
|
+
let n = /* @__PURE__ */ new Map();
|
|
559
|
+
if (r(t)) return n;
|
|
537
560
|
for (let t of e) {
|
|
538
|
-
let e =
|
|
539
|
-
e.add(t.column),
|
|
561
|
+
let e = n.get(t.table) ?? /* @__PURE__ */ new Set();
|
|
562
|
+
e.add(t.column), n.set(t.table, e);
|
|
540
563
|
}
|
|
541
|
-
return
|
|
542
|
-
},
|
|
564
|
+
return n;
|
|
565
|
+
}, Ze = (e, t) => {
|
|
543
566
|
if (t !== "postgres") return "";
|
|
544
567
|
let n = /* @__PURE__ */ new Map();
|
|
545
568
|
for (let t of e) for (let e of Object.values(t.fields)) {
|
|
@@ -555,12 +578,12 @@ $$ LANGUAGE plpgsql;
|
|
|
555
578
|
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 $$;`);
|
|
556
579
|
}
|
|
557
580
|
return r.join("\n\n");
|
|
558
|
-
},
|
|
581
|
+
}, Qe = (e, t) => {
|
|
559
582
|
if (t !== "postgres") return "";
|
|
560
583
|
let n = /* @__PURE__ */ new Set();
|
|
561
584
|
for (let t of e) for (let e of Object.values(t.fields)) e.type === "enum" && e.enumName && n.add(e.enumName);
|
|
562
585
|
let r = [];
|
|
563
|
-
for (let e of n) for (let { from: t, to: n } of
|
|
586
|
+
for (let e of n) for (let { from: t, to: n } of s(e)) {
|
|
564
587
|
let i = e.replace(/'/g, "''"), a = t.replace(/'/g, "''"), o = n.replace(/'/g, "''");
|
|
565
588
|
r.push(`DO \$\$
|
|
566
589
|
DECLARE
|
|
@@ -569,71 +592,71 @@ BEGIN
|
|
|
569
592
|
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 $$;`);
|
|
570
593
|
}
|
|
571
594
|
return r.join("\n\n");
|
|
572
|
-
},
|
|
595
|
+
}, $e = (e) => {
|
|
573
596
|
let t = [], n = [];
|
|
574
|
-
for (let r of e)
|
|
597
|
+
for (let r of e) f(r) ? n.push(r) : t.push(r);
|
|
575
598
|
return {
|
|
576
599
|
tables: t,
|
|
577
600
|
views: n
|
|
578
601
|
};
|
|
579
|
-
},
|
|
580
|
-
let { tables: i, views: o } =
|
|
581
|
-
|
|
582
|
-
let d =
|
|
602
|
+
}, et = (e, t, n = D) => {
|
|
603
|
+
let { tables: i, views: o } = $e(e), { sorted: s, cyclic: c } = Je(i.map((e) => ce(e, t))), l = Xe(c, t), u = [];
|
|
604
|
+
t === "postgres" && Ke(s) && u.push("CREATE EXTENSION IF NOT EXISTS vector;"), t === "postgres" && qe(s) && u.push("CREATE EXTENSION IF NOT EXISTS postgis;");
|
|
605
|
+
let d = Qe(s, t);
|
|
583
606
|
d && u.push(d);
|
|
584
|
-
let f =
|
|
607
|
+
let f = Ze(s, t);
|
|
585
608
|
f && u.push(f);
|
|
586
|
-
for (let e of s) u.push(
|
|
587
|
-
if (!t
|
|
609
|
+
for (let e of s) u.push(Re(e, t, null, l.get(e.tableName)));
|
|
610
|
+
if (!r(t)) for (let e of c) u.push(Ye(e, t));
|
|
588
611
|
for (let e of s) {
|
|
589
|
-
let
|
|
590
|
-
|
|
612
|
+
let n = Be(e, t);
|
|
613
|
+
n && u.push(n);
|
|
591
614
|
}
|
|
592
|
-
u.push(...
|
|
593
|
-
for (let e of o) u.push(a(e,
|
|
615
|
+
u.push(...Ve(s, t, n));
|
|
616
|
+
for (let e of o) u.push(a(e, t));
|
|
594
617
|
return u.join("\n\n") + "\n";
|
|
595
|
-
},
|
|
596
|
-
let
|
|
618
|
+
}, tt = (e, t, n) => {
|
|
619
|
+
let r = i(e, t);
|
|
597
620
|
switch (t) {
|
|
598
|
-
case "postgres": return `CREATE SCHEMA IF NOT EXISTS ${
|
|
621
|
+
case "postgres": return `CREATE SCHEMA IF NOT EXISTS ${r};`;
|
|
599
622
|
case "mysql":
|
|
600
|
-
case "mariadb": return `CREATE DATABASE IF NOT EXISTS ${
|
|
601
|
-
case "mssql": return `IF SCHEMA_ID('${e.replace(/'/g, "''")}') IS NULL EXEC(N'CREATE SCHEMA ${
|
|
623
|
+
case "mariadb": return `CREATE DATABASE IF NOT EXISTS ${r};`;
|
|
624
|
+
case "mssql": return `IF SCHEMA_ID('${e.replace(/'/g, "''")}') IS NULL EXEC(N'CREATE SCHEMA ${r.replace(/'/g, "''")}');`;
|
|
602
625
|
case "turso":
|
|
603
|
-
case "sqlite": return `ATTACH DATABASE '${(
|
|
604
|
-
}
|
|
605
|
-
},
|
|
606
|
-
let o =
|
|
607
|
-
if (
|
|
608
|
-
if (
|
|
609
|
-
let { sorted: c, cyclic: l } =
|
|
610
|
-
for (let e of c) d.push(
|
|
611
|
-
if (!t
|
|
626
|
+
case "sqlite": return `ATTACH DATABASE '${(n ?? `${e}.db`).replace(/'/g, "''")}' AS ${r};`;
|
|
627
|
+
}
|
|
628
|
+
}, nt = (e, t, n, a) => {
|
|
629
|
+
let o = tt(n, t, a), s = i(n, t);
|
|
630
|
+
if (t === "postgres") return `${o}\n\nSET search_path TO ${s};\n\n${et(e, t)}`;
|
|
631
|
+
if (t === "mysql" || t === "mariadb") return `${o}\n\nUSE ${s};\n\n${et(e, t)}`;
|
|
632
|
+
let { sorted: c, cyclic: l } = Je(e), u = Xe(l, t), d = [o];
|
|
633
|
+
for (let e of c) d.push(Re(e, t, n, u.get(e.tableName)));
|
|
634
|
+
if (!r(t)) for (let e of l) d.push(Ye(e, t, n));
|
|
612
635
|
for (let e of c) {
|
|
613
|
-
let
|
|
614
|
-
|
|
636
|
+
let r = Be(e, t, n);
|
|
637
|
+
r && d.push(r);
|
|
615
638
|
}
|
|
616
639
|
return d.join("\n\n") + "\n";
|
|
617
|
-
},
|
|
618
|
-
let { sorted: r, cyclic: i } =
|
|
640
|
+
}, rt = (e, t, n = D) => {
|
|
641
|
+
let { sorted: r, cyclic: i } = Je(e), a = Xe(i, t), o = [], s = Qe(r, t);
|
|
619
642
|
s && o.push(s);
|
|
620
|
-
let c =
|
|
643
|
+
let c = Ze(r, t);
|
|
621
644
|
c && o.push(c);
|
|
622
|
-
for (let e of r) o.push(
|
|
623
|
-
for (let e of i) o.push(
|
|
624
|
-
return o.push(...
|
|
625
|
-
},
|
|
626
|
-
let r = yield*
|
|
627
|
-
yield*
|
|
628
|
-
}),
|
|
645
|
+
for (let e of r) o.push(Re(e, t, null, a.get(e.tableName)));
|
|
646
|
+
for (let e of i) o.push(Ye(e, t));
|
|
647
|
+
return o.push(...Ve(r, t, n)), o.join("\n\n") + "\n";
|
|
648
|
+
}, it = (e, t = "postgres", n = D) => b.gen(function* () {
|
|
649
|
+
let r = yield* S.SqlClient;
|
|
650
|
+
yield* we(r, st(r, et(e, t, n), t)).pipe(b.orDie);
|
|
651
|
+
}), at = (e, t) => {
|
|
629
652
|
let n = t.cause ?? {};
|
|
630
653
|
return (n.errno === 1061 || n.code === "ER_DUP_KEYNAME") && /^\s*CREATE\s+(UNIQUE\s+|FULLTEXT\s+)?INDEX\b/i.test(e);
|
|
631
|
-
},
|
|
632
|
-
let t = yield*
|
|
633
|
-
for (let n of
|
|
634
|
-
|
|
635
|
-
})),
|
|
636
|
-
}),
|
|
654
|
+
}, ot = (e) => b.gen(function* () {
|
|
655
|
+
let t = yield* S.SqlClient;
|
|
656
|
+
for (let n of ct(e)) process.env.VOLTRO_MIGRATE_DEBUG === "1" && ke.debug(`EXEC: ${n.replace(/\s+/g, " ").slice(0, 180)}`), yield* t.unsafe(n).pipe(b.catchIf((e) => at(n, e), () => b.void), b.tapError((e) => b.sync(() => {
|
|
657
|
+
ke.error(re("migrate: statement failed", n, e, 200));
|
|
658
|
+
})), b.orDieWith((e) => Error(re("migrate: statement failed", n, e, 400).trim())));
|
|
659
|
+
}), st = (e, t, n) => (n === "turso" ? ot(t) : e.withTransaction(ot(t))).pipe(b.orDie), ct = (e) => {
|
|
637
660
|
let t = [], n = "", r = !1;
|
|
638
661
|
for (let i = 0; i < e.length; i++) {
|
|
639
662
|
if (e.slice(i, i + 2) === "$$") {
|
|
@@ -648,40 +671,43 @@ BEGIN
|
|
|
648
671
|
}
|
|
649
672
|
let i = n.trim();
|
|
650
673
|
return i.length > 0 && t.push(i), t;
|
|
651
|
-
},
|
|
652
|
-
await
|
|
653
|
-
},
|
|
654
|
-
let r =
|
|
655
|
-
yield*
|
|
656
|
-
}),
|
|
657
|
-
await
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
674
|
+
}, lt = async (e, t, n = "postgres") => {
|
|
675
|
+
await b.runPromise(it(e, n).pipe(b.provide(t)));
|
|
676
|
+
}, ut = (e, t, n = "postgres") => b.gen(function* () {
|
|
677
|
+
let r = nt(e, n, t), i = yield* S.SqlClient;
|
|
678
|
+
yield* we(i, st(i, r, n)).pipe(b.orDie);
|
|
679
|
+
}), dt = async (e, t, n, r = "postgres") => {
|
|
680
|
+
await b.runPromise(ut(e, t, r).pipe(b.provide(n))), await d({
|
|
681
|
+
namespace: t,
|
|
682
|
+
dialect: r
|
|
683
|
+
});
|
|
684
|
+
}, ft = async (e, t, n = "postgres") => {
|
|
685
|
+
await b.runPromise(b.gen(function* () {
|
|
686
|
+
let t = rt(e, n), r = yield* S.SqlClient;
|
|
687
|
+
yield* we(r, st(r, t, n)).pipe(b.orDie);
|
|
688
|
+
}).pipe(b.provide(t)));
|
|
689
|
+
}, pt = (e) => {
|
|
664
690
|
if (e == null) return e;
|
|
665
|
-
if (Array.isArray(e)) return e.map(
|
|
691
|
+
if (Array.isArray(e)) return e.map(pt);
|
|
666
692
|
if (typeof e != "object") return e;
|
|
667
693
|
let t = {};
|
|
668
694
|
for (let n of Object.keys(e).sort()) {
|
|
669
695
|
let r = e[n];
|
|
670
|
-
r !== void 0 && (t[n] =
|
|
696
|
+
r !== void 0 && (t[n] = pt(r));
|
|
671
697
|
}
|
|
672
698
|
return t;
|
|
673
|
-
},
|
|
699
|
+
}, mt = (e) => {
|
|
674
700
|
if (!e.idScheme && !e.sensitive && !e.safe) return e;
|
|
675
701
|
let { idScheme: t, sensitive: n, safe: r, ...i } = e;
|
|
676
702
|
return i;
|
|
677
|
-
},
|
|
703
|
+
}, ht = (e) => [...e].map(mt).sort((e, t) => e.name.localeCompare(t.name)), gt = (e) => [...e].sort((e, t) => e.name.localeCompare(t.name)), _t = (e) => [...e].map((e) => ({
|
|
678
704
|
name: e.name,
|
|
679
|
-
columns:
|
|
680
|
-
indexes:
|
|
681
|
-
})).sort((e, t) => e.name.localeCompare(t.name)),
|
|
682
|
-
let t = { tables:
|
|
683
|
-
return
|
|
684
|
-
},
|
|
705
|
+
columns: ht(e.columns),
|
|
706
|
+
indexes: gt(e.indexes)
|
|
707
|
+
})).sort((e, t) => e.name.localeCompare(t.name)), M = (e) => {
|
|
708
|
+
let t = { tables: _t(e.tables) }, n = JSON.stringify(pt(t));
|
|
709
|
+
return C("sha256").update(n).digest("hex");
|
|
710
|
+
}, N = (e) => e.slice(0, 16), vt = (e, t) => {
|
|
685
711
|
let n = t.references ? {
|
|
686
712
|
table: t.references().tableName,
|
|
687
713
|
column: "id",
|
|
@@ -720,7 +746,7 @@ BEGIN
|
|
|
720
746
|
...t.enumValues === void 0 ? {} : { enumValues: t.enumValues },
|
|
721
747
|
...t.spatial === void 0 ? {} : { spatial: t.spatial }
|
|
722
748
|
};
|
|
723
|
-
},
|
|
749
|
+
}, yt = (e) => {
|
|
724
750
|
let t = [], n = /* @__PURE__ */ new Set(), r = (e) => {
|
|
725
751
|
n.has(e.name) || (n.add(e.name), t.push(e));
|
|
726
752
|
}, i = e.appliedPrimaryKey ?? [];
|
|
@@ -760,7 +786,7 @@ BEGIN
|
|
|
760
786
|
let i = n;
|
|
761
787
|
if (i.dropped || !i.references || i.fkAutoIndex === !1 || s.has(t)) continue;
|
|
762
788
|
let a = `${e.tableName}_${t}_idx`;
|
|
763
|
-
|
|
789
|
+
p(e.tableName, a, !0, [t]), r({
|
|
764
790
|
name: a,
|
|
765
791
|
table: e.tableName,
|
|
766
792
|
columns: [t],
|
|
@@ -768,11 +794,11 @@ BEGIN
|
|
|
768
794
|
});
|
|
769
795
|
}
|
|
770
796
|
return t;
|
|
771
|
-
},
|
|
797
|
+
}, bt = (e) => {
|
|
772
798
|
let t = new Set((e.appliedUniques ?? []).filter((e) => e.fields.length === 1).map((e) => e.fields[0])), n = [];
|
|
773
799
|
for (let [r, i] of Object.entries(e.fields)) {
|
|
774
800
|
if (i.dropped) continue;
|
|
775
|
-
let e =
|
|
801
|
+
let e = vt(r, i);
|
|
776
802
|
n.push(t.has(r) ? {
|
|
777
803
|
...e,
|
|
778
804
|
unique: !0
|
|
@@ -782,44 +808,52 @@ BEGIN
|
|
|
782
808
|
return {
|
|
783
809
|
name: e.tableName,
|
|
784
810
|
columns: n,
|
|
785
|
-
indexes:
|
|
811
|
+
indexes: yt(e),
|
|
786
812
|
...r.length > 0 ? { primaryKey: [...r] } : {},
|
|
787
813
|
...i === void 0 ? {} : { renamedFrom: i }
|
|
788
814
|
};
|
|
789
|
-
},
|
|
815
|
+
}, xt = (e) => {
|
|
790
816
|
let t = /* @__PURE__ */ new Map();
|
|
791
817
|
for (let n of e) for (let e of n.indexes) {
|
|
792
818
|
let r = t.get(e.name);
|
|
793
819
|
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}').`);
|
|
794
820
|
t.set(e.name, n.name);
|
|
795
821
|
}
|
|
796
|
-
},
|
|
797
|
-
let n = e.map((e) =>
|
|
798
|
-
return
|
|
799
|
-
},
|
|
800
|
-
let r =
|
|
801
|
-
|
|
822
|
+
}, P = (e, t) => {
|
|
823
|
+
let n = e.map((e) => bt(ce(e, t)));
|
|
824
|
+
return xt(n), { tables: n };
|
|
825
|
+
}, St = (e, t, n = D) => b.gen(function* () {
|
|
826
|
+
let r = (yield* e`
|
|
827
|
+
SELECT p.prosrc
|
|
828
|
+
FROM pg_proc p
|
|
829
|
+
JOIN pg_namespace n ON n.oid = p.pronamespace
|
|
830
|
+
WHERE n.nspname = current_schema()
|
|
831
|
+
AND p.proname = ${De(n)}
|
|
832
|
+
`)[0]?.prosrc, i = r !== void 0 && !r.includes("voltro:notify:v2"), a = t.filter((e) => !e.tableName.startsWith("_voltro_"));
|
|
833
|
+
if (a.length === 0) return {
|
|
802
834
|
missing: [],
|
|
803
|
-
stale: []
|
|
835
|
+
stale: [],
|
|
836
|
+
functionOutdated: i
|
|
804
837
|
};
|
|
805
|
-
let
|
|
838
|
+
let o = yield* e`
|
|
806
839
|
SELECT t.tgname
|
|
807
840
|
FROM pg_trigger t
|
|
808
841
|
JOIN pg_class c ON c.oid = t.tgrelid
|
|
809
842
|
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
810
843
|
WHERE NOT t.tgisinternal
|
|
811
844
|
AND n.nspname = current_schema()
|
|
812
|
-
AND t.tgname LIKE ${`${
|
|
813
|
-
`,
|
|
814
|
-
for (let e of
|
|
815
|
-
let t =
|
|
816
|
-
r && !t &&
|
|
845
|
+
AND t.tgname LIKE ${`${Te}%`}
|
|
846
|
+
`, s = new Set(o.map((e) => e.tgname)), c = [], l = [];
|
|
847
|
+
for (let e of a) {
|
|
848
|
+
let t = s.has(Ee(e.tableName, n)), r = e.isReactive !== !1;
|
|
849
|
+
r && !t && c.push(e.tableName), !r && t && l.push(e.tableName);
|
|
817
850
|
}
|
|
818
851
|
return {
|
|
819
|
-
missing:
|
|
820
|
-
stale:
|
|
852
|
+
missing: c.sort(),
|
|
853
|
+
stale: l.sort(),
|
|
854
|
+
functionOutdated: i
|
|
821
855
|
};
|
|
822
|
-
}),
|
|
856
|
+
}), Ct = (e) => {
|
|
823
857
|
let t = [];
|
|
824
858
|
if (e.missing.length > 0) {
|
|
825
859
|
let n = e.missing.slice(0, 8).join(", "), r = e.missing.length > 8 ? `, … (+${e.missing.length - 8})` : "";
|
|
@@ -829,20 +863,20 @@ BEGIN
|
|
|
829
863
|
let n = e.stale.slice(0, 8).join(", ");
|
|
830
864
|
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.`);
|
|
831
865
|
}
|
|
832
|
-
return t.length > 0 ? t.join("\n") : void 0;
|
|
833
|
-
},
|
|
866
|
+
return e.functionOutdated && t.push("The change-notify function in this database is OLDER than the framework installed here. Its triggers fire, so nothing looks wrong — but a row too large for a NOTIFY payload (postgres caps it at 8000 bytes) is emitted without its primary key, and a change that carries no key cannot be re-read: it reaches subscriptions as a re-query and reaches every TAP — search index, analytics mirror, cdc-out, row history — as nothing at all. Run `voltro db apply` to replace it."), t.length > 0 ? t.join("\n") : void 0;
|
|
867
|
+
}, wt = {
|
|
834
868
|
postgres: !0,
|
|
835
869
|
mysql: !0,
|
|
836
870
|
mariadb: !0,
|
|
837
871
|
mssql: !0,
|
|
838
872
|
sqlite: !0
|
|
839
|
-
},
|
|
873
|
+
}, Tt = {
|
|
840
874
|
postgres: !0,
|
|
841
875
|
mysql: !1,
|
|
842
876
|
mariadb: !1,
|
|
843
877
|
mssql: !0,
|
|
844
878
|
sqlite: !0
|
|
845
|
-
},
|
|
879
|
+
}, Et = (e) => {
|
|
846
880
|
switch (e) {
|
|
847
881
|
case "add-column":
|
|
848
882
|
case "drop-column":
|
|
@@ -852,47 +886,47 @@ BEGIN
|
|
|
852
886
|
case "add-foreign-key":
|
|
853
887
|
case "add-unique":
|
|
854
888
|
case "add-unique-composite":
|
|
855
|
-
case "add-check": return
|
|
856
|
-
default: return
|
|
889
|
+
case "add-check": return Tt;
|
|
890
|
+
default: return wt;
|
|
857
891
|
}
|
|
858
|
-
},
|
|
892
|
+
}, Dt = (e, t) => {
|
|
859
893
|
let n = new Map(t.tables.map((e) => [e.name, e]));
|
|
860
894
|
return new Map(e.map((e) => [e.tableName, {
|
|
861
895
|
table: e,
|
|
862
896
|
snapshot: n.get(e.tableName),
|
|
863
897
|
columnDefs: e.fields
|
|
864
898
|
}]));
|
|
865
|
-
},
|
|
899
|
+
}, F = (e) => {
|
|
866
900
|
if (typeof e != "object" || !e) return JSON.stringify(e);
|
|
867
|
-
if (Array.isArray(e)) return `[${e.map(
|
|
901
|
+
if (Array.isArray(e)) return `[${e.map(F).join(",")}]`;
|
|
868
902
|
let t = e;
|
|
869
|
-
return `{${Object.keys(t).sort().map((e) => `${JSON.stringify(e)}:${
|
|
870
|
-
},
|
|
903
|
+
return `{${Object.keys(t).sort().map((e) => `${JSON.stringify(e)}:${F(t[e])}`).join(",")}}`;
|
|
904
|
+
}, Ot = (e) => {
|
|
871
905
|
if (!e.hasDefault || e.defaultValue === void 0 || e.defaultValue === null) return null;
|
|
872
906
|
let t = e.defaultValue;
|
|
873
907
|
if (typeof t == "boolean") return t ? "__BOOL_TRUE__" : "__BOOL_FALSE__";
|
|
874
908
|
if (typeof t == "number") return String(t);
|
|
875
|
-
if (typeof t != "string") return
|
|
909
|
+
if (typeof t != "string") return F(t);
|
|
876
910
|
let n = t.trim(), r = n.toLowerCase().replace(/\s+/g, "");
|
|
877
911
|
if (r === "now" || r === "now()" || /^current_timestamp(\(\d*\))?$/.test(r) || r === "sysutcdatetime()" || r === "sysdatetime()" || r === "getutcdate()" || r === "getdate()") return "__NOW__";
|
|
878
912
|
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);
|
|
879
913
|
if (o) try {
|
|
880
|
-
return
|
|
914
|
+
return F(JSON.parse(o[1]));
|
|
881
915
|
} catch {}
|
|
882
916
|
let s = /^convert\(\s*(?:_\w+\s*)?\\?'(.*?)\\?'\s+using\s+[\w]+\s*\)$/i.exec(a);
|
|
883
917
|
if (s) try {
|
|
884
|
-
return
|
|
918
|
+
return F(JSON.parse(s[1]));
|
|
885
919
|
} catch {}
|
|
886
920
|
if (a.startsWith("{") || a.startsWith("[")) try {
|
|
887
|
-
return
|
|
921
|
+
return F(JSON.parse(a));
|
|
888
922
|
} catch {}
|
|
889
923
|
return /^-?\d+(\.\d+)?$/.test(a) ? String(Number(a)) : a;
|
|
890
|
-
},
|
|
891
|
-
let n =
|
|
924
|
+
}, kt = (e, t) => {
|
|
925
|
+
let n = Ot(e), r = Ot(t);
|
|
892
926
|
if (n === r) return !0;
|
|
893
927
|
let i = (e) => e === "1" || e === "true" ? "__BOOL_TRUE__" : e === "0" || e === "false" ? "__BOOL_FALSE__" : e;
|
|
894
928
|
return i(n) === i(r);
|
|
895
|
-
},
|
|
929
|
+
}, At = (e) => {
|
|
896
930
|
if (!e) return null;
|
|
897
931
|
let t = (e) => e === "restrict" ? "noAction" : e;
|
|
898
932
|
return {
|
|
@@ -901,25 +935,25 @@ BEGIN
|
|
|
901
935
|
onDelete: t(e.onDelete),
|
|
902
936
|
onUpdate: t(e.onUpdate)
|
|
903
937
|
};
|
|
904
|
-
},
|
|
938
|
+
}, jt = (e, t) => {
|
|
905
939
|
let n = e && e.length > 0 ? e : void 0, r = t && t.length > 0 ? t : void 0;
|
|
906
940
|
if (!n && !r) return !0;
|
|
907
941
|
if (!n || !r || n.length !== r.length) return !1;
|
|
908
942
|
let i = [...n].sort(), a = [...r].sort();
|
|
909
943
|
return i.every((e, t) => e === a[t]);
|
|
910
|
-
},
|
|
944
|
+
}, Mt = (e, t) => {
|
|
911
945
|
try {
|
|
912
|
-
let n = /\((\d+)\)/.exec(
|
|
946
|
+
let n = /\((\d+)\)/.exec(k(e, t));
|
|
913
947
|
return n ? Number(n[1]) : void 0;
|
|
914
948
|
} catch {
|
|
915
949
|
return;
|
|
916
950
|
}
|
|
917
|
-
},
|
|
951
|
+
}, Nt = (e, t, n) => n === void 0 || r(n) ? !0 : e.type !== "text" || t.type !== "text" || Mt(e, n) === t.maxLength, Pt = (e, t, n) => e.type === t.type && e.nullable === t.nullable && e.unique === t.unique && kt(e, t) && Nt(e, t, n) && JSON.stringify(At(e.references)) === JSON.stringify(At(t.references)) && jt(e.oneOf, t.oneOf), Ft = (e, t) => t.oneOf && t.oneOf.length > 0 ? I({
|
|
918
952
|
kind: "add-check",
|
|
919
953
|
table: e,
|
|
920
954
|
column: t.name,
|
|
921
955
|
values: t.oneOf
|
|
922
|
-
}, "safe", `enum-membership CHECK on ${e}.${t.name}`) : void 0,
|
|
956
|
+
}, "safe", `enum-membership CHECK on ${e}.${t.name}`) : void 0, It = (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])), Lt = (e, t, n) => {
|
|
923
957
|
let r = new Map(t.columns.map((e) => [e.name, e])), i = new Map(e.columns.map((e) => [e.name, e])), a = [], o = [], s = [];
|
|
924
958
|
for (let t of e.columns) {
|
|
925
959
|
let e = r.get(t.name);
|
|
@@ -927,7 +961,7 @@ BEGIN
|
|
|
927
961
|
a.push(t);
|
|
928
962
|
continue;
|
|
929
963
|
}
|
|
930
|
-
|
|
964
|
+
Pt(t, e, n) || s.push({
|
|
931
965
|
declared: t,
|
|
932
966
|
live: e
|
|
933
967
|
});
|
|
@@ -938,47 +972,47 @@ BEGIN
|
|
|
938
972
|
removed: o,
|
|
939
973
|
changed: s
|
|
940
974
|
};
|
|
941
|
-
},
|
|
975
|
+
}, Rt = (e, t) => {
|
|
942
976
|
let n = new Map(t.indexes.map((e) => [e.name, e])), r = new Map(e.indexes.map((e) => [e.name, e])), i = [], a = [];
|
|
943
977
|
for (let t of e.indexes) {
|
|
944
978
|
let e = n.get(t.name);
|
|
945
|
-
(!e || !
|
|
979
|
+
(!e || !It(t, e)) && i.push(t);
|
|
946
980
|
}
|
|
947
981
|
for (let e of t.indexes) r.has(e.name) || a.push(e);
|
|
948
982
|
return {
|
|
949
983
|
added: i,
|
|
950
984
|
removed: a
|
|
951
985
|
};
|
|
952
|
-
},
|
|
986
|
+
}, I = (e, t, n, r) => ({
|
|
953
987
|
op: e,
|
|
954
988
|
classification: t,
|
|
955
|
-
atomicByDialect:
|
|
989
|
+
atomicByDialect: Et(e.kind),
|
|
956
990
|
...n ? { reason: n } : {},
|
|
957
991
|
...r ? { blocked: r } : {}
|
|
958
|
-
}),
|
|
992
|
+
}), zt = (e, t, n) => {
|
|
959
993
|
let r = {
|
|
960
994
|
kind: "add-column",
|
|
961
995
|
table: e,
|
|
962
996
|
column: t
|
|
963
997
|
};
|
|
964
|
-
return t.nullable ?
|
|
965
|
-
},
|
|
998
|
+
return t.nullable ? I(r, "safe", "nullable column add — no backfill needed") : t.hasDefault && t.defaultValue !== void 0 ? I(r, "needs-default", "NOT NULL with literal default — DDL DEFAULT will populate existing rows") : n?.backfill ? I(r, "needs-backfill", `backfill declared (kind=${n.backfill.kind}) — applier will run 3-step add/update/set-not-null`) : I(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` });
|
|
999
|
+
}, Bt = (e, t, n) => {
|
|
966
1000
|
let r = {
|
|
967
1001
|
kind: "drop-column",
|
|
968
1002
|
table: e,
|
|
969
1003
|
column: t.name
|
|
970
1004
|
};
|
|
971
|
-
return n?.dropped ?
|
|
972
|
-
},
|
|
1005
|
+
return n?.dropped ? I(r, "lossy", `column dropped via \`${t.name}: dropped()\` marker — intentional`) : I(r, "lossy", "column missing from declared schema", { fix: `if intentional, add \`${t.name}: dropped()\` to the schema. If a typo, restore the field` });
|
|
1006
|
+
}, Vt = (e, t, n, r, i) => I({
|
|
973
1007
|
kind: "create-table",
|
|
974
1008
|
table: e,
|
|
975
1009
|
columns: t,
|
|
976
1010
|
indexes: n,
|
|
977
1011
|
...r && r.length > 0 ? { primaryKey: r } : {}
|
|
978
|
-
}, "safe", i ?? "new table — no data to preserve"),
|
|
1012
|
+
}, "safe", i ?? "new table — no data to preserve"), Ht = (e) => I({
|
|
979
1013
|
kind: "drop-table",
|
|
980
1014
|
table: e
|
|
981
|
-
}, "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" }),
|
|
1015
|
+
}, "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" }), Ut = 5e4, Wt = (e, t, n, r) => {
|
|
982
1016
|
let i = (n ?? 0) >= r, a;
|
|
983
1017
|
return a = t.unique ? t.columns.length === 1 ? {
|
|
984
1018
|
kind: "add-unique",
|
|
@@ -993,16 +1027,16 @@ BEGIN
|
|
|
993
1027
|
kind: "add-index",
|
|
994
1028
|
table: e,
|
|
995
1029
|
index: t
|
|
996
|
-
}, i ?
|
|
997
|
-
},
|
|
1030
|
+
}, i ? I(a, "online-required", `online build on ${n} rows — CONCURRENTLY / INPLACE / WITH(ONLINE=ON) depending on dialect`) : I(a, "safe", t.unique ? t.columns.length === 1 ? "add unique constraint" : "add composite UNIQUE constraint" : "add B-tree index");
|
|
1031
|
+
}, Gt = (e, t) => t.unique && t.columns.length > 1 ? I({
|
|
998
1032
|
kind: "drop-unique-composite",
|
|
999
1033
|
table: e,
|
|
1000
1034
|
name: t.name
|
|
1001
|
-
}, "safe", "drop composite UNIQUE constraint (no data loss — constraint is derived metadata)") :
|
|
1035
|
+
}, "safe", "drop composite UNIQUE constraint (no data loss — constraint is derived metadata)") : I({
|
|
1002
1036
|
kind: "drop-index",
|
|
1003
1037
|
table: e,
|
|
1004
1038
|
index: t.name
|
|
1005
|
-
}, "safe", "drop index (no data loss — index is derived data)"),
|
|
1039
|
+
}, "safe", "drop index (no data loss — index is derived data)"), Kt = (e, t) => {
|
|
1006
1040
|
if (e === "reference") return "text";
|
|
1007
1041
|
switch (t) {
|
|
1008
1042
|
case "sqlite":
|
|
@@ -1010,106 +1044,107 @@ BEGIN
|
|
|
1010
1044
|
case "mssql": return e === "json" ? "text" : e;
|
|
1011
1045
|
default: return e;
|
|
1012
1046
|
}
|
|
1013
|
-
},
|
|
1047
|
+
}, qt = (e, t, n, i, a, o) => {
|
|
1014
1048
|
let s = [];
|
|
1015
|
-
if (
|
|
1016
|
-
let
|
|
1049
|
+
if (t.nullable !== n.nullable) {
|
|
1050
|
+
let n = {
|
|
1017
1051
|
kind: "alter-column-nullability",
|
|
1018
1052
|
table: e,
|
|
1019
|
-
column:
|
|
1020
|
-
toNullable:
|
|
1053
|
+
column: t.name,
|
|
1054
|
+
toNullable: t.nullable
|
|
1021
1055
|
};
|
|
1022
|
-
s.push(
|
|
1056
|
+
s.push(t.nullable ? I(n, "safe", `loosen ${e}.${t.name} to nullable`) : I(n, "needs-backfill", `tighten ${e}.${t.name} to NOT NULL — existing rows must already be non-null`));
|
|
1023
1057
|
}
|
|
1024
|
-
if (
|
|
1025
|
-
let
|
|
1058
|
+
if (t.unique !== n.unique) if (t.unique) {
|
|
1059
|
+
let n = {
|
|
1026
1060
|
kind: "add-unique",
|
|
1027
1061
|
table: e,
|
|
1028
|
-
column:
|
|
1062
|
+
column: t.name
|
|
1029
1063
|
}, r = (a ?? 0) >= o;
|
|
1030
|
-
s.push(r ?
|
|
1031
|
-
} else s.push(
|
|
1064
|
+
s.push(r ? I(n, "online-required", `online unique build on ${a} rows — CONCURRENTLY / INPLACE / WITH(ONLINE=ON) depending on dialect`) : I(n, "safe", `add unique constraint on ${e}.${t.name} — existing rows must already be distinct (else the DB rejects at migrate)`));
|
|
1065
|
+
} else s.push(I({
|
|
1032
1066
|
kind: "drop-unique",
|
|
1033
1067
|
table: e,
|
|
1034
|
-
column:
|
|
1035
|
-
}, "safe", `drop unique constraint on ${e}.${
|
|
1036
|
-
if (
|
|
1037
|
-
let
|
|
1038
|
-
if (
|
|
1068
|
+
column: t.name
|
|
1069
|
+
}, "safe", `drop unique constraint on ${e}.${t.name} — constraint is derived metadata, no data loss`));
|
|
1070
|
+
if (Kt(t.type, i) !== Kt(n.type, i)) {
|
|
1071
|
+
let r = t.narrowedFrom;
|
|
1072
|
+
if (r && r.from === n.type) {
|
|
1039
1073
|
let i = {
|
|
1040
1074
|
kind: "alter-column-type",
|
|
1041
1075
|
table: e,
|
|
1042
|
-
column:
|
|
1043
|
-
from:
|
|
1044
|
-
to:
|
|
1045
|
-
...
|
|
1076
|
+
column: t.name,
|
|
1077
|
+
from: n.type,
|
|
1078
|
+
to: t.type,
|
|
1079
|
+
...r.using === void 0 ? {} : { using: r.using }
|
|
1046
1080
|
};
|
|
1047
|
-
s.push(
|
|
1081
|
+
s.push(I(i, "needs-backfill", `type change ${n.type} → ${t.type} on ${e}.${t.name} — narrowedFrom('${r.from}') declared` + (r.using ? ` with USING ${r.using}` : " (implicit cast)")));
|
|
1048
1082
|
} else {
|
|
1049
|
-
let
|
|
1083
|
+
let r = {
|
|
1050
1084
|
kind: "alter-column-type",
|
|
1051
1085
|
table: e,
|
|
1052
|
-
column:
|
|
1053
|
-
from:
|
|
1054
|
-
to:
|
|
1086
|
+
column: t.name,
|
|
1087
|
+
from: n.type,
|
|
1088
|
+
to: t.type
|
|
1055
1089
|
};
|
|
1056
|
-
s.push(
|
|
1090
|
+
s.push(I(r, "lossy", `type change ${n.type} → ${t.type} on ${e}.${t.name} may not be value-preserving`, { fix: `acknowledge it on the column: \`.narrowedFrom('${n.type}', { using: '${t.name}::${t.type}' })\` (drop \`using\` if the cast is implicit), or use a file-based migration` }));
|
|
1057
1091
|
}
|
|
1058
1092
|
}
|
|
1059
|
-
if (
|
|
1060
|
-
let
|
|
1061
|
-
s.push(
|
|
1093
|
+
if (t.type === "text" && n.type === "text" && !r(i ?? "postgres") && t.maxLength !== n.maxLength) {
|
|
1094
|
+
let r = t.maxLength === void 0 || n.maxLength !== void 0 && t.maxLength > n.maxLength, i = n.maxLength === void 0 ? "unbounded" : `varchar(${n.maxLength})`, a = t.maxLength === void 0 ? "unbounded" : `varchar(${t.maxLength})`;
|
|
1095
|
+
s.push(I({
|
|
1062
1096
|
kind: "alter-column-type",
|
|
1063
1097
|
table: e,
|
|
1064
|
-
column:
|
|
1098
|
+
column: t.name,
|
|
1065
1099
|
from: "text",
|
|
1066
1100
|
to: "text"
|
|
1067
|
-
},
|
|
1101
|
+
}, r ? "safe" : "needs-backfill", r ? `text length ${i} → ${a} on ${e}.${t.name} — widening, no data can be lost` : `text length ${i} → ${a} on ${e}.${t.name} — NARROWING; the ALTER fails if any existing value is longer than ${String(t.maxLength)}. Check first: SELECT COUNT(*) FROM ${e} WHERE LENGTH(${t.name}) > ${String(t.maxLength)}`));
|
|
1068
1102
|
}
|
|
1069
|
-
if (!
|
|
1070
|
-
let
|
|
1103
|
+
if (!kt(t, n)) {
|
|
1104
|
+
let n = {
|
|
1071
1105
|
kind: "alter-column-default",
|
|
1072
1106
|
table: e,
|
|
1073
|
-
column:
|
|
1074
|
-
hasDefault:
|
|
1075
|
-
...
|
|
1107
|
+
column: t.name,
|
|
1108
|
+
hasDefault: t.hasDefault,
|
|
1109
|
+
...t.defaultValue === void 0 ? {} : { defaultValue: t.defaultValue }
|
|
1076
1110
|
};
|
|
1077
|
-
s.push(
|
|
1111
|
+
s.push(I(n, "safe", `default change on ${e}.${t.name} affects only future inserts`));
|
|
1078
1112
|
}
|
|
1079
|
-
if (!
|
|
1080
|
-
|
|
1113
|
+
if (!jt(t.oneOf, n.oneOf)) {
|
|
1114
|
+
n.oneOf && n.oneOf.length > 0 && s.push(I({
|
|
1081
1115
|
kind: "drop-check",
|
|
1082
1116
|
table: e,
|
|
1083
|
-
column:
|
|
1084
|
-
}, "safe", `drop stale enum CHECK on ${e}.${
|
|
1085
|
-
let
|
|
1086
|
-
|
|
1117
|
+
column: t.name
|
|
1118
|
+
}, "safe", `drop stale enum CHECK on ${e}.${t.name}`));
|
|
1119
|
+
let r = Ft(e, t);
|
|
1120
|
+
r && s.push(r);
|
|
1087
1121
|
}
|
|
1088
|
-
|
|
1122
|
+
return JSON.stringify(At(t.references)) !== JSON.stringify(At(n.references)) && (n.references && s.push(I({
|
|
1089
1123
|
kind: "drop-foreign-key",
|
|
1090
1124
|
table: e,
|
|
1091
|
-
column:
|
|
1092
|
-
}, "safe", `drop FK on ${e}.${
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
},
|
|
1125
|
+
column: t.name
|
|
1126
|
+
}, "safe", `drop FK on ${e}.${t.name} (→ ${n.references.table})`)), s.push(...Jt(e, t))), s;
|
|
1127
|
+
}, Jt = (e, t) => {
|
|
1128
|
+
let n = [];
|
|
1129
|
+
if (!t.references) return n;
|
|
1130
|
+
let r = t.references.table, i = t.references.orphanPolicy ?? "fail", a = {
|
|
1131
|
+
kind: "add-foreign-key",
|
|
1132
|
+
table: e,
|
|
1133
|
+
column: t.name,
|
|
1134
|
+
targetTable: r,
|
|
1135
|
+
targetColumn: t.references.column,
|
|
1136
|
+
...t.references.onDelete ? { onDelete: t.references.onDelete } : {},
|
|
1137
|
+
...t.references.onUpdate ? { onUpdate: t.references.onUpdate } : {}
|
|
1138
|
+
};
|
|
1139
|
+
return i === "null" && !t.nullable ? n.push(I(a, "lossy", `add FK ${e}.${t.name} → ${r}: orphanPolicy 'null' needs a nullable column (the NULL-out would violate NOT NULL)`, { fix: `make ${t.name} \`.nullable()\`, OR use \`orphanPolicy: 'delete'\`, OR clean the orphans and drop the policy` })) : i === "null" || i === "delete" ? n.push(I({
|
|
1140
|
+
...a,
|
|
1141
|
+
orphanPolicy: i
|
|
1142
|
+
}, "lossy", i === "delete" ? `add FK ${e}.${t.name} → ${r}: orphanPolicy 'delete' first DELETES rows whose ${t.name} points at a missing ${r}` : `add FK ${e}.${t.name} → ${r}: orphanPolicy 'null' first NULLs ${t.name} where it points at a missing ${r}`)) : n.push(I(a, "needs-backfill", `add FK ${e}.${t.name} → ${r} — existing rows must already reference a valid ${r} (else set orphanPolicy:'null'/'delete')`)), n;
|
|
1143
|
+
}, Yt = (e, t, n) => {
|
|
1109
1144
|
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));
|
|
1110
1145
|
for (let [n, c] of Object.entries(t)) {
|
|
1111
1146
|
let t = c.renamedFrom;
|
|
1112
|
-
t && o.has(t) && s.has(n) && (i.add(n), a.add(t), r.push(
|
|
1147
|
+
t && o.has(t) && s.has(n) && (i.add(n), a.add(t), r.push(I({
|
|
1113
1148
|
kind: "rename-column",
|
|
1114
1149
|
table: e.tableName,
|
|
1115
1150
|
from: t,
|
|
@@ -1121,26 +1156,26 @@ BEGIN
|
|
|
1121
1156
|
consumedAddedNames: i,
|
|
1122
1157
|
consumedRemovedNames: a
|
|
1123
1158
|
};
|
|
1124
|
-
},
|
|
1159
|
+
}, Xt = /* @__PURE__ */ new Set([
|
|
1125
1160
|
"postgres",
|
|
1126
1161
|
"mysql",
|
|
1127
1162
|
"mariadb",
|
|
1128
1163
|
"mssql"
|
|
1129
|
-
]),
|
|
1164
|
+
]), Zt = {
|
|
1130
1165
|
ops: [],
|
|
1131
1166
|
consumedAddedNames: /* @__PURE__ */ new Set(),
|
|
1132
1167
|
consumedRemovedNames: /* @__PURE__ */ new Set()
|
|
1133
|
-
},
|
|
1134
|
-
if (i === void 0 || !
|
|
1135
|
-
let a = t.added.filter((e) =>
|
|
1136
|
-
if (a.length === 0) return
|
|
1168
|
+
}, Qt = (e) => !e.unique && !e.expression, $t = (e, t, n, r, i) => {
|
|
1169
|
+
if (i === void 0 || !Xt.has(i)) return Zt;
|
|
1170
|
+
let a = t.added.filter((e) => Qt(e) && !r.has(e.name));
|
|
1171
|
+
if (a.length === 0) return Zt;
|
|
1137
1172
|
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();
|
|
1138
1173
|
for (let n of t.removed) {
|
|
1139
|
-
if (!
|
|
1174
|
+
if (!Qt(n)) continue;
|
|
1140
1175
|
let t = o(n), r = a.filter((e) => !l.has(e.name) && s(e) === t);
|
|
1141
1176
|
if (r.length !== 1) continue;
|
|
1142
1177
|
let i = r[0];
|
|
1143
|
-
i.name !== n.name && (l.add(i.name), u.add(n.name), c.push(
|
|
1178
|
+
i.name !== n.name && (l.add(i.name), u.add(n.name), c.push(I({
|
|
1144
1179
|
kind: "rename-index",
|
|
1145
1180
|
table: e,
|
|
1146
1181
|
from: n.name,
|
|
@@ -1152,7 +1187,7 @@ BEGIN
|
|
|
1152
1187
|
consumedAddedNames: l,
|
|
1153
1188
|
consumedRemovedNames: u
|
|
1154
1189
|
};
|
|
1155
|
-
},
|
|
1190
|
+
}, en = (e, t, n) => {
|
|
1156
1191
|
let r = [], i = /* @__PURE__ */ new Map(), a = /* @__PURE__ */ new Set(), o = /* @__PURE__ */ new Map(), s = /* @__PURE__ */ new Map(), c = /* @__PURE__ */ new Set();
|
|
1157
1192
|
for (let t of e.tables) t.renamedFrom !== void 0 && (s.set(t.renamedFrom, (s.get(t.renamedFrom) ?? 0) + 1), c.add(t.renamedFrom));
|
|
1158
1193
|
for (let c of e.tables) {
|
|
@@ -1163,7 +1198,7 @@ BEGIN
|
|
|
1163
1198
|
continue;
|
|
1164
1199
|
}
|
|
1165
1200
|
if (t.has(c.name)) {
|
|
1166
|
-
r.push(
|
|
1201
|
+
r.push(I({
|
|
1167
1202
|
kind: "rename-table",
|
|
1168
1203
|
from: e,
|
|
1169
1204
|
to: c.name
|
|
@@ -1171,14 +1206,14 @@ BEGIN
|
|
|
1171
1206
|
continue;
|
|
1172
1207
|
}
|
|
1173
1208
|
if ((s.get(e) ?? 0) !== 1) {
|
|
1174
|
-
r.push(
|
|
1209
|
+
r.push(I({
|
|
1175
1210
|
kind: "rename-table",
|
|
1176
1211
|
from: e,
|
|
1177
1212
|
to: c.name
|
|
1178
1213
|
}, "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.` }));
|
|
1179
1214
|
continue;
|
|
1180
1215
|
}
|
|
1181
|
-
i.set(c.name, e), a.add(e), r.push(
|
|
1216
|
+
i.set(c.name, e), a.add(e), r.push(I({
|
|
1182
1217
|
kind: "rename-table",
|
|
1183
1218
|
from: e,
|
|
1184
1219
|
to: c.name
|
|
@@ -1192,19 +1227,19 @@ BEGIN
|
|
|
1192
1227
|
claimedOldNames: c,
|
|
1193
1228
|
refusals: o
|
|
1194
1229
|
};
|
|
1195
|
-
},
|
|
1230
|
+
}, tn = (e, t, n, r) => {
|
|
1196
1231
|
if (t === void 0) return {
|
|
1197
1232
|
live: e,
|
|
1198
1233
|
ops: []
|
|
1199
1234
|
};
|
|
1200
|
-
let i = r !== void 0 &&
|
|
1235
|
+
let i = r !== void 0 && Xt.has(r), a = [], o = e.indexes.map((e) => {
|
|
1201
1236
|
if (!e.name.startsWith(t)) return e;
|
|
1202
1237
|
let r = `${n}${e.name.slice(t.length)}`;
|
|
1203
1238
|
return r === e.name ? e : e.name === `${t}_pkey` ? {
|
|
1204
1239
|
...e,
|
|
1205
1240
|
name: r,
|
|
1206
1241
|
table: n
|
|
1207
|
-
} : i ? (a.push(
|
|
1242
|
+
} : i ? (a.push(I({
|
|
1208
1243
|
kind: "rename-index",
|
|
1209
1244
|
table: n,
|
|
1210
1245
|
from: e.name,
|
|
@@ -1223,7 +1258,7 @@ BEGIN
|
|
|
1223
1258
|
},
|
|
1224
1259
|
ops: a
|
|
1225
1260
|
};
|
|
1226
|
-
},
|
|
1261
|
+
}, nn = (e) => {
|
|
1227
1262
|
let t = {
|
|
1228
1263
|
safe: 0,
|
|
1229
1264
|
needsDefault: 0,
|
|
@@ -1258,51 +1293,51 @@ BEGIN
|
|
|
1258
1293
|
break;
|
|
1259
1294
|
}
|
|
1260
1295
|
return t;
|
|
1261
|
-
},
|
|
1262
|
-
let t =
|
|
1296
|
+
}, rn = (e) => {
|
|
1297
|
+
let t = P(e.declared, e.dialect), n = Dt(e.declared, t), r = new Map(e.live.tables.map((e) => [e.name, e])), i = e.onlineAfter ?? Ut, a = [], o = en(t, r, n);
|
|
1263
1298
|
a.push(...o.ops);
|
|
1264
1299
|
for (let e of t.tables) if (!o.renamedInto.has(e.name) && !r.has(e.name)) {
|
|
1265
|
-
a.push(
|
|
1300
|
+
a.push(Vt(e.name, e.columns, e.indexes, e.primaryKey, o.refusals.get(e.name)));
|
|
1266
1301
|
for (let t of e.columns) {
|
|
1267
|
-
let n =
|
|
1302
|
+
let n = Ft(e.name, t);
|
|
1268
1303
|
n && a.push(n);
|
|
1269
1304
|
}
|
|
1270
1305
|
}
|
|
1271
|
-
let
|
|
1272
|
-
for (let t of e.live.tables) n.has(t.name) || o.claimedOldNames.has(t.name) ||
|
|
1306
|
+
let s = new Set(e.ignoreTables ?? []);
|
|
1307
|
+
for (let t of e.live.tables) n.has(t.name) || o.claimedOldNames.has(t.name) || m(t.name) || v(t.name) || s.has(t.name) || a.push(Ht(t.name));
|
|
1273
1308
|
for (let s of t.tables) {
|
|
1274
1309
|
let t = o.renamedInto.get(s.name), c = r.get(s.name) ?? (t === void 0 ? void 0 : r.get(t));
|
|
1275
1310
|
if (!c) continue;
|
|
1276
|
-
let l =
|
|
1311
|
+
let l = tn(c, t, s.name, e.dialect);
|
|
1277
1312
|
a.push(...l.ops);
|
|
1278
|
-
let
|
|
1279
|
-
a.push(...
|
|
1280
|
-
for (let e of
|
|
1281
|
-
if (
|
|
1282
|
-
let t =
|
|
1283
|
-
a.push(
|
|
1313
|
+
let u = l.live, d = n.get(s.name), f = Lt(s, u, e.dialect), p = Rt(s, u), m = Yt(d.table, d.columnDefs, f);
|
|
1314
|
+
a.push(...m.ops);
|
|
1315
|
+
for (let e of f.removed) {
|
|
1316
|
+
if (m.consumedRemovedNames.has(e.name) || v(e.name)) continue;
|
|
1317
|
+
let t = d.columnDefs[e.name];
|
|
1318
|
+
a.push(Bt(s.name, e, t));
|
|
1284
1319
|
}
|
|
1285
|
-
for (let e of
|
|
1286
|
-
if (
|
|
1287
|
-
let t =
|
|
1288
|
-
a.push(
|
|
1289
|
-
let n =
|
|
1290
|
-
n && a.push(n);
|
|
1320
|
+
for (let e of f.added) {
|
|
1321
|
+
if (m.consumedAddedNames.has(e.name)) continue;
|
|
1322
|
+
let t = d.columnDefs[e.name];
|
|
1323
|
+
a.push(zt(s.name, e, t));
|
|
1324
|
+
let n = Ft(s.name, e);
|
|
1325
|
+
n && a.push(n), a.push(...Jt(s.name, e));
|
|
1291
1326
|
}
|
|
1292
|
-
let
|
|
1293
|
-
a.push(...
|
|
1294
|
-
for (let e of
|
|
1295
|
-
for (let e of
|
|
1296
|
-
for (let { declared: t, live: n } of
|
|
1297
|
-
}
|
|
1298
|
-
let
|
|
1299
|
-
let t =
|
|
1300
|
-
return t ? t.columns.flatMap((e) => e.references &&
|
|
1327
|
+
let h = new Map(m.ops.flatMap((e) => e.op.kind === "rename-column" ? [[e.op.from, e.op.to]] : [])), g = $t(s.name, p, h, new Set(u.indexes.map((e) => e.name)), e.dialect);
|
|
1328
|
+
a.push(...g.ops);
|
|
1329
|
+
for (let e of p.added) g.consumedAddedNames.has(e.name) || a.push(Wt(s.name, e, u.rowCount, i));
|
|
1330
|
+
for (let e of p.removed) g.consumedRemovedNames.has(e.name) || a.push(Gt(s.name, e));
|
|
1331
|
+
for (let { declared: t, live: n } of f.changed) a.push(...qt(s.name, t, n, e.dialect, u.rowCount, i));
|
|
1332
|
+
}
|
|
1333
|
+
let c = new Set(a.flatMap((e) => e.op.kind === "create-table" ? [e.op.table] : [])), l = new Map(t.tables.map((e) => [e.name, e])), u = (e) => {
|
|
1334
|
+
let t = l.get(e);
|
|
1335
|
+
return t ? t.columns.flatMap((e) => e.references && c.has(e.references.table) ? [{
|
|
1301
1336
|
column: e.name,
|
|
1302
1337
|
target: e.references.table
|
|
1303
1338
|
}] : []) : [];
|
|
1304
|
-
},
|
|
1305
|
-
for (let e of t.tables) if (
|
|
1339
|
+
}, d = le([...c], u), f = [];
|
|
1340
|
+
for (let e of t.tables) if (c.has(e.name)) for (let t of e.columns) !t.references || !d.has(w(e.name, t.name)) || f.push(I({
|
|
1306
1341
|
kind: "add-foreign-key",
|
|
1307
1342
|
table: e.name,
|
|
1308
1343
|
column: t.name,
|
|
@@ -1311,14 +1346,14 @@ BEGIN
|
|
|
1311
1346
|
...t.references.onDelete ? { onDelete: t.references.onDelete } : {},
|
|
1312
1347
|
...t.references.onUpdate ? { onUpdate: t.references.onUpdate } : {}
|
|
1313
1348
|
}, "safe", `deferred FK ${e.name}.${t.name} → ${t.references.table} — breaks a create-time table cycle`));
|
|
1314
|
-
if (
|
|
1349
|
+
if (d.size > 0) {
|
|
1315
1350
|
let e = (e) => e.references ? Object.fromEntries(Object.entries(e).filter(([e]) => e !== "references")) : e;
|
|
1316
1351
|
for (let t = 0; t < a.length; t += 1) {
|
|
1317
1352
|
let n = a[t];
|
|
1318
1353
|
if (n.op.kind !== "create-table") continue;
|
|
1319
1354
|
let r = n.op;
|
|
1320
|
-
if (!r.columns.some((e) =>
|
|
1321
|
-
let i = r.columns.map((t) =>
|
|
1355
|
+
if (!r.columns.some((e) => d.has(w(r.table, e.name)))) continue;
|
|
1356
|
+
let i = r.columns.map((t) => d.has(w(r.table, t.name)) ? e(t) : t);
|
|
1322
1357
|
a[t] = {
|
|
1323
1358
|
...n,
|
|
1324
1359
|
op: {
|
|
@@ -1327,23 +1362,23 @@ BEGIN
|
|
|
1327
1362
|
}
|
|
1328
1363
|
};
|
|
1329
1364
|
}
|
|
1330
|
-
a.push(...
|
|
1365
|
+
a.push(...f);
|
|
1331
1366
|
}
|
|
1332
|
-
let
|
|
1333
|
-
let n =
|
|
1367
|
+
let p = new Map(t.tables.map((e) => [e.name, e])), h = /* @__PURE__ */ new Map(), g = (e, t = /* @__PURE__ */ new Set()) => {
|
|
1368
|
+
let n = h.get(e);
|
|
1334
1369
|
if (n !== void 0) return n;
|
|
1335
1370
|
if (t.has(e)) return 0;
|
|
1336
|
-
let r =
|
|
1371
|
+
let r = p.get(e);
|
|
1337
1372
|
if (!r) return 0;
|
|
1338
1373
|
let i = new Set(t).add(e), a = 0;
|
|
1339
1374
|
for (let t of r.columns) {
|
|
1340
1375
|
let n = t.references?.table;
|
|
1341
|
-
n && n !== e && !
|
|
1376
|
+
n && n !== e && !d.has(w(e, t.name)) && (a = Math.max(a, g(n, i) + 1));
|
|
1342
1377
|
}
|
|
1343
|
-
return
|
|
1344
|
-
},
|
|
1378
|
+
return h.set(e, a), a;
|
|
1379
|
+
}, _ = (e) => e === "rename-table" ? -1 : e === "create-table" ? 0 : e === "drop-table" ? 2 : 1, y = (e) => {
|
|
1345
1380
|
let t = "table" in e ? e.table : "";
|
|
1346
|
-
return e.kind === "create-table" ?
|
|
1381
|
+
return e.kind === "create-table" ? g(t) : e.kind === "drop-table" ? -g(t) : 0;
|
|
1347
1382
|
}, b = {
|
|
1348
1383
|
"add-column": 0,
|
|
1349
1384
|
"rename-column": 1,
|
|
@@ -1363,7 +1398,7 @@ BEGIN
|
|
|
1363
1398
|
"add-foreign-key": 5,
|
|
1364
1399
|
"add-check": 5
|
|
1365
1400
|
}, x = (e) => b[e] ?? 9, S = [...a].sort((e, t) => {
|
|
1366
|
-
let n =
|
|
1401
|
+
let n = _(e.op.kind), r = _(t.op.kind);
|
|
1367
1402
|
if (n !== r) return n - r;
|
|
1368
1403
|
let i = y(e.op), a = y(t.op);
|
|
1369
1404
|
if (i !== a) return i - a;
|
|
@@ -1374,11 +1409,87 @@ BEGIN
|
|
|
1374
1409
|
});
|
|
1375
1410
|
return {
|
|
1376
1411
|
operations: S,
|
|
1377
|
-
fromFingerprint:
|
|
1378
|
-
toFingerprint:
|
|
1379
|
-
summary:
|
|
1412
|
+
fromFingerprint: M(e.live),
|
|
1413
|
+
toFingerprint: M(t),
|
|
1414
|
+
summary: nn(S)
|
|
1380
1415
|
};
|
|
1381
|
-
},
|
|
1416
|
+
}, an = (e) => e.onDialectOrElse({
|
|
1417
|
+
pg: () => "postgres",
|
|
1418
|
+
mysql: () => "mysql",
|
|
1419
|
+
mssql: () => "mssql",
|
|
1420
|
+
sqlite: () => "sqlite",
|
|
1421
|
+
orElse: () => "sqlite"
|
|
1422
|
+
}), on = {
|
|
1423
|
+
postgres: "plan-transactional",
|
|
1424
|
+
mssql: "plan-transactional",
|
|
1425
|
+
mysql: "per-operation",
|
|
1426
|
+
mariadb: "per-operation",
|
|
1427
|
+
sqlite: "per-operation",
|
|
1428
|
+
turso: "per-operation"
|
|
1429
|
+
}, sn = (e) => on[e], L = "_voltro_migration_ops", cn = l(L, {
|
|
1430
|
+
id: e({ prefix: "mop" }),
|
|
1431
|
+
runId: _(),
|
|
1432
|
+
planFingerprint: _(),
|
|
1433
|
+
opIndex: n(),
|
|
1434
|
+
opKind: _(),
|
|
1435
|
+
opTarget: _(),
|
|
1436
|
+
status: _(),
|
|
1437
|
+
payload: _(),
|
|
1438
|
+
startedAt: t().nullable(),
|
|
1439
|
+
appliedAt: t().nullable(),
|
|
1440
|
+
durationMs: n().nullable(),
|
|
1441
|
+
createdAt: t().default("now")
|
|
1442
|
+
}), ln = (e) => {
|
|
1443
|
+
switch (e.kind) {
|
|
1444
|
+
case "create-table": return /* @__PURE__ */ new Set([
|
|
1445
|
+
`table:${e.table}`,
|
|
1446
|
+
...e.columns.map((t) => `column:${e.table}.${t.name}`),
|
|
1447
|
+
...e.indexes.map((t) => `index:${e.table}.${t.name}`)
|
|
1448
|
+
]);
|
|
1449
|
+
case "drop-table": return /* @__PURE__ */ new Set([`table:${e.table}`]);
|
|
1450
|
+
case "rename-table": return /* @__PURE__ */ new Set([`table:${e.from}`, `table:${e.to}`]);
|
|
1451
|
+
case "add-column": return /* @__PURE__ */ new Set([`column:${e.table}.${e.column.name}`]);
|
|
1452
|
+
case "rename-column": return /* @__PURE__ */ new Set([`column:${e.table}.${e.from}`, `column:${e.table}.${e.to}`]);
|
|
1453
|
+
case "drop-column":
|
|
1454
|
+
case "alter-column-nullability":
|
|
1455
|
+
case "alter-column-type":
|
|
1456
|
+
case "alter-column-default":
|
|
1457
|
+
case "add-unique":
|
|
1458
|
+
case "drop-unique":
|
|
1459
|
+
case "add-foreign-key":
|
|
1460
|
+
case "drop-foreign-key":
|
|
1461
|
+
case "add-check":
|
|
1462
|
+
case "drop-check": return /* @__PURE__ */ new Set([`column:${e.table}.${e.column}`]);
|
|
1463
|
+
case "add-index": return /* @__PURE__ */ new Set([`index:${e.table}.${e.index.name}`]);
|
|
1464
|
+
case "drop-index": return /* @__PURE__ */ new Set([`index:${e.table}.${e.index}`]);
|
|
1465
|
+
case "rename-index": return /* @__PURE__ */ new Set([`index:${e.table}.${e.from}`, `index:${e.table}.${e.to}`]);
|
|
1466
|
+
case "add-unique-composite":
|
|
1467
|
+
case "drop-unique-composite": return /* @__PURE__ */ new Set([`unique:${e.table}.${e.name}`]);
|
|
1468
|
+
}
|
|
1469
|
+
}, un = (e) => [...ln(e)].join(" "), dn = (e, t) => {
|
|
1470
|
+
let n = ln(t);
|
|
1471
|
+
for (let t of ln(e)) if (n.has(t)) return !0;
|
|
1472
|
+
return !1;
|
|
1473
|
+
}, fn = (e, t) => e.operations.some((e) => dn(e.op, t)), pn = "__voltro_rebuild", mn = "__shadow", hn = "__old", gn = /* @__PURE__ */ new Set([
|
|
1474
|
+
"add-unique",
|
|
1475
|
+
"drop-unique",
|
|
1476
|
+
"add-unique-composite",
|
|
1477
|
+
"drop-unique-composite",
|
|
1478
|
+
"add-foreign-key",
|
|
1479
|
+
"drop-foreign-key",
|
|
1480
|
+
"add-check",
|
|
1481
|
+
"drop-check",
|
|
1482
|
+
"alter-column-nullability",
|
|
1483
|
+
"alter-column-type",
|
|
1484
|
+
"alter-column-default"
|
|
1485
|
+
]), _n = /* @__PURE__ */ new Set(["alter-column-default", "alter-column-nullability"]), vn = (e, t, n) => {
|
|
1486
|
+
let r = n === "sqlite" || n === "turso";
|
|
1487
|
+
return e.kind === "alter-column-type" && t === "online-required" || r && gn.has(e.kind) ? "artefact-repair" : _n.has(e.kind) ? "idempotent" : "probe-then-replay";
|
|
1488
|
+
};
|
|
1489
|
+
Object.keys(on);
|
|
1490
|
+
//#endregion
|
|
1491
|
+
//#region src/migrations/introspect.ts
|
|
1492
|
+
var yn = (e) => {
|
|
1382
1493
|
let t = /[`"[]?([A-Za-z_][A-Za-z0-9_]*)[`"\]]?\s*(?:in\b|=)/i.exec(e);
|
|
1383
1494
|
if (!t) return null;
|
|
1384
1495
|
let n = [...e.matchAll(/'((?:[^']|'')*)'/g)].map((e) => e[1].replace(/''/g, "'"));
|
|
@@ -1386,7 +1497,7 @@ BEGIN
|
|
|
1386
1497
|
column: t[1],
|
|
1387
1498
|
values: n
|
|
1388
1499
|
};
|
|
1389
|
-
},
|
|
1500
|
+
}, bn = (e, t) => {
|
|
1390
1501
|
if (t === "vector") return "vector";
|
|
1391
1502
|
switch (e) {
|
|
1392
1503
|
case "text":
|
|
@@ -1413,7 +1524,7 @@ BEGIN
|
|
|
1413
1524
|
case "USER-DEFINED": return "text";
|
|
1414
1525
|
default: return "text";
|
|
1415
1526
|
}
|
|
1416
|
-
},
|
|
1527
|
+
}, R = (e) => {
|
|
1417
1528
|
switch (e.toUpperCase()) {
|
|
1418
1529
|
case "CASCADE": return "cascade";
|
|
1419
1530
|
case "RESTRICT": return "restrict";
|
|
@@ -1421,25 +1532,25 @@ BEGIN
|
|
|
1421
1532
|
case "SET DEFAULT": return "noAction";
|
|
1422
1533
|
default: return "noAction";
|
|
1423
1534
|
}
|
|
1424
|
-
},
|
|
1535
|
+
}, xn = (e, t) => {
|
|
1425
1536
|
let n = e !== void 0 && e !== "" ? Number(e) : NaN;
|
|
1426
1537
|
return Number.isInteger(n) && n > 0 ? n : t;
|
|
1427
|
-
},
|
|
1538
|
+
}, Sn = () => xn(process.env.VOLTRO_INTROSPECT_BATCH, 20), Cn = 300, wn = () => {
|
|
1428
1539
|
let e = process.env.VOLTRO_INTROSPECT_TIMEOUT_MS;
|
|
1429
|
-
return e !== void 0 && e.trim() === "0" ? 0 :
|
|
1430
|
-
},
|
|
1540
|
+
return e !== void 0 && e.trim() === "0" ? 0 : xn(e, 3e4);
|
|
1541
|
+
}, Tn = (e, t) => {
|
|
1431
1542
|
let n = Number.isFinite(t) && t >= 1 ? Math.floor(t) : 1, r = [];
|
|
1432
1543
|
for (let t = 0; t < e.length; t += n) r.push(e.slice(t, t + n));
|
|
1433
1544
|
return r;
|
|
1434
|
-
},
|
|
1435
|
-
let t =
|
|
1545
|
+
}, En = (e) => {
|
|
1546
|
+
let t = wn(), n = b.gen(function* () {
|
|
1436
1547
|
t > 0 && (yield* e.unsafe(`SET LOCAL statement_timeout = ${t}`));
|
|
1437
|
-
let n = (t) => e.unsafe("(" + t.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ") + ")"), r = (t) => e.unsafe(String(Math.max(0, Math.trunc(t)))), i =
|
|
1548
|
+
let n = (t) => e.unsafe("(" + t.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ") + ")"), r = (t) => e.unsafe(String(Math.max(0, Math.trunc(t)))), i = Sn(), a = (e, t) => b.gen(function* () {
|
|
1438
1549
|
let r = [];
|
|
1439
|
-
for (let a of
|
|
1550
|
+
for (let a of Tn(e, i)) a.length !== 0 && r.push(...yield* t(n(a)));
|
|
1440
1551
|
return r;
|
|
1441
1552
|
}), o = [];
|
|
1442
|
-
for (let t = 0;; t +=
|
|
1553
|
+
for (let t = 0;; t += Cn) {
|
|
1443
1554
|
let n = yield* e`
|
|
1444
1555
|
SELECT c.relname AS table_name,
|
|
1445
1556
|
COALESCE(c.reltuples::bigint, 0) AS row_count
|
|
@@ -1447,9 +1558,9 @@ BEGIN
|
|
|
1447
1558
|
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
1448
1559
|
WHERE n.nspname = current_schema() AND c.relkind = 'r'
|
|
1449
1560
|
ORDER BY c.relname
|
|
1450
|
-
LIMIT ${r(
|
|
1561
|
+
LIMIT ${r(Cn)} OFFSET ${r(t)}
|
|
1451
1562
|
`;
|
|
1452
|
-
if (o.push(...n), n.length <
|
|
1563
|
+
if (o.push(...n), n.length < Cn) break;
|
|
1453
1564
|
}
|
|
1454
1565
|
let s = /* @__PURE__ */ new Set(), c = [];
|
|
1455
1566
|
for (let e of o) s.has(e.table_name) || (s.add(e.table_name), c.push(e));
|
|
@@ -1476,7 +1587,7 @@ BEGIN
|
|
|
1476
1587
|
JOIN pg_namespace nsp ON nsp.oid = rel.relnamespace
|
|
1477
1588
|
JOIN pg_attribute att ON att.attrelid = rel.oid AND att.attnum = ANY (con.conkey)
|
|
1478
1589
|
WHERE con.contype = 'u' AND nsp.nspname = current_schema() AND rel.relname IN ${t}
|
|
1479
|
-
`),
|
|
1590
|
+
`), p = yield* a(l, (t) => e`
|
|
1480
1591
|
SELECT rel.relname AS table_name, att.attname AS column_name, pg_get_constraintdef(con.oid) AS def
|
|
1481
1592
|
FROM pg_constraint con
|
|
1482
1593
|
JOIN pg_class rel ON rel.oid = con.conrelid
|
|
@@ -1484,12 +1595,12 @@ BEGIN
|
|
|
1484
1595
|
JOIN pg_attribute att ON att.attrelid = rel.oid AND att.attnum = con.conkey[1]
|
|
1485
1596
|
WHERE con.contype = 'c' AND nsp.nspname = current_schema() AND array_length(con.conkey, 1) = 1
|
|
1486
1597
|
AND rel.relname IN ${t}
|
|
1487
|
-
`),
|
|
1488
|
-
for (let e of
|
|
1598
|
+
`), m = /* @__PURE__ */ new Map();
|
|
1599
|
+
for (let e of p) {
|
|
1489
1600
|
let t = [...e.def.matchAll(/'((?:[^']|'')*)'/g)].map((e) => e[1].replace(/''/g, "'"));
|
|
1490
|
-
t.length > 0 &&
|
|
1601
|
+
t.length > 0 && m.set(`${e.table_name}.${e.column_name}`, t);
|
|
1491
1602
|
}
|
|
1492
|
-
let
|
|
1603
|
+
let h = yield* a(l, (t) => e`
|
|
1493
1604
|
SELECT
|
|
1494
1605
|
rel.relname AS source_table,
|
|
1495
1606
|
att.attname AS source_column,
|
|
@@ -1514,7 +1625,7 @@ BEGIN
|
|
|
1514
1625
|
WHERE con.contype = 'f' AND nsp.nspname = current_schema()
|
|
1515
1626
|
AND rel.relname IN ${t}
|
|
1516
1627
|
ORDER BY rel.relname, con.conname, ck.ord
|
|
1517
|
-
`),
|
|
1628
|
+
`), g = yield* a(l, (t) => e`
|
|
1518
1629
|
WITH ix AS (
|
|
1519
1630
|
SELECT
|
|
1520
1631
|
c.relname AS table_name,
|
|
@@ -1535,31 +1646,31 @@ BEGIN
|
|
|
1535
1646
|
AND c.relname IN ${t}
|
|
1536
1647
|
)
|
|
1537
1648
|
SELECT * FROM ix ORDER BY table_name, index_name, position
|
|
1538
|
-
`),
|
|
1649
|
+
`), _ = /* @__PURE__ */ new Map(), v = /* @__PURE__ */ new Map(), y = /* @__PURE__ */ new Map(), x = /* @__PURE__ */ new Map(), S = /* @__PURE__ */ new Map(), C = /* @__PURE__ */ new Map();
|
|
1539
1650
|
for (let e of u) {
|
|
1540
|
-
let t =
|
|
1541
|
-
t || (t = [],
|
|
1651
|
+
let t = _.get(e.table_name);
|
|
1652
|
+
t || (t = [], _.set(e.table_name, t)), t.push(e);
|
|
1542
1653
|
}
|
|
1543
1654
|
for (let e of d) {
|
|
1544
|
-
let t =
|
|
1545
|
-
t || (t = /* @__PURE__ */ new Set(),
|
|
1655
|
+
let t = v.get(e.table_name);
|
|
1656
|
+
t || (t = /* @__PURE__ */ new Set(), v.set(e.table_name, t)), t.add(e.column_name);
|
|
1546
1657
|
}
|
|
1547
|
-
let
|
|
1658
|
+
let ee = /* @__PURE__ */ new Map();
|
|
1548
1659
|
for (let e of f) {
|
|
1549
|
-
let t = `${e.table_name}\u0000${e.constraint_name}`, n =
|
|
1660
|
+
let t = `${e.table_name}\u0000${e.constraint_name}`, n = ee.get(t);
|
|
1550
1661
|
n || (n = {
|
|
1551
1662
|
table: e.table_name,
|
|
1552
1663
|
columns: []
|
|
1553
|
-
},
|
|
1664
|
+
}, ee.set(t, n)), n.columns.push({
|
|
1554
1665
|
name: e.column_name,
|
|
1555
1666
|
position: e.position
|
|
1556
1667
|
});
|
|
1557
1668
|
}
|
|
1558
|
-
for (let [e, t] of
|
|
1669
|
+
for (let [e, t] of ee) {
|
|
1559
1670
|
let n = [...t.columns].sort((e, t) => e.position - t.position).map((e) => e.name);
|
|
1560
1671
|
if (n.length === 1) {
|
|
1561
|
-
let e =
|
|
1562
|
-
e || (e = /* @__PURE__ */ new Set(),
|
|
1672
|
+
let e = y.get(t.table);
|
|
1673
|
+
e || (e = /* @__PURE__ */ new Set(), y.set(t.table, e)), e.add(n[0]);
|
|
1563
1674
|
} else {
|
|
1564
1675
|
let r = x.get(t.table);
|
|
1565
1676
|
r || (r = [], x.set(t.table, r)), r.push({
|
|
@@ -1570,29 +1681,29 @@ BEGIN
|
|
|
1570
1681
|
});
|
|
1571
1682
|
}
|
|
1572
1683
|
}
|
|
1573
|
-
for (let e of
|
|
1684
|
+
for (let e of h) {
|
|
1574
1685
|
let t = S.get(e.source_table);
|
|
1575
1686
|
t || (t = [], S.set(e.source_table, t)), t.push(e);
|
|
1576
1687
|
}
|
|
1577
|
-
for (let e of
|
|
1578
|
-
let t =
|
|
1579
|
-
t || (t = /* @__PURE__ */ new Map(),
|
|
1688
|
+
for (let e of g) {
|
|
1689
|
+
let t = C.get(e.table_name);
|
|
1690
|
+
t || (t = /* @__PURE__ */ new Map(), C.set(e.table_name, t));
|
|
1580
1691
|
let n = t.get(e.index_name);
|
|
1581
1692
|
n || (n = [], t.set(e.index_name, n)), n.push(e);
|
|
1582
1693
|
}
|
|
1583
|
-
let
|
|
1694
|
+
let te = [];
|
|
1584
1695
|
for (let e of c) {
|
|
1585
|
-
let t = /* @__PURE__ */ new Set(), n = (
|
|
1586
|
-
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" :
|
|
1696
|
+
let t = /* @__PURE__ */ new Set(), n = (_.get(e.table_name) ?? []).filter((e) => t.has(e.column_name) ? !1 : (t.add(e.column_name), !0)), r = v.get(e.table_name) ?? /* @__PURE__ */ new Set(), i = y.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) => {
|
|
1697
|
+
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" : bn(t.data_type, t.udt_name), c = a ? {
|
|
1587
1698
|
table: a.target_table,
|
|
1588
1699
|
column: a.target_column,
|
|
1589
|
-
onDelete:
|
|
1590
|
-
onUpdate:
|
|
1591
|
-
} : void 0, l =
|
|
1700
|
+
onDelete: R(a.delete_rule),
|
|
1701
|
+
onUpdate: R(a.update_rule)
|
|
1702
|
+
} : void 0, l = m.get(`${e.table_name}.${t.column_name}`);
|
|
1592
1703
|
return {
|
|
1593
1704
|
name: t.column_name,
|
|
1594
1705
|
type: s,
|
|
1595
|
-
...
|
|
1706
|
+
...V(t.data_type, t.character_maximum_length) === void 0 ? {} : { maxLength: V(t.data_type, t.character_maximum_length) },
|
|
1596
1707
|
nullable: t.is_nullable === "YES",
|
|
1597
1708
|
unique: i.has(t.column_name) || n && t.column_name === "id",
|
|
1598
1709
|
hasDefault: t.column_default !== null,
|
|
@@ -1600,7 +1711,7 @@ BEGIN
|
|
|
1600
1711
|
...c ? { references: c } : {},
|
|
1601
1712
|
...l ? { oneOf: l } : {}
|
|
1602
1713
|
};
|
|
1603
|
-
}), l = [], u =
|
|
1714
|
+
}), l = [], u = C.get(e.table_name);
|
|
1604
1715
|
if (u) for (let [t, n] of u) {
|
|
1605
1716
|
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) => {
|
|
1606
1717
|
let t = e.position;
|
|
@@ -1624,20 +1735,20 @@ BEGIN
|
|
|
1624
1735
|
unique: !0
|
|
1625
1736
|
});
|
|
1626
1737
|
let d = Number(e.row_count ?? 0);
|
|
1627
|
-
|
|
1738
|
+
te.push({
|
|
1628
1739
|
name: e.table_name,
|
|
1629
1740
|
columns: c,
|
|
1630
1741
|
indexes: l,
|
|
1631
1742
|
...d > 0 ? { rowCount: d } : {}
|
|
1632
1743
|
});
|
|
1633
1744
|
}
|
|
1634
|
-
return { tables:
|
|
1745
|
+
return { tables: te };
|
|
1635
1746
|
});
|
|
1636
|
-
return (t > 0 ? e.withTransaction(n) : n).pipe(
|
|
1747
|
+
return (t > 0 ? e.withTransaction(n) : n).pipe(b.catchAll((e) => {
|
|
1637
1748
|
let n = `${String(e)} ${String(e.cause ?? "")}`.toLowerCase();
|
|
1638
|
-
return n.includes("statement timeout") || n.includes("57014") ?
|
|
1749
|
+
return n.includes("statement timeout") || n.includes("57014") ? b.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.`)) : b.fail(e);
|
|
1639
1750
|
}));
|
|
1640
|
-
},
|
|
1751
|
+
}, Dn = (e, t) => {
|
|
1641
1752
|
switch (e.toLowerCase()) {
|
|
1642
1753
|
case "varchar":
|
|
1643
1754
|
case "char":
|
|
@@ -1669,21 +1780,21 @@ BEGIN
|
|
|
1669
1780
|
case "json": return "json";
|
|
1670
1781
|
default: return "text";
|
|
1671
1782
|
}
|
|
1672
|
-
},
|
|
1783
|
+
}, z = (e) => e.map((e) => {
|
|
1673
1784
|
let t = e, n = {};
|
|
1674
1785
|
for (let e of Object.keys(t)) n[e.toLowerCase()] = t[e];
|
|
1675
1786
|
return n;
|
|
1676
|
-
}),
|
|
1677
|
-
let t =
|
|
1787
|
+
}), On = (e) => b.gen(function* () {
|
|
1788
|
+
let t = z(yield* e`
|
|
1678
1789
|
SELECT table_name, COALESCE(table_rows, 0) AS table_rows
|
|
1679
1790
|
FROM information_schema.tables
|
|
1680
1791
|
WHERE table_schema = DATABASE() AND table_type = 'BASE TABLE'
|
|
1681
|
-
ORDER BY table_name`), n =
|
|
1792
|
+
ORDER BY table_name`), n = z(yield* e`
|
|
1682
1793
|
SELECT table_name, column_name, data_type, column_type, is_nullable, column_default, column_key,
|
|
1683
1794
|
character_maximum_length
|
|
1684
1795
|
FROM information_schema.columns
|
|
1685
1796
|
WHERE table_schema = DATABASE()
|
|
1686
|
-
ORDER BY table_name, ordinal_position`), r =
|
|
1797
|
+
ORDER BY table_name, ordinal_position`), r = z(yield* e`
|
|
1687
1798
|
SELECT k.table_name AS source_table,
|
|
1688
1799
|
k.column_name AS source_column,
|
|
1689
1800
|
k.referenced_table_name AS target_table,
|
|
@@ -1693,21 +1804,21 @@ BEGIN
|
|
|
1693
1804
|
JOIN information_schema.referential_constraints r
|
|
1694
1805
|
ON k.constraint_name = r.constraint_name
|
|
1695
1806
|
AND k.table_schema = r.constraint_schema
|
|
1696
|
-
WHERE k.table_schema = DATABASE() AND k.referenced_table_name IS NOT NULL`), i =
|
|
1807
|
+
WHERE k.table_schema = DATABASE() AND k.referenced_table_name IS NOT NULL`), i = z(yield* e`
|
|
1697
1808
|
SELECT table_name, index_name, non_unique, column_name, seq_in_index
|
|
1698
1809
|
FROM information_schema.statistics
|
|
1699
1810
|
WHERE table_schema = DATABASE() AND index_name <> 'PRIMARY'
|
|
1700
|
-
ORDER BY table_name, index_name, seq_in_index`), a =
|
|
1811
|
+
ORDER BY table_name, index_name, seq_in_index`), a = z(yield* e`
|
|
1701
1812
|
SELECT table_name, check_clause
|
|
1702
1813
|
FROM information_schema.check_constraints
|
|
1703
|
-
WHERE constraint_schema = DATABASE()`.unprepared.pipe(
|
|
1814
|
+
WHERE constraint_schema = DATABASE()`.unprepared.pipe(b.orElseSucceed(() => []))), o = /* @__PURE__ */ new Set(), s = /* @__PURE__ */ new Map();
|
|
1704
1815
|
for (let e of a) {
|
|
1705
1816
|
let t = /json_valid\(`?([^`)]+)`?\)/i.exec(e.check_clause);
|
|
1706
1817
|
if (t) {
|
|
1707
1818
|
o.add(`${e.table_name}.${t[1]}`);
|
|
1708
1819
|
continue;
|
|
1709
1820
|
}
|
|
1710
|
-
let n =
|
|
1821
|
+
let n = yn(e.check_clause);
|
|
1711
1822
|
n && s.set(`${e.table_name}.${n.column}`, n.values);
|
|
1712
1823
|
}
|
|
1713
1824
|
let c = /* @__PURE__ */ new Map(), l = /* @__PURE__ */ new Map(), u = /* @__PURE__ */ new Map();
|
|
@@ -1730,17 +1841,17 @@ BEGIN
|
|
|
1730
1841
|
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);
|
|
1731
1842
|
if (f) for (let e of f.values()) e.length === 1 && e[0].non_unique === 0 && a.add(e[0].column_name);
|
|
1732
1843
|
let p = t.map((t) => {
|
|
1733
|
-
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" :
|
|
1844
|
+
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" : Dn(t.data_type, t.column_type), u = c ? {
|
|
1734
1845
|
table: c.target_table,
|
|
1735
1846
|
column: c.target_column,
|
|
1736
|
-
onDelete:
|
|
1737
|
-
onUpdate:
|
|
1847
|
+
onDelete: R(c.delete_rule),
|
|
1848
|
+
onUpdate: R(c.update_rule)
|
|
1738
1849
|
} : 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}`);
|
|
1739
1850
|
return {
|
|
1740
1851
|
name: t.column_name,
|
|
1741
1852
|
type: l,
|
|
1742
1853
|
nullable: t.is_nullable === "YES",
|
|
1743
|
-
...
|
|
1854
|
+
...V(t.data_type, t.character_maximum_length) === void 0 ? {} : { maxLength: V(t.data_type, t.character_maximum_length) },
|
|
1744
1855
|
unique: d,
|
|
1745
1856
|
hasDefault: f !== null,
|
|
1746
1857
|
...f === null ? {} : { defaultValue: f },
|
|
@@ -1772,10 +1883,10 @@ BEGIN
|
|
|
1772
1883
|
});
|
|
1773
1884
|
}
|
|
1774
1885
|
return { tables: d };
|
|
1775
|
-
}),
|
|
1886
|
+
}), kn = (e) => {
|
|
1776
1887
|
let t = e.toUpperCase();
|
|
1777
1888
|
return t.includes("INT") ? "integer" : t.includes("REAL") || t.includes("FLOA") || t.includes("DOUB") ? "real" : t.includes("BLOB") ? "bytes" : "text";
|
|
1778
|
-
},
|
|
1889
|
+
}, An = (e) => b.gen(function* () {
|
|
1779
1890
|
let t = yield* e`
|
|
1780
1891
|
SELECT name, sql FROM sqlite_master
|
|
1781
1892
|
WHERE type = 'table' AND name NOT LIKE 'sqlite_%'
|
|
@@ -1792,11 +1903,11 @@ BEGIN
|
|
|
1792
1903
|
d.set(t.name, r), t.unique === 1 && r.length === 1 && f.add(r[0]);
|
|
1793
1904
|
}
|
|
1794
1905
|
let p = o.map((e) => {
|
|
1795
|
-
let n = u.length === 1 && u[0] === e.name, r = l.get(e.name), i = r ? "reference" : n && e.name === "id" ? "id" :
|
|
1906
|
+
let n = u.length === 1 && u[0] === e.name, r = l.get(e.name), i = r ? "reference" : n && e.name === "id" ? "id" : kn(e.type), a = r ? {
|
|
1796
1907
|
table: r.table,
|
|
1797
1908
|
column: r.to,
|
|
1798
|
-
onDelete:
|
|
1799
|
-
onUpdate:
|
|
1909
|
+
onDelete: R(r.on_delete),
|
|
1910
|
+
onUpdate: R(r.on_update)
|
|
1800
1911
|
} : void 0, o = t.get(e.name);
|
|
1801
1912
|
return {
|
|
1802
1913
|
name: e.name,
|
|
@@ -1831,7 +1942,7 @@ BEGIN
|
|
|
1831
1942
|
});
|
|
1832
1943
|
}
|
|
1833
1944
|
return { tables: r };
|
|
1834
|
-
}),
|
|
1945
|
+
}), jn = (e) => {
|
|
1835
1946
|
switch (e.toLowerCase()) {
|
|
1836
1947
|
case "int":
|
|
1837
1948
|
case "smallint":
|
|
@@ -1853,7 +1964,7 @@ BEGIN
|
|
|
1853
1964
|
case "image": return "bytes";
|
|
1854
1965
|
default: return "text";
|
|
1855
1966
|
}
|
|
1856
|
-
},
|
|
1967
|
+
}, B = (e) => e === !0 || e === 1, Mn = (e) => b.gen(function* () {
|
|
1857
1968
|
let t = yield* e`
|
|
1858
1969
|
SELECT TABLE_NAME AS table_name FROM INFORMATION_SCHEMA.TABLES
|
|
1859
1970
|
WHERE TABLE_TYPE = 'BASE TABLE' ORDER BY TABLE_NAME`, n = yield* e`
|
|
@@ -1910,21 +2021,21 @@ BEGIN
|
|
|
1910
2021
|
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();
|
|
1911
2022
|
if (a) for (let [, e] of a) {
|
|
1912
2023
|
let t = [...e].sort((e, t) => e.key_ordinal - t.key_ordinal);
|
|
1913
|
-
if (
|
|
1914
|
-
else
|
|
2024
|
+
if (B(t[0].is_primary)) for (let e of t) d.push(e.column_name);
|
|
2025
|
+
else B(t[0].is_unique) && t.length === 1 && f.add(t[0].column_name);
|
|
1915
2026
|
}
|
|
1916
2027
|
let p = t.map((t) => {
|
|
1917
|
-
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" :
|
|
2028
|
+
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" : jn(t.data_type), s = i ? {
|
|
1918
2029
|
table: i.target_table,
|
|
1919
2030
|
column: i.target_column,
|
|
1920
|
-
onDelete:
|
|
1921
|
-
onUpdate:
|
|
1922
|
-
} : void 0, c = t.column_default === null ? null :
|
|
2031
|
+
onDelete: Nn(i.delete_rule),
|
|
2032
|
+
onUpdate: Nn(i.update_rule)
|
|
2033
|
+
} : void 0, c = t.column_default === null ? null : Pn(t.column_default), l = o.get(`${e}.${t.column_name}`);
|
|
1923
2034
|
return {
|
|
1924
2035
|
name: t.column_name,
|
|
1925
2036
|
type: a,
|
|
1926
2037
|
nullable: t.is_nullable === "YES",
|
|
1927
|
-
...
|
|
2038
|
+
...V(t.data_type, t.character_maximum_length) === void 0 ? {} : { maxLength: V(t.data_type, t.character_maximum_length) },
|
|
1928
2039
|
unique: f.has(t.column_name) || n && t.column_name === "id",
|
|
1929
2040
|
hasDefault: c !== null,
|
|
1930
2041
|
...c === null ? {} : { defaultValue: c },
|
|
@@ -1934,11 +2045,11 @@ BEGIN
|
|
|
1934
2045
|
}), m = [];
|
|
1935
2046
|
if (a) for (let [t, n] of a) {
|
|
1936
2047
|
let r = [...n].sort((e, t) => e.key_ordinal - t.key_ordinal);
|
|
1937
|
-
|
|
2048
|
+
B(r[0].is_primary) || r.length === 1 && B(r[0].is_unique) || r.length === 1 && !B(r[0].is_unique) && i.has(r[0].column_name) && t !== `${e}_${r[0].column_name}_idx` || m.push({
|
|
1938
2049
|
name: t,
|
|
1939
2050
|
table: e,
|
|
1940
2051
|
columns: r.map((e) => e.column_name),
|
|
1941
|
-
unique:
|
|
2052
|
+
unique: B(r[0].is_unique)
|
|
1942
2053
|
});
|
|
1943
2054
|
}
|
|
1944
2055
|
d.length > 0 && m.push({
|
|
@@ -1953,51 +2064,54 @@ BEGIN
|
|
|
1953
2064
|
});
|
|
1954
2065
|
}
|
|
1955
2066
|
return { tables: u };
|
|
1956
|
-
}),
|
|
2067
|
+
}), Nn = (e) => {
|
|
1957
2068
|
switch (e.toUpperCase().replace(/_/g, " ")) {
|
|
1958
2069
|
case "CASCADE": return "cascade";
|
|
1959
2070
|
case "SET NULL": return "setNull";
|
|
1960
2071
|
case "SET DEFAULT": return "noAction";
|
|
1961
2072
|
default: return "noAction";
|
|
1962
2073
|
}
|
|
1963
|
-
},
|
|
2074
|
+
}, Pn = (e) => {
|
|
1964
2075
|
let t = e.trim();
|
|
1965
2076
|
for (; t.startsWith("(") && t.endsWith(")");) t = t.slice(1, -1).trim();
|
|
1966
2077
|
return t.startsWith("N'") && t.endsWith("'") ? t = t.slice(2, -1) : t.startsWith("'") && t.endsWith("'") && (t = t.slice(1, -1)), t;
|
|
1967
|
-
},
|
|
2078
|
+
}, Fn = /* @__PURE__ */ new Set([
|
|
1968
2079
|
"character varying",
|
|
1969
2080
|
"varchar",
|
|
1970
2081
|
"nvarchar",
|
|
1971
2082
|
"char",
|
|
1972
2083
|
"character",
|
|
1973
2084
|
"nchar"
|
|
1974
|
-
]),
|
|
1975
|
-
if (!
|
|
2085
|
+
]), V = (e, t) => {
|
|
2086
|
+
if (!Fn.has(e.toLowerCase())) return;
|
|
1976
2087
|
let n = Number(t);
|
|
1977
2088
|
if (!(!Number.isFinite(n) || n <= 0)) return n;
|
|
1978
|
-
},
|
|
1979
|
-
mysql: () =>
|
|
1980
|
-
mssql: () =>
|
|
1981
|
-
sqlite: () =>
|
|
1982
|
-
orElse: () =>
|
|
1983
|
-
})
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
}),
|
|
2089
|
+
}, In = /* @__PURE__ */ new Set([L]), Ln = (e) => e.onDialectOrElse({
|
|
2090
|
+
mysql: () => On(e),
|
|
2091
|
+
mssql: () => Mn(e),
|
|
2092
|
+
sqlite: () => An(e),
|
|
2093
|
+
orElse: () => En(e)
|
|
2094
|
+
}).pipe(b.map((e) => ({
|
|
2095
|
+
...e,
|
|
2096
|
+
tables: e.tables.filter((e) => !In.has(e.name))
|
|
2097
|
+
}))), H = (e, t) => e.onDialectOrElse({
|
|
2098
|
+
mysql: () => b.succeed(t.replace("T", " ").replace("Z", "")),
|
|
2099
|
+
orElse: () => b.succeed(t)
|
|
2100
|
+
}), Rn = (e, t) => `${t}(${e.precision ?? 38}, ${e.scale ?? 0})`, zn = (e, t) => {
|
|
1987
2101
|
if (!(e.type === "text" && e.maxLength !== void 0) && !e.spatial && e.type !== "vector" && e.type !== "array" && e.type !== "enum") return;
|
|
1988
2102
|
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;
|
|
1989
2103
|
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.`);
|
|
1990
|
-
return
|
|
1991
|
-
},
|
|
1992
|
-
let t =
|
|
1993
|
-
return t === void 0 ?
|
|
1994
|
-
},
|
|
2104
|
+
return k(e, t);
|
|
2105
|
+
}, Bn = (e) => {
|
|
2106
|
+
let t = zn(e, "postgres");
|
|
2107
|
+
return t === void 0 ? Vn(e) : t;
|
|
2108
|
+
}, Vn = (e) => {
|
|
1995
2109
|
switch (e.type) {
|
|
1996
2110
|
case "id": return "text";
|
|
1997
2111
|
case "text": return "text";
|
|
1998
2112
|
case "integer": return "integer";
|
|
1999
2113
|
case "real": return "double precision";
|
|
2000
|
-
case "decimal": return
|
|
2114
|
+
case "decimal": return Rn(e, "NUMERIC");
|
|
2001
2115
|
case "bigint": return "bigint";
|
|
2002
2116
|
case "boolean": return "boolean";
|
|
2003
2117
|
case "timestamp": return "timestamptz";
|
|
@@ -2011,15 +2125,15 @@ BEGIN
|
|
|
2011
2125
|
case "interval": return "interval";
|
|
2012
2126
|
case "raw": return e.rawDdl ?? "text";
|
|
2013
2127
|
}
|
|
2014
|
-
},
|
|
2015
|
-
let t =
|
|
2128
|
+
}, Hn = (e) => {
|
|
2129
|
+
let t = zn(e, "mariadb");
|
|
2016
2130
|
if (t !== void 0) return t;
|
|
2017
2131
|
switch (e.type) {
|
|
2018
2132
|
case "id": return "VARCHAR(64)";
|
|
2019
2133
|
case "text": return e.hasDefault && e.defaultValue !== void 0 || e.oneOf && e.oneOf.length > 0 ? "VARCHAR(255)" : "LONGTEXT";
|
|
2020
2134
|
case "integer": return "INT";
|
|
2021
2135
|
case "real": return "DOUBLE";
|
|
2022
|
-
case "decimal": return
|
|
2136
|
+
case "decimal": return Rn(e, "DECIMAL");
|
|
2023
2137
|
case "bigint": return "BIGINT";
|
|
2024
2138
|
case "boolean": return "TINYINT(1)";
|
|
2025
2139
|
case "timestamp": return "DATETIME(6)";
|
|
@@ -2033,82 +2147,82 @@ BEGIN
|
|
|
2033
2147
|
case "interval": return "BIGINT";
|
|
2034
2148
|
case "raw": return e.rawDdl ?? "LONGTEXT";
|
|
2035
2149
|
}
|
|
2036
|
-
},
|
|
2150
|
+
}, U = (e) => `"${e}"`, W = (e) => `\`${e.replace(/`/g, "``")}\``, Un = (e, t) => `ALTER TABLE ${t(e.table)} DROP COLUMN IF EXISTS ${t(e.column)};`, Wn = (e) => `ALTER TABLE ${W(e.table)} DROP COLUMN IF EXISTS ${W(e.column)}, ALGORITHM=COPY;`, Gn = (e, t) => `ALTER TABLE ${t(e.table)} RENAME COLUMN ${t(e.from)} TO ${t(e.to)};`, Kn = (e, t, n) => `ALTER TABLE ${n(e.table)} RENAME COLUMN ${n(e.column)} TO ${n(`${e.column}${_r(t)}`)};`, qn = (e, t, n) => e.hasDefault && e.defaultValue !== void 0 ? `ALTER TABLE ${t(e.table)} ALTER COLUMN ${t(e.column)} SET ${Jn(e, n)};` : `ALTER TABLE ${t(e.table)} ALTER COLUMN ${t(e.column)} DROP DEFAULT;`, Jn = (e, t) => {
|
|
2037
2151
|
let n = t(e.defaultValue);
|
|
2038
2152
|
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.`);
|
|
2039
2153
|
return n;
|
|
2040
|
-
},
|
|
2041
|
-
let r =
|
|
2042
|
-
return r ?
|
|
2043
|
-
},
|
|
2044
|
-
let r = n ?? e.nullable, i =
|
|
2154
|
+
}, G = (e, t, n) => e.find((e) => e.tableName === t)?.fields[n], Yn = (e, t, n) => {
|
|
2155
|
+
let r = G(e, t, n);
|
|
2156
|
+
return r ? vt(n, r) : void 0;
|
|
2157
|
+
}, Xn = (e, t, n) => {
|
|
2158
|
+
let r = n ?? e.nullable, i = Ae(e, "mariadb");
|
|
2045
2159
|
return [
|
|
2046
|
-
|
|
2047
|
-
|
|
2160
|
+
W(t),
|
|
2161
|
+
k(e, "mariadb"),
|
|
2048
2162
|
r ? null : "NOT NULL",
|
|
2049
2163
|
i
|
|
2050
2164
|
].filter(Boolean).join(" ");
|
|
2051
|
-
},
|
|
2165
|
+
}, Zn = (e, t) => e == null ? null : Array.isArray(e) ? Qn(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" ? A(e, "mariadb") : null, Qn = (e, t, n) => je(e, t ?? { type: "json" }, n), $n = (e, t) => e == null ? null : Array.isArray(e) ? Qn(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" ? A(e, "postgres") : null, K = (e, t) => {
|
|
2052
2166
|
if (!e.generatedAs) return null;
|
|
2053
2167
|
let { expr: n, stored: r } = e.generatedAs;
|
|
2054
2168
|
return t === "postgres" ? `GENERATED ALWAYS AS (${n}) STORED` : t === "mssql" ? `AS (${n})${r ? " PERSISTED" : ""}` : `GENERATED ALWAYS AS (${n}) ${r ? "STORED" : "VIRTUAL"}`;
|
|
2055
|
-
},
|
|
2056
|
-
if (e.type === "id" && e.idScheme?.kind === "numeric") return
|
|
2057
|
-
let t =
|
|
2058
|
-
if (t) return `${
|
|
2059
|
-
let n = [
|
|
2169
|
+
}, er = (e) => {
|
|
2170
|
+
if (e.type === "id" && e.idScheme?.kind === "numeric") return j(e.name, "postgres");
|
|
2171
|
+
let t = K(e, "postgres");
|
|
2172
|
+
if (t) return `${U(e.name)} ${Bn(e)} ${t}`;
|
|
2173
|
+
let n = [U(e.name), Bn(e)];
|
|
2060
2174
|
if (e.type === "id" && n.push("PRIMARY KEY"), e.nullable || n.push("NOT NULL"), e.unique && e.type !== "id" && n.push("UNIQUE"), e.hasDefault) {
|
|
2061
|
-
let t =
|
|
2175
|
+
let t = $n(e.defaultValue, e);
|
|
2062
2176
|
t && n.push(t);
|
|
2063
2177
|
}
|
|
2064
|
-
return e.references && (n.push(`REFERENCES ${
|
|
2178
|
+
return e.references && (n.push(`REFERENCES ${U(e.references.table)} (${U(e.references.column)})`), e.references.onDelete && n.push(`ON DELETE ${{
|
|
2065
2179
|
cascade: "CASCADE",
|
|
2066
2180
|
restrict: "RESTRICT",
|
|
2067
2181
|
setNull: "SET NULL",
|
|
2068
2182
|
noAction: "NO ACTION"
|
|
2069
2183
|
}[e.references.onDelete]}`)), n.join(" ");
|
|
2070
|
-
},
|
|
2071
|
-
let t = e.columns.map((e) => ` ${
|
|
2072
|
-
for (let t of e.indexes) t.name !== `${e.table}_pkey` && n.push(`CREATE ${t.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${
|
|
2184
|
+
}, tr = (e, t) => e.primaryKey && e.primaryKey.length > 0 ? `,\n PRIMARY KEY (${e.primaryKey.map(t).join(", ")})` : "", nr = (e) => {
|
|
2185
|
+
let t = e.columns.map((e) => ` ${er(e)}`).join(",\n"), n = [`CREATE TABLE IF NOT EXISTS ${U(e.table)} (\n${t}${tr(e, U)}\n)`];
|
|
2186
|
+
for (let t of e.indexes) t.name !== `${e.table}_pkey` && n.push(`CREATE ${t.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${U(t.name)} ON ${U(e.table)} (${t.columns.map(Sr).join(", ")})`);
|
|
2073
2187
|
return n;
|
|
2074
|
-
},
|
|
2075
|
-
let t =
|
|
2188
|
+
}, rr = 191, ir = (e) => {
|
|
2189
|
+
let t = Hn(e).toUpperCase();
|
|
2076
2190
|
return t.endsWith("TEXT") || t.endsWith("BLOB");
|
|
2077
|
-
},
|
|
2078
|
-
if (e.type === "id" && e.idScheme?.kind === "numeric") return
|
|
2079
|
-
let t =
|
|
2080
|
-
if (t) return `${
|
|
2081
|
-
let n = [
|
|
2082
|
-
if (e.type === "id" && n.push("PRIMARY KEY"), e.nullable || n.push("NOT NULL"), e.unique && e.type !== "id" && !
|
|
2083
|
-
let t =
|
|
2191
|
+
}, ar = (e) => {
|
|
2192
|
+
if (e.type === "id" && e.idScheme?.kind === "numeric") return j(e.name, "mariadb");
|
|
2193
|
+
let t = K(e, "mariadb");
|
|
2194
|
+
if (t) return `${W(e.name)} ${Hn(e)} ${t}`;
|
|
2195
|
+
let n = [W(e.name), Hn(e)];
|
|
2196
|
+
if (e.type === "id" && n.push("PRIMARY KEY"), e.nullable || n.push("NOT NULL"), e.unique && e.type !== "id" && !ir(e) && n.push("UNIQUE"), e.hasDefault) {
|
|
2197
|
+
let t = Zn(e.defaultValue, e);
|
|
2084
2198
|
t && n.push(t);
|
|
2085
2199
|
}
|
|
2086
|
-
return e.references && (n.push(`REFERENCES ${
|
|
2200
|
+
return e.references && (n.push(`REFERENCES ${W(e.references.table)} (${W(e.references.column)})`), e.references.onDelete && n.push(`ON DELETE ${{
|
|
2087
2201
|
cascade: "CASCADE",
|
|
2088
2202
|
restrict: "RESTRICT",
|
|
2089
2203
|
setNull: "SET NULL",
|
|
2090
2204
|
noAction: "NO ACTION"
|
|
2091
2205
|
}[e.references.onDelete]}`)), n.join(" ");
|
|
2092
|
-
},
|
|
2206
|
+
}, or = (e) => {
|
|
2093
2207
|
let t = new Map(e.columns.map((e) => [e.name, e])), n = (e) => {
|
|
2094
|
-
let n = t.get(e);
|
|
2095
|
-
return n
|
|
2096
|
-
}, r = e.columns.map((e) => ` ${
|
|
2097
|
-
for (let t of e.columns) t.unique && t.type !== "id" &&
|
|
2098
|
-
for (let t of e.indexes) t.name !== `${e.table}_pkey` && i.push(`CREATE ${t.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${
|
|
2208
|
+
let n = br(t.get(e));
|
|
2209
|
+
return n === void 0 ? W(e) : `${W(e)}(${n})`;
|
|
2210
|
+
}, r = e.columns.map((e) => ` ${ar(e)}`).join(",\n"), i = [`CREATE TABLE IF NOT EXISTS ${W(e.table)} (\n${r}${tr(e, n)}\n)`];
|
|
2211
|
+
for (let t of e.columns) t.unique && t.type !== "id" && ir(t) && i.push(`CREATE UNIQUE INDEX IF NOT EXISTS ${W(`${e.table}_${t.name}_key`)} ON ${W(e.table)} (${n(t.name)})`);
|
|
2212
|
+
for (let t of e.indexes) t.name !== `${e.table}_pkey` && i.push(`CREATE ${t.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${W(t.name)} ON ${W(e.table)} (${t.columns.map(n).join(", ")})`);
|
|
2099
2213
|
return i;
|
|
2100
|
-
},
|
|
2214
|
+
}, sr = {
|
|
2101
2215
|
cascade: "CASCADE",
|
|
2102
2216
|
restrict: "RESTRICT",
|
|
2103
2217
|
setNull: "SET NULL",
|
|
2104
2218
|
noAction: "NO ACTION"
|
|
2105
|
-
},
|
|
2219
|
+
}, cr = {
|
|
2106
2220
|
cascade: "CASCADE",
|
|
2107
2221
|
restrict: "NO ACTION",
|
|
2108
2222
|
setNull: "SET NULL",
|
|
2109
2223
|
noAction: "NO ACTION"
|
|
2110
|
-
},
|
|
2111
|
-
let t =
|
|
2224
|
+
}, lr = (e) => {
|
|
2225
|
+
let t = zn(e, "sqlite");
|
|
2112
2226
|
if (t !== void 0) return t;
|
|
2113
2227
|
switch (e.type) {
|
|
2114
2228
|
case "integer":
|
|
@@ -2119,60 +2233,60 @@ BEGIN
|
|
|
2119
2233
|
case "bytes": return "BLOB";
|
|
2120
2234
|
default: return "TEXT";
|
|
2121
2235
|
}
|
|
2122
|
-
},
|
|
2123
|
-
if (e.type === "id" && e.idScheme?.kind === "numeric") return
|
|
2124
|
-
let t =
|
|
2125
|
-
if (t) return `${
|
|
2126
|
-
let n = [
|
|
2236
|
+
}, q = (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" ? A(e, "sqlite") : null, ur = (e) => {
|
|
2237
|
+
if (e.type === "id" && e.idScheme?.kind === "numeric") return j(e.name, "sqlite");
|
|
2238
|
+
let t = K(e, "sqlite");
|
|
2239
|
+
if (t) return `${U(e.name)} ${lr(e)} ${t}`;
|
|
2240
|
+
let n = [U(e.name), lr(e)];
|
|
2127
2241
|
if (e.type === "id" && n.push("PRIMARY KEY"), e.nullable || n.push("NOT NULL"), e.unique && e.type !== "id" && n.push("UNIQUE"), e.hasDefault) {
|
|
2128
|
-
let t =
|
|
2242
|
+
let t = q(e.defaultValue, "CURRENT_TIMESTAMP");
|
|
2129
2243
|
t && n.push(t);
|
|
2130
2244
|
}
|
|
2131
|
-
if (e.references && (n.push(`REFERENCES ${
|
|
2245
|
+
if (e.references && (n.push(`REFERENCES ${U(e.references.table)} (${U(e.references.column)})`), e.references.onDelete && n.push(`ON DELETE ${sr[e.references.onDelete]}`)), e.oneOf && e.oneOf.length > 0) {
|
|
2132
2246
|
let t = e.oneOf.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ");
|
|
2133
|
-
n.push(`CHECK (${
|
|
2247
|
+
n.push(`CHECK (${U(e.name)} IN (${t}))`);
|
|
2134
2248
|
}
|
|
2135
2249
|
return n.join(" ");
|
|
2136
|
-
},
|
|
2137
|
-
let t = e.columns.map((e) => ` ${
|
|
2138
|
-
for (let t of e.indexes) t.name !== `${e.table}_pkey` && n.push(`CREATE ${t.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${
|
|
2250
|
+
}, dr = (e) => {
|
|
2251
|
+
let t = e.columns.map((e) => ` ${ur(e)}`).join(",\n"), n = [`CREATE TABLE IF NOT EXISTS ${U(e.table)} (\n${t}${tr(e, U)}\n)`];
|
|
2252
|
+
for (let t of e.indexes) t.name !== `${e.table}_pkey` && n.push(`CREATE ${t.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${U(t.name)} ON ${U(e.table)} (${t.columns.map(U).join(", ")})`);
|
|
2139
2253
|
return n;
|
|
2140
|
-
},
|
|
2254
|
+
}, J = (e, t) => {
|
|
2141
2255
|
let n = t.find((t) => t.tableName === e);
|
|
2142
2256
|
if (!n) return [];
|
|
2143
|
-
let r =
|
|
2144
|
-
...
|
|
2257
|
+
let r = P([n]).tables[0], i = `${e}__voltro_rebuild`, a = r.columns.map((e) => U(e.name)).join(", "), o = [
|
|
2258
|
+
...dr({
|
|
2145
2259
|
kind: "create-table",
|
|
2146
2260
|
table: i,
|
|
2147
2261
|
columns: r.columns,
|
|
2148
2262
|
indexes: [],
|
|
2149
2263
|
...r.primaryKey ? { primaryKey: r.primaryKey } : {}
|
|
2150
2264
|
}),
|
|
2151
|
-
`INSERT INTO ${
|
|
2152
|
-
`DROP TABLE ${
|
|
2153
|
-
`ALTER TABLE ${
|
|
2265
|
+
`INSERT INTO ${U(i)} (${a}) SELECT ${a} FROM ${U(e)}`,
|
|
2266
|
+
`DROP TABLE ${U(e)}`,
|
|
2267
|
+
`ALTER TABLE ${U(i)} RENAME TO ${U(e)}`
|
|
2154
2268
|
];
|
|
2155
|
-
for (let t of r.indexes) t.name !== `${e}_pkey` && o.push(`CREATE ${t.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${
|
|
2269
|
+
for (let t of r.indexes) t.name !== `${e}_pkey` && o.push(`CREATE ${t.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${U(t.name)} ON ${U(e)} (${t.columns.map(U).join(", ")})`);
|
|
2156
2270
|
return o;
|
|
2157
|
-
},
|
|
2271
|
+
}, fr = (e, t, n, r) => {
|
|
2158
2272
|
let i = r ?? e.nullable;
|
|
2159
|
-
return `ALTER TABLE ${
|
|
2160
|
-
},
|
|
2161
|
-
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 ${
|
|
2273
|
+
return `ALTER TABLE ${Y(n)} ALTER COLUMN ${Y(t)} ${k(e, "mssql")} ${i ? "NULL" : "NOT NULL"};`;
|
|
2274
|
+
}, pr = (e) => {
|
|
2275
|
+
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 ${Y(e.table)} DROP CONSTRAINT [' + @dc + ']');`;
|
|
2162
2276
|
if (!e.hasDefault) return t;
|
|
2163
|
-
let n =
|
|
2277
|
+
let n = q(e.defaultValue, "SYSUTCDATETIME()");
|
|
2164
2278
|
if (!n) return t;
|
|
2165
2279
|
let r = n.replace(/^DEFAULT\s+/i, "");
|
|
2166
|
-
return `${t} ALTER TABLE ${
|
|
2167
|
-
},
|
|
2168
|
-
let t =
|
|
2280
|
+
return `${t} ALTER TABLE ${Y(e.table)} ADD DEFAULT ${r} FOR ${Y(e.column)};`;
|
|
2281
|
+
}, Y = (e) => `[${e.replace(/]/g, "]]")}]`, mr = (e) => {
|
|
2282
|
+
let t = zn(e, "mssql");
|
|
2169
2283
|
if (t !== void 0) return t;
|
|
2170
2284
|
switch (e.type) {
|
|
2171
2285
|
case "id":
|
|
2172
2286
|
case "reference": return "NVARCHAR(64)";
|
|
2173
2287
|
case "integer": return "INT";
|
|
2174
2288
|
case "real": return "FLOAT";
|
|
2175
|
-
case "decimal": return
|
|
2289
|
+
case "decimal": return Rn(e, "DECIMAL");
|
|
2176
2290
|
case "bigint": return "BIGINT";
|
|
2177
2291
|
case "boolean": return "BIT";
|
|
2178
2292
|
case "timestamp": return "DATETIME2(6)";
|
|
@@ -2180,55 +2294,59 @@ BEGIN
|
|
|
2180
2294
|
case "bytes": return "VARBINARY(MAX)";
|
|
2181
2295
|
default: return "NVARCHAR(MAX)";
|
|
2182
2296
|
}
|
|
2183
|
-
},
|
|
2184
|
-
if (e.type === "id" && e.idScheme?.kind === "numeric") return
|
|
2185
|
-
let n = e.type === "text" && t?.has(e.name) ? "NVARCHAR(450)" :
|
|
2186
|
-
if (r) return `${
|
|
2187
|
-
let i = [
|
|
2297
|
+
}, hr = (e, t) => {
|
|
2298
|
+
if (e.type === "id" && e.idScheme?.kind === "numeric") return j(e.name, "mssql");
|
|
2299
|
+
let n = e.type === "text" && t?.has(e.name) ? "NVARCHAR(450)" : mr(e), r = K(e, "mssql");
|
|
2300
|
+
if (r) return `${Y(e.name)} ${r}`;
|
|
2301
|
+
let i = [Y(e.name), n];
|
|
2188
2302
|
if (e.type === "id" && i.push("PRIMARY KEY"), e.nullable || i.push("NOT NULL"), e.unique && e.type !== "id" && i.push("UNIQUE"), e.hasDefault) {
|
|
2189
|
-
let t =
|
|
2303
|
+
let t = q(e.defaultValue, "SYSUTCDATETIME()");
|
|
2190
2304
|
t && i.push(t);
|
|
2191
2305
|
}
|
|
2192
|
-
return e.references && (i.push(`REFERENCES ${
|
|
2193
|
-
},
|
|
2194
|
-
let t = (e) => e.replace(/'/g, "''"), n = new Set(e.indexes.flatMap((e) => e.columns)), r = e.columns.map((e) => ` ${
|
|
2306
|
+
return e.references && (i.push(`REFERENCES ${Y(e.references.table)} (${Y(e.references.column)})`), e.references.onDelete && i.push(`ON DELETE ${cr[e.references.onDelete]}`)), i.join(" ");
|
|
2307
|
+
}, gr = (e) => {
|
|
2308
|
+
let t = (e) => e.replace(/'/g, "''"), n = new Set(e.indexes.flatMap((e) => e.columns)), r = e.columns.map((e) => ` ${hr(e, n)}`).join(",\n"), i = `CREATE TABLE ${Y(e.table)} (\n${r}${tr(e, Y)}\n)`, a = [`IF OBJECT_ID(N'${t(e.table)}', N'U') IS NULL EXEC(N'${t(i)}')`];
|
|
2195
2309
|
for (let n of e.indexes) {
|
|
2196
2310
|
if (n.name === `${e.table}_pkey`) continue;
|
|
2197
|
-
let r = `CREATE ${n.unique ? "UNIQUE " : ""}INDEX ${
|
|
2311
|
+
let r = `CREATE ${n.unique ? "UNIQUE " : ""}INDEX ${Y(n.name)} ON ${Y(e.table)} (${n.columns.map(Y).join(", ")})`;
|
|
2198
2312
|
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)}')`);
|
|
2199
2313
|
}
|
|
2200
2314
|
return a;
|
|
2201
|
-
},
|
|
2202
|
-
let r = `${e.table}${
|
|
2315
|
+
}, _r = (e) => `__dropped_${e.replace(/[-:T.Z]/g, "").slice(0, 14)}`, vr = (e, t) => `DROP TABLE IF EXISTS ${t(e.table)};`, yr = (e, t, n) => {
|
|
2316
|
+
let r = `${e.table}${_r(t)}`;
|
|
2203
2317
|
return `ALTER TABLE ${n(e.table)} RENAME TO ${n(r)};`;
|
|
2204
|
-
},
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2318
|
+
}, br = (e) => {
|
|
2319
|
+
if (e === void 0 || !ir(e)) return;
|
|
2320
|
+
let t = typeof e.maxLength == "number" && e.maxLength > 0 ? e.maxLength : void 0;
|
|
2321
|
+
return t === void 0 ? rr : Math.min(rr, t);
|
|
2322
|
+
}, xr = (e, t, n) => {
|
|
2323
|
+
let r = br(Yn(e, t, n));
|
|
2324
|
+
return r === void 0 ? W(n) : `${W(n)}(${r})`;
|
|
2325
|
+
}, Sr = (e) => e.startsWith("(") ? e : U(e), Cr = (e, t) => `CREATE ${e.index.unique ? "UNIQUE " : ""}INDEX ${t ? "CONCURRENTLY " : ""}IF NOT EXISTS ${U(e.index.name)} ON ${U(e.table)} (${e.index.columns.map(Sr).join(", ")});`, wr = (e, t) => `CREATE ${e.index.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${W(e.index.name)} ON ${W(e.table)} (${e.index.columns.map((n) => xr(t, e.table, n)).join(", ")});`, Tr = (e) => `DROP INDEX IF EXISTS ${U(e.index)};`, Er = (e) => `DROP INDEX ${W(e.index)} ON ${W(e.table)};`, Dr = (e) => `DROP INDEX IF EXISTS ${Y(e.index)} ON ${Y(e.table)};`, Or = (e, t) => `ALTER TABLE ${t(e.from)} RENAME TO ${t(e.to)};`, kr = (e) => {
|
|
2208
2326
|
let t = (e) => e.replace(/'/g, "''");
|
|
2209
2327
|
return `EXEC sp_rename N'${t(e.from)}', N'${t(e.to)}';`;
|
|
2210
|
-
},
|
|
2328
|
+
}, Ar = (e) => `ALTER INDEX ${U(e.from)} RENAME TO ${U(e.to)};`, jr = (e) => `ALTER TABLE ${W(e.table)} RENAME INDEX ${W(e.from)} TO ${W(e.to)};`, Mr = (e) => {
|
|
2211
2329
|
let t = (e) => e.replace(/'/g, "''");
|
|
2212
2330
|
return `EXEC sp_rename N'${t(e.table)}.${t(e.from)}', N'${t(e.to)}', N'INDEX';`;
|
|
2213
|
-
},
|
|
2214
|
-
let t = (e) => e.replace(/'/g, "''"), n = `CREATE ${e.index.unique ? "UNIQUE " : ""}INDEX ${
|
|
2331
|
+
}, Nr = (e) => {
|
|
2332
|
+
let t = (e) => e.replace(/'/g, "''"), n = `CREATE ${e.index.unique ? "UNIQUE " : ""}INDEX ${Y(e.index.name)} ON ${Y(e.table)} (${e.index.columns.map(Y).join(", ")})`;
|
|
2215
2333
|
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)}');`;
|
|
2216
|
-
},
|
|
2334
|
+
}, Pr = (e, t, n) => {
|
|
2217
2335
|
let r = n.find((t) => t.tableName === e);
|
|
2218
2336
|
if (!r) return [];
|
|
2219
2337
|
let i = [];
|
|
2220
2338
|
for (let n of t) {
|
|
2221
2339
|
let t = r.fields[n];
|
|
2222
|
-
t && t.type === "text" && !t.generatedAs && i.push(`ALTER TABLE ${
|
|
2340
|
+
t && t.type === "text" && !t.generatedAs && i.push(`ALTER TABLE ${Y(e)} ALTER COLUMN ${Y(n)} NVARCHAR(450) ${t.nullable ? "NULL" : "NOT NULL"};`);
|
|
2223
2341
|
}
|
|
2224
2342
|
return i;
|
|
2225
|
-
},
|
|
2226
|
-
let t = e.fields.map((e) =>
|
|
2227
|
-
return `ALTER TABLE ${
|
|
2228
|
-
},
|
|
2229
|
-
let n = e.fields.map((n) =>
|
|
2230
|
-
return `ALTER TABLE ${
|
|
2231
|
-
},
|
|
2343
|
+
}, Fr = (e) => `ALTER TABLE ${U(e.table)} ADD CONSTRAINT ${U(`${e.table}_${e.column}_key`)} UNIQUE (${U(e.column)});`, Ir = (e, t) => `ALTER TABLE ${W(e.table)} ADD CONSTRAINT ${W(`${e.table}_${e.column}_key`)} UNIQUE (${xr(t, e.table, e.column)});`, Lr = (e) => {
|
|
2344
|
+
let t = e.fields.map((e) => U(e)).join(", ");
|
|
2345
|
+
return `ALTER TABLE ${U(e.table)} ADD CONSTRAINT ${U(e.name)} UNIQUE (${t});`;
|
|
2346
|
+
}, Rr = (e, t) => {
|
|
2347
|
+
let n = e.fields.map((n) => xr(t, e.table, n)).join(", ");
|
|
2348
|
+
return `ALTER TABLE ${W(e.table)} ADD CONSTRAINT ${W(e.name)} UNIQUE (${n});`;
|
|
2349
|
+
}, zr = (e) => `ALTER TABLE ${U(e.table)} DROP CONSTRAINT IF EXISTS ${U(e.name)};`, Br = (e) => `ALTER TABLE ${W(e.table)} DROP INDEX ${W(e.name)};`, Vr = (e) => `ALTER TABLE ${U(e.table)} DROP CONSTRAINT IF EXISTS ${U(`${e.table}_${e.column}_key`)};`, Hr = (e) => `ALTER TABLE ${W(e.table)} DROP INDEX ${W(`${e.table}_${e.column}_key`)};`, Ur = (e, t) => {
|
|
2232
2350
|
let n = [], r = t(e.table), i = t(e.column), a = t(e.targetTable), o = t(e.targetColumn);
|
|
2233
2351
|
if (e.orphanPolicy) {
|
|
2234
2352
|
let t = `${i} IS NOT NULL AND ${i} NOT IN (SELECT ${o} FROM ${a})`;
|
|
@@ -2246,11 +2364,11 @@ BEGIN
|
|
|
2246
2364
|
setNull: "SET NULL",
|
|
2247
2365
|
noAction: "NO ACTION"
|
|
2248
2366
|
}[e.onDelete]}`), n.push(s.join(" ") + ";"), n;
|
|
2249
|
-
},
|
|
2367
|
+
}, Wr = (e) => `ALTER TABLE ${U(e.table)} DROP CONSTRAINT IF EXISTS ${U(`${e.table}_${e.column}_fkey`)};`, Gr = (e) => `ALTER TABLE ${W(e.table)} DROP FOREIGN KEY ${W(`${e.table}_${e.column}_fkey`)};`, Kr = (e, t) => {
|
|
2250
2368
|
let n = e.values.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ");
|
|
2251
2369
|
return `ALTER TABLE ${t(e.table)} ADD CONSTRAINT ${t(`${e.table}_${e.column}_check`)} CHECK (${t(e.column)} IN (${n}));`;
|
|
2252
|
-
},
|
|
2253
|
-
let i = e.column, a = r === "mariadb", o = a ?
|
|
2370
|
+
}, qr = (e) => `ALTER TABLE ${U(e.table)} DROP CONSTRAINT IF EXISTS ${U(`${e.table}_${e.column}_check`)};`, Jr = (e) => `ALTER TABLE ${W(e.table)} DROP CHECK ${W(`${e.table}_${e.column}_check`)};`, Yr = (e, t, n, r) => {
|
|
2371
|
+
let i = e.column, a = r === "mariadb", o = a ? W : r === "mssql" ? Y : U, s = a ? Hn(i) : r === "mssql" ? mr(i) : r === "sqlite" ? lr(i) : Bn(i), c = r === "mssql" ? "ADD" : "ADD COLUMN", l = (e) => a ? Zn(e, i) : r === "mssql" ? q(e, "SYSUTCDATETIME()") : r === "sqlite" ? q(e, "CURRENT_TIMESTAMP") : $n(e, i), u = K(i, r);
|
|
2254
2372
|
if (u) {
|
|
2255
2373
|
let t = r === "mssql" ? `${o(i.name)} ${u}` : `${o(i.name)} ${s} ${u}`;
|
|
2256
2374
|
return {
|
|
@@ -2278,7 +2396,7 @@ BEGIN
|
|
|
2278
2396
|
post: f ? [f] : []
|
|
2279
2397
|
};
|
|
2280
2398
|
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;
|
|
2281
|
-
},
|
|
2399
|
+
}, Xr = (e) => {
|
|
2282
2400
|
let t = new Map(e.map((e) => [e.tableName, e]));
|
|
2283
2401
|
return (e, n) => {
|
|
2284
2402
|
let r = t.get(e);
|
|
@@ -2298,297 +2416,416 @@ BEGIN
|
|
|
2298
2416
|
...o
|
|
2299
2417
|
};
|
|
2300
2418
|
};
|
|
2301
|
-
},
|
|
2302
|
-
process.stderr.write(
|
|
2303
|
-
})),
|
|
2304
|
-
let r =
|
|
2419
|
+
}, X = (e, t, n) => e.unsafe(t).pipe(b.tapError((e) => b.sync(() => {
|
|
2420
|
+
process.stderr.write(re(`applier: statement failed (op=${n})`, t, e, 400));
|
|
2421
|
+
})), b.mapError((e) => {
|
|
2422
|
+
let r = re(`applier: statement failed (op=${n})`, t, e, 400).trim();
|
|
2305
2423
|
return Object.assign(e, { message: r });
|
|
2306
|
-
}),
|
|
2307
|
-
let
|
|
2308
|
-
|
|
2424
|
+
}), b.map(() => void 0)), Z = (e, t, n) => b.forEach(t, (t) => X(e, t, n), { discard: !0 }), Q = (e, t) => e.literal(`LIMIT ${Math.max(1, Math.floor(Number.isFinite(t) ? t : 1))}`), $ = (e, t, n, r) => e.unsafe(`SELECT ${r === void 0 ? "*" : i(r, t)} FROM ${i(n, t)} WHERE 1=0`).pipe(b.as(!0), b.catchAll(() => b.succeed(!1))), Zr = (e, t) => b.gen(function* () {
|
|
2425
|
+
let n = P([cn], t).tables[0];
|
|
2426
|
+
if (!n) return yield* b.die(/* @__PURE__ */ Error("migration applier: the resume-ledger table descriptor produced no snapshot. This is a framework bug."));
|
|
2427
|
+
let r = {
|
|
2428
|
+
kind: "create-table",
|
|
2429
|
+
table: L,
|
|
2430
|
+
columns: n.columns,
|
|
2431
|
+
indexes: []
|
|
2432
|
+
}, i = yield* e.onDialectOrElse({
|
|
2433
|
+
mysql: () => b.succeed(or(r)),
|
|
2434
|
+
mssql: () => b.succeed(gr(r)),
|
|
2435
|
+
sqlite: () => b.succeed(dr(r)),
|
|
2436
|
+
orElse: () => b.succeed(nr(r))
|
|
2437
|
+
});
|
|
2438
|
+
yield* b.forEach(i, (t) => X(e, t, "resume-ledger-bootstrap"), { discard: !0 });
|
|
2439
|
+
}), Qr = (e) => e`
|
|
2440
|
+
SELECT ${e("runId")}, ${e("planFingerprint")}, ${e("opIndex")},
|
|
2441
|
+
${e("status")}, ${e("payload")}
|
|
2442
|
+
FROM ${e(L)}
|
|
2443
|
+
`, $r = (e, t, n, r) => b.gen(function* () {
|
|
2444
|
+
let i = yield* H(e, (/* @__PURE__ */ new Date()).toISOString());
|
|
2445
|
+
yield* b.forEach(r, ({ index: r, planned: a }) => e`
|
|
2446
|
+
INSERT INTO ${e(L)}
|
|
2447
|
+
(${e("id")}, ${e("runId")}, ${e("planFingerprint")}, ${e("opIndex")},
|
|
2448
|
+
${e("opKind")}, ${e("opTarget")}, ${e("status")}, ${e("payload")},
|
|
2449
|
+
${e("createdAt")})
|
|
2450
|
+
VALUES
|
|
2451
|
+
(${`mop_${t}_${r}`}, ${t}, ${n}, ${r},
|
|
2452
|
+
${a.op.kind}, ${un(a.op)}, ${"pending"},
|
|
2453
|
+
${JSON.stringify(a)}, ${i})
|
|
2454
|
+
`, { discard: !0 });
|
|
2455
|
+
}), ei = (e, t, n, r, i) => b.gen(function* () {
|
|
2456
|
+
let a = yield* H(e, (/* @__PURE__ */ new Date()).toISOString());
|
|
2457
|
+
if (r === "applied") {
|
|
2458
|
+
yield* e`
|
|
2459
|
+
UPDATE ${e(L)}
|
|
2460
|
+
SET ${e("status")} = ${r}, ${e("appliedAt")} = ${a},
|
|
2461
|
+
${e("durationMs")} = ${i ?? null}
|
|
2462
|
+
WHERE ${e("runId")} = ${t} AND ${e("opIndex")} = ${n}
|
|
2463
|
+
`;
|
|
2464
|
+
return;
|
|
2465
|
+
}
|
|
2466
|
+
yield* e`
|
|
2467
|
+
UPDATE ${e(L)}
|
|
2468
|
+
SET ${e("status")} = ${r}, ${e("startedAt")} = ${a}
|
|
2469
|
+
WHERE ${e("runId")} = ${t} AND ${e("opIndex")} = ${n}
|
|
2470
|
+
`;
|
|
2471
|
+
}), ti = (e, t, n, r) => b.gen(function* () {
|
|
2472
|
+
let i = yield* H(e, (/* @__PURE__ */ new Date()).toISOString());
|
|
2473
|
+
r.length > 0 && (yield* e`
|
|
2474
|
+
UPDATE ${e(L)}
|
|
2475
|
+
SET ${e("status")} = ${"applied"}, ${e("appliedAt")} = ${i}
|
|
2476
|
+
WHERE ${e("runId")} = ${t} AND ${e("opIndex")} IN ${e.in(r)}
|
|
2477
|
+
`), n.length > 0 && (yield* e`
|
|
2478
|
+
UPDATE ${e(L)}
|
|
2479
|
+
SET ${e("status")} = ${"pending"}
|
|
2480
|
+
WHERE ${e("runId")} = ${t} AND ${e("opIndex")} IN ${e.in(n)}
|
|
2481
|
+
`);
|
|
2482
|
+
}), ni = (e, t) => t === void 0 ? e`DELETE FROM ${e(L)}`.pipe(b.asVoid) : e`DELETE FROM ${e(L)} WHERE ${e("runId")} = ${t}`.pipe(b.asVoid), ri = (e, t, n) => b.gen(function* () {
|
|
2483
|
+
if (vn(n.op, n.classification, t) !== "artefact-repair") return "clean";
|
|
2484
|
+
let r = n.op, a = (e) => i(e, t);
|
|
2485
|
+
if (t === "sqlite" || t === "turso") {
|
|
2486
|
+
if (!("table" in r)) return "clean";
|
|
2487
|
+
let n = `${r.table}${pn}`;
|
|
2488
|
+
return (yield* $(e, t, n)) ? ((yield* $(e, t, r.table)) ? yield* X(e, `DROP TABLE ${a(n)}`, "resume-repair") : yield* X(e, `ALTER TABLE ${a(n)} RENAME TO ${a(r.table)}`, "resume-repair"), yield* b.logWarning(`migration resume: reconciled an interrupted sqlite table rebuild on '${r.table}' — replaying the operation`).pipe(b.annotateLogs({ scope: "voltro:migrate" })), "outstanding") : "clean";
|
|
2489
|
+
}
|
|
2490
|
+
if (r.kind !== "alter-column-type") return "clean";
|
|
2491
|
+
let o = `${r.column}${mn}`, s = `${r.column}${hn}`, c = yield* $(e, t, r.table, r.column), l = yield* $(e, t, r.table, o), u = yield* $(e, t, r.table, s), d = (e) => b.logWarning(`migration resume: ${e}`).pipe(b.annotateLogs({ scope: "voltro:migrate" }));
|
|
2492
|
+
return !l && !u ? "clean" : c && l && !u ? (yield* X(e, `ALTER TABLE ${a(r.table)} DROP COLUMN ${a(o)}`, "resume-repair"), yield* d(`dropped a half-populated shadow column '${r.table}.${o}' — replaying the type change`), "outstanding") : !c && l && u ? (yield* X(e, `ALTER TABLE ${a(r.table)} RENAME COLUMN ${a(o)} TO ${a(r.column)}`, "resume-repair"), yield* X(e, `ALTER TABLE ${a(r.table)} DROP COLUMN ${a(s)}`, "resume-repair"), yield* d(`completed an interrupted shadow swap on '${r.table}.${r.column}'`), "applied") : c && !l && u ? (yield* X(e, `ALTER TABLE ${a(r.table)} DROP COLUMN ${a(s)}`, "resume-repair"), yield* d(`dropped the leftover pre-swap column '${r.table}.${s}'`), "applied") : yield* b.die(/* @__PURE__ */ Error(`applyPlan: an interrupted online type change on '${r.table}.${r.column}' left a shadow-swap state this resume cannot classify (column=${c}, ${o}=${l}, ${s}=${u}).\nNothing was changed. Inspect those three columns and reconcile them by hand — the data is in whichever of them is populated — then re-run the migration.`));
|
|
2493
|
+
}), ii = (e, t, n, r = "fresh") => b.gen(function* () {
|
|
2494
|
+
let i = process.hrtime.bigint(), a = Xr(n.declared), o = t.op;
|
|
2495
|
+
switch (o.kind) {
|
|
2309
2496
|
case "create-table": {
|
|
2310
2497
|
let t = yield* e.onDialectOrElse({
|
|
2311
|
-
mysql: () =>
|
|
2312
|
-
mssql: () =>
|
|
2313
|
-
sqlite: () =>
|
|
2314
|
-
orElse: () =>
|
|
2498
|
+
mysql: () => b.succeed(or(o)),
|
|
2499
|
+
mssql: () => b.succeed(gr(o)),
|
|
2500
|
+
sqlite: () => b.succeed(dr(o)),
|
|
2501
|
+
orElse: () => b.succeed(nr(o))
|
|
2315
2502
|
});
|
|
2316
|
-
for (let n of t) yield*
|
|
2503
|
+
for (let n of t) yield* X(e, n, o.kind);
|
|
2317
2504
|
break;
|
|
2318
2505
|
}
|
|
2319
2506
|
case "drop-table": {
|
|
2320
|
-
let t = process.env.VOLTRO_SOFT_DROP === "1", n = (/* @__PURE__ */ new Date()).toISOString(), r = (e) => t ?
|
|
2507
|
+
let t = process.env.VOLTRO_SOFT_DROP === "1", n = (/* @__PURE__ */ new Date()).toISOString(), r = (e) => t ? yr(o, n, e) : vr(o, e);
|
|
2321
2508
|
yield* e.onDialectOrElse({
|
|
2322
|
-
mysql: () =>
|
|
2323
|
-
orElse: () =>
|
|
2509
|
+
mysql: () => X(e, r(W), o.kind),
|
|
2510
|
+
orElse: () => X(e, r(U), o.kind)
|
|
2324
2511
|
});
|
|
2325
2512
|
break;
|
|
2326
2513
|
}
|
|
2327
2514
|
case "drop-column": {
|
|
2328
2515
|
let t = process.env.VOLTRO_SOFT_DROP === "1", n = (/* @__PURE__ */ new Date()).toISOString();
|
|
2329
2516
|
yield* e.onDialectOrElse({
|
|
2330
|
-
mysql: () =>
|
|
2331
|
-
orElse: () =>
|
|
2517
|
+
mysql: () => X(e, t ? Kn(o, n, W) : Wn(o), o.kind),
|
|
2518
|
+
orElse: () => X(e, t ? Kn(o, n, U) : Un(o, U), o.kind)
|
|
2332
2519
|
});
|
|
2333
2520
|
break;
|
|
2334
2521
|
}
|
|
2335
2522
|
case "rename-column":
|
|
2336
2523
|
yield* e.onDialectOrElse({
|
|
2337
|
-
mysql: () =>
|
|
2338
|
-
mssql: () =>
|
|
2339
|
-
orElse: () =>
|
|
2524
|
+
mysql: () => X(e, Gn(o, W), o.kind),
|
|
2525
|
+
mssql: () => X(e, `EXEC sp_rename N'${o.table.replace(/'/g, "''")}.${o.from.replace(/'/g, "''")}', N'${o.to.replace(/'/g, "''")}', 'COLUMN';`, o.kind),
|
|
2526
|
+
orElse: () => X(e, Gn(o, U), o.kind)
|
|
2340
2527
|
});
|
|
2341
2528
|
break;
|
|
2342
2529
|
case "add-index":
|
|
2343
2530
|
yield* e.onDialectOrElse({
|
|
2344
|
-
mysql: () =>
|
|
2345
|
-
mssql: () =>
|
|
2346
|
-
orElse: () =>
|
|
2531
|
+
mysql: () => X(e, wr(o, n.declared), o.kind),
|
|
2532
|
+
mssql: () => Z(e, [...Pr(o.table, o.index.columns, n.declared), Nr(o)], o.kind),
|
|
2533
|
+
orElse: () => X(e, Cr(o, t.classification === "online-required"), o.kind)
|
|
2347
2534
|
});
|
|
2348
2535
|
break;
|
|
2349
2536
|
case "drop-index":
|
|
2350
2537
|
yield* e.onDialectOrElse({
|
|
2351
|
-
mysql: () =>
|
|
2352
|
-
mssql: () =>
|
|
2353
|
-
orElse: () =>
|
|
2538
|
+
mysql: () => X(e, Er(o), o.kind),
|
|
2539
|
+
mssql: () => X(e, Dr(o), o.kind),
|
|
2540
|
+
orElse: () => X(e, Tr(o), o.kind)
|
|
2354
2541
|
});
|
|
2355
2542
|
break;
|
|
2356
2543
|
case "rename-table":
|
|
2357
2544
|
yield* e.onDialectOrElse({
|
|
2358
|
-
mysql: () =>
|
|
2359
|
-
mssql: () =>
|
|
2360
|
-
orElse: () =>
|
|
2545
|
+
mysql: () => X(e, Or(o, W), o.kind),
|
|
2546
|
+
mssql: () => X(e, kr(o), o.kind),
|
|
2547
|
+
orElse: () => X(e, Or(o, U), o.kind)
|
|
2361
2548
|
});
|
|
2362
2549
|
break;
|
|
2363
2550
|
case "rename-index":
|
|
2364
2551
|
yield* e.onDialectOrElse({
|
|
2365
|
-
mysql: () =>
|
|
2366
|
-
mssql: () =>
|
|
2367
|
-
orElse: () =>
|
|
2552
|
+
mysql: () => X(e, jr(o), o.kind),
|
|
2553
|
+
mssql: () => X(e, Mr(o), o.kind),
|
|
2554
|
+
orElse: () => X(e, Ar(o), o.kind)
|
|
2368
2555
|
});
|
|
2369
2556
|
break;
|
|
2370
2557
|
case "add-unique":
|
|
2371
2558
|
yield* e.onDialectOrElse({
|
|
2372
|
-
mysql: () =>
|
|
2373
|
-
sqlite: () =>
|
|
2374
|
-
mssql: () =>
|
|
2375
|
-
orElse: () =>
|
|
2559
|
+
mysql: () => X(e, Ir(o, n.declared), o.kind),
|
|
2560
|
+
sqlite: () => Z(e, J(o.table, n.declared), o.kind),
|
|
2561
|
+
mssql: () => Z(e, [...Pr(o.table, [o.column], n.declared), Fr(o)], o.kind),
|
|
2562
|
+
orElse: () => X(e, Fr(o), o.kind)
|
|
2376
2563
|
});
|
|
2377
2564
|
break;
|
|
2378
2565
|
case "drop-unique":
|
|
2379
2566
|
yield* e.onDialectOrElse({
|
|
2380
|
-
mysql: () =>
|
|
2381
|
-
sqlite: () =>
|
|
2382
|
-
orElse: () =>
|
|
2567
|
+
mysql: () => X(e, Hr(o), o.kind),
|
|
2568
|
+
sqlite: () => Z(e, J(o.table, n.declared), o.kind),
|
|
2569
|
+
orElse: () => X(e, Vr(o), o.kind)
|
|
2383
2570
|
});
|
|
2384
2571
|
break;
|
|
2385
2572
|
case "add-unique-composite":
|
|
2386
2573
|
yield* e.onDialectOrElse({
|
|
2387
|
-
mysql: () =>
|
|
2388
|
-
sqlite: () =>
|
|
2389
|
-
mssql: () =>
|
|
2390
|
-
orElse: () =>
|
|
2574
|
+
mysql: () => X(e, Rr(o, n.declared), o.kind),
|
|
2575
|
+
sqlite: () => Z(e, J(o.table, n.declared), o.kind),
|
|
2576
|
+
mssql: () => Z(e, [...Pr(o.table, o.fields, n.declared), Lr(o)], o.kind),
|
|
2577
|
+
orElse: () => X(e, Lr(o), o.kind)
|
|
2391
2578
|
});
|
|
2392
2579
|
break;
|
|
2393
2580
|
case "drop-unique-composite":
|
|
2394
2581
|
yield* e.onDialectOrElse({
|
|
2395
|
-
mysql: () =>
|
|
2396
|
-
sqlite: () =>
|
|
2397
|
-
orElse: () =>
|
|
2582
|
+
mysql: () => X(e, Br(o), o.kind),
|
|
2583
|
+
sqlite: () => Z(e, J(o.table, n.declared), o.kind),
|
|
2584
|
+
orElse: () => X(e, zr(o), o.kind)
|
|
2398
2585
|
});
|
|
2399
2586
|
break;
|
|
2400
2587
|
case "add-foreign-key":
|
|
2401
2588
|
yield* e.onDialectOrElse({
|
|
2402
|
-
mysql: () =>
|
|
2403
|
-
sqlite: () =>
|
|
2404
|
-
orElse: () =>
|
|
2589
|
+
mysql: () => Z(e, Ur(o, W), o.kind),
|
|
2590
|
+
sqlite: () => Z(e, J(o.table, n.declared), o.kind),
|
|
2591
|
+
orElse: () => Z(e, Ur(o, U), o.kind)
|
|
2405
2592
|
});
|
|
2406
2593
|
break;
|
|
2407
2594
|
case "add-column": {
|
|
2408
|
-
let n =
|
|
2409
|
-
mysql: () =>
|
|
2410
|
-
mssql: () =>
|
|
2411
|
-
sqlite: () =>
|
|
2412
|
-
orElse: () =>
|
|
2595
|
+
let n = Yr(o, t, a, yield* e.onDialectOrElse({
|
|
2596
|
+
mysql: () => b.succeed("mariadb"),
|
|
2597
|
+
mssql: () => b.succeed("mssql"),
|
|
2598
|
+
sqlite: () => b.succeed("sqlite"),
|
|
2599
|
+
orElse: () => b.succeed("postgres")
|
|
2413
2600
|
}));
|
|
2414
|
-
for (let t of n.pre) yield*
|
|
2415
|
-
if (n.backfillKind === "sql" && n.backfillSql) if (!(t.classification === "online-required" || n.batchSize !== void 0)) yield* e`UPDATE ${e(
|
|
2601
|
+
if (!(r === "resume" && (yield* $(e, an(e), o.table, o.column.name)))) for (let t of n.pre) yield* X(e, t, o.kind);
|
|
2602
|
+
if (n.backfillKind === "sql" && n.backfillSql) if (!(t.classification === "online-required" || n.batchSize !== void 0)) yield* e`UPDATE ${e(o.table)} SET ${e(o.column.name)} = ${n.backfillSql}`;
|
|
2416
2603
|
else {
|
|
2417
|
-
let t = n.batchSize ?? 5e3, r = n.sleepMs ?? 0, i,
|
|
2604
|
+
let t = n.batchSize ?? 5e3, r = n.sleepMs ?? 0, i, a = 0;
|
|
2418
2605
|
for (;;) {
|
|
2419
|
-
let s = i === void 0 ? yield* e`SELECT ${e("id")} FROM ${e(
|
|
2606
|
+
let s = i === void 0 ? yield* e`SELECT ${e("id")} FROM ${e(o.table)} WHERE ${e(o.column.name)} IS NULL ORDER BY ${e("id")} ${Q(e, t)}` : yield* e`SELECT ${e("id")} FROM ${e(o.table)} WHERE ${e(o.column.name)} IS NULL AND ${e("id")} > ${i} ORDER BY ${e("id")} ${Q(e, t)}`;
|
|
2420
2607
|
if (s.length === 0) break;
|
|
2421
2608
|
let c = s.map((e) => e.id);
|
|
2422
|
-
yield* e`UPDATE ${e(
|
|
2609
|
+
yield* e`UPDATE ${e(o.table)} SET ${e(o.column.name)} = ${n.backfillSql} WHERE ${e("id")} IN ${e.in(c)}`, a += s.length, yield* b.logInfo(`backfill ${o.table}.${o.column.name}: ${a} rows`).pipe(b.annotateLogs({
|
|
2423
2610
|
scope: "voltro:migrate",
|
|
2424
2611
|
mode: "sql-batched"
|
|
2425
|
-
})), i = s[s.length - 1].id, r > 0 && (yield*
|
|
2612
|
+
})), i = s[s.length - 1].id, r > 0 && (yield* b.sleep(`${r} millis`));
|
|
2426
2613
|
}
|
|
2427
2614
|
}
|
|
2428
2615
|
else if (n.backfillKind === "js" && n.backfillJs) {
|
|
2429
|
-
let t = n.batchSize ?? 1e3, r = n.sleepMs ?? 0, i,
|
|
2616
|
+
let t = n.batchSize ?? 1e3, r = n.sleepMs ?? 0, i, a = 0;
|
|
2430
2617
|
for (;;) {
|
|
2431
|
-
let s = i === void 0 ? yield* e`SELECT ${e("id")} FROM ${e(
|
|
2618
|
+
let s = i === void 0 ? yield* e`SELECT ${e("id")} FROM ${e(o.table)} WHERE ${e(o.column.name)} IS NULL ORDER BY ${e("id")} ${Q(e, t)}` : yield* e`SELECT ${e("id")} FROM ${e(o.table)} WHERE ${e(o.column.name)} IS NULL AND ${e("id")} > ${i} ORDER BY ${e("id")} ${Q(e, t)}`;
|
|
2432
2619
|
if (s.length === 0) break;
|
|
2433
2620
|
for (let t of s) {
|
|
2434
|
-
let r = yield*
|
|
2621
|
+
let r = yield* b.tryPromise({
|
|
2435
2622
|
try: () => Promise.resolve(n.backfillJs({ id: t.id })),
|
|
2436
2623
|
catch: (e) => e
|
|
2437
|
-
}).pipe(
|
|
2438
|
-
yield* e`UPDATE ${e(
|
|
2624
|
+
}).pipe(b.orDie);
|
|
2625
|
+
yield* e`UPDATE ${e(o.table)} SET ${e(o.column.name)} = ${r} WHERE ${e("id")} = ${t.id}`;
|
|
2439
2626
|
}
|
|
2440
|
-
|
|
2627
|
+
a += s.length, yield* b.logInfo(`backfill ${o.table}.${o.column.name}: ${a} rows processed`).pipe(b.annotateLogs({
|
|
2441
2628
|
scope: "voltro:migrate",
|
|
2442
|
-
table:
|
|
2443
|
-
column:
|
|
2444
|
-
})), i = s[s.length - 1].id, r > 0 && (yield*
|
|
2629
|
+
table: o.table,
|
|
2630
|
+
column: o.column.name
|
|
2631
|
+
})), i = s[s.length - 1].id, r > 0 && (yield* b.sleep(`${r} millis`));
|
|
2445
2632
|
}
|
|
2446
2633
|
}
|
|
2447
|
-
for (let t of n.post) yield*
|
|
2634
|
+
for (let t of n.post) yield* X(e, t, o.kind);
|
|
2448
2635
|
break;
|
|
2449
2636
|
}
|
|
2450
2637
|
case "alter-column-nullability":
|
|
2451
2638
|
yield* e.onDialectOrElse({
|
|
2452
2639
|
mysql: () => {
|
|
2453
|
-
let t =
|
|
2454
|
-
return t ?
|
|
2640
|
+
let t = G(n.declared, o.table, o.column);
|
|
2641
|
+
return t ? X(e, `ALTER TABLE ${W(o.table)} MODIFY COLUMN ${Xn(t, o.column, o.toNullable)};`, o.kind) : b.die(/* @__PURE__ */ Error(`alter-column-nullability on mariadb needs the declared column '${o.table}.${o.column}' (to render MODIFY COLUMN); it was not in ctx.declared.`));
|
|
2455
2642
|
},
|
|
2456
2643
|
mssql: () => {
|
|
2457
|
-
let t =
|
|
2458
|
-
return t ?
|
|
2644
|
+
let t = G(n.declared, o.table, o.column);
|
|
2645
|
+
return t ? X(e, fr(t, o.column, o.table, o.toNullable), o.kind) : b.die(/* @__PURE__ */ Error(`alter-column-nullability on mssql needs the declared column '${o.table}.${o.column}'.`));
|
|
2459
2646
|
},
|
|
2460
|
-
sqlite: () =>
|
|
2461
|
-
orElse: () =>
|
|
2647
|
+
sqlite: () => Z(e, J(o.table, n.declared), o.kind),
|
|
2648
|
+
orElse: () => X(e, `ALTER TABLE ${U(o.table)} ALTER COLUMN ${U(o.column)} ${o.toNullable ? "DROP NOT NULL" : "SET NOT NULL"};`, o.kind)
|
|
2462
2649
|
});
|
|
2463
2650
|
break;
|
|
2464
2651
|
case "alter-column-default": {
|
|
2465
|
-
let t =
|
|
2652
|
+
let t = Yn(n.declared, o.table, o.column);
|
|
2466
2653
|
yield* e.onDialectOrElse({
|
|
2467
|
-
mysql: () =>
|
|
2468
|
-
mssql: () =>
|
|
2469
|
-
sqlite: () =>
|
|
2470
|
-
orElse: () =>
|
|
2654
|
+
mysql: () => X(e, qn(o, W, (e) => Zn(e, t)), o.kind),
|
|
2655
|
+
mssql: () => X(e, pr(o), o.kind),
|
|
2656
|
+
sqlite: () => Z(e, J(o.table, n.declared), o.kind),
|
|
2657
|
+
orElse: () => X(e, qn(o, U, (e) => $n(e, t)), o.kind)
|
|
2471
2658
|
});
|
|
2472
2659
|
break;
|
|
2473
2660
|
}
|
|
2474
2661
|
case "alter-column-type": {
|
|
2475
|
-
let r =
|
|
2662
|
+
let r = Bn(Yn(n.declared, o.table, o.column) ?? { type: o.to }), i = o.using ? ` USING ${o.using}` : "";
|
|
2476
2663
|
if (t.classification !== "online-required") {
|
|
2477
2664
|
yield* e.onDialectOrElse({
|
|
2478
2665
|
mysql: () => {
|
|
2479
|
-
let t =
|
|
2480
|
-
return t ?
|
|
2666
|
+
let t = G(n.declared, o.table, o.column);
|
|
2667
|
+
return t ? X(e, `ALTER TABLE ${W(o.table)} MODIFY COLUMN ${Xn(t, o.column)};`, o.kind) : b.die(/* @__PURE__ */ Error(`alter-column-type on mariadb needs the declared column '${o.table}.${o.column}' (to render MODIFY COLUMN); it was not in ctx.declared.`));
|
|
2481
2668
|
},
|
|
2482
2669
|
mssql: () => {
|
|
2483
|
-
let t =
|
|
2484
|
-
return t ?
|
|
2670
|
+
let t = G(n.declared, o.table, o.column);
|
|
2671
|
+
return t ? X(e, fr(t, o.column, o.table), o.kind) : b.die(/* @__PURE__ */ Error(`alter-column-type on mssql needs the declared column '${o.table}.${o.column}'.`));
|
|
2485
2672
|
},
|
|
2486
|
-
sqlite: () =>
|
|
2487
|
-
orElse: () =>
|
|
2673
|
+
sqlite: () => Z(e, J(o.table, n.declared), o.kind),
|
|
2674
|
+
orElse: () => X(e, `ALTER TABLE ${U(o.table)} ALTER COLUMN ${U(o.column)} TYPE ${r}${i};`, o.kind)
|
|
2488
2675
|
});
|
|
2489
2676
|
break;
|
|
2490
2677
|
}
|
|
2491
2678
|
yield* e.onDialectOrElse({
|
|
2492
|
-
mysql: () =>
|
|
2493
|
-
orElse: () =>
|
|
2679
|
+
mysql: () => b.die(/* @__PURE__ */ Error(`online-required alter-column-type on ${o.table}.${o.column} is not supported on mariadb (the shadow-swap path is postgres-only). Use a smaller-table window or an external online-DDL tool.`)),
|
|
2680
|
+
orElse: () => b.void
|
|
2494
2681
|
});
|
|
2495
|
-
let
|
|
2496
|
-
yield* e.unsafe(`ALTER TABLE ${
|
|
2497
|
-
let c =
|
|
2682
|
+
let a = `${o.column}__shadow`, s = `${o.column}__old`;
|
|
2683
|
+
yield* e.unsafe(`ALTER TABLE ${U(o.table)} ADD COLUMN ${U(a)} ${r}`);
|
|
2684
|
+
let c = o.using ?? U(o.column), l;
|
|
2498
2685
|
for (;;) {
|
|
2499
|
-
let t = l === void 0 ? yield* e`SELECT ${e("id")} FROM ${e(
|
|
2686
|
+
let t = l === void 0 ? yield* e`SELECT ${e("id")} FROM ${e(o.table)} WHERE ${e(a)} IS NULL ORDER BY ${e("id")} ${Q(e, 5e3)}` : yield* e`SELECT ${e("id")} FROM ${e(o.table)} WHERE ${e(a)} IS NULL AND ${e("id")} > ${l} ORDER BY ${e("id")} ${Q(e, 5e3)}`;
|
|
2500
2687
|
if (t.length === 0) break;
|
|
2501
|
-
yield* e.unsafe(`UPDATE ${
|
|
2688
|
+
yield* e.unsafe(`UPDATE ${U(o.table)} SET ${U(a)} = ${c} WHERE ${U(o.column)} IS NOT NULL AND ${U(a)} IS NULL AND ${U("id")} IN (${t.map((e) => `'${e.id.replace(/'/g, "''")}'`).join(",")})`), l = t[t.length - 1].id;
|
|
2502
2689
|
}
|
|
2503
|
-
yield*
|
|
2690
|
+
yield* b.logInfo(`alter-column-type shadow ${o.table}.${o.column}: shadow populated`).pipe(b.annotateLogs({ scope: "voltro:migrate" })), yield* e.unsafe(`ALTER TABLE ${U(o.table)} RENAME COLUMN ${U(o.column)} TO ${U(s)}`), yield* e.unsafe(`ALTER TABLE ${U(o.table)} RENAME COLUMN ${U(a)} TO ${U(o.column)}`), yield* e.unsafe(`ALTER TABLE ${U(o.table)} DROP COLUMN ${U(s)}`);
|
|
2504
2691
|
break;
|
|
2505
2692
|
}
|
|
2506
2693
|
case "drop-foreign-key":
|
|
2507
2694
|
yield* e.onDialectOrElse({
|
|
2508
|
-
mysql: () =>
|
|
2509
|
-
sqlite: () =>
|
|
2510
|
-
orElse: () =>
|
|
2695
|
+
mysql: () => X(e, Gr(o), o.kind),
|
|
2696
|
+
sqlite: () => Z(e, J(o.table, n.declared), o.kind),
|
|
2697
|
+
orElse: () => X(e, Wr(o), o.kind)
|
|
2511
2698
|
});
|
|
2512
2699
|
break;
|
|
2513
2700
|
case "add-check":
|
|
2514
2701
|
yield* e.onDialectOrElse({
|
|
2515
|
-
mysql: () =>
|
|
2516
|
-
sqlite: () =>
|
|
2517
|
-
orElse: () =>
|
|
2702
|
+
mysql: () => X(e, Kr(o, W), o.kind),
|
|
2703
|
+
sqlite: () => Z(e, J(o.table, n.declared), o.kind),
|
|
2704
|
+
orElse: () => X(e, Kr(o, U), o.kind)
|
|
2518
2705
|
});
|
|
2519
2706
|
break;
|
|
2520
2707
|
case "drop-check":
|
|
2521
2708
|
yield* e.onDialectOrElse({
|
|
2522
|
-
mysql: () =>
|
|
2523
|
-
sqlite: () =>
|
|
2524
|
-
orElse: () =>
|
|
2709
|
+
mysql: () => X(e, Jr(o), o.kind),
|
|
2710
|
+
sqlite: () => Z(e, J(o.table, n.declared), o.kind),
|
|
2711
|
+
orElse: () => X(e, qr(o), o.kind)
|
|
2525
2712
|
});
|
|
2526
2713
|
break;
|
|
2527
2714
|
}
|
|
2528
2715
|
return {
|
|
2529
|
-
op:
|
|
2716
|
+
op: o,
|
|
2530
2717
|
status: "applied",
|
|
2531
|
-
durationMs: Number((process.hrtime.bigint() -
|
|
2718
|
+
durationMs: Number((process.hrtime.bigint() - i) / 1000000n)
|
|
2532
2719
|
};
|
|
2533
|
-
}),
|
|
2720
|
+
}), ai = (e, t, n, r) => b.gen(function* () {
|
|
2721
|
+
let i = [...new Set(r.map((e) => e.runId))];
|
|
2722
|
+
if (i.length > 1) return yield* b.die(/* @__PURE__ */ Error(`applyPlan: the resume ledger holds ${i.length} unfinished runs (${i.join(", ")}). Only one migration may be in flight at a time — the advisory lock enforces it — so this means a run was interrupted and a LATER one wrote over it without clearing.\nNothing was changed. Inspect \`${L}\`, reconcile the schema by hand, and delete the rows of the run you have finished.`));
|
|
2723
|
+
let a = i[0], o = [...r].map((e) => ({
|
|
2724
|
+
index: Number(e.opIndex),
|
|
2725
|
+
status: e.status,
|
|
2726
|
+
planned: JSON.parse(e.payload)
|
|
2727
|
+
})).sort((e, t) => e.index - t.index), s = o.find((e) => e.status === "started"), c = o.filter((e) => e.status === "started").length;
|
|
2728
|
+
if (c > 1) return yield* b.die(/* @__PURE__ */ Error(`applyPlan: the resume ledger records ${c} operations as in-flight for run ${a}. It is written strictly sequentially, so at most one can be — this ledger is corrupt and the schema state it describes cannot be trusted.\nNothing was changed. Reconcile \`${L}\` by hand before re-running.`));
|
|
2729
|
+
if (yield* b.logWarning(`migration resume: found an interrupted run (${a}) — ${o.filter((e) => e.status === "applied").length} of ${o.length} operation(s) completed${s ? `, in flight: ${s.planned.op.kind} ${un(s.planned.op)}` : ""}`).pipe(b.annotateLogs({ scope: "voltro:migrate" })), o.every((e) => e.status === "pending")) return {
|
|
2730
|
+
kind: "discard",
|
|
2731
|
+
runId: a,
|
|
2732
|
+
why: "no operation had started — no DDL ran"
|
|
2733
|
+
};
|
|
2734
|
+
let l = s ? yield* ri(e, t, s.planned) : "clean", u = r[0].planFingerprint;
|
|
2735
|
+
if (u !== n.toFingerprint) return {
|
|
2736
|
+
kind: "discard",
|
|
2737
|
+
runId: a,
|
|
2738
|
+
why: `the declared schema changed since the interruption (recorded target ${u.slice(0, 12)}…, current ${n.toFingerprint.slice(0, 12)}…) — applying the freshly-diffed plan instead`
|
|
2739
|
+
};
|
|
2740
|
+
let d = (e) => e.status === "applied" ? !1 : s && e.index === s.index && l !== "clean" ? l === "outstanding" : fn(n, e.planned.op), f = o.filter(d), p = o.filter((e) => e.status !== "applied" && !d(e));
|
|
2741
|
+
return f.length === 0 ? {
|
|
2742
|
+
kind: "discard",
|
|
2743
|
+
runId: a,
|
|
2744
|
+
why: "every recorded operation had already taken effect"
|
|
2745
|
+
} : (yield* ti(e, a, f.map((e) => e.index), p.map((e) => e.index)), yield* b.logWarning(`migration resume: continuing run ${a} — replaying ${f.length} operation(s), ${o.length - f.length} already applied`).pipe(b.annotateLogs({ scope: "voltro:migrate" })), {
|
|
2746
|
+
kind: "resume",
|
|
2747
|
+
runId: a,
|
|
2748
|
+
ops: f.map((e) => ({
|
|
2749
|
+
index: e.index,
|
|
2750
|
+
planned: e.planned
|
|
2751
|
+
}))
|
|
2752
|
+
});
|
|
2753
|
+
}), oi = (e, t, n) => b.gen(function* () {
|
|
2534
2754
|
let r = t.operations.filter((e) => e.blocked);
|
|
2535
|
-
if (r.length > 0) return yield*
|
|
2536
|
-
yield*
|
|
2537
|
-
let i = Date.now(), a = []
|
|
2755
|
+
if (r.length > 0) return yield* b.die(/* @__PURE__ */ Error(`applyPlan: refusing to execute — ${r.length} blocked operations. Run \`voltro db plan\` to see the fixes.`));
|
|
2756
|
+
yield* T(e);
|
|
2757
|
+
let i = Date.now(), a = [], o = an(e), s = sn(o) === "plan-transactional", l = s ? t.operations.filter((e) => e.classification !== "online-required") : [], u = s ? t.operations.filter((e) => e.classification === "online-required") : t.operations, d = `plan_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`, f = u.map((e, t) => ({
|
|
2758
|
+
index: t,
|
|
2759
|
+
planned: e
|
|
2760
|
+
})), p = "fresh";
|
|
2538
2761
|
try {
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2762
|
+
yield* Zr(e, o);
|
|
2763
|
+
let r = yield* Qr(e);
|
|
2764
|
+
if (r.length > 0) {
|
|
2765
|
+
let n = yield* ai(e, o, t, r);
|
|
2766
|
+
n.kind === "resume" ? (d = n.runId, f = n.ops, p = "resume") : (yield* b.logWarning(`migration resume: discarding the ledger of run ${n.runId} — ${n.why}`).pipe(b.annotateLogs({ scope: "voltro:migrate" })), yield* ni(e, n.runId));
|
|
2767
|
+
}
|
|
2768
|
+
p === "fresh" && f.length > 0 && (yield* $r(e, d, t.toFingerprint, f)), l.length > 0 && (yield* e.withTransaction(b.gen(function* () {
|
|
2769
|
+
let e = yield* S.SqlClient;
|
|
2770
|
+
for (let t of l) a.push(yield* ii(e, t, n));
|
|
2547
2771
|
})));
|
|
2548
|
-
for (let t
|
|
2772
|
+
for (let { index: t, planned: r } of f) {
|
|
2773
|
+
yield* ei(e, d, t, "started");
|
|
2774
|
+
let i = yield* ii(e, r, n, p);
|
|
2775
|
+
a.push(i), yield* ei(e, d, t, "applied", i.durationMs);
|
|
2776
|
+
}
|
|
2549
2777
|
} finally {
|
|
2550
|
-
yield*
|
|
2778
|
+
yield* E(e).pipe(b.orDie);
|
|
2551
2779
|
}
|
|
2552
|
-
let
|
|
2553
|
-
if (
|
|
2554
|
-
let e =
|
|
2555
|
-
return yield*
|
|
2780
|
+
let m = yield* n.replan(e);
|
|
2781
|
+
if (m.operations.length > 0) {
|
|
2782
|
+
let e = m.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");
|
|
2783
|
+
return yield* b.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 ${m.operations.length}:\n${e}${m.operations.length > 10 ? `\n … and ${m.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.`));
|
|
2556
2784
|
}
|
|
2557
|
-
let
|
|
2558
|
-
|
|
2785
|
+
let h = a.filter((e) => e.status === "applied").length, g = Date.now() - i, _ = d, v = (/* @__PURE__ */ new Date()).toISOString(), y = yield* H(e, v), x = [...l, ...f.map((e) => e.planned)];
|
|
2786
|
+
yield* e`
|
|
2559
2787
|
INSERT INTO ${e("_voltro_migration_plans")}
|
|
2560
2788
|
(${e("id")}, ${e("fingerprint")}, ${e("liveFingerprint")}, ${e("operations")},
|
|
2561
2789
|
${e("appliedBy")}, ${e("environment")}, ${e("source")},
|
|
2562
2790
|
${e("durationMs")}, ${e("appliedAt")}, ${e("notes")})
|
|
2563
2791
|
VALUES
|
|
2564
|
-
(${
|
|
2792
|
+
(${_}, ${t.toFingerprint}, ${m.fromFingerprint}, ${JSON.stringify(x)},
|
|
2565
2793
|
${n.appliedBy}, ${n.environment}, ${n.source},
|
|
2566
|
-
${
|
|
2567
|
-
`,
|
|
2568
|
-
|
|
2569
|
-
|
|
2794
|
+
${g}, ${y}, ${n.notes ?? null})
|
|
2795
|
+
`, yield* ni(e, d);
|
|
2796
|
+
let C = [...new Set(x.flatMap((e) => e.op.kind === "rename-table" ? [e.op.to] : [e.op.table]))];
|
|
2797
|
+
return yield* b.promise(() => c({ changedTables: C }).catch(() => {})), {
|
|
2798
|
+
id: _,
|
|
2799
|
+
appliedAt: v,
|
|
2570
2800
|
fingerprint: t.toFingerprint,
|
|
2571
|
-
operations:
|
|
2801
|
+
operations: x,
|
|
2572
2802
|
appliedBy: n.appliedBy,
|
|
2573
2803
|
environment: n.environment,
|
|
2574
|
-
durationMs:
|
|
2575
|
-
appliedOps:
|
|
2804
|
+
durationMs: g,
|
|
2805
|
+
appliedOps: h,
|
|
2576
2806
|
source: n.source,
|
|
2577
2807
|
...n.notes ? { notes: n.notes } : {}
|
|
2578
2808
|
};
|
|
2579
|
-
}),
|
|
2809
|
+
}), si = (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"), ci = (e) => e`
|
|
2580
2810
|
SELECT fingerprint
|
|
2581
2811
|
FROM _voltro_migration_plans
|
|
2582
2812
|
ORDER BY ${e("appliedAt")} DESC
|
|
2583
2813
|
LIMIT 1
|
|
2584
|
-
`.pipe(
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
}
|
|
2591
|
-
|
|
2814
|
+
`.pipe(b.map((e) => e.length > 0 ? e[0].fingerprint : void 0), b.catchAll(() => b.succeed(void 0))), li = (e, t, n) => b.gen(function* () {
|
|
2815
|
+
if ((yield* ci(e)) === t.toFingerprint) return "already-current";
|
|
2816
|
+
let r = `plan_${Date.now().toString(36)}`, i = yield* H(e, (/* @__PURE__ */ new Date()).toISOString());
|
|
2817
|
+
return yield* e`
|
|
2818
|
+
INSERT INTO ${e("_voltro_migration_plans")}
|
|
2819
|
+
(${e("id")}, ${e("fingerprint")}, ${e("liveFingerprint")}, ${e("operations")},
|
|
2820
|
+
${e("appliedBy")}, ${e("environment")}, ${e("source")},
|
|
2821
|
+
${e("durationMs")}, ${e("appliedAt")}, ${e("notes")})
|
|
2822
|
+
VALUES
|
|
2823
|
+
(${r}, ${t.toFingerprint}, ${t.fromFingerprint}, ${JSON.stringify([])},
|
|
2824
|
+
${n.appliedBy}, ${n.environment}, ${n.source},
|
|
2825
|
+
${0}, ${i}, ${"no DDL — the declared schema already matched the database"})
|
|
2826
|
+
`, "recorded";
|
|
2827
|
+
}).pipe(b.catchAll(() => b.succeed("already-current"))), ui = (e, t) => b.gen(function* () {
|
|
2828
|
+
let n = M(P(t, an(e))), r = yield* ci(e);
|
|
2592
2829
|
return r === n ? {
|
|
2593
2830
|
kind: "up-to-date",
|
|
2594
2831
|
fingerprint: n
|
|
@@ -2597,23 +2834,23 @@ BEGIN
|
|
|
2597
2834
|
expected: n,
|
|
2598
2835
|
...r === void 0 ? {} : { actual: r }
|
|
2599
2836
|
};
|
|
2600
|
-
}),
|
|
2837
|
+
}), di = [
|
|
2601
2838
|
"pgeagertest_",
|
|
2602
2839
|
"mig_e2e_",
|
|
2603
2840
|
"mig_test_",
|
|
2604
2841
|
"mysqleagertest_",
|
|
2605
2842
|
"mssqleagertest_",
|
|
2606
2843
|
"sqliteeagertest_"
|
|
2607
|
-
],
|
|
2844
|
+
], fi = (e) => di.some((t) => e.startsWith(t)), pi = (e, t) => b.gen(function* () {
|
|
2608
2845
|
if (process.env.VOLTRO_TEST_FIXTURE_GC === "off") return [];
|
|
2609
|
-
let n = t.filter((e) =>
|
|
2846
|
+
let n = t.filter((e) => fi(e.name)).map((e) => e.name);
|
|
2610
2847
|
if (n.length === 0) return [];
|
|
2611
|
-
for (let t of n) yield* e.unsafe(`DROP TABLE IF EXISTS "${t}" CASCADE`).pipe(
|
|
2848
|
+
for (let t of n) yield* e.unsafe(`DROP TABLE IF EXISTS "${t}" CASCADE`).pipe(b.catchAll(() => b.void));
|
|
2612
2849
|
return n;
|
|
2613
|
-
}),
|
|
2850
|
+
}), mi = () => {
|
|
2614
2851
|
let e = (process.env.VOLTRO_DB_IGNORE_TABLES ?? "").split(",").map((e) => e.trim()).filter((e) => e.length > 0);
|
|
2615
2852
|
return e.length > 0 ? { ignoreTables: e } : {};
|
|
2616
|
-
},
|
|
2853
|
+
}, hi = (e) => {
|
|
2617
2854
|
if (e === void 0) return;
|
|
2618
2855
|
let t = e.trim();
|
|
2619
2856
|
if (t === "") return;
|
|
@@ -2623,83 +2860,87 @@ BEGIN
|
|
|
2623
2860
|
kind: "tables",
|
|
2624
2861
|
tables: new Set(n)
|
|
2625
2862
|
} : void 0;
|
|
2626
|
-
},
|
|
2863
|
+
}, gi = (e) => typeof e.table == "string" ? e.table : void 0, _i = (e, t = { kind: "all" }) => ({
|
|
2627
2864
|
...e,
|
|
2628
2865
|
operations: e.operations.map((e) => {
|
|
2629
2866
|
if (!e.blocked || e.classification !== "lossy") return e;
|
|
2630
2867
|
if (t?.kind === "tables") {
|
|
2631
|
-
let n =
|
|
2868
|
+
let n = gi(e.op);
|
|
2632
2869
|
if (n === void 0 || !t.tables.has(n)) return e;
|
|
2633
2870
|
}
|
|
2634
2871
|
let { blocked: n, ...r } = e;
|
|
2635
2872
|
return r;
|
|
2636
2873
|
})
|
|
2637
|
-
}),
|
|
2638
|
-
let t =
|
|
2639
|
-
return t === void 0 ?
|
|
2640
|
-
}),
|
|
2641
|
-
let r =
|
|
2874
|
+
}), vi = (e, t, n) => t === "postgres" ? St(e, n).pipe(b.flatMap((e) => {
|
|
2875
|
+
let t = Ct(e);
|
|
2876
|
+
return t === void 0 ? b.void : b.logWarning(`auto-migrate: ${t}`).pipe(b.annotateLogs({ scope: "voltro:migrate" }));
|
|
2877
|
+
}), b.catchAll(() => b.void)) : b.void, yi = (e, t, n) => b.gen(function* () {
|
|
2878
|
+
let r = an(e), i = t;
|
|
2642
2879
|
if (process.env.VOLTRO_MIGRATE_FORCE !== "1") {
|
|
2643
|
-
let t = P(
|
|
2644
|
-
if ((yield*
|
|
2880
|
+
let t = M(P(i, r));
|
|
2881
|
+
if ((yield* ci(e)) === t) return yield* vi(e, r, i), {
|
|
2645
2882
|
kind: "up-to-date",
|
|
2646
2883
|
fingerprint: t
|
|
2647
2884
|
};
|
|
2648
2885
|
}
|
|
2649
|
-
let a = yield*
|
|
2650
|
-
o.length > 0 && (yield*
|
|
2886
|
+
let a = yield* Ln(e), o = yield* pi(e, a.tables);
|
|
2887
|
+
o.length > 0 && (yield* b.logInfo(`auto-migrate: reaped ${o.length} test-fixture leftover(s)`).pipe(b.annotateLogs({
|
|
2651
2888
|
scope: "voltro:migrate",
|
|
2652
2889
|
count: o.length
|
|
2653
|
-
})), a = yield*
|
|
2654
|
-
let
|
|
2890
|
+
})), a = yield* Ln(e));
|
|
2891
|
+
let s = new Set(t.map((e) => e.tableName)), c = (e) => e.filter((e) => !m(e.name) || s.has(e.name)), l = rn({
|
|
2655
2892
|
declared: i,
|
|
2656
|
-
live: { tables:
|
|
2893
|
+
live: { tables: c(a.tables) },
|
|
2657
2894
|
dialect: r,
|
|
2658
|
-
...
|
|
2895
|
+
...mi()
|
|
2659
2896
|
});
|
|
2660
|
-
if (
|
|
2661
|
-
let e =
|
|
2662
|
-
if (t === void 0 || !e || (
|
|
2897
|
+
if (l.summary.blocked > 0) {
|
|
2898
|
+
let e = l.operations.filter((e) => e.blocked).every((e) => e.classification === "lossy"), t = hi(process.env.VOLTRO_DESTRUCTIVE_OK);
|
|
2899
|
+
if (t === void 0 || !e || (l = _i(l, t), l.operations.some((e) => e.blocked))) return {
|
|
2663
2900
|
kind: "refused-blocked",
|
|
2664
|
-
plan:
|
|
2901
|
+
plan: l
|
|
2665
2902
|
};
|
|
2666
2903
|
}
|
|
2667
|
-
return
|
|
2904
|
+
return l.operations.length === 0 ? (yield* li(e, l, {
|
|
2905
|
+
appliedBy: n.appliedBy,
|
|
2906
|
+
environment: n.environment === "prod" ? "dev" : n.environment,
|
|
2907
|
+
source: "auto-diff"
|
|
2908
|
+
}), {
|
|
2668
2909
|
kind: "up-to-date",
|
|
2669
|
-
fingerprint:
|
|
2670
|
-
} : {
|
|
2910
|
+
fingerprint: l.toFingerprint
|
|
2911
|
+
}) : {
|
|
2671
2912
|
kind: "applied",
|
|
2672
|
-
applied: yield*
|
|
2913
|
+
applied: yield* oi(e, l, {
|
|
2673
2914
|
declared: t,
|
|
2674
2915
|
appliedBy: n.appliedBy,
|
|
2675
2916
|
environment: n.environment === "prod" ? "dev" : n.environment,
|
|
2676
2917
|
source: "auto-diff",
|
|
2677
|
-
replan: (e) =>
|
|
2678
|
-
let t = yield*
|
|
2679
|
-
return
|
|
2918
|
+
replan: (e) => b.gen(function* () {
|
|
2919
|
+
let t = yield* Ln(e);
|
|
2920
|
+
return rn({
|
|
2680
2921
|
declared: i,
|
|
2681
|
-
live: { tables:
|
|
2922
|
+
live: { tables: c(t.tables) },
|
|
2682
2923
|
dialect: r,
|
|
2683
|
-
...
|
|
2924
|
+
...mi()
|
|
2684
2925
|
});
|
|
2685
2926
|
})
|
|
2686
2927
|
}),
|
|
2687
|
-
plan:
|
|
2928
|
+
plan: l
|
|
2688
2929
|
};
|
|
2689
|
-
}),
|
|
2930
|
+
}), bi = (e, t, n) => process.env.VOLTRO_AUTO_MIGRATE === "0" ? b.succeed({
|
|
2690
2931
|
kind: "skipped",
|
|
2691
2932
|
reason: "VOLTRO_AUTO_MIGRATE=0"
|
|
2692
|
-
}) : n.environment === "prod" ?
|
|
2933
|
+
}) : n.environment === "prod" ? ui(e, t) : yi(e, t, n), xi = (e, t) => {
|
|
2693
2934
|
switch (e.kind) {
|
|
2694
2935
|
case "skipped": return `auto-migrate: skipped (${e.reason})`;
|
|
2695
|
-
case "up-to-date": return `auto-migrate: schema up to date (${t}, fingerprint=${
|
|
2936
|
+
case "up-to-date": return `auto-migrate: schema up to date (${t}, fingerprint=${N(e.fingerprint)})`;
|
|
2696
2937
|
case "applied": {
|
|
2697
2938
|
let t = e.plan.summary;
|
|
2698
|
-
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=${
|
|
2939
|
+
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=${N(e.applied.fingerprint)}`;
|
|
2699
2940
|
}
|
|
2700
|
-
case "prod-mismatch": return `auto-migrate: SCHEMA FINGERPRINT MISMATCH — declared=${
|
|
2941
|
+
case "prod-mismatch": return `auto-migrate: SCHEMA FINGERPRINT MISMATCH — declared=${N(e.expected)}` + (e.actual ? ` live=${N(e.actual)}` : " live=<no _voltro_migration_plans row>") + ". Run `voltro db apply --plan plan.json` from the deploy pipeline before serving.";
|
|
2701
2942
|
case "refused-blocked": {
|
|
2702
|
-
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` +
|
|
2943
|
+
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` + si(e.plan), c = [""];
|
|
2703
2944
|
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) {
|
|
2704
2945
|
let t = /* @__PURE__ */ new Map(), n = /* @__PURE__ */ new Map();
|
|
2705
2946
|
for (let r of e.plan.operations) {
|
|
@@ -2716,7 +2957,7 @@ BEGIN
|
|
|
2716
2957
|
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");
|
|
2717
2958
|
}
|
|
2718
2959
|
}
|
|
2719
|
-
},
|
|
2960
|
+
}, Si = /* @__PURE__ */ new Set([
|
|
2720
2961
|
"node_modules",
|
|
2721
2962
|
"dist",
|
|
2722
2963
|
"build",
|
|
@@ -2725,24 +2966,24 @@ BEGIN
|
|
|
2725
2966
|
".next",
|
|
2726
2967
|
".git",
|
|
2727
2968
|
".framework"
|
|
2728
|
-
]),
|
|
2969
|
+
]), Ci = async (e) => {
|
|
2729
2970
|
let t = [], n = async (e) => {
|
|
2730
|
-
let r = await
|
|
2971
|
+
let r = await ee.readdir(e, { withFileTypes: !0 }).catch(() => []);
|
|
2731
2972
|
for (let i of r) {
|
|
2732
|
-
if (
|
|
2733
|
-
let r =
|
|
2734
|
-
i.isDirectory() ? await n(r) : i.isFile() &&
|
|
2973
|
+
if (Si.has(i.name)) continue;
|
|
2974
|
+
let r = te(e, i.name);
|
|
2975
|
+
i.isDirectory() ? await n(r) : i.isFile() && u.test(i.name) && t.push(r);
|
|
2735
2976
|
}
|
|
2736
2977
|
};
|
|
2737
2978
|
return await n(e), t.sort((e, t) => {
|
|
2738
2979
|
let n = e.split("/").pop() ?? e, r = t.split("/").pop() ?? t;
|
|
2739
2980
|
return n.localeCompare(r);
|
|
2740
2981
|
});
|
|
2741
|
-
},
|
|
2982
|
+
}, wi = async (e) => {
|
|
2742
2983
|
let t = [];
|
|
2743
2984
|
for (let n of e) {
|
|
2744
|
-
let e = (await import(
|
|
2745
|
-
if (!
|
|
2985
|
+
let e = (await import(ne(n).href)).default;
|
|
2986
|
+
if (!o(e)) {
|
|
2746
2987
|
process.stderr.write(`[voltro:migrate] ${n} has no default-exported \`migration(...)\` — skipped\n`);
|
|
2747
2988
|
continue;
|
|
2748
2989
|
}
|
|
@@ -2752,11 +2993,11 @@ BEGIN
|
|
|
2752
2993
|
});
|
|
2753
2994
|
}
|
|
2754
2995
|
return t;
|
|
2755
|
-
},
|
|
2996
|
+
}, Ti = (e) => e`
|
|
2756
2997
|
SELECT ${e("id")}
|
|
2757
2998
|
FROM ${e("_voltro_migration_plans")}
|
|
2758
2999
|
WHERE ${e("source")} = 'file'
|
|
2759
|
-
`.pipe(
|
|
3000
|
+
`.pipe(b.map((e) => e.map((e) => e.id)), b.catchAll(() => b.succeed([]))), Ei = class extends Error {
|
|
2760
3001
|
migrationId;
|
|
2761
3002
|
file;
|
|
2762
3003
|
cause;
|
|
@@ -2764,28 +3005,28 @@ BEGIN
|
|
|
2764
3005
|
constructor(e, t, n) {
|
|
2765
3006
|
super(`migration ${e}: up() SUCCEEDED but the ledger row could not be written.\n The change IS in the database. It is NOT recorded, so the next run would apply it AGAIN.\n Do not re-run ${t} until this is resolved.\n Cause: ${n instanceof Error ? n.message : String(n)}`), this.migrationId = e, this.file = t, this.cause = n, this.name = "FileMigrationLedgerError";
|
|
2766
3007
|
}
|
|
2767
|
-
},
|
|
3008
|
+
}, Di = (e, t, n, r) => b.gen(function* () {
|
|
2768
3009
|
let i = Date.now(), a = (/* @__PURE__ */ new Date()).toISOString(), o = {
|
|
2769
3010
|
sql: e,
|
|
2770
3011
|
log: {
|
|
2771
|
-
info: (e, n) =>
|
|
3012
|
+
info: (e, n) => b.logInfo(e).pipe(b.annotateLogs({
|
|
2772
3013
|
scope: `migrations:file:${t.migration.id}`,
|
|
2773
3014
|
...n ?? {}
|
|
2774
3015
|
})),
|
|
2775
|
-
warn: (e, n) =>
|
|
3016
|
+
warn: (e, n) => b.logWarning(e).pipe(b.annotateLogs({
|
|
2776
3017
|
scope: `migrations:file:${t.migration.id}`,
|
|
2777
3018
|
...n ?? {}
|
|
2778
3019
|
}))
|
|
2779
3020
|
},
|
|
2780
3021
|
appliedAt: a
|
|
2781
3022
|
}, s = t.migration.up(o);
|
|
2782
|
-
|
|
3023
|
+
b.isEffect(s) ? yield* s : yield* b.tryPromise({
|
|
2783
3024
|
try: () => s,
|
|
2784
3025
|
catch: (e) => e
|
|
2785
3026
|
});
|
|
2786
3027
|
let c = Date.now() - i;
|
|
2787
|
-
!
|
|
2788
|
-
let l = yield*
|
|
3028
|
+
!b.isEffect(s) && c < 2 && (yield* b.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(b.annotateLogs({ scope: `migrations:file:${t.migration.id}` })));
|
|
3029
|
+
let l = yield* H(e, a);
|
|
2789
3030
|
return yield* e`
|
|
2790
3031
|
INSERT INTO ${e("_voltro_migration_plans")}
|
|
2791
3032
|
(${e("id")}, ${e("fingerprint")}, ${e("operations")},
|
|
@@ -2798,94 +3039,94 @@ BEGIN
|
|
|
2798
3039
|
}])},
|
|
2799
3040
|
${r}, ${n}, ${"file"},
|
|
2800
3041
|
${c}, ${l}, ${t.migration.description})
|
|
2801
|
-
`.pipe(
|
|
2802
|
-
}),
|
|
2803
|
-
let n = yield*
|
|
2804
|
-
try: () =>
|
|
3042
|
+
`.pipe(b.mapError((e) => new Ei(t.migration.id, t.file, e))), { durationMs: c };
|
|
3043
|
+
}), Oi = (e, t) => b.gen(function* () {
|
|
3044
|
+
let n = yield* b.tryPromise({
|
|
3045
|
+
try: () => Ci(te(t, "migrations")),
|
|
2805
3046
|
catch: (e) => e
|
|
2806
3047
|
});
|
|
2807
3048
|
if (n.length === 0) return {
|
|
2808
3049
|
pending: [],
|
|
2809
3050
|
skipped: []
|
|
2810
3051
|
};
|
|
2811
|
-
let r = yield*
|
|
2812
|
-
try: () =>
|
|
3052
|
+
let r = yield* b.tryPromise({
|
|
3053
|
+
try: () => wi(n),
|
|
2813
3054
|
catch: (e) => e
|
|
2814
3055
|
});
|
|
2815
3056
|
if (r.length === 0) return {
|
|
2816
3057
|
pending: [],
|
|
2817
3058
|
skipped: []
|
|
2818
3059
|
};
|
|
2819
|
-
let i = new Set(yield*
|
|
3060
|
+
let i = new Set(yield* Ti(e));
|
|
2820
3061
|
return {
|
|
2821
3062
|
pending: r.filter((e) => !i.has(e.migration.id)),
|
|
2822
3063
|
skipped: r.filter((e) => i.has(e.migration.id)).map((e) => e.migration.id)
|
|
2823
3064
|
};
|
|
2824
|
-
}),
|
|
2825
|
-
let { pending: n, skipped: r } = yield*
|
|
3065
|
+
}), ki = (e, t) => Oi(e, t).pipe(b.map((e) => e.pending.map((e) => e.migration.id))), Ai = (e, t) => b.gen(function* () {
|
|
3066
|
+
let { pending: n, skipped: r } = yield* Oi(e, t.projectRoot);
|
|
2826
3067
|
if (n.length === 0) return {
|
|
2827
3068
|
applied: [],
|
|
2828
3069
|
skipped: r
|
|
2829
3070
|
};
|
|
2830
|
-
yield*
|
|
3071
|
+
yield* T(e);
|
|
2831
3072
|
let i = [];
|
|
2832
3073
|
try {
|
|
2833
3074
|
for (let r of n) {
|
|
2834
|
-
yield*
|
|
3075
|
+
yield* b.logInfo("file-based migration applying").pipe(b.annotateLogs({
|
|
2835
3076
|
scope: "voltro:migrate",
|
|
2836
3077
|
id: r.migration.id,
|
|
2837
3078
|
file: r.file
|
|
2838
3079
|
}));
|
|
2839
|
-
let { durationMs: n } = yield*
|
|
3080
|
+
let { durationMs: n } = yield* Di(e, r, t.env, t.appliedBy);
|
|
2840
3081
|
i.push({
|
|
2841
3082
|
id: r.migration.id,
|
|
2842
3083
|
durationMs: n
|
|
2843
|
-
}), yield*
|
|
3084
|
+
}), yield* b.logInfo("file-based migration applied").pipe(b.annotateLogs({
|
|
2844
3085
|
scope: "voltro:migrate",
|
|
2845
3086
|
id: r.migration.id,
|
|
2846
3087
|
durationMs: n
|
|
2847
3088
|
}));
|
|
2848
3089
|
}
|
|
2849
3090
|
} finally {
|
|
2850
|
-
yield*
|
|
3091
|
+
yield* E(e).pipe(b.orDie);
|
|
2851
3092
|
}
|
|
2852
3093
|
return {
|
|
2853
3094
|
applied: i,
|
|
2854
3095
|
skipped: r
|
|
2855
3096
|
};
|
|
2856
|
-
}),
|
|
3097
|
+
}), ji = (e, t) => e`
|
|
2857
3098
|
SELECT ${e("id")}
|
|
2858
3099
|
FROM ${e("_voltro_migration_plans")}
|
|
2859
3100
|
WHERE ${e("id")} = ${t} AND ${e("source")} = 'file'
|
|
2860
3101
|
LIMIT 1
|
|
2861
|
-
`,
|
|
2862
|
-
if (!(yield*
|
|
2863
|
-
let n = yield*
|
|
2864
|
-
try: () =>
|
|
3102
|
+
`, Mi = (e, t) => b.gen(function* () {
|
|
3103
|
+
if (!(yield* ji(e, t.id))[0]) return yield* b.fail(/* @__PURE__ */ Error(`rollback: ${t.id} not found in _voltro_migration_plans (file source)`));
|
|
3104
|
+
let n = yield* b.tryPromise({
|
|
3105
|
+
try: () => Ci(te(t.projectRoot, "migrations")),
|
|
2865
3106
|
catch: (e) => e
|
|
2866
|
-
}), r = (yield*
|
|
2867
|
-
try: () =>
|
|
3107
|
+
}), r = (yield* b.tryPromise({
|
|
3108
|
+
try: () => wi(n),
|
|
2868
3109
|
catch: (e) => e
|
|
2869
3110
|
})).find((e) => e.migration.id === t.id);
|
|
2870
|
-
if (!r) return yield*
|
|
2871
|
-
yield*
|
|
3111
|
+
if (!r) return yield* b.fail(/* @__PURE__ */ Error(`rollback: file for migration ${t.id} not found on disk under ${t.projectRoot}/migrations/`));
|
|
3112
|
+
yield* T(e);
|
|
2872
3113
|
let i = Date.now();
|
|
2873
3114
|
try {
|
|
2874
3115
|
let n = {
|
|
2875
3116
|
sql: e,
|
|
2876
3117
|
log: {
|
|
2877
|
-
info: (e, n) =>
|
|
3118
|
+
info: (e, n) => b.logInfo(e).pipe(b.annotateLogs({
|
|
2878
3119
|
scope: `migrations:file:${t.id}`,
|
|
2879
3120
|
...n ?? {}
|
|
2880
3121
|
})),
|
|
2881
|
-
warn: (e, n) =>
|
|
3122
|
+
warn: (e, n) => b.logWarning(e).pipe(b.annotateLogs({
|
|
2882
3123
|
scope: `migrations:file:${t.id}`,
|
|
2883
3124
|
...n ?? {}
|
|
2884
3125
|
}))
|
|
2885
3126
|
},
|
|
2886
3127
|
appliedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2887
3128
|
}, a = r.migration.down(n);
|
|
2888
|
-
|
|
3129
|
+
b.isEffect(a) ? yield* a : yield* b.tryPromise({
|
|
2889
3130
|
try: () => a,
|
|
2890
3131
|
catch: (e) => e
|
|
2891
3132
|
});
|
|
@@ -2895,19 +3136,19 @@ BEGIN
|
|
|
2895
3136
|
durationMs: o
|
|
2896
3137
|
};
|
|
2897
3138
|
} finally {
|
|
2898
|
-
yield*
|
|
3139
|
+
yield* E(e).pipe(b.orDie);
|
|
2899
3140
|
}
|
|
2900
|
-
}),
|
|
2901
|
-
let t = yield*
|
|
3141
|
+
}), Ni = (e) => b.gen(function* () {
|
|
3142
|
+
let t = yield* S.SqlClient, n = yield* t`
|
|
2902
3143
|
SELECT id, fingerprint, appliedAt FROM _voltro_migration_plans
|
|
2903
3144
|
WHERE source = 'auto-diff'
|
|
2904
3145
|
AND squashedAt IS NULL
|
|
2905
3146
|
AND appliedAt < ${e.before}
|
|
2906
3147
|
ORDER BY appliedAt DESC
|
|
2907
3148
|
`;
|
|
2908
|
-
if (n.length === 0) return yield*
|
|
3149
|
+
if (n.length === 0) return yield* b.fail(/* @__PURE__ */ Error(`squash: no rows applied before ${e.before}`));
|
|
2909
3150
|
let r = n[0];
|
|
2910
|
-
if (r.fingerprint !== e.fingerprint) return yield*
|
|
3151
|
+
if (r.fingerprint !== e.fingerprint) return yield* b.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.`));
|
|
2911
3152
|
yield* t`
|
|
2912
3153
|
UPDATE _voltro_migration_plans
|
|
2913
3154
|
SET squashedAt = CURRENT_TIMESTAMP
|
|
@@ -2942,7 +3183,7 @@ BEGIN
|
|
|
2942
3183
|
snapshotFingerprint: e.fingerprint,
|
|
2943
3184
|
snapshotId: i
|
|
2944
3185
|
};
|
|
2945
|
-
}),
|
|
3186
|
+
}), Pi = { safe: !0 }, Fi = (e) => {
|
|
2946
3187
|
switch (e.kind) {
|
|
2947
3188
|
case "drop-column": return {
|
|
2948
3189
|
safe: !1,
|
|
@@ -2969,7 +3210,7 @@ BEGIN
|
|
|
2969
3210
|
reason: `old instances read/write "${e.table}"."${e.column}" as ${e.from}; the new ${e.to} type can reject their writes or fail to decode their reads`,
|
|
2970
3211
|
remedy: "add a new column of the target type + backfill + dual-write, cut code over, then drop the old column in a LATER deploy"
|
|
2971
3212
|
};
|
|
2972
|
-
case "alter-column-nullability": return e.toNullable ?
|
|
3213
|
+
case "alter-column-nullability": return e.toNullable ? Pi : {
|
|
2973
3214
|
safe: !1,
|
|
2974
3215
|
reason: `old instances may INSERT "${e.table}" without (or with NULL in) "${e.column}", which the new NOT NULL rejects`,
|
|
2975
3216
|
remedy: `make code always write "${e.column}" and deploy that first; add NOT NULL in a LATER deploy (with a default to cover the gap)`
|
|
@@ -2999,12 +3240,12 @@ BEGIN
|
|
|
2999
3240
|
case "drop-unique":
|
|
3000
3241
|
case "drop-unique-composite":
|
|
3001
3242
|
case "drop-foreign-key":
|
|
3002
|
-
case "drop-check": return
|
|
3243
|
+
case "drop-check": return Pi;
|
|
3003
3244
|
}
|
|
3004
|
-
},
|
|
3245
|
+
}, Ii = (e) => {
|
|
3005
3246
|
let t = [];
|
|
3006
3247
|
for (let n of e) {
|
|
3007
|
-
let e =
|
|
3248
|
+
let e = Fi(n.op);
|
|
3008
3249
|
e.safe || t.push({
|
|
3009
3250
|
op: n,
|
|
3010
3251
|
reason: e.reason,
|
|
@@ -3012,7 +3253,7 @@ BEGIN
|
|
|
3012
3253
|
});
|
|
3013
3254
|
}
|
|
3014
3255
|
return t;
|
|
3015
|
-
},
|
|
3256
|
+
}, Li = (e) => {
|
|
3016
3257
|
if (!e.enabled || e.unsafeCount === 0) return {
|
|
3017
3258
|
refuse: !1,
|
|
3018
3259
|
warnForced: !1,
|
|
@@ -3028,10 +3269,127 @@ BEGIN
|
|
|
3028
3269
|
warnForced: !1,
|
|
3029
3270
|
message: `${t}. Old instances would break against the new schema mid-rollout. Split into expand → deploy → contract (see the ⚠ list above), deploy with no overlap (maintenance window / scale-to-zero), or pass --force to override.`
|
|
3030
3271
|
};
|
|
3031
|
-
},
|
|
3272
|
+
}, Ri = (e) => {
|
|
3273
|
+
let t = e.table;
|
|
3274
|
+
if (typeof t == "string") return t;
|
|
3275
|
+
let n = e.from;
|
|
3276
|
+
return typeof n == "string" ? n : void 0;
|
|
3277
|
+
}, zi = (e) => ({
|
|
3278
|
+
kind: e.op.kind,
|
|
3279
|
+
table: Ri(e.op),
|
|
3280
|
+
classification: e.classification,
|
|
3281
|
+
reason: e.reason,
|
|
3282
|
+
blockedFix: e.blocked?.fix
|
|
3283
|
+
}), Bi = (e) => e.operations.map(zi), Vi = async (e) => {
|
|
3284
|
+
let t = e.log ?? (() => {}), n = {
|
|
3285
|
+
branchId: e.branchId,
|
|
3286
|
+
mechanism: e.mechanism,
|
|
3287
|
+
branchedTables: e.branchedTables,
|
|
3288
|
+
replayedForeignKeys: e.replayedForeignKeys
|
|
3289
|
+
}, r = [], i = !1, a;
|
|
3290
|
+
try {
|
|
3291
|
+
await e.provision(), i = !0, t(`branch ${e.branchId} provisioned (${e.mechanism}, ${e.branchedTables} table(s), ${e.replayedForeignKeys} foreign key(s))`);
|
|
3292
|
+
let o = await e.fidelityPlan();
|
|
3293
|
+
if (o.operations.length > 0) a = {
|
|
3294
|
+
...n,
|
|
3295
|
+
outcome: "infidelity",
|
|
3296
|
+
operations: r,
|
|
3297
|
+
lossy: r,
|
|
3298
|
+
blocked: r,
|
|
3299
|
+
applied: !1,
|
|
3300
|
+
converged: !1,
|
|
3301
|
+
residual: r,
|
|
3302
|
+
infidelity: Bi(o)
|
|
3303
|
+
};
|
|
3304
|
+
else {
|
|
3305
|
+
let i = await e.plan(), o = Bi(i), s = o.filter((e) => e.classification === "lossy"), c = _i(i), l = c.operations.filter((e) => e.blocked !== void 0).map(zi);
|
|
3306
|
+
if (l.length > 0) a = {
|
|
3307
|
+
...n,
|
|
3308
|
+
outcome: "blocked",
|
|
3309
|
+
operations: o,
|
|
3310
|
+
lossy: s,
|
|
3311
|
+
blocked: l,
|
|
3312
|
+
applied: !1,
|
|
3313
|
+
converged: !1,
|
|
3314
|
+
residual: r,
|
|
3315
|
+
infidelity: r
|
|
3316
|
+
};
|
|
3317
|
+
else if (o.length === 0) a = {
|
|
3318
|
+
...n,
|
|
3319
|
+
outcome: "clean",
|
|
3320
|
+
operations: o,
|
|
3321
|
+
lossy: s,
|
|
3322
|
+
blocked: l,
|
|
3323
|
+
applied: !1,
|
|
3324
|
+
converged: !0,
|
|
3325
|
+
residual: r,
|
|
3326
|
+
infidelity: r
|
|
3327
|
+
};
|
|
3328
|
+
else {
|
|
3329
|
+
await e.apply(c), t(`branch ${e.branchId}: applied ${o.length} operation(s)`);
|
|
3330
|
+
let i = Bi(await e.replan());
|
|
3331
|
+
a = {
|
|
3332
|
+
...n,
|
|
3333
|
+
outcome: i.length > 0 ? "diverged" : s.length > 0 ? "lossy" : "clean",
|
|
3334
|
+
operations: o,
|
|
3335
|
+
lossy: s,
|
|
3336
|
+
blocked: l,
|
|
3337
|
+
applied: !0,
|
|
3338
|
+
converged: i.length === 0,
|
|
3339
|
+
residual: i,
|
|
3340
|
+
infidelity: r
|
|
3341
|
+
};
|
|
3342
|
+
}
|
|
3343
|
+
}
|
|
3344
|
+
} catch (e) {
|
|
3345
|
+
a = {
|
|
3346
|
+
...n,
|
|
3347
|
+
outcome: "failed",
|
|
3348
|
+
operations: r,
|
|
3349
|
+
lossy: r,
|
|
3350
|
+
blocked: r,
|
|
3351
|
+
applied: !1,
|
|
3352
|
+
converged: !1,
|
|
3353
|
+
residual: r,
|
|
3354
|
+
infidelity: r,
|
|
3355
|
+
error: e instanceof Error ? e.message : String(e)
|
|
3356
|
+
};
|
|
3357
|
+
}
|
|
3358
|
+
let o = !1;
|
|
3359
|
+
if (i && e.keep !== !0) try {
|
|
3360
|
+
await e.teardown(), o = !0;
|
|
3361
|
+
} catch (n) {
|
|
3362
|
+
t(`branch ${e.branchId}: TEARDOWN FAILED — drop it by hand: ${n instanceof Error ? n.message : String(n)}`);
|
|
3363
|
+
}
|
|
3364
|
+
return {
|
|
3365
|
+
...a,
|
|
3366
|
+
tornDown: o
|
|
3367
|
+
};
|
|
3368
|
+
}, Hi = (e) => {
|
|
3369
|
+
switch (e.outcome) {
|
|
3370
|
+
case "clean": return 0;
|
|
3371
|
+
case "lossy": return 2;
|
|
3372
|
+
case "blocked": return 2;
|
|
3373
|
+
case "diverged": return 1;
|
|
3374
|
+
case "infidelity": return 1;
|
|
3375
|
+
case "failed": return 1;
|
|
3376
|
+
}
|
|
3377
|
+
}, Ui = (e) => ` ${e.classification === "lossy" ? "✗" : "•"} ${e.kind}${e.table ? ` ${e.table}` : ""} [${e.classification}]${e.reason ? ` — ${e.reason}` : ""}${e.blockedFix ? `\n ! ${e.blockedFix}` : ""}`, Wi = (e) => {
|
|
3378
|
+
let t = [];
|
|
3379
|
+
if (t.push(`branch rehearsal · ${e.branchId} · mechanism ${e.mechanism}`), t.push(` branched ${e.branchedTables} table(s), replayed ${e.replayedForeignKeys} foreign key(s)`), e.outcome === "failed") t.push(` FAILED: ${e.error ?? "unknown error"}`);
|
|
3380
|
+
else if (e.outcome === "infidelity") {
|
|
3381
|
+
t.push(" BRANCH IS NOT A FAITHFUL COPY of the parent — the rehearsal proves nothing about your migration."), t.push(" The parent's own schema still differs from the branch by:");
|
|
3382
|
+
for (let n of e.infidelity) t.push(Ui(n));
|
|
3383
|
+
} else {
|
|
3384
|
+
t.push(` plan: ${e.operations.length} operation(s), ${e.lossy.length} lossy, ${e.blocked.length} refused`);
|
|
3385
|
+
for (let n of e.operations) t.push(Ui(n));
|
|
3386
|
+
if (e.lossy.length > 0 && (t.push(""), t.push(` ⚠ ${e.lossy.length} operation(s) DESTROY DATA. They were executed on the branch (it is`), t.push(" disposable) so they are rehearsed, but production refuses them until you set"), t.push(" VOLTRO_DESTRUCTIVE_OK — naming the tables, not `1`.")), e.blocked.length > 0 && (t.push(""), t.push(` ${e.blocked.length} operation(s) the planner will not auto-apply anywhere — the plan was NOT executed.`)), e.applied && (t.push(""), t.push(e.converged ? " ✓ applied on the branch, and the re-plan is EMPTY (the migration converges)." : ` ✗ applied, but the re-plan STILL proposes ${e.residual.length} operation(s) — this migration does not converge:`), !e.converged)) for (let n of e.residual) t.push(Ui(n));
|
|
3387
|
+
}
|
|
3388
|
+
return t.push(e.tornDown ? ` branch ${e.branchId} torn down.` : ` branch ${e.branchId} KEPT — drop it when you are done.`), t.join("\n");
|
|
3389
|
+
}, Gi = (e, ...t) => ({
|
|
3032
3390
|
_tag: "RawSqlFragment",
|
|
3033
3391
|
strings: [...e],
|
|
3034
3392
|
values: [...t]
|
|
3035
|
-
}),
|
|
3393
|
+
}), Ki = (e) => typeof e == "object" && !!e && e._tag === "RawSqlFragment";
|
|
3036
3394
|
//#endregion
|
|
3037
|
-
export {
|
|
3395
|
+
export { D as DEFAULT_CDC_CHANNEL, u as FILE_MIGRATION_PATTERN, Ei as FileMigrationLedgerError, Te as REACTIVE_TRIGGER_PREFIX, ue as VOLTRO_MIGRATION_LOCK_KEY, T as acquireMigrationLock, ut as applyNamespacedSchema, oi as applyPlan, it as applySchema, Li as assessRollingDeployGate, Hi as branchRehearsalExitCode, Tn as chunkTables, Fi as classifyRollingDeploySafety, P as declaredSnapshot, je as defaultArrayClause, Ae as defaultClause, A as defaultJsonClause, xi as describeOutcome, hi as destructiveScope, St as detectReactiveTriggerDrift, wr as emitAddIndexMysql, or as emitCreateTableMysql, Un as emitDropColumnDdl, Wn as emitDropColumnDdlMysql, rt as emitFrameworkBootstrapSql, tt as emitNamespaceProvisionDdl, nt as emitNamespacedSchemaSql, et as emitSchemaSql, M as fingerprintSchema, Wi as formatBranchRehearsal, Ct as formatReactiveTriggerDrift, mi as ignoreTablesFromEnv, Ln as introspectSchema, o as isFileMigration, Ki as isRawSqlFragment, bn as mapPgType, h as migration, br as mysqlIndexPrefixFor, j as numericIdSql, yn as parseEnumCheck, ki as pendingFileMigrationIds, rn as planMigrations, dt as provisionTenantNamespace, Ge as reactiveTriggerRepairSql, li as recordUpToDate, Vi as rehearseMigrationOnBranch, zi as rehearsedOperation, E as releaseMigrationLock, ar as renderColumnMysql, er as renderColumnPg, Mi as rollbackFileBasedMigration, Ii as rollingDeployUnsafeOps, Ai as runFileBasedMigrations, ft as runFrameworkBootstrap, lt as runMigrate, bi as runPlannedMigrations, N as shortFingerprint, vt as snapshotColumn, Gi as sql, k as sqlType, Ni as squashMigrationPlans, _i as unblockLossy, we as withMigrationLock };
|