@quereus/store 4.10.0 → 4.12.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 (85) hide show
  1. package/README.md +143 -13
  2. package/dist/src/common/cached-kv-store.d.ts +16 -0
  3. package/dist/src/common/cached-kv-store.d.ts.map +1 -1
  4. package/dist/src/common/cached-kv-store.js +64 -6
  5. package/dist/src/common/cached-kv-store.js.map +1 -1
  6. package/dist/src/common/index.d.ts +2 -0
  7. package/dist/src/common/index.d.ts.map +1 -1
  8. package/dist/src/common/index.js +4 -0
  9. package/dist/src/common/index.js.map +1 -1
  10. package/dist/src/common/key-builder.d.ts +9 -6
  11. package/dist/src/common/key-builder.d.ts.map +1 -1
  12. package/dist/src/common/key-builder.js +17 -6
  13. package/dist/src/common/key-builder.js.map +1 -1
  14. package/dist/src/common/kv-store.d.ts +142 -4
  15. package/dist/src/common/kv-store.d.ts.map +1 -1
  16. package/dist/src/common/kv-store.js +21 -1
  17. package/dist/src/common/kv-store.js.map +1 -1
  18. package/dist/src/common/memory-store.d.ts +1 -0
  19. package/dist/src/common/memory-store.d.ts.map +1 -1
  20. package/dist/src/common/memory-store.js +16 -1
  21. package/dist/src/common/memory-store.js.map +1 -1
  22. package/dist/src/common/paged-iterate.d.ts +42 -0
  23. package/dist/src/common/paged-iterate.d.ts.map +1 -0
  24. package/dist/src/common/paged-iterate.js +113 -0
  25. package/dist/src/common/paged-iterate.js.map +1 -0
  26. package/dist/src/common/pk-key-resolution.d.ts +17 -4
  27. package/dist/src/common/pk-key-resolution.d.ts.map +1 -1
  28. package/dist/src/common/pk-key-resolution.js +23 -8
  29. package/dist/src/common/pk-key-resolution.js.map +1 -1
  30. package/dist/src/common/serialization.d.ts +11 -1
  31. package/dist/src/common/serialization.d.ts.map +1 -1
  32. package/dist/src/common/serialization.js +114 -31
  33. package/dist/src/common/serialization.js.map +1 -1
  34. package/dist/src/common/store-module-access-plan.d.ts +1 -1
  35. package/dist/src/common/store-module-access-plan.d.ts.map +1 -1
  36. package/dist/src/common/store-module-access-plan.js +434 -105
  37. package/dist/src/common/store-module-access-plan.js.map +1 -1
  38. package/dist/src/common/store-module-alter-column.d.ts.map +1 -1
  39. package/dist/src/common/store-module-alter-column.js +25 -2
  40. package/dist/src/common/store-module-alter-column.js.map +1 -1
  41. package/dist/src/common/store-module-alter.d.ts.map +1 -1
  42. package/dist/src/common/store-module-alter.js +36 -13
  43. package/dist/src/common/store-module-alter.js.map +1 -1
  44. package/dist/src/common/store-module-catalog.d.ts +6 -4
  45. package/dist/src/common/store-module-catalog.d.ts.map +1 -1
  46. package/dist/src/common/store-module-catalog.js +8 -6
  47. package/dist/src/common/store-module-catalog.js.map +1 -1
  48. package/dist/src/common/store-module-index.d.ts +98 -7
  49. package/dist/src/common/store-module-index.d.ts.map +1 -1
  50. package/dist/src/common/store-module-index.js +236 -81
  51. package/dist/src/common/store-module-index.js.map +1 -1
  52. package/dist/src/common/store-module.d.ts.map +1 -1
  53. package/dist/src/common/store-module.js +7 -3
  54. package/dist/src/common/store-module.js.map +1 -1
  55. package/dist/src/common/store-table-base.d.ts +30 -0
  56. package/dist/src/common/store-table-base.d.ts.map +1 -1
  57. package/dist/src/common/store-table-base.js +71 -12
  58. package/dist/src/common/store-table-base.js.map +1 -1
  59. package/dist/src/common/store-table-constraints.js +3 -3
  60. package/dist/src/common/store-table-constraints.js.map +1 -1
  61. package/dist/src/common/store-table-scan.d.ts +208 -98
  62. package/dist/src/common/store-table-scan.d.ts.map +1 -1
  63. package/dist/src/common/store-table-scan.js +353 -105
  64. package/dist/src/common/store-table-scan.js.map +1 -1
  65. package/dist/src/testing/index.d.ts +18 -2
  66. package/dist/src/testing/index.d.ts.map +1 -1
  67. package/dist/src/testing/index.js +17 -2
  68. package/dist/src/testing/index.js.map +1 -1
  69. package/dist/src/testing/kv-conformance.d.ts +101 -3
  70. package/dist/src/testing/kv-conformance.d.ts.map +1 -1
  71. package/dist/src/testing/kv-conformance.js +277 -7
  72. package/dist/src/testing/kv-conformance.js.map +1 -1
  73. package/dist/src/testing/kv-lifecycle.d.ts +22 -0
  74. package/dist/src/testing/kv-lifecycle.d.ts.map +1 -0
  75. package/dist/src/testing/kv-lifecycle.js +12 -0
  76. package/dist/src/testing/kv-lifecycle.js.map +1 -0
  77. package/dist/src/testing/kv-naming-conformance.d.ts +77 -0
  78. package/dist/src/testing/kv-naming-conformance.d.ts.map +1 -0
  79. package/dist/src/testing/kv-naming-conformance.js +208 -0
  80. package/dist/src/testing/kv-naming-conformance.js.map +1 -0
  81. package/dist/src/testing/kv-reclaim-conformance.d.ts +52 -0
  82. package/dist/src/testing/kv-reclaim-conformance.d.ts.map +1 -0
  83. package/dist/src/testing/kv-reclaim-conformance.js +287 -0
  84. package/dist/src/testing/kv-reclaim-conformance.js.map +1 -0
  85. package/package.json +5 -5
