@quereus/store 4.5.0 → 4.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/README.md +34 -6
  2. package/dist/src/common/encoding.d.ts +4 -3
  3. package/dist/src/common/encoding.d.ts.map +1 -1
  4. package/dist/src/common/encoding.js +17 -13
  5. package/dist/src/common/encoding.js.map +1 -1
  6. package/dist/src/common/events.d.ts +31 -25
  7. package/dist/src/common/events.d.ts.map +1 -1
  8. package/dist/src/common/events.js +38 -39
  9. package/dist/src/common/events.js.map +1 -1
  10. package/dist/src/common/implicit-unique-index.d.ts +80 -0
  11. package/dist/src/common/implicit-unique-index.d.ts.map +1 -0
  12. package/dist/src/common/implicit-unique-index.js +148 -0
  13. package/dist/src/common/implicit-unique-index.js.map +1 -0
  14. package/dist/src/common/index.d.ts +7 -3
  15. package/dist/src/common/index.d.ts.map +1 -1
  16. package/dist/src/common/index.js +5 -2
  17. package/dist/src/common/index.js.map +1 -1
  18. package/dist/src/common/json-key.d.ts +30 -2
  19. package/dist/src/common/json-key.d.ts.map +1 -1
  20. package/dist/src/common/json-key.js +49 -2
  21. package/dist/src/common/json-key.js.map +1 -1
  22. package/dist/src/common/key-builder.d.ts +40 -20
  23. package/dist/src/common/key-builder.d.ts.map +1 -1
  24. package/dist/src/common/key-builder.js +31 -23
  25. package/dist/src/common/key-builder.js.map +1 -1
  26. package/dist/src/common/kv-store.d.ts +10 -0
  27. package/dist/src/common/kv-store.d.ts.map +1 -1
  28. package/dist/src/common/pk-key-resolution.d.ts +348 -0
  29. package/dist/src/common/pk-key-resolution.d.ts.map +1 -0
  30. package/dist/src/common/pk-key-resolution.js +481 -0
  31. package/dist/src/common/pk-key-resolution.js.map +1 -0
  32. package/dist/src/common/store-module-access-plan.d.ts +32 -0
  33. package/dist/src/common/store-module-access-plan.d.ts.map +1 -0
  34. package/dist/src/common/store-module-access-plan.js +428 -0
  35. package/dist/src/common/store-module-access-plan.js.map +1 -0
  36. package/dist/src/common/store-module-alter-column.d.ts +23 -0
  37. package/dist/src/common/store-module-alter-column.d.ts.map +1 -0
  38. package/dist/src/common/store-module-alter-column.js +389 -0
  39. package/dist/src/common/store-module-alter-column.js.map +1 -0
  40. package/dist/src/common/store-module-alter.d.ts +55 -0
  41. package/dist/src/common/store-module-alter.d.ts.map +1 -0
  42. package/dist/src/common/store-module-alter.js +504 -0
  43. package/dist/src/common/store-module-alter.js.map +1 -0
  44. package/dist/src/common/store-module-base.d.ts +268 -0
  45. package/dist/src/common/store-module-base.d.ts.map +1 -0
  46. package/dist/src/common/store-module-base.js +362 -0
  47. package/dist/src/common/store-module-base.js.map +1 -0
  48. package/dist/src/common/store-module-catalog.d.ts +269 -0
  49. package/dist/src/common/store-module-catalog.d.ts.map +1 -0
  50. package/dist/src/common/store-module-catalog.js +462 -0
  51. package/dist/src/common/store-module-catalog.js.map +1 -0
  52. package/dist/src/common/store-module-index-build.d.ts +88 -0
  53. package/dist/src/common/store-module-index-build.d.ts.map +1 -0
  54. package/dist/src/common/store-module-index-build.js +280 -0
  55. package/dist/src/common/store-module-index-build.js.map +1 -0
  56. package/dist/src/common/store-module-index.d.ts +116 -0
  57. package/dist/src/common/store-module-index.d.ts.map +1 -0
  58. package/dist/src/common/store-module-index.js +373 -0
  59. package/dist/src/common/store-module-index.js.map +1 -0
  60. package/dist/src/common/store-module-rename.d.ts +49 -0
  61. package/dist/src/common/store-module-rename.d.ts.map +1 -0
  62. package/dist/src/common/store-module-rename.js +268 -0
  63. package/dist/src/common/store-module-rename.js.map +1 -0
  64. package/dist/src/common/store-module-schema-rewrite.d.ts +57 -0
  65. package/dist/src/common/store-module-schema-rewrite.d.ts.map +1 -0
  66. package/dist/src/common/store-module-schema-rewrite.js +140 -0
  67. package/dist/src/common/store-module-schema-rewrite.js.map +1 -0
  68. package/dist/src/common/store-module-schema-sync.d.ts +232 -0
  69. package/dist/src/common/store-module-schema-sync.d.ts.map +1 -0
  70. package/dist/src/common/store-module-schema-sync.js +529 -0
  71. package/dist/src/common/store-module-schema-sync.js.map +1 -0
  72. package/dist/src/common/store-module.d.ts +43 -909
  73. package/dist/src/common/store-module.d.ts.map +1 -1
  74. package/dist/src/common/store-module.js +184 -3276
  75. package/dist/src/common/store-module.js.map +1 -1
  76. package/dist/src/common/store-table-base.d.ts +539 -0
  77. package/dist/src/common/store-table-base.d.ts.map +1 -0
  78. package/dist/src/common/store-table-base.js +900 -0
  79. package/dist/src/common/store-table-base.js.map +1 -0
  80. package/dist/src/common/store-table-constraints.d.ts +237 -0
  81. package/dist/src/common/store-table-constraints.d.ts.map +1 -0
  82. package/dist/src/common/store-table-constraints.js +624 -0
  83. package/dist/src/common/store-table-constraints.js.map +1 -0
  84. package/dist/src/common/store-table-scan.d.ts +452 -0
  85. package/dist/src/common/store-table-scan.d.ts.map +1 -0
  86. package/dist/src/common/store-table-scan.js +901 -0
  87. package/dist/src/common/store-table-scan.js.map +1 -0
  88. package/dist/src/common/store-table.d.ts +71 -1144
  89. package/dist/src/common/store-table.d.ts.map +1 -1
  90. package/dist/src/common/store-table.js +168 -2385
  91. package/dist/src/common/store-table.js.map +1 -1
  92. package/dist/src/testing/index.d.ts +12 -0
  93. package/dist/src/testing/index.d.ts.map +1 -0
  94. package/dist/src/testing/index.js +12 -0
  95. package/dist/src/testing/index.js.map +1 -0
  96. package/dist/src/testing/kv-assert.d.ts +19 -0
  97. package/dist/src/testing/kv-assert.d.ts.map +1 -0
  98. package/dist/src/testing/kv-assert.js +25 -0
  99. package/dist/src/testing/kv-assert.js.map +1 -0
  100. package/dist/src/testing/kv-conformance.d.ts.map +1 -1
  101. package/dist/src/testing/kv-conformance.js +1 -15
  102. package/dist/src/testing/kv-conformance.js.map +1 -1
  103. package/dist/src/testing/kv-provider-conformance.d.ts +54 -0
  104. package/dist/src/testing/kv-provider-conformance.d.ts.map +1 -0
  105. package/dist/src/testing/kv-provider-conformance.js +183 -0
  106. package/dist/src/testing/kv-provider-conformance.js.map +1 -0
  107. package/package.json +8 -8
