@voltro/database 0.85.0 → 0.87.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 +5987 -0
- package/THIRD-PARTY-NOTICES.md +5 -257
- 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 +23 -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 +121 -0
- package/dist/frameworkTables.js +143 -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 +166 -22
- package/dist/index.js +82 -2183
- 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 +644 -0
- package/dist/migration.js +18 -0
- package/dist/migrations/appliedAtValue.js +8 -0
- package/dist/migrations/applier.js +1100 -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/ddlStatements.js +9 -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 +332 -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 +801 -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 +785 -0
- package/dist/migrations/postgresEnumCheck.js +22 -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/sqliteCatalogName.js +13 -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-CG2j7J3f.js → optimisticVersion.js} +2 -2
- package/dist/pendingAttribution.js +79 -0
- package/dist/pluginRef.js +17 -0
- package/dist/predicate.js +273 -0
- package/dist/predicateCoercion.js +13 -0
- package/dist/projectedColumn.js +37 -0
- package/dist/queryBuilder.js +388 -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 +173 -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 +2792 -3773
- package/dist/sql.js +42 -6905
- package/dist/sqlCompiler.js +360 -0
- package/dist/sqlError.js +8 -0
- package/dist/sqlMutation.js +55 -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 +100 -0
- package/dist/transientContention.js +36 -0
- package/dist/typeId.js +51 -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.js +6 -2
- package/dist/writeAttribution.js +16 -0
- package/dist/writeRecorder.js +38 -0
- package/package.json +3 -4
- package/dist/replaceInProgress-DC88Pdiy.js +0 -3735
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import { causeBranches as e, classifyConstraintViolation as t, extractDbCause as n } from "../dbCause.js";
|
|
2
|
+
import { derivedRowId as r } from "../derivedRowId.js";
|
|
3
|
+
import { FILE_MIGRATION_PATTERN as i, IrreversibleMigrationError as a, MigrationExpectationFailed as o, MigrationSkipped as s, TransactionBrokenByBodyError as c, irreversibleReason as l, isFileMigration as u } from "./fileBased.js";
|
|
4
|
+
import { acquireMigrationLock as d } from "./lock.js";
|
|
5
|
+
import { MIGRATION_OPS_TABLE as f, dialectOf as p } from "./resumeLedger.js";
|
|
6
|
+
import { migrationTableExists as m } from "./migrationTableExists.js";
|
|
7
|
+
import { appliedAtValue as h } from "./appliedAtValue.js";
|
|
8
|
+
import { ensureLedgerTable as g, ensurePlanLedgerTable as _ } from "./applier.js";
|
|
9
|
+
import { isFileMigrationRow as v } from "./ledgerRows.js";
|
|
10
|
+
import { failedExpectations as y, makeFileMigrationContext as b } from "./fileContext.js";
|
|
11
|
+
import { withSchemaProbeRecorder as x } from "./fileSkipProbes.js";
|
|
12
|
+
import { Effect as S } from "effect";
|
|
13
|
+
import { createHash as C } from "node:crypto";
|
|
14
|
+
import { SqlClient as w } from "effect/sql";
|
|
15
|
+
import { createLogger as T } from "@voltro/logger";
|
|
16
|
+
import { promises as E } from "node:fs";
|
|
17
|
+
import { join as D } from "node:path";
|
|
18
|
+
import { pathToFileURL as O } from "node:url";
|
|
19
|
+
//#region src/migrations/fileDiscovery.ts
|
|
20
|
+
var k = T({ scope: "voltro:migrate" }), A = (e) => C("sha256").update(e).digest("hex").slice(0, 16), j = [{
|
|
21
|
+
codemod: "0.78.0/01_file-migration-phase",
|
|
22
|
+
inverse: (e) => {
|
|
23
|
+
let t = /\n {2}phase: 'before-schema',\n {2}/g, n = /\n {2}phase: 'before-schema'(?=\r?\n[ \t]*\})/g, r = e.includes("\r\n") ? "\r\n" : "\n", i = (t) => {
|
|
24
|
+
let n = e.lastIndexOf("\n", t - 1) + 1;
|
|
25
|
+
return /^[ \t]*/.exec(e.slice(n, t))[0];
|
|
26
|
+
};
|
|
27
|
+
return [e.replace(t, (e, t) => r + i(t)).replace(n, ""), e.replace(t, " ").replace(n, "")];
|
|
28
|
+
}
|
|
29
|
+
}], M = (e) => new Set([e, ...j.flatMap((t) => t.inverse(e))].map(A)), N = /* @__PURE__ */ new Set([
|
|
30
|
+
"node_modules",
|
|
31
|
+
"dist",
|
|
32
|
+
"build",
|
|
33
|
+
".turbo",
|
|
34
|
+
".vite",
|
|
35
|
+
".next",
|
|
36
|
+
".git",
|
|
37
|
+
".framework"
|
|
38
|
+
]), P = async (e) => {
|
|
39
|
+
let t = [], n = async (e) => {
|
|
40
|
+
let r = await E.readdir(e, { withFileTypes: !0 }).catch(() => []);
|
|
41
|
+
for (let a of r) {
|
|
42
|
+
if (N.has(a.name)) continue;
|
|
43
|
+
let r = D(e, a.name);
|
|
44
|
+
a.isDirectory() ? await n(r) : a.isFile() && i.test(a.name) && t.push(r);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
return await n(e), t.sort((e, t) => {
|
|
48
|
+
let n = e.split("/").pop() ?? e, r = t.split("/").pop() ?? t;
|
|
49
|
+
return n.localeCompare(r);
|
|
50
|
+
});
|
|
51
|
+
}, F = async (e) => await import(O(e).href), I = async (e, t = F) => {
|
|
52
|
+
let n = [];
|
|
53
|
+
for (let r of e) {
|
|
54
|
+
let e = (await t(r)).default;
|
|
55
|
+
if (!u(e)) {
|
|
56
|
+
k.warn("migration file has no default-exported `migration(...)` — skipped", { file: r });
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
let i = await E.readFile(r, "utf8").catch(() => "");
|
|
60
|
+
n.push({
|
|
61
|
+
file: r,
|
|
62
|
+
migration: e,
|
|
63
|
+
hash: A(i),
|
|
64
|
+
accepted: M(i)
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return n;
|
|
68
|
+
}, L = "_voltro_migration_plans", R = (e) => z(e).pipe(S.map((e) => e.map((e) => e.id))), z = (e) => m(e, L).pipe(S.flatMap((t) => t ? e`
|
|
69
|
+
SELECT ${e("id")}, ${e("fingerprint")}
|
|
70
|
+
FROM ${e(L)}
|
|
71
|
+
WHERE ${v(e)}
|
|
72
|
+
`.pipe(S.map((e) => e.map((e) => ({
|
|
73
|
+
id: e.id,
|
|
74
|
+
hash: e.fingerprint ?? ""
|
|
75
|
+
})))) : S.succeed([]))), B = (e, t) => t.filter((t) => {
|
|
76
|
+
let n = e.get(t.migration.id);
|
|
77
|
+
return n !== void 0 && n !== "" && !t.accepted.has(n);
|
|
78
|
+
}).map((e) => e.migration.id), V = /* @__PURE__ */ new Set(), H = (e) => t(n(e))?.kind === "unique", U = class extends Error {
|
|
79
|
+
migrationId;
|
|
80
|
+
file;
|
|
81
|
+
cause;
|
|
82
|
+
_tag = "FileMigrationLedgerError";
|
|
83
|
+
constructor(e, t, n) {
|
|
84
|
+
super(H(n) ? `migration ${e}: up() SUCCEEDED here, and ANOTHER process had already recorded the same migration.\n The body has run TWICE. The ledger row is correct — do not delete it; the next run will not apply ${t} again.\n Two processes applied it without being serialised by the migration lock: check that both reach the same database and lock schema.\n Cause: ${n instanceof Error ? n.message : String(n)}` : `migration ${e}: up() SUCCEEDED but the ledger row could not be written.\n The change IS in the database. It is NOT recorded, so the next run would apply it AGAIN.\n Do not re-run ${t} until this is resolved.\n Cause: ${n instanceof Error ? n.message : String(n)}`), this.migrationId = e, this.file = t, this.cause = n, this.name = "FileMigrationLedgerError";
|
|
85
|
+
}
|
|
86
|
+
}, W = /* @__PURE__ */ new Set([
|
|
87
|
+
"mysql",
|
|
88
|
+
"mariadb",
|
|
89
|
+
"mssql"
|
|
90
|
+
]), G = (e, t) => W.has(e) ? t.filter((e) => e.length > 64) : [], K = class extends Error {
|
|
91
|
+
dialect;
|
|
92
|
+
migrationIds;
|
|
93
|
+
_tag = "MigrationIdTooLongError";
|
|
94
|
+
constructor(e, t) {
|
|
95
|
+
super(`migration id(s) longer than 64 characters cannot be recorded on ${e}: ` + t.map((e) => `${e} (${e.length})`).join(", ") + ". Nothing was applied. Rename the migration file and its `id` to at most 64 characters."), this.dialect = e, this.migrationIds = t, this.name = "MigrationIdTooLongError";
|
|
96
|
+
}
|
|
97
|
+
}, q = (e, t, n) => S.gen(function* () {
|
|
98
|
+
let r = yield* h(e, n.appliedAtIso);
|
|
99
|
+
yield* e`
|
|
100
|
+
INSERT INTO ${e("_voltro_migration_plans")}
|
|
101
|
+
(${e("id")}, ${e("fingerprint")}, ${e("operations")},
|
|
102
|
+
${e("appliedBy")}, ${e("environment")}, ${e("source")},
|
|
103
|
+
${e("durationMs")}, ${e("appliedAt")}, ${e("notes")})
|
|
104
|
+
VALUES
|
|
105
|
+
(${t.migration.id}, ${t.hash}, ${JSON.stringify([{
|
|
106
|
+
kind: "file",
|
|
107
|
+
file: t.file,
|
|
108
|
+
phase: t.migration.phase
|
|
109
|
+
}])},
|
|
110
|
+
${n.appliedBy}, ${n.env}, ${"file"},
|
|
111
|
+
${n.durationMs}, ${r}, ${n.notes})
|
|
112
|
+
`.pipe(S.mapError((e) => new U(t.migration.id, t.file, e)));
|
|
113
|
+
}), J = (t) => {
|
|
114
|
+
for (let n of e(t)) if (n instanceof s) return n.reason;
|
|
115
|
+
}, Y = (e, t, n) => S.gen(function* () {
|
|
116
|
+
let r = x(t), i = r.ctx, a = e.migration.up(i), s = !S.isEffect(a), c = yield* (S.isEffect(a) ? a : S.tryPromise({
|
|
117
|
+
try: () => a,
|
|
118
|
+
catch: (e) => e
|
|
119
|
+
})).pipe(S.as(void 0), S.catch((e) => {
|
|
120
|
+
let t = J(e);
|
|
121
|
+
return t === void 0 ? S.fail(e) : S.succeed(t);
|
|
122
|
+
}));
|
|
123
|
+
if (c === void 0) {
|
|
124
|
+
let t = yield* y(n);
|
|
125
|
+
if (t.length > 0) return yield* S.fail(new o(e.migration.id, t));
|
|
126
|
+
}
|
|
127
|
+
return {
|
|
128
|
+
skipped: c,
|
|
129
|
+
promiseForm: s,
|
|
130
|
+
absent: r.absent()
|
|
131
|
+
};
|
|
132
|
+
}), X = (e, t, n, i, a, o, s, l) => S.gen(function* () {
|
|
133
|
+
let u = Date.now(), d = (/* @__PURE__ */ new Date()).toISOString(), m = p(e), h = `migrations:file:${n.migration.id}`, _ = (e) => ({
|
|
134
|
+
durationMs: Date.now() - u,
|
|
135
|
+
notes: e.skipped === void 0 ? n.migration.description : `skipped: ${e.skipped}`
|
|
136
|
+
});
|
|
137
|
+
if (n.migration.transactional !== !1 && !o) {
|
|
138
|
+
if (t.run === void 0) return yield* S.fail(/* @__PURE__ */ Error(`migration ${n.migration.id}: transactional: true needs a SQL-backed store`));
|
|
139
|
+
let o = m === "mysql" || m === "mariadb", u = `probe_${n.migration.id}_${Date.now().toString(36)}`;
|
|
140
|
+
o && (yield* g(e, m, s));
|
|
141
|
+
let p = (e) => e.executeUnprepared(`SELECT COUNT(*) AS n FROM \`${f}\` WHERE \`runId\` = ?`, [u], void 0), v = () => (l === void 0 ? S.scoped(S.flatMap(e.reserve, p)) : p(l)).pipe(S.map((e) => Number(e[0]?.n ?? 0) > 0)), y = yield* S.tryPromise({
|
|
142
|
+
try: () => t.transactional(async (t) => {
|
|
143
|
+
o && await t.run(e`INSERT INTO ${e(f)} (${e("id")}, ${e("runId")}, ${e("planFingerprint")}, ${e("opIndex")}, ${e("status")}, ${e("opKind")}, ${e("opTarget")}, ${e("payload")})
|
|
144
|
+
VALUES (${r("mop", u)}, ${u}, ${""}, ${0}, ${"pending"}, ${"probe"}, ${""}, ${"{}"})`);
|
|
145
|
+
let { ctx: s, expectations: c } = b({
|
|
146
|
+
sql: e,
|
|
147
|
+
store: t,
|
|
148
|
+
dialect: m,
|
|
149
|
+
migrationId: n.migration.id,
|
|
150
|
+
appliedAt: d
|
|
151
|
+
}), l = await t.run(Y(n, s, c)), p = o && await S.runPromise(v().pipe(S.provideService(w.SqlClient, e))), { durationMs: h, notes: g } = _(l);
|
|
152
|
+
return await t.run(q(e, n, {
|
|
153
|
+
env: i,
|
|
154
|
+
appliedBy: a,
|
|
155
|
+
durationMs: h,
|
|
156
|
+
appliedAtIso: d,
|
|
157
|
+
notes: p ? `${g} — transactional guarantee broken by the body (DDL on ${m})` : g
|
|
158
|
+
})), o && await t.run(e`DELETE FROM ${e(f)} WHERE ${e("runId")} = ${u}`), {
|
|
159
|
+
...l,
|
|
160
|
+
durationMs: h,
|
|
161
|
+
broken: p
|
|
162
|
+
};
|
|
163
|
+
}),
|
|
164
|
+
catch: (e) => e
|
|
165
|
+
});
|
|
166
|
+
return y.broken ? yield* S.fail(new c(n.migration.id, m)) : (y.skipped !== void 0 && (yield* S.logInfo(`migration ${n.migration.id} skipped: ${y.skipped}`).pipe(S.annotateLogs({ scope: h }))), {
|
|
167
|
+
durationMs: y.durationMs,
|
|
168
|
+
skip: y.skipped === void 0 ? void 0 : {
|
|
169
|
+
id: n.migration.id,
|
|
170
|
+
reason: y.skipped,
|
|
171
|
+
absent: y.absent
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
let { ctx: v, expectations: y } = b({
|
|
176
|
+
sql: e,
|
|
177
|
+
store: t,
|
|
178
|
+
dialect: m,
|
|
179
|
+
migrationId: n.migration.id,
|
|
180
|
+
appliedAt: d
|
|
181
|
+
}), x = yield* Y(n, v, y), { durationMs: C, notes: T } = _(x);
|
|
182
|
+
return x.promiseForm && x.skipped === void 0 && C < 2 && (yield* S.logWarning(`migration ${n.migration.id}: up() resolved in ${C}ms — did the body \`await\` an \`effect/sql\` Effect by accident? \`sql.unsafe(...)\` returns an Effect, not a Promise; \`await\` on it is a silent no-op. Switch the body to \`Effect.gen(function* () { yield* sql.unsafe(...) })\` form.`).pipe(S.annotateLogs({ scope: h }))), x.skipped !== void 0 && (yield* S.logInfo(`migration ${n.migration.id} skipped: ${x.skipped}`).pipe(S.annotateLogs({ scope: h }))), yield* q(e, n, {
|
|
183
|
+
env: i,
|
|
184
|
+
appliedBy: a,
|
|
185
|
+
durationMs: C,
|
|
186
|
+
appliedAtIso: d,
|
|
187
|
+
notes: T
|
|
188
|
+
}), {
|
|
189
|
+
durationMs: C,
|
|
190
|
+
skip: x.skipped === void 0 ? void 0 : {
|
|
191
|
+
id: n.migration.id,
|
|
192
|
+
reason: x.skipped,
|
|
193
|
+
absent: x.absent
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
}), Z = (e, t, n, r) => S.gen(function* () {
|
|
197
|
+
let i = yield* S.tryPromise({
|
|
198
|
+
try: () => P(D(t, "migrations")),
|
|
199
|
+
catch: (e) => e
|
|
200
|
+
});
|
|
201
|
+
if (i.length === 0) return {
|
|
202
|
+
pending: [],
|
|
203
|
+
alreadyApplied: [],
|
|
204
|
+
drifted: []
|
|
205
|
+
};
|
|
206
|
+
let a = yield* S.tryPromise({
|
|
207
|
+
try: () => I(i, r),
|
|
208
|
+
catch: (e) => e
|
|
209
|
+
});
|
|
210
|
+
if (a.length === 0) return {
|
|
211
|
+
pending: [],
|
|
212
|
+
alreadyApplied: [],
|
|
213
|
+
drifted: []
|
|
214
|
+
};
|
|
215
|
+
let o = new Map((yield* z(e)).map((e) => [e.id, e.hash])), s = B(o, a), c = s.length >= 32 ? /* @__PURE__ */ new Map() : void 0;
|
|
216
|
+
if (c !== void 0) for (let e of a) c.has(e.migration.id) || c.set(e.migration.id, e);
|
|
217
|
+
for (let e of s) {
|
|
218
|
+
let t = c === void 0 ? a.find((t) => t.migration.id === e) : c.get(e), n = `${t?.file ?? e}:${t?.hash ?? ""}`;
|
|
219
|
+
V.has(n) || (V.add(n), k.warn("file migration: the body on disk is not the one that was applied — the file no longer describes this database", {
|
|
220
|
+
id: e,
|
|
221
|
+
file: t?.file
|
|
222
|
+
}));
|
|
223
|
+
}
|
|
224
|
+
let l = n === void 0 ? a : a.filter((e) => e.migration.phase === n);
|
|
225
|
+
return {
|
|
226
|
+
pending: l.filter((e) => !o.has(e.migration.id)),
|
|
227
|
+
alreadyApplied: l.filter((e) => o.has(e.migration.id)),
|
|
228
|
+
drifted: s
|
|
229
|
+
};
|
|
230
|
+
}), Q = (e, t, n, r) => Z(e, t, n, r).pipe(S.map((e) => e.pending.map((e) => e.migration.id))), ee = (e, t, n) => Z(e, t, n).pipe(S.map((e) => e.pending.map((e) => ({
|
|
231
|
+
id: e.migration.id,
|
|
232
|
+
phase: e.migration.phase,
|
|
233
|
+
irreversible: l(e.migration.down)
|
|
234
|
+
})))), te = (e, t) => Z(e, t).pipe(S.map((e) => {
|
|
235
|
+
let t = new Set(e.drifted);
|
|
236
|
+
return [...e.alreadyApplied.map((e) => ({
|
|
237
|
+
id: e.migration.id,
|
|
238
|
+
phase: e.migration.phase,
|
|
239
|
+
applied: !0,
|
|
240
|
+
bodyChanged: t.has(e.migration.id)
|
|
241
|
+
})), ...e.pending.map((e) => ({
|
|
242
|
+
id: e.migration.id,
|
|
243
|
+
phase: e.migration.phase,
|
|
244
|
+
applied: !1,
|
|
245
|
+
bodyChanged: !1
|
|
246
|
+
}))].sort((e, t) => e.id < t.id ? -1 : 1);
|
|
247
|
+
})), ne = (e, t) => S.gen(function* () {
|
|
248
|
+
let { pending: n, alreadyApplied: r } = yield* Z(e, t.projectRoot, t.phase), i = r.map((e) => e.migration.id);
|
|
249
|
+
if (n.length === 0) return {
|
|
250
|
+
applied: [],
|
|
251
|
+
skipped: i,
|
|
252
|
+
skips: []
|
|
253
|
+
};
|
|
254
|
+
let a = G(p(e), n.map((e) => e.migration.id));
|
|
255
|
+
if (a.length > 0) return yield* S.fail(new K(p(e), a));
|
|
256
|
+
yield* _(e, p(e), t.charset);
|
|
257
|
+
let o = yield* S.acquireUseRelease(d(e, { schema: t.lockSchema }), (r) => S.gen(function* () {
|
|
258
|
+
let i = new Set((yield* z(e)).map((e) => e.id)), a = n.filter((e) => !i.has(e.migration.id));
|
|
259
|
+
a.length < n.length && (yield* S.logInfo("file-based migrations applied by another process while this one waited for the migration lock").pipe(S.annotateLogs({
|
|
260
|
+
scope: "voltro:migrate",
|
|
261
|
+
ids: n.filter((e) => i.has(e.migration.id)).map((e) => e.migration.id).join(",")
|
|
262
|
+
})));
|
|
263
|
+
let o = [];
|
|
264
|
+
a.length > 0 && t.beforeExecute !== void 0 && (yield* t.beforeExecute);
|
|
265
|
+
let s = [];
|
|
266
|
+
for (let n of a) {
|
|
267
|
+
yield* S.logInfo("file-based migration applying").pipe(S.annotateLogs({
|
|
268
|
+
scope: "voltro:migrate",
|
|
269
|
+
id: n.migration.id,
|
|
270
|
+
file: n.file,
|
|
271
|
+
phase: n.migration.phase
|
|
272
|
+
}));
|
|
273
|
+
let { durationMs: i, skip: a } = yield* X(e, t.store, n, t.env, t.appliedBy, t.enclosed === !0, t.charset, r.connection);
|
|
274
|
+
o.push({
|
|
275
|
+
id: n.migration.id,
|
|
276
|
+
durationMs: i
|
|
277
|
+
}), a !== void 0 && s.push(a), yield* S.logInfo("file-based migration applied").pipe(S.annotateLogs({
|
|
278
|
+
scope: "voltro:migrate",
|
|
279
|
+
id: n.migration.id,
|
|
280
|
+
durationMs: i,
|
|
281
|
+
phase: n.migration.phase
|
|
282
|
+
}));
|
|
283
|
+
}
|
|
284
|
+
return {
|
|
285
|
+
done: o,
|
|
286
|
+
skips: s
|
|
287
|
+
};
|
|
288
|
+
}), (e) => e.release.pipe(S.orDie));
|
|
289
|
+
return {
|
|
290
|
+
applied: o.done,
|
|
291
|
+
skipped: i,
|
|
292
|
+
skips: o.skips
|
|
293
|
+
};
|
|
294
|
+
}), $ = (e, t) => e`
|
|
295
|
+
SELECT ${e("id")}
|
|
296
|
+
FROM ${e("_voltro_migration_plans")}
|
|
297
|
+
WHERE ${e("id")} = ${t} AND ${e("source")} = 'file'
|
|
298
|
+
`, re = (e, t) => S.gen(function* () {
|
|
299
|
+
if (!(yield* $(e, t.id))[0]) return yield* S.fail(/* @__PURE__ */ Error(`rollback: ${t.id} not found in _voltro_migration_plans (file source)`));
|
|
300
|
+
let n = yield* S.tryPromise({
|
|
301
|
+
try: () => P(D(t.projectRoot, "migrations")),
|
|
302
|
+
catch: (e) => e
|
|
303
|
+
}), r = (yield* S.tryPromise({
|
|
304
|
+
try: () => I(n),
|
|
305
|
+
catch: (e) => e
|
|
306
|
+
})).find((e) => e.migration.id === t.id);
|
|
307
|
+
if (!r) return yield* S.fail(/* @__PURE__ */ Error(`rollback: file for migration ${t.id} not found on disk under ${t.projectRoot}/migrations/`));
|
|
308
|
+
let i = l(r.migration.down);
|
|
309
|
+
return i === void 0 ? yield* S.acquireUseRelease(d(e, { schema: t.lockSchema }), () => S.gen(function* () {
|
|
310
|
+
if ((yield* $(e, t.id))[0] === void 0) return yield* S.fail(/* @__PURE__ */ Error(`rollback: ${t.id} was rolled back by another process while this one waited for the migration lock — nothing to do`));
|
|
311
|
+
let n = Date.now(), { ctx: i } = b({
|
|
312
|
+
sql: e,
|
|
313
|
+
store: t.store,
|
|
314
|
+
dialect: p(e),
|
|
315
|
+
migrationId: t.id,
|
|
316
|
+
appliedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
317
|
+
});
|
|
318
|
+
t.beforeExecute !== void 0 && (yield* t.beforeExecute);
|
|
319
|
+
let a = r.migration.down(i);
|
|
320
|
+
S.isEffect(a) ? yield* a : yield* S.tryPromise({
|
|
321
|
+
try: () => a,
|
|
322
|
+
catch: (e) => e
|
|
323
|
+
});
|
|
324
|
+
let o = Date.now() - n;
|
|
325
|
+
return yield* e`DELETE FROM ${e("_voltro_migration_plans")} WHERE ${e("id")} = ${t.id} AND ${e("source")} = 'file'`, {
|
|
326
|
+
id: t.id,
|
|
327
|
+
durationMs: o
|
|
328
|
+
};
|
|
329
|
+
}), (e) => e.release.pipe(S.orDie)) : yield* S.fail(new a(t.id, i));
|
|
330
|
+
});
|
|
331
|
+
//#endregion
|
|
332
|
+
export { i as FILE_MIGRATION_PATTERN, j as FRAMEWORK_MIGRATION_EDITS, U as FileMigrationLedgerError, K as MigrationIdTooLongError, M as acceptedBodyHashes, R as appliedFileMigrationIds, z as appliedFileMigrations, te as fileMigrationStatus, A as migrationBodyHash, Q as pendingFileMigrationIds, ee as pendingFileMigrationSummary, re as rollbackFileBasedMigration, ne as runFileBasedMigrations, G as unrecordableMigrationIds, P as walkMigrationFiles };
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { Effect as e } from "effect";
|
|
2
|
+
//#region src/migrations/fileSkipProbes.ts
|
|
3
|
+
var t = (e) => e.kind === "table" ? e.table : `${e.table}.${e.name}`, n = (e, n) => e.kind === n.kind && e.table === n.table && t(e) === t(n), r = (e) => JSON.stringify([
|
|
4
|
+
e.kind,
|
|
5
|
+
e.table,
|
|
6
|
+
e.kind === "table" ? null : e.name
|
|
7
|
+
]), i = (e) => {
|
|
8
|
+
if (Object.getPrototypeOf(e) !== Object.prototype) return !1;
|
|
9
|
+
let t = Object.getOwnPropertyDescriptor(e, "kind");
|
|
10
|
+
if (t === void 0 || !("value" in t) || typeof t.value != "string") return !1;
|
|
11
|
+
for (let n of ["table", ...t.value === "table" ? [] : ["name"]]) {
|
|
12
|
+
let t = Object.getOwnPropertyDescriptor(e, n);
|
|
13
|
+
if (t === void 0 || !("value" in t) || typeof t.value != "string") return !1;
|
|
14
|
+
}
|
|
15
|
+
return !0;
|
|
16
|
+
}, a = (t) => {
|
|
17
|
+
let i = [], a = /* @__PURE__ */ new Set(), o = !1, s = (e) => (t) => {
|
|
18
|
+
if (!t) {
|
|
19
|
+
if (o) i.some((t) => n(t, e)) || i.push(e);
|
|
20
|
+
else {
|
|
21
|
+
let t = r(e);
|
|
22
|
+
a.has(t) || (a.add(t), i.push(e));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return t;
|
|
26
|
+
}, c = (t, n) => (...r) => t(...r).pipe(e.map(s(n(...r)))), l = {
|
|
27
|
+
tableExists: c(t.schema.tableExists, (e) => ({
|
|
28
|
+
kind: "table",
|
|
29
|
+
table: e
|
|
30
|
+
})),
|
|
31
|
+
columnExists: c(t.schema.columnExists, (e, t) => ({
|
|
32
|
+
kind: "column",
|
|
33
|
+
table: e,
|
|
34
|
+
name: t
|
|
35
|
+
})),
|
|
36
|
+
indexExists: c(t.schema.indexExists, (e, t) => ({
|
|
37
|
+
kind: "index",
|
|
38
|
+
table: e,
|
|
39
|
+
name: t
|
|
40
|
+
})),
|
|
41
|
+
foreignKeyExists: c(t.schema.foreignKeyExists, (e, t) => ({
|
|
42
|
+
kind: "foreign-key",
|
|
43
|
+
table: e,
|
|
44
|
+
name: t
|
|
45
|
+
}))
|
|
46
|
+
};
|
|
47
|
+
return {
|
|
48
|
+
ctx: {
|
|
49
|
+
...t,
|
|
50
|
+
schema: {
|
|
51
|
+
...t.schema,
|
|
52
|
+
...l
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
absent: () => (o = !0, i.slice())
|
|
56
|
+
};
|
|
57
|
+
}, o = (e) => {
|
|
58
|
+
let t = [], n = /* @__PURE__ */ new Set(), i = (e) => {
|
|
59
|
+
let i = r(e);
|
|
60
|
+
n.has(i) || (n.add(i), t.push(e));
|
|
61
|
+
};
|
|
62
|
+
for (let t of e.operations) {
|
|
63
|
+
let e = t.op;
|
|
64
|
+
switch (e.kind) {
|
|
65
|
+
case "create-table":
|
|
66
|
+
i({
|
|
67
|
+
kind: "table",
|
|
68
|
+
table: e.table
|
|
69
|
+
});
|
|
70
|
+
for (let t of e.columns) i({
|
|
71
|
+
kind: "column",
|
|
72
|
+
table: e.table,
|
|
73
|
+
name: t.name
|
|
74
|
+
});
|
|
75
|
+
for (let t of e.indexes) i({
|
|
76
|
+
kind: "index",
|
|
77
|
+
table: e.table,
|
|
78
|
+
name: t.name
|
|
79
|
+
});
|
|
80
|
+
break;
|
|
81
|
+
case "add-column":
|
|
82
|
+
i({
|
|
83
|
+
kind: "column",
|
|
84
|
+
table: e.table,
|
|
85
|
+
name: e.column.name
|
|
86
|
+
});
|
|
87
|
+
break;
|
|
88
|
+
case "add-index":
|
|
89
|
+
i({
|
|
90
|
+
kind: "index",
|
|
91
|
+
table: e.table,
|
|
92
|
+
name: e.index.name
|
|
93
|
+
});
|
|
94
|
+
break;
|
|
95
|
+
case "add-foreign-key": i({
|
|
96
|
+
kind: "foreign-key",
|
|
97
|
+
table: e.table,
|
|
98
|
+
name: e.column
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return t;
|
|
103
|
+
}, s = (e, t) => {
|
|
104
|
+
let a = o(t);
|
|
105
|
+
if (a.length === 0) return [];
|
|
106
|
+
let s = a.length >= 32 ? new Set(a.map(r)) : void 0;
|
|
107
|
+
return e.flatMap((e) => {
|
|
108
|
+
let t = e.absent.filter((e) => s !== void 0 && i(e) ? s.has(r(e)) : a.some((t) => n(t, e)));
|
|
109
|
+
return t.length === 0 ? [] : [{
|
|
110
|
+
id: e.id,
|
|
111
|
+
reason: e.reason,
|
|
112
|
+
created: t
|
|
113
|
+
}];
|
|
114
|
+
});
|
|
115
|
+
};
|
|
116
|
+
//#endregion
|
|
117
|
+
export { t as describeProbe, s as permanentSkips, o as plannedCreations, a as withSchemaProbeRecorder };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { isSoftDropSnapshotName as e } from "./frameworkLiveTables.js";
|
|
2
|
+
import { canonicalIdColumn as t, comparableDefault as n, comparableIndex as r, comparableSpatial as i, isPgSequenceDefault as a, isSolePrimaryKeyId as o, normRef as s } from "./columnEquivalence.js";
|
|
3
|
+
import { createHash as c } from "node:crypto";
|
|
4
|
+
//#region src/migrations/fingerprint.ts
|
|
5
|
+
var l = (e) => {
|
|
6
|
+
if (e == null) return e;
|
|
7
|
+
if (Array.isArray(e)) return e.map(l);
|
|
8
|
+
if (typeof e != "object") return e;
|
|
9
|
+
let t = {};
|
|
10
|
+
for (let n of Object.keys(e).sort()) {
|
|
11
|
+
let r = e[n];
|
|
12
|
+
r !== void 0 && (t[n] = l(r));
|
|
13
|
+
}
|
|
14
|
+
return t;
|
|
15
|
+
}, u = (e) => {
|
|
16
|
+
if (!e.idScheme && !e.sensitive && !e.safe && !e.generatedAs && !e.jsonChecks && !e.narrowedFrom && !e.rawDdl && !e.charset) return e;
|
|
17
|
+
let { idScheme: t, sensitive: n, safe: r, generatedAs: i, jsonChecks: a, narrowedFrom: o, rawDdl: s, charset: c, ...l } = e;
|
|
18
|
+
return l;
|
|
19
|
+
}, d = (e) => {
|
|
20
|
+
let t = u((() => {
|
|
21
|
+
if (e.storageType === void 0) return e;
|
|
22
|
+
let { storageType: t, enumValues: n, enumName: r, arrayElement: i, ...a } = e;
|
|
23
|
+
return {
|
|
24
|
+
...a,
|
|
25
|
+
type: t
|
|
26
|
+
};
|
|
27
|
+
})()), { references: r, defaultValue: o, hasDefault: c, oneOf: l, spatial: d, enumValues: f, ...p } = t, m = t.type === "id" && a(t) ? null : n(t), h = s(t.references);
|
|
28
|
+
return {
|
|
29
|
+
...p,
|
|
30
|
+
hasDefault: m !== null,
|
|
31
|
+
...m === null ? {} : { defaultValue: m },
|
|
32
|
+
...h === null ? {} : { references: h },
|
|
33
|
+
...t.oneOf !== void 0 && t.oneOf.length > 0 ? { oneOf: [...t.oneOf].sort() } : {},
|
|
34
|
+
...t.spatial === void 0 ? {} : { spatial: i(t.spatial) },
|
|
35
|
+
...t.enumValues === void 0 ? {} : { enumValues: [...t.enumValues].sort() }
|
|
36
|
+
};
|
|
37
|
+
}, f = (n) => n.columns.filter((t) => !e(t.name)).map((e) => d(t(e, o(n, e)))).sort((e, t) => e.name.localeCompare(t.name)), p = (e) => [...e].map(r).sort((e, t) => e.name.localeCompare(t.name)), m = (t) => [...t].filter((t) => !e(t.name)).map((e) => ({
|
|
38
|
+
name: e.name,
|
|
39
|
+
columns: f(e),
|
|
40
|
+
indexes: p(e.indexes)
|
|
41
|
+
})).sort((e, t) => e.name.localeCompare(t.name)), h = (e) => {
|
|
42
|
+
let t = { tables: m(e.tables) }, n = JSON.stringify(l(t));
|
|
43
|
+
return c("sha256").update(n).digest("hex");
|
|
44
|
+
}, g = (e) => e.slice(0, 16);
|
|
45
|
+
//#endregion
|
|
46
|
+
export { h as fingerprintSchema, g as shortFingerprint };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region src/migrations/fkCycles.ts
|
|
2
|
+
var e = (e, t) => `${e}::${t}`, t = (t, n) => {
|
|
3
|
+
let r = /* @__PURE__ */ new Set(), i = /* @__PURE__ */ new Set(), a = /* @__PURE__ */ new Set(), o = (t) => {
|
|
4
|
+
r.add(t);
|
|
5
|
+
let s = [...n(t)].filter((e) => e.target !== t).sort((e, t) => e.column.localeCompare(t.column));
|
|
6
|
+
for (let n of s) r.has(n.target) ? a.add(e(t, n.column)) : i.has(n.target) || o(n.target);
|
|
7
|
+
r.delete(t), i.add(t);
|
|
8
|
+
};
|
|
9
|
+
for (let e of [...t].sort()) i.has(e) || o(e);
|
|
10
|
+
return a;
|
|
11
|
+
};
|
|
12
|
+
//#endregion
|
|
13
|
+
export { t as findCyclicFkBackEdges, e as fkEdgeKey };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//#region src/migrations/frameworkLiveTables.ts
|
|
2
|
+
var e = /* @__PURE__ */ new Set(["voltro_isr_cache"]), t = (t) => e.has(t) || t.startsWith("_voltro_") || t.startsWith("_cloud_") || t.startsWith("cluster_") || t.startsWith("__turso_internal_"), n = "_voltro_staging_", r = (e) => e.startsWith(n), i = /__dropped_\d{14}$/, a = (e) => i.test(e), o = 7, s = (e) => {
|
|
3
|
+
let t = /__dropped_(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})$/.exec(e);
|
|
4
|
+
if (t === null) return;
|
|
5
|
+
let [, n, r, i, a, o, s] = t;
|
|
6
|
+
return new Date(Date.UTC(Number(n), Number(r) - 1, Number(i), Number(a), Number(o), Number(s)));
|
|
7
|
+
}, c = (e, t = {}) => {
|
|
8
|
+
let n = t.now ?? /* @__PURE__ */ new Date(), r = t.retentionDays ?? 7, i = [], o = (e, t) => {
|
|
9
|
+
let a = s(e);
|
|
10
|
+
if (a === void 0) return;
|
|
11
|
+
let o = Math.floor((n.getTime() - a.getTime()) / 864e5);
|
|
12
|
+
o >= r && i.push({
|
|
13
|
+
name: t,
|
|
14
|
+
droppedAt: a,
|
|
15
|
+
ageDays: o
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
for (let t of e.tables) if (o(t.name, t.name), !a(t.name)) for (let e of t.columns ?? []) o(e.name, `${t.name}.${e.name}`);
|
|
19
|
+
return i;
|
|
20
|
+
}, l = (e, t) => {
|
|
21
|
+
let n = e.reduce((e, t) => e.ageDays >= t.ageDays ? e : t), r = (/* @__PURE__ */ new Date(Date.now() - t * 864e5)).toISOString().slice(0, 10);
|
|
22
|
+
return `${e.length} soft-drop snapshot(s) older than ${t} day(s) still hold the dropped data (oldest: ${n.name}, ${n.ageDays}d) — ${e.map((e) => e.name).join(", ")}. Restore with \`voltro db restore-snapshot\` or reclaim with \`voltro db gc-snapshots --before ${r}\`; the window is \`database.softDropRetentionDays\` in app.config.`;
|
|
23
|
+
}, u = (e, n) => e.filter((e) => !t(e.name) || n.has(e.name));
|
|
24
|
+
//#endregion
|
|
25
|
+
export { o as DEFAULT_SOFT_DROP_RETENTION_DAYS, e as FRAMEWORK_LIVE_TABLES, n as STAGING_TABLE_PREFIX, c as agedSoftDropSnapshots, l as describeAgedSoftDropSnapshots, t as isFrameworkOwnedLiveTable, a as isSoftDropSnapshotName, r as isStagingTableName, u as plannableLiveTables, s as softDropSnapshotStamp };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
//#region src/migrations/idempotentDdl.ts
|
|
2
|
+
var e = (e) => {
|
|
3
|
+
let t = e;
|
|
4
|
+
for (let e = 0; e < 6 && typeof t == "object" && t; e++) {
|
|
5
|
+
let e = t;
|
|
6
|
+
if (typeof e.errno == "number") return e.errno;
|
|
7
|
+
t = e.cause;
|
|
8
|
+
}
|
|
9
|
+
}, t = (e) => {
|
|
10
|
+
let t = [], n = e;
|
|
11
|
+
for (let e = 0; e < 6 && typeof n == "object" && n; e++) {
|
|
12
|
+
let e = n;
|
|
13
|
+
typeof e.message == "string" && t.push(e.message), n = e.cause;
|
|
14
|
+
}
|
|
15
|
+
return t.join(" | ");
|
|
16
|
+
}, n = (n, r) => {
|
|
17
|
+
if (r.length === 0) return !1;
|
|
18
|
+
let i = e(n), a = t(n);
|
|
19
|
+
return r.some((e) => e.errno !== void 0 && i !== void 0 && e.errno.includes(i) || e.messageIncludes !== void 0 && e.messageIncludes.some((e) => a.includes(e)));
|
|
20
|
+
}, r = {
|
|
21
|
+
why: "mysql has no CREATE INDEX IF NOT EXISTS; 1061 is exactly that condition",
|
|
22
|
+
errno: [1061]
|
|
23
|
+
}, i = {
|
|
24
|
+
why: "mysql has no DROP COLUMN IF EXISTS; 1091 is exactly that condition",
|
|
25
|
+
errno: [1091]
|
|
26
|
+
}, a = {
|
|
27
|
+
why: "sqlite has no DROP COLUMN IF EXISTS, and reports the absent column only in the message",
|
|
28
|
+
messageIncludes: ["no such column"]
|
|
29
|
+
}, o = {
|
|
30
|
+
why: "mysql/mariadb DROP CONSTRAINT has no portable IF EXISTS; 1091 / 3940 are exactly \"already gone\"",
|
|
31
|
+
errno: [1091, 3940]
|
|
32
|
+
}, s = {
|
|
33
|
+
why: "mysql/mariadb ADD CONSTRAINT has no IF NOT EXISTS; 1826 is exactly \"already there\"",
|
|
34
|
+
errno: [1826]
|
|
35
|
+
};
|
|
36
|
+
//#endregion
|
|
37
|
+
export { i as MYSQL_COLUMN_OR_INDEX_ABSENT, o as MYSQL_CONSTRAINT_ABSENT, s as MYSQL_FOREIGN_KEY_EXISTS, r as MYSQL_INDEX_EXISTS, a as SQLITE_COLUMN_ABSENT, n as isAlreadySatisfied };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
//#region src/migrations/indexPredicate.ts
|
|
2
|
+
var e = /'(?:''|[^'])*'|"(?:""|[^"])*"|\[(?:\]\]|[^\]])*\]|`(?:``|[^`])*`|[A-Za-z_][A-Za-z_0-9$]*|\d+(?:\.\d+)?|::|<>|!=|<=|>=|\|\||[^\s]/g, t = (t) => {
|
|
3
|
+
let n = 0;
|
|
4
|
+
for (let r of t.matchAll(e)) if (r[0] === "(" && n++, r[0] === ")" && n--, n === 0 && r[0].toLowerCase() === "where") return t.slice(r.index + r[0].length).trim().replace(/;$/, "").trim();
|
|
5
|
+
}, n = (t) => {
|
|
6
|
+
if (t === void 0) return;
|
|
7
|
+
let n = t.match(e) ?? [], r = "(?:'(?:''|[^'])*'(?:\\s*::\\s*text)?|[0-9]+)", i = RegExp(`^\\s*${r}(?:\\s*,\\s*${r})*\\s*$`, "i");
|
|
8
|
+
for (let t = 0; t < n.length - 5; t++) {
|
|
9
|
+
let r = n[t + 4];
|
|
10
|
+
n[t] === "=" && n[t + 1]?.toLowerCase() === "any" && n[t + 2] === "(" && n[t + 3]?.toLowerCase() === "array" && r.startsWith("[") && r.endsWith("]") && n[t + 5] === ")" && i.test(r.slice(1, -1)) && n.splice(t, 6, "in", "(", ...r.slice(1, -1).match(e) ?? [], ")");
|
|
11
|
+
}
|
|
12
|
+
let a = n.filter((e, t) => !(e === "::" && n[t - 1]?.startsWith("'") && n[t + 1]?.toLowerCase() === "text" || e.toLowerCase() === "text" && n[t - 1] === "::" && n[t - 2]?.startsWith("'"))).map((e) => {
|
|
13
|
+
if (e.startsWith("'")) return e;
|
|
14
|
+
if (e.startsWith("\"") || e.startsWith("[") || e.startsWith("`")) {
|
|
15
|
+
let t = e.slice(1, -1).replace(/""/g, "\"").replace(/\]\]/g, "]").replace(/``/g, "`");
|
|
16
|
+
return /^[a-z_][a-z_0-9$]*$/.test(t) ? t : JSON.stringify(t);
|
|
17
|
+
}
|
|
18
|
+
return e.toLowerCase();
|
|
19
|
+
}), o = (e) => {
|
|
20
|
+
let t = e, n = () => {
|
|
21
|
+
if (t[0] !== "(" || t.at(-1) !== ")") return !1;
|
|
22
|
+
let e = 0;
|
|
23
|
+
for (let n = 0; n < t.length - 1; n++) if (t[n] === "(" && e++, t[n] === ")" && e--, e === 0) return !1;
|
|
24
|
+
return !0;
|
|
25
|
+
};
|
|
26
|
+
for (; n();) t = t.slice(1, -1);
|
|
27
|
+
if (!t.includes("between")) for (let e of ["or", "and"]) {
|
|
28
|
+
let n = 0, r = [[]];
|
|
29
|
+
for (let i of t) i === "(" && n++, i === ")" && n--, n === 0 && i === e ? r.push([]) : r[r.length - 1].push(i);
|
|
30
|
+
if (r.length > 1) return [e, ...r.map(o)];
|
|
31
|
+
}
|
|
32
|
+
let r = [];
|
|
33
|
+
for (let e = 0; e < t.length; e++) t[e] === "(" && t[e + 2] === ")" && t[e + 1] !== "(" && t[e + 1] !== ")" && (e === 0 || [
|
|
34
|
+
"=",
|
|
35
|
+
"<>",
|
|
36
|
+
"!=",
|
|
37
|
+
"<",
|
|
38
|
+
">",
|
|
39
|
+
"<=",
|
|
40
|
+
">=",
|
|
41
|
+
"is",
|
|
42
|
+
"not",
|
|
43
|
+
"("
|
|
44
|
+
].includes(t[e - 1])) ? (r.push(t[e + 1]), e += 2) : r.push(t[e]);
|
|
45
|
+
return r;
|
|
46
|
+
};
|
|
47
|
+
return JSON.stringify(o(a));
|
|
48
|
+
};
|
|
49
|
+
//#endregion
|
|
50
|
+
export { t as extractIndexPredicate, n as normalizeIndexPredicate };
|