@voltro/database 0.45.0 → 0.47.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 +206 -0
- package/dist/{frameworkLiveTables-XPFqj8n3.js → frameworkLiveTables-DNu6F6P8.js} +35 -35
- package/dist/index.d.ts +105 -9
- package/dist/index.js +552 -514
- package/dist/sql.js +256 -256
- package/package.json +2 -2
package/dist/sql.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { $ as e, Q as t, Rt as n, St as r, Xt as i, Yt as a, Z as o, _ as s, a as c, c as l, gt as u, i as d, l as f, m as p, n as m, nt as h, o as g, p as _, r as v, rt as y, zt as b } from "./frameworkLiveTables-DNu6F6P8.js";
|
|
2
2
|
import { Effect as x, Schedule as S } from "effect";
|
|
3
3
|
import { createHash as ee } from "node:crypto";
|
|
4
4
|
import { createLogger as te } from "@voltro/logger";
|
|
@@ -154,19 +154,19 @@ var ae = (e, t, n, r = 400) => {
|
|
|
154
154
|
mariadb: "per-operation",
|
|
155
155
|
sqlite: "per-operation",
|
|
156
156
|
turso: "per-operation"
|
|
157
|
-
}, Ie = (e) => Fe[e], D = "_voltro_migration_ops", Le =
|
|
158
|
-
id:
|
|
159
|
-
runId:
|
|
160
|
-
planFingerprint:
|
|
161
|
-
opIndex:
|
|
162
|
-
opKind:
|
|
163
|
-
opTarget:
|
|
164
|
-
status:
|
|
165
|
-
payload:
|
|
166
|
-
startedAt:
|
|
167
|
-
appliedAt:
|
|
168
|
-
durationMs:
|
|
169
|
-
createdAt:
|
|
157
|
+
}, Ie = (e) => Fe[e], D = "_voltro_migration_ops", Le = h(D, {
|
|
158
|
+
id: n({ prefix: "mop" }),
|
|
159
|
+
runId: a(),
|
|
160
|
+
planFingerprint: a(),
|
|
161
|
+
opIndex: b(),
|
|
162
|
+
opKind: a(),
|
|
163
|
+
opTarget: a(),
|
|
164
|
+
status: a(),
|
|
165
|
+
payload: a(),
|
|
166
|
+
startedAt: i().nullable(),
|
|
167
|
+
appliedAt: i().nullable(),
|
|
168
|
+
durationMs: b().nullable(),
|
|
169
|
+
createdAt: i().default("now")
|
|
170
170
|
}), Re = (e) => {
|
|
171
171
|
switch (e.kind) {
|
|
172
172
|
case "create-table": return /* @__PURE__ */ new Set([
|
|
@@ -219,54 +219,54 @@ Object.keys(Fe);
|
|
|
219
219
|
//#region src/migrations/reactiveTriggerName.ts
|
|
220
220
|
var O = "framework_changes", Je = "framework_changes_", Ye = (e, t) => t === "framework_changes" ? `${Je}${e}` : `${Je}${t}_${e}`, Xe = (e) => e === "framework_changes" ? "framework_notify_change" : `framework_notify_change_${e}`, Ze = "voltro:notify:v2", Qe = te({ scope: "voltro:migrate" }), k = (e) => {
|
|
221
221
|
Qe.warn(e.replace(/^\[voltro:migrate\]\s*/, ""));
|
|
222
|
-
}, A = (e,
|
|
223
|
-
let
|
|
222
|
+
}, A = (e, n) => {
|
|
223
|
+
let r = e.type;
|
|
224
224
|
if (e.spatial) {
|
|
225
|
-
if (
|
|
226
|
-
throw Error(`@voltro/plugin-postgis: ${e.spatial.kind}('${e.spatial.geomKind}', ${e.spatial.srid}) is postgres-only — the active dialect is '${
|
|
225
|
+
if (n === "postgres") return `${e.spatial.kind}(${e.spatial.geomKind},${e.spatial.srid})`;
|
|
226
|
+
throw Error(`@voltro/plugin-postgis: ${e.spatial.kind}('${e.spatial.geomKind}', ${e.spatial.srid}) is postgres-only — the active dialect is '${n}'. PostGIS has no portable equivalent; use store: 'postgres' (or DB_DIALECT=postgres) for spatial columns, or model the value as plain text/json.`);
|
|
227
227
|
}
|
|
228
|
-
if (
|
|
228
|
+
if (r === "raw") {
|
|
229
229
|
if (e.rawDdl === void 0) throw Error("raw() column is missing its DDL fragment — this is a framework bug.");
|
|
230
230
|
return e.rawDdl;
|
|
231
231
|
}
|
|
232
|
-
if (
|
|
233
|
-
if (
|
|
232
|
+
if (r === "array" && e.arrayElement) {
|
|
233
|
+
if (n === "postgres") return `${A({
|
|
234
234
|
...e,
|
|
235
235
|
type: e.arrayElement,
|
|
236
236
|
arrayElement: void 0
|
|
237
|
-
},
|
|
238
|
-
if (
|
|
239
|
-
if (
|
|
240
|
-
if (
|
|
241
|
-
}
|
|
242
|
-
if (
|
|
243
|
-
let
|
|
244
|
-
if (
|
|
245
|
-
let
|
|
246
|
-
if (
|
|
247
|
-
if (
|
|
248
|
-
if (
|
|
249
|
-
if (
|
|
250
|
-
}
|
|
251
|
-
if (
|
|
252
|
-
if (
|
|
253
|
-
if (
|
|
254
|
-
if (
|
|
255
|
-
}
|
|
256
|
-
if (
|
|
257
|
-
let
|
|
258
|
-
if (
|
|
259
|
-
return
|
|
260
|
-
}
|
|
261
|
-
if (
|
|
262
|
-
if (
|
|
263
|
-
if (
|
|
264
|
-
if (
|
|
265
|
-
if (
|
|
266
|
-
if (
|
|
267
|
-
}
|
|
268
|
-
if (
|
|
269
|
-
if (
|
|
237
|
+
}, n)}[]`;
|
|
238
|
+
if (n === "mysql" || n === "mariadb") return "JSON";
|
|
239
|
+
if (n === "mssql") return "NVARCHAR(MAX)";
|
|
240
|
+
if (t(n)) return "TEXT";
|
|
241
|
+
}
|
|
242
|
+
if (r === "vector") {
|
|
243
|
+
let r = e.vectorDim;
|
|
244
|
+
if (r === void 0) throw Error("vector column is missing its dimension — this is a framework bug.");
|
|
245
|
+
let i = e.vectorPrecision === "half";
|
|
246
|
+
if (n === "postgres") return i ? `HALFVEC(${r})` : `VECTOR(${r})`;
|
|
247
|
+
if (n === "mysql" || n === "mariadb") return `VECTOR(${r})`;
|
|
248
|
+
if (n === "mssql") return "VARBINARY(MAX)";
|
|
249
|
+
if (t(n)) return "BLOB";
|
|
250
|
+
}
|
|
251
|
+
if (r === "interval") {
|
|
252
|
+
if (n === "postgres") return "INTERVAL";
|
|
253
|
+
if (n === "mysql" || n === "mariadb" || n === "mssql") return "BIGINT";
|
|
254
|
+
if (t(n)) return "INTEGER";
|
|
255
|
+
}
|
|
256
|
+
if (r === "decimal") {
|
|
257
|
+
let r = e.precision, i = e.scale ?? 0;
|
|
258
|
+
if (r === void 0) throw Error("decimal column is missing its precision — this is a framework bug.");
|
|
259
|
+
return t(n) ? "NUMERIC" : n === "postgres" ? `NUMERIC(${r}, ${i})` : `DECIMAL(${r}, ${i})`;
|
|
260
|
+
}
|
|
261
|
+
if (r === "bigint") return t(n) ? "INTEGER" : "BIGINT";
|
|
262
|
+
if (r === "enum") {
|
|
263
|
+
if (n === "postgres") return e.enumName ?? "TEXT";
|
|
264
|
+
if (n === "mysql" || n === "mariadb") return `ENUM(${(e.enumValues ?? []).map((e) => `'${e.replace(/'/g, "''")}'`).join(", ")})`;
|
|
265
|
+
if (n === "mssql") return "NVARCHAR(255)";
|
|
266
|
+
if (t(n)) return "TEXT";
|
|
267
|
+
}
|
|
268
|
+
if (r === "text" && e.maxLength !== void 0 && e.generatedAs === void 0) return n === "mssql" ? `NVARCHAR(${e.maxLength})` : t(n) ? "TEXT" : `VARCHAR(${e.maxLength})`;
|
|
269
|
+
if (t(n)) switch (r) {
|
|
270
270
|
case "id": return "TEXT";
|
|
271
271
|
case "text": return "TEXT";
|
|
272
272
|
case "integer": return "INTEGER";
|
|
@@ -282,7 +282,7 @@ var O = "framework_changes", Je = "framework_changes_", Ye = (e, t) => t === "fr
|
|
|
282
282
|
case "array": return "TEXT";
|
|
283
283
|
case "interval": return "INTEGER";
|
|
284
284
|
}
|
|
285
|
-
if (
|
|
285
|
+
if (n === "mysql" || n === "mariadb") switch (r) {
|
|
286
286
|
case "id": return "VARCHAR(64)";
|
|
287
287
|
case "text": return e.hasDefault && e.defaultFactory === void 0 || e.oneOf && e.oneOf.length > 0 ? "VARCHAR(255)" : e.generatedAs ? "TEXT" : "LONGTEXT";
|
|
288
288
|
case "integer": return "INT";
|
|
@@ -298,7 +298,7 @@ var O = "framework_changes", Je = "framework_changes_", Ye = (e, t) => t === "fr
|
|
|
298
298
|
case "array": return "JSON";
|
|
299
299
|
case "interval": return "BIGINT";
|
|
300
300
|
}
|
|
301
|
-
if (
|
|
301
|
+
if (n === "mssql") switch (r) {
|
|
302
302
|
case "id": return "NVARCHAR(64)";
|
|
303
303
|
case "text": return e.hasDefault && e.defaultFactory === void 0 || e.oneOf && e.oneOf.length > 0 ? "NVARCHAR(450)" : "NVARCHAR(MAX)";
|
|
304
304
|
case "integer": return "INT";
|
|
@@ -314,7 +314,7 @@ var O = "framework_changes", Je = "framework_changes_", Ye = (e, t) => t === "fr
|
|
|
314
314
|
case "array": return "NVARCHAR(MAX)";
|
|
315
315
|
case "interval": return "BIGINT";
|
|
316
316
|
}
|
|
317
|
-
switch (
|
|
317
|
+
switch (r) {
|
|
318
318
|
case "id": return "TEXT";
|
|
319
319
|
case "text": return "TEXT";
|
|
320
320
|
case "integer": return "INTEGER";
|
|
@@ -330,10 +330,10 @@ var O = "framework_changes", Je = "framework_changes_", Ye = (e, t) => t === "fr
|
|
|
330
330
|
case "array": return "TEXT[]";
|
|
331
331
|
case "interval": return "INTERVAL";
|
|
332
332
|
}
|
|
333
|
-
}, $e = (e,
|
|
333
|
+
}, $e = (e, n) => {
|
|
334
334
|
if (!e.hasDefault || e.defaultFactory !== void 0) return null;
|
|
335
|
-
let
|
|
336
|
-
return
|
|
335
|
+
let r = e.defaultValue;
|
|
336
|
+
return r === "now" ? t(n) ? "DEFAULT CURRENT_TIMESTAMP" : n === "mssql" ? "DEFAULT SYSUTCDATETIME()" : n === "mysql" || n === "mariadb" ? "DEFAULT CURRENT_TIMESTAMP(6)" : "DEFAULT now()" : typeof r == "boolean" ? n === "postgres" ? `DEFAULT ${r}` : `DEFAULT ${+!!r}` : typeof r == "number" ? `DEFAULT ${r}` : typeof r == "string" ? `DEFAULT '${r.replace(/'/g, "''")}'` : Array.isArray(r) ? et(r, e, n) : typeof r == "object" && r ? j(r, n) : null;
|
|
337
337
|
}, et = (e, t, n) => {
|
|
338
338
|
if (n !== "postgres" || t.type !== "array" || t.arrayElement === void 0) return j(e, n);
|
|
339
339
|
let r = A({
|
|
@@ -362,14 +362,14 @@ var O = "framework_changes", Je = "framework_changes_", Ye = (e, t) => t === "fr
|
|
|
362
362
|
case "setNull": return "SET NULL";
|
|
363
363
|
case "noAction": return "NO ACTION";
|
|
364
364
|
}
|
|
365
|
-
}, tt = (
|
|
365
|
+
}, tt = (t, n, r) => r === null ? e(t, n) : `${e(r, n)}.${e(t, n)}`, nt = 191, rt = (e, t) => {
|
|
366
366
|
if (!e || t !== "mysql" && t !== "mariadb") return !1;
|
|
367
367
|
let n = A(e, t).toUpperCase();
|
|
368
368
|
return n.endsWith("TEXT") || n.endsWith("BLOB");
|
|
369
|
-
}, it = (
|
|
370
|
-
let i =
|
|
369
|
+
}, it = (t, n, r) => {
|
|
370
|
+
let i = e(t, r);
|
|
371
371
|
if (r !== "mysql" && r !== "mariadb") return i;
|
|
372
|
-
let a = t
|
|
372
|
+
let a = n[t];
|
|
373
373
|
if (!a) return i;
|
|
374
374
|
let o = A(a, r).toUpperCase();
|
|
375
375
|
return o.endsWith("TEXT") || o.endsWith("BLOB") ? `${i}(${nt})` : i;
|
|
@@ -381,98 +381,98 @@ var O = "framework_changes", Je = "framework_changes_", Ye = (e, t) => t === "fr
|
|
|
381
381
|
if (t === "mssql") return `AS (${n})${r ? " PERSISTED" : ""}`;
|
|
382
382
|
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.");
|
|
383
383
|
return `GENERATED ALWAYS AS (${n}) ${r ? "STORED" : "VIRTUAL"}`;
|
|
384
|
-
}, ot = (
|
|
385
|
-
if (
|
|
386
|
-
let l =
|
|
387
|
-
|
|
388
|
-
let d = at(
|
|
384
|
+
}, ot = (n, r, i, a, s = !1, c = !1) => {
|
|
385
|
+
if (r.type === "id" && r.idScheme?.kind === "numeric") return M(n, i);
|
|
386
|
+
let l = i === "mssql" && r.type === "text" && !r.generatedAs && r.maxLength === void 0 && a?.has(n) ? "NVARCHAR(450)" : A(r, i), u = [e(n, i), l];
|
|
387
|
+
r.type === "id" && !c && u.push("PRIMARY KEY");
|
|
388
|
+
let d = at(r, i);
|
|
389
389
|
if (d) u.push(d);
|
|
390
390
|
else {
|
|
391
|
-
|
|
392
|
-
let e = $e(
|
|
391
|
+
r.nullable || u.push("NOT NULL"), r.unique && r.type !== "id" && !rt(r, i) && u.push("UNIQUE");
|
|
392
|
+
let e = $e(r, i);
|
|
393
393
|
e && u.push(e);
|
|
394
394
|
}
|
|
395
|
-
if (
|
|
396
|
-
let
|
|
397
|
-
u.push(`REFERENCES ${
|
|
398
|
-
let
|
|
399
|
-
u.push(`ON DELETE ${
|
|
400
|
-
let a = N(
|
|
395
|
+
if (r.type === "reference" && r.references && !s && !o(i)) {
|
|
396
|
+
let t = r.references();
|
|
397
|
+
u.push(`REFERENCES ${e(t.tableName, i)} (${e("id", i)})`);
|
|
398
|
+
let n = N(r.onDelete ?? "restrict", i);
|
|
399
|
+
u.push(`ON DELETE ${n}`);
|
|
400
|
+
let a = N(r.refOnUpdate ?? "noAction", i);
|
|
401
401
|
a !== "NO ACTION" && u.push(`ON UPDATE ${a}`);
|
|
402
402
|
}
|
|
403
|
-
if (
|
|
404
|
-
let
|
|
405
|
-
u.push(`CHECK (${n
|
|
403
|
+
if (r.oneOf && r.oneOf.length > 0) {
|
|
404
|
+
let t = r.oneOf.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ");
|
|
405
|
+
u.push(`CHECK (${e(n, i)} IN (${t}))`);
|
|
406
406
|
}
|
|
407
|
-
if (
|
|
408
|
-
if (
|
|
409
|
-
let
|
|
410
|
-
u.push(`CHECK (${n
|
|
407
|
+
if (r.checks && r.checks.length > 0) for (let e of r.checks) u.push(`CHECK (${e})`);
|
|
408
|
+
if (r.type === "enum" && r.enumValues && r.enumValues.length > 0 && (i === "mssql" || t(i))) {
|
|
409
|
+
let t = r.enumValues.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ");
|
|
410
|
+
u.push(`CHECK (${e(n, i)} IN (${t}))`);
|
|
411
411
|
}
|
|
412
|
-
if (
|
|
413
|
-
let
|
|
414
|
-
|
|
412
|
+
if (r.type === "json" || r.type === "array") {
|
|
413
|
+
let r = e(n, i);
|
|
414
|
+
t(i) ? u.push(`CHECK (${r} IS NULL OR json_valid(${r}))`) : i === "mssql" && u.push(`CHECK (${r} IS NULL OR ISJSON(${r}) = 1)`);
|
|
415
415
|
}
|
|
416
416
|
return u.join(" ");
|
|
417
|
-
}, st = (
|
|
418
|
-
let
|
|
419
|
-
for (let
|
|
420
|
-
for (let
|
|
421
|
-
let s =
|
|
422
|
-
let i = r.fields.map((
|
|
423
|
-
return ` CONSTRAINT ${
|
|
424
|
-
}), d = (
|
|
425
|
-
if (o.type !== "reference" || !o.references || (
|
|
426
|
-
let s = o.references(), c = N(o.onDelete ?? "restrict",
|
|
427
|
-
return [` CONSTRAINT ${
|
|
417
|
+
}, st = (t, n, r = null, i) => {
|
|
418
|
+
let a = /* @__PURE__ */ new Set();
|
|
419
|
+
for (let e of t.appliedIndexes ?? []) for (let t of e.fields) typeof t == "string" && a.add(t);
|
|
420
|
+
for (let e of t.appliedUniques ?? []) for (let t of e.fields) typeof t == "string" && a.add(t);
|
|
421
|
+
let s = t.appliedPrimaryKey ?? [], c = s.length > 0, l = Object.entries(t.fields).map(([e, t]) => ` ${ot(e, t, n, a, i?.has(e) ?? !1, c)}`).join(",\n"), u = (t.appliedUniques ?? []).map((r) => {
|
|
422
|
+
let i = r.fields.map((e) => it(e, t.fields, n)).join(", ");
|
|
423
|
+
return ` CONSTRAINT ${e(r.name, n)} UNIQUE (${i})`;
|
|
424
|
+
}), d = (t.appliedChecks ?? []).map((t) => ` CONSTRAINT ${e(t.name, n)} CHECK (${t.expr})`), f = c ? [` PRIMARY KEY (${s.map((e) => it(e, t.fields, n)).join(", ")})`] : [], p = o(n) ? Object.entries(t.fields).flatMap(([a, o]) => {
|
|
425
|
+
if (o.type !== "reference" || !o.references || (i?.has(a) ?? !1)) return [];
|
|
426
|
+
let s = o.references(), c = N(o.onDelete ?? "restrict", n), l = N(o.refOnUpdate ?? "noAction", n);
|
|
427
|
+
return [` CONSTRAINT ${e(`${t.tableName}_${a}_fkey`, n)} FOREIGN KEY (${e(a, n)}) REFERENCES ${tt(s.tableName, n, r)} (${e("id", n)}) ON DELETE ${c}${l === "NO ACTION" ? "" : ` ON UPDATE ${l}`}`];
|
|
428
428
|
}) : [], m = [
|
|
429
429
|
...f,
|
|
430
430
|
...u,
|
|
431
431
|
...d,
|
|
432
432
|
...p
|
|
433
|
-
], h = m.length > 0 ? `${l},\n${m.join(",\n")}` : l, g = tt(
|
|
434
|
-
if (
|
|
435
|
-
let
|
|
436
|
-
return `IF OBJECT_ID('${
|
|
433
|
+
], h = m.length > 0 ? `${l},\n${m.join(",\n")}` : l, g = tt(t.tableName, n, r);
|
|
434
|
+
if (n === "mssql") {
|
|
435
|
+
let e = r === null ? t.tableName : `${r}.${t.tableName}`, n = `CREATE TABLE ${g} (\n${h}\n)`;
|
|
436
|
+
return `IF OBJECT_ID('${e.replace(/'/g, "''")}', 'U') IS NULL EXEC(N'${n.replace(/'/g, "''")}');`;
|
|
437
437
|
}
|
|
438
438
|
return [
|
|
439
439
|
`CREATE TABLE IF NOT EXISTS ${g} (`,
|
|
440
440
|
h,
|
|
441
441
|
");"
|
|
442
442
|
].join("\n");
|
|
443
|
-
}, ct = (
|
|
444
|
-
let i = `${
|
|
445
|
-
return c.push(`CREATE VIRTUAL TABLE IF NOT EXISTS ${a(i)} USING fts5(${o}, row_id UNINDEXED, tokenize='porter');`), c.push(`CREATE TRIGGER IF NOT EXISTS ${a(`${i}_ai`)} AFTER INSERT ON ${a(
|
|
446
|
-
}, lt = (
|
|
447
|
-
let i = [], a = /* @__PURE__ */ new Set(), o = (e) => e.join("\0"), s = (e,
|
|
443
|
+
}, ct = (t, n, r) => {
|
|
444
|
+
let i = `${t}_${n.name}_fts`, a = (t) => e(t, r), o = n.columns.map(a).join(", "), s = n.columns.map((e) => `new.${a(e)}`).join(", "), c = [];
|
|
445
|
+
return c.push(`CREATE VIRTUAL TABLE IF NOT EXISTS ${a(i)} USING fts5(${o}, row_id UNINDEXED, tokenize='porter');`), c.push(`CREATE TRIGGER IF NOT EXISTS ${a(`${i}_ai`)} AFTER INSERT ON ${a(t)} BEGIN\n INSERT INTO ${a(i)} (${o}, row_id) VALUES (${s}, new.${a("id")});\nEND;`), c.push(`CREATE TRIGGER IF NOT EXISTS ${a(`${i}_ad`)} AFTER DELETE ON ${a(t)} BEGIN\n DELETE FROM ${a(i)} WHERE row_id = old.${a("id")};\nEND;`), c.push(`CREATE TRIGGER IF NOT EXISTS ${a(`${i}_au`)} AFTER UPDATE ON ${a(t)} BEGIN\n DELETE FROM ${a(i)} WHERE row_id = old.${a("id")};\n INSERT INTO ${a(i)} (${o}, row_id) VALUES (${s}, new.${a("id")});\nEND;`), c;
|
|
446
|
+
}, lt = (t, n, r = null) => {
|
|
447
|
+
let i = [], a = /* @__PURE__ */ new Set(), o = (e) => e.join("\0"), s = (e, t, r, i) => {
|
|
448
448
|
let a = {
|
|
449
449
|
using: "",
|
|
450
450
|
withSuffix: "",
|
|
451
451
|
skip: !1
|
|
452
452
|
};
|
|
453
453
|
if (r === void 0 || r === "btree") return a;
|
|
454
|
-
if (r === "gist") return
|
|
454
|
+
if (r === "gist") return n === "postgres" ? {
|
|
455
455
|
using: " USING GIST",
|
|
456
456
|
withSuffix: "",
|
|
457
457
|
skip: !1
|
|
458
|
-
} : (k(`[voltro:migrate] index '${e}' on '${
|
|
459
|
-
if (r === "gin") return
|
|
458
|
+
} : (k(`[voltro:migrate] index '${e}' on '${t}': ${n} doesn't support GiST — falling back to btree. Spatial queries on this column will fall back to sequential scan.`), a);
|
|
459
|
+
if (r === "gin") return n === "postgres" ? {
|
|
460
460
|
using: " USING GIN",
|
|
461
461
|
withSuffix: "",
|
|
462
462
|
skip: !1
|
|
463
|
-
} :
|
|
463
|
+
} : n === "mysql" || n === "mariadb" ? (k(`[voltro:migrate] gin index '${e}' on '${t}': ${n} has no GIN access method — skipping (a btree on a JSON/array column can't serve containment/membership queries). Add a generated column for the specific path you query, then index that with .expressionIndex(...).`), {
|
|
464
464
|
using: "",
|
|
465
465
|
withSuffix: "",
|
|
466
466
|
skip: !0
|
|
467
|
-
}) : (k(`[voltro:migrate] gin index '${e}' on '${
|
|
468
|
-
if (r === "brin") return
|
|
467
|
+
}) : (k(`[voltro:migrate] gin index '${e}' on '${t}': ${n} has no GIN access method — falling back to btree. Containment/membership queries won't use this index efficiently.`), a);
|
|
468
|
+
if (r === "brin") return n === "postgres" ? {
|
|
469
469
|
using: " USING BRIN",
|
|
470
470
|
withSuffix: "",
|
|
471
471
|
skip: !1
|
|
472
|
-
} : (k(`[voltro:migrate] brin index '${e}' on '${
|
|
472
|
+
} : (k(`[voltro:migrate] brin index '${e}' on '${t}': ${n} has no BRIN access method — falling back to btree (larger on disk, but the range scan still works).`), a);
|
|
473
473
|
if (r === "hnsw") {
|
|
474
|
-
if (
|
|
475
|
-
let e = i?.hnsw?.m ?? 16, t = i?.hnsw?.efConstruction ?? 64, n = i?.hnsw?.opclass ??
|
|
474
|
+
if (n === "postgres") {
|
|
475
|
+
let e = i?.hnsw?.m ?? 16, t = i?.hnsw?.efConstruction ?? 64, n = i?.hnsw?.opclass ?? y(i?.hnsw?.distance);
|
|
476
476
|
return {
|
|
477
477
|
using: " USING hnsw",
|
|
478
478
|
withSuffix: ` WITH (m = ${e}, ef_construction = ${t})`,
|
|
@@ -480,60 +480,60 @@ var O = "framework_changes", Je = "framework_changes_", Ye = (e, t) => t === "fr
|
|
|
480
480
|
skip: !1
|
|
481
481
|
};
|
|
482
482
|
}
|
|
483
|
-
return k(`[voltro:migrate] hnsw index '${e}' on '${
|
|
483
|
+
return k(`[voltro:migrate] hnsw index '${e}' on '${t}': ${n} has no pgvector / HNSW access method — skipping. Nearest-neighbour queries on this column fall back to a sequential scan.`), {
|
|
484
484
|
using: "",
|
|
485
485
|
withSuffix: "",
|
|
486
486
|
skip: !0
|
|
487
487
|
};
|
|
488
488
|
}
|
|
489
489
|
return a;
|
|
490
|
-
}, c = (
|
|
491
|
-
let i =
|
|
492
|
-
if (typeof
|
|
493
|
-
if ("jsonPath" in
|
|
494
|
-
let { column:
|
|
495
|
-
return `(${_(
|
|
490
|
+
}, c = (e, r) => {
|
|
491
|
+
let i = e.map((e) => {
|
|
492
|
+
if (typeof e == "string") return it(e, t.fields, n);
|
|
493
|
+
if ("jsonPath" in e) {
|
|
494
|
+
let { column: t, path: r, numeric: i } = e.jsonPath;
|
|
495
|
+
return `(${_(t, r, n, { numeric: i })})`;
|
|
496
496
|
}
|
|
497
|
-
return `(${
|
|
497
|
+
return `(${e.expr})`;
|
|
498
498
|
});
|
|
499
499
|
return r !== void 0 && i.length === 1 ? `${i[0]} ${r}` : i.join(", ");
|
|
500
|
-
}, l = (
|
|
501
|
-
let f = s(
|
|
500
|
+
}, l = (t, i, a, o, l, u = "", d = !1) => {
|
|
501
|
+
let f = s(t, i, o, l);
|
|
502
502
|
if (f.skip) return "";
|
|
503
|
-
let p = c(a, f.opclass), m = `${f.withSuffix}${u}`, h = tt(i,
|
|
504
|
-
return
|
|
503
|
+
let p = c(a, f.opclass), m = `${f.withSuffix}${u}`, h = tt(i, n, r), g = d ? "UNIQUE " : "";
|
|
504
|
+
return n === "mssql" ? `IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = '${t}') CREATE ${g}INDEX ${e(t, n)} ON ${h}${f.using} (${p})${m};` : n === "mysql" ? `CREATE ${g}INDEX ${e(t, n)} ON ${h}${f.using} (${p})${m};` : `CREATE ${g}INDEX IF NOT EXISTS ${e(t, n)} ON ${h}${f.using} (${p})${m};`;
|
|
505
505
|
};
|
|
506
|
-
if (
|
|
507
|
-
if (!r.unique || r.type === "id" || !rt(r,
|
|
508
|
-
let a = l(`${
|
|
506
|
+
if (n === "mysql" || n === "mariadb") for (let [e, r] of Object.entries(t.fields)) {
|
|
507
|
+
if (!r.unique || r.type === "id" || !rt(r, n)) continue;
|
|
508
|
+
let a = l(`${t.tableName}_${e}_key`, t.tableName, [e], void 0, void 0, "", !0);
|
|
509
509
|
a && i.push(a);
|
|
510
510
|
}
|
|
511
|
-
for (let r of
|
|
512
|
-
let a = r.config ?? "english", o = `${r.name}_tsv`, s = r.columns.map((
|
|
513
|
-
let i = r.weights?.[
|
|
514
|
-
return `setweight(to_tsvector('${a}', coalesce(${
|
|
511
|
+
for (let r of t.appliedFullText ?? []) if (n === "postgres") {
|
|
512
|
+
let a = r.config ?? "english", o = `${r.name}_tsv`, s = r.columns.map((t) => {
|
|
513
|
+
let i = r.weights?.[t] ?? "A";
|
|
514
|
+
return `setweight(to_tsvector('${a}', coalesce(${e(t, n)}, '')), '${i}')`;
|
|
515
515
|
}).join(" || ");
|
|
516
|
-
i.push(`ALTER TABLE ${
|
|
517
|
-
} else if (
|
|
518
|
-
let a = r.columns.map((
|
|
519
|
-
i.push(`CREATE FULLTEXT INDEX ${
|
|
520
|
-
} else if (
|
|
521
|
-
else
|
|
522
|
-
for (let
|
|
523
|
-
if ((
|
|
524
|
-
k(`[voltro:migrate] json-path index '${
|
|
516
|
+
i.push(`ALTER TABLE ${e(t.tableName, n)} ADD COLUMN IF NOT EXISTS ${e(o, n)} tsvector GENERATED ALWAYS AS (${s}) STORED;`), i.push(`CREATE INDEX IF NOT EXISTS ${e(r.name, n)} ON ${e(t.tableName, n)} USING GIN (${e(o, n)});`);
|
|
517
|
+
} else if (n === "mysql" || n === "mariadb") {
|
|
518
|
+
let a = r.columns.map((t) => e(t, n)).join(", ");
|
|
519
|
+
i.push(`CREATE FULLTEXT INDEX ${e(r.name, n)} ON ${e(t.tableName, n)} (${a});`);
|
|
520
|
+
} else if (n === "turso") throw Error(`@voltro/sql-turso: full-text index '${r.name}' on '${t.tableName}' is not supported — Turso's MVCC mode has no FTS5 virtual tables. Drop .fullTextIndex(), or use the 'sqlite' / 'postgres' dialect for this table.`);
|
|
521
|
+
else n === "sqlite" ? i.push(...ct(t.tableName, r, n)) : k(`[voltro:migrate] full-text index '${r.name}' on '${t.tableName}': ${n} uses a ranked LIKE search at query time (no native FTS catalog required).`);
|
|
522
|
+
for (let e of t.appliedIndexes) {
|
|
523
|
+
if ((n === "mariadb" || n === "mssql") && e.fields.some((e) => typeof e != "string" && "jsonPath" in e)) {
|
|
524
|
+
k(`[voltro:migrate] json-path index '${e.name}' on '${t.tableName}': ${n} cannot index a JSON expression directly — skipping. For an indexed JSON path on ${n}, add a generated column for the path (text().generatedAs(...) / .stored()) and .index([...]) it, then filter on that column instead of jsonField(...).`);
|
|
525
525
|
continue;
|
|
526
526
|
}
|
|
527
|
-
if (
|
|
527
|
+
if (e.unique && e.where !== void 0 && e.where.length > 0 && (n === "mysql" || n === "mariadb")) throw Error(`@voltro/migrate: uniqueActive index '${e.name}' on '${t.tableName}' cannot be expressed on ${n} — it has no partial (WHERE) index support, and a full UNIQUE index would forbid re-creating a soft-deleted row. Use a generated STORED column (text().generatedAs("CASE WHEN \`deletedAt\` IS NULL THEN <key> ELSE NULL END", { stored: true })) + .unique([...]) on ${n}, or move this table to postgres/sqlite/mssql.`);
|
|
528
528
|
let r = "";
|
|
529
|
-
|
|
530
|
-
let s = l(
|
|
531
|
-
s !== "" && i.push(s),
|
|
529
|
+
e.where !== void 0 && e.where.length > 0 && (n === "mysql" || n === "mariadb" ? k(`[voltro:migrate] partial index '${e.name}' on '${t.tableName}': ${n} does not support WHERE clauses on indexes — emitting a full index`) : r = ` WHERE ${e.where}`);
|
|
530
|
+
let s = l(e.name, t.tableName, e.fields, e.kind, e.kindOptions, r, e.unique === !0);
|
|
531
|
+
s !== "" && i.push(s), e.fields.every((e) => typeof e == "string") && a.add(o(e.fields));
|
|
532
532
|
}
|
|
533
|
-
for (let [
|
|
534
|
-
if (n.type !== "reference" || n.fkAutoIndex === !1 || a.has(o([
|
|
535
|
-
let r = `${
|
|
536
|
-
i.push(l(r,
|
|
533
|
+
for (let [e, n] of Object.entries(t.fields)) {
|
|
534
|
+
if (n.type !== "reference" || n.fkAutoIndex === !1 || a.has(o([e]))) continue;
|
|
535
|
+
let r = `${t.tableName}_${e}_idx`;
|
|
536
|
+
i.push(l(r, t.tableName, [e])), a.add(o([e]));
|
|
537
537
|
}
|
|
538
538
|
return i.join("\n");
|
|
539
539
|
}, ut = (e, t, n = O) => {
|
|
@@ -651,29 +651,29 @@ $$ LANGUAGE plpgsql;
|
|
|
651
651
|
sorted: o,
|
|
652
652
|
cyclic: r
|
|
653
653
|
};
|
|
654
|
-
}, vt = (
|
|
655
|
-
let i = (
|
|
656
|
-
switch (
|
|
654
|
+
}, vt = (t, n, r = null) => {
|
|
655
|
+
let i = (t) => r ? `${e(r, n)}.${e(t, n)}` : e(t, n), a = `${t.table}_${t.column}_fkey`, o = N(t.onDelete, n), s = N(t.onUpdate, n), c = ` ON DELETE ${o}${s === "NO ACTION" ? "" : ` ON UPDATE ${s}`}`, l = (r) => `ADD CONSTRAINT ${e(a, n)} FOREIGN KEY ${r ? "IF NOT EXISTS " : ""}(${e(t.column, n)}) REFERENCES ${i(t.targetTable)} (${e("id", n)})${c}`;
|
|
656
|
+
switch (n) {
|
|
657
657
|
case "postgres": return [
|
|
658
658
|
"DO $$ BEGIN",
|
|
659
659
|
" IF NOT EXISTS (SELECT 1 FROM pg_constraint",
|
|
660
|
-
` WHERE conname = '${a}' AND conrelid = '${r ? `${r}.` : ""}${
|
|
661
|
-
` ALTER TABLE ${i(
|
|
660
|
+
` WHERE conname = '${a}' AND conrelid = '${r ? `${r}.` : ""}${t.table}'::regclass) THEN`,
|
|
661
|
+
` ALTER TABLE ${i(t.table)} ${l(!1)};`,
|
|
662
662
|
" END IF;",
|
|
663
663
|
"END $$;"
|
|
664
664
|
].join("\n");
|
|
665
|
-
case "mssql": return `IF NOT EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = '${a}') ALTER TABLE ${i(
|
|
666
|
-
case "mariadb": return `ALTER TABLE ${i(
|
|
667
|
-
default: return `ALTER TABLE ${i(
|
|
665
|
+
case "mssql": return `IF NOT EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = '${a}') ALTER TABLE ${i(t.table)} ${l(!1)};`;
|
|
666
|
+
case "mariadb": return `ALTER TABLE ${i(t.table)} ${l(!0)};`;
|
|
667
|
+
default: return `ALTER TABLE ${i(t.table)} ${l(!1)};`;
|
|
668
668
|
}
|
|
669
|
-
}, yt = (e,
|
|
670
|
-
let
|
|
671
|
-
if (
|
|
669
|
+
}, yt = (e, n) => {
|
|
670
|
+
let r = /* @__PURE__ */ new Map();
|
|
671
|
+
if (t(n)) return r;
|
|
672
672
|
for (let t of e) {
|
|
673
|
-
let e =
|
|
674
|
-
e.add(t.column),
|
|
673
|
+
let e = r.get(t.table) ?? /* @__PURE__ */ new Set();
|
|
674
|
+
e.add(t.column), r.set(t.table, e);
|
|
675
675
|
}
|
|
676
|
-
return
|
|
676
|
+
return r;
|
|
677
677
|
}, bt = (e, t) => {
|
|
678
678
|
if (t !== "postgres") return "";
|
|
679
679
|
let n = /* @__PURE__ */ new Map();
|
|
@@ -694,16 +694,16 @@ $$ LANGUAGE plpgsql;
|
|
|
694
694
|
if (t !== "postgres") return "";
|
|
695
695
|
let n = /* @__PURE__ */ new Set();
|
|
696
696
|
for (let t of e) for (let e of Object.values(t.fields)) e.type === "enum" && e.enumName && n.add(e.enumName);
|
|
697
|
-
let
|
|
698
|
-
for (let e of n) for (let { from: t, to: n } of
|
|
699
|
-
let
|
|
700
|
-
|
|
697
|
+
let i = [];
|
|
698
|
+
for (let e of n) for (let { from: t, to: n } of r(e)) {
|
|
699
|
+
let r = e.replace(/'/g, "''"), a = t.replace(/'/g, "''"), o = n.replace(/'/g, "''");
|
|
700
|
+
i.push(`DO \$\$
|
|
701
701
|
DECLARE
|
|
702
702
|
type_oid oid;
|
|
703
703
|
BEGIN
|
|
704
|
-
SELECT oid INTO type_oid FROM pg_type WHERE typname = '${
|
|
704
|
+
SELECT oid INTO type_oid FROM pg_type WHERE typname = '${r}';\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', '${r}', '${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 $$;`);
|
|
705
705
|
}
|
|
706
|
-
return
|
|
706
|
+
return i.join("\n\n");
|
|
707
707
|
}, St = (e) => {
|
|
708
708
|
let t = [], n = [];
|
|
709
709
|
for (let r of e) p(r) ? n.push(r) : t.push(r);
|
|
@@ -711,42 +711,42 @@ BEGIN
|
|
|
711
711
|
tables: t,
|
|
712
712
|
views: n
|
|
713
713
|
};
|
|
714
|
-
}, Ct = (e,
|
|
715
|
-
let { tables:
|
|
716
|
-
|
|
717
|
-
let d = xt(
|
|
714
|
+
}, Ct = (e, n, r = O) => {
|
|
715
|
+
let { tables: i, views: a } = St(e), { sorted: o, cyclic: c } = _t(i.map((e) => de(e, n))), l = yt(c, n), u = [];
|
|
716
|
+
n === "postgres" && ht(o) && u.push("CREATE EXTENSION IF NOT EXISTS vector;"), n === "postgres" && gt(o) && u.push("CREATE EXTENSION IF NOT EXISTS postgis;");
|
|
717
|
+
let d = xt(o, n);
|
|
718
718
|
d && u.push(d);
|
|
719
|
-
let f = bt(
|
|
719
|
+
let f = bt(o, n);
|
|
720
720
|
f && u.push(f);
|
|
721
|
-
for (let e of
|
|
722
|
-
if (!
|
|
723
|
-
for (let e of
|
|
724
|
-
let
|
|
725
|
-
|
|
721
|
+
for (let e of o) u.push(st(e, n, null, l.get(e.tableName)));
|
|
722
|
+
if (!t(n)) for (let e of c) u.push(vt(e, n));
|
|
723
|
+
for (let e of o) {
|
|
724
|
+
let t = lt(e, n);
|
|
725
|
+
t && u.push(t);
|
|
726
726
|
}
|
|
727
|
-
u.push(...ut(
|
|
728
|
-
for (let e of
|
|
727
|
+
u.push(...ut(o, n, r));
|
|
728
|
+
for (let e of a) u.push(s(e, n));
|
|
729
729
|
return u.join("\n\n") + "\n";
|
|
730
|
-
}, wt = (
|
|
731
|
-
let i =
|
|
732
|
-
switch (
|
|
730
|
+
}, wt = (t, n, r) => {
|
|
731
|
+
let i = e(t, n);
|
|
732
|
+
switch (n) {
|
|
733
733
|
case "postgres": return `CREATE SCHEMA IF NOT EXISTS ${i};`;
|
|
734
734
|
case "mysql":
|
|
735
735
|
case "mariadb": return `CREATE DATABASE IF NOT EXISTS ${i};`;
|
|
736
|
-
case "mssql": return `IF SCHEMA_ID('${
|
|
736
|
+
case "mssql": return `IF SCHEMA_ID('${t.replace(/'/g, "''")}') IS NULL EXEC(N'CREATE SCHEMA ${i.replace(/'/g, "''")}');`;
|
|
737
737
|
case "turso":
|
|
738
|
-
case "sqlite": return `ATTACH DATABASE '${(r ?? `${
|
|
739
|
-
}
|
|
740
|
-
}, Tt = (
|
|
741
|
-
let
|
|
742
|
-
if (
|
|
743
|
-
if (
|
|
744
|
-
let { sorted: c, cyclic: l } = _t(
|
|
745
|
-
for (let e of c) d.push(st(e,
|
|
746
|
-
if (!
|
|
738
|
+
case "sqlite": return `ATTACH DATABASE '${(r ?? `${t}.db`).replace(/'/g, "''")}' AS ${i};`;
|
|
739
|
+
}
|
|
740
|
+
}, Tt = (n, r, i, a) => {
|
|
741
|
+
let o = wt(i, r, a), s = e(i, r);
|
|
742
|
+
if (r === "postgres") return `${o}\n\nSET search_path TO ${s};\n\n${Ct(n, r)}`;
|
|
743
|
+
if (r === "mysql" || r === "mariadb") return `${o}\n\nUSE ${s};\n\n${Ct(n, r)}`;
|
|
744
|
+
let { sorted: c, cyclic: l } = _t(n), u = yt(l, r), d = [o];
|
|
745
|
+
for (let e of c) d.push(st(e, r, i, u.get(e.tableName)));
|
|
746
|
+
if (!t(r)) for (let e of l) d.push(vt(e, r, i));
|
|
747
747
|
for (let e of c) {
|
|
748
|
-
let
|
|
749
|
-
|
|
748
|
+
let t = lt(e, r, i);
|
|
749
|
+
t && d.push(t);
|
|
750
750
|
}
|
|
751
751
|
return d.join("\n\n") + "\n";
|
|
752
752
|
}, Et = (e, t, n = O) => {
|
|
@@ -1084,7 +1084,7 @@ BEGIN
|
|
|
1084
1084
|
} catch {
|
|
1085
1085
|
return;
|
|
1086
1086
|
}
|
|
1087
|
-
}, sn = (e,
|
|
1087
|
+
}, sn = (e, n, r) => r === void 0 || t(r) ? !0 : e.type !== "text" || n.type !== "text" || on(e, r) === n.maxLength, cn = (e, t, n) => e.type === t.type && e.nullable === t.nullable && e.unique === t.unique && nn(e, t) && sn(e, t, n) && JSON.stringify(rn(e.references)) === JSON.stringify(rn(t.references)) && an(e.oneOf, t.oneOf), ln = (e, t) => t.oneOf && t.oneOf.length > 0 ? R({
|
|
1088
1088
|
kind: "add-check",
|
|
1089
1089
|
table: e,
|
|
1090
1090
|
column: t.name,
|
|
@@ -1183,88 +1183,88 @@ BEGIN
|
|
|
1183
1183
|
}, xn = (e, t, n) => {
|
|
1184
1184
|
let r = `acknowledge it on the column: \`.narrowedFrom('${e}')\``;
|
|
1185
1185
|
return n === "postgres" ? `${r} — add \`{ using: '${t.name}::${t.type}' }\` when the cast is not implicit, or use a file-based migration` : `${r} — ${n === void 0 ? "this dialect" : n} converts in place (\`using\` is postgres-only and is ignored here), so verify the conversion is value-preserving for your data, or use a file-based migration`;
|
|
1186
|
-
}, Sn = (e,
|
|
1186
|
+
}, Sn = (e, n, r, i, a, o) => {
|
|
1187
1187
|
let s = [];
|
|
1188
|
-
if (
|
|
1189
|
-
let
|
|
1188
|
+
if (n.nullable !== r.nullable) {
|
|
1189
|
+
let t = {
|
|
1190
1190
|
kind: "alter-column-nullability",
|
|
1191
1191
|
table: e,
|
|
1192
|
-
column:
|
|
1193
|
-
toNullable:
|
|
1192
|
+
column: n.name,
|
|
1193
|
+
toNullable: n.nullable
|
|
1194
1194
|
};
|
|
1195
|
-
s.push(
|
|
1195
|
+
s.push(n.nullable ? R(t, "safe", `loosen ${e}.${n.name} to nullable`) : R(t, "needs-backfill", `tighten ${e}.${n.name} to NOT NULL — existing rows must already be non-null`));
|
|
1196
1196
|
}
|
|
1197
|
-
if (
|
|
1198
|
-
if (
|
|
1199
|
-
let
|
|
1197
|
+
if (n.unique !== r.unique) {
|
|
1198
|
+
if (n.unique) {
|
|
1199
|
+
let t = {
|
|
1200
1200
|
kind: "add-unique",
|
|
1201
1201
|
table: e,
|
|
1202
|
-
column:
|
|
1203
|
-
}, r = (
|
|
1204
|
-
s.push(r ? R(
|
|
1202
|
+
column: n.name
|
|
1203
|
+
}, r = (a ?? 0) >= o;
|
|
1204
|
+
s.push(r ? R(t, "online-required", `online unique build on ${a} rows — CONCURRENTLY / INPLACE / WITH(ONLINE=ON) depending on dialect`) : R(t, "safe", `add unique constraint on ${e}.${n.name} — existing rows must already be distinct (else the DB rejects at migrate)`));
|
|
1205
1205
|
} else s.push(R({
|
|
1206
1206
|
kind: "drop-unique",
|
|
1207
1207
|
table: e,
|
|
1208
|
-
column:
|
|
1209
|
-
}, "safe", `drop unique constraint on ${e}.${
|
|
1208
|
+
column: n.name
|
|
1209
|
+
}, "safe", `drop unique constraint on ${e}.${n.name} — constraint is derived metadata, no data loss`));
|
|
1210
1210
|
}
|
|
1211
|
-
if (bn(
|
|
1212
|
-
let
|
|
1213
|
-
if (
|
|
1214
|
-
let
|
|
1211
|
+
if (bn(n.type, i) !== bn(r.type, i)) {
|
|
1212
|
+
let t = n.narrowedFrom;
|
|
1213
|
+
if (t && t.from === r.type) {
|
|
1214
|
+
let i = {
|
|
1215
1215
|
kind: "alter-column-type",
|
|
1216
1216
|
table: e,
|
|
1217
|
-
column:
|
|
1218
|
-
from:
|
|
1219
|
-
to:
|
|
1220
|
-
...
|
|
1217
|
+
column: n.name,
|
|
1218
|
+
from: r.type,
|
|
1219
|
+
to: n.type,
|
|
1220
|
+
...t.using === void 0 ? {} : { using: t.using }
|
|
1221
1221
|
};
|
|
1222
|
-
s.push(R(
|
|
1222
|
+
s.push(R(i, "needs-backfill", `type change ${r.type} → ${n.type} on ${e}.${n.name} — narrowedFrom('${t.from}') declared` + (t.using ? ` with USING ${t.using}` : " (implicit cast)")));
|
|
1223
1223
|
} else {
|
|
1224
|
-
let
|
|
1224
|
+
let t = {
|
|
1225
1225
|
kind: "alter-column-type",
|
|
1226
1226
|
table: e,
|
|
1227
|
-
column:
|
|
1228
|
-
from:
|
|
1229
|
-
to:
|
|
1227
|
+
column: n.name,
|
|
1228
|
+
from: r.type,
|
|
1229
|
+
to: n.type
|
|
1230
1230
|
};
|
|
1231
|
-
s.push(R(
|
|
1231
|
+
s.push(R(t, "lossy", `type change ${r.type} → ${n.type} on ${e}.${n.name} may not be value-preserving`, { fix: xn(r.type, n, i) }));
|
|
1232
1232
|
}
|
|
1233
1233
|
}
|
|
1234
|
-
if (
|
|
1235
|
-
let
|
|
1234
|
+
if (n.type === "text" && r.type === "text" && !t(i ?? "postgres") && n.maxLength !== r.maxLength) {
|
|
1235
|
+
let t = n.maxLength === void 0 || r.maxLength !== void 0 && n.maxLength > r.maxLength, i = r.maxLength === void 0 ? "unbounded" : `varchar(${r.maxLength})`, a = n.maxLength === void 0 ? "unbounded" : `varchar(${n.maxLength})`;
|
|
1236
1236
|
s.push(R({
|
|
1237
1237
|
kind: "alter-column-type",
|
|
1238
1238
|
table: e,
|
|
1239
|
-
column:
|
|
1239
|
+
column: n.name,
|
|
1240
1240
|
from: "text",
|
|
1241
1241
|
to: "text"
|
|
1242
|
-
},
|
|
1242
|
+
}, t ? "safe" : "needs-backfill", t ? `text length ${i} → ${a} on ${e}.${n.name} — widening, no data can be lost` : `text length ${i} → ${a} on ${e}.${n.name} — NARROWING; the ALTER fails if any existing value is longer than ${String(n.maxLength)}. Check first: SELECT COUNT(*) FROM ${e} WHERE LENGTH(${n.name}) > ${String(n.maxLength)}`));
|
|
1243
1243
|
}
|
|
1244
|
-
if (!nn(
|
|
1245
|
-
let
|
|
1244
|
+
if (!nn(n, r)) {
|
|
1245
|
+
let t = {
|
|
1246
1246
|
kind: "alter-column-default",
|
|
1247
1247
|
table: e,
|
|
1248
|
-
column:
|
|
1249
|
-
hasDefault:
|
|
1250
|
-
...
|
|
1248
|
+
column: n.name,
|
|
1249
|
+
hasDefault: n.hasDefault,
|
|
1250
|
+
...n.defaultValue === void 0 ? {} : { defaultValue: n.defaultValue }
|
|
1251
1251
|
};
|
|
1252
|
-
s.push(R(
|
|
1252
|
+
s.push(R(t, "safe", `default change on ${e}.${n.name} affects only future inserts`));
|
|
1253
1253
|
}
|
|
1254
|
-
if (!an(
|
|
1255
|
-
|
|
1254
|
+
if (!an(n.oneOf, r.oneOf)) {
|
|
1255
|
+
r.oneOf && r.oneOf.length > 0 && s.push(R({
|
|
1256
1256
|
kind: "drop-check",
|
|
1257
1257
|
table: e,
|
|
1258
|
-
column:
|
|
1259
|
-
}, "safe", `drop stale enum CHECK on ${e}.${
|
|
1260
|
-
let
|
|
1261
|
-
|
|
1258
|
+
column: n.name
|
|
1259
|
+
}, "safe", `drop stale enum CHECK on ${e}.${n.name}`));
|
|
1260
|
+
let t = ln(e, n);
|
|
1261
|
+
t && s.push(t);
|
|
1262
1262
|
}
|
|
1263
|
-
return JSON.stringify(rn(
|
|
1263
|
+
return JSON.stringify(rn(n.references)) !== JSON.stringify(rn(r.references)) && (r.references && s.push(R({
|
|
1264
1264
|
kind: "drop-foreign-key",
|
|
1265
1265
|
table: e,
|
|
1266
|
-
column:
|
|
1267
|
-
}, "safe", `drop FK on ${e}.${
|
|
1266
|
+
column: n.name
|
|
1267
|
+
}, "safe", `drop FK on ${e}.${n.name} (→ ${r.references.table})`)), s.push(...Cn(e, n))), s;
|
|
1268
1268
|
}, Cn = (e, t) => {
|
|
1269
1269
|
let n = [];
|
|
1270
1270
|
if (!t.references) return n;
|
|
@@ -2565,7 +2565,7 @@ BEGIN
|
|
|
2565
2565
|
})), x.mapError((e) => {
|
|
2566
2566
|
let r = ae(`applier: statement failed (op=${n})`, t, e, 400).trim();
|
|
2567
2567
|
return Object.assign(e, { message: r });
|
|
2568
|
-
}), x.map(() => void 0)), Z = (e, t, n) => x.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))}`), $ = (
|
|
2568
|
+
}), x.map(() => void 0)), Z = (e, t, n) => x.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))}`), $ = (t, n, r, i) => t.unsafe(`SELECT ${i === void 0 ? "*" : e(i, n)} FROM ${e(r, n)} WHERE 1=0`).pipe(x.as(!0), x.catchAll(() => x.succeed(!1))), Di = (e, t) => x.gen(function* () {
|
|
2569
2569
|
let n = I([Le], t).tables[0];
|
|
2570
2570
|
if (!n) return yield* x.die(/* @__PURE__ */ Error("migration applier: the resume-ledger table descriptor produced no snapshot. This is a framework bug."));
|
|
2571
2571
|
let r = {
|
|
@@ -2623,17 +2623,17 @@ BEGIN
|
|
|
2623
2623
|
SET ${e("status")} = ${"pending"}
|
|
2624
2624
|
WHERE ${e("runId")} = ${t} AND ${e("opIndex")} IN ${e.in(n)}
|
|
2625
2625
|
`);
|
|
2626
|
-
}), Mi = (e, t) => t === void 0 ? e`DELETE FROM ${e(D)}`.pipe(x.asVoid) : e`DELETE FROM ${e(D)} WHERE ${e("runId")} = ${t}`.pipe(x.asVoid), Ni = (
|
|
2627
|
-
if (qe(r.op, r.classification,
|
|
2628
|
-
let i = r.op, a = (
|
|
2629
|
-
if (
|
|
2626
|
+
}), Mi = (e, t) => t === void 0 ? e`DELETE FROM ${e(D)}`.pipe(x.asVoid) : e`DELETE FROM ${e(D)} WHERE ${e("runId")} = ${t}`.pipe(x.asVoid), Ni = (t, n, r) => x.gen(function* () {
|
|
2627
|
+
if (qe(r.op, r.classification, n) !== "artefact-repair") return "clean";
|
|
2628
|
+
let i = r.op, a = (t) => e(t, n);
|
|
2629
|
+
if (n === "sqlite" || n === "turso") {
|
|
2630
2630
|
if (!("table" in i)) return "clean";
|
|
2631
|
-
let
|
|
2632
|
-
return (yield* $(
|
|
2631
|
+
let e = `${i.table}${He}`;
|
|
2632
|
+
return (yield* $(t, n, e)) ? ((yield* $(t, n, i.table)) ? yield* X(t, `DROP TABLE ${a(e)}`, "resume-repair") : yield* X(t, `ALTER TABLE ${a(e)} RENAME TO ${a(i.table)}`, "resume-repair"), yield* x.logWarning(`migration resume: reconciled an interrupted sqlite table rebuild on '${i.table}' — replaying the operation`).pipe(x.annotateLogs({ scope: "voltro:migrate" })), "outstanding") : "clean";
|
|
2633
2633
|
}
|
|
2634
2634
|
if (i.kind !== "alter-column-type") return "clean";
|
|
2635
|
-
let o = `${i.column}${Ue}`, s = `${i.column}${We}`, c = yield* $(
|
|
2636
|
-
return !l && !u ? "clean" : c && l && !u ? (yield* X(
|
|
2635
|
+
let o = `${i.column}${Ue}`, s = `${i.column}${We}`, c = yield* $(t, n, i.table, i.column), l = yield* $(t, n, i.table, o), u = yield* $(t, n, i.table, s), d = (e) => x.logWarning(`migration resume: ${e}`).pipe(x.annotateLogs({ scope: "voltro:migrate" }));
|
|
2636
|
+
return !l && !u ? "clean" : c && l && !u ? (yield* X(t, `ALTER TABLE ${a(i.table)} DROP COLUMN ${a(o)}`, "resume-repair"), yield* d(`dropped a half-populated shadow column '${i.table}.${o}' — replaying the type change`), "outstanding") : !c && l && u ? (yield* X(t, `ALTER TABLE ${a(i.table)} RENAME COLUMN ${a(o)} TO ${a(i.column)}`, "resume-repair"), yield* X(t, `ALTER TABLE ${a(i.table)} DROP COLUMN ${a(s)}`, "resume-repair"), yield* d(`completed an interrupted shadow swap on '${i.table}.${i.column}'`), "applied") : c && !l && u ? (yield* X(t, `ALTER TABLE ${a(i.table)} DROP COLUMN ${a(s)}`, "resume-repair"), yield* d(`dropped the leftover pre-swap column '${i.table}.${s}'`), "applied") : yield* x.die(/* @__PURE__ */ Error(`applyPlan: an interrupted online type change on '${i.table}.${i.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.`));
|
|
2637
2637
|
}), Pi = (e, t, n, r = "fresh") => x.gen(function* () {
|
|
2638
2638
|
let i = process.hrtime.bigint(), a = Ei(n.declared), o = t.op;
|
|
2639
2639
|
switch (o.kind) {
|