@@ -16,7 +16,11 @@ export interface IterateOptions {
16
16
  lt?: Uint8Array;
17
17
  /** Iterate in reverse order. */
18
18
  reverse?: boolean;
19
- /** Maximum number of entries to return. */
19
+ /**
20
+ * Maximum number of entries to return. Omitted means unbounded and `0` means no
21
+ * entries; a NEGATIVE limit is not a valid input — backends disagree on it
22
+ * (`abstract-level` reads `-1` as unbounded, the in-memory store as zero).
23
+ */
20
24
  limit?: number;
21
25
  }
22
26
  /**
@@ -114,6 +118,14 @@ export interface AtomicBatch {
114
118
  /**
115
119
  * Abstract key-value store interface.
116
120
  * Provides sorted key-value storage with range iteration support.
121
+ *
122
+ * BUFFER OWNERSHIP. Every buffer crossing this interface is independent of the store's
123
+ * internal state: `get` and `iterate` hand back buffers the caller may scribble on
124
+ * without corrupting stored data, and `put`/`delete` do not retain the caller's buffer,
125
+ * so mutating it afterwards changes nothing stored. A backend that deserializes per read
126
+ * (LevelDB, IndexedDB) gets this for free; one holding live buffers (the in-memory store,
127
+ * a cache wrapper) must copy at the boundary. Tiers 1 and 3 of
128
+ * `runKVStoreConformance` enforce it.
117
129
  */
118
130
  export interface KVStore {
119
131
  /**
@@ -121,6 +133,34 @@ export interface KVStore {
121
133
  * @returns The value, or undefined if not found.
122
134
  */
123
135
  get(key: Uint8Array): Promise<Uint8Array | undefined>;
136
+ /**
137
+ * Point-read several keys at once.
138
+ *
139
+ * POSITIONAL. `result[i]` is the value for `keys[i]`, `undefined` when that key is
140
+ * absent. The result array is always exactly `keys.length` long — a missing key must
141
+ * never shorten it or shift the ones after it — and a key repeated in `keys` yields
142
+ * its value at every position it occupies. `getMany([])` resolves to `[]` without
143
+ * touching backing storage.
144
+ *
145
+ * ONE ROUND TRIP WHERE THE BACKEND HAS ONE. This exists because the caller-side
146
+ * alternative — awaiting `get` per key — serializes N round trips, and on IndexedDB
147
+ * each `get` opens its own readonly transaction, so resolving the rows behind an
148
+ * index scan cost one transaction PER ROW. A backend with a native multi-get
149
+ * (`abstract-level`) or a way to issue every request on one transaction (IndexedDB)
150
+ * must override; everything else delegates to {@link defaultGetMany}, which is
151
+ * correct but no faster than the loop it replaces.
152
+ *
153
+ * BOUNDED BY THE CALLER. Unlike {@link iterate} there is no internal paging: the
154
+ * implementation reads exactly the keys it was handed, so the CALLER owns peak
155
+ * memory and must pass a bounded batch (`StoreTableScan` pages at
156
+ * `ROW_RESOLUTION_BATCH`).
157
+ *
158
+ * Same buffer-ownership rule as {@link get}: the caller may scribble on what it gets
159
+ * back without corrupting stored data. That extends ACROSS positions — a key repeated
160
+ * in `keys` must come back as two independent buffers, not one object at two indices,
161
+ * or scribbling on one silently rewrites the other.
162
+ */
163
+ getMany(keys: readonly Uint8Array[]): Promise<(Uint8Array | undefined)[]>;
124
164
  /**
125
165
  * Put a key-value pair.
126
166
  * @param options - Optional per-write durability hint (see {@link WriteOptions}).
@@ -138,6 +178,46 @@ export interface KVStore {
138
178
  /**
139
179
  * Iterate over key-value pairs in sorted order.
140
180
  * Keys are compared lexicographically by bytes.
181
+ *
182
+ * BOUNDED PEAK. Peak memory must be independent of how many entries the range
183
+ * holds. An implementation may buffer a FIXED-SIZE batch — IndexedDB pages 256
184
+ * entries at a time, `abstract-level` hands back one entry per `next()` — but it
185
+ * must never read the whole range before yielding the first entry. Reason: a full
186
+ * table scan calls `iterate(buildFullScanBounds())` with NO limit, and the mobile
187
+ * backends that run this code have the least memory headroom of any of them.
188
+ *
189
+ * A backend whose dataset is already wholly resident in memory by construction
190
+ * (`InMemoryKVStore`) satisfies this trivially — the requirement bounds reads from
191
+ * BACKING STORAGE, and such a backend has none.
192
+ *
193
+ * EARLY TERMINATION IS CHEAP. A consumer that stops after k entries must cost
194
+ * roughly k entries of work, not the size of the range: `limit: 10` over a million
195
+ * rows must not read a million rows. Concretely, reads from backing storage must
196
+ * stay within `k + (one batch)`, whatever the implementation's batch size is.
197
+ *
198
+ * TOTAL WORK IS LINEAR. Draining the whole range must read about one entry per
199
+ * entry. A paged backend resumes from the LAST KEY SEEN — never by re-reading from
200
+ * the start and discarding a growing prefix. `limit`/`offset` paging is O(n²) reads
201
+ * yet still looks fine to a peak-memory check, so this is stated separately.
202
+ *
203
+ * EARLY TERMINATION RELEASES RESOURCES. When the consumer `break`s or throws, the
204
+ * returned iterable's `return()`/`throw()` runs; the implementation must close its
205
+ * cursor / transaction / statement there — a `try/finally` around the yield loop —
206
+ * not only on natural exhaustion. After an abandoned iteration the store must still
207
+ * serve `get`/`iterate` normally and `close()` must still resolve.
208
+ *
209
+ * NO SNAPSHOT PROMISE. Batching splits one logical read into several physical ones,
210
+ * so a write committed mid-scan may become visible partway through a scan where a
211
+ * single-shot read could not have shown it. `iterate` therefore does NOT promise a
212
+ * point-in-time view and consumers must not assume one. This is honest about what
213
+ * the stack already does: `StoreTable`'s scan merges the transaction coordinator's
214
+ * pending ops over the committed range, and the store stack declares
215
+ * `readCommittedSnapshot: false`. A real snapshot read is separate, future work.
216
+ *
217
+ * Backends without a streaming cursor (a SQL `select` hands back a whole result set)
218
+ * should page with {@link pagedIterate} rather than re-deriving the resume edge.
219
+ * `runKVStoreConformance`'s bounded-iteration tier enforces all of the above for any
220
+ * backend whose adapter supplies a read meter.
141
221
  */
142
222
  iterate(options?: IterateOptions): AsyncIterable<KVEntry>;
143
223
  /**
@@ -154,6 +234,25 @@ export interface KVStore {
154
234
  */
155
235
  approximateCount(options?: IterateOptions): Promise<number>;
156
236
  }