@@ -0,0 +1,481 @@
1
+ /**
2
+ * Resolution of the physical KEY properties of a table's primary-key and
3
+ * secondary-index columns: the per-column key collation, the key-identity value
4
+ * transform for semantic-ordering types, and the order-safety predicates that
5
+ * decide whether a byte window may stand in for a comparator range.
6
+ *
7
+ * Shared by the store table (data-key + index-maintenance encoding) and
8
+ * `StoreModule` (index rebuild, access planning) so key encoding can never drift
9
+ * between the two.
10
+ */
11
+ import { createTypedComparator, hasSemanticOrdering, semanticKeyTransform, logicalTypeCanHoldText, pkKeyCollationName, JSON_TYPE, TIMESPAN_TYPE, } from '@quereus/quereus';
12
+ import { jsonKeyEncodable, jsonStructuralKey } from './json-key.js';
13
+ /**
14
+ * Resolve the per-column KEY collation for each primary-key column.
15
+ *
16
+ * The store encodes PRIMARY KEY uniqueness/ordering PHYSICALLY in the key bytes, so each
17
+ * text-capable PK column's key must be encoded under the collation the engine actually
18
+ * COMPARES that column under. Returns one entry per PK member, in `pkDef` order:
19
+ * - collation-aware member (`text`, `any` — the types whose `compare` applies the
20
+ * collation it is handed, `LogicalType.collationAware`) → its declared `collation`
21
+ * (normalized upper-case), or `fallback` (the table key collation K) when the
22
+ * column carries none.
23
+ * - text-capable but collation-blind member — `json` and the temporal types
24
+ * (`date` / `time` / `datetime` / `timespan`) → hard-coded `'BINARY'`: those
25
+ * types' `compare` is not the generic storage-class + collation comparison (the
26
+ * temporals ignore the argument; JSON ranks structurally), and their empty
27
+ * `supportedCollations` list keeps a non-BINARY column COLLATE out at DDL time.
28
+ * - never-text member → `undefined`: collation is meaningless for
29
+ * integer/real/blob keys (they encode type-natively), so the encoder ignores
30
+ * it and the data/index key bytes are identical regardless.
31
+ *
32
+ * Note that `undefined` does NOT mean "encode type-natively" to `encodeValue` — it means
33
+ * "fall back to `options.collation`", the table key collation K. Only a never-text column
34
+ * may be left `undefined`, because only it never reaches the collation branch.
35
+ *
36
+ * Every name returned here is encoded through the key-normalizer resolver carried
37
+ * in `EncodeOptions.normalizers` (`db.getKeyNormalizerResolver()`), so a collation
38
+ * registered or overridden with `db.registerCollation` produces key bytes that agree
39
+ * with the comparator the store's UNIQUE enforcement uses. A collation that cannot
40
+ * key — unregistered, or registered with a comparator but no normalizer — is rejected
41
+ * at DDL time by `StoreTableBase.validateKeyCollations`, so no encode call ever has
42
+ * to fall back. Shared by `StoreTable` (data-key + index-maintenance) and
43
+ * the index rebuild `buildIndexEntries` (store-module-index-build.ts) so the PK suffix
44
+ * encoding can never drift between the two.
45
+ */
46
+ export function resolvePkKeyCollations(pkDef, columns, fallback) {
47
+ return pkDef.map(def => {
48
+ const col = columns[def.index];
49
+ // Delegates the collation-aware/collation-blind/never-text branch to the engine's
50
+ // `pkKeyCollationName` — the same decision `quereus-isolation`'s modified-PK set
51
+ // makes for its own key normalizers, so the two can never drift apart again. Only
52
+ // the fallback-to-K and uppercase-normalization are store-specific.
53
+ const name = pkKeyCollationName(col);
54
+ return name === undefined ? undefined : (name || fallback).toUpperCase();
55
+ });
56
+ }
57
+ /**
58
+ * Per-column KEY collation for a secondary index's OWN columns (the index half of
59
+ * `buildIndexKey`), positionally aligned with `index.columns` — the index twin of
60
+ * {@link resolvePkKeyCollations}. Same three-way branch, delegated to the engine's
61
+ * `pkKeyCollationName`:
62
+ * - never-text column (`integer`, `real`, `blob`) → `undefined`: encoded
63
+ * type-natively, collation is moot.
64
+ * - text-capable but collation-blind (`json`, the temporal types) → hard-coded
65
+ * `'BINARY'` — those types' `compare` is not the generic collation comparison.
66
+ * - collation-aware (`text`, `any`) → the index column's own COLLATE, else the
67
+ * table column's declared collation, else `'BINARY'`.
68
+ *
69
+ * The fallback is `BINARY`, NOT the table key collation K — deliberately asymmetric
70
+ * with {@link resolvePkKeyCollations}, whose fallback IS K. For a PK member the
71
+ * asymmetry is illusory: `reconcilePkCollations` (store-module-schema-rewrite.ts)
72
+ * rewrites an undecorated text PK column's declared collation to K at CREATE time, so
73
+ * for a PK member K *is* the declared collation. No such rewrite exists for non-PK
74
+ * columns — an undecorated `text` column genuinely compares under BINARY (in the
75
+ * engine's operators and in the store's `matchesFilters` residual alike), so BINARY is
76
+ * what its index bytes must encode under, or the stored order/equality disagrees with
77
+ * every comparison made against it.
78
+ */
79
+ export function resolveIndexKeyCollations(index, columns) {
80
+ return index.columns.map(col => {
81
+ const tableCol = columns[col.index];
82
+ // The collation handed to `pkKeyCollationName` is always defined, so an
83
+ // `undefined` answer means never-text (its textual branch returns the collation
84
+ // verbatim; passing a bare `col.collation ?? tableCol.collation` would conflate
85
+ // "undecorated text column" with "never-text" and silently fall back to K at
86
+ // encode time — the exact drift this resolver exists to remove).
87
+ const name = pkKeyCollationName(tableCol === undefined ? undefined : {
88
+ logicalType: tableCol.logicalType,
89
+ collation: col.collation ?? tableCol.collation ?? 'BINARY',
90
+ });
91
+ return name === undefined ? undefined : (name || 'BINARY').toUpperCase();
92
+ });
93
+ }
94
+ /**
95
+ * The store's per-type key VALUE TRANSFORM for a semantic-ordering logical type, or
96
+ * `undefined` when raw values already key faithfully.
97
+ *
98
+ * The contract is TWO-sided — identity AND order. `StoreTable` implements no
99
+ * `comparePrimaryKey`, so the isolation layer merges overlay against underlying with
100
+ * its own fallback comparator (the PK type's `compare` via `createTypedComparator`)
101
+ * and needs this table's key BYTE order to agree with it; and byte equality must be
102
+ * exactly the type's equality, or two spellings of one value land on two keys.
103
+ *
104
+ * - JSON → the store-local structural encoder ({@link jsonStructuralKey}): a
105
+ * `Uint8Array` whose memcmp order reproduces the type's structural `compare`
106
+ * (canonical-text bytes are identity-faithful but sort `[10]` before `[2]`,
107
+ * which broke overlay shadowing — see json-key.ts).
108
+ * - Everything else → the engine's {@link semanticKeyTransform} (the type's
109
+ * `groupKey`). Today that is TIMESPAN: total seconds against the same reference
110
+ * date as `TIMESPAN.compare`, so 'PT1H' and 'PT60M' collide on one key AND byte
111
+ * order is elapsed-time order.
112
+ *
113
+ * JSON deliberately gets NO engine `groupKey` — `groupKey` also drives GROUP BY /
114
+ * `IN` / hash-join identity, where canonical-text identity is already correct and a
115
+ * `Uint8Array` key would be a behaviour change for no benefit. Hence this
116
+ * store-local seam. A semantic-ordering type with NEITHER a `groupKey` nor an entry
117
+ * here cannot key a persisted structure soundly; `StoreTable` rejects it at DDL time
118
+ * (see `validateSemanticKeyTransforms`).
119
+ *
120
+ * This is the PHYSICAL KEY BYTES resolver — it feeds `buildIndexKey` / `buildDataKey`
121
+ * and its output is memcmp-ordered and persisted. {@link storeDedupeKeyTransform} is
122
+ * its build/validate-time twin: same job, but for the transient UNIQUE dedupe
123
+ * signature, which is free to leave a comparable value for `serializeKey`'s collation
124
+ * normalizer instead of committing to structural bytes.
125
+ */
126
+ export function storeSemanticKeyTransform(type) {
127
+ // Matched by NAME, not object identity (`type === JSON_TYPE`): the engine and this
128
+ // package can be loaded as two module instances (e.g. the logic suite runs the
129
+ // engine from src via ts-node while the store resolves `@quereus/quereus` to dist),
130
+ // and each instance carries its own JSON_TYPE singleton. The `hasSemanticOrdering`
131
+ // gate keeps a hypothetical plain custom type named 'JSON' out of this branch.
132
+ if (hasSemanticOrdering(type) && type.name === JSON_TYPE.name)
133
+ return jsonStructuralKey;
134
+ return semanticKeyTransform(type);
135
+ }
136
+ /**
137
+ * The build/validate-time twin of {@link storeSemanticKeyTransform}, for the UNIQUE
138
+ * dedupe SIGNATURE rather than physical key bytes (`buildIndexEntries`'s in-pass
139
+ * `seen` check and `assertNoDuplicateRows`, both in store-module-index-build.ts).
140
+ *
141
+ * The signature is a `serializeKey` call over the (possibly transformed) values, and
142
+ * `serializeKey` applies a column's collation NORMALIZER only to a raw `string` value
143
+ * — a transform that returns a `Uint8Array` (as {@link storeSemanticKeyTransform} does
144
+ * for JSON, via {@link jsonStructuralKey}) is tagged as an opaque byte array instead,
145
+ * and the normalizer never runs. For JSON that would silently drop the index
146
+ * COLLATE on exactly the case `JSON_TYPE.compare` treats as text (a string-scalar
147
+ * pair) and let a build-time UNIQUE index admit rows a write-time insert would then
148
+ * reject (bug-store-index-build-dedupe-skips-collation) — so this resolver special-
149
+ * cases JSON: a string scalar is left AS a string, so `serializeKey`'s normalizer
150
+ * still runs; every other JSON value (object, array, number, boolean, or null) falls
151
+ * back to the structural bytes, matching `JSON_TYPE.compare`'s non-text ranking.
152
+ *
153
+ * Every other semantic-ordering type (TIMESPAN today) is unaffected: its `compare`
154
+ * ignores the collation it is handed, and its `groupKey` transform already collides
155
+ * every equal-identity spelling onto one value on both the build and write paths, so
156
+ * this resolver defers to {@link storeSemanticKeyTransform} unchanged.
157
+ */
158
+ export function storeDedupeKeyTransform(type) {
159
+ if (hasSemanticOrdering(type) && type.name === JSON_TYPE.name)
160
+ return jsonDedupeKey;
161
+ return storeSemanticKeyTransform(type);
162
+ }
163
+ /**
164
+ * A declared-JSON value as the UNIQUE dedupe signature must see it: a top-level string
165
+ * scalar verbatim (so `serializeKey` runs the column's collation normalizer over it,
166
+ * mirroring `JSON_TYPE.compare`'s collation-honoring string/string branch), every other
167
+ * node as {@link jsonStructuralKey}'s bytes (mirroring `deepCompareJson`, which takes no
168
+ * collation — a NESTED string leaf is code-point-compared even under an index COLLATE).
169
+ */
170
+ function jsonDedupeKey(value) {
171
+ return typeof value === 'string' ? value : jsonStructuralKey(value);
172
+ }
173
+ /**
174
+ * Resolve the per-column key value transform for each primary-key column —
175
+ * {@link storeSemanticKeyTransform} of the member's logical type. One entry per PK
176
+ * member in `pkDef` order; `undefined` for every type whose raw value already keys
177
+ * faithfully. The companion of {@link resolvePkKeyCollations}: every call site that
178
+ * threads the collations MUST thread these too, or key identity/order drifts
179
+ * between the write path and the rebuild/lookup path.
180
+ */
181
+ export function resolvePkKeyTransforms(pkDef, columns) {
182
+ return pkDef.map(def => storeSemanticKeyTransform(columns[def.index]?.logicalType));
183
+ }
184
+ /**
185
+ * Per-column key value transforms for a secondary index's OWN columns (the
186
+ * index half of `buildIndexKey`), positionally aligned with `index.columns`.
187
+ * Same rule as {@link resolvePkKeyTransforms}: a semantic-ordering member
188
+ * canonicalizes through {@link storeSemanticKeyTransform}, everything else
189
+ * encodes raw.
190
+ */
191
+ export function resolveIndexKeyTransforms(index, columns) {
192
+ return index.columns.map(col => storeSemanticKeyTransform(columns[col.index]?.logicalType));
193
+ }
194
+ /**
195
+ * Per-column {@link storeDedupeKeyTransform} for an arbitrary column-index list —
196
+ * the UNIQUE dedupe-signature counterpart of {@link resolveIndexKeyTransforms},
197
+ * shared by both build/validate-time dedupe sites (`buildIndexEntries`' in-pass
198
+ * `seen` check over an index's columns, `assertNoDuplicateRows` over a constraint's)
199
+ * so the two cannot drift onto different transforms.
200
+ */
201
+ export function resolveDedupeKeyTransforms(colIndices, columns) {
202
+ return colIndices.map(i => storeDedupeKeyTransform(columns[i]?.logicalType));
203
+ }
204
+ /**
205
+ * Per-PK-column typed equality comparator for semantic-ordering members, `undefined`
206
+ * elsewhere. Backs `StoreTableConstraints.keysEqual`: two PK arrays naming the same
207
+ * physical key under {@link resolvePkKeyTransforms} ('PT1H' / 'PT60M') must also
208
+ * answer "same row" here, or self-PK exclusion in the UNIQUE checks false-conflicts
209
+ * a row against its own differently-spelled image.
210
+ */
211
+ export function resolvePkSemanticEquality(pkDef, columns) {
212
+ return pkDef.map(def => {
213
+ const logicalType = columns[def.index]?.logicalType;
214
+ return hasSemanticOrdering(logicalType) ? createTypedComparator(logicalType) : undefined;
215
+ });
216
+ }
217
+ /**
218
+ * True when `col` can produce a TEXT value at runtime, and so when its physical
219
+ * key bytes are produced by a collation normalizer rather than a type-native
220
+ * encoding. The `ColumnSchema`-shaped wrapper over the engine's
221
+ * {@link logicalTypeCanHoldText}; both collation-safety guards over the store's
222
+ * secondary indexes — the write-side `StoreTableConstraints.indexSeekHonorsEnforcementCollation`
223
+ * and the read-side {@link keyOrderMatchesCollation} — exempt a never-text column from their
224
+ * key-vs-comparison collation test, so a false "non-text" answer is a silent wrong-result
225
+ * (a seek under the wrong collation, with the residual dropped).
226
+ */
227
+ export function columnCanHoldText(col) {
228
+ return logicalTypeCanHoldText(col?.logicalType);
229
+ }
230
+ /**
231
+ * True when this semantic-ordering type's store key bytes memcmp in EXACTLY the type's
232
+ * `compare` order — and byte-equal exactly when `compare` says equal — for every value a
233
+ * store-backed table can HOLD.
234
+ *
235
+ * An explicit per-type assertion, deliberately NOT
236
+ * `storeSemanticKeyTransform(type) !== undefined`: a transform is only required to be
237
+ * identity-faithful, and `validateSemanticKeyTransforms` can check only that one exists —
238
+ * nothing forces a future type's transform to be order-preserving. A type absent here
239
+ * keeps the pre-existing blanket decline in {@link keyOrderMatchesCollation} — no byte
240
+ * window, no ordering advertisement, answered by full scan + the type-aware residual.
241
+ *
242
+ * Why the stored-value claim holds for the two listed types:
243
+ * - TIMESPAN — every write path coerces through `TIMESPAN_TYPE.parse` (row coercion,
244
+ * the engine's pre-coerced UPDATE fast path, the ALTER retype backfill's
245
+ * `validateAndParse`), which raises on anything unparseable. So every stored value
246
+ * parses, `groupKey` returns total seconds for all of them, and every stored key
247
+ * member is a fixed-width NUMERIC whose memcmp order is elapsed-time order. The
248
+ * raw-text fallback branch of `groupKey` is unreachable for stored values.
249
+ * - JSON — every stored value is a `JSON_TYPE.parse` output (never a blob, never a
250
+ * bigint), all of which {@link jsonStructuralKey} encodes; its tag order reproduces
251
+ * both `deepCompareJson`'s rank order and, for cross-storage-class pairs (where
252
+ * `createTypedComparator` short-circuits before reaching `JSON_TYPE.compare`),
253
+ * `getStorageClass`'s NULL < NUMERIC < TEXT < BLOB < OBJECT. SQL NULL never reaches
254
+ * a transform (`encodeCompositeKey` exempts it) and its `0x00` tag sorts below the
255
+ * BLOB tag the structural bytes travel under, matching NULL-first placement.
256
+ *
257
+ * Matched by NAME, not object identity, for the same dual-module-instance reason
258
+ * {@link storeSemanticKeyTransform} documents. This is a claim about values the table
259
+ * holds; a query-supplied seek bound needs {@link semanticProbeIsKeyFaithful} on top.
260
+ */
261
+ export function semanticKeyOrderIsFaithful(type) {
262
+ return hasSemanticOrdering(type)
263
+ && (type.name === TIMESPAN_TYPE.name || type.name === JSON_TYPE.name);
264
+ }
265
+ /**
266
+ * True when `probe`'s key bytes sit at the position the type's `compare` gives it
267
+ * relative to every STORED value — the per-VALUE precondition every re-opened seek
268
+ * window (range bound AND equality probe) needs on top of
269
+ * {@link semanticKeyOrderIsFaithful}, which is a claim about stored values only. A probe
270
+ * comes from the query, and nothing coerces it to the column's declared type, so an
271
+ * unfaithful probe is reachable where an unfaithful stored value is not. Two concrete
272
+ * TIMESPAN under-fetches motivate the gate:
273
+ *
274
+ * - `where d > 5`: `createTypedComparator` short-circuits on the storage-class
275
+ * mismatch before `TIMESPAN.compare` runs, so every stored (TEXT-class) value ranks
276
+ * ABOVE the NUMERIC probe and the predicate admits every row — but `groupKey(5)`
277
+ * passes the non-string through unchanged, so the byte window is `> NUMERIC(5)` and
278
+ * a row storing 'PT1S' (total 1) falls outside it. Rows lost.
279
+ * - `where d > 'not a duration'`: `TIMESPAN.compare` falls back to BINARY text against
280
+ * the canonical stored text, while `groupKey` falls back to the raw text, which
281
+ * encodes TEXT-tagged and sorts above every NUMERIC-tagged stored key. Different
282
+ * position, rows lost.
283
+ *
284
+ * THE canonical statement of how each arm degrades on `false` — the call sites point
285
+ * here rather than restating it. A window may only ever be WIDENED, never narrowed, and
286
+ * the type-aware residual (`StoreTableScan.matchesFilters`) decides rows in every case:
287
+ *
288
+ * - a RANGE bound is SKIPPED (`buildPKRangeBounds`, `buildIndexRangeBounds`) — dropping
289
+ * one bound only widens the window, worst case back to the full scan.
290
+ * - a full-PK EQUALITY declines the WHOLE point arm (`analyzePKAccess`) — a point window
291
+ * is a single byte position, so it cannot be widened, only under-fetched.
292
+ * - a secondary index's EQ PREFIX stops SHORT at that column (`analyzeIndexAccess`) — a
293
+ * window over the columns before it is a strict superset.
294
+ * - an IN-list MULTI-SEEK has none of the three available (its merged windows are the
295
+ * whole access) and so never reaches this gate: it is declined per SCHEMA upstream.
296
+ *
297
+ * The TIMESPAN arm calls the COLUMN's own `groupKey`, not the imported singleton's,
298
+ * for the dual-module-instance reason {@link storeSemanticKeyTransform} documents.
299
+ * A semantic-ordering type outside the {@link semanticKeyOrderIsFaithful} allow-list
300
+ * answers `false`, consistent with that predicate — no window is built for it anyway.
301
+ */
302
+ export function semanticProbeIsKeyFaithful(type, probe) {
303
+ if (!hasSemanticOrdering(type))
304
+ return true;
305
+ if (type.name === TIMESPAN_TYPE.name) {
306
+ // A number out of groupKey means the probe parsed; the raw-string fallback (or
307
+ // a non-string probe) means it has no faithful byte position.
308
+ return typeof probe === 'string' && typeof type.groupKey?.(probe) === 'number';
309
+ }
310
+ if (type.name === JSON_TYPE.name)
311
+ return jsonKeyEncodable(probe);
312
+ return false;
313
+ }
314
+ /**
315
+ * True when a byte window — or a byte-order advertisement — over `column` is SOUND.
316
+ *
317
+ * The store physically orders rows by memcmp of each column's key bytes, which for a
318
+ * text-capable column are produced by `keyCollation`'s normalizer. A range seek (and the
319
+ * `providesOrdering` / `monotonicOn` advertisement) equates that byte order with the order
320
+ * `compareCollation`'s comparator defines. Two things must hold for that equation:
321
+ *
322
+ * - **Same collation on both sides** (`compareCollation === keyCollation`). A `keyCollation`
323
+ * merely COARSER than `compareCollation` is not enough: it would need the key normalizer
324
+ * to be monotone with respect to the OTHER collation's order. It generally is not, even
325
+ * for built-ins: with `keyCollation = NOCASE` and `compareCollation = BINARY`,
326
+ * 'K' (U+212A KELVIN SIGN) is `> 'z'` under BINARY, yet its key bytes are
327
+ * `toLowerCase('K') = 'k'`, which sorts BEFORE 'z' — the row falls outside a `> 'z'`
328
+ * window and is silently dropped.
329
+ * - **The normalizer preserves order** (`db._isCollationOrderPreserving`). `registerCollation`
330
+ * promises only that a normalizer partitions strings the way the comparator calls them
331
+ * equal; a custom pair may agree on equality and disagree on order.
332
+ *
333
+ * A never-text column is exempt: its key bytes are type-native and collation-independent.
334
+ *
335
+ * Returning `false` costs an optimization (the caller full-scans and lets the
336
+ * collation-aware `matchesFilters` residual decide); returning `true` wrongly is a silent
337
+ * wrong-result. Shared by `StoreTable`'s read arms and `StoreModule`'s access planner so the
338
+ * "mark handled" and "build a window" decisions can never disagree.
339
+ *
340
+ * The PK caller ({@link pkOrderPreservingPrefixLength}) can genuinely pass two DIFFERENT
341
+ * collations, because {@link resolvePkKeyCollations} still falls back to the table key
342
+ * collation K for an undecorated text PK member. The secondary-index caller
343
+ * ({@link indexLeadingRangeIsOrderSafe}) usually passes the same name on both sides — index
344
+ * key bytes encode under the index column's own effective collation, which is what the scan
345
+ * residual re-compares under — so for a `text` or `any` column it reduces to the
346
+ * `orderPreserving` assertion alone. The one index shape where the two sides still differ is
347
+ * a collation-blind column (`json`, the temporal types) under an index column carrying an
348
+ * explicit non-BINARY COLLATE (index DDL does not type-gate the way column DDL does): its
349
+ * key bytes are hard-`BINARY` while its residual compares under the declared name, and this
350
+ * predicate correctly declines it.
351
+ */
352
+ export function keyOrderMatchesCollation(db, column, keyCollation, compareCollation) {
353
+ // A semantic-ordering logical type (TIMESPAN, JSON — see docs/types.md "Semantic
354
+ // ordering") is ordered by its `compare` (elapsed time / structural), which the
355
+ // engine's Sort, comparison operators, and the memory backend all use. Byte order
356
+ // stands in for that order only for the types `semanticKeyOrderIsFaithful` asserts
357
+ // it for; any other semantic-ordering type keeps the blanket decline — no byte
358
+ // window, no ordering advertisement, full scan + the type-aware residual. A
359
+ // faithful type FALLS THROUGH to the collation checks below rather than returning
360
+ // true: `logicalTypeCanHoldText` admits both types (json is OBJECT-physical but
361
+ // text-capable; timespan is TEXT-physical), and the collation comparison is what
362
+ // correctly declines a collation-blind column under an INDEX column carrying an
363
+ // explicit non-BINARY COLLATE (index DDL does not type-gate the way column DDL
364
+ // does) — its key bytes are hard-BINARY while the residual re-compares under the
365
+ // declared name. Seek BOUNDS additionally pass through the per-value
366
+ // `semanticProbeIsKeyFaithful` gate at window-build time.
367
+ if (hasSemanticOrdering(column?.logicalType) && !semanticKeyOrderIsFaithful(column?.logicalType))
368
+ return false;
369
+ if (!columnCanHoldText(column))
370
+ return true;
371
+ if (compareCollation.toUpperCase() !== keyCollation.toUpperCase())
372
+ return false;
373
+ return db._isCollationOrderPreserving(keyCollation);
374
+ }
375
+ /**
376
+ * The collation `StoreTableScan.matchesFilters` re-compares index position `position`
377
+ * under — the index column's own `COLLATE`, else the table column's declared collation,
378
+ * else BINARY (see `indexColumnCollations`). The same resolution the planner used when it
379
+ * decided the residual Filter could be dropped, so it is the order/equality a byte window
380
+ * over that position has to reproduce.
381
+ *
382
+ * NOTE: this duplicates {@link resolveIndexKeyCollations}' collation-aware branch — same
383
+ * three-step fallback, same BINARY default — which is why both gates below admit every
384
+ * `text` and `any` column. Change either fallback without the other and the gates start
385
+ * declining (or, worse, admitting) shapes nobody intended; keep the two in step.
386
+ */
387
+ function indexResidualCollation(columns, index, position) {
388
+ const spec = index.columns[position];
389
+ return (spec?.collation ?? columns[spec?.index ?? -1]?.collation ?? 'BINARY').toUpperCase();
390
+ }
391
+ /**
392
+ * True when an EQUALITY/prefix byte window over the leading `prefixLength` columns of
393
+ * secondary `index` is EXACTLY the qualifying set — the condition under which the access
394
+ * planner may claim the equality filters handled and drop the residual Filter.
395
+ *
396
+ * `keyCollations` is {@link resolveIndexKeyCollations}' output for `index` (the scan passes
397
+ * its memoized copy, the access planner resolves one on the spot): the collation each
398
+ * position's key bytes are ACTUALLY encoded under. The window equals the qualifying set iff
399
+ * that agrees, per position, with {@link indexResidualCollation}.
400
+ *
401
+ * For a `text` or `any` column the two agree by construction — both resolve to the index
402
+ * `COLLATE` else the declared collation else BINARY — so those admit, including the
403
+ * `text` column of a table whose key collation K is something else entirely, and an
404
+ * `x any collate nocase` index column (whose window is now genuinely the NOCASE-equal
405
+ * set — ANY's `compare` honors the collation, so key bytes, residual, and enforcement
406
+ * all agree). The shape that does NOT agree is a collation-blind column (`json`, the
407
+ * temporal types) under an index column carrying an explicit non-BINARY COLLATE:
408
+ * `pkKeyCollationName` keys those hard-`BINARY` while the residual still compares under
409
+ * the declared name, so the window would be byte-equal only and miss rows the residual
410
+ * admits — declined. Never-text columns are exempt — their key bytes are type-native.
411
+ */
412
+ export function indexPrefixSeekIsCollationExact(columns, index, keyCollations, prefixLength) {
413
+ for (let i = 0; i < prefixLength; i++) {
414
+ const spec = index.columns[i];
415
+ if (!spec)
416
+ return false;
417
+ if (!columnCanHoldText(columns[spec.index]))
418
+ continue;
419
+ if ((keyCollations[i] ?? 'BINARY').toUpperCase() !== indexResidualCollation(columns, index, i))
420
+ return false;
421
+ }
422
+ return true;
423
+ }
424
+ /**
425
+ * True when a byte RANGE window over the LEADING column of secondary `index` is SOUND.
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
430
+ * comparator's ORDER. Both halves are {@link keyOrderMatchesCollation}, which admits a
431
+ * semantic-ordering column only when {@link semanticKeyOrderIsFaithful} asserts its key
432
+ * bytes order as the type's `compare` does (and the collation sides agree — a `collate`
433
+ * decoration on the index column still declines it).
434
+ *
435
+ * THE shared predicate for the two decision sites: `tryIndexAccessPlan`
436
+ * (store-module-access-plan.ts) marks the range filters handled only when it holds, and
437
+ * `StoreTableScan.analyzeIndexAccess` builds a window only when it holds. They must agree —
438
+ * a plan that claims a filter the scan then declines to window loses the residual Filter and
439
+ * returns the whole table — so they call this rather than each restating it.
440
+ */
441
+ export function indexLeadingRangeIsOrderSafe(db, columns, index, keyCollations) {
442
+ const leading = index.columns[0];
443
+ if (!leading)
444
+ return false;
445
+ return keyOrderMatchesCollation(db, columns[leading.index], keyCollations[0] ?? 'BINARY', indexResidualCollation(columns, index, 0));
446
+ }
447
+ /**
448
+ * How many LEADING primary-key members have key bytes whose memcmp order matches their
449
+ * declared collation's comparator order, per {@link keyOrderMatchesCollation}.
450
+ *
451
+ * `pkKeyCollations` is {@link resolvePkKeyCollations}' output for this table; an `undefined`
452
+ * entry belongs to a never-text member, which {@link keyOrderMatchesCollation} exempts
453
+ * outright — `tableKeyCollation` is passed only so the exempt branch has a name to ignore.
454
+ * The comparison collation is the column's declared one, which `reconcilePkCollations` has
455
+ * already aligned with the key collation for every *textual* PK member.
456
+ *
457
+ * What the advertisement is measured against is the order the planner's `Sort` would have
458
+ * produced. For most members Sort orders under the operand's COLLATION, so a collation match
459
+ * is the question; for a member whose logical type carries SEMANTIC ordering (TIMESPAN, JSON
460
+ * — see docs/types.md "Semantic ordering") Sort ranks by `logicalType.compare`, and the
461
+ * member counts only when {@link semanticKeyOrderIsFaithful} asserts the stored key bytes
462
+ * memcmp in exactly that order (TIMESPAN's total-seconds transform, JSON's structural
463
+ * encoding). A semantic-ordering type outside that allow-list breaks the prefix, and the
464
+ * memory backend's typed BTree order remains the canonical order for it.
465
+ *
466
+ * `0` ⇒ even the leading member is unsafe: no range seek, no PK-order advertisement.
467
+ * A prefix shorter than the PK truncates the ordering advertisement rather than voiding it.
468
+ */
469
+ export function pkOrderPreservingPrefixLength(db, schema, pkKeyCollations, tableKeyCollation) {
470
+ const pk = schema.primaryKeyDefinition;
471
+ let n = 0;
472
+ while (n < pk.length) {
473
+ const col = schema.columns[pk[n].index];
474
+ const keyCollation = pkKeyCollations[n] ?? tableKeyCollation;
475
+ if (!keyOrderMatchesCollation(db, col, keyCollation, col?.collation ?? 'BINARY'))
476
+ break;
477
+ n++;
478
+ }
479
+ return n;
480
+ }
481
+ //# sourceMappingURL=pk-key-resolution.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pk-key-resolution.js","sourceRoot":"","sources":["../../../src/common/pk-key-resolution.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Access planning for a store-backed table: which physical access path
3
+ * (`_primary_` point / range seek, a secondary-index seek or multi-seek, or a full
4
+ * scan) this module advertises for a pushed predicate, which of the pushed filters it
5
+ * claims as handled, and what ordering the chosen path provides.
6
+ *
7
+ * The mirror of `store-table-scan.ts`: the planner here decides which access path to
8
+ * advertise, and the scan layer there executes it. A plan that claims a filter the scan
9
+ * cannot honor drops the residual Filter and returns wrong rows, so every soundness
10
+ * predicate the two share lives in `pk-key-resolution.ts` (`keyOrderMatchesCollation`,
11
+ * `indexLeadingRangeIsOrderSafe`, `pkOrderPreservingPrefixLength`) rather than being
12
+ * restated here. The declines that are NOT shared — partial indexes, the multi-seek cap,
13
+ * and the semantic-ordering column ban on multi-seeks specifically — are stated in both
14
+ * files, and changing one means changing the other.
15
+ *
16
+ * Free functions rather than a layer of the store-module chain — access planning reads
17
+ * no module state beyond the table's configured key collation, which the caller passes in.
18
+ */
19
+ import type { BestAccessPlanRequest, BestAccessPlanResult, Database, TableSchema } from '@quereus/quereus';
20
+ /**
21
+ * The access plan this module advertises for `request`, before the caller stamps the
22
+ * module-wide `honorsCollatedRangeBounds` flag onto it.
23
+ *
24
+ * `tableKeyCollation` is the table's resolved key collation K, passed in rather than
25
+ * looked up: the caller (`StoreModule.getBestAccessPlan`) owns the module's table map.
26
+ * It is the PRIMARY-KEY arms' concern only — `resolvePkKeyCollations` still falls back to
27
+ * K for an undecorated text PK member. The secondary-index arm never sees it: index key
28
+ * bytes encode under each index column's own collation, so {@link tryIndexAccessPlan}
29
+ * judges its filters against that instead.
30
+ */
31
+ export declare function computeBestAccessPlan(db: Database, tableInfo: TableSchema, request: BestAccessPlanRequest, tableKeyCollation: string): BestAccessPlanResult;
32
+ //# sourceMappingURL=store-module-access-plan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store-module-access-plan.d.ts","sourceRoot":"","sources":["../../../src/common/store-module-access-plan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EACX,qBAAqB,EACrB,oBAAoB,EACpB,QAAQ,EAIR,WAAW,EACX,MAAM,kBAAkB,CAAC;AAoG1B;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CACpC,EAAE,EAAE,QAAQ,EACZ,SAAS,EAAE,WAAW,EACtB,OAAO,EAAE,qBAAqB,EAC9B,iBAAiB,EAAE,MAAM,GACvB,oBAAoB,CAoHtB"}