@voltro/database 0.85.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 +666 -0
- package/THIRD-PARTY-NOTICES.md +5 -33
- 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 +18 -7
- package/dist/index.js +81 -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 +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-CG2j7J3f.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 +27 -6
- package/dist/sql.js +41 -6905
- 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.js +6 -2
- package/dist/writeAttribution.js +16 -0
- package/dist/writeRecorder.js +37 -0
- package/package.json +3 -3
- package/dist/replaceInProgress-DC88Pdiy.js +0 -3735
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { encodeSqliteDecimal as e } from "../sqliteDecimal.js";
|
|
2
|
+
import { sqlError as t } from "../sqlError.js";
|
|
3
|
+
import { readSqliteTableSchema as n } from "./sqliteTableSchema.js";
|
|
4
|
+
import { Effect as r } from "effect";
|
|
5
|
+
//#region src/migrations/sqliteDecimalSource.ts
|
|
6
|
+
var i = (e) => `CASE WHEN typeof(${e}) IN ('text','blob') THEN hex(${e}) ELSE quote(${e}) END`, a = (a, o, c, l, u) => r.gen(function* () {
|
|
7
|
+
let d = (e) => r.fail(t(e));
|
|
8
|
+
if (c.length === 0 || new Set(c).size !== c.length || new Set(l.map((e) => e.name)).size !== l.length) return yield* d("Decimal source requires distinct key and conversion columns");
|
|
9
|
+
yield* n(a, o);
|
|
10
|
+
let f = yield* a.executeUnprepared(`PRAGMA main.table_xinfo(${s(o)})`, [], void 0);
|
|
11
|
+
if ([...c, ...l.map((e) => e.name)].some((e) => !f.some((t) => t.name === e))) return yield* d("Decimal source columns do not match the retained table");
|
|
12
|
+
yield* r.try({
|
|
13
|
+
try: () => l.forEach((t) => {
|
|
14
|
+
e("0", t.target), typeof t.source != "string" && e("0", t.source.shape);
|
|
15
|
+
}),
|
|
16
|
+
catch: (e) => t("Invalid decimal source or target shape", e)
|
|
17
|
+
});
|
|
18
|
+
let p = c.flatMap((e, t) => [`typeof(${s(e)}) AS ${s(`k${t}t`)}`, `${i(s(e))} AS ${s(`k${t}v`)}`]);
|
|
19
|
+
l.forEach((e, t) => {
|
|
20
|
+
p.push(`typeof(${s(e.name)}) AS ${s(`c${t}t`)}`), p.push(`${e.source === "numeric" ? `CASE WHEN ${s(e.name)} IS NULL THEN NULL ELSE quote(${s(e.name)}) END` : `CAST(${s(e.name)} AS TEXT)`} AS ${s(`c${t}v`)}`);
|
|
21
|
+
});
|
|
22
|
+
let m = c.flatMap((e, t) => [`k${t}t`, `k${t}v`]), h = m.map((e) => `${s(e)} COLLATE BINARY`).join(", "), g = `SELECT ${p.join(", ")} FROM main.${s(o)}`, _, v;
|
|
23
|
+
for (;;) {
|
|
24
|
+
let e = _ === void 0 ? 256 : 257, t = _ === void 0 ? "" : ` WHERE (${h}) >= (${m.map(() => "?").join(", ")})`, n = yield* a.executeUnprepared(`SELECT * FROM (${g}) AS observed${t} ORDER BY ${h} LIMIT ${e}`, _ ?? [], void 0);
|
|
25
|
+
if (_ !== void 0 && (n.length === 0 || m.some((e, t) => n[0][e] !== _[t]))) return yield* d("Retained decimal source changed during paged observation");
|
|
26
|
+
let r = [];
|
|
27
|
+
for (let e of n.slice(_ === void 0 ? 0 : 1)) {
|
|
28
|
+
let t = JSON.stringify(c.map((t, n) => [
|
|
29
|
+
t,
|
|
30
|
+
e[`k${n}t`],
|
|
31
|
+
e[`k${n}v`]
|
|
32
|
+
]));
|
|
33
|
+
if (t === v) return yield* d("Retained source has ambiguous row identities");
|
|
34
|
+
v = t;
|
|
35
|
+
for (let [n, i] of l.entries()) {
|
|
36
|
+
let a = e[`c${n}t`], o = e[`c${n}v`] ?? null;
|
|
37
|
+
if (a !== "null" && !(i.source === "numeric" ? a === "integer" || a === "real" : a === "text")) return yield* d("Retained decimal storage class does not match its reviewed source kind");
|
|
38
|
+
r.push({
|
|
39
|
+
rowIdentity: t,
|
|
40
|
+
column: i.name,
|
|
41
|
+
target: i.target,
|
|
42
|
+
source: typeof i.source == "string" ? {
|
|
43
|
+
kind: i.source,
|
|
44
|
+
value: o
|
|
45
|
+
} : {
|
|
46
|
+
kind: "sortable-v1",
|
|
47
|
+
value: o,
|
|
48
|
+
shape: i.source.shape
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (r.length > 0 && (yield* u(r)), n.length < e) break;
|
|
54
|
+
let i = n[n.length - 1], o = [];
|
|
55
|
+
for (let e of m) {
|
|
56
|
+
let t = i[e];
|
|
57
|
+
if (typeof t != "string") return yield* d("Retained source key projection is not an exact SQL value");
|
|
58
|
+
o.push(t);
|
|
59
|
+
}
|
|
60
|
+
_ = o;
|
|
61
|
+
}
|
|
62
|
+
}), o = (e, t, n, i) => r.gen(function* () {
|
|
63
|
+
let o = [];
|
|
64
|
+
return yield* a(e, t, n, i, (e) => r.sync(() => {
|
|
65
|
+
for (let t of e) o.push(t);
|
|
66
|
+
})), o;
|
|
67
|
+
}), s = (e) => "\"" + e.replaceAll("\"", "\"\"") + "\"";
|
|
68
|
+
//#endregion
|
|
69
|
+
export { a as forEachSqliteDecimalSourcePage, o as readSqliteDecimalSource, i as sqliteDecimalKeyValueSql };
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { encodeSqliteDecimal as e } from "../sqliteDecimal.js";
|
|
2
|
+
import { sqliteDecimalStorageShape as t } from "../sqliteDecimalStorage.js";
|
|
3
|
+
import { sqlError as n } from "../sqlError.js";
|
|
4
|
+
import { assertSqliteForeignKeys as r } from "./sqliteForeignKeys.js";
|
|
5
|
+
import { runSqliteMigrationStatement as i } from "./sqliteMigrationBatch.js";
|
|
6
|
+
import { withMigrationTransaction as a } from "./transactionCause.js";
|
|
7
|
+
import { emitCreateTableSqlite as o } from "./applier.js";
|
|
8
|
+
import { assertSqliteTableSchema as s, sqliteTableSchemaMatchesSql as c } from "./sqliteTableSchema.js";
|
|
9
|
+
import { sqliteDecimalKeyValueSql as l } from "./sqliteDecimalSource.js";
|
|
10
|
+
import { restoreSqliteSequenceSql as u } from "./sqliteRowIdentity.js";
|
|
11
|
+
import { assertSqliteSnapshot as d } from "./sqliteSnapshotGuard.js";
|
|
12
|
+
import { readSqliteSchemaEpoch as f, sqliteSchemaEpochMatchesSql as p } from "./sqliteSchemaEpoch.js";
|
|
13
|
+
import { sqliteWitnessProtectionStatements as m } from "./sqliteWitnessProtection.js";
|
|
14
|
+
import { forEachSqliteDecimalReviewRow as h } from "./sqliteDecimalReviewStore.js";
|
|
15
|
+
import { prepareSqliteRetainedSource as g } from "./sqliteRetainedSource.js";
|
|
16
|
+
import { Effect as _, Option as v } from "effect";
|
|
17
|
+
import { SqlClient as y } from "effect/sql";
|
|
18
|
+
//#region src/migrations/sqliteDecimalStage.ts
|
|
19
|
+
var b = (b, T, E) => _.gen(function* () {
|
|
20
|
+
let y = (e) => _.fail(n(e)), D = T.decimalReview;
|
|
21
|
+
if (!D || D.columns.length === 0 || E.name !== T.source) return yield* y("Decimal stage requires complete reviewed cells and the matching target table");
|
|
22
|
+
let O = yield* _.try({
|
|
23
|
+
try: () => {
|
|
24
|
+
let n = E.columns.map((n) => {
|
|
25
|
+
let r = t(n), i = D.columns.find((e) => e.name === n.name);
|
|
26
|
+
if (r === void 0) {
|
|
27
|
+
if (i) throw TypeError("Reviewed decimal column has no encoded target");
|
|
28
|
+
return n;
|
|
29
|
+
}
|
|
30
|
+
if (!i || i.target.precision !== r.precision || i.target.scale !== r.scale) throw TypeError("Decimal target shape does not match the review");
|
|
31
|
+
if (!n.hasDefault || n.defaultValue === null) return n;
|
|
32
|
+
if (typeof n.defaultValue != "string") throw TypeError("Decimal target default requires an exact string");
|
|
33
|
+
return {
|
|
34
|
+
...n,
|
|
35
|
+
defaultValue: e(n.defaultValue, r)
|
|
36
|
+
};
|
|
37
|
+
});
|
|
38
|
+
if (D.columns.some((e) => !E.columns.some((t) => t.name === e.name))) throw TypeError("Reviewed column is absent from the target");
|
|
39
|
+
if (E.indexes.some((e) => e.expression || e.kind && e.kind !== "btree")) throw TypeError("Decimal stage cannot silently simplify an expression or specialized index");
|
|
40
|
+
return { columns: n };
|
|
41
|
+
},
|
|
42
|
+
catch: (e) => n("Invalid reviewed decimal target", e)
|
|
43
|
+
}), k = yield* g(b, T), A = k.reviewStore;
|
|
44
|
+
if (A === null) return yield* y("Decimal stage requires an owned review store");
|
|
45
|
+
let j = yield* _.scoped(_.gen(function* () {
|
|
46
|
+
let e = yield* b.reserve, t = yield* f(e);
|
|
47
|
+
return yield* d(e, k.copy, k.contents, k.rowIdentity), t;
|
|
48
|
+
})), M = `${k.marker}_stage`, N = `${k.marker}_stage_owner`, P = `${k.marker}_stage_guard`, F = E.indexes.filter((e) => e.name !== `${E.name}_pkey`).map((e, t) => ({
|
|
49
|
+
...e,
|
|
50
|
+
table: M,
|
|
51
|
+
name: `${M}_index_${t}`
|
|
52
|
+
})), I = o({
|
|
53
|
+
kind: "create-table",
|
|
54
|
+
table: M,
|
|
55
|
+
columns: O.columns,
|
|
56
|
+
indexes: F,
|
|
57
|
+
...E.primaryKey ? { primaryKey: E.primaryKey } : {}
|
|
58
|
+
}).map((e) => e.replace(/ IF NOT EXISTS /, " ")), L = O.columns.filter((e) => !e.generatedAs), R = k.rowIdentity.alias;
|
|
59
|
+
if (R !== null && O.columns.some((e) => e.name.toLowerCase() === R)) return yield* y("Target shadows the retained rowid alias");
|
|
60
|
+
if (k.rowIdentity.autoincrement && !O.columns.some((e) => e.idScheme?.kind === "numeric")) return yield* y("Target must preserve source AUTOINCREMENT allocation");
|
|
61
|
+
let z = (e, { identity: t, values: n }) => {
|
|
62
|
+
let r = JSON.parse(t), i = (e, t) => w(r.map(([r, i, a]) => {
|
|
63
|
+
let o = `${e}.${x(r)}`;
|
|
64
|
+
return t && n.has(r) ? `${o} COLLATE BINARY IS ${C(n.get(r))}` : `typeof(${o}) = ${S(i)} AND ${l(o)} COLLATE BINARY = ${S(a)}`;
|
|
65
|
+
})), a = i("c", !1);
|
|
66
|
+
if (e === "insert") return `INSERT INTO ${x(M)} (${[...R === null ? [] : [x(R)], ...L.map((e) => x(e.name))].join(", ")}) SELECT ${R === null ? "" : `c.${x(R)}, `}${L.map((e) => n.has(e.name) ? C(n.get(e.name)) : `c.${x(e.name)}`).join(", ")} FROM main.${x(k.copy)} c WHERE ${a}`;
|
|
67
|
+
let o = O.columns.map((e) => {
|
|
68
|
+
let t = x(e.name);
|
|
69
|
+
return n.has(e.name) ? `s.${t} COLLATE BINARY IS ${C(n.get(e.name))}` : `(typeof(s.${t}) = typeof(c.${t}) AND s.${t} COLLATE BINARY IS c.${t} COLLATE BINARY)`;
|
|
70
|
+
});
|
|
71
|
+
return R !== null && o.push(`s.${x(R)} IS c.${x(R)}`), `(SELECT COUNT(*) FROM main.${x(M)} s, main.${x(k.copy)} c WHERE ${a} AND ${i("s", !0)} AND ${w(o)}) = 1`;
|
|
72
|
+
}, B = (e) => _.gen(function* () {
|
|
73
|
+
let t = [], n = 0, r = (t) => _.gen(function* () {
|
|
74
|
+
if ((yield* e.executeUnprepared(`SELECT 1 WHERE NOT (${t})`, [], void 0)).length > 0) return yield* y("Decimal stage rows no longer match the reviewed conversion");
|
|
75
|
+
});
|
|
76
|
+
yield* h(A, (e) => _.gen(function* () {
|
|
77
|
+
t.push(z("match", e)), n++, t.length === 32 && (yield* r(w(t)), t = []);
|
|
78
|
+
})), t.length > 0 && (yield* r(w(t))), yield* r(w([`(SELECT COUNT(*) FROM main.${x(M)}) = ${n}`, c(k.copy, k.witnessSchema)]));
|
|
79
|
+
}), V = JSON.stringify({
|
|
80
|
+
version: 2,
|
|
81
|
+
sourceOwner: k.marker,
|
|
82
|
+
target: E,
|
|
83
|
+
review: A.witness
|
|
84
|
+
}), H = `CREATE TABLE ${x(N)} (manifest TEXT NOT NULL, schema TEXT NOT NULL)`, U = yield* b.unsafe("SELECT sql FROM main.sqlite_master WHERE name = ? COLLATE NOCASE", [N]);
|
|
85
|
+
if (U.length === 0) yield* a(b, _.gen(function* () {
|
|
86
|
+
let e = yield* _.serviceOption(b.transactionService);
|
|
87
|
+
if (v.isNone(e)) return yield* y("Decimal stage requires a pinned transaction connection");
|
|
88
|
+
let t = e.value[0];
|
|
89
|
+
yield* i(t, `CREATE TABLE ${x(P)} (valid INTEGER CHECK(valid = 1))`), yield* i(t, `INSERT INTO ${x(P)} SELECT CASE WHEN ${p(j, 1)} THEN 1 ELSE 0 END`), yield* i(t, H);
|
|
90
|
+
for (let e of I) yield* i(t, e);
|
|
91
|
+
yield* h(A, (e) => i(t, z("insert", e)));
|
|
92
|
+
for (let e of u(M, k.rowIdentity)) yield* i(t, e);
|
|
93
|
+
yield* B(t), yield* i(t, `DROP TABLE ${x(P)}`);
|
|
94
|
+
for (let e of m(M, N)) yield* i(t, e);
|
|
95
|
+
if (yield* i(t, `INSERT INTO ${x(N)} SELECT ${S(V)}, json_group_array(json_object('type',type,'name',name,'sql',sql)) FROM (SELECT type,name,sql FROM main.sqlite_master WHERE tbl_name = ${S(M)} AND type IN ('table','index','trigger') ORDER BY type COLLATE BINARY,name COLLATE BINARY)`), (yield* t.executeUnprepared("PRAGMA foreign_key_check", [], void 0)).length > 0) return yield* y("Decimal stage would leave invalid foreign key references");
|
|
96
|
+
yield* r(t);
|
|
97
|
+
}));
|
|
98
|
+
else {
|
|
99
|
+
if (U.length !== 1 || U[0]?.sql !== H) return yield* y("Decimal stage marker is not owned by this conversion");
|
|
100
|
+
let e = yield* b.unsafe(`SELECT manifest FROM main.${x(N)}`);
|
|
101
|
+
if (e.length !== 1 || e[0]?.manifest !== V) return yield* y("Decimal stage identity does not match the reviewed conversion");
|
|
102
|
+
}
|
|
103
|
+
let W = yield* b.unsafe(`SELECT schema FROM main.${x(N)}`), G = yield* _.try({
|
|
104
|
+
try: () => ({ objects: JSON.parse(W[0].schema) }),
|
|
105
|
+
catch: (e) => n("Invalid retained decimal stage schema witness", e)
|
|
106
|
+
}), K = yield* a(b, _.gen(function* () {
|
|
107
|
+
let e = yield* _.serviceOption(b.transactionService);
|
|
108
|
+
if (v.isNone(e)) return yield* y("Decimal stage validation requires a pinned transaction");
|
|
109
|
+
let t = e.value[0], n = yield* f(t);
|
|
110
|
+
return yield* d(t, k.copy, k.contents, k.rowIdentity), yield* B(t), yield* s(t, M, G), yield* s(t, T.source, T.reviewedSchema), n;
|
|
111
|
+
}));
|
|
112
|
+
return {
|
|
113
|
+
stage: M,
|
|
114
|
+
marker: N,
|
|
115
|
+
sourceCopy: k.copy,
|
|
116
|
+
sourceMarker: k.marker,
|
|
117
|
+
sourceManifest: k.manifest,
|
|
118
|
+
sourceContents: k.contents,
|
|
119
|
+
sourceRowIdentity: k.rowIdentity,
|
|
120
|
+
manifest: V,
|
|
121
|
+
schema: G,
|
|
122
|
+
validationEpoch: K,
|
|
123
|
+
indexes: F.map((e) => e.name)
|
|
124
|
+
};
|
|
125
|
+
}).pipe(_.provideService(y.SqlClient, b)), x = (e) => "\"" + e.replaceAll("\"", "\"\"") + "\"", S = (e) => "'" + e.replaceAll("'", "''") + "'", C = (e) => e === null ? "NULL" : S(e), w = (e) => {
|
|
126
|
+
let t = (n, r) => {
|
|
127
|
+
if (n === r) return "1";
|
|
128
|
+
if (r - n === 1) return `(${e[n]})`;
|
|
129
|
+
let i = n + Math.floor((r - n) / 2);
|
|
130
|
+
return `(${t(n, i)} AND ${t(i, r)})`;
|
|
131
|
+
};
|
|
132
|
+
return t(0, e.length);
|
|
133
|
+
};
|
|
134
|
+
//#endregion
|
|
135
|
+
export { b as prepareSqliteDecimalStage };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { sqlError as e } from "../sqlError.js";
|
|
2
|
+
import { Effect as t } from "effect";
|
|
3
|
+
//#region src/migrations/sqliteForeignKeys.ts
|
|
4
|
+
var n = (e) => "\"" + e.replaceAll("\"", "\"\"") + "\"", r = (e) => e.replace(/[A-Z]/g, (e) => e.toLowerCase()), i = (i) => t.gen(function* () {
|
|
5
|
+
let a = (e) => i.executeUnprepared(e, [], void 0), o = () => t.fail(e("SQLite schema rewrite would leave invalid foreign key references")), s = yield* a("SELECT name FROM main.sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'"), c = /* @__PURE__ */ new Map(), l = /* @__PURE__ */ new Map();
|
|
6
|
+
for (let { name: e } of s) {
|
|
7
|
+
let t = yield* a(`PRAGMA main.foreign_key_list(${n(e)})`), i = /* @__PURE__ */ new Map();
|
|
8
|
+
for (let e of t) {
|
|
9
|
+
let t = i.get(e.id);
|
|
10
|
+
t === void 0 ? i.set(e.id, [e]) : t.push(e);
|
|
11
|
+
}
|
|
12
|
+
for (let t of i.values()) {
|
|
13
|
+
let i = t.toSorted((e, t) => e.seq - t.seq), s = i[0].table, u = r(s), d = c.get(u);
|
|
14
|
+
if (d === void 0) {
|
|
15
|
+
let e = yield* a(`PRAGMA main.table_info(${n(s)})`);
|
|
16
|
+
d = {
|
|
17
|
+
columns: e,
|
|
18
|
+
primary: e.filter((e) => e.pk > 0).toSorted((e, t) => e.pk - t.pk).map((e) => e.name)
|
|
19
|
+
}, c.set(u, d);
|
|
20
|
+
}
|
|
21
|
+
let { columns: f, primary: p } = d, m = i.map((e, t) => e.to ?? p[t]);
|
|
22
|
+
if (i.some((e) => e.to === null) && p.length !== i.length || m.some((e) => e === void 0 || !f.some((t) => r(t.name) === r(e)))) return yield* o();
|
|
23
|
+
let h = m.filter((e) => e !== void 0), g = (e) => e.length === h.length && e.every((e) => h.some((t) => r(t) === r(e))), _ = g(p);
|
|
24
|
+
!_ && d.indexes === void 0 && (d.indexes = yield* a(`PRAGMA main.index_list(${n(s)})`));
|
|
25
|
+
let v = _ ? [] : d.indexes;
|
|
26
|
+
for (let e of v) {
|
|
27
|
+
if (_ || e.unique !== 1 || e.partial !== 0) continue;
|
|
28
|
+
let t = r(e.name), i = l.get(t);
|
|
29
|
+
i === void 0 && (i = yield* a(`PRAGMA main.index_info(${n(e.name)})`), l.set(t, i)), _ = i.every((e) => e.name !== null) && g(i.map((e) => e.name));
|
|
30
|
+
}
|
|
31
|
+
if (!_) return yield* o();
|
|
32
|
+
let y = i.map((e) => `child.${n(e.from)} IS NOT NULL`).join(" AND "), b = i.map((e, t) => `parent.${n(h[t])} = child.${n(e.from)}`).join(" AND ");
|
|
33
|
+
if ((yield* a(`SELECT 1 FROM main.${n(e)} AS child WHERE ${y} AND NOT EXISTS (SELECT 1 FROM main.${n(s)} AS parent WHERE ${b}) LIMIT 1`)).length > 0) return yield* o();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
//#endregion
|
|
38
|
+
export { i as assertSqliteForeignKeys };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { sqlError as e } from "../sqlError.js";
|
|
2
|
+
import { dialectOf as t } from "./resumeLedger.js";
|
|
3
|
+
import { assertSqliteForeignKeys as n } from "./sqliteForeignKeys.js";
|
|
4
|
+
import { Effect as r, Option as i } from "effect";
|
|
5
|
+
import { randomUUID as a } from "node:crypto";
|
|
6
|
+
//#region src/migrations/sqliteMigrationBatch.ts
|
|
7
|
+
var o = (t, n) => r.try({
|
|
8
|
+
try: () => t.executeUnprepared(n, [], void 0),
|
|
9
|
+
catch: (t) => e("Failed to prepare SQLite migration statement", t)
|
|
10
|
+
}).pipe(r.flatten, r.asVoid), s = /* @__PURE__ */ new WeakMap(), c = (e, t) => {
|
|
11
|
+
s.set(e, t);
|
|
12
|
+
}, l = (e) => {
|
|
13
|
+
let t = "\"__voltro_migration_guard_" + a().replaceAll("-", "") + "\"";
|
|
14
|
+
return [
|
|
15
|
+
...e,
|
|
16
|
+
`CREATE TABLE ${t} (valid INTEGER CHECK (valid = 1))`,
|
|
17
|
+
`INSERT INTO ${t} SELECT 0 FROM pragma_foreign_key_check`,
|
|
18
|
+
`DROP TABLE ${t}`
|
|
19
|
+
];
|
|
20
|
+
}, u = (a, c) => r.gen(function* () {
|
|
21
|
+
if (c.length === 0) return;
|
|
22
|
+
let l = t(a);
|
|
23
|
+
if (l !== "sqlite" && l !== "turso") return yield* r.fail(e("SQLite migration batches require a SQLite-family client"));
|
|
24
|
+
if (i.isSome(yield* r.serviceOption(a.transactionService))) return yield* r.fail(e("SQLite schema rewrites must run outside an enclosing transaction"));
|
|
25
|
+
let u = s.get(a);
|
|
26
|
+
return u ? yield* u(c) : yield* r.scoped(r.gen(function* () {
|
|
27
|
+
let t = yield* a.reserve, i = (e) => o(t, e), s = i(`PRAGMA foreign_keys=${(yield* t.executeUnprepared("PRAGMA foreign_keys", [], void 0))[0]?.foreign_keys === 0 ? "OFF" : "ON"}`);
|
|
28
|
+
yield* r.acquireUseRelease(i("PRAGMA foreign_keys=OFF"), () => r.acquireUseRelease(i("BEGIN IMMEDIATE").pipe(r.as({ committed: !1 })), (a) => r.gen(function* () {
|
|
29
|
+
if (yield* r.forEach(c, i, { discard: !0 }), (yield* t.executeUnprepared("PRAGMA foreign_key_check", [], void 0)).length > 0) return yield* r.fail(e("SQLite schema rewrite would leave invalid foreign key references"));
|
|
30
|
+
yield* n(t), yield* r.uninterruptible(i("COMMIT").pipe(r.tap(() => r.sync(() => {
|
|
31
|
+
a.committed = !0;
|
|
32
|
+
}))));
|
|
33
|
+
}), (e) => e.committed ? r.void : i("ROLLBACK").pipe(r.orDie)), () => s.pipe(r.orDie));
|
|
34
|
+
}));
|
|
35
|
+
});
|
|
36
|
+
//#endregion
|
|
37
|
+
export { c as registerSqliteMigrationBatch, u as runSqliteMigrationBatch, o as runSqliteMigrationStatement, l as sqliteMigrationBatchStatements };
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { sqlError as e } from "../sqlError.js";
|
|
2
|
+
import { dialectOf as t } from "./resumeLedger.js";
|
|
3
|
+
import { runSqliteMigrationStatement as n } from "./sqliteMigrationBatch.js";
|
|
4
|
+
import { withMigrationTransaction as r } from "./transactionCause.js";
|
|
5
|
+
import { assertSqliteTableSchema as i, readSqliteTableSchema as a } from "./sqliteTableSchema.js";
|
|
6
|
+
import { assertSqliteRowIdentity as o, readSqliteRowIdentity as s } from "./sqliteRowIdentity.js";
|
|
7
|
+
import { assertSqliteTableContents as c, readSqliteTableContents as l } from "./sqliteTableContents.js";
|
|
8
|
+
import { sqliteDecimalReviewInputWitness as u } from "./sqliteDecimalReviewWitness.js";
|
|
9
|
+
import { sqliteWitnessProtectionStatements as d } from "./sqliteWitnessProtection.js";
|
|
10
|
+
import { importSqliteDecimalReview as f, reopenSqliteDecimalReview as p, verifySqliteDecimalReviewCoverage as m } from "./sqliteDecimalReviewStore.js";
|
|
11
|
+
import { assertSqliteTableCopy as h } from "./sqliteTableCopy.js";
|
|
12
|
+
import { Effect as g, Option as _ } from "effect";
|
|
13
|
+
import { createHash as v } from "node:crypto";
|
|
14
|
+
import { SqlClient as y } from "effect/sql";
|
|
15
|
+
//#region src/migrations/sqliteRetainedSource.ts
|
|
16
|
+
var b = (e) => {
|
|
17
|
+
let t = `_voltro_review_${v("sha256").update(JSON.stringify([e.migrationId, e.source])).digest("hex")}`;
|
|
18
|
+
return {
|
|
19
|
+
table: t,
|
|
20
|
+
marker: `${t}_owner`
|
|
21
|
+
};
|
|
22
|
+
}, x = (e) => {
|
|
23
|
+
let t = `_voltro_source_${v("sha256").update(JSON.stringify([e.migrationId, e.source])).digest("hex")}`;
|
|
24
|
+
return {
|
|
25
|
+
marker: t,
|
|
26
|
+
copy: `${t}_rows`
|
|
27
|
+
};
|
|
28
|
+
}, S = (v, S) => g.gen(function* () {
|
|
29
|
+
let y = (t) => g.fail(e(t));
|
|
30
|
+
return _.isSome(yield* g.serviceOption(v.transactionService)) ? yield* y("Retained source preparation must run outside an enclosing transaction") : ["sqlite", "turso"].includes(t(v)) ? yield* r(v, g.gen(function* () {
|
|
31
|
+
let t = yield* g.serviceOption(v.transactionService);
|
|
32
|
+
if (_.isNone(t)) return yield* y("Retained source preparation requires a pinned transaction connection");
|
|
33
|
+
let r = t.value[0];
|
|
34
|
+
if (!S.migrationId || !S.source || !S.targetIdentity) return yield* y("Retained source requires migration, source and target identities");
|
|
35
|
+
let { marker: T, copy: E } = x(S), D = `CREATE TABLE ${C(T)} (manifest TEXT NOT NULL, schema TEXT NOT NULL)`, O = yield* v.unsafe("SELECT sql FROM main.sqlite_master WHERE name = ? COLLATE NOCASE", [T]), k = null;
|
|
36
|
+
if (S.decimalReview !== void 0) {
|
|
37
|
+
let t = S.decimalReview, n = b(S), r = yield* g.try({
|
|
38
|
+
try: () => u(t.cells),
|
|
39
|
+
catch: (t) => e("Invalid explicit decimal conversion review", t)
|
|
40
|
+
});
|
|
41
|
+
if (O.length === 0) {
|
|
42
|
+
if (k = yield* f(n.table, T, t.cells), JSON.stringify(k.witness) !== JSON.stringify(r)) return yield* y("Decimal review input changed between reads; provide a repeatable canonical iterable");
|
|
43
|
+
} else k = yield* p(n.table, T, r);
|
|
44
|
+
}
|
|
45
|
+
let A = (e) => g.gen(function* () {
|
|
46
|
+
if (S.decimalReview === void 0 || k === null) return null;
|
|
47
|
+
let t = S.decimalReview;
|
|
48
|
+
return yield* m(k, e, t.keys, t.columns), k.witness;
|
|
49
|
+
}), j = yield* g.scoped(g.gen(function* () {
|
|
50
|
+
return yield* i(r, S.source, S.reviewedSchema), {
|
|
51
|
+
rowIdentity: yield* s(r, S.source),
|
|
52
|
+
decimalReview: yield* A(S.source),
|
|
53
|
+
contents: yield* l(r, S.source)
|
|
54
|
+
};
|
|
55
|
+
})), M = JSON.stringify({
|
|
56
|
+
version: 1,
|
|
57
|
+
migrationId: S.migrationId,
|
|
58
|
+
source: S.source,
|
|
59
|
+
targetIdentity: S.targetIdentity,
|
|
60
|
+
reviewedSchema: S.reviewedSchema,
|
|
61
|
+
...j,
|
|
62
|
+
copy: E,
|
|
63
|
+
...k === null ? {} : { reviewArtifact: {
|
|
64
|
+
table: k.table,
|
|
65
|
+
marker: k.marker,
|
|
66
|
+
manifest: k.manifest
|
|
67
|
+
} }
|
|
68
|
+
});
|
|
69
|
+
if (O.length > 0) {
|
|
70
|
+
if (O.length !== 1 || O[0]?.sql !== D) return yield* y("Retained source marker is not owned by this conversion");
|
|
71
|
+
let e = yield* v.unsafe(`SELECT manifest FROM main.${C(T)}`);
|
|
72
|
+
if (e.length !== 1 || e[0]?.manifest !== M) return yield* y("Retained source identity or reviewed schema does not match this conversion");
|
|
73
|
+
} else {
|
|
74
|
+
let e = [...j.rowIdentity.alias === null ? [] : [j.rowIdentity.alias], ...j.contents.columns].map(C).join(", ");
|
|
75
|
+
yield* g.forEach([
|
|
76
|
+
D,
|
|
77
|
+
`CREATE TABLE ${C(E)} AS SELECT * FROM main.${C(S.source)} WHERE 0`,
|
|
78
|
+
`INSERT INTO ${C(E)} (${e}) SELECT ${e} FROM main.${C(S.source)}`,
|
|
79
|
+
...d(E, T),
|
|
80
|
+
`INSERT INTO ${C(T)} SELECT ${w(M)}, json_group_array(json_object('type',type,'name',name,'sql',sql)) FROM (SELECT type,name,sql FROM main.sqlite_master WHERE tbl_name = ${w(E)} AND type IN ('table','index','trigger') ORDER BY type COLLATE BINARY,name COLLATE BINARY)`
|
|
81
|
+
], (e) => n(r, e), { discard: !0 });
|
|
82
|
+
}
|
|
83
|
+
let N = yield* v.unsafe(`SELECT schema FROM main.${C(T)}`), P = yield* g.try({
|
|
84
|
+
try: () => {
|
|
85
|
+
if (N.length !== 1) throw TypeError("Expected one retained schema");
|
|
86
|
+
let e = JSON.parse(N[0].schema);
|
|
87
|
+
if (!Array.isArray(e) || e.length !== 4 || e.some((e) => typeof e != "object" || !e || typeof e.type != "string" || typeof e.name != "string" || typeof e.sql != "string")) throw TypeError("Invalid retained witness schema");
|
|
88
|
+
return { objects: e };
|
|
89
|
+
},
|
|
90
|
+
catch: (t) => e("Retained witness protection metadata is invalid", t)
|
|
91
|
+
});
|
|
92
|
+
return yield* g.scoped(g.gen(function* () {
|
|
93
|
+
yield* i(r, E, P), yield* a(r, E), yield* h(r, S.source, E, S.reviewedSchema), yield* c(r, E, j.contents), yield* A(E), yield* o(r, E, j.rowIdentity);
|
|
94
|
+
})), {
|
|
95
|
+
copy: E,
|
|
96
|
+
marker: T,
|
|
97
|
+
manifest: M,
|
|
98
|
+
contents: j.contents,
|
|
99
|
+
rowIdentity: j.rowIdentity,
|
|
100
|
+
witnessSchema: P,
|
|
101
|
+
reviewStore: k
|
|
102
|
+
};
|
|
103
|
+
})) : yield* y("Retained sources require a SQLite-family client");
|
|
104
|
+
}).pipe(g.provideService(y.SqlClient, v)), C = (e) => "\"" + e.replaceAll("\"", "\"\"") + "\"", w = (e) => "'" + e.replaceAll("'", "''") + "'";
|
|
105
|
+
//#endregion
|
|
106
|
+
export { S as prepareSqliteRetainedSource, b as sqliteRetainedReviewNames, x as sqliteRetainedSourceNames };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { sqlError as e } from "../sqlError.js";
|
|
2
|
+
import { sqliteDecimalKeyValueSql as t } from "./sqliteDecimalSource.js";
|
|
3
|
+
import { Effect as n } from "effect";
|
|
4
|
+
import { createHash as r } from "node:crypto";
|
|
5
|
+
//#region src/migrations/sqliteRowIdentity.ts
|
|
6
|
+
var i = (e) => {
|
|
7
|
+
if (typeof e != "object" || !e) return !1;
|
|
8
|
+
let t = e;
|
|
9
|
+
return (t.alias === null || typeof t.alias == "string") && Array.isArray(t.columns) && t.columns.every((e) => typeof e == "string") && typeof t.rowCount == "number" && Number.isSafeInteger(t.rowCount) && t.rowCount >= 0 && typeof t.digest == "string" && /^[a-f0-9]{64}$/.test(t.digest) && (t.sequence === null || typeof t.sequence == "string" && /^\d+$/.test(t.sequence)) && typeof t.autoincrement == "boolean" && (t.sequenceBackend === "sqlite" || t.sequenceBackend === "turso");
|
|
10
|
+
}, a = (e) => [
|
|
11
|
+
"_rowid_",
|
|
12
|
+
"rowid",
|
|
13
|
+
"oid"
|
|
14
|
+
].find((t) => !e.some((e) => e.toLowerCase() === t)), o = (t, r) => n.gen(function* () {
|
|
15
|
+
let i = yield* t.executeUnprepared(`PRAGMA main.table_xinfo(${h(r)})`, [], void 0), o = (yield* t.executeUnprepared("PRAGMA main.table_list", [], void 0)).find((e) => e.name === r);
|
|
16
|
+
if (!o || o.wr !== 0 && o.wr !== 1) return yield* n.fail(e("Cannot verify SQLite row identity metadata"));
|
|
17
|
+
let s = i.map((e) => e.name), c = o.wr === 1 ? null : a(s);
|
|
18
|
+
if (c === void 0) return yield* n.fail(e("All SQLite rowid aliases are shadowed; cannot preserve implicit row identity"));
|
|
19
|
+
let { rowCount: u, digest: d } = yield* l(t, r, {
|
|
20
|
+
alias: c,
|
|
21
|
+
columns: s
|
|
22
|
+
}), f = yield* t.executeUnprepared("SELECT 1 FROM main.sqlite_master WHERE name='sqlite_sequence' AND type='table'", [], void 0), p = yield* t.executeUnprepared("SELECT 1 FROM main.sqlite_master WHERE name=? AND type='table'", [m(r)], void 0), g = ((yield* t.executeUnprepared("SELECT sql FROM main.sqlite_master WHERE name=? AND type='table'", [r], void 0))[0]?.sql ?? "").replace(/'(?:''|[^'])*'|"(?:""|[^"])*"|`(?:``|[^`])*`|\[[^\]]*\]|--[^\n]*|\/\*[\s\S]*?\*\//g, " "), _ = /\bAUTOINCREMENT\b/i.test(g), v = f.length === 0 ? [] : yield* t.executeUnprepared("SELECT CAST(seq AS TEXT) AS value FROM main.sqlite_sequence WHERE name=?", [r], void 0);
|
|
23
|
+
if (v.length > 1 || v.some((e) => !/^\d+$/.test(e.value))) return yield* n.fail(e("Invalid SQLite sequence watermark"));
|
|
24
|
+
let y = p.length === 0 ? [] : yield* t.executeUnprepared(`SELECT CAST(MAX(value) AS TEXT) AS value FROM main.${h(m(r))} WHERE is_called=1`, [], void 0);
|
|
25
|
+
return {
|
|
26
|
+
alias: c,
|
|
27
|
+
columns: s,
|
|
28
|
+
rowCount: u,
|
|
29
|
+
digest: d,
|
|
30
|
+
sequence: p.length > 0 ? y[0]?.value ?? null : v[0]?.value ?? null,
|
|
31
|
+
autoincrement: _,
|
|
32
|
+
sequenceBackend: p.length > 0 ? "turso" : "sqlite"
|
|
33
|
+
};
|
|
34
|
+
}), s = (e, t, r) => c(e, t, r).pipe(n.asVoid), c = (t, r, i) => n.gen(function* () {
|
|
35
|
+
let a = yield* l(t, r, i);
|
|
36
|
+
return a.rowCount !== i.rowCount || a.digest !== i.digest ? yield* n.fail(e("Retained physical row identity changed")) : a;
|
|
37
|
+
}), l = (e, t, i) => n.gen(function* () {
|
|
38
|
+
let n = r("sha256").update("["), a = 0, o;
|
|
39
|
+
if (i.alias !== null) {
|
|
40
|
+
let r = h(i.alias);
|
|
41
|
+
for (;;) {
|
|
42
|
+
let s = yield* e.executeUnprepared(`SELECT CAST(${r} AS TEXT) AS id, ${f(i.columns)} AS signature FROM main.${h(t)}
|
|
43
|
+
WHERE ${o === void 0 ? "1" : `${r} > CAST(? AS INTEGER)`} ORDER BY ${r} LIMIT 256`, o === void 0 ? [] : [o], void 0);
|
|
44
|
+
for (let e of s) a > 0 && n.update(","), n.update(JSON.stringify([e.id, e.signature])), a++;
|
|
45
|
+
if (s.length < 256) break;
|
|
46
|
+
o = s[s.length - 1].id;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
rowCount: a,
|
|
51
|
+
digest: n.update("]").digest("hex")
|
|
52
|
+
};
|
|
53
|
+
}), u = (e, t) => {
|
|
54
|
+
let n = [];
|
|
55
|
+
return t.autoincrement && (t.sequenceBackend === "turso" ? n.push(`(SELECT CAST(MAX(value) AS TEXT) FROM main.${h(m(e))} WHERE is_called=1) IS ${t.sequence === null ? "NULL" : g(t.sequence)}`) : t.sequence === null ? n.push(`NOT EXISTS (SELECT 1 FROM main.sqlite_sequence WHERE name=${g(e)})`) : n.push(`(SELECT COUNT(*) FROM main.sqlite_sequence WHERE name=${g(e)}) = 1 AND (SELECT CAST(seq AS TEXT) FROM main.sqlite_sequence WHERE name=${g(e)}) = ${g(t.sequence)}`)), n.length === 0 ? "1" : n.map((e) => `(${e})`).join(" AND ");
|
|
56
|
+
}, d = (e, t) => {
|
|
57
|
+
let n = t.sequence;
|
|
58
|
+
if (n === null) return [];
|
|
59
|
+
if (!/^\d+$/.test(n)) throw TypeError("Invalid SQLite sequence watermark");
|
|
60
|
+
return t.sequenceBackend === "turso" ? [`SELECT setval(${g(p(e))},${n})`] : [`UPDATE main.sqlite_sequence SET seq=${n} WHERE name=${g(e)}`, `INSERT INTO main.sqlite_sequence(name,seq) SELECT ${g(e)},${n} WHERE NOT EXISTS (SELECT 1 FROM main.sqlite_sequence WHERE name=${g(e)})`];
|
|
61
|
+
}, f = (e) => `CAST(json_array(${e.map((e) => `json_array(typeof(${h(e)}), ${t(h(e))})`).join(", ")}) AS TEXT)`, p = (e) => "__turso_internal_autoincrement_" + e, m = (e) => "__turso_internal_seq_" + p(e), h = (e) => "\"" + e.replaceAll("\"", "\"\"") + "\"", g = (e) => "'" + e.replaceAll("'", "''") + "'";
|
|
62
|
+
//#endregion
|
|
63
|
+
export { s as assertSqliteRowIdentity, i as isSqliteRowIdentity, o as readSqliteRowIdentity, d as restoreSqliteSequenceSql, a as sqliteRowidAlias, u as sqliteSequenceMatchesSql };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { sqlError as e } from "../sqlError.js";
|
|
2
|
+
import { Effect as t } from "effect";
|
|
3
|
+
//#region src/migrations/sqliteSchemaEpoch.ts
|
|
4
|
+
var n = (n) => t.gen(function* () {
|
|
5
|
+
let r = yield* n.executeUnprepared("PRAGMA main.schema_version", [], void 0), i = r[0]?.schema_version;
|
|
6
|
+
return r.length !== 1 || typeof i != "number" || !Number.isInteger(i) || i < -2147483648 || i > 2147483647 ? yield* t.fail(e("Cannot verify SQLite schema epoch")) : i;
|
|
7
|
+
}), r = (e, t) => {
|
|
8
|
+
if (!Number.isInteger(e) || e < -2147483648 || e > 2147483647 || !Number.isSafeInteger(t) || t < 0 || t > 2147483647) throw TypeError("Invalid SQLite schema epoch guard");
|
|
9
|
+
return `(SELECT schema_version FROM pragma_schema_version) = ${e + t | 0}`;
|
|
10
|
+
};
|
|
11
|
+
//#endregion
|
|
12
|
+
export { n as readSqliteSchemaEpoch, r as sqliteSchemaEpochMatchesSql };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { sqlError as e } from "../sqlError.js";
|
|
2
|
+
import { assertSqliteRowIdentity as t } from "./sqliteRowIdentity.js";
|
|
3
|
+
import { assertSqliteTableContents as n } from "./sqliteTableContents.js";
|
|
4
|
+
import { Effect as r } from "effect";
|
|
5
|
+
//#region src/migrations/sqliteSnapshotGuard.ts
|
|
6
|
+
var i = (e, i, o, s) => r.gen(function* () {
|
|
7
|
+
yield* a(o, s), yield* n(e, i, o), yield* t(e, i, s);
|
|
8
|
+
}), a = (t, n) => JSON.stringify(t.columns) === JSON.stringify(n.columns) ? r.void : r.fail(e("Retained content and physical identity columns differ"));
|
|
9
|
+
//#endregion
|
|
10
|
+
export { i as assertSqliteSnapshot };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { sqlError as e } from "../sqlError.js";
|
|
2
|
+
import { readSqliteTableSchema as t } from "./sqliteTableSchema.js";
|
|
3
|
+
import { sqliteDecimalKeyValueSql as n } from "./sqliteDecimalSource.js";
|
|
4
|
+
import { Effect as r } from "effect";
|
|
5
|
+
import { createHash as i } from "node:crypto";
|
|
6
|
+
//#region src/migrations/sqliteTableContents.ts
|
|
7
|
+
var a = 1024, o = (o, s) => r.gen(function* () {
|
|
8
|
+
yield* t(o, s);
|
|
9
|
+
let c = (yield* o.executeUnprepared(`PRAGMA main.table_xinfo(${l(s)})`, [], void 0)).map((e) => e.name);
|
|
10
|
+
if (c.length === 0) return yield* r.fail(e("Retained content witness requires declared columns"));
|
|
11
|
+
let u = `CAST(json_array(${c.map((e) => `json_array(typeof(${l(e)}), ${n(l(e))})`).join(", ")}) AS TEXT)`, d = i("sha256").update(`{"columns":${JSON.stringify(c)},"values":[`), f, p = !0;
|
|
12
|
+
for (;;) {
|
|
13
|
+
let t = yield* o.executeUnprepared(`SELECT ${u} AS signature, CAST(COUNT(*) AS TEXT) AS copies FROM main.${l(s)}
|
|
14
|
+
WHERE ${f === void 0 ? "1" : `${u} COLLATE BINARY > ?`}
|
|
15
|
+
GROUP BY ${u} COLLATE BINARY ORDER BY ${u} COLLATE BINARY LIMIT ${a}`, f === void 0 ? [] : [f], void 0);
|
|
16
|
+
for (let n of t) {
|
|
17
|
+
let t = Number(n.copies);
|
|
18
|
+
if (!Number.isSafeInteger(t) || t < 1) return yield* r.fail(e("Retained content witness has an invalid duplicate count"));
|
|
19
|
+
let i = JSON.stringify(n.signature);
|
|
20
|
+
for (let e = 0; e < t; e++) p || d.update(","), d.update(i), p = !1;
|
|
21
|
+
}
|
|
22
|
+
if (t.length < a) break;
|
|
23
|
+
f = t[t.length - 1].signature;
|
|
24
|
+
}
|
|
25
|
+
return { contents: {
|
|
26
|
+
columns: c,
|
|
27
|
+
digest: d.update("]}").digest("hex")
|
|
28
|
+
} };
|
|
29
|
+
}), s = (e, t) => o(e, t).pipe(r.map((e) => e.contents)), c = (t, n, i) => r.gen(function* () {
|
|
30
|
+
let a = yield* s(t, n);
|
|
31
|
+
if (a.digest !== i.digest || JSON.stringify(a.columns) !== JSON.stringify(i.columns)) return yield* r.fail(e("Retained source contents changed; refuse publication or rollback"));
|
|
32
|
+
}), l = (e) => "\"" + e.replaceAll("\"", "\"\"") + "\"";
|
|
33
|
+
//#endregion
|
|
34
|
+
export { c as assertSqliteTableContents, s as readSqliteTableContents };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { sqlError as e } from "../sqlError.js";
|
|
2
|
+
import { assertSqliteTableSchema as t } from "./sqliteTableSchema.js";
|
|
3
|
+
import { Effect as n } from "effect";
|
|
4
|
+
//#region src/migrations/sqliteTableCopy.ts
|
|
5
|
+
var r = (e) => "\"" + e.replaceAll("\"", "\"\"") + "\"", i = (e, t, n) => {
|
|
6
|
+
if (n.length === 0) throw TypeError("Table copy comparison requires columns");
|
|
7
|
+
let i = n.flatMap((e) => [`typeof(${r(e)})`, `${r(e)} COLLATE BINARY`]), a = (e) => `SELECT ${i.join(", ")}, COUNT(*) FROM main.${r(e)} GROUP BY ${i.join(", ")}`;
|
|
8
|
+
return `WITH source_rows AS (${a(e)}), copy_rows AS (${a(t)}),
|
|
9
|
+
missing_from_copy AS (SELECT * FROM source_rows EXCEPT SELECT * FROM copy_rows),
|
|
10
|
+
missing_from_source AS (SELECT * FROM copy_rows EXCEPT SELECT * FROM source_rows)
|
|
11
|
+
SELECT 1 AS changed WHERE EXISTS (SELECT * FROM missing_from_copy)
|
|
12
|
+
OR EXISTS (SELECT * FROM missing_from_source)`;
|
|
13
|
+
}, a = (a, o, s, c) => n.gen(function* () {
|
|
14
|
+
let l = (t) => n.fail(e(`Table copy verification failed: ${t}; retain the source and rebuild the conversion stage`));
|
|
15
|
+
if (o.toLowerCase() === s.toLowerCase()) return yield* l("source and copy must be distinct tables");
|
|
16
|
+
c !== void 0 && (yield* t(a, o, c));
|
|
17
|
+
let u = (e) => n.gen(function* () {
|
|
18
|
+
let t = yield* a.executeUnprepared("SELECT sql FROM main.sqlite_master WHERE type = ? AND name = ? COLLATE NOCASE", ["table", e], void 0);
|
|
19
|
+
return t.length !== 1 || !/^CREATE\s+TABLE\b/i.test(t[0]?.sql ?? "") ? yield* l("both inputs must be existing ordinary tables in main") : (yield* a.executeUnprepared(`PRAGMA main.table_xinfo(${r(e)})`, [], void 0)).map((e) => e.name);
|
|
20
|
+
}), d = yield* u(o), f = yield* u(s);
|
|
21
|
+
if (d.length === 0 || d.length !== f.length || d.some((e, t) => e !== f[t])) return yield* l("source and copy columns differ");
|
|
22
|
+
if ((yield* a.executeUnprepared(i(o, s, d), [], void 0)).length > 0) return yield* l("source rows changed or the retained copy is incomplete");
|
|
23
|
+
});
|
|
24
|
+
//#endregion
|
|
25
|
+
export { a as assertSqliteTableCopy, i as sqliteTableCopyDifferenceSql };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { sqlError as e } from "../sqlError.js";
|
|
2
|
+
import { Effect as t } from "effect";
|
|
3
|
+
//#region src/migrations/sqliteTableSchema.ts
|
|
4
|
+
var n = (e, t) => {
|
|
5
|
+
let n = `main.sqlite_master WHERE tbl_name = ${r(e)} COLLATE NOCASE AND type IN ('table', 'index', 'trigger')`;
|
|
6
|
+
return [`(SELECT COUNT(*) FROM ${n}) = ${t.objects.length}`, ...t.objects.map((e) => `EXISTS (SELECT 1 FROM ${n} AND type = ${r(e.type)} AND name = ${r(e.name)} AND sql IS ${e.sql === null ? "NULL" : r(e.sql)})`)].join(" AND ");
|
|
7
|
+
}, r = (e) => "'" + e.replaceAll("'", "''") + "'", i = (n, r) => t.gen(function* () {
|
|
8
|
+
let i = yield* n.executeUnprepared("SELECT type, name, sql FROM main.sqlite_master\nWHERE tbl_name = ? COLLATE NOCASE AND type IN ('table', 'index', 'trigger')\nORDER BY type COLLATE BINARY, name COLLATE BINARY", [r], void 0), a = i.filter((e) => e.type === "table");
|
|
9
|
+
return a.length !== 1 || !/^CREATE\s+TABLE\b/i.test(a[0]?.sql ?? "") ? yield* t.fail(e("Schema verification requires an existing ordinary table in main")) : { objects: i.map(({ type: e, name: t, sql: n }) => ({
|
|
10
|
+
type: e,
|
|
11
|
+
name: t,
|
|
12
|
+
sql: n
|
|
13
|
+
})) };
|
|
14
|
+
}), a = (n, r, a) => t.gen(function* () {
|
|
15
|
+
let o = yield* i(n, r);
|
|
16
|
+
if (JSON.stringify(o) !== JSON.stringify(a)) return yield* t.fail(e("Source table schema changed; retain the source and review the conversion stage again"));
|
|
17
|
+
});
|
|
18
|
+
//#endregion
|
|
19
|
+
export { a as assertSqliteTableSchema, i as readSqliteTableSchema, n as sqliteTableSchemaMatchesSql };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
//#region src/migrations/sqliteWitnessProtection.ts
|
|
2
|
+
var e = (e, t) => n.map((n) => `CREATE TRIGGER ${r(`${t}_protect_${n.toLowerCase()}`)} BEFORE ${n} ON ${r(e)} BEGIN SELECT RAISE(ABORT, 'Retained conversion witness is immutable'); END`), t = (e) => n.map((t) => `DROP TRIGGER ${r(`${e}_protect_${t.toLowerCase()}`)}`), n = [
|
|
3
|
+
"INSERT",
|
|
4
|
+
"UPDATE",
|
|
5
|
+
"DELETE"
|
|
6
|
+
], r = (e) => "\"" + e.replaceAll("\"", "\"\"") + "\"";
|
|
7
|
+
//#endregion
|
|
8
|
+
export { t as dropSqliteWitnessProtectionStatements, e as sqliteWitnessProtectionStatements };
|