237
+ /**
238
+ * The {@link KVStore.getMany} every backend without a real batch point-read delegates to:
239
+ * one `get` per key, issued together and awaited as a group.
240
+ *
241
+ * Concurrency is all it buys — the reads still cost one round trip each — but it is the
242
+ * CORRECT default everywhere, so adding `getMany` to the interface cannot break a backend
243
+ * and each one opts into a genuine batch when it has one to opt into. Reading through
244
+ * `store.get` (rather than the backend's internals) also keeps a wrapper's own `get` in
245
+ * the path, which is what lets `CachedKVStore` and the counting test doubles keep working.
246
+ *
247
+ * `Promise.all([])` resolves to `[]`, satisfying the empty-input rule without a special case.
248
+ *
249
+ * NOTE: because no `get` is issued for an empty key list, a backend delegating here answers
250
+ * `getMany([])` with `[]` even after `close()`, where LevelDB and IndexedDB check open first
251
+ * and reject. Unspecified rather than wrong — every caller guards the empty case before it
252
+ * reaches a store (`StoreTableBase.readEffectiveRowsByKeys`). Pin one behavior in tier 8 if
253
+ * a caller ever hands an empty batch to a closed store.
254
+ */
255
+ export declare function defaultGetMany(store: Pick<KVStore, 'get'>, keys: readonly Uint8Array[]): Promise<(Uint8Array | undefined)[]>;
157
256
  /**
158
257
  * Factory function to open a KVStore.
159
258
  */
