@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.
- package/dist/src/core/database-assertions.js +3 -3
- package/dist/src/core/database-assertions.js.map +1 -1
- package/dist/src/core/database.d.ts +17 -3
- package/dist/src/core/database.d.ts.map +1 -1
- package/dist/src/core/database.js +79 -6
- package/dist/src/core/database.js.map +1 -1
- package/dist/src/core/statement.js +3 -3
- package/dist/src/core/statement.js.map +1 -1
- package/dist/src/emit/ast-stringify.d.ts +0 -1
- package/dist/src/emit/ast-stringify.d.ts.map +1 -1
- package/dist/src/emit/ast-stringify.js +187 -78
- package/dist/src/emit/ast-stringify.js.map +1 -1
- package/dist/src/index.d.ts +2 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/parser/ast.d.ts +0 -4
- package/dist/src/parser/ast.d.ts.map +1 -1
- package/dist/src/parser/parser.d.ts +9 -1
- package/dist/src/parser/parser.d.ts.map +1 -1
- package/dist/src/parser/parser.js +33 -20
- package/dist/src/parser/parser.js.map +1 -1
- package/dist/src/parser/visitor.js +1 -1
- package/dist/src/parser/visitor.js.map +1 -1
- package/dist/src/planner/analysis/attribute-provenance.d.ts +45 -0
- package/dist/src/planner/analysis/attribute-provenance.d.ts.map +1 -0
- package/dist/src/planner/analysis/attribute-provenance.js +81 -0
- package/dist/src/planner/analysis/attribute-provenance.js.map +1 -0
- package/dist/src/planner/analysis/const-evaluator.js +5 -5
- package/dist/src/planner/analysis/const-evaluator.js.map +1 -1
- package/dist/src/planner/building/select-window.d.ts.map +1 -1
- package/dist/src/planner/building/select-window.js +54 -21
- package/dist/src/planner/building/select-window.js.map +1 -1
- package/dist/src/planner/cache/correlation-detector.d.ts +7 -0
- package/dist/src/planner/cache/correlation-detector.d.ts.map +1 -1
- package/dist/src/planner/cache/correlation-detector.js +34 -2
- package/dist/src/planner/cache/correlation-detector.js.map +1 -1
- package/dist/src/planner/nodes/async-gather-node.d.ts +169 -0
- package/dist/src/planner/nodes/async-gather-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/async-gather-node.js +488 -0
- package/dist/src/planner/nodes/async-gather-node.js.map +1 -0
- package/dist/src/planner/nodes/bloom-join-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/bloom-join-node.js +8 -7
- package/dist/src/planner/nodes/bloom-join-node.js.map +1 -1
- package/dist/src/planner/nodes/eager-prefetch-node.d.ts +47 -0
- package/dist/src/planner/nodes/eager-prefetch-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/eager-prefetch-node.js +96 -0
- package/dist/src/planner/nodes/eager-prefetch-node.js.map +1 -0
- package/dist/src/planner/nodes/fanout-lookup-join-node.d.ts +150 -0
- package/dist/src/planner/nodes/fanout-lookup-join-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/fanout-lookup-join-node.js +265 -0
- package/dist/src/planner/nodes/fanout-lookup-join-node.js.map +1 -0
- package/dist/src/planner/nodes/merge-join-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/merge-join-node.js +8 -7
- package/dist/src/planner/nodes/merge-join-node.js.map +1 -1
- package/dist/src/planner/nodes/plan-node-type.d.ts +3 -0
- package/dist/src/planner/nodes/plan-node-type.d.ts.map +1 -1
- package/dist/src/planner/nodes/plan-node-type.js +3 -0
- package/dist/src/planner/nodes/plan-node-type.js.map +1 -1
- package/dist/src/planner/nodes/plan-node.d.ts +36 -0
- package/dist/src/planner/nodes/plan-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/plan-node.js +26 -0
- package/dist/src/planner/nodes/plan-node.js.map +1 -1
- package/dist/src/planner/nodes/reference.d.ts.map +1 -1
- package/dist/src/planner/nodes/reference.js +36 -1
- package/dist/src/planner/nodes/reference.js.map +1 -1
- package/dist/src/planner/optimizer-tuning.d.ts +107 -0
- package/dist/src/planner/optimizer-tuning.d.ts.map +1 -1
- package/dist/src/planner/optimizer-tuning.js +43 -0
- package/dist/src/planner/optimizer-tuning.js.map +1 -1
- package/dist/src/planner/optimizer.d.ts.map +1 -1
- package/dist/src/planner/optimizer.js +91 -0
- package/dist/src/planner/optimizer.js.map +1 -1
- package/dist/src/planner/rules/access/rule-monotonic-range-access.d.ts.map +1 -1
- package/dist/src/planner/rules/access/rule-monotonic-range-access.js +1 -6
- package/dist/src/planner/rules/access/rule-monotonic-range-access.js.map +1 -1
- package/dist/src/planner/rules/join/rule-fanout-batched-outer.d.ts +74 -0
- package/dist/src/planner/rules/join/rule-fanout-batched-outer.d.ts.map +1 -0
- package/dist/src/planner/rules/join/rule-fanout-batched-outer.js +139 -0
- package/dist/src/planner/rules/join/rule-fanout-batched-outer.js.map +1 -0
- package/dist/src/planner/rules/join/rule-fanout-lookup-join.d.ts +58 -0
- package/dist/src/planner/rules/join/rule-fanout-lookup-join.d.ts.map +1 -0
- package/dist/src/planner/rules/join/rule-fanout-lookup-join.js +592 -0
- package/dist/src/planner/rules/join/rule-fanout-lookup-join.js.map +1 -0
- package/dist/src/planner/rules/parallel/rule-async-gather-union-all.d.ts +43 -0
- package/dist/src/planner/rules/parallel/rule-async-gather-union-all.d.ts.map +1 -0
- package/dist/src/planner/rules/parallel/rule-async-gather-union-all.js +115 -0
- package/dist/src/planner/rules/parallel/rule-async-gather-union-all.js.map +1 -0
- package/dist/src/planner/rules/parallel/rule-async-gather-zip-by-key.d.ts +102 -0
- package/dist/src/planner/rules/parallel/rule-async-gather-zip-by-key.d.ts.map +1 -0
- package/dist/src/planner/rules/parallel/rule-async-gather-zip-by-key.js +545 -0
- package/dist/src/planner/rules/parallel/rule-async-gather-zip-by-key.js.map +1 -0
- package/dist/src/planner/rules/parallel/rule-eager-prefetch-probe.d.ts +45 -0
- package/dist/src/planner/rules/parallel/rule-eager-prefetch-probe.d.ts.map +1 -0
- package/dist/src/planner/rules/parallel/rule-eager-prefetch-probe.js +78 -0
- package/dist/src/planner/rules/parallel/rule-eager-prefetch-probe.js.map +1 -0
- package/dist/src/planner/scopes/param.d.ts.map +1 -1
- package/dist/src/planner/scopes/param.js +13 -11
- package/dist/src/planner/scopes/param.js.map +1 -1
- package/dist/src/planner/type-utils.js +1 -1
- package/dist/src/planner/type-utils.js.map +1 -1
- package/dist/src/planner/validation/plan-validator.d.ts.map +1 -1
- package/dist/src/planner/validation/plan-validator.js +17 -19
- package/dist/src/planner/validation/plan-validator.js.map +1 -1
- package/dist/src/runtime/async-semaphore.d.ts +36 -0
- package/dist/src/runtime/async-semaphore.d.ts.map +1 -0
- package/dist/src/runtime/async-semaphore.js +72 -0
- package/dist/src/runtime/async-semaphore.js.map +1 -0
- package/dist/src/runtime/deferred-constraint-queue.d.ts.map +1 -1
- package/dist/src/runtime/deferred-constraint-queue.js +4 -3
- package/dist/src/runtime/deferred-constraint-queue.js.map +1 -1
- package/dist/src/runtime/emit/add-constraint.d.ts.map +1 -1
- package/dist/src/runtime/emit/add-constraint.js +51 -37
- package/dist/src/runtime/emit/add-constraint.js.map +1 -1
- package/dist/src/runtime/emit/alter-table.d.ts.map +1 -1
- package/dist/src/runtime/emit/alter-table.js +0 -2
- package/dist/src/runtime/emit/alter-table.js.map +1 -1
- package/dist/src/runtime/emit/async-gather.d.ts +77 -0
- package/dist/src/runtime/emit/async-gather.d.ts.map +1 -0
- package/dist/src/runtime/emit/async-gather.js +234 -0
- package/dist/src/runtime/emit/async-gather.js.map +1 -0
- package/dist/src/runtime/emit/bloom-join.d.ts.map +1 -1
- package/dist/src/runtime/emit/bloom-join.js +38 -17
- package/dist/src/runtime/emit/bloom-join.js.map +1 -1
- package/dist/src/runtime/emit/dml-executor.d.ts.map +1 -1
- package/dist/src/runtime/emit/dml-executor.js +12 -52
- package/dist/src/runtime/emit/dml-executor.js.map +1 -1
- package/dist/src/runtime/emit/eager-prefetch.d.ts +77 -0
- package/dist/src/runtime/emit/eager-prefetch.d.ts.map +1 -0
- package/dist/src/runtime/emit/eager-prefetch.js +223 -0
- package/dist/src/runtime/emit/eager-prefetch.js.map +1 -0
- package/dist/src/runtime/emit/fanout-lookup-join.d.ts +130 -0
- package/dist/src/runtime/emit/fanout-lookup-join.d.ts.map +1 -0
- package/dist/src/runtime/emit/fanout-lookup-join.js +521 -0
- package/dist/src/runtime/emit/fanout-lookup-join.js.map +1 -0
- package/dist/src/runtime/emit/schema-declarative.d.ts.map +1 -1
- package/dist/src/runtime/emit/schema-declarative.js +28 -2
- package/dist/src/runtime/emit/schema-declarative.js.map +1 -1
- package/dist/src/runtime/emit/transaction.d.ts.map +1 -1
- package/dist/src/runtime/emit/transaction.js +4 -22
- package/dist/src/runtime/emit/transaction.js.map +1 -1
- package/dist/src/runtime/parallel-driver.d.ts +68 -0
- package/dist/src/runtime/parallel-driver.d.ts.map +1 -0
- package/dist/src/runtime/parallel-driver.js +233 -0
- package/dist/src/runtime/parallel-driver.js.map +1 -0
- package/dist/src/runtime/register.d.ts.map +1 -1
- package/dist/src/runtime/register.js +9 -0
- package/dist/src/runtime/register.js.map +1 -1
- package/dist/src/runtime/strict-fork.d.ts +36 -0
- package/dist/src/runtime/strict-fork.d.ts.map +1 -0
- package/dist/src/runtime/strict-fork.js +125 -0
- package/dist/src/runtime/strict-fork.js.map +1 -0
- package/dist/src/schema/manager.d.ts.map +1 -1
- package/dist/src/schema/manager.js +0 -4
- package/dist/src/schema/manager.js.map +1 -1
- package/dist/src/util/comparison.d.ts.map +1 -1
- package/dist/src/util/comparison.js +11 -1
- package/dist/src/util/comparison.js.map +1 -1
- package/dist/src/vtab/capabilities.d.ts +26 -0
- package/dist/src/vtab/capabilities.d.ts.map +1 -1
- package/dist/src/vtab/concurrency.d.ts +29 -0
- package/dist/src/vtab/concurrency.d.ts.map +1 -0
- package/dist/src/vtab/concurrency.js +47 -0
- package/dist/src/vtab/concurrency.js.map +1 -0
- package/dist/src/vtab/memory/layer/scan-layer.d.ts.map +1 -1
- package/dist/src/vtab/memory/layer/scan-layer.js +67 -29
- package/dist/src/vtab/memory/layer/scan-layer.js.map +1 -1
- package/dist/src/vtab/memory/module.d.ts +21 -0
- package/dist/src/vtab/memory/module.d.ts.map +1 -1
- package/dist/src/vtab/memory/module.js +23 -0
- package/dist/src/vtab/memory/module.js.map +1 -1
- package/dist/src/vtab/memory/table.d.ts.map +1 -1
- package/dist/src/vtab/memory/table.js +2 -0
- package/dist/src/vtab/memory/table.js.map +1 -1
- package/dist/src/vtab/module.d.ts +51 -1
- package/dist/src/vtab/module.d.ts.map +1 -1
- 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"}
|