@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.
- package/dist/src/core/database-assertions.d.ts.map +1 -1
- package/dist/src/core/database-assertions.js +7 -3
- package/dist/src/core/database-assertions.js.map +1 -1
- package/dist/src/core/database.js +3 -3
- package/dist/src/core/database.js.map +1 -1
- package/dist/src/core/statement.js +3 -3
- package/dist/src/core/statement.js.map +1 -1
- package/dist/src/emit/ast-stringify.js +2 -2
- package/dist/src/emit/ast-stringify.js.map +1 -1
- package/dist/src/index.d.ts +2 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/parser/parser.d.ts +1 -1
- package/dist/src/parser/parser.d.ts.map +1 -1
- package/dist/src/parser/parser.js +6 -2
- package/dist/src/parser/parser.js.map +1 -1
- package/dist/src/parser/visitor.js +1 -1
- package/dist/src/parser/visitor.js.map +1 -1
- package/dist/src/planner/analysis/attribute-provenance.d.ts +45 -0
- package/dist/src/planner/analysis/attribute-provenance.d.ts.map +1 -0
- package/dist/src/planner/analysis/attribute-provenance.js +81 -0
- package/dist/src/planner/analysis/attribute-provenance.js.map +1 -0
- package/dist/src/planner/analysis/binding-extractor.d.ts.map +1 -1
- package/dist/src/planner/analysis/binding-extractor.js +9 -6
- package/dist/src/planner/analysis/binding-extractor.js.map +1 -1
- package/dist/src/planner/analysis/change-scope.d.ts.map +1 -1
- package/dist/src/planner/analysis/change-scope.js +7 -0
- package/dist/src/planner/analysis/change-scope.js.map +1 -1
- package/dist/src/planner/analysis/const-evaluator.js +5 -5
- package/dist/src/planner/analysis/const-evaluator.js.map +1 -1
- package/dist/src/planner/analysis/constraint-extractor.d.ts +10 -0
- package/dist/src/planner/analysis/constraint-extractor.d.ts.map +1 -1
- package/dist/src/planner/analysis/constraint-extractor.js +28 -12
- package/dist/src/planner/analysis/constraint-extractor.js.map +1 -1
- package/dist/src/planner/building/delete.d.ts.map +1 -1
- package/dist/src/planner/building/delete.js +7 -4
- package/dist/src/planner/building/delete.js.map +1 -1
- package/dist/src/planner/building/select-aggregates.d.ts.map +1 -1
- package/dist/src/planner/building/select-aggregates.js +4 -13
- package/dist/src/planner/building/select-aggregates.js.map +1 -1
- package/dist/src/planner/building/select-window.d.ts.map +1 -1
- package/dist/src/planner/building/select-window.js +54 -21
- package/dist/src/planner/building/select-window.js.map +1 -1
- package/dist/src/planner/cache/correlation-detector.d.ts +7 -0
- package/dist/src/planner/cache/correlation-detector.d.ts.map +1 -1
- package/dist/src/planner/cache/correlation-detector.js +34 -2
- package/dist/src/planner/cache/correlation-detector.js.map +1 -1
- package/dist/src/planner/framework/physical-utils.d.ts.map +1 -1
- package/dist/src/planner/framework/physical-utils.js +7 -1
- package/dist/src/planner/framework/physical-utils.js.map +1 -1
- package/dist/src/planner/nodes/aggregate-node.d.ts +6 -4
- package/dist/src/planner/nodes/aggregate-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/aggregate-node.js +8 -6
- package/dist/src/planner/nodes/aggregate-node.js.map +1 -1
- package/dist/src/planner/nodes/analyze-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/analyze-node.js +3 -0
- package/dist/src/planner/nodes/analyze-node.js.map +1 -1
- package/dist/src/planner/nodes/async-gather-node.d.ts +169 -0
- package/dist/src/planner/nodes/async-gather-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/async-gather-node.js +488 -0
- package/dist/src/planner/nodes/async-gather-node.js.map +1 -0
- package/dist/src/planner/nodes/bloom-join-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/bloom-join-node.js +8 -7
- package/dist/src/planner/nodes/bloom-join-node.js.map +1 -1
- package/dist/src/planner/nodes/eager-prefetch-node.d.ts +47 -0
- package/dist/src/planner/nodes/eager-prefetch-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/eager-prefetch-node.js +96 -0
- package/dist/src/planner/nodes/eager-prefetch-node.js.map +1 -0
- package/dist/src/planner/nodes/fanout-lookup-join-node.d.ts +150 -0
- package/dist/src/planner/nodes/fanout-lookup-join-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/fanout-lookup-join-node.js +265 -0
- package/dist/src/planner/nodes/fanout-lookup-join-node.js.map +1 -0
- package/dist/src/planner/nodes/hash-aggregate.d.ts.map +1 -1
- package/dist/src/planner/nodes/hash-aggregate.js +6 -16
- package/dist/src/planner/nodes/hash-aggregate.js.map +1 -1
- package/dist/src/planner/nodes/join-utils.d.ts.map +1 -1
- package/dist/src/planner/nodes/join-utils.js +7 -1
- package/dist/src/planner/nodes/join-utils.js.map +1 -1
- package/dist/src/planner/nodes/limit-offset.d.ts +12 -0
- package/dist/src/planner/nodes/limit-offset.d.ts.map +1 -1
- package/dist/src/planner/nodes/limit-offset.js +53 -3
- package/dist/src/planner/nodes/limit-offset.js.map +1 -1
- package/dist/src/planner/nodes/merge-join-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/merge-join-node.js +8 -7
- package/dist/src/planner/nodes/merge-join-node.js.map +1 -1
- package/dist/src/planner/nodes/plan-node-type.d.ts +3 -0
- package/dist/src/planner/nodes/plan-node-type.d.ts.map +1 -1
- package/dist/src/planner/nodes/plan-node-type.js +3 -0
- package/dist/src/planner/nodes/plan-node-type.js.map +1 -1
- package/dist/src/planner/nodes/plan-node.d.ts +36 -0
- package/dist/src/planner/nodes/plan-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/plan-node.js +26 -0
- package/dist/src/planner/nodes/plan-node.js.map +1 -1
- package/dist/src/planner/nodes/project-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/project-node.js +18 -5
- package/dist/src/planner/nodes/project-node.js.map +1 -1
- package/dist/src/planner/nodes/reference.d.ts.map +1 -1
- package/dist/src/planner/nodes/reference.js +14 -3
- package/dist/src/planner/nodes/reference.js.map +1 -1
- package/dist/src/planner/nodes/set-operation-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/set-operation-node.js +12 -1
- package/dist/src/planner/nodes/set-operation-node.js.map +1 -1
- package/dist/src/planner/nodes/sort.js +1 -1
- package/dist/src/planner/nodes/sort.js.map +1 -1
- package/dist/src/planner/nodes/stream-aggregate.d.ts.map +1 -1
- package/dist/src/planner/nodes/stream-aggregate.js +8 -23
- package/dist/src/planner/nodes/stream-aggregate.js.map +1 -1
- package/dist/src/planner/nodes/values-node.d.ts +2 -1
- package/dist/src/planner/nodes/values-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/values-node.js +16 -0
- package/dist/src/planner/nodes/values-node.js.map +1 -1
- package/dist/src/planner/nodes/window-node.js +1 -1
- package/dist/src/planner/nodes/window-node.js.map +1 -1
- package/dist/src/planner/optimizer-tuning.d.ts +107 -0
- package/dist/src/planner/optimizer-tuning.d.ts.map +1 -1
- package/dist/src/planner/optimizer-tuning.js +43 -0
- package/dist/src/planner/optimizer-tuning.js.map +1 -1
- package/dist/src/planner/optimizer.d.ts.map +1 -1
- package/dist/src/planner/optimizer.js +91 -0
- package/dist/src/planner/optimizer.js.map +1 -1
- package/dist/src/planner/rules/access/rule-monotonic-range-access.d.ts.map +1 -1
- package/dist/src/planner/rules/access/rule-monotonic-range-access.js +1 -6
- package/dist/src/planner/rules/access/rule-monotonic-range-access.js.map +1 -1
- package/dist/src/planner/rules/aggregate/rule-aggregate-streaming.d.ts.map +1 -1
- package/dist/src/planner/rules/aggregate/rule-aggregate-streaming.js +8 -27
- package/dist/src/planner/rules/aggregate/rule-aggregate-streaming.js.map +1 -1
- package/dist/src/planner/rules/aggregate/rule-groupby-fd-simplification.d.ts +9 -3
- package/dist/src/planner/rules/aggregate/rule-groupby-fd-simplification.d.ts.map +1 -1
- package/dist/src/planner/rules/aggregate/rule-groupby-fd-simplification.js +47 -5
- package/dist/src/planner/rules/aggregate/rule-groupby-fd-simplification.js.map +1 -1
- package/dist/src/planner/rules/distinct/rule-distinct-elimination.d.ts +8 -7
- package/dist/src/planner/rules/distinct/rule-distinct-elimination.d.ts.map +1 -1
- package/dist/src/planner/rules/distinct/rule-distinct-elimination.js +14 -21
- package/dist/src/planner/rules/distinct/rule-distinct-elimination.js.map +1 -1
- package/dist/src/planner/rules/join/equi-pair-extractor.js +4 -4
- package/dist/src/planner/rules/join/equi-pair-extractor.js.map +1 -1
- package/dist/src/planner/rules/join/rule-fanout-batched-outer.d.ts +74 -0
- package/dist/src/planner/rules/join/rule-fanout-batched-outer.d.ts.map +1 -0
- package/dist/src/planner/rules/join/rule-fanout-batched-outer.js +139 -0
- package/dist/src/planner/rules/join/rule-fanout-batched-outer.js.map +1 -0
- package/dist/src/planner/rules/join/rule-fanout-lookup-join.d.ts +58 -0
- package/dist/src/planner/rules/join/rule-fanout-lookup-join.d.ts.map +1 -0
- package/dist/src/planner/rules/join/rule-fanout-lookup-join.js +590 -0
- package/dist/src/planner/rules/join/rule-fanout-lookup-join.js.map +1 -0
- package/dist/src/planner/rules/join/rule-join-greedy-commute.d.ts.map +1 -1
- package/dist/src/planner/rules/join/rule-join-greedy-commute.js +10 -0
- package/dist/src/planner/rules/join/rule-join-greedy-commute.js.map +1 -1
- package/dist/src/planner/rules/join/rule-join-physical-selection.d.ts.map +1 -1
- package/dist/src/planner/rules/join/rule-join-physical-selection.js +2 -1
- package/dist/src/planner/rules/join/rule-join-physical-selection.js.map +1 -1
- package/dist/src/planner/rules/join/rule-lateral-top1-asof.d.ts.map +1 -1
- package/dist/src/planner/rules/join/rule-lateral-top1-asof.js +1 -2
- package/dist/src/planner/rules/join/rule-lateral-top1-asof.js.map +1 -1
- package/dist/src/planner/rules/parallel/rule-async-gather-union-all.d.ts +43 -0
- package/dist/src/planner/rules/parallel/rule-async-gather-union-all.d.ts.map +1 -0
- package/dist/src/planner/rules/parallel/rule-async-gather-union-all.js +115 -0
- package/dist/src/planner/rules/parallel/rule-async-gather-union-all.js.map +1 -0
- package/dist/src/planner/rules/parallel/rule-async-gather-zip-by-key.d.ts +102 -0
- package/dist/src/planner/rules/parallel/rule-async-gather-zip-by-key.d.ts.map +1 -0
- package/dist/src/planner/rules/parallel/rule-async-gather-zip-by-key.js +545 -0
- package/dist/src/planner/rules/parallel/rule-async-gather-zip-by-key.js.map +1 -0
- package/dist/src/planner/rules/parallel/rule-eager-prefetch-probe.d.ts +45 -0
- package/dist/src/planner/rules/parallel/rule-eager-prefetch-probe.d.ts.map +1 -0
- package/dist/src/planner/rules/parallel/rule-eager-prefetch-probe.js +78 -0
- package/dist/src/planner/rules/parallel/rule-eager-prefetch-probe.js.map +1 -0
- package/dist/src/planner/rules/predicate/rule-aggregate-predicate-pushdown.js +1 -1
- package/dist/src/planner/rules/predicate/rule-aggregate-predicate-pushdown.js.map +1 -1
- package/dist/src/planner/rules/retrieve/rule-grow-retrieve.js +2 -2
- package/dist/src/planner/rules/retrieve/rule-grow-retrieve.js.map +1 -1
- package/dist/src/planner/rules/sort/rule-orderby-fd-pruning.d.ts +16 -0
- package/dist/src/planner/rules/sort/rule-orderby-fd-pruning.d.ts.map +1 -1
- package/dist/src/planner/rules/sort/rule-orderby-fd-pruning.js +47 -4
- package/dist/src/planner/rules/sort/rule-orderby-fd-pruning.js.map +1 -1
- package/dist/src/planner/rules/window/rule-monotonic-window.js +1 -1
- package/dist/src/planner/rules/window/rule-monotonic-window.js.map +1 -1
- package/dist/src/planner/scopes/param.d.ts.map +1 -1
- package/dist/src/planner/scopes/param.js +13 -11
- package/dist/src/planner/scopes/param.js.map +1 -1
- package/dist/src/planner/type-utils.js +1 -1
- package/dist/src/planner/type-utils.js.map +1 -1
- package/dist/src/planner/util/fd-utils.d.ts +59 -1
- package/dist/src/planner/util/fd-utils.d.ts.map +1 -1
- package/dist/src/planner/util/fd-utils.js +122 -0
- package/dist/src/planner/util/fd-utils.js.map +1 -1
- package/dist/src/planner/util/key-utils.d.ts +26 -3
- package/dist/src/planner/util/key-utils.d.ts.map +1 -1
- package/dist/src/planner/util/key-utils.js +113 -33
- package/dist/src/planner/util/key-utils.js.map +1 -1
- package/dist/src/planner/validation/plan-validator.d.ts.map +1 -1
- package/dist/src/planner/validation/plan-validator.js +17 -19
- package/dist/src/planner/validation/plan-validator.js.map +1 -1
- package/dist/src/runtime/async-semaphore.d.ts +36 -0
- package/dist/src/runtime/async-semaphore.d.ts.map +1 -0
- package/dist/src/runtime/async-semaphore.js +72 -0
- package/dist/src/runtime/async-semaphore.js.map +1 -0
- package/dist/src/runtime/deferred-constraint-queue.d.ts.map +1 -1
- package/dist/src/runtime/deferred-constraint-queue.js +4 -3
- package/dist/src/runtime/deferred-constraint-queue.js.map +1 -1
- package/dist/src/runtime/delta-executor.d.ts.map +1 -1
- package/dist/src/runtime/delta-executor.js +9 -0
- package/dist/src/runtime/delta-executor.js.map +1 -1
- package/dist/src/runtime/emit/asof-scan.d.ts.map +1 -1
- package/dist/src/runtime/emit/asof-scan.js +6 -4
- package/dist/src/runtime/emit/asof-scan.js.map +1 -1
- package/dist/src/runtime/emit/async-gather.d.ts +77 -0
- package/dist/src/runtime/emit/async-gather.d.ts.map +1 -0
- package/dist/src/runtime/emit/async-gather.js +234 -0
- package/dist/src/runtime/emit/async-gather.js.map +1 -0
- package/dist/src/runtime/emit/binary.d.ts.map +1 -1
- package/dist/src/runtime/emit/binary.js +19 -27
- package/dist/src/runtime/emit/binary.js.map +1 -1
- package/dist/src/runtime/emit/bloom-join.d.ts.map +1 -1
- package/dist/src/runtime/emit/bloom-join.js +42 -19
- package/dist/src/runtime/emit/bloom-join.js.map +1 -1
- package/dist/src/runtime/emit/constraint-check.d.ts.map +1 -1
- package/dist/src/runtime/emit/constraint-check.js +35 -1
- package/dist/src/runtime/emit/constraint-check.js.map +1 -1
- package/dist/src/runtime/emit/delete.d.ts.map +1 -1
- package/dist/src/runtime/emit/delete.js +15 -5
- package/dist/src/runtime/emit/delete.js.map +1 -1
- package/dist/src/runtime/emit/eager-prefetch.d.ts +77 -0
- package/dist/src/runtime/emit/eager-prefetch.d.ts.map +1 -0
- package/dist/src/runtime/emit/eager-prefetch.js +223 -0
- package/dist/src/runtime/emit/eager-prefetch.js.map +1 -0
- package/dist/src/runtime/emit/fanout-lookup-join.d.ts +130 -0
- package/dist/src/runtime/emit/fanout-lookup-join.d.ts.map +1 -0
- package/dist/src/runtime/emit/fanout-lookup-join.js +521 -0
- package/dist/src/runtime/emit/fanout-lookup-join.js.map +1 -0
- package/dist/src/runtime/emit/merge-join.d.ts.map +1 -1
- package/dist/src/runtime/emit/merge-join.js +4 -2
- package/dist/src/runtime/emit/merge-join.js.map +1 -1
- package/dist/src/runtime/parallel-driver.d.ts +68 -0
- package/dist/src/runtime/parallel-driver.d.ts.map +1 -0
- package/dist/src/runtime/parallel-driver.js +233 -0
- package/dist/src/runtime/parallel-driver.js.map +1 -0
- package/dist/src/runtime/register.d.ts.map +1 -1
- package/dist/src/runtime/register.js +9 -0
- package/dist/src/runtime/register.js.map +1 -1
- package/dist/src/runtime/strict-fork.d.ts +36 -0
- package/dist/src/runtime/strict-fork.d.ts.map +1 -0
- package/dist/src/runtime/strict-fork.js +125 -0
- package/dist/src/runtime/strict-fork.js.map +1 -0
- package/dist/src/types/temporal-types.d.ts.map +1 -1
- package/dist/src/types/temporal-types.js +71 -36
- package/dist/src/types/temporal-types.js.map +1 -1
- package/dist/src/util/comparison.d.ts.map +1 -1
- package/dist/src/util/comparison.js +11 -1
- package/dist/src/util/comparison.js.map +1 -1
- package/dist/src/vtab/concurrency.d.ts +29 -0
- package/dist/src/vtab/concurrency.d.ts.map +1 -0
- package/dist/src/vtab/concurrency.js +47 -0
- package/dist/src/vtab/concurrency.js.map +1 -0
- package/dist/src/vtab/memory/layer/scan-layer.d.ts.map +1 -1
- package/dist/src/vtab/memory/layer/scan-layer.js +67 -29
- package/dist/src/vtab/memory/layer/scan-layer.js.map +1 -1
- package/dist/src/vtab/memory/module.d.ts +21 -0
- package/dist/src/vtab/memory/module.d.ts.map +1 -1
- package/dist/src/vtab/memory/module.js +21 -0
- package/dist/src/vtab/memory/module.js.map +1 -1
- package/dist/src/vtab/module.d.ts +47 -0
- package/dist/src/vtab/module.d.ts.map +1 -1
- package/package.json +4 -3
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createLogger } from '../../../common/logger.js';
|
|
2
2
|
import { DistinctNode } from '../../nodes/distinct-node.js';
|
|
3
|
-
import {
|
|
3
|
+
import { keysOf } from '../../util/fd-utils.js';
|
|
4
4
|
const log = createLogger('optimizer:rule:distinct-elimination');
|
|
5
5
|
/**
|
|
6
6
|
* Rule: DISTINCT Elimination
|
|
@@ -8,30 +8,23 @@ const log = createLogger('optimizer:rule:distinct-elimination');
|
|
|
8
8
|
* When a DistinctNode's source already guarantees unique rows, the DISTINCT is
|
|
9
9
|
* redundant and can be removed.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
11
|
+
* Uniqueness is read through the single `keysOf` surface, which reconciles all
|
|
12
|
+
* three places a uniqueness fact can live (declared `RelationType.keys`, the
|
|
13
|
+
* physical FD set, and `RelationType.isSet`): a non-empty key set ⟺ the source
|
|
14
|
+
* is already a set ⟺ DISTINCT is a no-op. This closes the gap where a
|
|
15
|
+
* `select distinct x, y` (which proves only the all-columns/`isSet` key, not a
|
|
16
|
+
* smaller FD/declared key) was invisible to the FD-only checks — so an outer
|
|
17
|
+
* `select distinct x, y from (select distinct x, y …)` now drops the redundant
|
|
18
|
+
* outer DISTINCT.
|
|
18
19
|
*/
|
|
19
20
|
export function ruleDistinctElimination(node, _context) {
|
|
20
21
|
if (!(node instanceof DistinctNode))
|
|
21
22
|
return null;
|
|
22
|
-
//
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
// Physical FDs: an FD whose determinants form a non-trivial superkey of the
|
|
29
|
-
// source columns proves uniqueness; the singleton `∅ → all_cols` proves
|
|
30
|
-
// at-most-one-row (also unique).
|
|
31
|
-
const sourcePhys = node.source.physical;
|
|
32
|
-
const colCount = node.source.getAttributes().length;
|
|
33
|
-
if (hasAnyKey(sourcePhys?.fds, colCount) || hasSingletonFd(sourcePhys?.fds, colCount)) {
|
|
34
|
-
log('Eliminating redundant DISTINCT: source FDs imply unique rows');
|
|
23
|
+
// A non-empty key set proves the source already produces unique rows. This
|
|
24
|
+
// covers logical keys, FD-derived keys, the at-most-one-row empty key, and
|
|
25
|
+
// the all-columns/`isSet` key — all via the unified surface.
|
|
26
|
+
if (keysOf(node.source).length > 0) {
|
|
27
|
+
log('Eliminating redundant DISTINCT: source has a proven unique key');
|
|
35
28
|
return node.source;
|
|
36
29
|
}
|
|
37
30
|
return null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rule-distinct-elimination.js","sourceRoot":"","sources":["../../../../../src/planner/rules/distinct/rule-distinct-elimination.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAGzD,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"rule-distinct-elimination.js","sourceRoot":"","sources":["../../../../../src/planner/rules/distinct/rule-distinct-elimination.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAGzD,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAEhD,MAAM,GAAG,GAAG,YAAY,CAAC,qCAAqC,CAAC,CAAC;AAEhE;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,uBAAuB,CAAC,IAAc,EAAE,QAAoB;IAC3E,IAAI,CAAC,CAAC,IAAI,YAAY,YAAY,CAAC;QAAE,OAAO,IAAI,CAAC;IAEjD,2EAA2E;IAC3E,2EAA2E;IAC3E,6DAA6D;IAC7D,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpC,GAAG,CAAC,gEAAgE,CAAC,CAAC;QACtE,OAAO,IAAI,CAAC,MAAM,CAAC;IACpB,CAAC;IAED,OAAO,IAAI,CAAC;AACb,CAAC"}
|
|
@@ -20,10 +20,10 @@ export function isOrderedOnEquiPairs(source, equiPairs, side) {
|
|
|
20
20
|
return false;
|
|
21
21
|
if (equiPairs.length > ordering.length)
|
|
22
22
|
return false;
|
|
23
|
-
const
|
|
23
|
+
const attrIndex = source.getAttributeIndex();
|
|
24
24
|
for (let i = 0; i < equiPairs.length; i++) {
|
|
25
25
|
const attrId = side === 'left' ? equiPairs[i].leftAttrId : equiPairs[i].rightAttrId;
|
|
26
|
-
const idx =
|
|
26
|
+
const idx = attrIndex.get(attrId) ?? -1;
|
|
27
27
|
if (idx === -1)
|
|
28
28
|
return false;
|
|
29
29
|
if (ordering[i].column !== idx || ordering[i].desc)
|
|
@@ -40,10 +40,10 @@ export function reorderEquiPairsForMerge(equiPairs, left, right) {
|
|
|
40
40
|
const leftOrdering = PlanNodeCharacteristics.getOrdering(left);
|
|
41
41
|
if (!leftOrdering || leftOrdering.length < equiPairs.length)
|
|
42
42
|
return null;
|
|
43
|
-
const
|
|
43
|
+
const leftAttrIndex = left.getAttributeIndex();
|
|
44
44
|
const colToEqIdx = new Map();
|
|
45
45
|
for (let i = 0; i < equiPairs.length; i++) {
|
|
46
|
-
const attrIdx =
|
|
46
|
+
const attrIdx = leftAttrIndex.get(equiPairs[i].leftAttrId) ?? -1;
|
|
47
47
|
if (attrIdx === -1)
|
|
48
48
|
return null;
|
|
49
49
|
colToEqIdx.set(attrIdx, i);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"equi-pair-extractor.js","sourceRoot":"","sources":["../../../../../src/planner/rules/join/equi-pair-extractor.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,wCAAwC,CAAC;AAC5E,OAAO,EAAE,uBAAuB,EAAE,MAAM,oCAAoC,CAAC;AAgB7E;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CACnC,MAA0B,EAC1B,SAAkC,EAClC,IAAsB;IAEtB,MAAM,QAAQ,GAAG,uBAAuB,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IAC7D,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACrD,IAAI,SAAS,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAErD,MAAM,
|
|
1
|
+
{"version":3,"file":"equi-pair-extractor.js","sourceRoot":"","sources":["../../../../../src/planner/rules/join/equi-pair-extractor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule: Fan-out Batched Outer
|
|
3
|
+
*
|
|
4
|
+
* Flips an already-formed `FanOutLookupJoinNode` from the default `serial` outer
|
|
5
|
+
* mode to `batched` (cross-row pipelined) when the cost model says overlapping
|
|
6
|
+
* lookups *across* outer rows pays off. This is the recognition rule that
|
|
7
|
+
* *chooses* the batched runtime (`runFanOutLookupJoinBatched`); the runtime
|
|
8
|
+
* itself landed in `parallel-fanout-lookup-join-batched-outer`.
|
|
9
|
+
*
|
|
10
|
+
* **Why a post-pass, not a formation-time decision.** `rule-fanout-lookup-join`
|
|
11
|
+
* forms the node in `PassId.Structural`; this rule runs in `PassId.Post-
|
|
12
|
+
* Optimization` (priority 16, in the `eager-prefetch-probe` / `async-gather`
|
|
13
|
+
* neighborhood) after physical-pass selection has finalized leaf
|
|
14
|
+
* `expectedLatencyMs` / `estimatedRows` / `concurrencySafe`. Matching the
|
|
15
|
+
* already-built `FanOutLookupJoinNode` keeps the batched decision a single,
|
|
16
|
+
* isolated rewrite rather than another recognition path.
|
|
17
|
+
*
|
|
18
|
+
* **When batched wins.** Batched mode helps when there are *many outer rows but
|
|
19
|
+
* few branches per row* — the per-row branch count under-saturates the global
|
|
20
|
+
* in-flight budget, so admitting more outer rows ahead of the emit frontier is
|
|
21
|
+
* the only way to fill it. All of the following must hold:
|
|
22
|
+
*
|
|
23
|
+
* - **Budget under-saturated per row:** `branchCount < outerBatchConcurrency`.
|
|
24
|
+
* When a single row's branches already meet/exceed the global budget,
|
|
25
|
+
* cross-row admission buys nothing (the budget is full from one row).
|
|
26
|
+
* - **High-latency branches:** the slowest branch's `expectedLatencyMs` clears
|
|
27
|
+
* `tuning.parallel.batchedOuterThresholdMs`. This is 0 on every memory-vtab
|
|
28
|
+
* leaf, so the rule is **inert by design on local-only plans** — the golden
|
|
29
|
+
* sweep is unaffected, same discipline as `gatherThresholdMs` /
|
|
30
|
+
* `prefetchProbeThresholdMs`.
|
|
31
|
+
* - **Large outer cardinality:** `outer.estimatedRows >= batchedOuterMinRows`,
|
|
32
|
+
* so cross-row overlap dominates the reorder-buffer + per-row-fork overhead.
|
|
33
|
+
* An unknown estimate fails the gate (never flip on a missing statistic).
|
|
34
|
+
*
|
|
35
|
+
* **Cross branches are out of scope.** A cross (1:n) branch's batched outer
|
|
36
|
+
* mode is owned by `parallel-fanout-lookup-join-cross-mode`; this rule only
|
|
37
|
+
* flips clusters whose branches are all `atMostOne-*`. A node carrying any
|
|
38
|
+
* `cross` or `cross-left` branch is left serial (both are 1:n cross factors).
|
|
39
|
+
*
|
|
40
|
+
* **Outer-source isolation (load-bearing correctness).** The batched driver
|
|
41
|
+
* pumps the outer source *concurrently* with in-flight per-row branch forks —
|
|
42
|
+
* unlike serial mode, which fully resolves one outer row before pulling the
|
|
43
|
+
* next. The scheduler runs every instruction against one shared
|
|
44
|
+
* `RuntimeContext`, so a raw outer sub-plan that mutates `rctx.context` during
|
|
45
|
+
* the pump (installing a row slot, etc.) would (a) risk a torn read for any
|
|
46
|
+
* branch reading that entry and (b) throw a strict-fork violation when the
|
|
47
|
+
* fan-out is nested under another fork (so `rctx.context` is strict-wrapped) and
|
|
48
|
+
* the live row forks hold the bump counter. To neutralize both, this rule wraps
|
|
49
|
+
* the outer in an `EagerPrefetchNode` when it flips to batched: the prefetch
|
|
50
|
+
* pump runs the outer sub-plan against its *own* forked context (mutations land
|
|
51
|
+
* on the fork, never on the shared `rctx.context` the row forks bump), and the
|
|
52
|
+
* batched pump merely drains the prefetch buffer — a pure buffer read that never
|
|
53
|
+
* touches `rctx.context`. So **batched implies prefetch** (the reverse does not
|
|
54
|
+
* hold; `eager-prefetch-probe` uses the node independently). The prefetch buffer
|
|
55
|
+
* also feeds the read-ahead window the batched driver consumes across rows, so
|
|
56
|
+
* the two compose rather than duplicate work. The branch correlations are
|
|
57
|
+
* already safe by construction: `rule-fanout-lookup-join` only clusters branches
|
|
58
|
+
* (spine + correlated scalar-aggregate subqueries) that reference the *outer
|
|
59
|
+
* row's* attributes, which the batched driver isolates per row in its own boxed
|
|
60
|
+
* slot.
|
|
61
|
+
*
|
|
62
|
+
* **Outer concurrency gate.** Because the prefetch pump iterates the outer
|
|
63
|
+
* concurrently with branch lookups, the outer must advertise
|
|
64
|
+
* `physical.concurrencySafe === true` (mirroring `eager-prefetch-probe` /
|
|
65
|
+
* `async-gather`). Serial mode never overlapped these, so this gate is
|
|
66
|
+
* batched-specific.
|
|
67
|
+
*
|
|
68
|
+
* **Idempotence.** After the rewrite `outerMode === 'batched'`, so a second
|
|
69
|
+
* firing returns null immediately.
|
|
70
|
+
*/
|
|
71
|
+
import type { OptContext } from '../../framework/context.js';
|
|
72
|
+
import type { PlanNode } from '../../nodes/plan-node.js';
|
|
73
|
+
export declare function ruleFanOutBatchedOuter(node: PlanNode, context: OptContext): PlanNode | null;
|
|
74
|
+
//# sourceMappingURL=rule-fanout-batched-outer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule-fanout-batched-outer.d.ts","sourceRoot":"","sources":["../../../../../src/planner/rules/join/rule-fanout-batched-outer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqEG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,KAAK,EAAE,QAAQ,EAAsB,MAAM,0BAA0B,CAAC;AAyB7E,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,GAAG,QAAQ,GAAG,IAAI,CAsD3F"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule: Fan-out Batched Outer
|
|
3
|
+
*
|
|
4
|
+
* Flips an already-formed `FanOutLookupJoinNode` from the default `serial` outer
|
|
5
|
+
* mode to `batched` (cross-row pipelined) when the cost model says overlapping
|
|
6
|
+
* lookups *across* outer rows pays off. This is the recognition rule that
|
|
7
|
+
* *chooses* the batched runtime (`runFanOutLookupJoinBatched`); the runtime
|
|
8
|
+
* itself landed in `parallel-fanout-lookup-join-batched-outer`.
|
|
9
|
+
*
|
|
10
|
+
* **Why a post-pass, not a formation-time decision.** `rule-fanout-lookup-join`
|
|
11
|
+
* forms the node in `PassId.Structural`; this rule runs in `PassId.Post-
|
|
12
|
+
* Optimization` (priority 16, in the `eager-prefetch-probe` / `async-gather`
|
|
13
|
+
* neighborhood) after physical-pass selection has finalized leaf
|
|
14
|
+
* `expectedLatencyMs` / `estimatedRows` / `concurrencySafe`. Matching the
|
|
15
|
+
* already-built `FanOutLookupJoinNode` keeps the batched decision a single,
|
|
16
|
+
* isolated rewrite rather than another recognition path.
|
|
17
|
+
*
|
|
18
|
+
* **When batched wins.** Batched mode helps when there are *many outer rows but
|
|
19
|
+
* few branches per row* — the per-row branch count under-saturates the global
|
|
20
|
+
* in-flight budget, so admitting more outer rows ahead of the emit frontier is
|
|
21
|
+
* the only way to fill it. All of the following must hold:
|
|
22
|
+
*
|
|
23
|
+
* - **Budget under-saturated per row:** `branchCount < outerBatchConcurrency`.
|
|
24
|
+
* When a single row's branches already meet/exceed the global budget,
|
|
25
|
+
* cross-row admission buys nothing (the budget is full from one row).
|
|
26
|
+
* - **High-latency branches:** the slowest branch's `expectedLatencyMs` clears
|
|
27
|
+
* `tuning.parallel.batchedOuterThresholdMs`. This is 0 on every memory-vtab
|
|
28
|
+
* leaf, so the rule is **inert by design on local-only plans** — the golden
|
|
29
|
+
* sweep is unaffected, same discipline as `gatherThresholdMs` /
|
|
30
|
+
* `prefetchProbeThresholdMs`.
|
|
31
|
+
* - **Large outer cardinality:** `outer.estimatedRows >= batchedOuterMinRows`,
|
|
32
|
+
* so cross-row overlap dominates the reorder-buffer + per-row-fork overhead.
|
|
33
|
+
* An unknown estimate fails the gate (never flip on a missing statistic).
|
|
34
|
+
*
|
|
35
|
+
* **Cross branches are out of scope.** A cross (1:n) branch's batched outer
|
|
36
|
+
* mode is owned by `parallel-fanout-lookup-join-cross-mode`; this rule only
|
|
37
|
+
* flips clusters whose branches are all `atMostOne-*`. A node carrying any
|
|
38
|
+
* `cross` or `cross-left` branch is left serial (both are 1:n cross factors).
|
|
39
|
+
*
|
|
40
|
+
* **Outer-source isolation (load-bearing correctness).** The batched driver
|
|
41
|
+
* pumps the outer source *concurrently* with in-flight per-row branch forks —
|
|
42
|
+
* unlike serial mode, which fully resolves one outer row before pulling the
|
|
43
|
+
* next. The scheduler runs every instruction against one shared
|
|
44
|
+
* `RuntimeContext`, so a raw outer sub-plan that mutates `rctx.context` during
|
|
45
|
+
* the pump (installing a row slot, etc.) would (a) risk a torn read for any
|
|
46
|
+
* branch reading that entry and (b) throw a strict-fork violation when the
|
|
47
|
+
* fan-out is nested under another fork (so `rctx.context` is strict-wrapped) and
|
|
48
|
+
* the live row forks hold the bump counter. To neutralize both, this rule wraps
|
|
49
|
+
* the outer in an `EagerPrefetchNode` when it flips to batched: the prefetch
|
|
50
|
+
* pump runs the outer sub-plan against its *own* forked context (mutations land
|
|
51
|
+
* on the fork, never on the shared `rctx.context` the row forks bump), and the
|
|
52
|
+
* batched pump merely drains the prefetch buffer — a pure buffer read that never
|
|
53
|
+
* touches `rctx.context`. So **batched implies prefetch** (the reverse does not
|
|
54
|
+
* hold; `eager-prefetch-probe` uses the node independently). The prefetch buffer
|
|
55
|
+
* also feeds the read-ahead window the batched driver consumes across rows, so
|
|
56
|
+
* the two compose rather than duplicate work. The branch correlations are
|
|
57
|
+
* already safe by construction: `rule-fanout-lookup-join` only clusters branches
|
|
58
|
+
* (spine + correlated scalar-aggregate subqueries) that reference the *outer
|
|
59
|
+
* row's* attributes, which the batched driver isolates per row in its own boxed
|
|
60
|
+
* slot.
|
|
61
|
+
*
|
|
62
|
+
* **Outer concurrency gate.** Because the prefetch pump iterates the outer
|
|
63
|
+
* concurrently with branch lookups, the outer must advertise
|
|
64
|
+
* `physical.concurrencySafe === true` (mirroring `eager-prefetch-probe` /
|
|
65
|
+
* `async-gather`). Serial mode never overlapped these, so this gate is
|
|
66
|
+
* batched-specific.
|
|
67
|
+
*
|
|
68
|
+
* **Idempotence.** After the rewrite `outerMode === 'batched'`, so a second
|
|
69
|
+
* firing returns null immediately.
|
|
70
|
+
*/
|
|
71
|
+
import { createLogger } from '../../../common/logger.js';
|
|
72
|
+
import { PlanNodeType } from '../../nodes/plan-node-type.js';
|
|
73
|
+
import { FanOutLookupJoinNode, isCrossBranchMode } from '../../nodes/fanout-lookup-join-node.js';
|
|
74
|
+
import { EagerPrefetchNode } from '../../nodes/eager-prefetch-node.js';
|
|
75
|
+
const log = createLogger('optimizer:rule:fanout-batched-outer');
|
|
76
|
+
/**
|
|
77
|
+
* Best-available row estimate for the fan-out's outer. The leaf access node
|
|
78
|
+
* carries `physical.estimatedRows` but several pass-through wrappers (notably
|
|
79
|
+
* `AliasNode`) propagate it via the `.estimatedRows` getter, which the leaves do
|
|
80
|
+
* not all populate — so a value can be present on the leaf's `physical` yet
|
|
81
|
+
* undefined on the wrapper above it. Read the node's own estimate first, then
|
|
82
|
+
* descend single-relation pass-throughs (alias/filter/sort/…) to recover the
|
|
83
|
+
* leaf's estimate. A multi-relation node (a join outer in a subquery cluster)
|
|
84
|
+
* returns `undefined`, which the caller treats as failing the cardinality gate.
|
|
85
|
+
*/
|
|
86
|
+
function outerRowEstimate(node) {
|
|
87
|
+
const direct = node.physical?.estimatedRows ?? node.estimatedRows;
|
|
88
|
+
if (direct !== undefined)
|
|
89
|
+
return direct;
|
|
90
|
+
const relations = node.getRelations();
|
|
91
|
+
if (relations.length === 1)
|
|
92
|
+
return outerRowEstimate(relations[0]);
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
export function ruleFanOutBatchedOuter(node, context) {
|
|
96
|
+
if (!(node instanceof FanOutLookupJoinNode))
|
|
97
|
+
return null;
|
|
98
|
+
if (node.outerMode === 'batched')
|
|
99
|
+
return null; // idempotence
|
|
100
|
+
const tuning = context.tuning.parallel;
|
|
101
|
+
const branchCount = node.branches.length;
|
|
102
|
+
// Cross-branch batched outer is owned by the cross-mode ticket; only flip
|
|
103
|
+
// clusters whose branches are all at-most-one. `cross-left` is a 1:n cross
|
|
104
|
+
// factor too, so it is excluded on the same grounds as `cross`.
|
|
105
|
+
if (node.branches.some(b => isCrossBranchMode(b.mode)))
|
|
106
|
+
return null;
|
|
107
|
+
// Budget under-saturation: batched only helps when one row's branches leave
|
|
108
|
+
// global-budget headroom for *more* outer rows to fill.
|
|
109
|
+
if (branchCount >= tuning.outerBatchConcurrency)
|
|
110
|
+
return null;
|
|
111
|
+
// Latency gate: the slowest branch must clear the threshold. Inert on
|
|
112
|
+
// memory-vtab plans (expectedLatencyMs = 0 throughout).
|
|
113
|
+
let maxLatency = 0;
|
|
114
|
+
for (const b of node.branches) {
|
|
115
|
+
const l = b.child.physical.expectedLatencyMs ?? 0;
|
|
116
|
+
if (l > maxLatency)
|
|
117
|
+
maxLatency = l;
|
|
118
|
+
}
|
|
119
|
+
if (maxLatency < tuning.batchedOuterThresholdMs)
|
|
120
|
+
return null;
|
|
121
|
+
// Cardinality gate: enough outer rows for cross-row overlap to amortize the
|
|
122
|
+
// reorder-buffer / per-row-fork overhead. Unknown estimate fails the gate.
|
|
123
|
+
const outerRows = outerRowEstimate(node.outer);
|
|
124
|
+
if (outerRows === undefined || outerRows < tuning.batchedOuterMinRows)
|
|
125
|
+
return null;
|
|
126
|
+
// Concurrency gate: the prefetch pump iterates the outer concurrently with
|
|
127
|
+
// branch lookups, so the outer must be proven concurrency-safe.
|
|
128
|
+
if (node.outer.physical.concurrencySafe !== true)
|
|
129
|
+
return null;
|
|
130
|
+
// Wrap the outer in EagerPrefetch (isolation + read-ahead feed) unless it is
|
|
131
|
+
// already prefetched. Sized to `maxOuterReadAhead` — the outer-read-ahead
|
|
132
|
+
// bound this node's batched driver works against.
|
|
133
|
+
const outer = node.outer.nodeType === PlanNodeType.EagerPrefetch
|
|
134
|
+
? node.outer
|
|
135
|
+
: new EagerPrefetchNode(node.scope, node.outer, tuning.maxOuterReadAhead);
|
|
136
|
+
log('Flipping FanOutLookupJoin %s to batched outer mode (branches=%d, maxLatency=%d ms, outerRows=%s, globalCap=%d)', node.id, branchCount, maxLatency, String(outerRows), tuning.outerBatchConcurrency);
|
|
137
|
+
return new FanOutLookupJoinNode(node.scope, outer, node.branches, node.concurrencyCap, node.preserveAttributeIds, 'batched');
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=rule-fanout-batched-outer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule-fanout-batched-outer.js","sourceRoot":"","sources":["../../../../../src/planner/rules/join/rule-fanout-batched-outer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
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 type { OptContext } from '../../framework/context.js';
|
|
56
|
+
import { type PlanNode } from '../../nodes/plan-node.js';
|
|
57
|
+
export declare function ruleFanOutLookupJoin(node: PlanNode, context: OptContext): PlanNode | null;
|
|
58
|
+
//# sourceMappingURL=rule-fanout-lookup-join.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule-fanout-lookup-join.d.ts","sourceRoot":"","sources":["../../../../../src/planner/rules/join/rule-fanout-lookup-join.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAGN,KAAK,QAAQ,EAGb,MAAM,0BAA0B,CAAC;AA4DlC,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,GAAG,QAAQ,GAAG,IAAI,CAgOzF"}
|