@quereus/store 4.11.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 +131 -10
- 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 +43 -0
- package/dist/src/common/cached-kv-store.js.map +1 -1
- package/dist/src/common/index.d.ts +1 -0
- package/dist/src/common/index.d.ts.map +1 -1
- package/dist/src/common/index.js +2 -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 +89 -3
- 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 +7 -0
- package/dist/src/common/memory-store.js.map +1 -1
- 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/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 +17 -5
- package/dist/src/testing/index.d.ts.map +1 -1
- package/dist/src/testing/index.js +16 -5
- package/dist/src/testing/index.js.map +1 -1
- package/dist/src/testing/kv-conformance.d.ts +49 -0
- package/dist/src/testing/kv-conformance.d.ts.map +1 -1
- package/dist/src/testing/kv-conformance.js +125 -0
- 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
|
@@ -11,6 +11,30 @@ import { IndexConstraintOp, type IdxStrSpec, type CollationFunction, type Compar
|
|
|
11
11
|
import type { IterateOptions, KVStore } from './kv-store.js';
|
|
12
12
|
import type { KeyValueTransform } from './encoding.js';
|
|
13
13
|
import { StoreTableBase } from './store-table-base.js';
|
|
14
|
+
/**
|
|
15
|
+
* Index entries per row-resolution batch: the scan arms below collect up to this many
|
|
16
|
+
* surviving index entries (or multi-seek point keys) and resolve them with ONE
|
|
17
|
+
* {@link StoreTableBase.readEffectiveRowsByKeys} call, so an indexed read of N rows
|
|
18
|
+
* costs on the order of N / 256 store round trips instead of N serialized point reads
|
|
19
|
+
* (on IndexedDB, one readonly transaction instead of N). This is the bound
|
|
20
|
+
* {@link KVStore.getMany}'s "bounded by the caller" note refers to.
|
|
21
|
+
*
|
|
22
|
+
* 256 matches the iterate pager's default page (`paged-iterate.ts` DEFAULT_BATCH_SIZE)
|
|
23
|
+
* and makes the same trade that pager already makes and the {@link KVStore.iterate}
|
|
24
|
+
* contract documents: a little early-termination cost for throughput — `limit 1`
|
|
25
|
+
* collects one batch rather than one row — while keeping peak memory independent of
|
|
26
|
+
* the range size.
|
|
27
|
+
*
|
|
28
|
+
* NOTE: that early-termination cost is bounded but not free — `limit 1` deserializes up
|
|
29
|
+
* to 256 rows to yield one, and holds them all at once. It is charged against ROUND
|
|
30
|
+
* TRIPS, which is what dominates on the backends this exists for (the index side already
|
|
31
|
+
* pages 256 entries per read, so a stopped scan costs one data round trip either way),
|
|
32
|
+
* so it is accepted as-is. Revisit if a `limit`-shaped query (a correlated `EXISTS`, a
|
|
33
|
+
* semi-join probe) shows up in a profile as row-decode or allocation cost: the fix is a
|
|
34
|
+
* SMALLER FIRST batch that ramps toward this bound, which keeps a drain's round-trip
|
|
35
|
+
* count within a log factor while making a stopped scan cost ~one row again.
|
|
36
|
+
*/
|
|
37
|
+
export declare const ROW_RESOLUTION_BATCH = 256;
|
|
14
38
|
/**
|
|
15
39
|
* Read-path layer of the generic KVStore-backed virtual table.
|
|
16
40
|
*
|
|
@@ -131,26 +155,61 @@ export declare abstract class StoreTableScan extends StoreTableBase {
|
|
|
131
155
|
* Analyze filter info to determine a secondary-index access pattern, mirroring
|
|
132
156
|
* {@link analyzePKAccess} but over the index chosen in `idxStr`.
|
|
133
157
|
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
158
|
+
* Three window shapes, in the order they are tried:
|
|
159
|
+
*
|
|
160
|
+
* - a contiguous leading-prefix EQ plus a LT/LE/GT/GE bound on the NEXT index column
|
|
161
|
+
* (the `prefixRangeSeek` / `plan=7` plan) — one `range` window covering just the
|
|
162
|
+
* bounded slice INSIDE the prefix;
|
|
163
|
+
* - a contiguous leading-prefix EQ alone → a `point` window (the prefix covers every
|
|
164
|
+
* entry sharing those leading values — an index seek is a PREFIX scan, not a single
|
|
165
|
+
* row, since the index need not be unique and the PK suffix varies);
|
|
166
|
+
* - otherwise a range (LT/LE/GT/GE) on the LEADING index column → a `range` window.
|
|
167
|
+
*
|
|
168
|
+
* Returns null when none applies or when the index is unresolved.
|
|
169
|
+
* {@link matchesFilters} stays the authoritative row filter, so the window need only be
|
|
170
|
+
* a SUPERSET — which is why every degraded path below WIDENS (a prefix-range that
|
|
171
|
+
* cannot narrow falls back to the prefix window, a prefix that cannot encode a member
|
|
172
|
+
* stops short) rather than declining outright.
|
|
141
173
|
*
|
|
142
174
|
* Index-column bytes are encoded under each column's own key collation
|
|
143
175
|
* ({@link indexKeyCollations}); {@link matchesFilters} re-checks a fetched row under the
|
|
144
176
|
* index column's `COLLATE` else the table column's declared collation. The EQ/prefix
|
|
145
177
|
* window is EXACTLY the qualifying set when those two agree, which
|
|
146
|
-
* {@link indexPrefixSeekIsCollationExact} decides;
|
|
147
|
-
* memcmp of the key bytes with the residual's comparison ORDER
|
|
148
|
-
* {@link indexRangeIsOrderSafe} decides. `tryIndexAccessPlan`
|
|
149
|
-
* (store-module-access-plan.ts) consults the SAME two predicates
|
|
150
|
-
* filters handled, so the two decisions cannot disagree.
|
|
151
|
-
* the caller full-scans, where the residual is authoritative.
|
|
178
|
+
* {@link indexPrefixSeekIsCollationExact} decides; a RANGE window additionally equates
|
|
179
|
+
* memcmp of the key bytes with the residual's comparison ORDER at the bounded column's
|
|
180
|
+
* position, which {@link indexRangeIsOrderSafe} decides. `tryIndexAccessPlan`
|
|
181
|
+
* (store-module-access-plan.ts) consults the SAME two predicates, at the same positions,
|
|
182
|
+
* before claiming the filters handled, so the two decisions cannot disagree. An EQ-prefix
|
|
183
|
+
* failure returns null and the caller full-scans, where the residual is authoritative.
|
|
152
184
|
*/
|
|
153
185
|
protected analyzeIndexAccess(filterInfo: FilterInfo): IndexAccessPattern | null;
|
|
186
|
+
/**
|
|
187
|
+
* The prefix length a `prefixRangeSeek` (`plan=7`) FilterInfo declares, or null when
|
|
188
|
+
* this FilterInfo is not one.
|
|
189
|
+
*
|
|
190
|
+
* Honoring the plan's own `prefixLen` — rather than re-deriving it from the constraint
|
|
191
|
+
* list — is what makes a plan/scan disagreement LOUD: a value outside `1 …
|
|
192
|
+
* index.columns.length - 1` cannot describe a window over this index, and the plan
|
|
193
|
+
* already dropped the residual for the filters it claimed, so guessing a length would
|
|
194
|
+
* silently address the wrong columns.
|
|
195
|
+
*/
|
|
196
|
+
private prefixRangeSeekLength;
|
|
197
|
+
/** The pushed LT/LE/GT/GE constraints on `columnIndex`, paired with their bound values. */
|
|
198
|
+
private rangeConstraintsOn;
|
|
199
|
+
/**
|
|
200
|
+
* The narrowed window for a prefix-equality + trailing-range seek: the equality prefix
|
|
201
|
+
* bytes, further bounded by the LT/LE/GT/GE constraints on the index column right after
|
|
202
|
+
* it. Null when the trailing bound cannot be used, in which case the caller falls back
|
|
203
|
+
* to the plain prefix window — a strict SUPERSET, so the answer is unchanged and only
|
|
204
|
+
* the narrowing is lost. Two reasons for that:
|
|
205
|
+
*
|
|
206
|
+
* - the trailing column's key bytes do not reproduce its residual comparator's ORDER
|
|
207
|
+
* ({@link indexRangeIsOrderSafe}) — `tryIndexAccessPlan` degrades to the plain prefix
|
|
208
|
+
* seek through the same predicate, so this is the matching scan-side move rather than
|
|
209
|
+
* a disagreement;
|
|
210
|
+
* - no bound constraint survived on that column at all (nothing to narrow with).
|
|
211
|
+
*/
|
|
212
|
+
private buildPrefixRangeWindow;
|
|
154
213
|
/**
|
|
155
214
|
* Lazy cache behind {@link indexKeyCollations}: one resolved array per index-schema
|
|
156
215
|
* object, invalidated when the column array it was resolved against is replaced (an
|
|
@@ -209,23 +268,35 @@ export declare abstract class StoreTableScan extends StoreTableBase {
|
|
|
209
268
|
*/
|
|
210
269
|
getIndexComparator(indexName: string): CompareFn[] | undefined;
|
|
211
270
|
/**
|
|
212
|
-
* True when a byte window
|
|
213
|
-
* residual comparison uses. Delegates to {@link
|
|
214
|
-
*
|
|
215
|
-
* it marks the range filters handled, so the "build a window" and "mark
|
|
216
|
-
* decisions cannot disagree. The memoized {@link indexKeyCollations} is passed
|
|
217
|
-
* key side, so the predicate judges the bytes this scan actually addresses.
|
|
271
|
+
* True when a byte window bounded on index column `position` reproduces the order the
|
|
272
|
+
* residual comparison uses there. Delegates to {@link indexRangeAtPositionIsOrderSafe} —
|
|
273
|
+
* the SAME call `tryIndexAccessPlan` (store-module-access-plan.ts) makes, at the same
|
|
274
|
+
* position, before it marks the range filters handled, so the "build a window" and "mark
|
|
275
|
+
* handled" decisions cannot disagree. The memoized {@link indexKeyCollations} is passed
|
|
276
|
+
* as the key side, so the predicate judges the bytes this scan actually addresses.
|
|
277
|
+
*
|
|
278
|
+
* `position` is 0 for a standalone leading-column range and the prefix length for a
|
|
279
|
+
* prefix-equality + trailing-range window.
|
|
218
280
|
*/
|
|
219
|
-
protected indexRangeIsOrderSafe(index: TableIndexSchema): boolean;
|
|
281
|
+
protected indexRangeIsOrderSafe(index: TableIndexSchema, position: number): boolean;
|
|
220
282
|
/**
|
|
221
|
-
* Convert
|
|
222
|
-
* `gte`/`lt` window
|
|
283
|
+
* Convert LT/LE/GT/GE constraints on ONE index column into a single encoded-byte
|
|
284
|
+
* `gte`/`lt` window, optionally nested inside a fixed equality prefix — the
|
|
285
|
+
* secondary-index analogue of {@link buildPKRangeBounds}.
|
|
286
|
+
*
|
|
287
|
+
* The bounded column sits at position `prefixValues.length`; `directions`,
|
|
288
|
+
* `collations` and `transforms` are the index's own per-column arrays sliced to cover
|
|
289
|
+
* the prefix AND that column. An empty `prefixValues` is the standalone leading-column
|
|
290
|
+
* range; a non-empty one is the trailing bound of a `prefixRangeSeek`.
|
|
223
291
|
*
|
|
224
|
-
* Each bound value is encoded under the
|
|
225
|
-
* ({@link indexKeyCollations}) and DESC
|
|
226
|
-
* encodes that column — via {@link buildIndexPrefixBounds},
|
|
227
|
-
* `[lo, hi)` whose
|
|
228
|
-
*
|
|
292
|
+
* Each bound value is encoded BEHIND the prefix, under the bounded column's own key
|
|
293
|
+
* collation ({@link indexKeyCollations}) and DESC direction — exactly as
|
|
294
|
+
* {@link buildIndexKey} encodes that column — via {@link buildIndexPrefixBounds},
|
|
295
|
+
* giving the byte region `[lo, hi)` whose prefix columns equal `prefixValues` and whose
|
|
296
|
+
* bounded column equals that value. The op maps that region's endpoints onto
|
|
297
|
+
* `gte`/`lt`, with the same DESC lower/upper SWAP as the PK path — driven by the BOUNDED
|
|
298
|
+
* column's direction alone, since a DESC prefix column merely inverts bytes *inside* a
|
|
299
|
+
* prefix that stays fixed:
|
|
229
300
|
*
|
|
230
301
|
* | op | ASC | DESC |
|
|
231
302
|
* |----|----------|----------|
|
|
@@ -234,40 +305,94 @@ export declare abstract class StoreTableScan extends StoreTableBase {
|
|
|
234
305
|
* | LE | lt = hi | gte = lo |
|
|
235
306
|
* | LT | lt = lo | gte = hi |
|
|
236
307
|
*
|
|
237
|
-
*
|
|
238
|
-
*
|
|
239
|
-
*
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
*
|
|
247
|
-
*
|
|
308
|
+
* Both sides START at the prefix's own window (`[P, incr(P))`, or unbounded for an
|
|
309
|
+
* empty prefix) and are then tightened: keep the MAX lower and MIN upper across
|
|
310
|
+
* constraints. So every degraded path lands back on the prefix window, never wider:
|
|
311
|
+
* an `undefined` upper (an `hi` whose increment overflowed all-0xff), a NULL/missing
|
|
312
|
+
* bound value (the planner never pushes `= NULL`, and a range op against NULL rejects
|
|
313
|
+
* every row in matchesFilters), and a bound over a semantic-ordering column
|
|
314
|
+
* (`logicalType`) whose VALUE has no faithful byte position
|
|
315
|
+
* ({@link semanticProbeIsKeyFaithful}) are all simply skipped — a dropped RANGE bound
|
|
316
|
+
* only widens the window, and matchesFilters reproduces the predicate under the type's
|
|
317
|
+
* compare; see the widen-vs-decline note on {@link buildPKRangeBounds}. `transforms`
|
|
318
|
+
* carry each column's key value transform ({@link indexKeyTransforms}), so a surviving
|
|
319
|
+
* bound addresses the same transformed bytes the index store holds.
|
|
248
320
|
*/
|
|
249
|
-
protected buildIndexRangeBounds(constraints: Array<{
|
|
321
|
+
protected buildIndexRangeBounds(prefixValues: readonly SqlValue[], constraints: Array<{
|
|
250
322
|
op: IndexConstraintOp;
|
|
251
323
|
value?: SqlValue;
|
|
252
|
-
}>,
|
|
324
|
+
}>, directions: ReadonlyArray<boolean>, collations: ReadonlyArray<string | undefined>, logicalType: LogicalType | undefined, transforms: ReadonlyArray<KeyValueTransform | undefined>): IterateOptions;
|
|
253
325
|
/**
|
|
254
326
|
* Scan a secondary index over `access.bounds`, resolving each index entry to its
|
|
255
327
|
* base row and re-filtering.
|
|
256
328
|
*
|
|
257
|
-
*
|
|
258
|
-
*
|
|
259
|
-
*
|
|
260
|
-
*
|
|
261
|
-
*
|
|
262
|
-
*
|
|
263
|
-
*
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
*
|
|
268
|
-
*
|
|
329
|
+
* One window of the producer/consumer pair below: {@link produceIndexEntries}
|
|
330
|
+
* walks the window's index entries and {@link resolveIndexEntries} resolves them
|
|
331
|
+
* to rows in {@link ROW_RESOLUTION_BATCH}-bounded batches — one
|
|
332
|
+
* {@link StoreTableBase.readEffectiveRowsByKeys} round trip per batch instead of
|
|
333
|
+
* one awaited point read per entry. A multi-seek runs the SAME consumer over the
|
|
334
|
+
* concatenation of every window's producer instead of calling this method — see
|
|
335
|
+
* {@link scanMultiSeek}.
|
|
336
|
+
*/
|
|
337
|
+
protected scanIndex(indexStore: KVStore, access: IndexAccessPattern, filterInfo: FilterInfo): AsyncIterable<Row>;
|
|
338
|
+
/**
|
|
339
|
+
* Producer half of the index-scan pair: walk one byte window's index entries —
|
|
340
|
+
* {@link iterateEffective} yields the committed entries merged with this
|
|
341
|
+
* transaction's pending index puts/deletes (read-your-own-writes over the index),
|
|
342
|
+
* in index-key byte order, the order the isolation overlay merge relies on
|
|
343
|
+
* (`isolated-table.ts` § buildSortKey) — and yield, per surviving entry, its data
|
|
344
|
+
* key plus the FilterInfos a resolved row may match. Entries stay in visit order
|
|
345
|
+
* end to end, so index-key order is preserved through the batching consumer.
|
|
346
|
+
*
|
|
347
|
+
* Only the defenses that need nothing but the entry itself run here, BEFORE the
|
|
348
|
+
* row read; everything row-dependent lives in {@link resolveRowBatch}.
|
|
269
349
|
*/
|
|
270
|
-
|
|
350
|
+
private produceIndexEntries;
|
|
351
|
+
/**
|
|
352
|
+
* Consumer half of the index-scan pair: collect up to {@link ROW_RESOLUTION_BATCH}
|
|
353
|
+
* produced entries and resolve each batch through {@link resolveRowBatch} — one
|
|
354
|
+
* store round trip per batch. Batching changes the round-trip count, never the row
|
|
355
|
+
* set or its order: batches are resolved and yielded in entry order.
|
|
356
|
+
*
|
|
357
|
+
* Early termination: a downstream `break` mid-batch finishes this generator, whose
|
|
358
|
+
* `for await` closes `entries` — and the iterate cursor under it — on the way out;
|
|
359
|
+
* no further batch is collected or read.
|
|
360
|
+
*/
|
|
361
|
+
private resolveIndexEntries;
|
|
362
|
+
/**
|
|
363
|
+
* Resolve one bounded batch of index entries to rows — a single
|
|
364
|
+
* {@link StoreTableBase.readEffectiveRowsByKeys} call (which applies this
|
|
365
|
+
* transaction's pending deletes/puts BEFORE touching the committed store; the
|
|
366
|
+
* precedence is not re-implemented here) — then run the row-dependent defenses in
|
|
367
|
+
* entry order, mirroring the memory layer's live-recheck:
|
|
368
|
+
*
|
|
369
|
+
* - a resolved-null row (the entry's row was deleted — a pending index delete
|
|
370
|
+
* would normally suppress the entry, but a committed entry can lag) is skipped,
|
|
371
|
+
* and cannot shift later positions (the read answers positionally);
|
|
372
|
+
* - the multi-seek `seen` set is RE-checked after the read: the producer's
|
|
373
|
+
* pre-check ran before this batch was collected, so two entries at one data key
|
|
374
|
+
* inside a single batch (a stale index entry) are only caught here — without
|
|
375
|
+
* the re-check the row would yield twice;
|
|
376
|
+
* - a row is accepted when it matches ANY of the entry's FilterInfos (the byte
|
|
377
|
+
* window is only a superset, and a stale entry whose indexed column no longer
|
|
378
|
+
* matches is dropped), and `seen` is added to on yield ALONE — a stale entry
|
|
379
|
+
* that fails its residual must not suppress the row's live entry later.
|
|
380
|
+
*
|
|
381
|
+
* NOTE: still one data-store point read per matched index entry — the row lives in
|
|
382
|
+
* the data store, not the index (the index value carries only the data key, no
|
|
383
|
+
* covering payload); batching collapses the reads' round trips, not their count.
|
|
384
|
+
* If index-covered scans ever dominate a profile, consider storing the serialized
|
|
385
|
+
* row as a covering index value, at the cost of an index rewrite on EVERY column
|
|
386
|
+
* change (not just indexed columns) — deliberately not done here.
|
|
387
|
+
*/
|
|
388
|
+
private resolveRowBatch;
|
|
389
|
+
/**
|
|
390
|
+
* Fail loudly on a structurally impossible FilterInfo — one whose own idxStr describes
|
|
391
|
+
* an access this table's schema cannot express. Not a soundness fallback: everything
|
|
392
|
+
* that is merely UNSERVEABLE degrades to a wider window plus the {@link matchesFilters}
|
|
393
|
+
* residual instead of raising. `kind` names the plan shape for the message.
|
|
394
|
+
*/
|
|
395
|
+
private malformedFilterInfo;
|
|
271
396
|
/**
|
|
272
397
|
* Fail loudly on a multi-seek FilterInfo this table cannot serve. The plan already
|
|
273
398
|
* dropped the residual Filter, and {@link matchesFilters} ANDs every pushed
|
|
@@ -303,27 +428,43 @@ export declare abstract class StoreTableScan extends StoreTableBase {
|
|
|
303
428
|
* away before scanning:
|
|
304
429
|
* - Tuples whose encoded prefix byte-matches (duplicate bound parameters, or
|
|
305
430
|
* case variants under a NOCASE key collation C) share ONE window, each kept
|
|
306
|
-
* as a residual alternative — see {@link
|
|
431
|
+
* as a residual alternative — see {@link PendingIndexEntry}.
|
|
307
432
|
* - A window with no finite upper bound (all-0xff prefix — see
|
|
308
433
|
* buildIndexPrefixBounds) contains every later-sorting window outright (any
|
|
309
434
|
* key ≥ an all-0xff prefix necessarily starts with it), so those windows fold
|
|
310
435
|
* into it instead of re-scanning an overlapping range out of order.
|
|
311
436
|
*
|
|
312
|
-
* Emission is lazy
|
|
313
|
-
*
|
|
314
|
-
*
|
|
315
|
-
*
|
|
437
|
+
* Emission is lazy and batched ACROSS windows: every window's
|
|
438
|
+
* {@link produceIndexEntries} feeds ONE {@link resolveIndexEntries} consumer, whose
|
|
439
|
+
* batches are bounded by ENTRY count, not window count — so a hundred single-row
|
|
440
|
+
* windows collapse into a single row-resolution round trip, and
|
|
441
|
+
* `… in (…) limit 1` materializes at most one {@link ROW_RESOLUTION_BATCH} batch
|
|
442
|
+
* rather than every window. Each window's entries still come from
|
|
443
|
+
* {@link iterateEffective}, so read-your-own-writes and the stale-entry /
|
|
444
|
+
* deleted-row defenses hold per seek key.
|
|
316
445
|
*/
|
|
317
446
|
protected scanMultiSeek(spec: IdxStrSpec, filterInfo: FilterInfo): AsyncIterable<Row>;
|
|
447
|
+
/**
|
|
448
|
+
* Concatenate the disjoint multi-seek windows' producers, in window (byte) order.
|
|
449
|
+
* Each entry carries its window's full FilterInfo list — the primary tuple plus
|
|
450
|
+
* the byte-equal merged alternatives — so acceptance stays per row, in order (see
|
|
451
|
+
* {@link PendingIndexEntry}).
|
|
452
|
+
*/
|
|
453
|
+
private produceMultiSeekEntries;
|
|
318
454
|
/**
|
|
319
455
|
* Multi-seek over the PRIMARY key: each tuple is a full PK, resolved by a point
|
|
320
456
|
* read in ascending encoded-data-key order (the table's native emission order).
|
|
321
457
|
*
|
|
322
|
-
*
|
|
323
|
-
*
|
|
324
|
-
*
|
|
325
|
-
*
|
|
326
|
-
*
|
|
458
|
+
* Reached from `computeBestAccessPlan`'s primary-key multi-seek arm
|
|
459
|
+
* (store-module-access-plan.ts), which claims a whole-primary-key `IN` — literal list
|
|
460
|
+
* or runtime-valued set — through `EQ_OR_IN_OPS` and advertises `_primary_` with every
|
|
461
|
+
* PK column as its seek columns. That arm declines exactly the two shapes the
|
|
462
|
+
* {@link multiSeekMalformed} throws below assert against (over the seek cap, and a
|
|
463
|
+
* semantic-ordering PK member), so a throw here means the plan and the scan disagreed.
|
|
464
|
+
*
|
|
465
|
+
* The ascending-encoded-key emission order is a contract, not an implementation
|
|
466
|
+
* detail: the isolation layer merges this stream with its overlay by primary key
|
|
467
|
+
* (`bug-isolation-multiseek-merge-order`), and the arm advertises PK ordering off it.
|
|
327
468
|
*/
|
|
328
469
|
protected scanMultiSeekPrimary(tuples: MultiSeekTuple[], seekWidth: number, filterInfo: FilterInfo): AsyncIterable<Row>;
|
|
329
470
|
/**
|
|
@@ -397,9 +538,10 @@ interface PKAccessPattern {
|
|
|
397
538
|
/**
|
|
398
539
|
* Secondary-index access pattern analysis result: the chosen index plus the
|
|
399
540
|
* encoded byte window {@link StoreTableScan.scanIndex} iterates. `point` is a
|
|
400
|
-
* leading-prefix EQ window
|
|
401
|
-
*
|
|
402
|
-
* single entry)
|
|
541
|
+
* leading-prefix EQ window; `range` is an LT/LE/GT/GE window, either on the leading
|
|
542
|
+
* column or nested inside an equality prefix (`prefixRangeSeek`). Both resolve to a
|
|
543
|
+
* `bounds` scan (an index seek is always a prefix scan, never a single entry) — nothing
|
|
544
|
+
* downstream branches on the tag, which is descriptive only.
|
|
403
545
|
*/
|
|
404
546
|
interface IndexAccessPattern {
|
|
405
547
|
index: TableIndexSchema;
|
|
@@ -416,37 +558,5 @@ interface MultiSeekTuple {
|
|
|
416
558
|
entries: ConstraintEntry[];
|
|
417
559
|
values: SqlValue[];
|
|
418
560
|
}
|
|
419
|
-
/**
|
|
420
|
-
* Per-window context a multi-seek passes to {@link StoreTableScan.scanIndex} (absent for
|
|
421
|
-
* ordinary single-window scans).
|
|
422
|
-
*
|
|
423
|
-
* `seen` — data-key hexes already YIELDED by an earlier window. Checked before the
|
|
424
|
-
* data-store read (a cross-window duplicate costs no extra `get`), added only on a
|
|
425
|
-
* yield: adding at visit time would let a stale index entry (row re-keyed since
|
|
426
|
-
* indexing) that FAILS its residual poison the set and suppress the row's live entry
|
|
427
|
-
* in a later window.
|
|
428
|
-
* NOTE: holds one hex string per row the multi-seek yields, for the whole seek — the
|
|
429
|
-
* only unbounded allocation on this path. Windows are byte-disjoint, so the only real
|
|
430
|
-
* duplicate source is a stale index entry; if a large-result `IN` ever shows up as a
|
|
431
|
-
* memory problem, the set can be scoped per window (or dropped for a consistent store).
|
|
432
|
-
*
|
|
433
|
-
* `collations` — the constraint collation map, resolved once per multi-seek (it is
|
|
434
|
-
* identical for every window).
|
|
435
|
-
*
|
|
436
|
-
* `extraTuples` — additional seek tuples whose encoded window byte-equals this
|
|
437
|
-
* window's (duplicate bound parameters, or case-variant values under a NOCASE key
|
|
438
|
-
* collation). A row is yielded when it matches the primary FilterInfo OR any of
|
|
439
|
-
* these. Index bytes now encode under the same collation C the residual compares
|
|
440
|
-
* under (`indexKeyCollations`), so byte-equal tuples are C-equal and each merged
|
|
441
|
-
* tuple's residual admits the same rows — the OR is redundancy, kept because it is
|
|
442
|
-
* what makes the fold safe by construction rather than by that argument, and it is
|
|
443
|
-
* what a custom equality-only normalizer (byte-equal ⇏ comparator-equal order but
|
|
444
|
-
* equal partition) still relies on.
|
|
445
|
-
*/
|
|
446
|
-
interface MultiSeekWindowContext {
|
|
447
|
-
seen: Set<string>;
|
|
448
|
-
collations: ReadonlyMap<number, CollationFunction>;
|
|
449
|
-
extraTuples: readonly FilterInfo[];
|
|
450
|
-
}
|
|
451
561
|
export {};
|
|
452
562
|
//# sourceMappingURL=store-table-scan.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store-table-scan.d.ts","sourceRoot":"","sources":["../../../src/common/store-table-scan.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EACN,iBAAiB,EASjB,KAAK,UAAU,EACf,KAAK,iBAAiB,EAEtB,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,KAAK,GAAG,EACR,KAAK,UAAU,EACf,KAAK,QAAQ,EACb,MAAM,kBAAkB,CAAC;AAE1B,OAAO,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAYvD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"store-table-scan.d.ts","sourceRoot":"","sources":["../../../src/common/store-table-scan.ts"],"names":[],"mappings":"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"}
|