@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,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"}