@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,592 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule: Fan-out Lookup Join (FK→PK + 1:n cross + correlated scalar-aggregate
|
|
3
|
+
* subqueries)
|
|
4
|
+
*
|
|
5
|
+
* Clusters per-outer-row branches into one `FanOutLookupJoinNode` that drives
|
|
6
|
+
* them concurrently per outer row. A branch is either *at-most-one* (≤1 row per
|
|
7
|
+
* outer row) or *cross* (data-driven 1:n, Cartesian product per outer row):
|
|
8
|
+
*
|
|
9
|
+
* 1. **Join-spine branches.** A chain of N LEFT/INNER/CROSS nested-loop joins
|
|
10
|
+
* from a common outer where every join's non-preserved side is a
|
|
11
|
+
* parameterized equi-lookup. FK→PK-aligned lookups become at-most-one
|
|
12
|
+
* branches (matching the alignment `ruleJoinElimination` trusts); lookups
|
|
13
|
+
* that are *not* provably at-most-one (no FK, or FK→non-unique) become
|
|
14
|
+
* `cross` branches whose 1:n product is bounded by the row/product guards
|
|
15
|
+
* (`tuning.parallel.maxCrossBranchRows` / `maxCrossProduct`). A chain may
|
|
16
|
+
* legitimately mix both modes.
|
|
17
|
+
*
|
|
18
|
+
* 2. **Subquery branches.** Correlated scalar-aggregate `ScalarSubqueryNode`s
|
|
19
|
+
* found anywhere in the SELECT projection list — bare (`(select count(*)
|
|
20
|
+
* from c where c.fk = o.k)`) or wrapped inside a scalar expression
|
|
21
|
+
* (`coalesce((select sum(...) ...), 0)`, `json((select json_group_array(
|
|
22
|
+
* ...) ...))`). A scalar aggregate with no GROUP BY emits exactly one row
|
|
23
|
+
* per outer row regardless of how many child rows match — relationally an
|
|
24
|
+
* `atMostOne-left` branch driven per outer row, exactly what the fan-out
|
|
25
|
+
* node already does. The subquery's relational root is used verbatim as
|
|
26
|
+
* the branch child (its correlation predicate is internal and resolves
|
|
27
|
+
* through `rctx.context`); only the *inner* `ScalarSubqueryNode` is
|
|
28
|
+
* rewritten to a column reference into the fan-out's wide row, leaving any
|
|
29
|
+
* wrapping expression (`coalesce(<colref>, 0)`) intact.
|
|
30
|
+
*
|
|
31
|
+
* When the *combined* branch count clears `tuning.parallel.minBranches` AND the
|
|
32
|
+
* projected latency win covers the per-branch setup overhead, the cluster
|
|
33
|
+
* forms.
|
|
34
|
+
*
|
|
35
|
+
* Cost gate is anchored on `physical.expectedLatencyMs` — populated 0 for
|
|
36
|
+
* in-process / memory-vtab paths, non-zero for remote vtabs whose access plan
|
|
37
|
+
* declares per-call latency. As a consequence, with no remote-vtab plugin in
|
|
38
|
+
* tree the rule is inert by design (memory-vtab golden plans don't change).
|
|
39
|
+
*
|
|
40
|
+
* Join-spine branch eligibility mirrors `ruleJoinElimination`:
|
|
41
|
+
* - AND-of-column-equalities ON-clause (any residual disqualifies the
|
|
42
|
+
* branch — leave it as a normal nested-loop join),
|
|
43
|
+
* - FK→PK alignment validated via `lookupCoveringFK` + `checkFkPkAlignment`,
|
|
44
|
+
* - INNER branches additionally require NOT-NULL FK + row-preserving path
|
|
45
|
+
* to the PK table.
|
|
46
|
+
*
|
|
47
|
+
* Subquery branch eligibility:
|
|
48
|
+
* - a `ScalarSubqueryNode` reached anywhere in a projection's scalar
|
|
49
|
+
* expression tree (bare or wrapped — `collectScalarSubqueries` finds it),
|
|
50
|
+
* - the subquery is correlated,
|
|
51
|
+
* - beneath pass-through wrappers the relational root is aggregate-shaped
|
|
52
|
+
* with zero grouping keys (⇒ exactly one row per outer),
|
|
53
|
+
* - the subquery exposes exactly one output attribute.
|
|
54
|
+
*/
|
|
55
|
+
import { createLogger } from '../../../common/logger.js';
|
|
56
|
+
import { isRelationalNode, } from '../../nodes/plan-node.js';
|
|
57
|
+
import { ProjectNode } from '../../nodes/project-node.js';
|
|
58
|
+
import { FilterNode } from '../../nodes/filter.js';
|
|
59
|
+
import { SortNode } from '../../nodes/sort.js';
|
|
60
|
+
import { LimitOffsetNode } from '../../nodes/limit-offset.js';
|
|
61
|
+
import { DistinctNode } from '../../nodes/distinct-node.js';
|
|
62
|
+
import { AliasNode } from '../../nodes/alias-node.js';
|
|
63
|
+
import { JoinNode, extractEquiPairsFromCondition } from '../../nodes/join-node.js';
|
|
64
|
+
import { ScalarSubqueryNode } from '../../nodes/subquery.js';
|
|
65
|
+
import { ColumnReferenceNode } from '../../nodes/reference.js';
|
|
66
|
+
import { normalizePredicate } from '../../analysis/predicate-normalizer.js';
|
|
67
|
+
import { checkFkPkAlignment, extractTableSchema } from '../../util/key-utils.js';
|
|
68
|
+
import { lookupCoveringFK, isRowPreservingPathToTable } from '../../util/ind-utils.js';
|
|
69
|
+
import { collectExternalReferences } from '../../cache/correlation-detector.js';
|
|
70
|
+
import { CapabilityDetectors } from '../../framework/characteristics.js';
|
|
71
|
+
import { isAndOfColumnEqualities } from './rule-join-elimination.js';
|
|
72
|
+
import { FanOutLookupJoinNode, isCrossBranchMode, isLeftBranchMode } from '../../nodes/fanout-lookup-join-node.js';
|
|
73
|
+
const log = createLogger('optimizer:rule:fanout-lookup-join');
|
|
74
|
+
export function ruleFanOutLookupJoin(node, context) {
|
|
75
|
+
if (!(node instanceof ProjectNode))
|
|
76
|
+
return null;
|
|
77
|
+
const tuning = context.tuning.parallel;
|
|
78
|
+
if (tuning.minBranches < 2)
|
|
79
|
+
return null;
|
|
80
|
+
// Walk pass-through wrappers down to the first JoinNode or a non-wrapper
|
|
81
|
+
// bottom. Unlike the join-only v1, hitting a non-JoinNode/non-wrapper node
|
|
82
|
+
// is NOT a bail — it just means there is no join spine and that node is the
|
|
83
|
+
// outer (e.g. the `orders` access node for `select …, (subq) from orders`).
|
|
84
|
+
const chain = [];
|
|
85
|
+
let current = node.source;
|
|
86
|
+
while (true) {
|
|
87
|
+
if (current instanceof JoinNode)
|
|
88
|
+
break;
|
|
89
|
+
if (current instanceof FilterNode) {
|
|
90
|
+
chain.push({ kind: 'filter', node: current });
|
|
91
|
+
current = current.source;
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (current instanceof SortNode) {
|
|
95
|
+
chain.push({ kind: 'sort', node: current });
|
|
96
|
+
current = current.source;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (current instanceof LimitOffsetNode) {
|
|
100
|
+
chain.push({ kind: 'limit', node: current });
|
|
101
|
+
current = current.source;
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (current instanceof DistinctNode) {
|
|
105
|
+
chain.push({ kind: 'distinct', node: current });
|
|
106
|
+
current = current.source;
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (current instanceof AliasNode) {
|
|
110
|
+
chain.push({ kind: 'alias', node: current });
|
|
111
|
+
current = current.source;
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
// Collect the join chain top-to-bottom and find the outer subtree at the
|
|
117
|
+
// deepest left. With no join spine the outer is `current` itself.
|
|
118
|
+
const joins = [];
|
|
119
|
+
let walker = current;
|
|
120
|
+
while (walker instanceof JoinNode) {
|
|
121
|
+
joins.push(walker);
|
|
122
|
+
walker = walker.left;
|
|
123
|
+
}
|
|
124
|
+
const outerSubtree = walker;
|
|
125
|
+
const outerAttrs = outerSubtree.getAttributes();
|
|
126
|
+
// Join-spine branches. FK→PK alignment is validated against the outer
|
|
127
|
+
// subtree's schema, so a spine requires the outer to resolve to a single
|
|
128
|
+
// table schema (mirrors `ruleJoinElimination`). `extractTableSchema` is
|
|
129
|
+
// needed ONLY here — pure-subquery clusters skip it.
|
|
130
|
+
//
|
|
131
|
+
// Bottom-up walk: joins[joins.length - 1] is the innermost (its .left ==
|
|
132
|
+
// outerSubtree), joins[0] is the outermost. Process bottom-up so the order
|
|
133
|
+
// of `spineBranches` reflects the natural wide-row layout.
|
|
134
|
+
const spineBranches = [];
|
|
135
|
+
if (joins.length > 0) {
|
|
136
|
+
const outerSchema = extractTableSchema(outerSubtree);
|
|
137
|
+
if (!outerSchema)
|
|
138
|
+
return null;
|
|
139
|
+
for (let i = joins.length - 1; i >= 0; i--) {
|
|
140
|
+
const recognized = recognizeBranch(joins[i], outerSchema, outerAttrs);
|
|
141
|
+
if (!recognized) {
|
|
142
|
+
// A non-eligible branch in the middle breaks the cluster — without
|
|
143
|
+
// a way to keep that branch in the original nested-loop position we
|
|
144
|
+
// would change semantics. Bail out conservatively.
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
spineBranches.push(recognized);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// Subquery branches: correlated scalar-aggregate ScalarSubqueryNodes found
|
|
151
|
+
// anywhere in a projection's scalar expression tree — bare (`(subq)`) or
|
|
152
|
+
// wrapped inside a scalar expression (`coalesce((subq), 0)`, `json((subq))`).
|
|
153
|
+
// `collectScalarSubqueries` walks each projection's scalar tree without
|
|
154
|
+
// descending into a subquery's own relational body, so a nested inner
|
|
155
|
+
// subquery stays part of its enclosing branch child rather than clustering
|
|
156
|
+
// separately.
|
|
157
|
+
const subqueryBranches = [];
|
|
158
|
+
const outerAttrIds = new Set(outerAttrs.map(a => a.id));
|
|
159
|
+
const seenSubqueries = new Set();
|
|
160
|
+
for (const proj of node.projections) {
|
|
161
|
+
const candidates = [];
|
|
162
|
+
collectScalarSubqueries(proj.node, candidates);
|
|
163
|
+
for (const cand of candidates) {
|
|
164
|
+
if (seenSubqueries.has(cand))
|
|
165
|
+
continue;
|
|
166
|
+
const recognized = recognizeSubqueryBranch(cand, outerAttrIds);
|
|
167
|
+
if (recognized) {
|
|
168
|
+
seenSubqueries.add(cand);
|
|
169
|
+
subqueryBranches.push(recognized);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
const totalBranches = spineBranches.length + subqueryBranches.length;
|
|
174
|
+
if (totalBranches < tuning.minBranches)
|
|
175
|
+
return null;
|
|
176
|
+
// Memory-safety gate (before clustering): a `cross` branch's 1:n fan-out
|
|
177
|
+
// makes the output the Cartesian product of the outer side and every cross
|
|
178
|
+
// branch. Refuse to cluster when a per-branch estimate or the whole product
|
|
179
|
+
// blows the configured caps — the chain then stays a streaming nested-loop
|
|
180
|
+
// join. Subquery branches are always at-most-one, so only spine branches can
|
|
181
|
+
// be cross (`cross` / `cross-left`). A `cross-left` branch still contributes a
|
|
182
|
+
// 1:n factor (its empty-match NULL-pad only adds the single preserved row), so
|
|
183
|
+
// it is gated identically to `cross`.
|
|
184
|
+
const crossLookups = spineBranches.filter(b => isCrossBranchMode(b.mode)).map(b => b.lookup);
|
|
185
|
+
if (!crossGuardsPass(outerSubtree, crossLookups, tuning))
|
|
186
|
+
return null;
|
|
187
|
+
// Cost gate over the COMBINED branch set. `expectedLatencyMs` is populated 0
|
|
188
|
+
// except on remote-vtab access plans (propagated up through the aggregate
|
|
189
|
+
// for subquery branches), so this skip keeps the rule inert for local chains.
|
|
190
|
+
let maxLatency = 0;
|
|
191
|
+
for (const b of spineBranches) {
|
|
192
|
+
const l = b.lookup.physical.expectedLatencyMs ?? 0;
|
|
193
|
+
if (l > maxLatency)
|
|
194
|
+
maxLatency = l;
|
|
195
|
+
}
|
|
196
|
+
for (const b of subqueryBranches) {
|
|
197
|
+
const l = b.subqueryRoot.physical.expectedLatencyMs ?? 0;
|
|
198
|
+
if (l > maxLatency)
|
|
199
|
+
maxLatency = l;
|
|
200
|
+
}
|
|
201
|
+
if (maxLatency === 0)
|
|
202
|
+
return null;
|
|
203
|
+
const concurrencyCap = Math.max(1, Math.min(tuning.concurrency, totalBranches));
|
|
204
|
+
const savings = (totalBranches - concurrencyCap) * maxLatency;
|
|
205
|
+
const overhead = totalBranches * tuning.branchSetupCost;
|
|
206
|
+
if (savings <= overhead)
|
|
207
|
+
return null;
|
|
208
|
+
// Build branch specs: spine branches first (preserving left-deep order),
|
|
209
|
+
// then subquery branches.
|
|
210
|
+
//
|
|
211
|
+
// A spine branch's `child` is the lookup wrapped in a FilterNode carrying
|
|
212
|
+
// the original equi-condition. A subquery branch's `child` is the subquery's
|
|
213
|
+
// relational root verbatim — its correlation predicate is already inside it.
|
|
214
|
+
// Both resolve outer-side references via `rctx.context`: the parent fork's
|
|
215
|
+
// snapshot carries the outer slot, set by `runFanOutLookupJoin` before the
|
|
216
|
+
// fork.
|
|
217
|
+
const branchSpecs = [];
|
|
218
|
+
for (const b of spineBranches) {
|
|
219
|
+
const parameterized = new FilterNode(node.scope, b.lookup, b.condition);
|
|
220
|
+
branchSpecs.push({
|
|
221
|
+
child: parameterized,
|
|
222
|
+
mode: b.mode,
|
|
223
|
+
outputAttrs: b.lookup.getAttributes(),
|
|
224
|
+
concurrencySafe: b.lookup.physical.concurrencySafe !== false,
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
for (const b of subqueryBranches) {
|
|
228
|
+
// Pin the branch to a single column (the scalar value) so its attribute
|
|
229
|
+
// count is invariant under the inner aggregate's logical→physical
|
|
230
|
+
// expansion. `attributeId: valueAttr.id` keeps the branch output attribute
|
|
231
|
+
// identical to what the outer projection's column reference targets.
|
|
232
|
+
const colRef = new ColumnReferenceNode(node.scope, columnExprFor(b.valueAttr.name), b.valueAttr.type, b.valueAttr.id, 0);
|
|
233
|
+
const projectedChild = new ProjectNode(node.scope, b.subqueryRoot, [{ node: colRef, alias: b.valueAttr.name, attributeId: b.valueAttr.id }]);
|
|
234
|
+
branchSpecs.push({
|
|
235
|
+
child: projectedChild,
|
|
236
|
+
mode: b.mode,
|
|
237
|
+
outputAttrs: projectedChild.getAttributes(),
|
|
238
|
+
concurrencySafe: b.concurrencySafe,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
// `preserveAttributeIds` pins the wide-row layout: outer attrs + each
|
|
242
|
+
// branch's output attrs (nullable-widened for left-preserving branches —
|
|
243
|
+
// atMostOne-left / cross-left). The branch outputs are the lookups'/
|
|
244
|
+
// subqueries' own attributes, so any reference resolves by attribute ID
|
|
245
|
+
// regardless of wide-row position.
|
|
246
|
+
const preserveAttrs = [];
|
|
247
|
+
for (const a of outerAttrs)
|
|
248
|
+
preserveAttrs.push(a);
|
|
249
|
+
for (const spec of branchSpecs) {
|
|
250
|
+
const nullable = isLeftBranchMode(spec.mode);
|
|
251
|
+
for (const a of spec.outputAttrs) {
|
|
252
|
+
if (nullable && !a.type.nullable) {
|
|
253
|
+
preserveAttrs.push({ ...a, type: { ...a.type, nullable: true } });
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
preserveAttrs.push(a);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
const fanout = new FanOutLookupJoinNode(node.scope, outerSubtree, branchSpecs, concurrencyCap, preserveAttrs);
|
|
261
|
+
// Build the projection rewrite map. Each subquery branch's single output
|
|
262
|
+
// attribute materializes at a fixed wide-row index (outer + preceding branch
|
|
263
|
+
// outputs); replace the ScalarSubqueryNode in the projection with a column
|
|
264
|
+
// reference at that index. Correctness comes from the attribute ID (resolved
|
|
265
|
+
// via the row descriptor); the index is the runtime read position.
|
|
266
|
+
const subqueryReplacements = new Map();
|
|
267
|
+
let wideIndex = outerAttrs.length;
|
|
268
|
+
for (const b of spineBranches)
|
|
269
|
+
wideIndex += b.lookup.getAttributes().length;
|
|
270
|
+
for (const b of subqueryBranches) {
|
|
271
|
+
const outAttr = b.valueAttr;
|
|
272
|
+
// atMostOne-left can null-fill (empty children), so the read type is
|
|
273
|
+
// nullable; this matches the wide-row widening in `preserveAttrs`.
|
|
274
|
+
const colType = outAttr.type.nullable
|
|
275
|
+
? outAttr.type
|
|
276
|
+
: { ...outAttr.type, nullable: true };
|
|
277
|
+
const colRef = new ColumnReferenceNode(node.scope, columnExprFor(outAttr.name), colType, outAttr.id, wideIndex);
|
|
278
|
+
subqueryReplacements.set(b.subqueryNode, colRef);
|
|
279
|
+
wideIndex += 1; // each subquery branch contributes exactly one column
|
|
280
|
+
}
|
|
281
|
+
log('Forming FanOutLookupJoin with %d branches (%d spine + %d subquery, cap=%d, maxLatency=%d)', totalBranches, spineBranches.length, subqueryBranches.length, concurrencyCap, maxLatency);
|
|
282
|
+
const rebuilt = rebuildChain(chain, fanout);
|
|
283
|
+
return rebuildProject(node, rebuilt, subqueryReplacements);
|
|
284
|
+
}
|
|
285
|
+
/** Minimal synthetic AST.ColumnExpr for a rewritten projection column ref. */
|
|
286
|
+
function columnExprFor(name) {
|
|
287
|
+
return { type: 'column', name };
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Collect every `ScalarSubqueryNode` reachable in a projection's scalar
|
|
291
|
+
* expression tree, in deterministic pre-order. A recognized subquery is a leaf
|
|
292
|
+
* for this walk: we push it and do NOT descend into its relational body, so a
|
|
293
|
+
* subquery nested *inside* another subquery's correlation predicate remains part
|
|
294
|
+
* of its enclosing branch child rather than being clustered as its own branch.
|
|
295
|
+
* (The relational body is filtered out by the `typeClass === 'scalar'` guard
|
|
296
|
+
* regardless, but stopping early keeps the intent explicit.)
|
|
297
|
+
*/
|
|
298
|
+
function collectScalarSubqueries(expr, out) {
|
|
299
|
+
if (expr instanceof ScalarSubqueryNode) {
|
|
300
|
+
out.push(expr);
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
for (const child of expr.getChildren()) {
|
|
304
|
+
if (child.getType().typeClass === 'scalar') {
|
|
305
|
+
collectScalarSubqueries(child, out);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Rebuild a projection's scalar expression with each recognized
|
|
311
|
+
* `ScalarSubqueryNode` replaced by its `ColumnReferenceNode` into the fan-out's
|
|
312
|
+
* wide row, leaving the wrapping expression (`coalesce(<colref>, 0)`) intact.
|
|
313
|
+
* For a bare-subquery projection the root itself is in the map and is returned
|
|
314
|
+
* directly; for a wrapped subquery the tree is rebuilt via `withChildren` with
|
|
315
|
+
* only the matched inner node substituted. Returns the input unchanged when no
|
|
316
|
+
* descendant is a recognized subquery.
|
|
317
|
+
*/
|
|
318
|
+
function substituteSubqueries(expr, replacements) {
|
|
319
|
+
if (expr instanceof ScalarSubqueryNode) {
|
|
320
|
+
return replacements.get(expr) ?? expr;
|
|
321
|
+
}
|
|
322
|
+
const children = expr.getChildren();
|
|
323
|
+
if (children.length === 0)
|
|
324
|
+
return expr;
|
|
325
|
+
const newChildren = [];
|
|
326
|
+
let changed = false;
|
|
327
|
+
for (const child of children) {
|
|
328
|
+
if (child.getType().typeClass === 'scalar') {
|
|
329
|
+
const replaced = substituteSubqueries(child, replacements);
|
|
330
|
+
newChildren.push(replaced);
|
|
331
|
+
if (replaced !== child)
|
|
332
|
+
changed = true;
|
|
333
|
+
}
|
|
334
|
+
else {
|
|
335
|
+
newChildren.push(child);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
if (!changed)
|
|
339
|
+
return expr;
|
|
340
|
+
return expr.withChildren(newChildren);
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Recognize a correlated scalar-aggregate subquery as an `atMostOne-left`
|
|
344
|
+
* fan-out branch. Returns null when the subquery is not correlated, correlates
|
|
345
|
+
* to anything other than the outer subtree, is not aggregate-shaped with zero
|
|
346
|
+
* grouping keys beneath pass-through wrappers, or does not expose exactly one
|
|
347
|
+
* output attribute.
|
|
348
|
+
*
|
|
349
|
+
* The correlation must resolve *entirely* against `outerAttrIds`: at runtime
|
|
350
|
+
* the fan-out installs only the outer row's slot before forking each branch, so
|
|
351
|
+
* a subquery referencing a sibling spine-branch attribute (produced inside the
|
|
352
|
+
* fan-out, never installed as a slot) would fail to resolve its column at
|
|
353
|
+
* runtime. Rejecting it here keeps such a subquery as an ordinary correlated
|
|
354
|
+
* projection. (See `correlated subquery referencing a spine-branch attribute`
|
|
355
|
+
* in `parallel-fanout.spec.ts`.)
|
|
356
|
+
*
|
|
357
|
+
* The aggregate-shape test uses `CapabilityDetectors.isAggregating`, which
|
|
358
|
+
* matches both the logical `AggregateNode` and the physical
|
|
359
|
+
* `StreamAggregateNode` / `HashAggregateNode`, so it is robust to optimizer
|
|
360
|
+
* pass ordering (the subquery root may still be logical at structural time).
|
|
361
|
+
*/
|
|
362
|
+
function recognizeSubqueryBranch(scalarSubquery, outerAttrIds) {
|
|
363
|
+
const external = collectExternalReferences(scalarSubquery.subquery);
|
|
364
|
+
if (external.size === 0)
|
|
365
|
+
return null; // not correlated
|
|
366
|
+
for (const id of external) {
|
|
367
|
+
if (!outerAttrIds.has(id))
|
|
368
|
+
return null; // correlates beyond the outer subtree
|
|
369
|
+
}
|
|
370
|
+
// Descend pass-through wrappers (Project/Alias/Sort/LimitOffset) to the
|
|
371
|
+
// aggregate root.
|
|
372
|
+
let root = scalarSubquery.subquery;
|
|
373
|
+
while (!CapabilityDetectors.isAggregating(root)) {
|
|
374
|
+
if (root instanceof ProjectNode ||
|
|
375
|
+
root instanceof AliasNode ||
|
|
376
|
+
root instanceof SortNode ||
|
|
377
|
+
root instanceof LimitOffsetNode) {
|
|
378
|
+
root = root.source;
|
|
379
|
+
continue;
|
|
380
|
+
}
|
|
381
|
+
return null;
|
|
382
|
+
}
|
|
383
|
+
// Empty grouping ⇒ exactly one row per outer ⇒ at-most-one branch. A
|
|
384
|
+
// GROUP BY subquery may yield more than one row and is rejected here.
|
|
385
|
+
if (root.getGroupingKeys().length !== 0)
|
|
386
|
+
return null;
|
|
387
|
+
// A scalar subquery's relational root exposes exactly one output column at
|
|
388
|
+
// structural time (validated at build); its column-0 attribute is the
|
|
389
|
+
// scalar value the branch contributes.
|
|
390
|
+
const subAttrs = scalarSubquery.subquery.getAttributes();
|
|
391
|
+
if (subAttrs.length !== 1)
|
|
392
|
+
return null;
|
|
393
|
+
return {
|
|
394
|
+
subqueryNode: scalarSubquery,
|
|
395
|
+
subqueryRoot: scalarSubquery.subquery,
|
|
396
|
+
valueAttr: subAttrs[0],
|
|
397
|
+
mode: 'atMostOne-left',
|
|
398
|
+
concurrencySafe: scalarSubquery.subquery.physical.concurrencySafe !== false,
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Decide whether `join`'s `right` side is a parameterized equi-lookup eligible
|
|
403
|
+
* for branch clustering, and at what cardinality `mode`. The FK side is sourced
|
|
404
|
+
* from `outerSchema` + `outerAttrs` — both the equi-pair's left attribute and
|
|
405
|
+
* its `outerAttrs` membership are checked, which is the safety net keeping
|
|
406
|
+
* per-join alignment honest in the presence of intermediate joins in the chain
|
|
407
|
+
* (the join's own `.left` resolves to a combined relation, so we cannot extract
|
|
408
|
+
* a single schema from it).
|
|
409
|
+
*
|
|
410
|
+
* Two cardinality outcomes:
|
|
411
|
+
*
|
|
412
|
+
* - **at-most-one** (`atMostOne-left` / `atMostOne-inner`) — the lookup is
|
|
413
|
+
* FK→PK aligned, so each outer row matches ≤1 lookup row. INNER additionally
|
|
414
|
+
* requires a covering NOT-NULL FK + a row-preserving path (else it would
|
|
415
|
+
* drop or duplicate rows the cluster cannot account for — bail to preserve
|
|
416
|
+
* the nested-loop join).
|
|
417
|
+
*
|
|
418
|
+
* - **cross** / **cross-left** — a clean parameterized equi-lookup whose
|
|
419
|
+
* FK→PK alignment is *absent* (no FK, or FK→non-unique), so the
|
|
420
|
+
* per-outer-row cardinality is data-driven (1:n). `inner` / `cross` join
|
|
421
|
+
* types yield `cross` (inner-drop on an empty branch); a `left` join yields
|
|
422
|
+
* `cross-left` (NULL-pad + preserve the outer row on an empty branch, with
|
|
423
|
+
* nullable-widened branch outputs). The unbounded Cartesian product is gated
|
|
424
|
+
* by the caller's row/product guards in both cases.
|
|
425
|
+
*
|
|
426
|
+
* Aligned-but-not-at-most-one INNER lookups (nullable FK, non-row-preserving
|
|
427
|
+
* path) are *not* reclassified as `cross`: FK→PK is still ≤1 match, so the issue
|
|
428
|
+
* is inner-drop semantics, not cardinality. They bail (return null) exactly as
|
|
429
|
+
* before, so the chain falls back to a nested-loop join.
|
|
430
|
+
*/
|
|
431
|
+
function recognizeBranch(join, outerSchema, outerAttrs) {
|
|
432
|
+
if (join.joinType !== 'left' && join.joinType !== 'inner' && join.joinType !== 'cross')
|
|
433
|
+
return null;
|
|
434
|
+
if (!join.condition)
|
|
435
|
+
return null;
|
|
436
|
+
const leftAttrs = join.left.getAttributes();
|
|
437
|
+
const rightAttrs = join.right.getAttributes();
|
|
438
|
+
const pairs = extractEquiPairsFromCondition(join.condition, leftAttrs, rightAttrs);
|
|
439
|
+
if (pairs.length === 0)
|
|
440
|
+
return null;
|
|
441
|
+
const normalized = normalizePredicate(join.condition);
|
|
442
|
+
if (!isAndOfColumnEqualities(normalized))
|
|
443
|
+
return null;
|
|
444
|
+
const outerAttrIdToIdx = new Map();
|
|
445
|
+
outerAttrs.forEach((a, i) => outerAttrIdToIdx.set(a.id, i));
|
|
446
|
+
// Translate each equi-pair from "(left subtree column index, right column
|
|
447
|
+
// index)" to "(outer column index, right column index)". The left subtree
|
|
448
|
+
// may span multiple joins, but the equi-pair's left attribute must
|
|
449
|
+
// originate in the outer subtree so the lookup is parameterizable from the
|
|
450
|
+
// outer row (and, for FK→PK, so the relationship makes sense).
|
|
451
|
+
const outerCols = [];
|
|
452
|
+
const rightCols = [];
|
|
453
|
+
for (const p of pairs) {
|
|
454
|
+
const leftAttrId = leftAttrs[p.left]?.id;
|
|
455
|
+
if (leftAttrId === undefined)
|
|
456
|
+
return null;
|
|
457
|
+
const outerIdx = outerAttrIdToIdx.get(leftAttrId);
|
|
458
|
+
if (outerIdx === undefined)
|
|
459
|
+
return null;
|
|
460
|
+
outerCols.push(outerIdx);
|
|
461
|
+
rightCols.push(p.right);
|
|
462
|
+
}
|
|
463
|
+
const rightSchema = extractTableSchema(join.right);
|
|
464
|
+
if (!rightSchema)
|
|
465
|
+
return null;
|
|
466
|
+
// At-most-one path: FK→PK alignment guarantees ≤1 match per outer row.
|
|
467
|
+
if (checkFkPkAlignment(outerSchema, rightSchema, outerCols, rightCols)) {
|
|
468
|
+
if (join.joinType === 'left') {
|
|
469
|
+
return { lookup: join.right, mode: 'atMostOne-left', condition: join.condition };
|
|
470
|
+
}
|
|
471
|
+
if (join.joinType === 'inner') {
|
|
472
|
+
const match = lookupCoveringFK(outerSchema, rightSchema, outerCols, rightCols);
|
|
473
|
+
if (!match || match.nullable)
|
|
474
|
+
return null;
|
|
475
|
+
if (!isRowPreservingPathToTable(join.right))
|
|
476
|
+
return null;
|
|
477
|
+
return { lookup: join.right, mode: 'atMostOne-inner', condition: join.condition };
|
|
478
|
+
}
|
|
479
|
+
// An aligned `cross` join type (unusual: a cross join carrying an
|
|
480
|
+
// equi-condition) falls through to the cross treatment below.
|
|
481
|
+
}
|
|
482
|
+
// Cross path: a clean parameterized equi-lookup that is not provably
|
|
483
|
+
// at-most-one (data-driven 1:n).
|
|
484
|
+
// - INNER/CROSS ⇒ `cross` (inner-drop on an empty branch).
|
|
485
|
+
// - LEFT ⇒ `cross-left` (NULL-pad + preserve the outer row on an empty
|
|
486
|
+
// branch; branch output attributes are nullable-widened by the node /
|
|
487
|
+
// `preserveAttrs`). Both contribute a 1:n factor gated by `crossGuardsPass`.
|
|
488
|
+
if (join.joinType === 'inner' || join.joinType === 'cross') {
|
|
489
|
+
return { lookup: join.right, mode: 'cross', condition: join.condition };
|
|
490
|
+
}
|
|
491
|
+
if (join.joinType === 'left') {
|
|
492
|
+
return { lookup: join.right, mode: 'cross-left', condition: join.condition };
|
|
493
|
+
}
|
|
494
|
+
return null;
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Cross-branch memory guard. A `cross` branch contributes a *data-driven* (1:n)
|
|
498
|
+
* row count, so the fan-out's output is the Cartesian product of the outer side
|
|
499
|
+
* and every cross branch. Left ungated, that product can be unbounded, so we
|
|
500
|
+
* refuse to cluster when:
|
|
501
|
+
*
|
|
502
|
+
* - any cross branch's lookup estimate exceeds `maxCrossBranchRows`, or
|
|
503
|
+
* - `outer.estimatedRows × Π(cross-branch estimatedRows)` exceeds
|
|
504
|
+
* `maxCrossProduct`.
|
|
505
|
+
*
|
|
506
|
+
* Unknown estimates are treated as exceeding the cap (return `false`) so a
|
|
507
|
+
* missing statistic never authorizes an unbounded product — the chain then
|
|
508
|
+
* stays a streaming / re-executing nested-loop join, which is already
|
|
509
|
+
* memory-safe. At-most-one branches are not passed in (they contribute ≤1 row
|
|
510
|
+
* per outer row and never widen the product).
|
|
511
|
+
*/
|
|
512
|
+
function crossGuardsPass(outer, crossLookups, tuning) {
|
|
513
|
+
if (crossLookups.length === 0)
|
|
514
|
+
return true;
|
|
515
|
+
const outerEst = rowEstimate(outer);
|
|
516
|
+
if (outerEst === undefined)
|
|
517
|
+
return false;
|
|
518
|
+
let product = outerEst;
|
|
519
|
+
for (const lk of crossLookups) {
|
|
520
|
+
const est = rowEstimate(lk);
|
|
521
|
+
if (est === undefined)
|
|
522
|
+
return false;
|
|
523
|
+
if (est > tuning.maxCrossBranchRows)
|
|
524
|
+
return false;
|
|
525
|
+
product *= est;
|
|
526
|
+
if (product > tuning.maxCrossProduct)
|
|
527
|
+
return false;
|
|
528
|
+
}
|
|
529
|
+
return true;
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Best-available row estimate for a node: prefer the computed physical estimate
|
|
533
|
+
* (populated by the stats pass / `computePhysical`), falling back to the node's
|
|
534
|
+
* own `estimatedRows`. Returns `undefined` when neither is known — callers treat
|
|
535
|
+
* that conservatively.
|
|
536
|
+
*/
|
|
537
|
+
function rowEstimate(node) {
|
|
538
|
+
return node.physical?.estimatedRows ?? node.estimatedRows;
|
|
539
|
+
}
|
|
540
|
+
function rebuildChain(chain, bottom) {
|
|
541
|
+
let current = bottom;
|
|
542
|
+
// Chain was collected top→bottom (root pushed first); rebuild bottom→top.
|
|
543
|
+
for (let i = chain.length - 1; i >= 0; i--) {
|
|
544
|
+
const entry = chain[i];
|
|
545
|
+
switch (entry.kind) {
|
|
546
|
+
case 'filter': {
|
|
547
|
+
current = new FilterNode(entry.node.scope, current, entry.node.predicate);
|
|
548
|
+
break;
|
|
549
|
+
}
|
|
550
|
+
case 'sort': {
|
|
551
|
+
current = new SortNode(entry.node.scope, current, entry.node.sortKeys);
|
|
552
|
+
break;
|
|
553
|
+
}
|
|
554
|
+
case 'limit': {
|
|
555
|
+
current = new LimitOffsetNode(entry.node.scope, current, entry.node.limit, entry.node.offset);
|
|
556
|
+
break;
|
|
557
|
+
}
|
|
558
|
+
case 'distinct': {
|
|
559
|
+
current = new DistinctNode(entry.node.scope, current);
|
|
560
|
+
break;
|
|
561
|
+
}
|
|
562
|
+
case 'alias': {
|
|
563
|
+
current = new AliasNode(entry.node.scope, current, entry.node.alias);
|
|
564
|
+
break;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
return current;
|
|
569
|
+
}
|
|
570
|
+
function rebuildProject(project, newSource, subqueryReplacements) {
|
|
571
|
+
const attributes = project.getAttributes();
|
|
572
|
+
const newProjections = project.projections.map((p, i) => {
|
|
573
|
+
// Substitute recognized subquery node(s) anywhere in the projection's
|
|
574
|
+
// scalar tree with the column reference into the fan-out's wide row. A
|
|
575
|
+
// bare-subquery projection is replaced wholesale; a wrapped subquery has
|
|
576
|
+
// only its inner node swapped, leaving the wrapping expression intact. The
|
|
577
|
+
// projection keeps its own attributeId/alias.
|
|
578
|
+
const node = subqueryReplacements
|
|
579
|
+
? substituteSubqueries(p.node, subqueryReplacements)
|
|
580
|
+
: p.node;
|
|
581
|
+
return {
|
|
582
|
+
node,
|
|
583
|
+
alias: p.alias,
|
|
584
|
+
attributeId: attributes[i].id,
|
|
585
|
+
};
|
|
586
|
+
});
|
|
587
|
+
if (!isRelationalNode(newSource)) {
|
|
588
|
+
throw new Error('rule-fanout-lookup-join: rebuilt source must be relational');
|
|
589
|
+
}
|
|
590
|
+
return new ProjectNode(project.scope, newSource, newProjections, undefined, attributes, project.preserveInputColumns);
|
|
591
|
+
}
|
|
592
|
+
//# sourceMappingURL=rule-fanout-lookup-join.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule-fanout-lookup-join.js","sourceRoot":"","sources":["../../../../../src/planner/rules/join/rule-fanout-lookup-join.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
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 type { OptContext } from '../../framework/context.js';
|
|
41
|
+
import type { PlanNode } from '../../nodes/plan-node.js';
|
|
42
|
+
export declare function ruleAsyncGatherUnionAll(node: PlanNode, context: OptContext): PlanNode | null;
|
|
43
|
+
//# sourceMappingURL=rule-async-gather-union-all.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule-async-gather-union-all.d.ts","sourceRoot":"","sources":["../../../../../src/planner/rules/parallel/rule-async-gather-union-all.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,KAAK,EAAE,QAAQ,EAAsB,MAAM,0BAA0B,CAAC;AAO7E,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,GAAG,QAAQ,GAAG,IAAI,CA+C5F"}
|