@voltro/database 0.36.0 → 0.38.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 +182 -0
- package/dist/sql.js +371 -370
- package/package.json +2 -2
package/dist/sql.js
CHANGED
|
@@ -38,20 +38,21 @@ var ie = (e, t, n, r = 400) => {
|
|
|
38
38
|
let t = o(e);
|
|
39
39
|
t !== void 0 && s.push(`db.${e}: ${t}`);
|
|
40
40
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
let c = ae(o("code"));
|
|
42
|
+
return c !== void 0 && s.push("", c), s.push(""), s.join("\n");
|
|
43
|
+
}, ae = (e) => e === "3F000" ? "remedy: the connection's search_path names a schema that does not exist. Voltro sets it from DB_SCHEMA — create the schema (`CREATE SCHEMA \"<name>\"`) or unset DB_SCHEMA to use the connection default. Voltro creates TABLES, never the schema itself: that is a namespace decision, and inventing one from a typo is how a migration lands somewhere nobody is looking." : void 0, oe = (e) => e.unique === !0 && typeof e.where == "string" && e.where.length > 0, se = (e) => e.replace(/"/g, "`"), ce = (e) => typeof e == "string" ? e : e.name ?? String(e), le = (e, t) => `_ua_${t}_${e}`.slice(0, 64), ue = (e, t) => {
|
|
44
|
+
if (t !== "mysql" && t !== "mariadb" || !e.appliedIndexes.some(oe)) return e;
|
|
44
45
|
let n = { ...e.fields }, r = [], i = [];
|
|
45
46
|
for (let t of e.appliedIndexes) {
|
|
46
|
-
if (!
|
|
47
|
+
if (!oe(t)) {
|
|
47
48
|
r.push(t);
|
|
48
49
|
continue;
|
|
49
50
|
}
|
|
50
|
-
let a =
|
|
51
|
+
let a = se(t.where), o = [];
|
|
51
52
|
for (let r of t.fields) {
|
|
52
|
-
let i =
|
|
53
|
+
let i = ce(r);
|
|
53
54
|
if (!(i in e.fields)) throw Error(`@voltro/migrate: uniqueActive '${t.name}' on '${e.tableName}' references unknown column '${i}'.`);
|
|
54
|
-
let s =
|
|
55
|
+
let s = le(t.name, i);
|
|
55
56
|
o.push(s), n[s] = {
|
|
56
57
|
type: "text",
|
|
57
58
|
nullable: !0,
|
|
@@ -74,7 +75,7 @@ var ie = (e, t, n, r = 400) => {
|
|
|
74
75
|
appliedIndexes: r,
|
|
75
76
|
appliedUniques: [...e.appliedUniques ?? [], ...i]
|
|
76
77
|
};
|
|
77
|
-
}, w = (e, t) => `${e}::${t}`,
|
|
78
|
+
}, w = (e, t) => `${e}::${t}`, de = (e, t) => {
|
|
78
79
|
let n = /* @__PURE__ */ new Set(), r = /* @__PURE__ */ new Set(), i = /* @__PURE__ */ new Set(), a = (e) => {
|
|
79
80
|
n.add(e);
|
|
80
81
|
let o = [...t(e)].filter((t) => t.target !== e).sort((e, t) => e.column.localeCompare(t.column));
|
|
@@ -83,21 +84,21 @@ var ie = (e, t, n, r = 400) => {
|
|
|
83
84
|
};
|
|
84
85
|
for (let t of [...e].sort()) r.has(t) || a(t);
|
|
85
86
|
return i;
|
|
86
|
-
},
|
|
87
|
+
}, fe = 6322741009312437n, pe = (e) => {
|
|
87
88
|
let t = e?.trim();
|
|
88
89
|
return t === void 0 || t === "" || t === "public" ? void 0 : t;
|
|
89
|
-
},
|
|
90
|
-
let t =
|
|
91
|
-
for (let n of new TextEncoder().encode(e)) t ^= BigInt(n), t = t *
|
|
90
|
+
}, me = (e) => e?.schema === void 0 ? pe(process.env.DB_SCHEMA) : pe(e.schema), he = 14695981039346656037n, ge = 1099511628211n, _e = 18446744073709551615n, ve = (e) => {
|
|
91
|
+
let t = he;
|
|
92
|
+
for (let n of new TextEncoder().encode(e)) t ^= BigInt(n), t = t * ge & _e;
|
|
92
93
|
return t;
|
|
93
|
-
}, ve = (e) => {
|
|
94
|
-
let t = fe(e);
|
|
95
|
-
return t === void 0 ? de : _e(`voltro_migration_lock:${t}`) & 9223372036854775807n;
|
|
96
94
|
}, ye = (e) => {
|
|
97
|
-
let t =
|
|
95
|
+
let t = pe(e);
|
|
96
|
+
return t === void 0 ? fe : ve(`voltro_migration_lock:${t}`) & 9223372036854775807n;
|
|
97
|
+
}, T = (e) => {
|
|
98
|
+
let t = `voltro_migration_${fe.toString(36)}`, n = pe(e);
|
|
98
99
|
if (n === void 0) return t;
|
|
99
100
|
let r = `${t}_${n}`;
|
|
100
|
-
return /^[A-Za-z0-9_]+$/.test(n) && r.length <= 64 ? r : `${t}_h${(
|
|
101
|
+
return /^[A-Za-z0-9_]+$/.test(n) && r.length <= 64 ? r : `${t}_h${(ve(`voltro_migration_lock:${n}`) & _e).toString(16)}`;
|
|
101
102
|
}, be = 250, xe = () => {
|
|
102
103
|
let e = Number(process.env.VOLTRO_MIGRATION_LOCK_TIMEOUT_MS);
|
|
103
104
|
return Number.isFinite(e) && e > 0 ? e : 3e4;
|
|
@@ -124,23 +125,23 @@ var ie = (e, t, n, r = 400) => {
|
|
|
124
125
|
(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
126
|
}), ke = (e, t) => b.gen(function* () {
|
|
126
127
|
yield* e`EXEC sp_releaseapplock @Resource = ${t}, @LockOwner = 'Session'`;
|
|
127
|
-
}), Ae = b.void,
|
|
128
|
-
let n =
|
|
128
|
+
}), Ae = b.void, E = (e, t) => {
|
|
129
|
+
let n = me(t);
|
|
129
130
|
return e.onDialectOrElse({
|
|
130
|
-
mysql: () => Te(e,
|
|
131
|
-
mssql: () => Oe(e,
|
|
131
|
+
mysql: () => Te(e, T(n)),
|
|
132
|
+
mssql: () => Oe(e, T(n)),
|
|
132
133
|
sqlite: () => Ae,
|
|
133
|
-
orElse: () => Se(e,
|
|
134
|
+
orElse: () => Se(e, ye(n))
|
|
134
135
|
});
|
|
135
|
-
},
|
|
136
|
-
let n =
|
|
136
|
+
}, D = (e, t) => {
|
|
137
|
+
let n = me(t);
|
|
137
138
|
return e.onDialectOrElse({
|
|
138
|
-
mysql: () => Ee(e,
|
|
139
|
-
mssql: () => ke(e,
|
|
139
|
+
mysql: () => Ee(e, T(n)),
|
|
140
|
+
mssql: () => ke(e, T(n)),
|
|
140
141
|
sqlite: () => Ae,
|
|
141
|
-
orElse: () => Ce(e,
|
|
142
|
+
orElse: () => Ce(e, ye(n))
|
|
142
143
|
});
|
|
143
|
-
}, je = (e, t, n) => b.acquireUseRelease(
|
|
144
|
+
}, je = (e, t, n) => b.acquireUseRelease(E(e, n), () => t, () => D(e, n).pipe(b.orDie)), Me = (e) => e.onDialectOrElse({
|
|
144
145
|
pg: () => "postgres",
|
|
145
146
|
mysql: () => "mysql",
|
|
146
147
|
mssql: () => "mssql",
|
|
@@ -153,7 +154,7 @@ var ie = (e, t, n, r = 400) => {
|
|
|
153
154
|
mariadb: "per-operation",
|
|
154
155
|
sqlite: "per-operation",
|
|
155
156
|
turso: "per-operation"
|
|
156
|
-
}, Pe = (e) => Ne[e],
|
|
157
|
+
}, Pe = (e) => Ne[e], O = "_voltro_migration_ops", Fe = l(O, {
|
|
157
158
|
id: e({ prefix: "mop" }),
|
|
158
159
|
runId: _(),
|
|
159
160
|
planFingerprint: _(),
|
|
@@ -216,9 +217,9 @@ var ie = (e, t, n, r = 400) => {
|
|
|
216
217
|
Object.keys(Ne);
|
|
217
218
|
//#endregion
|
|
218
219
|
//#region src/migrations/reactiveTriggerName.ts
|
|
219
|
-
var
|
|
220
|
+
var k = "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" }), A = (e) => {
|
|
220
221
|
Xe.warn(e.replace(/^\[voltro:migrate\]\s*/, ""));
|
|
221
|
-
},
|
|
222
|
+
}, j = (e, t) => {
|
|
222
223
|
let n = e.type;
|
|
223
224
|
if (e.spatial) {
|
|
224
225
|
if (t === "postgres") return `${e.spatial.kind}(${e.spatial.geomKind},${e.spatial.srid})`;
|
|
@@ -229,7 +230,7 @@ var O = "framework_changes", Ke = "framework_changes_", qe = (e, t) => t === "fr
|
|
|
229
230
|
return e.rawDdl;
|
|
230
231
|
}
|
|
231
232
|
if (n === "array" && e.arrayElement) {
|
|
232
|
-
if (t === "postgres") return `${
|
|
233
|
+
if (t === "postgres") return `${j({
|
|
233
234
|
...e,
|
|
234
235
|
type: e.arrayElement,
|
|
235
236
|
arrayElement: void 0
|
|
@@ -332,19 +333,19 @@ var O = "framework_changes", Ke = "framework_changes_", qe = (e, t) => t === "fr
|
|
|
332
333
|
}, Ze = (e, t) => {
|
|
333
334
|
if (!e.hasDefault || e.defaultFactory !== void 0) return null;
|
|
334
335
|
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 ?
|
|
336
|
+
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 ? M(n, t) : null;
|
|
336
337
|
}, Qe = (e, t, n) => {
|
|
337
|
-
if (n !== "postgres" || t.type !== "array" || t.arrayElement === void 0) return
|
|
338
|
-
let r =
|
|
338
|
+
if (n !== "postgres" || t.type !== "array" || t.arrayElement === void 0) return M(e, n);
|
|
339
|
+
let r = j({
|
|
339
340
|
...t,
|
|
340
341
|
type: t.arrayElement,
|
|
341
342
|
arrayElement: void 0
|
|
342
343
|
}, n);
|
|
343
344
|
return `DEFAULT '{${e.map((e) => typeof e == "number" || typeof e == "boolean" ? String(e) : `"${String(e).replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`).join(",").replace(/'/g, "''")}}'::${r}[]`;
|
|
344
|
-
},
|
|
345
|
+
}, M = (e, t) => {
|
|
345
346
|
let n = JSON.stringify(e).replace(/'/g, "''");
|
|
346
347
|
return t === "postgres" ? `DEFAULT '${n}'::jsonb` : t === "mysql" || t === "mariadb" ? `DEFAULT (CONVERT('${n}' USING utf8mb4))` : `DEFAULT '${n}'`;
|
|
347
|
-
},
|
|
348
|
+
}, N = (e, t) => {
|
|
348
349
|
switch (t) {
|
|
349
350
|
case "postgres": return `"${e}" BIGSERIAL PRIMARY KEY`;
|
|
350
351
|
case "mysql":
|
|
@@ -366,19 +367,19 @@ var O = "framework_changes", Ke = "framework_changes_", qe = (e, t) => t === "fr
|
|
|
366
367
|
if (n !== "mysql" && n !== "mariadb") return r;
|
|
367
368
|
let a = t[e];
|
|
368
369
|
if (!a) return r;
|
|
369
|
-
let o =
|
|
370
|
+
let o = j(a, n).toUpperCase();
|
|
370
371
|
return o.endsWith("TEXT") || o.endsWith("BLOB") ? `${r}(${tt})` : r;
|
|
371
372
|
}, rt = (e, t) => {
|
|
372
373
|
if (!e.generatedAs) return null;
|
|
373
374
|
let { expr: n, stored: r } = e.generatedAs;
|
|
374
|
-
if (t === "postgres") return r ||
|
|
375
|
+
if (t === "postgres") return r || A("[voltro:migrate] generated column requested as VIRTUAL but postgres only supports STORED — promoting"), `GENERATED ALWAYS AS (${n}) STORED`;
|
|
375
376
|
if (t === "mysql" || t === "mariadb") return `GENERATED ALWAYS AS (${n}) ${r ? "STORED" : "VIRTUAL"}`;
|
|
376
377
|
if (t === "mssql") return `AS (${n})${r ? " PERSISTED" : ""}`;
|
|
377
378
|
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.");
|
|
378
379
|
return `GENERATED ALWAYS AS (${n}) ${r ? "STORED" : "VIRTUAL"}`;
|
|
379
380
|
}, it = (e, t, n, a, o = !1, s = !1) => {
|
|
380
|
-
if (t.type === "id" && t.idScheme?.kind === "numeric") return
|
|
381
|
-
let c = n === "mssql" && t.type === "text" && !t.generatedAs && t.maxLength === void 0 && a?.has(e) ? "NVARCHAR(450)" :
|
|
381
|
+
if (t.type === "id" && t.idScheme?.kind === "numeric") return N(e, n);
|
|
382
|
+
let c = n === "mssql" && t.type === "text" && !t.generatedAs && t.maxLength === void 0 && a?.has(e) ? "NVARCHAR(450)" : j(t, n), l = [i(e, n), c];
|
|
382
383
|
t.type === "id" && !s && l.push("PRIMARY KEY");
|
|
383
384
|
let u = rt(t, n);
|
|
384
385
|
if (u) l.push(u);
|
|
@@ -445,21 +446,21 @@ var O = "framework_changes", Ke = "framework_changes_", qe = (e, t) => t === "fr
|
|
|
445
446
|
using: " USING GIST",
|
|
446
447
|
withSuffix: "",
|
|
447
448
|
skip: !1
|
|
448
|
-
} : (
|
|
449
|
+
} : (A(`[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
450
|
if (r === "gin") return t === "postgres" ? {
|
|
450
451
|
using: " USING GIN",
|
|
451
452
|
withSuffix: "",
|
|
452
453
|
skip: !1
|
|
453
|
-
} : t === "mysql" || t === "mariadb" ? (
|
|
454
|
+
} : t === "mysql" || t === "mariadb" ? (A(`[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(...).`), {
|
|
454
455
|
using: "",
|
|
455
456
|
withSuffix: "",
|
|
456
457
|
skip: !0
|
|
457
|
-
}) : (
|
|
458
|
+
}) : (A(`[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
459
|
if (r === "brin") return t === "postgres" ? {
|
|
459
460
|
using: " USING BRIN",
|
|
460
461
|
withSuffix: "",
|
|
461
462
|
skip: !1
|
|
462
|
-
} : (
|
|
463
|
+
} : (A(`[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
464
|
if (r === "hnsw") {
|
|
464
465
|
if (t === "postgres") {
|
|
465
466
|
let e = i?.hnsw?.m ?? 16, t = i?.hnsw?.efConstruction ?? 64, n = i?.hnsw?.opclass ?? y(i?.hnsw?.distance);
|
|
@@ -470,7 +471,7 @@ var O = "framework_changes", Ke = "framework_changes_", qe = (e, t) => t === "fr
|
|
|
470
471
|
skip: !1
|
|
471
472
|
};
|
|
472
473
|
}
|
|
473
|
-
return
|
|
474
|
+
return A(`[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.`), {
|
|
474
475
|
using: "",
|
|
475
476
|
withSuffix: "",
|
|
476
477
|
skip: !0
|
|
@@ -503,15 +504,15 @@ var O = "framework_changes", Ke = "framework_changes_", qe = (e, t) => t === "fr
|
|
|
503
504
|
let a = n.columns.map((e) => i(e, t)).join(", ");
|
|
504
505
|
r.push(`CREATE FULLTEXT INDEX ${i(n.name, t)} ON ${i(e.tableName, t)} (${a});`);
|
|
505
506
|
} 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)) :
|
|
507
|
+
else t === "sqlite" ? r.push(...ot(e.tableName, n, t)) : A(`[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
508
|
for (let n of e.appliedIndexes) {
|
|
508
509
|
if ((t === "mariadb" || t === "mssql") && n.fields.some((e) => typeof e != "string" && "jsonPath" in e)) {
|
|
509
|
-
|
|
510
|
+
A(`[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(...).`);
|
|
510
511
|
continue;
|
|
511
512
|
}
|
|
512
513
|
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
514
|
let i = "";
|
|
514
|
-
n.where !== void 0 && n.where.length > 0 && (t === "mysql" || t === "mariadb" ?
|
|
515
|
+
n.where !== void 0 && n.where.length > 0 && (t === "mysql" || t === "mariadb" ? A(`[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
516
|
let s = l(n.name, e.tableName, n.fields, n.kind, n.kindOptions, i, n.unique === !0);
|
|
516
517
|
s !== "" && r.push(s), n.fields.every((e) => typeof e == "string") && a.add(o(n.fields));
|
|
517
518
|
}
|
|
@@ -521,7 +522,7 @@ var O = "framework_changes", Ke = "framework_changes_", qe = (e, t) => t === "fr
|
|
|
521
522
|
r.push(l(i, e.tableName, [t])), a.add(o([t]));
|
|
522
523
|
}
|
|
523
524
|
return r.join("\n");
|
|
524
|
-
}, ct = (e, t, n =
|
|
525
|
+
}, ct = (e, t, n = k) => {
|
|
525
526
|
if (t !== "postgres") return [];
|
|
526
527
|
let r = e.filter((e) => e.isReactive !== !1);
|
|
527
528
|
return r.length === 0 ? [] : [lt(n), ...r.map((e) => dt(e, n))];
|
|
@@ -600,7 +601,7 @@ $$ LANGUAGE plpgsql;
|
|
|
600
601
|
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`);
|
|
601
602
|
return r;
|
|
602
603
|
}, 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 =
|
|
604
|
+
let t = new Map(e.map((e) => [e.tableName, e])), n = de(e.map((e) => e.tableName), (e) => {
|
|
604
605
|
let n = t.get(e);
|
|
605
606
|
return n ? Object.entries(n.fields).flatMap(([e, n]) => {
|
|
606
607
|
if (n.type !== "reference" || !n.references) return [];
|
|
@@ -696,8 +697,8 @@ BEGIN
|
|
|
696
697
|
tables: t,
|
|
697
698
|
views: n
|
|
698
699
|
};
|
|
699
|
-
}, xt = (e, t, n =
|
|
700
|
-
let { tables: i, views: o } = bt(e), { sorted: s, cyclic: c } = ht(i.map((e) =>
|
|
700
|
+
}, xt = (e, t, n = k) => {
|
|
701
|
+
let { tables: i, views: o } = bt(e), { sorted: s, cyclic: c } = ht(i.map((e) => ue(e, t))), l = _t(c, t), u = [];
|
|
701
702
|
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
703
|
let d = yt(s, t);
|
|
703
704
|
d && u.push(d);
|
|
@@ -734,7 +735,7 @@ BEGIN
|
|
|
734
735
|
r && d.push(r);
|
|
735
736
|
}
|
|
736
737
|
return d.join("\n\n") + "\n";
|
|
737
|
-
}, wt = (e, t, n =
|
|
738
|
+
}, wt = (e, t, n = k) => {
|
|
738
739
|
let { sorted: r, cyclic: i } = ht(e), a = _t(i, t), o = [], s = yt(r, t);
|
|
739
740
|
s && o.push(s);
|
|
740
741
|
let c = vt(r, t);
|
|
@@ -742,7 +743,7 @@ BEGIN
|
|
|
742
743
|
for (let e of r) o.push(at(e, t, null, a.get(e.tableName)));
|
|
743
744
|
for (let e of i) o.push(gt(e, t));
|
|
744
745
|
return o.push(...ct(r, t, n)), o.join("\n\n") + "\n";
|
|
745
|
-
}, Tt = (e, t = "postgres", n =
|
|
746
|
+
}, Tt = (e, t = "postgres", n = k, r = {}) => b.gen(function* () {
|
|
746
747
|
let i = yield* C.SqlClient;
|
|
747
748
|
yield* je(i, Mt(i, xt(e, t, n), t, r.retryFilter), { schema: r.lockSchema }).pipe(b.orDie);
|
|
748
749
|
}), Et = (e, t) => {
|
|
@@ -825,10 +826,10 @@ BEGIN
|
|
|
825
826
|
name: e.name,
|
|
826
827
|
columns: Bt(e.columns),
|
|
827
828
|
indexes: Vt(e.indexes)
|
|
828
|
-
})).sort((e, t) => e.name.localeCompare(t.name)),
|
|
829
|
+
})).sort((e, t) => e.name.localeCompare(t.name)), P = (e) => {
|
|
829
830
|
let t = { tables: Ht(e.tables) }, n = JSON.stringify(Rt(t));
|
|
830
831
|
return ee("sha256").update(n).digest("hex");
|
|
831
|
-
},
|
|
832
|
+
}, F = (e) => e.slice(0, 16), Ut = (e, t) => {
|
|
832
833
|
let n = t.references ? {
|
|
833
834
|
table: t.references().tableName,
|
|
834
835
|
column: "id",
|
|
@@ -940,10 +941,10 @@ BEGIN
|
|
|
940
941
|
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}').`);
|
|
941
942
|
t.set(e.name, n.name);
|
|
942
943
|
}
|
|
943
|
-
},
|
|
944
|
-
let n = e.map((e) => Gt(
|
|
944
|
+
}, I = (e, t) => {
|
|
945
|
+
let n = e.map((e) => Gt(ue(e, t)));
|
|
945
946
|
return Kt(n), { tables: n };
|
|
946
|
-
}, qt = (e, t, n =
|
|
947
|
+
}, qt = (e, t, n = k) => b.gen(function* () {
|
|
947
948
|
let r = (yield* e`
|
|
948
949
|
SELECT p.prosrc
|
|
949
950
|
FROM pg_proc p
|
|
@@ -1017,29 +1018,29 @@ BEGIN
|
|
|
1017
1018
|
snapshot: n.get(e.tableName),
|
|
1018
1019
|
columnDefs: e.fields
|
|
1019
1020
|
}]));
|
|
1020
|
-
},
|
|
1021
|
+
}, L = (e) => {
|
|
1021
1022
|
if (typeof e != "object" || !e) return JSON.stringify(e);
|
|
1022
|
-
if (Array.isArray(e)) return `[${e.map(
|
|
1023
|
+
if (Array.isArray(e)) return `[${e.map(L).join(",")}]`;
|
|
1023
1024
|
let t = e;
|
|
1024
|
-
return `{${Object.keys(t).sort().map((e) => `${JSON.stringify(e)}:${
|
|
1025
|
+
return `{${Object.keys(t).sort().map((e) => `${JSON.stringify(e)}:${L(t[e])}`).join(",")}}`;
|
|
1025
1026
|
}, $t = (e) => {
|
|
1026
1027
|
if (!e.hasDefault || e.defaultValue === void 0 || e.defaultValue === null) return null;
|
|
1027
1028
|
let t = e.defaultValue;
|
|
1028
1029
|
if (typeof t == "boolean") return t ? "__BOOL_TRUE__" : "__BOOL_FALSE__";
|
|
1029
1030
|
if (typeof t == "number") return String(t);
|
|
1030
|
-
if (typeof t != "string") return
|
|
1031
|
+
if (typeof t != "string") return L(t);
|
|
1031
1032
|
let n = t.trim(), r = n.toLowerCase().replace(/\s+/g, "");
|
|
1032
1033
|
if (r === "now" || r === "now()" || /^current_timestamp(\(\d*\))?$/.test(r) || r === "sysutcdatetime()" || r === "sysdatetime()" || r === "getutcdate()" || r === "getdate()") return "__NOW__";
|
|
1033
1034
|
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);
|
|
1034
1035
|
if (o) try {
|
|
1035
|
-
return
|
|
1036
|
+
return L(JSON.parse(o[1]));
|
|
1036
1037
|
} catch {}
|
|
1037
1038
|
let s = /^convert\(\s*(?:_\w+\s*)?\\?'(.*?)\\?'\s+using\s+[\w]+\s*\)$/i.exec(a);
|
|
1038
1039
|
if (s) try {
|
|
1039
|
-
return
|
|
1040
|
+
return L(JSON.parse(s[1]));
|
|
1040
1041
|
} catch {}
|
|
1041
1042
|
if (a.startsWith("{") || a.startsWith("[")) try {
|
|
1042
|
-
return
|
|
1043
|
+
return L(JSON.parse(a));
|
|
1043
1044
|
} catch {}
|
|
1044
1045
|
return /^-?\d+(\.\d+)?$/.test(a) ? String(Number(a)) : a;
|
|
1045
1046
|
}, en = (e, t) => {
|
|
@@ -1064,12 +1065,12 @@ BEGIN
|
|
|
1064
1065
|
return i.every((e, t) => e === a[t]);
|
|
1065
1066
|
}, rn = (e, t) => {
|
|
1066
1067
|
try {
|
|
1067
|
-
let n = /\((\d+)\)/.exec(
|
|
1068
|
+
let n = /\((\d+)\)/.exec(j(e, t));
|
|
1068
1069
|
return n ? Number(n[1]) : void 0;
|
|
1069
1070
|
} catch {
|
|
1070
1071
|
return;
|
|
1071
1072
|
}
|
|
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 ?
|
|
1073
|
+
}, 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 ? R({
|
|
1073
1074
|
kind: "add-check",
|
|
1074
1075
|
table: e,
|
|
1075
1076
|
column: t.name,
|
|
@@ -1104,7 +1105,7 @@ BEGIN
|
|
|
1104
1105
|
added: i,
|
|
1105
1106
|
removed: a
|
|
1106
1107
|
};
|
|
1107
|
-
},
|
|
1108
|
+
}, R = (e, t, n, r) => ({
|
|
1108
1109
|
op: e,
|
|
1109
1110
|
classification: t,
|
|
1110
1111
|
atomicByDialect: Zt(e.kind),
|
|
@@ -1116,21 +1117,21 @@ BEGIN
|
|
|
1116
1117
|
table: e,
|
|
1117
1118
|
column: t
|
|
1118
1119
|
};
|
|
1119
|
-
return t.nullable ?
|
|
1120
|
+
return t.nullable ? R(r, "safe", "nullable column add — no backfill needed") : t.hasDefault && t.defaultValue !== void 0 ? R(r, "needs-default", "NOT NULL with literal default — DDL DEFAULT will populate existing rows") : n?.backfill ? R(r, "needs-backfill", `backfill declared (kind=${n.backfill.kind}) — applier will run 3-step add/update/set-not-null`) : R(r, "needs-backfill", "NOT NULL column on a table whose row count is unknown", { fix: `declare \`${t.name}: <type>().backfill(sql\`...\`)\` OR \`.default(value)\` so existing rows survive the migration` });
|
|
1120
1121
|
}, fn = (e, t, n) => {
|
|
1121
1122
|
let r = {
|
|
1122
1123
|
kind: "drop-column",
|
|
1123
1124
|
table: e,
|
|
1124
1125
|
column: t.name
|
|
1125
1126
|
};
|
|
1126
|
-
return n?.dropped ?
|
|
1127
|
-
}, pn = (e, t, n, r, i) =>
|
|
1127
|
+
return n?.dropped ? R(r, "lossy", `column dropped via \`${t.name}: dropped()\` marker — intentional`) : R(r, "lossy", "column missing from declared schema", { fix: `if intentional, add \`${t.name}: dropped()\` to the schema. If a typo, restore the field` });
|
|
1128
|
+
}, pn = (e, t, n, r, i) => R({
|
|
1128
1129
|
kind: "create-table",
|
|
1129
1130
|
table: e,
|
|
1130
1131
|
columns: t,
|
|
1131
1132
|
indexes: n,
|
|
1132
1133
|
...r && r.length > 0 ? { primaryKey: r } : {}
|
|
1133
|
-
}, "safe", i ?? "new table — no data to preserve"), mn = (e) =>
|
|
1134
|
+
}, "safe", i ?? "new table — no data to preserve"), mn = (e) => R({
|
|
1134
1135
|
kind: "drop-table",
|
|
1135
1136
|
table: e
|
|
1136
1137
|
}, "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) => {
|
|
@@ -1148,12 +1149,12 @@ BEGIN
|
|
|
1148
1149
|
kind: "add-index",
|
|
1149
1150
|
table: e,
|
|
1150
1151
|
index: t
|
|
1151
|
-
}, i ?
|
|
1152
|
-
}, _n = (e, t) => t.unique && t.columns.length > 1 ?
|
|
1152
|
+
}, i ? R(a, "online-required", `online build on ${n} rows — CONCURRENTLY / INPLACE / WITH(ONLINE=ON) depending on dialect`) : R(a, "safe", t.unique ? t.columns.length === 1 ? "add unique constraint" : "add composite UNIQUE constraint" : "add B-tree index");
|
|
1153
|
+
}, _n = (e, t) => t.unique && t.columns.length > 1 ? R({
|
|
1153
1154
|
kind: "drop-unique-composite",
|
|
1154
1155
|
table: e,
|
|
1155
1156
|
name: t.name
|
|
1156
|
-
}, "safe", "drop composite UNIQUE constraint (no data loss — constraint is derived metadata)") :
|
|
1157
|
+
}, "safe", "drop composite UNIQUE constraint (no data loss — constraint is derived metadata)") : R({
|
|
1157
1158
|
kind: "drop-index",
|
|
1158
1159
|
table: e,
|
|
1159
1160
|
index: t.name
|
|
@@ -1174,7 +1175,7 @@ BEGIN
|
|
|
1174
1175
|
column: t.name,
|
|
1175
1176
|
toNullable: t.nullable
|
|
1176
1177
|
};
|
|
1177
|
-
s.push(t.nullable ?
|
|
1178
|
+
s.push(t.nullable ? R(n, "safe", `loosen ${e}.${t.name} to nullable`) : R(n, "needs-backfill", `tighten ${e}.${t.name} to NOT NULL — existing rows must already be non-null`));
|
|
1178
1179
|
}
|
|
1179
1180
|
if (t.unique !== n.unique) if (t.unique) {
|
|
1180
1181
|
let n = {
|
|
@@ -1182,8 +1183,8 @@ BEGIN
|
|
|
1182
1183
|
table: e,
|
|
1183
1184
|
column: t.name
|
|
1184
1185
|
}, r = (a ?? 0) >= o;
|
|
1185
|
-
s.push(r ?
|
|
1186
|
-
} else s.push(
|
|
1186
|
+
s.push(r ? R(n, "online-required", `online unique build on ${a} rows — CONCURRENTLY / INPLACE / WITH(ONLINE=ON) depending on dialect`) : R(n, "safe", `add unique constraint on ${e}.${t.name} — existing rows must already be distinct (else the DB rejects at migrate)`));
|
|
1187
|
+
} else s.push(R({
|
|
1187
1188
|
kind: "drop-unique",
|
|
1188
1189
|
table: e,
|
|
1189
1190
|
column: t.name
|
|
@@ -1199,7 +1200,7 @@ BEGIN
|
|
|
1199
1200
|
to: t.type,
|
|
1200
1201
|
...r.using === void 0 ? {} : { using: r.using }
|
|
1201
1202
|
};
|
|
1202
|
-
s.push(
|
|
1203
|
+
s.push(R(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)")));
|
|
1203
1204
|
} else {
|
|
1204
1205
|
let r = {
|
|
1205
1206
|
kind: "alter-column-type",
|
|
@@ -1208,12 +1209,12 @@ BEGIN
|
|
|
1208
1209
|
from: n.type,
|
|
1209
1210
|
to: t.type
|
|
1210
1211
|
};
|
|
1211
|
-
s.push(
|
|
1212
|
+
s.push(R(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` }));
|
|
1212
1213
|
}
|
|
1213
1214
|
}
|
|
1214
1215
|
if (t.type === "text" && n.type === "text" && !r(i ?? "postgres") && t.maxLength !== n.maxLength) {
|
|
1215
1216
|
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(
|
|
1217
|
+
s.push(R({
|
|
1217
1218
|
kind: "alter-column-type",
|
|
1218
1219
|
table: e,
|
|
1219
1220
|
column: t.name,
|
|
@@ -1229,10 +1230,10 @@ BEGIN
|
|
|
1229
1230
|
hasDefault: t.hasDefault,
|
|
1230
1231
|
...t.defaultValue === void 0 ? {} : { defaultValue: t.defaultValue }
|
|
1231
1232
|
};
|
|
1232
|
-
s.push(
|
|
1233
|
+
s.push(R(n, "safe", `default change on ${e}.${t.name} affects only future inserts`));
|
|
1233
1234
|
}
|
|
1234
1235
|
if (!nn(t.oneOf, n.oneOf)) {
|
|
1235
|
-
n.oneOf && n.oneOf.length > 0 && s.push(
|
|
1236
|
+
n.oneOf && n.oneOf.length > 0 && s.push(R({
|
|
1236
1237
|
kind: "drop-check",
|
|
1237
1238
|
table: e,
|
|
1238
1239
|
column: t.name
|
|
@@ -1240,7 +1241,7 @@ BEGIN
|
|
|
1240
1241
|
let r = sn(e, t);
|
|
1241
1242
|
r && s.push(r);
|
|
1242
1243
|
}
|
|
1243
|
-
return JSON.stringify(tn(t.references)) !== JSON.stringify(tn(n.references)) && (n.references && s.push(
|
|
1244
|
+
return JSON.stringify(tn(t.references)) !== JSON.stringify(tn(n.references)) && (n.references && s.push(R({
|
|
1244
1245
|
kind: "drop-foreign-key",
|
|
1245
1246
|
table: e,
|
|
1246
1247
|
column: t.name
|
|
@@ -1257,15 +1258,15 @@ BEGIN
|
|
|
1257
1258
|
...t.references.onDelete ? { onDelete: t.references.onDelete } : {},
|
|
1258
1259
|
...t.references.onUpdate ? { onUpdate: t.references.onUpdate } : {}
|
|
1259
1260
|
};
|
|
1260
|
-
return i === "null" && !t.nullable ? n.push(
|
|
1261
|
+
return i === "null" && !t.nullable ? n.push(R(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(R({
|
|
1261
1262
|
...a,
|
|
1262
1263
|
orphanPolicy: i
|
|
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(
|
|
1264
|
+
}, "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(R(a, "needs-backfill", `add FK ${e}.${t.name} → ${r} — existing rows must already reference a valid ${r} (else set orphanPolicy:'null'/'delete')`)), n;
|
|
1264
1265
|
}, xn = (e, t, n) => {
|
|
1265
1266
|
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));
|
|
1266
1267
|
for (let [n, c] of Object.entries(t)) {
|
|
1267
1268
|
let t = c.renamedFrom;
|
|
1268
|
-
t && o.has(t) && s.has(n) && (i.add(n), a.add(t), r.push(
|
|
1269
|
+
t && o.has(t) && s.has(n) && (i.add(n), a.add(t), r.push(R({
|
|
1269
1270
|
kind: "rename-column",
|
|
1270
1271
|
table: e.tableName,
|
|
1271
1272
|
from: t,
|
|
@@ -1296,7 +1297,7 @@ BEGIN
|
|
|
1296
1297
|
let t = o(n), r = a.filter((e) => !l.has(e.name) && s(e) === t);
|
|
1297
1298
|
if (r.length !== 1) continue;
|
|
1298
1299
|
let i = r[0];
|
|
1299
|
-
i.name !== n.name && (l.add(i.name), u.add(n.name), c.push(
|
|
1300
|
+
i.name !== n.name && (l.add(i.name), u.add(n.name), c.push(R({
|
|
1300
1301
|
kind: "rename-index",
|
|
1301
1302
|
table: e,
|
|
1302
1303
|
from: n.name,
|
|
@@ -1319,7 +1320,7 @@ BEGIN
|
|
|
1319
1320
|
continue;
|
|
1320
1321
|
}
|
|
1321
1322
|
if (t.has(c.name)) {
|
|
1322
|
-
r.push(
|
|
1323
|
+
r.push(R({
|
|
1323
1324
|
kind: "rename-table",
|
|
1324
1325
|
from: e,
|
|
1325
1326
|
to: c.name
|
|
@@ -1327,14 +1328,14 @@ BEGIN
|
|
|
1327
1328
|
continue;
|
|
1328
1329
|
}
|
|
1329
1330
|
if ((s.get(e) ?? 0) !== 1) {
|
|
1330
|
-
r.push(
|
|
1331
|
+
r.push(R({
|
|
1331
1332
|
kind: "rename-table",
|
|
1332
1333
|
from: e,
|
|
1333
1334
|
to: c.name
|
|
1334
1335
|
}, "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.` }));
|
|
1335
1336
|
continue;
|
|
1336
1337
|
}
|
|
1337
|
-
i.set(c.name, e), a.add(e), r.push(
|
|
1338
|
+
i.set(c.name, e), a.add(e), r.push(R({
|
|
1338
1339
|
kind: "rename-table",
|
|
1339
1340
|
from: e,
|
|
1340
1341
|
to: c.name
|
|
@@ -1360,7 +1361,7 @@ BEGIN
|
|
|
1360
1361
|
...e,
|
|
1361
1362
|
name: r,
|
|
1362
1363
|
table: n
|
|
1363
|
-
} : i ? (a.push(
|
|
1364
|
+
} : i ? (a.push(R({
|
|
1364
1365
|
kind: "rename-index",
|
|
1365
1366
|
table: n,
|
|
1366
1367
|
from: e.name,
|
|
@@ -1415,7 +1416,7 @@ BEGIN
|
|
|
1415
1416
|
}
|
|
1416
1417
|
return t;
|
|
1417
1418
|
}, kn = (e) => {
|
|
1418
|
-
let t =
|
|
1419
|
+
let t = I(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);
|
|
1419
1420
|
a.push(...o.ops);
|
|
1420
1421
|
for (let e of t.tables) if (!o.renamedInto.has(e.name) && !r.has(e.name)) {
|
|
1421
1422
|
a.push(pn(e.name, e.columns, e.indexes, e.primaryKey, o.refusals.get(e.name)));
|
|
@@ -1457,8 +1458,8 @@ BEGIN
|
|
|
1457
1458
|
column: e.name,
|
|
1458
1459
|
target: e.references.table
|
|
1459
1460
|
}] : []) : [];
|
|
1460
|
-
}, d =
|
|
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(
|
|
1461
|
+
}, d = de([...c], u), f = [];
|
|
1462
|
+
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(R({
|
|
1462
1463
|
kind: "add-foreign-key",
|
|
1463
1464
|
table: e.name,
|
|
1464
1465
|
column: t.name,
|
|
@@ -1530,8 +1531,8 @@ BEGIN
|
|
|
1530
1531
|
});
|
|
1531
1532
|
return {
|
|
1532
1533
|
operations: S,
|
|
1533
|
-
fromFingerprint:
|
|
1534
|
-
toFingerprint:
|
|
1534
|
+
fromFingerprint: P(e.live),
|
|
1535
|
+
toFingerprint: P(t),
|
|
1535
1536
|
summary: On(S)
|
|
1536
1537
|
};
|
|
1537
1538
|
}, An = (e) => {
|
|
@@ -1569,7 +1570,7 @@ BEGIN
|
|
|
1569
1570
|
case "USER-DEFINED": return "text";
|
|
1570
1571
|
default: return "text";
|
|
1571
1572
|
}
|
|
1572
|
-
},
|
|
1573
|
+
}, z = (e) => {
|
|
1573
1574
|
switch (e.toUpperCase()) {
|
|
1574
1575
|
case "CASCADE": return "cascade";
|
|
1575
1576
|
case "RESTRICT": return "restrict";
|
|
@@ -1742,13 +1743,13 @@ BEGIN
|
|
|
1742
1743
|
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 ? {
|
|
1743
1744
|
table: a.target_table,
|
|
1744
1745
|
column: a.target_column,
|
|
1745
|
-
onDelete:
|
|
1746
|
-
onUpdate:
|
|
1746
|
+
onDelete: z(a.delete_rule),
|
|
1747
|
+
onUpdate: z(a.update_rule)
|
|
1747
1748
|
} : void 0, l = m.get(`${e.table_name}.${t.column_name}`);
|
|
1748
1749
|
return {
|
|
1749
1750
|
name: t.column_name,
|
|
1750
1751
|
type: s,
|
|
1751
|
-
...
|
|
1752
|
+
...H(t.data_type, t.character_maximum_length) === void 0 ? {} : { maxLength: H(t.data_type, t.character_maximum_length) },
|
|
1752
1753
|
nullable: t.is_nullable === "YES",
|
|
1753
1754
|
unique: i.has(t.column_name) || n && t.column_name === "id",
|
|
1754
1755
|
hasDefault: t.column_default !== null,
|
|
@@ -1825,21 +1826,21 @@ BEGIN
|
|
|
1825
1826
|
case "json": return "json";
|
|
1826
1827
|
default: return "text";
|
|
1827
1828
|
}
|
|
1828
|
-
},
|
|
1829
|
+
}, B = (e) => e.map((e) => {
|
|
1829
1830
|
let t = e, n = {};
|
|
1830
1831
|
for (let e of Object.keys(t)) n[e.toLowerCase()] = t[e];
|
|
1831
1832
|
return n;
|
|
1832
1833
|
}), zn = (e) => b.gen(function* () {
|
|
1833
|
-
let t =
|
|
1834
|
+
let t = B(yield* e`
|
|
1834
1835
|
SELECT table_name, COALESCE(table_rows, 0) AS table_rows
|
|
1835
1836
|
FROM information_schema.tables
|
|
1836
1837
|
WHERE table_schema = DATABASE() AND table_type = 'BASE TABLE'
|
|
1837
|
-
ORDER BY table_name`), n =
|
|
1838
|
+
ORDER BY table_name`), n = B(yield* e`
|
|
1838
1839
|
SELECT table_name, column_name, data_type, column_type, is_nullable, column_default, column_key,
|
|
1839
1840
|
character_maximum_length
|
|
1840
1841
|
FROM information_schema.columns
|
|
1841
1842
|
WHERE table_schema = DATABASE()
|
|
1842
|
-
ORDER BY table_name, ordinal_position`), r =
|
|
1843
|
+
ORDER BY table_name, ordinal_position`), r = B(yield* e`
|
|
1843
1844
|
SELECT k.table_name AS source_table,
|
|
1844
1845
|
k.column_name AS source_column,
|
|
1845
1846
|
k.referenced_table_name AS target_table,
|
|
@@ -1849,11 +1850,11 @@ BEGIN
|
|
|
1849
1850
|
JOIN information_schema.referential_constraints r
|
|
1850
1851
|
ON k.constraint_name = r.constraint_name
|
|
1851
1852
|
AND k.table_schema = r.constraint_schema
|
|
1852
|
-
WHERE k.table_schema = DATABASE() AND k.referenced_table_name IS NOT NULL`), i =
|
|
1853
|
+
WHERE k.table_schema = DATABASE() AND k.referenced_table_name IS NOT NULL`), i = B(yield* e`
|
|
1853
1854
|
SELECT table_name, index_name, non_unique, column_name, seq_in_index
|
|
1854
1855
|
FROM information_schema.statistics
|
|
1855
1856
|
WHERE table_schema = DATABASE() AND index_name <> 'PRIMARY'
|
|
1856
|
-
ORDER BY table_name, index_name, seq_in_index`), a =
|
|
1857
|
+
ORDER BY table_name, index_name, seq_in_index`), a = B(yield* e`
|
|
1857
1858
|
SELECT table_name, check_clause
|
|
1858
1859
|
FROM information_schema.check_constraints
|
|
1859
1860
|
WHERE constraint_schema = DATABASE()`.unprepared.pipe(b.orElseSucceed(() => []))), o = /* @__PURE__ */ new Set(), s = /* @__PURE__ */ new Map();
|
|
@@ -1889,14 +1890,14 @@ BEGIN
|
|
|
1889
1890
|
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 ? {
|
|
1890
1891
|
table: c.target_table,
|
|
1891
1892
|
column: c.target_column,
|
|
1892
|
-
onDelete:
|
|
1893
|
-
onUpdate:
|
|
1893
|
+
onDelete: z(c.delete_rule),
|
|
1894
|
+
onUpdate: z(c.update_rule)
|
|
1894
1895
|
} : 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}`);
|
|
1895
1896
|
return {
|
|
1896
1897
|
name: t.column_name,
|
|
1897
1898
|
type: l,
|
|
1898
1899
|
nullable: t.is_nullable === "YES",
|
|
1899
|
-
...
|
|
1900
|
+
...H(t.data_type, t.character_maximum_length) === void 0 ? {} : { maxLength: H(t.data_type, t.character_maximum_length) },
|
|
1900
1901
|
unique: d,
|
|
1901
1902
|
hasDefault: f !== null,
|
|
1902
1903
|
...f === null ? {} : { defaultValue: f },
|
|
@@ -1951,8 +1952,8 @@ BEGIN
|
|
|
1951
1952
|
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 ? {
|
|
1952
1953
|
table: r.table,
|
|
1953
1954
|
column: r.to,
|
|
1954
|
-
onDelete:
|
|
1955
|
-
onUpdate:
|
|
1955
|
+
onDelete: z(r.on_delete),
|
|
1956
|
+
onUpdate: z(r.on_update)
|
|
1956
1957
|
} : void 0, o = t.get(e.name);
|
|
1957
1958
|
return {
|
|
1958
1959
|
name: e.name,
|
|
@@ -2009,7 +2010,7 @@ BEGIN
|
|
|
2009
2010
|
case "image": return "bytes";
|
|
2010
2011
|
default: return "text";
|
|
2011
2012
|
}
|
|
2012
|
-
},
|
|
2013
|
+
}, V = (e) => e === !0 || e === 1, Un = (e) => b.gen(function* () {
|
|
2013
2014
|
let t = yield* e`
|
|
2014
2015
|
SELECT TABLE_NAME AS table_name FROM INFORMATION_SCHEMA.TABLES
|
|
2015
2016
|
WHERE TABLE_TYPE = 'BASE TABLE' ORDER BY TABLE_NAME`, n = yield* e`
|
|
@@ -2066,8 +2067,8 @@ BEGIN
|
|
|
2066
2067
|
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();
|
|
2067
2068
|
if (a) for (let [, e] of a) {
|
|
2068
2069
|
let t = [...e].sort((e, t) => e.key_ordinal - t.key_ordinal);
|
|
2069
|
-
if (
|
|
2070
|
-
else
|
|
2070
|
+
if (V(t[0].is_primary)) for (let e of t) d.push(e.column_name);
|
|
2071
|
+
else V(t[0].is_unique) && t.length === 1 && f.add(t[0].column_name);
|
|
2071
2072
|
}
|
|
2072
2073
|
let p = t.map((t) => {
|
|
2073
2074
|
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 ? {
|
|
@@ -2080,7 +2081,7 @@ BEGIN
|
|
|
2080
2081
|
name: t.column_name,
|
|
2081
2082
|
type: a,
|
|
2082
2083
|
nullable: t.is_nullable === "YES",
|
|
2083
|
-
...
|
|
2084
|
+
...H(t.data_type, t.character_maximum_length) === void 0 ? {} : { maxLength: H(t.data_type, t.character_maximum_length) },
|
|
2084
2085
|
unique: f.has(t.column_name) || n && t.column_name === "id",
|
|
2085
2086
|
hasDefault: c !== null,
|
|
2086
2087
|
...c === null ? {} : { defaultValue: c },
|
|
@@ -2090,11 +2091,11 @@ BEGIN
|
|
|
2090
2091
|
}), m = [];
|
|
2091
2092
|
if (a) for (let [t, n] of a) {
|
|
2092
2093
|
let r = [...n].sort((e, t) => e.key_ordinal - t.key_ordinal);
|
|
2093
|
-
|
|
2094
|
+
V(r[0].is_primary) || r.length === 1 && V(r[0].is_unique) || r.length === 1 && !V(r[0].is_unique) && i.has(r[0].column_name) && t !== `${e}_${r[0].column_name}_idx` || m.push({
|
|
2094
2095
|
name: t,
|
|
2095
2096
|
table: e,
|
|
2096
2097
|
columns: r.map((e) => e.column_name),
|
|
2097
|
-
unique:
|
|
2098
|
+
unique: V(r[0].is_unique)
|
|
2098
2099
|
});
|
|
2099
2100
|
}
|
|
2100
2101
|
d.length > 0 && m.push({
|
|
@@ -2127,11 +2128,11 @@ BEGIN
|
|
|
2127
2128
|
"char",
|
|
2128
2129
|
"character",
|
|
2129
2130
|
"nchar"
|
|
2130
|
-
]),
|
|
2131
|
+
]), H = (e, t) => {
|
|
2131
2132
|
if (!Kn.has(e.toLowerCase())) return;
|
|
2132
2133
|
let n = Number(t);
|
|
2133
2134
|
if (!(!Number.isFinite(n) || n <= 0)) return n;
|
|
2134
|
-
}, qn = /* @__PURE__ */ new Set([
|
|
2135
|
+
}, qn = /* @__PURE__ */ new Set([O]), Jn = (e) => e.onDialectOrElse({
|
|
2135
2136
|
mysql: () => zn(e),
|
|
2136
2137
|
mssql: () => Un(e),
|
|
2137
2138
|
sqlite: () => Vn(e),
|
|
@@ -2139,14 +2140,14 @@ BEGIN
|
|
|
2139
2140
|
}).pipe(b.map((e) => ({
|
|
2140
2141
|
...e,
|
|
2141
2142
|
tables: e.tables.filter((e) => !qn.has(e.name))
|
|
2142
|
-
}))),
|
|
2143
|
+
}))), U = (e, t) => e.onDialectOrElse({
|
|
2143
2144
|
mysql: () => b.succeed(t.replace("T", " ").replace("Z", "")),
|
|
2144
2145
|
orElse: () => b.succeed(t)
|
|
2145
2146
|
}), Yn = (e, t) => `${t}(${e.precision ?? 38}, ${e.scale ?? 0})`, Xn = (e, t) => {
|
|
2146
2147
|
if (!(e.type === "text" && e.maxLength !== void 0) && !e.spatial && e.type !== "vector" && e.type !== "array" && e.type !== "enum") return;
|
|
2147
2148
|
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;
|
|
2148
2149
|
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.`);
|
|
2149
|
-
return
|
|
2150
|
+
return j(e, t);
|
|
2150
2151
|
}, Zn = (e) => {
|
|
2151
2152
|
let t = Xn(e, "postgres");
|
|
2152
2153
|
return t === void 0 ? Qn(e) : t;
|
|
@@ -2192,57 +2193,57 @@ BEGIN
|
|
|
2192
2193
|
case "interval": return "BIGINT";
|
|
2193
2194
|
case "raw": return e.rawDdl ?? "LONGTEXT";
|
|
2194
2195
|
}
|
|
2195
|
-
},
|
|
2196
|
+
}, W = (e) => `"${e}"`, G = (e) => `\`${e.replace(/`/g, "``")}\``, er = (e, t) => `ALTER TABLE ${t(e.table)} DROP COLUMN IF EXISTS ${t(e.column)};`, tr = (e) => `ALTER TABLE ${G(e.table)} DROP COLUMN IF EXISTS ${G(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}${kr(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) => {
|
|
2196
2197
|
let n = t(e.defaultValue);
|
|
2197
2198
|
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.`);
|
|
2198
2199
|
return n;
|
|
2199
|
-
},
|
|
2200
|
-
let r =
|
|
2200
|
+
}, K = (e, t, n) => e.find((e) => e.tableName === t)?.fields[n], or = (e, t, n) => {
|
|
2201
|
+
let r = K(e, t, n);
|
|
2201
2202
|
return r ? Ut(n, r) : void 0;
|
|
2202
2203
|
}, sr = (e, t, n) => {
|
|
2203
2204
|
let r = n ?? e.nullable, i = Ze(e, "mariadb");
|
|
2204
2205
|
return [
|
|
2205
|
-
|
|
2206
|
-
|
|
2206
|
+
G(t),
|
|
2207
|
+
j(e, "mariadb"),
|
|
2207
2208
|
r ? null : "NOT NULL",
|
|
2208
2209
|
i
|
|
2209
2210
|
].filter(Boolean).join(" ");
|
|
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" ?
|
|
2211
|
+
}, 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" ? M(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" ? M(e, "postgres") : null, q = (e, t) => {
|
|
2211
2212
|
if (!e.generatedAs) return null;
|
|
2212
2213
|
let { expr: n, stored: r } = e.generatedAs;
|
|
2213
2214
|
return t === "postgres" ? `GENERATED ALWAYS AS (${n}) STORED` : t === "mssql" ? `AS (${n})${r ? " PERSISTED" : ""}` : `GENERATED ALWAYS AS (${n}) ${r ? "STORED" : "VIRTUAL"}`;
|
|
2214
2215
|
}, dr = (e) => {
|
|
2215
|
-
if (e.type === "id" && e.idScheme?.kind === "numeric") return
|
|
2216
|
-
let t =
|
|
2217
|
-
if (t) return `${
|
|
2218
|
-
let n = [
|
|
2216
|
+
if (e.type === "id" && e.idScheme?.kind === "numeric") return N(e.name, "postgres");
|
|
2217
|
+
let t = q(e, "postgres");
|
|
2218
|
+
if (t) return `${W(e.name)} ${Zn(e)} ${t}`;
|
|
2219
|
+
let n = [W(e.name), Zn(e)];
|
|
2219
2220
|
if (e.type === "id" && n.push("PRIMARY KEY"), e.nullable || n.push("NOT NULL"), e.unique && e.type !== "id" && n.push("UNIQUE"), e.hasDefault) {
|
|
2220
2221
|
let t = ur(e.defaultValue, e);
|
|
2221
2222
|
t && n.push(t);
|
|
2222
2223
|
}
|
|
2223
|
-
return e.references && (n.push(`REFERENCES ${
|
|
2224
|
+
return e.references && (n.push(`REFERENCES ${W(e.references.table)} (${W(e.references.column)})`), e.references.onDelete && n.push(`ON DELETE ${{
|
|
2224
2225
|
cascade: "CASCADE",
|
|
2225
2226
|
restrict: "RESTRICT",
|
|
2226
2227
|
setNull: "SET NULL",
|
|
2227
2228
|
noAction: "NO ACTION"
|
|
2228
2229
|
}[e.references.onDelete]}`)), n.join(" ");
|
|
2229
2230
|
}, 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 ${
|
|
2231
|
-
for (let t of e.indexes) t.name !== `${e.table}_pkey` && n.push(`CREATE ${t.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${
|
|
2231
|
+
let t = e.columns.map((e) => ` ${dr(e)}`).join(",\n"), n = [`CREATE TABLE IF NOT EXISTS ${W(e.table)} (\n${t}${fr(e, W)}\n)`];
|
|
2232
|
+
for (let t of e.indexes) t.name !== `${e.table}_pkey` && n.push(`CREATE ${t.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${W(t.name)} ON ${W(e.table)} (${t.columns.map(Pr).join(", ")})`);
|
|
2232
2233
|
return n;
|
|
2233
2234
|
}, mr = 191, hr = (e) => {
|
|
2234
2235
|
let t = $n(e).toUpperCase();
|
|
2235
2236
|
return t.endsWith("TEXT") || t.endsWith("BLOB");
|
|
2236
2237
|
}, gr = (e) => {
|
|
2237
|
-
if (e.type === "id" && e.idScheme?.kind === "numeric") return
|
|
2238
|
-
let t =
|
|
2239
|
-
if (t) return `${
|
|
2240
|
-
let n = [
|
|
2238
|
+
if (e.type === "id" && e.idScheme?.kind === "numeric") return N(e.name, "mariadb");
|
|
2239
|
+
let t = q(e, "mariadb");
|
|
2240
|
+
if (t) return `${G(e.name)} ${$n(e)} ${t}`;
|
|
2241
|
+
let n = [G(e.name), $n(e)];
|
|
2241
2242
|
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
2243
|
let t = cr(e.defaultValue, e);
|
|
2243
2244
|
t && n.push(t);
|
|
2244
2245
|
}
|
|
2245
|
-
return e.references && (n.push(`REFERENCES ${
|
|
2246
|
+
return e.references && (n.push(`REFERENCES ${G(e.references.table)} (${G(e.references.column)})`), e.references.onDelete && n.push(`ON DELETE ${{
|
|
2246
2247
|
cascade: "CASCADE",
|
|
2247
2248
|
restrict: "RESTRICT",
|
|
2248
2249
|
setNull: "SET NULL",
|
|
@@ -2250,11 +2251,11 @@ BEGIN
|
|
|
2250
2251
|
}[e.references.onDelete]}`)), n.join(" ");
|
|
2251
2252
|
}, _r = (e) => {
|
|
2252
2253
|
let t = new Map(e.columns.map((e) => [e.name, e])), n = (e) => {
|
|
2253
|
-
let n =
|
|
2254
|
-
return n === void 0 ?
|
|
2255
|
-
}, r = e.columns.map((e) => ` ${gr(e)}`).join(",\n"), i = [`CREATE TABLE IF NOT EXISTS ${
|
|
2256
|
-
for (let t of e.columns) t.unique && t.type !== "id" && hr(t) && i.push(`CREATE UNIQUE INDEX IF NOT EXISTS ${
|
|
2257
|
-
for (let t of e.indexes) t.name !== `${e.table}_pkey` && i.push(`CREATE ${t.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${
|
|
2254
|
+
let n = Mr(t.get(e));
|
|
2255
|
+
return n === void 0 ? G(e) : `${G(e)}(${n})`;
|
|
2256
|
+
}, r = e.columns.map((e) => ` ${gr(e)}`).join(",\n"), i = [`CREATE TABLE IF NOT EXISTS ${G(e.table)} (\n${r}${fr(e, n)}\n)`];
|
|
2257
|
+
for (let t of e.columns) t.unique && t.type !== "id" && hr(t) && i.push(`CREATE UNIQUE INDEX IF NOT EXISTS ${G(`${e.table}_${t.name}_key`)} ON ${G(e.table)} (${n(t.name)})`);
|
|
2258
|
+
for (let t of e.indexes) t.name !== `${e.table}_pkey` && i.push(`CREATE ${t.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${G(t.name)} ON ${G(e.table)} (${t.columns.map(n).join(", ")})`);
|
|
2258
2259
|
return i;
|
|
2259
2260
|
}, vr = {
|
|
2260
2261
|
cascade: "CASCADE",
|
|
@@ -2278,52 +2279,52 @@ BEGIN
|
|
|
2278
2279
|
case "bytes": return "BLOB";
|
|
2279
2280
|
default: return "TEXT";
|
|
2280
2281
|
}
|
|
2281
|
-
},
|
|
2282
|
-
if (e.type === "id" && e.idScheme?.kind === "numeric") return
|
|
2283
|
-
let t =
|
|
2284
|
-
if (t) return `${
|
|
2285
|
-
let n = [
|
|
2282
|
+
}, xr = (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" ? M(e, "sqlite") : null, Sr = (e) => {
|
|
2283
|
+
if (e.type === "id" && e.idScheme?.kind === "numeric") return N(e.name, "sqlite");
|
|
2284
|
+
let t = q(e, "sqlite");
|
|
2285
|
+
if (t) return `${W(e.name)} ${br(e)} ${t}`;
|
|
2286
|
+
let n = [W(e.name), br(e)];
|
|
2286
2287
|
if (e.type === "id" && n.push("PRIMARY KEY"), e.nullable || n.push("NOT NULL"), e.unique && e.type !== "id" && n.push("UNIQUE"), e.hasDefault) {
|
|
2287
|
-
let t =
|
|
2288
|
+
let t = xr(e.defaultValue, "CURRENT_TIMESTAMP");
|
|
2288
2289
|
t && n.push(t);
|
|
2289
2290
|
}
|
|
2290
|
-
if (e.references && (n.push(`REFERENCES ${
|
|
2291
|
+
if (e.references && (n.push(`REFERENCES ${W(e.references.table)} (${W(e.references.column)})`), e.references.onDelete && n.push(`ON DELETE ${vr[e.references.onDelete]}`)), e.oneOf && e.oneOf.length > 0) {
|
|
2291
2292
|
let t = e.oneOf.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ");
|
|
2292
|
-
n.push(`CHECK (${
|
|
2293
|
+
n.push(`CHECK (${W(e.name)} IN (${t}))`);
|
|
2293
2294
|
}
|
|
2294
2295
|
return n.join(" ");
|
|
2295
|
-
},
|
|
2296
|
-
let t = e.columns.map((e) => ` ${
|
|
2297
|
-
for (let t of e.indexes) t.name !== `${e.table}_pkey` && n.push(`CREATE ${t.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${
|
|
2296
|
+
}, Cr = (e) => {
|
|
2297
|
+
let t = e.columns.map((e) => ` ${Sr(e)}`).join(",\n"), n = [`CREATE TABLE IF NOT EXISTS ${W(e.table)} (\n${t}${fr(e, W)}\n)`];
|
|
2298
|
+
for (let t of e.indexes) t.name !== `${e.table}_pkey` && n.push(`CREATE ${t.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${W(t.name)} ON ${W(e.table)} (${t.columns.map(W).join(", ")})`);
|
|
2298
2299
|
return n;
|
|
2299
2300
|
}, J = (e, t) => {
|
|
2300
2301
|
let n = t.find((t) => t.tableName === e);
|
|
2301
2302
|
if (!n) return [];
|
|
2302
|
-
let r =
|
|
2303
|
-
...
|
|
2303
|
+
let r = I([n]).tables[0], i = `${e}__voltro_rebuild`, a = r.columns.map((e) => W(e.name)).join(", "), o = [
|
|
2304
|
+
...Cr({
|
|
2304
2305
|
kind: "create-table",
|
|
2305
2306
|
table: i,
|
|
2306
2307
|
columns: r.columns,
|
|
2307
2308
|
indexes: [],
|
|
2308
2309
|
...r.primaryKey ? { primaryKey: r.primaryKey } : {}
|
|
2309
2310
|
}),
|
|
2310
|
-
`INSERT INTO ${
|
|
2311
|
-
`DROP TABLE ${
|
|
2312
|
-
`ALTER TABLE ${
|
|
2311
|
+
`INSERT INTO ${W(i)} (${a}) SELECT ${a} FROM ${W(e)}`,
|
|
2312
|
+
`DROP TABLE ${W(e)}`,
|
|
2313
|
+
`ALTER TABLE ${W(i)} RENAME TO ${W(e)}`
|
|
2313
2314
|
];
|
|
2314
|
-
for (let t of r.indexes) t.name !== `${e}_pkey` && o.push(`CREATE ${t.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${
|
|
2315
|
+
for (let t of r.indexes) t.name !== `${e}_pkey` && o.push(`CREATE ${t.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${W(t.name)} ON ${W(e)} (${t.columns.map(W).join(", ")})`);
|
|
2315
2316
|
return o;
|
|
2316
|
-
},
|
|
2317
|
+
}, wr = (e, t, n, r) => {
|
|
2317
2318
|
let i = r ?? e.nullable;
|
|
2318
|
-
return `ALTER TABLE ${Y(n)} ALTER COLUMN ${Y(t)} ${
|
|
2319
|
-
},
|
|
2319
|
+
return `ALTER TABLE ${Y(n)} ALTER COLUMN ${Y(t)} ${j(e, "mssql")} ${i ? "NULL" : "NOT NULL"};`;
|
|
2320
|
+
}, Tr = (e) => {
|
|
2320
2321
|
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 + ']');`;
|
|
2321
2322
|
if (!e.hasDefault) return t;
|
|
2322
|
-
let n =
|
|
2323
|
+
let n = xr(e.defaultValue, "SYSUTCDATETIME()");
|
|
2323
2324
|
if (!n) return t;
|
|
2324
2325
|
let r = n.replace(/^DEFAULT\s+/i, "");
|
|
2325
2326
|
return `${t} ALTER TABLE ${Y(e.table)} ADD DEFAULT ${r} FOR ${Y(e.column)};`;
|
|
2326
|
-
}, Y = (e) => `[${e.replace(/]/g, "]]")}]`,
|
|
2327
|
+
}, Y = (e) => `[${e.replace(/]/g, "]]")}]`, Er = (e) => {
|
|
2327
2328
|
let t = Xn(e, "mssql");
|
|
2328
2329
|
if (t !== void 0) return t;
|
|
2329
2330
|
switch (e.type) {
|
|
@@ -2339,44 +2340,44 @@ BEGIN
|
|
|
2339
2340
|
case "bytes": return "VARBINARY(MAX)";
|
|
2340
2341
|
default: return "NVARCHAR(MAX)";
|
|
2341
2342
|
}
|
|
2342
|
-
},
|
|
2343
|
-
if (e.type === "id" && e.idScheme?.kind === "numeric") return
|
|
2344
|
-
let n = e.type === "text" && t?.has(e.name) ? "NVARCHAR(450)" :
|
|
2343
|
+
}, Dr = (e, t) => {
|
|
2344
|
+
if (e.type === "id" && e.idScheme?.kind === "numeric") return N(e.name, "mssql");
|
|
2345
|
+
let n = e.type === "text" && t?.has(e.name) ? "NVARCHAR(450)" : Er(e), r = q(e, "mssql");
|
|
2345
2346
|
if (r) return `${Y(e.name)} ${r}`;
|
|
2346
2347
|
let i = [Y(e.name), n];
|
|
2347
2348
|
if (e.type === "id" && i.push("PRIMARY KEY"), e.nullable || i.push("NOT NULL"), e.unique && e.type !== "id" && i.push("UNIQUE"), e.hasDefault) {
|
|
2348
|
-
let t =
|
|
2349
|
+
let t = xr(e.defaultValue, "SYSUTCDATETIME()");
|
|
2349
2350
|
t && i.push(t);
|
|
2350
2351
|
}
|
|
2351
2352
|
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
|
-
},
|
|
2353
|
-
let t = (e) => e.replace(/'/g, "''"), n = new Set(e.indexes.flatMap((e) => e.columns)), r = e.columns.map((e) => ` ${
|
|
2353
|
+
}, Or = (e) => {
|
|
2354
|
+
let t = (e) => e.replace(/'/g, "''"), n = new Set(e.indexes.flatMap((e) => e.columns)), r = e.columns.map((e) => ` ${Dr(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)}')`];
|
|
2354
2355
|
for (let n of e.indexes) {
|
|
2355
2356
|
if (n.name === `${e.table}_pkey`) continue;
|
|
2356
2357
|
let r = `CREATE ${n.unique ? "UNIQUE " : ""}INDEX ${Y(n.name)} ON ${Y(e.table)} (${n.columns.map(Y).join(", ")})`;
|
|
2357
2358
|
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)}')`);
|
|
2358
2359
|
}
|
|
2359
2360
|
return a;
|
|
2360
|
-
},
|
|
2361
|
-
let r = `${e.table}${
|
|
2361
|
+
}, kr = (e) => `__dropped_${e.replace(/[-:T.Z]/g, "").slice(0, 14)}`, Ar = (e, t) => `DROP TABLE IF EXISTS ${t(e.table)};`, jr = (e, t, n) => {
|
|
2362
|
+
let r = `${e.table}${kr(t)}`;
|
|
2362
2363
|
return `ALTER TABLE ${n(e.table)} RENAME TO ${n(r)};`;
|
|
2363
|
-
},
|
|
2364
|
+
}, Mr = (e) => {
|
|
2364
2365
|
if (e === void 0 || !hr(e)) return;
|
|
2365
2366
|
let t = typeof e.maxLength == "number" && e.maxLength > 0 ? e.maxLength : void 0;
|
|
2366
2367
|
return t === void 0 ? mr : Math.min(mr, t);
|
|
2367
|
-
},
|
|
2368
|
-
let r =
|
|
2369
|
-
return r === void 0 ?
|
|
2370
|
-
},
|
|
2368
|
+
}, Nr = (e, t, n) => {
|
|
2369
|
+
let r = Mr(or(e, t, n));
|
|
2370
|
+
return r === void 0 ? G(n) : `${G(n)}(${r})`;
|
|
2371
|
+
}, Pr = (e) => e.startsWith("(") ? e : W(e), Fr = (e, t) => `CREATE ${e.index.unique ? "UNIQUE " : ""}INDEX ${t ? "CONCURRENTLY " : ""}IF NOT EXISTS ${W(e.index.name)} ON ${W(e.table)} (${e.index.columns.map(Pr).join(", ")});`, Ir = (e, t) => `CREATE ${e.index.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${G(e.index.name)} ON ${G(e.table)} (${e.index.columns.map((n) => Nr(t, e.table, n)).join(", ")});`, Lr = (e) => `DROP INDEX IF EXISTS ${W(e.index)};`, Rr = (e) => `DROP INDEX ${G(e.index)} ON ${G(e.table)};`, zr = (e) => `DROP INDEX IF EXISTS ${Y(e.index)} ON ${Y(e.table)};`, Br = (e, t) => `ALTER TABLE ${t(e.from)} RENAME TO ${t(e.to)};`, Vr = (e) => {
|
|
2371
2372
|
let t = (e) => e.replace(/'/g, "''");
|
|
2372
2373
|
return `EXEC sp_rename N'${t(e.from)}', N'${t(e.to)}';`;
|
|
2373
|
-
},
|
|
2374
|
+
}, Hr = (e) => `ALTER INDEX ${W(e.from)} RENAME TO ${W(e.to)};`, Ur = (e) => `ALTER TABLE ${G(e.table)} RENAME INDEX ${G(e.from)} TO ${G(e.to)};`, Wr = (e) => {
|
|
2374
2375
|
let t = (e) => e.replace(/'/g, "''");
|
|
2375
2376
|
return `EXEC sp_rename N'${t(e.table)}.${t(e.from)}', N'${t(e.to)}', N'INDEX';`;
|
|
2376
|
-
},
|
|
2377
|
+
}, Gr = (e) => {
|
|
2377
2378
|
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(", ")})`;
|
|
2378
2379
|
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)}');`;
|
|
2379
|
-
},
|
|
2380
|
+
}, Kr = (e, t, n) => {
|
|
2380
2381
|
let r = n.find((t) => t.tableName === e);
|
|
2381
2382
|
if (!r) return [];
|
|
2382
2383
|
let i = [];
|
|
@@ -2385,13 +2386,13 @@ BEGIN
|
|
|
2385
2386
|
t && t.type === "text" && !t.generatedAs && i.push(`ALTER TABLE ${Y(e)} ALTER COLUMN ${Y(n)} NVARCHAR(450) ${t.nullable ? "NULL" : "NOT NULL"};`);
|
|
2386
2387
|
}
|
|
2387
2388
|
return i;
|
|
2388
|
-
},
|
|
2389
|
-
let t = e.fields.map((e) =>
|
|
2390
|
-
return `ALTER TABLE ${
|
|
2391
|
-
},
|
|
2392
|
-
let n = e.fields.map((n) =>
|
|
2393
|
-
return `ALTER TABLE ${
|
|
2394
|
-
},
|
|
2389
|
+
}, qr = (e) => `ALTER TABLE ${W(e.table)} ADD CONSTRAINT ${W(`${e.table}_${e.column}_key`)} UNIQUE (${W(e.column)});`, Jr = (e, t) => `ALTER TABLE ${G(e.table)} ADD CONSTRAINT ${G(`${e.table}_${e.column}_key`)} UNIQUE (${Nr(t, e.table, e.column)});`, Yr = (e) => {
|
|
2390
|
+
let t = e.fields.map((e) => W(e)).join(", ");
|
|
2391
|
+
return `ALTER TABLE ${W(e.table)} ADD CONSTRAINT ${W(e.name)} UNIQUE (${t});`;
|
|
2392
|
+
}, Xr = (e, t) => {
|
|
2393
|
+
let n = e.fields.map((n) => Nr(t, e.table, n)).join(", ");
|
|
2394
|
+
return `ALTER TABLE ${G(e.table)} ADD CONSTRAINT ${G(e.name)} UNIQUE (${n});`;
|
|
2395
|
+
}, Zr = (e) => `ALTER TABLE ${W(e.table)} DROP CONSTRAINT IF EXISTS ${W(e.name)};`, Qr = (e) => `ALTER TABLE ${G(e.table)} DROP INDEX ${G(e.name)};`, $r = (e) => `ALTER TABLE ${W(e.table)} DROP CONSTRAINT IF EXISTS ${W(`${e.table}_${e.column}_key`)};`, ei = (e) => `ALTER TABLE ${G(e.table)} DROP INDEX ${G(`${e.table}_${e.column}_key`)};`, ti = (e, t) => {
|
|
2395
2396
|
let n = [], r = t(e.table), i = t(e.column), a = t(e.targetTable), o = t(e.targetColumn);
|
|
2396
2397
|
if (e.orphanPolicy) {
|
|
2397
2398
|
let t = `${i} IS NOT NULL AND ${i} NOT IN (SELECT ${o} FROM ${a})`;
|
|
@@ -2409,11 +2410,11 @@ BEGIN
|
|
|
2409
2410
|
setNull: "SET NULL",
|
|
2410
2411
|
noAction: "NO ACTION"
|
|
2411
2412
|
}[e.onDelete]}`), n.push(s.join(" ") + ";"), n;
|
|
2412
|
-
},
|
|
2413
|
+
}, ni = (e) => `ALTER TABLE ${W(e.table)} DROP CONSTRAINT IF EXISTS ${W(`${e.table}_${e.column}_fkey`)};`, ri = (e) => `ALTER TABLE ${G(e.table)} DROP FOREIGN KEY ${G(`${e.table}_${e.column}_fkey`)};`, ii = (e, t) => {
|
|
2413
2414
|
let n = e.values.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ");
|
|
2414
2415
|
return `ALTER TABLE ${t(e.table)} ADD CONSTRAINT ${t(`${e.table}_${e.column}_check`)} CHECK (${t(e.column)} IN (${n}));`;
|
|
2415
|
-
},
|
|
2416
|
-
let i = e.column, a = r === "mariadb", o = a ?
|
|
2416
|
+
}, ai = (e) => `ALTER TABLE ${W(e.table)} DROP CONSTRAINT IF EXISTS ${W(`${e.table}_${e.column}_check`)};`, oi = (e) => `ALTER TABLE ${G(e.table)} DROP CHECK ${G(`${e.table}_${e.column}_check`)};`, si = (e, t, n, r) => {
|
|
2417
|
+
let i = e.column, a = r === "mariadb", o = a ? G : r === "mssql" ? Y : W, s = a ? $n(i) : r === "mssql" ? Er(i) : r === "sqlite" ? br(i) : Zn(i), c = r === "mssql" ? "ADD" : "ADD COLUMN", l = (e) => a ? cr(e, i) : r === "mssql" ? xr(e, "SYSUTCDATETIME()") : r === "sqlite" ? xr(e, "CURRENT_TIMESTAMP") : ur(e, i), u = q(i, r);
|
|
2417
2418
|
if (u) {
|
|
2418
2419
|
let t = r === "mssql" ? `${o(i.name)} ${u}` : `${o(i.name)} ${s} ${u}`;
|
|
2419
2420
|
return {
|
|
@@ -2441,7 +2442,7 @@ BEGIN
|
|
|
2441
2442
|
post: f ? [f] : []
|
|
2442
2443
|
};
|
|
2443
2444
|
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;
|
|
2444
|
-
},
|
|
2445
|
+
}, ci = (e) => {
|
|
2445
2446
|
let t = new Map(e.map((e) => [e.tableName, e]));
|
|
2446
2447
|
return (e, n) => {
|
|
2447
2448
|
let r = t.get(e);
|
|
@@ -2466,29 +2467,29 @@ BEGIN
|
|
|
2466
2467
|
})), b.mapError((e) => {
|
|
2467
2468
|
let r = ie(`applier: statement failed (op=${n})`, t, e, 400).trim();
|
|
2468
2469
|
return Object.assign(e, { message: r });
|
|
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))),
|
|
2470
|
-
let n =
|
|
2470
|
+
}), 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))), li = (e, t) => b.gen(function* () {
|
|
2471
|
+
let n = I([Fe], t).tables[0];
|
|
2471
2472
|
if (!n) return yield* b.die(/* @__PURE__ */ Error("migration applier: the resume-ledger table descriptor produced no snapshot. This is a framework bug."));
|
|
2472
2473
|
let r = {
|
|
2473
2474
|
kind: "create-table",
|
|
2474
|
-
table:
|
|
2475
|
+
table: O,
|
|
2475
2476
|
columns: n.columns,
|
|
2476
2477
|
indexes: []
|
|
2477
2478
|
}, i = yield* e.onDialectOrElse({
|
|
2478
2479
|
mysql: () => b.succeed(_r(r)),
|
|
2479
|
-
mssql: () => b.succeed(
|
|
2480
|
-
sqlite: () => b.succeed(
|
|
2480
|
+
mssql: () => b.succeed(Or(r)),
|
|
2481
|
+
sqlite: () => b.succeed(Cr(r)),
|
|
2481
2482
|
orElse: () => b.succeed(pr(r))
|
|
2482
2483
|
});
|
|
2483
2484
|
yield* b.forEach(i, (t) => X(e, t, "resume-ledger-bootstrap"), { discard: !0 });
|
|
2484
|
-
}),
|
|
2485
|
+
}), ui = (e) => e`
|
|
2485
2486
|
SELECT ${e("runId")}, ${e("planFingerprint")}, ${e("opIndex")},
|
|
2486
2487
|
${e("status")}, ${e("payload")}
|
|
2487
|
-
FROM ${e(
|
|
2488
|
-
`,
|
|
2489
|
-
let i = yield*
|
|
2488
|
+
FROM ${e(O)}
|
|
2489
|
+
`, di = (e, t, n, r) => b.gen(function* () {
|
|
2490
|
+
let i = yield* U(e, (/* @__PURE__ */ new Date()).toISOString());
|
|
2490
2491
|
yield* b.forEach(r, ({ index: r, planned: a }) => e`
|
|
2491
|
-
INSERT INTO ${e(
|
|
2492
|
+
INSERT INTO ${e(O)}
|
|
2492
2493
|
(${e("id")}, ${e("runId")}, ${e("planFingerprint")}, ${e("opIndex")},
|
|
2493
2494
|
${e("opKind")}, ${e("opTarget")}, ${e("status")}, ${e("payload")},
|
|
2494
2495
|
${e("createdAt")})
|
|
@@ -2497,11 +2498,11 @@ BEGIN
|
|
|
2497
2498
|
${a.op.kind}, ${Le(a.op)}, ${"pending"},
|
|
2498
2499
|
${JSON.stringify(a)}, ${i})
|
|
2499
2500
|
`, { discard: !0 });
|
|
2500
|
-
}),
|
|
2501
|
-
let a = yield*
|
|
2501
|
+
}), fi = (e, t, n, r, i) => b.gen(function* () {
|
|
2502
|
+
let a = yield* U(e, (/* @__PURE__ */ new Date()).toISOString());
|
|
2502
2503
|
if (r === "applied") {
|
|
2503
2504
|
yield* e`
|
|
2504
|
-
UPDATE ${e(
|
|
2505
|
+
UPDATE ${e(O)}
|
|
2505
2506
|
SET ${e("status")} = ${r}, ${e("appliedAt")} = ${a},
|
|
2506
2507
|
${e("durationMs")} = ${i ?? null}
|
|
2507
2508
|
WHERE ${e("runId")} = ${t} AND ${e("opIndex")} = ${n}
|
|
@@ -2509,22 +2510,22 @@ BEGIN
|
|
|
2509
2510
|
return;
|
|
2510
2511
|
}
|
|
2511
2512
|
yield* e`
|
|
2512
|
-
UPDATE ${e(
|
|
2513
|
+
UPDATE ${e(O)}
|
|
2513
2514
|
SET ${e("status")} = ${r}, ${e("startedAt")} = ${a}
|
|
2514
2515
|
WHERE ${e("runId")} = ${t} AND ${e("opIndex")} = ${n}
|
|
2515
2516
|
`;
|
|
2516
|
-
}),
|
|
2517
|
-
let i = yield*
|
|
2517
|
+
}), pi = (e, t, n, r) => b.gen(function* () {
|
|
2518
|
+
let i = yield* U(e, (/* @__PURE__ */ new Date()).toISOString());
|
|
2518
2519
|
r.length > 0 && (yield* e`
|
|
2519
|
-
UPDATE ${e(
|
|
2520
|
+
UPDATE ${e(O)}
|
|
2520
2521
|
SET ${e("status")} = ${"applied"}, ${e("appliedAt")} = ${i}
|
|
2521
2522
|
WHERE ${e("runId")} = ${t} AND ${e("opIndex")} IN ${e.in(r)}
|
|
2522
2523
|
`), n.length > 0 && (yield* e`
|
|
2523
|
-
UPDATE ${e(
|
|
2524
|
+
UPDATE ${e(O)}
|
|
2524
2525
|
SET ${e("status")} = ${"pending"}
|
|
2525
2526
|
WHERE ${e("runId")} = ${t} AND ${e("opIndex")} IN ${e.in(n)}
|
|
2526
2527
|
`);
|
|
2527
|
-
}),
|
|
2528
|
+
}), mi = (e, t) => t === void 0 ? e`DELETE FROM ${e(O)}`.pipe(b.asVoid) : e`DELETE FROM ${e(O)} WHERE ${e("runId")} = ${t}`.pipe(b.asVoid), hi = (e, t, n) => b.gen(function* () {
|
|
2528
2529
|
if (Ge(n.op, n.classification, t) !== "artefact-repair") return "clean";
|
|
2529
2530
|
let r = n.op, a = (e) => i(e, t);
|
|
2530
2531
|
if (t === "sqlite" || t === "turso") {
|
|
@@ -2535,109 +2536,109 @@ BEGIN
|
|
|
2535
2536
|
if (r.kind !== "alter-column-type") return "clean";
|
|
2536
2537
|
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
2538
|
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
|
-
}),
|
|
2539
|
-
let i = process.hrtime.bigint(), a =
|
|
2539
|
+
}), gi = (e, t, n, r = "fresh") => b.gen(function* () {
|
|
2540
|
+
let i = process.hrtime.bigint(), a = ci(n.declared), o = t.op;
|
|
2540
2541
|
switch (o.kind) {
|
|
2541
2542
|
case "create-table": {
|
|
2542
2543
|
let t = yield* e.onDialectOrElse({
|
|
2543
2544
|
mysql: () => b.succeed(_r(o)),
|
|
2544
|
-
mssql: () => b.succeed(
|
|
2545
|
-
sqlite: () => b.succeed(
|
|
2545
|
+
mssql: () => b.succeed(Or(o)),
|
|
2546
|
+
sqlite: () => b.succeed(Cr(o)),
|
|
2546
2547
|
orElse: () => b.succeed(pr(o))
|
|
2547
2548
|
});
|
|
2548
2549
|
for (let n of t) yield* X(e, n, o.kind);
|
|
2549
2550
|
break;
|
|
2550
2551
|
}
|
|
2551
2552
|
case "drop-table": {
|
|
2552
|
-
let t = process.env.VOLTRO_SOFT_DROP === "1", n = (/* @__PURE__ */ new Date()).toISOString(), r = (e) => t ?
|
|
2553
|
+
let t = process.env.VOLTRO_SOFT_DROP === "1", n = (/* @__PURE__ */ new Date()).toISOString(), r = (e) => t ? jr(o, n, e) : Ar(o, e);
|
|
2553
2554
|
yield* e.onDialectOrElse({
|
|
2554
|
-
mysql: () => X(e, r(
|
|
2555
|
-
orElse: () => X(e, r(
|
|
2555
|
+
mysql: () => X(e, r(G), o.kind),
|
|
2556
|
+
orElse: () => X(e, r(W), o.kind)
|
|
2556
2557
|
});
|
|
2557
2558
|
break;
|
|
2558
2559
|
}
|
|
2559
2560
|
case "drop-column": {
|
|
2560
2561
|
let t = process.env.VOLTRO_SOFT_DROP === "1", n = (/* @__PURE__ */ new Date()).toISOString();
|
|
2561
2562
|
yield* e.onDialectOrElse({
|
|
2562
|
-
mysql: () => X(e, t ? rr(o, n,
|
|
2563
|
-
orElse: () => X(e, t ? rr(o, n,
|
|
2563
|
+
mysql: () => X(e, t ? rr(o, n, G) : tr(o), o.kind),
|
|
2564
|
+
orElse: () => X(e, t ? rr(o, n, W) : er(o, W), o.kind)
|
|
2564
2565
|
});
|
|
2565
2566
|
break;
|
|
2566
2567
|
}
|
|
2567
2568
|
case "rename-column":
|
|
2568
2569
|
yield* e.onDialectOrElse({
|
|
2569
|
-
mysql: () => X(e, nr(o,
|
|
2570
|
+
mysql: () => X(e, nr(o, G), o.kind),
|
|
2570
2571
|
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,
|
|
2572
|
+
orElse: () => X(e, nr(o, W), o.kind)
|
|
2572
2573
|
});
|
|
2573
2574
|
break;
|
|
2574
2575
|
case "add-index":
|
|
2575
2576
|
yield* e.onDialectOrElse({
|
|
2576
|
-
mysql: () => X(e,
|
|
2577
|
-
mssql: () => Z(e, [...
|
|
2578
|
-
orElse: () => X(e,
|
|
2577
|
+
mysql: () => X(e, Ir(o, n.declared), o.kind),
|
|
2578
|
+
mssql: () => Z(e, [...Kr(o.table, o.index.columns, n.declared), Gr(o)], o.kind),
|
|
2579
|
+
orElse: () => X(e, Fr(o, t.classification === "online-required"), o.kind)
|
|
2579
2580
|
});
|
|
2580
2581
|
break;
|
|
2581
2582
|
case "drop-index":
|
|
2582
2583
|
yield* e.onDialectOrElse({
|
|
2583
|
-
mysql: () => X(e,
|
|
2584
|
-
mssql: () => X(e,
|
|
2585
|
-
orElse: () => X(e,
|
|
2584
|
+
mysql: () => X(e, Rr(o), o.kind),
|
|
2585
|
+
mssql: () => X(e, zr(o), o.kind),
|
|
2586
|
+
orElse: () => X(e, Lr(o), o.kind)
|
|
2586
2587
|
});
|
|
2587
2588
|
break;
|
|
2588
2589
|
case "rename-table":
|
|
2589
2590
|
yield* e.onDialectOrElse({
|
|
2590
|
-
mysql: () => X(e,
|
|
2591
|
-
mssql: () => X(e,
|
|
2592
|
-
orElse: () => X(e,
|
|
2591
|
+
mysql: () => X(e, Br(o, G), o.kind),
|
|
2592
|
+
mssql: () => X(e, Vr(o), o.kind),
|
|
2593
|
+
orElse: () => X(e, Br(o, W), o.kind)
|
|
2593
2594
|
});
|
|
2594
2595
|
break;
|
|
2595
2596
|
case "rename-index":
|
|
2596
2597
|
yield* e.onDialectOrElse({
|
|
2597
|
-
mysql: () => X(e,
|
|
2598
|
-
mssql: () => X(e,
|
|
2599
|
-
orElse: () => X(e,
|
|
2598
|
+
mysql: () => X(e, Ur(o), o.kind),
|
|
2599
|
+
mssql: () => X(e, Wr(o), o.kind),
|
|
2600
|
+
orElse: () => X(e, Hr(o), o.kind)
|
|
2600
2601
|
});
|
|
2601
2602
|
break;
|
|
2602
2603
|
case "add-unique":
|
|
2603
2604
|
yield* e.onDialectOrElse({
|
|
2604
|
-
mysql: () => X(e,
|
|
2605
|
+
mysql: () => X(e, Jr(o, n.declared), o.kind),
|
|
2605
2606
|
sqlite: () => Z(e, J(o.table, n.declared), o.kind),
|
|
2606
|
-
mssql: () => Z(e, [...
|
|
2607
|
-
orElse: () => X(e,
|
|
2607
|
+
mssql: () => Z(e, [...Kr(o.table, [o.column], n.declared), qr(o)], o.kind),
|
|
2608
|
+
orElse: () => X(e, qr(o), o.kind)
|
|
2608
2609
|
});
|
|
2609
2610
|
break;
|
|
2610
2611
|
case "drop-unique":
|
|
2611
2612
|
yield* e.onDialectOrElse({
|
|
2612
|
-
mysql: () => X(e,
|
|
2613
|
+
mysql: () => X(e, ei(o), o.kind),
|
|
2613
2614
|
sqlite: () => Z(e, J(o.table, n.declared), o.kind),
|
|
2614
|
-
orElse: () => X(e,
|
|
2615
|
+
orElse: () => X(e, $r(o), o.kind)
|
|
2615
2616
|
});
|
|
2616
2617
|
break;
|
|
2617
2618
|
case "add-unique-composite":
|
|
2618
2619
|
yield* e.onDialectOrElse({
|
|
2619
|
-
mysql: () => X(e,
|
|
2620
|
+
mysql: () => X(e, Xr(o, n.declared), o.kind),
|
|
2620
2621
|
sqlite: () => Z(e, J(o.table, n.declared), o.kind),
|
|
2621
|
-
mssql: () => Z(e, [...
|
|
2622
|
-
orElse: () => X(e,
|
|
2622
|
+
mssql: () => Z(e, [...Kr(o.table, o.fields, n.declared), Yr(o)], o.kind),
|
|
2623
|
+
orElse: () => X(e, Yr(o), o.kind)
|
|
2623
2624
|
});
|
|
2624
2625
|
break;
|
|
2625
2626
|
case "drop-unique-composite":
|
|
2626
2627
|
yield* e.onDialectOrElse({
|
|
2627
|
-
mysql: () => X(e,
|
|
2628
|
+
mysql: () => X(e, Qr(o), o.kind),
|
|
2628
2629
|
sqlite: () => Z(e, J(o.table, n.declared), o.kind),
|
|
2629
|
-
orElse: () => X(e,
|
|
2630
|
+
orElse: () => X(e, Zr(o), o.kind)
|
|
2630
2631
|
});
|
|
2631
2632
|
break;
|
|
2632
2633
|
case "add-foreign-key":
|
|
2633
2634
|
yield* e.onDialectOrElse({
|
|
2634
|
-
mysql: () => Z(e,
|
|
2635
|
+
mysql: () => Z(e, ti(o, G), o.kind),
|
|
2635
2636
|
sqlite: () => Z(e, J(o.table, n.declared), o.kind),
|
|
2636
|
-
orElse: () => Z(e,
|
|
2637
|
+
orElse: () => Z(e, ti(o, W), o.kind)
|
|
2637
2638
|
});
|
|
2638
2639
|
break;
|
|
2639
2640
|
case "add-column": {
|
|
2640
|
-
let n =
|
|
2641
|
+
let n = si(o, t, a, yield* e.onDialectOrElse({
|
|
2641
2642
|
mysql: () => b.succeed("mariadb"),
|
|
2642
2643
|
mssql: () => b.succeed("mssql"),
|
|
2643
2644
|
sqlite: () => b.succeed("sqlite"),
|
|
@@ -2682,24 +2683,24 @@ BEGIN
|
|
|
2682
2683
|
case "alter-column-nullability":
|
|
2683
2684
|
yield* e.onDialectOrElse({
|
|
2684
2685
|
mysql: () => {
|
|
2685
|
-
let t =
|
|
2686
|
-
return t ? X(e, `ALTER TABLE ${
|
|
2686
|
+
let t = K(n.declared, o.table, o.column);
|
|
2687
|
+
return t ? X(e, `ALTER TABLE ${G(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.`));
|
|
2687
2688
|
},
|
|
2688
2689
|
mssql: () => {
|
|
2689
|
-
let t =
|
|
2690
|
-
return t ? X(e,
|
|
2690
|
+
let t = K(n.declared, o.table, o.column);
|
|
2691
|
+
return t ? X(e, wr(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}'.`));
|
|
2691
2692
|
},
|
|
2692
2693
|
sqlite: () => Z(e, J(o.table, n.declared), o.kind),
|
|
2693
|
-
orElse: () => X(e, `ALTER TABLE ${
|
|
2694
|
+
orElse: () => X(e, `ALTER TABLE ${W(o.table)} ALTER COLUMN ${W(o.column)} ${o.toNullable ? "DROP NOT NULL" : "SET NOT NULL"};`, o.kind)
|
|
2694
2695
|
});
|
|
2695
2696
|
break;
|
|
2696
2697
|
case "alter-column-default": {
|
|
2697
2698
|
let t = or(n.declared, o.table, o.column);
|
|
2698
2699
|
yield* e.onDialectOrElse({
|
|
2699
|
-
mysql: () => X(e, ir(o,
|
|
2700
|
-
mssql: () => X(e,
|
|
2700
|
+
mysql: () => X(e, ir(o, G, (e) => cr(e, t)), o.kind),
|
|
2701
|
+
mssql: () => X(e, Tr(o), o.kind),
|
|
2701
2702
|
sqlite: () => Z(e, J(o.table, n.declared), o.kind),
|
|
2702
|
-
orElse: () => X(e, ir(o,
|
|
2703
|
+
orElse: () => X(e, ir(o, W, (e) => ur(e, t)), o.kind)
|
|
2703
2704
|
});
|
|
2704
2705
|
break;
|
|
2705
2706
|
}
|
|
@@ -2708,15 +2709,15 @@ BEGIN
|
|
|
2708
2709
|
if (t.classification !== "online-required") {
|
|
2709
2710
|
yield* e.onDialectOrElse({
|
|
2710
2711
|
mysql: () => {
|
|
2711
|
-
let t =
|
|
2712
|
-
return t ? X(e, `ALTER TABLE ${
|
|
2712
|
+
let t = K(n.declared, o.table, o.column);
|
|
2713
|
+
return t ? X(e, `ALTER TABLE ${G(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.`));
|
|
2713
2714
|
},
|
|
2714
2715
|
mssql: () => {
|
|
2715
|
-
let t =
|
|
2716
|
-
return t ? X(e,
|
|
2716
|
+
let t = K(n.declared, o.table, o.column);
|
|
2717
|
+
return t ? X(e, wr(t, o.column, o.table), o.kind) : b.die(/* @__PURE__ */ Error(`alter-column-type on mssql needs the declared column '${o.table}.${o.column}'.`));
|
|
2717
2718
|
},
|
|
2718
2719
|
sqlite: () => Z(e, J(o.table, n.declared), o.kind),
|
|
2719
|
-
orElse: () => X(e, `ALTER TABLE ${
|
|
2720
|
+
orElse: () => X(e, `ALTER TABLE ${W(o.table)} ALTER COLUMN ${W(o.column)} TYPE ${r}${i};`, o.kind)
|
|
2720
2721
|
});
|
|
2721
2722
|
break;
|
|
2722
2723
|
}
|
|
@@ -2725,35 +2726,35 @@ BEGIN
|
|
|
2725
2726
|
orElse: () => b.void
|
|
2726
2727
|
});
|
|
2727
2728
|
let a = `${o.column}__shadow`, s = `${o.column}__old`;
|
|
2728
|
-
yield* e.unsafe(`ALTER TABLE ${
|
|
2729
|
-
let c = o.using ??
|
|
2729
|
+
yield* e.unsafe(`ALTER TABLE ${W(o.table)} ADD COLUMN ${W(a)} ${r}`);
|
|
2730
|
+
let c = o.using ?? W(o.column), l;
|
|
2730
2731
|
for (;;) {
|
|
2731
2732
|
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)}`;
|
|
2732
2733
|
if (t.length === 0) break;
|
|
2733
|
-
yield* e.unsafe(`UPDATE ${
|
|
2734
|
+
yield* e.unsafe(`UPDATE ${W(o.table)} SET ${W(a)} = ${c} WHERE ${W(o.column)} IS NOT NULL AND ${W(a)} IS NULL AND ${W("id")} IN (${t.map((e) => `'${e.id.replace(/'/g, "''")}'`).join(",")})`), l = t[t.length - 1].id;
|
|
2734
2735
|
}
|
|
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 ${
|
|
2736
|
+
yield* b.logInfo(`alter-column-type shadow ${o.table}.${o.column}: shadow populated`).pipe(b.annotateLogs({ scope: "voltro:migrate" })), yield* e.unsafe(`ALTER TABLE ${W(o.table)} RENAME COLUMN ${W(o.column)} TO ${W(s)}`), yield* e.unsafe(`ALTER TABLE ${W(o.table)} RENAME COLUMN ${W(a)} TO ${W(o.column)}`), yield* e.unsafe(`ALTER TABLE ${W(o.table)} DROP COLUMN ${W(s)}`);
|
|
2736
2737
|
break;
|
|
2737
2738
|
}
|
|
2738
2739
|
case "drop-foreign-key":
|
|
2739
2740
|
yield* e.onDialectOrElse({
|
|
2740
|
-
mysql: () => X(e,
|
|
2741
|
+
mysql: () => X(e, ri(o), o.kind),
|
|
2741
2742
|
sqlite: () => Z(e, J(o.table, n.declared), o.kind),
|
|
2742
|
-
orElse: () => X(e,
|
|
2743
|
+
orElse: () => X(e, ni(o), o.kind)
|
|
2743
2744
|
});
|
|
2744
2745
|
break;
|
|
2745
2746
|
case "add-check":
|
|
2746
2747
|
yield* e.onDialectOrElse({
|
|
2747
|
-
mysql: () => X(e,
|
|
2748
|
+
mysql: () => X(e, ii(o, G), o.kind),
|
|
2748
2749
|
sqlite: () => Z(e, J(o.table, n.declared), o.kind),
|
|
2749
|
-
orElse: () => X(e,
|
|
2750
|
+
orElse: () => X(e, ii(o, W), o.kind)
|
|
2750
2751
|
});
|
|
2751
2752
|
break;
|
|
2752
2753
|
case "drop-check":
|
|
2753
2754
|
yield* e.onDialectOrElse({
|
|
2754
|
-
mysql: () => X(e,
|
|
2755
|
+
mysql: () => X(e, oi(o), o.kind),
|
|
2755
2756
|
sqlite: () => Z(e, J(o.table, n.declared), o.kind),
|
|
2756
|
-
orElse: () => X(e,
|
|
2757
|
+
orElse: () => X(e, ai(o), o.kind)
|
|
2757
2758
|
});
|
|
2758
2759
|
break;
|
|
2759
2760
|
}
|
|
@@ -2762,21 +2763,21 @@ BEGIN
|
|
|
2762
2763
|
status: "applied",
|
|
2763
2764
|
durationMs: Number((process.hrtime.bigint() - i) / 1000000n)
|
|
2764
2765
|
};
|
|
2765
|
-
}),
|
|
2766
|
+
}), _i = (e, t, n, r) => b.gen(function* () {
|
|
2766
2767
|
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 \`${
|
|
2768
|
+
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 \`${O}\`, reconcile the schema by hand, and delete the rows of the run you have finished.`));
|
|
2768
2769
|
let a = i[0], o = [...r].map((e) => ({
|
|
2769
2770
|
index: Number(e.opIndex),
|
|
2770
2771
|
status: e.status,
|
|
2771
2772
|
planned: JSON.parse(e.payload)
|
|
2772
2773
|
})).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 \`${
|
|
2774
|
+
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 \`${O}\` by hand before re-running.`));
|
|
2774
2775
|
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
2776
|
kind: "discard",
|
|
2776
2777
|
runId: a,
|
|
2777
2778
|
why: "no operation had started — no DDL ran"
|
|
2778
2779
|
};
|
|
2779
|
-
let l = s ? yield*
|
|
2780
|
+
let l = s ? yield* hi(e, t, s.planned) : "clean", u = r[0].planFingerprint;
|
|
2780
2781
|
if (u !== n.toFingerprint) return {
|
|
2781
2782
|
kind: "discard",
|
|
2782
2783
|
runId: a,
|
|
@@ -2787,7 +2788,7 @@ BEGIN
|
|
|
2787
2788
|
kind: "discard",
|
|
2788
2789
|
runId: a,
|
|
2789
2790
|
why: "every recorded operation had already taken effect"
|
|
2790
|
-
} : (yield*
|
|
2791
|
+
} : (yield* pi(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
2792
|
kind: "resume",
|
|
2792
2793
|
runId: a,
|
|
2793
2794
|
ops: f.map((e) => ({
|
|
@@ -2795,39 +2796,39 @@ BEGIN
|
|
|
2795
2796
|
planned: e.planned
|
|
2796
2797
|
}))
|
|
2797
2798
|
});
|
|
2798
|
-
}),
|
|
2799
|
+
}), vi = (e, t, n) => b.gen(function* () {
|
|
2799
2800
|
let r = t.operations.filter((e) => e.blocked);
|
|
2800
2801
|
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*
|
|
2802
|
+
yield* E(e, { schema: n.lockSchema });
|
|
2802
2803
|
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
2804
|
index: t,
|
|
2804
2805
|
planned: e
|
|
2805
2806
|
})), p = "fresh";
|
|
2806
2807
|
try {
|
|
2807
|
-
yield*
|
|
2808
|
-
let r = yield*
|
|
2808
|
+
yield* li(e, o);
|
|
2809
|
+
let r = yield* ui(e);
|
|
2809
2810
|
if (r.length > 0) {
|
|
2810
|
-
let n = yield*
|
|
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*
|
|
2811
|
+
let n = yield* _i(e, o, t, r);
|
|
2812
|
+
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* mi(e, n.runId));
|
|
2812
2813
|
}
|
|
2813
|
-
p === "fresh" && f.length > 0 && (yield*
|
|
2814
|
+
p === "fresh" && f.length > 0 && (yield* di(e, d, t.toFingerprint, f)), l.length > 0 && (yield* e.withTransaction(b.gen(function* () {
|
|
2814
2815
|
let e = yield* C.SqlClient;
|
|
2815
|
-
for (let t of l) a.push(yield*
|
|
2816
|
+
for (let t of l) a.push(yield* gi(e, t, n));
|
|
2816
2817
|
})));
|
|
2817
2818
|
for (let { index: t, planned: r } of f) {
|
|
2818
|
-
yield*
|
|
2819
|
-
let i = yield*
|
|
2820
|
-
a.push(i), yield*
|
|
2819
|
+
yield* fi(e, d, t, "started");
|
|
2820
|
+
let i = yield* gi(e, r, n, p);
|
|
2821
|
+
a.push(i), yield* fi(e, d, t, "applied", i.durationMs);
|
|
2821
2822
|
}
|
|
2822
2823
|
} finally {
|
|
2823
|
-
yield*
|
|
2824
|
+
yield* D(e, { schema: n.lockSchema }).pipe(b.orDie);
|
|
2824
2825
|
}
|
|
2825
2826
|
let m = yield* n.replan(e);
|
|
2826
2827
|
if (m.operations.length > 0) {
|
|
2827
2828
|
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
2829
|
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.`));
|
|
2829
2830
|
}
|
|
2830
|
-
let h = a.filter((e) => e.status === "applied").length, g = Date.now() - i, _ = d, v = (/* @__PURE__ */ new Date()).toISOString(), y = yield*
|
|
2831
|
+
let h = a.filter((e) => e.status === "applied").length, g = Date.now() - i, _ = d, v = (/* @__PURE__ */ new Date()).toISOString(), y = yield* U(e, v), x = [...l, ...f.map((e) => e.planned)];
|
|
2831
2832
|
yield* e`
|
|
2832
2833
|
INSERT INTO ${e("_voltro_migration_plans")}
|
|
2833
2834
|
(${e("id")}, ${e("fingerprint")}, ${e("liveFingerprint")}, ${e("operations")},
|
|
@@ -2837,7 +2838,7 @@ BEGIN
|
|
|
2837
2838
|
(${_}, ${t.toFingerprint}, ${m.fromFingerprint}, ${JSON.stringify(x)},
|
|
2838
2839
|
${n.appliedBy}, ${n.environment}, ${n.source},
|
|
2839
2840
|
${g}, ${y}, ${n.notes ?? null})
|
|
2840
|
-
`, yield*
|
|
2841
|
+
`, yield* mi(e, d);
|
|
2841
2842
|
let S = [...new Set(x.flatMap((e) => e.op.kind === "rename-table" ? [e.op.to] : [e.op.table]))];
|
|
2842
2843
|
return yield* b.promise(() => c({ changedTables: S }).catch(() => {})), {
|
|
2843
2844
|
id: _,
|
|
@@ -2851,14 +2852,14 @@ BEGIN
|
|
|
2851
2852
|
source: n.source,
|
|
2852
2853
|
...n.notes ? { notes: n.notes } : {}
|
|
2853
2854
|
};
|
|
2854
|
-
}),
|
|
2855
|
+
}), yi = (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"), bi = (e) => e`
|
|
2855
2856
|
SELECT fingerprint
|
|
2856
2857
|
FROM _voltro_migration_plans
|
|
2857
2858
|
ORDER BY ${e("appliedAt")} DESC
|
|
2858
2859
|
LIMIT 1
|
|
2859
|
-
`.pipe(b.map((e) => e.length > 0 ? e[0].fingerprint : void 0), b.catchAll(() => b.succeed(void 0))),
|
|
2860
|
-
if ((yield*
|
|
2861
|
-
let r = `plan_${Date.now().toString(36)}`, i = yield*
|
|
2860
|
+
`.pipe(b.map((e) => e.length > 0 ? e[0].fingerprint : void 0), b.catchAll(() => b.succeed(void 0))), xi = (e, t, n) => b.gen(function* () {
|
|
2861
|
+
if ((yield* bi(e)) === t.toFingerprint) return "already-current";
|
|
2862
|
+
let r = `plan_${Date.now().toString(36)}`, i = yield* U(e, (/* @__PURE__ */ new Date()).toISOString());
|
|
2862
2863
|
return yield* e`
|
|
2863
2864
|
INSERT INTO ${e("_voltro_migration_plans")}
|
|
2864
2865
|
(${e("id")}, ${e("fingerprint")}, ${e("liveFingerprint")}, ${e("operations")},
|
|
@@ -2869,8 +2870,8 @@ BEGIN
|
|
|
2869
2870
|
${n.appliedBy}, ${n.environment}, ${n.source},
|
|
2870
2871
|
${0}, ${i}, ${"no DDL — the declared schema already matched the database"})
|
|
2871
2872
|
`, "recorded";
|
|
2872
|
-
}).pipe(b.catchAll(() => b.succeed("already-current"))),
|
|
2873
|
-
let n =
|
|
2873
|
+
}).pipe(b.catchAll(() => b.succeed("already-current"))), Si = (e, t) => b.gen(function* () {
|
|
2874
|
+
let n = P(I(t, Me(e))), r = yield* bi(e);
|
|
2874
2875
|
return r === n ? {
|
|
2875
2876
|
kind: "up-to-date",
|
|
2876
2877
|
fingerprint: n
|
|
@@ -2879,23 +2880,23 @@ BEGIN
|
|
|
2879
2880
|
expected: n,
|
|
2880
2881
|
...r === void 0 ? {} : { actual: r }
|
|
2881
2882
|
};
|
|
2882
|
-
}),
|
|
2883
|
+
}), Ci = [
|
|
2883
2884
|
"pgeagertest_",
|
|
2884
2885
|
"mig_e2e_",
|
|
2885
2886
|
"mig_test_",
|
|
2886
2887
|
"mysqleagertest_",
|
|
2887
2888
|
"mssqleagertest_",
|
|
2888
2889
|
"sqliteeagertest_"
|
|
2889
|
-
],
|
|
2890
|
+
], wi = (e) => Ci.some((t) => e.startsWith(t)), Ti = (e, t) => b.gen(function* () {
|
|
2890
2891
|
if (process.env.VOLTRO_TEST_FIXTURE_GC === "off") return [];
|
|
2891
|
-
let n = t.filter((e) =>
|
|
2892
|
+
let n = t.filter((e) => wi(e.name)).map((e) => e.name);
|
|
2892
2893
|
if (n.length === 0) return [];
|
|
2893
2894
|
for (let t of n) yield* e.unsafe(`DROP TABLE IF EXISTS "${t}" CASCADE`).pipe(b.catchAll(() => b.void));
|
|
2894
2895
|
return n;
|
|
2895
|
-
}),
|
|
2896
|
+
}), Ei = () => {
|
|
2896
2897
|
let e = (process.env.VOLTRO_DB_IGNORE_TABLES ?? "").split(",").map((e) => e.trim()).filter((e) => e.length > 0);
|
|
2897
2898
|
return e.length > 0 ? { ignoreTables: e } : {};
|
|
2898
|
-
},
|
|
2899
|
+
}, Di = (e) => {
|
|
2899
2900
|
if (e === void 0) return;
|
|
2900
2901
|
let t = e.trim();
|
|
2901
2902
|
if (t === "") return;
|
|
@@ -2905,30 +2906,30 @@ BEGIN
|
|
|
2905
2906
|
kind: "tables",
|
|
2906
2907
|
tables: new Set(n)
|
|
2907
2908
|
} : void 0;
|
|
2908
|
-
},
|
|
2909
|
+
}, Oi = (e) => typeof e.table == "string" ? e.table : void 0, ki = (e, t = { kind: "all" }) => ({
|
|
2909
2910
|
...e,
|
|
2910
2911
|
operations: e.operations.map((e) => {
|
|
2911
2912
|
if (!e.blocked || e.classification !== "lossy") return e;
|
|
2912
2913
|
if (t?.kind === "tables") {
|
|
2913
|
-
let n =
|
|
2914
|
+
let n = Oi(e.op);
|
|
2914
2915
|
if (n === void 0 || !t.tables.has(n)) return e;
|
|
2915
2916
|
}
|
|
2916
2917
|
let { blocked: n, ...r } = e;
|
|
2917
2918
|
return r;
|
|
2918
2919
|
})
|
|
2919
|
-
}),
|
|
2920
|
+
}), Ai = (e, t, n) => t === "postgres" ? qt(e, n).pipe(b.flatMap((e) => {
|
|
2920
2921
|
let t = Jt(e);
|
|
2921
2922
|
return t === void 0 ? b.void : b.logWarning(`auto-migrate: ${t}`).pipe(b.annotateLogs({ scope: "voltro:migrate" }));
|
|
2922
|
-
}), b.catchAll(() => b.void)) : b.void,
|
|
2923
|
+
}), b.catchAll(() => b.void)) : b.void, ji = (e, t, n) => b.gen(function* () {
|
|
2923
2924
|
let r = Me(e), i = t;
|
|
2924
2925
|
if (process.env.VOLTRO_MIGRATE_FORCE !== "1") {
|
|
2925
|
-
let t =
|
|
2926
|
-
if ((yield*
|
|
2926
|
+
let t = P(I(i, r));
|
|
2927
|
+
if ((yield* bi(e)) === t) return yield* Ai(e, r, i), {
|
|
2927
2928
|
kind: "up-to-date",
|
|
2928
2929
|
fingerprint: t
|
|
2929
2930
|
};
|
|
2930
2931
|
}
|
|
2931
|
-
let a = yield* Jn(e), o = yield*
|
|
2932
|
+
let a = yield* Jn(e), o = yield* Ti(e, a.tables);
|
|
2932
2933
|
o.length > 0 && (yield* b.logInfo(`auto-migrate: reaped ${o.length} test-fixture leftover(s)`).pipe(b.annotateLogs({
|
|
2933
2934
|
scope: "voltro:migrate",
|
|
2934
2935
|
count: o.length
|
|
@@ -2937,16 +2938,16 @@ BEGIN
|
|
|
2937
2938
|
declared: i,
|
|
2938
2939
|
live: { tables: c(a.tables) },
|
|
2939
2940
|
dialect: r,
|
|
2940
|
-
...
|
|
2941
|
+
...Ei()
|
|
2941
2942
|
});
|
|
2942
2943
|
if (l.summary.blocked > 0) {
|
|
2943
|
-
let e = l.operations.filter((e) => e.blocked).every((e) => e.classification === "lossy"), t =
|
|
2944
|
-
if (t === void 0 || !e || (l =
|
|
2944
|
+
let e = l.operations.filter((e) => e.blocked).every((e) => e.classification === "lossy"), t = Di(process.env.VOLTRO_DESTRUCTIVE_OK);
|
|
2945
|
+
if (t === void 0 || !e || (l = ki(l, t), l.operations.some((e) => e.blocked))) return {
|
|
2945
2946
|
kind: "refused-blocked",
|
|
2946
2947
|
plan: l
|
|
2947
2948
|
};
|
|
2948
2949
|
}
|
|
2949
|
-
return l.operations.length === 0 ? (yield*
|
|
2950
|
+
return l.operations.length === 0 ? (yield* xi(e, l, {
|
|
2950
2951
|
appliedBy: n.appliedBy,
|
|
2951
2952
|
environment: n.environment === "prod" ? "dev" : n.environment,
|
|
2952
2953
|
source: "auto-diff"
|
|
@@ -2955,7 +2956,7 @@ BEGIN
|
|
|
2955
2956
|
fingerprint: l.toFingerprint
|
|
2956
2957
|
}) : {
|
|
2957
2958
|
kind: "applied",
|
|
2958
|
-
applied: yield*
|
|
2959
|
+
applied: yield* vi(e, l, {
|
|
2959
2960
|
declared: t,
|
|
2960
2961
|
appliedBy: n.appliedBy,
|
|
2961
2962
|
environment: n.environment === "prod" ? "dev" : n.environment,
|
|
@@ -2966,26 +2967,26 @@ BEGIN
|
|
|
2966
2967
|
declared: i,
|
|
2967
2968
|
live: { tables: c(t.tables) },
|
|
2968
2969
|
dialect: r,
|
|
2969
|
-
...
|
|
2970
|
+
...Ei()
|
|
2970
2971
|
});
|
|
2971
2972
|
})
|
|
2972
2973
|
}),
|
|
2973
2974
|
plan: l
|
|
2974
2975
|
};
|
|
2975
|
-
}),
|
|
2976
|
+
}), Mi = (e, t, n) => process.env.VOLTRO_AUTO_MIGRATE === "0" ? b.succeed({
|
|
2976
2977
|
kind: "skipped",
|
|
2977
2978
|
reason: "VOLTRO_AUTO_MIGRATE=0"
|
|
2978
|
-
}) : n.environment === "prod" ?
|
|
2979
|
+
}) : n.environment === "prod" ? Si(e, t) : ji(e, t, n), Ni = (e, t) => {
|
|
2979
2980
|
switch (e.kind) {
|
|
2980
2981
|
case "skipped": return `auto-migrate: skipped (${e.reason})`;
|
|
2981
|
-
case "up-to-date": return `auto-migrate: schema up to date (${t}, fingerprint=${
|
|
2982
|
+
case "up-to-date": return `auto-migrate: schema up to date (${t}, fingerprint=${F(e.fingerprint)})`;
|
|
2982
2983
|
case "applied": {
|
|
2983
2984
|
let t = e.plan.summary;
|
|
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=${
|
|
2985
|
+
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=${F(e.applied.fingerprint)}`;
|
|
2985
2986
|
}
|
|
2986
|
-
case "prod-mismatch": return `auto-migrate: SCHEMA FINGERPRINT MISMATCH — declared=${
|
|
2987
|
+
case "prod-mismatch": return `auto-migrate: SCHEMA FINGERPRINT MISMATCH — declared=${F(e.expected)}` + (e.actual ? ` live=${F(e.actual)}` : " live=<no _voltro_migration_plans row>") + ". Run `voltro db apply --plan plan.json` from the deploy pipeline before serving.";
|
|
2987
2988
|
case "refused-blocked": {
|
|
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` +
|
|
2989
|
+
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` + yi(e.plan), c = [""];
|
|
2989
2990
|
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) {
|
|
2990
2991
|
let t = /* @__PURE__ */ new Map(), n = /* @__PURE__ */ new Map();
|
|
2991
2992
|
for (let r of e.plan.operations) {
|
|
@@ -3002,7 +3003,7 @@ BEGIN
|
|
|
3002
3003
|
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");
|
|
3003
3004
|
}
|
|
3004
3005
|
}
|
|
3005
|
-
},
|
|
3006
|
+
}, Pi = /* @__PURE__ */ new Set([
|
|
3006
3007
|
"node_modules",
|
|
3007
3008
|
"dist",
|
|
3008
3009
|
"build",
|
|
@@ -3011,11 +3012,11 @@ BEGIN
|
|
|
3011
3012
|
".next",
|
|
3012
3013
|
".git",
|
|
3013
3014
|
".framework"
|
|
3014
|
-
]),
|
|
3015
|
+
]), Fi = async (e) => {
|
|
3015
3016
|
let t = [], n = async (e) => {
|
|
3016
3017
|
let r = await te.readdir(e, { withFileTypes: !0 }).catch(() => []);
|
|
3017
3018
|
for (let i of r) {
|
|
3018
|
-
if (
|
|
3019
|
+
if (Pi.has(i.name)) continue;
|
|
3019
3020
|
let r = ne(e, i.name);
|
|
3020
3021
|
i.isDirectory() ? await n(r) : i.isFile() && u.test(i.name) && t.push(r);
|
|
3021
3022
|
}
|
|
@@ -3024,7 +3025,7 @@ BEGIN
|
|
|
3024
3025
|
let n = e.split("/").pop() ?? e, r = t.split("/").pop() ?? t;
|
|
3025
3026
|
return n.localeCompare(r);
|
|
3026
3027
|
});
|
|
3027
|
-
},
|
|
3028
|
+
}, Ii = async (e) => {
|
|
3028
3029
|
let t = [];
|
|
3029
3030
|
for (let n of e) {
|
|
3030
3031
|
let e = (await import(re(n).href)).default;
|
|
@@ -3038,11 +3039,11 @@ BEGIN
|
|
|
3038
3039
|
});
|
|
3039
3040
|
}
|
|
3040
3041
|
return t;
|
|
3041
|
-
},
|
|
3042
|
+
}, Li = (e) => e`
|
|
3042
3043
|
SELECT ${e("id")}
|
|
3043
3044
|
FROM ${e("_voltro_migration_plans")}
|
|
3044
3045
|
WHERE ${e("source")} = 'file'
|
|
3045
|
-
`.pipe(b.map((e) => e.map((e) => e.id)), b.catchAll(() => b.succeed([]))),
|
|
3046
|
+
`.pipe(b.map((e) => e.map((e) => e.id)), b.catchAll(() => b.succeed([]))), Ri = class extends Error {
|
|
3046
3047
|
migrationId;
|
|
3047
3048
|
file;
|
|
3048
3049
|
cause;
|
|
@@ -3050,7 +3051,7 @@ BEGIN
|
|
|
3050
3051
|
constructor(e, t, n) {
|
|
3051
3052
|
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";
|
|
3052
3053
|
}
|
|
3053
|
-
},
|
|
3054
|
+
}, zi = (e, t, n, r) => b.gen(function* () {
|
|
3054
3055
|
let i = Date.now(), a = (/* @__PURE__ */ new Date()).toISOString(), o = {
|
|
3055
3056
|
sql: e,
|
|
3056
3057
|
log: {
|
|
@@ -3071,7 +3072,7 @@ BEGIN
|
|
|
3071
3072
|
});
|
|
3072
3073
|
let c = Date.now() - i;
|
|
3073
3074
|
!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*
|
|
3075
|
+
let l = yield* U(e, a);
|
|
3075
3076
|
return yield* e`
|
|
3076
3077
|
INSERT INTO ${e("_voltro_migration_plans")}
|
|
3077
3078
|
(${e("id")}, ${e("fingerprint")}, ${e("operations")},
|
|
@@ -3084,10 +3085,10 @@ BEGIN
|
|
|
3084
3085
|
}])},
|
|
3085
3086
|
${r}, ${n}, ${"file"},
|
|
3086
3087
|
${c}, ${l}, ${t.migration.description})
|
|
3087
|
-
`.pipe(b.mapError((e) => new
|
|
3088
|
-
}),
|
|
3088
|
+
`.pipe(b.mapError((e) => new Ri(t.migration.id, t.file, e))), { durationMs: c };
|
|
3089
|
+
}), Bi = (e, t) => b.gen(function* () {
|
|
3089
3090
|
let n = yield* b.tryPromise({
|
|
3090
|
-
try: () =>
|
|
3091
|
+
try: () => Fi(ne(t, "migrations")),
|
|
3091
3092
|
catch: (e) => e
|
|
3092
3093
|
});
|
|
3093
3094
|
if (n.length === 0) return {
|
|
@@ -3095,25 +3096,25 @@ BEGIN
|
|
|
3095
3096
|
skipped: []
|
|
3096
3097
|
};
|
|
3097
3098
|
let r = yield* b.tryPromise({
|
|
3098
|
-
try: () =>
|
|
3099
|
+
try: () => Ii(n),
|
|
3099
3100
|
catch: (e) => e
|
|
3100
3101
|
});
|
|
3101
3102
|
if (r.length === 0) return {
|
|
3102
3103
|
pending: [],
|
|
3103
3104
|
skipped: []
|
|
3104
3105
|
};
|
|
3105
|
-
let i = new Set(yield*
|
|
3106
|
+
let i = new Set(yield* Li(e));
|
|
3106
3107
|
return {
|
|
3107
3108
|
pending: r.filter((e) => !i.has(e.migration.id)),
|
|
3108
3109
|
skipped: r.filter((e) => i.has(e.migration.id)).map((e) => e.migration.id)
|
|
3109
3110
|
};
|
|
3110
|
-
}),
|
|
3111
|
-
let { pending: n, skipped: r } = yield*
|
|
3111
|
+
}), Vi = (e, t) => Bi(e, t).pipe(b.map((e) => e.pending.map((e) => e.migration.id))), Hi = (e, t) => b.gen(function* () {
|
|
3112
|
+
let { pending: n, skipped: r } = yield* Bi(e, t.projectRoot);
|
|
3112
3113
|
if (n.length === 0) return {
|
|
3113
3114
|
applied: [],
|
|
3114
3115
|
skipped: r
|
|
3115
3116
|
};
|
|
3116
|
-
yield*
|
|
3117
|
+
yield* E(e, { schema: t.lockSchema });
|
|
3117
3118
|
let i = [];
|
|
3118
3119
|
try {
|
|
3119
3120
|
for (let r of n) {
|
|
@@ -3122,7 +3123,7 @@ BEGIN
|
|
|
3122
3123
|
id: r.migration.id,
|
|
3123
3124
|
file: r.file
|
|
3124
3125
|
}));
|
|
3125
|
-
let { durationMs: n } = yield*
|
|
3126
|
+
let { durationMs: n } = yield* zi(e, r, t.env, t.appliedBy);
|
|
3126
3127
|
i.push({
|
|
3127
3128
|
id: r.migration.id,
|
|
3128
3129
|
durationMs: n
|
|
@@ -3133,28 +3134,28 @@ BEGIN
|
|
|
3133
3134
|
}));
|
|
3134
3135
|
}
|
|
3135
3136
|
} finally {
|
|
3136
|
-
yield*
|
|
3137
|
+
yield* D(e, { schema: t.lockSchema }).pipe(b.orDie);
|
|
3137
3138
|
}
|
|
3138
3139
|
return {
|
|
3139
3140
|
applied: i,
|
|
3140
3141
|
skipped: r
|
|
3141
3142
|
};
|
|
3142
|
-
}),
|
|
3143
|
+
}), Ui = (e, t) => e`
|
|
3143
3144
|
SELECT ${e("id")}
|
|
3144
3145
|
FROM ${e("_voltro_migration_plans")}
|
|
3145
3146
|
WHERE ${e("id")} = ${t} AND ${e("source")} = 'file'
|
|
3146
3147
|
LIMIT 1
|
|
3147
|
-
`,
|
|
3148
|
-
if (!(yield*
|
|
3148
|
+
`, Wi = (e, t) => b.gen(function* () {
|
|
3149
|
+
if (!(yield* Ui(e, t.id))[0]) return yield* b.fail(/* @__PURE__ */ Error(`rollback: ${t.id} not found in _voltro_migration_plans (file source)`));
|
|
3149
3150
|
let n = yield* b.tryPromise({
|
|
3150
|
-
try: () =>
|
|
3151
|
+
try: () => Fi(ne(t.projectRoot, "migrations")),
|
|
3151
3152
|
catch: (e) => e
|
|
3152
3153
|
}), r = (yield* b.tryPromise({
|
|
3153
|
-
try: () =>
|
|
3154
|
+
try: () => Ii(n),
|
|
3154
3155
|
catch: (e) => e
|
|
3155
3156
|
})).find((e) => e.migration.id === t.id);
|
|
3156
3157
|
if (!r) return yield* b.fail(/* @__PURE__ */ Error(`rollback: file for migration ${t.id} not found on disk under ${t.projectRoot}/migrations/`));
|
|
3157
|
-
yield*
|
|
3158
|
+
yield* E(e, { schema: t.lockSchema });
|
|
3158
3159
|
let i = Date.now();
|
|
3159
3160
|
try {
|
|
3160
3161
|
let n = {
|
|
@@ -3181,9 +3182,9 @@ BEGIN
|
|
|
3181
3182
|
durationMs: o
|
|
3182
3183
|
};
|
|
3183
3184
|
} finally {
|
|
3184
|
-
yield*
|
|
3185
|
+
yield* D(e, { schema: t.lockSchema }).pipe(b.orDie);
|
|
3185
3186
|
}
|
|
3186
|
-
}),
|
|
3187
|
+
}), Gi = (e) => b.gen(function* () {
|
|
3187
3188
|
let t = yield* C.SqlClient, n = yield* t`
|
|
3188
3189
|
SELECT id, fingerprint, appliedAt FROM _voltro_migration_plans
|
|
3189
3190
|
WHERE source = 'auto-diff'
|
|
@@ -3228,7 +3229,7 @@ BEGIN
|
|
|
3228
3229
|
snapshotFingerprint: e.fingerprint,
|
|
3229
3230
|
snapshotId: i
|
|
3230
3231
|
};
|
|
3231
|
-
}),
|
|
3232
|
+
}), Ki = { safe: !0 }, qi = (e) => {
|
|
3232
3233
|
switch (e.kind) {
|
|
3233
3234
|
case "drop-column": return {
|
|
3234
3235
|
safe: !1,
|
|
@@ -3255,7 +3256,7 @@ BEGIN
|
|
|
3255
3256
|
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`,
|
|
3256
3257
|
remedy: "add a new column of the target type + backfill + dual-write, cut code over, then drop the old column in a LATER deploy"
|
|
3257
3258
|
};
|
|
3258
|
-
case "alter-column-nullability": return e.toNullable ?
|
|
3259
|
+
case "alter-column-nullability": return e.toNullable ? Ki : {
|
|
3259
3260
|
safe: !1,
|
|
3260
3261
|
reason: `old instances may INSERT "${e.table}" without (or with NULL in) "${e.column}", which the new NOT NULL rejects`,
|
|
3261
3262
|
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)`
|
|
@@ -3285,12 +3286,12 @@ BEGIN
|
|
|
3285
3286
|
case "drop-unique":
|
|
3286
3287
|
case "drop-unique-composite":
|
|
3287
3288
|
case "drop-foreign-key":
|
|
3288
|
-
case "drop-check": return
|
|
3289
|
+
case "drop-check": return Ki;
|
|
3289
3290
|
}
|
|
3290
|
-
},
|
|
3291
|
+
}, Ji = (e) => {
|
|
3291
3292
|
let t = [];
|
|
3292
3293
|
for (let n of e) {
|
|
3293
|
-
let e =
|
|
3294
|
+
let e = qi(n.op);
|
|
3294
3295
|
e.safe || t.push({
|
|
3295
3296
|
op: n,
|
|
3296
3297
|
reason: e.reason,
|
|
@@ -3298,7 +3299,7 @@ BEGIN
|
|
|
3298
3299
|
});
|
|
3299
3300
|
}
|
|
3300
3301
|
return t;
|
|
3301
|
-
},
|
|
3302
|
+
}, Yi = (e) => {
|
|
3302
3303
|
if (!e.enabled || e.unsafeCount === 0) return {
|
|
3303
3304
|
refuse: !1,
|
|
3304
3305
|
warnForced: !1,
|
|
@@ -3314,18 +3315,18 @@ BEGIN
|
|
|
3314
3315
|
warnForced: !1,
|
|
3315
3316
|
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.`
|
|
3316
3317
|
};
|
|
3317
|
-
},
|
|
3318
|
+
}, Xi = (e) => {
|
|
3318
3319
|
let t = e.table;
|
|
3319
3320
|
if (typeof t == "string") return t;
|
|
3320
3321
|
let n = e.from;
|
|
3321
3322
|
return typeof n == "string" ? n : void 0;
|
|
3322
|
-
},
|
|
3323
|
+
}, Zi = (e) => ({
|
|
3323
3324
|
kind: e.op.kind,
|
|
3324
|
-
table:
|
|
3325
|
+
table: Xi(e.op),
|
|
3325
3326
|
classification: e.classification,
|
|
3326
3327
|
reason: e.reason,
|
|
3327
3328
|
blockedFix: e.blocked?.fix
|
|
3328
|
-
}),
|
|
3329
|
+
}), Qi = (e) => e.operations.map(Zi), $i = async (e) => {
|
|
3329
3330
|
let t = e.log ?? (() => {}), n = {
|
|
3330
3331
|
branchId: e.branchId,
|
|
3331
3332
|
mechanism: e.mechanism,
|
|
@@ -3344,10 +3345,10 @@ BEGIN
|
|
|
3344
3345
|
applied: !1,
|
|
3345
3346
|
converged: !1,
|
|
3346
3347
|
residual: r,
|
|
3347
|
-
infidelity:
|
|
3348
|
+
infidelity: Qi(o)
|
|
3348
3349
|
};
|
|
3349
3350
|
else {
|
|
3350
|
-
let i = await e.plan(), o =
|
|
3351
|
+
let i = await e.plan(), o = Qi(i), s = o.filter((e) => e.classification === "lossy"), c = ki(i), l = c.operations.filter((e) => e.blocked !== void 0).map(Zi);
|
|
3351
3352
|
if (l.length > 0) a = {
|
|
3352
3353
|
...n,
|
|
3353
3354
|
outcome: "blocked",
|
|
@@ -3372,7 +3373,7 @@ BEGIN
|
|
|
3372
3373
|
};
|
|
3373
3374
|
else {
|
|
3374
3375
|
await e.apply(c), t(`branch ${e.branchId}: applied ${o.length} operation(s)`);
|
|
3375
|
-
let i =
|
|
3376
|
+
let i = Qi(await e.replan());
|
|
3376
3377
|
a = {
|
|
3377
3378
|
...n,
|
|
3378
3379
|
outcome: i.length > 0 ? "diverged" : s.length > 0 ? "lossy" : "clean",
|
|
@@ -3410,7 +3411,7 @@ BEGIN
|
|
|
3410
3411
|
...a,
|
|
3411
3412
|
tornDown: o
|
|
3412
3413
|
};
|
|
3413
|
-
},
|
|
3414
|
+
}, ea = (e) => {
|
|
3414
3415
|
switch (e.outcome) {
|
|
3415
3416
|
case "clean": return 0;
|
|
3416
3417
|
case "lossy": return 2;
|
|
@@ -3419,22 +3420,22 @@ BEGIN
|
|
|
3419
3420
|
case "infidelity": return 1;
|
|
3420
3421
|
case "failed": return 1;
|
|
3421
3422
|
}
|
|
3422
|
-
},
|
|
3423
|
+
}, ta = (e) => ` ${e.classification === "lossy" ? "✗" : "•"} ${e.kind}${e.table ? ` ${e.table}` : ""} [${e.classification}]${e.reason ? ` — ${e.reason}` : ""}${e.blockedFix ? `\n ! ${e.blockedFix}` : ""}`, na = (e) => {
|
|
3423
3424
|
let t = [];
|
|
3424
3425
|
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
3426
|
else if (e.outcome === "infidelity") {
|
|
3426
3427
|
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(
|
|
3428
|
+
for (let n of e.infidelity) t.push(ta(n));
|
|
3428
3429
|
} else {
|
|
3429
3430
|
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(
|
|
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(
|
|
3431
|
+
for (let n of e.operations) t.push(ta(n));
|
|
3432
|
+
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(ta(n));
|
|
3432
3433
|
}
|
|
3433
3434
|
return t.push(e.tornDown ? ` branch ${e.branchId} torn down.` : ` branch ${e.branchId} KEPT — drop it when you are done.`), t.join("\n");
|
|
3434
|
-
},
|
|
3435
|
+
}, ra = (e, ...t) => ({
|
|
3435
3436
|
_tag: "RawSqlFragment",
|
|
3436
3437
|
strings: [...e],
|
|
3437
3438
|
values: [...t]
|
|
3438
|
-
}),
|
|
3439
|
+
}), ia = (e) => typeof e == "object" && !!e && e._tag === "RawSqlFragment";
|
|
3439
3440
|
//#endregion
|
|
3440
|
-
export {
|
|
3441
|
+
export { k as DEFAULT_CDC_CHANNEL, u as FILE_MIGRATION_PATTERN, Ri as FileMigrationLedgerError, Ke as REACTIVE_TRIGGER_PREFIX, fe as VOLTRO_MIGRATION_LOCK_KEY, E as acquireMigrationLock, U as appliedAtValue, Ft as applyNamespacedSchema, vi as applyPlan, Tt as applySchema, Yi as assessRollingDeployGate, ea as branchRehearsalExitCode, In as chunkTables, qi as classifyRollingDeploySafety, I as declaredSnapshot, Qe as defaultArrayClause, Ze as defaultClause, M as defaultJsonClause, Ni as describeOutcome, Di as destructiveScope, qt as detectReactiveTriggerDrift, Ir as emitAddIndexMysql, _r as emitCreateTableMysql, er as emitDropColumnDdl, tr as emitDropColumnDdlMysql, wt as emitFrameworkBootstrapSql, St as emitNamespaceProvisionDdl, Ct as emitNamespacedSchemaSql, xt as emitSchemaSql, P as fingerprintSchema, ve as fnv1a64, na as formatBranchRehearsal, Jt as formatReactiveTriggerDrift, Ei as ignoreTablesFromEnv, Jn as introspectSchema, o as isFileMigration, ia as isRawSqlFragment, Dt as isReRunSafeDdl, jn as mapPgType, h as migration, ye as migrationLockKeyForSchema, T as migrationLockNameForSchema, Mr as mysqlIndexPrefixFor, Je as notifyFunctionName, N as numericIdSql, An as parseEnumCheck, Vi as pendingFileMigrationIds, kn as planMigrations, It as provisionTenantNamespace, qe as reactiveTriggerName, ft as reactiveTriggerRepairSql, xi as recordUpToDate, $i as rehearseMigrationOnBranch, Zi as rehearsedOperation, D as releaseMigrationLock, gr as renderColumnMysql, dr as renderColumnPg, me as resolveMigrationLockSchema, Wi as rollbackFileBasedMigration, Ji as rollingDeployUnsafeOps, Hi as runFileBasedMigrations, Lt as runFrameworkBootstrap, Pt as runMigrate, Mi as runPlannedMigrations, F as shortFingerprint, Ut as snapshotColumn, ra as sql, j as sqlType, Gi as squashMigrationPlans, ki as unblockLossy, je as withMigrationLock };
|