@quereus/quereus 3.1.1 → 3.2.1

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 (177) hide show
  1. package/dist/src/core/database-assertions.js +3 -3
  2. package/dist/src/core/database-assertions.js.map +1 -1
  3. package/dist/src/core/database.d.ts +17 -3
  4. package/dist/src/core/database.d.ts.map +1 -1
  5. package/dist/src/core/database.js +79 -6
  6. package/dist/src/core/database.js.map +1 -1
  7. package/dist/src/core/statement.js +3 -3
  8. package/dist/src/core/statement.js.map +1 -1
  9. package/dist/src/emit/ast-stringify.d.ts +0 -1
  10. package/dist/src/emit/ast-stringify.d.ts.map +1 -1
  11. package/dist/src/emit/ast-stringify.js +187 -78
  12. package/dist/src/emit/ast-stringify.js.map +1 -1
  13. package/dist/src/index.d.ts +2 -1
  14. package/dist/src/index.d.ts.map +1 -1
  15. package/dist/src/index.js +1 -0
  16. package/dist/src/index.js.map +1 -1
  17. package/dist/src/parser/ast.d.ts +0 -4
  18. package/dist/src/parser/ast.d.ts.map +1 -1
  19. package/dist/src/parser/parser.d.ts +9 -1
  20. package/dist/src/parser/parser.d.ts.map +1 -1
  21. package/dist/src/parser/parser.js +33 -20
  22. package/dist/src/parser/parser.js.map +1 -1
  23. package/dist/src/parser/visitor.js +1 -1
  24. package/dist/src/parser/visitor.js.map +1 -1
  25. package/dist/src/planner/analysis/attribute-provenance.d.ts +45 -0
  26. package/dist/src/planner/analysis/attribute-provenance.d.ts.map +1 -0
  27. package/dist/src/planner/analysis/attribute-provenance.js +81 -0
  28. package/dist/src/planner/analysis/attribute-provenance.js.map +1 -0
  29. package/dist/src/planner/analysis/const-evaluator.js +5 -5
  30. package/dist/src/planner/analysis/const-evaluator.js.map +1 -1
  31. package/dist/src/planner/building/select-window.d.ts.map +1 -1
  32. package/dist/src/planner/building/select-window.js +54 -21
  33. package/dist/src/planner/building/select-window.js.map +1 -1
  34. package/dist/src/planner/cache/correlation-detector.d.ts +7 -0
  35. package/dist/src/planner/cache/correlation-detector.d.ts.map +1 -1
  36. package/dist/src/planner/cache/correlation-detector.js +34 -2
  37. package/dist/src/planner/cache/correlation-detector.js.map +1 -1
  38. package/dist/src/planner/nodes/async-gather-node.d.ts +169 -0
  39. package/dist/src/planner/nodes/async-gather-node.d.ts.map +1 -0
  40. package/dist/src/planner/nodes/async-gather-node.js +488 -0
  41. package/dist/src/planner/nodes/async-gather-node.js.map +1 -0
  42. package/dist/src/planner/nodes/bloom-join-node.d.ts.map +1 -1
  43. package/dist/src/planner/nodes/bloom-join-node.js +8 -7
  44. package/dist/src/planner/nodes/bloom-join-node.js.map +1 -1
  45. package/dist/src/planner/nodes/eager-prefetch-node.d.ts +47 -0
  46. package/dist/src/planner/nodes/eager-prefetch-node.d.ts.map +1 -0
  47. package/dist/src/planner/nodes/eager-prefetch-node.js +96 -0
  48. package/dist/src/planner/nodes/eager-prefetch-node.js.map +1 -0
  49. package/dist/src/planner/nodes/fanout-lookup-join-node.d.ts +150 -0
  50. package/dist/src/planner/nodes/fanout-lookup-join-node.d.ts.map +1 -0
  51. package/dist/src/planner/nodes/fanout-lookup-join-node.js +265 -0
  52. package/dist/src/planner/nodes/fanout-lookup-join-node.js.map +1 -0
  53. package/dist/src/planner/nodes/merge-join-node.d.ts.map +1 -1
  54. package/dist/src/planner/nodes/merge-join-node.js +8 -7
  55. package/dist/src/planner/nodes/merge-join-node.js.map +1 -1
  56. package/dist/src/planner/nodes/plan-node-type.d.ts +3 -0
  57. package/dist/src/planner/nodes/plan-node-type.d.ts.map +1 -1
  58. package/dist/src/planner/nodes/plan-node-type.js +3 -0
  59. package/dist/src/planner/nodes/plan-node-type.js.map +1 -1
  60. package/dist/src/planner/nodes/plan-node.d.ts +36 -0
  61. package/dist/src/planner/nodes/plan-node.d.ts.map +1 -1
  62. package/dist/src/planner/nodes/plan-node.js +26 -0
  63. package/dist/src/planner/nodes/plan-node.js.map +1 -1
  64. package/dist/src/planner/nodes/reference.d.ts.map +1 -1
  65. package/dist/src/planner/nodes/reference.js +36 -1
  66. package/dist/src/planner/nodes/reference.js.map +1 -1
  67. package/dist/src/planner/optimizer-tuning.d.ts +107 -0
  68. package/dist/src/planner/optimizer-tuning.d.ts.map +1 -1
  69. package/dist/src/planner/optimizer-tuning.js +43 -0
  70. package/dist/src/planner/optimizer-tuning.js.map +1 -1
  71. package/dist/src/planner/optimizer.d.ts.map +1 -1
  72. package/dist/src/planner/optimizer.js +91 -0
  73. package/dist/src/planner/optimizer.js.map +1 -1
  74. package/dist/src/planner/rules/access/rule-monotonic-range-access.d.ts.map +1 -1
  75. package/dist/src/planner/rules/access/rule-monotonic-range-access.js +1 -6
  76. package/dist/src/planner/rules/access/rule-monotonic-range-access.js.map +1 -1
  77. package/dist/src/planner/rules/join/rule-fanout-batched-outer.d.ts +74 -0
  78. package/dist/src/planner/rules/join/rule-fanout-batched-outer.d.ts.map +1 -0
  79. package/dist/src/planner/rules/join/rule-fanout-batched-outer.js +139 -0
  80. package/dist/src/planner/rules/join/rule-fanout-batched-outer.js.map +1 -0
  81. package/dist/src/planner/rules/join/rule-fanout-lookup-join.d.ts +58 -0
  82. package/dist/src/planner/rules/join/rule-fanout-lookup-join.d.ts.map +1 -0
  83. package/dist/src/planner/rules/join/rule-fanout-lookup-join.js +592 -0
  84. package/dist/src/planner/rules/join/rule-fanout-lookup-join.js.map +1 -0
  85. package/dist/src/planner/rules/parallel/rule-async-gather-union-all.d.ts +43 -0
  86. package/dist/src/planner/rules/parallel/rule-async-gather-union-all.d.ts.map +1 -0
  87. package/dist/src/planner/rules/parallel/rule-async-gather-union-all.js +115 -0
  88. package/dist/src/planner/rules/parallel/rule-async-gather-union-all.js.map +1 -0
  89. package/dist/src/planner/rules/parallel/rule-async-gather-zip-by-key.d.ts +102 -0
  90. package/dist/src/planner/rules/parallel/rule-async-gather-zip-by-key.d.ts.map +1 -0
  91. package/dist/src/planner/rules/parallel/rule-async-gather-zip-by-key.js +545 -0
  92. package/dist/src/planner/rules/parallel/rule-async-gather-zip-by-key.js.map +1 -0
  93. package/dist/src/planner/rules/parallel/rule-eager-prefetch-probe.d.ts +45 -0
  94. package/dist/src/planner/rules/parallel/rule-eager-prefetch-probe.d.ts.map +1 -0
  95. package/dist/src/planner/rules/parallel/rule-eager-prefetch-probe.js +78 -0
  96. package/dist/src/planner/rules/parallel/rule-eager-prefetch-probe.js.map +1 -0
  97. package/dist/src/planner/scopes/param.d.ts.map +1 -1
  98. package/dist/src/planner/scopes/param.js +13 -11
  99. package/dist/src/planner/scopes/param.js.map +1 -1
  100. package/dist/src/planner/type-utils.js +1 -1
  101. package/dist/src/planner/type-utils.js.map +1 -1
  102. package/dist/src/planner/validation/plan-validator.d.ts.map +1 -1
  103. package/dist/src/planner/validation/plan-validator.js +17 -19
  104. package/dist/src/planner/validation/plan-validator.js.map +1 -1
  105. package/dist/src/runtime/async-semaphore.d.ts +36 -0
  106. package/dist/src/runtime/async-semaphore.d.ts.map +1 -0
  107. package/dist/src/runtime/async-semaphore.js +72 -0
  108. package/dist/src/runtime/async-semaphore.js.map +1 -0
  109. package/dist/src/runtime/deferred-constraint-queue.d.ts.map +1 -1
  110. package/dist/src/runtime/deferred-constraint-queue.js +4 -3
  111. package/dist/src/runtime/deferred-constraint-queue.js.map +1 -1
  112. package/dist/src/runtime/emit/add-constraint.d.ts.map +1 -1
  113. package/dist/src/runtime/emit/add-constraint.js +51 -37
  114. package/dist/src/runtime/emit/add-constraint.js.map +1 -1
  115. package/dist/src/runtime/emit/alter-table.d.ts.map +1 -1
  116. package/dist/src/runtime/emit/alter-table.js +0 -2
  117. package/dist/src/runtime/emit/alter-table.js.map +1 -1
  118. package/dist/src/runtime/emit/async-gather.d.ts +77 -0
  119. package/dist/src/runtime/emit/async-gather.d.ts.map +1 -0
  120. package/dist/src/runtime/emit/async-gather.js +234 -0
  121. package/dist/src/runtime/emit/async-gather.js.map +1 -0
  122. package/dist/src/runtime/emit/bloom-join.d.ts.map +1 -1
  123. package/dist/src/runtime/emit/bloom-join.js +38 -17
  124. package/dist/src/runtime/emit/bloom-join.js.map +1 -1
  125. package/dist/src/runtime/emit/dml-executor.d.ts.map +1 -1
  126. package/dist/src/runtime/emit/dml-executor.js +12 -52
  127. package/dist/src/runtime/emit/dml-executor.js.map +1 -1
  128. package/dist/src/runtime/emit/eager-prefetch.d.ts +77 -0
  129. package/dist/src/runtime/emit/eager-prefetch.d.ts.map +1 -0
  130. package/dist/src/runtime/emit/eager-prefetch.js +223 -0
  131. package/dist/src/runtime/emit/eager-prefetch.js.map +1 -0
  132. package/dist/src/runtime/emit/fanout-lookup-join.d.ts +130 -0
  133. package/dist/src/runtime/emit/fanout-lookup-join.d.ts.map +1 -0
  134. package/dist/src/runtime/emit/fanout-lookup-join.js +521 -0
  135. package/dist/src/runtime/emit/fanout-lookup-join.js.map +1 -0
  136. package/dist/src/runtime/emit/schema-declarative.d.ts.map +1 -1
  137. package/dist/src/runtime/emit/schema-declarative.js +28 -2
  138. package/dist/src/runtime/emit/schema-declarative.js.map +1 -1
  139. package/dist/src/runtime/emit/transaction.d.ts.map +1 -1
  140. package/dist/src/runtime/emit/transaction.js +4 -22
  141. package/dist/src/runtime/emit/transaction.js.map +1 -1
  142. package/dist/src/runtime/parallel-driver.d.ts +68 -0
  143. package/dist/src/runtime/parallel-driver.d.ts.map +1 -0
  144. package/dist/src/runtime/parallel-driver.js +233 -0
  145. package/dist/src/runtime/parallel-driver.js.map +1 -0
  146. package/dist/src/runtime/register.d.ts.map +1 -1
  147. package/dist/src/runtime/register.js +9 -0
  148. package/dist/src/runtime/register.js.map +1 -1
  149. package/dist/src/runtime/strict-fork.d.ts +36 -0
  150. package/dist/src/runtime/strict-fork.d.ts.map +1 -0
  151. package/dist/src/runtime/strict-fork.js +125 -0
  152. package/dist/src/runtime/strict-fork.js.map +1 -0
  153. package/dist/src/schema/manager.d.ts.map +1 -1
  154. package/dist/src/schema/manager.js +0 -4
  155. package/dist/src/schema/manager.js.map +1 -1
  156. package/dist/src/util/comparison.d.ts.map +1 -1
  157. package/dist/src/util/comparison.js +11 -1
  158. package/dist/src/util/comparison.js.map +1 -1
  159. package/dist/src/vtab/capabilities.d.ts +26 -0
  160. package/dist/src/vtab/capabilities.d.ts.map +1 -1
  161. package/dist/src/vtab/concurrency.d.ts +29 -0
  162. package/dist/src/vtab/concurrency.d.ts.map +1 -0
  163. package/dist/src/vtab/concurrency.js +47 -0
  164. package/dist/src/vtab/concurrency.js.map +1 -0
  165. package/dist/src/vtab/memory/layer/scan-layer.d.ts.map +1 -1
  166. package/dist/src/vtab/memory/layer/scan-layer.js +67 -29
  167. package/dist/src/vtab/memory/layer/scan-layer.js.map +1 -1
  168. package/dist/src/vtab/memory/module.d.ts +21 -0
  169. package/dist/src/vtab/memory/module.d.ts.map +1 -1
  170. package/dist/src/vtab/memory/module.js +23 -0
  171. package/dist/src/vtab/memory/module.js.map +1 -1
  172. package/dist/src/vtab/memory/table.d.ts.map +1 -1
  173. package/dist/src/vtab/memory/table.js +2 -0
  174. package/dist/src/vtab/memory/table.js.map +1 -1
  175. package/dist/src/vtab/module.d.ts +51 -1
  176. package/dist/src/vtab/module.d.ts.map +1 -1
  177. package/package.json +4 -3
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Rule: Async Gather UNION ALL
3
+ *
4
+ * Recognizes a chain of `SetOperationNode(op='unionAll')` and folds the entire
5
+ * left-deep (or arbitrary nesting) tree into a single N-ary
6
+ * `AsyncGatherNode({ kind: 'unionAll' })` that drives the N branches
7
+ * concurrently.
8
+ *
9
+ * Two gates must clear for the rewrite to fire:
10
+ *
11
+ * 1. **Concurrency safety.** Every flattened child must declare
12
+ * `physical.concurrencySafe === true`. Any non-safe branch (mutating
13
+ * subplan, serial-module read without a per-branch connection, holding a
14
+ * non-reentrant cursor) poisons the rewrite — leave the chain as
15
+ * sequential `SetOperationNode`s.
16
+ *
17
+ * 2. **Latency win.** The slowest child's `physical.expectedLatencyMs` must
18
+ * meet `tuning.parallel.gatherThresholdMs`. `expectedLatencyMs` is 0 on
19
+ * all in-process / memory-vtab paths, so the rule is inert by design in
20
+ * local-only configurations (the no-rewrite invariant is locked by the
21
+ * golden-plan sweep).
22
+ *
23
+ * The rule fires in `PassId.PostOptimization` after physical-property
24
+ * selection has finalized `expectedLatencyMs` / `concurrencySafe` on the
25
+ * leaves but before `materialization-advisory` so any cache wrapping the
26
+ * advisory would inject sits *inside* the gather's branches (preserving the
27
+ * parallel-drive intent of overlapping high-latency I/O with branch-local
28
+ * compute).
29
+ *
30
+ * Attribute IDs are preserved: the rewritten gather node inherits the
31
+ * outermost `SetOperationNode`'s attributes via `preserveAttributeIds`,
32
+ * which already mirrors the leftmost child's attributes per
33
+ * `SetOperationNode.buildAttributes`. Downstream consumers that referenced
34
+ * the SetOp's output (e.g. an enclosing `ORDER BY x`) continue to resolve
35
+ * unchanged.
36
+ *
37
+ * Idempotence: after the rewrite the root node is an `AsyncGatherNode`, not
38
+ * a `SetOperationNode`, so a second firing's matcher rejects immediately.
39
+ */
40
+ import { createLogger } from '../../../common/logger.js';
41
+ import { PlanNodeType } from '../../nodes/plan-node-type.js';
42
+ import { SetOperationNode } from '../../nodes/set-operation-node.js';
43
+ import { AsyncGatherNode } from '../../nodes/async-gather-node.js';
44
+ const log = createLogger('optimizer:rule:async-gather-union-all');
45
+ export function ruleAsyncGatherUnionAll(node, context) {
46
+ if (!(node instanceof SetOperationNode))
47
+ return null;
48
+ if (node.op !== 'unionAll')
49
+ return null;
50
+ const tuning = context.tuning.parallel;
51
+ if (tuning.minBranches < 2)
52
+ return null;
53
+ // Flatten the entire unionAll tree (any shape — left-deep, right-deep,
54
+ // balanced). Stops at the first non-unionAll-SetOperation child.
55
+ const children = [];
56
+ collectUnionAllChildren(node, children);
57
+ if (children.length < tuning.minBranches)
58
+ return null;
59
+ // Gate 1: every child must be concurrency-safe. A single unsafe branch
60
+ // poisons the rewrite.
61
+ for (const child of children) {
62
+ if (child.physical.concurrencySafe !== true) {
63
+ log('Aborting rewrite: child %s is not concurrencySafe', child.id);
64
+ return null;
65
+ }
66
+ }
67
+ // Gate 2: max-of-children latency must meet the threshold. Memory-vtab /
68
+ // in-process leaves declare expectedLatencyMs=0, so this skips the
69
+ // rewrite for purely local plans (the no-rewrite invariant under the
70
+ // golden-plan sweep).
71
+ let maxLatency = 0;
72
+ for (const child of children) {
73
+ const l = child.physical.expectedLatencyMs ?? 0;
74
+ if (l > maxLatency)
75
+ maxLatency = l;
76
+ }
77
+ if (maxLatency < tuning.gatherThresholdMs)
78
+ return null;
79
+ const concurrencyCap = Math.max(1, Math.min(tuning.concurrency, children.length));
80
+ log('Folding unionAll chain of %d branches into AsyncGather (cap=%d, maxLatency=%d ms, threshold=%d ms)', children.length, concurrencyCap, maxLatency, tuning.gatherThresholdMs);
81
+ return new AsyncGatherNode(node.scope, children, { kind: 'unionAll' }, concurrencyCap, node.getAttributes());
82
+ }
83
+ /**
84
+ * Walk an arbitrary tree of unionAll-`SetOperationNode`s and push the leaves
85
+ * (first non-unionAll-SetOp descendant on each branch) into `out`. Recursion is
86
+ * bounded by the unionAll chain depth, which is the same depth bound the
87
+ * optimizer's pass already enforces.
88
+ *
89
+ * Also absorbs nested `AsyncGatherNode({ kind: 'unionAll' })` children — the
90
+ * rule is registered for bottom-up traversal, so the inner `SetOperation` of
91
+ * `(A unionAll B) unionAll C` will already have been rewritten into a
92
+ * 2-branch gather by the time the outer `SetOperation` fires. Without this
93
+ * absorption the outer rewrite would produce a 2-branch gather whose first
94
+ * child is itself a gather, defeating the "single gather per chain"
95
+ * invariant the tests pin.
96
+ */
97
+ function collectUnionAllChildren(node, out) {
98
+ if (node.nodeType === PlanNodeType.SetOperation && node.op === 'unionAll') {
99
+ const setOp = node;
100
+ collectUnionAllChildren(setOp.left, out);
101
+ collectUnionAllChildren(setOp.right, out);
102
+ return;
103
+ }
104
+ if (node.nodeType === PlanNodeType.AsyncGather) {
105
+ const gather = node;
106
+ if (gather.combinator.kind === 'unionAll') {
107
+ for (const child of gather.children) {
108
+ collectUnionAllChildren(child, out);
109
+ }
110
+ return;
111
+ }
112
+ }
113
+ out.push(node);
114
+ }
115
+ //# sourceMappingURL=rule-async-gather-union-all.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule-async-gather-union-all.js","sourceRoot":"","sources":["../../../../../src/planner/rules/parallel/rule-async-gather-union-all.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Rule: Async Gather ZIP BY KEY
3
+ *
4
+ * Recognizes a `Project` over a chain of binary full-outer `JoinNode`s that
5
+ * all equate the **same** key column set across every participating relation,
6
+ * and folds the whole shape into a single N-ary
7
+ * `AsyncGatherNode({ kind: 'zipByKey', branchKeyAttrs, outputKeyAttrs })`.
8
+ *
9
+ * Generalizes `rule-async-gather-union-all` from the `unionAll` combinator to
10
+ * `zipByKey`. The recognized SQL is the natural spelling of an N-way
11
+ * full-outer merge on a shared key:
12
+ *
13
+ * select coalesce(a.k, b.k, c.k) as k, a.av, b.bv, c.cv
14
+ * from a full outer join b on a.k = b.k
15
+ * full outer join c on a.k = c.k
16
+ *
17
+ * which the builder produces as:
18
+ *
19
+ * Project[ coalesce(a.k,b.k,c.k) as k, a.av, b.bv, c.cv ]
20
+ * Join(full, on a.k = c.k)
21
+ * Join(full, on a.k = b.k)
22
+ * <a> <b>
23
+ * <c>
24
+ *
25
+ * The binary full-outer chain is O(N²) null-padding and infers worse FDs than
26
+ * the symmetric N-ary merge; the `zipByKey` gather drives the N branches
27
+ * concurrently and hash-merges them by key. (Binary FULL JOIN has no runtime
28
+ * lowering at all — this rewrite is the only execution path for it.)
29
+ *
30
+ * ## Recognized shape
31
+ *
32
+ * 1. A `ProjectNode` whose `source` is a `JoinNode(joinType='full')`.
33
+ * 2. The full-join chain flattens (any nesting) into ≥ `minBranches` leaf
34
+ * branches. Each join's `ON` condition is a pure conjunction of
35
+ * column-ref equalities (no residual / non-equi predicate — those block).
36
+ * (`USING`/`NATURAL` full joins carry no synthesized `ON` condition, so the
37
+ * chain walk declines them — out of scope; see *Out of scope* below.)
38
+ * 3. Those equalities partition the branches' key columns into K equivalence
39
+ * classes ("key positions"), and **every branch contributes exactly one
40
+ * column to every class** (the shared-key precondition). A branch missing
41
+ * from any class would be a cross-product, not a zip — block.
42
+ * 4. The projection list must express, in **any order**:
43
+ * - K merged keys, each a `coalesce(...)` whose argument set is exactly
44
+ * one key class's per-branch key attrs; and
45
+ * - the forwarded non-key column references it selects (a subset is fine),
46
+ * plus arbitrary additional pure scalar expressions over those outputs
47
+ * (e.g. `coalesce(a.k, b.k) * 10`). The only hard constraint: a branch *key*
48
+ * column may appear **only** inside a recognizing full-group `coalesce` — a
49
+ * bare/partial reference to it (e.g. `select a.k …`) blocks, because the
50
+ * per-branch key is consumed into the single merged key and is unavailable
51
+ * above the gather.
52
+ *
53
+ * When the projection happens to be exactly the emitter's canonical order
54
+ * (`[K coalesce calls][branch0 non-key][branch1 non-key]…`), the gather
55
+ * replaces the `Project` outright (fast path). Otherwise the gather is built
56
+ * in canonical layout and wrapped in a thin reordering `Project` that
57
+ * reproduces the user's list (rewriting each full-group `coalesce` to a
58
+ * reference to the gather's merged-key output).
59
+ *
60
+ * ## Gates (mirror `rule-async-gather-union-all`)
61
+ *
62
+ * - **Concurrency safety.** Every branch must declare
63
+ * `physical.concurrencySafe === true`.
64
+ * - **Uncorrelated branches.** No branch may reference attributes outside its
65
+ * own subtree (lateral dependency) — the parallel driver forks independent
66
+ * contexts. `isCorrelatedSubquery` on each branch must be false.
67
+ * - **Latency win.** The slowest branch's `physical.expectedLatencyMs` must
68
+ * meet `tuning.parallel.gatherThresholdMs`. This is 0 on memory-vtab /
69
+ * in-process leaves, so the rule is inert by design on local-only plans
70
+ * (the golden-plan no-rewrite invariant).
71
+ * - **Key collation agreement.** Every key column at a given key position must
72
+ * declare the *same* collation across all branches (binary or not). The
73
+ * runtime comparator derives from branch 0 only, so a disagreement would
74
+ * compare keys under the wrong collation. Non-binary collations are allowed:
75
+ * the emitter composes the merged key deterministically from the
76
+ * lowest-indexed present branch (`composeMergedKeyCells`), matching
77
+ * `coalesce`'s left-to-right pick even when collation-equal keys are
78
+ * byte-distinct (e.g. NOCASE merging `'A'`/`'a'`). This mirrors the
79
+ * *agreement* invariant `AsyncGatherNode.validateZipByKey` enforces (which
80
+ * throws on a true mismatch); checking it here declines gracefully instead.
81
+ *
82
+ * ## Attribute provenance (Option A — per-branch refs + minted output keys)
83
+ *
84
+ * - `branchKeyAttrs[b]` — branch b's K key attr ids, in key order (distinct
85
+ * per branch; each branch originates its own key id — provenance-clean).
86
+ * - `outputKeyAttrs` — the K ids the `Project` minted for its `coalesce`
87
+ * outputs (computed expressions → fresh ids, disjoint from all child ids).
88
+ * The gather *mints* these, so `preserveAttributeIds[0..K-1] ===
89
+ * outputKeyAttrs` and downstream references to the coalesced key resolve.
90
+ * - `preserveAttributeIds` — the `Project`'s full output attribute list,
91
+ * which (because we matched the canonical order) is exactly
92
+ * `[minted keys] ++ [each branch's non-key attrs]`.
93
+ *
94
+ * ## Idempotence
95
+ *
96
+ * After the rewrite the matched node is an `AsyncGatherNode`, not a
97
+ * `ProjectNode`, so a second firing's matcher rejects immediately.
98
+ */
99
+ import type { OptContext } from '../../framework/context.js';
100
+ import { PlanNode } from '../../nodes/plan-node.js';
101
+ export declare function ruleAsyncGatherZipByKey(node: PlanNode, context: OptContext): PlanNode | null;
102
+ //# sourceMappingURL=rule-async-gather-zip-by-key.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule-async-gather-zip-by-key.d.ts","sourceRoot":"","sources":["../../../../../src/planner/rules/parallel/rule-async-gather-zip-by-key.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiGG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,QAAQ,EAAoB,MAAM,0BAA0B,CAAC;AAqBtE,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,GAAG,QAAQ,GAAG,IAAI,CAmH5F"}