@quereus/quereus 3.1.2 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. package/dist/src/core/database-assertions.d.ts.map +1 -1
  2. package/dist/src/core/database-assertions.js +7 -3
  3. package/dist/src/core/database-assertions.js.map +1 -1
  4. package/dist/src/core/database.js +3 -3
  5. package/dist/src/core/database.js.map +1 -1
  6. package/dist/src/core/statement.js +3 -3
  7. package/dist/src/core/statement.js.map +1 -1
  8. package/dist/src/emit/ast-stringify.js +2 -2
  9. package/dist/src/emit/ast-stringify.js.map +1 -1
  10. package/dist/src/index.d.ts +2 -1
  11. package/dist/src/index.d.ts.map +1 -1
  12. package/dist/src/index.js +1 -0
  13. package/dist/src/index.js.map +1 -1
  14. package/dist/src/parser/parser.d.ts +1 -1
  15. package/dist/src/parser/parser.d.ts.map +1 -1
  16. package/dist/src/parser/parser.js +6 -2
  17. package/dist/src/parser/parser.js.map +1 -1
  18. package/dist/src/parser/visitor.js +1 -1
  19. package/dist/src/parser/visitor.js.map +1 -1
  20. package/dist/src/planner/analysis/attribute-provenance.d.ts +45 -0
  21. package/dist/src/planner/analysis/attribute-provenance.d.ts.map +1 -0
  22. package/dist/src/planner/analysis/attribute-provenance.js +81 -0
  23. package/dist/src/planner/analysis/attribute-provenance.js.map +1 -0
  24. package/dist/src/planner/analysis/binding-extractor.d.ts.map +1 -1
  25. package/dist/src/planner/analysis/binding-extractor.js +9 -6
  26. package/dist/src/planner/analysis/binding-extractor.js.map +1 -1
  27. package/dist/src/planner/analysis/change-scope.d.ts.map +1 -1
  28. package/dist/src/planner/analysis/change-scope.js +7 -0
  29. package/dist/src/planner/analysis/change-scope.js.map +1 -1
  30. package/dist/src/planner/analysis/const-evaluator.js +5 -5
  31. package/dist/src/planner/analysis/const-evaluator.js.map +1 -1
  32. package/dist/src/planner/analysis/constraint-extractor.d.ts +10 -0
  33. package/dist/src/planner/analysis/constraint-extractor.d.ts.map +1 -1
  34. package/dist/src/planner/analysis/constraint-extractor.js +28 -12
  35. package/dist/src/planner/analysis/constraint-extractor.js.map +1 -1
  36. package/dist/src/planner/building/delete.d.ts.map +1 -1
  37. package/dist/src/planner/building/delete.js +7 -4
  38. package/dist/src/planner/building/delete.js.map +1 -1
  39. package/dist/src/planner/building/select-aggregates.d.ts.map +1 -1
  40. package/dist/src/planner/building/select-aggregates.js +4 -13
  41. package/dist/src/planner/building/select-aggregates.js.map +1 -1
  42. package/dist/src/planner/building/select-window.d.ts.map +1 -1
  43. package/dist/src/planner/building/select-window.js +54 -21
  44. package/dist/src/planner/building/select-window.js.map +1 -1
  45. package/dist/src/planner/cache/correlation-detector.d.ts +7 -0
  46. package/dist/src/planner/cache/correlation-detector.d.ts.map +1 -1
  47. package/dist/src/planner/cache/correlation-detector.js +34 -2
  48. package/dist/src/planner/cache/correlation-detector.js.map +1 -1
  49. package/dist/src/planner/framework/physical-utils.d.ts.map +1 -1
  50. package/dist/src/planner/framework/physical-utils.js +7 -1
  51. package/dist/src/planner/framework/physical-utils.js.map +1 -1
  52. package/dist/src/planner/nodes/aggregate-node.d.ts +6 -4
  53. package/dist/src/planner/nodes/aggregate-node.d.ts.map +1 -1
  54. package/dist/src/planner/nodes/aggregate-node.js +8 -6
  55. package/dist/src/planner/nodes/aggregate-node.js.map +1 -1
  56. package/dist/src/planner/nodes/analyze-node.d.ts.map +1 -1
  57. package/dist/src/planner/nodes/analyze-node.js +3 -0
  58. package/dist/src/planner/nodes/analyze-node.js.map +1 -1
  59. package/dist/src/planner/nodes/async-gather-node.d.ts +169 -0
  60. package/dist/src/planner/nodes/async-gather-node.d.ts.map +1 -0
  61. package/dist/src/planner/nodes/async-gather-node.js +488 -0
  62. package/dist/src/planner/nodes/async-gather-node.js.map +1 -0
  63. package/dist/src/planner/nodes/bloom-join-node.d.ts.map +1 -1
  64. package/dist/src/planner/nodes/bloom-join-node.js +8 -7
  65. package/dist/src/planner/nodes/bloom-join-node.js.map +1 -1
  66. package/dist/src/planner/nodes/eager-prefetch-node.d.ts +47 -0
  67. package/dist/src/planner/nodes/eager-prefetch-node.d.ts.map +1 -0
  68. package/dist/src/planner/nodes/eager-prefetch-node.js +96 -0
  69. package/dist/src/planner/nodes/eager-prefetch-node.js.map +1 -0
  70. package/dist/src/planner/nodes/fanout-lookup-join-node.d.ts +150 -0
  71. package/dist/src/planner/nodes/fanout-lookup-join-node.d.ts.map +1 -0
  72. package/dist/src/planner/nodes/fanout-lookup-join-node.js +265 -0
  73. package/dist/src/planner/nodes/fanout-lookup-join-node.js.map +1 -0
  74. package/dist/src/planner/nodes/hash-aggregate.d.ts.map +1 -1
  75. package/dist/src/planner/nodes/hash-aggregate.js +6 -16
  76. package/dist/src/planner/nodes/hash-aggregate.js.map +1 -1
  77. package/dist/src/planner/nodes/join-utils.d.ts.map +1 -1
  78. package/dist/src/planner/nodes/join-utils.js +7 -1
  79. package/dist/src/planner/nodes/join-utils.js.map +1 -1
  80. package/dist/src/planner/nodes/limit-offset.d.ts +12 -0
  81. package/dist/src/planner/nodes/limit-offset.d.ts.map +1 -1
  82. package/dist/src/planner/nodes/limit-offset.js +53 -3
  83. package/dist/src/planner/nodes/limit-offset.js.map +1 -1
  84. package/dist/src/planner/nodes/merge-join-node.d.ts.map +1 -1
  85. package/dist/src/planner/nodes/merge-join-node.js +8 -7
  86. package/dist/src/planner/nodes/merge-join-node.js.map +1 -1
  87. package/dist/src/planner/nodes/plan-node-type.d.ts +3 -0
  88. package/dist/src/planner/nodes/plan-node-type.d.ts.map +1 -1
  89. package/dist/src/planner/nodes/plan-node-type.js +3 -0
  90. package/dist/src/planner/nodes/plan-node-type.js.map +1 -1
  91. package/dist/src/planner/nodes/plan-node.d.ts +36 -0
  92. package/dist/src/planner/nodes/plan-node.d.ts.map +1 -1
  93. package/dist/src/planner/nodes/plan-node.js +26 -0
  94. package/dist/src/planner/nodes/plan-node.js.map +1 -1
  95. package/dist/src/planner/nodes/project-node.d.ts.map +1 -1
  96. package/dist/src/planner/nodes/project-node.js +18 -5
  97. package/dist/src/planner/nodes/project-node.js.map +1 -1
  98. package/dist/src/planner/nodes/reference.d.ts.map +1 -1
  99. package/dist/src/planner/nodes/reference.js +14 -3
  100. package/dist/src/planner/nodes/reference.js.map +1 -1
  101. package/dist/src/planner/nodes/set-operation-node.d.ts.map +1 -1
  102. package/dist/src/planner/nodes/set-operation-node.js +12 -1
  103. package/dist/src/planner/nodes/set-operation-node.js.map +1 -1
  104. package/dist/src/planner/nodes/sort.js +1 -1
  105. package/dist/src/planner/nodes/sort.js.map +1 -1
  106. package/dist/src/planner/nodes/stream-aggregate.d.ts.map +1 -1
  107. package/dist/src/planner/nodes/stream-aggregate.js +8 -23
  108. package/dist/src/planner/nodes/stream-aggregate.js.map +1 -1
  109. package/dist/src/planner/nodes/values-node.d.ts +2 -1
  110. package/dist/src/planner/nodes/values-node.d.ts.map +1 -1
  111. package/dist/src/planner/nodes/values-node.js +16 -0
  112. package/dist/src/planner/nodes/values-node.js.map +1 -1
  113. package/dist/src/planner/nodes/window-node.js +1 -1
  114. package/dist/src/planner/nodes/window-node.js.map +1 -1
  115. package/dist/src/planner/optimizer-tuning.d.ts +107 -0
  116. package/dist/src/planner/optimizer-tuning.d.ts.map +1 -1
  117. package/dist/src/planner/optimizer-tuning.js +43 -0
  118. package/dist/src/planner/optimizer-tuning.js.map +1 -1
  119. package/dist/src/planner/optimizer.d.ts.map +1 -1
  120. package/dist/src/planner/optimizer.js +91 -0
  121. package/dist/src/planner/optimizer.js.map +1 -1
  122. package/dist/src/planner/rules/access/rule-monotonic-range-access.d.ts.map +1 -1
  123. package/dist/src/planner/rules/access/rule-monotonic-range-access.js +1 -6
  124. package/dist/src/planner/rules/access/rule-monotonic-range-access.js.map +1 -1
  125. package/dist/src/planner/rules/aggregate/rule-aggregate-streaming.d.ts.map +1 -1
  126. package/dist/src/planner/rules/aggregate/rule-aggregate-streaming.js +8 -27
  127. package/dist/src/planner/rules/aggregate/rule-aggregate-streaming.js.map +1 -1
  128. package/dist/src/planner/rules/aggregate/rule-groupby-fd-simplification.d.ts +9 -3
  129. package/dist/src/planner/rules/aggregate/rule-groupby-fd-simplification.d.ts.map +1 -1
  130. package/dist/src/planner/rules/aggregate/rule-groupby-fd-simplification.js +47 -5
  131. package/dist/src/planner/rules/aggregate/rule-groupby-fd-simplification.js.map +1 -1
  132. package/dist/src/planner/rules/distinct/rule-distinct-elimination.d.ts +8 -7
  133. package/dist/src/planner/rules/distinct/rule-distinct-elimination.d.ts.map +1 -1
  134. package/dist/src/planner/rules/distinct/rule-distinct-elimination.js +14 -21
  135. package/dist/src/planner/rules/distinct/rule-distinct-elimination.js.map +1 -1
  136. package/dist/src/planner/rules/join/equi-pair-extractor.js +4 -4
  137. package/dist/src/planner/rules/join/equi-pair-extractor.js.map +1 -1
  138. package/dist/src/planner/rules/join/rule-fanout-batched-outer.d.ts +74 -0
  139. package/dist/src/planner/rules/join/rule-fanout-batched-outer.d.ts.map +1 -0
  140. package/dist/src/planner/rules/join/rule-fanout-batched-outer.js +139 -0
  141. package/dist/src/planner/rules/join/rule-fanout-batched-outer.js.map +1 -0
  142. package/dist/src/planner/rules/join/rule-fanout-lookup-join.d.ts +58 -0
  143. package/dist/src/planner/rules/join/rule-fanout-lookup-join.d.ts.map +1 -0
  144. package/dist/src/planner/rules/join/rule-fanout-lookup-join.js +590 -0
  145. package/dist/src/planner/rules/join/rule-fanout-lookup-join.js.map +1 -0
  146. package/dist/src/planner/rules/join/rule-join-greedy-commute.d.ts.map +1 -1
  147. package/dist/src/planner/rules/join/rule-join-greedy-commute.js +10 -0
  148. package/dist/src/planner/rules/join/rule-join-greedy-commute.js.map +1 -1
  149. package/dist/src/planner/rules/join/rule-join-physical-selection.d.ts.map +1 -1
  150. package/dist/src/planner/rules/join/rule-join-physical-selection.js +2 -1
  151. package/dist/src/planner/rules/join/rule-join-physical-selection.js.map +1 -1
  152. package/dist/src/planner/rules/join/rule-lateral-top1-asof.d.ts.map +1 -1
  153. package/dist/src/planner/rules/join/rule-lateral-top1-asof.js +1 -2
  154. package/dist/src/planner/rules/join/rule-lateral-top1-asof.js.map +1 -1
  155. package/dist/src/planner/rules/parallel/rule-async-gather-union-all.d.ts +43 -0
  156. package/dist/src/planner/rules/parallel/rule-async-gather-union-all.d.ts.map +1 -0
  157. package/dist/src/planner/rules/parallel/rule-async-gather-union-all.js +115 -0
  158. package/dist/src/planner/rules/parallel/rule-async-gather-union-all.js.map +1 -0
  159. package/dist/src/planner/rules/parallel/rule-async-gather-zip-by-key.d.ts +102 -0
  160. package/dist/src/planner/rules/parallel/rule-async-gather-zip-by-key.d.ts.map +1 -0
  161. package/dist/src/planner/rules/parallel/rule-async-gather-zip-by-key.js +545 -0
  162. package/dist/src/planner/rules/parallel/rule-async-gather-zip-by-key.js.map +1 -0
  163. package/dist/src/planner/rules/parallel/rule-eager-prefetch-probe.d.ts +45 -0
  164. package/dist/src/planner/rules/parallel/rule-eager-prefetch-probe.d.ts.map +1 -0
  165. package/dist/src/planner/rules/parallel/rule-eager-prefetch-probe.js +78 -0
  166. package/dist/src/planner/rules/parallel/rule-eager-prefetch-probe.js.map +1 -0
  167. package/dist/src/planner/rules/predicate/rule-aggregate-predicate-pushdown.js +1 -1
  168. package/dist/src/planner/rules/predicate/rule-aggregate-predicate-pushdown.js.map +1 -1
  169. package/dist/src/planner/rules/retrieve/rule-grow-retrieve.js +2 -2
  170. package/dist/src/planner/rules/retrieve/rule-grow-retrieve.js.map +1 -1
  171. package/dist/src/planner/rules/sort/rule-orderby-fd-pruning.d.ts +16 -0
  172. package/dist/src/planner/rules/sort/rule-orderby-fd-pruning.d.ts.map +1 -1
  173. package/dist/src/planner/rules/sort/rule-orderby-fd-pruning.js +47 -4
  174. package/dist/src/planner/rules/sort/rule-orderby-fd-pruning.js.map +1 -1
  175. package/dist/src/planner/rules/window/rule-monotonic-window.js +1 -1
  176. package/dist/src/planner/rules/window/rule-monotonic-window.js.map +1 -1
  177. package/dist/src/planner/scopes/param.d.ts.map +1 -1
  178. package/dist/src/planner/scopes/param.js +13 -11
  179. package/dist/src/planner/scopes/param.js.map +1 -1
  180. package/dist/src/planner/type-utils.js +1 -1
  181. package/dist/src/planner/type-utils.js.map +1 -1
  182. package/dist/src/planner/util/fd-utils.d.ts +59 -1
  183. package/dist/src/planner/util/fd-utils.d.ts.map +1 -1
  184. package/dist/src/planner/util/fd-utils.js +122 -0
  185. package/dist/src/planner/util/fd-utils.js.map +1 -1
  186. package/dist/src/planner/util/key-utils.d.ts +26 -3
  187. package/dist/src/planner/util/key-utils.d.ts.map +1 -1
  188. package/dist/src/planner/util/key-utils.js +113 -33
  189. package/dist/src/planner/util/key-utils.js.map +1 -1
  190. package/dist/src/planner/validation/plan-validator.d.ts.map +1 -1
  191. package/dist/src/planner/validation/plan-validator.js +17 -19
  192. package/dist/src/planner/validation/plan-validator.js.map +1 -1
  193. package/dist/src/runtime/async-semaphore.d.ts +36 -0
  194. package/dist/src/runtime/async-semaphore.d.ts.map +1 -0
  195. package/dist/src/runtime/async-semaphore.js +72 -0
  196. package/dist/src/runtime/async-semaphore.js.map +1 -0
  197. package/dist/src/runtime/deferred-constraint-queue.d.ts.map +1 -1
  198. package/dist/src/runtime/deferred-constraint-queue.js +4 -3
  199. package/dist/src/runtime/deferred-constraint-queue.js.map +1 -1
  200. package/dist/src/runtime/delta-executor.d.ts.map +1 -1
  201. package/dist/src/runtime/delta-executor.js +9 -0
  202. package/dist/src/runtime/delta-executor.js.map +1 -1
  203. package/dist/src/runtime/emit/asof-scan.d.ts.map +1 -1
  204. package/dist/src/runtime/emit/asof-scan.js +6 -4
  205. package/dist/src/runtime/emit/asof-scan.js.map +1 -1
  206. package/dist/src/runtime/emit/async-gather.d.ts +77 -0
  207. package/dist/src/runtime/emit/async-gather.d.ts.map +1 -0
  208. package/dist/src/runtime/emit/async-gather.js +234 -0
  209. package/dist/src/runtime/emit/async-gather.js.map +1 -0
  210. package/dist/src/runtime/emit/binary.d.ts.map +1 -1
  211. package/dist/src/runtime/emit/binary.js +19 -27
  212. package/dist/src/runtime/emit/binary.js.map +1 -1
  213. package/dist/src/runtime/emit/bloom-join.d.ts.map +1 -1
  214. package/dist/src/runtime/emit/bloom-join.js +42 -19
  215. package/dist/src/runtime/emit/bloom-join.js.map +1 -1
  216. package/dist/src/runtime/emit/constraint-check.d.ts.map +1 -1
  217. package/dist/src/runtime/emit/constraint-check.js +35 -1
  218. package/dist/src/runtime/emit/constraint-check.js.map +1 -1
  219. package/dist/src/runtime/emit/delete.d.ts.map +1 -1
  220. package/dist/src/runtime/emit/delete.js +15 -5
  221. package/dist/src/runtime/emit/delete.js.map +1 -1
  222. package/dist/src/runtime/emit/eager-prefetch.d.ts +77 -0
  223. package/dist/src/runtime/emit/eager-prefetch.d.ts.map +1 -0
  224. package/dist/src/runtime/emit/eager-prefetch.js +223 -0
  225. package/dist/src/runtime/emit/eager-prefetch.js.map +1 -0
  226. package/dist/src/runtime/emit/fanout-lookup-join.d.ts +130 -0
  227. package/dist/src/runtime/emit/fanout-lookup-join.d.ts.map +1 -0
  228. package/dist/src/runtime/emit/fanout-lookup-join.js +521 -0
  229. package/dist/src/runtime/emit/fanout-lookup-join.js.map +1 -0
  230. package/dist/src/runtime/emit/merge-join.d.ts.map +1 -1
  231. package/dist/src/runtime/emit/merge-join.js +4 -2
  232. package/dist/src/runtime/emit/merge-join.js.map +1 -1
  233. package/dist/src/runtime/parallel-driver.d.ts +68 -0
  234. package/dist/src/runtime/parallel-driver.d.ts.map +1 -0
  235. package/dist/src/runtime/parallel-driver.js +233 -0
  236. package/dist/src/runtime/parallel-driver.js.map +1 -0
  237. package/dist/src/runtime/register.d.ts.map +1 -1
  238. package/dist/src/runtime/register.js +9 -0
  239. package/dist/src/runtime/register.js.map +1 -1
  240. package/dist/src/runtime/strict-fork.d.ts +36 -0
  241. package/dist/src/runtime/strict-fork.d.ts.map +1 -0
  242. package/dist/src/runtime/strict-fork.js +125 -0
  243. package/dist/src/runtime/strict-fork.js.map +1 -0
  244. package/dist/src/types/temporal-types.d.ts.map +1 -1
  245. package/dist/src/types/temporal-types.js +71 -36
  246. package/dist/src/types/temporal-types.js.map +1 -1
  247. package/dist/src/util/comparison.d.ts.map +1 -1
  248. package/dist/src/util/comparison.js +11 -1
  249. package/dist/src/util/comparison.js.map +1 -1
  250. package/dist/src/vtab/concurrency.d.ts +29 -0
  251. package/dist/src/vtab/concurrency.d.ts.map +1 -0
  252. package/dist/src/vtab/concurrency.js +47 -0
  253. package/dist/src/vtab/concurrency.js.map +1 -0
  254. package/dist/src/vtab/memory/layer/scan-layer.d.ts.map +1 -1
  255. package/dist/src/vtab/memory/layer/scan-layer.js +67 -29
  256. package/dist/src/vtab/memory/layer/scan-layer.js.map +1 -1
  257. package/dist/src/vtab/memory/module.d.ts +21 -0
  258. package/dist/src/vtab/memory/module.d.ts.map +1 -1
  259. package/dist/src/vtab/memory/module.js +21 -0
  260. package/dist/src/vtab/memory/module.js.map +1 -1
  261. package/dist/src/vtab/module.d.ts +47 -0
  262. package/dist/src/vtab/module.d.ts.map +1 -1
  263. package/package.json +4 -3
