@voltro/database 0.33.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 +1801 -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 +1269 -912
- package/package.json +2 -2
package/dist/sql.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Effect as
|
|
3
|
-
import { createLogger as
|
|
4
|
-
import { SqlClient as
|
|
5
|
-
import { createHash as
|
|
6
|
-
import { promises as
|
|
7
|
-
import { join as
|
|
8
|
-
import { pathToFileURL as
|
|
1
|
+
import { 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,87 +1044,87 @@ 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
|
-
return JSON.stringify(
|
|
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
|
-
},
|
|
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) => {
|
|
1094
1128
|
let n = [];
|
|
1095
1129
|
if (!t.references) return n;
|
|
1096
1130
|
let r = t.references.table, i = t.references.orphanPolicy ?? "fail", a = {
|
|
@@ -1102,15 +1136,15 @@ BEGIN
|
|
|
1102
1136
|
...t.references.onDelete ? { onDelete: t.references.onDelete } : {},
|
|
1103
1137
|
...t.references.onUpdate ? { onUpdate: t.references.onUpdate } : {}
|
|
1104
1138
|
};
|
|
1105
|
-
return i === "null" && !t.nullable ? n.push(
|
|
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({
|
|
1106
1140
|
...a,
|
|
1107
1141
|
orphanPolicy: i
|
|
1108
|
-
}, "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(
|
|
1109
|
-
},
|
|
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) => {
|
|
1110
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));
|
|
1111
1145
|
for (let [n, c] of Object.entries(t)) {
|
|
1112
1146
|
let t = c.renamedFrom;
|
|
1113
|
-
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({
|
|
1114
1148
|
kind: "rename-column",
|
|
1115
1149
|
table: e.tableName,
|
|
1116
1150
|
from: t,
|
|
@@ -1122,26 +1156,26 @@ BEGIN
|
|
|
1122
1156
|
consumedAddedNames: i,
|
|
1123
1157
|
consumedRemovedNames: a
|
|
1124
1158
|
};
|
|
1125
|
-
},
|
|
1159
|
+
}, Xt = /* @__PURE__ */ new Set([
|
|
1126
1160
|
"postgres",
|
|
1127
1161
|
"mysql",
|
|
1128
1162
|
"mariadb",
|
|
1129
1163
|
"mssql"
|
|
1130
|
-
]),
|
|
1164
|
+
]), Zt = {
|
|
1131
1165
|
ops: [],
|
|
1132
1166
|
consumedAddedNames: /* @__PURE__ */ new Set(),
|
|
1133
1167
|
consumedRemovedNames: /* @__PURE__ */ new Set()
|
|
1134
|
-
},
|
|
1135
|
-
if (i === void 0 || !
|
|
1136
|
-
let a = t.added.filter((e) =>
|
|
1137
|
-
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;
|
|
1138
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();
|
|
1139
1173
|
for (let n of t.removed) {
|
|
1140
|
-
if (!
|
|
1174
|
+
if (!Qt(n)) continue;
|
|
1141
1175
|
let t = o(n), r = a.filter((e) => !l.has(e.name) && s(e) === t);
|
|
1142
1176
|
if (r.length !== 1) continue;
|
|
1143
1177
|
let i = r[0];
|
|
1144
|
-
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({
|
|
1145
1179
|
kind: "rename-index",
|
|
1146
1180
|
table: e,
|
|
1147
1181
|
from: n.name,
|
|
@@ -1153,7 +1187,7 @@ BEGIN
|
|
|
1153
1187
|
consumedAddedNames: l,
|
|
1154
1188
|
consumedRemovedNames: u
|
|
1155
1189
|
};
|
|
1156
|
-
},
|
|
1190
|
+
}, en = (e, t, n) => {
|
|
1157
1191
|
let r = [], i = /* @__PURE__ */ new Map(), a = /* @__PURE__ */ new Set(), o = /* @__PURE__ */ new Map(), s = /* @__PURE__ */ new Map(), c = /* @__PURE__ */ new Set();
|
|
1158
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));
|
|
1159
1193
|
for (let c of e.tables) {
|
|
@@ -1164,7 +1198,7 @@ BEGIN
|
|
|
1164
1198
|
continue;
|
|
1165
1199
|
}
|
|
1166
1200
|
if (t.has(c.name)) {
|
|
1167
|
-
r.push(
|
|
1201
|
+
r.push(I({
|
|
1168
1202
|
kind: "rename-table",
|
|
1169
1203
|
from: e,
|
|
1170
1204
|
to: c.name
|
|
@@ -1172,14 +1206,14 @@ BEGIN
|
|
|
1172
1206
|
continue;
|
|
1173
1207
|
}
|
|
1174
1208
|
if ((s.get(e) ?? 0) !== 1) {
|
|
1175
|
-
r.push(
|
|
1209
|
+
r.push(I({
|
|
1176
1210
|
kind: "rename-table",
|
|
1177
1211
|
from: e,
|
|
1178
1212
|
to: c.name
|
|
1179
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.` }));
|
|
1180
1214
|
continue;
|
|
1181
1215
|
}
|
|
1182
|
-
i.set(c.name, e), a.add(e), r.push(
|
|
1216
|
+
i.set(c.name, e), a.add(e), r.push(I({
|
|
1183
1217
|
kind: "rename-table",
|
|
1184
1218
|
from: e,
|
|
1185
1219
|
to: c.name
|
|
@@ -1193,19 +1227,19 @@ BEGIN
|
|
|
1193
1227
|
claimedOldNames: c,
|
|
1194
1228
|
refusals: o
|
|
1195
1229
|
};
|
|
1196
|
-
},
|
|
1230
|
+
}, tn = (e, t, n, r) => {
|
|
1197
1231
|
if (t === void 0) return {
|
|
1198
1232
|
live: e,
|
|
1199
1233
|
ops: []
|
|
1200
1234
|
};
|
|
1201
|
-
let i = r !== void 0 &&
|
|
1235
|
+
let i = r !== void 0 && Xt.has(r), a = [], o = e.indexes.map((e) => {
|
|
1202
1236
|
if (!e.name.startsWith(t)) return e;
|
|
1203
1237
|
let r = `${n}${e.name.slice(t.length)}`;
|
|
1204
1238
|
return r === e.name ? e : e.name === `${t}_pkey` ? {
|
|
1205
1239
|
...e,
|
|
1206
1240
|
name: r,
|
|
1207
1241
|
table: n
|
|
1208
|
-
} : i ? (a.push(
|
|
1242
|
+
} : i ? (a.push(I({
|
|
1209
1243
|
kind: "rename-index",
|
|
1210
1244
|
table: n,
|
|
1211
1245
|
from: e.name,
|
|
@@ -1224,7 +1258,7 @@ BEGIN
|
|
|
1224
1258
|
},
|
|
1225
1259
|
ops: a
|
|
1226
1260
|
};
|
|
1227
|
-
},
|
|
1261
|
+
}, nn = (e) => {
|
|
1228
1262
|
let t = {
|
|
1229
1263
|
safe: 0,
|
|
1230
1264
|
needsDefault: 0,
|
|
@@ -1259,51 +1293,51 @@ BEGIN
|
|
|
1259
1293
|
break;
|
|
1260
1294
|
}
|
|
1261
1295
|
return t;
|
|
1262
|
-
},
|
|
1263
|
-
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);
|
|
1264
1298
|
a.push(...o.ops);
|
|
1265
1299
|
for (let e of t.tables) if (!o.renamedInto.has(e.name) && !r.has(e.name)) {
|
|
1266
|
-
a.push(
|
|
1300
|
+
a.push(Vt(e.name, e.columns, e.indexes, e.primaryKey, o.refusals.get(e.name)));
|
|
1267
1301
|
for (let t of e.columns) {
|
|
1268
|
-
let n =
|
|
1302
|
+
let n = Ft(e.name, t);
|
|
1269
1303
|
n && a.push(n);
|
|
1270
1304
|
}
|
|
1271
1305
|
}
|
|
1272
|
-
let
|
|
1273
|
-
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));
|
|
1274
1308
|
for (let s of t.tables) {
|
|
1275
1309
|
let t = o.renamedInto.get(s.name), c = r.get(s.name) ?? (t === void 0 ? void 0 : r.get(t));
|
|
1276
1310
|
if (!c) continue;
|
|
1277
|
-
let l =
|
|
1311
|
+
let l = tn(c, t, s.name, e.dialect);
|
|
1278
1312
|
a.push(...l.ops);
|
|
1279
|
-
let
|
|
1280
|
-
a.push(...
|
|
1281
|
-
for (let e of
|
|
1282
|
-
if (
|
|
1283
|
-
let t =
|
|
1284
|
-
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));
|
|
1285
1319
|
}
|
|
1286
|
-
for (let e of
|
|
1287
|
-
if (
|
|
1288
|
-
let t =
|
|
1289
|
-
a.push(
|
|
1290
|
-
let n =
|
|
1291
|
-
n && a.push(n), a.push(...
|
|
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));
|
|
1292
1326
|
}
|
|
1293
|
-
let
|
|
1294
|
-
a.push(...
|
|
1295
|
-
for (let e of
|
|
1296
|
-
for (let e of
|
|
1297
|
-
for (let { declared: t, live: n } of
|
|
1298
|
-
}
|
|
1299
|
-
let
|
|
1300
|
-
let t =
|
|
1301
|
-
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) ? [{
|
|
1302
1336
|
column: e.name,
|
|
1303
1337
|
target: e.references.table
|
|
1304
1338
|
}] : []) : [];
|
|
1305
|
-
},
|
|
1306
|
-
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({
|
|
1307
1341
|
kind: "add-foreign-key",
|
|
1308
1342
|
table: e.name,
|
|
1309
1343
|
column: t.name,
|
|
@@ -1312,14 +1346,14 @@ BEGIN
|
|
|
1312
1346
|
...t.references.onDelete ? { onDelete: t.references.onDelete } : {},
|
|
1313
1347
|
...t.references.onUpdate ? { onUpdate: t.references.onUpdate } : {}
|
|
1314
1348
|
}, "safe", `deferred FK ${e.name}.${t.name} → ${t.references.table} — breaks a create-time table cycle`));
|
|
1315
|
-
if (
|
|
1349
|
+
if (d.size > 0) {
|
|
1316
1350
|
let e = (e) => e.references ? Object.fromEntries(Object.entries(e).filter(([e]) => e !== "references")) : e;
|
|
1317
1351
|
for (let t = 0; t < a.length; t += 1) {
|
|
1318
1352
|
let n = a[t];
|
|
1319
1353
|
if (n.op.kind !== "create-table") continue;
|
|
1320
1354
|
let r = n.op;
|
|
1321
|
-
if (!r.columns.some((e) =>
|
|
1322
|
-
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);
|
|
1323
1357
|
a[t] = {
|
|
1324
1358
|
...n,
|
|
1325
1359
|
op: {
|
|
@@ -1328,23 +1362,23 @@ BEGIN
|
|
|
1328
1362
|
}
|
|
1329
1363
|
};
|
|
1330
1364
|
}
|
|
1331
|
-
a.push(...
|
|
1365
|
+
a.push(...f);
|
|
1332
1366
|
}
|
|
1333
|
-
let
|
|
1334
|
-
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);
|
|
1335
1369
|
if (n !== void 0) return n;
|
|
1336
1370
|
if (t.has(e)) return 0;
|
|
1337
|
-
let r =
|
|
1371
|
+
let r = p.get(e);
|
|
1338
1372
|
if (!r) return 0;
|
|
1339
1373
|
let i = new Set(t).add(e), a = 0;
|
|
1340
1374
|
for (let t of r.columns) {
|
|
1341
1375
|
let n = t.references?.table;
|
|
1342
|
-
n && n !== e && !
|
|
1376
|
+
n && n !== e && !d.has(w(e, t.name)) && (a = Math.max(a, g(n, i) + 1));
|
|
1343
1377
|
}
|
|
1344
|
-
return
|
|
1345
|
-
},
|
|
1378
|
+
return h.set(e, a), a;
|
|
1379
|
+
}, _ = (e) => e === "rename-table" ? -1 : e === "create-table" ? 0 : e === "drop-table" ? 2 : 1, y = (e) => {
|
|
1346
1380
|
let t = "table" in e ? e.table : "";
|
|
1347
|
-
return e.kind === "create-table" ?
|
|
1381
|
+
return e.kind === "create-table" ? g(t) : e.kind === "drop-table" ? -g(t) : 0;
|
|
1348
1382
|
}, b = {
|
|
1349
1383
|
"add-column": 0,
|
|
1350
1384
|
"rename-column": 1,
|
|
@@ -1364,7 +1398,7 @@ BEGIN
|
|
|
1364
1398
|
"add-foreign-key": 5,
|
|
1365
1399
|
"add-check": 5
|
|
1366
1400
|
}, x = (e) => b[e] ?? 9, S = [...a].sort((e, t) => {
|
|
1367
|
-
let n =
|
|
1401
|
+
let n = _(e.op.kind), r = _(t.op.kind);
|
|
1368
1402
|
if (n !== r) return n - r;
|
|
1369
1403
|
let i = y(e.op), a = y(t.op);
|
|
1370
1404
|
if (i !== a) return i - a;
|
|
@@ -1375,11 +1409,87 @@ BEGIN
|
|
|
1375
1409
|
});
|
|
1376
1410
|
return {
|
|
1377
1411
|
operations: S,
|
|
1378
|
-
fromFingerprint:
|
|
1379
|
-
toFingerprint:
|
|
1380
|
-
summary:
|
|
1412
|
+
fromFingerprint: M(e.live),
|
|
1413
|
+
toFingerprint: M(t),
|
|
1414
|
+
summary: nn(S)
|
|
1381
1415
|
};
|
|
1382
|
-
},
|
|
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) => {
|
|
1383
1493
|
let t = /[`"[]?([A-Za-z_][A-Za-z0-9_]*)[`"\]]?\s*(?:in\b|=)/i.exec(e);
|
|
1384
1494
|
if (!t) return null;
|
|
1385
1495
|
let n = [...e.matchAll(/'((?:[^']|'')*)'/g)].map((e) => e[1].replace(/''/g, "'"));
|
|
@@ -1387,7 +1497,7 @@ BEGIN
|
|
|
1387
1497
|
column: t[1],
|
|
1388
1498
|
values: n
|
|
1389
1499
|
};
|
|
1390
|
-
},
|
|
1500
|
+
}, bn = (e, t) => {
|
|
1391
1501
|
if (t === "vector") return "vector";
|
|
1392
1502
|
switch (e) {
|
|
1393
1503
|
case "text":
|
|
@@ -1414,7 +1524,7 @@ BEGIN
|
|
|
1414
1524
|
case "USER-DEFINED": return "text";
|
|
1415
1525
|
default: return "text";
|
|
1416
1526
|
}
|
|
1417
|
-
},
|
|
1527
|
+
}, R = (e) => {
|
|
1418
1528
|
switch (e.toUpperCase()) {
|
|
1419
1529
|
case "CASCADE": return "cascade";
|
|
1420
1530
|
case "RESTRICT": return "restrict";
|
|
@@ -1422,25 +1532,25 @@ BEGIN
|
|
|
1422
1532
|
case "SET DEFAULT": return "noAction";
|
|
1423
1533
|
default: return "noAction";
|
|
1424
1534
|
}
|
|
1425
|
-
},
|
|
1535
|
+
}, xn = (e, t) => {
|
|
1426
1536
|
let n = e !== void 0 && e !== "" ? Number(e) : NaN;
|
|
1427
1537
|
return Number.isInteger(n) && n > 0 ? n : t;
|
|
1428
|
-
},
|
|
1538
|
+
}, Sn = () => xn(process.env.VOLTRO_INTROSPECT_BATCH, 20), Cn = 300, wn = () => {
|
|
1429
1539
|
let e = process.env.VOLTRO_INTROSPECT_TIMEOUT_MS;
|
|
1430
|
-
return e !== void 0 && e.trim() === "0" ? 0 :
|
|
1431
|
-
},
|
|
1540
|
+
return e !== void 0 && e.trim() === "0" ? 0 : xn(e, 3e4);
|
|
1541
|
+
}, Tn = (e, t) => {
|
|
1432
1542
|
let n = Number.isFinite(t) && t >= 1 ? Math.floor(t) : 1, r = [];
|
|
1433
1543
|
for (let t = 0; t < e.length; t += n) r.push(e.slice(t, t + n));
|
|
1434
1544
|
return r;
|
|
1435
|
-
},
|
|
1436
|
-
let t =
|
|
1545
|
+
}, En = (e) => {
|
|
1546
|
+
let t = wn(), n = b.gen(function* () {
|
|
1437
1547
|
t > 0 && (yield* e.unsafe(`SET LOCAL statement_timeout = ${t}`));
|
|
1438
|
-
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* () {
|
|
1439
1549
|
let r = [];
|
|
1440
|
-
for (let a of
|
|
1550
|
+
for (let a of Tn(e, i)) a.length !== 0 && r.push(...yield* t(n(a)));
|
|
1441
1551
|
return r;
|
|
1442
1552
|
}), o = [];
|
|
1443
|
-
for (let t = 0;; t +=
|
|
1553
|
+
for (let t = 0;; t += Cn) {
|
|
1444
1554
|
let n = yield* e`
|
|
1445
1555
|
SELECT c.relname AS table_name,
|
|
1446
1556
|
COALESCE(c.reltuples::bigint, 0) AS row_count
|
|
@@ -1448,9 +1558,9 @@ BEGIN
|
|
|
1448
1558
|
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
1449
1559
|
WHERE n.nspname = current_schema() AND c.relkind = 'r'
|
|
1450
1560
|
ORDER BY c.relname
|
|
1451
|
-
LIMIT ${r(
|
|
1561
|
+
LIMIT ${r(Cn)} OFFSET ${r(t)}
|
|
1452
1562
|
`;
|
|
1453
|
-
if (o.push(...n), n.length <
|
|
1563
|
+
if (o.push(...n), n.length < Cn) break;
|
|
1454
1564
|
}
|
|
1455
1565
|
let s = /* @__PURE__ */ new Set(), c = [];
|
|
1456
1566
|
for (let e of o) s.has(e.table_name) || (s.add(e.table_name), c.push(e));
|
|
@@ -1477,7 +1587,7 @@ BEGIN
|
|
|
1477
1587
|
JOIN pg_namespace nsp ON nsp.oid = rel.relnamespace
|
|
1478
1588
|
JOIN pg_attribute att ON att.attrelid = rel.oid AND att.attnum = ANY (con.conkey)
|
|
1479
1589
|
WHERE con.contype = 'u' AND nsp.nspname = current_schema() AND rel.relname IN ${t}
|
|
1480
|
-
`),
|
|
1590
|
+
`), p = yield* a(l, (t) => e`
|
|
1481
1591
|
SELECT rel.relname AS table_name, att.attname AS column_name, pg_get_constraintdef(con.oid) AS def
|
|
1482
1592
|
FROM pg_constraint con
|
|
1483
1593
|
JOIN pg_class rel ON rel.oid = con.conrelid
|
|
@@ -1485,12 +1595,12 @@ BEGIN
|
|
|
1485
1595
|
JOIN pg_attribute att ON att.attrelid = rel.oid AND att.attnum = con.conkey[1]
|
|
1486
1596
|
WHERE con.contype = 'c' AND nsp.nspname = current_schema() AND array_length(con.conkey, 1) = 1
|
|
1487
1597
|
AND rel.relname IN ${t}
|
|
1488
|
-
`),
|
|
1489
|
-
for (let e of
|
|
1598
|
+
`), m = /* @__PURE__ */ new Map();
|
|
1599
|
+
for (let e of p) {
|
|
1490
1600
|
let t = [...e.def.matchAll(/'((?:[^']|'')*)'/g)].map((e) => e[1].replace(/''/g, "'"));
|
|
1491
|
-
t.length > 0 &&
|
|
1601
|
+
t.length > 0 && m.set(`${e.table_name}.${e.column_name}`, t);
|
|
1492
1602
|
}
|
|
1493
|
-
let
|
|
1603
|
+
let h = yield* a(l, (t) => e`
|
|
1494
1604
|
SELECT
|
|
1495
1605
|
rel.relname AS source_table,
|
|
1496
1606
|
att.attname AS source_column,
|
|
@@ -1515,7 +1625,7 @@ BEGIN
|
|
|
1515
1625
|
WHERE con.contype = 'f' AND nsp.nspname = current_schema()
|
|
1516
1626
|
AND rel.relname IN ${t}
|
|
1517
1627
|
ORDER BY rel.relname, con.conname, ck.ord
|
|
1518
|
-
`),
|
|
1628
|
+
`), g = yield* a(l, (t) => e`
|
|
1519
1629
|
WITH ix AS (
|
|
1520
1630
|
SELECT
|
|
1521
1631
|
c.relname AS table_name,
|
|
@@ -1536,31 +1646,31 @@ BEGIN
|
|
|
1536
1646
|
AND c.relname IN ${t}
|
|
1537
1647
|
)
|
|
1538
1648
|
SELECT * FROM ix ORDER BY table_name, index_name, position
|
|
1539
|
-
`),
|
|
1649
|
+
`), _ = /* @__PURE__ */ new Map(), v = /* @__PURE__ */ new Map(), y = /* @__PURE__ */ new Map(), x = /* @__PURE__ */ new Map(), S = /* @__PURE__ */ new Map(), C = /* @__PURE__ */ new Map();
|
|
1540
1650
|
for (let e of u) {
|
|
1541
|
-
let t =
|
|
1542
|
-
t || (t = [],
|
|
1651
|
+
let t = _.get(e.table_name);
|
|
1652
|
+
t || (t = [], _.set(e.table_name, t)), t.push(e);
|
|
1543
1653
|
}
|
|
1544
1654
|
for (let e of d) {
|
|
1545
|
-
let t =
|
|
1546
|
-
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);
|
|
1547
1657
|
}
|
|
1548
|
-
let
|
|
1658
|
+
let ee = /* @__PURE__ */ new Map();
|
|
1549
1659
|
for (let e of f) {
|
|
1550
|
-
let t = `${e.table_name}\u0000${e.constraint_name}`, n =
|
|
1660
|
+
let t = `${e.table_name}\u0000${e.constraint_name}`, n = ee.get(t);
|
|
1551
1661
|
n || (n = {
|
|
1552
1662
|
table: e.table_name,
|
|
1553
1663
|
columns: []
|
|
1554
|
-
},
|
|
1664
|
+
}, ee.set(t, n)), n.columns.push({
|
|
1555
1665
|
name: e.column_name,
|
|
1556
1666
|
position: e.position
|
|
1557
1667
|
});
|
|
1558
1668
|
}
|
|
1559
|
-
for (let [e, t] of
|
|
1669
|
+
for (let [e, t] of ee) {
|
|
1560
1670
|
let n = [...t.columns].sort((e, t) => e.position - t.position).map((e) => e.name);
|
|
1561
1671
|
if (n.length === 1) {
|
|
1562
|
-
let e =
|
|
1563
|
-
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]);
|
|
1564
1674
|
} else {
|
|
1565
1675
|
let r = x.get(t.table);
|
|
1566
1676
|
r || (r = [], x.set(t.table, r)), r.push({
|
|
@@ -1571,29 +1681,29 @@ BEGIN
|
|
|
1571
1681
|
});
|
|
1572
1682
|
}
|
|
1573
1683
|
}
|
|
1574
|
-
for (let e of
|
|
1684
|
+
for (let e of h) {
|
|
1575
1685
|
let t = S.get(e.source_table);
|
|
1576
1686
|
t || (t = [], S.set(e.source_table, t)), t.push(e);
|
|
1577
1687
|
}
|
|
1578
|
-
for (let e of
|
|
1579
|
-
let t =
|
|
1580
|
-
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));
|
|
1581
1691
|
let n = t.get(e.index_name);
|
|
1582
1692
|
n || (n = [], t.set(e.index_name, n)), n.push(e);
|
|
1583
1693
|
}
|
|
1584
|
-
let
|
|
1694
|
+
let te = [];
|
|
1585
1695
|
for (let e of c) {
|
|
1586
|
-
let t = /* @__PURE__ */ new Set(), n = (
|
|
1587
|
-
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 ? {
|
|
1588
1698
|
table: a.target_table,
|
|
1589
1699
|
column: a.target_column,
|
|
1590
|
-
onDelete:
|
|
1591
|
-
onUpdate:
|
|
1592
|
-
} : 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}`);
|
|
1593
1703
|
return {
|
|
1594
1704
|
name: t.column_name,
|
|
1595
1705
|
type: s,
|
|
1596
|
-
...
|
|
1706
|
+
...V(t.data_type, t.character_maximum_length) === void 0 ? {} : { maxLength: V(t.data_type, t.character_maximum_length) },
|
|
1597
1707
|
nullable: t.is_nullable === "YES",
|
|
1598
1708
|
unique: i.has(t.column_name) || n && t.column_name === "id",
|
|
1599
1709
|
hasDefault: t.column_default !== null,
|
|
@@ -1601,7 +1711,7 @@ BEGIN
|
|
|
1601
1711
|
...c ? { references: c } : {},
|
|
1602
1712
|
...l ? { oneOf: l } : {}
|
|
1603
1713
|
};
|
|
1604
|
-
}), l = [], u =
|
|
1714
|
+
}), l = [], u = C.get(e.table_name);
|
|
1605
1715
|
if (u) for (let [t, n] of u) {
|
|
1606
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) => {
|
|
1607
1717
|
let t = e.position;
|
|
@@ -1625,20 +1735,20 @@ BEGIN
|
|
|
1625
1735
|
unique: !0
|
|
1626
1736
|
});
|
|
1627
1737
|
let d = Number(e.row_count ?? 0);
|
|
1628
|
-
|
|
1738
|
+
te.push({
|
|
1629
1739
|
name: e.table_name,
|
|
1630
1740
|
columns: c,
|
|
1631
1741
|
indexes: l,
|
|
1632
1742
|
...d > 0 ? { rowCount: d } : {}
|
|
1633
1743
|
});
|
|
1634
1744
|
}
|
|
1635
|
-
return { tables:
|
|
1745
|
+
return { tables: te };
|
|
1636
1746
|
});
|
|
1637
|
-
return (t > 0 ? e.withTransaction(n) : n).pipe(
|
|
1747
|
+
return (t > 0 ? e.withTransaction(n) : n).pipe(b.catchAll((e) => {
|
|
1638
1748
|
let n = `${String(e)} ${String(e.cause ?? "")}`.toLowerCase();
|
|
1639
|
-
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);
|
|
1640
1750
|
}));
|
|
1641
|
-
},
|
|
1751
|
+
}, Dn = (e, t) => {
|
|
1642
1752
|
switch (e.toLowerCase()) {
|
|
1643
1753
|
case "varchar":
|
|
1644
1754
|
case "char":
|
|
@@ -1670,21 +1780,21 @@ BEGIN
|
|
|
1670
1780
|
case "json": return "json";
|
|
1671
1781
|
default: return "text";
|
|
1672
1782
|
}
|
|
1673
|
-
},
|
|
1783
|
+
}, z = (e) => e.map((e) => {
|
|
1674
1784
|
let t = e, n = {};
|
|
1675
1785
|
for (let e of Object.keys(t)) n[e.toLowerCase()] = t[e];
|
|
1676
1786
|
return n;
|
|
1677
|
-
}),
|
|
1678
|
-
let t =
|
|
1787
|
+
}), On = (e) => b.gen(function* () {
|
|
1788
|
+
let t = z(yield* e`
|
|
1679
1789
|
SELECT table_name, COALESCE(table_rows, 0) AS table_rows
|
|
1680
1790
|
FROM information_schema.tables
|
|
1681
1791
|
WHERE table_schema = DATABASE() AND table_type = 'BASE TABLE'
|
|
1682
|
-
ORDER BY table_name`), n =
|
|
1792
|
+
ORDER BY table_name`), n = z(yield* e`
|
|
1683
1793
|
SELECT table_name, column_name, data_type, column_type, is_nullable, column_default, column_key,
|
|
1684
1794
|
character_maximum_length
|
|
1685
1795
|
FROM information_schema.columns
|
|
1686
1796
|
WHERE table_schema = DATABASE()
|
|
1687
|
-
ORDER BY table_name, ordinal_position`), r =
|
|
1797
|
+
ORDER BY table_name, ordinal_position`), r = z(yield* e`
|
|
1688
1798
|
SELECT k.table_name AS source_table,
|
|
1689
1799
|
k.column_name AS source_column,
|
|
1690
1800
|
k.referenced_table_name AS target_table,
|
|
@@ -1694,21 +1804,21 @@ BEGIN
|
|
|
1694
1804
|
JOIN information_schema.referential_constraints r
|
|
1695
1805
|
ON k.constraint_name = r.constraint_name
|
|
1696
1806
|
AND k.table_schema = r.constraint_schema
|
|
1697
|
-
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`
|
|
1698
1808
|
SELECT table_name, index_name, non_unique, column_name, seq_in_index
|
|
1699
1809
|
FROM information_schema.statistics
|
|
1700
1810
|
WHERE table_schema = DATABASE() AND index_name <> 'PRIMARY'
|
|
1701
|
-
ORDER BY table_name, index_name, seq_in_index`), a =
|
|
1811
|
+
ORDER BY table_name, index_name, seq_in_index`), a = z(yield* e`
|
|
1702
1812
|
SELECT table_name, check_clause
|
|
1703
1813
|
FROM information_schema.check_constraints
|
|
1704
|
-
WHERE constraint_schema = DATABASE()`.unprepared.pipe(
|
|
1814
|
+
WHERE constraint_schema = DATABASE()`.unprepared.pipe(b.orElseSucceed(() => []))), o = /* @__PURE__ */ new Set(), s = /* @__PURE__ */ new Map();
|
|
1705
1815
|
for (let e of a) {
|
|
1706
1816
|
let t = /json_valid\(`?([^`)]+)`?\)/i.exec(e.check_clause);
|
|
1707
1817
|
if (t) {
|
|
1708
1818
|
o.add(`${e.table_name}.${t[1]}`);
|
|
1709
1819
|
continue;
|
|
1710
1820
|
}
|
|
1711
|
-
let n =
|
|
1821
|
+
let n = yn(e.check_clause);
|
|
1712
1822
|
n && s.set(`${e.table_name}.${n.column}`, n.values);
|
|
1713
1823
|
}
|
|
1714
1824
|
let c = /* @__PURE__ */ new Map(), l = /* @__PURE__ */ new Map(), u = /* @__PURE__ */ new Map();
|
|
@@ -1731,17 +1841,17 @@ BEGIN
|
|
|
1731
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);
|
|
1732
1842
|
if (f) for (let e of f.values()) e.length === 1 && e[0].non_unique === 0 && a.add(e[0].column_name);
|
|
1733
1843
|
let p = t.map((t) => {
|
|
1734
|
-
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 ? {
|
|
1735
1845
|
table: c.target_table,
|
|
1736
1846
|
column: c.target_column,
|
|
1737
|
-
onDelete:
|
|
1738
|
-
onUpdate:
|
|
1847
|
+
onDelete: R(c.delete_rule),
|
|
1848
|
+
onUpdate: R(c.update_rule)
|
|
1739
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}`);
|
|
1740
1850
|
return {
|
|
1741
1851
|
name: t.column_name,
|
|
1742
1852
|
type: l,
|
|
1743
1853
|
nullable: t.is_nullable === "YES",
|
|
1744
|
-
...
|
|
1854
|
+
...V(t.data_type, t.character_maximum_length) === void 0 ? {} : { maxLength: V(t.data_type, t.character_maximum_length) },
|
|
1745
1855
|
unique: d,
|
|
1746
1856
|
hasDefault: f !== null,
|
|
1747
1857
|
...f === null ? {} : { defaultValue: f },
|
|
@@ -1773,10 +1883,10 @@ BEGIN
|
|
|
1773
1883
|
});
|
|
1774
1884
|
}
|
|
1775
1885
|
return { tables: d };
|
|
1776
|
-
}),
|
|
1886
|
+
}), kn = (e) => {
|
|
1777
1887
|
let t = e.toUpperCase();
|
|
1778
1888
|
return t.includes("INT") ? "integer" : t.includes("REAL") || t.includes("FLOA") || t.includes("DOUB") ? "real" : t.includes("BLOB") ? "bytes" : "text";
|
|
1779
|
-
},
|
|
1889
|
+
}, An = (e) => b.gen(function* () {
|
|
1780
1890
|
let t = yield* e`
|
|
1781
1891
|
SELECT name, sql FROM sqlite_master
|
|
1782
1892
|
WHERE type = 'table' AND name NOT LIKE 'sqlite_%'
|
|
@@ -1793,11 +1903,11 @@ BEGIN
|
|
|
1793
1903
|
d.set(t.name, r), t.unique === 1 && r.length === 1 && f.add(r[0]);
|
|
1794
1904
|
}
|
|
1795
1905
|
let p = o.map((e) => {
|
|
1796
|
-
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 ? {
|
|
1797
1907
|
table: r.table,
|
|
1798
1908
|
column: r.to,
|
|
1799
|
-
onDelete:
|
|
1800
|
-
onUpdate:
|
|
1909
|
+
onDelete: R(r.on_delete),
|
|
1910
|
+
onUpdate: R(r.on_update)
|
|
1801
1911
|
} : void 0, o = t.get(e.name);
|
|
1802
1912
|
return {
|
|
1803
1913
|
name: e.name,
|
|
@@ -1832,7 +1942,7 @@ BEGIN
|
|
|
1832
1942
|
});
|
|
1833
1943
|
}
|
|
1834
1944
|
return { tables: r };
|
|
1835
|
-
}),
|
|
1945
|
+
}), jn = (e) => {
|
|
1836
1946
|
switch (e.toLowerCase()) {
|
|
1837
1947
|
case "int":
|
|
1838
1948
|
case "smallint":
|
|
@@ -1854,7 +1964,7 @@ BEGIN
|
|
|
1854
1964
|
case "image": return "bytes";
|
|
1855
1965
|
default: return "text";
|
|
1856
1966
|
}
|
|
1857
|
-
},
|
|
1967
|
+
}, B = (e) => e === !0 || e === 1, Mn = (e) => b.gen(function* () {
|
|
1858
1968
|
let t = yield* e`
|
|
1859
1969
|
SELECT TABLE_NAME AS table_name FROM INFORMATION_SCHEMA.TABLES
|
|
1860
1970
|
WHERE TABLE_TYPE = 'BASE TABLE' ORDER BY TABLE_NAME`, n = yield* e`
|
|
@@ -1911,21 +2021,21 @@ BEGIN
|
|
|
1911
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();
|
|
1912
2022
|
if (a) for (let [, e] of a) {
|
|
1913
2023
|
let t = [...e].sort((e, t) => e.key_ordinal - t.key_ordinal);
|
|
1914
|
-
if (
|
|
1915
|
-
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);
|
|
1916
2026
|
}
|
|
1917
2027
|
let p = t.map((t) => {
|
|
1918
|
-
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 ? {
|
|
1919
2029
|
table: i.target_table,
|
|
1920
2030
|
column: i.target_column,
|
|
1921
|
-
onDelete:
|
|
1922
|
-
onUpdate:
|
|
1923
|
-
} : 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}`);
|
|
1924
2034
|
return {
|
|
1925
2035
|
name: t.column_name,
|
|
1926
2036
|
type: a,
|
|
1927
2037
|
nullable: t.is_nullable === "YES",
|
|
1928
|
-
...
|
|
2038
|
+
...V(t.data_type, t.character_maximum_length) === void 0 ? {} : { maxLength: V(t.data_type, t.character_maximum_length) },
|
|
1929
2039
|
unique: f.has(t.column_name) || n && t.column_name === "id",
|
|
1930
2040
|
hasDefault: c !== null,
|
|
1931
2041
|
...c === null ? {} : { defaultValue: c },
|
|
@@ -1935,11 +2045,11 @@ BEGIN
|
|
|
1935
2045
|
}), m = [];
|
|
1936
2046
|
if (a) for (let [t, n] of a) {
|
|
1937
2047
|
let r = [...n].sort((e, t) => e.key_ordinal - t.key_ordinal);
|
|
1938
|
-
|
|
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({
|
|
1939
2049
|
name: t,
|
|
1940
2050
|
table: e,
|
|
1941
2051
|
columns: r.map((e) => e.column_name),
|
|
1942
|
-
unique:
|
|
2052
|
+
unique: B(r[0].is_unique)
|
|
1943
2053
|
});
|
|
1944
2054
|
}
|
|
1945
2055
|
d.length > 0 && m.push({
|
|
@@ -1954,51 +2064,54 @@ BEGIN
|
|
|
1954
2064
|
});
|
|
1955
2065
|
}
|
|
1956
2066
|
return { tables: u };
|
|
1957
|
-
}),
|
|
2067
|
+
}), Nn = (e) => {
|
|
1958
2068
|
switch (e.toUpperCase().replace(/_/g, " ")) {
|
|
1959
2069
|
case "CASCADE": return "cascade";
|
|
1960
2070
|
case "SET NULL": return "setNull";
|
|
1961
2071
|
case "SET DEFAULT": return "noAction";
|
|
1962
2072
|
default: return "noAction";
|
|
1963
2073
|
}
|
|
1964
|
-
},
|
|
2074
|
+
}, Pn = (e) => {
|
|
1965
2075
|
let t = e.trim();
|
|
1966
2076
|
for (; t.startsWith("(") && t.endsWith(")");) t = t.slice(1, -1).trim();
|
|
1967
2077
|
return t.startsWith("N'") && t.endsWith("'") ? t = t.slice(2, -1) : t.startsWith("'") && t.endsWith("'") && (t = t.slice(1, -1)), t;
|
|
1968
|
-
},
|
|
2078
|
+
}, Fn = /* @__PURE__ */ new Set([
|
|
1969
2079
|
"character varying",
|
|
1970
2080
|
"varchar",
|
|
1971
2081
|
"nvarchar",
|
|
1972
2082
|
"char",
|
|
1973
2083
|
"character",
|
|
1974
2084
|
"nchar"
|
|
1975
|
-
]),
|
|
1976
|
-
if (!
|
|
2085
|
+
]), V = (e, t) => {
|
|
2086
|
+
if (!Fn.has(e.toLowerCase())) return;
|
|
1977
2087
|
let n = Number(t);
|
|
1978
2088
|
if (!(!Number.isFinite(n) || n <= 0)) return n;
|
|
1979
|
-
},
|
|
1980
|
-
mysql: () =>
|
|
1981
|
-
mssql: () =>
|
|
1982
|
-
sqlite: () =>
|
|
1983
|
-
orElse: () =>
|
|
1984
|
-
})
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
}),
|
|
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) => {
|
|
1988
2101
|
if (!(e.type === "text" && e.maxLength !== void 0) && !e.spatial && e.type !== "vector" && e.type !== "array" && e.type !== "enum") return;
|
|
1989
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;
|
|
1990
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.`);
|
|
1991
|
-
return
|
|
1992
|
-
},
|
|
1993
|
-
let t =
|
|
1994
|
-
return t === void 0 ?
|
|
1995
|
-
},
|
|
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) => {
|
|
1996
2109
|
switch (e.type) {
|
|
1997
2110
|
case "id": return "text";
|
|
1998
2111
|
case "text": return "text";
|
|
1999
2112
|
case "integer": return "integer";
|
|
2000
2113
|
case "real": return "double precision";
|
|
2001
|
-
case "decimal": return
|
|
2114
|
+
case "decimal": return Rn(e, "NUMERIC");
|
|
2002
2115
|
case "bigint": return "bigint";
|
|
2003
2116
|
case "boolean": return "boolean";
|
|
2004
2117
|
case "timestamp": return "timestamptz";
|
|
@@ -2012,15 +2125,15 @@ BEGIN
|
|
|
2012
2125
|
case "interval": return "interval";
|
|
2013
2126
|
case "raw": return e.rawDdl ?? "text";
|
|
2014
2127
|
}
|
|
2015
|
-
},
|
|
2016
|
-
let t =
|
|
2128
|
+
}, Hn = (e) => {
|
|
2129
|
+
let t = zn(e, "mariadb");
|
|
2017
2130
|
if (t !== void 0) return t;
|
|
2018
2131
|
switch (e.type) {
|
|
2019
2132
|
case "id": return "VARCHAR(64)";
|
|
2020
2133
|
case "text": return e.hasDefault && e.defaultValue !== void 0 || e.oneOf && e.oneOf.length > 0 ? "VARCHAR(255)" : "LONGTEXT";
|
|
2021
2134
|
case "integer": return "INT";
|
|
2022
2135
|
case "real": return "DOUBLE";
|
|
2023
|
-
case "decimal": return
|
|
2136
|
+
case "decimal": return Rn(e, "DECIMAL");
|
|
2024
2137
|
case "bigint": return "BIGINT";
|
|
2025
2138
|
case "boolean": return "TINYINT(1)";
|
|
2026
2139
|
case "timestamp": return "DATETIME(6)";
|
|
@@ -2034,82 +2147,82 @@ BEGIN
|
|
|
2034
2147
|
case "interval": return "BIGINT";
|
|
2035
2148
|
case "raw": return e.rawDdl ?? "LONGTEXT";
|
|
2036
2149
|
}
|
|
2037
|
-
},
|
|
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) => {
|
|
2038
2151
|
let n = t(e.defaultValue);
|
|
2039
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.`);
|
|
2040
2153
|
return n;
|
|
2041
|
-
},
|
|
2042
|
-
let r =
|
|
2043
|
-
return r ?
|
|
2044
|
-
},
|
|
2045
|
-
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");
|
|
2046
2159
|
return [
|
|
2047
|
-
|
|
2048
|
-
|
|
2160
|
+
W(t),
|
|
2161
|
+
k(e, "mariadb"),
|
|
2049
2162
|
r ? null : "NOT NULL",
|
|
2050
2163
|
i
|
|
2051
2164
|
].filter(Boolean).join(" ");
|
|
2052
|
-
},
|
|
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) => {
|
|
2053
2166
|
if (!e.generatedAs) return null;
|
|
2054
2167
|
let { expr: n, stored: r } = e.generatedAs;
|
|
2055
2168
|
return t === "postgres" ? `GENERATED ALWAYS AS (${n}) STORED` : t === "mssql" ? `AS (${n})${r ? " PERSISTED" : ""}` : `GENERATED ALWAYS AS (${n}) ${r ? "STORED" : "VIRTUAL"}`;
|
|
2056
|
-
},
|
|
2057
|
-
if (e.type === "id" && e.idScheme?.kind === "numeric") return
|
|
2058
|
-
let t =
|
|
2059
|
-
if (t) return `${
|
|
2060
|
-
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)];
|
|
2061
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) {
|
|
2062
|
-
let t =
|
|
2175
|
+
let t = $n(e.defaultValue, e);
|
|
2063
2176
|
t && n.push(t);
|
|
2064
2177
|
}
|
|
2065
|
-
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 ${{
|
|
2066
2179
|
cascade: "CASCADE",
|
|
2067
2180
|
restrict: "RESTRICT",
|
|
2068
2181
|
setNull: "SET NULL",
|
|
2069
2182
|
noAction: "NO ACTION"
|
|
2070
2183
|
}[e.references.onDelete]}`)), n.join(" ");
|
|
2071
|
-
},
|
|
2072
|
-
let t = e.columns.map((e) => ` ${
|
|
2073
|
-
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(", ")})`);
|
|
2074
2187
|
return n;
|
|
2075
|
-
},
|
|
2076
|
-
let t =
|
|
2188
|
+
}, rr = 191, ir = (e) => {
|
|
2189
|
+
let t = Hn(e).toUpperCase();
|
|
2077
2190
|
return t.endsWith("TEXT") || t.endsWith("BLOB");
|
|
2078
|
-
},
|
|
2079
|
-
if (e.type === "id" && e.idScheme?.kind === "numeric") return
|
|
2080
|
-
let t =
|
|
2081
|
-
if (t) return `${
|
|
2082
|
-
let n = [
|
|
2083
|
-
if (e.type === "id" && n.push("PRIMARY KEY"), e.nullable || n.push("NOT NULL"), e.unique && e.type !== "id" && !
|
|
2084
|
-
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);
|
|
2085
2198
|
t && n.push(t);
|
|
2086
2199
|
}
|
|
2087
|
-
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 ${{
|
|
2088
2201
|
cascade: "CASCADE",
|
|
2089
2202
|
restrict: "RESTRICT",
|
|
2090
2203
|
setNull: "SET NULL",
|
|
2091
2204
|
noAction: "NO ACTION"
|
|
2092
2205
|
}[e.references.onDelete]}`)), n.join(" ");
|
|
2093
|
-
},
|
|
2206
|
+
}, or = (e) => {
|
|
2094
2207
|
let t = new Map(e.columns.map((e) => [e.name, e])), n = (e) => {
|
|
2095
|
-
let n = t.get(e);
|
|
2096
|
-
return n
|
|
2097
|
-
}, r = e.columns.map((e) => ` ${
|
|
2098
|
-
for (let t of e.columns) t.unique && t.type !== "id" &&
|
|
2099
|
-
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(", ")})`);
|
|
2100
2213
|
return i;
|
|
2101
|
-
},
|
|
2214
|
+
}, sr = {
|
|
2102
2215
|
cascade: "CASCADE",
|
|
2103
2216
|
restrict: "RESTRICT",
|
|
2104
2217
|
setNull: "SET NULL",
|
|
2105
2218
|
noAction: "NO ACTION"
|
|
2106
|
-
},
|
|
2219
|
+
}, cr = {
|
|
2107
2220
|
cascade: "CASCADE",
|
|
2108
2221
|
restrict: "NO ACTION",
|
|
2109
2222
|
setNull: "SET NULL",
|
|
2110
2223
|
noAction: "NO ACTION"
|
|
2111
|
-
},
|
|
2112
|
-
let t =
|
|
2224
|
+
}, lr = (e) => {
|
|
2225
|
+
let t = zn(e, "sqlite");
|
|
2113
2226
|
if (t !== void 0) return t;
|
|
2114
2227
|
switch (e.type) {
|
|
2115
2228
|
case "integer":
|
|
@@ -2120,60 +2233,60 @@ BEGIN
|
|
|
2120
2233
|
case "bytes": return "BLOB";
|
|
2121
2234
|
default: return "TEXT";
|
|
2122
2235
|
}
|
|
2123
|
-
},
|
|
2124
|
-
if (e.type === "id" && e.idScheme?.kind === "numeric") return
|
|
2125
|
-
let t =
|
|
2126
|
-
if (t) return `${
|
|
2127
|
-
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)];
|
|
2128
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) {
|
|
2129
|
-
let t =
|
|
2242
|
+
let t = q(e.defaultValue, "CURRENT_TIMESTAMP");
|
|
2130
2243
|
t && n.push(t);
|
|
2131
2244
|
}
|
|
2132
|
-
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) {
|
|
2133
2246
|
let t = e.oneOf.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ");
|
|
2134
|
-
n.push(`CHECK (${
|
|
2247
|
+
n.push(`CHECK (${U(e.name)} IN (${t}))`);
|
|
2135
2248
|
}
|
|
2136
2249
|
return n.join(" ");
|
|
2137
|
-
},
|
|
2138
|
-
let t = e.columns.map((e) => ` ${
|
|
2139
|
-
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(", ")})`);
|
|
2140
2253
|
return n;
|
|
2141
|
-
},
|
|
2254
|
+
}, J = (e, t) => {
|
|
2142
2255
|
let n = t.find((t) => t.tableName === e);
|
|
2143
2256
|
if (!n) return [];
|
|
2144
|
-
let r =
|
|
2145
|
-
...
|
|
2257
|
+
let r = P([n]).tables[0], i = `${e}__voltro_rebuild`, a = r.columns.map((e) => U(e.name)).join(", "), o = [
|
|
2258
|
+
...dr({
|
|
2146
2259
|
kind: "create-table",
|
|
2147
2260
|
table: i,
|
|
2148
2261
|
columns: r.columns,
|
|
2149
2262
|
indexes: [],
|
|
2150
2263
|
...r.primaryKey ? { primaryKey: r.primaryKey } : {}
|
|
2151
2264
|
}),
|
|
2152
|
-
`INSERT INTO ${
|
|
2153
|
-
`DROP TABLE ${
|
|
2154
|
-
`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)}`
|
|
2155
2268
|
];
|
|
2156
|
-
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(", ")})`);
|
|
2157
2270
|
return o;
|
|
2158
|
-
},
|
|
2271
|
+
}, fr = (e, t, n, r) => {
|
|
2159
2272
|
let i = r ?? e.nullable;
|
|
2160
|
-
return `ALTER TABLE ${
|
|
2161
|
-
},
|
|
2162
|
-
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 + ']');`;
|
|
2163
2276
|
if (!e.hasDefault) return t;
|
|
2164
|
-
let n =
|
|
2277
|
+
let n = q(e.defaultValue, "SYSUTCDATETIME()");
|
|
2165
2278
|
if (!n) return t;
|
|
2166
2279
|
let r = n.replace(/^DEFAULT\s+/i, "");
|
|
2167
|
-
return `${t} ALTER TABLE ${
|
|
2168
|
-
},
|
|
2169
|
-
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");
|
|
2170
2283
|
if (t !== void 0) return t;
|
|
2171
2284
|
switch (e.type) {
|
|
2172
2285
|
case "id":
|
|
2173
2286
|
case "reference": return "NVARCHAR(64)";
|
|
2174
2287
|
case "integer": return "INT";
|
|
2175
2288
|
case "real": return "FLOAT";
|
|
2176
|
-
case "decimal": return
|
|
2289
|
+
case "decimal": return Rn(e, "DECIMAL");
|
|
2177
2290
|
case "bigint": return "BIGINT";
|
|
2178
2291
|
case "boolean": return "BIT";
|
|
2179
2292
|
case "timestamp": return "DATETIME2(6)";
|
|
@@ -2181,55 +2294,59 @@ BEGIN
|
|
|
2181
2294
|
case "bytes": return "VARBINARY(MAX)";
|
|
2182
2295
|
default: return "NVARCHAR(MAX)";
|
|
2183
2296
|
}
|
|
2184
|
-
},
|
|
2185
|
-
if (e.type === "id" && e.idScheme?.kind === "numeric") return
|
|
2186
|
-
let n = e.type === "text" && t?.has(e.name) ? "NVARCHAR(450)" :
|
|
2187
|
-
if (r) return `${
|
|
2188
|
-
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];
|
|
2189
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) {
|
|
2190
|
-
let t =
|
|
2303
|
+
let t = q(e.defaultValue, "SYSUTCDATETIME()");
|
|
2191
2304
|
t && i.push(t);
|
|
2192
2305
|
}
|
|
2193
|
-
return e.references && (i.push(`REFERENCES ${
|
|
2194
|
-
},
|
|
2195
|
-
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)}')`];
|
|
2196
2309
|
for (let n of e.indexes) {
|
|
2197
2310
|
if (n.name === `${e.table}_pkey`) continue;
|
|
2198
|
-
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(", ")})`;
|
|
2199
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)}')`);
|
|
2200
2313
|
}
|
|
2201
2314
|
return a;
|
|
2202
|
-
},
|
|
2203
|
-
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)}`;
|
|
2204
2317
|
return `ALTER TABLE ${n(e.table)} RENAME TO ${n(r)};`;
|
|
2205
|
-
},
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
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) => {
|
|
2209
2326
|
let t = (e) => e.replace(/'/g, "''");
|
|
2210
2327
|
return `EXEC sp_rename N'${t(e.from)}', N'${t(e.to)}';`;
|
|
2211
|
-
},
|
|
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) => {
|
|
2212
2329
|
let t = (e) => e.replace(/'/g, "''");
|
|
2213
2330
|
return `EXEC sp_rename N'${t(e.table)}.${t(e.from)}', N'${t(e.to)}', N'INDEX';`;
|
|
2214
|
-
},
|
|
2215
|
-
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(", ")})`;
|
|
2216
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)}');`;
|
|
2217
|
-
},
|
|
2334
|
+
}, Pr = (e, t, n) => {
|
|
2218
2335
|
let r = n.find((t) => t.tableName === e);
|
|
2219
2336
|
if (!r) return [];
|
|
2220
2337
|
let i = [];
|
|
2221
2338
|
for (let n of t) {
|
|
2222
2339
|
let t = r.fields[n];
|
|
2223
|
-
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"};`);
|
|
2224
2341
|
}
|
|
2225
2342
|
return i;
|
|
2226
|
-
},
|
|
2227
|
-
let t = e.fields.map((e) =>
|
|
2228
|
-
return `ALTER TABLE ${
|
|
2229
|
-
},
|
|
2230
|
-
let n = e.fields.map((n) =>
|
|
2231
|
-
return `ALTER TABLE ${
|
|
2232
|
-
},
|
|
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) => {
|
|
2233
2350
|
let n = [], r = t(e.table), i = t(e.column), a = t(e.targetTable), o = t(e.targetColumn);
|
|
2234
2351
|
if (e.orphanPolicy) {
|
|
2235
2352
|
let t = `${i} IS NOT NULL AND ${i} NOT IN (SELECT ${o} FROM ${a})`;
|
|
@@ -2247,11 +2364,11 @@ BEGIN
|
|
|
2247
2364
|
setNull: "SET NULL",
|
|
2248
2365
|
noAction: "NO ACTION"
|
|
2249
2366
|
}[e.onDelete]}`), n.push(s.join(" ") + ";"), n;
|
|
2250
|
-
},
|
|
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) => {
|
|
2251
2368
|
let n = e.values.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ");
|
|
2252
2369
|
return `ALTER TABLE ${t(e.table)} ADD CONSTRAINT ${t(`${e.table}_${e.column}_check`)} CHECK (${t(e.column)} IN (${n}));`;
|
|
2253
|
-
},
|
|
2254
|
-
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);
|
|
2255
2372
|
if (u) {
|
|
2256
2373
|
let t = r === "mssql" ? `${o(i.name)} ${u}` : `${o(i.name)} ${s} ${u}`;
|
|
2257
2374
|
return {
|
|
@@ -2279,7 +2396,7 @@ BEGIN
|
|
|
2279
2396
|
post: f ? [f] : []
|
|
2280
2397
|
};
|
|
2281
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;
|
|
2282
|
-
},
|
|
2399
|
+
}, Xr = (e) => {
|
|
2283
2400
|
let t = new Map(e.map((e) => [e.tableName, e]));
|
|
2284
2401
|
return (e, n) => {
|
|
2285
2402
|
let r = t.get(e);
|
|
@@ -2299,297 +2416,416 @@ BEGIN
|
|
|
2299
2416
|
...o
|
|
2300
2417
|
};
|
|
2301
2418
|
};
|
|
2302
|
-
},
|
|
2303
|
-
process.stderr.write(
|
|
2304
|
-
})),
|
|
2305
|
-
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();
|
|
2306
2423
|
return Object.assign(e, { message: r });
|
|
2307
|
-
}),
|
|
2308
|
-
let
|
|
2309
|
-
|
|
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) {
|
|
2310
2496
|
case "create-table": {
|
|
2311
2497
|
let t = yield* e.onDialectOrElse({
|
|
2312
|
-
mysql: () =>
|
|
2313
|
-
mssql: () =>
|
|
2314
|
-
sqlite: () =>
|
|
2315
|
-
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))
|
|
2316
2502
|
});
|
|
2317
|
-
for (let n of t) yield*
|
|
2503
|
+
for (let n of t) yield* X(e, n, o.kind);
|
|
2318
2504
|
break;
|
|
2319
2505
|
}
|
|
2320
2506
|
case "drop-table": {
|
|
2321
|
-
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);
|
|
2322
2508
|
yield* e.onDialectOrElse({
|
|
2323
|
-
mysql: () =>
|
|
2324
|
-
orElse: () =>
|
|
2509
|
+
mysql: () => X(e, r(W), o.kind),
|
|
2510
|
+
orElse: () => X(e, r(U), o.kind)
|
|
2325
2511
|
});
|
|
2326
2512
|
break;
|
|
2327
2513
|
}
|
|
2328
2514
|
case "drop-column": {
|
|
2329
2515
|
let t = process.env.VOLTRO_SOFT_DROP === "1", n = (/* @__PURE__ */ new Date()).toISOString();
|
|
2330
2516
|
yield* e.onDialectOrElse({
|
|
2331
|
-
mysql: () =>
|
|
2332
|
-
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)
|
|
2333
2519
|
});
|
|
2334
2520
|
break;
|
|
2335
2521
|
}
|
|
2336
2522
|
case "rename-column":
|
|
2337
2523
|
yield* e.onDialectOrElse({
|
|
2338
|
-
mysql: () =>
|
|
2339
|
-
mssql: () =>
|
|
2340
|
-
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)
|
|
2341
2527
|
});
|
|
2342
2528
|
break;
|
|
2343
2529
|
case "add-index":
|
|
2344
2530
|
yield* e.onDialectOrElse({
|
|
2345
|
-
mysql: () =>
|
|
2346
|
-
mssql: () =>
|
|
2347
|
-
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)
|
|
2348
2534
|
});
|
|
2349
2535
|
break;
|
|
2350
2536
|
case "drop-index":
|
|
2351
2537
|
yield* e.onDialectOrElse({
|
|
2352
|
-
mysql: () =>
|
|
2353
|
-
mssql: () =>
|
|
2354
|
-
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)
|
|
2355
2541
|
});
|
|
2356
2542
|
break;
|
|
2357
2543
|
case "rename-table":
|
|
2358
2544
|
yield* e.onDialectOrElse({
|
|
2359
|
-
mysql: () =>
|
|
2360
|
-
mssql: () =>
|
|
2361
|
-
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)
|
|
2362
2548
|
});
|
|
2363
2549
|
break;
|
|
2364
2550
|
case "rename-index":
|
|
2365
2551
|
yield* e.onDialectOrElse({
|
|
2366
|
-
mysql: () =>
|
|
2367
|
-
mssql: () =>
|
|
2368
|
-
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)
|
|
2369
2555
|
});
|
|
2370
2556
|
break;
|
|
2371
2557
|
case "add-unique":
|
|
2372
2558
|
yield* e.onDialectOrElse({
|
|
2373
|
-
mysql: () =>
|
|
2374
|
-
sqlite: () =>
|
|
2375
|
-
mssql: () =>
|
|
2376
|
-
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)
|
|
2377
2563
|
});
|
|
2378
2564
|
break;
|
|
2379
2565
|
case "drop-unique":
|
|
2380
2566
|
yield* e.onDialectOrElse({
|
|
2381
|
-
mysql: () =>
|
|
2382
|
-
sqlite: () =>
|
|
2383
|
-
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)
|
|
2384
2570
|
});
|
|
2385
2571
|
break;
|
|
2386
2572
|
case "add-unique-composite":
|
|
2387
2573
|
yield* e.onDialectOrElse({
|
|
2388
|
-
mysql: () =>
|
|
2389
|
-
sqlite: () =>
|
|
2390
|
-
mssql: () =>
|
|
2391
|
-
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)
|
|
2392
2578
|
});
|
|
2393
2579
|
break;
|
|
2394
2580
|
case "drop-unique-composite":
|
|
2395
2581
|
yield* e.onDialectOrElse({
|
|
2396
|
-
mysql: () =>
|
|
2397
|
-
sqlite: () =>
|
|
2398
|
-
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)
|
|
2399
2585
|
});
|
|
2400
2586
|
break;
|
|
2401
2587
|
case "add-foreign-key":
|
|
2402
2588
|
yield* e.onDialectOrElse({
|
|
2403
|
-
mysql: () =>
|
|
2404
|
-
sqlite: () =>
|
|
2405
|
-
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)
|
|
2406
2592
|
});
|
|
2407
2593
|
break;
|
|
2408
2594
|
case "add-column": {
|
|
2409
|
-
let n =
|
|
2410
|
-
mysql: () =>
|
|
2411
|
-
mssql: () =>
|
|
2412
|
-
sqlite: () =>
|
|
2413
|
-
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")
|
|
2414
2600
|
}));
|
|
2415
|
-
for (let t of n.pre) yield*
|
|
2416
|
-
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}`;
|
|
2417
2603
|
else {
|
|
2418
|
-
let t = n.batchSize ?? 5e3, r = n.sleepMs ?? 0, i,
|
|
2604
|
+
let t = n.batchSize ?? 5e3, r = n.sleepMs ?? 0, i, a = 0;
|
|
2419
2605
|
for (;;) {
|
|
2420
|
-
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)}`;
|
|
2421
2607
|
if (s.length === 0) break;
|
|
2422
2608
|
let c = s.map((e) => e.id);
|
|
2423
|
-
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({
|
|
2424
2610
|
scope: "voltro:migrate",
|
|
2425
2611
|
mode: "sql-batched"
|
|
2426
|
-
})), i = s[s.length - 1].id, r > 0 && (yield*
|
|
2612
|
+
})), i = s[s.length - 1].id, r > 0 && (yield* b.sleep(`${r} millis`));
|
|
2427
2613
|
}
|
|
2428
2614
|
}
|
|
2429
2615
|
else if (n.backfillKind === "js" && n.backfillJs) {
|
|
2430
|
-
let t = n.batchSize ?? 1e3, r = n.sleepMs ?? 0, i,
|
|
2616
|
+
let t = n.batchSize ?? 1e3, r = n.sleepMs ?? 0, i, a = 0;
|
|
2431
2617
|
for (;;) {
|
|
2432
|
-
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)}`;
|
|
2433
2619
|
if (s.length === 0) break;
|
|
2434
2620
|
for (let t of s) {
|
|
2435
|
-
let r = yield*
|
|
2621
|
+
let r = yield* b.tryPromise({
|
|
2436
2622
|
try: () => Promise.resolve(n.backfillJs({ id: t.id })),
|
|
2437
2623
|
catch: (e) => e
|
|
2438
|
-
}).pipe(
|
|
2439
|
-
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}`;
|
|
2440
2626
|
}
|
|
2441
|
-
|
|
2627
|
+
a += s.length, yield* b.logInfo(`backfill ${o.table}.${o.column.name}: ${a} rows processed`).pipe(b.annotateLogs({
|
|
2442
2628
|
scope: "voltro:migrate",
|
|
2443
|
-
table:
|
|
2444
|
-
column:
|
|
2445
|
-
})), 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`));
|
|
2446
2632
|
}
|
|
2447
2633
|
}
|
|
2448
|
-
for (let t of n.post) yield*
|
|
2634
|
+
for (let t of n.post) yield* X(e, t, o.kind);
|
|
2449
2635
|
break;
|
|
2450
2636
|
}
|
|
2451
2637
|
case "alter-column-nullability":
|
|
2452
2638
|
yield* e.onDialectOrElse({
|
|
2453
2639
|
mysql: () => {
|
|
2454
|
-
let t =
|
|
2455
|
-
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.`));
|
|
2456
2642
|
},
|
|
2457
2643
|
mssql: () => {
|
|
2458
|
-
let t =
|
|
2459
|
-
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}'.`));
|
|
2460
2646
|
},
|
|
2461
|
-
sqlite: () =>
|
|
2462
|
-
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)
|
|
2463
2649
|
});
|
|
2464
2650
|
break;
|
|
2465
2651
|
case "alter-column-default": {
|
|
2466
|
-
let t =
|
|
2652
|
+
let t = Yn(n.declared, o.table, o.column);
|
|
2467
2653
|
yield* e.onDialectOrElse({
|
|
2468
|
-
mysql: () =>
|
|
2469
|
-
mssql: () =>
|
|
2470
|
-
sqlite: () =>
|
|
2471
|
-
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)
|
|
2472
2658
|
});
|
|
2473
2659
|
break;
|
|
2474
2660
|
}
|
|
2475
2661
|
case "alter-column-type": {
|
|
2476
|
-
let r =
|
|
2662
|
+
let r = Bn(Yn(n.declared, o.table, o.column) ?? { type: o.to }), i = o.using ? ` USING ${o.using}` : "";
|
|
2477
2663
|
if (t.classification !== "online-required") {
|
|
2478
2664
|
yield* e.onDialectOrElse({
|
|
2479
2665
|
mysql: () => {
|
|
2480
|
-
let t =
|
|
2481
|
-
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.`));
|
|
2482
2668
|
},
|
|
2483
2669
|
mssql: () => {
|
|
2484
|
-
let t =
|
|
2485
|
-
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}'.`));
|
|
2486
2672
|
},
|
|
2487
|
-
sqlite: () =>
|
|
2488
|
-
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)
|
|
2489
2675
|
});
|
|
2490
2676
|
break;
|
|
2491
2677
|
}
|
|
2492
2678
|
yield* e.onDialectOrElse({
|
|
2493
|
-
mysql: () =>
|
|
2494
|
-
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
|
|
2495
2681
|
});
|
|
2496
|
-
let
|
|
2497
|
-
yield* e.unsafe(`ALTER TABLE ${
|
|
2498
|
-
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;
|
|
2499
2685
|
for (;;) {
|
|
2500
|
-
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)}`;
|
|
2501
2687
|
if (t.length === 0) break;
|
|
2502
|
-
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;
|
|
2503
2689
|
}
|
|
2504
|
-
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)}`);
|
|
2505
2691
|
break;
|
|
2506
2692
|
}
|
|
2507
2693
|
case "drop-foreign-key":
|
|
2508
2694
|
yield* e.onDialectOrElse({
|
|
2509
|
-
mysql: () =>
|
|
2510
|
-
sqlite: () =>
|
|
2511
|
-
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)
|
|
2512
2698
|
});
|
|
2513
2699
|
break;
|
|
2514
2700
|
case "add-check":
|
|
2515
2701
|
yield* e.onDialectOrElse({
|
|
2516
|
-
mysql: () =>
|
|
2517
|
-
sqlite: () =>
|
|
2518
|
-
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)
|
|
2519
2705
|
});
|
|
2520
2706
|
break;
|
|
2521
2707
|
case "drop-check":
|
|
2522
2708
|
yield* e.onDialectOrElse({
|
|
2523
|
-
mysql: () =>
|
|
2524
|
-
sqlite: () =>
|
|
2525
|
-
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)
|
|
2526
2712
|
});
|
|
2527
2713
|
break;
|
|
2528
2714
|
}
|
|
2529
2715
|
return {
|
|
2530
|
-
op:
|
|
2716
|
+
op: o,
|
|
2531
2717
|
status: "applied",
|
|
2532
|
-
durationMs: Number((process.hrtime.bigint() -
|
|
2718
|
+
durationMs: Number((process.hrtime.bigint() - i) / 1000000n)
|
|
2719
|
+
};
|
|
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"
|
|
2533
2733
|
};
|
|
2534
|
-
|
|
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* () {
|
|
2535
2754
|
let r = t.operations.filter((e) => e.blocked);
|
|
2536
|
-
if (r.length > 0) return yield*
|
|
2537
|
-
yield*
|
|
2538
|
-
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";
|
|
2539
2761
|
try {
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
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));
|
|
2548
2771
|
})));
|
|
2549
|
-
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
|
+
}
|
|
2550
2777
|
} finally {
|
|
2551
|
-
yield*
|
|
2778
|
+
yield* E(e).pipe(b.orDie);
|
|
2552
2779
|
}
|
|
2553
|
-
let
|
|
2554
|
-
if (
|
|
2555
|
-
let e =
|
|
2556
|
-
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.`));
|
|
2557
2784
|
}
|
|
2558
|
-
let
|
|
2559
|
-
|
|
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`
|
|
2560
2787
|
INSERT INTO ${e("_voltro_migration_plans")}
|
|
2561
2788
|
(${e("id")}, ${e("fingerprint")}, ${e("liveFingerprint")}, ${e("operations")},
|
|
2562
2789
|
${e("appliedBy")}, ${e("environment")}, ${e("source")},
|
|
2563
2790
|
${e("durationMs")}, ${e("appliedAt")}, ${e("notes")})
|
|
2564
2791
|
VALUES
|
|
2565
|
-
(${
|
|
2792
|
+
(${_}, ${t.toFingerprint}, ${m.fromFingerprint}, ${JSON.stringify(x)},
|
|
2566
2793
|
${n.appliedBy}, ${n.environment}, ${n.source},
|
|
2567
|
-
${
|
|
2568
|
-
`,
|
|
2569
|
-
|
|
2570
|
-
|
|
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,
|
|
2571
2800
|
fingerprint: t.toFingerprint,
|
|
2572
|
-
operations:
|
|
2801
|
+
operations: x,
|
|
2573
2802
|
appliedBy: n.appliedBy,
|
|
2574
2803
|
environment: n.environment,
|
|
2575
|
-
durationMs:
|
|
2576
|
-
appliedOps:
|
|
2804
|
+
durationMs: g,
|
|
2805
|
+
appliedOps: h,
|
|
2577
2806
|
source: n.source,
|
|
2578
2807
|
...n.notes ? { notes: n.notes } : {}
|
|
2579
2808
|
};
|
|
2580
|
-
}),
|
|
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`
|
|
2581
2810
|
SELECT fingerprint
|
|
2582
2811
|
FROM _voltro_migration_plans
|
|
2583
2812
|
ORDER BY ${e("appliedAt")} DESC
|
|
2584
2813
|
LIMIT 1
|
|
2585
|
-
`.pipe(
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
}
|
|
2592
|
-
|
|
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);
|
|
2593
2829
|
return r === n ? {
|
|
2594
2830
|
kind: "up-to-date",
|
|
2595
2831
|
fingerprint: n
|
|
@@ -2598,23 +2834,23 @@ BEGIN
|
|
|
2598
2834
|
expected: n,
|
|
2599
2835
|
...r === void 0 ? {} : { actual: r }
|
|
2600
2836
|
};
|
|
2601
|
-
}),
|
|
2837
|
+
}), di = [
|
|
2602
2838
|
"pgeagertest_",
|
|
2603
2839
|
"mig_e2e_",
|
|
2604
2840
|
"mig_test_",
|
|
2605
2841
|
"mysqleagertest_",
|
|
2606
2842
|
"mssqleagertest_",
|
|
2607
2843
|
"sqliteeagertest_"
|
|
2608
|
-
],
|
|
2844
|
+
], fi = (e) => di.some((t) => e.startsWith(t)), pi = (e, t) => b.gen(function* () {
|
|
2609
2845
|
if (process.env.VOLTRO_TEST_FIXTURE_GC === "off") return [];
|
|
2610
|
-
let n = t.filter((e) =>
|
|
2846
|
+
let n = t.filter((e) => fi(e.name)).map((e) => e.name);
|
|
2611
2847
|
if (n.length === 0) return [];
|
|
2612
|
-
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));
|
|
2613
2849
|
return n;
|
|
2614
|
-
}),
|
|
2850
|
+
}), mi = () => {
|
|
2615
2851
|
let e = (process.env.VOLTRO_DB_IGNORE_TABLES ?? "").split(",").map((e) => e.trim()).filter((e) => e.length > 0);
|
|
2616
2852
|
return e.length > 0 ? { ignoreTables: e } : {};
|
|
2617
|
-
},
|
|
2853
|
+
}, hi = (e) => {
|
|
2618
2854
|
if (e === void 0) return;
|
|
2619
2855
|
let t = e.trim();
|
|
2620
2856
|
if (t === "") return;
|
|
@@ -2624,83 +2860,87 @@ BEGIN
|
|
|
2624
2860
|
kind: "tables",
|
|
2625
2861
|
tables: new Set(n)
|
|
2626
2862
|
} : void 0;
|
|
2627
|
-
},
|
|
2863
|
+
}, gi = (e) => typeof e.table == "string" ? e.table : void 0, _i = (e, t = { kind: "all" }) => ({
|
|
2628
2864
|
...e,
|
|
2629
2865
|
operations: e.operations.map((e) => {
|
|
2630
2866
|
if (!e.blocked || e.classification !== "lossy") return e;
|
|
2631
2867
|
if (t?.kind === "tables") {
|
|
2632
|
-
let n =
|
|
2868
|
+
let n = gi(e.op);
|
|
2633
2869
|
if (n === void 0 || !t.tables.has(n)) return e;
|
|
2634
2870
|
}
|
|
2635
2871
|
let { blocked: n, ...r } = e;
|
|
2636
2872
|
return r;
|
|
2637
2873
|
})
|
|
2638
|
-
}),
|
|
2639
|
-
let t =
|
|
2640
|
-
return t === void 0 ?
|
|
2641
|
-
}),
|
|
2642
|
-
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;
|
|
2643
2879
|
if (process.env.VOLTRO_MIGRATE_FORCE !== "1") {
|
|
2644
|
-
let t = P(
|
|
2645
|
-
if ((yield*
|
|
2880
|
+
let t = M(P(i, r));
|
|
2881
|
+
if ((yield* ci(e)) === t) return yield* vi(e, r, i), {
|
|
2646
2882
|
kind: "up-to-date",
|
|
2647
2883
|
fingerprint: t
|
|
2648
2884
|
};
|
|
2649
2885
|
}
|
|
2650
|
-
let a = yield*
|
|
2651
|
-
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({
|
|
2652
2888
|
scope: "voltro:migrate",
|
|
2653
2889
|
count: o.length
|
|
2654
|
-
})), a = yield*
|
|
2655
|
-
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({
|
|
2656
2892
|
declared: i,
|
|
2657
|
-
live: { tables:
|
|
2893
|
+
live: { tables: c(a.tables) },
|
|
2658
2894
|
dialect: r,
|
|
2659
|
-
...
|
|
2895
|
+
...mi()
|
|
2660
2896
|
});
|
|
2661
|
-
if (
|
|
2662
|
-
let e =
|
|
2663
|
-
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 {
|
|
2664
2900
|
kind: "refused-blocked",
|
|
2665
|
-
plan:
|
|
2901
|
+
plan: l
|
|
2666
2902
|
};
|
|
2667
2903
|
}
|
|
2668
|
-
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
|
+
}), {
|
|
2669
2909
|
kind: "up-to-date",
|
|
2670
|
-
fingerprint:
|
|
2671
|
-
} : {
|
|
2910
|
+
fingerprint: l.toFingerprint
|
|
2911
|
+
}) : {
|
|
2672
2912
|
kind: "applied",
|
|
2673
|
-
applied: yield*
|
|
2913
|
+
applied: yield* oi(e, l, {
|
|
2674
2914
|
declared: t,
|
|
2675
2915
|
appliedBy: n.appliedBy,
|
|
2676
2916
|
environment: n.environment === "prod" ? "dev" : n.environment,
|
|
2677
2917
|
source: "auto-diff",
|
|
2678
|
-
replan: (e) =>
|
|
2679
|
-
let t = yield*
|
|
2680
|
-
return
|
|
2918
|
+
replan: (e) => b.gen(function* () {
|
|
2919
|
+
let t = yield* Ln(e);
|
|
2920
|
+
return rn({
|
|
2681
2921
|
declared: i,
|
|
2682
|
-
live: { tables:
|
|
2922
|
+
live: { tables: c(t.tables) },
|
|
2683
2923
|
dialect: r,
|
|
2684
|
-
...
|
|
2924
|
+
...mi()
|
|
2685
2925
|
});
|
|
2686
2926
|
})
|
|
2687
2927
|
}),
|
|
2688
|
-
plan:
|
|
2928
|
+
plan: l
|
|
2689
2929
|
};
|
|
2690
|
-
}),
|
|
2930
|
+
}), bi = (e, t, n) => process.env.VOLTRO_AUTO_MIGRATE === "0" ? b.succeed({
|
|
2691
2931
|
kind: "skipped",
|
|
2692
2932
|
reason: "VOLTRO_AUTO_MIGRATE=0"
|
|
2693
|
-
}) : n.environment === "prod" ?
|
|
2933
|
+
}) : n.environment === "prod" ? ui(e, t) : yi(e, t, n), xi = (e, t) => {
|
|
2694
2934
|
switch (e.kind) {
|
|
2695
2935
|
case "skipped": return `auto-migrate: skipped (${e.reason})`;
|
|
2696
|
-
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)})`;
|
|
2697
2937
|
case "applied": {
|
|
2698
2938
|
let t = e.plan.summary;
|
|
2699
|
-
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)}`;
|
|
2700
2940
|
}
|
|
2701
|
-
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.";
|
|
2702
2942
|
case "refused-blocked": {
|
|
2703
|
-
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 = [""];
|
|
2704
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) {
|
|
2705
2945
|
let t = /* @__PURE__ */ new Map(), n = /* @__PURE__ */ new Map();
|
|
2706
2946
|
for (let r of e.plan.operations) {
|
|
@@ -2717,7 +2957,7 @@ BEGIN
|
|
|
2717
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");
|
|
2718
2958
|
}
|
|
2719
2959
|
}
|
|
2720
|
-
},
|
|
2960
|
+
}, Si = /* @__PURE__ */ new Set([
|
|
2721
2961
|
"node_modules",
|
|
2722
2962
|
"dist",
|
|
2723
2963
|
"build",
|
|
@@ -2726,24 +2966,24 @@ BEGIN
|
|
|
2726
2966
|
".next",
|
|
2727
2967
|
".git",
|
|
2728
2968
|
".framework"
|
|
2729
|
-
]),
|
|
2969
|
+
]), Ci = async (e) => {
|
|
2730
2970
|
let t = [], n = async (e) => {
|
|
2731
|
-
let r = await
|
|
2971
|
+
let r = await ee.readdir(e, { withFileTypes: !0 }).catch(() => []);
|
|
2732
2972
|
for (let i of r) {
|
|
2733
|
-
if (
|
|
2734
|
-
let r =
|
|
2735
|
-
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);
|
|
2736
2976
|
}
|
|
2737
2977
|
};
|
|
2738
2978
|
return await n(e), t.sort((e, t) => {
|
|
2739
2979
|
let n = e.split("/").pop() ?? e, r = t.split("/").pop() ?? t;
|
|
2740
2980
|
return n.localeCompare(r);
|
|
2741
2981
|
});
|
|
2742
|
-
},
|
|
2982
|
+
}, wi = async (e) => {
|
|
2743
2983
|
let t = [];
|
|
2744
2984
|
for (let n of e) {
|
|
2745
|
-
let e = (await import(
|
|
2746
|
-
if (!
|
|
2985
|
+
let e = (await import(ne(n).href)).default;
|
|
2986
|
+
if (!o(e)) {
|
|
2747
2987
|
process.stderr.write(`[voltro:migrate] ${n} has no default-exported \`migration(...)\` — skipped\n`);
|
|
2748
2988
|
continue;
|
|
2749
2989
|
}
|
|
@@ -2753,11 +2993,11 @@ BEGIN
|
|
|
2753
2993
|
});
|
|
2754
2994
|
}
|
|
2755
2995
|
return t;
|
|
2756
|
-
},
|
|
2996
|
+
}, Ti = (e) => e`
|
|
2757
2997
|
SELECT ${e("id")}
|
|
2758
2998
|
FROM ${e("_voltro_migration_plans")}
|
|
2759
2999
|
WHERE ${e("source")} = 'file'
|
|
2760
|
-
`.pipe(
|
|
3000
|
+
`.pipe(b.map((e) => e.map((e) => e.id)), b.catchAll(() => b.succeed([]))), Ei = class extends Error {
|
|
2761
3001
|
migrationId;
|
|
2762
3002
|
file;
|
|
2763
3003
|
cause;
|
|
@@ -2765,28 +3005,28 @@ BEGIN
|
|
|
2765
3005
|
constructor(e, t, n) {
|
|
2766
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";
|
|
2767
3007
|
}
|
|
2768
|
-
},
|
|
3008
|
+
}, Di = (e, t, n, r) => b.gen(function* () {
|
|
2769
3009
|
let i = Date.now(), a = (/* @__PURE__ */ new Date()).toISOString(), o = {
|
|
2770
3010
|
sql: e,
|
|
2771
3011
|
log: {
|
|
2772
|
-
info: (e, n) =>
|
|
3012
|
+
info: (e, n) => b.logInfo(e).pipe(b.annotateLogs({
|
|
2773
3013
|
scope: `migrations:file:${t.migration.id}`,
|
|
2774
3014
|
...n ?? {}
|
|
2775
3015
|
})),
|
|
2776
|
-
warn: (e, n) =>
|
|
3016
|
+
warn: (e, n) => b.logWarning(e).pipe(b.annotateLogs({
|
|
2777
3017
|
scope: `migrations:file:${t.migration.id}`,
|
|
2778
3018
|
...n ?? {}
|
|
2779
3019
|
}))
|
|
2780
3020
|
},
|
|
2781
3021
|
appliedAt: a
|
|
2782
3022
|
}, s = t.migration.up(o);
|
|
2783
|
-
|
|
3023
|
+
b.isEffect(s) ? yield* s : yield* b.tryPromise({
|
|
2784
3024
|
try: () => s,
|
|
2785
3025
|
catch: (e) => e
|
|
2786
3026
|
});
|
|
2787
3027
|
let c = Date.now() - i;
|
|
2788
|
-
!
|
|
2789
|
-
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);
|
|
2790
3030
|
return yield* e`
|
|
2791
3031
|
INSERT INTO ${e("_voltro_migration_plans")}
|
|
2792
3032
|
(${e("id")}, ${e("fingerprint")}, ${e("operations")},
|
|
@@ -2799,94 +3039,94 @@ BEGIN
|
|
|
2799
3039
|
}])},
|
|
2800
3040
|
${r}, ${n}, ${"file"},
|
|
2801
3041
|
${c}, ${l}, ${t.migration.description})
|
|
2802
|
-
`.pipe(
|
|
2803
|
-
}),
|
|
2804
|
-
let n = yield*
|
|
2805
|
-
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")),
|
|
2806
3046
|
catch: (e) => e
|
|
2807
3047
|
});
|
|
2808
3048
|
if (n.length === 0) return {
|
|
2809
3049
|
pending: [],
|
|
2810
3050
|
skipped: []
|
|
2811
3051
|
};
|
|
2812
|
-
let r = yield*
|
|
2813
|
-
try: () =>
|
|
3052
|
+
let r = yield* b.tryPromise({
|
|
3053
|
+
try: () => wi(n),
|
|
2814
3054
|
catch: (e) => e
|
|
2815
3055
|
});
|
|
2816
3056
|
if (r.length === 0) return {
|
|
2817
3057
|
pending: [],
|
|
2818
3058
|
skipped: []
|
|
2819
3059
|
};
|
|
2820
|
-
let i = new Set(yield*
|
|
3060
|
+
let i = new Set(yield* Ti(e));
|
|
2821
3061
|
return {
|
|
2822
3062
|
pending: r.filter((e) => !i.has(e.migration.id)),
|
|
2823
3063
|
skipped: r.filter((e) => i.has(e.migration.id)).map((e) => e.migration.id)
|
|
2824
3064
|
};
|
|
2825
|
-
}),
|
|
2826
|
-
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);
|
|
2827
3067
|
if (n.length === 0) return {
|
|
2828
3068
|
applied: [],
|
|
2829
3069
|
skipped: r
|
|
2830
3070
|
};
|
|
2831
|
-
yield*
|
|
3071
|
+
yield* T(e);
|
|
2832
3072
|
let i = [];
|
|
2833
3073
|
try {
|
|
2834
3074
|
for (let r of n) {
|
|
2835
|
-
yield*
|
|
3075
|
+
yield* b.logInfo("file-based migration applying").pipe(b.annotateLogs({
|
|
2836
3076
|
scope: "voltro:migrate",
|
|
2837
3077
|
id: r.migration.id,
|
|
2838
3078
|
file: r.file
|
|
2839
3079
|
}));
|
|
2840
|
-
let { durationMs: n } = yield*
|
|
3080
|
+
let { durationMs: n } = yield* Di(e, r, t.env, t.appliedBy);
|
|
2841
3081
|
i.push({
|
|
2842
3082
|
id: r.migration.id,
|
|
2843
3083
|
durationMs: n
|
|
2844
|
-
}), yield*
|
|
3084
|
+
}), yield* b.logInfo("file-based migration applied").pipe(b.annotateLogs({
|
|
2845
3085
|
scope: "voltro:migrate",
|
|
2846
3086
|
id: r.migration.id,
|
|
2847
3087
|
durationMs: n
|
|
2848
3088
|
}));
|
|
2849
3089
|
}
|
|
2850
3090
|
} finally {
|
|
2851
|
-
yield*
|
|
3091
|
+
yield* E(e).pipe(b.orDie);
|
|
2852
3092
|
}
|
|
2853
3093
|
return {
|
|
2854
3094
|
applied: i,
|
|
2855
3095
|
skipped: r
|
|
2856
3096
|
};
|
|
2857
|
-
}),
|
|
3097
|
+
}), ji = (e, t) => e`
|
|
2858
3098
|
SELECT ${e("id")}
|
|
2859
3099
|
FROM ${e("_voltro_migration_plans")}
|
|
2860
3100
|
WHERE ${e("id")} = ${t} AND ${e("source")} = 'file'
|
|
2861
3101
|
LIMIT 1
|
|
2862
|
-
`,
|
|
2863
|
-
if (!(yield*
|
|
2864
|
-
let n = yield*
|
|
2865
|
-
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")),
|
|
2866
3106
|
catch: (e) => e
|
|
2867
|
-
}), r = (yield*
|
|
2868
|
-
try: () =>
|
|
3107
|
+
}), r = (yield* b.tryPromise({
|
|
3108
|
+
try: () => wi(n),
|
|
2869
3109
|
catch: (e) => e
|
|
2870
3110
|
})).find((e) => e.migration.id === t.id);
|
|
2871
|
-
if (!r) return yield*
|
|
2872
|
-
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);
|
|
2873
3113
|
let i = Date.now();
|
|
2874
3114
|
try {
|
|
2875
3115
|
let n = {
|
|
2876
3116
|
sql: e,
|
|
2877
3117
|
log: {
|
|
2878
|
-
info: (e, n) =>
|
|
3118
|
+
info: (e, n) => b.logInfo(e).pipe(b.annotateLogs({
|
|
2879
3119
|
scope: `migrations:file:${t.id}`,
|
|
2880
3120
|
...n ?? {}
|
|
2881
3121
|
})),
|
|
2882
|
-
warn: (e, n) =>
|
|
3122
|
+
warn: (e, n) => b.logWarning(e).pipe(b.annotateLogs({
|
|
2883
3123
|
scope: `migrations:file:${t.id}`,
|
|
2884
3124
|
...n ?? {}
|
|
2885
3125
|
}))
|
|
2886
3126
|
},
|
|
2887
3127
|
appliedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2888
3128
|
}, a = r.migration.down(n);
|
|
2889
|
-
|
|
3129
|
+
b.isEffect(a) ? yield* a : yield* b.tryPromise({
|
|
2890
3130
|
try: () => a,
|
|
2891
3131
|
catch: (e) => e
|
|
2892
3132
|
});
|
|
@@ -2896,19 +3136,19 @@ BEGIN
|
|
|
2896
3136
|
durationMs: o
|
|
2897
3137
|
};
|
|
2898
3138
|
} finally {
|
|
2899
|
-
yield*
|
|
3139
|
+
yield* E(e).pipe(b.orDie);
|
|
2900
3140
|
}
|
|
2901
|
-
}),
|
|
2902
|
-
let t = yield*
|
|
3141
|
+
}), Ni = (e) => b.gen(function* () {
|
|
3142
|
+
let t = yield* S.SqlClient, n = yield* t`
|
|
2903
3143
|
SELECT id, fingerprint, appliedAt FROM _voltro_migration_plans
|
|
2904
3144
|
WHERE source = 'auto-diff'
|
|
2905
3145
|
AND squashedAt IS NULL
|
|
2906
3146
|
AND appliedAt < ${e.before}
|
|
2907
3147
|
ORDER BY appliedAt DESC
|
|
2908
3148
|
`;
|
|
2909
|
-
if (n.length === 0) return yield*
|
|
3149
|
+
if (n.length === 0) return yield* b.fail(/* @__PURE__ */ Error(`squash: no rows applied before ${e.before}`));
|
|
2910
3150
|
let r = n[0];
|
|
2911
|
-
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.`));
|
|
2912
3152
|
yield* t`
|
|
2913
3153
|
UPDATE _voltro_migration_plans
|
|
2914
3154
|
SET squashedAt = CURRENT_TIMESTAMP
|
|
@@ -2943,7 +3183,7 @@ BEGIN
|
|
|
2943
3183
|
snapshotFingerprint: e.fingerprint,
|
|
2944
3184
|
snapshotId: i
|
|
2945
3185
|
};
|
|
2946
|
-
}),
|
|
3186
|
+
}), Pi = { safe: !0 }, Fi = (e) => {
|
|
2947
3187
|
switch (e.kind) {
|
|
2948
3188
|
case "drop-column": return {
|
|
2949
3189
|
safe: !1,
|
|
@@ -2970,7 +3210,7 @@ BEGIN
|
|
|
2970
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`,
|
|
2971
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"
|
|
2972
3212
|
};
|
|
2973
|
-
case "alter-column-nullability": return e.toNullable ?
|
|
3213
|
+
case "alter-column-nullability": return e.toNullable ? Pi : {
|
|
2974
3214
|
safe: !1,
|
|
2975
3215
|
reason: `old instances may INSERT "${e.table}" without (or with NULL in) "${e.column}", which the new NOT NULL rejects`,
|
|
2976
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)`
|
|
@@ -3000,12 +3240,12 @@ BEGIN
|
|
|
3000
3240
|
case "drop-unique":
|
|
3001
3241
|
case "drop-unique-composite":
|
|
3002
3242
|
case "drop-foreign-key":
|
|
3003
|
-
case "drop-check": return
|
|
3243
|
+
case "drop-check": return Pi;
|
|
3004
3244
|
}
|
|
3005
|
-
},
|
|
3245
|
+
}, Ii = (e) => {
|
|
3006
3246
|
let t = [];
|
|
3007
3247
|
for (let n of e) {
|
|
3008
|
-
let e =
|
|
3248
|
+
let e = Fi(n.op);
|
|
3009
3249
|
e.safe || t.push({
|
|
3010
3250
|
op: n,
|
|
3011
3251
|
reason: e.reason,
|
|
@@ -3013,7 +3253,7 @@ BEGIN
|
|
|
3013
3253
|
});
|
|
3014
3254
|
}
|
|
3015
3255
|
return t;
|
|
3016
|
-
},
|
|
3256
|
+
}, Li = (e) => {
|
|
3017
3257
|
if (!e.enabled || e.unsafeCount === 0) return {
|
|
3018
3258
|
refuse: !1,
|
|
3019
3259
|
warnForced: !1,
|
|
@@ -3029,10 +3269,127 @@ BEGIN
|
|
|
3029
3269
|
warnForced: !1,
|
|
3030
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.`
|
|
3031
3271
|
};
|
|
3032
|
-
},
|
|
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) => ({
|
|
3033
3390
|
_tag: "RawSqlFragment",
|
|
3034
3391
|
strings: [...e],
|
|
3035
3392
|
values: [...t]
|
|
3036
|
-
}),
|
|
3393
|
+
}), Ki = (e) => typeof e == "object" && !!e && e._tag === "RawSqlFragment";
|
|
3037
3394
|
//#endregion
|
|
3038
|
-
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 };
|