@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.
Files changed (107) hide show
  1. package/README.md +34 -6
  2. package/dist/src/common/encoding.d.ts +4 -3
  3. package/dist/src/common/encoding.d.ts.map +1 -1
  4. package/dist/src/common/encoding.js +17 -13
  5. package/dist/src/common/encoding.js.map +1 -1
  6. package/dist/src/common/events.d.ts +31 -25
  7. package/dist/src/common/events.d.ts.map +1 -1
  8. package/dist/src/common/events.js +38 -39
  9. package/dist/src/common/events.js.map +1 -1
  10. package/dist/src/common/implicit-unique-index.d.ts +80 -0
  11. package/dist/src/common/implicit-unique-index.d.ts.map +1 -0
  12. package/dist/src/common/implicit-unique-index.js +148 -0
  13. package/dist/src/common/implicit-unique-index.js.map +1 -0
  14. package/dist/src/common/index.d.ts +7 -3
  15. package/dist/src/common/index.d.ts.map +1 -1
  16. package/dist/src/common/index.js +5 -2
  17. package/dist/src/common/index.js.map +1 -1
  18. package/dist/src/common/json-key.d.ts +30 -2
  19. package/dist/src/common/json-key.d.ts.map +1 -1
  20. package/dist/src/common/json-key.js +49 -2
  21. package/dist/src/common/json-key.js.map +1 -1
  22. package/dist/src/common/key-builder.d.ts +40 -20
  23. package/dist/src/common/key-builder.d.ts.map +1 -1
  24. package/dist/src/common/key-builder.js +31 -23
  25. package/dist/src/common/key-builder.js.map +1 -1
  26. package/dist/src/common/kv-store.d.ts +10 -0
  27. package/dist/src/common/kv-store.d.ts.map +1 -1
  28. package/dist/src/common/pk-key-resolution.d.ts +348 -0
  29. package/dist/src/common/pk-key-resolution.d.ts.map +1 -0
  30. package/dist/src/common/pk-key-resolution.js +481 -0
  31. package/dist/src/common/pk-key-resolution.js.map +1 -0
  32. package/dist/src/common/store-module-access-plan.d.ts +32 -0
  33. package/dist/src/common/store-module-access-plan.d.ts.map +1 -0
  34. package/dist/src/common/store-module-access-plan.js +428 -0
  35. package/dist/src/common/store-module-access-plan.js.map +1 -0
  36. package/dist/src/common/store-module-alter-column.d.ts +23 -0
  37. package/dist/src/common/store-module-alter-column.d.ts.map +1 -0
  38. package/dist/src/common/store-module-alter-column.js +389 -0
  39. package/dist/src/common/store-module-alter-column.js.map +1 -0
  40. package/dist/src/common/store-module-alter.d.ts +55 -0
  41. package/dist/src/common/store-module-alter.d.ts.map +1 -0
  42. package/dist/src/common/store-module-alter.js +504 -0
  43. package/dist/src/common/store-module-alter.js.map +1 -0
  44. package/dist/src/common/store-module-base.d.ts +268 -0
  45. package/dist/src/common/store-module-base.d.ts.map +1 -0
  46. package/dist/src/common/store-module-base.js +362 -0
  47. package/dist/src/common/store-module-base.js.map +1 -0
  48. package/dist/src/common/store-module-catalog.d.ts +269 -0
  49. package/dist/src/common/store-module-catalog.d.ts.map +1 -0
  50. package/dist/src/common/store-module-catalog.js +462 -0
  51. package/dist/src/common/store-module-catalog.js.map +1 -0
  52. package/dist/src/common/store-module-index-build.d.ts +88 -0
  53. package/dist/src/common/store-module-index-build.d.ts.map +1 -0
  54. package/dist/src/common/store-module-index-build.js +280 -0
  55. package/dist/src/common/store-module-index-build.js.map +1 -0
  56. package/dist/src/common/store-module-index.d.ts +116 -0
  57. package/dist/src/common/store-module-index.d.ts.map +1 -0
  58. package/dist/src/common/store-module-index.js +373 -0
  59. package/dist/src/common/store-module-index.js.map +1 -0
  60. package/dist/src/common/store-module-rename.d.ts +49 -0
  61. package/dist/src/common/store-module-rename.d.ts.map +1 -0
  62. package/dist/src/common/store-module-rename.js +268 -0
  63. package/dist/src/common/store-module-rename.js.map +1 -0
  64. package/dist/src/common/store-module-schema-rewrite.d.ts +57 -0
  65. package/dist/src/common/store-module-schema-rewrite.d.ts.map +1 -0
  66. package/dist/src/common/store-module-schema-rewrite.js +140 -0
  67. package/dist/src/common/store-module-schema-rewrite.js.map +1 -0
  68. package/dist/src/common/store-module-schema-sync.d.ts +232 -0
  69. package/dist/src/common/store-module-schema-sync.d.ts.map +1 -0
  70. package/dist/src/common/store-module-schema-sync.js +529 -0
  71. package/dist/src/common/store-module-schema-sync.js.map +1 -0
  72. package/dist/src/common/store-module.d.ts +43 -909
  73. package/dist/src/common/store-module.d.ts.map +1 -1
  74. package/dist/src/common/store-module.js +184 -3276
  75. package/dist/src/common/store-module.js.map +1 -1
  76. package/dist/src/common/store-table-base.d.ts +539 -0
  77. package/dist/src/common/store-table-base.d.ts.map +1 -0
  78. package/dist/src/common/store-table-base.js +900 -0
  79. package/dist/src/common/store-table-base.js.map +1 -0
  80. package/dist/src/common/store-table-constraints.d.ts +237 -0
  81. package/dist/src/common/store-table-constraints.d.ts.map +1 -0
  82. package/dist/src/common/store-table-constraints.js +624 -0
  83. package/dist/src/common/store-table-constraints.js.map +1 -0
  84. package/dist/src/common/store-table-scan.d.ts +452 -0
  85. package/dist/src/common/store-table-scan.d.ts.map +1 -0
  86. package/dist/src/common/store-table-scan.js +901 -0
  87. package/dist/src/common/store-table-scan.js.map +1 -0
  88. package/dist/src/common/store-table.d.ts +71 -1144
  89. package/dist/src/common/store-table.d.ts.map +1 -1
  90. package/dist/src/common/store-table.js +168 -2385
  91. package/dist/src/common/store-table.js.map +1 -1
  92. package/dist/src/testing/index.d.ts +12 -0
  93. package/dist/src/testing/index.d.ts.map +1 -0
  94. package/dist/src/testing/index.js +12 -0
  95. package/dist/src/testing/index.js.map +1 -0
  96. package/dist/src/testing/kv-assert.d.ts +19 -0
  97. package/dist/src/testing/kv-assert.d.ts.map +1 -0
  98. package/dist/src/testing/kv-assert.js +25 -0
  99. package/dist/src/testing/kv-assert.js.map +1 -0
  100. package/dist/src/testing/kv-conformance.d.ts.map +1 -1
  101. package/dist/src/testing/kv-conformance.js +1 -15
  102. package/dist/src/testing/kv-conformance.js.map +1 -1
  103. package/dist/src/testing/kv-provider-conformance.d.ts +54 -0
  104. package/dist/src/testing/kv-provider-conformance.d.ts.map +1 -0
  105. package/dist/src/testing/kv-provider-conformance.js +183 -0
  106. package/dist/src/testing/kv-provider-conformance.js.map +1 -0
  107. 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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"}