@voltro/database 0.22.1 → 0.24.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 +427 -0
- package/dist/{fileBased-CDnutVVk.js → frameworkLiveTables-BvPIm2cD.js} +174 -174
- package/dist/index.d.ts +40 -0
- package/dist/index.js +651 -651
- package/dist/sql.d.ts +114 -5
- package/dist/sql.js +981 -861
- package/package.json +2 -2
package/dist/sql.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Effect as
|
|
3
|
-
import { createLogger as
|
|
4
|
-
import { SqlClient as
|
|
5
|
-
import { createHash as
|
|
6
|
-
import { promises as
|
|
7
|
-
import { join as
|
|
8
|
-
import { pathToFileURL as
|
|
1
|
+
import { H as e, U as t, a as n, c as r, d as i, ft as a, i as o, n as s, o as c, ot as l, q as u, r as d, s as f } from "./frameworkLiveTables-BvPIm2cD.js";
|
|
2
|
+
import { Effect as p } from "effect";
|
|
3
|
+
import { createLogger as m } from "@voltro/logger";
|
|
4
|
+
import { SqlClient as h } from "@effect/sql";
|
|
5
|
+
import { createHash as g } from "node:crypto";
|
|
6
|
+
import { promises as _ } from "node:fs";
|
|
7
|
+
import { join as v } from "node:path";
|
|
8
|
+
import { pathToFileURL as y } from "node:url";
|
|
9
9
|
//#region src/migrations/ddlError.ts
|
|
10
|
-
var
|
|
10
|
+
var b = (e, t, n, r = 400) => {
|
|
11
11
|
let i = n, a = i.cause ?? {}, o = (e) => {
|
|
12
12
|
let t = a[e];
|
|
13
13
|
return t == null || t === "" ? void 0 : String(t);
|
|
@@ -39,19 +39,19 @@ var _ = (e, t, n, r = 400) => {
|
|
|
39
39
|
t !== void 0 && s.push(`db.${e}: ${t}`);
|
|
40
40
|
}
|
|
41
41
|
return s.push(""), s.join("\n");
|
|
42
|
-
},
|
|
43
|
-
if (t !== "mysql" && t !== "mariadb" || !e.appliedIndexes.some(
|
|
42
|
+
}, x = (e) => e.unique === !0 && typeof e.where == "string" && e.where.length > 0, S = (e) => e.replace(/"/g, "`"), ee = (e) => typeof e == "string" ? e : e.name ?? String(e), C = (e, t) => `_ua_${t}_${e}`.slice(0, 64), te = (e, t) => {
|
|
43
|
+
if (t !== "mysql" && t !== "mariadb" || !e.appliedIndexes.some(x)) return e;
|
|
44
44
|
let n = { ...e.fields }, r = [], i = [];
|
|
45
45
|
for (let t of e.appliedIndexes) {
|
|
46
|
-
if (!
|
|
46
|
+
if (!x(t)) {
|
|
47
47
|
r.push(t);
|
|
48
48
|
continue;
|
|
49
49
|
}
|
|
50
|
-
let a =
|
|
50
|
+
let a = S(t.where), o = [];
|
|
51
51
|
for (let r of t.fields) {
|
|
52
|
-
let i =
|
|
52
|
+
let i = ee(r);
|
|
53
53
|
if (!(i in e.fields)) throw Error(`@voltro/migrate: uniqueActive '${t.name}' on '${e.tableName}' references unknown column '${i}'.`);
|
|
54
|
-
let s =
|
|
54
|
+
let s = C(t.name, i);
|
|
55
55
|
o.push(s), n[s] = {
|
|
56
56
|
type: "text",
|
|
57
57
|
nullable: !0,
|
|
@@ -74,11 +74,11 @@ var _ = (e, t, n, r = 400) => {
|
|
|
74
74
|
appliedIndexes: r,
|
|
75
75
|
appliedUniques: [...e.appliedUniques ?? [], ...i]
|
|
76
76
|
};
|
|
77
|
-
},
|
|
77
|
+
}, w = (e, t) => `${e}::${t}`, ne = (e, t) => {
|
|
78
78
|
let n = /* @__PURE__ */ new Set(), r = /* @__PURE__ */ new Set(), i = /* @__PURE__ */ new Set(), a = (e) => {
|
|
79
79
|
n.add(e);
|
|
80
80
|
let o = [...t(e)].filter((t) => t.target !== e).sort((e, t) => e.column.localeCompare(t.column));
|
|
81
|
-
for (let t of o) n.has(t.target) ? i.add(
|
|
81
|
+
for (let t of o) n.has(t.target) ? i.add(w(e, t.column)) : r.has(t.target) || a(t.target);
|
|
82
82
|
n.delete(e), r.add(e);
|
|
83
83
|
};
|
|
84
84
|
for (let t of [...e].sort()) r.has(t) || a(t);
|
|
@@ -86,89 +86,89 @@ var _ = (e, t, n, r = 400) => {
|
|
|
86
86
|
}, re = 6322741009312437n, ie = 250, ae = () => {
|
|
87
87
|
let e = Number(process.env.VOLTRO_MIGRATION_LOCK_TIMEOUT_MS);
|
|
88
88
|
return Number.isFinite(e) && e > 0 ? e : 3e4;
|
|
89
|
-
}, oe = (e) =>
|
|
89
|
+
}, oe = (e) => p.gen(function* () {
|
|
90
90
|
let t = ae(), n = Date.now();
|
|
91
91
|
for (;;) {
|
|
92
92
|
if ((yield* e`
|
|
93
93
|
SELECT pg_try_advisory_lock(${"6322741009312437"}::bigint) AS got`)[0]?.got) return;
|
|
94
|
-
Date.now() - n >= t && (yield*
|
|
94
|
+
Date.now() - n >= t && (yield* p.die(/* @__PURE__ */ Error(`could not acquire the postgres migration advisory lock within ${Math.round(t / 1e3)}s. Another migration is in progress, or a prior boot crashed while holding it. (A crashed process's session-level advisory lock is released once its DB connection closes; if this persists, find + terminate the holder via pg_stat_activity, or raise VOLTRO_MIGRATION_LOCK_TIMEOUT_MS.)`))), yield* p.sleep(`${ie} millis`);
|
|
95
95
|
}
|
|
96
|
-
}), se = (e) =>
|
|
96
|
+
}), se = (e) => p.gen(function* () {
|
|
97
97
|
yield* e`SELECT pg_advisory_unlock(${re.toString()}::bigint)`;
|
|
98
|
-
}), ce = `voltro_migration_${re.toString(36)}`, le = 30, ue = (e) =>
|
|
98
|
+
}), ce = `voltro_migration_${re.toString(36)}`, le = 30, ue = (e) => p.gen(function* () {
|
|
99
99
|
(yield* e`
|
|
100
|
-
SELECT GET_LOCK(${ce}, ${le}) AS got`)[0]?.got !== 1 && (yield*
|
|
101
|
-
}), de = (e) =>
|
|
100
|
+
SELECT GET_LOCK(${ce}, ${le}) AS got`)[0]?.got !== 1 && (yield* p.die(/* @__PURE__ */ Error(`could not acquire the migration lock '${ce}' within ${le}s (another migration is in progress, or a prior one left it held).`)));
|
|
101
|
+
}), de = (e) => p.gen(function* () {
|
|
102
102
|
yield* e`SELECT RELEASE_LOCK(${ce})`;
|
|
103
|
-
}), fe = `voltro_migration_${re.toString(36)}`, pe = 3e4, me = (e) =>
|
|
103
|
+
}), fe = `voltro_migration_${re.toString(36)}`, pe = 3e4, me = (e) => p.gen(function* () {
|
|
104
104
|
let t = yield* e`
|
|
105
105
|
DECLARE @res int;
|
|
106
106
|
EXEC @res = sp_getapplock @Resource = ${fe}, @LockMode = 'Exclusive',
|
|
107
107
|
@LockOwner = 'Session', @LockTimeout = ${pe};
|
|
108
108
|
SELECT @res AS res`;
|
|
109
|
-
(t[0]?.res ?? -999) < 0 && (yield*
|
|
110
|
-
}), he = (e) =>
|
|
109
|
+
(t[0]?.res ?? -999) < 0 && (yield* p.die(/* @__PURE__ */ Error(`could not acquire the migration lock '${fe}' (sp_getapplock returned ${t[0]?.res ?? "no row"}; another migration is in progress).`)));
|
|
110
|
+
}), he = (e) => p.gen(function* () {
|
|
111
111
|
yield* e`EXEC sp_releaseapplock @Resource = ${fe}, @LockOwner = 'Session'`;
|
|
112
|
-
}), ge =
|
|
112
|
+
}), ge = p.void, T = (e) => e.onDialectOrElse({
|
|
113
113
|
mysql: () => ue(e),
|
|
114
114
|
mssql: () => me(e),
|
|
115
115
|
sqlite: () => ge,
|
|
116
116
|
orElse: () => oe(e)
|
|
117
|
-
}),
|
|
117
|
+
}), E = (e) => e.onDialectOrElse({
|
|
118
118
|
mysql: () => de(e),
|
|
119
119
|
mssql: () => he(e),
|
|
120
120
|
sqlite: () => ge,
|
|
121
121
|
orElse: () => se(e)
|
|
122
|
-
}),
|
|
123
|
-
|
|
124
|
-
},
|
|
125
|
-
let
|
|
126
|
-
if (
|
|
127
|
-
if (
|
|
128
|
-
throw Error(`@voltro/plugin-postgis: ${
|
|
129
|
-
}
|
|
130
|
-
if (
|
|
131
|
-
if (
|
|
132
|
-
return
|
|
133
|
-
}
|
|
134
|
-
if (
|
|
135
|
-
if (
|
|
136
|
-
...
|
|
137
|
-
type:
|
|
122
|
+
}), D = (e, t) => p.acquireUseRelease(T(e), () => t, () => E(e).pipe(p.orDie)), O = "framework_changes", k = "framework_changes_", _e = (e, t) => t === "framework_changes" ? `${k}${e}` : `${k}${t}_${e}`, ve = (e) => e === "framework_changes" ? "framework_notify_change" : `framework_notify_change_${e}`, ye = m({ scope: "voltro:migrate" }), A = (e) => {
|
|
123
|
+
ye.warn(e.replace(/^\[voltro:migrate\]\s*/, ""));
|
|
124
|
+
}, j = (t, n) => {
|
|
125
|
+
let r = t.type;
|
|
126
|
+
if (t.spatial) {
|
|
127
|
+
if (n === "postgres") return `${t.spatial.kind}(${t.spatial.geomKind},${t.spatial.srid})`;
|
|
128
|
+
throw Error(`@voltro/plugin-postgis: ${t.spatial.kind}('${t.spatial.geomKind}', ${t.spatial.srid}) is postgres-only — the active dialect is '${n}'. PostGIS has no portable equivalent; use store: 'postgres' (or DB_DIALECT=postgres) for spatial columns, or model the value as plain text/json.`);
|
|
129
|
+
}
|
|
130
|
+
if (r === "raw") {
|
|
131
|
+
if (t.rawDdl === void 0) throw Error("raw() column is missing its DDL fragment — this is a framework bug.");
|
|
132
|
+
return t.rawDdl;
|
|
133
|
+
}
|
|
134
|
+
if (r === "array" && t.arrayElement) {
|
|
135
|
+
if (n === "postgres") return `${j({
|
|
136
|
+
...t,
|
|
137
|
+
type: t.arrayElement,
|
|
138
138
|
arrayElement: void 0
|
|
139
|
-
},
|
|
140
|
-
if (
|
|
141
|
-
if (
|
|
142
|
-
if (
|
|
143
|
-
}
|
|
144
|
-
if (
|
|
145
|
-
let
|
|
146
|
-
if (
|
|
147
|
-
let
|
|
148
|
-
if (
|
|
149
|
-
if (
|
|
150
|
-
if (
|
|
151
|
-
if (
|
|
152
|
-
}
|
|
153
|
-
if (
|
|
154
|
-
if (
|
|
155
|
-
if (
|
|
156
|
-
if (
|
|
157
|
-
}
|
|
158
|
-
if (
|
|
159
|
-
let
|
|
160
|
-
if (
|
|
161
|
-
return
|
|
162
|
-
}
|
|
163
|
-
if (
|
|
164
|
-
if (
|
|
165
|
-
if (
|
|
166
|
-
if (
|
|
167
|
-
if (
|
|
168
|
-
if (
|
|
169
|
-
}
|
|
170
|
-
if (
|
|
171
|
-
if (
|
|
139
|
+
}, n)}[]`;
|
|
140
|
+
if (n === "mysql" || n === "mariadb") return "JSON";
|
|
141
|
+
if (n === "mssql") return "NVARCHAR(MAX)";
|
|
142
|
+
if (e(n)) return "TEXT";
|
|
143
|
+
}
|
|
144
|
+
if (r === "vector") {
|
|
145
|
+
let r = t.vectorDim;
|
|
146
|
+
if (r === void 0) throw Error("vector column is missing its dimension — this is a framework bug.");
|
|
147
|
+
let i = t.vectorPrecision === "half";
|
|
148
|
+
if (n === "postgres") return i ? `HALFVEC(${r})` : `VECTOR(${r})`;
|
|
149
|
+
if (n === "mysql" || n === "mariadb") return `VECTOR(${r})`;
|
|
150
|
+
if (n === "mssql") return "VARBINARY(MAX)";
|
|
151
|
+
if (e(n)) return "BLOB";
|
|
152
|
+
}
|
|
153
|
+
if (r === "interval") {
|
|
154
|
+
if (n === "postgres") return "INTERVAL";
|
|
155
|
+
if (n === "mysql" || n === "mariadb" || n === "mssql") return "BIGINT";
|
|
156
|
+
if (e(n)) return "INTEGER";
|
|
157
|
+
}
|
|
158
|
+
if (r === "decimal") {
|
|
159
|
+
let r = t.precision, i = t.scale ?? 0;
|
|
160
|
+
if (r === void 0) throw Error("decimal column is missing its precision — this is a framework bug.");
|
|
161
|
+
return e(n) ? "NUMERIC" : n === "postgres" ? `NUMERIC(${r}, ${i})` : `DECIMAL(${r}, ${i})`;
|
|
162
|
+
}
|
|
163
|
+
if (r === "bigint") return e(n) ? "INTEGER" : "BIGINT";
|
|
164
|
+
if (r === "enum") {
|
|
165
|
+
if (n === "postgres") return t.enumName ?? "TEXT";
|
|
166
|
+
if (n === "mysql" || n === "mariadb") return `ENUM(${(t.enumValues ?? []).map((e) => `'${e.replace(/'/g, "''")}'`).join(", ")})`;
|
|
167
|
+
if (n === "mssql") return "NVARCHAR(255)";
|
|
168
|
+
if (e(n)) return "TEXT";
|
|
169
|
+
}
|
|
170
|
+
if (r === "text" && t.maxLength !== void 0 && t.generatedAs === void 0) return n === "mssql" ? `NVARCHAR(${t.maxLength})` : e(n) ? "TEXT" : `VARCHAR(${t.maxLength})`;
|
|
171
|
+
if (e(n)) switch (r) {
|
|
172
172
|
case "id": return "TEXT";
|
|
173
173
|
case "text": return "TEXT";
|
|
174
174
|
case "integer": return "INTEGER";
|
|
@@ -184,9 +184,9 @@ var _ = (e, t, n, r = 400) => {
|
|
|
184
184
|
case "array": return "TEXT";
|
|
185
185
|
case "interval": return "INTEGER";
|
|
186
186
|
}
|
|
187
|
-
if (
|
|
187
|
+
if (n === "mysql" || n === "mariadb") switch (r) {
|
|
188
188
|
case "id": return "VARCHAR(64)";
|
|
189
|
-
case "text": return
|
|
189
|
+
case "text": return t.hasDefault && t.defaultFactory === void 0 || t.oneOf && t.oneOf.length > 0 ? "VARCHAR(255)" : t.generatedAs ? "TEXT" : "LONGTEXT";
|
|
190
190
|
case "integer": return "INT";
|
|
191
191
|
case "real": return "DOUBLE";
|
|
192
192
|
case "boolean": return "TINYINT(1)";
|
|
@@ -200,9 +200,9 @@ var _ = (e, t, n, r = 400) => {
|
|
|
200
200
|
case "array": return "JSON";
|
|
201
201
|
case "interval": return "BIGINT";
|
|
202
202
|
}
|
|
203
|
-
if (
|
|
203
|
+
if (n === "mssql") switch (r) {
|
|
204
204
|
case "id": return "NVARCHAR(64)";
|
|
205
|
-
case "text": return
|
|
205
|
+
case "text": return t.hasDefault && t.defaultFactory === void 0 || t.oneOf && t.oneOf.length > 0 ? "NVARCHAR(450)" : "NVARCHAR(MAX)";
|
|
206
206
|
case "integer": return "INT";
|
|
207
207
|
case "real": return "FLOAT";
|
|
208
208
|
case "boolean": return "BIT";
|
|
@@ -216,7 +216,7 @@ var _ = (e, t, n, r = 400) => {
|
|
|
216
216
|
case "array": return "NVARCHAR(MAX)";
|
|
217
217
|
case "interval": return "BIGINT";
|
|
218
218
|
}
|
|
219
|
-
switch (
|
|
219
|
+
switch (r) {
|
|
220
220
|
case "id": return "TEXT";
|
|
221
221
|
case "text": return "TEXT";
|
|
222
222
|
case "integer": return "INTEGER";
|
|
@@ -228,26 +228,26 @@ var _ = (e, t, n, r = 400) => {
|
|
|
228
228
|
case "bytes": return "BYTEA";
|
|
229
229
|
case "reference": return "TEXT";
|
|
230
230
|
case "vector": return "VECTOR";
|
|
231
|
-
case "enum": return
|
|
231
|
+
case "enum": return t.enumName ?? "TEXT";
|
|
232
232
|
case "array": return "TEXT[]";
|
|
233
233
|
case "interval": return "INTERVAL";
|
|
234
234
|
}
|
|
235
|
-
},
|
|
236
|
-
if (!
|
|
237
|
-
let
|
|
238
|
-
return
|
|
239
|
-
},
|
|
240
|
-
if (n !== "postgres" || t.type !== "array" || t.arrayElement === void 0) return
|
|
241
|
-
let r =
|
|
235
|
+
}, be = (t, n) => {
|
|
236
|
+
if (!t.hasDefault || t.defaultFactory !== void 0) return null;
|
|
237
|
+
let r = t.defaultValue;
|
|
238
|
+
return r === "now" ? e(n) ? "DEFAULT CURRENT_TIMESTAMP" : n === "mssql" ? "DEFAULT SYSUTCDATETIME()" : n === "mysql" || n === "mariadb" ? "DEFAULT CURRENT_TIMESTAMP(6)" : "DEFAULT now()" : typeof r == "boolean" ? n === "postgres" ? `DEFAULT ${r}` : `DEFAULT ${+!!r}` : typeof r == "number" ? `DEFAULT ${r}` : typeof r == "string" ? `DEFAULT '${r.replace(/'/g, "''")}'` : Array.isArray(r) ? xe(r, t, n) : typeof r == "object" && r ? M(r, n) : null;
|
|
239
|
+
}, xe = (e, t, n) => {
|
|
240
|
+
if (n !== "postgres" || t.type !== "array" || t.arrayElement === void 0) return M(e, n);
|
|
241
|
+
let r = j({
|
|
242
242
|
...t,
|
|
243
243
|
type: t.arrayElement,
|
|
244
244
|
arrayElement: void 0
|
|
245
245
|
}, n);
|
|
246
246
|
return `DEFAULT '{${e.map((e) => typeof e == "number" || typeof e == "boolean" ? String(e) : `"${String(e).replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`).join(",").replace(/'/g, "''")}}'::${r}[]`;
|
|
247
|
-
},
|
|
247
|
+
}, M = (e, t) => {
|
|
248
248
|
let n = JSON.stringify(e).replace(/'/g, "''");
|
|
249
249
|
return t === "postgres" ? `DEFAULT '${n}'::jsonb` : t === "mysql" || t === "mariadb" ? `DEFAULT (CAST('${n}' AS JSON))` : `DEFAULT '${n}'`;
|
|
250
|
-
},
|
|
250
|
+
}, N = (e, t) => {
|
|
251
251
|
switch (t) {
|
|
252
252
|
case "postgres": return `"${e}" BIGSERIAL PRIMARY KEY`;
|
|
253
253
|
case "mysql":
|
|
@@ -256,7 +256,7 @@ var _ = (e, t, n, r = 400) => {
|
|
|
256
256
|
case "sqlite": return `"${e}" INTEGER PRIMARY KEY AUTOINCREMENT`;
|
|
257
257
|
case "turso": return `"${e}" INTEGER PRIMARY KEY`;
|
|
258
258
|
}
|
|
259
|
-
},
|
|
259
|
+
}, Se = (e, t) => {
|
|
260
260
|
if (e === "restrict" && t === "mssql") return "NO ACTION";
|
|
261
261
|
switch (e) {
|
|
262
262
|
case "cascade": return "CASCADE";
|
|
@@ -264,173 +264,222 @@ var _ = (e, t, n, r = 400) => {
|
|
|
264
264
|
case "setNull": return "SET NULL";
|
|
265
265
|
case "noAction": return "NO ACTION";
|
|
266
266
|
}
|
|
267
|
-
},
|
|
268
|
-
let i = e
|
|
267
|
+
}, Ce = (e, n, r) => r === null ? t(e, n) : `${t(r, n)}.${t(e, n)}`, we = 191, Te = (e, n, r) => {
|
|
268
|
+
let i = t(e, r);
|
|
269
269
|
if (r !== "mysql" && r !== "mariadb") return i;
|
|
270
|
-
let a = n[
|
|
270
|
+
let a = n[e];
|
|
271
271
|
if (!a) return i;
|
|
272
|
-
let o =
|
|
273
|
-
return o.endsWith("TEXT") || o.endsWith("BLOB") ? `${i}(${
|
|
274
|
-
},
|
|
272
|
+
let o = j(a, r).toUpperCase();
|
|
273
|
+
return o.endsWith("TEXT") || o.endsWith("BLOB") ? `${i}(${we})` : i;
|
|
274
|
+
}, Ee = (e, t) => {
|
|
275
275
|
if (!e.generatedAs) return null;
|
|
276
276
|
let { expr: n, stored: r } = e.generatedAs;
|
|
277
|
-
if (t === "postgres") return r ||
|
|
277
|
+
if (t === "postgres") return r || A("[voltro:migrate] generated column requested as VIRTUAL but postgres only supports STORED — promoting"), `GENERATED ALWAYS AS (${n}) STORED`;
|
|
278
278
|
if (t === "mysql" || t === "mariadb") return `GENERATED ALWAYS AS (${n}) ${r ? "STORED" : "VIRTUAL"}`;
|
|
279
279
|
if (t === "mssql") return `AS (${n})${r ? " PERSISTED" : ""}`;
|
|
280
280
|
if (t === "turso") throw Error("@voltro/sql-turso: generated columns (.generatedAs(...)) are not supported — Turso's MVCC mode (journal_mode=experimental_mvcc) rejects them. Drop the generated column, or use the 'sqlite' / 'postgres' dialect for this table.");
|
|
281
281
|
return `GENERATED ALWAYS AS (${n}) ${r ? "STORED" : "VIRTUAL"}`;
|
|
282
|
-
},
|
|
283
|
-
if (
|
|
284
|
-
let
|
|
285
|
-
|
|
286
|
-
let
|
|
287
|
-
if (
|
|
282
|
+
}, De = (n, r, i, a, o = !1, s = !1) => {
|
|
283
|
+
if (r.type === "id" && r.idScheme?.kind === "numeric") return N(n, i);
|
|
284
|
+
let c = i === "mssql" && r.type === "text" && !r.generatedAs && r.maxLength === void 0 && a?.has(n) ? "NVARCHAR(450)" : j(r, i), l = [t(n, i), c];
|
|
285
|
+
r.type === "id" && !s && l.push("PRIMARY KEY");
|
|
286
|
+
let u = Ee(r, i);
|
|
287
|
+
if (u) l.push(u);
|
|
288
288
|
else {
|
|
289
|
-
|
|
290
|
-
let e =
|
|
291
|
-
e &&
|
|
292
|
-
}
|
|
293
|
-
if (
|
|
294
|
-
let
|
|
295
|
-
|
|
296
|
-
let
|
|
297
|
-
|
|
298
|
-
let a =
|
|
299
|
-
a !== "NO ACTION" &&
|
|
300
|
-
}
|
|
301
|
-
if (
|
|
302
|
-
let
|
|
303
|
-
|
|
304
|
-
}
|
|
305
|
-
if (
|
|
306
|
-
if (
|
|
307
|
-
let
|
|
308
|
-
|
|
309
|
-
}
|
|
310
|
-
if (
|
|
311
|
-
let
|
|
312
|
-
|
|
313
|
-
}
|
|
314
|
-
return
|
|
315
|
-
},
|
|
289
|
+
r.nullable || l.push("NOT NULL"), r.unique && r.type !== "id" && l.push("UNIQUE");
|
|
290
|
+
let e = be(r, i);
|
|
291
|
+
e && l.push(e);
|
|
292
|
+
}
|
|
293
|
+
if (r.type === "reference" && r.references && !o) {
|
|
294
|
+
let e = r.references();
|
|
295
|
+
l.push(`REFERENCES ${t(e.tableName, i)} (${t("id", i)})`);
|
|
296
|
+
let n = Se(r.onDelete ?? "restrict", i);
|
|
297
|
+
l.push(`ON DELETE ${n}`);
|
|
298
|
+
let a = Se(r.refOnUpdate ?? "noAction", i);
|
|
299
|
+
a !== "NO ACTION" && l.push(`ON UPDATE ${a}`);
|
|
300
|
+
}
|
|
301
|
+
if (r.oneOf && r.oneOf.length > 0) {
|
|
302
|
+
let e = r.oneOf.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ");
|
|
303
|
+
l.push(`CHECK (${t(n, i)} IN (${e}))`);
|
|
304
|
+
}
|
|
305
|
+
if (r.checks && r.checks.length > 0) for (let e of r.checks) l.push(`CHECK (${e})`);
|
|
306
|
+
if (r.type === "enum" && r.enumValues && r.enumValues.length > 0 && (i === "mssql" || e(i))) {
|
|
307
|
+
let e = r.enumValues.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ");
|
|
308
|
+
l.push(`CHECK (${t(n, i)} IN (${e}))`);
|
|
309
|
+
}
|
|
310
|
+
if (r.type === "json" || r.type === "array") {
|
|
311
|
+
let r = t(n, i);
|
|
312
|
+
e(i) ? l.push(`CHECK (${r} IS NULL OR json_valid(${r}))`) : i === "mssql" && l.push(`CHECK (${r} IS NULL OR ISJSON(${r}) = 1)`);
|
|
313
|
+
}
|
|
314
|
+
return l.join(" ");
|
|
315
|
+
}, Oe = (e, n, r = null, i) => {
|
|
316
316
|
let a = /* @__PURE__ */ new Set();
|
|
317
|
-
for (let
|
|
318
|
-
for (let
|
|
319
|
-
let o =
|
|
320
|
-
let i = r.fields.map((
|
|
321
|
-
return ` CONSTRAINT ${
|
|
322
|
-
}), u = (
|
|
323
|
-
...s ? [` PRIMARY KEY (${o.map((
|
|
317
|
+
for (let t of e.appliedIndexes ?? []) for (let e of t.fields) typeof e == "string" && a.add(e);
|
|
318
|
+
for (let t of e.appliedUniques ?? []) for (let e of t.fields) typeof e == "string" && a.add(e);
|
|
319
|
+
let o = e.appliedPrimaryKey ?? [], s = o.length > 0, c = Object.entries(e.fields).map(([e, t]) => ` ${De(e, t, n, a, i?.has(e) ?? !1, s)}`).join(",\n"), l = (e.appliedUniques ?? []).map((r) => {
|
|
320
|
+
let i = r.fields.map((t) => Te(t, e.fields, n)).join(", ");
|
|
321
|
+
return ` CONSTRAINT ${t(r.name, n)} UNIQUE (${i})`;
|
|
322
|
+
}), u = (e.appliedChecks ?? []).map((e) => ` CONSTRAINT ${t(e.name, n)} CHECK (${e.expr})`), d = [
|
|
323
|
+
...s ? [` PRIMARY KEY (${o.map((t) => Te(t, e.fields, n)).join(", ")})`] : [],
|
|
324
324
|
...l,
|
|
325
325
|
...u
|
|
326
|
-
], f = d.length > 0 ? `${c},\n${d.join(",\n")}` : c, p =
|
|
326
|
+
], f = d.length > 0 ? `${c},\n${d.join(",\n")}` : c, p = Ce(e.tableName, n, r);
|
|
327
327
|
if (n === "mssql") {
|
|
328
|
-
let
|
|
329
|
-
return `IF OBJECT_ID('${
|
|
328
|
+
let t = r === null ? e.tableName : `${r}.${e.tableName}`, n = `CREATE TABLE ${p} (\n${f}\n)`;
|
|
329
|
+
return `IF OBJECT_ID('${t.replace(/'/g, "''")}', 'U') IS NULL EXEC(N'${n.replace(/'/g, "''")}');`;
|
|
330
330
|
}
|
|
331
331
|
return [
|
|
332
332
|
`CREATE TABLE IF NOT EXISTS ${p} (`,
|
|
333
333
|
f,
|
|
334
334
|
");"
|
|
335
335
|
].join("\n");
|
|
336
|
-
},
|
|
337
|
-
let i = `${
|
|
338
|
-
return c.push(`CREATE VIRTUAL TABLE IF NOT EXISTS ${a(i)} USING fts5(${o}, row_id UNINDEXED, tokenize='porter');`), c.push(`CREATE TRIGGER IF NOT EXISTS ${a(`${i}_ai`)} AFTER INSERT ON ${a(
|
|
339
|
-
},
|
|
340
|
-
let
|
|
341
|
-
let
|
|
336
|
+
}, ke = (e, n, r) => {
|
|
337
|
+
let i = `${e}_${n.name}_fts`, a = (e) => t(e, r), o = n.columns.map(a).join(", "), s = n.columns.map((e) => `new.${a(e)}`).join(", "), c = [];
|
|
338
|
+
return c.push(`CREATE VIRTUAL TABLE IF NOT EXISTS ${a(i)} USING fts5(${o}, row_id UNINDEXED, tokenize='porter');`), c.push(`CREATE TRIGGER IF NOT EXISTS ${a(`${i}_ai`)} AFTER INSERT ON ${a(e)} BEGIN\n INSERT INTO ${a(i)} (${o}, row_id) VALUES (${s}, new.${a("id")});\nEND;`), c.push(`CREATE TRIGGER IF NOT EXISTS ${a(`${i}_ad`)} AFTER DELETE ON ${a(e)} BEGIN\n DELETE FROM ${a(i)} WHERE row_id = old.${a("id")};\nEND;`), c.push(`CREATE TRIGGER IF NOT EXISTS ${a(`${i}_au`)} AFTER UPDATE ON ${a(e)} BEGIN\n DELETE FROM ${a(i)} WHERE row_id = old.${a("id")};\n INSERT INTO ${a(i)} (${o}, row_id) VALUES (${s}, new.${a("id")});\nEND;`), c;
|
|
339
|
+
}, Ae = (e, n, r = null) => {
|
|
340
|
+
let i = [], a = /* @__PURE__ */ new Set(), o = (e) => e.join("\0"), s = (e, t, r, i) => {
|
|
341
|
+
let a = {
|
|
342
342
|
using: "",
|
|
343
343
|
withSuffix: "",
|
|
344
344
|
skip: !1
|
|
345
345
|
};
|
|
346
|
-
if (
|
|
347
|
-
if (
|
|
346
|
+
if (r === void 0 || r === "btree") return a;
|
|
347
|
+
if (r === "gist") return n === "postgres" ? {
|
|
348
348
|
using: " USING GIST",
|
|
349
349
|
withSuffix: "",
|
|
350
350
|
skip: !1
|
|
351
|
-
} : (
|
|
352
|
-
if (
|
|
351
|
+
} : (A(`[voltro:migrate] index '${e}' on '${t}': ${n} doesn't support GiST — falling back to btree. Spatial queries on this column will fall back to sequential scan.`), a);
|
|
352
|
+
if (r === "gin") return n === "postgres" ? {
|
|
353
353
|
using: " USING GIN",
|
|
354
354
|
withSuffix: "",
|
|
355
355
|
skip: !1
|
|
356
|
-
} :
|
|
356
|
+
} : n === "mysql" || n === "mariadb" ? (A(`[voltro:migrate] gin index '${e}' on '${t}': ${n} has no GIN access method — skipping (a btree on a JSON/array column can't serve containment/membership queries). Add a generated column for the specific path you query, then index that with .expressionIndex(...).`), {
|
|
357
357
|
using: "",
|
|
358
358
|
withSuffix: "",
|
|
359
359
|
skip: !0
|
|
360
|
-
}) : (
|
|
361
|
-
if (
|
|
360
|
+
}) : (A(`[voltro:migrate] gin index '${e}' on '${t}': ${n} has no GIN access method — falling back to btree. Containment/membership queries won't use this index efficiently.`), a);
|
|
361
|
+
if (r === "brin") return n === "postgres" ? {
|
|
362
362
|
using: " USING BRIN",
|
|
363
363
|
withSuffix: "",
|
|
364
364
|
skip: !1
|
|
365
|
-
} : (
|
|
366
|
-
if (
|
|
367
|
-
if (
|
|
368
|
-
let e =
|
|
365
|
+
} : (A(`[voltro:migrate] brin index '${e}' on '${t}': ${n} has no BRIN access method — falling back to btree (larger on disk, but the range scan still works).`), a);
|
|
366
|
+
if (r === "hnsw") {
|
|
367
|
+
if (n === "postgres") {
|
|
368
|
+
let e = i?.hnsw?.m ?? 16, t = i?.hnsw?.efConstruction ?? 64, n = i?.hnsw?.opclass ?? u(i?.hnsw?.distance);
|
|
369
369
|
return {
|
|
370
370
|
using: " USING hnsw",
|
|
371
|
-
withSuffix: ` WITH (m = ${e}, ef_construction = ${
|
|
372
|
-
opclass:
|
|
371
|
+
withSuffix: ` WITH (m = ${e}, ef_construction = ${t})`,
|
|
372
|
+
opclass: n,
|
|
373
373
|
skip: !1
|
|
374
374
|
};
|
|
375
375
|
}
|
|
376
|
-
return
|
|
376
|
+
return A(`[voltro:migrate] hnsw index '${e}' on '${t}': ${n} has no pgvector / HNSW access method — skipping. Nearest-neighbour queries on this column fall back to a sequential scan.`), {
|
|
377
377
|
using: "",
|
|
378
378
|
withSuffix: "",
|
|
379
379
|
skip: !0
|
|
380
380
|
};
|
|
381
381
|
}
|
|
382
|
-
return
|
|
383
|
-
},
|
|
384
|
-
let
|
|
385
|
-
if (typeof
|
|
386
|
-
if ("jsonPath" in
|
|
387
|
-
let { column:
|
|
388
|
-
return `(${
|
|
382
|
+
return a;
|
|
383
|
+
}, c = (t, r) => {
|
|
384
|
+
let i = t.map((t) => {
|
|
385
|
+
if (typeof t == "string") return Te(t, e.fields, n);
|
|
386
|
+
if ("jsonPath" in t) {
|
|
387
|
+
let { column: e, path: r, numeric: i } = t.jsonPath;
|
|
388
|
+
return `(${f(e, r, n, { numeric: i })})`;
|
|
389
389
|
}
|
|
390
|
-
return `(${
|
|
390
|
+
return `(${t.expr})`;
|
|
391
391
|
});
|
|
392
|
-
return
|
|
393
|
-
},
|
|
394
|
-
let f =
|
|
392
|
+
return r !== void 0 && i.length === 1 ? `${i[0]} ${r}` : i.join(", ");
|
|
393
|
+
}, l = (e, i, a, o, l, u = "", d = !1) => {
|
|
394
|
+
let f = s(e, i, o, l);
|
|
395
395
|
if (f.skip) return "";
|
|
396
|
-
let p =
|
|
397
|
-
return
|
|
396
|
+
let p = c(a, f.opclass), m = `${f.withSuffix}${u}`, h = Ce(i, n, r), g = d ? "UNIQUE " : "";
|
|
397
|
+
return n === "mssql" ? `IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = '${e}') CREATE ${g}INDEX ${t(e, n)} ON ${h}${f.using} (${p})${m};` : n === "mysql" ? `CREATE ${g}INDEX ${t(e, n)} ON ${h}${f.using} (${p})${m};` : `CREATE ${g}INDEX IF NOT EXISTS ${t(e, n)} ON ${h}${f.using} (${p})${m};`;
|
|
398
398
|
};
|
|
399
|
-
for (let
|
|
400
|
-
let
|
|
401
|
-
let
|
|
402
|
-
return `setweight(to_tsvector('${
|
|
399
|
+
for (let r of e.appliedFullText ?? []) if (n === "postgres") {
|
|
400
|
+
let a = r.config ?? "english", o = `${r.name}_tsv`, s = r.columns.map((e) => {
|
|
401
|
+
let i = r.weights?.[e] ?? "A";
|
|
402
|
+
return `setweight(to_tsvector('${a}', coalesce(${t(e, n)}, '')), '${i}')`;
|
|
403
403
|
}).join(" || ");
|
|
404
|
-
|
|
405
|
-
} else if (
|
|
406
|
-
let
|
|
407
|
-
|
|
408
|
-
} else if (
|
|
409
|
-
else
|
|
410
|
-
for (let
|
|
411
|
-
if ((
|
|
412
|
-
|
|
404
|
+
i.push(`ALTER TABLE ${t(e.tableName, n)} ADD COLUMN IF NOT EXISTS ${t(o, n)} tsvector GENERATED ALWAYS AS (${s}) STORED;`), i.push(`CREATE INDEX IF NOT EXISTS ${t(r.name, n)} ON ${t(e.tableName, n)} USING GIN (${t(o, n)});`);
|
|
405
|
+
} else if (n === "mysql" || n === "mariadb") {
|
|
406
|
+
let a = r.columns.map((e) => t(e, n)).join(", ");
|
|
407
|
+
i.push(`CREATE FULLTEXT INDEX ${t(r.name, n)} ON ${t(e.tableName, n)} (${a});`);
|
|
408
|
+
} else if (n === "turso") throw Error(`@voltro/sql-turso: full-text index '${r.name}' on '${e.tableName}' is not supported — Turso's MVCC mode has no FTS5 virtual tables. Drop .fullTextIndex(), or use the 'sqlite' / 'postgres' dialect for this table.`);
|
|
409
|
+
else n === "sqlite" ? i.push(...ke(e.tableName, r, n)) : A(`[voltro:migrate] full-text index '${r.name}' on '${e.tableName}': ${n} uses a ranked LIKE search at query time (no native FTS catalog required).`);
|
|
410
|
+
for (let t of e.appliedIndexes) {
|
|
411
|
+
if ((n === "mariadb" || n === "mssql") && t.fields.some((e) => typeof e != "string" && "jsonPath" in e)) {
|
|
412
|
+
A(`[voltro:migrate] json-path index '${t.name}' on '${e.tableName}': ${n} cannot index a JSON expression directly — skipping. For an indexed JSON path on ${n}, add a generated column for the path (text().generatedAs(...) / .stored()) and .index([...]) it, then filter on that column instead of jsonField(...).`);
|
|
413
413
|
continue;
|
|
414
414
|
}
|
|
415
|
-
if (
|
|
416
|
-
let
|
|
417
|
-
|
|
418
|
-
let
|
|
419
|
-
|
|
420
|
-
}
|
|
421
|
-
for (let [
|
|
422
|
-
if (
|
|
423
|
-
let r = `${
|
|
424
|
-
|
|
425
|
-
}
|
|
426
|
-
return
|
|
427
|
-
},
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
415
|
+
if (t.unique && t.where !== void 0 && t.where.length > 0 && (n === "mysql" || n === "mariadb")) throw Error(`@voltro/migrate: uniqueActive index '${t.name}' on '${e.tableName}' cannot be expressed on ${n} — it has no partial (WHERE) index support, and a full UNIQUE index would forbid re-creating a soft-deleted row. Use a generated STORED column (text().generatedAs("CASE WHEN \`deletedAt\` IS NULL THEN <key> ELSE NULL END", { stored: true })) + .unique([...]) on ${n}, or move this table to postgres/sqlite/mssql.`);
|
|
416
|
+
let r = "";
|
|
417
|
+
t.where !== void 0 && t.where.length > 0 && (n === "mysql" || n === "mariadb" ? A(`[voltro:migrate] partial index '${t.name}' on '${e.tableName}': ${n} does not support WHERE clauses on indexes — emitting a full index`) : r = ` WHERE ${t.where}`);
|
|
418
|
+
let s = l(t.name, e.tableName, t.fields, t.kind, t.kindOptions, r, t.unique === !0);
|
|
419
|
+
s !== "" && i.push(s), t.fields.every((e) => typeof e == "string") && a.add(o(t.fields));
|
|
420
|
+
}
|
|
421
|
+
for (let [t, n] of Object.entries(e.fields)) {
|
|
422
|
+
if (n.type !== "reference" || n.fkAutoIndex === !1 || a.has(o([t]))) continue;
|
|
423
|
+
let r = `${e.tableName}_${t}_idx`;
|
|
424
|
+
i.push(l(r, e.tableName, [t])), a.add(o([t]));
|
|
425
|
+
}
|
|
426
|
+
return i.join("\n");
|
|
427
|
+
}, je = (e, t, n = O) => {
|
|
428
|
+
if (t !== "postgres") return [];
|
|
429
|
+
let r = e.filter((e) => e.isReactive !== !1);
|
|
430
|
+
return r.length === 0 ? [] : [Me(n), ...r.map((e) => Pe(e, n))];
|
|
431
|
+
}, Me = (e) => `
|
|
432
|
+
CREATE OR REPLACE FUNCTION ${ve(e)}() RETURNS trigger AS $$
|
|
433
|
+
DECLARE
|
|
434
|
+
payload TEXT;
|
|
435
|
+
BEGIN
|
|
436
|
+
payload := json_build_object(
|
|
437
|
+
'table', TG_TABLE_NAME,
|
|
438
|
+
-- The schema the write landed in. Under namespace isolation this is
|
|
439
|
+
-- the tenant schema (tenant_<id>); the CDC consumer uses it so a
|
|
440
|
+
-- write in tenant A namespace does not spuriously wake tenant B
|
|
441
|
+
-- subscriptions on the same-named table. public/absent = shared.
|
|
442
|
+
'schema', TG_TABLE_SCHEMA,
|
|
443
|
+
'op', lower(TG_OP),
|
|
444
|
+
'old', CASE WHEN TG_OP = 'INSERT' THEN NULL ELSE row_to_json(OLD) END,
|
|
445
|
+
'new', CASE WHEN TG_OP = 'DELETE' THEN NULL ELSE row_to_json(NEW) END
|
|
446
|
+
)::text;
|
|
447
|
+
-- pg_notify hard-caps the payload at 8000 bytes; a wide reactive row
|
|
448
|
+
-- (big text / json column) would otherwise abort the write itself.
|
|
449
|
+
-- Fall back to a body-less notice — the CDC consumer treats absent
|
|
450
|
+
-- old/new as "all columns changed" and re-queries, so correctness is
|
|
451
|
+
-- preserved; only the delta-diff fast path is skipped for that write.
|
|
452
|
+
IF octet_length(payload) >= 7999 THEN
|
|
453
|
+
payload := json_build_object(
|
|
454
|
+
'table', TG_TABLE_NAME,
|
|
455
|
+
'schema', TG_TABLE_SCHEMA,
|
|
456
|
+
'op', lower(TG_OP),
|
|
457
|
+
'old', NULL,
|
|
458
|
+
'new', NULL,
|
|
459
|
+
'oversized', true
|
|
460
|
+
)::text;
|
|
461
|
+
END IF;
|
|
462
|
+
PERFORM pg_notify('${e}', payload);
|
|
463
|
+
RETURN COALESCE(NEW, OLD);
|
|
464
|
+
END;
|
|
465
|
+
$$ LANGUAGE plpgsql;
|
|
466
|
+
`.trim(), Ne = (e, t) => {
|
|
467
|
+
let n = _e(e.tableName, t);
|
|
468
|
+
return [
|
|
469
|
+
`ALTER TABLE "${e.tableName}" REPLICA IDENTITY FULL`,
|
|
470
|
+
`DROP TRIGGER IF EXISTS ${n} ON "${e.tableName}"`,
|
|
471
|
+
`CREATE TRIGGER ${n}\nAFTER INSERT OR UPDATE OR DELETE ON "${e.tableName}"\nFOR EACH ROW EXECUTE FUNCTION ${ve(t)}()`
|
|
472
|
+
];
|
|
473
|
+
}, Pe = (e, t) => Ne(e, t).map((e) => `${e};`).join("\n"), Fe = (e) => {
|
|
474
|
+
if (e.dialect !== "postgres") return [];
|
|
475
|
+
let t = e.channel ?? "framework_changes", n = new Map(e.tables.map((e) => [e.tableName, e])), r = [], i = e.missing.filter((e) => n.has(e));
|
|
476
|
+
if (i.length > 0) {
|
|
477
|
+
r.push(Me(t));
|
|
478
|
+
for (let e of i) r.push(...Ne(n.get(e), t));
|
|
479
|
+
}
|
|
480
|
+
for (let n of e.stale) r.push(`DROP TRIGGER IF EXISTS ${_e(n, t)} ON "${n}"`), r.push(`ALTER TABLE "${n}" REPLICA IDENTITY DEFAULT`);
|
|
481
|
+
return r;
|
|
482
|
+
}, Ie = (e) => e.some((e) => Object.values(e.fields).some((e) => e.type === "vector")), Le = (e) => e.some((e) => Object.values(e.fields).some((e) => e.spatial !== void 0)), Re = (e) => {
|
|
434
483
|
let t = new Map(e.map((e) => [e.tableName, e])), n = ne(e.map((e) => e.tableName), (e) => {
|
|
435
484
|
let n = t.get(e);
|
|
436
485
|
return n ? Object.entries(n.fields).flatMap(([e, n]) => {
|
|
@@ -442,7 +491,7 @@ FOR EACH ROW EXECUTE FUNCTION framework_notify_change();
|
|
|
442
491
|
}] : [];
|
|
443
492
|
}) : [];
|
|
444
493
|
}), r = [];
|
|
445
|
-
for (let t of e) for (let [e, i] of Object.entries(t.fields)) i.type !== "reference" || !i.references || n.has(
|
|
494
|
+
for (let t of e) for (let [e, i] of Object.entries(t.fields)) i.type !== "reference" || !i.references || n.has(w(t.tableName, e)) && r.push({
|
|
446
495
|
table: t.tableName,
|
|
447
496
|
column: e,
|
|
448
497
|
targetTable: i.references().tableName,
|
|
@@ -453,7 +502,7 @@ FOR EACH ROW EXECUTE FUNCTION framework_notify_change();
|
|
|
453
502
|
if (!(i.has(e.tableName) || a.has(e.tableName))) {
|
|
454
503
|
a.add(e.tableName);
|
|
455
504
|
for (let [r, i] of Object.entries(e.fields)) {
|
|
456
|
-
if (i.type !== "reference" || !i.references || n.has(
|
|
505
|
+
if (i.type !== "reference" || !i.references || n.has(w(e.tableName, r))) continue;
|
|
457
506
|
let a = i.references();
|
|
458
507
|
if (a.tableName === e.tableName) continue;
|
|
459
508
|
let o = t.get(a.tableName);
|
|
@@ -467,30 +516,30 @@ FOR EACH ROW EXECUTE FUNCTION framework_notify_change();
|
|
|
467
516
|
sorted: o,
|
|
468
517
|
cyclic: r
|
|
469
518
|
};
|
|
470
|
-
},
|
|
471
|
-
let i = (
|
|
519
|
+
}, ze = (e, n, r = null) => {
|
|
520
|
+
let i = (e) => r ? `${t(r, n)}.${t(e, n)}` : t(e, n), a = `${e.table}_${e.column}_fkey`, o = Se(e.onDelete, n), s = Se(e.onUpdate, n), c = ` ON DELETE ${o}${s === "NO ACTION" ? "" : ` ON UPDATE ${s}`}`, l = (r) => `ADD CONSTRAINT ${t(a, n)} FOREIGN KEY ${r ? "IF NOT EXISTS " : ""}(${t(e.column, n)}) REFERENCES ${i(e.targetTable)} (${t("id", n)})${c}`;
|
|
472
521
|
switch (n) {
|
|
473
522
|
case "postgres": return [
|
|
474
523
|
"DO $$ BEGIN",
|
|
475
524
|
" IF NOT EXISTS (SELECT 1 FROM pg_constraint",
|
|
476
|
-
` WHERE conname = '${a}' AND conrelid = '${r ? `${r}.` : ""}${
|
|
477
|
-
` ALTER TABLE ${i(
|
|
525
|
+
` WHERE conname = '${a}' AND conrelid = '${r ? `${r}.` : ""}${e.table}'::regclass) THEN`,
|
|
526
|
+
` ALTER TABLE ${i(e.table)} ${l(!1)};`,
|
|
478
527
|
" END IF;",
|
|
479
528
|
"END $$;"
|
|
480
529
|
].join("\n");
|
|
481
|
-
case "mssql": return `IF NOT EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = '${a}') ALTER TABLE ${i(
|
|
482
|
-
case "mariadb": return `ALTER TABLE ${i(
|
|
483
|
-
default: return `ALTER TABLE ${i(
|
|
530
|
+
case "mssql": return `IF NOT EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = '${a}') ALTER TABLE ${i(e.table)} ${l(!1)};`;
|
|
531
|
+
case "mariadb": return `ALTER TABLE ${i(e.table)} ${l(!0)};`;
|
|
532
|
+
default: return `ALTER TABLE ${i(e.table)} ${l(!1)};`;
|
|
484
533
|
}
|
|
485
|
-
},
|
|
486
|
-
let
|
|
487
|
-
if (
|
|
488
|
-
for (let
|
|
489
|
-
let
|
|
490
|
-
|
|
534
|
+
}, Be = (t, n) => {
|
|
535
|
+
let r = /* @__PURE__ */ new Map();
|
|
536
|
+
if (e(n)) return r;
|
|
537
|
+
for (let e of t) {
|
|
538
|
+
let t = r.get(e.table) ?? /* @__PURE__ */ new Set();
|
|
539
|
+
t.add(e.column), r.set(e.table, t);
|
|
491
540
|
}
|
|
492
|
-
return
|
|
493
|
-
},
|
|
541
|
+
return r;
|
|
542
|
+
}, Ve = (e, t) => {
|
|
494
543
|
if (t !== "postgres") return "";
|
|
495
544
|
let n = /* @__PURE__ */ new Map();
|
|
496
545
|
for (let t of e) for (let e of Object.values(t.fields)) {
|
|
@@ -506,7 +555,7 @@ FOR EACH ROW EXECUTE FUNCTION framework_notify_change();
|
|
|
506
555
|
r.push(`DO $$ BEGIN\n IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = '${e.replace(/'/g, "''")}') THEN\n CREATE TYPE "${e}" AS ENUM (${n});\n END IF;\nEND $$;`);
|
|
507
556
|
}
|
|
508
557
|
return r.join("\n\n");
|
|
509
|
-
},
|
|
558
|
+
}, He = (e, t) => {
|
|
510
559
|
if (t !== "postgres") return "";
|
|
511
560
|
let n = /* @__PURE__ */ new Set();
|
|
512
561
|
for (let t of e) for (let e of Object.values(t.fields)) e.type === "enum" && e.enumName && n.add(e.enumName);
|
|
@@ -520,80 +569,71 @@ BEGIN
|
|
|
520
569
|
SELECT oid INTO type_oid FROM pg_type WHERE typname = '${i}';\n IF type_oid IS NOT NULL\n AND EXISTS (SELECT 1 FROM pg_enum WHERE enumtypid = type_oid AND enumlabel = '${a}')\n AND NOT EXISTS (SELECT 1 FROM pg_enum WHERE enumtypid = type_oid AND enumlabel = '${o}') THEN\n IF current_setting('server_version_num')::int >= 140000 THEN\n EXECUTE format('ALTER TYPE %I RENAME VALUE %L TO %L', '${i}', '${a}', '${o}');\n ELSE\n UPDATE pg_enum SET enumlabel = '${o}' WHERE enumtypid = type_oid AND enumlabel = '${a}';\n END IF;\n END IF;\nEND $$;`);
|
|
521
570
|
}
|
|
522
571
|
return r.join("\n\n");
|
|
523
|
-
},
|
|
524
|
-
let t = [],
|
|
525
|
-
for (let i of e)
|
|
572
|
+
}, Ue = (e) => {
|
|
573
|
+
let t = [], n = [];
|
|
574
|
+
for (let i of e) r(i) ? n.push(i) : t.push(i);
|
|
526
575
|
return {
|
|
527
576
|
tables: t,
|
|
528
|
-
views:
|
|
577
|
+
views: n
|
|
529
578
|
};
|
|
530
|
-
},
|
|
531
|
-
let { tables:
|
|
532
|
-
|
|
533
|
-
let
|
|
534
|
-
|
|
535
|
-
let
|
|
536
|
-
|
|
537
|
-
for (let e of
|
|
538
|
-
if (!
|
|
539
|
-
for (let e of
|
|
540
|
-
let
|
|
541
|
-
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
for (let e of i) c.push(r(e, t));
|
|
549
|
-
return c.join("\n\n") + "\n";
|
|
550
|
-
}, ze = (t, n, r) => {
|
|
551
|
-
let i = e(t, n);
|
|
579
|
+
}, We = (t, n, r = O) => {
|
|
580
|
+
let { tables: a, views: o } = Ue(t), { sorted: s, cyclic: c } = Re(a.map((e) => te(e, n))), l = Be(c, n), u = [];
|
|
581
|
+
n === "postgres" && Ie(s) && u.push("CREATE EXTENSION IF NOT EXISTS vector;"), n === "postgres" && Le(s) && u.push("CREATE EXTENSION IF NOT EXISTS postgis;");
|
|
582
|
+
let d = He(s, n);
|
|
583
|
+
d && u.push(d);
|
|
584
|
+
let f = Ve(s, n);
|
|
585
|
+
f && u.push(f);
|
|
586
|
+
for (let e of s) u.push(Oe(e, n, null, l.get(e.tableName)));
|
|
587
|
+
if (!e(n)) for (let e of c) u.push(ze(e, n));
|
|
588
|
+
for (let e of s) {
|
|
589
|
+
let t = Ae(e, n);
|
|
590
|
+
t && u.push(t);
|
|
591
|
+
}
|
|
592
|
+
u.push(...je(s, n, r));
|
|
593
|
+
for (let e of o) u.push(i(e, n));
|
|
594
|
+
return u.join("\n\n") + "\n";
|
|
595
|
+
}, Ge = (e, n, r) => {
|
|
596
|
+
let i = t(e, n);
|
|
552
597
|
switch (n) {
|
|
553
598
|
case "postgres": return `CREATE SCHEMA IF NOT EXISTS ${i};`;
|
|
554
599
|
case "mysql":
|
|
555
600
|
case "mariadb": return `CREATE DATABASE IF NOT EXISTS ${i};`;
|
|
556
|
-
case "mssql": return `IF SCHEMA_ID('${
|
|
601
|
+
case "mssql": return `IF SCHEMA_ID('${e.replace(/'/g, "''")}') IS NULL EXEC(N'CREATE SCHEMA ${i.replace(/'/g, "''")}');`;
|
|
557
602
|
case "turso":
|
|
558
|
-
case "sqlite": return `ATTACH DATABASE '${(r ?? `${
|
|
559
|
-
}
|
|
560
|
-
},
|
|
561
|
-
let
|
|
562
|
-
if (
|
|
563
|
-
if (
|
|
564
|
-
let { sorted:
|
|
565
|
-
for (let e of
|
|
566
|
-
if (!
|
|
567
|
-
for (let e of
|
|
568
|
-
let t =
|
|
603
|
+
case "sqlite": return `ATTACH DATABASE '${(r ?? `${e}.db`).replace(/'/g, "''")}' AS ${i};`;
|
|
604
|
+
}
|
|
605
|
+
}, Ke = (n, r, i, a) => {
|
|
606
|
+
let o = Ge(i, r, a), s = t(i, r);
|
|
607
|
+
if (r === "postgres") return `${o}\n\nSET search_path TO ${s};\n\n${We(n, r)}`;
|
|
608
|
+
if (r === "mysql" || r === "mariadb") return `${o}\n\nUSE ${s};\n\n${We(n, r)}`;
|
|
609
|
+
let { sorted: c, cyclic: l } = Re(n), u = Be(l, r), d = [o];
|
|
610
|
+
for (let e of c) d.push(Oe(e, r, i, u.get(e.tableName)));
|
|
611
|
+
if (!e(r)) for (let e of l) d.push(ze(e, r, i));
|
|
612
|
+
for (let e of c) {
|
|
613
|
+
let t = Ae(e, r, i);
|
|
569
614
|
t && d.push(t);
|
|
570
615
|
}
|
|
571
616
|
return d.join("\n\n") + "\n";
|
|
572
|
-
},
|
|
573
|
-
let { sorted:
|
|
574
|
-
|
|
575
|
-
let
|
|
576
|
-
|
|
577
|
-
for (let e of
|
|
578
|
-
for (let e of
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
return a.join("\n\n") + "\n";
|
|
585
|
-
}, He = (e, t = "postgres") => d.gen(function* () {
|
|
586
|
-
let n = yield* p.SqlClient;
|
|
587
|
-
yield* _e(n, Ge(n, A(e, t), t)).pipe(d.orDie);
|
|
588
|
-
}), Ue = (e, t) => {
|
|
617
|
+
}, qe = (e, t, n = O) => {
|
|
618
|
+
let { sorted: r, cyclic: i } = Re(e), a = Be(i, t), o = [], s = He(r, t);
|
|
619
|
+
s && o.push(s);
|
|
620
|
+
let c = Ve(r, t);
|
|
621
|
+
c && o.push(c);
|
|
622
|
+
for (let e of r) o.push(Oe(e, t, null, a.get(e.tableName)));
|
|
623
|
+
for (let e of i) o.push(ze(e, t));
|
|
624
|
+
return o.push(...je(r, t, n)), o.join("\n\n") + "\n";
|
|
625
|
+
}, Je = (e, t = "postgres", n = O) => p.gen(function* () {
|
|
626
|
+
let r = yield* h.SqlClient;
|
|
627
|
+
yield* D(r, Ze(r, We(e, t, n), t)).pipe(p.orDie);
|
|
628
|
+
}), Ye = (e, t) => {
|
|
589
629
|
let n = t.cause ?? {};
|
|
590
630
|
return (n.errno === 1061 || n.code === "ER_DUP_KEYNAME") && /^\s*CREATE\s+(UNIQUE\s+|FULLTEXT\s+)?INDEX\b/i.test(e);
|
|
591
|
-
},
|
|
592
|
-
let t = yield*
|
|
593
|
-
for (let n of
|
|
594
|
-
|
|
595
|
-
})),
|
|
596
|
-
}),
|
|
631
|
+
}, Xe = (e) => p.gen(function* () {
|
|
632
|
+
let t = yield* h.SqlClient;
|
|
633
|
+
for (let n of Qe(e)) process.env.VOLTRO_MIGRATE_DEBUG === "1" && ye.debug(`EXEC: ${n.replace(/\s+/g, " ").slice(0, 180)}`), yield* t.unsafe(n).pipe(p.catchIf((e) => Ye(n, e), () => p.void), p.tapError((e) => p.sync(() => {
|
|
634
|
+
ye.error(b("migrate: statement failed", n, e, 200));
|
|
635
|
+
})), p.orDieWith((e) => Error(b("migrate: statement failed", n, e, 400).trim())));
|
|
636
|
+
}), Ze = (e, t, n) => (n === "turso" ? Xe(t) : e.withTransaction(Xe(t))).pipe(p.orDie), Qe = (e) => {
|
|
597
637
|
let t = [], n = "", r = !1;
|
|
598
638
|
for (let i = 0; i < e.length; i++) {
|
|
599
639
|
if (e.slice(i, i + 2) === "$$") {
|
|
@@ -608,40 +648,40 @@ BEGIN
|
|
|
608
648
|
}
|
|
609
649
|
let i = n.trim();
|
|
610
650
|
return i.length > 0 && t.push(i), t;
|
|
611
|
-
},
|
|
612
|
-
await
|
|
613
|
-
},
|
|
614
|
-
let r =
|
|
615
|
-
yield*
|
|
616
|
-
}),
|
|
617
|
-
await
|
|
618
|
-
},
|
|
619
|
-
await
|
|
620
|
-
let t =
|
|
621
|
-
yield*
|
|
622
|
-
}).pipe(
|
|
623
|
-
},
|
|
651
|
+
}, $e = async (e, t, n = "postgres") => {
|
|
652
|
+
await p.runPromise(Je(e, n).pipe(p.provide(t)));
|
|
653
|
+
}, et = (e, t, n = "postgres") => p.gen(function* () {
|
|
654
|
+
let r = Ke(e, n, t), i = yield* h.SqlClient;
|
|
655
|
+
yield* D(i, Ze(i, r, n)).pipe(p.orDie);
|
|
656
|
+
}), tt = async (e, t, n, r = "postgres") => {
|
|
657
|
+
await p.runPromise(et(e, t, r).pipe(p.provide(n)));
|
|
658
|
+
}, nt = async (e, t, n = "postgres") => {
|
|
659
|
+
await p.runPromise(p.gen(function* () {
|
|
660
|
+
let t = qe(e, n), r = yield* h.SqlClient;
|
|
661
|
+
yield* D(r, Ze(r, t, n)).pipe(p.orDie);
|
|
662
|
+
}).pipe(p.provide(t)));
|
|
663
|
+
}, rt = (e) => {
|
|
624
664
|
if (e == null) return e;
|
|
625
|
-
if (Array.isArray(e)) return e.map(
|
|
665
|
+
if (Array.isArray(e)) return e.map(rt);
|
|
626
666
|
if (typeof e != "object") return e;
|
|
627
667
|
let t = {};
|
|
628
668
|
for (let n of Object.keys(e).sort()) {
|
|
629
669
|
let r = e[n];
|
|
630
|
-
r !== void 0 && (t[n] =
|
|
670
|
+
r !== void 0 && (t[n] = rt(r));
|
|
631
671
|
}
|
|
632
672
|
return t;
|
|
633
|
-
},
|
|
673
|
+
}, it = (e) => {
|
|
634
674
|
if (!e.idScheme && !e.sensitive && !e.safe) return e;
|
|
635
675
|
let { idScheme: t, sensitive: n, safe: r, ...i } = e;
|
|
636
676
|
return i;
|
|
637
|
-
},
|
|
677
|
+
}, at = (e) => [...e].map(it).sort((e, t) => e.name.localeCompare(t.name)), ot = (e) => [...e].sort((e, t) => e.name.localeCompare(t.name)), st = (e) => [...e].map((e) => ({
|
|
638
678
|
name: e.name,
|
|
639
|
-
columns:
|
|
640
|
-
indexes:
|
|
641
|
-
})).sort((e, t) => e.name.localeCompare(t.name)),
|
|
642
|
-
let t = { tables:
|
|
643
|
-
return
|
|
644
|
-
},
|
|
679
|
+
columns: at(e.columns),
|
|
680
|
+
indexes: ot(e.indexes)
|
|
681
|
+
})).sort((e, t) => e.name.localeCompare(t.name)), P = (e) => {
|
|
682
|
+
let t = { tables: st(e.tables) }, n = JSON.stringify(rt(t));
|
|
683
|
+
return g("sha256").update(n).digest("hex");
|
|
684
|
+
}, F = (e) => e.slice(0, 16), ct = (e, t) => {
|
|
645
685
|
let n = t.references ? {
|
|
646
686
|
table: t.references().tableName,
|
|
647
687
|
column: "id",
|
|
@@ -680,7 +720,7 @@ BEGIN
|
|
|
680
720
|
...t.enumValues === void 0 ? {} : { enumValues: t.enumValues },
|
|
681
721
|
...t.spatial === void 0 ? {} : { spatial: t.spatial }
|
|
682
722
|
};
|
|
683
|
-
},
|
|
723
|
+
}, lt = (e) => {
|
|
684
724
|
let t = [], n = /* @__PURE__ */ new Set(), r = (e) => {
|
|
685
725
|
n.has(e.name) || (n.add(e.name), t.push(e));
|
|
686
726
|
}, i = e.appliedPrimaryKey ?? [];
|
|
@@ -720,7 +760,7 @@ BEGIN
|
|
|
720
760
|
let i = n;
|
|
721
761
|
if (i.dropped || !i.references || i.fkAutoIndex === !1 || s.has(t)) continue;
|
|
722
762
|
let a = `${e.tableName}_${t}_idx`;
|
|
723
|
-
|
|
763
|
+
l(e.tableName, a, !0, [t]), r({
|
|
724
764
|
name: a,
|
|
725
765
|
table: e.tableName,
|
|
726
766
|
columns: [t],
|
|
@@ -728,11 +768,11 @@ BEGIN
|
|
|
728
768
|
});
|
|
729
769
|
}
|
|
730
770
|
return t;
|
|
731
|
-
},
|
|
771
|
+
}, ut = (e) => {
|
|
732
772
|
let t = new Set((e.appliedUniques ?? []).filter((e) => e.fields.length === 1).map((e) => e.fields[0])), n = [];
|
|
733
773
|
for (let [r, i] of Object.entries(e.fields)) {
|
|
734
774
|
if (i.dropped) continue;
|
|
735
|
-
let e =
|
|
775
|
+
let e = ct(r, i);
|
|
736
776
|
n.push(t.has(r) ? {
|
|
737
777
|
...e,
|
|
738
778
|
unique: !0
|
|
@@ -742,44 +782,44 @@ BEGIN
|
|
|
742
782
|
return {
|
|
743
783
|
name: e.tableName,
|
|
744
784
|
columns: n,
|
|
745
|
-
indexes:
|
|
785
|
+
indexes: lt(e),
|
|
746
786
|
...r.length > 0 ? { primaryKey: [...r] } : {},
|
|
747
787
|
...i === void 0 ? {} : { renamedFrom: i }
|
|
748
788
|
};
|
|
749
|
-
},
|
|
789
|
+
}, dt = (e) => {
|
|
750
790
|
let t = /* @__PURE__ */ new Map();
|
|
751
791
|
for (let n of e) for (let e of n.indexes) {
|
|
752
792
|
let r = t.get(e.name);
|
|
753
793
|
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}').`);
|
|
754
794
|
t.set(e.name, n.name);
|
|
755
795
|
}
|
|
756
|
-
},
|
|
757
|
-
let n = e.map((e) =>
|
|
758
|
-
return
|
|
759
|
-
},
|
|
760
|
-
let
|
|
761
|
-
if (
|
|
796
|
+
}, I = (e, t) => {
|
|
797
|
+
let n = e.map((e) => ut(te(e, t)));
|
|
798
|
+
return dt(n), { tables: n };
|
|
799
|
+
}, ft = (e, t, n = O) => p.gen(function* () {
|
|
800
|
+
let r = t.filter((e) => !e.tableName.startsWith("_voltro_"));
|
|
801
|
+
if (r.length === 0) return {
|
|
762
802
|
missing: [],
|
|
763
803
|
stale: []
|
|
764
804
|
};
|
|
765
|
-
let
|
|
805
|
+
let i = yield* e`
|
|
766
806
|
SELECT t.tgname
|
|
767
807
|
FROM pg_trigger t
|
|
768
808
|
JOIN pg_class c ON c.oid = t.tgrelid
|
|
769
809
|
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
770
810
|
WHERE NOT t.tgisinternal
|
|
771
811
|
AND n.nspname = current_schema()
|
|
772
|
-
AND t.tgname LIKE ${`${
|
|
773
|
-
`,
|
|
774
|
-
for (let e of
|
|
775
|
-
let t =
|
|
776
|
-
|
|
812
|
+
AND t.tgname LIKE ${`${k}%`}
|
|
813
|
+
`, a = new Set(i.map((e) => e.tgname)), o = [], s = [];
|
|
814
|
+
for (let e of r) {
|
|
815
|
+
let t = a.has(_e(e.tableName, n)), r = e.isReactive !== !1;
|
|
816
|
+
r && !t && o.push(e.tableName), !r && t && s.push(e.tableName);
|
|
777
817
|
}
|
|
778
818
|
return {
|
|
779
|
-
missing:
|
|
780
|
-
stale:
|
|
819
|
+
missing: o.sort(),
|
|
820
|
+
stale: s.sort()
|
|
781
821
|
};
|
|
782
|
-
}),
|
|
822
|
+
}), pt = (e) => {
|
|
783
823
|
let t = [];
|
|
784
824
|
if (e.missing.length > 0) {
|
|
785
825
|
let n = e.missing.slice(0, 8).join(", "), r = e.missing.length > 8 ? `, … (+${e.missing.length - 8})` : "";
|
|
@@ -790,19 +830,19 @@ BEGIN
|
|
|
790
830
|
t.push(`${e.stale.length} .nonReactive() table(s) still carry a change trigger — ${n}. They keep paying REPLICA IDENTITY FULL and a NOTIFY on every write for a subscription nobody receives. \`voltro db apply\` removes them.`);
|
|
791
831
|
}
|
|
792
832
|
return t.length > 0 ? t.join("\n") : void 0;
|
|
793
|
-
},
|
|
833
|
+
}, mt = {
|
|
794
834
|
postgres: !0,
|
|
795
835
|
mysql: !0,
|
|
796
836
|
mariadb: !0,
|
|
797
837
|
mssql: !0,
|
|
798
838
|
sqlite: !0
|
|
799
|
-
},
|
|
839
|
+
}, ht = {
|
|
800
840
|
postgres: !0,
|
|
801
841
|
mysql: !1,
|
|
802
842
|
mariadb: !1,
|
|
803
843
|
mssql: !0,
|
|
804
844
|
sqlite: !0
|
|
805
|
-
},
|
|
845
|
+
}, gt = (e) => {
|
|
806
846
|
switch (e) {
|
|
807
847
|
case "add-column":
|
|
808
848
|
case "drop-column":
|
|
@@ -812,43 +852,43 @@ BEGIN
|
|
|
812
852
|
case "add-foreign-key":
|
|
813
853
|
case "add-unique":
|
|
814
854
|
case "add-unique-composite":
|
|
815
|
-
case "add-check": return
|
|
816
|
-
default: return
|
|
855
|
+
case "add-check": return ht;
|
|
856
|
+
default: return mt;
|
|
817
857
|
}
|
|
818
|
-
},
|
|
858
|
+
}, _t = (e, t) => {
|
|
819
859
|
let n = new Map(t.tables.map((e) => [e.name, e]));
|
|
820
860
|
return new Map(e.map((e) => [e.tableName, {
|
|
821
861
|
table: e,
|
|
822
862
|
snapshot: n.get(e.tableName),
|
|
823
863
|
columnDefs: e.fields
|
|
824
864
|
}]));
|
|
825
|
-
},
|
|
865
|
+
}, L = (e) => {
|
|
826
866
|
if (typeof e != "object" || !e) return JSON.stringify(e);
|
|
827
|
-
if (Array.isArray(e)) return `[${e.map(
|
|
867
|
+
if (Array.isArray(e)) return `[${e.map(L).join(",")}]`;
|
|
828
868
|
let t = e;
|
|
829
|
-
return `{${Object.keys(t).sort().map((e) => `${JSON.stringify(e)}:${
|
|
830
|
-
},
|
|
869
|
+
return `{${Object.keys(t).sort().map((e) => `${JSON.stringify(e)}:${L(t[e])}`).join(",")}}`;
|
|
870
|
+
}, vt = (e) => {
|
|
831
871
|
if (!e.hasDefault || e.defaultValue === void 0 || e.defaultValue === null) return null;
|
|
832
872
|
let t = e.defaultValue;
|
|
833
873
|
if (typeof t == "boolean") return t ? "__BOOL_TRUE__" : "__BOOL_FALSE__";
|
|
834
874
|
if (typeof t == "number") return String(t);
|
|
835
|
-
if (typeof t != "string") return
|
|
875
|
+
if (typeof t != "string") return L(t);
|
|
836
876
|
let n = t.trim(), r = n.toLowerCase().replace(/\s+/g, "");
|
|
837
877
|
if (r === "now" || r === "now()" || /^current_timestamp(\(\d*\))?$/.test(r) || r === "sysutcdatetime()" || r === "sysdatetime()" || r === "getutcdate()" || r === "getdate()") return "__NOW__";
|
|
838
878
|
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);
|
|
839
879
|
if (o) try {
|
|
840
|
-
return
|
|
880
|
+
return L(JSON.parse(o[1]));
|
|
841
881
|
} catch {}
|
|
842
882
|
if (a.startsWith("{") || a.startsWith("[")) try {
|
|
843
|
-
return
|
|
883
|
+
return L(JSON.parse(a));
|
|
844
884
|
} catch {}
|
|
845
885
|
return /^-?\d+(\.\d+)?$/.test(a) ? String(Number(a)) : a;
|
|
846
|
-
},
|
|
847
|
-
let n =
|
|
886
|
+
}, yt = (e, t) => {
|
|
887
|
+
let n = vt(e), r = vt(t);
|
|
848
888
|
if (n === r) return !0;
|
|
849
889
|
let i = (e) => e === "1" || e === "true" ? "__BOOL_TRUE__" : e === "0" || e === "false" ? "__BOOL_FALSE__" : e;
|
|
850
890
|
return i(n) === i(r);
|
|
851
|
-
},
|
|
891
|
+
}, bt = (e) => {
|
|
852
892
|
if (!e) return null;
|
|
853
893
|
let t = (e) => e === "restrict" ? "noAction" : e;
|
|
854
894
|
return {
|
|
@@ -857,25 +897,25 @@ BEGIN
|
|
|
857
897
|
onDelete: t(e.onDelete),
|
|
858
898
|
onUpdate: t(e.onUpdate)
|
|
859
899
|
};
|
|
860
|
-
},
|
|
900
|
+
}, xt = (e, t) => {
|
|
861
901
|
let n = e && e.length > 0 ? e : void 0, r = t && t.length > 0 ? t : void 0;
|
|
862
902
|
if (!n && !r) return !0;
|
|
863
903
|
if (!n || !r || n.length !== r.length) return !1;
|
|
864
904
|
let i = [...n].sort(), a = [...r].sort();
|
|
865
905
|
return i.every((e, t) => e === a[t]);
|
|
866
|
-
},
|
|
906
|
+
}, St = (e, t) => {
|
|
867
907
|
try {
|
|
868
|
-
let n = /\((\d+)\)/.exec(
|
|
908
|
+
let n = /\((\d+)\)/.exec(j(e, t));
|
|
869
909
|
return n ? Number(n[1]) : void 0;
|
|
870
910
|
} catch {
|
|
871
911
|
return;
|
|
872
912
|
}
|
|
873
|
-
},
|
|
913
|
+
}, Ct = (t, n, r) => r === void 0 || e(r) ? !0 : t.type !== "text" || n.type !== "text" || St(t, r) === n.maxLength, wt = (e, t, n) => e.type === t.type && e.nullable === t.nullable && e.unique === t.unique && yt(e, t) && Ct(e, t, n) && JSON.stringify(bt(e.references)) === JSON.stringify(bt(t.references)) && xt(e.oneOf, t.oneOf), Tt = (e, t) => t.oneOf && t.oneOf.length > 0 ? R({
|
|
874
914
|
kind: "add-check",
|
|
875
915
|
table: e,
|
|
876
916
|
column: t.name,
|
|
877
917
|
values: t.oneOf
|
|
878
|
-
}, "safe", `enum-membership CHECK on ${e}.${t.name}`) : void 0,
|
|
918
|
+
}, "safe", `enum-membership CHECK on ${e}.${t.name}`) : void 0, Et = (e, t) => e.unique === t.unique && (e.expression || t.expression ? !0 : e.columns.length === t.columns.length && e.columns.every((e, n) => e === t.columns[n])), Dt = (e, t, n) => {
|
|
879
919
|
let r = new Map(t.columns.map((e) => [e.name, e])), i = new Map(e.columns.map((e) => [e.name, e])), a = [], o = [], s = [];
|
|
880
920
|
for (let t of e.columns) {
|
|
881
921
|
let e = r.get(t.name);
|
|
@@ -883,7 +923,7 @@ BEGIN
|
|
|
883
923
|
a.push(t);
|
|
884
924
|
continue;
|
|
885
925
|
}
|
|
886
|
-
|
|
926
|
+
wt(t, e, n) || s.push({
|
|
887
927
|
declared: t,
|
|
888
928
|
live: e
|
|
889
929
|
});
|
|
@@ -894,47 +934,47 @@ BEGIN
|
|
|
894
934
|
removed: o,
|
|
895
935
|
changed: s
|
|
896
936
|
};
|
|
897
|
-
},
|
|
937
|
+
}, Ot = (e, t) => {
|
|
898
938
|
let n = new Map(t.indexes.map((e) => [e.name, e])), r = new Map(e.indexes.map((e) => [e.name, e])), i = [], a = [];
|
|
899
939
|
for (let t of e.indexes) {
|
|
900
940
|
let e = n.get(t.name);
|
|
901
|
-
(!e || !
|
|
941
|
+
(!e || !Et(t, e)) && i.push(t);
|
|
902
942
|
}
|
|
903
943
|
for (let e of t.indexes) r.has(e.name) || a.push(e);
|
|
904
944
|
return {
|
|
905
945
|
added: i,
|
|
906
946
|
removed: a
|
|
907
947
|
};
|
|
908
|
-
},
|
|
948
|
+
}, R = (e, t, n, r) => ({
|
|
909
949
|
op: e,
|
|
910
950
|
classification: t,
|
|
911
|
-
atomicByDialect:
|
|
951
|
+
atomicByDialect: gt(e.kind),
|
|
912
952
|
...n ? { reason: n } : {},
|
|
913
953
|
...r ? { blocked: r } : {}
|
|
914
|
-
}),
|
|
954
|
+
}), kt = (e, t, n) => {
|
|
915
955
|
let r = {
|
|
916
956
|
kind: "add-column",
|
|
917
957
|
table: e,
|
|
918
958
|
column: t
|
|
919
959
|
};
|
|
920
|
-
return t.nullable ?
|
|
921
|
-
},
|
|
960
|
+
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` });
|
|
961
|
+
}, At = (e, t, n) => {
|
|
922
962
|
let r = {
|
|
923
963
|
kind: "drop-column",
|
|
924
964
|
table: e,
|
|
925
965
|
column: t.name
|
|
926
966
|
};
|
|
927
|
-
return n?.dropped ?
|
|
928
|
-
},
|
|
967
|
+
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` });
|
|
968
|
+
}, jt = (e, t, n, r, i) => R({
|
|
929
969
|
kind: "create-table",
|
|
930
970
|
table: e,
|
|
931
971
|
columns: t,
|
|
932
972
|
indexes: n,
|
|
933
973
|
...r && r.length > 0 ? { primaryKey: r } : {}
|
|
934
|
-
}, "safe", i ?? "new table — no data to preserve"),
|
|
974
|
+
}, "safe", i ?? "new table — no data to preserve"), Mt = (e) => R({
|
|
935
975
|
kind: "drop-table",
|
|
936
976
|
table: e
|
|
937
|
-
}, "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" }),
|
|
977
|
+
}, "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" }), Nt = 5e4, Pt = (e, t, n, r) => {
|
|
938
978
|
let i = (n ?? 0) >= r, a;
|
|
939
979
|
return a = t.unique ? t.columns.length === 1 ? {
|
|
940
980
|
kind: "add-unique",
|
|
@@ -949,16 +989,16 @@ BEGIN
|
|
|
949
989
|
kind: "add-index",
|
|
950
990
|
table: e,
|
|
951
991
|
index: t
|
|
952
|
-
}, i ?
|
|
953
|
-
},
|
|
992
|
+
}, 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");
|
|
993
|
+
}, Ft = (e, t) => t.unique && t.columns.length > 1 ? R({
|
|
954
994
|
kind: "drop-unique-composite",
|
|
955
995
|
table: e,
|
|
956
996
|
name: t.name
|
|
957
|
-
}, "safe", "drop composite UNIQUE constraint (no data loss — constraint is derived metadata)") :
|
|
997
|
+
}, "safe", "drop composite UNIQUE constraint (no data loss — constraint is derived metadata)") : R({
|
|
958
998
|
kind: "drop-index",
|
|
959
999
|
table: e,
|
|
960
1000
|
index: t.name
|
|
961
|
-
}, "safe", "drop index (no data loss — index is derived data)"),
|
|
1001
|
+
}, "safe", "drop index (no data loss — index is derived data)"), It = (e, t) => {
|
|
962
1002
|
if (e === "reference") return "text";
|
|
963
1003
|
switch (t) {
|
|
964
1004
|
case "sqlite":
|
|
@@ -966,106 +1006,106 @@ BEGIN
|
|
|
966
1006
|
case "mssql": return e === "json" ? "text" : e;
|
|
967
1007
|
default: return e;
|
|
968
1008
|
}
|
|
969
|
-
},
|
|
970
|
-
let
|
|
971
|
-
if (
|
|
972
|
-
let
|
|
1009
|
+
}, Lt = (t, n, r, i, a, o) => {
|
|
1010
|
+
let s = [];
|
|
1011
|
+
if (n.nullable !== r.nullable) {
|
|
1012
|
+
let e = {
|
|
973
1013
|
kind: "alter-column-nullability",
|
|
974
|
-
table:
|
|
975
|
-
column:
|
|
976
|
-
toNullable:
|
|
1014
|
+
table: t,
|
|
1015
|
+
column: n.name,
|
|
1016
|
+
toNullable: n.nullable
|
|
977
1017
|
};
|
|
978
|
-
|
|
1018
|
+
s.push(n.nullable ? R(e, "safe", `loosen ${t}.${n.name} to nullable`) : R(e, "needs-backfill", `tighten ${t}.${n.name} to NOT NULL — existing rows must already be non-null`));
|
|
979
1019
|
}
|
|
980
|
-
if (
|
|
981
|
-
let
|
|
1020
|
+
if (n.unique !== r.unique) if (n.unique) {
|
|
1021
|
+
let e = {
|
|
982
1022
|
kind: "add-unique",
|
|
983
|
-
table:
|
|
984
|
-
column:
|
|
985
|
-
}, r = (
|
|
986
|
-
|
|
987
|
-
} else
|
|
1023
|
+
table: t,
|
|
1024
|
+
column: n.name
|
|
1025
|
+
}, r = (a ?? 0) >= o;
|
|
1026
|
+
s.push(r ? R(e, "online-required", `online unique build on ${a} rows — CONCURRENTLY / INPLACE / WITH(ONLINE=ON) depending on dialect`) : R(e, "safe", `add unique constraint on ${t}.${n.name} — existing rows must already be distinct (else the DB rejects at migrate)`));
|
|
1027
|
+
} else s.push(R({
|
|
988
1028
|
kind: "drop-unique",
|
|
989
|
-
table:
|
|
990
|
-
column:
|
|
991
|
-
}, "safe", `drop unique constraint on ${
|
|
992
|
-
if (
|
|
993
|
-
let
|
|
994
|
-
if (
|
|
1029
|
+
table: t,
|
|
1030
|
+
column: n.name
|
|
1031
|
+
}, "safe", `drop unique constraint on ${t}.${n.name} — constraint is derived metadata, no data loss`));
|
|
1032
|
+
if (It(n.type, i) !== It(r.type, i)) {
|
|
1033
|
+
let e = n.narrowedFrom;
|
|
1034
|
+
if (e && e.from === r.type) {
|
|
995
1035
|
let i = {
|
|
996
1036
|
kind: "alter-column-type",
|
|
997
|
-
table:
|
|
998
|
-
column:
|
|
999
|
-
from:
|
|
1000
|
-
to:
|
|
1001
|
-
...
|
|
1037
|
+
table: t,
|
|
1038
|
+
column: n.name,
|
|
1039
|
+
from: r.type,
|
|
1040
|
+
to: n.type,
|
|
1041
|
+
...e.using === void 0 ? {} : { using: e.using }
|
|
1002
1042
|
};
|
|
1003
|
-
|
|
1043
|
+
s.push(R(i, "needs-backfill", `type change ${r.type} → ${n.type} on ${t}.${n.name} — narrowedFrom('${e.from}') declared` + (e.using ? ` with USING ${e.using}` : " (implicit cast)")));
|
|
1004
1044
|
} else {
|
|
1005
|
-
let
|
|
1045
|
+
let e = {
|
|
1006
1046
|
kind: "alter-column-type",
|
|
1007
|
-
table:
|
|
1008
|
-
column:
|
|
1009
|
-
from:
|
|
1010
|
-
to:
|
|
1047
|
+
table: t,
|
|
1048
|
+
column: n.name,
|
|
1049
|
+
from: r.type,
|
|
1050
|
+
to: n.type
|
|
1011
1051
|
};
|
|
1012
|
-
|
|
1052
|
+
s.push(R(e, "lossy", `type change ${r.type} → ${n.type} on ${t}.${n.name} may not be value-preserving`, { fix: `acknowledge it on the column: \`.narrowedFrom('${r.type}', { using: '${n.name}::${n.type}' })\` (drop \`using\` if the cast is implicit), or use a file-based migration` }));
|
|
1013
1053
|
}
|
|
1014
1054
|
}
|
|
1015
|
-
if (
|
|
1016
|
-
let
|
|
1017
|
-
|
|
1055
|
+
if (n.type === "text" && r.type === "text" && !e(i ?? "postgres") && n.maxLength !== r.maxLength) {
|
|
1056
|
+
let e = n.maxLength === void 0 || r.maxLength !== void 0 && n.maxLength > r.maxLength, i = r.maxLength === void 0 ? "unbounded" : `varchar(${r.maxLength})`, a = n.maxLength === void 0 ? "unbounded" : `varchar(${n.maxLength})`;
|
|
1057
|
+
s.push(R({
|
|
1018
1058
|
kind: "alter-column-type",
|
|
1019
|
-
table:
|
|
1020
|
-
column:
|
|
1059
|
+
table: t,
|
|
1060
|
+
column: n.name,
|
|
1021
1061
|
from: "text",
|
|
1022
1062
|
to: "text"
|
|
1023
|
-
},
|
|
1063
|
+
}, e ? "safe" : "needs-backfill", e ? `text length ${i} → ${a} on ${t}.${n.name} — widening, no data can be lost` : `text length ${i} → ${a} on ${t}.${n.name} — NARROWING; the ALTER fails if any existing value is longer than ${String(n.maxLength)}. Check first: SELECT COUNT(*) FROM ${t} WHERE LENGTH(${n.name}) > ${String(n.maxLength)}`));
|
|
1024
1064
|
}
|
|
1025
|
-
if (!
|
|
1026
|
-
let
|
|
1065
|
+
if (!yt(n, r)) {
|
|
1066
|
+
let e = {
|
|
1027
1067
|
kind: "alter-column-default",
|
|
1028
|
-
table:
|
|
1029
|
-
column:
|
|
1030
|
-
hasDefault:
|
|
1031
|
-
...
|
|
1068
|
+
table: t,
|
|
1069
|
+
column: n.name,
|
|
1070
|
+
hasDefault: n.hasDefault,
|
|
1071
|
+
...n.defaultValue === void 0 ? {} : { defaultValue: n.defaultValue }
|
|
1032
1072
|
};
|
|
1033
|
-
|
|
1073
|
+
s.push(R(e, "safe", `default change on ${t}.${n.name} affects only future inserts`));
|
|
1034
1074
|
}
|
|
1035
|
-
if (!
|
|
1036
|
-
|
|
1075
|
+
if (!xt(n.oneOf, r.oneOf)) {
|
|
1076
|
+
r.oneOf && r.oneOf.length > 0 && s.push(R({
|
|
1037
1077
|
kind: "drop-check",
|
|
1038
|
-
table:
|
|
1039
|
-
column:
|
|
1040
|
-
}, "safe", `drop stale enum CHECK on ${
|
|
1041
|
-
let
|
|
1042
|
-
|
|
1078
|
+
table: t,
|
|
1079
|
+
column: n.name
|
|
1080
|
+
}, "safe", `drop stale enum CHECK on ${t}.${n.name}`));
|
|
1081
|
+
let e = Tt(t, n);
|
|
1082
|
+
e && s.push(e);
|
|
1043
1083
|
}
|
|
1044
|
-
if (JSON.stringify(
|
|
1084
|
+
if (JSON.stringify(bt(n.references)) !== JSON.stringify(bt(r.references)) && (r.references && s.push(R({
|
|
1045
1085
|
kind: "drop-foreign-key",
|
|
1046
|
-
table:
|
|
1047
|
-
column:
|
|
1048
|
-
}, "safe", `drop FK on ${
|
|
1049
|
-
let
|
|
1086
|
+
table: t,
|
|
1087
|
+
column: n.name
|
|
1088
|
+
}, "safe", `drop FK on ${t}.${n.name} (→ ${r.references.table})`)), n.references)) {
|
|
1089
|
+
let e = n.references.table, r = n.references.orphanPolicy ?? "fail", i = {
|
|
1050
1090
|
kind: "add-foreign-key",
|
|
1051
|
-
table:
|
|
1052
|
-
column:
|
|
1053
|
-
targetTable:
|
|
1054
|
-
targetColumn:
|
|
1055
|
-
...
|
|
1056
|
-
...
|
|
1091
|
+
table: t,
|
|
1092
|
+
column: n.name,
|
|
1093
|
+
targetTable: e,
|
|
1094
|
+
targetColumn: n.references.column,
|
|
1095
|
+
...n.references.onDelete ? { onDelete: n.references.onDelete } : {},
|
|
1096
|
+
...n.references.onUpdate ? { onUpdate: n.references.onUpdate } : {}
|
|
1057
1097
|
};
|
|
1058
|
-
r === "null" && !
|
|
1098
|
+
r === "null" && !n.nullable ? s.push(R(i, "lossy", `add FK ${t}.${n.name} → ${e}: orphanPolicy 'null' needs a nullable column (the NULL-out would violate NOT NULL)`, { fix: `make ${n.name} \`.nullable()\`, OR use \`orphanPolicy: 'delete'\`, OR clean the orphans and drop the policy` })) : r === "null" || r === "delete" ? s.push(R({
|
|
1059
1099
|
...i,
|
|
1060
1100
|
orphanPolicy: r
|
|
1061
|
-
}, "lossy", r === "delete" ? `add FK ${
|
|
1101
|
+
}, "lossy", r === "delete" ? `add FK ${t}.${n.name} → ${e}: orphanPolicy 'delete' first DELETES rows whose ${n.name} points at a missing ${e}` : `add FK ${t}.${n.name} → ${e}: orphanPolicy 'null' first NULLs ${n.name} where it points at a missing ${e}`)) : s.push(R(i, "needs-backfill", `add FK ${t}.${n.name} → ${e} — existing rows must already reference a valid ${e} (else set orphanPolicy:'null'/'delete')`));
|
|
1062
1102
|
}
|
|
1063
|
-
return
|
|
1064
|
-
},
|
|
1103
|
+
return s;
|
|
1104
|
+
}, Rt = (e, t, n) => {
|
|
1065
1105
|
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));
|
|
1066
1106
|
for (let [n, c] of Object.entries(t)) {
|
|
1067
1107
|
let t = c.renamedFrom;
|
|
1068
|
-
t && o.has(t) && s.has(n) && (i.add(n), a.add(t), r.push(
|
|
1108
|
+
t && o.has(t) && s.has(n) && (i.add(n), a.add(t), r.push(R({
|
|
1069
1109
|
kind: "rename-column",
|
|
1070
1110
|
table: e.tableName,
|
|
1071
1111
|
from: t,
|
|
@@ -1077,26 +1117,26 @@ BEGIN
|
|
|
1077
1117
|
consumedAddedNames: i,
|
|
1078
1118
|
consumedRemovedNames: a
|
|
1079
1119
|
};
|
|
1080
|
-
},
|
|
1120
|
+
}, zt = /* @__PURE__ */ new Set([
|
|
1081
1121
|
"postgres",
|
|
1082
1122
|
"mysql",
|
|
1083
1123
|
"mariadb",
|
|
1084
1124
|
"mssql"
|
|
1085
|
-
]),
|
|
1125
|
+
]), Bt = {
|
|
1086
1126
|
ops: [],
|
|
1087
1127
|
consumedAddedNames: /* @__PURE__ */ new Set(),
|
|
1088
1128
|
consumedRemovedNames: /* @__PURE__ */ new Set()
|
|
1089
|
-
},
|
|
1090
|
-
if (i === void 0 || !
|
|
1091
|
-
let a = t.added.filter((e) =>
|
|
1092
|
-
if (a.length === 0) return
|
|
1129
|
+
}, Vt = (e) => !e.unique && !e.expression, Ht = (e, t, n, r, i) => {
|
|
1130
|
+
if (i === void 0 || !zt.has(i)) return Bt;
|
|
1131
|
+
let a = t.added.filter((e) => Vt(e) && !r.has(e.name));
|
|
1132
|
+
if (a.length === 0) return Bt;
|
|
1093
1133
|
let o = (e) => e.columns.map((e) => n.get(e) ?? e).join("\0"), s = (e) => e.columns.join("\0"), c = [], l = /* @__PURE__ */ new Set(), u = /* @__PURE__ */ new Set();
|
|
1094
1134
|
for (let n of t.removed) {
|
|
1095
|
-
if (!
|
|
1135
|
+
if (!Vt(n)) continue;
|
|
1096
1136
|
let t = o(n), r = a.filter((e) => !l.has(e.name) && s(e) === t);
|
|
1097
1137
|
if (r.length !== 1) continue;
|
|
1098
1138
|
let i = r[0];
|
|
1099
|
-
i.name !== n.name && (l.add(i.name), u.add(n.name), c.push(
|
|
1139
|
+
i.name !== n.name && (l.add(i.name), u.add(n.name), c.push(R({
|
|
1100
1140
|
kind: "rename-index",
|
|
1101
1141
|
table: e,
|
|
1102
1142
|
from: n.name,
|
|
@@ -1108,33 +1148,33 @@ BEGIN
|
|
|
1108
1148
|
consumedAddedNames: l,
|
|
1109
1149
|
consumedRemovedNames: u
|
|
1110
1150
|
};
|
|
1111
|
-
},
|
|
1151
|
+
}, Ut = (e, t, n) => {
|
|
1112
1152
|
let r = [], i = /* @__PURE__ */ new Map(), a = /* @__PURE__ */ new Set(), o = /* @__PURE__ */ new Map(), s = /* @__PURE__ */ new Map(), c = /* @__PURE__ */ new Set();
|
|
1113
1153
|
for (let t of e.tables) t.renamedFrom !== void 0 && (s.set(t.renamedFrom, (s.get(t.renamedFrom) ?? 0) + 1), c.add(t.renamedFrom));
|
|
1114
1154
|
for (let c of e.tables) {
|
|
1115
1155
|
let e = c.renamedFrom;
|
|
1116
1156
|
if (e !== void 0 && e !== c.name && t.has(e)) {
|
|
1157
|
+
if (n.has(e)) {
|
|
1158
|
+
o.set(c.name, `.renamedFrom('${e}') NOT applied — '${e}' is still declared by this schema, so it belongs to that declaration. This table is created empty; that is the intended outcome when an app owns a table of the same name.`);
|
|
1159
|
+
continue;
|
|
1160
|
+
}
|
|
1117
1161
|
if (t.has(c.name)) {
|
|
1118
|
-
r.push(
|
|
1162
|
+
r.push(R({
|
|
1119
1163
|
kind: "rename-table",
|
|
1120
1164
|
from: e,
|
|
1121
1165
|
to: c.name
|
|
1122
1166
|
}, "needs-rename-annotation", `.renamedFrom('${e}') cannot be applied — both '${e}' and '${c.name}' exist in the database`, { fix: `decide which holds the real rows: move them into '${c.name}' and drop '${e}', or drop the empty '${c.name}' so the rename can run. '${e}' is left untouched until you do.` }));
|
|
1123
1167
|
continue;
|
|
1124
1168
|
}
|
|
1125
|
-
if (n.has(e)) {
|
|
1126
|
-
o.set(c.name, `.renamedFrom('${e}') NOT applied — '${e}' is still declared by this schema, so it belongs to that declaration. This table is created empty; that is the intended outcome when an app owns a table of the same name.`);
|
|
1127
|
-
continue;
|
|
1128
|
-
}
|
|
1129
1169
|
if ((s.get(e) ?? 0) !== 1) {
|
|
1130
|
-
r.push(
|
|
1170
|
+
r.push(R({
|
|
1131
1171
|
kind: "rename-table",
|
|
1132
1172
|
from: e,
|
|
1133
1173
|
to: c.name
|
|
1134
1174
|
}, "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.` }));
|
|
1135
1175
|
continue;
|
|
1136
1176
|
}
|
|
1137
|
-
i.set(c.name, e), a.add(e), r.push(
|
|
1177
|
+
i.set(c.name, e), a.add(e), r.push(R({
|
|
1138
1178
|
kind: "rename-table",
|
|
1139
1179
|
from: e,
|
|
1140
1180
|
to: c.name
|
|
@@ -1148,19 +1188,19 @@ BEGIN
|
|
|
1148
1188
|
claimedOldNames: c,
|
|
1149
1189
|
refusals: o
|
|
1150
1190
|
};
|
|
1151
|
-
},
|
|
1191
|
+
}, Wt = (e, t, n, r) => {
|
|
1152
1192
|
if (t === void 0) return {
|
|
1153
1193
|
live: e,
|
|
1154
1194
|
ops: []
|
|
1155
1195
|
};
|
|
1156
|
-
let i = r !== void 0 &&
|
|
1196
|
+
let i = r !== void 0 && zt.has(r), a = [], o = e.indexes.map((e) => {
|
|
1157
1197
|
if (!e.name.startsWith(t)) return e;
|
|
1158
1198
|
let r = `${n}${e.name.slice(t.length)}`;
|
|
1159
1199
|
return r === e.name ? e : e.name === `${t}_pkey` ? {
|
|
1160
1200
|
...e,
|
|
1161
1201
|
name: r,
|
|
1162
1202
|
table: n
|
|
1163
|
-
} : i ? (a.push(
|
|
1203
|
+
} : i ? (a.push(R({
|
|
1164
1204
|
kind: "rename-index",
|
|
1165
1205
|
table: n,
|
|
1166
1206
|
from: e.name,
|
|
@@ -1179,7 +1219,7 @@ BEGIN
|
|
|
1179
1219
|
},
|
|
1180
1220
|
ops: a
|
|
1181
1221
|
};
|
|
1182
|
-
},
|
|
1222
|
+
}, Gt = (e) => {
|
|
1183
1223
|
let t = {
|
|
1184
1224
|
safe: 0,
|
|
1185
1225
|
needsDefault: 0,
|
|
@@ -1214,51 +1254,51 @@ BEGIN
|
|
|
1214
1254
|
break;
|
|
1215
1255
|
}
|
|
1216
1256
|
return t;
|
|
1217
|
-
},
|
|
1218
|
-
let t =
|
|
1257
|
+
}, Kt = (e) => {
|
|
1258
|
+
let t = I(e.declared, e.dialect), n = _t(e.declared, t), r = new Map(e.live.tables.map((e) => [e.name, e])), i = e.onlineAfter ?? Nt, a = [], o = Ut(t, r, n);
|
|
1219
1259
|
a.push(...o.ops);
|
|
1220
1260
|
for (let e of t.tables) if (!o.renamedInto.has(e.name) && !r.has(e.name)) {
|
|
1221
|
-
a.push(
|
|
1261
|
+
a.push(jt(e.name, e.columns, e.indexes, e.primaryKey, o.refusals.get(e.name)));
|
|
1222
1262
|
for (let t of e.columns) {
|
|
1223
|
-
let n =
|
|
1263
|
+
let n = Tt(e.name, t);
|
|
1224
1264
|
n && a.push(n);
|
|
1225
1265
|
}
|
|
1226
1266
|
}
|
|
1227
|
-
let
|
|
1228
|
-
for (let t of e.live.tables) n.has(t.name) || o.claimedOldNames.has(t.name) ||
|
|
1267
|
+
let c = new Set(e.ignoreTables ?? []);
|
|
1268
|
+
for (let t of e.live.tables) n.has(t.name) || o.claimedOldNames.has(t.name) || s(t.name) || d(t.name) || c.has(t.name) || a.push(Mt(t.name));
|
|
1229
1269
|
for (let s of t.tables) {
|
|
1230
1270
|
let t = o.renamedInto.get(s.name), c = r.get(s.name) ?? (t === void 0 ? void 0 : r.get(t));
|
|
1231
1271
|
if (!c) continue;
|
|
1232
|
-
let l =
|
|
1272
|
+
let l = Wt(c, t, s.name, e.dialect);
|
|
1233
1273
|
a.push(...l.ops);
|
|
1234
|
-
let u = l.live,
|
|
1235
|
-
a.push(...
|
|
1236
|
-
for (let e of
|
|
1237
|
-
if (
|
|
1238
|
-
let t =
|
|
1239
|
-
a.push(
|
|
1274
|
+
let u = l.live, f = n.get(s.name), p = Dt(s, u, e.dialect), m = Ot(s, u), h = Rt(f.table, f.columnDefs, p);
|
|
1275
|
+
a.push(...h.ops);
|
|
1276
|
+
for (let e of p.removed) {
|
|
1277
|
+
if (h.consumedRemovedNames.has(e.name) || d(e.name)) continue;
|
|
1278
|
+
let t = f.columnDefs[e.name];
|
|
1279
|
+
a.push(At(s.name, e, t));
|
|
1240
1280
|
}
|
|
1241
|
-
for (let e of
|
|
1242
|
-
if (
|
|
1243
|
-
let t =
|
|
1244
|
-
a.push(
|
|
1245
|
-
let n =
|
|
1281
|
+
for (let e of p.added) {
|
|
1282
|
+
if (h.consumedAddedNames.has(e.name)) continue;
|
|
1283
|
+
let t = f.columnDefs[e.name];
|
|
1284
|
+
a.push(kt(s.name, e, t));
|
|
1285
|
+
let n = Tt(s.name, e);
|
|
1246
1286
|
n && a.push(n);
|
|
1247
1287
|
}
|
|
1248
|
-
let
|
|
1249
|
-
a.push(...
|
|
1250
|
-
for (let e of
|
|
1251
|
-
for (let e of
|
|
1252
|
-
for (let { declared: t, live: n } of
|
|
1253
|
-
}
|
|
1254
|
-
let
|
|
1255
|
-
let t =
|
|
1256
|
-
return t ? t.columns.flatMap((e) => e.references &&
|
|
1288
|
+
let g = new Map(h.ops.flatMap((e) => e.op.kind === "rename-column" ? [[e.op.from, e.op.to]] : [])), _ = Ht(s.name, m, g, new Set(u.indexes.map((e) => e.name)), e.dialect);
|
|
1289
|
+
a.push(..._.ops);
|
|
1290
|
+
for (let e of m.added) _.consumedAddedNames.has(e.name) || a.push(Pt(s.name, e, u.rowCount, i));
|
|
1291
|
+
for (let e of m.removed) _.consumedRemovedNames.has(e.name) || a.push(Ft(s.name, e));
|
|
1292
|
+
for (let { declared: t, live: n } of p.changed) a.push(...Lt(s.name, t, n, e.dialect, u.rowCount, i));
|
|
1293
|
+
}
|
|
1294
|
+
let l = new Set(a.flatMap((e) => e.op.kind === "create-table" ? [e.op.table] : [])), u = new Map(t.tables.map((e) => [e.name, e])), f = (e) => {
|
|
1295
|
+
let t = u.get(e);
|
|
1296
|
+
return t ? t.columns.flatMap((e) => e.references && l.has(e.references.table) ? [{
|
|
1257
1297
|
column: e.name,
|
|
1258
1298
|
target: e.references.table
|
|
1259
1299
|
}] : []) : [];
|
|
1260
|
-
},
|
|
1261
|
-
for (let e of t.tables) if (
|
|
1300
|
+
}, p = ne([...l], f), m = [];
|
|
1301
|
+
for (let e of t.tables) if (l.has(e.name)) for (let t of e.columns) !t.references || !p.has(w(e.name, t.name)) || m.push(R({
|
|
1262
1302
|
kind: "add-foreign-key",
|
|
1263
1303
|
table: e.name,
|
|
1264
1304
|
column: t.name,
|
|
@@ -1267,14 +1307,14 @@ BEGIN
|
|
|
1267
1307
|
...t.references.onDelete ? { onDelete: t.references.onDelete } : {},
|
|
1268
1308
|
...t.references.onUpdate ? { onUpdate: t.references.onUpdate } : {}
|
|
1269
1309
|
}, "safe", `deferred FK ${e.name}.${t.name} → ${t.references.table} — breaks a create-time table cycle`));
|
|
1270
|
-
if (
|
|
1310
|
+
if (p.size > 0) {
|
|
1271
1311
|
let e = (e) => e.references ? Object.fromEntries(Object.entries(e).filter(([e]) => e !== "references")) : e;
|
|
1272
1312
|
for (let t = 0; t < a.length; t += 1) {
|
|
1273
1313
|
let n = a[t];
|
|
1274
1314
|
if (n.op.kind !== "create-table") continue;
|
|
1275
1315
|
let r = n.op;
|
|
1276
|
-
if (!r.columns.some((e) =>
|
|
1277
|
-
let i = r.columns.map((t) =>
|
|
1316
|
+
if (!r.columns.some((e) => p.has(w(r.table, e.name)))) continue;
|
|
1317
|
+
let i = r.columns.map((t) => p.has(w(r.table, t.name)) ? e(t) : t);
|
|
1278
1318
|
a[t] = {
|
|
1279
1319
|
...n,
|
|
1280
1320
|
op: {
|
|
@@ -1283,24 +1323,24 @@ BEGIN
|
|
|
1283
1323
|
}
|
|
1284
1324
|
};
|
|
1285
1325
|
}
|
|
1286
|
-
a.push(...
|
|
1326
|
+
a.push(...m);
|
|
1287
1327
|
}
|
|
1288
|
-
let
|
|
1289
|
-
let n =
|
|
1328
|
+
let h = new Map(t.tables.map((e) => [e.name, e])), g = /* @__PURE__ */ new Map(), _ = (e, t = /* @__PURE__ */ new Set()) => {
|
|
1329
|
+
let n = g.get(e);
|
|
1290
1330
|
if (n !== void 0) return n;
|
|
1291
1331
|
if (t.has(e)) return 0;
|
|
1292
|
-
let r =
|
|
1332
|
+
let r = h.get(e);
|
|
1293
1333
|
if (!r) return 0;
|
|
1294
1334
|
let i = new Set(t).add(e), a = 0;
|
|
1295
1335
|
for (let t of r.columns) {
|
|
1296
1336
|
let n = t.references?.table;
|
|
1297
|
-
n && n !== e && !
|
|
1337
|
+
n && n !== e && !p.has(w(e, t.name)) && (a = Math.max(a, _(n, i) + 1));
|
|
1298
1338
|
}
|
|
1299
|
-
return
|
|
1300
|
-
},
|
|
1339
|
+
return g.set(e, a), a;
|
|
1340
|
+
}, v = (e) => e === "rename-table" ? -1 : e === "create-table" ? 0 : e === "drop-table" ? 2 : 1, y = (e) => {
|
|
1301
1341
|
let t = "table" in e ? e.table : "";
|
|
1302
|
-
return e.kind === "create-table" ?
|
|
1303
|
-
},
|
|
1342
|
+
return e.kind === "create-table" ? _(t) : e.kind === "drop-table" ? -_(t) : 0;
|
|
1343
|
+
}, b = {
|
|
1304
1344
|
"add-column": 0,
|
|
1305
1345
|
"rename-column": 1,
|
|
1306
1346
|
"rename-index": 1,
|
|
@@ -1318,23 +1358,23 @@ BEGIN
|
|
|
1318
1358
|
"add-unique-composite": 5,
|
|
1319
1359
|
"add-foreign-key": 5,
|
|
1320
1360
|
"add-check": 5
|
|
1321
|
-
},
|
|
1322
|
-
let n =
|
|
1361
|
+
}, x = (e) => b[e] ?? 9, S = [...a].sort((e, t) => {
|
|
1362
|
+
let n = v(e.op.kind), r = v(t.op.kind);
|
|
1323
1363
|
if (n !== r) return n - r;
|
|
1324
|
-
let i =
|
|
1364
|
+
let i = y(e.op), a = y(t.op);
|
|
1325
1365
|
if (i !== a) return i - a;
|
|
1326
1366
|
let o = "table" in e.op ? e.op.table : "", s = "table" in t.op ? t.op.table : "";
|
|
1327
1367
|
if (o !== s) return o.localeCompare(s);
|
|
1328
|
-
let c =
|
|
1368
|
+
let c = x(e.op.kind), l = x(t.op.kind);
|
|
1329
1369
|
return c === l ? e.op.kind.localeCompare(t.op.kind) : c - l;
|
|
1330
1370
|
});
|
|
1331
1371
|
return {
|
|
1332
|
-
operations:
|
|
1333
|
-
fromFingerprint:
|
|
1334
|
-
toFingerprint:
|
|
1335
|
-
summary:
|
|
1372
|
+
operations: S,
|
|
1373
|
+
fromFingerprint: P(e.live),
|
|
1374
|
+
toFingerprint: P(t),
|
|
1375
|
+
summary: Gt(S)
|
|
1336
1376
|
};
|
|
1337
|
-
},
|
|
1377
|
+
}, qt = (e) => {
|
|
1338
1378
|
let t = /[`"[]?([A-Za-z_][A-Za-z0-9_]*)[`"\]]?\s*(?:in\b|=)/i.exec(e);
|
|
1339
1379
|
if (!t) return null;
|
|
1340
1380
|
let n = [...e.matchAll(/'((?:[^']|'')*)'/g)].map((e) => e[1].replace(/''/g, "'"));
|
|
@@ -1342,7 +1382,7 @@ BEGIN
|
|
|
1342
1382
|
column: t[1],
|
|
1343
1383
|
values: n
|
|
1344
1384
|
};
|
|
1345
|
-
},
|
|
1385
|
+
}, Jt = (e, t) => {
|
|
1346
1386
|
if (t === "vector") return "vector";
|
|
1347
1387
|
switch (e) {
|
|
1348
1388
|
case "text":
|
|
@@ -1369,7 +1409,7 @@ BEGIN
|
|
|
1369
1409
|
case "USER-DEFINED": return "text";
|
|
1370
1410
|
default: return "text";
|
|
1371
1411
|
}
|
|
1372
|
-
},
|
|
1412
|
+
}, z = (e) => {
|
|
1373
1413
|
switch (e.toUpperCase()) {
|
|
1374
1414
|
case "CASCADE": return "cascade";
|
|
1375
1415
|
case "RESTRICT": return "restrict";
|
|
@@ -1377,25 +1417,25 @@ BEGIN
|
|
|
1377
1417
|
case "SET DEFAULT": return "noAction";
|
|
1378
1418
|
default: return "noAction";
|
|
1379
1419
|
}
|
|
1380
|
-
},
|
|
1420
|
+
}, Yt = (e, t) => {
|
|
1381
1421
|
let n = e !== void 0 && e !== "" ? Number(e) : NaN;
|
|
1382
1422
|
return Number.isInteger(n) && n > 0 ? n : t;
|
|
1383
|
-
},
|
|
1423
|
+
}, Xt = () => Yt(process.env.VOLTRO_INTROSPECT_BATCH, 20), Zt = 300, Qt = () => {
|
|
1384
1424
|
let e = process.env.VOLTRO_INTROSPECT_TIMEOUT_MS;
|
|
1385
|
-
return e !== void 0 && e.trim() === "0" ? 0 :
|
|
1386
|
-
},
|
|
1425
|
+
return e !== void 0 && e.trim() === "0" ? 0 : Yt(e, 3e4);
|
|
1426
|
+
}, $t = (e, t) => {
|
|
1387
1427
|
let n = Number.isFinite(t) && t >= 1 ? Math.floor(t) : 1, r = [];
|
|
1388
1428
|
for (let t = 0; t < e.length; t += n) r.push(e.slice(t, t + n));
|
|
1389
1429
|
return r;
|
|
1390
|
-
},
|
|
1391
|
-
let t =
|
|
1430
|
+
}, en = (e) => {
|
|
1431
|
+
let t = Qt(), n = p.gen(function* () {
|
|
1392
1432
|
t > 0 && (yield* e.unsafe(`SET LOCAL statement_timeout = ${t}`));
|
|
1393
|
-
let n = (t) => e.unsafe("(" + t.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ") + ")"), r = (t) => e.unsafe(String(Math.max(0, Math.trunc(t)))), i =
|
|
1433
|
+
let n = (t) => e.unsafe("(" + t.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ") + ")"), r = (t) => e.unsafe(String(Math.max(0, Math.trunc(t)))), i = Xt(), a = (e, t) => p.gen(function* () {
|
|
1394
1434
|
let r = [];
|
|
1395
|
-
for (let a of
|
|
1435
|
+
for (let a of $t(e, i)) a.length !== 0 && r.push(...yield* t(n(a)));
|
|
1396
1436
|
return r;
|
|
1397
1437
|
}), o = [];
|
|
1398
|
-
for (let t = 0;; t +=
|
|
1438
|
+
for (let t = 0;; t += Zt) {
|
|
1399
1439
|
let n = yield* e`
|
|
1400
1440
|
SELECT c.relname AS table_name,
|
|
1401
1441
|
COALESCE(c.reltuples::bigint, 0) AS row_count
|
|
@@ -1403,9 +1443,9 @@ BEGIN
|
|
|
1403
1443
|
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
1404
1444
|
WHERE n.nspname = current_schema() AND c.relkind = 'r'
|
|
1405
1445
|
ORDER BY c.relname
|
|
1406
|
-
LIMIT ${r(
|
|
1446
|
+
LIMIT ${r(Zt)} OFFSET ${r(t)}
|
|
1407
1447
|
`;
|
|
1408
|
-
if (o.push(...n), n.length <
|
|
1448
|
+
if (o.push(...n), n.length < Zt) break;
|
|
1409
1449
|
}
|
|
1410
1450
|
let s = /* @__PURE__ */ new Set(), c = [];
|
|
1411
1451
|
for (let e of o) s.has(e.table_name) || (s.add(e.table_name), c.push(e));
|
|
@@ -1415,7 +1455,7 @@ BEGIN
|
|
|
1415
1455
|
FROM information_schema.columns
|
|
1416
1456
|
WHERE table_schema = current_schema() AND table_name IN ${t}
|
|
1417
1457
|
ORDER BY table_name, ordinal_position
|
|
1418
|
-
`),
|
|
1458
|
+
`), d = yield* a(l, (t) => e`
|
|
1419
1459
|
SELECT rel.relname AS table_name, att.attname AS column_name
|
|
1420
1460
|
FROM pg_constraint con
|
|
1421
1461
|
JOIN pg_class rel ON rel.oid = con.conrelid
|
|
@@ -1424,7 +1464,7 @@ BEGIN
|
|
|
1424
1464
|
WHERE con.contype = 'p' AND nsp.nspname = current_schema()
|
|
1425
1465
|
AND rel.relname IN ${t}
|
|
1426
1466
|
ORDER BY rel.relname, array_position(con.conkey, att.attnum)
|
|
1427
|
-
`),
|
|
1467
|
+
`), f = yield* a(l, (t) => e`
|
|
1428
1468
|
SELECT rel.relname AS table_name, con.conname AS constraint_name,
|
|
1429
1469
|
att.attname AS column_name, array_position(con.conkey, att.attnum) AS position
|
|
1430
1470
|
FROM pg_constraint con
|
|
@@ -1470,7 +1510,7 @@ BEGIN
|
|
|
1470
1510
|
WHERE con.contype = 'f' AND nsp.nspname = current_schema()
|
|
1471
1511
|
AND rel.relname IN ${t}
|
|
1472
1512
|
ORDER BY rel.relname, con.conname, ck.ord
|
|
1473
|
-
`),
|
|
1513
|
+
`), _ = yield* a(l, (t) => e`
|
|
1474
1514
|
WITH ix AS (
|
|
1475
1515
|
SELECT
|
|
1476
1516
|
c.relname AS table_name,
|
|
@@ -1491,34 +1531,34 @@ BEGIN
|
|
|
1491
1531
|
AND c.relname IN ${t}
|
|
1492
1532
|
)
|
|
1493
1533
|
SELECT * FROM ix ORDER BY table_name, index_name, position
|
|
1494
|
-
`),
|
|
1534
|
+
`), v = /* @__PURE__ */ new Map(), y = /* @__PURE__ */ new Map(), b = /* @__PURE__ */ new Map(), x = /* @__PURE__ */ new Map(), S = /* @__PURE__ */ new Map(), ee = /* @__PURE__ */ new Map();
|
|
1495
1535
|
for (let e of u) {
|
|
1496
|
-
let t = _.get(e.table_name);
|
|
1497
|
-
t || (t = [], _.set(e.table_name, t)), t.push(e);
|
|
1498
|
-
}
|
|
1499
|
-
for (let e of f) {
|
|
1500
1536
|
let t = v.get(e.table_name);
|
|
1501
|
-
t || (t =
|
|
1537
|
+
t || (t = [], v.set(e.table_name, t)), t.push(e);
|
|
1538
|
+
}
|
|
1539
|
+
for (let e of d) {
|
|
1540
|
+
let t = y.get(e.table_name);
|
|
1541
|
+
t || (t = /* @__PURE__ */ new Set(), y.set(e.table_name, t)), t.add(e.column_name);
|
|
1502
1542
|
}
|
|
1503
|
-
let
|
|
1504
|
-
for (let e of
|
|
1505
|
-
let t = `${e.table_name}\u0000${e.constraint_name}`, n =
|
|
1543
|
+
let C = /* @__PURE__ */ new Map();
|
|
1544
|
+
for (let e of f) {
|
|
1545
|
+
let t = `${e.table_name}\u0000${e.constraint_name}`, n = C.get(t);
|
|
1506
1546
|
n || (n = {
|
|
1507
1547
|
table: e.table_name,
|
|
1508
1548
|
columns: []
|
|
1509
|
-
},
|
|
1549
|
+
}, C.set(t, n)), n.columns.push({
|
|
1510
1550
|
name: e.column_name,
|
|
1511
1551
|
position: e.position
|
|
1512
1552
|
});
|
|
1513
1553
|
}
|
|
1514
|
-
for (let [e, t] of
|
|
1554
|
+
for (let [e, t] of C) {
|
|
1515
1555
|
let n = [...t.columns].sort((e, t) => e.position - t.position).map((e) => e.name);
|
|
1516
1556
|
if (n.length === 1) {
|
|
1517
|
-
let e =
|
|
1518
|
-
e || (e = /* @__PURE__ */ new Set(),
|
|
1557
|
+
let e = b.get(t.table);
|
|
1558
|
+
e || (e = /* @__PURE__ */ new Set(), b.set(t.table, e)), e.add(n[0]);
|
|
1519
1559
|
} else {
|
|
1520
|
-
let r =
|
|
1521
|
-
r || (r = [],
|
|
1560
|
+
let r = x.get(t.table);
|
|
1561
|
+
r || (r = [], x.set(t.table, r)), r.push({
|
|
1522
1562
|
name: e.slice(e.indexOf("\0") + 1),
|
|
1523
1563
|
table: t.table,
|
|
1524
1564
|
columns: n,
|
|
@@ -1527,28 +1567,28 @@ BEGIN
|
|
|
1527
1567
|
}
|
|
1528
1568
|
}
|
|
1529
1569
|
for (let e of g) {
|
|
1530
|
-
let t =
|
|
1531
|
-
t || (t = [],
|
|
1570
|
+
let t = S.get(e.source_table);
|
|
1571
|
+
t || (t = [], S.set(e.source_table, t)), t.push(e);
|
|
1532
1572
|
}
|
|
1533
|
-
for (let e of
|
|
1534
|
-
let t =
|
|
1535
|
-
t || (t = /* @__PURE__ */ new Map(),
|
|
1573
|
+
for (let e of _) {
|
|
1574
|
+
let t = ee.get(e.table_name);
|
|
1575
|
+
t || (t = /* @__PURE__ */ new Map(), ee.set(e.table_name, t));
|
|
1536
1576
|
let n = t.get(e.index_name);
|
|
1537
1577
|
n || (n = [], t.set(e.index_name, n)), n.push(e);
|
|
1538
1578
|
}
|
|
1539
|
-
let
|
|
1579
|
+
let te = [];
|
|
1540
1580
|
for (let e of c) {
|
|
1541
|
-
let t = /* @__PURE__ */ new Set(), n = (
|
|
1542
|
-
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" :
|
|
1581
|
+
let t = /* @__PURE__ */ new Set(), n = (v.get(e.table_name) ?? []).filter((e) => t.has(e.column_name) ? !1 : (t.add(e.column_name), !0)), r = y.get(e.table_name) ?? /* @__PURE__ */ new Set(), i = b.get(e.table_name) ?? /* @__PURE__ */ new Set(), a = S.get(e.table_name) ?? [], o = new Map(a.map((e) => [e.source_column, e])), s = new Set(n.filter((e) => e.udt_name === "tsvector").map((e) => e.column_name)), c = n.filter((e) => !s.has(e.column_name)).map((t) => {
|
|
1582
|
+
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" : Jt(t.data_type, t.udt_name), c = a ? {
|
|
1543
1583
|
table: a.target_table,
|
|
1544
1584
|
column: a.target_column,
|
|
1545
|
-
onDelete:
|
|
1546
|
-
onUpdate:
|
|
1585
|
+
onDelete: z(a.delete_rule),
|
|
1586
|
+
onUpdate: z(a.update_rule)
|
|
1547
1587
|
} : void 0, l = h.get(`${e.table_name}.${t.column_name}`);
|
|
1548
1588
|
return {
|
|
1549
1589
|
name: t.column_name,
|
|
1550
1590
|
type: s,
|
|
1551
|
-
...
|
|
1591
|
+
...H(t.data_type, t.character_maximum_length) === void 0 ? {} : { maxLength: H(t.data_type, t.character_maximum_length) },
|
|
1552
1592
|
nullable: t.is_nullable === "YES",
|
|
1553
1593
|
unique: i.has(t.column_name) || n && t.column_name === "id",
|
|
1554
1594
|
hasDefault: t.column_default !== null,
|
|
@@ -1556,7 +1596,7 @@ BEGIN
|
|
|
1556
1596
|
...c ? { references: c } : {},
|
|
1557
1597
|
...l ? { oneOf: l } : {}
|
|
1558
1598
|
};
|
|
1559
|
-
}), l = [], u =
|
|
1599
|
+
}), l = [], u = ee.get(e.table_name);
|
|
1560
1600
|
if (u) for (let [t, n] of u) {
|
|
1561
1601
|
let r = /* @__PURE__ */ new Set(), i = [...n].filter((e) => e.column_name !== null && e.position !== null).sort((e, t) => (e.position ?? 0) - (t.position ?? 0)).filter((e) => {
|
|
1562
1602
|
let t = e.position;
|
|
@@ -1572,7 +1612,7 @@ BEGIN
|
|
|
1572
1612
|
...a ? { expression: !0 } : {}
|
|
1573
1613
|
});
|
|
1574
1614
|
}
|
|
1575
|
-
for (let t of
|
|
1615
|
+
for (let t of x.get(e.table_name) ?? []) l.push(t);
|
|
1576
1616
|
r.size > 0 && l.push({
|
|
1577
1617
|
name: `${e.table_name}_pkey`,
|
|
1578
1618
|
table: e.table_name,
|
|
@@ -1580,20 +1620,20 @@ BEGIN
|
|
|
1580
1620
|
unique: !0
|
|
1581
1621
|
});
|
|
1582
1622
|
let d = Number(e.row_count ?? 0);
|
|
1583
|
-
|
|
1623
|
+
te.push({
|
|
1584
1624
|
name: e.table_name,
|
|
1585
1625
|
columns: c,
|
|
1586
1626
|
indexes: l,
|
|
1587
1627
|
...d > 0 ? { rowCount: d } : {}
|
|
1588
1628
|
});
|
|
1589
1629
|
}
|
|
1590
|
-
return { tables:
|
|
1630
|
+
return { tables: te };
|
|
1591
1631
|
});
|
|
1592
|
-
return (t > 0 ? e.withTransaction(n) : n).pipe(
|
|
1632
|
+
return (t > 0 ? e.withTransaction(n) : n).pipe(p.catchAll((e) => {
|
|
1593
1633
|
let n = `${String(e)} ${String(e.cause ?? "")}`.toLowerCase();
|
|
1594
|
-
return n.includes("statement timeout") || n.includes("57014") ?
|
|
1634
|
+
return n.includes("statement timeout") || n.includes("57014") ? p.die(/* @__PURE__ */ Error(`schema introspection exceeded VOLTRO_INTROSPECT_TIMEOUT_MS (${t}ms) — the schema is very large / FK-dense or the database is slow. Raise VOLTRO_INTROSPECT_TIMEOUT_MS, set it to 0 to disable the ceiling, or use DB_DIRECT_URL for a non-pooler connection.`)) : p.fail(e);
|
|
1595
1635
|
}));
|
|
1596
|
-
},
|
|
1636
|
+
}, tn = (e, t) => {
|
|
1597
1637
|
switch (e.toLowerCase()) {
|
|
1598
1638
|
case "varchar":
|
|
1599
1639
|
case "char":
|
|
@@ -1625,21 +1665,21 @@ BEGIN
|
|
|
1625
1665
|
case "json": return "json";
|
|
1626
1666
|
default: return "text";
|
|
1627
1667
|
}
|
|
1628
|
-
},
|
|
1668
|
+
}, B = (e) => e.map((e) => {
|
|
1629
1669
|
let t = e, n = {};
|
|
1630
1670
|
for (let e of Object.keys(t)) n[e.toLowerCase()] = t[e];
|
|
1631
1671
|
return n;
|
|
1632
|
-
}),
|
|
1633
|
-
let t =
|
|
1672
|
+
}), nn = (e) => p.gen(function* () {
|
|
1673
|
+
let t = B(yield* e`
|
|
1634
1674
|
SELECT table_name, COALESCE(table_rows, 0) AS table_rows
|
|
1635
1675
|
FROM information_schema.tables
|
|
1636
1676
|
WHERE table_schema = DATABASE() AND table_type = 'BASE TABLE'
|
|
1637
|
-
ORDER BY table_name`), n =
|
|
1677
|
+
ORDER BY table_name`), n = B(yield* e`
|
|
1638
1678
|
SELECT table_name, column_name, data_type, column_type, is_nullable, column_default, column_key,
|
|
1639
1679
|
character_maximum_length
|
|
1640
1680
|
FROM information_schema.columns
|
|
1641
1681
|
WHERE table_schema = DATABASE()
|
|
1642
|
-
ORDER BY table_name, ordinal_position`), r =
|
|
1682
|
+
ORDER BY table_name, ordinal_position`), r = B(yield* e`
|
|
1643
1683
|
SELECT k.table_name AS source_table,
|
|
1644
1684
|
k.column_name AS source_column,
|
|
1645
1685
|
k.referenced_table_name AS target_table,
|
|
@@ -1649,21 +1689,21 @@ BEGIN
|
|
|
1649
1689
|
JOIN information_schema.referential_constraints r
|
|
1650
1690
|
ON k.constraint_name = r.constraint_name
|
|
1651
1691
|
AND k.table_schema = r.constraint_schema
|
|
1652
|
-
WHERE k.table_schema = DATABASE() AND k.referenced_table_name IS NOT NULL`), i =
|
|
1692
|
+
WHERE k.table_schema = DATABASE() AND k.referenced_table_name IS NOT NULL`), i = B(yield* e`
|
|
1653
1693
|
SELECT table_name, index_name, non_unique, column_name, seq_in_index
|
|
1654
1694
|
FROM information_schema.statistics
|
|
1655
1695
|
WHERE table_schema = DATABASE() AND index_name <> 'PRIMARY'
|
|
1656
|
-
ORDER BY table_name, index_name, seq_in_index`), a =
|
|
1696
|
+
ORDER BY table_name, index_name, seq_in_index`), a = B(yield* e`
|
|
1657
1697
|
SELECT table_name, check_clause
|
|
1658
1698
|
FROM information_schema.check_constraints
|
|
1659
|
-
WHERE constraint_schema = DATABASE()`.unprepared.pipe(
|
|
1699
|
+
WHERE constraint_schema = DATABASE()`.unprepared.pipe(p.orElseSucceed(() => []))), o = /* @__PURE__ */ new Set(), s = /* @__PURE__ */ new Map();
|
|
1660
1700
|
for (let e of a) {
|
|
1661
1701
|
let t = /json_valid\(`?([^`)]+)`?\)/i.exec(e.check_clause);
|
|
1662
1702
|
if (t) {
|
|
1663
1703
|
o.add(`${e.table_name}.${t[1]}`);
|
|
1664
1704
|
continue;
|
|
1665
1705
|
}
|
|
1666
|
-
let n =
|
|
1706
|
+
let n = qt(e.check_clause);
|
|
1667
1707
|
n && s.set(`${e.table_name}.${n.column}`, n.values);
|
|
1668
1708
|
}
|
|
1669
1709
|
let c = /* @__PURE__ */ new Map(), l = /* @__PURE__ */ new Map(), u = /* @__PURE__ */ new Map();
|
|
@@ -1681,22 +1721,22 @@ BEGIN
|
|
|
1681
1721
|
let n = t.get(e.index_name) ?? [];
|
|
1682
1722
|
n.push(e), t.set(e.index_name, n);
|
|
1683
1723
|
}
|
|
1684
|
-
let
|
|
1724
|
+
let d = [];
|
|
1685
1725
|
for (let e of t) {
|
|
1686
|
-
let t = c.get(e.table_name) ?? [], n = l.get(e.table_name) ?? [], r = new Map(n.map((e) => [e.source_column, e])), i = t.filter((e) => e.column_key === "PRI").map((e) => e.column_name), a = /* @__PURE__ */ new Set(),
|
|
1687
|
-
if (
|
|
1726
|
+
let t = c.get(e.table_name) ?? [], n = l.get(e.table_name) ?? [], r = new Map(n.map((e) => [e.source_column, e])), i = t.filter((e) => e.column_key === "PRI").map((e) => e.column_name), a = /* @__PURE__ */ new Set(), f = u.get(e.table_name);
|
|
1727
|
+
if (f) for (let e of f.values()) e.length === 1 && e[0].non_unique === 0 && a.add(e[0].column_name);
|
|
1688
1728
|
let p = t.map((t) => {
|
|
1689
|
-
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" :
|
|
1729
|
+
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" : tn(t.data_type, t.column_type), u = c ? {
|
|
1690
1730
|
table: c.target_table,
|
|
1691
1731
|
column: c.target_column,
|
|
1692
|
-
onDelete:
|
|
1693
|
-
onUpdate:
|
|
1732
|
+
onDelete: z(c.delete_rule),
|
|
1733
|
+
onUpdate: z(c.update_rule)
|
|
1694
1734
|
} : 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}`);
|
|
1695
1735
|
return {
|
|
1696
1736
|
name: t.column_name,
|
|
1697
1737
|
type: l,
|
|
1698
1738
|
nullable: t.is_nullable === "YES",
|
|
1699
|
-
...
|
|
1739
|
+
...H(t.data_type, t.character_maximum_length) === void 0 ? {} : { maxLength: H(t.data_type, t.character_maximum_length) },
|
|
1700
1740
|
unique: d,
|
|
1701
1741
|
hasDefault: f !== null,
|
|
1702
1742
|
...f === null ? {} : { defaultValue: f },
|
|
@@ -1704,7 +1744,7 @@ BEGIN
|
|
|
1704
1744
|
...p ? { oneOf: p } : {}
|
|
1705
1745
|
};
|
|
1706
1746
|
}), m = new Set(n.map((e) => e.source_column)), h = [];
|
|
1707
|
-
if (
|
|
1747
|
+
if (f) for (let [t, n] of f) {
|
|
1708
1748
|
let r = [...n].sort((e, t) => e.seq_in_index - t.seq_in_index);
|
|
1709
1749
|
r.length === 1 && r[0].non_unique === 0 || r.length === 1 && r[0].non_unique !== 0 && m.has(r[0].column_name) && t !== `${e.table_name}_${r[0].column_name}_idx` || h.push({
|
|
1710
1750
|
name: t,
|
|
@@ -1720,18 +1760,18 @@ BEGIN
|
|
|
1720
1760
|
unique: !0
|
|
1721
1761
|
});
|
|
1722
1762
|
let g = Number(e.table_rows ?? 0);
|
|
1723
|
-
|
|
1763
|
+
d.push({
|
|
1724
1764
|
name: e.table_name,
|
|
1725
1765
|
columns: p,
|
|
1726
1766
|
indexes: h,
|
|
1727
1767
|
...g > 0 ? { rowCount: g } : {}
|
|
1728
1768
|
});
|
|
1729
1769
|
}
|
|
1730
|
-
return { tables:
|
|
1731
|
-
}),
|
|
1770
|
+
return { tables: d };
|
|
1771
|
+
}), rn = (e) => {
|
|
1732
1772
|
let t = e.toUpperCase();
|
|
1733
1773
|
return t.includes("INT") ? "integer" : t.includes("REAL") || t.includes("FLOA") || t.includes("DOUB") ? "real" : t.includes("BLOB") ? "bytes" : "text";
|
|
1734
|
-
},
|
|
1774
|
+
}, an = (e) => p.gen(function* () {
|
|
1735
1775
|
let t = yield* e`
|
|
1736
1776
|
SELECT name, sql FROM sqlite_master
|
|
1737
1777
|
WHERE type = 'table' AND name NOT LIKE 'sqlite_%'
|
|
@@ -1748,11 +1788,11 @@ BEGIN
|
|
|
1748
1788
|
d.set(t.name, r), t.unique === 1 && r.length === 1 && f.add(r[0]);
|
|
1749
1789
|
}
|
|
1750
1790
|
let p = o.map((e) => {
|
|
1751
|
-
let n = u.length === 1 && u[0] === e.name, r = l.get(e.name), i = r ? "reference" : n && e.name === "id" ? "id" :
|
|
1791
|
+
let n = u.length === 1 && u[0] === e.name, r = l.get(e.name), i = r ? "reference" : n && e.name === "id" ? "id" : rn(e.type), a = r ? {
|
|
1752
1792
|
table: r.table,
|
|
1753
1793
|
column: r.to,
|
|
1754
|
-
onDelete:
|
|
1755
|
-
onUpdate:
|
|
1794
|
+
onDelete: z(r.on_delete),
|
|
1795
|
+
onUpdate: z(r.on_update)
|
|
1756
1796
|
} : void 0, o = t.get(e.name);
|
|
1757
1797
|
return {
|
|
1758
1798
|
name: e.name,
|
|
@@ -1787,7 +1827,7 @@ BEGIN
|
|
|
1787
1827
|
});
|
|
1788
1828
|
}
|
|
1789
1829
|
return { tables: r };
|
|
1790
|
-
}),
|
|
1830
|
+
}), on = (e) => {
|
|
1791
1831
|
switch (e.toLowerCase()) {
|
|
1792
1832
|
case "int":
|
|
1793
1833
|
case "smallint":
|
|
@@ -1809,7 +1849,7 @@ BEGIN
|
|
|
1809
1849
|
case "image": return "bytes";
|
|
1810
1850
|
default: return "text";
|
|
1811
1851
|
}
|
|
1812
|
-
},
|
|
1852
|
+
}, V = (e) => e === !0 || e === 1, sn = (e) => p.gen(function* () {
|
|
1813
1853
|
let t = yield* e`
|
|
1814
1854
|
SELECT TABLE_NAME AS table_name FROM INFORMATION_SCHEMA.TABLES
|
|
1815
1855
|
WHERE TABLE_TYPE = 'BASE TABLE' ORDER BY TABLE_NAME`, n = yield* e`
|
|
@@ -1866,21 +1906,21 @@ BEGIN
|
|
|
1866
1906
|
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();
|
|
1867
1907
|
if (a) for (let [, e] of a) {
|
|
1868
1908
|
let t = [...e].sort((e, t) => e.key_ordinal - t.key_ordinal);
|
|
1869
|
-
if (
|
|
1870
|
-
else
|
|
1909
|
+
if (V(t[0].is_primary)) for (let e of t) d.push(e.column_name);
|
|
1910
|
+
else V(t[0].is_unique) && t.length === 1 && f.add(t[0].column_name);
|
|
1871
1911
|
}
|
|
1872
1912
|
let p = t.map((t) => {
|
|
1873
|
-
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" :
|
|
1913
|
+
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" : on(t.data_type), s = i ? {
|
|
1874
1914
|
table: i.target_table,
|
|
1875
1915
|
column: i.target_column,
|
|
1876
|
-
onDelete:
|
|
1877
|
-
onUpdate:
|
|
1878
|
-
} : void 0, c = t.column_default === null ? null :
|
|
1916
|
+
onDelete: cn(i.delete_rule),
|
|
1917
|
+
onUpdate: cn(i.update_rule)
|
|
1918
|
+
} : void 0, c = t.column_default === null ? null : ln(t.column_default), l = o.get(`${e}.${t.column_name}`);
|
|
1879
1919
|
return {
|
|
1880
1920
|
name: t.column_name,
|
|
1881
1921
|
type: a,
|
|
1882
1922
|
nullable: t.is_nullable === "YES",
|
|
1883
|
-
...
|
|
1923
|
+
...H(t.data_type, t.character_maximum_length) === void 0 ? {} : { maxLength: H(t.data_type, t.character_maximum_length) },
|
|
1884
1924
|
unique: f.has(t.column_name) || n && t.column_name === "id",
|
|
1885
1925
|
hasDefault: c !== null,
|
|
1886
1926
|
...c === null ? {} : { defaultValue: c },
|
|
@@ -1890,11 +1930,11 @@ BEGIN
|
|
|
1890
1930
|
}), m = [];
|
|
1891
1931
|
if (a) for (let [t, n] of a) {
|
|
1892
1932
|
let r = [...n].sort((e, t) => e.key_ordinal - t.key_ordinal);
|
|
1893
|
-
|
|
1933
|
+
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({
|
|
1894
1934
|
name: t,
|
|
1895
1935
|
table: e,
|
|
1896
1936
|
columns: r.map((e) => e.column_name),
|
|
1897
|
-
unique:
|
|
1937
|
+
unique: V(r[0].is_unique)
|
|
1898
1938
|
});
|
|
1899
1939
|
}
|
|
1900
1940
|
d.length > 0 && m.push({
|
|
@@ -1909,48 +1949,51 @@ BEGIN
|
|
|
1909
1949
|
});
|
|
1910
1950
|
}
|
|
1911
1951
|
return { tables: u };
|
|
1912
|
-
}),
|
|
1952
|
+
}), cn = (e) => {
|
|
1913
1953
|
switch (e.toUpperCase().replace(/_/g, " ")) {
|
|
1914
1954
|
case "CASCADE": return "cascade";
|
|
1915
1955
|
case "SET NULL": return "setNull";
|
|
1916
1956
|
case "SET DEFAULT": return "noAction";
|
|
1917
1957
|
default: return "noAction";
|
|
1918
1958
|
}
|
|
1919
|
-
},
|
|
1959
|
+
}, ln = (e) => {
|
|
1920
1960
|
let t = e.trim();
|
|
1921
1961
|
for (; t.startsWith("(") && t.endsWith(")");) t = t.slice(1, -1).trim();
|
|
1922
1962
|
return t.startsWith("N'") && t.endsWith("'") ? t = t.slice(2, -1) : t.startsWith("'") && t.endsWith("'") && (t = t.slice(1, -1)), t;
|
|
1923
|
-
},
|
|
1963
|
+
}, un = /* @__PURE__ */ new Set([
|
|
1924
1964
|
"character varying",
|
|
1925
1965
|
"varchar",
|
|
1926
1966
|
"nvarchar",
|
|
1927
1967
|
"char",
|
|
1928
1968
|
"character",
|
|
1929
1969
|
"nchar"
|
|
1930
|
-
]),
|
|
1931
|
-
if (!
|
|
1970
|
+
]), H = (e, t) => {
|
|
1971
|
+
if (!un.has(e.toLowerCase())) return;
|
|
1932
1972
|
let n = Number(t);
|
|
1933
1973
|
if (!(!Number.isFinite(n) || n <= 0)) return n;
|
|
1934
|
-
},
|
|
1935
|
-
mysql: () =>
|
|
1936
|
-
mssql: () =>
|
|
1937
|
-
sqlite: () =>
|
|
1938
|
-
orElse: () =>
|
|
1939
|
-
}),
|
|
1974
|
+
}, U = (e) => e.onDialectOrElse({
|
|
1975
|
+
mysql: () => nn(e),
|
|
1976
|
+
mssql: () => sn(e),
|
|
1977
|
+
sqlite: () => an(e),
|
|
1978
|
+
orElse: () => en(e)
|
|
1979
|
+
}), dn = (e, t) => e.onDialectOrElse({
|
|
1980
|
+
mysql: () => p.succeed(t.replace("T", " ").replace("Z", "")),
|
|
1981
|
+
orElse: () => p.succeed(t)
|
|
1982
|
+
}), fn = (e, t) => `${t}(${e.precision ?? 38}, ${e.scale ?? 0})`, W = (e, t) => {
|
|
1940
1983
|
if (!(e.type === "text" && e.maxLength !== void 0) && !e.spatial && e.type !== "vector" && e.type !== "array" && e.type !== "enum") return;
|
|
1941
1984
|
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;
|
|
1942
1985
|
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.`);
|
|
1943
|
-
return
|
|
1944
|
-
},
|
|
1945
|
-
let t =
|
|
1946
|
-
return t === void 0 ?
|
|
1947
|
-
},
|
|
1986
|
+
return j(e, t);
|
|
1987
|
+
}, pn = (e) => {
|
|
1988
|
+
let t = W(e, "postgres");
|
|
1989
|
+
return t === void 0 ? mn(e) : t;
|
|
1990
|
+
}, mn = (e) => {
|
|
1948
1991
|
switch (e.type) {
|
|
1949
1992
|
case "id": return "text";
|
|
1950
1993
|
case "text": return "text";
|
|
1951
1994
|
case "integer": return "integer";
|
|
1952
1995
|
case "real": return "double precision";
|
|
1953
|
-
case "decimal": return
|
|
1996
|
+
case "decimal": return fn(e, "NUMERIC");
|
|
1954
1997
|
case "bigint": return "bigint";
|
|
1955
1998
|
case "boolean": return "boolean";
|
|
1956
1999
|
case "timestamp": return "timestamptz";
|
|
@@ -1964,15 +2007,15 @@ BEGIN
|
|
|
1964
2007
|
case "interval": return "interval";
|
|
1965
2008
|
case "raw": return e.rawDdl ?? "text";
|
|
1966
2009
|
}
|
|
1967
|
-
},
|
|
1968
|
-
let t =
|
|
2010
|
+
}, hn = (e) => {
|
|
2011
|
+
let t = W(e, "mariadb");
|
|
1969
2012
|
if (t !== void 0) return t;
|
|
1970
2013
|
switch (e.type) {
|
|
1971
2014
|
case "id": return "VARCHAR(64)";
|
|
1972
2015
|
case "text": return e.hasDefault && e.defaultValue !== void 0 || e.oneOf && e.oneOf.length > 0 ? "VARCHAR(255)" : "LONGTEXT";
|
|
1973
2016
|
case "integer": return "INT";
|
|
1974
2017
|
case "real": return "DOUBLE";
|
|
1975
|
-
case "decimal": return
|
|
2018
|
+
case "decimal": return fn(e, "DECIMAL");
|
|
1976
2019
|
case "bigint": return "BIGINT";
|
|
1977
2020
|
case "boolean": return "TINYINT(1)";
|
|
1978
2021
|
case "timestamp": return "DATETIME(6)";
|
|
@@ -1986,32 +2029,32 @@ BEGIN
|
|
|
1986
2029
|
case "interval": return "BIGINT";
|
|
1987
2030
|
case "raw": return e.rawDdl ?? "LONGTEXT";
|
|
1988
2031
|
}
|
|
1989
|
-
}, G = (e) => `"${e}"`, K = (e) => `\`${e.replace(/`/g, "``")}\``,
|
|
2032
|
+
}, G = (e) => `"${e}"`, K = (e) => `\`${e.replace(/`/g, "``")}\``, gn = (e, t) => `ALTER TABLE ${t(e.table)} DROP COLUMN IF EXISTS ${t(e.column)};`, _n = (e) => `ALTER TABLE ${K(e.table)} DROP COLUMN IF EXISTS ${K(e.column)}, ALGORITHM=COPY;`, vn = (e, t) => `ALTER TABLE ${t(e.table)} RENAME COLUMN ${t(e.from)} TO ${t(e.to)};`, yn = (e, t, n) => `ALTER TABLE ${n(e.table)} RENAME COLUMN ${n(e.column)} TO ${n(`${e.column}${Wn(t)}`)};`, bn = (e, t, n) => e.hasDefault && e.defaultValue !== void 0 ? `ALTER TABLE ${t(e.table)} ALTER COLUMN ${t(e.column)} SET ${xn(e, n)};` : `ALTER TABLE ${t(e.table)} ALTER COLUMN ${t(e.column)} DROP DEFAULT;`, xn = (e, t) => {
|
|
1990
2033
|
let n = t(e.defaultValue);
|
|
1991
2034
|
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.`);
|
|
1992
2035
|
return n;
|
|
1993
|
-
}, q = (e, t, n) => e.find((e) => e.tableName === t)?.fields[n],
|
|
2036
|
+
}, q = (e, t, n) => e.find((e) => e.tableName === t)?.fields[n], Sn = (e, t, n) => {
|
|
1994
2037
|
let r = q(e, t, n);
|
|
1995
|
-
return r ?
|
|
1996
|
-
},
|
|
1997
|
-
let r = n ?? e.nullable, i =
|
|
2038
|
+
return r ? ct(n, r) : void 0;
|
|
2039
|
+
}, Cn = (e, t, n) => {
|
|
2040
|
+
let r = n ?? e.nullable, i = be(e, "mariadb");
|
|
1998
2041
|
return [
|
|
1999
2042
|
K(t),
|
|
2000
|
-
|
|
2043
|
+
j(e, "mariadb"),
|
|
2001
2044
|
r ? null : "NOT NULL",
|
|
2002
2045
|
i
|
|
2003
2046
|
].filter(Boolean).join(" ");
|
|
2004
|
-
},
|
|
2047
|
+
}, wn = (e, t) => e == null ? null : Array.isArray(e) ? Tn(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, "mysql") : null, Tn = (e, t, n) => xe(e, t ?? { type: "json" }, n), En = (e, t) => e == null ? null : Array.isArray(e) ? Tn(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, J = (e, t) => {
|
|
2005
2048
|
if (!e.generatedAs) return null;
|
|
2006
2049
|
let { expr: n, stored: r } = e.generatedAs;
|
|
2007
2050
|
return t === "postgres" ? `GENERATED ALWAYS AS (${n}) STORED` : t === "mssql" ? `AS (${n})${r ? " PERSISTED" : ""}` : `GENERATED ALWAYS AS (${n}) ${r ? "STORED" : "VIRTUAL"}`;
|
|
2008
|
-
},
|
|
2009
|
-
if (e.type === "id" && e.idScheme?.kind === "numeric") return
|
|
2051
|
+
}, Dn = (e) => {
|
|
2052
|
+
if (e.type === "id" && e.idScheme?.kind === "numeric") return N(e.name, "postgres");
|
|
2010
2053
|
let t = J(e, "postgres");
|
|
2011
|
-
if (t) return `${G(e.name)} ${
|
|
2012
|
-
let n = [G(e.name),
|
|
2054
|
+
if (t) return `${G(e.name)} ${pn(e)} ${t}`;
|
|
2055
|
+
let n = [G(e.name), pn(e)];
|
|
2013
2056
|
if (e.type === "id" && n.push("PRIMARY KEY"), e.nullable || n.push("NOT NULL"), e.unique && e.type !== "id" && n.push("UNIQUE"), e.hasDefault) {
|
|
2014
|
-
let t =
|
|
2057
|
+
let t = En(e.defaultValue, e);
|
|
2015
2058
|
t && n.push(t);
|
|
2016
2059
|
}
|
|
2017
2060
|
return e.references && (n.push(`REFERENCES ${G(e.references.table)} (${G(e.references.column)})`), e.references.onDelete && n.push(`ON DELETE ${{
|
|
@@ -2020,20 +2063,20 @@ BEGIN
|
|
|
2020
2063
|
setNull: "SET NULL",
|
|
2021
2064
|
noAction: "NO ACTION"
|
|
2022
2065
|
}[e.references.onDelete]}`)), n.join(" ");
|
|
2023
|
-
},
|
|
2024
|
-
let t = e.columns.map((e) => ` ${
|
|
2025
|
-
for (let t of e.indexes) t.name !== `${e.table}_pkey` && n.push(`CREATE ${t.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${G(t.name)} ON ${G(e.table)} (${t.columns.map(
|
|
2066
|
+
}, On = (e, t) => e.primaryKey && e.primaryKey.length > 0 ? `,\n PRIMARY KEY (${e.primaryKey.map(t).join(", ")})` : "", kn = (e) => {
|
|
2067
|
+
let t = e.columns.map((e) => ` ${Dn(e)}`).join(",\n"), n = [`CREATE TABLE IF NOT EXISTS ${G(e.table)} (\n${t}${On(e, G)}\n)`];
|
|
2068
|
+
for (let t of e.indexes) t.name !== `${e.table}_pkey` && n.push(`CREATE ${t.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${G(t.name)} ON ${G(e.table)} (${t.columns.map(Yn).join(", ")})`);
|
|
2026
2069
|
return n;
|
|
2027
|
-
},
|
|
2028
|
-
let t =
|
|
2070
|
+
}, An = 191, jn = (e) => {
|
|
2071
|
+
let t = hn(e).toUpperCase();
|
|
2029
2072
|
return t.endsWith("TEXT") || t.endsWith("BLOB");
|
|
2030
|
-
},
|
|
2031
|
-
if (e.type === "id" && e.idScheme?.kind === "numeric") return
|
|
2073
|
+
}, Mn = (e) => {
|
|
2074
|
+
if (e.type === "id" && e.idScheme?.kind === "numeric") return N(e.name, "mariadb");
|
|
2032
2075
|
let t = J(e, "mariadb");
|
|
2033
|
-
if (t) return `${K(e.name)} ${
|
|
2034
|
-
let n = [K(e.name),
|
|
2035
|
-
if (e.type === "id" && n.push("PRIMARY KEY"), e.nullable || n.push("NOT NULL"), e.unique && e.type !== "id" && !
|
|
2036
|
-
let t =
|
|
2076
|
+
if (t) return `${K(e.name)} ${hn(e)} ${t}`;
|
|
2077
|
+
let n = [K(e.name), hn(e)];
|
|
2078
|
+
if (e.type === "id" && n.push("PRIMARY KEY"), e.nullable || n.push("NOT NULL"), e.unique && e.type !== "id" && !jn(e) && n.push("UNIQUE"), e.hasDefault) {
|
|
2079
|
+
let t = wn(e.defaultValue, e);
|
|
2037
2080
|
t && n.push(t);
|
|
2038
2081
|
}
|
|
2039
2082
|
return e.references && (n.push(`REFERENCES ${K(e.references.table)} (${K(e.references.column)})`), e.references.onDelete && n.push(`ON DELETE ${{
|
|
@@ -2042,26 +2085,26 @@ BEGIN
|
|
|
2042
2085
|
setNull: "SET NULL",
|
|
2043
2086
|
noAction: "NO ACTION"
|
|
2044
2087
|
}[e.references.onDelete]}`)), n.join(" ");
|
|
2045
|
-
},
|
|
2088
|
+
}, Nn = (e) => {
|
|
2046
2089
|
let t = new Map(e.columns.map((e) => [e.name, e])), n = (e) => {
|
|
2047
2090
|
let n = t.get(e);
|
|
2048
|
-
return n &&
|
|
2049
|
-
}, r = e.columns.map((e) => ` ${
|
|
2050
|
-
for (let t of e.columns) t.unique && t.type !== "id" &&
|
|
2091
|
+
return n && jn(n) ? `${K(e)}(${An})` : K(e);
|
|
2092
|
+
}, r = e.columns.map((e) => ` ${Mn(e)}`).join(",\n"), i = [`CREATE TABLE IF NOT EXISTS ${K(e.table)} (\n${r}${On(e, n)}\n)`];
|
|
2093
|
+
for (let t of e.columns) t.unique && t.type !== "id" && jn(t) && i.push(`CREATE UNIQUE INDEX IF NOT EXISTS ${K(`${e.table}_${t.name}_key`)} ON ${K(e.table)} (${n(t.name)})`);
|
|
2051
2094
|
for (let t of e.indexes) t.name !== `${e.table}_pkey` && i.push(`CREATE ${t.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${K(t.name)} ON ${K(e.table)} (${t.columns.map(n).join(", ")})`);
|
|
2052
2095
|
return i;
|
|
2053
|
-
},
|
|
2096
|
+
}, Pn = {
|
|
2054
2097
|
cascade: "CASCADE",
|
|
2055
2098
|
restrict: "RESTRICT",
|
|
2056
2099
|
setNull: "SET NULL",
|
|
2057
2100
|
noAction: "NO ACTION"
|
|
2058
|
-
},
|
|
2101
|
+
}, Fn = {
|
|
2059
2102
|
cascade: "CASCADE",
|
|
2060
2103
|
restrict: "NO ACTION",
|
|
2061
2104
|
setNull: "SET NULL",
|
|
2062
2105
|
noAction: "NO ACTION"
|
|
2063
|
-
},
|
|
2064
|
-
let t =
|
|
2106
|
+
}, In = (e) => {
|
|
2107
|
+
let t = W(e, "sqlite");
|
|
2065
2108
|
if (t !== void 0) return t;
|
|
2066
2109
|
switch (e.type) {
|
|
2067
2110
|
case "integer":
|
|
@@ -2072,29 +2115,29 @@ BEGIN
|
|
|
2072
2115
|
case "bytes": return "BLOB";
|
|
2073
2116
|
default: return "TEXT";
|
|
2074
2117
|
}
|
|
2075
|
-
}, Y = (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" ?
|
|
2076
|
-
if (e.type === "id" && e.idScheme?.kind === "numeric") return
|
|
2118
|
+
}, Y = (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, Ln = (e) => {
|
|
2119
|
+
if (e.type === "id" && e.idScheme?.kind === "numeric") return N(e.name, "sqlite");
|
|
2077
2120
|
let t = J(e, "sqlite");
|
|
2078
|
-
if (t) return `${G(e.name)} ${
|
|
2079
|
-
let n = [G(e.name),
|
|
2121
|
+
if (t) return `${G(e.name)} ${In(e)} ${t}`;
|
|
2122
|
+
let n = [G(e.name), In(e)];
|
|
2080
2123
|
if (e.type === "id" && n.push("PRIMARY KEY"), e.nullable || n.push("NOT NULL"), e.unique && e.type !== "id" && n.push("UNIQUE"), e.hasDefault) {
|
|
2081
2124
|
let t = Y(e.defaultValue, "CURRENT_TIMESTAMP");
|
|
2082
2125
|
t && n.push(t);
|
|
2083
2126
|
}
|
|
2084
|
-
if (e.references && (n.push(`REFERENCES ${G(e.references.table)} (${G(e.references.column)})`), e.references.onDelete && n.push(`ON DELETE ${
|
|
2127
|
+
if (e.references && (n.push(`REFERENCES ${G(e.references.table)} (${G(e.references.column)})`), e.references.onDelete && n.push(`ON DELETE ${Pn[e.references.onDelete]}`)), e.oneOf && e.oneOf.length > 0) {
|
|
2085
2128
|
let t = e.oneOf.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ");
|
|
2086
2129
|
n.push(`CHECK (${G(e.name)} IN (${t}))`);
|
|
2087
2130
|
}
|
|
2088
2131
|
return n.join(" ");
|
|
2089
|
-
},
|
|
2090
|
-
let t = e.columns.map((e) => ` ${
|
|
2132
|
+
}, Rn = (e) => {
|
|
2133
|
+
let t = e.columns.map((e) => ` ${Ln(e)}`).join(",\n"), n = [`CREATE TABLE IF NOT EXISTS ${G(e.table)} (\n${t}${On(e, G)}\n)`];
|
|
2091
2134
|
for (let t of e.indexes) t.name !== `${e.table}_pkey` && n.push(`CREATE ${t.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${G(t.name)} ON ${G(e.table)} (${t.columns.map(G).join(", ")})`);
|
|
2092
2135
|
return n;
|
|
2093
2136
|
}, X = (e, t) => {
|
|
2094
2137
|
let n = t.find((t) => t.tableName === e);
|
|
2095
2138
|
if (!n) return [];
|
|
2096
|
-
let r =
|
|
2097
|
-
...
|
|
2139
|
+
let r = I([n]).tables[0], i = `${e}__voltro_rebuild`, a = r.columns.map((e) => G(e.name)).join(", "), o = [
|
|
2140
|
+
...Rn({
|
|
2098
2141
|
kind: "create-table",
|
|
2099
2142
|
table: i,
|
|
2100
2143
|
columns: r.columns,
|
|
@@ -2107,25 +2150,25 @@ BEGIN
|
|
|
2107
2150
|
];
|
|
2108
2151
|
for (let t of r.indexes) t.name !== `${e}_pkey` && o.push(`CREATE ${t.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${G(t.name)} ON ${G(e)} (${t.columns.map(G).join(", ")})`);
|
|
2109
2152
|
return o;
|
|
2110
|
-
},
|
|
2153
|
+
}, zn = (e, t, n, r) => {
|
|
2111
2154
|
let i = r ?? e.nullable;
|
|
2112
|
-
return `ALTER TABLE ${Z(n)} ALTER COLUMN ${Z(t)} ${
|
|
2113
|
-
},
|
|
2155
|
+
return `ALTER TABLE ${Z(n)} ALTER COLUMN ${Z(t)} ${j(e, "mssql")} ${i ? "NULL" : "NOT NULL"};`;
|
|
2156
|
+
}, Bn = (e) => {
|
|
2114
2157
|
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 ${Z(e.table)} DROP CONSTRAINT [' + @dc + ']');`;
|
|
2115
2158
|
if (!e.hasDefault) return t;
|
|
2116
2159
|
let n = Y(e.defaultValue, "SYSUTCDATETIME()");
|
|
2117
2160
|
if (!n) return t;
|
|
2118
2161
|
let r = n.replace(/^DEFAULT\s+/i, "");
|
|
2119
2162
|
return `${t} ALTER TABLE ${Z(e.table)} ADD DEFAULT ${r} FOR ${Z(e.column)};`;
|
|
2120
|
-
}, Z = (e) => `[${e.replace(/]/g, "]]")}]`,
|
|
2121
|
-
let t =
|
|
2163
|
+
}, Z = (e) => `[${e.replace(/]/g, "]]")}]`, Vn = (e) => {
|
|
2164
|
+
let t = W(e, "mssql");
|
|
2122
2165
|
if (t !== void 0) return t;
|
|
2123
2166
|
switch (e.type) {
|
|
2124
2167
|
case "id":
|
|
2125
2168
|
case "reference": return "NVARCHAR(64)";
|
|
2126
2169
|
case "integer": return "INT";
|
|
2127
2170
|
case "real": return "FLOAT";
|
|
2128
|
-
case "decimal": return
|
|
2171
|
+
case "decimal": return fn(e, "DECIMAL");
|
|
2129
2172
|
case "bigint": return "BIGINT";
|
|
2130
2173
|
case "boolean": return "BIT";
|
|
2131
2174
|
case "timestamp": return "DATETIME2(6)";
|
|
@@ -2133,40 +2176,40 @@ BEGIN
|
|
|
2133
2176
|
case "bytes": return "VARBINARY(MAX)";
|
|
2134
2177
|
default: return "NVARCHAR(MAX)";
|
|
2135
2178
|
}
|
|
2136
|
-
},
|
|
2137
|
-
if (e.type === "id" && e.idScheme?.kind === "numeric") return
|
|
2138
|
-
let n = e.type === "text" && t?.has(e.name) ? "NVARCHAR(450)" :
|
|
2179
|
+
}, Hn = (e, t) => {
|
|
2180
|
+
if (e.type === "id" && e.idScheme?.kind === "numeric") return N(e.name, "mssql");
|
|
2181
|
+
let n = e.type === "text" && t?.has(e.name) ? "NVARCHAR(450)" : Vn(e), r = J(e, "mssql");
|
|
2139
2182
|
if (r) return `${Z(e.name)} ${r}`;
|
|
2140
2183
|
let i = [Z(e.name), n];
|
|
2141
2184
|
if (e.type === "id" && i.push("PRIMARY KEY"), e.nullable || i.push("NOT NULL"), e.unique && e.type !== "id" && i.push("UNIQUE"), e.hasDefault) {
|
|
2142
2185
|
let t = Y(e.defaultValue, "SYSUTCDATETIME()");
|
|
2143
2186
|
t && i.push(t);
|
|
2144
2187
|
}
|
|
2145
|
-
return e.references && (i.push(`REFERENCES ${Z(e.references.table)} (${Z(e.references.column)})`), e.references.onDelete && i.push(`ON DELETE ${
|
|
2146
|
-
},
|
|
2147
|
-
let t = (e) => e.replace(/'/g, "''"), n = new Set(e.indexes.flatMap((e) => e.columns)), r = e.columns.map((e) => ` ${
|
|
2188
|
+
return e.references && (i.push(`REFERENCES ${Z(e.references.table)} (${Z(e.references.column)})`), e.references.onDelete && i.push(`ON DELETE ${Fn[e.references.onDelete]}`)), i.join(" ");
|
|
2189
|
+
}, Un = (e) => {
|
|
2190
|
+
let t = (e) => e.replace(/'/g, "''"), n = new Set(e.indexes.flatMap((e) => e.columns)), r = e.columns.map((e) => ` ${Hn(e, n)}`).join(",\n"), i = `CREATE TABLE ${Z(e.table)} (\n${r}${On(e, Z)}\n)`, a = [`IF OBJECT_ID(N'${t(e.table)}', N'U') IS NULL EXEC(N'${t(i)}')`];
|
|
2148
2191
|
for (let n of e.indexes) {
|
|
2149
2192
|
if (n.name === `${e.table}_pkey`) continue;
|
|
2150
2193
|
let r = `CREATE ${n.unique ? "UNIQUE " : ""}INDEX ${Z(n.name)} ON ${Z(e.table)} (${n.columns.map(Z).join(", ")})`;
|
|
2151
2194
|
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)}')`);
|
|
2152
2195
|
}
|
|
2153
2196
|
return a;
|
|
2154
|
-
},
|
|
2155
|
-
let r = `${e.table}${
|
|
2197
|
+
}, Wn = (e) => `__dropped_${e.replace(/[-:T.Z]/g, "").slice(0, 14)}`, Gn = (e, t) => `DROP TABLE IF EXISTS ${t(e.table)};`, Kn = (e, t, n) => {
|
|
2198
|
+
let r = `${e.table}${Wn(t)}`;
|
|
2156
2199
|
return `ALTER TABLE ${n(e.table)} RENAME TO ${n(r)};`;
|
|
2157
|
-
},
|
|
2200
|
+
}, qn = (e, t, n) => {
|
|
2158
2201
|
let r = q(e, t, n);
|
|
2159
|
-
return r ?
|
|
2160
|
-
},
|
|
2202
|
+
return r ? jn({ type: r.type }) : !1;
|
|
2203
|
+
}, Jn = (e, t, n) => qn(e, t, n) ? `${K(n)}(${An})` : K(n), Yn = (e) => e.startsWith("(") ? e : G(e), Xn = (e, t) => `CREATE ${e.index.unique ? "UNIQUE " : ""}INDEX ${t ? "CONCURRENTLY " : ""}IF NOT EXISTS ${G(e.index.name)} ON ${G(e.table)} (${e.index.columns.map(Yn).join(", ")});`, Zn = (e, t) => `CREATE ${e.index.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${K(e.index.name)} ON ${K(e.table)} (${e.index.columns.map((n) => Jn(t, e.table, n)).join(", ")});`, Qn = (e) => `DROP INDEX IF EXISTS ${G(e.index)};`, $n = (e) => `DROP INDEX ${K(e.index)} ON ${K(e.table)};`, er = (e) => `DROP INDEX IF EXISTS ${Z(e.index)} ON ${Z(e.table)};`, tr = (e, t) => `ALTER TABLE ${t(e.from)} RENAME TO ${t(e.to)};`, nr = (e) => {
|
|
2161
2204
|
let t = (e) => e.replace(/'/g, "''");
|
|
2162
2205
|
return `EXEC sp_rename N'${t(e.from)}', N'${t(e.to)}';`;
|
|
2163
|
-
},
|
|
2206
|
+
}, rr = (e) => `ALTER INDEX ${G(e.from)} RENAME TO ${G(e.to)};`, ir = (e) => `ALTER TABLE ${K(e.table)} RENAME INDEX ${K(e.from)} TO ${K(e.to)};`, ar = (e) => {
|
|
2164
2207
|
let t = (e) => e.replace(/'/g, "''");
|
|
2165
2208
|
return `EXEC sp_rename N'${t(e.table)}.${t(e.from)}', N'${t(e.to)}', N'INDEX';`;
|
|
2166
|
-
},
|
|
2209
|
+
}, or = (e) => {
|
|
2167
2210
|
let t = (e) => e.replace(/'/g, "''"), n = `CREATE ${e.index.unique ? "UNIQUE " : ""}INDEX ${Z(e.index.name)} ON ${Z(e.table)} (${e.index.columns.map(Z).join(", ")})`;
|
|
2168
2211
|
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)}');`;
|
|
2169
|
-
},
|
|
2212
|
+
}, sr = (e, t, n) => {
|
|
2170
2213
|
let r = n.find((t) => t.tableName === e);
|
|
2171
2214
|
if (!r) return [];
|
|
2172
2215
|
let i = [];
|
|
@@ -2175,13 +2218,13 @@ BEGIN
|
|
|
2175
2218
|
t && t.type === "text" && !t.generatedAs && i.push(`ALTER TABLE ${Z(e)} ALTER COLUMN ${Z(n)} NVARCHAR(450) ${t.nullable ? "NULL" : "NOT NULL"};`);
|
|
2176
2219
|
}
|
|
2177
2220
|
return i;
|
|
2178
|
-
},
|
|
2221
|
+
}, cr = (e) => `ALTER TABLE ${G(e.table)} ADD CONSTRAINT ${G(`${e.table}_${e.column}_key`)} UNIQUE (${G(e.column)});`, lr = (e, t) => `ALTER TABLE ${K(e.table)} ADD CONSTRAINT ${K(`${e.table}_${e.column}_key`)} UNIQUE (${Jn(t, e.table, e.column)});`, ur = (e) => {
|
|
2179
2222
|
let t = e.fields.map((e) => G(e)).join(", ");
|
|
2180
2223
|
return `ALTER TABLE ${G(e.table)} ADD CONSTRAINT ${G(e.name)} UNIQUE (${t});`;
|
|
2181
|
-
},
|
|
2182
|
-
let n = e.fields.map((n) =>
|
|
2224
|
+
}, dr = (e, t) => {
|
|
2225
|
+
let n = e.fields.map((n) => Jn(t, e.table, n)).join(", ");
|
|
2183
2226
|
return `ALTER TABLE ${K(e.table)} ADD CONSTRAINT ${K(e.name)} UNIQUE (${n});`;
|
|
2184
|
-
},
|
|
2227
|
+
}, fr = (e) => `ALTER TABLE ${G(e.table)} DROP CONSTRAINT IF EXISTS ${G(e.name)};`, pr = (e) => `ALTER TABLE ${K(e.table)} DROP INDEX ${K(e.name)};`, mr = (e) => `ALTER TABLE ${G(e.table)} DROP CONSTRAINT IF EXISTS ${G(`${e.table}_${e.column}_key`)};`, hr = (e) => `ALTER TABLE ${K(e.table)} DROP INDEX ${K(`${e.table}_${e.column}_key`)};`, gr = (e, t) => {
|
|
2185
2228
|
let n = [], r = t(e.table), i = t(e.column), a = t(e.targetTable), o = t(e.targetColumn);
|
|
2186
2229
|
if (e.orphanPolicy) {
|
|
2187
2230
|
let t = `${i} IS NOT NULL AND ${i} NOT IN (SELECT ${o} FROM ${a})`;
|
|
@@ -2199,11 +2242,11 @@ BEGIN
|
|
|
2199
2242
|
setNull: "SET NULL",
|
|
2200
2243
|
noAction: "NO ACTION"
|
|
2201
2244
|
}[e.onDelete]}`), n.push(s.join(" ") + ";"), n;
|
|
2202
|
-
},
|
|
2245
|
+
}, _r = (e) => `ALTER TABLE ${G(e.table)} DROP CONSTRAINT IF EXISTS ${G(`${e.table}_${e.column}_fkey`)};`, vr = (e) => `ALTER TABLE ${K(e.table)} DROP FOREIGN KEY ${K(`${e.table}_${e.column}_fkey`)};`, yr = (e, t) => {
|
|
2203
2246
|
let n = e.values.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ");
|
|
2204
2247
|
return `ALTER TABLE ${t(e.table)} ADD CONSTRAINT ${t(`${e.table}_${e.column}_check`)} CHECK (${t(e.column)} IN (${n}));`;
|
|
2205
|
-
},
|
|
2206
|
-
let i = e.column, a = r === "mariadb", o = a ? K : r === "mssql" ? Z : G, s = a ?
|
|
2248
|
+
}, br = (e) => `ALTER TABLE ${G(e.table)} DROP CONSTRAINT IF EXISTS ${G(`${e.table}_${e.column}_check`)};`, xr = (e) => `ALTER TABLE ${K(e.table)} DROP CHECK ${K(`${e.table}_${e.column}_check`)};`, Sr = (e, t, n, r) => {
|
|
2249
|
+
let i = e.column, a = r === "mariadb", o = a ? K : r === "mssql" ? Z : G, s = a ? hn(i) : r === "mssql" ? Vn(i) : r === "sqlite" ? In(i) : pn(i), c = r === "mssql" ? "ADD" : "ADD COLUMN", l = (e) => a ? wn(e, i) : r === "mssql" ? Y(e, "SYSUTCDATETIME()") : r === "sqlite" ? Y(e, "CURRENT_TIMESTAMP") : En(e, i), u = J(i, r);
|
|
2207
2250
|
if (u) {
|
|
2208
2251
|
let t = r === "mssql" ? `${o(i.name)} ${u}` : `${o(i.name)} ${s} ${u}`;
|
|
2209
2252
|
return {
|
|
@@ -2231,7 +2274,7 @@ BEGIN
|
|
|
2231
2274
|
post: f ? [f] : []
|
|
2232
2275
|
};
|
|
2233
2276
|
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;
|
|
2234
|
-
},
|
|
2277
|
+
}, Cr = (e) => {
|
|
2235
2278
|
let t = new Map(e.map((e) => [e.tableName, e]));
|
|
2236
2279
|
return (e, n) => {
|
|
2237
2280
|
let r = t.get(e);
|
|
@@ -2251,26 +2294,26 @@ BEGIN
|
|
|
2251
2294
|
...o
|
|
2252
2295
|
};
|
|
2253
2296
|
};
|
|
2254
|
-
}, Q = (e, t, n) => e.unsafe(t).pipe(
|
|
2255
|
-
process.stderr.write(
|
|
2256
|
-
})),
|
|
2257
|
-
let r =
|
|
2297
|
+
}, Q = (e, t, n) => e.unsafe(t).pipe(p.tapError((e) => p.sync(() => {
|
|
2298
|
+
process.stderr.write(b(`applier: statement failed (op=${n})`, t, e, 400));
|
|
2299
|
+
})), p.mapError((e) => {
|
|
2300
|
+
let r = b(`applier: statement failed (op=${n})`, t, e, 400).trim();
|
|
2258
2301
|
return Object.assign(e, { message: r });
|
|
2259
|
-
}),
|
|
2260
|
-
let r = process.hrtime.bigint(), i =
|
|
2302
|
+
}), p.map(() => void 0)), $ = (e, t, n) => p.forEach(t, (t) => Q(e, t, n), { discard: !0 }), wr = (e, t, n) => p.gen(function* () {
|
|
2303
|
+
let r = process.hrtime.bigint(), i = Cr(n.declared), a = t.op;
|
|
2261
2304
|
switch (a.kind) {
|
|
2262
2305
|
case "create-table": {
|
|
2263
2306
|
let t = yield* e.onDialectOrElse({
|
|
2264
|
-
mysql: () =>
|
|
2265
|
-
mssql: () =>
|
|
2266
|
-
sqlite: () =>
|
|
2267
|
-
orElse: () =>
|
|
2307
|
+
mysql: () => p.succeed(Nn(a)),
|
|
2308
|
+
mssql: () => p.succeed(Un(a)),
|
|
2309
|
+
sqlite: () => p.succeed(Rn(a)),
|
|
2310
|
+
orElse: () => p.succeed(kn(a))
|
|
2268
2311
|
});
|
|
2269
2312
|
for (let n of t) yield* Q(e, n, a.kind);
|
|
2270
2313
|
break;
|
|
2271
2314
|
}
|
|
2272
2315
|
case "drop-table": {
|
|
2273
|
-
let t = process.env.VOLTRO_SOFT_DROP === "1", n = (/* @__PURE__ */ new Date()).toISOString(), r = (e) => t ?
|
|
2316
|
+
let t = process.env.VOLTRO_SOFT_DROP === "1", n = (/* @__PURE__ */ new Date()).toISOString(), r = (e) => t ? Kn(a, n, e) : Gn(a, e);
|
|
2274
2317
|
yield* e.onDialectOrElse({
|
|
2275
2318
|
mysql: () => Q(e, r(K), a.kind),
|
|
2276
2319
|
orElse: () => Q(e, r(G), a.kind)
|
|
@@ -2280,89 +2323,89 @@ BEGIN
|
|
|
2280
2323
|
case "drop-column": {
|
|
2281
2324
|
let t = process.env.VOLTRO_SOFT_DROP === "1", n = (/* @__PURE__ */ new Date()).toISOString();
|
|
2282
2325
|
yield* e.onDialectOrElse({
|
|
2283
|
-
mysql: () => Q(e, t ?
|
|
2284
|
-
orElse: () => Q(e, t ?
|
|
2326
|
+
mysql: () => Q(e, t ? yn(a, n, K) : _n(a), a.kind),
|
|
2327
|
+
orElse: () => Q(e, t ? yn(a, n, G) : gn(a, G), a.kind)
|
|
2285
2328
|
});
|
|
2286
2329
|
break;
|
|
2287
2330
|
}
|
|
2288
2331
|
case "rename-column":
|
|
2289
2332
|
yield* e.onDialectOrElse({
|
|
2290
|
-
mysql: () => Q(e,
|
|
2333
|
+
mysql: () => Q(e, vn(a, K), a.kind),
|
|
2291
2334
|
mssql: () => Q(e, `EXEC sp_rename N'${a.table.replace(/'/g, "''")}.${a.from.replace(/'/g, "''")}', N'${a.to.replace(/'/g, "''")}', 'COLUMN';`, a.kind),
|
|
2292
|
-
orElse: () => Q(e,
|
|
2335
|
+
orElse: () => Q(e, vn(a, G), a.kind)
|
|
2293
2336
|
});
|
|
2294
2337
|
break;
|
|
2295
2338
|
case "add-index":
|
|
2296
2339
|
yield* e.onDialectOrElse({
|
|
2297
|
-
mysql: () => Q(e,
|
|
2298
|
-
mssql: () => $(e, [...
|
|
2299
|
-
orElse: () => Q(e,
|
|
2340
|
+
mysql: () => Q(e, Zn(a, n.declared), a.kind),
|
|
2341
|
+
mssql: () => $(e, [...sr(a.table, a.index.columns, n.declared), or(a)], a.kind),
|
|
2342
|
+
orElse: () => Q(e, Xn(a, t.classification === "online-required"), a.kind)
|
|
2300
2343
|
});
|
|
2301
2344
|
break;
|
|
2302
2345
|
case "drop-index":
|
|
2303
2346
|
yield* e.onDialectOrElse({
|
|
2304
|
-
mysql: () => Q(e,
|
|
2305
|
-
mssql: () => Q(e,
|
|
2306
|
-
orElse: () => Q(e,
|
|
2347
|
+
mysql: () => Q(e, $n(a), a.kind),
|
|
2348
|
+
mssql: () => Q(e, er(a), a.kind),
|
|
2349
|
+
orElse: () => Q(e, Qn(a), a.kind)
|
|
2307
2350
|
});
|
|
2308
2351
|
break;
|
|
2309
2352
|
case "rename-table":
|
|
2310
2353
|
yield* e.onDialectOrElse({
|
|
2311
|
-
mysql: () => Q(e,
|
|
2312
|
-
mssql: () => Q(e,
|
|
2313
|
-
orElse: () => Q(e,
|
|
2354
|
+
mysql: () => Q(e, tr(a, K), a.kind),
|
|
2355
|
+
mssql: () => Q(e, nr(a), a.kind),
|
|
2356
|
+
orElse: () => Q(e, tr(a, G), a.kind)
|
|
2314
2357
|
});
|
|
2315
2358
|
break;
|
|
2316
2359
|
case "rename-index":
|
|
2317
2360
|
yield* e.onDialectOrElse({
|
|
2318
|
-
mysql: () => Q(e,
|
|
2319
|
-
mssql: () => Q(e,
|
|
2320
|
-
orElse: () => Q(e,
|
|
2361
|
+
mysql: () => Q(e, ir(a), a.kind),
|
|
2362
|
+
mssql: () => Q(e, ar(a), a.kind),
|
|
2363
|
+
orElse: () => Q(e, rr(a), a.kind)
|
|
2321
2364
|
});
|
|
2322
2365
|
break;
|
|
2323
2366
|
case "add-unique":
|
|
2324
2367
|
yield* e.onDialectOrElse({
|
|
2325
|
-
mysql: () => Q(e,
|
|
2368
|
+
mysql: () => Q(e, lr(a, n.declared), a.kind),
|
|
2326
2369
|
sqlite: () => $(e, X(a.table, n.declared), a.kind),
|
|
2327
|
-
mssql: () => $(e, [...
|
|
2328
|
-
orElse: () => Q(e,
|
|
2370
|
+
mssql: () => $(e, [...sr(a.table, [a.column], n.declared), cr(a)], a.kind),
|
|
2371
|
+
orElse: () => Q(e, cr(a), a.kind)
|
|
2329
2372
|
});
|
|
2330
2373
|
break;
|
|
2331
2374
|
case "drop-unique":
|
|
2332
2375
|
yield* e.onDialectOrElse({
|
|
2333
|
-
mysql: () => Q(e,
|
|
2376
|
+
mysql: () => Q(e, hr(a), a.kind),
|
|
2334
2377
|
sqlite: () => $(e, X(a.table, n.declared), a.kind),
|
|
2335
|
-
orElse: () => Q(e,
|
|
2378
|
+
orElse: () => Q(e, mr(a), a.kind)
|
|
2336
2379
|
});
|
|
2337
2380
|
break;
|
|
2338
2381
|
case "add-unique-composite":
|
|
2339
2382
|
yield* e.onDialectOrElse({
|
|
2340
|
-
mysql: () => Q(e,
|
|
2383
|
+
mysql: () => Q(e, dr(a, n.declared), a.kind),
|
|
2341
2384
|
sqlite: () => $(e, X(a.table, n.declared), a.kind),
|
|
2342
|
-
mssql: () => $(e, [...
|
|
2343
|
-
orElse: () => Q(e,
|
|
2385
|
+
mssql: () => $(e, [...sr(a.table, a.fields, n.declared), ur(a)], a.kind),
|
|
2386
|
+
orElse: () => Q(e, ur(a), a.kind)
|
|
2344
2387
|
});
|
|
2345
2388
|
break;
|
|
2346
2389
|
case "drop-unique-composite":
|
|
2347
2390
|
yield* e.onDialectOrElse({
|
|
2348
|
-
mysql: () => Q(e,
|
|
2391
|
+
mysql: () => Q(e, pr(a), a.kind),
|
|
2349
2392
|
sqlite: () => $(e, X(a.table, n.declared), a.kind),
|
|
2350
|
-
orElse: () => Q(e,
|
|
2393
|
+
orElse: () => Q(e, fr(a), a.kind)
|
|
2351
2394
|
});
|
|
2352
2395
|
break;
|
|
2353
2396
|
case "add-foreign-key":
|
|
2354
2397
|
yield* e.onDialectOrElse({
|
|
2355
|
-
mysql: () => $(e,
|
|
2398
|
+
mysql: () => $(e, gr(a, K), a.kind),
|
|
2356
2399
|
sqlite: () => $(e, X(a.table, n.declared), a.kind),
|
|
2357
|
-
orElse: () => $(e,
|
|
2400
|
+
orElse: () => $(e, gr(a, G), a.kind)
|
|
2358
2401
|
});
|
|
2359
2402
|
break;
|
|
2360
2403
|
case "add-column": {
|
|
2361
|
-
let n =
|
|
2362
|
-
mysql: () =>
|
|
2363
|
-
mssql: () =>
|
|
2364
|
-
sqlite: () =>
|
|
2365
|
-
orElse: () =>
|
|
2404
|
+
let n = Sr(a, t, i, yield* e.onDialectOrElse({
|
|
2405
|
+
mysql: () => p.succeed("mariadb"),
|
|
2406
|
+
mssql: () => p.succeed("mssql"),
|
|
2407
|
+
sqlite: () => p.succeed("sqlite"),
|
|
2408
|
+
orElse: () => p.succeed("postgres")
|
|
2366
2409
|
}));
|
|
2367
2410
|
for (let t of n.pre) yield* Q(e, t, a.kind);
|
|
2368
2411
|
if (n.backfillKind === "sql" && n.backfillSql) if (!(t.classification === "online-required" || n.batchSize !== void 0)) yield* e`UPDATE ${e(a.table)} SET ${e(a.column.name)} = ${n.backfillSql}`;
|
|
@@ -2372,10 +2415,10 @@ BEGIN
|
|
|
2372
2415
|
let s = i === void 0 ? yield* e`SELECT ${e("id")} FROM ${e(a.table)} WHERE ${e(a.column.name)} IS NULL ORDER BY ${e("id")} LIMIT ${t}` : yield* e`SELECT ${e("id")} FROM ${e(a.table)} WHERE ${e(a.column.name)} IS NULL AND ${e("id")} > ${i} ORDER BY ${e("id")} LIMIT ${t}`;
|
|
2373
2416
|
if (s.length === 0) break;
|
|
2374
2417
|
let c = s.map((e) => e.id);
|
|
2375
|
-
yield* e`UPDATE ${e(a.table)} SET ${e(a.column.name)} = ${n.backfillSql} WHERE ${e("id")} IN ${e.in(c)}`, o += s.length, yield*
|
|
2418
|
+
yield* e`UPDATE ${e(a.table)} SET ${e(a.column.name)} = ${n.backfillSql} WHERE ${e("id")} IN ${e.in(c)}`, o += s.length, yield* p.logInfo(`backfill ${a.table}.${a.column.name}: ${o} rows`).pipe(p.annotateLogs({
|
|
2376
2419
|
scope: "voltro:migrate",
|
|
2377
2420
|
mode: "sql-batched"
|
|
2378
|
-
})), i = s[s.length - 1].id, r > 0 && (yield*
|
|
2421
|
+
})), i = s[s.length - 1].id, r > 0 && (yield* p.sleep(`${r} millis`));
|
|
2379
2422
|
}
|
|
2380
2423
|
}
|
|
2381
2424
|
else if (n.backfillKind === "js" && n.backfillJs) {
|
|
@@ -2384,17 +2427,17 @@ BEGIN
|
|
|
2384
2427
|
let s = i === void 0 ? yield* e`SELECT ${e("id")} FROM ${e(a.table)} WHERE ${e(a.column.name)} IS NULL ORDER BY ${e("id")} LIMIT ${t}` : yield* e`SELECT ${e("id")} FROM ${e(a.table)} WHERE ${e(a.column.name)} IS NULL AND ${e("id")} > ${i} ORDER BY ${e("id")} LIMIT ${t}`;
|
|
2385
2428
|
if (s.length === 0) break;
|
|
2386
2429
|
for (let t of s) {
|
|
2387
|
-
let r = yield*
|
|
2430
|
+
let r = yield* p.tryPromise({
|
|
2388
2431
|
try: () => Promise.resolve(n.backfillJs({ id: t.id })),
|
|
2389
2432
|
catch: (e) => e
|
|
2390
|
-
}).pipe(
|
|
2433
|
+
}).pipe(p.orDie);
|
|
2391
2434
|
yield* e`UPDATE ${e(a.table)} SET ${e(a.column.name)} = ${r} WHERE ${e("id")} = ${t.id}`;
|
|
2392
2435
|
}
|
|
2393
|
-
o += s.length, yield*
|
|
2436
|
+
o += s.length, yield* p.logInfo(`backfill ${a.table}.${a.column.name}: ${o} rows processed`).pipe(p.annotateLogs({
|
|
2394
2437
|
scope: "voltro:migrate",
|
|
2395
2438
|
table: a.table,
|
|
2396
2439
|
column: a.column.name
|
|
2397
|
-
})), i = s[s.length - 1].id, r > 0 && (yield*
|
|
2440
|
+
})), i = s[s.length - 1].id, r > 0 && (yield* p.sleep(`${r} millis`));
|
|
2398
2441
|
}
|
|
2399
2442
|
}
|
|
2400
2443
|
for (let t of n.post) yield* Q(e, t, a.kind);
|
|
@@ -2404,37 +2447,37 @@ BEGIN
|
|
|
2404
2447
|
yield* e.onDialectOrElse({
|
|
2405
2448
|
mysql: () => {
|
|
2406
2449
|
let t = q(n.declared, a.table, a.column);
|
|
2407
|
-
return t ? Q(e, `ALTER TABLE ${K(a.table)} MODIFY COLUMN ${
|
|
2450
|
+
return t ? Q(e, `ALTER TABLE ${K(a.table)} MODIFY COLUMN ${Cn(t, a.column, a.toNullable)};`, a.kind) : p.die(/* @__PURE__ */ Error(`alter-column-nullability on mariadb needs the declared column '${a.table}.${a.column}' (to render MODIFY COLUMN); it was not in ctx.declared.`));
|
|
2408
2451
|
},
|
|
2409
2452
|
mssql: () => {
|
|
2410
2453
|
let t = q(n.declared, a.table, a.column);
|
|
2411
|
-
return t ? Q(e,
|
|
2454
|
+
return t ? Q(e, zn(t, a.column, a.table, a.toNullable), a.kind) : p.die(/* @__PURE__ */ Error(`alter-column-nullability on mssql needs the declared column '${a.table}.${a.column}'.`));
|
|
2412
2455
|
},
|
|
2413
2456
|
sqlite: () => $(e, X(a.table, n.declared), a.kind),
|
|
2414
2457
|
orElse: () => Q(e, `ALTER TABLE ${G(a.table)} ALTER COLUMN ${G(a.column)} ${a.toNullable ? "DROP NOT NULL" : "SET NOT NULL"};`, a.kind)
|
|
2415
2458
|
});
|
|
2416
2459
|
break;
|
|
2417
2460
|
case "alter-column-default": {
|
|
2418
|
-
let t =
|
|
2461
|
+
let t = Sn(n.declared, a.table, a.column);
|
|
2419
2462
|
yield* e.onDialectOrElse({
|
|
2420
|
-
mysql: () => Q(e,
|
|
2421
|
-
mssql: () => Q(e,
|
|
2463
|
+
mysql: () => Q(e, bn(a, K, (e) => wn(e, t)), a.kind),
|
|
2464
|
+
mssql: () => Q(e, Bn(a), a.kind),
|
|
2422
2465
|
sqlite: () => $(e, X(a.table, n.declared), a.kind),
|
|
2423
|
-
orElse: () => Q(e,
|
|
2466
|
+
orElse: () => Q(e, bn(a, G, (e) => En(e, t)), a.kind)
|
|
2424
2467
|
});
|
|
2425
2468
|
break;
|
|
2426
2469
|
}
|
|
2427
2470
|
case "alter-column-type": {
|
|
2428
|
-
let r =
|
|
2471
|
+
let r = pn(Sn(n.declared, a.table, a.column) ?? { type: a.to }), i = a.using ? ` USING ${a.using}` : "";
|
|
2429
2472
|
if (t.classification !== "online-required") {
|
|
2430
2473
|
yield* e.onDialectOrElse({
|
|
2431
2474
|
mysql: () => {
|
|
2432
2475
|
let t = q(n.declared, a.table, a.column);
|
|
2433
|
-
return t ? Q(e, `ALTER TABLE ${K(a.table)} MODIFY COLUMN ${
|
|
2476
|
+
return t ? Q(e, `ALTER TABLE ${K(a.table)} MODIFY COLUMN ${Cn(t, a.column)};`, a.kind) : p.die(/* @__PURE__ */ Error(`alter-column-type on mariadb needs the declared column '${a.table}.${a.column}' (to render MODIFY COLUMN); it was not in ctx.declared.`));
|
|
2434
2477
|
},
|
|
2435
2478
|
mssql: () => {
|
|
2436
2479
|
let t = q(n.declared, a.table, a.column);
|
|
2437
|
-
return t ? Q(e,
|
|
2480
|
+
return t ? Q(e, zn(t, a.column, a.table), a.kind) : p.die(/* @__PURE__ */ Error(`alter-column-type on mssql needs the declared column '${a.table}.${a.column}'.`));
|
|
2438
2481
|
},
|
|
2439
2482
|
sqlite: () => $(e, X(a.table, n.declared), a.kind),
|
|
2440
2483
|
orElse: () => Q(e, `ALTER TABLE ${G(a.table)} ALTER COLUMN ${G(a.column)} TYPE ${r}${i};`, a.kind)
|
|
@@ -2442,8 +2485,8 @@ BEGIN
|
|
|
2442
2485
|
break;
|
|
2443
2486
|
}
|
|
2444
2487
|
yield* e.onDialectOrElse({
|
|
2445
|
-
mysql: () =>
|
|
2446
|
-
orElse: () =>
|
|
2488
|
+
mysql: () => p.die(/* @__PURE__ */ Error(`online-required alter-column-type on ${a.table}.${a.column} is not supported on mariadb (the shadow-swap path is postgres-only). Use a smaller-table window or an external online-DDL tool.`)),
|
|
2489
|
+
orElse: () => p.void
|
|
2447
2490
|
});
|
|
2448
2491
|
let o = `${a.column}__shadow`, s = `${a.column}__old`;
|
|
2449
2492
|
yield* e.unsafe(`ALTER TABLE ${G(a.table)} ADD COLUMN ${G(o)} ${r}`);
|
|
@@ -2453,28 +2496,28 @@ BEGIN
|
|
|
2453
2496
|
if (t.length === 0) break;
|
|
2454
2497
|
yield* e.unsafe(`UPDATE ${G(a.table)} SET ${G(o)} = ${c} WHERE ${G(a.column)} IS NOT NULL AND ${G(o)} IS NULL AND ${G("id")} IN (${t.map((e) => `'${e.id.replace(/'/g, "''")}'`).join(",")})`), l = t[t.length - 1].id;
|
|
2455
2498
|
}
|
|
2456
|
-
yield*
|
|
2499
|
+
yield* p.logInfo(`alter-column-type shadow ${a.table}.${a.column}: shadow populated`).pipe(p.annotateLogs({ scope: "voltro:migrate" })), yield* e.unsafe(`ALTER TABLE ${G(a.table)} RENAME COLUMN ${G(a.column)} TO ${G(s)}`), yield* e.unsafe(`ALTER TABLE ${G(a.table)} RENAME COLUMN ${G(o)} TO ${G(a.column)}`), yield* e.unsafe(`ALTER TABLE ${G(a.table)} DROP COLUMN ${G(s)}`);
|
|
2457
2500
|
break;
|
|
2458
2501
|
}
|
|
2459
2502
|
case "drop-foreign-key":
|
|
2460
2503
|
yield* e.onDialectOrElse({
|
|
2461
|
-
mysql: () => Q(e,
|
|
2504
|
+
mysql: () => Q(e, vr(a), a.kind),
|
|
2462
2505
|
sqlite: () => $(e, X(a.table, n.declared), a.kind),
|
|
2463
|
-
orElse: () => Q(e,
|
|
2506
|
+
orElse: () => Q(e, _r(a), a.kind)
|
|
2464
2507
|
});
|
|
2465
2508
|
break;
|
|
2466
2509
|
case "add-check":
|
|
2467
2510
|
yield* e.onDialectOrElse({
|
|
2468
|
-
mysql: () => Q(e,
|
|
2511
|
+
mysql: () => Q(e, yr(a, K), a.kind),
|
|
2469
2512
|
sqlite: () => $(e, X(a.table, n.declared), a.kind),
|
|
2470
|
-
orElse: () => Q(e,
|
|
2513
|
+
orElse: () => Q(e, yr(a, G), a.kind)
|
|
2471
2514
|
});
|
|
2472
2515
|
break;
|
|
2473
2516
|
case "drop-check":
|
|
2474
2517
|
yield* e.onDialectOrElse({
|
|
2475
|
-
mysql: () => Q(e,
|
|
2518
|
+
mysql: () => Q(e, xr(a), a.kind),
|
|
2476
2519
|
sqlite: () => $(e, X(a.table, n.declared), a.kind),
|
|
2477
|
-
orElse: () => Q(e,
|
|
2520
|
+
orElse: () => Q(e, br(a), a.kind)
|
|
2478
2521
|
});
|
|
2479
2522
|
break;
|
|
2480
2523
|
}
|
|
@@ -2483,34 +2526,31 @@ BEGIN
|
|
|
2483
2526
|
status: "applied",
|
|
2484
2527
|
durationMs: Number((process.hrtime.bigint() - r) / 1000000n)
|
|
2485
2528
|
};
|
|
2486
|
-
}),
|
|
2529
|
+
}), Tr = (e, t, n) => p.gen(function* () {
|
|
2487
2530
|
let r = t.operations.filter((e) => e.blocked);
|
|
2488
|
-
if (r.length > 0) return yield*
|
|
2489
|
-
yield*
|
|
2531
|
+
if (r.length > 0) return yield* p.die(/* @__PURE__ */ Error(`applyPlan: refusing to execute — ${r.length} blocked operations. Run \`voltro db plan\` to see the fixes.`));
|
|
2532
|
+
yield* T(e);
|
|
2490
2533
|
let i = Date.now(), a = [];
|
|
2491
2534
|
try {
|
|
2492
2535
|
let r = yield* e.onDialectOrElse({
|
|
2493
|
-
pg: () =>
|
|
2494
|
-
mssql: () =>
|
|
2495
|
-
orElse: () =>
|
|
2536
|
+
pg: () => p.succeed(!0),
|
|
2537
|
+
mssql: () => p.succeed(!0),
|
|
2538
|
+
orElse: () => p.succeed(!1)
|
|
2496
2539
|
}), i = r ? t.operations.filter((e) => e.classification !== "online-required") : [], o = r ? t.operations.filter((e) => e.classification === "online-required") : t.operations;
|
|
2497
|
-
i.length > 0 && (yield* e.withTransaction(
|
|
2498
|
-
let e = yield*
|
|
2499
|
-
for (let t of i) a.push(yield*
|
|
2540
|
+
i.length > 0 && (yield* e.withTransaction(p.gen(function* () {
|
|
2541
|
+
let e = yield* h.SqlClient;
|
|
2542
|
+
for (let t of i) a.push(yield* wr(e, t, n));
|
|
2500
2543
|
})));
|
|
2501
|
-
for (let t of o) a.push(yield*
|
|
2544
|
+
for (let t of o) a.push(yield* wr(e, t, n));
|
|
2502
2545
|
} finally {
|
|
2503
|
-
yield*
|
|
2546
|
+
yield* E(e).pipe(p.orDie);
|
|
2504
2547
|
}
|
|
2505
2548
|
let o = yield* n.replan(e);
|
|
2506
2549
|
if (o.operations.length > 0) {
|
|
2507
2550
|
let e = o.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");
|
|
2508
|
-
return yield*
|
|
2551
|
+
return yield* p.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 ${o.operations.length}:\n${e}${o.operations.length > 10 ? `\n … and ${o.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.`));
|
|
2509
2552
|
}
|
|
2510
|
-
let s = a.filter((e) => e.status === "applied").length, c = Date.now() - i, l = `plan_${Date.now().toString(36)}`, u = (/* @__PURE__ */ new Date()).toISOString(),
|
|
2511
|
-
mysql: () => d.succeed(u.replace("T", " ").replace("Z", "")),
|
|
2512
|
-
orElse: () => d.succeed(u)
|
|
2513
|
-
});
|
|
2553
|
+
let s = a.filter((e) => e.status === "applied").length, c = Date.now() - i, l = `plan_${Date.now().toString(36)}`, u = (/* @__PURE__ */ new Date()).toISOString(), d = yield* dn(e, u);
|
|
2514
2554
|
return yield* e`
|
|
2515
2555
|
INSERT INTO ${e("_voltro_migration_plans")}
|
|
2516
2556
|
(${e("id")}, ${e("fingerprint")}, ${e("liveFingerprint")}, ${e("operations")},
|
|
@@ -2519,7 +2559,7 @@ BEGIN
|
|
|
2519
2559
|
VALUES
|
|
2520
2560
|
(${l}, ${t.toFingerprint}, ${o.fromFingerprint}, ${JSON.stringify(t.operations)},
|
|
2521
2561
|
${n.appliedBy}, ${n.environment}, ${n.source},
|
|
2522
|
-
${c}, ${
|
|
2562
|
+
${c}, ${d}, ${n.notes ?? null})
|
|
2523
2563
|
`, {
|
|
2524
2564
|
id: l,
|
|
2525
2565
|
appliedAt: u,
|
|
@@ -2532,19 +2572,19 @@ BEGIN
|
|
|
2532
2572
|
source: n.source,
|
|
2533
2573
|
...n.notes ? { notes: n.notes } : {}
|
|
2534
2574
|
};
|
|
2535
|
-
}),
|
|
2575
|
+
}), Er = (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"), Dr = (e) => e`
|
|
2536
2576
|
SELECT fingerprint
|
|
2537
2577
|
FROM _voltro_migration_plans
|
|
2538
2578
|
ORDER BY ${e("appliedAt")} DESC
|
|
2539
2579
|
LIMIT 1
|
|
2540
|
-
`.pipe(
|
|
2580
|
+
`.pipe(p.map((e) => e.length > 0 ? e[0].fingerprint : void 0), p.catchAll(() => p.succeed(void 0))), Or = (e) => e.onDialectOrElse({
|
|
2541
2581
|
pg: () => "postgres",
|
|
2542
2582
|
mysql: () => "mysql",
|
|
2543
2583
|
mssql: () => "mssql",
|
|
2544
2584
|
sqlite: () => "sqlite",
|
|
2545
2585
|
orElse: () => "sqlite"
|
|
2546
|
-
}),
|
|
2547
|
-
let n =
|
|
2586
|
+
}), kr = (e, t) => p.gen(function* () {
|
|
2587
|
+
let n = P(I(t, Or(e))), r = yield* Dr(e);
|
|
2548
2588
|
return r === n ? {
|
|
2549
2589
|
kind: "up-to-date",
|
|
2550
2590
|
fingerprint: n
|
|
@@ -2553,23 +2593,23 @@ BEGIN
|
|
|
2553
2593
|
expected: n,
|
|
2554
2594
|
...r === void 0 ? {} : { actual: r }
|
|
2555
2595
|
};
|
|
2556
|
-
}),
|
|
2596
|
+
}), Ar = [
|
|
2557
2597
|
"pgeagertest_",
|
|
2558
2598
|
"mig_e2e_",
|
|
2559
2599
|
"mig_test_",
|
|
2560
2600
|
"mysqleagertest_",
|
|
2561
2601
|
"mssqleagertest_",
|
|
2562
2602
|
"sqliteeagertest_"
|
|
2563
|
-
],
|
|
2603
|
+
], jr = (e) => Ar.some((t) => e.startsWith(t)), Mr = (e, t) => p.gen(function* () {
|
|
2564
2604
|
if (process.env.VOLTRO_TEST_FIXTURE_GC === "off") return [];
|
|
2565
|
-
let n = t.filter((e) =>
|
|
2605
|
+
let n = t.filter((e) => jr(e.name)).map((e) => e.name);
|
|
2566
2606
|
if (n.length === 0) return [];
|
|
2567
|
-
for (let t of n) yield* e.unsafe(`DROP TABLE IF EXISTS "${t}" CASCADE`).pipe(
|
|
2607
|
+
for (let t of n) yield* e.unsafe(`DROP TABLE IF EXISTS "${t}" CASCADE`).pipe(p.catchAll(() => p.void));
|
|
2568
2608
|
return n;
|
|
2569
|
-
}),
|
|
2609
|
+
}), Nr = () => {
|
|
2570
2610
|
let e = (process.env.VOLTRO_DB_IGNORE_TABLES ?? "").split(",").map((e) => e.trim()).filter((e) => e.length > 0);
|
|
2571
2611
|
return e.length > 0 ? { ignoreTables: e } : {};
|
|
2572
|
-
},
|
|
2612
|
+
}, Pr = (e) => {
|
|
2573
2613
|
if (e === void 0) return;
|
|
2574
2614
|
let t = e.trim();
|
|
2575
2615
|
if (t === "") return;
|
|
@@ -2579,83 +2619,83 @@ BEGIN
|
|
|
2579
2619
|
kind: "tables",
|
|
2580
2620
|
tables: new Set(n)
|
|
2581
2621
|
} : void 0;
|
|
2582
|
-
},
|
|
2622
|
+
}, Fr = (e) => typeof e.table == "string" ? e.table : void 0, Ir = (e, t = { kind: "all" }) => ({
|
|
2583
2623
|
...e,
|
|
2584
2624
|
operations: e.operations.map((e) => {
|
|
2585
2625
|
if (!e.blocked || e.classification !== "lossy") return e;
|
|
2586
2626
|
if (t?.kind === "tables") {
|
|
2587
|
-
let n =
|
|
2627
|
+
let n = Fr(e.op);
|
|
2588
2628
|
if (n === void 0 || !t.tables.has(n)) return e;
|
|
2589
2629
|
}
|
|
2590
2630
|
let { blocked: n, ...r } = e;
|
|
2591
2631
|
return r;
|
|
2592
2632
|
})
|
|
2593
|
-
}),
|
|
2594
|
-
let t =
|
|
2595
|
-
return t === void 0 ?
|
|
2596
|
-
}),
|
|
2597
|
-
let r =
|
|
2633
|
+
}), Lr = (e, t, n) => t === "postgres" ? ft(e, n).pipe(p.flatMap((e) => {
|
|
2634
|
+
let t = pt(e);
|
|
2635
|
+
return t === void 0 ? p.void : p.logWarning(`auto-migrate: ${t}`).pipe(p.annotateLogs({ scope: "voltro:migrate" }));
|
|
2636
|
+
}), p.catchAll(() => p.void)) : p.void, Rr = (e, t, n) => p.gen(function* () {
|
|
2637
|
+
let r = Or(e), i = t;
|
|
2598
2638
|
if (process.env.VOLTRO_MIGRATE_FORCE !== "1") {
|
|
2599
|
-
let t =
|
|
2600
|
-
if ((yield*
|
|
2639
|
+
let t = P(I(i, r));
|
|
2640
|
+
if ((yield* Dr(e)) === t) return yield* Lr(e, r, i), {
|
|
2601
2641
|
kind: "up-to-date",
|
|
2602
2642
|
fingerprint: t
|
|
2603
2643
|
};
|
|
2604
2644
|
}
|
|
2605
|
-
let a = yield*
|
|
2606
|
-
o.length > 0 && (yield*
|
|
2645
|
+
let a = yield* U(e), o = yield* Mr(e, a.tables);
|
|
2646
|
+
o.length > 0 && (yield* p.logInfo(`auto-migrate: reaped ${o.length} test-fixture leftover(s)`).pipe(p.annotateLogs({
|
|
2607
2647
|
scope: "voltro:migrate",
|
|
2608
2648
|
count: o.length
|
|
2609
|
-
})), a = yield*
|
|
2610
|
-
let
|
|
2649
|
+
})), a = yield* U(e));
|
|
2650
|
+
let c = new Set(t.map((e) => e.tableName)), l = (e) => e.filter((e) => !s(e.name) || c.has(e.name)), u = Kt({
|
|
2611
2651
|
declared: i,
|
|
2612
|
-
live: { tables:
|
|
2652
|
+
live: { tables: l(a.tables) },
|
|
2613
2653
|
dialect: r,
|
|
2614
|
-
...
|
|
2654
|
+
...Nr()
|
|
2615
2655
|
});
|
|
2616
|
-
if (
|
|
2617
|
-
let e =
|
|
2618
|
-
if (t === void 0 || !e || (
|
|
2656
|
+
if (u.summary.blocked > 0) {
|
|
2657
|
+
let e = u.operations.filter((e) => e.blocked).every((e) => e.classification === "lossy"), t = Pr(process.env.VOLTRO_DESTRUCTIVE_OK);
|
|
2658
|
+
if (t === void 0 || !e || (u = Ir(u, t), u.operations.some((e) => e.blocked))) return {
|
|
2619
2659
|
kind: "refused-blocked",
|
|
2620
|
-
plan:
|
|
2660
|
+
plan: u
|
|
2621
2661
|
};
|
|
2622
2662
|
}
|
|
2623
|
-
return
|
|
2663
|
+
return u.operations.length === 0 ? {
|
|
2624
2664
|
kind: "up-to-date",
|
|
2625
|
-
fingerprint:
|
|
2665
|
+
fingerprint: u.toFingerprint
|
|
2626
2666
|
} : {
|
|
2627
2667
|
kind: "applied",
|
|
2628
|
-
applied: yield*
|
|
2668
|
+
applied: yield* Tr(e, u, {
|
|
2629
2669
|
declared: t,
|
|
2630
2670
|
appliedBy: n.appliedBy,
|
|
2631
2671
|
environment: n.environment === "prod" ? "dev" : n.environment,
|
|
2632
2672
|
source: "auto-diff",
|
|
2633
|
-
replan: (e) =>
|
|
2634
|
-
let t = yield*
|
|
2635
|
-
return
|
|
2673
|
+
replan: (e) => p.gen(function* () {
|
|
2674
|
+
let t = yield* U(e);
|
|
2675
|
+
return Kt({
|
|
2636
2676
|
declared: i,
|
|
2637
|
-
live: { tables:
|
|
2677
|
+
live: { tables: l(t.tables) },
|
|
2638
2678
|
dialect: r,
|
|
2639
|
-
...
|
|
2679
|
+
...Nr()
|
|
2640
2680
|
});
|
|
2641
2681
|
})
|
|
2642
2682
|
}),
|
|
2643
|
-
plan:
|
|
2683
|
+
plan: u
|
|
2644
2684
|
};
|
|
2645
|
-
}),
|
|
2685
|
+
}), zr = (e, t, n) => process.env.VOLTRO_AUTO_MIGRATE === "0" ? p.succeed({
|
|
2646
2686
|
kind: "skipped",
|
|
2647
2687
|
reason: "VOLTRO_AUTO_MIGRATE=0"
|
|
2648
|
-
}) : n.environment === "prod" ?
|
|
2688
|
+
}) : n.environment === "prod" ? kr(e, t) : Rr(e, t, n), Br = (e, t) => {
|
|
2649
2689
|
switch (e.kind) {
|
|
2650
2690
|
case "skipped": return `auto-migrate: skipped (${e.reason})`;
|
|
2651
|
-
case "up-to-date": return `auto-migrate: schema up to date (${t}, fingerprint=${
|
|
2691
|
+
case "up-to-date": return `auto-migrate: schema up to date (${t}, fingerprint=${F(e.fingerprint)})`;
|
|
2652
2692
|
case "applied": {
|
|
2653
2693
|
let t = e.plan.summary;
|
|
2654
|
-
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=${
|
|
2694
|
+
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)}`;
|
|
2655
2695
|
}
|
|
2656
|
-
case "prod-mismatch": return `auto-migrate: SCHEMA FINGERPRINT MISMATCH — declared=${
|
|
2696
|
+
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.";
|
|
2657
2697
|
case "refused-blocked": {
|
|
2658
|
-
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` +
|
|
2698
|
+
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` + Er(e.plan), c = [""];
|
|
2659
2699
|
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) {
|
|
2660
2700
|
let t = /* @__PURE__ */ new Map(), n = /* @__PURE__ */ new Map();
|
|
2661
2701
|
for (let r of e.plan.operations) {
|
|
@@ -2672,7 +2712,7 @@ BEGIN
|
|
|
2672
2712
|
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");
|
|
2673
2713
|
}
|
|
2674
2714
|
}
|
|
2675
|
-
},
|
|
2715
|
+
}, Vr = /* @__PURE__ */ new Set([
|
|
2676
2716
|
"node_modules",
|
|
2677
2717
|
"dist",
|
|
2678
2718
|
"build",
|
|
@@ -2681,58 +2721,68 @@ BEGIN
|
|
|
2681
2721
|
".next",
|
|
2682
2722
|
".git",
|
|
2683
2723
|
".framework"
|
|
2684
|
-
]),
|
|
2724
|
+
]), Hr = async (e) => {
|
|
2685
2725
|
let t = [], n = async (e) => {
|
|
2686
|
-
let r = await
|
|
2726
|
+
let r = await _.readdir(e, { withFileTypes: !0 }).catch(() => []);
|
|
2687
2727
|
for (let i of r) {
|
|
2688
|
-
if (
|
|
2689
|
-
let r =
|
|
2690
|
-
i.isDirectory() ? await n(r) : i.isFile() &&
|
|
2728
|
+
if (Vr.has(i.name)) continue;
|
|
2729
|
+
let r = v(e, i.name);
|
|
2730
|
+
i.isDirectory() ? await n(r) : i.isFile() && o.test(i.name) && t.push(r);
|
|
2691
2731
|
}
|
|
2692
2732
|
};
|
|
2693
2733
|
return await n(e), t.sort((e, t) => {
|
|
2694
2734
|
let n = e.split("/").pop() ?? e, r = t.split("/").pop() ?? t;
|
|
2695
2735
|
return n.localeCompare(r);
|
|
2696
2736
|
});
|
|
2697
|
-
},
|
|
2737
|
+
}, Ur = async (e) => {
|
|
2698
2738
|
let t = [];
|
|
2699
|
-
for (let
|
|
2700
|
-
let e = (await import(
|
|
2701
|
-
if (!
|
|
2702
|
-
process.stderr.write(`[voltro:migrate] ${
|
|
2739
|
+
for (let r of e) {
|
|
2740
|
+
let e = (await import(y(r).href)).default;
|
|
2741
|
+
if (!n(e)) {
|
|
2742
|
+
process.stderr.write(`[voltro:migrate] ${r} has no default-exported \`migration(...)\` — skipped\n`);
|
|
2703
2743
|
continue;
|
|
2704
2744
|
}
|
|
2705
2745
|
t.push({
|
|
2706
|
-
file:
|
|
2746
|
+
file: r,
|
|
2707
2747
|
migration: e
|
|
2708
2748
|
});
|
|
2709
2749
|
}
|
|
2710
2750
|
return t;
|
|
2711
|
-
},
|
|
2751
|
+
}, Wr = (e) => e`
|
|
2712
2752
|
SELECT ${e("id")}
|
|
2713
2753
|
FROM ${e("_voltro_migration_plans")}
|
|
2714
2754
|
WHERE ${e("source")} = 'file'
|
|
2715
|
-
`.pipe(
|
|
2755
|
+
`.pipe(p.map((e) => e.map((e) => e.id)), p.catchAll(() => p.succeed([]))), Gr = class extends Error {
|
|
2756
|
+
migrationId;
|
|
2757
|
+
file;
|
|
2758
|
+
cause;
|
|
2759
|
+
_tag = "FileMigrationLedgerError";
|
|
2760
|
+
constructor(e, t, n) {
|
|
2761
|
+
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";
|
|
2762
|
+
}
|
|
2763
|
+
}, Kr = (e, t, n, r) => p.gen(function* () {
|
|
2716
2764
|
let i = Date.now(), a = (/* @__PURE__ */ new Date()).toISOString(), o = {
|
|
2717
2765
|
sql: e,
|
|
2718
2766
|
log: {
|
|
2719
|
-
info: (e, n) =>
|
|
2767
|
+
info: (e, n) => p.logInfo(e).pipe(p.annotateLogs({
|
|
2720
2768
|
scope: `migrations:file:${t.migration.id}`,
|
|
2721
2769
|
...n ?? {}
|
|
2722
2770
|
})),
|
|
2723
|
-
warn: (e, n) =>
|
|
2771
|
+
warn: (e, n) => p.logWarning(e).pipe(p.annotateLogs({
|
|
2724
2772
|
scope: `migrations:file:${t.migration.id}`,
|
|
2725
2773
|
...n ?? {}
|
|
2726
2774
|
}))
|
|
2727
2775
|
},
|
|
2728
2776
|
appliedAt: a
|
|
2729
2777
|
}, s = t.migration.up(o);
|
|
2730
|
-
|
|
2778
|
+
p.isEffect(s) ? yield* s : yield* p.tryPromise({
|
|
2731
2779
|
try: () => s,
|
|
2732
2780
|
catch: (e) => e
|
|
2733
2781
|
});
|
|
2734
2782
|
let c = Date.now() - i;
|
|
2735
|
-
|
|
2783
|
+
!p.isEffect(s) && c < 2 && (yield* p.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(p.annotateLogs({ scope: `migrations:file:${t.migration.id}` })));
|
|
2784
|
+
let l = yield* dn(e, a);
|
|
2785
|
+
return yield* e`
|
|
2736
2786
|
INSERT INTO ${e("_voltro_migration_plans")}
|
|
2737
2787
|
(${e("id")}, ${e("fingerprint")}, ${e("operations")},
|
|
2738
2788
|
${e("appliedBy")}, ${e("environment")}, ${e("source")},
|
|
@@ -2743,95 +2793,95 @@ BEGIN
|
|
|
2743
2793
|
file: t.file
|
|
2744
2794
|
}])},
|
|
2745
2795
|
${r}, ${n}, ${"file"},
|
|
2746
|
-
${c}, ${
|
|
2747
|
-
|
|
2748
|
-
}),
|
|
2749
|
-
let n = yield*
|
|
2750
|
-
try: () =>
|
|
2796
|
+
${c}, ${l}, ${t.migration.description})
|
|
2797
|
+
`.pipe(p.mapError((e) => new Gr(t.migration.id, t.file, e))), { durationMs: c };
|
|
2798
|
+
}), qr = (e, t) => p.gen(function* () {
|
|
2799
|
+
let n = yield* p.tryPromise({
|
|
2800
|
+
try: () => Hr(v(t, "migrations")),
|
|
2751
2801
|
catch: (e) => e
|
|
2752
2802
|
});
|
|
2753
2803
|
if (n.length === 0) return {
|
|
2754
2804
|
pending: [],
|
|
2755
2805
|
skipped: []
|
|
2756
2806
|
};
|
|
2757
|
-
let r = yield*
|
|
2758
|
-
try: () =>
|
|
2807
|
+
let r = yield* p.tryPromise({
|
|
2808
|
+
try: () => Ur(n),
|
|
2759
2809
|
catch: (e) => e
|
|
2760
2810
|
});
|
|
2761
2811
|
if (r.length === 0) return {
|
|
2762
2812
|
pending: [],
|
|
2763
2813
|
skipped: []
|
|
2764
2814
|
};
|
|
2765
|
-
let i = new Set(yield*
|
|
2815
|
+
let i = new Set(yield* Wr(e));
|
|
2766
2816
|
return {
|
|
2767
2817
|
pending: r.filter((e) => !i.has(e.migration.id)),
|
|
2768
2818
|
skipped: r.filter((e) => i.has(e.migration.id)).map((e) => e.migration.id)
|
|
2769
2819
|
};
|
|
2770
|
-
}),
|
|
2771
|
-
let { pending: n, skipped: r } = yield*
|
|
2820
|
+
}), Jr = (e, t) => qr(e, t).pipe(p.map((e) => e.pending.map((e) => e.migration.id))), Yr = (e, t) => p.gen(function* () {
|
|
2821
|
+
let { pending: n, skipped: r } = yield* qr(e, t.projectRoot);
|
|
2772
2822
|
if (n.length === 0) return {
|
|
2773
2823
|
applied: [],
|
|
2774
2824
|
skipped: r
|
|
2775
2825
|
};
|
|
2776
|
-
yield*
|
|
2826
|
+
yield* T(e);
|
|
2777
2827
|
let i = [];
|
|
2778
2828
|
try {
|
|
2779
2829
|
for (let r of n) {
|
|
2780
|
-
yield*
|
|
2830
|
+
yield* p.logInfo("file-based migration applying").pipe(p.annotateLogs({
|
|
2781
2831
|
scope: "voltro:migrate",
|
|
2782
2832
|
id: r.migration.id,
|
|
2783
2833
|
file: r.file
|
|
2784
2834
|
}));
|
|
2785
|
-
let { durationMs: n } = yield*
|
|
2835
|
+
let { durationMs: n } = yield* Kr(e, r, t.env, t.appliedBy);
|
|
2786
2836
|
i.push({
|
|
2787
2837
|
id: r.migration.id,
|
|
2788
2838
|
durationMs: n
|
|
2789
|
-
}), yield*
|
|
2839
|
+
}), yield* p.logInfo("file-based migration applied").pipe(p.annotateLogs({
|
|
2790
2840
|
scope: "voltro:migrate",
|
|
2791
2841
|
id: r.migration.id,
|
|
2792
2842
|
durationMs: n
|
|
2793
2843
|
}));
|
|
2794
2844
|
}
|
|
2795
2845
|
} finally {
|
|
2796
|
-
yield*
|
|
2846
|
+
yield* E(e).pipe(p.orDie);
|
|
2797
2847
|
}
|
|
2798
2848
|
return {
|
|
2799
2849
|
applied: i,
|
|
2800
2850
|
skipped: r
|
|
2801
2851
|
};
|
|
2802
|
-
}),
|
|
2852
|
+
}), Xr = (e, t) => e`
|
|
2803
2853
|
SELECT ${e("id")}
|
|
2804
2854
|
FROM ${e("_voltro_migration_plans")}
|
|
2805
2855
|
WHERE ${e("id")} = ${t} AND ${e("source")} = 'file'
|
|
2806
2856
|
LIMIT 1
|
|
2807
|
-
`,
|
|
2808
|
-
if (!(yield*
|
|
2809
|
-
let n = yield*
|
|
2810
|
-
try: () =>
|
|
2857
|
+
`, Zr = (e, t) => p.gen(function* () {
|
|
2858
|
+
if (!(yield* Xr(e, t.id))[0]) return yield* p.fail(/* @__PURE__ */ Error(`rollback: ${t.id} not found in _voltro_migration_plans (file source)`));
|
|
2859
|
+
let n = yield* p.tryPromise({
|
|
2860
|
+
try: () => Hr(v(t.projectRoot, "migrations")),
|
|
2811
2861
|
catch: (e) => e
|
|
2812
|
-
}), r = (yield*
|
|
2813
|
-
try: () =>
|
|
2862
|
+
}), r = (yield* p.tryPromise({
|
|
2863
|
+
try: () => Ur(n),
|
|
2814
2864
|
catch: (e) => e
|
|
2815
2865
|
})).find((e) => e.migration.id === t.id);
|
|
2816
|
-
if (!r) return yield*
|
|
2817
|
-
yield*
|
|
2866
|
+
if (!r) return yield* p.fail(/* @__PURE__ */ Error(`rollback: file for migration ${t.id} not found on disk under ${t.projectRoot}/migrations/`));
|
|
2867
|
+
yield* T(e);
|
|
2818
2868
|
let i = Date.now();
|
|
2819
2869
|
try {
|
|
2820
2870
|
let n = {
|
|
2821
2871
|
sql: e,
|
|
2822
2872
|
log: {
|
|
2823
|
-
info: (e, n) =>
|
|
2873
|
+
info: (e, n) => p.logInfo(e).pipe(p.annotateLogs({
|
|
2824
2874
|
scope: `migrations:file:${t.id}`,
|
|
2825
2875
|
...n ?? {}
|
|
2826
2876
|
})),
|
|
2827
|
-
warn: (e, n) =>
|
|
2877
|
+
warn: (e, n) => p.logWarning(e).pipe(p.annotateLogs({
|
|
2828
2878
|
scope: `migrations:file:${t.id}`,
|
|
2829
2879
|
...n ?? {}
|
|
2830
2880
|
}))
|
|
2831
2881
|
},
|
|
2832
2882
|
appliedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2833
2883
|
}, a = r.migration.down(n);
|
|
2834
|
-
|
|
2884
|
+
p.isEffect(a) ? yield* a : yield* p.tryPromise({
|
|
2835
2885
|
try: () => a,
|
|
2836
2886
|
catch: (e) => e
|
|
2837
2887
|
});
|
|
@@ -2841,19 +2891,19 @@ BEGIN
|
|
|
2841
2891
|
durationMs: o
|
|
2842
2892
|
};
|
|
2843
2893
|
} finally {
|
|
2844
|
-
yield*
|
|
2894
|
+
yield* E(e).pipe(p.orDie);
|
|
2845
2895
|
}
|
|
2846
|
-
}),
|
|
2847
|
-
let t = yield*
|
|
2896
|
+
}), Qr = (e) => p.gen(function* () {
|
|
2897
|
+
let t = yield* h.SqlClient, n = yield* t`
|
|
2848
2898
|
SELECT id, fingerprint, appliedAt FROM _voltro_migration_plans
|
|
2849
2899
|
WHERE source = 'auto-diff'
|
|
2850
2900
|
AND squashedAt IS NULL
|
|
2851
2901
|
AND appliedAt < ${e.before}
|
|
2852
2902
|
ORDER BY appliedAt DESC
|
|
2853
2903
|
`;
|
|
2854
|
-
if (n.length === 0) return yield*
|
|
2904
|
+
if (n.length === 0) return yield* p.fail(/* @__PURE__ */ Error(`squash: no rows applied before ${e.before}`));
|
|
2855
2905
|
let r = n[0];
|
|
2856
|
-
if (r.fingerprint !== e.fingerprint) return yield*
|
|
2906
|
+
if (r.fingerprint !== e.fingerprint) return yield* p.fail(/* @__PURE__ */ Error(`squash: latest pre-squash fingerprint (${r.fingerprint.slice(0, 12)}…) differs from current declared-schema fingerprint (${e.fingerprint.slice(0, 12)}…). This means the working tree carries un-applied changes — squashing now would lock the drift in.\n\nFix: run \`voltro db apply\` first to align the live schema with the working tree, then re-squash.`));
|
|
2857
2907
|
yield* t`
|
|
2858
2908
|
UPDATE _voltro_migration_plans
|
|
2859
2909
|
SET squashedAt = CURRENT_TIMESTAMP
|
|
@@ -2888,10 +2938,80 @@ BEGIN
|
|
|
2888
2938
|
snapshotFingerprint: e.fingerprint,
|
|
2889
2939
|
snapshotId: i
|
|
2890
2940
|
};
|
|
2891
|
-
}),
|
|
2941
|
+
}), $r = { safe: !0 }, ei = (e) => {
|
|
2942
|
+
switch (e.kind) {
|
|
2943
|
+
case "drop-column": return {
|
|
2944
|
+
safe: !1,
|
|
2945
|
+
reason: `old instances still SELECT/INSERT "${e.table}"."${e.column}"; after the drop those queries error mid-rollout`,
|
|
2946
|
+
remedy: "stop reading the column in code and deploy that first; drop it in a LATER deploy (expand/contract)"
|
|
2947
|
+
};
|
|
2948
|
+
case "drop-table": return {
|
|
2949
|
+
safe: !1,
|
|
2950
|
+
reason: `old instances still query table "${e.table}"; after the drop every such query errors mid-rollout`,
|
|
2951
|
+
remedy: `remove all code that touches "${e.table}" and deploy that first; drop the table in a LATER deploy`
|
|
2952
|
+
};
|
|
2953
|
+
case "rename-column": return {
|
|
2954
|
+
safe: !1,
|
|
2955
|
+
reason: `old instances reference "${e.table}"."${e.from}", which no longer exists after the rename`,
|
|
2956
|
+
remedy: `add "${e.to}" alongside "${e.from}" + backfill + dual-write, cut code over, then drop "${e.from}" in a LATER deploy`
|
|
2957
|
+
};
|
|
2958
|
+
case "rename-table": return {
|
|
2959
|
+
safe: !1,
|
|
2960
|
+
reason: `old instances query table "${e.from}", which no longer exists after the rename`,
|
|
2961
|
+
remedy: `create "${e.to}" as a copy + dual-write, cut code over, then drop "${e.from}" in a LATER deploy`
|
|
2962
|
+
};
|
|
2963
|
+
case "alter-column-type": return {
|
|
2964
|
+
safe: !1,
|
|
2965
|
+
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`,
|
|
2966
|
+
remedy: "add a new column of the target type + backfill + dual-write, cut code over, then drop the old column in a LATER deploy"
|
|
2967
|
+
};
|
|
2968
|
+
case "alter-column-nullability": return e.toNullable ? $r : {
|
|
2969
|
+
safe: !1,
|
|
2970
|
+
reason: `old instances may INSERT "${e.table}" without (or with NULL in) "${e.column}", which the new NOT NULL rejects`,
|
|
2971
|
+
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)`
|
|
2972
|
+
};
|
|
2973
|
+
case "add-unique":
|
|
2974
|
+
case "add-unique-composite": return {
|
|
2975
|
+
safe: !1,
|
|
2976
|
+
reason: `old instances still write "${e.table}" without honouring the new uniqueness; a colliding insert now fails mid-rollout`,
|
|
2977
|
+
remedy: "make code enforce uniqueness first and deploy that; add the constraint in a LATER deploy once no old writer remains"
|
|
2978
|
+
};
|
|
2979
|
+
case "add-check": return {
|
|
2980
|
+
safe: !1,
|
|
2981
|
+
reason: `old instances may write values to "${e.table}" the new CHECK rejects`,
|
|
2982
|
+
remedy: "make code satisfy the constraint first and deploy that; add the CHECK in a LATER deploy"
|
|
2983
|
+
};
|
|
2984
|
+
case "add-foreign-key": return {
|
|
2985
|
+
safe: !1,
|
|
2986
|
+
reason: `old instances may write "${e.table}"."${e.column}" values with no matching parent row, which the new FK rejects`,
|
|
2987
|
+
remedy: "make code guarantee the referent first and deploy that; add the FK in a LATER deploy"
|
|
2988
|
+
};
|
|
2989
|
+
case "create-table":
|
|
2990
|
+
case "add-column":
|
|
2991
|
+
case "add-index":
|
|
2992
|
+
case "drop-index":
|
|
2993
|
+
case "rename-index":
|
|
2994
|
+
case "alter-column-default":
|
|
2995
|
+
case "drop-unique":
|
|
2996
|
+
case "drop-unique-composite":
|
|
2997
|
+
case "drop-foreign-key":
|
|
2998
|
+
case "drop-check": return $r;
|
|
2999
|
+
}
|
|
3000
|
+
}, ti = (e) => {
|
|
3001
|
+
let t = [];
|
|
3002
|
+
for (let n of e) {
|
|
3003
|
+
let e = ei(n.op);
|
|
3004
|
+
e.safe || t.push({
|
|
3005
|
+
op: n,
|
|
3006
|
+
reason: e.reason,
|
|
3007
|
+
remedy: e.remedy
|
|
3008
|
+
});
|
|
3009
|
+
}
|
|
3010
|
+
return t;
|
|
3011
|
+
}, ni = (e, ...t) => ({
|
|
2892
3012
|
_tag: "RawSqlFragment",
|
|
2893
3013
|
strings: [...e],
|
|
2894
3014
|
values: [...t]
|
|
2895
|
-
}),
|
|
3015
|
+
}), ri = (e) => typeof e == "object" && !!e && e._tag === "RawSqlFragment";
|
|
2896
3016
|
//#endregion
|
|
2897
|
-
export {
|
|
3017
|
+
export { O as DEFAULT_CDC_CHANNEL, o as FILE_MIGRATION_PATTERN, Gr as FileMigrationLedgerError, k as REACTIVE_TRIGGER_PREFIX, re as VOLTRO_MIGRATION_LOCK_KEY, T as acquireMigrationLock, et as applyNamespacedSchema, Tr as applyPlan, Je as applySchema, $t as chunkTables, ei as classifyRollingDeploySafety, I as declaredSnapshot, xe as defaultArrayClause, be as defaultClause, M as defaultJsonClause, Br as describeOutcome, Pr as destructiveScope, ft as detectReactiveTriggerDrift, gn as emitDropColumnDdl, _n as emitDropColumnDdlMysql, qe as emitFrameworkBootstrapSql, Ge as emitNamespaceProvisionDdl, Ke as emitNamespacedSchemaSql, We as emitSchemaSql, P as fingerprintSchema, pt as formatReactiveTriggerDrift, Nr as ignoreTablesFromEnv, U as introspectSchema, n as isFileMigration, ri as isRawSqlFragment, Jt as mapPgType, c as migration, N as numericIdSql, qt as parseEnumCheck, Jr as pendingFileMigrationIds, Kt as planMigrations, tt as provisionTenantNamespace, Fe as reactiveTriggerRepairSql, E as releaseMigrationLock, Mn as renderColumnMysql, Dn as renderColumnPg, Zr as rollbackFileBasedMigration, ti as rollingDeployUnsafeOps, Yr as runFileBasedMigrations, nt as runFrameworkBootstrap, $e as runMigrate, zr as runPlannedMigrations, F as shortFingerprint, ct as snapshotColumn, ni as sql, j as sqlType, Qr as squashMigrationPlans, Ir as unblockLossy, D as withMigrationLock };
|