@voltro/database 0.4.0 → 0.6.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 +68 -0
- package/dist/{fileBased-CRXgPJkv.js → fileBased-Cc-F1oFv.js} +143 -143
- package/dist/index.d.ts +192 -14
- package/dist/index.js +695 -533
- package/dist/sql.d.ts +18 -0
- package/dist/sql.js +240 -231
- package/package.json +2 -2
package/dist/sql.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { H as e, R as t, a as n, c as r, i, n as a, r as o, st as s, t as c, tt as l, z as u } from "./fileBased-Cc-F1oFv.js";
|
|
2
2
|
import { Effect as d } from "effect";
|
|
3
3
|
import { createLogger as f } from "@voltro/logger";
|
|
4
4
|
import { SqlClient as p } from "@effect/sql";
|
|
@@ -121,54 +121,54 @@ var _ = (e, t, n, r = 400) => {
|
|
|
121
121
|
orElse: () => oe(e)
|
|
122
122
|
}), ge = (e, t) => d.acquireUseRelease(w(e), () => t, () => T(e).pipe(d.orDie)), _e = f({ scope: "voltro:migrate" }), E = (e) => {
|
|
123
123
|
_e.warn(e.replace(/^\[voltro:migrate\]\s*/, ""));
|
|
124
|
-
}, D = (
|
|
125
|
-
let r =
|
|
126
|
-
if (
|
|
127
|
-
if (n === "postgres") return `${
|
|
128
|
-
throw Error(`@voltro/plugin-postgis: ${
|
|
124
|
+
}, D = (e, n) => {
|
|
125
|
+
let r = e.type;
|
|
126
|
+
if (e.spatial) {
|
|
127
|
+
if (n === "postgres") return `${e.spatial.kind}(${e.spatial.geomKind},${e.spatial.srid})`;
|
|
128
|
+
throw Error(`@voltro/plugin-postgis: ${e.spatial.kind}('${e.spatial.geomKind}', ${e.spatial.srid}) is postgres-only — the active dialect is '${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
129
|
}
|
|
130
130
|
if (r === "raw") {
|
|
131
|
-
if (
|
|
132
|
-
return
|
|
131
|
+
if (e.rawDdl === void 0) throw Error("raw() column is missing its DDL fragment — this is a framework bug.");
|
|
132
|
+
return e.rawDdl;
|
|
133
133
|
}
|
|
134
|
-
if (r === "array" &&
|
|
134
|
+
if (r === "array" && e.arrayElement) {
|
|
135
135
|
if (n === "postgres") return `${D({
|
|
136
|
-
...
|
|
137
|
-
type:
|
|
136
|
+
...e,
|
|
137
|
+
type: e.arrayElement,
|
|
138
138
|
arrayElement: void 0
|
|
139
139
|
}, n)}[]`;
|
|
140
140
|
if (n === "mysql" || n === "mariadb") return "JSON";
|
|
141
141
|
if (n === "mssql") return "NVARCHAR(MAX)";
|
|
142
|
-
if (
|
|
142
|
+
if (t(n)) return "TEXT";
|
|
143
143
|
}
|
|
144
144
|
if (r === "vector") {
|
|
145
|
-
let r =
|
|
145
|
+
let r = e.vectorDim;
|
|
146
146
|
if (r === void 0) throw Error("vector column is missing its dimension — this is a framework bug.");
|
|
147
|
-
let i =
|
|
147
|
+
let i = e.vectorPrecision === "half";
|
|
148
148
|
if (n === "postgres") return i ? `HALFVEC(${r})` : `VECTOR(${r})`;
|
|
149
149
|
if (n === "mysql" || n === "mariadb") return `VECTOR(${r})`;
|
|
150
150
|
if (n === "mssql") return "VARBINARY(MAX)";
|
|
151
|
-
if (
|
|
151
|
+
if (t(n)) return "BLOB";
|
|
152
152
|
}
|
|
153
153
|
if (r === "interval") {
|
|
154
154
|
if (n === "postgres") return "INTERVAL";
|
|
155
155
|
if (n === "mysql" || n === "mariadb" || n === "mssql") return "BIGINT";
|
|
156
|
-
if (
|
|
156
|
+
if (t(n)) return "INTEGER";
|
|
157
157
|
}
|
|
158
158
|
if (r === "decimal") {
|
|
159
|
-
let r =
|
|
159
|
+
let r = e.precision, i = e.scale ?? 0;
|
|
160
160
|
if (r === void 0) throw Error("decimal column is missing its precision — this is a framework bug.");
|
|
161
|
-
return
|
|
161
|
+
return t(n) ? "NUMERIC" : n === "postgres" ? `NUMERIC(${r}, ${i})` : `DECIMAL(${r}, ${i})`;
|
|
162
162
|
}
|
|
163
|
-
if (r === "bigint") return
|
|
163
|
+
if (r === "bigint") return t(n) ? "INTEGER" : "BIGINT";
|
|
164
164
|
if (r === "enum") {
|
|
165
|
-
if (n === "postgres") return
|
|
166
|
-
if (n === "mysql" || n === "mariadb") return `ENUM(${(
|
|
165
|
+
if (n === "postgres") return e.enumName ?? "TEXT";
|
|
166
|
+
if (n === "mysql" || n === "mariadb") return `ENUM(${(e.enumValues ?? []).map((e) => `'${e.replace(/'/g, "''")}'`).join(", ")})`;
|
|
167
167
|
if (n === "mssql") return "NVARCHAR(255)";
|
|
168
|
-
if (
|
|
168
|
+
if (t(n)) return "TEXT";
|
|
169
169
|
}
|
|
170
|
-
if (r === "text" &&
|
|
171
|
-
if (
|
|
170
|
+
if (r === "text" && e.maxLength !== void 0 && e.generatedAs === void 0) return n === "mssql" ? `NVARCHAR(${e.maxLength})` : t(n) ? "TEXT" : `VARCHAR(${e.maxLength})`;
|
|
171
|
+
if (t(n)) switch (r) {
|
|
172
172
|
case "id": return "TEXT";
|
|
173
173
|
case "text": return "TEXT";
|
|
174
174
|
case "integer": return "INTEGER";
|
|
@@ -186,7 +186,7 @@ var _ = (e, t, n, r = 400) => {
|
|
|
186
186
|
}
|
|
187
187
|
if (n === "mysql" || n === "mariadb") switch (r) {
|
|
188
188
|
case "id": return "VARCHAR(64)";
|
|
189
|
-
case "text": return
|
|
189
|
+
case "text": return e.hasDefault && e.defaultFactory === void 0 || e.oneOf && e.oneOf.length > 0 ? "VARCHAR(255)" : e.generatedAs ? "TEXT" : "LONGTEXT";
|
|
190
190
|
case "integer": return "INT";
|
|
191
191
|
case "real": return "DOUBLE";
|
|
192
192
|
case "boolean": return "TINYINT(1)";
|
|
@@ -202,7 +202,7 @@ var _ = (e, t, n, r = 400) => {
|
|
|
202
202
|
}
|
|
203
203
|
if (n === "mssql") switch (r) {
|
|
204
204
|
case "id": return "NVARCHAR(64)";
|
|
205
|
-
case "text": return
|
|
205
|
+
case "text": return e.hasDefault && e.defaultFactory === void 0 || e.oneOf && e.oneOf.length > 0 ? "NVARCHAR(450)" : "NVARCHAR(MAX)";
|
|
206
206
|
case "integer": return "INT";
|
|
207
207
|
case "real": return "FLOAT";
|
|
208
208
|
case "boolean": return "BIT";
|
|
@@ -228,14 +228,14 @@ 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 e.enumName ?? "TEXT";
|
|
232
232
|
case "array": return "TEXT[]";
|
|
233
233
|
case "interval": return "INTERVAL";
|
|
234
234
|
}
|
|
235
|
-
}, ve = (
|
|
236
|
-
if (!
|
|
237
|
-
let r =
|
|
238
|
-
return r === "now" ?
|
|
235
|
+
}, ve = (e, n) => {
|
|
236
|
+
if (!e.hasDefault || e.defaultFactory !== void 0) return null;
|
|
237
|
+
let r = e.defaultValue;
|
|
238
|
+
return r === "now" ? t(n) ? "DEFAULT CURRENT_TIMESTAMP" : n === "mssql" ? "DEFAULT SYSUTCDATETIME()" : n === "mysql" || n === "mariadb" ? "DEFAULT CURRENT_TIMESTAMP(6)" : "DEFAULT now()" : typeof r == "boolean" ? n === "postgres" ? `DEFAULT ${r}` : `DEFAULT ${+!!r}` : typeof r == "number" ? `DEFAULT ${r}` : typeof r == "string" ? `DEFAULT '${r.replace(/'/g, "''")}'` : typeof r == "object" && r && !Array.isArray(r) ? O(r, n) : null;
|
|
239
239
|
}, O = (e, t) => {
|
|
240
240
|
let n = JSON.stringify(e).replace(/'/g, "''");
|
|
241
241
|
return t === "postgres" ? `DEFAULT '${n}'::jsonb` : t === "mysql" || t === "mariadb" ? `DEFAULT (CAST('${n}' AS JSON))` : `DEFAULT '${n}'`;
|
|
@@ -256,13 +256,13 @@ var _ = (e, t, n, r = 400) => {
|
|
|
256
256
|
case "setNull": return "SET NULL";
|
|
257
257
|
case "noAction": return "NO ACTION";
|
|
258
258
|
}
|
|
259
|
-
}, ye = (e,
|
|
260
|
-
let
|
|
261
|
-
if (
|
|
262
|
-
let
|
|
263
|
-
if (!
|
|
264
|
-
let
|
|
265
|
-
return
|
|
259
|
+
}, ye = (e, t, n) => n === null ? u(e, t) : `${u(n, t)}.${u(e, t)}`, be = 191, xe = (e, t, n) => {
|
|
260
|
+
let r = u(e, n);
|
|
261
|
+
if (n !== "mysql" && n !== "mariadb") return r;
|
|
262
|
+
let i = t[e];
|
|
263
|
+
if (!i) return r;
|
|
264
|
+
let a = D(i, n).toUpperCase();
|
|
265
|
+
return a.endsWith("TEXT") || a.endsWith("BLOB") ? `${r}(${be})` : r;
|
|
266
266
|
}, Se = (e, t) => {
|
|
267
267
|
if (!e.generatedAs) return null;
|
|
268
268
|
let { expr: n, stored: r } = e.generatedAs;
|
|
@@ -271,149 +271,149 @@ var _ = (e, t, n, r = 400) => {
|
|
|
271
271
|
if (t === "mssql") return `AS (${n})${r ? " PERSISTED" : ""}`;
|
|
272
272
|
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.");
|
|
273
273
|
return `GENERATED ALWAYS AS (${n}) ${r ? "STORED" : "VIRTUAL"}`;
|
|
274
|
-
}, Ce = (n, r, i, a
|
|
275
|
-
if (
|
|
276
|
-
let
|
|
277
|
-
|
|
278
|
-
let
|
|
279
|
-
if (
|
|
274
|
+
}, Ce = (e, n, r, i, a = !1, o = !1) => {
|
|
275
|
+
if (n.type === "id" && n.idScheme?.kind === "numeric") return k(e, r);
|
|
276
|
+
let s = r === "mssql" && n.type === "text" && !n.generatedAs && n.maxLength === void 0 && i?.has(e) ? "NVARCHAR(450)" : D(n, r), c = [u(e, r), s];
|
|
277
|
+
n.type === "id" && !o && c.push("PRIMARY KEY");
|
|
278
|
+
let l = Se(n, r);
|
|
279
|
+
if (l) c.push(l);
|
|
280
280
|
else {
|
|
281
|
-
|
|
282
|
-
let e = ve(
|
|
283
|
-
e &&
|
|
284
|
-
}
|
|
285
|
-
if (
|
|
286
|
-
let e =
|
|
287
|
-
|
|
288
|
-
let
|
|
289
|
-
|
|
290
|
-
let
|
|
291
|
-
|
|
292
|
-
}
|
|
293
|
-
if (
|
|
294
|
-
let
|
|
295
|
-
|
|
296
|
-
}
|
|
297
|
-
if (
|
|
298
|
-
if (
|
|
299
|
-
let
|
|
300
|
-
|
|
301
|
-
}
|
|
302
|
-
if (
|
|
303
|
-
let
|
|
304
|
-
|
|
305
|
-
}
|
|
306
|
-
return
|
|
307
|
-
}, we = (e,
|
|
308
|
-
let
|
|
309
|
-
for (let t of e.appliedIndexes ?? []) for (let e of t.fields) typeof e == "string" &&
|
|
310
|
-
for (let t of e.appliedUniques ?? []) for (let e of t.fields) typeof e == "string" &&
|
|
311
|
-
let
|
|
312
|
-
let
|
|
313
|
-
return ` CONSTRAINT ${
|
|
314
|
-
}),
|
|
315
|
-
...
|
|
316
|
-
...
|
|
317
|
-
...
|
|
318
|
-
], f = d.length > 0 ? `${
|
|
319
|
-
if (
|
|
320
|
-
let t =
|
|
321
|
-
return `IF OBJECT_ID('${t.replace(/'/g, "''")}', 'U') IS NULL EXEC(N'${
|
|
281
|
+
n.nullable || c.push("NOT NULL"), n.unique && n.type !== "id" && c.push("UNIQUE");
|
|
282
|
+
let e = ve(n, r);
|
|
283
|
+
e && c.push(e);
|
|
284
|
+
}
|
|
285
|
+
if (n.type === "reference" && n.references && !a) {
|
|
286
|
+
let e = n.references();
|
|
287
|
+
c.push(`REFERENCES ${u(e.tableName, r)} (${u("id", r)})`);
|
|
288
|
+
let t = A(n.onDelete ?? "restrict", r);
|
|
289
|
+
c.push(`ON DELETE ${t}`);
|
|
290
|
+
let i = A(n.refOnUpdate ?? "noAction", r);
|
|
291
|
+
i !== "NO ACTION" && c.push(`ON UPDATE ${i}`);
|
|
292
|
+
}
|
|
293
|
+
if (n.oneOf && n.oneOf.length > 0) {
|
|
294
|
+
let t = n.oneOf.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ");
|
|
295
|
+
c.push(`CHECK (${u(e, r)} IN (${t}))`);
|
|
296
|
+
}
|
|
297
|
+
if (n.checks && n.checks.length > 0) for (let e of n.checks) c.push(`CHECK (${e})`);
|
|
298
|
+
if (n.type === "enum" && n.enumValues && n.enumValues.length > 0 && (r === "mssql" || t(r))) {
|
|
299
|
+
let t = n.enumValues.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ");
|
|
300
|
+
c.push(`CHECK (${u(e, r)} IN (${t}))`);
|
|
301
|
+
}
|
|
302
|
+
if (n.type === "json" || n.type === "array") {
|
|
303
|
+
let n = u(e, r);
|
|
304
|
+
t(r) ? c.push(`CHECK (${n} IS NULL OR json_valid(${n}))`) : r === "mssql" && c.push(`CHECK (${n} IS NULL OR ISJSON(${n}) = 1)`);
|
|
305
|
+
}
|
|
306
|
+
return c.join(" ");
|
|
307
|
+
}, we = (e, t, n = null, r) => {
|
|
308
|
+
let i = /* @__PURE__ */ new Set();
|
|
309
|
+
for (let t of e.appliedIndexes ?? []) for (let e of t.fields) typeof e == "string" && i.add(e);
|
|
310
|
+
for (let t of e.appliedUniques ?? []) for (let e of t.fields) typeof e == "string" && i.add(e);
|
|
311
|
+
let a = e.appliedPrimaryKey ?? [], o = a.length > 0, s = Object.entries(e.fields).map(([e, n]) => ` ${Ce(e, n, t, i, r?.has(e) ?? !1, o)}`).join(",\n"), c = (e.appliedUniques ?? []).map((n) => {
|
|
312
|
+
let r = n.fields.map((n) => xe(n, e.fields, t)).join(", ");
|
|
313
|
+
return ` CONSTRAINT ${u(n.name, t)} UNIQUE (${r})`;
|
|
314
|
+
}), l = (e.appliedChecks ?? []).map((e) => ` CONSTRAINT ${u(e.name, t)} CHECK (${e.expr})`), d = [
|
|
315
|
+
...o ? [` PRIMARY KEY (${a.map((n) => xe(n, e.fields, t)).join(", ")})`] : [],
|
|
316
|
+
...c,
|
|
317
|
+
...l
|
|
318
|
+
], f = d.length > 0 ? `${s},\n${d.join(",\n")}` : s, p = ye(e.tableName, t, n);
|
|
319
|
+
if (t === "mssql") {
|
|
320
|
+
let t = n === null ? e.tableName : `${n}.${e.tableName}`, r = `CREATE TABLE ${p} (\n${f}\n)`;
|
|
321
|
+
return `IF OBJECT_ID('${t.replace(/'/g, "''")}', 'U') IS NULL EXEC(N'${r.replace(/'/g, "''")}');`;
|
|
322
322
|
}
|
|
323
323
|
return [
|
|
324
324
|
`CREATE TABLE IF NOT EXISTS ${p} (`,
|
|
325
325
|
f,
|
|
326
326
|
");"
|
|
327
327
|
].join("\n");
|
|
328
|
-
}, Te = (e,
|
|
329
|
-
let
|
|
330
|
-
return
|
|
331
|
-
}, Ee = (
|
|
332
|
-
let a = [],
|
|
328
|
+
}, Te = (e, t, n) => {
|
|
329
|
+
let r = `${e}_${t.name}_fts`, i = (e) => u(e, n), a = t.columns.map(i).join(", "), o = t.columns.map((e) => `new.${i(e)}`).join(", "), s = [];
|
|
330
|
+
return s.push(`CREATE VIRTUAL TABLE IF NOT EXISTS ${i(r)} USING fts5(${a}, row_id UNINDEXED, tokenize='porter');`), s.push(`CREATE TRIGGER IF NOT EXISTS ${i(`${r}_ai`)} AFTER INSERT ON ${i(e)} BEGIN\n INSERT INTO ${i(r)} (${a}, row_id) VALUES (${o}, new.${i("id")});\nEND;`), s.push(`CREATE TRIGGER IF NOT EXISTS ${i(`${r}_ad`)} AFTER DELETE ON ${i(e)} BEGIN\n DELETE FROM ${i(r)} WHERE row_id = old.${i("id")};\nEND;`), s.push(`CREATE TRIGGER IF NOT EXISTS ${i(`${r}_au`)} AFTER UPDATE ON ${i(e)} BEGIN\n DELETE FROM ${i(r)} WHERE row_id = old.${i("id")};\n INSERT INTO ${i(r)} (${a}, row_id) VALUES (${o}, new.${i("id")});\nEND;`), s;
|
|
331
|
+
}, Ee = (t, n, r = null) => {
|
|
332
|
+
let a = [], o = /* @__PURE__ */ new Set(), s = (e) => e.join("\0"), c = (t, r, i, a) => {
|
|
333
333
|
let o = {
|
|
334
334
|
using: "",
|
|
335
335
|
withSuffix: "",
|
|
336
336
|
skip: !1
|
|
337
337
|
};
|
|
338
338
|
if (i === void 0 || i === "btree") return o;
|
|
339
|
-
if (i === "gist") return
|
|
339
|
+
if (i === "gist") return n === "postgres" ? {
|
|
340
340
|
using: " USING GIST",
|
|
341
341
|
withSuffix: "",
|
|
342
342
|
skip: !1
|
|
343
|
-
} : (E(`[voltro:migrate] index '${
|
|
344
|
-
if (i === "gin") return
|
|
343
|
+
} : (E(`[voltro:migrate] index '${t}' on '${r}': ${n} doesn't support GiST — falling back to btree. Spatial queries on this column will fall back to sequential scan.`), o);
|
|
344
|
+
if (i === "gin") return n === "postgres" ? {
|
|
345
345
|
using: " USING GIN",
|
|
346
346
|
withSuffix: "",
|
|
347
347
|
skip: !1
|
|
348
|
-
} :
|
|
348
|
+
} : n === "mysql" || n === "mariadb" ? (E(`[voltro:migrate] gin index '${t}' on '${r}': ${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(...).`), {
|
|
349
349
|
using: "",
|
|
350
350
|
withSuffix: "",
|
|
351
351
|
skip: !0
|
|
352
|
-
}) : (E(`[voltro:migrate] gin index '${
|
|
353
|
-
if (i === "brin") return
|
|
352
|
+
}) : (E(`[voltro:migrate] gin index '${t}' on '${r}': ${n} has no GIN access method — falling back to btree. Containment/membership queries won't use this index efficiently.`), o);
|
|
353
|
+
if (i === "brin") return n === "postgres" ? {
|
|
354
354
|
using: " USING BRIN",
|
|
355
355
|
withSuffix: "",
|
|
356
356
|
skip: !1
|
|
357
|
-
} : (E(`[voltro:migrate] brin index '${
|
|
357
|
+
} : (E(`[voltro:migrate] brin index '${t}' on '${r}': ${n} has no BRIN access method — falling back to btree (larger on disk, but the range scan still works).`), o);
|
|
358
358
|
if (i === "hnsw") {
|
|
359
|
-
if (
|
|
360
|
-
let
|
|
359
|
+
if (n === "postgres") {
|
|
360
|
+
let t = a?.hnsw?.m ?? 16, n = a?.hnsw?.efConstruction ?? 64, r = a?.hnsw?.opclass ?? e(a?.hnsw?.distance);
|
|
361
361
|
return {
|
|
362
362
|
using: " USING hnsw",
|
|
363
|
-
withSuffix: ` WITH (m = ${
|
|
363
|
+
withSuffix: ` WITH (m = ${t}, ef_construction = ${n})`,
|
|
364
364
|
opclass: r,
|
|
365
365
|
skip: !1
|
|
366
366
|
};
|
|
367
367
|
}
|
|
368
|
-
return E(`[voltro:migrate] hnsw index '${
|
|
368
|
+
return E(`[voltro:migrate] hnsw index '${t}' on '${r}': ${n} has no pgvector / HNSW access method — skipping. Nearest-neighbour queries on this column fall back to a sequential scan.`), {
|
|
369
369
|
using: "",
|
|
370
370
|
withSuffix: "",
|
|
371
371
|
skip: !0
|
|
372
372
|
};
|
|
373
373
|
}
|
|
374
374
|
return o;
|
|
375
|
-
},
|
|
376
|
-
let
|
|
377
|
-
if (typeof
|
|
378
|
-
if ("jsonPath" in
|
|
379
|
-
let { column:
|
|
380
|
-
return `(${
|
|
375
|
+
}, l = (e, r) => {
|
|
376
|
+
let a = e.map((e) => {
|
|
377
|
+
if (typeof e == "string") return xe(e, t.fields, n);
|
|
378
|
+
if ("jsonPath" in e) {
|
|
379
|
+
let { column: t, path: r, numeric: a } = e.jsonPath;
|
|
380
|
+
return `(${i(t, r, n, { numeric: a })})`;
|
|
381
381
|
}
|
|
382
|
-
return `(${
|
|
382
|
+
return `(${e.expr})`;
|
|
383
383
|
});
|
|
384
|
-
return
|
|
385
|
-
}, d = (e,
|
|
386
|
-
let f =
|
|
384
|
+
return r !== void 0 && a.length === 1 ? `${a[0]} ${r}` : a.join(", ");
|
|
385
|
+
}, d = (e, t, i, a, o, s = "", d = !1) => {
|
|
386
|
+
let f = c(e, t, a, o);
|
|
387
387
|
if (f.skip) return "";
|
|
388
|
-
let p =
|
|
389
|
-
return
|
|
388
|
+
let p = l(i, f.opclass), m = `${f.withSuffix}${s}`, h = ye(t, n, r), g = d ? "UNIQUE " : "";
|
|
389
|
+
return n === "mssql" ? `IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = '${e}') CREATE ${g}INDEX ${u(e, n)} ON ${h}${f.using} (${p})${m};` : n === "mysql" ? `CREATE ${g}INDEX ${u(e, n)} ON ${h}${f.using} (${p})${m};` : `CREATE ${g}INDEX IF NOT EXISTS ${u(e, n)} ON ${h}${f.using} (${p})${m};`;
|
|
390
390
|
};
|
|
391
|
-
for (let
|
|
392
|
-
let
|
|
393
|
-
let
|
|
394
|
-
return `setweight(to_tsvector('${
|
|
391
|
+
for (let e of t.appliedFullText ?? []) if (n === "postgres") {
|
|
392
|
+
let r = e.config ?? "english", i = `${e.name}_tsv`, o = e.columns.map((t) => {
|
|
393
|
+
let i = e.weights?.[t] ?? "A";
|
|
394
|
+
return `setweight(to_tsvector('${r}', coalesce(${u(t, n)}, '')), '${i}')`;
|
|
395
395
|
}).join(" || ");
|
|
396
|
-
a.push(`ALTER TABLE ${t
|
|
397
|
-
} else if (
|
|
398
|
-
let
|
|
399
|
-
a.push(`CREATE FULLTEXT INDEX ${
|
|
400
|
-
} else if (
|
|
401
|
-
else
|
|
402
|
-
for (let
|
|
403
|
-
if ((
|
|
404
|
-
E(`[voltro:migrate] json-path index '${
|
|
396
|
+
a.push(`ALTER TABLE ${u(t.tableName, n)} ADD COLUMN IF NOT EXISTS ${u(i, n)} tsvector GENERATED ALWAYS AS (${o}) STORED;`), a.push(`CREATE INDEX IF NOT EXISTS ${u(e.name, n)} ON ${u(t.tableName, n)} USING GIN (${u(i, n)});`);
|
|
397
|
+
} else if (n === "mysql" || n === "mariadb") {
|
|
398
|
+
let r = e.columns.map((e) => u(e, n)).join(", ");
|
|
399
|
+
a.push(`CREATE FULLTEXT INDEX ${u(e.name, n)} ON ${u(t.tableName, n)} (${r});`);
|
|
400
|
+
} else if (n === "turso") throw Error(`@voltro/sql-turso: full-text index '${e.name}' on '${t.tableName}' is not supported — Turso's MVCC mode has no FTS5 virtual tables. Drop .fullTextIndex(), or use the 'sqlite' / 'postgres' dialect for this table.`);
|
|
401
|
+
else n === "sqlite" ? a.push(...Te(t.tableName, e, n)) : E(`[voltro:migrate] full-text index '${e.name}' on '${t.tableName}': ${n} uses a ranked LIKE search at query time (no native FTS catalog required).`);
|
|
402
|
+
for (let e of t.appliedIndexes) {
|
|
403
|
+
if ((n === "mariadb" || n === "mssql") && e.fields.some((e) => typeof e != "string" && "jsonPath" in e)) {
|
|
404
|
+
E(`[voltro:migrate] json-path index '${e.name}' on '${t.tableName}': ${n} cannot index a JSON expression directly — skipping. For an indexed JSON path on ${n}, add a generated column for the path (text().generatedAs(...) / .stored()) and .index([...]) it, then filter on that column instead of jsonField(...).`);
|
|
405
405
|
continue;
|
|
406
406
|
}
|
|
407
|
-
if (
|
|
408
|
-
let
|
|
409
|
-
|
|
410
|
-
let i = d(
|
|
411
|
-
i !== "" && a.push(i),
|
|
407
|
+
if (e.unique && e.where !== void 0 && e.where.length > 0 && (n === "mysql" || n === "mariadb")) throw Error(`@voltro/migrate: uniqueActive index '${e.name}' on '${t.tableName}' cannot be expressed on ${n} — it has no partial (WHERE) index support, and a full UNIQUE index would forbid re-creating a soft-deleted row. Use a generated STORED column (text().generatedAs("CASE WHEN \`deletedAt\` IS NULL THEN <key> ELSE NULL END", { stored: true })) + .unique([...]) on ${n}, or move this table to postgres/sqlite/mssql.`);
|
|
408
|
+
let r = "";
|
|
409
|
+
e.where !== void 0 && e.where.length > 0 && (n === "mysql" || n === "mariadb" ? E(`[voltro:migrate] partial index '${e.name}' on '${t.tableName}': ${n} does not support WHERE clauses on indexes — emitting a full index`) : r = ` WHERE ${e.where}`);
|
|
410
|
+
let i = d(e.name, t.tableName, e.fields, e.kind, e.kindOptions, r, e.unique === !0);
|
|
411
|
+
i !== "" && a.push(i), e.fields.every((e) => typeof e == "string") && o.add(s(e.fields));
|
|
412
412
|
}
|
|
413
|
-
for (let [
|
|
414
|
-
if (n.type !== "reference" || n.fkAutoIndex === !1 ||
|
|
415
|
-
let r = `${
|
|
416
|
-
a.push(d(r,
|
|
413
|
+
for (let [e, n] of Object.entries(t.fields)) {
|
|
414
|
+
if (n.type !== "reference" || n.fkAutoIndex === !1 || o.has(s([e]))) continue;
|
|
415
|
+
let r = `${t.tableName}_${e}_idx`;
|
|
416
|
+
a.push(d(r, t.tableName, [e])), o.add(s([e]));
|
|
417
417
|
}
|
|
418
418
|
return a.join("\n");
|
|
419
419
|
}, De = "CREATE OR REPLACE FUNCTION framework_notify_change() RETURNS trigger AS $$\nDECLARE\n payload TEXT;\nBEGIN\n payload := json_build_object(\n 'table', TG_TABLE_NAME,\n -- The schema the write landed in. Under namespace isolation this is\n -- the tenant schema (tenant_<id>); the CDC consumer uses it so a\n -- write in tenant A namespace does not spuriously wake tenant B\n -- subscriptions on the same-named table. public/absent = shared.\n 'schema', TG_TABLE_SCHEMA,\n 'op', lower(TG_OP),\n 'old', CASE WHEN TG_OP = 'INSERT' THEN NULL ELSE row_to_json(OLD) END,\n 'new', CASE WHEN TG_OP = 'DELETE' THEN NULL ELSE row_to_json(NEW) END\n )::text;\n -- pg_notify hard-caps the payload at 8000 bytes; a wide reactive row\n -- (big text / json column) would otherwise abort the write itself.\n -- Fall back to a body-less notice — the CDC consumer treats absent\n -- old/new as \"all columns changed\" and re-queries, so correctness is\n -- preserved; only the delta-diff fast path is skipped for that write.\n IF octet_length(payload) >= 7999 THEN\n payload := json_build_object(\n 'table', TG_TABLE_NAME,\n 'schema', TG_TABLE_SCHEMA,\n 'op', lower(TG_OP),\n 'old', NULL,\n 'new', NULL,\n 'oversized', true\n )::text;\n END IF;\n PERFORM pg_notify('framework_changes', payload);\n RETURN COALESCE(NEW, OLD);\nEND;\n$$ LANGUAGE plpgsql;", Oe = (e) => `
|
|
@@ -459,27 +459,27 @@ FOR EACH ROW EXECUTE FUNCTION framework_notify_change();
|
|
|
459
459
|
sorted: o,
|
|
460
460
|
cyclic: r
|
|
461
461
|
};
|
|
462
|
-
}, Me = (e,
|
|
463
|
-
let
|
|
464
|
-
switch (
|
|
462
|
+
}, Me = (e, t, n = null) => {
|
|
463
|
+
let r = (e) => n ? `${u(n, t)}.${u(e, t)}` : u(e, t), i = `${e.table}_${e.column}_fkey`, a = A(e.onDelete, t), o = A(e.onUpdate, t), s = ` ON DELETE ${a}${o === "NO ACTION" ? "" : ` ON UPDATE ${o}`}`, c = (n) => `ADD CONSTRAINT ${u(i, t)} FOREIGN KEY ${n ? "IF NOT EXISTS " : ""}(${u(e.column, t)}) REFERENCES ${r(e.targetTable)} (${u("id", t)})${s}`;
|
|
464
|
+
switch (t) {
|
|
465
465
|
case "postgres": return [
|
|
466
466
|
"DO $$ BEGIN",
|
|
467
467
|
" IF NOT EXISTS (SELECT 1 FROM pg_constraint",
|
|
468
|
-
` WHERE conname = '${
|
|
469
|
-
` ALTER TABLE ${
|
|
468
|
+
` WHERE conname = '${i}' AND conrelid = '${n ? `${n}.` : ""}${e.table}'::regclass) THEN`,
|
|
469
|
+
` ALTER TABLE ${r(e.table)} ${c(!1)};`,
|
|
470
470
|
" END IF;",
|
|
471
471
|
"END $$;"
|
|
472
472
|
].join("\n");
|
|
473
|
-
case "mssql": return `IF NOT EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = '${
|
|
474
|
-
case "mariadb": return `ALTER TABLE ${
|
|
475
|
-
default: return `ALTER TABLE ${
|
|
473
|
+
case "mssql": return `IF NOT EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = '${i}') ALTER TABLE ${r(e.table)} ${c(!1)};`;
|
|
474
|
+
case "mariadb": return `ALTER TABLE ${r(e.table)} ${c(!0)};`;
|
|
475
|
+
default: return `ALTER TABLE ${r(e.table)} ${c(!1)};`;
|
|
476
476
|
}
|
|
477
|
-
}, Ne = (
|
|
477
|
+
}, Ne = (e, n) => {
|
|
478
478
|
let r = /* @__PURE__ */ new Map();
|
|
479
|
-
if (
|
|
480
|
-
for (let
|
|
481
|
-
let
|
|
482
|
-
|
|
479
|
+
if (t(n)) return r;
|
|
480
|
+
for (let t of e) {
|
|
481
|
+
let e = r.get(t.table) ?? /* @__PURE__ */ new Set();
|
|
482
|
+
e.add(t.column), r.set(t.table, e);
|
|
483
483
|
}
|
|
484
484
|
return r;
|
|
485
485
|
}, Pe = (e, t) => {
|
|
@@ -503,7 +503,7 @@ FOR EACH ROW EXECUTE FUNCTION framework_notify_change();
|
|
|
503
503
|
let n = /* @__PURE__ */ new Set();
|
|
504
504
|
for (let t of e) for (let e of Object.values(t.fields)) e.type === "enum" && e.enumName && n.add(e.enumName);
|
|
505
505
|
let r = [];
|
|
506
|
-
for (let e of n) for (let { from: t, to: n } of
|
|
506
|
+
for (let e of n) for (let { from: t, to: n } of s(e)) {
|
|
507
507
|
let i = e.replace(/'/g, "''"), a = t.replace(/'/g, "''"), o = n.replace(/'/g, "''");
|
|
508
508
|
r.push(`DO \$\$
|
|
509
509
|
DECLARE
|
|
@@ -513,21 +513,21 @@ BEGIN
|
|
|
513
513
|
}
|
|
514
514
|
return r.join("\n\n");
|
|
515
515
|
}, Ie = (e) => {
|
|
516
|
-
let t = [],
|
|
517
|
-
for (let i of e)
|
|
516
|
+
let t = [], r = [];
|
|
517
|
+
for (let i of e) n(i) ? r.push(i) : t.push(i);
|
|
518
518
|
return {
|
|
519
519
|
tables: t,
|
|
520
|
-
views:
|
|
520
|
+
views: r
|
|
521
521
|
};
|
|
522
|
-
}, j = (
|
|
523
|
-
let { tables:
|
|
522
|
+
}, j = (e, n) => {
|
|
523
|
+
let { tables: i, views: a } = Ie(e), { sorted: o, cyclic: s } = je(i.map((e) => b(e, n))), c = Ne(s, n), l = [];
|
|
524
524
|
n === "postgres" && ke(o) && l.push("CREATE EXTENSION IF NOT EXISTS vector;"), n === "postgres" && Ae(o) && l.push("CREATE EXTENSION IF NOT EXISTS postgis;");
|
|
525
525
|
let u = Fe(o, n);
|
|
526
526
|
u && l.push(u);
|
|
527
527
|
let d = Pe(o, n);
|
|
528
528
|
d && l.push(d);
|
|
529
529
|
for (let e of o) l.push(we(e, n, null, c.get(e.tableName)));
|
|
530
|
-
if (!
|
|
530
|
+
if (!t(n)) for (let e of s) l.push(Me(e, n));
|
|
531
531
|
for (let e of o) {
|
|
532
532
|
let t = Ee(e, n);
|
|
533
533
|
t && l.push(t);
|
|
@@ -537,57 +537,57 @@ BEGIN
|
|
|
537
537
|
l.push(De);
|
|
538
538
|
for (let e of f) l.push(Oe(e));
|
|
539
539
|
}
|
|
540
|
-
for (let e of a) l.push(
|
|
540
|
+
for (let e of a) l.push(r(e, n));
|
|
541
541
|
return l.join("\n\n") + "\n";
|
|
542
|
-
}, Le = (e,
|
|
543
|
-
let
|
|
544
|
-
switch (
|
|
545
|
-
case "postgres": return `CREATE SCHEMA IF NOT EXISTS ${
|
|
542
|
+
}, Le = (e, t, n) => {
|
|
543
|
+
let r = u(e, t);
|
|
544
|
+
switch (t) {
|
|
545
|
+
case "postgres": return `CREATE SCHEMA IF NOT EXISTS ${r};`;
|
|
546
546
|
case "mysql":
|
|
547
|
-
case "mariadb": return `CREATE DATABASE IF NOT EXISTS ${
|
|
548
|
-
case "mssql": return `IF SCHEMA_ID('${e.replace(/'/g, "''")}') IS NULL EXEC(N'CREATE SCHEMA ${
|
|
547
|
+
case "mariadb": return `CREATE DATABASE IF NOT EXISTS ${r};`;
|
|
548
|
+
case "mssql": return `IF SCHEMA_ID('${e.replace(/'/g, "''")}') IS NULL EXEC(N'CREATE SCHEMA ${r.replace(/'/g, "''")}');`;
|
|
549
549
|
case "turso":
|
|
550
|
-
case "sqlite": return `ATTACH DATABASE '${(
|
|
551
|
-
}
|
|
552
|
-
}, Re = (n, r, i
|
|
553
|
-
let
|
|
554
|
-
if (
|
|
555
|
-
if (
|
|
556
|
-
let { sorted:
|
|
557
|
-
for (let e of
|
|
558
|
-
if (!
|
|
559
|
-
for (let e of
|
|
560
|
-
let t = Ee(e,
|
|
550
|
+
case "sqlite": return `ATTACH DATABASE '${(n ?? `${e}.db`).replace(/'/g, "''")}' AS ${r};`;
|
|
551
|
+
}
|
|
552
|
+
}, Re = (e, n, r, i) => {
|
|
553
|
+
let a = Le(r, n, i), o = u(r, n);
|
|
554
|
+
if (n === "postgres") return `${a}\n\nSET search_path TO ${o};\n\n${j(e, n)}`;
|
|
555
|
+
if (n === "mysql" || n === "mariadb") return `${a}\n\nUSE ${o};\n\n${j(e, n)}`;
|
|
556
|
+
let { sorted: s, cyclic: c } = je(e), l = Ne(c, n), d = [a];
|
|
557
|
+
for (let e of s) d.push(we(e, n, r, l.get(e.tableName)));
|
|
558
|
+
if (!t(n)) for (let e of c) d.push(Me(e, n, r));
|
|
559
|
+
for (let e of s) {
|
|
560
|
+
let t = Ee(e, n, r);
|
|
561
561
|
t && d.push(t);
|
|
562
562
|
}
|
|
563
563
|
return d.join("\n\n") + "\n";
|
|
564
|
-
}, ze = (e,
|
|
565
|
-
if (
|
|
566
|
-
let { sorted:
|
|
567
|
-
|
|
568
|
-
let
|
|
569
|
-
|
|
570
|
-
for (let e of
|
|
571
|
-
for (let e of
|
|
572
|
-
for (let e of
|
|
573
|
-
let
|
|
574
|
-
if (
|
|
575
|
-
let
|
|
576
|
-
let
|
|
577
|
-
return `ALTER TABLE ${
|
|
564
|
+
}, ze = (e, t) => {
|
|
565
|
+
if (t !== "postgres") return j(e, t);
|
|
566
|
+
let { sorted: n, cyclic: r } = je(e), i = Ne(r, t), a = [], o = Fe(n, t);
|
|
567
|
+
o && a.push(o);
|
|
568
|
+
let s = Pe(n, t);
|
|
569
|
+
s && a.push(s);
|
|
570
|
+
for (let e of n) a.push(we(e, t, null, i.get(e.tableName)));
|
|
571
|
+
for (let e of r) a.push(Me(e, t));
|
|
572
|
+
for (let e of n) {
|
|
573
|
+
let n = Object.entries(e.fields);
|
|
574
|
+
if (n.length === 0) continue;
|
|
575
|
+
let r = n.map(([n, r]) => {
|
|
576
|
+
let a = Ce(n, r, t, void 0, i.get(e.tableName)?.has(n) ?? !1);
|
|
577
|
+
return `ALTER TABLE ${u(e.tableName, t)} ADD COLUMN IF NOT EXISTS ${a};`;
|
|
578
578
|
});
|
|
579
|
-
|
|
579
|
+
a.push(r.join("\n"));
|
|
580
580
|
}
|
|
581
|
-
for (let e of
|
|
582
|
-
let
|
|
583
|
-
|
|
581
|
+
for (let e of n) {
|
|
582
|
+
let n = Ee(e, t);
|
|
583
|
+
n && a.push(n);
|
|
584
584
|
}
|
|
585
|
-
let
|
|
586
|
-
if (
|
|
587
|
-
|
|
588
|
-
for (let e of
|
|
585
|
+
let c = n.filter((e) => e.isReactive);
|
|
586
|
+
if (c.length > 0) {
|
|
587
|
+
a.push(De);
|
|
588
|
+
for (let e of c) a.push(Oe(e));
|
|
589
589
|
}
|
|
590
|
-
return
|
|
590
|
+
return a.join("\n\n") + "\n";
|
|
591
591
|
}, Be = (e, t = "postgres") => d.gen(function* () {
|
|
592
592
|
let n = yield* p.SqlClient;
|
|
593
593
|
yield* ge(n, Ue(n, j(e, t), t)).pipe(d.orDie);
|
|
@@ -694,8 +694,8 @@ BEGIN
|
|
|
694
694
|
columns: ["id"],
|
|
695
695
|
unique: !0
|
|
696
696
|
});
|
|
697
|
-
let
|
|
698
|
-
for (let t of
|
|
697
|
+
let a = e.appliedIndexes ?? [];
|
|
698
|
+
for (let t of a) {
|
|
699
699
|
if (t.unique && t.where) continue;
|
|
700
700
|
let n = t.fields.map((e) => typeof e == "string" ? e : "jsonPath" in e ? `(json:${e.jsonPath.column}->${e.jsonPath.path.join(".")}${e.jsonPath.numeric ? "::num" : ""})` : `(${e.expr})`), i = t.fields.some((e) => typeof e != "string");
|
|
701
701
|
r({
|
|
@@ -706,21 +706,21 @@ BEGIN
|
|
|
706
706
|
...i ? { expression: !0 } : {}
|
|
707
707
|
});
|
|
708
708
|
}
|
|
709
|
-
let
|
|
710
|
-
for (let t of
|
|
709
|
+
let o = e.appliedUniques ?? [];
|
|
710
|
+
for (let t of o) t.fields.length < 2 || r({
|
|
711
711
|
name: t.name,
|
|
712
712
|
table: e.tableName,
|
|
713
713
|
columns: t.fields,
|
|
714
714
|
unique: !0
|
|
715
715
|
});
|
|
716
|
-
let
|
|
717
|
-
for (let e of
|
|
716
|
+
let s = /* @__PURE__ */ new Set();
|
|
717
|
+
for (let e of a) e.fields.every((e) => typeof e == "string") && s.add(e.fields.join("\0"));
|
|
718
718
|
for (let [t, n] of Object.entries(e.fields)) {
|
|
719
719
|
let i = n;
|
|
720
|
-
if (i.dropped || !i.references || i.fkAutoIndex === !1 ||
|
|
721
|
-
let
|
|
722
|
-
|
|
723
|
-
name:
|
|
720
|
+
if (i.dropped || !i.references || i.fkAutoIndex === !1 || s.has(t)) continue;
|
|
721
|
+
let a = `${e.tableName}_${t}_idx`;
|
|
722
|
+
l(e.tableName, a, !0, [t]), r({
|
|
723
|
+
name: a,
|
|
724
724
|
table: e.tableName,
|
|
725
725
|
columns: [t],
|
|
726
726
|
unique: !1
|
|
@@ -1261,6 +1261,7 @@ BEGIN
|
|
|
1261
1261
|
JOIN pg_attribute att ON att.attrelid = con.conrelid AND att.attnum = ANY (con.conkey)
|
|
1262
1262
|
WHERE con.contype = 'p' AND nsp.nspname = current_schema()
|
|
1263
1263
|
AND rel.relname IN ${t}
|
|
1264
|
+
ORDER BY rel.relname, array_position(con.conkey, att.attnum)
|
|
1264
1265
|
`), p = yield* a(l, (t) => e`
|
|
1265
1266
|
SELECT rel.relname AS table_name, con.conname AS constraint_name,
|
|
1266
1267
|
att.attname AS column_name, array_position(con.conkey, att.attnum) AS position
|
|
@@ -2337,7 +2338,14 @@ BEGIN
|
|
|
2337
2338
|
}), rr = () => {
|
|
2338
2339
|
let e = (process.env.VOLTRO_DB_IGNORE_TABLES ?? "").split(",").map((e) => e.trim()).filter((e) => e.length > 0);
|
|
2339
2340
|
return e.length > 0 ? { ignoreTables: e } : {};
|
|
2340
|
-
}, ir = (e
|
|
2341
|
+
}, ir = (e) => ({
|
|
2342
|
+
...e,
|
|
2343
|
+
operations: e.operations.map((e) => {
|
|
2344
|
+
if (!e.blocked || e.classification !== "lossy") return e;
|
|
2345
|
+
let { blocked: t, ...n } = e;
|
|
2346
|
+
return n;
|
|
2347
|
+
})
|
|
2348
|
+
}), ar = (e, t, n) => d.gen(function* () {
|
|
2341
2349
|
let r = Qn(e), i = t.filter((e) => !at(e.tableName));
|
|
2342
2350
|
if (process.env.VOLTRO_MIGRATE_FORCE !== "1") {
|
|
2343
2351
|
let t = M(P(i, r));
|
|
@@ -2363,6 +2371,7 @@ BEGIN
|
|
|
2363
2371
|
kind: "refused-blocked",
|
|
2364
2372
|
plan: s
|
|
2365
2373
|
};
|
|
2374
|
+
s = ir(s);
|
|
2366
2375
|
}
|
|
2367
2376
|
return s.operations.length === 0 ? {
|
|
2368
2377
|
kind: "up-to-date",
|
|
@@ -2377,10 +2386,10 @@ BEGIN
|
|
|
2377
2386
|
}),
|
|
2378
2387
|
plan: s
|
|
2379
2388
|
};
|
|
2380
|
-
}),
|
|
2389
|
+
}), or = (e, t, n) => process.env.VOLTRO_AUTO_MIGRATE === "0" ? d.succeed({
|
|
2381
2390
|
kind: "skipped",
|
|
2382
2391
|
reason: "VOLTRO_AUTO_MIGRATE=0"
|
|
2383
|
-
}) : n.environment === "prod" ? $n(e, t) :
|
|
2392
|
+
}) : n.environment === "prod" ? $n(e, t) : ar(e, t, n), sr = (e, t) => {
|
|
2384
2393
|
switch (e.kind) {
|
|
2385
2394
|
case "skipped": return `auto-migrate: skipped (${e.reason})`;
|
|
2386
2395
|
case "up-to-date": return `auto-migrate: schema up to date (${t}, fingerprint=${N(e.fingerprint)})`;
|
|
@@ -2394,7 +2403,7 @@ BEGIN
|
|
|
2394
2403
|
return n > 0 && s.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=${t.filter((e) => e.op.kind === "drop-table").map((e) => "table" in e.op ? e.op.table : "").filter(Boolean).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.", " → Add it to your declared set + chain `.dropped()` on it, OR", " write a file-based migration in `migrations/` that DROPs it, OR", " set `VOLTRO_DESTRUCTIVE_OK=1` for ONE `voltro dev` run (acknowledges data loss).", " 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`)."), r > 0 && s.push(`${r} 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."), i > 0 && s.push(`${i} column(s) are missing from your declared schema.`, " → If intentional: chain `.dropped()` on the column slot in the table declaration.", " → If a typo: restore the field. Data survives until you drop it."), a > 0 && s.push(`${a} 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."), s.push("", "See `voltro db plan` for the full machine-readable diff."), o + "\n" + s.join("\n");
|
|
2395
2404
|
}
|
|
2396
2405
|
}
|
|
2397
|
-
},
|
|
2406
|
+
}, cr = /* @__PURE__ */ new Set([
|
|
2398
2407
|
"node_modules",
|
|
2399
2408
|
"dist",
|
|
2400
2409
|
"build",
|
|
@@ -2403,24 +2412,24 @@ BEGIN
|
|
|
2403
2412
|
".next",
|
|
2404
2413
|
".git",
|
|
2405
2414
|
".framework"
|
|
2406
|
-
]),
|
|
2415
|
+
]), lr = async (e) => {
|
|
2407
2416
|
let t = [], n = async (e) => {
|
|
2408
2417
|
let r = await h.readdir(e, { withFileTypes: !0 }).catch(() => []);
|
|
2409
2418
|
for (let i of r) {
|
|
2410
|
-
if (
|
|
2419
|
+
if (cr.has(i.name)) continue;
|
|
2411
2420
|
let r = g(e, i.name);
|
|
2412
|
-
i.isDirectory() ? await n(r) : i.isFile() &&
|
|
2421
|
+
i.isDirectory() ? await n(r) : i.isFile() && c.test(i.name) && t.push(r);
|
|
2413
2422
|
}
|
|
2414
2423
|
};
|
|
2415
2424
|
return await n(e), t.sort((e, t) => {
|
|
2416
2425
|
let n = e.split("/").pop() ?? e, r = t.split("/").pop() ?? t;
|
|
2417
2426
|
return n.localeCompare(r);
|
|
2418
2427
|
});
|
|
2419
|
-
},
|
|
2428
|
+
}, ur = async (e) => {
|
|
2420
2429
|
let t = [];
|
|
2421
2430
|
for (let n of e) {
|
|
2422
2431
|
let e = (await import(ee(n).href)).default;
|
|
2423
|
-
if (!
|
|
2432
|
+
if (!a(e)) {
|
|
2424
2433
|
process.stderr.write(`[voltro:migrate] ${n} has no default-exported \`migration(...)\` — skipped\n`);
|
|
2425
2434
|
continue;
|
|
2426
2435
|
}
|
|
@@ -2430,11 +2439,11 @@ BEGIN
|
|
|
2430
2439
|
});
|
|
2431
2440
|
}
|
|
2432
2441
|
return t;
|
|
2433
|
-
},
|
|
2442
|
+
}, dr = (e) => e`
|
|
2434
2443
|
SELECT ${e("id")}
|
|
2435
2444
|
FROM ${e("_voltro_migration_plans")}
|
|
2436
2445
|
WHERE ${e("source")} = 'file'
|
|
2437
|
-
`.pipe(d.map((e) => e.map((e) => e.id)), d.catchAll(() => d.succeed([]))),
|
|
2446
|
+
`.pipe(d.map((e) => e.map((e) => e.id)), d.catchAll(() => d.succeed([]))), fr = (e, t, n, r) => d.gen(function* () {
|
|
2438
2447
|
let i = Date.now(), a = (/* @__PURE__ */ new Date()).toISOString(), o = {
|
|
2439
2448
|
sql: e,
|
|
2440
2449
|
log: {
|
|
@@ -2467,9 +2476,9 @@ BEGIN
|
|
|
2467
2476
|
${r}, ${n}, ${"file"},
|
|
2468
2477
|
${c}, ${a}, ${t.migration.description})
|
|
2469
2478
|
`, { durationMs: c };
|
|
2470
|
-
}),
|
|
2479
|
+
}), pr = (e, t) => d.gen(function* () {
|
|
2471
2480
|
let n = yield* d.tryPromise({
|
|
2472
|
-
try: () =>
|
|
2481
|
+
try: () => lr(g(t.projectRoot, "migrations")),
|
|
2473
2482
|
catch: (e) => e
|
|
2474
2483
|
});
|
|
2475
2484
|
if (n.length === 0) return {
|
|
@@ -2477,14 +2486,14 @@ BEGIN
|
|
|
2477
2486
|
skipped: []
|
|
2478
2487
|
};
|
|
2479
2488
|
let r = yield* d.tryPromise({
|
|
2480
|
-
try: () =>
|
|
2489
|
+
try: () => ur(n),
|
|
2481
2490
|
catch: (e) => e
|
|
2482
2491
|
});
|
|
2483
2492
|
if (r.length === 0) return {
|
|
2484
2493
|
applied: [],
|
|
2485
2494
|
skipped: []
|
|
2486
2495
|
};
|
|
2487
|
-
let i = new Set(yield*
|
|
2496
|
+
let i = new Set(yield* dr(e)), a = r.filter((e) => !i.has(e.migration.id)), o = r.filter((e) => i.has(e.migration.id)).map((e) => e.migration.id);
|
|
2488
2497
|
if (a.length === 0) return {
|
|
2489
2498
|
applied: [],
|
|
2490
2499
|
skipped: o
|
|
@@ -2498,7 +2507,7 @@ BEGIN
|
|
|
2498
2507
|
id: n.migration.id,
|
|
2499
2508
|
file: n.file
|
|
2500
2509
|
}));
|
|
2501
|
-
let { durationMs: r } = yield*
|
|
2510
|
+
let { durationMs: r } = yield* fr(e, n, t.env, t.appliedBy);
|
|
2502
2511
|
s.push({
|
|
2503
2512
|
id: n.migration.id,
|
|
2504
2513
|
durationMs: r
|
|
@@ -2515,18 +2524,18 @@ BEGIN
|
|
|
2515
2524
|
applied: s,
|
|
2516
2525
|
skipped: o
|
|
2517
2526
|
};
|
|
2518
|
-
}),
|
|
2527
|
+
}), mr = (e, t) => e`
|
|
2519
2528
|
SELECT ${e("id")}
|
|
2520
2529
|
FROM ${e("_voltro_migration_plans")}
|
|
2521
2530
|
WHERE ${e("id")} = ${t} AND ${e("source")} = 'file'
|
|
2522
2531
|
LIMIT 1
|
|
2523
|
-
`,
|
|
2524
|
-
if (!(yield*
|
|
2532
|
+
`, hr = (e, t) => d.gen(function* () {
|
|
2533
|
+
if (!(yield* mr(e, t.id))[0]) return yield* d.fail(/* @__PURE__ */ Error(`rollback: ${t.id} not found in _voltro_migration_plans (file source)`));
|
|
2525
2534
|
let n = yield* d.tryPromise({
|
|
2526
|
-
try: () =>
|
|
2535
|
+
try: () => lr(g(t.projectRoot, "migrations")),
|
|
2527
2536
|
catch: (e) => e
|
|
2528
2537
|
}), r = (yield* d.tryPromise({
|
|
2529
|
-
try: () =>
|
|
2538
|
+
try: () => ur(n),
|
|
2530
2539
|
catch: (e) => e
|
|
2531
2540
|
})).find((e) => e.migration.id === t.id);
|
|
2532
2541
|
if (!r) return yield* d.fail(/* @__PURE__ */ Error(`rollback: file for migration ${t.id} not found on disk under ${t.projectRoot}/migrations/`));
|
|
@@ -2559,7 +2568,7 @@ BEGIN
|
|
|
2559
2568
|
} finally {
|
|
2560
2569
|
yield* T(e).pipe(d.orDie);
|
|
2561
2570
|
}
|
|
2562
|
-
}),
|
|
2571
|
+
}), gr = (e) => d.gen(function* () {
|
|
2563
2572
|
let t = yield* p.SqlClient, n = yield* t`
|
|
2564
2573
|
SELECT id, fingerprint, appliedAt FROM _voltro_migration_plans
|
|
2565
2574
|
WHERE source = 'auto-diff'
|
|
@@ -2604,10 +2613,10 @@ BEGIN
|
|
|
2604
2613
|
snapshotFingerprint: e.fingerprint,
|
|
2605
2614
|
snapshotId: i
|
|
2606
2615
|
};
|
|
2607
|
-
}),
|
|
2616
|
+
}), _r = (e, ...t) => ({
|
|
2608
2617
|
_tag: "RawSqlFragment",
|
|
2609
2618
|
strings: [...e],
|
|
2610
2619
|
values: [...t]
|
|
2611
|
-
}),
|
|
2620
|
+
}), vr = (e) => typeof e == "object" && !!e && e._tag === "RawSqlFragment";
|
|
2612
2621
|
//#endregion
|
|
2613
|
-
export {
|
|
2622
|
+
export { c as FILE_MIGRATION_PATTERN, C as VOLTRO_MIGRATION_LOCK_KEY, ut as _internalCmp, w as acquireMigrationLock, Ke as applyNamespacedSchema, Yn as applyPlan, Be as applySchema, Lt as chunkTables, P as declaredSnapshot, ve as defaultClause, O as defaultJsonClause, sr as describeOutcome, Yt as emitDropColumnDdl, Xt as emitDropColumnDdlMysql, ze as emitFrameworkBootstrapSql, Le as emitNamespaceProvisionDdl, Re as emitNamespacedSchemaSql, j as emitSchemaSql, M as fingerprintSchema, rr as ignoreTablesFromEnv, qt as introspectSchema, a as isFileMigration, vr as isRawSqlFragment, Mt as mapPgType, o as migration, k as numericIdSql, jt as parseEnumCheck, At as planMigrations, qe as provisionTenantNamespace, T as releaseMigrationLock, sn as renderColumnMysql, rn as renderColumnPg, hr as rollbackFileBasedMigration, pr as runFileBasedMigrations, Je as runFrameworkBootstrap, Ge as runMigrate, or as runPlannedMigrations, N as shortFingerprint, _r as sql, D as sqlType, gr as squashMigrationPlans, ir as unblockLossy, ge as withMigrationLock };
|