@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,268 @@
1
+ /**
2
+ * Module-wide state and storage plumbing shared by every layer of the generic
3
+ * KVStore-backed virtual-table module: the provider handle, the cached store and
4
+ * {@link StoreTable} maps, the single shared transaction coordinator, the
5
+ * serialized catalog-write queue, and the physical-store-name collision guard.
6
+ *
7
+ * Base of the store-module layer chain:
8
+ * StoreModuleBase -> StoreModuleCatalog -> StoreModuleSchemaSync -> StoreModuleIndex
9
+ * -> StoreModuleAlterColumn -> StoreModuleAlter -> StoreModuleRename -> StoreModule
10
+ */
11
+ import type { BaseModuleConfig, Database, LensDeploymentSnapshot, SqlValue, TableSchema } from '@quereus/quereus';
12
+ import type { KVStore, KVStoreProvider } from './kv-store.js';
13
+ import type { StoreEventEmitter } from './events.js';
14
+ import { TransactionCoordinator } from './transaction.js';
15
+ import { StoreTable } from './store-table.js';
16
+ import type { StoreTableConfig } from './store-table-base.js';
17
+ /**
18
+ * Listener the host binds via `StoreModule.setLensDeploymentListener` to
19
+ * forward a logical `apply schema` lens deployment onward (typically to the sync
20
+ * layer's basis-table lifecycle bookkeeping). Kept as a plain callback so
21
+ * `@quereus/store` stays free of a `@quereus/sync` dependency; the worker — which
22
+ * depends on both — wires the two together.
23
+ */
24
+ export type LensDeploymentListener = (db: Database, logicalSchemaName: string, snapshot: LensDeploymentSnapshot) => void | Promise<void>;
25
+ /**
26
+ * Configuration options for StoreModule tables.
27
+ */
28
+ export interface StoreModuleConfig extends BaseModuleConfig {
29
+ /** Collation for text keys. Default: 'NOCASE'. */
30
+ collation?: 'BINARY' | 'NOCASE';
31
+ /**
32
+ * Serialized-byte budget for a single index-build write batch (see
33
+ * `buildIndexEntries` (store-module-index-build.ts)). Set from the `max_batch_bytes`
34
+ * module arg; a missing / non-positive value falls back to
35
+ * {@link DEFAULT_MAX_BATCH_BYTES}. Module-wide, not per-index.
36
+ */
37
+ maxBatchBytes?: number;
38
+ /** Additional platform-specific options. */
39
+ [key: string]: unknown;
40
+ }
41
+ /**
42
+ * Default serialized-byte budget for a single index-build write batch. Bounds
43
+ * heap directly: `buildIndexEntries` (store-module-index-build.ts) flushes and starts a fresh
44
+ * batch whenever the accumulated key bytes cross this, so a table larger than
45
+ * memory never buffers its whole index in one batch. A byte budget (rather than a
46
+ * fixed entry count) is used because index entries vary in size, so bytes bound
47
+ * memory more tightly. 8 MiB trades a modest number of extra flushes against peak
48
+ * memory; override per store with `using store (max_batch_bytes = …)`.
49
+ */
50
+ export declare const DEFAULT_MAX_BATCH_BYTES: number;
51
+ /**
52
+ * Build the set of physical store names this module's data/index stores
53
+ * currently occupy in `schemaName`, mapping each name to a human description
54
+ * of the logical object that owns it (for sited collision messages).
55
+ *
56
+ * Physical store names are built by string concatenation (`{schema}.{table}` /
57
+ * `{schema}.{table}_idx_{index}`) and the `_idx_` delimiter is itself a legal
58
+ * substring of any identifier, so two distinct logical objects (e.g. index
59
+ * `archive` on `t` and a sibling table `t_idx_archive`) can collapse to the
60
+ * same physical name. This set is the authoritative occupancy used by
61
+ * {@link StoreModuleBase.assertStoreNameFree} to reject such a collision at CREATE time.
62
+ *
63
+ * The occupancy is the union of two sources, robust to lazy connection and the
64
+ * isolation wrapper (see the ticket's "Enumeration source" section):
65
+ * 1. `tables` — every store table the owning module touched this session.
66
+ * 2. the target schema's catalog tables whose `vtabModule === owner` and which
67
+ * are not views — store-backed tables not yet lazily connected.
68
+ * Names embed the schema prefix, so cross-schema entries never collide and no
69
+ * per-schema filter on `tables` is needed. Memory-backed siblings and
70
+ * views own no store in this provider and are excluded (the `=== owner` /
71
+ * `!isView` filter) to avoid false-positive rejects.
72
+ *
73
+ * No self-exclusion: at each guarded call the candidate object is not yet
74
+ * registered (create/createIndex run before the engine adds it) so it cannot
75
+ * self-collide; and for renameTable the renamed table's OWN stores stay in the
76
+ * set deliberately. Any overlap between a name the rename introduces and an
77
+ * own current store is a footprint-swap rename (`t` with index `x` → `t_idx_x`,
78
+ * or table `u_idx_x` with index `x` → `u`) that providers cannot relocate
79
+ * safely — relocation order determines whether a source is clobbered before it
80
+ * is moved — while no benign rename produces such an overlap. Keeping own
81
+ * stores in the set therefore causes no false rejects and keeps the
82
+ * reject-before-any-side-effect guarantee uniform.
83
+ *
84
+ * `owner` is the module instance whose stores these are — the concrete `StoreModule` at
85
+ * the top of the layer chain, which this base layer cannot name without an import cycle.
86
+ * It is a parameter (rather than `this`) purely so the identity check keeps a real type
87
+ * instead of needing a cast; the enumeration itself reads no other module state.
88
+ */
89
+ export declare function collectOccupiedStoreNames(tables: Iterable<StoreTable>, db: Database, schemaName: string, owner: object): Map<string, string>;
90
+ export declare abstract class StoreModuleBase {
91
+ protected provider: KVStoreProvider;
92
+ protected stores: Map<string, KVStore>;
93
+ /**
94
+ * The single transaction coordinator shared by every {@link StoreTable} this
95
+ * module owns — the unit of cross-table atomicity (see {@link getCoordinator}).
96
+ * Lives for the module's lifetime: a single table's drop must NOT evict it
97
+ * (sibling tables still use it); only `StoreModule.closeAll` clears it.
98
+ */
99
+ protected moduleCoordinator?: TransactionCoordinator;
100
+ protected tables: Map<string, StoreTable>;
101
+ protected eventEmitter?: StoreEventEmitter;
102
+ /**
103
+ * Optional listener wired by the host (the worker) to forward a logical
104
+ * `apply schema` lens deployment to the sync layer's lifecycle bookkeeping.
105
+ * The store module is the only basis-backing host with both persistence and a
106
+ * `db` handle, so it is the forwarder; `@quereus/store` must not depend on
107
+ * `@quereus/sync`, so the listener is a plain callback the worker binds.
108
+ */
109
+ protected lensDeploymentListener?: LensDeploymentListener;
110
+ /** Unsubscribe thunk for the engine `SchemaChangeNotifier` listener, set on first hook with a `db`. */
111
+ protected schemaListenerUnsub?: () => void;
112
+ /** The `Database` whose notifier we subscribed to. One module instance serves one `Database`. */
113
+ protected subscribedDb?: Database;
114
+ /**
115
+ * Serialized chain of pending catalog writes triggered by engine schema-change
116
+ * events (catalog-only tag swaps). `notifyChange` invokes listeners synchronously
117
+ * and does not await them, so the actual read-compare-write runs here, in order,
118
+ * and is drained by `closeAll` (and `whenCatalogPersisted`) before the provider closes.
119
+ */
120
+ protected persistQueue: Promise<unknown>;
121
+ /**
122
+ * The JSON-serialized stale-MV set last enqueued onto {@link persistQueue} (the
123
+ * `\x00meta\x00stale_mvs` value), or `undefined` when no baseline is established
124
+ * for the current subscription. The schema-change listener compares each
125
+ * recomputed set against this so an unrelated `table_modified` (e.g. a tag swap
126
+ * that changes no MV's staleness) costs no extra fsync. Reset to `undefined`
127
+ * whenever a fresh `Database` is subscribed (`StoreModuleSchemaSync.ensureSchemaSubscription`) so
128
+ * a reopened module re-establishes the baseline from the first relevant event (or
129
+ * the rehydrate tail).
130
+ */
131
+ protected lastPersistedStaleMvs: string | undefined;
132
+ /**
133
+ * Whether the provider exposes an atomic cross-store commit domain
134
+ * ({@link KVStoreProvider.beginAtomicBatch}) — the capability the MV adopt fast
135
+ * path's gate-5 drop hinges on. Fixed for the provider's lifetime, so cached at
136
+ * construction (and used as the single gate for both writing AND reading the durable
137
+ * stale-MV set). Gating the **write** on it is load-bearing for soundness, not just an
138
+ * fsync saving: only an atomic-capable session — whose commits can never tear a source
139
+ * from its same-module backing — ever writes the durable set, so the set a later atomic
140
+ * session reads was necessarily written under that same no-tear guarantee. A persistent
141
+ * non-atomic provider therefore can never leave a torn-but-"not-stale" set for an atomic
142
+ * reopen to trust (it writes none); such a store falls back to the clean-shutdown marker.
143
+ */
144
+ protected readonly atomicProvider: boolean;
145
+ constructor(provider: KVStoreProvider, eventEmitter?: StoreEventEmitter);
146
+ /**
147
+ * Get the KVStoreProvider used by this module.
148
+ */
149
+ getProvider(): KVStoreProvider;
150
+ /**
151
+ * Get the event emitter for this module.
152
+ */
153
+ getEventEmitter(): StoreEventEmitter | undefined;
154
+ /**
155
+ * Get a table by schema and name.
156
+ */
157
+ getTable(schemaName: string, tableName: string): StoreTable | undefined;
158
+ /**
159
+ * Parse module configuration from vtab args.
160
+ */
161
+ protected parseConfig(args: Record<string, SqlValue> | undefined): StoreModuleConfig;
162
+ /**
163
+ * Get or create a data store for a table.
164
+ */
165
+ getStore(schemaName: string, tableName: string, _config: StoreTableConfig): Promise<KVStore>;
166
+ /**
167
+ * Get or create an index store for a table.
168
+ */
169
+ getIndexStore(schemaName: string, tableName: string, indexName: string): Promise<KVStore>;
170
+ /**
171
+ * Get or create a stats store for a table.
172
+ */
173
+ getStatsStore(schemaName: string, tableName: string): Promise<KVStore>;
174
+ /**
175
+ * Get (lazily constructing) the SINGLE transaction coordinator shared by every
176
+ * table this module owns — the unit of cross-table atomicity. Every
177
+ * {@link StoreTable} attaches to the same instance, so a transaction touching
178
+ * several of the module's tables commits/rolls back as one all-or-nothing
179
+ * batch.
180
+ *
181
+ * Synchronously constructible: ops are addressed by explicit store handle, so
182
+ * the coordinator never needs to open a store to be created. This lets callers
183
+ * that must stay synchronous (e.g. a `BackingHost.connect()`) obtain a working
184
+ * coordinator before any table's store has been opened.
185
+ */
186
+ getCoordinator(): TransactionCoordinator;
187
+ /**
188
+ * Flush the module's buffered writes before a DDL operation that physically
189
+ * rewrites or relocates storage.
190
+ *
191
+ * Such a DDL cannot honor the coordinator's buffer: pending ops are
192
+ * `(keyBytes, valueBytes, store)` triples computed at DML time under the
193
+ * PRE-DDL schema, and every physical rewrite (`StoreTable.rekeyRows` /
194
+ * `migrateRows`, the provider's directory move) reads and writes the COMMITTED
195
+ * store directly. Replaying stale-schema ops over the rewritten store on the
196
+ * eventual commit corrupts, loses, or misfiles those rows. So an `ALTER TABLE`
197
+ * that touches storage is effectively DDL-committing on a store-backed table:
198
+ * flush NOW, before the first physical read, so the rewrite sees every live row
199
+ * and there is nothing left to replay afterwards.
200
+ *
201
+ * Consequences, both deliberate:
202
+ * - The coordinator is module-wide, so this commits the WHOLE module
203
+ * transaction — every table's pending ops, not just the altered/renamed
204
+ * table's — in one all-or-nothing batch. An ALTER cannot half-commit some
205
+ * sibling tables.
206
+ * - Validation running AFTER this point (e.g. `ALTER PRIMARY KEY`'s duplicate-key
207
+ * pass — `rekeyRows` pass 1, which must see the flushed rows because a pending
208
+ * insert can itself be the duplicate) throws with the enclosing transaction
209
+ * already flushed. The store stays unmutated — only the transaction is gone.
210
+ * Validation that can read effectively belongs before the call, so the
211
+ * transaction survives it — the SET COLLATE PK re-key does exactly that
212
+ * (`StoreTable.validateRekeyedPrimaryKey` reads the effective rows, pending
213
+ * ops included, before this flush; `rekeyRows` pass 1 is only its backstop).
214
+ *
215
+ * Subsequent `commit()` calls on the same coordinator are no-ops
216
+ * (`inTransaction` is cleared), which keeps the enclosing transaction safe.
217
+ * The savepoint stack is cleared too, so a later `ROLLBACK TO` / `RELEASE` that
218
+ * the engine still broadcasts warns and degrades rather than throwing — see
219
+ * `TransactionCoordinator.rollbackToSavepoint`, which mirrors the memory
220
+ * module's identical posture.
221
+ */
222
+ protected ddlCommitPendingOps(): Promise<void>;
223
+ /**
224
+ * Append a catalog-persistence task to the serialized `persistQueue`, so successive
225
+ * mutations (e.g. SET TAGS (a=1) then SET TAGS ()) apply in order and are drained by
226
+ * `closeAll`/`whenCatalogPersisted` before the provider closes. Errors are
227
+ * swallowed+logged to mirror `notifyChange`'s own try/catch contract — a listener
228
+ * rejection must never escape.
229
+ */
230
+ protected enqueuePersist(work: () => Promise<void>): void;
231
+ /**
232
+ * Resolve once all catalog writes queued by async schema-change listeners
233
+ * (catalog-only tag swaps) have settled. A durability barrier: `closeAll` awaits
234
+ * it internally; callers/tests that need the persisted catalog current without a
235
+ * full close can await it directly. Never rejects — queued errors are logged in
236
+ * the chain.
237
+ */
238
+ whenCatalogPersisted(): Promise<void>;
239
+ /**
240
+ * The physical index-store names a table owns for DROP / RENAME TABLE to relocate
241
+ * or reclaim: the MATERIALIZED index set — every explicit `CREATE INDEX` PLUS the
242
+ * hidden `_uc_*` realizing each plain UNIQUE. Reading only `getSchema().indexes`
243
+ * (the engine-facing schema, which carries no `_uc_*`) would strand a `_uc_*` store
244
+ * on disk after DROP TABLE, and — worse — after RENAME TABLE leave the renamed
245
+ * table seeking a freshly-created EMPTY `_uc_*` store, silently accepting a
246
+ * duplicate of a pre-rename row. Prefers the cached StoreTable's own materialized
247
+ * schema; falls back to materializing the schema-manager copy when the instance is
248
+ * already gone.
249
+ */
250
+ protected materializedIndexNames(table: StoreTable | undefined, fallback: TableSchema | undefined): string[];
251
+ /**
252
+ * Throws `StatusCode.ERROR` when `candidate` (a physical store name produced by
253
+ * the key-builder for an object about to be created/renamed) already names an
254
+ * existing data or index store in `schemaName`. `candidateDesc` describes the
255
+ * incoming logical object; the message names the candidate physical store and
256
+ * both conflicting logical objects and is actionable (rename one of them).
257
+ *
258
+ * Must run BEFORE any storage side-effect (`getStore` / `getIndexStore` / the
259
+ * physical relocation): the provider opens/creates the store eagerly, so a
260
+ * guard that ran after would already have aliased the colliding object's store.
261
+ *
262
+ * Callers checking several candidates against the same occupancy (renameTable
263
+ * checks the new data store plus every relocated index store) pass a
264
+ * precomputed `occupied` map so the occupancy is collected once, not per call.
265
+ */
266
+ protected assertStoreNameFree(db: Database, schemaName: string, candidate: string, candidateDesc: string, occupied?: Map<string, string>): void;
267
+ }
268
+ //# sourceMappingURL=store-module-base.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store-module-base.d.ts","sourceRoot":"","sources":["../../../src/common/store-module-base.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,362 @@
1
+ /**
2
+ * Module-wide state and storage plumbing shared by every layer of the generic
3
+ * KVStore-backed virtual-table module: the provider handle, the cached store and
4
+ * {@link StoreTable} maps, the single shared transaction coordinator, the
5
+ * serialized catalog-write queue, and the physical-store-name collision guard.
6
+ *
7
+ * Base of the store-module layer chain:
8
+ * StoreModuleBase -> StoreModuleCatalog -> StoreModuleSchemaSync -> StoreModuleIndex
9
+ * -> StoreModuleAlterColumn -> StoreModuleAlter -> StoreModuleRename -> StoreModule
10
+ */
11
+ import { QuereusError, StatusCode } from '@quereus/quereus';
12
+ import { TransactionCoordinator } from './transaction.js';
13
+ import { withImplicitUniqueIndexes } from './implicit-unique-index.js';
14
+ import { buildDataStoreName, buildIndexStoreName } from './key-builder.js';
15
+ /**
16
+ * Default serialized-byte budget for a single index-build write batch. Bounds
17
+ * heap directly: `buildIndexEntries` (store-module-index-build.ts) flushes and starts a fresh
18
+ * batch whenever the accumulated key bytes cross this, so a table larger than
19
+ * memory never buffers its whole index in one batch. A byte budget (rather than a
20
+ * fixed entry count) is used because index entries vary in size, so bytes bound
21
+ * memory more tightly. 8 MiB trades a modest number of extra flushes against peak
22
+ * memory; override per store with `using store (max_batch_bytes = …)`.
23
+ */
24
+ export const DEFAULT_MAX_BATCH_BYTES = 8 * 1024 * 1024; // 8 MiB
25
+ /**
26
+ * Resolve the index-build batch byte budget from a `max_batch_bytes` module arg.
27
+ * A missing, non-numeric, or non-positive value falls back to
28
+ * {@link DEFAULT_MAX_BATCH_BYTES} — a zero or negative budget must NEVER disable
29
+ * flushing, which would restore the unbounded single-batch behavior this guards
30
+ * against.
31
+ */
32
+ function resolveMaxBatchBytes(raw) {
33
+ let n;
34
+ if (typeof raw === 'number')
35
+ n = raw;
36
+ else if (typeof raw === 'bigint')
37
+ n = Number(raw);
38
+ else if (typeof raw === 'string' && raw.trim() !== '')
39
+ n = Number(raw);
40
+ else
41
+ return DEFAULT_MAX_BATCH_BYTES;
42
+ if (!Number.isFinite(n) || n <= 0)
43
+ return DEFAULT_MAX_BATCH_BYTES;
44
+ return Math.floor(n);
45
+ }
46
+ /**
47
+ * Build the set of physical store names this module's data/index stores
48
+ * currently occupy in `schemaName`, mapping each name to a human description
49
+ * of the logical object that owns it (for sited collision messages).
50
+ *
51
+ * Physical store names are built by string concatenation (`{schema}.{table}` /
52
+ * `{schema}.{table}_idx_{index}`) and the `_idx_` delimiter is itself a legal
53
+ * substring of any identifier, so two distinct logical objects (e.g. index
54
+ * `archive` on `t` and a sibling table `t_idx_archive`) can collapse to the
55
+ * same physical name. This set is the authoritative occupancy used by
56
+ * {@link StoreModuleBase.assertStoreNameFree} to reject such a collision at CREATE time.
57
+ *
58
+ * The occupancy is the union of two sources, robust to lazy connection and the
59
+ * isolation wrapper (see the ticket's "Enumeration source" section):
60
+ * 1. `tables` — every store table the owning module touched this session.
61
+ * 2. the target schema's catalog tables whose `vtabModule === owner` and which
62
+ * are not views — store-backed tables not yet lazily connected.
63
+ * Names embed the schema prefix, so cross-schema entries never collide and no
64
+ * per-schema filter on `tables` is needed. Memory-backed siblings and
65
+ * views own no store in this provider and are excluded (the `=== owner` /
66
+ * `!isView` filter) to avoid false-positive rejects.
67
+ *
68
+ * No self-exclusion: at each guarded call the candidate object is not yet
69
+ * registered (create/createIndex run before the engine adds it) so it cannot
70
+ * self-collide; and for renameTable the renamed table's OWN stores stay in the
71
+ * set deliberately. Any overlap between a name the rename introduces and an
72
+ * own current store is a footprint-swap rename (`t` with index `x` → `t_idx_x`,
73
+ * or table `u_idx_x` with index `x` → `u`) that providers cannot relocate
74
+ * safely — relocation order determines whether a source is clobbered before it
75
+ * is moved — while no benign rename produces such an overlap. Keeping own
76
+ * stores in the set therefore causes no false rejects and keeps the
77
+ * reject-before-any-side-effect guarantee uniform.
78
+ *
79
+ * `owner` is the module instance whose stores these are — the concrete `StoreModule` at
80
+ * the top of the layer chain, which this base layer cannot name without an import cycle.
81
+ * It is a parameter (rather than `this`) purely so the identity check keeps a real type
82
+ * instead of needing a cast; the enumeration itself reads no other module state.
83
+ */
84
+ // NOTE: the builders below carry the unpaired-surrogate identifier guard, so this
85
+ // enumeration can THROW on an EXISTING schema object, not just on the incoming one —
86
+ // an unrelated CREATE/RENAME in the same schema would then fail with a confusing
87
+ // "cannot store the identifier ..." error. Unreachable today: `create` throws before
88
+ // the table is registered, and a catalog entry written before the guard existed was
89
+ // folded to a real U+FFFD character, which passes. If a lone-surrogate-named table ever
90
+ // does reach the schema manager by another route, skip such an object here (it occupies
91
+ // no store) rather than letting the enumeration throw.
92
+ export function collectOccupiedStoreNames(tables, db, schemaName, owner) {
93
+ const names = new Map();
94
+ const add = (s) => {
95
+ const dataName = buildDataStoreName(s.schemaName, s.name);
96
+ if (!names.has(dataName)) {
97
+ names.set(dataName, `data store of table '${s.schemaName}.${s.name}'`);
98
+ }
99
+ for (const idx of s.indexes ?? []) {
100
+ const idxName = buildIndexStoreName(s.schemaName, s.name, idx.name);
101
+ if (!names.has(idxName)) {
102
+ names.set(idxName, `index store of index '${idx.name}' on table '${s.schemaName}.${s.name}'`);
103
+ }
104
+ }
105
+ };
106
+ for (const t of tables) {
107
+ add(t.getSchema());
108
+ }
109
+ for (const t of db.schemaManager.getSchemaOrFail(schemaName).getAllTables()) {
110
+ if (t.vtabModule !== owner || t.isView)
111
+ continue;
112
+ add(t);
113
+ }
114
+ return names;
115
+ }
116
+ export class StoreModuleBase {
117
+ provider;
118
+ stores = new Map();
119
+ /**
120
+ * The single transaction coordinator shared by every {@link StoreTable} this
121
+ * module owns — the unit of cross-table atomicity (see {@link getCoordinator}).
122
+ * Lives for the module's lifetime: a single table's drop must NOT evict it
123
+ * (sibling tables still use it); only `StoreModule.closeAll` clears it.
124
+ */
125
+ moduleCoordinator;
126
+ tables = new Map();
127
+ eventEmitter;
128
+ /**
129
+ * Optional listener wired by the host (the worker) to forward a logical
130
+ * `apply schema` lens deployment to the sync layer's lifecycle bookkeeping.
131
+ * The store module is the only basis-backing host with both persistence and a
132
+ * `db` handle, so it is the forwarder; `@quereus/store` must not depend on
133
+ * `@quereus/sync`, so the listener is a plain callback the worker binds.
134
+ */
135
+ lensDeploymentListener;
136
+ /** Unsubscribe thunk for the engine `SchemaChangeNotifier` listener, set on first hook with a `db`. */
137
+ schemaListenerUnsub;
138
+ /** The `Database` whose notifier we subscribed to. One module instance serves one `Database`. */
139
+ subscribedDb;
140
+ /**
141
+ * Serialized chain of pending catalog writes triggered by engine schema-change
142
+ * events (catalog-only tag swaps). `notifyChange` invokes listeners synchronously
143
+ * and does not await them, so the actual read-compare-write runs here, in order,
144
+ * and is drained by `closeAll` (and `whenCatalogPersisted`) before the provider closes.
145
+ */
146
+ persistQueue = Promise.resolve();
147
+ /**
148
+ * The JSON-serialized stale-MV set last enqueued onto {@link persistQueue} (the
149
+ * `\x00meta\x00stale_mvs` value), or `undefined` when no baseline is established
150
+ * for the current subscription. The schema-change listener compares each
151
+ * recomputed set against this so an unrelated `table_modified` (e.g. a tag swap
152
+ * that changes no MV's staleness) costs no extra fsync. Reset to `undefined`
153
+ * whenever a fresh `Database` is subscribed (`StoreModuleSchemaSync.ensureSchemaSubscription`) so
154
+ * a reopened module re-establishes the baseline from the first relevant event (or
155
+ * the rehydrate tail).
156
+ */
157
+ lastPersistedStaleMvs;
158
+ /**
159
+ * Whether the provider exposes an atomic cross-store commit domain
160
+ * ({@link KVStoreProvider.beginAtomicBatch}) — the capability the MV adopt fast
161
+ * path's gate-5 drop hinges on. Fixed for the provider's lifetime, so cached at
162
+ * construction (and used as the single gate for both writing AND reading the durable
163
+ * stale-MV set). Gating the **write** on it is load-bearing for soundness, not just an
164
+ * fsync saving: only an atomic-capable session — whose commits can never tear a source
165
+ * from its same-module backing — ever writes the durable set, so the set a later atomic
166
+ * session reads was necessarily written under that same no-tear guarantee. A persistent
167
+ * non-atomic provider therefore can never leave a torn-but-"not-stale" set for an atomic
168
+ * reopen to trust (it writes none); such a store falls back to the clean-shutdown marker.
169
+ */
170
+ atomicProvider;
171
+ constructor(provider, eventEmitter) {
172
+ this.provider = provider;
173
+ this.eventEmitter = eventEmitter;
174
+ this.atomicProvider = typeof provider.beginAtomicBatch === 'function';
175
+ }
176
+ /**
177
+ * Get the KVStoreProvider used by this module.
178
+ */
179
+ getProvider() {
180
+ return this.provider;
181
+ }
182
+ /**
183
+ * Get the event emitter for this module.
184
+ */
185
+ getEventEmitter() {
186
+ return this.eventEmitter;
187
+ }
188
+ /**
189
+ * Get a table by schema and name.
190
+ */
191
+ getTable(schemaName, tableName) {
192
+ const tableKey = `${schemaName}.${tableName}`.toLowerCase();
193
+ return this.tables.get(tableKey);
194
+ }
195
+ /**
196
+ * Parse module configuration from vtab args.
197
+ */
198
+ parseConfig(args) {
199
+ return {
200
+ collation: args?.collation || 'NOCASE',
201
+ // Index-build batch budget; malformed / non-positive clamps to the default
202
+ // so a bad arg can never disable the bounded-memory flush (see resolveMaxBatchBytes).
203
+ maxBatchBytes: resolveMaxBatchBytes(args?.max_batch_bytes),
204
+ };
205
+ }
206
+ // --- Engine schema-change subscription (catalog-only tag persistence) ---
207
+ /**
208
+ * Get or create a data store for a table.
209
+ */
210
+ async getStore(schemaName, tableName, _config) {
211
+ const tableKey = `${schemaName}.${tableName}`.toLowerCase();
212
+ let store = this.stores.get(tableKey);
213
+ if (!store) {
214
+ store = await this.provider.getStore(schemaName, tableName);
215
+ if (!store) {
216
+ throw new Error(`Provider.getStore returned null/undefined for ${tableKey}`);
217
+ }
218
+ this.stores.set(tableKey, store);
219
+ }
220
+ return store;
221
+ }
222
+ /**
223
+ * Get or create an index store for a table.
224
+ */
225
+ async getIndexStore(schemaName, tableName, indexName) {
226
+ return this.provider.getIndexStore(schemaName, tableName, indexName);
227
+ }
228
+ /**
229
+ * Get or create a stats store for a table.
230
+ */
231
+ async getStatsStore(schemaName, tableName) {
232
+ return this.provider.getStatsStore(schemaName, tableName);
233
+ }
234
+ /**
235
+ * Get (lazily constructing) the SINGLE transaction coordinator shared by every
236
+ * table this module owns — the unit of cross-table atomicity. Every
237
+ * {@link StoreTable} attaches to the same instance, so a transaction touching
238
+ * several of the module's tables commits/rolls back as one all-or-nothing
239
+ * batch.
240
+ *
241
+ * Synchronously constructible: ops are addressed by explicit store handle, so
242
+ * the coordinator never needs to open a store to be created. This lets callers
243
+ * that must stay synchronous (e.g. a `BackingHost.connect()`) obtain a working
244
+ * coordinator before any table's store has been opened.
245
+ */
246
+ getCoordinator() {
247
+ if (!this.moduleCoordinator) {
248
+ this.moduleCoordinator = new TransactionCoordinator(this.eventEmitter,
249
+ // Re-evaluated per commit (see TransactionCoordinator.atomicBatchFactory)
250
+ // so a provider that gains/loses the capability is always honored.
251
+ () => this.provider.beginAtomicBatch?.());
252
+ }
253
+ return this.moduleCoordinator;
254
+ }
255
+ /**
256
+ * Flush the module's buffered writes before a DDL operation that physically
257
+ * rewrites or relocates storage.
258
+ *
259
+ * Such a DDL cannot honor the coordinator's buffer: pending ops are
260
+ * `(keyBytes, valueBytes, store)` triples computed at DML time under the
261
+ * PRE-DDL schema, and every physical rewrite (`StoreTable.rekeyRows` /
262
+ * `migrateRows`, the provider's directory move) reads and writes the COMMITTED
263
+ * store directly. Replaying stale-schema ops over the rewritten store on the
264
+ * eventual commit corrupts, loses, or misfiles those rows. So an `ALTER TABLE`
265
+ * that touches storage is effectively DDL-committing on a store-backed table:
266
+ * flush NOW, before the first physical read, so the rewrite sees every live row
267
+ * and there is nothing left to replay afterwards.
268
+ *
269
+ * Consequences, both deliberate:
270
+ * - The coordinator is module-wide, so this commits the WHOLE module
271
+ * transaction — every table's pending ops, not just the altered/renamed
272
+ * table's — in one all-or-nothing batch. An ALTER cannot half-commit some
273
+ * sibling tables.
274
+ * - Validation running AFTER this point (e.g. `ALTER PRIMARY KEY`'s duplicate-key
275
+ * pass — `rekeyRows` pass 1, which must see the flushed rows because a pending
276
+ * insert can itself be the duplicate) throws with the enclosing transaction
277
+ * already flushed. The store stays unmutated — only the transaction is gone.
278
+ * Validation that can read effectively belongs before the call, so the
279
+ * transaction survives it — the SET COLLATE PK re-key does exactly that
280
+ * (`StoreTable.validateRekeyedPrimaryKey` reads the effective rows, pending
281
+ * ops included, before this flush; `rekeyRows` pass 1 is only its backstop).
282
+ *
283
+ * Subsequent `commit()` calls on the same coordinator are no-ops
284
+ * (`inTransaction` is cleared), which keeps the enclosing transaction safe.
285
+ * The savepoint stack is cleared too, so a later `ROLLBACK TO` / `RELEASE` that
286
+ * the engine still broadcasts warns and degrades rather than throwing — see
287
+ * `TransactionCoordinator.rollbackToSavepoint`, which mirrors the memory
288
+ * module's identical posture.
289
+ */
290
+ async ddlCommitPendingOps() {
291
+ if (this.moduleCoordinator?.isInTransaction()) {
292
+ await this.moduleCoordinator.commit();
293
+ }
294
+ }
295
+ /**
296
+ * Append a catalog-persistence task to the serialized `persistQueue`, so successive
297
+ * mutations (e.g. SET TAGS (a=1) then SET TAGS ()) apply in order and are drained by
298
+ * `closeAll`/`whenCatalogPersisted` before the provider closes. Errors are
299
+ * swallowed+logged to mirror `notifyChange`'s own try/catch contract — a listener
300
+ * rejection must never escape.
301
+ */
302
+ enqueuePersist(work) {
303
+ this.persistQueue = this.persistQueue
304
+ .then(work)
305
+ .catch((err) => {
306
+ const message = err instanceof Error ? err.message : String(err);
307
+ console.warn(`[StoreModule] Failed to persist catalog DDL after schema change: ${message}`);
308
+ });
309
+ }
310
+ /**
311
+ * Resolve once all catalog writes queued by async schema-change listeners
312
+ * (catalog-only tag swaps) have settled. A durability barrier: `closeAll` awaits
313
+ * it internally; callers/tests that need the persisted catalog current without a
314
+ * full close can await it directly. Never rejects — queued errors are logged in
315
+ * the chain.
316
+ */
317
+ async whenCatalogPersisted() {
318
+ await this.persistQueue;
319
+ }
320
+ /**
321
+ * The physical index-store names a table owns for DROP / RENAME TABLE to relocate
322
+ * or reclaim: the MATERIALIZED index set — every explicit `CREATE INDEX` PLUS the
323
+ * hidden `_uc_*` realizing each plain UNIQUE. Reading only `getSchema().indexes`
324
+ * (the engine-facing schema, which carries no `_uc_*`) would strand a `_uc_*` store
325
+ * on disk after DROP TABLE, and — worse — after RENAME TABLE leave the renamed
326
+ * table seeking a freshly-created EMPTY `_uc_*` store, silently accepting a
327
+ * duplicate of a pre-rename row. Prefers the cached StoreTable's own materialized
328
+ * schema; falls back to materializing the schema-manager copy when the instance is
329
+ * already gone.
330
+ */
331
+ materializedIndexNames(table, fallback) {
332
+ const schema = table
333
+ ? table.getMaterializedSchema()
334
+ : (fallback ? withImplicitUniqueIndexes(fallback) : undefined);
335
+ return (schema?.indexes ?? []).map(i => i.name);
336
+ }
337
+ /**
338
+ * Throws `StatusCode.ERROR` when `candidate` (a physical store name produced by
339
+ * the key-builder for an object about to be created/renamed) already names an
340
+ * existing data or index store in `schemaName`. `candidateDesc` describes the
341
+ * incoming logical object; the message names the candidate physical store and
342
+ * both conflicting logical objects and is actionable (rename one of them).
343
+ *
344
+ * Must run BEFORE any storage side-effect (`getStore` / `getIndexStore` / the
345
+ * physical relocation): the provider opens/creates the store eagerly, so a
346
+ * guard that ran after would already have aliased the colliding object's store.
347
+ *
348
+ * Callers checking several candidates against the same occupancy (renameTable
349
+ * checks the new data store plus every relocated index store) pass a
350
+ * precomputed `occupied` map so the occupancy is collected once, not per call.
351
+ */
352
+ assertStoreNameFree(db, schemaName, candidate, candidateDesc, occupied) {
353
+ const occupiedBy = (occupied ?? collectOccupiedStoreNames(this.tables.values(), db, schemaName, this)).get(candidate);
354
+ if (occupiedBy !== undefined) {
355
+ throw new QuereusError(`Physical store-name collision: the ${candidateDesc} would map to physical store `
356
+ + `'${candidate}', which already backs the ${occupiedBy}. These two objects would `
357
+ + `share one physical store and silently corrupt each other. Rename the table or the `
358
+ + `colliding index.`, StatusCode.ERROR);
359
+ }
360
+ }
361
+ }
362
+ //# sourceMappingURL=store-module-base.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store-module-base.js","sourceRoot":"","sources":["../../../src/common/store-module-base.ts"],"names":[],"mappings":"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"}