@@ -0,0 +1,590 @@
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
+ // Drive the branch off the subquery root verbatim — its column-0 attribute
229
+ // IS `valueAttr` (a no-GROUP-BY scalar aggregate advertises exactly its
230
+ // logical schema in both logical and physical form, and the recognition
231
+ // gate already rejected any root with `getAttributes().length !== 1`).
232
+ branchSpecs.push({
233
+ child: b.subqueryRoot,
234
+ mode: b.mode,
235
+ outputAttrs: b.subqueryRoot.getAttributes(),
236
+ concurrencySafe: b.concurrencySafe,
237
+ });
238
+ }
239
+ // `preserveAttributeIds` pins the wide-row layout: outer attrs + each
240
+ // branch's output attrs (nullable-widened for left-preserving branches —
241
+ // atMostOne-left / cross-left). The branch outputs are the lookups'/
242
+ // subqueries' own attributes, so any reference resolves by attribute ID
243
+ // regardless of wide-row position.
244
+ const preserveAttrs = [];
245
+ for (const a of outerAttrs)
246
+ preserveAttrs.push(a);
247
+ for (const spec of branchSpecs) {
248
+ const nullable = isLeftBranchMode(spec.mode);
249
+ for (const a of spec.outputAttrs) {
250
+ if (nullable && !a.type.nullable) {
251
+ preserveAttrs.push({ ...a, type: { ...a.type, nullable: true } });
252
+ }
253
+ else {
254
+ preserveAttrs.push(a);
255
+ }
256
+ }
257
+ }
258
+ const fanout = new FanOutLookupJoinNode(node.scope, outerSubtree, branchSpecs, concurrencyCap, preserveAttrs);
259
+ // Build the projection rewrite map. Each subquery branch's single output
260
+ // attribute materializes at a fixed wide-row index (outer + preceding branch
261
+ // outputs); replace the ScalarSubqueryNode in the projection with a column
262
+ // reference at that index. Correctness comes from the attribute ID (resolved
263
+ // via the row descriptor); the index is the runtime read position.
264
+ const subqueryReplacements = new Map();
265
+ let wideIndex = outerAttrs.length;
266
+ for (const b of spineBranches)
267
+ wideIndex += b.lookup.getAttributes().length;
268
+ for (const b of subqueryBranches) {
269
+ const outAttr = b.valueAttr;
270
+ // atMostOne-left can null-fill (empty children), so the read type is
271
+ // nullable; this matches the wide-row widening in `preserveAttrs`.
272
+ const colType = outAttr.type.nullable
273
+ ? outAttr.type
274
+ : { ...outAttr.type, nullable: true };
275
+ const colRef = new ColumnReferenceNode(node.scope, columnExprFor(outAttr.name), colType, outAttr.id, wideIndex);
276
+ subqueryReplacements.set(b.subqueryNode, colRef);
277
+ wideIndex += 1; // each subquery branch contributes exactly one column
278
+ }
279
+ log('Forming FanOutLookupJoin with %d branches (%d spine + %d subquery, cap=%d, maxLatency=%d)', totalBranches, spineBranches.length, subqueryBranches.length, concurrencyCap, maxLatency);
280
+ const rebuilt = rebuildChain(chain, fanout);
281
+ return rebuildProject(node, rebuilt, subqueryReplacements);
282
+ }
283
+ /** Minimal synthetic AST.ColumnExpr for a rewritten projection column ref. */
284
+ function columnExprFor(name) {
285
+ return { type: 'column', name };
286
+ }
287
+ /**
288
+ * Collect every `ScalarSubqueryNode` reachable in a projection's scalar
289
+ * expression tree, in deterministic pre-order. A recognized subquery is a leaf
290
+ * for this walk: we push it and do NOT descend into its relational body, so a
291
+ * subquery nested *inside* another subquery's correlation predicate remains part
292
+ * of its enclosing branch child rather than being clustered as its own branch.
293
+ * (The relational body is filtered out by the `typeClass === 'scalar'` guard
294
+ * regardless, but stopping early keeps the intent explicit.)
295
+ */
296
+ function collectScalarSubqueries(expr, out) {
297
+ if (expr instanceof ScalarSubqueryNode) {
298
+ out.push(expr);
299
+ return;
300
+ }
301
+ for (const child of expr.getChildren()) {
302
+ if (child.getType().typeClass === 'scalar') {
303
+ collectScalarSubqueries(child, out);
304
+ }
305
+ }
306
+ }
307
+ /**
308
+ * Rebuild a projection's scalar expression with each recognized
309
+ * `ScalarSubqueryNode` replaced by its `ColumnReferenceNode` into the fan-out's
310
+ * wide row, leaving the wrapping expression (`coalesce(<colref>, 0)`) intact.
311
+ * For a bare-subquery projection the root itself is in the map and is returned
312
+ * directly; for a wrapped subquery the tree is rebuilt via `withChildren` with
313
+ * only the matched inner node substituted. Returns the input unchanged when no
314
+ * descendant is a recognized subquery.
315
+ */
316
+ function substituteSubqueries(expr, replacements) {
317
+ if (expr instanceof ScalarSubqueryNode) {
318
+ return replacements.get(expr) ?? expr;
319
+ }
320
+ const children = expr.getChildren();
321
+ if (children.length === 0)
322
+ return expr;
323
+ const newChildren = [];
324
+ let changed = false;
325
+ for (const child of children) {
326
+ if (child.getType().typeClass === 'scalar') {
327
+ const replaced = substituteSubqueries(child, replacements);
328
+ newChildren.push(replaced);
329
+ if (replaced !== child)
330
+ changed = true;
331
+ }
332
+ else {
333
+ newChildren.push(child);
334
+ }
335
+ }
336
+ if (!changed)
337
+ return expr;
338
+ return expr.withChildren(newChildren);
339
+ }
340
+ /**
341
+ * Recognize a correlated scalar-aggregate subquery as an `atMostOne-left`
342
+ * fan-out branch. Returns null when the subquery is not correlated, correlates
343
+ * to anything other than the outer subtree, is not aggregate-shaped with zero
344
+ * grouping keys beneath pass-through wrappers, or does not expose exactly one
345
+ * output attribute.
346
+ *
347
+ * The correlation must resolve *entirely* against `outerAttrIds`: at runtime
348
+ * the fan-out installs only the outer row's slot before forking each branch, so
349
+ * a subquery referencing a sibling spine-branch attribute (produced inside the
350
+ * fan-out, never installed as a slot) would fail to resolve its column at
351
+ * runtime. Rejecting it here keeps such a subquery as an ordinary correlated
352
+ * projection. (See `correlated subquery referencing a spine-branch attribute`
353
+ * in `parallel-fanout.spec.ts`.)
354
+ *
355
+ * The aggregate-shape test uses `CapabilityDetectors.isAggregating`, which
356
+ * matches both the logical `AggregateNode` and the physical
357
+ * `StreamAggregateNode` / `HashAggregateNode`, so it is robust to optimizer
358
+ * pass ordering (the subquery root may still be logical at structural time).
359
+ */
360
+ function recognizeSubqueryBranch(scalarSubquery, outerAttrIds) {
361
+ const external = collectExternalReferences(scalarSubquery.subquery);
362
+ if (external.size === 0)
363
+ return null; // not correlated
364
+ for (const id of external) {
365
+ if (!outerAttrIds.has(id))
366
+ return null; // correlates beyond the outer subtree
367
+ }
368
+ // Descend pass-through wrappers (Project/Alias/Sort/LimitOffset) to the
369
+ // aggregate root.
370
+ let root = scalarSubquery.subquery;
371
+ while (!CapabilityDetectors.isAggregating(root)) {
372
+ if (root instanceof ProjectNode ||
373
+ root instanceof AliasNode ||
374
+ root instanceof SortNode ||
375
+ root instanceof LimitOffsetNode) {
376
+ root = root.source;
377
+ continue;
378
+ }
379
+ return null;
380
+ }
381
+ // Empty grouping ⇒ exactly one row per outer ⇒ at-most-one branch. A
382
+ // GROUP BY subquery may yield more than one row and is rejected here.
383
+ if (root.getGroupingKeys().length !== 0)
384
+ return null;
385
+ // A scalar subquery's relational root exposes exactly one output column at
386
+ // structural time (validated at build); its column-0 attribute is the
387
+ // scalar value the branch contributes.
388
+ const subAttrs = scalarSubquery.subquery.getAttributes();
389
+ if (subAttrs.length !== 1)
390
+ return null;
391
+ return {
392
+ subqueryNode: scalarSubquery,
393
+ subqueryRoot: scalarSubquery.subquery,
394
+ valueAttr: subAttrs[0],
395
+ mode: 'atMostOne-left',
396
+ concurrencySafe: scalarSubquery.subquery.physical.concurrencySafe !== false,
397
+ };
398
+ }
399
+ /**
400
+ * Decide whether `join`'s `right` side is a parameterized equi-lookup eligible
401
+ * for branch clustering, and at what cardinality `mode`. The FK side is sourced
402
+ * from `outerSchema` + `outerAttrs` — both the equi-pair's left attribute and
403
+ * its `outerAttrs` membership are checked, which is the safety net keeping
404
+ * per-join alignment honest in the presence of intermediate joins in the chain
405
+ * (the join's own `.left` resolves to a combined relation, so we cannot extract
406
+ * a single schema from it).
407
+ *
408
+ * Two cardinality outcomes:
409
+ *
410
+ * - **at-most-one** (`atMostOne-left` / `atMostOne-inner`) — the lookup is
411
+ * FK→PK aligned, so each outer row matches ≤1 lookup row. INNER additionally
412
+ * requires a covering NOT-NULL FK + a row-preserving path (else it would
413
+ * drop or duplicate rows the cluster cannot account for — bail to preserve
414
+ * the nested-loop join).
415
+ *
416
+ * - **cross** / **cross-left** — a clean parameterized equi-lookup whose
417
+ * FK→PK alignment is *absent* (no FK, or FK→non-unique), so the
418
+ * per-outer-row cardinality is data-driven (1:n). `inner` / `cross` join
419
+ * types yield `cross` (inner-drop on an empty branch); a `left` join yields
420
+ * `cross-left` (NULL-pad + preserve the outer row on an empty branch, with
421
+ * nullable-widened branch outputs). The unbounded Cartesian product is gated
422
+ * by the caller's row/product guards in both cases.
423
+ *
424
+ * Aligned-but-not-at-most-one INNER lookups (nullable FK, non-row-preserving
425
+ * path) are *not* reclassified as `cross`: FK→PK is still ≤1 match, so the issue
426
+ * is inner-drop semantics, not cardinality. They bail (return null) exactly as
427
+ * before, so the chain falls back to a nested-loop join.
428
+ */
429
+ function recognizeBranch(join, outerSchema, outerAttrs) {
430
+ if (join.joinType !== 'left' && join.joinType !== 'inner' && join.joinType !== 'cross')
431
+ return null;
432
+ if (!join.condition)
433
+ return null;
434
+ const leftAttrs = join.left.getAttributes();
435
+ const rightAttrs = join.right.getAttributes();
436
+ const pairs = extractEquiPairsFromCondition(join.condition, leftAttrs, rightAttrs);
437
+ if (pairs.length === 0)
438
+ return null;
439
+ const normalized = normalizePredicate(join.condition);
440
+ if (!isAndOfColumnEqualities(normalized))
441
+ return null;
442
+ const outerAttrIdToIdx = new Map();
443
+ outerAttrs.forEach((a, i) => outerAttrIdToIdx.set(a.id, i));
444
+ // Translate each equi-pair from "(left subtree column index, right column
445
+ // index)" to "(outer column index, right column index)". The left subtree
446
+ // may span multiple joins, but the equi-pair's left attribute must
447
+ // originate in the outer subtree so the lookup is parameterizable from the
448
+ // outer row (and, for FK→PK, so the relationship makes sense).
449
+ const outerCols = [];
450
+ const rightCols = [];
451
+ for (const p of pairs) {
452
+ const leftAttrId = leftAttrs[p.left]?.id;
453
+ if (leftAttrId === undefined)
454
+ return null;
455
+ const outerIdx = outerAttrIdToIdx.get(leftAttrId);
456
+ if (outerIdx === undefined)
457
+ return null;
458
+ outerCols.push(outerIdx);
459
+ rightCols.push(p.right);
460
+ }
461
+ const rightSchema = extractTableSchema(join.right);
462
+ if (!rightSchema)
463
+ return null;
464
+ // At-most-one path: FK→PK alignment guarantees ≤1 match per outer row.
465
+ if (checkFkPkAlignment(outerSchema, rightSchema, outerCols, rightCols)) {
466
+ if (join.joinType === 'left') {
467
+ return { lookup: join.right, mode: 'atMostOne-left', condition: join.condition };
468
+ }
469
+ if (join.joinType === 'inner') {
470
+ const match = lookupCoveringFK(outerSchema, rightSchema, outerCols, rightCols);
471
+ if (!match || match.nullable)
472
+ return null;
473
+ if (!isRowPreservingPathToTable(join.right))
474
+ return null;
475
+ return { lookup: join.right, mode: 'atMostOne-inner', condition: join.condition };
476
+ }
477
+ // An aligned `cross` join type (unusual: a cross join carrying an
478
+ // equi-condition) falls through to the cross treatment below.
479
+ }
480
+ // Cross path: a clean parameterized equi-lookup that is not provably
481
+ // at-most-one (data-driven 1:n).
482
+ // - INNER/CROSS ⇒ `cross` (inner-drop on an empty branch).
483
+ // - LEFT ⇒ `cross-left` (NULL-pad + preserve the outer row on an empty
484
+ // branch; branch output attributes are nullable-widened by the node /
485
+ // `preserveAttrs`). Both contribute a 1:n factor gated by `crossGuardsPass`.
486
+ if (join.joinType === 'inner' || join.joinType === 'cross') {
487
+ return { lookup: join.right, mode: 'cross', condition: join.condition };
488
+ }
489
+ if (join.joinType === 'left') {
490
+ return { lookup: join.right, mode: 'cross-left', condition: join.condition };
491
+ }
492
+ return null;
493
+ }
494
+ /**
495
+ * Cross-branch memory guard. A `cross` branch contributes a *data-driven* (1:n)
496
+ * row count, so the fan-out's output is the Cartesian product of the outer side
497
+ * and every cross branch. Left ungated, that product can be unbounded, so we
498
+ * refuse to cluster when:
499
+ *
500
+ * - any cross branch's lookup estimate exceeds `maxCrossBranchRows`, or
501
+ * - `outer.estimatedRows × Π(cross-branch estimatedRows)` exceeds
502
+ * `maxCrossProduct`.
503
+ *
504
+ * Unknown estimates are treated as exceeding the cap (return `false`) so a
505
+ * missing statistic never authorizes an unbounded product — the chain then
506
+ * stays a streaming / re-executing nested-loop join, which is already
507
+ * memory-safe. At-most-one branches are not passed in (they contribute ≤1 row
508
+ * per outer row and never widen the product).
509
+ */
510
+ function crossGuardsPass(outer, crossLookups, tuning) {
511
+ if (crossLookups.length === 0)
512
+ return true;
513
+ const outerEst = rowEstimate(outer);
514
+ if (outerEst === undefined)
515
+ return false;
516
+ let product = outerEst;
517
+ for (const lk of crossLookups) {
518
+ const est = rowEstimate(lk);
519
+ if (est === undefined)
520
+ return false;
521
+ if (est > tuning.maxCrossBranchRows)
522
+ return false;
523
+ product *= est;
524
+ if (product > tuning.maxCrossProduct)
525
+ return false;
526
+ }
527
+ return true;
528
+ }
529
+ /**
530
+ * Best-available row estimate for a node: prefer the computed physical estimate
531
+ * (populated by the stats pass / `computePhysical`), falling back to the node's
532
+ * own `estimatedRows`. Returns `undefined` when neither is known — callers treat
533
+ * that conservatively.
534
+ */
535
+ function rowEstimate(node) {
536
+ return node.physical?.estimatedRows ?? node.estimatedRows;
537
+ }
538
+ function rebuildChain(chain, bottom) {
539
+ let current = bottom;
540
+ // Chain was collected top→bottom (root pushed first); rebuild bottom→top.
541
+ for (let i = chain.length - 1; i >= 0; i--) {
542
+ const entry = chain[i];
543
+ switch (entry.kind) {
544
+ case 'filter': {
545
+ current = new FilterNode(entry.node.scope, current, entry.node.predicate);
546
+ break;
547
+ }
548
+ case 'sort': {
549
+ current = new SortNode(entry.node.scope, current, entry.node.sortKeys);
550
+ break;
551
+ }
552
+ case 'limit': {
553
+ current = new LimitOffsetNode(entry.node.scope, current, entry.node.limit, entry.node.offset);
554
+ break;
555
+ }
556
+ case 'distinct': {
557
+ current = new DistinctNode(entry.node.scope, current);
558
+ break;
559
+ }
560
+ case 'alias': {
561
+ current = new AliasNode(entry.node.scope, current, entry.node.alias);
562
+ break;
563
+ }
564
+ }
565
+ }
566
+ return current;
567
+ }
568
+ function rebuildProject(project, newSource, subqueryReplacements) {
569
+ const attributes = project.getAttributes();
570
+ const newProjections = project.projections.map((p, i) => {
571
+ // Substitute recognized subquery node(s) anywhere in the projection's
572
+ // scalar tree with the column reference into the fan-out's wide row. A
573
+ // bare-subquery projection is replaced wholesale; a wrapped subquery has
574
+ // only its inner node swapped, leaving the wrapping expression intact. The
575
+ // projection keeps its own attributeId/alias.
576
+ const node = subqueryReplacements
577
+ ? substituteSubqueries(p.node, subqueryReplacements)
578
+ : p.node;
579
+ return {
580
+ node,
581
+ alias: p.alias,
582
+ attributeId: attributes[i].id,
583
+ };
584
+ });
585
+ if (!isRelationalNode(newSource)) {
586
+ throw new Error('rule-fanout-lookup-join: rebuilt source must be relational');
587
+ }
588
+ return new ProjectNode(project.scope, newSource, newProjections, undefined, attributes, project.preserveInputColumns);
589
+ }
590
+ //# 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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"}
@@ -1 +1 @@
1
- {"version":3,"file":"rule-join-greedy-commute.d.ts","sourceRoot":"","sources":["../../../../../src/planner/rules/join/rule-join-greedy-commute.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAsB,MAAM,0BAA0B,CAAC;AAC7E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAa7D;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,GAAG,QAAQ,GAAG,IAAI,CA4B3F"}
1
+ {"version":3,"file":"rule-join-greedy-commute.d.ts","sourceRoot":"","sources":["../../../../../src/planner/rules/join/rule-join-greedy-commute.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAsB,MAAM,0BAA0B,CAAC;AAC7E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAc7D;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,GAAG,QAAQ,GAAG,IAAI,CAsC3F"}
@@ -1,6 +1,7 @@
1
1
  import { createLogger } from '../../../common/logger.js';
2
2
  import { JoinNode } from '../../nodes/join-node.js';
3
3
  import { hasSingletonFd } from '../../util/fd-utils.js';
4
+ import { isCorrelatedSubquery } from '../../cache/correlation-detector.js';
4
5
  const log = createLogger('optimizer:rule:join-greedy-commute');
5
6
  /** True when the relation provably emits at most one row. */
6
7
  function isSingleton(node) {
@@ -24,6 +25,15 @@ export function ruleJoinGreedyCommute(node, _context) {
24
25
  return null;
25
26
  if (node.joinType !== 'inner' && node.joinType !== 'cross')
26
27
  return null;
28
+ // A correlated input (LATERAL referencing the other side) imposes an
29
+ // evaluation order: the correlated side must be the driven (right) side so the
30
+ // relation defining its outer references is in scope. Commuting would move it
31
+ // to the outer position and break that correlation. Skip the swap in that case
32
+ // — a ≤1-row correlated lateral (e.g. `LIMIT 1`) now advertises a singleton FD,
33
+ // which would otherwise mark it as the preferred driver.
34
+ if (isCorrelatedSubquery(node.getRightSource()) || isCorrelatedSubquery(node.getLeftSource())) {
35
+ return null;
36
+ }
27
37
  const leftRows = node.getLeftSource().estimatedRows ?? Number.POSITIVE_INFINITY;
28
38
  const rightRows = node.getRightSource().estimatedRows ?? Number.POSITIVE_INFINITY;
29
39
  // Prefer known finite estimatedRows; also detect <=1 row driver on either side
@@ -1 +1 @@
1
- {"version":3,"file":"rule-join-greedy-commute.js","sourceRoot":"","sources":["../../../../../src/planner/rules/join/rule-join-greedy-commute.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAGzD,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,MAAM,GAAG,GAAG,YAAY,CAAC,oCAAoC,CAAC,CAAC;AAE/D,6DAA6D;AAC7D,SAAS,WAAW,CAAC,IAAwB;IAC5C,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC,MAAM,CAAC;IAC7C,IAAI,QAAQ,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC,QAAQ,EAAE,aAAa,KAAK,CAAC,CAAC;IAC9D,OAAO,cAAc,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC;AACrD,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAc,EAAE,QAAoB;IACxE,IAAI,CAAC,CAAC,IAAI,YAAY,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7C,IAAI,IAAI,CAAC,QAAQ,KAAK,OAAO,IAAI,IAAI,CAAC,QAAQ,KAAK,OAAO;QAAE,OAAO,IAAI,CAAC;IAExE,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC,aAAa,IAAI,MAAM,CAAC,iBAAiB,CAAC;IAChF,MAAM,SAAS,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC,aAAa,IAAI,MAAM,CAAC,iBAAiB,CAAC;IAElF,+EAA+E;IAC/E,MAAM,eAAe,GAAG,WAAW,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC;IAC1D,MAAM,gBAAgB,GAAG,WAAW,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC;IAE5D,kEAAkE;IAClE,MAAM,UAAU,GAAG,CAAC,gBAAgB,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,gBAAgB,IAAI,CAAC,eAAe,IAAI,SAAS,GAAG,QAAQ,CAAC,CAAC;IAC7H,IAAI,CAAC,UAAU;QAAE,OAAO,IAAI,CAAC;IAE7B,GAAG,CAAC,wFAAwF,EAAE,MAAM,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC;IAEnI,qEAAqE;IACrE,MAAM,OAAO,GAAG,IAAI,QAAQ,CAC1B,IAAI,CAAC,KAAK,EACV,IAAI,CAAC,cAAc,EAAwB,EAC3C,IAAI,CAAC,aAAa,EAAwB,EAC1C,IAAI,CAAC,WAAW,EAAE,EAClB,IAAI,CAAC,gBAAgB,EAAE,EACvB,IAAI,CAAC,eAAe,EAAE,CACvB,CAAC;IAEF,OAAO,OAAO,CAAC;AACjB,CAAC"}
1
+ {"version":3,"file":"rule-join-greedy-commute.js","sourceRoot":"","sources":["../../../../../src/planner/rules/join/rule-join-greedy-commute.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAGzD,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qCAAqC,CAAC;AAE3E,MAAM,GAAG,GAAG,YAAY,CAAC,oCAAoC,CAAC,CAAC;AAE/D,6DAA6D;AAC7D,SAAS,WAAW,CAAC,IAAwB;IAC5C,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC,MAAM,CAAC;IAC7C,IAAI,QAAQ,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC,QAAQ,EAAE,aAAa,KAAK,CAAC,CAAC;IAC9D,OAAO,cAAc,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC;AACrD,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAc,EAAE,QAAoB;IACxE,IAAI,CAAC,CAAC,IAAI,YAAY,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7C,IAAI,IAAI,CAAC,QAAQ,KAAK,OAAO,IAAI,IAAI,CAAC,QAAQ,KAAK,OAAO;QAAE,OAAO,IAAI,CAAC;IAExE,qEAAqE;IACrE,+EAA+E;IAC/E,8EAA8E;IAC9E,+EAA+E;IAC/E,gFAAgF;IAChF,yDAAyD;IACzD,IAAI,oBAAoB,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC,IAAI,oBAAoB,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC,EAAE,CAAC;QAC9F,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC,aAAa,IAAI,MAAM,CAAC,iBAAiB,CAAC;IAChF,MAAM,SAAS,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC,aAAa,IAAI,MAAM,CAAC,iBAAiB,CAAC;IAElF,+EAA+E;IAC/E,MAAM,eAAe,GAAG,WAAW,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC;IAC1D,MAAM,gBAAgB,GAAG,WAAW,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC;IAE5D,kEAAkE;IAClE,MAAM,UAAU,GAAG,CAAC,gBAAgB,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,gBAAgB,IAAI,CAAC,eAAe,IAAI,SAAS,GAAG,QAAQ,CAAC,CAAC;IAC7H,IAAI,CAAC,UAAU;QAAE,OAAO,IAAI,CAAC;IAE7B,GAAG,CAAC,wFAAwF,EAAE,MAAM,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC;IAEnI,qEAAqE;IACrE,MAAM,OAAO,GAAG,IAAI,QAAQ,CAC1B,IAAI,CAAC,KAAK,EACV,IAAI,CAAC,cAAc,EAAwB,EAC3C,IAAI,CAAC,aAAa,EAAwB,EAC1C,IAAI,CAAC,WAAW,EAAE,EAClB,IAAI,CAAC,gBAAgB,EAAE,EACvB,IAAI,CAAC,eAAe,EAAE,CACvB,CAAC;IAEF,OAAO,OAAO,CAAC;AACjB,CAAC"}