@quereus/store 4.5.0 → 4.7.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/README.md +34 -6
- package/dist/src/common/encoding.d.ts +4 -3
- package/dist/src/common/encoding.d.ts.map +1 -1
- package/dist/src/common/encoding.js +17 -13
- package/dist/src/common/encoding.js.map +1 -1
- package/dist/src/common/events.d.ts +31 -25
- package/dist/src/common/events.d.ts.map +1 -1
- package/dist/src/common/events.js +38 -39
- package/dist/src/common/events.js.map +1 -1
- package/dist/src/common/implicit-unique-index.d.ts +80 -0
- package/dist/src/common/implicit-unique-index.d.ts.map +1 -0
- package/dist/src/common/implicit-unique-index.js +148 -0
- package/dist/src/common/implicit-unique-index.js.map +1 -0
- package/dist/src/common/index.d.ts +7 -3
- package/dist/src/common/index.d.ts.map +1 -1
- package/dist/src/common/index.js +5 -2
- package/dist/src/common/index.js.map +1 -1
- package/dist/src/common/json-key.d.ts +30 -2
- package/dist/src/common/json-key.d.ts.map +1 -1
- package/dist/src/common/json-key.js +49 -2
- package/dist/src/common/json-key.js.map +1 -1
- package/dist/src/common/key-builder.d.ts +40 -20
- package/dist/src/common/key-builder.d.ts.map +1 -1
- package/dist/src/common/key-builder.js +31 -23
- package/dist/src/common/key-builder.js.map +1 -1
- package/dist/src/common/kv-store.d.ts +10 -0
- package/dist/src/common/kv-store.d.ts.map +1 -1
- package/dist/src/common/pk-key-resolution.d.ts +348 -0
- package/dist/src/common/pk-key-resolution.d.ts.map +1 -0
- package/dist/src/common/pk-key-resolution.js +481 -0
- package/dist/src/common/pk-key-resolution.js.map +1 -0
- package/dist/src/common/store-module-access-plan.d.ts +32 -0
- package/dist/src/common/store-module-access-plan.d.ts.map +1 -0
- package/dist/src/common/store-module-access-plan.js +428 -0
- package/dist/src/common/store-module-access-plan.js.map +1 -0
- package/dist/src/common/store-module-alter-column.d.ts +23 -0
- package/dist/src/common/store-module-alter-column.d.ts.map +1 -0
- package/dist/src/common/store-module-alter-column.js +389 -0
- package/dist/src/common/store-module-alter-column.js.map +1 -0
- package/dist/src/common/store-module-alter.d.ts +55 -0
- package/dist/src/common/store-module-alter.d.ts.map +1 -0
- package/dist/src/common/store-module-alter.js +504 -0
- package/dist/src/common/store-module-alter.js.map +1 -0
- package/dist/src/common/store-module-base.d.ts +268 -0
- package/dist/src/common/store-module-base.d.ts.map +1 -0
- package/dist/src/common/store-module-base.js +362 -0
- package/dist/src/common/store-module-base.js.map +1 -0
- package/dist/src/common/store-module-catalog.d.ts +269 -0
- package/dist/src/common/store-module-catalog.d.ts.map +1 -0
- package/dist/src/common/store-module-catalog.js +462 -0
- package/dist/src/common/store-module-catalog.js.map +1 -0
- package/dist/src/common/store-module-index-build.d.ts +88 -0
- package/dist/src/common/store-module-index-build.d.ts.map +1 -0
- package/dist/src/common/store-module-index-build.js +280 -0
- package/dist/src/common/store-module-index-build.js.map +1 -0
- package/dist/src/common/store-module-index.d.ts +116 -0
- package/dist/src/common/store-module-index.d.ts.map +1 -0
- package/dist/src/common/store-module-index.js +373 -0
- package/dist/src/common/store-module-index.js.map +1 -0
- package/dist/src/common/store-module-rename.d.ts +49 -0
- package/dist/src/common/store-module-rename.d.ts.map +1 -0
- package/dist/src/common/store-module-rename.js +268 -0
- package/dist/src/common/store-module-rename.js.map +1 -0
- package/dist/src/common/store-module-schema-rewrite.d.ts +57 -0
- package/dist/src/common/store-module-schema-rewrite.d.ts.map +1 -0
- package/dist/src/common/store-module-schema-rewrite.js +140 -0
- package/dist/src/common/store-module-schema-rewrite.js.map +1 -0
- package/dist/src/common/store-module-schema-sync.d.ts +232 -0
- package/dist/src/common/store-module-schema-sync.d.ts.map +1 -0
- package/dist/src/common/store-module-schema-sync.js +529 -0
- package/dist/src/common/store-module-schema-sync.js.map +1 -0
- package/dist/src/common/store-module.d.ts +43 -909
- package/dist/src/common/store-module.d.ts.map +1 -1
- package/dist/src/common/store-module.js +184 -3276
- package/dist/src/common/store-module.js.map +1 -1
- package/dist/src/common/store-table-base.d.ts +539 -0
- package/dist/src/common/store-table-base.d.ts.map +1 -0
- package/dist/src/common/store-table-base.js +900 -0
- package/dist/src/common/store-table-base.js.map +1 -0
- package/dist/src/common/store-table-constraints.d.ts +237 -0
- package/dist/src/common/store-table-constraints.d.ts.map +1 -0
- package/dist/src/common/store-table-constraints.js +624 -0
- package/dist/src/common/store-table-constraints.js.map +1 -0
- package/dist/src/common/store-table-scan.d.ts +452 -0
- package/dist/src/common/store-table-scan.d.ts.map +1 -0
- package/dist/src/common/store-table-scan.js +901 -0
- package/dist/src/common/store-table-scan.js.map +1 -0
- package/dist/src/common/store-table.d.ts +71 -1144
- package/dist/src/common/store-table.d.ts.map +1 -1
- package/dist/src/common/store-table.js +168 -2385
- package/dist/src/common/store-table.js.map +1 -1
- package/dist/src/testing/index.d.ts +12 -0
- package/dist/src/testing/index.d.ts.map +1 -0
- package/dist/src/testing/index.js +12 -0
- package/dist/src/testing/index.js.map +1 -0
- package/dist/src/testing/kv-assert.d.ts +19 -0
- package/dist/src/testing/kv-assert.d.ts.map +1 -0
- package/dist/src/testing/kv-assert.js +25 -0
- package/dist/src/testing/kv-assert.js.map +1 -0
- package/dist/src/testing/kv-conformance.d.ts.map +1 -1
- package/dist/src/testing/kv-conformance.js +1 -15
- package/dist/src/testing/kv-conformance.js.map +1 -1
- package/dist/src/testing/kv-provider-conformance.d.ts +54 -0
- package/dist/src/testing/kv-provider-conformance.d.ts.map +1 -0
- package/dist/src/testing/kv-provider-conformance.js +183 -0
- package/dist/src/testing/kv-provider-conformance.js.map +1 -0
- package/package.json +8 -8
|
@@ -0,0 +1,529 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keeping the engine's live schema and the persisted catalog in agreement, in both
|
|
3
|
+
* directions: reading the catalog back at open (rehydration), lazily reconnecting a
|
|
4
|
+
* table the engine already knows about, and reacting to the engine's schema-change
|
|
5
|
+
* notifications (catalog-only tag swaps, view/materialized-view DDL upkeep, and the
|
|
6
|
+
* stale-materialized-view set that survives a restart).
|
|
7
|
+
*
|
|
8
|
+
* Third layer of the store-module chain:
|
|
9
|
+
* StoreModuleBase -> StoreModuleCatalog -> StoreModuleSchemaSync -> StoreModuleIndex
|
|
10
|
+
* -> StoreModuleAlterColumn -> StoreModuleAlter -> StoreModuleRename -> StoreModule
|
|
11
|
+
*/
|
|
12
|
+
import { isMaintainedTable } from '@quereus/quereus';
|
|
13
|
+
import { StoreTable } from './store-table.js';
|
|
14
|
+
import { buildCatalogKey, classifyCatalogKey, parseMaterializedViewCatalogKey } from './key-builder.js';
|
|
15
|
+
import { StoreModuleCatalog } from './store-module-catalog.js';
|
|
16
|
+
export class StoreModuleSchemaSync extends StoreModuleCatalog {
|
|
17
|
+
/**
|
|
18
|
+
* Returns the connected StoreTable for `schemaName.tableName`, lazily
|
|
19
|
+
* reconnecting from the engine's schema registry when absent.
|
|
20
|
+
*
|
|
21
|
+
* `renameTable` evicts the old key from `this.tables` and expects the next
|
|
22
|
+
* `connect()` to repopulate under the new name, but `apply schema` can run
|
|
23
|
+
* follow-up DDL (ALTER TABLE, CREATE/DROP INDEX) against the new name
|
|
24
|
+
* without an intervening connect. Mirror connect()'s schemaManager lookup
|
|
25
|
+
* so that DDL finds the moved table. Safe for the index paths because
|
|
26
|
+
* SchemaManager calls the module BEFORE mutating the registered table
|
|
27
|
+
* schema, so the reconnected cache matches what a connected instance holds.
|
|
28
|
+
*/
|
|
29
|
+
getOrReconnectTable(db, schemaName, tableName) {
|
|
30
|
+
const tableKey = `${schemaName}.${tableName}`.toLowerCase();
|
|
31
|
+
let table = this.tables.get(tableKey);
|
|
32
|
+
if (!table) {
|
|
33
|
+
const registeredSchema = db.schemaManager.getTable(schemaName, tableName);
|
|
34
|
+
if (registeredSchema) {
|
|
35
|
+
table = new StoreTable(db, this, registeredSchema, this.parseConfig(registeredSchema.vtabArgs ?? {}), this.eventEmitter, true);
|
|
36
|
+
this.tables.set(tableKey, table);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return table;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Rehydrate the persisted catalog into the in-memory schema manager, in
|
|
43
|
+
* dependency order.
|
|
44
|
+
*
|
|
45
|
+
* Re-asserts the engine schema-change subscription up front — normally already
|
|
46
|
+
* established by `StoreModule.onRegister` at `Database.registerModule`, but this
|
|
47
|
+
* entry point is also reachable with a `db` this module was never registered on, and
|
|
48
|
+
* phase 3 cannot observe MV staleness without it. Then loads every catalog entry
|
|
49
|
+
* once, classifies each by its key prefix into {tables, views, materialized views},
|
|
50
|
+
* and imports in three phases:
|
|
51
|
+
*
|
|
52
|
+
* 1. **Tables** — `importCatalog` (connect to existing storage; refresh connected
|
|
53
|
+
* `StoreTable` schemas).
|
|
54
|
+
* 2. **Views** — `importCatalog` (engine silent-register; body validation deferred
|
|
55
|
+
* to query time, so order among views — and view-over-MV / view-over-view —
|
|
56
|
+
* does not matter, and no schema-change event fires → phase 2 writes nothing).
|
|
57
|
+
* 3. **Materialized views** — `importCatalog` per entry (engine re-materialize:
|
|
58
|
+
* rebuilds the memory backing from current source data, re-registers row-time
|
|
59
|
+
* maintenance, re-runs the eligibility gate — the same core the create emitter
|
|
60
|
+
* uses, but silent: no `materialized_view_added` fires, so phase 3 writes
|
|
61
|
+
* nothing back to the catalog). A store-hosted backing that phase 1 already
|
|
62
|
+
* rehydrated is **adopted without the refill** when the engine's adopt gates
|
|
63
|
+
* pass — see the clean-shutdown marker below.
|
|
64
|
+
*
|
|
65
|
+
* **Clean-shutdown marker.** Before anything loads, the reserved
|
|
66
|
+
* `\x00meta\x00clean_shutdown` catalog entry (written by `StoreModule.closeAll` after
|
|
67
|
+
* every batch flushed) is consumed: parsed into `{ trusted, staleAtClose }`, then
|
|
68
|
+
* **deleted immediately** — single-use, so a crash later in this session (or a
|
|
69
|
+
* second rehydrate without an intervening clean close) is detected at the next open
|
|
70
|
+
* and every adopt falls back to the always-correct drop+refill, self-healing any
|
|
71
|
+
* crash-window divergence (coordinated commit is not 2PC across stores). The marker
|
|
72
|
+
* payload is the JSON set of MVs that were **stale-at-close** (row-time maintenance
|
|
73
|
+
* detached, so the durable backing may be behind); phase 3 withholds trust per-entry
|
|
74
|
+
* for those — `trustBackings: trusted && !staleAtClose.has(name)` — so a
|
|
75
|
+
* stale-at-close MV refills (recomputing content and re-arming maintenance) while
|
|
76
|
+
* every live-at-close MV keeps the fast path. The one shared `adoptedBackings` set
|
|
77
|
+
* composes across fixpoint rounds (an upstream MV adopted in round 1 enables its
|
|
78
|
+
* dependent in round 2, while a refilled — or stale-at-close — upstream is never
|
|
79
|
+
* added to it, forcing dependents to refill).
|
|
80
|
+
*
|
|
81
|
+
* **MV-over-MV ordering** is handled by a fixpoint retry rather than a static topo
|
|
82
|
+
* sort: an MV's resolved `sourceTables` are computed at import time, not serialized
|
|
83
|
+
* in the DDL, so they are unavailable before import. Each round passes the names of
|
|
84
|
+
* every OTHER still-pending MV entry as `pendingDerivations`; the engine defers any
|
|
85
|
+
* entry whose body reads one (its source already pre-exists as a phase-1 plain
|
|
86
|
+
* table, so the body would otherwise plan against content the upstream's own import
|
|
87
|
+
* may be about to replace). The loop repeats while any MV makes progress — robust to
|
|
88
|
+
* arbitrary nesting depth. A genuinely unbuildable MV — a missing (e.g. memory)
|
|
89
|
+
* source, or an unresolvable cycle — makes no progress in a round and is recorded in
|
|
90
|
+
* `errors`.
|
|
91
|
+
*
|
|
92
|
+
* Per-entry errors in any phase are collected (not fatal) so one bad object does not
|
|
93
|
+
* abort the rest.
|
|
94
|
+
*
|
|
95
|
+
* Call after `db.registerModule()` (and `db.setDefaultVtabName()` if DDL may lack a
|
|
96
|
+
* USING clause).
|
|
97
|
+
*/
|
|
98
|
+
async rehydrateCatalog(db) {
|
|
99
|
+
// Idempotent: `StoreModule.onRegister` already subscribed at `db.registerModule`.
|
|
100
|
+
// Kept because `rehydrateCatalog` is also reachable with a `db` this module was
|
|
101
|
+
// never registered on (tests, embedders driving rehydration directly), and because
|
|
102
|
+
// the subscription must exist before phase 3 can observe MV staleness.
|
|
103
|
+
this.ensureSchemaSubscription(db);
|
|
104
|
+
// Capability gate (method presence — matching the coordinator's own gate): an
|
|
105
|
+
// atomic-commit provider commits a source write and its same-module backing in
|
|
106
|
+
// one all-or-nothing batch, so a crash can no longer tear them apart (the
|
|
107
|
+
// crash-divergence window gate 5 historically guarded). In that domain, gate 4
|
|
108
|
+
// alone governs same-module backings and a non-stale backing adopts after a crash
|
|
109
|
+
// too — the LOGICAL-staleness window is closed instead by the durable stale-MV
|
|
110
|
+
// set, which (unlike the clean-shutdown marker) survives a crash. A non-atomic
|
|
111
|
+
// provider never writes the set (see `StoreModuleBase.atomicProvider`), so skip the read.
|
|
112
|
+
const atomic = this.atomicProvider;
|
|
113
|
+
const durableStale = atomic ? await this.readDurableStaleMvSet() : { present: false };
|
|
114
|
+
// Consume the clean-shutdown marker FIRST (before the catalog scan): in the
|
|
115
|
+
// non-atomic domain its presence is the adopt trust basis for this rehydration
|
|
116
|
+
// only, and deleting it immediately makes it single-use. Its payload names the MVs
|
|
117
|
+
// that were stale-at-close. Always consumed for single-use hygiene even in the
|
|
118
|
+
// atomic branch (where its trust bit is ignored in favor of the durable set).
|
|
119
|
+
const { trusted, staleAtClose } = await this.consumeCleanShutdownMarker();
|
|
120
|
+
// Per-entry adopt trust basis, capability-aware:
|
|
121
|
+
// - Atomic domain WITH a durable stale-set on disk → gate 4 alone governs; the
|
|
122
|
+
// marker is NOT required, so a non-stale backing adopts even after a crash. A
|
|
123
|
+
// present-but-unparseable stale-set (`stale === null`) refills everything.
|
|
124
|
+
// - Otherwise (non-atomic provider, OR atomic but no stale-set yet — the upgrade
|
|
125
|
+
// path) → today's marker path, byte-for-byte.
|
|
126
|
+
const trustBacking = (name) => atomic && durableStale.present
|
|
127
|
+
? durableStale.stale !== null && !durableStale.stale.has(name)
|
|
128
|
+
: trusted && !staleAtClose.has(name);
|
|
129
|
+
const entries = await this.loadCatalogEntries();
|
|
130
|
+
const result = { tables: [], indexes: [], views: [], materializedViews: [], errors: [] };
|
|
131
|
+
if (entries.length === 0)
|
|
132
|
+
return result;
|
|
133
|
+
const recordError = (ddl, e) => {
|
|
134
|
+
const error = e instanceof Error ? e : new Error(String(e));
|
|
135
|
+
console.warn(`[StoreModule] Failed to rehydrate DDL entry, skipping: ${error.message}\n DDL: ${ddl.substring(0, 120)}`);
|
|
136
|
+
result.errors.push({ ddl, error });
|
|
137
|
+
};
|
|
138
|
+
// Classify every loaded entry by key prefix. The full-range catalog scan returns
|
|
139
|
+
// table, view, and MV entries intermixed; each must reach the correct phase — a
|
|
140
|
+
// view/MV entry fed to the table-phase importCatalog would fail-loud or mis-handle.
|
|
141
|
+
const tableDDLs = [];
|
|
142
|
+
const viewDDLs = [];
|
|
143
|
+
// MV entries retain their qualified `schema.mv` name (derived from the catalog
|
|
144
|
+
// key) so phase 3 can withhold the adopt fast path per-entry for any MV that
|
|
145
|
+
// was stale-at-close.
|
|
146
|
+
const mvEntries = [];
|
|
147
|
+
for (const { key, ddl } of entries) {
|
|
148
|
+
switch (classifyCatalogKey(key)) {
|
|
149
|
+
case 'view': {
|
|
150
|
+
viewDDLs.push(ddl);
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
case 'materializedView': {
|
|
154
|
+
mvEntries.push({ name: parseMaterializedViewCatalogKey(key), ddl });
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
// Meta entries are store-internal, never DDL. (The marker itself was
|
|
158
|
+
// already consumed above; this guards any future meta key.)
|
|
159
|
+
case 'meta': {
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
default: {
|
|
163
|
+
tableDDLs.push(ddl);
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// Phase 1 — tables. Per-entry import isolates a corrupt entry so the rest load.
|
|
169
|
+
for (const ddl of tableDDLs) {
|
|
170
|
+
try {
|
|
171
|
+
const imported = await db.schemaManager.importCatalog([ddl]);
|
|
172
|
+
result.tables.push(...imported.tables);
|
|
173
|
+
result.indexes.push(...imported.indexes);
|
|
174
|
+
}
|
|
175
|
+
catch (e) {
|
|
176
|
+
recordError(ddl, e);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
// Phase 2 — views (silent register; deferred body validation → order-independent).
|
|
180
|
+
for (const ddl of viewDDLs) {
|
|
181
|
+
try {
|
|
182
|
+
const imported = await db.schemaManager.importCatalog([ddl]);
|
|
183
|
+
result.views.push(...imported.views);
|
|
184
|
+
}
|
|
185
|
+
catch (e) {
|
|
186
|
+
recordError(ddl, e);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
// Phase 3 — materialized views, dependency-ordered via fixpoint retry (see
|
|
190
|
+
// docstring). One shared adopt ledger across all rounds: adopted upstream
|
|
191
|
+
// backings unlock their dependents' adoption in later rounds.
|
|
192
|
+
const adoptedBackings = new Set();
|
|
193
|
+
let pending = mvEntries;
|
|
194
|
+
while (pending.length > 0) {
|
|
195
|
+
const failed = [];
|
|
196
|
+
let progressed = false;
|
|
197
|
+
for (const entry of pending) {
|
|
198
|
+
try {
|
|
199
|
+
// Ordering gate (unified model): a dependent's source may already
|
|
200
|
+
// pre-exist as the upstream's phase-1 *plain* table, so its body
|
|
201
|
+
// PLANS before the upstream's own MV entry has imported. Pass the
|
|
202
|
+
// names of every OTHER still-pending MV entry; the engine defers
|
|
203
|
+
// (throws → retried next round) any entry whose body reads one.
|
|
204
|
+
const pendingDerivations = new Set(pending.filter(p => p !== entry).map(p => p.name));
|
|
205
|
+
// Trust this backing only when the per-entry capability-aware basis
|
|
206
|
+
// (`trustBacking`) holds: in the atomic domain a non-stale backing is
|
|
207
|
+
// trusted (even after a crash); otherwise a clean shutdown AND not
|
|
208
|
+
// stale-at-close. A withheld MV refills — recomputing content and
|
|
209
|
+
// re-arming maintenance (clearing `stale`) — and is never added to
|
|
210
|
+
// `adoptedBackings`, so the ledger gate forces its MV-over-MV dependents
|
|
211
|
+
// to refill too.
|
|
212
|
+
const imported = await db.schemaManager.importCatalog([entry.ddl], {
|
|
213
|
+
trustBackings: trustBacking(entry.name),
|
|
214
|
+
adoptedBackings,
|
|
215
|
+
pendingDerivations,
|
|
216
|
+
});
|
|
217
|
+
result.materializedViews.push(...imported.materializedViews);
|
|
218
|
+
progressed = true;
|
|
219
|
+
}
|
|
220
|
+
catch (e) {
|
|
221
|
+
failed.push({ entry, error: e });
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
if (!progressed) {
|
|
225
|
+
// No MV built this round → the remaining failures are genuine (missing
|
|
226
|
+
// source, ineligible body, unresolvable cycle). Record them and stop.
|
|
227
|
+
for (const f of failed)
|
|
228
|
+
recordError(f.entry.ddl, f.error);
|
|
229
|
+
break;
|
|
230
|
+
}
|
|
231
|
+
pending = failed.map(f => f.entry);
|
|
232
|
+
}
|
|
233
|
+
// Refresh each connected StoreTable from the now-current registry. During
|
|
234
|
+
// import, `importTable` connects a StoreTable holding the table-only schema,
|
|
235
|
+
// then `importIndex` appends the index (and its derived UNIQUE constraint) to
|
|
236
|
+
// the SchemaManager's registered schema but NOT to that live StoreTable
|
|
237
|
+
// instance — `importCatalog` deliberately skips module hooks to stay generic,
|
|
238
|
+
// so the store module reconciles here. Without this, DML on a rehydrated table
|
|
239
|
+
// would not maintain its indexes and the derived UNIQUE would not enforce.
|
|
240
|
+
//
|
|
241
|
+
// Per-table isolation, matching the per-entry posture above: `updateSchema`
|
|
242
|
+
// validates the incoming schema's key collations, and an INDEX-appended schema
|
|
243
|
+
// can fail where the phase-1 table-only import passed (an index column naming a
|
|
244
|
+
// collation this connection never registered, or a comparator-only one). On a
|
|
245
|
+
// refusal, record the error and EVICT the instance rather than leaving it live
|
|
246
|
+
// on the import-time index-less schema — a half-schema'd table would accept DML
|
|
247
|
+
// without maintaining its indexes. The registered schema stays; the next
|
|
248
|
+
// statement to touch the table reconnects, and the StoreTable constructor
|
|
249
|
+
// re-raises the same error at the point of use.
|
|
250
|
+
for (const [tableKey, table] of [...this.tables.entries()]) {
|
|
251
|
+
const current = table.getSchema();
|
|
252
|
+
const fresh = db.schemaManager.getTable(current.schemaName, current.name);
|
|
253
|
+
if (!fresh)
|
|
254
|
+
continue;
|
|
255
|
+
try {
|
|
256
|
+
table.updateSchema(fresh);
|
|
257
|
+
}
|
|
258
|
+
catch (e) {
|
|
259
|
+
recordError(`<index reconcile for ${current.schemaName}.${current.name}>`, e);
|
|
260
|
+
this.tables.delete(tableKey);
|
|
261
|
+
// Fire-and-forget: the eviction is complete without it (nothing reads the
|
|
262
|
+
// instance again), so a failing stats flush must not reject the rehydrate —
|
|
263
|
+
// but it must not become an unhandled rejection either.
|
|
264
|
+
void table.dispose().catch(disposeError => console.warn(`[StoreModule] dispose of evicted ${current.schemaName}.${current.name} failed: ${String(disposeError)}`));
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
// Recompute the durable stale-MV set from the now-current flags and compare-write
|
|
268
|
+
// it: refilled MVs cleared `stale`, adopted MVs were never stale, so the on-disk
|
|
269
|
+
// entry (which a crash may have left naming an MV this rehydrate just refilled)
|
|
270
|
+
// now reflects post-rehydrate truth — preventing a wasteful re-refill at the next
|
|
271
|
+
// open. Phase 3's `importCatalog` is silent (no schema-change events fire), so the
|
|
272
|
+
// listener never ran this rehydration; this explicit tail write establishes the
|
|
273
|
+
// session baseline (`lastPersistedStaleMvs`). It rides `persistQueue`, drained by
|
|
274
|
+
// the next `closeAll`/`whenCatalogPersisted`. (An optimization, not soundness: a
|
|
275
|
+
// crash before it lands leaves a stale-set that only over-names → a sound refill.)
|
|
276
|
+
this.persistStaleMvSetIfChanged();
|
|
277
|
+
return result;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Subscribe (once) to the engine's `SchemaChangeNotifier`, the channel every
|
|
281
|
+
* event-driven catalog write rides: `table_added` (a new store table's entry),
|
|
282
|
+
* catalog-only mutations that bypass `module.alterTable` — notably `ALTER … SET TAGS`
|
|
283
|
+
* and the programmatic `setTableTags`/`setColumnTags`/`setConstraintTags` — and the
|
|
284
|
+
* whole view / materialized-view lifecycle.
|
|
285
|
+
*
|
|
286
|
+
* Normally called from `StoreModule.onRegister` at `Database.registerModule`, before
|
|
287
|
+
* any statement runs; also called from `rehydrateCatalog` and the `create`/`connect`/
|
|
288
|
+
* `alterTable` hooks, which are reachable with a `db` this module was not registered
|
|
289
|
+
* on. Idempotent.
|
|
290
|
+
*
|
|
291
|
+
* One `StoreModule` instance is assumed to serve one `Database`. A later hook
|
|
292
|
+
* carrying a *different* `db` keeps the existing subscription (multi-database
|
|
293
|
+
* sharing of a single module instance is out of scope) and logs.
|
|
294
|
+
*/
|
|
295
|
+
ensureSchemaSubscription(db) {
|
|
296
|
+
if (this.schemaListenerUnsub) {
|
|
297
|
+
if (this.subscribedDb && this.subscribedDb !== db) {
|
|
298
|
+
console.warn('[StoreModule] ensureSchemaSubscription called with a different Database; '
|
|
299
|
+
+ 'keeping the existing subscription (one module instance is assumed to serve one Database).');
|
|
300
|
+
}
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
this.subscribedDb = db;
|
|
304
|
+
// A fresh subscription has no on-disk stale-set baseline yet (this module never
|
|
305
|
+
// wrote one this session); reset so the first relevant event — or the rehydrate
|
|
306
|
+
// tail — re-establishes it rather than comparing against a prior subscription's.
|
|
307
|
+
this.lastPersistedStaleMvs = undefined;
|
|
308
|
+
this.schemaListenerUnsub = db.schemaManager.getChangeNotifier().addListener(this.onEngineSchemaChange);
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Engine schema-change listener. Persists the catalog incrementally for the events
|
|
312
|
+
* that bypass `module.alterTable` / `module.destroy`:
|
|
313
|
+
*
|
|
314
|
+
* - `table_added` — a table the engine has just fully validated and registered. This is
|
|
315
|
+
* what persists a store table nobody ever reads or writes (the lazy
|
|
316
|
+
* first-storage-access save never runs for one).
|
|
317
|
+
* - `table_modified` — every catalog-only tag swap (and the redundant follow-up a
|
|
318
|
+
* structural ALTER fires). Keeps a connected `StoreTable`'s cached schema consistent
|
|
319
|
+
* (SET TAGS does not call `updateSchema`) then read-compare-writes the table bundle.
|
|
320
|
+
* - `view_added` / `view_modified` / `view_removed` — plain `CREATE`/`ALTER … SET TAGS`/
|
|
321
|
+
* `DROP VIEW` (the engine fires these from the runtime emitters).
|
|
322
|
+
* - `materialized_view_added` / `_modified` / `_refreshed` / `_removed` — MV lifecycle.
|
|
323
|
+
* Like `table_modified`, the `_added`/`_modified`/`_refreshed` arms also synchronously
|
|
324
|
+
* refresh the connected `StoreTable`'s cached schema so a tag change (e.g.
|
|
325
|
+
* `quereus.sync.replicate`) takes effect immediately without reopen.
|
|
326
|
+
*
|
|
327
|
+
* Unlike the table path there is **no** catalog-absent self-filter for view/MV
|
|
328
|
+
* add/remove: one `StoreModule` instance serves one `Database`, so that database's
|
|
329
|
+
* views/MVs belong in its catalog unconditionally. A MEMORY-hosted maintained table
|
|
330
|
+
* fires `table_added`/`table_removed`/`table_modified` like any table; those stay
|
|
331
|
+
* ignored (`table_added` fails the ownership filter, `table_removed` falls through,
|
|
332
|
+
* its `table_modified` is catalog-absent → skipped), so only the MV entry persists for
|
|
333
|
+
* it. A STORE-hosted maintained table additionally persists its own table bundle
|
|
334
|
+
* through the ordinary store-table machinery (which phase-1 rehydrate connects for the
|
|
335
|
+
* adopt fast path).
|
|
336
|
+
*
|
|
337
|
+
* Synchronous by contract (`notifyChange` does not await listeners); every async write
|
|
338
|
+
* rides `persistQueue`, drained by `closeAll`/`whenCatalogPersisted`.
|
|
339
|
+
*
|
|
340
|
+
* After dispatching the event's own catalog persistence, the listener recomputes the
|
|
341
|
+
* durable stale-MV set and compare-writes it (see {@link persistStaleMvSetIfChanged}).
|
|
342
|
+
* Because the engine's MV manager subscribes to the same notifier in the `Database`
|
|
343
|
+
* constructor — before this lazy subscription — its listener runs first, so the
|
|
344
|
+
* `derivation.stale` flags are already current when this recompute reads them.
|
|
345
|
+
*/
|
|
346
|
+
onEngineSchemaChange = (event) => {
|
|
347
|
+
this.dispatchSchemaChange(event);
|
|
348
|
+
// Every staleness SET transition is bracketed by an event observed here (a source
|
|
349
|
+
// `table_modified`/`table_removed`, or the synthetic backing-invalidation
|
|
350
|
+
// `table_modified` for an MV-over-MV cascade), and every CLEAR but the no-event
|
|
351
|
+
// rename-restore fires one too — so recompute on each event keeps the durable set
|
|
352
|
+
// current. Enqueued on `persistQueue` AFTER the dispatch above, so the `sync` lands
|
|
353
|
+
// after the event's own source-DDL write is queued (the durability ordering the
|
|
354
|
+
// adopt soundness argument relies on — see `docs/mv-backing-host.md` § Cross-module atomicity).
|
|
355
|
+
this.persistStaleMvSetIfChanged();
|
|
356
|
+
};
|
|
357
|
+
/**
|
|
358
|
+
* Dispatch a single engine schema-change event to its catalog-persistence arm.
|
|
359
|
+
*
|
|
360
|
+
* The `table_added` arm is where a store table's catalog entry is written. It fires
|
|
361
|
+
* from `SchemaManager.createTable` only AFTER the engine has fully validated the table
|
|
362
|
+
* (notably `validateForeignKeyCollations`, which runs after `module.create` returns and
|
|
363
|
+
* throws WITHOUT calling `destroy`) and registered it — so, unlike an eager write
|
|
364
|
+
* inside `StoreModule.create`, it can never leave an entry behind for a table the user
|
|
365
|
+
* never got, which would reopen as a phantom table.
|
|
366
|
+
*
|
|
367
|
+
* Two things distinguish this arm from the view/MV arms above it:
|
|
368
|
+
*
|
|
369
|
+
* - **It self-filters on ownership.** Every table the engine registers is announced,
|
|
370
|
+
* including memory tables and other modules'. `ownsTableCatalogEntry` is the test for
|
|
371
|
+
* "would this module write that table's entry" (and already handles the isolation
|
|
372
|
+
* wrapper, whose `vtabModule` is the wrapper exposing us as `underlying`). The
|
|
373
|
+
* view/MV arms need no such filter — views and MVs are not owned by a module.
|
|
374
|
+
* - **It compare-writes rather than blind-puts.** `table_added` is re-delivered during
|
|
375
|
+
* rehydration for a store-hosted materialized-view backing
|
|
376
|
+
* (`SchemaManager.createBackingTable`), where the entry phase 1 already wrote is
|
|
377
|
+
* current; comparing keeps that a no-op so a second consecutive reopen still yields
|
|
378
|
+
* identical catalog bytes. (Plain table import during rehydration is silent — no
|
|
379
|
+
* `table_added` fires at all.)
|
|
380
|
+
*
|
|
381
|
+
* A failure on this path is logged and swallowed by `enqueuePersist`, not raised on the
|
|
382
|
+
* statement — identical to how `create view` / `create materialized view` already
|
|
383
|
+
* persist. That is why `StoreTableBase.initializeStore` keeps its first-storage-access
|
|
384
|
+
* catalog write as a compare-write BACKSTOP: it is the only site where a table whose
|
|
385
|
+
* DDL text cannot be encoded at all (a lone surrogate in a quoted column name, a
|
|
386
|
+
* DEFAULT string literal, a CHECK constant) still raises on a statement.
|
|
387
|
+
*/
|
|
388
|
+
dispatchSchemaChange(event) {
|
|
389
|
+
switch (event.type) {
|
|
390
|
+
// NOTE: the compare-write costs one catalog READ per CREATE TABLE, and the
|
|
391
|
+
// first-storage-access backstop costs a second one. DDL is rare, so neither is
|
|
392
|
+
// worth avoiding today; if a schema-heavy `apply schema` creating many tables at
|
|
393
|
+
// once ever shows up hot here, have the arm hand the freshly written bundle to
|
|
394
|
+
// the StoreTable (as `createIndex` does with `markDdlSaved`) so the backstop can
|
|
395
|
+
// be skipped outright.
|
|
396
|
+
case 'table_added': {
|
|
397
|
+
const table = event.newObject;
|
|
398
|
+
if (!this.ownsTableCatalogEntry(table))
|
|
399
|
+
return;
|
|
400
|
+
// `newObject` is the post-`finalizeCreatedTableSchema` schema, so it already
|
|
401
|
+
// carries the store's reconciled PK collations — byte-identical to what the
|
|
402
|
+
// first-storage-access backstop in `StoreTableBase.initializeStore` writes,
|
|
403
|
+
// which is what makes that backstop a compare-skip rather than a second put.
|
|
404
|
+
this.enqueuePersist(() => this.persistTableCatalogEntryIfChanged(table));
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
case 'table_modified': {
|
|
408
|
+
// SET TAGS does not call `table.updateSchema`, so a connected instance's cached
|
|
409
|
+
// schema would otherwise go stale (and a later lazy `saveTableDDL` could re-write
|
|
410
|
+
// tag-less DDL). Persistence below always reads `newObject`, never this cache.
|
|
411
|
+
const tableKey = `${event.schemaName}.${event.objectName}`.toLowerCase();
|
|
412
|
+
const connected = this.tables.get(tableKey);
|
|
413
|
+
if (connected)
|
|
414
|
+
connected.updateSchema(event.newObject);
|
|
415
|
+
const key = buildCatalogKey(event.schemaName, event.objectName);
|
|
416
|
+
const newObject = event.newObject;
|
|
417
|
+
this.enqueuePersist(() => this.persistCatalogIfChanged(key, newObject));
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
case 'view_added':
|
|
421
|
+
case 'view_modified': {
|
|
422
|
+
const view = event.newObject;
|
|
423
|
+
this.enqueuePersist(() => this.saveViewDDL(view));
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
case 'view_removed': {
|
|
427
|
+
const { schemaName, objectName } = event;
|
|
428
|
+
this.enqueuePersist(() => this.removeViewDDL(schemaName, objectName));
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
case 'materialized_view_added':
|
|
432
|
+
case 'materialized_view_modified':
|
|
433
|
+
// Unified model: the payload is the maintained table itself.
|
|
434
|
+
this.refreshConnectedMaterializedView(event.schemaName, event.objectName, event.newObject);
|
|
435
|
+
return;
|
|
436
|
+
case 'materialized_view_refreshed':
|
|
437
|
+
// DDL is usually unchanged by a REFRESH (body/tags identical) → compare-skip,
|
|
438
|
+
// but re-read tags in case they were updated alongside the refresh.
|
|
439
|
+
this.refreshConnectedMaterializedView(event.schemaName, event.objectName, event.object);
|
|
440
|
+
return;
|
|
441
|
+
case 'materialized_view_removed': {
|
|
442
|
+
const { schemaName, objectName } = event;
|
|
443
|
+
// DROP MAINTAINED detaches catalog-only: the engine has already swapped the
|
|
444
|
+
// catalog entry to a plain (derivation-less) schema before firing this event,
|
|
445
|
+
// but a connected `StoreTable` still caches the maintained schema. The store's
|
|
446
|
+
// `alterTable` reads that cache (`getSchema`), so a following structural ALTER
|
|
447
|
+
// would spread the stale `derivation` onto the rebuilt schema and re-register
|
|
448
|
+
// the table as a materialized view (rejecting the next ALTER). Refresh the cache
|
|
449
|
+
// to the now-plain catalog entry. When the entry is gone entirely (DROP TABLE /
|
|
450
|
+
// DROP MATERIALIZED VIEW), there is nothing to refresh — `destroy` retires it.
|
|
451
|
+
const plain = this.subscribedDb?.schemaManager.getTable(schemaName, objectName);
|
|
452
|
+
if (plain && !isMaintainedTable(plain)) {
|
|
453
|
+
const connected = this.tables.get(`${schemaName}.${objectName}`.toLowerCase());
|
|
454
|
+
if (connected)
|
|
455
|
+
connected.updateSchema(plain);
|
|
456
|
+
}
|
|
457
|
+
this.enqueuePersist(() => this.removeMaterializedViewDDL(schemaName, objectName));
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
default:
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Shared MV add/modify/refresh handling. Narrow defensively — a derivation-less
|
|
466
|
+
* payload would be an engine bug, so skip. Otherwise, mirror `table_modified`:
|
|
467
|
+
* synchronously refresh a connected `StoreTable`'s cached schema (so a tag change
|
|
468
|
+
* such as `quereus.sync.replicate` takes effect immediately without reopen) before
|
|
469
|
+
* enqueuing the catalog DDL persist.
|
|
470
|
+
*/
|
|
471
|
+
refreshConnectedMaterializedView(schemaName, objectName, payload) {
|
|
472
|
+
if (!isMaintainedTable(payload))
|
|
473
|
+
return;
|
|
474
|
+
const key = `${schemaName}.${objectName}`.toLowerCase();
|
|
475
|
+
const connected = this.tables.get(key);
|
|
476
|
+
if (connected)
|
|
477
|
+
connected.updateSchema(payload);
|
|
478
|
+
this.enqueuePersist(() => this.saveMaterializedViewDDL(payload));
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* The qualified lowercased `schema.mv` names of every maintained table currently
|
|
482
|
+
* marked `derivation.stale` — an MV whose row-time maintenance detached mid-session
|
|
483
|
+
* (a body-relevant `table_modified`/`table_removed` on a source, or a cascade
|
|
484
|
+
* backing-invalidation), so its durable backing may be behind. The single source of
|
|
485
|
+
* truth for both the clean-shutdown marker payload and the durable stale-MV set.
|
|
486
|
+
*
|
|
487
|
+
* No subscribed db ⇒ the empty set. Since `StoreModule.onRegister` subscribes at
|
|
488
|
+
* `Database.registerModule`, that now means a module never registered on any
|
|
489
|
+
* `Database` (constructed and closed directly) — which by construction observed no
|
|
490
|
+
* schema at all, so it detached no persisted MV's maintenance. Memory-backed MVs that appear here
|
|
491
|
+
* are harmless — their catalog entries always refill (no phase-1 pre-existing
|
|
492
|
+
* backing), so withholding trust from them is a no-op.
|
|
493
|
+
*/
|
|
494
|
+
computeStaleMvSet() {
|
|
495
|
+
return this.subscribedDb
|
|
496
|
+
? this.subscribedDb.schemaManager.getAllMaintainedTables()
|
|
497
|
+
.filter(mv => mv.derivation.stale)
|
|
498
|
+
.map(mv => `${mv.schemaName}.${mv.name}`.toLowerCase())
|
|
499
|
+
: [];
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* Recompute the durable stale-MV set and, only when it differs from the last value
|
|
503
|
+
* enqueued this subscription (`StoreModuleBase.lastPersistedStaleMvs`), enqueue a `sync: true`
|
|
504
|
+
* point-write of it onto `StoreModuleBase.persistQueue`. The compare-skip keeps an unrelated
|
|
505
|
+
* `table_modified` (a tag swap that changes no MV's staleness) from costing an fsync.
|
|
506
|
+
*
|
|
507
|
+
* The recompute reads `derivation.stale` synchronously (the flags are current at
|
|
508
|
+
* listener-dispatch time); the field is updated synchronously at enqueue time, and
|
|
509
|
+
* `persistQueue` serializes the writes in order, so the field always names the last
|
|
510
|
+
* enqueued value. Riding `persistQueue` (rather than a bare `put`) also serializes
|
|
511
|
+
* this `sync` behind the triggering event's own source-DDL compare-write, so the
|
|
512
|
+
* stale-set becomes durable no-later-than the source DDL that caused the staleness —
|
|
513
|
+
* the ordering the adopt soundness argument depends on.
|
|
514
|
+
*/
|
|
515
|
+
persistStaleMvSetIfChanged() {
|
|
516
|
+
// Only an atomic-capable session writes the durable set — it is the sole reader and
|
|
517
|
+
// trust basis (see `StoreModuleBase.atomicProvider`); a non-atomic session's trust basis is the
|
|
518
|
+
// clean-shutdown marker, so writing the set there is both useless and a soundness
|
|
519
|
+
// hazard (it could be trusted by a later atomic reopen). Skip before recomputing.
|
|
520
|
+
if (!this.atomicProvider)
|
|
521
|
+
return;
|
|
522
|
+
const json = JSON.stringify(this.computeStaleMvSet());
|
|
523
|
+
if (json === this.lastPersistedStaleMvs)
|
|
524
|
+
return;
|
|
525
|
+
this.lastPersistedStaleMvs = json;
|
|
526
|
+
this.enqueuePersist(() => this.writeDurableStaleMvSet(json));
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
//# sourceMappingURL=store-module-schema-sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store-module-schema-sync.js","sourceRoot":"","sources":["../../../src/common/store-module-schema-sync.ts"],"names":[],"mappings":"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"}
|