@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.
- package/README.md +143 -13
- package/dist/src/common/cached-kv-store.d.ts +16 -0
- package/dist/src/common/cached-kv-store.d.ts.map +1 -1
- package/dist/src/common/cached-kv-store.js +64 -6
- package/dist/src/common/cached-kv-store.js.map +1 -1
- package/dist/src/common/index.d.ts +2 -0
- package/dist/src/common/index.d.ts.map +1 -1
- package/dist/src/common/index.js +4 -0
- package/dist/src/common/index.js.map +1 -1
- package/dist/src/common/key-builder.d.ts +9 -6
- package/dist/src/common/key-builder.d.ts.map +1 -1
- package/dist/src/common/key-builder.js +17 -6
- package/dist/src/common/key-builder.js.map +1 -1
- package/dist/src/common/kv-store.d.ts +142 -4
- package/dist/src/common/kv-store.d.ts.map +1 -1
- package/dist/src/common/kv-store.js +21 -1
- package/dist/src/common/kv-store.js.map +1 -1
- package/dist/src/common/memory-store.d.ts +1 -0
- package/dist/src/common/memory-store.d.ts.map +1 -1
- package/dist/src/common/memory-store.js +16 -1
- package/dist/src/common/memory-store.js.map +1 -1
- package/dist/src/common/paged-iterate.d.ts +42 -0
- package/dist/src/common/paged-iterate.d.ts.map +1 -0
- package/dist/src/common/paged-iterate.js +113 -0
- package/dist/src/common/paged-iterate.js.map +1 -0
- package/dist/src/common/pk-key-resolution.d.ts +17 -4
- package/dist/src/common/pk-key-resolution.d.ts.map +1 -1
- package/dist/src/common/pk-key-resolution.js +23 -8
- package/dist/src/common/pk-key-resolution.js.map +1 -1
- package/dist/src/common/serialization.d.ts +11 -1
- package/dist/src/common/serialization.d.ts.map +1 -1
- package/dist/src/common/serialization.js +114 -31
- package/dist/src/common/serialization.js.map +1 -1
- package/dist/src/common/store-module-access-plan.d.ts +1 -1
- package/dist/src/common/store-module-access-plan.d.ts.map +1 -1
- package/dist/src/common/store-module-access-plan.js +434 -105
- package/dist/src/common/store-module-access-plan.js.map +1 -1
- package/dist/src/common/store-module-alter-column.d.ts.map +1 -1
- package/dist/src/common/store-module-alter-column.js +25 -2
- package/dist/src/common/store-module-alter-column.js.map +1 -1
- package/dist/src/common/store-module-alter.d.ts.map +1 -1
- package/dist/src/common/store-module-alter.js +36 -13
- package/dist/src/common/store-module-alter.js.map +1 -1
- package/dist/src/common/store-module-catalog.d.ts +6 -4
- package/dist/src/common/store-module-catalog.d.ts.map +1 -1
- package/dist/src/common/store-module-catalog.js +8 -6
- package/dist/src/common/store-module-catalog.js.map +1 -1
- package/dist/src/common/store-module-index.d.ts +98 -7
- package/dist/src/common/store-module-index.d.ts.map +1 -1
- package/dist/src/common/store-module-index.js +236 -81
- package/dist/src/common/store-module-index.js.map +1 -1
- package/dist/src/common/store-module.d.ts.map +1 -1
- package/dist/src/common/store-module.js +7 -3
- package/dist/src/common/store-module.js.map +1 -1
- package/dist/src/common/store-table-base.d.ts +30 -0
- package/dist/src/common/store-table-base.d.ts.map +1 -1
- package/dist/src/common/store-table-base.js +71 -12
- package/dist/src/common/store-table-base.js.map +1 -1
- package/dist/src/common/store-table-constraints.js +3 -3
- package/dist/src/common/store-table-constraints.js.map +1 -1
- package/dist/src/common/store-table-scan.d.ts +208 -98
- package/dist/src/common/store-table-scan.d.ts.map +1 -1
- package/dist/src/common/store-table-scan.js +353 -105
- package/dist/src/common/store-table-scan.js.map +1 -1
- package/dist/src/testing/index.d.ts +18 -2
- package/dist/src/testing/index.d.ts.map +1 -1
- package/dist/src/testing/index.js +17 -2
- package/dist/src/testing/index.js.map +1 -1
- package/dist/src/testing/kv-conformance.d.ts +101 -3
- package/dist/src/testing/kv-conformance.d.ts.map +1 -1
- package/dist/src/testing/kv-conformance.js +277 -7
- package/dist/src/testing/kv-conformance.js.map +1 -1
- package/dist/src/testing/kv-lifecycle.d.ts +22 -0
- package/dist/src/testing/kv-lifecycle.d.ts.map +1 -0
- package/dist/src/testing/kv-lifecycle.js +12 -0
- package/dist/src/testing/kv-lifecycle.js.map +1 -0
- package/dist/src/testing/kv-naming-conformance.d.ts +77 -0
- package/dist/src/testing/kv-naming-conformance.d.ts.map +1 -0
- package/dist/src/testing/kv-naming-conformance.js +208 -0
- package/dist/src/testing/kv-naming-conformance.js.map +1 -0
- package/dist/src/testing/kv-reclaim-conformance.d.ts +52 -0
- package/dist/src/testing/kv-reclaim-conformance.d.ts.map +1 -0
- package/dist/src/testing/kv-reclaim-conformance.js +287 -0
- package/dist/src/testing/kv-reclaim-conformance.js.map +1 -0
- 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
|
-
/**
|
|
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
|
-
*
|
|
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
|
|
246
|
-
*
|
|
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":"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
|
|
1
|
+
{"version":3,"file":"kv-store.d.ts","sourceRoot":"","sources":["../../../src/common/kv-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,5 +2,25 @@
|
|
|
2
2
|
* Abstract key-value store interface.
|
|
3
3
|
* Implemented by LevelDBStore (Node.js) and IndexedDBStore (browser).
|
|
4
4
|
*/
|
|
5
|
-
|
|
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;
|
|
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":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;
|
|
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
|
|
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
|
|
311
|
-
* key-vs-residual collation agreement, PLUS the collation's `orderPreserving`
|
|
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
|
|
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
|
|
428
|
-
* key-vs-residual collation agreement, PLUS the collation's `orderPreserving`
|
|
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
|
|
442
|
-
const
|
|
443
|
-
if (!
|
|
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[
|
|
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
|