@voltro/database 0.84.0 → 0.86.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 +1215 -0
- package/THIRD-PARTY-NOTICES.md +5 -89
- package/dist/actorSnapshot.js +24 -0
- package/dist/arrayCodec.js +32 -0
- package/dist/branchExecute.js +92 -0
- package/dist/branching.js +106 -0
- package/dist/bulkInsertLimits.js +25 -0
- package/dist/cdcOffsetsTables.js +19 -0
- package/dist/changeEvent.js +41 -0
- package/dist/changeIdentity.js +65 -0
- package/dist/changeListenerCeiling.js +6 -0
- package/dist/columnReference.js +17 -0
- package/dist/columns.js +326 -0
- package/dist/committedChanges.js +12 -0
- package/dist/connectionIdentity.js +36 -0
- package/dist/coreTables.js +16 -0
- package/dist/coreTablesRegistry.js +10 -0
- package/dist/dataTransfers.js +22 -0
- package/dist/dbCause.js +222 -0
- package/dist/dbMetrics.js +65 -0
- package/dist/decimalComparison.js +27 -0
- package/dist/derivedRowId.js +5 -0
- package/dist/dialect.js +13 -0
- package/dist/enumRenames.js +14 -0
- package/dist/expiresMixin.js +10 -0
- package/dist/fieldEncryption.js +98 -0
- package/dist/frameworkTables.js +130 -0
- package/dist/ginIndex.js +10 -0
- package/dist/hybridSearch.js +32 -0
- package/dist/idGenerator.js +57 -0
- package/dist/identifierLimits.js +32 -0
- package/dist/index.d.ts +122 -64
- package/dist/index.js +81 -2080
- package/dist/indexAudit.js +61 -0
- package/dist/indexStorage.js +58 -0
- package/dist/joinCompiler.js +160 -0
- package/dist/jsonEagerCompiler.js +436 -0
- package/dist/jsonPatch.js +78 -0
- package/dist/jsonPatchAssignments.js +13 -0
- package/dist/jsonPath.js +31 -0
- package/dist/localFirstMixin.js +12 -0
- package/dist/migrate.js +624 -0
- package/dist/migration.js +18 -0
- package/dist/migrations/appliedAtValue.js +8 -0
- package/dist/migrations/applier.js +1085 -0
- package/dist/migrations/boot.js +196 -0
- package/dist/migrations/branchRehearsal.js +122 -0
- package/dist/migrations/columnEquivalence.js +85 -0
- package/dist/migrations/ddlError.js +63 -0
- package/dist/migrations/declaredSchema.js +162 -0
- package/dist/migrations/deployLedger.js +19 -0
- package/dist/migrations/fileBased.js +57 -0
- package/dist/migrations/fileContext.js +482 -0
- package/dist/migrations/fileDiscovery.js +318 -0
- package/dist/migrations/fileSkipProbes.js +117 -0
- package/dist/migrations/fingerprint.js +46 -0
- package/dist/migrations/fkCycles.js +13 -0
- package/dist/migrations/frameworkLiveTables.js +25 -0
- package/dist/migrations/idempotentDdl.js +37 -0
- package/dist/migrations/indexPredicate.js +50 -0
- package/dist/migrations/introspect.js +799 -0
- package/dist/migrations/ledgerRows.js +4 -0
- package/dist/migrations/lock.js +91 -0
- package/dist/migrations/migrationTableExists.js +9 -0
- package/dist/migrations/mssqlColumnDependencies.js +76 -0
- package/dist/migrations/mysqlEngine.js +11 -0
- package/dist/migrations/namespaceCharset.js +51 -0
- package/dist/migrations/planner.js +775 -0
- package/dist/migrations/postgresEnumCheck.js +19 -0
- package/dist/migrations/reactiveTriggerDrift.js +58 -0
- package/dist/migrations/reactiveTriggerName.js +7 -0
- package/dist/migrations/renameMssqlCheck.js +21 -0
- package/dist/migrations/restoreSnapshots.js +206 -0
- package/dist/migrations/resumeLedger.js +86 -0
- package/dist/migrations/rollingDeploy.js +106 -0
- package/dist/migrations/schemaOwner.js +46 -0
- package/dist/migrations/schemaOwnerTable.js +4 -0
- package/dist/migrations/softDropSnapshot.js +20 -0
- package/dist/migrations/sqlLimit.js +8 -0
- package/dist/migrations/sqliteConstraintIdentity.js +97 -0
- package/dist/migrations/sqliteDecimalBoot.js +22 -0
- package/dist/migrations/sqliteDecimalPublication.js +257 -0
- package/dist/migrations/sqliteDecimalReview.js +30 -0
- package/dist/migrations/sqliteDecimalReviewStore.js +188 -0
- package/dist/migrations/sqliteDecimalReviewWitness.js +38 -0
- package/dist/migrations/sqliteDecimalSource.js +69 -0
- package/dist/migrations/sqliteDecimalStage.js +135 -0
- package/dist/migrations/sqliteForeignKeys.js +38 -0
- package/dist/migrations/sqliteMigrationBatch.js +37 -0
- package/dist/migrations/sqliteRetainedSource.js +106 -0
- package/dist/migrations/sqliteRowIdentity.js +63 -0
- package/dist/migrations/sqliteSchemaEpoch.js +12 -0
- package/dist/migrations/sqliteSnapshotGuard.js +10 -0
- package/dist/migrations/sqliteTableContents.js +34 -0
- package/dist/migrations/sqliteTableCopy.js +25 -0
- package/dist/migrations/sqliteTableSchema.js +19 -0
- package/dist/migrations/sqliteWitnessProtection.js +8 -0
- package/dist/migrations/squash.js +81 -0
- package/dist/migrations/storeCharset.js +66 -0
- package/dist/migrations/transactionCause.js +10 -0
- package/dist/migrations/undeclaredCharset.js +40 -0
- package/dist/migrations/uniqueActiveLowering.js +39 -0
- package/dist/mixins.js +22 -0
- package/dist/mssqlDecimalDistinct.js +51 -0
- package/dist/mssqlJsonValueCheck.js +42 -0
- package/dist/mssqlTextStorage.js +9 -0
- package/dist/namespace.js +21 -0
- package/dist/{wire-B28cPjEp.js → optimisticVersion.js} +2 -2
- package/dist/pendingAttribution.js +79 -0
- package/dist/pluginRef.js +17 -0
- package/dist/predicate.js +266 -0
- package/dist/predicateCoercion.js +13 -0
- package/dist/projectedColumn.js +37 -0
- package/dist/queryBuilder.js +382 -0
- package/dist/rawSql.js +8 -0
- package/dist/relations.js +69 -0
- package/dist/relationsRegistry.js +38 -0
- package/dist/replaceInProgress.js +53 -0
- package/dist/requiredColumns.js +26 -0
- package/dist/residency.js +52 -0
- package/dist/retention.js +62 -0
- package/dist/rowDecoder.js +123 -0
- package/dist/rowKey.js +8 -0
- package/dist/rowSchema.js +31 -0
- package/dist/scopeKey.js +8 -0
- package/dist/seed.js +33 -0
- package/dist/snowflake.js +26 -0
- package/dist/sql.d.ts +58 -29
- package/dist/sql.js +41 -6661
- package/dist/sqlCompiler.js +343 -0
- package/dist/sqlError.js +8 -0
- package/dist/sqlPoolCapacity.js +12 -0
- package/dist/sqliteDecimal.js +22 -0
- package/dist/sqliteDecimalAggregateSql.js +61 -0
- package/dist/sqliteDecimalColumnSql.js +108 -0
- package/dist/sqliteDecimalConversion.js +15 -0
- package/dist/sqliteDecimalCte.js +154 -0
- package/dist/sqliteDecimalEncodeSql.js +32 -0
- package/dist/sqliteDecimalOutputs.js +74 -0
- package/dist/sqliteDecimalPredicate.js +66 -0
- package/dist/sqliteDecimalQuery.js +183 -0
- package/dist/sqliteDecimalQueryPlan.js +37 -0
- package/dist/sqliteDecimalResultSql.js +44 -0
- package/dist/sqliteDecimalRowQuery.js +63 -0
- package/dist/sqliteDecimalSetQuery.js +50 -0
- package/dist/sqliteDecimalSource.js +63 -0
- package/dist/sqliteDecimalStorage.js +29 -0
- package/dist/sqliteDecimalSubquery.js +19 -0
- package/dist/sqliteDecimalWindowQuery.js +196 -0
- package/dist/sqliteValueCoercion.js +4 -0
- package/dist/stampId.js +21 -0
- package/dist/storeErrors.js +9 -0
- package/dist/storeRefusal.js +20 -0
- package/dist/stream.js +36 -0
- package/dist/subjectGraph.js +175 -0
- package/dist/table.js +394 -0
- package/dist/tableRegistry.js +16 -0
- package/dist/tenantSelection.js +27 -0
- package/dist/transactionOutcome.js +97 -0
- package/dist/transientContention.js +36 -0
- package/dist/typedStore.js +16 -0
- package/dist/upsertUpdate.js +20 -0
- package/dist/vectorEmbedding.js +31 -0
- package/dist/view.js +39 -0
- package/dist/wire.d.ts +9 -10
- package/dist/wire.js +6 -2
- package/dist/writeAttribution.js +16 -0
- package/dist/writeRecorder.js +37 -0
- package/package.json +4 -6
- package/dist/replaceInProgress-DUHl5JEz.js +0 -3557
|
@@ -0,0 +1,1085 @@
|
|
|
1
|
+
import { isMysqlFamily as e, isSqliteFamily as t, quoteIdent as n } from "../dialect.js";
|
|
2
|
+
import { sqliteDecimalStorageCheck as r, sqliteDecimalStorageShape as i } from "../sqliteDecimalStorage.js";
|
|
3
|
+
import { decodeRowsForTable as a } from "../rowDecoder.js";
|
|
4
|
+
import { fireSchemaChangeSeeds as o } from "../seed.js";
|
|
5
|
+
import { sqlError as s } from "../sqlError.js";
|
|
6
|
+
import { _voltroMigrationPlansTable as c, _voltroPluginMigrationsTable as l, _voltroSchemaOwnerTable as u } from "../frameworkTables.js";
|
|
7
|
+
import { REPLACE_IN_PROGRESS_TABLE as d, _voltroReplaceInProgressTable as f } from "../replaceInProgress.js";
|
|
8
|
+
import { pgIndexClauses as p } from "../indexStorage.js";
|
|
9
|
+
import { formatDdlError as m } from "./ddlError.js";
|
|
10
|
+
import { acquireMigrationLock as ee } from "./lock.js";
|
|
11
|
+
import { MIGRATION_OPS_TABLE as h, SHADOW_COLUMN_SUFFIX as te, SHADOW_OLD_SUFFIX as g, SQLITE_REBUILD_SUFFIX as ne, _voltroMigrationOpsTable as re, dialectOf as _, migrationAtomicityFor as ie, opStillOutstanding as v, opTargetLabel as ae, resumeIdempotencyFor as y } from "./resumeLedger.js";
|
|
12
|
+
import { mssqlJsonValueCheck as b } from "../mssqlJsonValueCheck.js";
|
|
13
|
+
import { defaultArrayClause as x, defaultClause as S, defaultJsonClause as C, enumRenameBlocks as oe, enumTypeBlocks as se, extensionBlocks as ce, numericIdSql as w, sqlType as T } from "../migrate.js";
|
|
14
|
+
import { runSqliteMigrationBatch as le } from "./sqliteMigrationBatch.js";
|
|
15
|
+
import { declaredSnapshot as ue, snapshotColumn as E } from "./declaredSchema.js";
|
|
16
|
+
import { DEPLOY_LEDGER_TABLE as de, _voltroDeploysTable as fe } from "./deployLedger.js";
|
|
17
|
+
import { parseEnumCheck as D } from "./introspect.js";
|
|
18
|
+
import { migrationTableExists as O } from "./migrationTableExists.js";
|
|
19
|
+
import { bindSoftDropSnapshot as pe, readSoftDropSnapshot as me } from "./softDropSnapshot.js";
|
|
20
|
+
import { limitClause as k } from "./sqlLimit.js";
|
|
21
|
+
import { resolveMysqlEngine as A } from "./mysqlEngine.js";
|
|
22
|
+
import { MYSQL_COLUMN_OR_INDEX_ABSENT as he, MYSQL_CONSTRAINT_ABSENT as ge, MYSQL_FOREIGN_KEY_EXISTS as _e, MYSQL_INDEX_EXISTS as ve, SQLITE_COLUMN_ABSENT as ye, isAlreadySatisfied as be } from "./idempotentDdl.js";
|
|
23
|
+
import { withMigrationTransaction as xe } from "./transactionCause.js";
|
|
24
|
+
import { renameMssqlCheck as Se } from "./renameMssqlCheck.js";
|
|
25
|
+
import { alterColumnWithDependenciesMssql as Ce } from "./mssqlColumnDependencies.js";
|
|
26
|
+
import { appliedAtValue as j } from "./appliedAtValue.js";
|
|
27
|
+
import { Effect as M } from "effect";
|
|
28
|
+
import { isDeepStrictEqual as we } from "node:util";
|
|
29
|
+
import { randomUUID as Te } from "node:crypto";
|
|
30
|
+
import { SqlClient as Ee } from "effect/sql";
|
|
31
|
+
//#region src/migrations/applier.ts
|
|
32
|
+
var De = (e, t) => `${t}(${e.precision ?? 38}, ${e.scale ?? 0})`, Oe = (e) => e.type === "text" && (e.hasDefault && e.defaultValue !== void 0 || e.oneOf !== void 0 && e.oneOf.length > 0), N = (e, t) => {
|
|
33
|
+
let n = e.type === "text" && e.maxLength !== void 0;
|
|
34
|
+
if (e.sqliteDecimalEncoding === void 0 && !n && !Oe(e) && !e.spatial && e.type !== "vector" && e.type !== "array" && e.type !== "enum") return;
|
|
35
|
+
let r = 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;
|
|
36
|
+
if (r !== void 0) throw Error(`migration applier: cannot render '${e.name}' (${e.type}) — the schema snapshot carries no ${r}. 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.`);
|
|
37
|
+
return T(e, t);
|
|
38
|
+
}, P = (e) => {
|
|
39
|
+
let t = N(e, "postgres");
|
|
40
|
+
return t === void 0 ? ke(e) : t;
|
|
41
|
+
}, ke = (e) => {
|
|
42
|
+
switch (e.type) {
|
|
43
|
+
case "id": return "text";
|
|
44
|
+
case "text": return "text";
|
|
45
|
+
case "integer": return "integer";
|
|
46
|
+
case "real": return "double precision";
|
|
47
|
+
case "decimal": return De(e, "NUMERIC");
|
|
48
|
+
case "bigint": return "bigint";
|
|
49
|
+
case "boolean": return "boolean";
|
|
50
|
+
case "timestamp": return "timestamptz";
|
|
51
|
+
case "date": return "date";
|
|
52
|
+
case "json": return "jsonb";
|
|
53
|
+
case "bytes": return "bytea";
|
|
54
|
+
case "reference": return e.referenceStorage === "numeric" ? "bigint" : "text";
|
|
55
|
+
case "vector":
|
|
56
|
+
case "enum":
|
|
57
|
+
case "array": throw Error(`snapshotPgType: '${e.type}' must go through parameterisedSqlType`);
|
|
58
|
+
case "interval": return "interval";
|
|
59
|
+
case "raw": return e.rawDdl ?? "text";
|
|
60
|
+
}
|
|
61
|
+
}, Ae = (e) => {
|
|
62
|
+
let t = N(e, "mariadb");
|
|
63
|
+
if (t !== void 0) return t;
|
|
64
|
+
switch (e.type) {
|
|
65
|
+
case "id": return "VARCHAR(64)";
|
|
66
|
+
case "text": return "LONGTEXT";
|
|
67
|
+
case "integer": return "INT";
|
|
68
|
+
case "real": return "DOUBLE";
|
|
69
|
+
case "decimal": return De(e, "DECIMAL");
|
|
70
|
+
case "bigint": return "BIGINT";
|
|
71
|
+
case "boolean": return "TINYINT(1)";
|
|
72
|
+
case "timestamp": return "DATETIME(6)";
|
|
73
|
+
case "date": return "DATE";
|
|
74
|
+
case "json": return "JSON";
|
|
75
|
+
case "bytes": return "LONGBLOB";
|
|
76
|
+
case "reference": return e.referenceStorage === "numeric" ? "BIGINT" : "VARCHAR(64)";
|
|
77
|
+
case "vector":
|
|
78
|
+
case "enum":
|
|
79
|
+
case "array": throw Error(`snapshotMysqlType: '${e.type}' must go through parameterisedSqlType`);
|
|
80
|
+
case "interval": return "BIGINT";
|
|
81
|
+
case "raw": return e.rawDdl ?? "LONGTEXT";
|
|
82
|
+
}
|
|
83
|
+
}, F = (e) => `"${e}"`, I = (e) => `\`${e.replace(/`/g, "``")}\``, je = (e) => e === "mariadb" ? "IF NOT EXISTS " : "", L = (e, t, n = !0) => `ALTER TABLE ${t(e.table)} DROP COLUMN ${n ? "IF EXISTS " : ""}${t(e.column)};`, Me = (e, t = "mariadb") => `ALTER TABLE ${I(e.table)} DROP COLUMN ${t === "mariadb" ? "IF EXISTS " : ""}${I(e.column)}, ALGORITHM=COPY;`, R = (e, t) => `ALTER TABLE ${t(e.table)} RENAME COLUMN ${t(e.from)} TO ${t(e.to)};`, Ne = (e, t, n) => `ALTER TABLE ${n(e.table)} RENAME COLUMN ${n(e.column)} TO ${n(t)};`, Pe = (e, t, n) => e.hasDefault && e.defaultValue !== void 0 ? `ALTER TABLE ${t(e.table)} ALTER COLUMN ${t(e.column)} SET ${Ie(e, n)};` : `ALTER TABLE ${t(e.table)} ALTER COLUMN ${t(e.column)} DROP DEFAULT;`, Fe = (e, t) => {
|
|
84
|
+
let { defaultFactory: n, ...r } = e;
|
|
85
|
+
return T(e, t) !== T({
|
|
86
|
+
...r,
|
|
87
|
+
hasDefault: !1
|
|
88
|
+
}, t);
|
|
89
|
+
}, Ie = (e, t) => {
|
|
90
|
+
let n = t(e.defaultValue);
|
|
91
|
+
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.`);
|
|
92
|
+
return n;
|
|
93
|
+
}, z = (e, t, n) => e.find((e) => e.tableName === t)?.fields[n], B = (e, t, n) => {
|
|
94
|
+
let r = z(e, t, n);
|
|
95
|
+
return r ? E(n, r) : void 0;
|
|
96
|
+
}, Le = (e, t, n, r) => {
|
|
97
|
+
let i = n ?? e.nullable, a = S(e, "mariadb");
|
|
98
|
+
return [
|
|
99
|
+
I(t),
|
|
100
|
+
T(e, "mariadb"),
|
|
101
|
+
i ? null : "NOT NULL",
|
|
102
|
+
a,
|
|
103
|
+
r === void 0 ? null : `CHECK (${r})`
|
|
104
|
+
].filter(Boolean).join(" ");
|
|
105
|
+
}, Re = /^json_valid\(`[^`]+`\)$/i, ze = (e, t) => A(e).pipe(M.flatMap((n) => n === "mariadb" ? e`
|
|
106
|
+
SELECT constraint_name AS n, check_clause AS c
|
|
107
|
+
FROM information_schema.check_constraints
|
|
108
|
+
WHERE constraint_schema = DATABASE() AND table_name = ${t} AND level = 'Column'`.pipe(M.map((e) => new Map(e.map((e) => [String(e.n ?? ""), String(e.c ?? "")]).filter(([e, t]) => e !== "" && t !== "" && !Re.test(t.trim()))))) : M.succeed(/* @__PURE__ */ new Map()))), V = (e, t, n, r, i) => M.map(ze(e, t), (e) => `ALTER TABLE ${I(t)} MODIFY COLUMN ${Le(n, r, i, e.get(r))};`), Be = (e, t) => e == null ? null : Array.isArray(e) ? Ve(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" ? C(e, "mariadb") : null, Ve = (e, t, n) => x(e, t ?? { type: "json" }, n), He = (e, t) => e == null ? null : Array.isArray(e) ? Ve(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" ? C(e, "postgres") : null, H = (e, t) => {
|
|
109
|
+
if (!e.generatedAs) return null;
|
|
110
|
+
let { expr: n, stored: r } = e.generatedAs;
|
|
111
|
+
return t === "postgres" ? `GENERATED ALWAYS AS (${n}) STORED` : t === "mssql" ? `AS (${n})${r ? " PERSISTED" : ""}` : `GENERATED ALWAYS AS (${n}) ${r ? "STORED" : "VIRTUAL"}`;
|
|
112
|
+
}, Ue = (e, t = !1) => {
|
|
113
|
+
if (e.type === "id" && e.idScheme?.kind === "numeric") return w(e.name, "postgres");
|
|
114
|
+
let n = H(e, "postgres");
|
|
115
|
+
if (n) return `${F(e.name)} ${P(e)} ${n}`;
|
|
116
|
+
let r = [F(e.name), P(e)];
|
|
117
|
+
if (e.type === "id" && !t && r.push("PRIMARY KEY"), e.nullable || r.push("NOT NULL"), e.unique && e.type !== "id" && r.push("UNIQUE"), e.hasDefault) {
|
|
118
|
+
let t = He(e.defaultValue, e);
|
|
119
|
+
t && r.push(t);
|
|
120
|
+
}
|
|
121
|
+
return e.references && (r.push(`REFERENCES ${F(e.references.table)} (${F(e.references.column)})`), e.references.onDelete && r.push(`ON DELETE ${{
|
|
122
|
+
cascade: "CASCADE",
|
|
123
|
+
restrict: "RESTRICT",
|
|
124
|
+
setNull: "SET NULL",
|
|
125
|
+
noAction: "NO ACTION"
|
|
126
|
+
}[e.references.onDelete]}`)), r.join(" ");
|
|
127
|
+
}, U = (e) => (e.primaryKey?.length ?? 0) > 0, W = (e, t) => e.primaryKey && e.primaryKey.length > 0 ? `,\n PRIMARY KEY (${e.primaryKey.map(t).join(", ")})` : "", We = (e) => {
|
|
128
|
+
let t = e.columns.map((t) => ` ${Ue(t, U(e))}`).join(",\n"), n = [`CREATE TABLE IF NOT EXISTS ${F(e.table)} (\n${t}${W(e, F)}\n)`];
|
|
129
|
+
for (let t of e.indexes) t.name !== `${e.table}_pkey` && n.push(gt({
|
|
130
|
+
kind: "add-index",
|
|
131
|
+
table: e.table,
|
|
132
|
+
index: t
|
|
133
|
+
}, !1));
|
|
134
|
+
return n;
|
|
135
|
+
}, Ge = 191, Ke = (e) => {
|
|
136
|
+
let t = Ae(e).toUpperCase();
|
|
137
|
+
return t.endsWith("TEXT") || t.endsWith("BLOB");
|
|
138
|
+
}, qe = (e) => e.charset === void 0 ? Ae(e) : `${Ae(e)} CHARACTER SET ${e.charset}`, Je = (e, t = !1) => {
|
|
139
|
+
if (e.type === "id" && e.idScheme?.kind === "numeric") return w(e.name, "mariadb");
|
|
140
|
+
let n = qe(e), r = H(e, "mariadb");
|
|
141
|
+
if (r) return `${I(e.name)} ${n} ${r}`;
|
|
142
|
+
let i = [I(e.name), n];
|
|
143
|
+
if (e.type === "id" && !t && i.push("PRIMARY KEY"), e.nullable || i.push("NOT NULL"), e.unique && e.type !== "id" && !Ke(e) && i.push("UNIQUE"), e.hasDefault) {
|
|
144
|
+
let t = Be(e.defaultValue, e);
|
|
145
|
+
t && i.push(t);
|
|
146
|
+
}
|
|
147
|
+
return i.join(" ");
|
|
148
|
+
}, Ye = (e) => e.columns.flatMap((t) => {
|
|
149
|
+
if (!t.references) return [];
|
|
150
|
+
let n = t.references.onDelete ? ` ON DELETE ${{
|
|
151
|
+
cascade: "CASCADE",
|
|
152
|
+
restrict: "RESTRICT",
|
|
153
|
+
setNull: "SET NULL",
|
|
154
|
+
noAction: "NO ACTION"
|
|
155
|
+
}[t.references.onDelete]}` : "";
|
|
156
|
+
return [` CONSTRAINT ${I(`${e.table}_${t.name}_fkey`)} FOREIGN KEY (${I(t.name)}) REFERENCES ${I(t.references.table)} (${I(t.references.column)})${n}`];
|
|
157
|
+
}), Xe = (e) => {
|
|
158
|
+
let t = e.find((e) => e.charset !== void 0)?.charset;
|
|
159
|
+
return t === void 0 ? "" : ` DEFAULT CHARACTER SET ${t}`;
|
|
160
|
+
}, Ze = (e, t, n = /* @__PURE__ */ new Map()) => {
|
|
161
|
+
let r = e.foreignKeys.map((e) => `ALTER TABLE ${I(e.table)} DROP FOREIGN KEY ${I(e.name)}`), i = e.columns.map((r) => {
|
|
162
|
+
let i = z(t, e.table, r), a = B(t, e.table, r);
|
|
163
|
+
if (i === void 0 || a === void 0) throw Error(`convert-charset: no declared column '${e.table}.${r}' to render MODIFY COLUMN from; it was not in ctx.declared.`);
|
|
164
|
+
let o = H(a, "mariadb"), s = `${I(r)} ${T(i, "mariadb")} CHARACTER SET ${e.charset}`, c = n.get(r), l = c === void 0 ? null : `CHECK (${c})`;
|
|
165
|
+
if (o) return `MODIFY COLUMN ${[
|
|
166
|
+
s,
|
|
167
|
+
o,
|
|
168
|
+
l
|
|
169
|
+
].filter(Boolean).join(" ")}`;
|
|
170
|
+
let u = S(i, "mariadb");
|
|
171
|
+
return `MODIFY COLUMN ${[
|
|
172
|
+
s,
|
|
173
|
+
i.nullable ? null : "NOT NULL",
|
|
174
|
+
u,
|
|
175
|
+
l
|
|
176
|
+
].filter(Boolean).join(" ")}`;
|
|
177
|
+
});
|
|
178
|
+
return {
|
|
179
|
+
drops: r,
|
|
180
|
+
rewrite: `ALTER TABLE ${I(e.table)} ${[...i, `DEFAULT CHARACTER SET ${e.charset}`].join(", ")}`,
|
|
181
|
+
restores: e.foreignKeys.filter((e) => e.restore === "here").map((e) => `ALTER TABLE ${I(e.table)} ADD CONSTRAINT ${I(e.name)} FOREIGN KEY (${I(e.column)}) REFERENCES ${I(e.targetTable)} (${I(e.targetColumn)})` + (e.onDelete ? ` ON DELETE ${$e[e.onDelete]}` : "") + (e.onUpdate && e.onUpdate !== "noAction" ? ` ON UPDATE ${$e[e.onUpdate]}` : ""))
|
|
182
|
+
};
|
|
183
|
+
}, Qe = (e, t = "mariadb") => {
|
|
184
|
+
for (let n of e.indexes) vt(n, t);
|
|
185
|
+
let n = new Map(e.columns.map((e) => [e.name, e])), r = (e) => {
|
|
186
|
+
let t = pt(n.get(e));
|
|
187
|
+
return t === void 0 ? I(e) : `${I(e)}(${t})`;
|
|
188
|
+
}, i = e.columns.map((t) => ` ${Je(t, U(e))}`).join(",\n"), a = Ye(e), o = a.length > 0 ? `${i},\n${a.join(",\n")}` : i, s = [`CREATE TABLE IF NOT EXISTS ${I(e.table)} (\n${o}${W(e, r)}\n)${Xe(e.columns)}`];
|
|
189
|
+
for (let n of e.columns) n.unique && n.type !== "id" && Ke(n) && s.push(`CREATE UNIQUE INDEX ${je(t)}${I(`${e.table}_${n.name}_key`)} ON ${I(e.table)} (${r(n.name)})`);
|
|
190
|
+
for (let n of e.indexes) n.name !== `${e.table}_pkey` && s.push(`CREATE ${n.unique ? "UNIQUE " : ""}INDEX ${je(t)}${I(n.name)} ON ${I(e.table)} (${n.columns.map(r).join(", ")})`);
|
|
191
|
+
return s;
|
|
192
|
+
}, $e = {
|
|
193
|
+
cascade: "CASCADE",
|
|
194
|
+
restrict: "RESTRICT",
|
|
195
|
+
setNull: "SET NULL",
|
|
196
|
+
noAction: "NO ACTION"
|
|
197
|
+
}, et = {
|
|
198
|
+
cascade: "CASCADE",
|
|
199
|
+
restrict: "NO ACTION",
|
|
200
|
+
setNull: "SET NULL",
|
|
201
|
+
noAction: "NO ACTION"
|
|
202
|
+
}, tt = (e) => {
|
|
203
|
+
let t = N(e, "sqlite");
|
|
204
|
+
if (t !== void 0) return t;
|
|
205
|
+
switch (e.type) {
|
|
206
|
+
case "integer":
|
|
207
|
+
case "boolean": return "INTEGER";
|
|
208
|
+
case "reference": return e.referenceStorage === "numeric" ? "INTEGER" : "TEXT";
|
|
209
|
+
case "bigint": return "INTEGER";
|
|
210
|
+
case "real": return "REAL";
|
|
211
|
+
case "decimal": return "NUMERIC";
|
|
212
|
+
case "bytes": return "BLOB";
|
|
213
|
+
default: return "TEXT";
|
|
214
|
+
}
|
|
215
|
+
}, G = (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" ? C(e, "sqlite") : null, nt = (e, t = !1) => {
|
|
216
|
+
if (e.type === "id" && e.idScheme?.kind === "numeric") return w(e.name, "sqlite");
|
|
217
|
+
let n = H(e, "sqlite"), a = i(e), o = a === void 0 ? void 0 : `CHECK (${r(F(e.name), a)})`;
|
|
218
|
+
if (n) return `${F(e.name)} ${tt(e)} ${n}${o ? ` ${o}` : ""}`;
|
|
219
|
+
let s = [F(e.name), tt(e)];
|
|
220
|
+
if (o && s.push(o), e.type === "id" && !t && s.push("PRIMARY KEY"), e.nullable || s.push("NOT NULL"), e.unique && e.type !== "id" && s.push("UNIQUE"), e.hasDefault) {
|
|
221
|
+
let t = G(e.defaultValue, "CURRENT_TIMESTAMP");
|
|
222
|
+
t && s.push(t);
|
|
223
|
+
}
|
|
224
|
+
if (e.references && (s.push(`REFERENCES ${F(e.references.table)} (${F(e.references.column)})`), e.references.onDelete && s.push(`ON DELETE ${$e[e.references.onDelete]}`)), e.oneOf && e.oneOf.length > 0) {
|
|
225
|
+
let t = e.oneOf.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ");
|
|
226
|
+
s.push(`CHECK (${F(e.name)} IN (${t}))`);
|
|
227
|
+
}
|
|
228
|
+
return s.join(" ");
|
|
229
|
+
}, rt = (e) => {
|
|
230
|
+
let t = e.columns.map((t) => ` ${nt(t, U(e))}`).join(",\n"), n = [`CREATE TABLE IF NOT EXISTS ${F(e.table)} (\n${t}${W(e, F)}\n)`];
|
|
231
|
+
for (let t of e.indexes) t.name !== `${e.table}_pkey` && n.push(`CREATE ${t.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${F(t.name)} ON ${F(e.table)} (${t.columns.map(F).join(", ")})${q(t)}`);
|
|
232
|
+
return n;
|
|
233
|
+
}, it = (e, t) => {
|
|
234
|
+
let n = t.find((t) => t.tableName === e);
|
|
235
|
+
return n ? at(ue([n], "sqlite").tables[0]) : [];
|
|
236
|
+
}, at = (e) => {
|
|
237
|
+
let t = e.name, n = `${t}__voltro_rebuild_${Te().replaceAll("-", "")}`, r = e.columns.map((e) => F(e.name)).join(", "), i = [
|
|
238
|
+
...rt({
|
|
239
|
+
kind: "create-table",
|
|
240
|
+
table: n,
|
|
241
|
+
columns: e.columns,
|
|
242
|
+
indexes: [],
|
|
243
|
+
...e.primaryKey ? { primaryKey: e.primaryKey } : {}
|
|
244
|
+
}).map((e) => e.replace(/^CREATE TABLE IF NOT EXISTS /, "CREATE TABLE ")),
|
|
245
|
+
`INSERT INTO ${F(n)} (${r}) SELECT ${r} FROM ${F(t)}`,
|
|
246
|
+
`DROP TABLE ${F(t)}`,
|
|
247
|
+
`ALTER TABLE ${F(n)} RENAME TO ${F(t)}`
|
|
248
|
+
];
|
|
249
|
+
for (let n of e.indexes) n.name !== `${t}_pkey` && i.push(`CREATE ${n.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${F(n.name)} ON ${F(t)} (${n.columns.map(F).join(", ")})${q(n)}`);
|
|
250
|
+
return i;
|
|
251
|
+
}, ot = (e, t, n, r) => {
|
|
252
|
+
let i = r ?? e.nullable;
|
|
253
|
+
return `ALTER TABLE ${K(n)} ALTER COLUMN ${K(t)} ${T(e, "mssql")} ${i ? "NULL" : "NOT NULL"};`;
|
|
254
|
+
}, st = (e, t) => {
|
|
255
|
+
let n = `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 ${K(e.table)} DROP CONSTRAINT [' + @dc + ']');` + (t ? ` ${t}` : "");
|
|
256
|
+
if (!e.hasDefault) return n;
|
|
257
|
+
let r = G(e.defaultValue, "SYSUTCDATETIME()");
|
|
258
|
+
if (!r) return n;
|
|
259
|
+
let i = r.replace(/^DEFAULT\s+/i, "");
|
|
260
|
+
return `${n} ALTER TABLE ${K(e.table)} ADD DEFAULT ${i} FOR ${K(e.column)};`;
|
|
261
|
+
}, K = (e) => `[${e.replace(/]/g, "]]")}]`, ct = (e) => {
|
|
262
|
+
let t = N(e, "mssql");
|
|
263
|
+
if (t !== void 0) return t;
|
|
264
|
+
switch (e.type) {
|
|
265
|
+
case "id": return "NVARCHAR(64)";
|
|
266
|
+
case "reference": return e.referenceStorage === "numeric" ? "BIGINT" : "NVARCHAR(64)";
|
|
267
|
+
case "integer": return "INT";
|
|
268
|
+
case "real": return "FLOAT";
|
|
269
|
+
case "decimal": return De(e, "DECIMAL");
|
|
270
|
+
case "bigint": return "BIGINT";
|
|
271
|
+
case "boolean": return "BIT";
|
|
272
|
+
case "timestamp": return "DATETIME2(6)";
|
|
273
|
+
case "date": return "DATE";
|
|
274
|
+
case "bytes": return "VARBINARY(MAX)";
|
|
275
|
+
default: return "NVARCHAR(MAX)";
|
|
276
|
+
}
|
|
277
|
+
}, lt = (e, t, n = !1) => {
|
|
278
|
+
if (e.type === "id" && e.idScheme?.kind === "numeric") return w(e.name, "mssql");
|
|
279
|
+
let r = e.type === "text" && t?.has(e.name) ? `NVARCHAR(${Math.min(e.maxLength ?? 450, 450)})` : ct(e), i = H(e, "mssql");
|
|
280
|
+
if (i) return `${K(e.name)} ${i}`;
|
|
281
|
+
let a = [K(e.name), r];
|
|
282
|
+
if (e.type === "json" && a.push(`CHECK ${b(K(e.name))}`), e.type === "array" && a.push(`CHECK (${K(e.name)} IS NULL OR ISJSON(${K(e.name)}) = 1)`), e.type === "id" && !n && a.push("PRIMARY KEY"), e.nullable || a.push("NOT NULL"), e.unique && e.type !== "id" && a.push("UNIQUE"), e.hasDefault) {
|
|
283
|
+
let t = G(e.defaultValue, "SYSUTCDATETIME()");
|
|
284
|
+
t && a.push(t);
|
|
285
|
+
}
|
|
286
|
+
return e.references && (a.push(`REFERENCES ${K(e.references.table)} (${K(e.references.column)})`), e.references.onDelete && a.push(`ON DELETE ${et[e.references.onDelete]}`)), a.join(" ");
|
|
287
|
+
}, ut = (e) => {
|
|
288
|
+
let t = (e) => e.replace(/'/g, "''"), n = /* @__PURE__ */ new Set([...e.indexes.flatMap((e) => e.columns), ...e.columns.filter((e) => e.unique).map((e) => e.name)]), r = e.columns.map((t) => ` ${lt(t, n, U(e))}`).join(",\n"), i = `CREATE TABLE ${K(e.table)} (\n${r}${W(e, K)}\n)`, a = [`IF OBJECT_ID(N'${t(e.table)}', N'U') IS NULL EXEC(N'${t(i)}')`];
|
|
289
|
+
for (let n of e.indexes) {
|
|
290
|
+
if (n.name === `${e.table}_pkey`) continue;
|
|
291
|
+
let r = `CREATE ${n.unique ? "UNIQUE " : ""}INDEX ${K(n.name)} ON ${K(e.table)} (${n.columns.map(K).join(", ")})${q(n)}`;
|
|
292
|
+
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)}')`);
|
|
293
|
+
}
|
|
294
|
+
return a;
|
|
295
|
+
}, dt = (e, t) => `DROP TABLE IF EXISTS ${t(e.table)};`, ft = (e, t, n) => `ALTER TABLE ${n(e.table)} RENAME TO ${n(t)};`, pt = (e) => {
|
|
296
|
+
if (e === void 0 || !Ke(e)) return;
|
|
297
|
+
let t = typeof e.maxLength == "number" && e.maxLength > 0 ? e.maxLength : void 0;
|
|
298
|
+
return t === void 0 ? Ge : Math.min(Ge, t);
|
|
299
|
+
}, mt = (e, t, n) => {
|
|
300
|
+
let r = pt(B(e, t, n));
|
|
301
|
+
return r === void 0 ? I(n) : `${I(n)}(${r})`;
|
|
302
|
+
}, ht = (e) => e.startsWith("(") ? e : F(e), q = (e) => e.where === void 0 ? "" : ` WHERE ${e.where}`, gt = (e, t) => {
|
|
303
|
+
let n = p(e.index.kind, e.index.kindOptions), r = e.index.columns.map(ht);
|
|
304
|
+
if (n.opclass !== void 0) {
|
|
305
|
+
if (r.length !== 1) throw Error("HNSW index requires exactly one column");
|
|
306
|
+
r[0] = `${r[0]} ${n.opclass}`;
|
|
307
|
+
}
|
|
308
|
+
return `CREATE ${e.index.unique ? "UNIQUE " : ""}INDEX ${t ? "CONCURRENTLY " : ""}IF NOT EXISTS ${F(e.index.name)} ON ${F(e.table)}${n.using} (${r.join(", ")})${n.withSuffix}${q(e.index)};`;
|
|
309
|
+
}, _t = (e, t, n = "mariadb") => (vt(e.index, n), `CREATE ${e.index.unique ? "UNIQUE " : ""}INDEX ${je(n)}${I(e.index.name)} ON ${I(e.table)} (${e.index.columns.map((n) => mt(t, e.table, n)).join(", ")});`), vt = (e, t) => {
|
|
310
|
+
if (e.where !== void 0) throw Error(`Partial index '${e.name}' is not supported by ${t}; uniqueActive must be lowered before applying the plan.`);
|
|
311
|
+
}, yt = (e) => `DROP INDEX IF EXISTS ${F(e.index)};`, bt = (e) => `DROP INDEX ${I(e.index)} ON ${I(e.table)};`, xt = (e) => `DROP INDEX IF EXISTS ${K(e.index)} ON ${K(e.table)};`, St = (e, t) => `ALTER TABLE ${t(e.from)} RENAME TO ${t(e.to)};`, Ct = (e) => {
|
|
312
|
+
let t = (e) => e.replace(/'/g, "''");
|
|
313
|
+
return `EXEC sp_rename N'${t(e.from)}', N'${t(e.to)}';`;
|
|
314
|
+
}, wt = (e) => `ALTER INDEX ${F(e.from)} RENAME TO ${F(e.to)};`, Tt = (e) => `ALTER TABLE ${I(e.table)} RENAME INDEX ${I(e.from)} TO ${I(e.to)};`, Et = (e) => {
|
|
315
|
+
let t = (e) => e.replace(/'/g, "''");
|
|
316
|
+
return `EXEC sp_rename N'${t(e.table)}.${t(e.from)}', N'${t(e.to)}', N'INDEX';`;
|
|
317
|
+
}, Dt = (e) => {
|
|
318
|
+
let t = (e) => e.replace(/'/g, "''"), n = `CREATE ${e.index.unique ? "UNIQUE " : ""}INDEX ${K(e.index.name)} ON ${K(e.table)} (${e.index.columns.map(K).join(", ")})${q(e.index)}`;
|
|
319
|
+
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)}');`;
|
|
320
|
+
}, Ot = (e, t, n) => {
|
|
321
|
+
let r = n.find((t) => t.tableName === e);
|
|
322
|
+
if (!r) return [];
|
|
323
|
+
let i = [];
|
|
324
|
+
for (let n of t) {
|
|
325
|
+
let t = r.fields[n];
|
|
326
|
+
if (t && t.type === "text" && !t.generatedAs) {
|
|
327
|
+
let r = Math.min(t.maxLength ?? 450, 450);
|
|
328
|
+
i.push(`ALTER TABLE ${K(e)} ALTER COLUMN ${K(n)} NVARCHAR(${r}) ${t.nullable ? "NULL" : "NOT NULL"};`);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return i;
|
|
332
|
+
}, kt = (e) => `ALTER TABLE ${F(e.table)} ADD CONSTRAINT ${F(`${e.table}_${e.column}_key`)} UNIQUE (${F(e.column)});`, At = (e, t) => `ALTER TABLE ${I(e.table)} ADD CONSTRAINT ${I(`${e.table}_${e.column}_key`)} UNIQUE (${mt(t, e.table, e.column)});`, jt = (e) => {
|
|
333
|
+
let t = e.fields.map((e) => F(e)).join(", ");
|
|
334
|
+
return `ALTER TABLE ${F(e.table)} ADD CONSTRAINT ${F(e.name)} UNIQUE (${t});`;
|
|
335
|
+
}, Mt = (e, t) => {
|
|
336
|
+
let n = e.fields.map((n) => mt(t, e.table, n)).join(", ");
|
|
337
|
+
return `ALTER TABLE ${I(e.table)} ADD CONSTRAINT ${I(e.name)} UNIQUE (${n});`;
|
|
338
|
+
}, Nt = (e) => `ALTER TABLE ${F(e.table)} DROP CONSTRAINT IF EXISTS ${F(e.name)};`, Pt = (e) => [Nt(e), `DROP INDEX IF EXISTS ${F(e.name)};`], Ft = (e) => e.name === `${e.table}_pkey` ? `ALTER TABLE ${I(e.table)} DROP PRIMARY KEY;` : `ALTER TABLE ${I(e.table)} DROP INDEX ${I(e.name)};`, It = (e) => `ALTER TABLE ${F(e.table)} DROP CONSTRAINT IF EXISTS ${F(`${e.table}_${e.column}_key`)};`, Lt = (e) => `ALTER TABLE ${I(e.table)} DROP INDEX ${I(`${e.table}_${e.column}_key`)};`, Rt = (e, t, n = $e) => {
|
|
339
|
+
let r = [], i = t(e.table), a = t(e.column), o = t(e.targetTable), s = t(e.targetColumn);
|
|
340
|
+
if (e.orphanPolicy) {
|
|
341
|
+
let t = `${a} IS NOT NULL AND ${a} NOT IN (SELECT ${s} FROM ${o})`;
|
|
342
|
+
r.push(e.orphanPolicy === "delete" ? `DELETE FROM ${i} WHERE ${t};` : `UPDATE ${i} SET ${a} = NULL WHERE ${t};`);
|
|
343
|
+
}
|
|
344
|
+
let c = [
|
|
345
|
+
`ALTER TABLE ${i}`,
|
|
346
|
+
`ADD CONSTRAINT ${t(`${e.table}_${e.column}_fkey`)}`,
|
|
347
|
+
`FOREIGN KEY (${a})`,
|
|
348
|
+
`REFERENCES ${o} (${s})`
|
|
349
|
+
];
|
|
350
|
+
return e.onDelete && c.push(`ON DELETE ${n[e.onDelete]}`), r.push(c.join(" ") + ";"), r;
|
|
351
|
+
}, zt = (e) => e.name ?? `${e.table}_${e.column}_fkey`, Bt = (e) => `ALTER TABLE ${F(e.table)} DROP CONSTRAINT IF EXISTS ${F(zt(e))};`, Vt = (e) => `ALTER TABLE ${I(e.table)} DROP FOREIGN KEY ${I(zt(e))};`, Ht = (e, t) => {
|
|
352
|
+
let n = e.values.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ");
|
|
353
|
+
return `ALTER TABLE ${t(e.table)} ADD CONSTRAINT ${t(`${e.table}_${e.column}_check`)} CHECK (${t(e.column)} IN (${n}));`;
|
|
354
|
+
}, Ut = (e) => `ALTER TABLE ${F(e.table)} DROP CONSTRAINT IF EXISTS ${F(`${e.table}_${e.column}_check`)};`, Wt = (e, t) => M.gen(function* () {
|
|
355
|
+
let n = yield* e`
|
|
356
|
+
SELECT cc.constraint_name AS n, cc.check_clause AS c
|
|
357
|
+
FROM information_schema.check_constraints cc
|
|
358
|
+
WHERE cc.constraint_schema = DATABASE() AND cc.table_name = ${t.table}`.pipe(M.catch(() => e`
|
|
359
|
+
SELECT cc.constraint_name AS n, cc.check_clause AS c
|
|
360
|
+
FROM information_schema.check_constraints cc
|
|
361
|
+
JOIN information_schema.table_constraints tc
|
|
362
|
+
ON tc.constraint_schema = cc.constraint_schema
|
|
363
|
+
AND tc.constraint_name = cc.constraint_name
|
|
364
|
+
WHERE tc.table_schema = DATABASE() AND tc.table_name = ${t.table}`), M.orElseSucceed(() => []));
|
|
365
|
+
for (let e of n) {
|
|
366
|
+
let n = String(e.n ?? "");
|
|
367
|
+
if (n !== "" && D(String(e.c ?? ""))?.column === t.column) return n;
|
|
368
|
+
}
|
|
369
|
+
}), Gt = (e, t, n) => M.gen(function* () {
|
|
370
|
+
let r = yield* Wt(e, t);
|
|
371
|
+
r !== void 0 && (yield* J(e, `ALTER TABLE ${I(t.table)} DROP CONSTRAINT ${I(r)};`, t.kind, [ge]));
|
|
372
|
+
let i = yield* Wt(e, t);
|
|
373
|
+
if (i === void 0) return;
|
|
374
|
+
let a = z(n, t.table, t.column);
|
|
375
|
+
if (!a) return yield* M.die(/* @__PURE__ */ Error(`applier: cannot drop the CHECK on '${t.table}.${t.column}' — it is column-level (catalog name '${i}'), which this family removes only by redefining the column, and the declared column is not available to redefine it from.`));
|
|
376
|
+
yield* J(e, `ALTER TABLE ${I(t.table)} MODIFY COLUMN ${Le(a, t.column)};`, t.kind);
|
|
377
|
+
}), Kt = (e, t, n, r) => {
|
|
378
|
+
let i = e.column, a = r === "mariadb", o = a ? I : r === "mssql" ? K : F, s = a ? qe(i) : r === "mssql" ? ct(i) : r === "sqlite" ? tt(i) : P(i), c = r === "mssql" ? "ADD" : "ADD COLUMN", l = r === "mssql" ? i.type === "json" ? ` CHECK ${b(o(i.name))}` : i.type === "array" ? ` CHECK (${o(i.name)} IS NULL OR ISJSON(${o(i.name)}) = 1)` : "" : "", u = (e) => a ? Be(e, i) : r === "mssql" ? G(e, "SYSUTCDATETIME()") : r === "sqlite" ? G(e, "CURRENT_TIMESTAMP") : He(e, i), d = H(i, r);
|
|
379
|
+
if (d) {
|
|
380
|
+
let t = r === "mssql" ? `${o(i.name)} ${d}` : `${o(i.name)} ${s} ${d}`;
|
|
381
|
+
return {
|
|
382
|
+
pre: [`ALTER TABLE ${o(e.table)} ${c} ${t};`],
|
|
383
|
+
post: []
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
if (i.type === "id" && i.idScheme?.kind === "numeric" && (r === "postgres" || a)) return {
|
|
387
|
+
pre: [`ALTER TABLE ${o(e.table)} ${c} ${w(i.name, r)};`],
|
|
388
|
+
post: []
|
|
389
|
+
};
|
|
390
|
+
if (t.classification === "safe") {
|
|
391
|
+
let t = [`ALTER TABLE ${o(e.table)} ${c} ${o(i.name)} ${s}`];
|
|
392
|
+
return i.unique && t.push("UNIQUE"), {
|
|
393
|
+
pre: [`${t.join(" ")}${l};`],
|
|
394
|
+
post: []
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
if (t.classification === "needs-default") {
|
|
398
|
+
let t = u(i.defaultValue);
|
|
399
|
+
if (t === null) throw Error(`migration applier: cannot render the declared default for '${e.table}.${i.name}' (${JSON.stringify(i.defaultValue)}) while adding it NOT NULL to a populated table. This is a framework bug — report the column type + default.`);
|
|
400
|
+
return {
|
|
401
|
+
pre: [`ALTER TABLE ${o(e.table)} ${c} ${o(i.name)} ${s} NOT NULL ${t}${l};`],
|
|
402
|
+
post: [],
|
|
403
|
+
...i.unique && i.type !== "id" ? { uniquePending: !0 } : {}
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
let f = n(e.table, i.name), p = a ? `ALTER TABLE ${o(e.table)} MODIFY COLUMN ${o(i.name)} ${s} NOT NULL;` : r === "mssql" ? `ALTER TABLE ${o(e.table)} ALTER COLUMN ${o(i.name)} ${s} NOT NULL;` : r === "sqlite" ? null : `ALTER TABLE ${o(e.table)} ALTER COLUMN ${o(i.name)} SET NOT NULL;`, m = {
|
|
407
|
+
pre: [`ALTER TABLE ${o(e.table)} ${c} ${o(i.name)} ${s}${l};`],
|
|
408
|
+
post: p ? [p] : []
|
|
409
|
+
};
|
|
410
|
+
return i.unique && i.type !== "id" && (m.uniquePending = !0), f?.sql && (m.backfillKind = "sql", m.backfillSql = f.sql), f?.js && (m.backfillKind = "js", m.backfillJs = f.js), f?.batchSize !== void 0 && (m.batchSize = f.batchSize), f?.sleepMs !== void 0 && (m.sleepMs = f.sleepMs), m;
|
|
411
|
+
}, qt = /* @__PURE__ */ new Set([
|
|
412
|
+
"drop-index",
|
|
413
|
+
"drop-check",
|
|
414
|
+
"drop-unique",
|
|
415
|
+
"drop-unique-composite",
|
|
416
|
+
"drop-foreign-key",
|
|
417
|
+
"drop-column",
|
|
418
|
+
"drop-table"
|
|
419
|
+
]), Jt = (e) => {
|
|
420
|
+
let t = new Map(e.map((e) => [e.tableName, e]));
|
|
421
|
+
return (e, n) => {
|
|
422
|
+
let r = t.get(e);
|
|
423
|
+
if (!r) return;
|
|
424
|
+
let i = r.fields[n];
|
|
425
|
+
if (!i || !i.backfill) return;
|
|
426
|
+
let a = i.backfill, o = {
|
|
427
|
+
...a.batchSize === void 0 ? {} : { batchSize: a.batchSize },
|
|
428
|
+
...a.sleepMs === void 0 ? {} : { sleepMs: a.sleepMs }
|
|
429
|
+
};
|
|
430
|
+
if (a.kind === "sql" && a.sql) return {
|
|
431
|
+
sql: a.sql,
|
|
432
|
+
...o
|
|
433
|
+
};
|
|
434
|
+
if (a.kind === "js" && a.fn) return {
|
|
435
|
+
js: a.fn,
|
|
436
|
+
...o
|
|
437
|
+
};
|
|
438
|
+
};
|
|
439
|
+
}, J = (e, t, n, r = []) => e.unsafe(t).pipe(M.catchIf((e) => be(e, r), () => M.void), M.tapError((e) => M.sync(() => {
|
|
440
|
+
process.stderr.write(m(`applier: statement failed (op=${n})`, t, e, 400));
|
|
441
|
+
})), M.mapError((e) => {
|
|
442
|
+
let r = m(`applier: statement failed (op=${n})`, t, e, 400).trim();
|
|
443
|
+
return s(r, e);
|
|
444
|
+
}), M.map(() => void 0)), Y = (e, t, n, r) => Yt(e, t, it(t, n), r), Yt = (e, t, n, r) => le(e, n).pipe(M.mapError((e) => s(`applier: atomic SQLite rebuild failed (op=${r}, table=${t}): ${e.message}`, e))), Xt = (e, t) => M.gen(function* () {
|
|
445
|
+
let r = (yield* e`
|
|
446
|
+
SELECT ck.name, ck.definition, ck.is_disabled AS disabled,
|
|
447
|
+
ck.is_not_trusted AS untrusted, ck.is_not_for_replication AS notForReplication
|
|
448
|
+
FROM sys.check_constraints ck
|
|
449
|
+
WHERE ck.parent_object_id = OBJECT_ID(${t.table}) AND EXISTS (
|
|
450
|
+
SELECT 1 FROM sys.sql_expression_dependencies dep
|
|
451
|
+
JOIN sys.columns col ON col.object_id = dep.referenced_id AND col.column_id = dep.referenced_minor_id
|
|
452
|
+
WHERE dep.referencing_id = ck.object_id AND col.object_id = ck.parent_object_id AND col.name = ${t.from}
|
|
453
|
+
) ORDER BY ck.name
|
|
454
|
+
`).map((e) => ({
|
|
455
|
+
...e,
|
|
456
|
+
definition: Se(e.definition, t.from, t.to)
|
|
457
|
+
})), i = (e) => n(e, "mssql"), a = (e) => e.replace(/'/g, "''");
|
|
458
|
+
return yield* X(e, [
|
|
459
|
+
...r.map((e) => `ALTER TABLE ${i(t.table)} DROP CONSTRAINT ${i(e.name)}`),
|
|
460
|
+
`EXEC sp_rename N'${a(`${i(t.table)}.${i(t.from)}`)}', N'${a(t.to)}', 'COLUMN';`,
|
|
461
|
+
...r.flatMap((e) => [`ALTER TABLE ${i(t.table)} WITH ${e.untrusted ? "NOCHECK" : "CHECK"} ADD CONSTRAINT ${i(e.name)} CHECK ${e.notForReplication ? "NOT FOR REPLICATION " : ""}${e.definition}`, ...e.disabled ? [`ALTER TABLE ${i(t.table)} NOCHECK CONSTRAINT ${i(e.name)}`] : []])
|
|
462
|
+
], t.kind);
|
|
463
|
+
}), Zt = (e, t, n) => e == null ? e : t.type === "json" ? JSON.stringify(e) : (t.type === "array" || t.type === "vector") && n !== "postgres" ? typeof e == "string" ? e : JSON.stringify(e) : e, X = (e, t, n) => M.forEach(t, (t) => J(e, t, n), { discard: !0 }), Z = (e, t, r, i) => e.unsafe(`SELECT ${i === void 0 ? "*" : n(i, t)} FROM ${n(r, t)} WHERE 1=0`).pipe(M.as(!0), M.catch(() => M.succeed(!1))), Qt = (e, t, n) => Q(e, t, re, h, n), $t = (e, t, n) => Q(e, t, c, "_voltro_migration_plans", n), en = (e, t, n) => Q(e, t, u, "_voltro_schema_owner", n), tn = (e, t, n) => Q(e, t, l, "_voltro_plugin_migrations", n), nn = (e, t, n) => Q(e, t, fe, de, n), rn = (e, t, n) => Q(e, t, f, d, n), Q = (t, n, r, i, a) => M.gen(function* () {
|
|
464
|
+
let o = ue([r], n, a !== void 0 && e(n) ? { charset: a } : {}).tables[0];
|
|
465
|
+
if (!o) return yield* M.die(/* @__PURE__ */ Error(`migration applier: the ${i} table descriptor produced no snapshot. This is a framework bug.`));
|
|
466
|
+
let s = {
|
|
467
|
+
kind: "create-table",
|
|
468
|
+
table: i,
|
|
469
|
+
columns: o.columns,
|
|
470
|
+
indexes: []
|
|
471
|
+
}, c = yield* t.onDialectOrElse({
|
|
472
|
+
mysql: () => A(t).pipe(M.map((e) => Qe(s, e))),
|
|
473
|
+
mssql: () => M.succeed(ut(s)),
|
|
474
|
+
sqlite: () => M.succeed(rt(s)),
|
|
475
|
+
orElse: () => M.succeed(We(s))
|
|
476
|
+
});
|
|
477
|
+
yield* M.forEach(c, (e) => J(t, e, "resume-ledger-bootstrap"), { discard: !0 });
|
|
478
|
+
}), an = (e) => e`
|
|
479
|
+
SELECT ${e("runId")}, ${e("planFingerprint")}, ${e("opIndex")},
|
|
480
|
+
${e("status")}, ${e("payload")}
|
|
481
|
+
FROM ${e(h)}
|
|
482
|
+
`, on = (e) => O(e, h).pipe(M.flatMap((t) => t ? an(e).pipe(M.map((e) => e.length > 0)) : M.succeed(!1))), sn = (e, t, n, r) => M.gen(function* () {
|
|
483
|
+
let i = yield* j(e, (/* @__PURE__ */ new Date()).toISOString());
|
|
484
|
+
yield* M.forEach(r, ({ index: r, planned: a }) => e`
|
|
485
|
+
INSERT INTO ${e(h)}
|
|
486
|
+
(${e("id")}, ${e("runId")}, ${e("planFingerprint")}, ${e("opIndex")},
|
|
487
|
+
${e("opKind")}, ${e("opTarget")}, ${e("status")}, ${e("payload")},
|
|
488
|
+
${e("createdAt")})
|
|
489
|
+
VALUES
|
|
490
|
+
(${`mop_${t}_${r}`}, ${t}, ${n}, ${r},
|
|
491
|
+
${a.op.kind}, ${ae(a.op)}, ${"pending"},
|
|
492
|
+
${JSON.stringify(a)}, ${i})
|
|
493
|
+
`, { discard: !0 });
|
|
494
|
+
}), $ = (e, t, n, r, i, a) => M.gen(function* () {
|
|
495
|
+
let o = yield* j(e, (/* @__PURE__ */ new Date()).toISOString());
|
|
496
|
+
r === "applied" ? yield* e`
|
|
497
|
+
UPDATE ${e(h)}
|
|
498
|
+
SET ${e("status")} = ${r}, ${e("appliedAt")} = ${o},
|
|
499
|
+
${e("durationMs")} = ${i ?? null}
|
|
500
|
+
WHERE ${e("runId")} = ${t} AND ${e("opIndex")} = ${n}
|
|
501
|
+
` : r === "started" && a !== void 0 && ["sqlite", "turso"].includes(_(e)) ? yield* e`
|
|
502
|
+
UPDATE ${e(h)}
|
|
503
|
+
SET ${e("status")} = ${r}, ${e("startedAt")} = ${o},
|
|
504
|
+
${e("payload")} = ${JSON.stringify({
|
|
505
|
+
...a,
|
|
506
|
+
sqliteAtomicRebuild: !0
|
|
507
|
+
})}
|
|
508
|
+
WHERE ${e("runId")} = ${t} AND ${e("opIndex")} = ${n}
|
|
509
|
+
` : yield* e`
|
|
510
|
+
UPDATE ${e(h)}
|
|
511
|
+
SET ${e("status")} = ${r}, ${e("startedAt")} = ${o}
|
|
512
|
+
WHERE ${e("runId")} = ${t} AND ${e("opIndex")} = ${n}
|
|
513
|
+
`;
|
|
514
|
+
}), cn = (e, t, n, r) => M.gen(function* () {
|
|
515
|
+
let i = yield* j(e, (/* @__PURE__ */ new Date()).toISOString());
|
|
516
|
+
r.length > 0 && (yield* e`
|
|
517
|
+
UPDATE ${e(h)}
|
|
518
|
+
SET ${e("status")} = ${"applied"}, ${e("appliedAt")} = ${i}
|
|
519
|
+
WHERE ${e("runId")} = ${t} AND ${e("opIndex")} IN ${e.in(r)}
|
|
520
|
+
`), n.length > 0 && (yield* e`
|
|
521
|
+
UPDATE ${e(h)}
|
|
522
|
+
SET ${e("status")} = ${"pending"}
|
|
523
|
+
WHERE ${e("runId")} = ${t} AND ${e("opIndex")} IN ${e.in(n)}
|
|
524
|
+
`);
|
|
525
|
+
}), ln = (e, t) => t === void 0 ? e`DELETE FROM ${e(h)}`.pipe(M.asVoid) : e`DELETE FROM ${e(h)} WHERE ${e("runId")} = ${t}`.pipe(M.asVoid), un = (e) => an(e).pipe(M.map((e) => e.filter((e) => e.status === "started" || e.status === "added" || e.status === "applied").map((e) => JSON.parse(e.payload))), M.catch(() => M.succeed([]))), dn = (e) => ln(e).pipe(M.catch(() => M.void)), fn = (e, t, r) => M.gen(function* () {
|
|
526
|
+
if (y(r.op, r.classification, t) !== "artefact-repair") return "clean";
|
|
527
|
+
let i = r.op, a = (e) => n(e, t);
|
|
528
|
+
if (t === "postgres" && i.kind === "add-index") {
|
|
529
|
+
let t = yield* e`
|
|
530
|
+
SELECT ns.nspname AS schema_name
|
|
531
|
+
FROM pg_index ix
|
|
532
|
+
JOIN pg_class idx ON idx.oid = ix.indexrelid
|
|
533
|
+
JOIN pg_namespace ns ON ns.oid = idx.relnamespace
|
|
534
|
+
WHERE ix.indrelid = to_regclass(${a(i.table)})
|
|
535
|
+
AND idx.relname = ${i.index.name}
|
|
536
|
+
AND NOT ix.indisvalid
|
|
537
|
+
`;
|
|
538
|
+
return t.length === 0 ? "clean" : (yield* J(e, `DROP INDEX CONCURRENTLY ${a(t[0].schema_name)}.${a(i.index.name)}`, "resume-repair"), "outstanding");
|
|
539
|
+
}
|
|
540
|
+
if (t === "sqlite" || t === "turso") {
|
|
541
|
+
if (!("table" in i)) return "clean";
|
|
542
|
+
let n = `${i.table}${ne}`;
|
|
543
|
+
return (yield* Z(e, t, n)) ? ((yield* Z(e, t, i.table)) ? yield* J(e, `DROP TABLE ${a(n)}`, "resume-repair") : yield* J(e, `ALTER TABLE ${a(n)} RENAME TO ${a(i.table)}`, "resume-repair"), yield* M.logWarning(`migration resume: reconciled an interrupted sqlite table rebuild on '${i.table}' — replaying the operation`).pipe(M.annotateLogs({ scope: "voltro:migrate" })), "outstanding") : "clean";
|
|
544
|
+
}
|
|
545
|
+
if (i.kind !== "alter-column-type") return "clean";
|
|
546
|
+
let o = `${i.column}${te}`, s = `${i.column}${g}`, c = yield* Z(e, t, i.table, i.column), l = yield* Z(e, t, i.table, o), u = yield* Z(e, t, i.table, s), d = (e) => M.logWarning(`migration resume: ${e}`).pipe(M.annotateLogs({ scope: "voltro:migrate" }));
|
|
547
|
+
return !l && !u ? "clean" : c && l && !u ? (yield* J(e, `ALTER TABLE ${a(i.table)} DROP COLUMN ${a(o)}`, "resume-repair"), yield* d(`dropped a half-populated shadow column '${i.table}.${o}' — replaying the type change`), "outstanding") : !c && l && u ? (yield* J(e, `ALTER TABLE ${a(i.table)} RENAME COLUMN ${a(o)} TO ${a(i.column)}`, "resume-repair"), yield* J(e, `ALTER TABLE ${a(i.table)} DROP COLUMN ${a(s)}`, "resume-repair"), yield* d(`completed an interrupted shadow swap on '${i.table}.${i.column}'`), "applied") : c && !l && u ? (yield* J(e, `ALTER TABLE ${a(i.table)} DROP COLUMN ${a(s)}`, "resume-repair"), yield* d(`dropped the leftover pre-swap column '${i.table}.${s}'`), "applied") : yield* M.die(/* @__PURE__ */ Error(`applyPlan: an interrupted online type change on '${i.table}.${i.column}' left a shadow-swap state this resume cannot classify (column=${c}, ${o}=${l}, ${s}=${u}).\nNothing was changed. Inspect those three columns and reconcile them by hand — the data is in whichever of them is populated — then re-run the migration.`));
|
|
548
|
+
}), pn = (e, r, i, o = "fresh", c) => M.gen(function* () {
|
|
549
|
+
let l = process.hrtime.bigint(), u = Jt(i.declared), d = r.op, f = _(e);
|
|
550
|
+
switch (d.kind) {
|
|
551
|
+
case "create-table": {
|
|
552
|
+
let t = yield* e.onDialectOrElse({
|
|
553
|
+
mysql: () => A(e).pipe(M.map((e) => Qe(d, e))),
|
|
554
|
+
mssql: () => M.succeed(ut(d)),
|
|
555
|
+
sqlite: () => M.succeed(rt(d)),
|
|
556
|
+
orElse: () => M.succeed(We(d))
|
|
557
|
+
}), n = yield* e.onDialectOrElse({
|
|
558
|
+
mysql: () => M.succeed([ve]),
|
|
559
|
+
orElse: () => M.succeed([])
|
|
560
|
+
});
|
|
561
|
+
for (let r of t) yield* J(e, r, d.kind, n);
|
|
562
|
+
break;
|
|
563
|
+
}
|
|
564
|
+
case "drop-table": {
|
|
565
|
+
let t = me(r), n = t !== void 0 || process.env.VOLTRO_SOFT_DROP === "1", i = t ?? pe(r, (/* @__PURE__ */ new Date()).toISOString()).softDropSnapshot, a = (e) => n ? ft(d, i, e) : dt(d, e);
|
|
566
|
+
yield* e.onDialectOrElse({
|
|
567
|
+
mysql: () => J(e, a(I), d.kind),
|
|
568
|
+
mssql: () => J(e, n ? Ct({
|
|
569
|
+
kind: "rename-table",
|
|
570
|
+
from: d.table,
|
|
571
|
+
to: i
|
|
572
|
+
}) : dt(d, F), d.kind),
|
|
573
|
+
orElse: () => J(e, a(F), d.kind)
|
|
574
|
+
});
|
|
575
|
+
break;
|
|
576
|
+
}
|
|
577
|
+
case "drop-column": {
|
|
578
|
+
let t = me(r), n = t !== void 0 || process.env.VOLTRO_SOFT_DROP === "1", i = t ?? pe(r, (/* @__PURE__ */ new Date()).toISOString()).softDropSnapshot;
|
|
579
|
+
yield* e.onDialectOrElse({
|
|
580
|
+
mysql: () => A(e).pipe(M.flatMap((t) => J(e, n ? Ne(d, i, I) : Me(d, t), d.kind, n ? [] : [he]))),
|
|
581
|
+
mssql: () => n ? Xt(e, {
|
|
582
|
+
kind: "rename-column",
|
|
583
|
+
table: d.table,
|
|
584
|
+
from: d.column,
|
|
585
|
+
to: i
|
|
586
|
+
}) : J(e, L(d, F), d.kind),
|
|
587
|
+
sqlite: () => J(e, n ? Ne(d, i, F) : L(d, F, !1), d.kind, n ? [] : [ye]),
|
|
588
|
+
orElse: () => J(e, n ? Ne(d, i, F) : L(d, F), d.kind)
|
|
589
|
+
});
|
|
590
|
+
break;
|
|
591
|
+
}
|
|
592
|
+
case "rename-column":
|
|
593
|
+
yield* e.onDialectOrElse({
|
|
594
|
+
mysql: () => J(e, R(d, I), d.kind),
|
|
595
|
+
mssql: () => Xt(e, d),
|
|
596
|
+
orElse: () => J(e, R(d, F), d.kind)
|
|
597
|
+
});
|
|
598
|
+
break;
|
|
599
|
+
case "add-index":
|
|
600
|
+
yield* e.onDialectOrElse({
|
|
601
|
+
sqlite: () => J(e, gt(d, !1), d.kind),
|
|
602
|
+
mysql: () => A(e).pipe(M.flatMap((t) => J(e, _t(d, i.declared, t), d.kind, [ve]))),
|
|
603
|
+
mssql: () => X(e, [...Ot(d.table, d.index.columns, i.declared), Dt(d)], d.kind),
|
|
604
|
+
orElse: () => J(e, gt(d, r.classification === "online-required"), d.kind)
|
|
605
|
+
});
|
|
606
|
+
break;
|
|
607
|
+
case "drop-index":
|
|
608
|
+
yield* e.onDialectOrElse({
|
|
609
|
+
mysql: () => J(e, bt(d), d.kind),
|
|
610
|
+
mssql: () => J(e, xt(d), d.kind),
|
|
611
|
+
orElse: () => J(e, yt(d), d.kind)
|
|
612
|
+
});
|
|
613
|
+
break;
|
|
614
|
+
case "rename-table":
|
|
615
|
+
yield* e.onDialectOrElse({
|
|
616
|
+
mysql: () => J(e, St(d, I), d.kind),
|
|
617
|
+
mssql: () => J(e, Ct(d), d.kind),
|
|
618
|
+
orElse: () => J(e, St(d, F), d.kind)
|
|
619
|
+
});
|
|
620
|
+
break;
|
|
621
|
+
case "rename-index":
|
|
622
|
+
yield* e.onDialectOrElse({
|
|
623
|
+
mysql: () => J(e, Tt(d), d.kind),
|
|
624
|
+
mssql: () => J(e, Et(d), d.kind),
|
|
625
|
+
orElse: () => J(e, wt(d), d.kind)
|
|
626
|
+
});
|
|
627
|
+
break;
|
|
628
|
+
case "add-unique":
|
|
629
|
+
yield* e.onDialectOrElse({
|
|
630
|
+
mysql: () => J(e, At(d, i.declared), d.kind),
|
|
631
|
+
sqlite: () => Y(e, d.table, i.declared, d.kind),
|
|
632
|
+
mssql: () => X(e, [...Ot(d.table, [d.column], i.declared), kt(d)], d.kind),
|
|
633
|
+
orElse: () => J(e, kt(d), d.kind)
|
|
634
|
+
});
|
|
635
|
+
break;
|
|
636
|
+
case "drop-unique":
|
|
637
|
+
yield* e.onDialectOrElse({
|
|
638
|
+
mysql: () => J(e, Lt(d), d.kind),
|
|
639
|
+
sqlite: () => Y(e, d.table, i.declared, d.kind),
|
|
640
|
+
orElse: () => J(e, It(d), d.kind)
|
|
641
|
+
});
|
|
642
|
+
break;
|
|
643
|
+
case "add-enum-value":
|
|
644
|
+
yield* e.onDialectOrElse({
|
|
645
|
+
pg: () => d.enumName === void 0 ? M.die(/* @__PURE__ */ Error(`add-enum-value on postgres needs the type name for ${d.table}.${d.column}; the plan carries none.`)) : J(e, `ALTER TYPE ${n(d.enumName, "postgres")} ADD VALUE IF NOT EXISTS '${d.value.replace(/'/g, "''")}'`, d.kind),
|
|
646
|
+
mysql: () => {
|
|
647
|
+
let t = z(i.declared, d.table, d.column);
|
|
648
|
+
return t === void 0 || t.enumValues === void 0 || t.enumValues.length === 0 ? M.die(/* @__PURE__ */ Error(`add-enum-value on the mysql family needs the declared column ${d.table}.${d.column} and its labels; the declaration carries none.`)) : M.flatMap(V(e, d.table, t, d.column), (t) => J(e, t, d.kind));
|
|
649
|
+
},
|
|
650
|
+
orElse: () => M.void
|
|
651
|
+
});
|
|
652
|
+
break;
|
|
653
|
+
case "add-unique-composite":
|
|
654
|
+
yield* e.onDialectOrElse({
|
|
655
|
+
mysql: () => J(e, Mt(d, i.declared), d.kind),
|
|
656
|
+
sqlite: () => Y(e, d.table, i.declared, d.kind),
|
|
657
|
+
mssql: () => X(e, [...Ot(d.table, d.fields, i.declared), jt(d)], d.kind),
|
|
658
|
+
orElse: () => J(e, jt(d), d.kind)
|
|
659
|
+
});
|
|
660
|
+
break;
|
|
661
|
+
case "drop-unique-composite":
|
|
662
|
+
yield* e.onDialectOrElse({
|
|
663
|
+
mysql: () => J(e, Ft(d), d.kind),
|
|
664
|
+
sqlite: () => Y(e, d.table, i.declared, d.kind),
|
|
665
|
+
mssql: () => J(e, Nt(d), d.kind),
|
|
666
|
+
orElse: () => X(e, Pt(d), d.kind)
|
|
667
|
+
});
|
|
668
|
+
break;
|
|
669
|
+
case "add-foreign-key":
|
|
670
|
+
yield* e.onDialectOrElse({
|
|
671
|
+
mysql: () => X(e, Rt(d, I), d.kind),
|
|
672
|
+
sqlite: () => Y(e, d.table, i.declared, d.kind),
|
|
673
|
+
mssql: () => X(e, Rt(d, K, et), d.kind),
|
|
674
|
+
orElse: () => X(e, Rt(d, F), d.kind)
|
|
675
|
+
});
|
|
676
|
+
break;
|
|
677
|
+
case "add-column": {
|
|
678
|
+
let n = Kt(d, r, u, yield* e.onDialectOrElse({
|
|
679
|
+
mysql: () => M.succeed("mariadb"),
|
|
680
|
+
mssql: () => M.succeed("mssql"),
|
|
681
|
+
sqlite: () => M.succeed("sqlite"),
|
|
682
|
+
orElse: () => M.succeed("postgres")
|
|
683
|
+
}));
|
|
684
|
+
if (!(o === "resume" && (yield* Z(e, _(e), d.table, d.column.name)))) for (let t of n.pre) yield* J(e, t, d.kind);
|
|
685
|
+
let s = r.classification === "online-required" || n.batchSize !== void 0 || n.sleepMs !== void 0, p = M.gen(function* () {
|
|
686
|
+
if (n.backfillKind === "sql" && n.backfillSql) {
|
|
687
|
+
if (r.classification !== "online-required" && n.batchSize === void 0) yield* e`UPDATE ${e(d.table)} SET ${e(d.column.name)} = ${n.backfillSql}`;
|
|
688
|
+
else {
|
|
689
|
+
let t = n.batchSize ?? 5e3, r = n.sleepMs ?? 0, i, a = 0;
|
|
690
|
+
for (;;) {
|
|
691
|
+
let o = i === void 0 ? yield* e`SELECT ${e("id")} FROM ${e(d.table)} WHERE ${e(d.column.name)} IS NULL ORDER BY ${e("id")} ${k(e, t)}` : yield* e`SELECT ${e("id")} FROM ${e(d.table)} WHERE ${e(d.column.name)} IS NULL AND ${e("id")} > ${i} ORDER BY ${e("id")} ${k(e, t)}`;
|
|
692
|
+
if (o.length === 0) break;
|
|
693
|
+
let s = o.map((e) => e.id);
|
|
694
|
+
yield* e`UPDATE ${e(d.table)} SET ${e(d.column.name)} = ${n.backfillSql} WHERE ${e("id")} IN ${e.in(s)}`, a += o.length, yield* M.logInfo(`backfill ${d.table}.${d.column.name}: ${a} rows`).pipe(M.annotateLogs({
|
|
695
|
+
scope: "voltro:migrate",
|
|
696
|
+
mode: "sql-batched"
|
|
697
|
+
})), i = o[o.length - 1].id, r > 0 && (yield* M.sleep(`${r} millis`));
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
} else if (n.backfillKind === "js" && n.backfillJs) {
|
|
701
|
+
let t = n.batchSize ?? 1e3, r = n.sleepMs ?? 0, o = i.declared.find((e) => e.tableName === d.table), s, c = 0;
|
|
702
|
+
for (;;) {
|
|
703
|
+
let i = s === void 0 ? yield* e`SELECT * FROM ${e(d.table)} WHERE ${e(d.column.name)} IS NULL ORDER BY ${e("id")} ${k(e, t)}` : yield* e`SELECT * FROM ${e(d.table)} WHERE ${e(d.column.name)} IS NULL AND ${e("id")} > ${s} ORDER BY ${e("id")} ${k(e, t)}`;
|
|
704
|
+
if (i.length === 0) break;
|
|
705
|
+
let l = o === void 0 ? i : a(i, o, f);
|
|
706
|
+
for (let t of l) {
|
|
707
|
+
let r = yield* M.tryPromise({
|
|
708
|
+
try: () => Promise.resolve(n.backfillJs(t)),
|
|
709
|
+
catch: (e) => e
|
|
710
|
+
}).pipe(M.orDie);
|
|
711
|
+
yield* e`UPDATE ${e(d.table)} SET ${e(d.column.name)} = ${Zt(r, d.column, f)} WHERE ${e("id")} = ${t.id}`;
|
|
712
|
+
}
|
|
713
|
+
c += l.length, yield* M.logInfo(`backfill ${d.table}.${d.column.name}: ${c} rows processed`).pipe(M.annotateLogs({
|
|
714
|
+
scope: "voltro:migrate",
|
|
715
|
+
table: d.table,
|
|
716
|
+
column: d.column.name
|
|
717
|
+
})), s = l[l.length - 1].id, r > 0 && (yield* M.sleep(`${r} millis`));
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
}), m = M.gen(function* () {
|
|
721
|
+
for (let t of n.post) yield* J(e, t, d.kind);
|
|
722
|
+
if (t(f) && (n.uniquePending === !0 || n.backfillKind !== void 0 && !d.column.nullable)) yield* Y(e, d.table, i.declared, d.kind);
|
|
723
|
+
else if (n.uniquePending === !0) {
|
|
724
|
+
let t = {
|
|
725
|
+
kind: "add-unique",
|
|
726
|
+
table: d.table,
|
|
727
|
+
column: d.column.name
|
|
728
|
+
};
|
|
729
|
+
yield* e.onDialectOrElse({
|
|
730
|
+
mysql: () => J(e, At(t, i.declared), d.kind),
|
|
731
|
+
mssql: () => X(e, [...Ot(d.table, [d.column.name], i.declared), kt(t)], d.kind),
|
|
732
|
+
orElse: () => J(e, kt(t), d.kind)
|
|
733
|
+
});
|
|
734
|
+
}
|
|
735
|
+
});
|
|
736
|
+
if (c !== void 0 && n.backfillKind !== void 0 && !s) return c({
|
|
737
|
+
table: d.table,
|
|
738
|
+
op: d,
|
|
739
|
+
data: p,
|
|
740
|
+
after: m
|
|
741
|
+
}), {
|
|
742
|
+
op: d,
|
|
743
|
+
status: "applied",
|
|
744
|
+
deferred: !0,
|
|
745
|
+
durationMs: Number(process.hrtime.bigint() - l) / 1e6
|
|
746
|
+
};
|
|
747
|
+
yield* p, yield* m;
|
|
748
|
+
break;
|
|
749
|
+
}
|
|
750
|
+
case "alter-column-nullability":
|
|
751
|
+
yield* e.onDialectOrElse({
|
|
752
|
+
mysql: () => {
|
|
753
|
+
let t = z(i.declared, d.table, d.column);
|
|
754
|
+
return t ? M.flatMap(V(e, d.table, t, d.column, d.toNullable), (t) => J(e, t, d.kind)) : M.die(/* @__PURE__ */ Error(`alter-column-nullability on mariadb needs the declared column '${d.table}.${d.column}' (to render MODIFY COLUMN); it was not in ctx.declared.`));
|
|
755
|
+
},
|
|
756
|
+
mssql: () => {
|
|
757
|
+
let t = z(i.declared, d.table, d.column);
|
|
758
|
+
return t ? J(e, ot(t, d.column, d.table, d.toNullable), d.kind) : M.die(/* @__PURE__ */ Error(`alter-column-nullability on mssql needs the declared column '${d.table}.${d.column}'.`));
|
|
759
|
+
},
|
|
760
|
+
sqlite: () => Y(e, d.table, i.declared, d.kind),
|
|
761
|
+
orElse: () => J(e, `ALTER TABLE ${F(d.table)} ALTER COLUMN ${F(d.column)} ${d.toNullable ? "DROP NOT NULL" : "SET NOT NULL"};`, d.kind)
|
|
762
|
+
});
|
|
763
|
+
break;
|
|
764
|
+
case "alter-column-default": {
|
|
765
|
+
let t = B(i.declared, d.table, d.column), n = z(i.declared, d.table, d.column), r = (e) => d.hasDefault && n !== void 0 && Fe(n, e) ? n : void 0;
|
|
766
|
+
yield* e.onDialectOrElse({
|
|
767
|
+
mysql: () => {
|
|
768
|
+
let n = r("mariadb");
|
|
769
|
+
return M.flatMap(n ? V(e, d.table, n, d.column) : M.succeed(Pe(d, I, (e) => Be(e, t))), (t) => J(e, t, d.kind));
|
|
770
|
+
},
|
|
771
|
+
mssql: () => {
|
|
772
|
+
let t = r("mssql");
|
|
773
|
+
return J(e, st(d, t ? ot(t, d.column, d.table) : void 0), d.kind);
|
|
774
|
+
},
|
|
775
|
+
sqlite: () => Y(e, d.table, i.declared, d.kind),
|
|
776
|
+
orElse: () => J(e, Pe(d, F, (e) => He(e, t)), d.kind)
|
|
777
|
+
});
|
|
778
|
+
break;
|
|
779
|
+
}
|
|
780
|
+
case "alter-column-type": {
|
|
781
|
+
let t = P(B(i.declared, d.table, d.column) ?? { type: d.to }), n = d.using ? ` USING ${d.using}` : "";
|
|
782
|
+
if (r.classification !== "online-required") {
|
|
783
|
+
yield* e.onDialectOrElse({
|
|
784
|
+
mysql: () => {
|
|
785
|
+
let t = z(i.declared, d.table, d.column);
|
|
786
|
+
return t ? M.flatMap(V(e, d.table, t, d.column), (t) => J(e, t, d.kind)) : M.die(/* @__PURE__ */ Error(`alter-column-type on mariadb needs the declared column '${d.table}.${d.column}' (to render MODIFY COLUMN); it was not in ctx.declared.`));
|
|
787
|
+
},
|
|
788
|
+
mssql: () => {
|
|
789
|
+
let t = z(i.declared, d.table, d.column);
|
|
790
|
+
if (!t) return M.die(/* @__PURE__ */ Error(`alter-column-type on mssql needs the declared column '${d.table}.${d.column}'.`));
|
|
791
|
+
if (d.using !== void 0) return M.fail(s(`SQL Server cannot apply a USING expression for ${d.table}.${d.column}; use a reviewed file migration for the explicit transform.`));
|
|
792
|
+
let n = ot(t, d.column, d.table);
|
|
793
|
+
return d.from === "integer" && d.to === "bigint" || d.from === "bigint" && d.to === "integer" ? Ce(e, d.table, d.column, n) : J(e, n, d.kind);
|
|
794
|
+
},
|
|
795
|
+
sqlite: () => Y(e, d.table, i.declared, d.kind),
|
|
796
|
+
orElse: () => J(e, `ALTER TABLE ${F(d.table)} ALTER COLUMN ${F(d.column)} TYPE ${t}${n};`, d.kind)
|
|
797
|
+
});
|
|
798
|
+
break;
|
|
799
|
+
}
|
|
800
|
+
yield* e.onDialectOrElse({
|
|
801
|
+
mysql: () => M.die(/* @__PURE__ */ Error(`online-required alter-column-type on ${d.table}.${d.column} is not supported on mariadb (the shadow-swap path is postgres-only). Use a smaller-table window or an external online-DDL tool.`)),
|
|
802
|
+
orElse: () => M.void
|
|
803
|
+
});
|
|
804
|
+
let a = `${d.column}__shadow`, o = `${d.column}__old`;
|
|
805
|
+
yield* e.unsafe(`ALTER TABLE ${F(d.table)} ADD COLUMN ${F(a)} ${t}`);
|
|
806
|
+
let c = d.using ?? F(d.column), l;
|
|
807
|
+
for (;;) {
|
|
808
|
+
let t = l === void 0 ? yield* e`SELECT ${e("id")} FROM ${e(d.table)} WHERE ${e(a)} IS NULL ORDER BY ${e("id")} ${k(e, 5e3)}` : yield* e`SELECT ${e("id")} FROM ${e(d.table)} WHERE ${e(a)} IS NULL AND ${e("id")} > ${l} ORDER BY ${e("id")} ${k(e, 5e3)}`;
|
|
809
|
+
if (t.length === 0) break;
|
|
810
|
+
yield* e.unsafe(`UPDATE ${F(d.table)} SET ${F(a)} = ${c} WHERE ${F(d.column)} IS NOT NULL AND ${F(a)} IS NULL AND ${F("id")} IN (${t.map((e) => `'${e.id.replace(/'/g, "''")}'`).join(",")})`), l = t[t.length - 1].id;
|
|
811
|
+
}
|
|
812
|
+
yield* M.logInfo(`alter-column-type shadow ${d.table}.${d.column}: shadow populated`).pipe(M.annotateLogs({ scope: "voltro:migrate" })), yield* e.unsafe(`ALTER TABLE ${F(d.table)} RENAME COLUMN ${F(d.column)} TO ${F(o)}`), yield* e.unsafe(`ALTER TABLE ${F(d.table)} RENAME COLUMN ${F(a)} TO ${F(d.column)}`), yield* e.unsafe(`ALTER TABLE ${F(d.table)} DROP COLUMN ${F(o)}`);
|
|
813
|
+
break;
|
|
814
|
+
}
|
|
815
|
+
case "drop-foreign-key":
|
|
816
|
+
yield* e.onDialectOrElse({
|
|
817
|
+
mysql: () => J(e, Vt(d), d.kind),
|
|
818
|
+
sqlite: () => Y(e, d.table, i.declared, d.kind),
|
|
819
|
+
orElse: () => J(e, Bt(d), d.kind)
|
|
820
|
+
});
|
|
821
|
+
break;
|
|
822
|
+
case "add-check":
|
|
823
|
+
yield* e.onDialectOrElse({
|
|
824
|
+
mysql: () => J(e, Ht(d, I), d.kind),
|
|
825
|
+
sqlite: () => Y(e, d.table, i.declared, d.kind),
|
|
826
|
+
orElse: () => J(e, Ht(d, F), d.kind)
|
|
827
|
+
});
|
|
828
|
+
break;
|
|
829
|
+
case "convert-charset":
|
|
830
|
+
yield* e.onDialectOrElse({
|
|
831
|
+
mysql: () => M.gen(function* () {
|
|
832
|
+
let { drops: t, rewrite: n, restores: r } = Ze(d, i.declared, yield* ze(e, d.table));
|
|
833
|
+
for (let n of t) yield* J(e, n, d.kind, [ge]);
|
|
834
|
+
yield* J(e, n, d.kind);
|
|
835
|
+
for (let t of r) yield* J(e, t, d.kind, [_e]);
|
|
836
|
+
}),
|
|
837
|
+
orElse: () => M.die(/* @__PURE__ */ Error("convert-charset requires MySQL or MariaDB"))
|
|
838
|
+
});
|
|
839
|
+
break;
|
|
840
|
+
case "reconcile-json-check": {
|
|
841
|
+
let t = K(d.column), n = d.validation === "value" ? b(t) : `(${t} IS NULL OR ISJSON(${t}) = 1)`;
|
|
842
|
+
yield* e.onDialectOrElse({
|
|
843
|
+
mssql: () => X(e, [...d.dropConstraints.map((e) => `ALTER TABLE ${K(d.table)} DROP CONSTRAINT ${K(e)}`), `ALTER TABLE ${K(d.table)} WITH CHECK ADD CHECK ${n}`], d.kind),
|
|
844
|
+
orElse: () => M.die(/* @__PURE__ */ Error("reconcile-json-check requires SQL Server"))
|
|
845
|
+
});
|
|
846
|
+
break;
|
|
847
|
+
}
|
|
848
|
+
case "drop-check": yield* e.onDialectOrElse({
|
|
849
|
+
mysql: () => Gt(e, d, i.declared),
|
|
850
|
+
sqlite: () => Y(e, d.table, i.declared, d.kind),
|
|
851
|
+
orElse: () => J(e, Ut(d), d.kind)
|
|
852
|
+
});
|
|
853
|
+
}
|
|
854
|
+
return {
|
|
855
|
+
op: d,
|
|
856
|
+
status: "applied",
|
|
857
|
+
durationMs: Number((process.hrtime.bigint() - l) / 1000000n)
|
|
858
|
+
};
|
|
859
|
+
}), mn = (e, t, n, r, i) => M.gen(function* () {
|
|
860
|
+
let a = [...new Set(r.map((e) => e.runId))];
|
|
861
|
+
if (a.length > 1) return yield* M.die(/* @__PURE__ */ Error(`applyPlan: the resume ledger holds ${a.length} unfinished runs (${a.join(", ")}). Only one migration may be in flight at a time — the advisory lock enforces it — so this means a run was interrupted and a LATER one wrote over it without clearing.\nNothing was changed. Inspect \`${h}\`, reconcile the schema by hand, and delete the rows of the run you have finished.`));
|
|
862
|
+
let o = a[0], s = [...r].map((e) => ({
|
|
863
|
+
index: Number(e.opIndex),
|
|
864
|
+
status: e.status,
|
|
865
|
+
planned: JSON.parse(e.payload)
|
|
866
|
+
})).sort((e, t) => e.index - t.index), c = s.find((e) => e.status === "started"), l = s.filter((e) => e.status === "started").length;
|
|
867
|
+
if (l > 1) return yield* M.die(/* @__PURE__ */ Error(`applyPlan: the resume ledger records ${l} operations as in-flight for run ${o}. It is written strictly sequentially, so at most one can be — this ledger is corrupt and the schema state it describes cannot be trusted.\nNothing was changed. Reconcile \`${h}\` by hand before re-running.`));
|
|
868
|
+
let u = s.filter((e) => e.status === "added");
|
|
869
|
+
if (yield* M.logWarning(`migration resume: found an interrupted run (${o}) — ${s.filter((e) => e.status === "applied").length} of ${s.length} operation(s) completed${c ? `, in flight: ${c.planned.op.kind} ${ae(c.planned.op)}` : ""}${u.length > 0 ? `, ${u.length} column(s) added with the backfill still owed` : ""}`).pipe(M.annotateLogs({ scope: "voltro:migrate" })), s.every((e) => e.status === "pending")) return {
|
|
870
|
+
kind: "discard",
|
|
871
|
+
runId: o,
|
|
872
|
+
why: "no operation had started — no DDL ran"
|
|
873
|
+
};
|
|
874
|
+
let d = c && !(["sqlite", "turso"].includes(t) && c.planned.sqliteAtomicRebuild === !0) ? yield* fn(e, t, c.planned) : "clean";
|
|
875
|
+
d !== "clean" && (n = yield* i());
|
|
876
|
+
let f = r[0].planFingerprint;
|
|
877
|
+
if (f !== n.toFingerprint) return {
|
|
878
|
+
kind: "discard",
|
|
879
|
+
runId: o,
|
|
880
|
+
why: `the declared schema changed since the interruption (recorded target ${f.slice(0, 12)}…, current ${n.toFingerprint.slice(0, 12)}…) — applying the freshly-diffed plan instead`
|
|
881
|
+
};
|
|
882
|
+
let p = (e) => e.status === "applied" ? !1 : c && e.index === c.index && d !== "clean" ? d === "outstanding" : v(n, e.planned.op), m = s.filter(p), ee = s.filter((e) => e.status !== "applied" && !p(e));
|
|
883
|
+
return yield* cn(e, o, m.map((e) => e.index), ee.map((e) => e.index)), yield* M.logWarning(`migration resume: continuing run ${o} — replaying ${m.length} operation(s), ${s.length - m.length} already applied`).pipe(M.annotateLogs({ scope: "voltro:migrate" })), {
|
|
884
|
+
kind: "resume",
|
|
885
|
+
runId: o,
|
|
886
|
+
ops: m.map((e) => ({
|
|
887
|
+
index: e.index,
|
|
888
|
+
planned: e.planned
|
|
889
|
+
})),
|
|
890
|
+
history: s.filter((e) => e.status !== "pending" || p(e)).map((e) => e.planned)
|
|
891
|
+
};
|
|
892
|
+
}), hn = (e, t, r) => M.gen(function* () {
|
|
893
|
+
let i, a = 0;
|
|
894
|
+
for (let o of t.operations) {
|
|
895
|
+
let s = o.op;
|
|
896
|
+
if (s.kind !== "add-index" || !s.index.unique || s.index.where === void 0) continue;
|
|
897
|
+
if (++a === 2 && t.operations.length >= 32) {
|
|
898
|
+
let e = /* @__PURE__ */ new Set();
|
|
899
|
+
for (let { op: n } of t.operations) n.kind === "rename-table" ? e.add(n.to) : (n.kind === "create-table" || n.kind === "add-column" || n.kind === "rename-column") && e.add(n.table);
|
|
900
|
+
i = e;
|
|
901
|
+
}
|
|
902
|
+
if (i === void 0 ? t.operations.some((e) => e.op.kind === "rename-table" ? e.op.to === s.table : e.op.table === s.table && [
|
|
903
|
+
"create-table",
|
|
904
|
+
"add-column",
|
|
905
|
+
"rename-column"
|
|
906
|
+
].includes(e.op.kind)) : i.has(s.table)) continue;
|
|
907
|
+
let c = s.index.columns.map((e) => e.startsWith("(") ? e : n(e, r)), l = r === "mssql" ? "" : c.map((e) => ` AND ${e} IS NOT NULL`).join(""), u = r === "mssql" ? "SELECT TOP (1) 1" : "SELECT 1", d = r === "mssql" ? "" : " LIMIT 1";
|
|
908
|
+
if ((yield* e.unsafe(`${u} FROM ${n(s.table, r)} WHERE (${s.index.where})${l} GROUP BY ${c.join(", ")} HAVING COUNT(*) > 1${d}`)).length > 0) return yield* M.die(/* @__PURE__ */ Error(`applyPlan: partial unique index '${s.index.name}' on '${s.table}' conflicts with existing qualifying rows. No schema or row changes were applied. Resolve the conflicting data explicitly before retrying.`));
|
|
909
|
+
}
|
|
910
|
+
}), gn = (e) => {
|
|
911
|
+
let t = e.op, n = (e) => typeof t[e] == "string" ? t[e] : "";
|
|
912
|
+
return [
|
|
913
|
+
t.kind,
|
|
914
|
+
n("table"),
|
|
915
|
+
n("column"),
|
|
916
|
+
n("name"),
|
|
917
|
+
n("to")
|
|
918
|
+
].join("\0");
|
|
919
|
+
}, _n = (e, t, n) => M.acquireUseRelease(ee(e, { schema: n.lockSchema }), () => vn(e, t, n), (e) => e.release.pipe(M.orDie)), vn = (e, t, n) => M.gen(function* () {
|
|
920
|
+
let r = t.operations.filter((e) => e.blocked);
|
|
921
|
+
if (r.length > 0) return yield* M.die(/* @__PURE__ */ Error(`applyPlan: refusing to execute — ${r.length} blocked operations. Run \`voltro db plan\` to see the fixes.`));
|
|
922
|
+
let i = Date.now(), a = [], c = _(e), l = yield* n.replan(e, "execution"), u = new Set(t.operations.filter((e) => !e.blocked).map((e) => gn(e))), d = {
|
|
923
|
+
...l,
|
|
924
|
+
operations: l.operations.map((e) => {
|
|
925
|
+
if (!e.blocked || !u.has(gn(e))) return e;
|
|
926
|
+
let { blocked: t, ...n } = e;
|
|
927
|
+
return n;
|
|
928
|
+
})
|
|
929
|
+
};
|
|
930
|
+
(d.operations.length !== t.operations.length || d.toFingerprint !== t.toFingerprint) && (yield* M.logInfo(`migration: re-planned under the lock — ${t.operations.length} operation(s) became ${d.operations.length}. Another writer changed the schema while this plan waited.`).pipe(M.annotateLogs({ scope: "voltro:migrate" })));
|
|
931
|
+
let f = d.operations.filter((e) => e.blocked);
|
|
932
|
+
if (f.length > 0) return yield* M.die(/* @__PURE__ */ Error(`applyPlan: refusing to execute — re-planning under the migration lock found ${f.length} blocked operation(s). Run \`voltro db plan\` to see the fixes.`));
|
|
933
|
+
let p = ce(n.declared, c);
|
|
934
|
+
p && (yield* J(e, p, "extensions"));
|
|
935
|
+
let m = oe(n.declared, c);
|
|
936
|
+
m && (yield* J(e, m, "enum-types"));
|
|
937
|
+
let ee = se(n.declared, c);
|
|
938
|
+
ee && (yield* J(e, ee, "enum-types"));
|
|
939
|
+
let h = ie(c) === "plan-transactional";
|
|
940
|
+
yield* hn(e, d, c), yield* Qt(e, c, n.charset);
|
|
941
|
+
let te = yield* an(e), g = te.length > 0 ? yield* mn(e, c, d, te, () => n.replan(e, "execution").pipe(M.map((e) => (d = {
|
|
942
|
+
...e,
|
|
943
|
+
operations: e.operations.map((e) => {
|
|
944
|
+
if (!e.blocked || !u.has(gn(e))) return e;
|
|
945
|
+
let { blocked: t, ...n } = e;
|
|
946
|
+
return n;
|
|
947
|
+
})
|
|
948
|
+
}, d)))) : void 0;
|
|
949
|
+
if (d.operations.some((e) => e.blocked)) return yield* M.die(/* @__PURE__ */ Error("applyPlan: refusing blocked operations after interrupted-run repair. Run `voltro db plan` to inspect the remaining work."));
|
|
950
|
+
g?.kind === "discard" && (yield* M.logWarning(`migration resume: discarding ledger of run ${g.runId} — ${g.why}`).pipe(M.annotateLogs({ scope: "voltro:migrate" })), yield* ln(e, g.runId));
|
|
951
|
+
let ne = (/* @__PURE__ */ new Date()).toISOString(), re = process.env.VOLTRO_SOFT_DROP === "1" ? d.operations.map((e) => pe(e, ne)) : d.operations, v = h ? re.filter((e) => e.classification !== "online-required") : [], ae = h ? re.filter((e) => e.classification === "online-required") : re, y = `plan_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`, b = ae.map((e, t) => ({
|
|
952
|
+
index: v.length + t,
|
|
953
|
+
planned: e
|
|
954
|
+
})), x = "fresh", S;
|
|
955
|
+
g?.kind === "resume" && (y = g.runId, b = g.ops, S = g.history, x = "resume"), (v.length > 0 || b.length > 0) && n.beforeExecute !== void 0 && (yield* n.beforeExecute), x === "fresh" && b.length > 0 && (yield* sn(e, y, d.toFingerprint, b)), v.length > 0 && (yield* xe(e, M.gen(function* () {
|
|
956
|
+
let e = yield* Ee.SqlClient;
|
|
957
|
+
if (x === "resume") return yield* M.die(/* @__PURE__ */ Error("applyPlan: transactional work appeared while resuming an interrupted run with the same target. Inspect the live schema and operation ledger before retrying."));
|
|
958
|
+
let t = v.map((e, t) => ({
|
|
959
|
+
index: t,
|
|
960
|
+
planned: e
|
|
961
|
+
}));
|
|
962
|
+
yield* sn(e, y, d.toFingerprint, t);
|
|
963
|
+
let r = [], i = () => M.gen(function* () {
|
|
964
|
+
for (let e of r.splice(0)) yield* e.data, yield* e.after;
|
|
965
|
+
});
|
|
966
|
+
for (let t of v) qt.has(t.op.kind) && r.length > 0 && (yield* i()), a.push(yield* pn(e, t, n, "fresh", (e) => r.push(e)));
|
|
967
|
+
yield* i();
|
|
968
|
+
for (let { index: n } of t) yield* $(e, y, n, "applied");
|
|
969
|
+
})));
|
|
970
|
+
let C = [], w = () => M.gen(function* () {
|
|
971
|
+
let t = C.splice(0);
|
|
972
|
+
if (t.length !== 0) {
|
|
973
|
+
yield* xe(e, M.gen(function* () {
|
|
974
|
+
for (let e of t) yield* e.data;
|
|
975
|
+
}));
|
|
976
|
+
for (let n of t) yield* n.after, a.push({
|
|
977
|
+
op: n.op,
|
|
978
|
+
status: "applied",
|
|
979
|
+
durationMs: Number(process.hrtime.bigint() - n.startedAt) / 1e6
|
|
980
|
+
}), yield* $(e, y, n.index, "applied", Number(process.hrtime.bigint() - n.startedAt) / 1e6);
|
|
981
|
+
}
|
|
982
|
+
});
|
|
983
|
+
for (let { index: t, planned: r } of b) {
|
|
984
|
+
qt.has(r.op.kind) && C.length > 0 && (yield* w()), yield* $(e, y, t, "started", void 0, r);
|
|
985
|
+
let i = a.length, o = process.hrtime.bigint(), c = yield* pn(e, r, n, x, (e) => C.push({
|
|
986
|
+
...e,
|
|
987
|
+
index: t,
|
|
988
|
+
startedAt: o
|
|
989
|
+
})).pipe(M.mapError((e) => s(`${String(e?.message ?? e)}\n\n progress: ${i} of ${b.length + v.length} operation(s) applied before this one${v.length > 0 ? ` (${v.length} of them in a transaction)` : ""}.\n` + (h ? (v.length > 0 ? "The transactional batch completed before this phase; this failure does not roll it back.\n" : "This phase runs outside a transactional batch; completed operations are not rolled back by this failure.\n") + "An enclosing deployment transaction may still undo its own changes. Inspect the live schema and operation ledger before retrying.\nThe failing operation may also have made partial progress.\n" : ` This dialect does NOT apply a plan atomically: those ${i} are COMMITTED and there is no rollback.\n The ledger records how far it got (operation ${t} was in flight).\n`), e)));
|
|
990
|
+
c.deferred === !0 ? yield* $(e, y, t, "added") : (a.push(c), yield* $(e, y, t, "applied", c.durationMs));
|
|
991
|
+
}
|
|
992
|
+
yield* w();
|
|
993
|
+
let T = yield* n.replan(e, "convergence");
|
|
994
|
+
if (T.operations.length > 0) {
|
|
995
|
+
let e = (e) => "column" in e ? typeof e.column == "string" ? `.${e.column}` : `.${String(e.column.name ?? "")}` : "", t = T.operations.slice(0, 10).map((t) => ` - ${t.op.kind} ${"table" in t.op ? t.op.table : ""}${e(t.op)}`).join("\n"), n = a.filter((e) => e.status === "applied"), r = n.slice(0, 10).map((t) => ` - ${t.op.kind} ${"table" in t.op ? t.op.table : ""}${e(t.op)}`).join("\n") + (n.length > 10 ? `\n … and ${n.length - 10} more` : "");
|
|
996
|
+
return yield* M.die(/* @__PURE__ */ Error(`applyPlan: the migration did not converge. ${d.operations.length} operation(s) were executed without error, but re-planning against the live schema still finds ${T.operations.length}:\n${t}${T.operations.length > 10 ? `\n … and ${T.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.\n` + (h ? (v.length > 0 ? "The transactional batch completed before this convergence check; this check does not roll it back.\n" : "This convergence check does not roll back completed operations; this plan had no transactional batch.\n") + `An enclosing deployment transaction may still undo its own changes. Inspect the live schema and operation ledger before retrying.\nOperations reported applied before this check:\n${r}\n` : ` This dialect does NOT apply a plan atomically. The ${n.length} operation(s) below are COMMITTED\n and there is no rollback — the schema is PART-APPLIED, not unchanged:\n${r}\n Re-running plans from where the database now is, which is a different plan from the one that failed.\n`) + "Two 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."));
|
|
997
|
+
}
|
|
998
|
+
let le = a.filter((e) => e.status === "applied").length, ue = Date.now() - i, E = y, de = (/* @__PURE__ */ new Date()).toISOString(), fe = yield* j(e, de), D = [...v, ...S ?? b.map((e) => e.planned)], O = x === "resume" ? yield* e`SELECT ${e("fingerprint")}, ${e("operations")}
|
|
999
|
+
FROM ${e("_voltro_migration_plans")} WHERE ${e("id")} = ${E}` : [];
|
|
1000
|
+
if (O.length > 0) {
|
|
1001
|
+
let e = O[0], t = typeof e.operations == "string" ? JSON.parse(e.operations) : e.operations;
|
|
1002
|
+
if (e.fingerprint !== d.toFingerprint || !Array.isArray(t) || !we(t, JSON.parse(JSON.stringify(D)))) return yield* M.die(/* @__PURE__ */ Error(`applyPlan: history for resumed run ${E} does not match its recorded operations; the resume ledger was retained.`));
|
|
1003
|
+
}
|
|
1004
|
+
O.length === 0 && (yield* e`
|
|
1005
|
+
INSERT INTO ${e("_voltro_migration_plans")}
|
|
1006
|
+
(${e("id")}, ${e("fingerprint")}, ${e("liveFingerprint")}, ${e("operations")},
|
|
1007
|
+
${e("appliedBy")}, ${e("environment")}, ${e("source")},
|
|
1008
|
+
${e("durationMs")}, ${e("appliedAt")}, ${e("notes")})
|
|
1009
|
+
VALUES
|
|
1010
|
+
(${E}, ${d.toFingerprint}, ${T.fromFingerprint}, ${JSON.stringify(D)},
|
|
1011
|
+
${n.appliedBy}, ${n.environment}, ${n.source},
|
|
1012
|
+
${ue}, ${fe}, ${n.notes ?? null})
|
|
1013
|
+
`.pipe(M.mapError((e) => s(`${String(e?.message ?? e)}\n\nMigration ${E}: schema work completed, but writing its history failed.\nThe operation ledger is retained for recovery. This error does not establish whole-plan rollback; inspect the live schema before retrying.`, e)))), yield* ln(e, y).pipe(M.mapError((e) => s(`${String(e?.message ?? e)}\n\nMigration ${E}: the history row was written, but operation-ledger cleanup failed.\nAn enclosing transaction may still roll back its own changes. Inspect the recorded history and any remaining operation ledger before retrying; do not assume the schema is unchanged.`, e)));
|
|
1014
|
+
let me = [...new Set(D.flatMap((e) => e.op.kind === "rename-table" ? [e.op.to] : [e.op.table]))];
|
|
1015
|
+
return yield* M.promise(() => o({ changedTables: me }).catch(() => {})), {
|
|
1016
|
+
id: E,
|
|
1017
|
+
appliedAt: de,
|
|
1018
|
+
fingerprint: d.toFingerprint,
|
|
1019
|
+
operations: D,
|
|
1020
|
+
appliedBy: n.appliedBy,
|
|
1021
|
+
environment: n.environment,
|
|
1022
|
+
durationMs: ue,
|
|
1023
|
+
appliedOps: le,
|
|
1024
|
+
source: n.source,
|
|
1025
|
+
...n.notes ? { notes: n.notes } : {}
|
|
1026
|
+
};
|
|
1027
|
+
}), yn = (e, t) => {
|
|
1028
|
+
switch (t.kind) {
|
|
1029
|
+
case "drop-foreign-key": return {
|
|
1030
|
+
...e,
|
|
1031
|
+
columns: e.columns.map((e) => {
|
|
1032
|
+
if (e.name !== t.column) return e;
|
|
1033
|
+
let { references: n, ...r } = e;
|
|
1034
|
+
return r;
|
|
1035
|
+
})
|
|
1036
|
+
};
|
|
1037
|
+
case "drop-index": return {
|
|
1038
|
+
...e,
|
|
1039
|
+
indexes: e.indexes.filter((e) => e.name !== t.index)
|
|
1040
|
+
};
|
|
1041
|
+
case "drop-column": return {
|
|
1042
|
+
...e,
|
|
1043
|
+
columns: e.columns.filter((e) => e.name !== t.column),
|
|
1044
|
+
indexes: e.indexes.filter((e) => !e.columns.includes(t.column))
|
|
1045
|
+
};
|
|
1046
|
+
case "rename-column": return {
|
|
1047
|
+
...e,
|
|
1048
|
+
columns: e.columns.map((e) => e.name === t.from ? {
|
|
1049
|
+
...e,
|
|
1050
|
+
name: t.to
|
|
1051
|
+
} : e),
|
|
1052
|
+
indexes: e.indexes.map((e) => ({
|
|
1053
|
+
...e,
|
|
1054
|
+
columns: e.columns.map((e) => e === t.from ? t.to : e)
|
|
1055
|
+
})),
|
|
1056
|
+
...e.primaryKey === void 0 ? {} : { primaryKey: e.primaryKey.map((e) => e === t.from ? t.to : e) }
|
|
1057
|
+
};
|
|
1058
|
+
}
|
|
1059
|
+
}, bn = (e, t, n) => {
|
|
1060
|
+
let r = n.tables.find((e) => e.name === t.table), i = () => r === void 0 ? M.void : Yt(e, t.table, at(yn(r, t)), t.kind);
|
|
1061
|
+
switch (t.kind) {
|
|
1062
|
+
case "drop-foreign-key": return e.onDialectOrElse({
|
|
1063
|
+
mysql: () => J(e, Vt(t), t.kind),
|
|
1064
|
+
sqlite: i,
|
|
1065
|
+
orElse: () => J(e, Bt(t), t.kind)
|
|
1066
|
+
});
|
|
1067
|
+
case "drop-index": return e.onDialectOrElse({
|
|
1068
|
+
mysql: () => J(e, bt(t), t.kind),
|
|
1069
|
+
mssql: () => J(e, xt(t), t.kind),
|
|
1070
|
+
orElse: () => J(e, yt(t), t.kind)
|
|
1071
|
+
});
|
|
1072
|
+
case "drop-column": return e.onDialectOrElse({
|
|
1073
|
+
mysql: () => A(e).pipe(M.flatMap((n) => J(e, Me(t, n), t.kind, [he]))),
|
|
1074
|
+
sqlite: i,
|
|
1075
|
+
orElse: () => J(e, L(t, F), t.kind)
|
|
1076
|
+
});
|
|
1077
|
+
case "rename-column": return e.onDialectOrElse({
|
|
1078
|
+
mysql: () => J(e, R(t, I), t.kind),
|
|
1079
|
+
mssql: () => Xt(e, t),
|
|
1080
|
+
orElse: () => J(e, R(t, F), t.kind)
|
|
1081
|
+
});
|
|
1082
|
+
}
|
|
1083
|
+
}, xn = (e, t, n) => pn(e, t, { declared: n });
|
|
1084
|
+
//#endregion
|
|
1085
|
+
export { Kt as addColumnDdl, un as appliedLedgerOperations, _n as applyPlan, dn as clearResumeLedger, ze as columnLevelChecksMysql, _t as emitAddIndexMysql, Ze as emitConvertCharsetMysql, ut as emitCreateTableMssql, Qe as emitCreateTableMysql, We as emitCreateTablePg, rt as emitCreateTableSqlite, L as emitDropColumnDdl, Me as emitDropColumnDdlMysql, nn as ensureDeployLedgerTable, Qt as ensureLedgerTable, $t as ensurePlanLedgerTable, tn as ensurePluginMigrationsLedgerTable, rn as ensureReplaceMarkerTable, en as ensureSchemaOwnerTable, on as hasInterruptedMigration, pt as mysqlIndexPrefixFor, Je as renderColumnMysql, Ue as renderColumnPg, xn as runMigrationOp, bn as runSchemaOp };
|