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