@@ -181,7 +280,15 @@ export interface KVStoreOptions {
181
280
  * {prefix}.__stats__ - Unified stats store (row counts for all tables)
182
281
  * __catalog__ - Catalog store (DDL metadata)
183
282
  *
184
- * Implementations should manage store lifecycle and caching.
283
+ * An implementation MUST build those names with `buildDataStoreName` /
284
+ * `buildIndexStoreName` rather than composing its own. It may prefix or escape the result
285
+ * to reach a legal native name, but that escaping must be INJECTIVE (decodable in
286
+ * principle) — two distinct logical names must never produce one native name. A native
287
+ * namespace that genuinely cannot represent a name must REJECT it, never fold it onto
288
+ * another. `runStoreNameDistinctness` (`@quereus/store/testing`) enforces this behaviorally.
289
+ *
290
+ * Implementations should manage store lifecycle and caching. Cache by the BUILT store name,
291
+ * so two spellings of one logical store cannot produce two cached handles.
185
292
  */
186
293
  export interface KVStoreProvider {
187
294
  /**
@@ -236,14 +343,45 @@ export interface KVStoreProvider {
236
343
  closeAll(): Promise<void>;
237
344
  /**
238
345
  * Delete an index store entirely (when dropping an index).
346
+ *
347
+ * "Delete" means ERASE, not "close the handle": see {@link deleteTableStores} for
348
+ * exactly what an implementation has to accomplish. Closing a cached handle is not
349
+ * enough — stores are re-opened lazily by name, so the next index created under this
350
+ * name would read the dropped index's entries.
351
+ *
239
352
  * @param schemaName - The schema name
240
353
  * @param tableName - The table name
241
354
  * @param indexName - The index name
242
355
  */
243
356
  deleteIndexStore?(schemaName: string, tableName: string, indexName: string): Promise<void>;
244
357
  /**
245
- * Delete all stores for a table (data, indexes, stats).
246
- * Called when dropping a table.
358
+ * Delete a table's data store and the named index stores. Called when dropping a table.
359
+ *
360
+ * NOT the table's row-count statistics: those live as one ENTRY in the unified
361
+ * `__stats__` store (see {@link getStatsStore}), which a provider must leave alone —
362
+ * removing that entry is the caller's business, not a store deletion.
363
+ *
364
+ * "Delete" means ERASE the physical storage, not merely drop a cached handle. Three
365
+ * requirements, all of them behavioral:
366
+ *
367
+ * - Each named store, RE-OPENED afterwards under the same name via
368
+ * `getStore`/`getIndexStore`, must read back EMPTY — no entry from a point `get`,
369
+ * none from `iterate`. Handles are re-opened lazily by name, so a provider that
370
+ * only closes its handle leaves the bytes in place and the next table created
371
+ * under that name comes back holding the dropped table's rows.
372
+ * - Deleting a store that was never created is a NO-OP, not an error. The engine
373
+ * calls these speculatively on its reclaim paths (see
374
+ * `StoreModuleBase.reclaimDetachedTable`), including with index names whose stores
375
+ * never materialized.
376
+ * - Every OTHER store must be untouched — sibling tables, the table's own index
377
+ * stores that were not named, and the reserved `__stats__` / `__catalog__` stores.
378
+ *
379
+ * Reclaiming bytes on disk is a separate, per-provider concern: a provider may leave an
380
+ * emptied database directory or an unshrunk file behind and still be correct here.
381
+ *
382
+ * Pinned behaviorally by the shared `runStoreReclaimConformance` battery
383
+ * (`@quereus/store/testing`), which every plugin's `test/conformance.spec.ts`
384
+ * registers — the way `runStoreNameDistinctness` pins the naming property.
247
385
  *
248
386
  * `indexNames` is the authoritative list of the table's secondary-index names
249
387
  * (from `tableSchema.indexes`). Implementations MUST build exact index store
@@ -1 +1 @@
1
- {"version":3,"file":"kv-store.d.ts","sourceRoot":"","sources":["../../../src/common/kv-store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"kv-store.d.ts","sourceRoot":"","sources":["../../../src/common/kv-store.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B,gEAAgE;IAChE,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB,6BAA6B;IAC7B,EAAE,CAAC,EAAE,UAAU,CAAC;IAChB,2BAA2B;IAC3B,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB,wDAAwD;IACxD,EAAE,CAAC,EAAE,UAAU,CAAC;IAChB,gCAAgC;IAChC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,OAAO;IACvB,GAAG,EAAE,UAAU,CAAC;IAChB,KAAK,EAAE,UAAU,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC5B;;;;;;;;;;;;OAYG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;CACf;AAED;;GAEG;AACH,MAAM,MAAM,OAAO,GAChB;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,GAAG,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,UAAU,CAAA;CAAE,GACnD;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,GAAG,EAAE,UAAU,CAAA;CAAE,CAAC;AAEvC;;;;;;;GAOG;AACH,MAAM,WAAW,UAAU;IAC1B,mFAAmF;IACnF,GAAG,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9C,sFAAsF;IACtF,MAAM,CAAC,GAAG,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,gDAAgD;IAChD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,qCAAqC;IACrC,KAAK,IAAI,IAAI,CAAC;CACd;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,WAAW,WAAW;IAC3B,2CAA2C;IAC3C,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9D,8CAA8C;IAC9C,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9C,+EAA+E;IAC/E,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,8BAA8B;IAC9B,KAAK,IAAI,IAAI,CAAC;CACd;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,OAAO;IACvB;;;OAGG;IACH,GAAG,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAAC;IAEtD;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,OAAO,CAAC,IAAI,EAAE,SAAS,UAAU,EAAE,GAAG,OAAO,CAAC,CAAC,UAAU,GAAG,SAAS,CAAC,EAAE,CAAC,CAAC;IAE1E;;;OAGG;IACH,GAAG,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE/E;;;OAGG;IACH,MAAM,CAAC,GAAG,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE/D;;OAEG;IACH,GAAG,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEvC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2CG;IACH,OAAO,CAAC,OAAO,CAAC,EAAE,cAAc,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;IAE1D;;OAEG;IACH,KAAK,IAAI,UAAU,CAAC;IAEpB;;OAEG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvB;;;OAGG;IACH,gBAAgB,CAAC,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC5D;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,cAAc,CAC7B,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,EAC3B,IAAI,EAAE,SAAS,UAAU,EAAE,GACzB,OAAO,CAAC,CAAC,UAAU,GAAG,SAAS,CAAC,EAAE,CAAC,CAErC;AAED;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,EAAE,cAAc,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAE3E;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,8CAA8C;IAC9C,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,qDAAqD;IACrD,aAAa,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AAQH,MAAM,WAAW,eAAe;IAC/B;;;;;;;OAOG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAErG;;;;;;;OAOG;IACH,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAE1F;;;;;;;OAOG;IACH,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEvE;;;;OAIG;IACH,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAEpC;;;;OAIG;IACH,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjE;;;;;OAKG;IACH,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEzF;;OAEG;IACH,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAE1B;;;;;;;;;;;OAWG;IACH,gBAAgB,CAAC,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE3F;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAuCG;IACH,iBAAiB,CAAC,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAExG;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACH,iBAAiB,CAAC,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvH;;;;;;;;;;OAUG;IACH,gBAAgB,CAAC,IAAI,WAAW,GAAG,SAAS,CAAC;CAC7C"}
@@ -2,5 +2,25 @@
2
2
  * Abstract key-value store interface.
3
3
  * Implemented by LevelDBStore (Node.js) and IndexedDBStore (browser).
4
4
  */
5
- export {};
5
+ /**
6
+ * The {@link KVStore.getMany} every backend without a real batch point-read delegates to:
7
+ * one `get` per key, issued together and awaited as a group.
8
+ *
9
+ * Concurrency is all it buys — the reads still cost one round trip each — but it is the
10
+ * CORRECT default everywhere, so adding `getMany` to the interface cannot break a backend
11
+ * and each one opts into a genuine batch when it has one to opt into. Reading through
12
+ * `store.get` (rather than the backend's internals) also keeps a wrapper's own `get` in
13
+ * the path, which is what lets `CachedKVStore` and the counting test doubles keep working.
14
+ *
15
+ * `Promise.all([])` resolves to `[]`, satisfying the empty-input rule without a special case.
16
+ *
17
+ * NOTE: because no `get` is issued for an empty key list, a backend delegating here answers
18
+ * `getMany([])` with `[]` even after `close()`, where LevelDB and IndexedDB check open first
19
+ * and reject. Unspecified rather than wrong — every caller guards the empty case before it
20
+ * reaches a store (`StoreTableBase.readEffectiveRowsByKeys`). Pin one behavior in tier 8 if
21
+ * a caller ever hands an empty batch to a closed store.
22
+ */
23
+ export function defaultGetMany(store, keys) {
24
+ return Promise.all(keys.map(key => store.get(key)));
25
+ }
6
26
  //# sourceMappingURL=kv-store.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"kv-store.js","sourceRoot":"","sources":["../../../src/common/kv-store.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
1
+ {"version":3,"file":"kv-store.js","sourceRoot":"","sources":["../../../src/common/kv-store.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAoPH;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,cAAc,CAC7B,KAA2B,EAC3B,IAA2B;IAE3B,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AACrD,CAAC"}
@@ -17,6 +17,7 @@ export declare class InMemoryKVStore implements KVStore {
17
17
  private data;
18
18
  private closed;
19
19
  get(key: Uint8Array): Promise<Uint8Array | undefined>;
20
+ getMany(keys: readonly Uint8Array[]): Promise<(Uint8Array | undefined)[]>;
20
21
  put(key: Uint8Array, value: Uint8Array, _options?: WriteOptions): Promise<void>;
21
22
  delete(key: Uint8Array, _options?: WriteOptions): Promise<void>;
22
23
  has(key: Uint8Array): Promise<boolean>;
@@ -1 +1 @@
1
- {"version":3,"file":"memory-store.d.ts","sourceRoot":"","sources":["../../../src/common/memory-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAsBhG;;;GAGG;AACH,qBAAa,eAAgB,YAAW,OAAO;IAC7C,OAAO,CAAC,IAAI,CAA6D;IACzE,OAAO,CAAC,MAAM,CAAS;IAEjB,GAAG,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IAWrD,GAAG,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAS/E,MAAM,CAAC,GAAG,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAK/D,GAAG,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC;IAKrC,OAAO,CAAC,OAAO,CAAC,EAAE,cAAc,GAAG,aAAa,CAAC,OAAO,CAAC;IAkDhE,KAAK,IAAI,UAAU;IA2Bb,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAKtB,gBAAgB,CAAC,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC;IAajE;;OAEG;IACH,KAAK,IAAI,IAAI;IAKb;;OAEG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,OAAO,CAAC,SAAS;CAKlB"}
1
+ {"version":3,"file":"memory-store.d.ts","sourceRoot":"","sources":["../../../src/common/memory-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAsBhG;;;GAGG;AACH,qBAAa,eAAgB,YAAW,OAAO;IAC7C,OAAO,CAAC,IAAI,CAA6D;IACzE,OAAO,CAAC,MAAM,CAAS;IAEjB,GAAG,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IAY3D,OAAO,CAAC,IAAI,EAAE,SAAS,UAAU,EAAE,GAAG,OAAO,CAAC,CAAC,UAAU,GAAG,SAAS,CAAC,EAAE,CAAC;IAMnE,GAAG,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAS/E,MAAM,CAAC,GAAG,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAK/D,GAAG,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC;IAKrC,OAAO,CAAC,OAAO,CAAC,EAAE,cAAc,GAAG,aAAa,CAAC,OAAO,CAAC;IA0DhE,KAAK,IAAI,UAAU;IA2Bb,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAKtB,gBAAgB,CAAC,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC;IAajE;;OAEG;IACH,KAAK,IAAI,IAAI;IAKb;;OAEG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,OAAO,CAAC,SAAS;CAKlB"}
@@ -9,6 +9,7 @@
9
9
  * Keys are stored using hex encoding for correct lexicographic ordering.
10
10
  */
11
11
  import { bytesToHex } from './bytes.js';
12
+ import { defaultGetMany } from './kv-store.js';
12
13
  /**
13
14
  * Convert Uint8Array to hex string for Map key storage.
14
15
  * Hex encoding preserves lexicographic ordering. Shared with the coordinator's
@@ -42,6 +43,12 @@ export class InMemoryKVStore {
42
43
  const stored = this.data.get(keyToHex(key))?.value;
43
44
  return stored === undefined ? undefined : new Uint8Array(stored);
44
45
  }
46
+ // Delegates to `this.get` rather than reading `data` directly: there is no round trip
47
+ // to save here, and going through the method keeps a subclass's overridden `get` (the
48
+ // counting doubles several specs subclass this with) in the path.
49
+ getMany(keys) {
50
+ return defaultGetMany(this, keys);
51
+ }
45
52
  // `_options` is accepted to satisfy the KVStore signature; an in-memory store
46
53
  // has no crash window, so the durability hint is a no-op here.
47
54
  async put(key, value, _options) {
@@ -62,7 +69,15 @@ export class InMemoryKVStore {
62
69
  }
63
70
  async *iterate(options) {
64
71
  this.checkOpen();
65
- // Sort entries by hex key for correct ordering
72
+ // Sort entries by hex key for correct ordering.
73
+ // This materializes and sorts the WHOLE map on every call, so even a `limit: 1`
74
+ // scan is O(n log n) and allocates an n-entry array. That does not violate
75
+ // KVStore.iterate's bounded-peak requirement — the requirement bounds reads from
76
+ // backing storage and this store's dataset is already wholly resident — and it is
77
+ // fine while the memory store backs test-sized data and build-time seeds.
78
+ // NOTE: if the memory store ever backs large tables with frequent small-limit
79
+ // scans, keep a sorted key index (or a sorted-insert structure) instead of
80
+ // re-sorting per call.
66
81
  const entries = Array.from(this.data.entries())
67
82
  .sort((a, b) => compareHex(a[0], b[0]));
68
83
  // Apply reverse if requested
@@ -1 +1 @@
1
- {"version":3,"file":"memory-store.js","sourceRoot":"","sources":["../../../src/common/memory-store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"memory-store.js","sourceRoot":"","sources":["../../../src/common/memory-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Shared batch/resume loop for KVStore backends that cannot stream.
3
+ *
4
+ * Some backends have no cursor to hold open across a consumer `await`: a SQL
5
+ * `select` hands back a whole result set, and an IndexedDB readonly transaction
6
+ * auto-commits while idle. Those backends satisfy {@link KVStore.iterate}'s
7
+ * bounded-peak requirement by paging — read a fixed-size batch, yield it, resume
8
+ * just past the last key seen — so peak memory is one batch regardless of how many
9
+ * entries the range holds.
10
+ *
11
+ * The resume edge is the subtle part, and the reason this lives in one place rather
12
+ * than being re-derived per backend:
13
+ * - forward tightens the LOWER bound, reverse tightens the UPPER bound, both
14
+ * EXCLUSIVE so no entry is yielded twice;
15
+ * - the resume key is captured BEFORE yielding, because entries are handed to the
16
+ * consumer by reference and a consumer that mutates a yielded key must not
17
+ * perturb where the next batch resumes from;
18
+ * - an exhausted edge can collapse the bounds to an empty range, which must read
19
+ * as "exhausted" rather than throw.
20
+ */
21
+ import type { IterateOptions, KVEntry } from './kv-store.js';
22
+ /**
23
+ * Fetch up to `want` entries for the given bounds.
24
+ *
25
+ * `bounds` is already resume-adjusted and carries `reverse`; it never carries
26
+ * `limit` — the cap is `want`, passed separately so a backend can push it into its
27
+ * query. Returning FEWER than `want` entries signals the range is exhausted, so an
28
+ * implementation must return a full batch whenever one is available. Returning MORE
29
+ * than `want` is a programming error and {@link pagedIterate} throws on it.
30
+ */
31
+ export type FetchBatch = (bounds: IterateOptions, want: number) => Promise<KVEntry[]>;
32
+ /**
33
+ * Drive a non-streaming backend as a bounded async iterable: fetch a batch, yield it,
34
+ * resume past the last key seen. Peak is one batch regardless of range size, and a
35
+ * consumer that stops after k entries costs at most one batch beyond k.
36
+ *
37
+ * @param options - The caller's iterate options (bounds, `reverse`, `limit`).
38
+ * @param fetchBatch - Backend read for one batch. See {@link FetchBatch}.
39
+ * @param batchSize - Entries per batch. Defaults to {@link DEFAULT_BATCH_SIZE}.
40
+ */
41
+ export declare function pagedIterate(options: IterateOptions | undefined, fetchBatch: FetchBatch, batchSize?: number): AsyncIterable<KVEntry>;
42
+ //# sourceMappingURL=paged-iterate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paged-iterate.d.ts","sourceRoot":"","sources":["../../../src/common/paged-iterate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAK7D;;;;;;;;GAQG;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,MAAM,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;AA6CtF;;;;;;;;GAQG;AACH,wBAAuB,YAAY,CAClC,OAAO,EAAE,cAAc,GAAG,SAAS,EACnC,UAAU,EAAE,UAAU,EACtB,SAAS,GAAE,MAA2B,GACpC,aAAa,CAAC,OAAO,CAAC,CAoCxB"}
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Shared batch/resume loop for KVStore backends that cannot stream.
3
+ *
4
+ * Some backends have no cursor to hold open across a consumer `await`: a SQL
5
+ * `select` hands back a whole result set, and an IndexedDB readonly transaction
6
+ * auto-commits while idle. Those backends satisfy {@link KVStore.iterate}'s
7
+ * bounded-peak requirement by paging — read a fixed-size batch, yield it, resume
8
+ * just past the last key seen — so peak memory is one batch regardless of how many
9
+ * entries the range holds.
10
+ *
11
+ * The resume edge is the subtle part, and the reason this lives in one place rather
12
+ * than being re-derived per backend:
13
+ * - forward tightens the LOWER bound, reverse tightens the UPPER bound, both
14
+ * EXCLUSIVE so no entry is yielded twice;
15
+ * - the resume key is captured BEFORE yielding, because entries are handed to the
16
+ * consumer by reference and a consumer that mutates a yielded key must not
17
+ * perturb where the next batch resumes from;
18
+ * - an exhausted edge can collapse the bounds to an empty range, which must read
19
+ * as "exhausted" rather than throw.
20
+ */
21
+ import { compareBytes } from './bytes.js';
22
+ /** Default entries per batch when the caller does not pick one. */
23
+ const DEFAULT_BATCH_SIZE = 256;
24
+ /** Derive the lower/upper edges of `options`, preferring the inclusive form. */
25
+ function edgesOf(options) {
26
+ let lower;
27
+ let upper;
28
+ if (options?.gte)
29
+ lower = { key: options.gte, open: false };
30
+ else if (options?.gt)
31
+ lower = { key: options.gt, open: true };
32
+ if (options?.lte)
33
+ upper = { key: options.lte, open: false };
34
+ else if (options?.lt)
35
+ upper = { key: options.lt, open: true };
36
+ return { lower, upper };
37
+ }
38
+ /**
39
+ * True when the edges describe a range that can hold nothing: crossed, or a single
40
+ * point excluded by either edge. Reached on the resume iteration when a batch fills
41
+ * exactly to a boundary key that also equals an inclusive opposite bound.
42
+ */
43
+ function isEmptyRange(lower, upper) {
44
+ if (!lower || !upper)
45
+ return false;
46
+ const c = compareBytes(lower.key, upper.key);
47
+ return c > 0 || (c === 0 && (lower.open || upper.open));
48
+ }
49
+ /** Rebuild an {@link IterateOptions} from explicit edges, dropping `limit`. */
50
+ function boundsOf(lower, upper, reverse) {
51
+ const bounds = {};
52
+ if (lower) {
53
+ if (lower.open)
54
+ bounds.gt = lower.key;
55
+ else
56
+ bounds.gte = lower.key;
57
+ }
58
+ if (upper) {
59
+ if (upper.open)
60
+ bounds.lt = upper.key;
61
+ else
62
+ bounds.lte = upper.key;
63
+ }
64
+ if (reverse)
65
+ bounds.reverse = true;
66
+ return bounds;
67
+ }
68
+ /**
69
+ * Drive a non-streaming backend as a bounded async iterable: fetch a batch, yield it,
70
+ * resume past the last key seen. Peak is one batch regardless of range size, and a
71
+ * consumer that stops after k entries costs at most one batch beyond k.
72
+ *
73
+ * @param options - The caller's iterate options (bounds, `reverse`, `limit`).
74
+ * @param fetchBatch - Backend read for one batch. See {@link FetchBatch}.
75
+ * @param batchSize - Entries per batch. Defaults to {@link DEFAULT_BATCH_SIZE}.
76
+ */
77
+ export async function* pagedIterate(options, fetchBatch, batchSize = DEFAULT_BATCH_SIZE) {
78
+ if (!Number.isInteger(batchSize) || batchSize < 1) {
79
+ throw new Error(`pagedIterate: batchSize must be a positive integer, got ${batchSize}`);
80
+ }
81
+ const reverse = options?.reverse ?? false;
82
+ const { lower, upper } = edgesOf(options);
83
+ let remaining = options?.limit; // undefined ⇒ unbounded
84
+ let resumeKey; // last key yielded — exclusive resume edge
85
+ for (;;) {
86
+ if (remaining !== undefined && remaining <= 0)
87
+ return;
88
+ const want = remaining === undefined ? batchSize : Math.min(batchSize, remaining);
89
+ // Resume just past the last key seen: forward tightens the lower bound,
90
+ // reverse tightens the upper bound — exclusive so no entry repeats.
91
+ const effLower = !reverse && resumeKey !== undefined ? { key: resumeKey, open: true } : lower;
92
+ const effUpper = reverse && resumeKey !== undefined ? { key: resumeKey, open: true } : upper;
93
+ if (isEmptyRange(effLower, effUpper))
94
+ return;
95
+ const batch = await fetchBatch(boundsOf(effLower, effUpper, reverse), want);
96
+ if (batch.length > want) {
97
+ throw new Error(`pagedIterate: fetchBatch returned ${batch.length} entries for want=${want}`);
98
+ }
99
+ // Capture the resume boundary BEFORE yielding, and as a COPY: entries go to the
100
+ // consumer by reference, and a consumer that mutates a yielded key must not move
101
+ // where the next batch resumes from.
102
+ const nextResumeKey = batch.length > 0 ? new Uint8Array(batch[batch.length - 1].key) : undefined;
103
+ for (const entry of batch) {
104
+ yield entry;
105
+ }
106
+ if (remaining !== undefined)
107
+ remaining -= batch.length;
108
+ if (batch.length < want)
109
+ return; // range exhausted before filling the batch
110
+ resumeKey = nextResumeKey;
111
+ }
112
+ }
113
+ //# sourceMappingURL=paged-iterate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paged-iterate.js","sourceRoot":"","sources":["../../../src/common/paged-iterate.ts"],"names":[],"mappings":"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"}
@@ -305,22 +305,35 @@ export declare function keyOrderMatchesCollation(db: Database, column: ColumnSch
305
305
  */
306
306
  export declare function indexPrefixSeekIsCollationExact(columns: ReadonlyArray<ColumnSchema>, index: TableIndexSchema, keyCollations: ReadonlyArray<string | undefined>, prefixLength: number): boolean;
307
307
  /**
308
- * True when a byte RANGE window over the LEADING column of secondary `index` is SOUND.
308
+ * True when a byte RANGE window over index-column `position` of secondary `index` is SOUND.
309
309
  *
310
- * Strictly stronger than {@link indexPrefixSeekIsCollationExact} at position 0: the same
311
- * key-vs-residual collation agreement, PLUS the collation's `orderPreserving` assertion,
312
- * because a range window also equates memcmp of the key bytes with the residual
310
+ * Strictly stronger than {@link indexPrefixSeekIsCollationExact} at the same position: the
311
+ * same key-vs-residual collation agreement, PLUS the collation's `orderPreserving`
312
+ * assertion, because a range window also equates memcmp of the key bytes with the residual
313
313
  * comparator's ORDER. Both halves are {@link keyOrderMatchesCollation}, which admits a
314
314
  * semantic-ordering column only when {@link semanticKeyOrderIsFaithful} asserts its key
315
315
  * bytes order as the type's `compare` does (and the collation sides agree — a `collate`
316
316
  * decoration on the index column still declines it).
317
317
  *
318
+ * `position` is 0 for a standalone leading-column range (see
319
+ * {@link indexLeadingRangeIsOrderSafe}) and `prefixLength` for the trailing bound of a
320
+ * prefix-equality + trailing-range seek. The question is identical at either position: the
321
+ * columns BEFORE it are pinned to one value each, so the window is a contiguous byte region
322
+ * whose ordering within the prefix is governed solely by this column's key bytes. What the
323
+ * pinned columns need is the (weaker) EXACTNESS test — {@link indexPrefixSeekIsCollationExact}
324
+ * over the prefix — which the callers apply alongside this one.
325
+ *
318
326
  * THE shared predicate for the two decision sites: `tryIndexAccessPlan`
319
327
  * (store-module-access-plan.ts) marks the range filters handled only when it holds, and
320
328
  * `StoreTableScan.analyzeIndexAccess` builds a window only when it holds. They must agree —
321
329
  * a plan that claims a filter the scan then declines to window loses the residual Filter and
322
330
  * returns the whole table — so they call this rather than each restating it.
323
331
  */
332
+ export declare function indexRangeAtPositionIsOrderSafe(db: Database, columns: ReadonlyArray<ColumnSchema>, index: TableIndexSchema, keyCollations: ReadonlyArray<string | undefined>, position: number): boolean;
333
+ /**
334
+ * {@link indexRangeAtPositionIsOrderSafe} at position 0 — the standalone leading-column
335
+ * range window, which needs no equality prefix ahead of it.
336
+ */
324
337
  export declare function indexLeadingRangeIsOrderSafe(db: Database, columns: ReadonlyArray<ColumnSchema>, index: TableIndexSchema, keyCollations: ReadonlyArray<string | undefined>): boolean;
325
338
  /**
326
339
  * How many LEADING primary-key members have key bytes whose memcmp order matches their
@@ -1 +1 @@
1
- {"version":3,"file":"pk-key-resolution.d.ts","sourceRoot":"","sources":["../../../src/common/pk-key-resolution.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pk-key-resolution.d.ts","sourceRoot":"","sources":["../../../src/common/pk-key-resolution.ts"],"names":[],"mappings":"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"}
@@ -422,27 +422,42 @@ export function indexPrefixSeekIsCollationExact(columns, index, keyCollations, p
422
422
  return true;
423
423
  }
424
424
  /**
425
- * True when a byte RANGE window over the LEADING column of secondary `index` is SOUND.
425
+ * True when a byte RANGE window over index-column `position` of secondary `index` is SOUND.
426
426
  *
427
- * Strictly stronger than {@link indexPrefixSeekIsCollationExact} at position 0: the same
428
- * key-vs-residual collation agreement, PLUS the collation's `orderPreserving` assertion,
429
- * because a range window also equates memcmp of the key bytes with the residual
427
+ * Strictly stronger than {@link indexPrefixSeekIsCollationExact} at the same position: the
428
+ * same key-vs-residual collation agreement, PLUS the collation's `orderPreserving`
429
+ * assertion, because a range window also equates memcmp of the key bytes with the residual
430
430
  * comparator's ORDER. Both halves are {@link keyOrderMatchesCollation}, which admits a
431
431
  * semantic-ordering column only when {@link semanticKeyOrderIsFaithful} asserts its key
432
432
  * bytes order as the type's `compare` does (and the collation sides agree — a `collate`
433
433
  * decoration on the index column still declines it).
434
434
  *
435
+ * `position` is 0 for a standalone leading-column range (see
436
+ * {@link indexLeadingRangeIsOrderSafe}) and `prefixLength` for the trailing bound of a
437
+ * prefix-equality + trailing-range seek. The question is identical at either position: the
438
+ * columns BEFORE it are pinned to one value each, so the window is a contiguous byte region
439
+ * whose ordering within the prefix is governed solely by this column's key bytes. What the
440
+ * pinned columns need is the (weaker) EXACTNESS test — {@link indexPrefixSeekIsCollationExact}
441
+ * over the prefix — which the callers apply alongside this one.
442
+ *
435
443
  * THE shared predicate for the two decision sites: `tryIndexAccessPlan`
436
444
  * (store-module-access-plan.ts) marks the range filters handled only when it holds, and
437
445
  * `StoreTableScan.analyzeIndexAccess` builds a window only when it holds. They must agree —
438
446
  * a plan that claims a filter the scan then declines to window loses the residual Filter and
439
447
  * returns the whole table — so they call this rather than each restating it.
440
448
  */
441
- export function indexLeadingRangeIsOrderSafe(db, columns, index, keyCollations) {
442
- const leading = index.columns[0];
443
- if (!leading)
449
+ export function indexRangeAtPositionIsOrderSafe(db, columns, index, keyCollations, position) {
450
+ const spec = index.columns[position];
451
+ if (!spec)
444
452
  return false;
445
- return keyOrderMatchesCollation(db, columns[leading.index], keyCollations[0] ?? 'BINARY', indexResidualCollation(columns, index, 0));
453
+ return keyOrderMatchesCollation(db, columns[spec.index], keyCollations[position] ?? 'BINARY', indexResidualCollation(columns, index, position));
454
+ }
455
+ /**
456
+ * {@link indexRangeAtPositionIsOrderSafe} at position 0 — the standalone leading-column
457
+ * range window, which needs no equality prefix ahead of it.
458
+ */
459
+ export function indexLeadingRangeIsOrderSafe(db, columns, index, keyCollations) {
460
+ return indexRangeAtPositionIsOrderSafe(db, columns, index, keyCollations, 0);
446
461
  }
447
462
  /**
448
463
  * How many LEADING primary-key members have key bytes whose memcmp order matches their