@quereus/quereus 4.4.1 → 4.6.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/README.md +5 -4
- package/dist/src/core/database-events.d.ts +99 -1
- package/dist/src/core/database-events.d.ts.map +1 -1
- package/dist/src/core/database-events.js +219 -3
- package/dist/src/core/database-events.js.map +1 -1
- package/dist/src/core/database-materialized-views-analysis.d.ts.map +1 -1
- package/dist/src/core/database-materialized-views-analysis.js +1 -0
- package/dist/src/core/database-materialized-views-analysis.js.map +1 -1
- package/dist/src/core/database-materialized-views-plan-builders.d.ts.map +1 -1
- package/dist/src/core/database-materialized-views-plan-builders.js +29 -2
- package/dist/src/core/database-materialized-views-plan-builders.js.map +1 -1
- package/dist/src/core/database-transaction.d.ts +9 -0
- package/dist/src/core/database-transaction.d.ts.map +1 -1
- package/dist/src/core/database-transaction.js +9 -0
- package/dist/src/core/database-transaction.js.map +1 -1
- package/dist/src/core/database.d.ts +7 -1
- package/dist/src/core/database.d.ts.map +1 -1
- package/dist/src/core/database.js +20 -5
- package/dist/src/core/database.js.map +1 -1
- package/dist/src/func/builtins/aggregate.js +11 -11
- package/dist/src/func/builtins/aggregate.js.map +1 -1
- package/dist/src/func/builtins/builtin-window-functions.d.ts.map +1 -1
- package/dist/src/func/builtins/builtin-window-functions.js +15 -85
- package/dist/src/func/builtins/builtin-window-functions.js.map +1 -1
- package/dist/src/func/builtins/conversion.d.ts.map +1 -1
- package/dist/src/func/builtins/conversion.js +10 -9
- package/dist/src/func/builtins/conversion.js.map +1 -1
- package/dist/src/func/builtins/datetime.d.ts.map +1 -1
- package/dist/src/func/builtins/datetime.js +28 -10
- package/dist/src/func/builtins/datetime.js.map +1 -1
- package/dist/src/func/builtins/json.d.ts.map +1 -1
- package/dist/src/func/builtins/json.js +23 -17
- package/dist/src/func/builtins/json.js.map +1 -1
- package/dist/src/func/builtins/mutation.js +2 -2
- package/dist/src/func/builtins/mutation.js.map +1 -1
- package/dist/src/func/builtins/return-types.d.ts +327 -0
- package/dist/src/func/builtins/return-types.d.ts.map +1 -0
- package/dist/src/func/builtins/return-types.js +43 -0
- package/dist/src/func/builtins/return-types.js.map +1 -0
- package/dist/src/func/builtins/scalar.d.ts.map +1 -1
- package/dist/src/func/builtins/scalar.js +193 -37
- package/dist/src/func/builtins/scalar.js.map +1 -1
- package/dist/src/func/builtins/schema.d.ts.map +1 -1
- package/dist/src/func/builtins/schema.js +11 -3
- package/dist/src/func/builtins/schema.js.map +1 -1
- package/dist/src/func/builtins/string.d.ts.map +1 -1
- package/dist/src/func/builtins/string.js +4 -2
- package/dist/src/func/builtins/string.js.map +1 -1
- package/dist/src/func/builtins/timespan.d.ts +3 -0
- package/dist/src/func/builtins/timespan.d.ts.map +1 -1
- package/dist/src/func/builtins/timespan.js +15 -11
- package/dist/src/func/builtins/timespan.js.map +1 -1
- package/dist/src/func/registration.d.ts +39 -1
- package/dist/src/func/registration.d.ts.map +1 -1
- package/dist/src/func/registration.js +236 -41
- package/dist/src/func/registration.js.map +1 -1
- package/dist/src/index.d.ts +14 -7
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +14 -5
- package/dist/src/index.js.map +1 -1
- package/dist/src/parser/parser.d.ts +46 -0
- package/dist/src/parser/parser.d.ts.map +1 -1
- package/dist/src/parser/parser.js +160 -44
- package/dist/src/parser/parser.js.map +1 -1
- package/dist/src/planner/analysis/check-extraction.d.ts.map +1 -1
- package/dist/src/planner/analysis/check-extraction.js +37 -4
- package/dist/src/planner/analysis/check-extraction.js.map +1 -1
- package/dist/src/planner/analysis/coarsened-key.d.ts +4 -3
- package/dist/src/planner/analysis/coarsened-key.d.ts.map +1 -1
- package/dist/src/planner/analysis/coarsened-key.js +37 -3
- package/dist/src/planner/analysis/coarsened-key.js.map +1 -1
- package/dist/src/planner/analysis/comparison-collation.d.ts +41 -13
- package/dist/src/planner/analysis/comparison-collation.d.ts.map +1 -1
- package/dist/src/planner/analysis/comparison-collation.js +51 -14
- package/dist/src/planner/analysis/comparison-collation.js.map +1 -1
- package/dist/src/planner/analysis/constraint-extractor.d.ts +0 -7
- package/dist/src/planner/analysis/constraint-extractor.d.ts.map +1 -1
- package/dist/src/planner/analysis/constraint-extractor.js +59 -59
- package/dist/src/planner/analysis/constraint-extractor.js.map +1 -1
- package/dist/src/planner/analysis/coverage-prover.d.ts +13 -2
- package/dist/src/planner/analysis/coverage-prover.d.ts.map +1 -1
- package/dist/src/planner/analysis/coverage-prover.js +10 -14
- package/dist/src/planner/analysis/coverage-prover.js.map +1 -1
- package/dist/src/planner/analysis/predicate-conjuncts.d.ts +10 -4
- package/dist/src/planner/analysis/predicate-conjuncts.d.ts.map +1 -1
- package/dist/src/planner/analysis/predicate-conjuncts.js +28 -7
- package/dist/src/planner/analysis/predicate-conjuncts.js.map +1 -1
- package/dist/src/planner/analysis/query-rewrite-matcher.d.ts.map +1 -1
- package/dist/src/planner/analysis/query-rewrite-matcher.js +8 -27
- package/dist/src/planner/analysis/query-rewrite-matcher.js.map +1 -1
- package/dist/src/planner/analysis/scalar-subqueries.d.ts +22 -3
- package/dist/src/planner/analysis/scalar-subqueries.d.ts.map +1 -1
- package/dist/src/planner/analysis/scalar-subqueries.js +32 -3
- package/dist/src/planner/analysis/scalar-subqueries.js.map +1 -1
- package/dist/src/planner/analysis/set-op-type-merge.d.ts +64 -0
- package/dist/src/planner/analysis/set-op-type-merge.d.ts.map +1 -0
- package/dist/src/planner/analysis/set-op-type-merge.js +97 -0
- package/dist/src/planner/analysis/set-op-type-merge.js.map +1 -0
- package/dist/src/planner/building/alter-table.d.ts.map +1 -1
- package/dist/src/planner/building/alter-table.js +65 -27
- package/dist/src/planner/building/alter-table.js.map +1 -1
- package/dist/src/planner/building/coercion.d.ts +54 -0
- package/dist/src/planner/building/coercion.d.ts.map +1 -0
- package/dist/src/planner/building/coercion.js +88 -0
- package/dist/src/planner/building/coercion.js.map +1 -0
- package/dist/src/planner/building/constraint-builder.d.ts.map +1 -1
- package/dist/src/planner/building/constraint-builder.js +5 -18
- package/dist/src/planner/building/constraint-builder.js.map +1 -1
- package/dist/src/planner/building/expression.d.ts +17 -0
- package/dist/src/planner/building/expression.d.ts.map +1 -1
- package/dist/src/planner/building/expression.js +23 -115
- package/dist/src/planner/building/expression.js.map +1 -1
- package/dist/src/planner/building/function-call.d.ts.map +1 -1
- package/dist/src/planner/building/function-call.js +13 -0
- package/dist/src/planner/building/function-call.js.map +1 -1
- package/dist/src/planner/building/select-aggregates.d.ts +1 -1
- package/dist/src/planner/building/select-aggregates.d.ts.map +1 -1
- package/dist/src/planner/building/select-aggregates.js +53 -2
- package/dist/src/planner/building/select-aggregates.js.map +1 -1
- package/dist/src/planner/building/select-compound.d.ts.map +1 -1
- package/dist/src/planner/building/select-compound.js +10 -6
- package/dist/src/planner/building/select-compound.js.map +1 -1
- package/dist/src/planner/building/select-context.d.ts +7 -0
- package/dist/src/planner/building/select-context.d.ts.map +1 -1
- package/dist/src/planner/building/select-context.js +10 -40
- package/dist/src/planner/building/select-context.js.map +1 -1
- package/dist/src/planner/building/select.d.ts +37 -1
- package/dist/src/planner/building/select.d.ts.map +1 -1
- package/dist/src/planner/building/select.js +112 -51
- package/dist/src/planner/building/select.js.map +1 -1
- package/dist/src/planner/cost/conjunct-cost.d.ts +82 -0
- package/dist/src/planner/cost/conjunct-cost.d.ts.map +1 -0
- package/dist/src/planner/cost/conjunct-cost.js +107 -0
- package/dist/src/planner/cost/conjunct-cost.js.map +1 -0
- package/dist/src/planner/cost/index.d.ts +10 -0
- package/dist/src/planner/cost/index.d.ts.map +1 -1
- package/dist/src/planner/cost/index.js +15 -0
- package/dist/src/planner/cost/index.js.map +1 -1
- package/dist/src/planner/mutation/propagate.d.ts.map +1 -1
- package/dist/src/planner/mutation/propagate.js +1 -0
- package/dist/src/planner/mutation/propagate.js.map +1 -1
- package/dist/src/planner/nodes/aggregate-node.d.ts +6 -0
- package/dist/src/planner/nodes/aggregate-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/aggregate-node.js +10 -2
- package/dist/src/planner/nodes/aggregate-node.js.map +1 -1
- package/dist/src/planner/nodes/alias-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/alias-node.js +2 -1
- package/dist/src/planner/nodes/alias-node.js.map +1 -1
- package/dist/src/planner/nodes/alter-table-node.d.ts +24 -8
- package/dist/src/planner/nodes/alter-table-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/alter-table-node.js.map +1 -1
- package/dist/src/planner/nodes/asof-scan-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/asof-scan-node.js +4 -1
- package/dist/src/planner/nodes/asof-scan-node.js.map +1 -1
- package/dist/src/planner/nodes/asserted-keys-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/asserted-keys-node.js +2 -1
- package/dist/src/planner/nodes/asserted-keys-node.js.map +1 -1
- package/dist/src/planner/nodes/async-gather-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/async-gather-node.js +19 -8
- package/dist/src/planner/nodes/async-gather-node.js.map +1 -1
- package/dist/src/planner/nodes/bloom-join-node.d.ts +2 -0
- package/dist/src/planner/nodes/bloom-join-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/bloom-join-node.js +17 -7
- package/dist/src/planner/nodes/bloom-join-node.js.map +1 -1
- package/dist/src/planner/nodes/cache-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/cache-node.js +2 -1
- package/dist/src/planner/nodes/cache-node.js.map +1 -1
- package/dist/src/planner/nodes/distinct-node.d.ts +6 -0
- package/dist/src/planner/nodes/distinct-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/distinct-node.js +10 -2
- package/dist/src/planner/nodes/distinct-node.js.map +1 -1
- package/dist/src/planner/nodes/eager-prefetch-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/eager-prefetch-node.js +2 -1
- package/dist/src/planner/nodes/eager-prefetch-node.js.map +1 -1
- package/dist/src/planner/nodes/fanout-lookup-join-node.d.ts +5 -0
- package/dist/src/planner/nodes/fanout-lookup-join-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/fanout-lookup-join-node.js +13 -6
- package/dist/src/planner/nodes/fanout-lookup-join-node.js.map +1 -1
- package/dist/src/planner/nodes/filter.d.ts.map +1 -1
- package/dist/src/planner/nodes/filter.js +12 -5
- package/dist/src/planner/nodes/filter.js.map +1 -1
- package/dist/src/planner/nodes/hash-aggregate.d.ts +6 -0
- package/dist/src/planner/nodes/hash-aggregate.d.ts.map +1 -1
- package/dist/src/planner/nodes/hash-aggregate.js +10 -2
- package/dist/src/planner/nodes/hash-aggregate.js.map +1 -1
- package/dist/src/planner/nodes/join-node.d.ts +25 -1
- package/dist/src/planner/nodes/join-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/join-node.js +38 -10
- package/dist/src/planner/nodes/join-node.js.map +1 -1
- package/dist/src/planner/nodes/join-utils.d.ts +78 -0
- package/dist/src/planner/nodes/join-utils.d.ts.map +1 -1
- package/dist/src/planner/nodes/join-utils.js +73 -0
- package/dist/src/planner/nodes/join-utils.js.map +1 -1
- package/dist/src/planner/nodes/key-set-semi-join-node.d.ts +87 -0
- package/dist/src/planner/nodes/key-set-semi-join-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/key-set-semi-join-node.js +141 -0
- package/dist/src/planner/nodes/key-set-semi-join-node.js.map +1 -0
- package/dist/src/planner/nodes/lens-auxiliary-access-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/lens-auxiliary-access-node.js +2 -1
- package/dist/src/planner/nodes/lens-auxiliary-access-node.js.map +1 -1
- package/dist/src/planner/nodes/limit-offset.d.ts +6 -0
- package/dist/src/planner/nodes/limit-offset.d.ts.map +1 -1
- package/dist/src/planner/nodes/limit-offset.js +10 -2
- package/dist/src/planner/nodes/limit-offset.js.map +1 -1
- package/dist/src/planner/nodes/merge-join-node.d.ts +2 -0
- package/dist/src/planner/nodes/merge-join-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/merge-join-node.js +21 -8
- package/dist/src/planner/nodes/merge-join-node.js.map +1 -1
- package/dist/src/planner/nodes/ordinal-slice-node.d.ts +5 -0
- package/dist/src/planner/nodes/ordinal-slice-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/ordinal-slice-node.js +9 -2
- package/dist/src/planner/nodes/ordinal-slice-node.js.map +1 -1
- package/dist/src/planner/nodes/plan-node-type.d.ts +1 -0
- package/dist/src/planner/nodes/plan-node-type.d.ts.map +1 -1
- package/dist/src/planner/nodes/plan-node-type.js +1 -0
- package/dist/src/planner/nodes/plan-node-type.js.map +1 -1
- package/dist/src/planner/nodes/plan-node.d.ts +24 -0
- package/dist/src/planner/nodes/plan-node.d.ts.map +1 -1
- 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 +2 -1
- 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 +3 -2
- package/dist/src/planner/nodes/reference.js.map +1 -1
- package/dist/src/planner/nodes/retrieve-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/retrieve-node.js +2 -1
- package/dist/src/planner/nodes/retrieve-node.js.map +1 -1
- package/dist/src/planner/nodes/scalar.d.ts.map +1 -1
- package/dist/src/planner/nodes/scalar.js +5 -18
- package/dist/src/planner/nodes/scalar.js.map +1 -1
- package/dist/src/planner/nodes/set-operation-node.d.ts +68 -16
- package/dist/src/planner/nodes/set-operation-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/set-operation-node.js +147 -26
- package/dist/src/planner/nodes/set-operation-node.js.map +1 -1
- package/dist/src/planner/nodes/sort.d.ts.map +1 -1
- package/dist/src/planner/nodes/sort.js +3 -1
- package/dist/src/planner/nodes/sort.js.map +1 -1
- package/dist/src/planner/nodes/stream-aggregate.d.ts +6 -0
- package/dist/src/planner/nodes/stream-aggregate.d.ts.map +1 -1
- package/dist/src/planner/nodes/stream-aggregate.js +10 -2
- package/dist/src/planner/nodes/stream-aggregate.js.map +1 -1
- package/dist/src/planner/nodes/table-access-nodes.d.ts +19 -1
- package/dist/src/planner/nodes/table-access-nodes.d.ts.map +1 -1
- package/dist/src/planner/nodes/table-access-nodes.js +18 -2
- package/dist/src/planner/nodes/table-access-nodes.js.map +1 -1
- package/dist/src/planner/nodes/window-function.d.ts.map +1 -1
- package/dist/src/planner/nodes/window-function.js +8 -0
- package/dist/src/planner/nodes/window-function.js.map +1 -1
- package/dist/src/planner/nodes/window-node.d.ts +1 -1
- package/dist/src/planner/nodes/window-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/window-node.js +3 -1
- package/dist/src/planner/nodes/window-node.js.map +1 -1
- package/dist/src/planner/optimizer.d.ts +9 -1
- package/dist/src/planner/optimizer.d.ts.map +1 -1
- package/dist/src/planner/optimizer.js +126 -6
- package/dist/src/planner/optimizer.js.map +1 -1
- package/dist/src/planner/rules/access/rule-key-set-seek.d.ts +68 -0
- package/dist/src/planner/rules/access/rule-key-set-seek.d.ts.map +1 -0
- package/dist/src/planner/rules/access/rule-key-set-seek.js +343 -0
- package/dist/src/planner/rules/access/rule-key-set-seek.js.map +1 -0
- 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 +2 -2
- package/dist/src/planner/rules/access/rule-monotonic-range-access.js.map +1 -1
- package/dist/src/planner/rules/access/rule-select-access-path.d.ts +46 -0
- package/dist/src/planner/rules/access/rule-select-access-path.d.ts.map +1 -1
- package/dist/src/planner/rules/access/rule-select-access-path.js +54 -13
- package/dist/src/planner/rules/access/rule-select-access-path.js.map +1 -1
- package/dist/src/planner/rules/cache/rule-cte-optimization.d.ts.map +1 -1
- package/dist/src/planner/rules/cache/rule-cte-optimization.js +9 -0
- package/dist/src/planner/rules/cache/rule-cte-optimization.js.map +1 -1
- package/dist/src/planner/rules/cache/rule-nested-loop-right-cache.d.ts.map +1 -1
- package/dist/src/planner/rules/cache/rule-nested-loop-right-cache.js +6 -4
- package/dist/src/planner/rules/cache/rule-nested-loop-right-cache.js.map +1 -1
- package/dist/src/planner/rules/join/equi-pair-extractor.d.ts +49 -49
- package/dist/src/planner/rules/join/equi-pair-extractor.d.ts.map +1 -1
- package/dist/src/planner/rules/join/equi-pair-extractor.js +54 -58
- package/dist/src/planner/rules/join/equi-pair-extractor.js.map +1 -1
- package/dist/src/planner/rules/join/index-nested-loop.d.ts +49 -0
- package/dist/src/planner/rules/join/index-nested-loop.d.ts.map +1 -0
- package/dist/src/planner/rules/join/index-nested-loop.js +285 -0
- package/dist/src/planner/rules/join/index-nested-loop.js.map +1 -0
- package/dist/src/planner/rules/join/rule-fanout-batched-outer.js +8 -8
- package/dist/src/planner/rules/join/rule-join-physical-selection.d.ts +10 -4
- 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 +97 -29
- package/dist/src/planner/rules/join/rule-join-physical-selection.js.map +1 -1
- package/dist/src/planner/rules/join/rule-monotonic-merge-join.d.ts.map +1 -1
- package/dist/src/planner/rules/join/rule-monotonic-merge-join.js +15 -5
- package/dist/src/planner/rules/join/rule-monotonic-merge-join.js.map +1 -1
- package/dist/src/planner/rules/predicate/rule-empty-relation-folding.d.ts +1 -1
- package/dist/src/planner/rules/predicate/rule-empty-relation-folding.js +1 -1
- package/dist/src/planner/rules/predicate/rule-filter-conjunct-ordering.d.ts +43 -0
- package/dist/src/planner/rules/predicate/rule-filter-conjunct-ordering.d.ts.map +1 -0
- package/dist/src/planner/rules/predicate/rule-filter-conjunct-ordering.js +119 -0
- package/dist/src/planner/rules/predicate/rule-filter-conjunct-ordering.js.map +1 -0
- package/dist/src/planner/rules/predicate/rule-filter-selectivity.d.ts +44 -3
- package/dist/src/planner/rules/predicate/rule-filter-selectivity.d.ts.map +1 -1
- package/dist/src/planner/rules/predicate/rule-filter-selectivity.js +142 -18
- package/dist/src/planner/rules/predicate/rule-filter-selectivity.js.map +1 -1
- package/dist/src/planner/rules/predicate/rule-join-predicate-pushdown.d.ts +73 -0
- package/dist/src/planner/rules/predicate/rule-join-predicate-pushdown.d.ts.map +1 -0
- package/dist/src/planner/rules/predicate/rule-join-predicate-pushdown.js +205 -0
- package/dist/src/planner/rules/predicate/rule-join-predicate-pushdown.js.map +1 -0
- package/dist/src/planner/rules/predicate/rule-predicate-inference-equivalence.d.ts +14 -11
- package/dist/src/planner/rules/predicate/rule-predicate-inference-equivalence.d.ts.map +1 -1
- package/dist/src/planner/rules/predicate/rule-predicate-inference-equivalence.js +21 -85
- package/dist/src/planner/rules/predicate/rule-predicate-inference-equivalence.js.map +1 -1
- package/dist/src/planner/rules/predicate/rule-predicate-pushdown.d.ts +10 -2
- package/dist/src/planner/rules/predicate/rule-predicate-pushdown.d.ts.map +1 -1
- package/dist/src/planner/rules/predicate/rule-predicate-pushdown.js +20 -2
- package/dist/src/planner/rules/predicate/rule-predicate-pushdown.js.map +1 -1
- package/dist/src/planner/rules/retrieve/rule-grow-retrieve.d.ts.map +1 -1
- package/dist/src/planner/rules/retrieve/rule-grow-retrieve.js +18 -3
- package/dist/src/planner/rules/retrieve/rule-grow-retrieve.js.map +1 -1
- package/dist/src/planner/rules/shared/access-leaf.d.ts +39 -0
- package/dist/src/planner/rules/shared/access-leaf.d.ts.map +1 -0
- package/dist/src/planner/rules/shared/access-leaf.js +79 -0
- package/dist/src/planner/rules/shared/access-leaf.js.map +1 -0
- package/dist/src/planner/rules/shared/index-style-context.d.ts +17 -0
- package/dist/src/planner/rules/shared/index-style-context.d.ts.map +1 -1
- package/dist/src/planner/rules/shared/index-style-context.js +7 -0
- package/dist/src/planner/rules/shared/index-style-context.js.map +1 -1
- package/dist/src/planner/rules/subquery/rule-anti-join-fk-empty.d.ts.map +1 -1
- package/dist/src/planner/rules/subquery/rule-anti-join-fk-empty.js +18 -8
- package/dist/src/planner/rules/subquery/rule-anti-join-fk-empty.js.map +1 -1
- package/dist/src/planner/rules/subquery/rule-semi-join-fk-trivial.d.ts +3 -1
- package/dist/src/planner/rules/subquery/rule-semi-join-fk-trivial.d.ts.map +1 -1
- package/dist/src/planner/rules/subquery/rule-semi-join-fk-trivial.js +27 -10
- package/dist/src/planner/rules/subquery/rule-semi-join-fk-trivial.js.map +1 -1
- package/dist/src/planner/rules/subquery/rule-subquery-decorrelation.d.ts +24 -6
- package/dist/src/planner/rules/subquery/rule-subquery-decorrelation.d.ts.map +1 -1
- package/dist/src/planner/rules/subquery/rule-subquery-decorrelation.js +243 -70
- package/dist/src/planner/rules/subquery/rule-subquery-decorrelation.js.map +1 -1
- package/dist/src/planner/rules/window/rule-monotonic-window.d.ts +17 -7
- package/dist/src/planner/rules/window/rule-monotonic-window.d.ts.map +1 -1
- package/dist/src/planner/rules/window/rule-monotonic-window.js +124 -87
- package/dist/src/planner/rules/window/rule-monotonic-window.js.map +1 -1
- package/dist/src/planner/scopes/aliased.d.ts.map +1 -1
- package/dist/src/planner/scopes/aliased.js +6 -0
- package/dist/src/planner/scopes/aliased.js.map +1 -1
- package/dist/src/planner/stats/catalog-stats.d.ts +87 -4
- package/dist/src/planner/stats/catalog-stats.d.ts.map +1 -1
- package/dist/src/planner/stats/catalog-stats.js +250 -28
- package/dist/src/planner/stats/catalog-stats.js.map +1 -1
- package/dist/src/planner/stats/conjunct-selectivity.d.ts +48 -0
- package/dist/src/planner/stats/conjunct-selectivity.d.ts.map +1 -0
- package/dist/src/planner/stats/conjunct-selectivity.js +168 -0
- package/dist/src/planner/stats/conjunct-selectivity.js.map +1 -0
- package/dist/src/planner/stats/index.d.ts +36 -2
- package/dist/src/planner/stats/index.d.ts.map +1 -1
- package/dist/src/planner/stats/index.js.map +1 -1
- package/dist/src/planner/stats/selectivity-combine.d.ts +53 -0
- package/dist/src/planner/stats/selectivity-combine.d.ts.map +1 -0
- package/dist/src/planner/stats/selectivity-combine.js +75 -0
- package/dist/src/planner/stats/selectivity-combine.js.map +1 -0
- package/dist/src/planner/stats/table-cardinality.d.ts +28 -0
- package/dist/src/planner/stats/table-cardinality.d.ts.map +1 -0
- package/dist/src/planner/stats/table-cardinality.js +29 -0
- package/dist/src/planner/stats/table-cardinality.js.map +1 -0
- package/dist/src/planner/util/column-origins.d.ts +79 -0
- package/dist/src/planner/util/column-origins.d.ts.map +1 -0
- package/dist/src/planner/util/column-origins.js +150 -0
- package/dist/src/planner/util/column-origins.js.map +1 -0
- package/dist/src/planner/util/fd-utils.d.ts +32 -8
- package/dist/src/planner/util/fd-utils.d.ts.map +1 -1
- package/dist/src/planner/util/fd-utils.js +46 -9
- package/dist/src/planner/util/fd-utils.js.map +1 -1
- package/dist/src/planner/util/ind-utils.d.ts +56 -17
- package/dist/src/planner/util/ind-utils.d.ts.map +1 -1
- package/dist/src/planner/util/ind-utils.js +82 -21
- package/dist/src/planner/util/ind-utils.js.map +1 -1
- package/dist/src/planner/util/key-utils.d.ts +20 -2
- package/dist/src/planner/util/key-utils.d.ts.map +1 -1
- package/dist/src/planner/util/key-utils.js +37 -3
- package/dist/src/planner/util/key-utils.js.map +1 -1
- package/dist/src/planner/util/row-estimates.d.ts +28 -0
- package/dist/src/planner/util/row-estimates.d.ts.map +1 -0
- package/dist/src/planner/util/row-estimates.js +40 -0
- package/dist/src/planner/util/row-estimates.js.map +1 -0
- package/dist/src/planner/util/row-population.d.ts +42 -0
- package/dist/src/planner/util/row-population.d.ts.map +1 -0
- package/dist/src/planner/util/row-population.js +71 -0
- package/dist/src/planner/util/row-population.js.map +1 -0
- package/dist/src/runtime/deferred-constraint-queue.d.ts +40 -0
- package/dist/src/runtime/deferred-constraint-queue.d.ts.map +1 -1
- package/dist/src/runtime/deferred-constraint-queue.js +90 -9
- package/dist/src/runtime/deferred-constraint-queue.js.map +1 -1
- package/dist/src/runtime/emit/add-constraint.d.ts.map +1 -1
- package/dist/src/runtime/emit/add-constraint.js +39 -1
- package/dist/src/runtime/emit/add-constraint.js.map +1 -1
- package/dist/src/runtime/emit/aggregate.d.ts.map +1 -1
- package/dist/src/runtime/emit/aggregate.js +4 -4
- package/dist/src/runtime/emit/aggregate.js.map +1 -1
- package/dist/src/runtime/emit/alter-schema-event.d.ts +42 -0
- package/dist/src/runtime/emit/alter-schema-event.d.ts.map +1 -0
- package/dist/src/runtime/emit/alter-schema-event.js +28 -0
- package/dist/src/runtime/emit/alter-schema-event.js.map +1 -0
- package/dist/src/runtime/emit/alter-table.d.ts.map +1 -1
- package/dist/src/runtime/emit/alter-table.js +386 -154
- package/dist/src/runtime/emit/alter-table.js.map +1 -1
- package/dist/src/runtime/emit/analyze.d.ts.map +1 -1
- package/dist/src/runtime/emit/analyze.js +35 -3
- package/dist/src/runtime/emit/analyze.js.map +1 -1
- package/dist/src/runtime/emit/asof-scan.d.ts.map +1 -1
- package/dist/src/runtime/emit/asof-scan.js +8 -3
- package/dist/src/runtime/emit/asof-scan.js.map +1 -1
- package/dist/src/runtime/emit/between.d.ts.map +1 -1
- package/dist/src/runtime/emit/between.js +4 -38
- package/dist/src/runtime/emit/between.js.map +1 -1
- package/dist/src/runtime/emit/binary.d.ts.map +1 -1
- package/dist/src/runtime/emit/binary.js +2 -23
- 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 +6 -35
- package/dist/src/runtime/emit/bloom-join.js.map +1 -1
- package/dist/src/runtime/emit/case.d.ts.map +1 -1
- package/dist/src/runtime/emit/case.js +40 -6
- package/dist/src/runtime/emit/case.js.map +1 -1
- package/dist/src/runtime/emit/cast.d.ts.map +1 -1
- package/dist/src/runtime/emit/cast.js +4 -19
- package/dist/src/runtime/emit/cast.js.map +1 -1
- package/dist/src/runtime/emit/constraint-check.d.ts.map +1 -1
- package/dist/src/runtime/emit/constraint-check.js +32 -8
- package/dist/src/runtime/emit/constraint-check.js.map +1 -1
- package/dist/src/runtime/emit/dml-executor.d.ts.map +1 -1
- package/dist/src/runtime/emit/dml-executor.js +90 -50
- package/dist/src/runtime/emit/dml-executor.js.map +1 -1
- package/dist/src/runtime/emit/drop-index.d.ts.map +1 -1
- package/dist/src/runtime/emit/drop-index.js +4 -3
- package/dist/src/runtime/emit/drop-index.js.map +1 -1
- package/dist/src/runtime/emit/filter.d.ts +18 -0
- package/dist/src/runtime/emit/filter.d.ts.map +1 -1
- package/dist/src/runtime/emit/filter.js +52 -10
- package/dist/src/runtime/emit/filter.js.map +1 -1
- package/dist/src/runtime/emit/hash-aggregate.d.ts.map +1 -1
- package/dist/src/runtime/emit/hash-aggregate.js +2 -2
- package/dist/src/runtime/emit/hash-aggregate.js.map +1 -1
- package/dist/src/runtime/emit/join-key-extractor.d.ts +46 -0
- package/dist/src/runtime/emit/join-key-extractor.d.ts.map +1 -0
- package/dist/src/runtime/emit/join-key-extractor.js +60 -0
- package/dist/src/runtime/emit/join-key-extractor.js.map +1 -0
- package/dist/src/runtime/emit/join.d.ts.map +1 -1
- package/dist/src/runtime/emit/join.js +8 -46
- package/dist/src/runtime/emit/join.js.map +1 -1
- package/dist/src/runtime/emit/key-set-semi-join.d.ts +29 -0
- package/dist/src/runtime/emit/key-set-semi-join.d.ts.map +1 -0
- package/dist/src/runtime/emit/key-set-semi-join.js +149 -0
- package/dist/src/runtime/emit/key-set-semi-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 +15 -8
- package/dist/src/runtime/emit/merge-join.js.map +1 -1
- package/dist/src/runtime/emit/operand-comparator.d.ts +70 -0
- package/dist/src/runtime/emit/operand-comparator.d.ts.map +1 -0
- package/dist/src/runtime/emit/operand-comparator.js +93 -0
- package/dist/src/runtime/emit/operand-comparator.js.map +1 -0
- package/dist/src/runtime/emit/scan.d.ts.map +1 -1
- package/dist/src/runtime/emit/scan.js +11 -2
- package/dist/src/runtime/emit/scan.js.map +1 -1
- package/dist/src/runtime/emit/schema-declarative.d.ts.map +1 -1
- package/dist/src/runtime/emit/schema-declarative.js +30 -0
- package/dist/src/runtime/emit/schema-declarative.js.map +1 -1
- package/dist/src/runtime/emit/subquery.d.ts.map +1 -1
- package/dist/src/runtime/emit/subquery.js +153 -52
- package/dist/src/runtime/emit/subquery.js.map +1 -1
- package/dist/src/runtime/emit/window.js +36 -23
- package/dist/src/runtime/emit/window.js.map +1 -1
- package/dist/src/runtime/parallel-driver.d.ts +5 -2
- package/dist/src/runtime/parallel-driver.d.ts.map +1 -1
- package/dist/src/runtime/parallel-driver.js +6 -2
- package/dist/src/runtime/parallel-driver.js.map +1 -1
- package/dist/src/runtime/register.d.ts.map +1 -1
- package/dist/src/runtime/register.js +2 -0
- package/dist/src/runtime/register.js.map +1 -1
- package/dist/src/runtime/types.d.ts +7 -0
- package/dist/src/runtime/types.d.ts.map +1 -1
- package/dist/src/runtime/types.js.map +1 -1
- package/dist/src/schema/catalog-persistability.d.ts +19 -16
- package/dist/src/schema/catalog-persistability.d.ts.map +1 -1
- package/dist/src/schema/catalog-persistability.js +76 -20
- package/dist/src/schema/catalog-persistability.js.map +1 -1
- package/dist/src/schema/catalog.d.ts +67 -2
- package/dist/src/schema/catalog.d.ts.map +1 -1
- package/dist/src/schema/catalog.js +194 -3
- package/dist/src/schema/catalog.js.map +1 -1
- package/dist/src/schema/column.d.ts +26 -3
- package/dist/src/schema/column.d.ts.map +1 -1
- package/dist/src/schema/column.js.map +1 -1
- package/dist/src/schema/ddl-generator.d.ts.map +1 -1
- package/dist/src/schema/ddl-generator.js +19 -6
- package/dist/src/schema/ddl-generator.js.map +1 -1
- package/dist/src/schema/function.d.ts +40 -0
- package/dist/src/schema/function.d.ts.map +1 -1
- package/dist/src/schema/function.js +10 -2
- package/dist/src/schema/function.js.map +1 -1
- package/dist/src/schema/manager.d.ts +84 -7
- package/dist/src/schema/manager.d.ts.map +1 -1
- package/dist/src/schema/manager.js +153 -53
- package/dist/src/schema/manager.js.map +1 -1
- package/dist/src/schema/schema-differ.d.ts.map +1 -1
- package/dist/src/schema/schema-differ.js +104 -28
- package/dist/src/schema/schema-differ.js.map +1 -1
- package/dist/src/schema/table.d.ts +149 -1
- package/dist/src/schema/table.d.ts.map +1 -1
- package/dist/src/schema/table.js +248 -1
- package/dist/src/schema/table.js.map +1 -1
- package/dist/src/schema/unique-enforcement.d.ts +5 -3
- package/dist/src/schema/unique-enforcement.d.ts.map +1 -1
- package/dist/src/schema/unique-enforcement.js +5 -3
- package/dist/src/schema/unique-enforcement.js.map +1 -1
- package/dist/src/types/builtin-types.d.ts +16 -0
- package/dist/src/types/builtin-types.d.ts.map +1 -1
- package/dist/src/types/builtin-types.js +76 -12
- package/dist/src/types/builtin-types.js.map +1 -1
- package/dist/src/types/cast-semantics.d.ts +42 -0
- package/dist/src/types/cast-semantics.d.ts.map +1 -1
- package/dist/src/types/cast-semantics.js +64 -0
- package/dist/src/types/cast-semantics.js.map +1 -1
- package/dist/src/types/comparison-coercion.d.ts +101 -0
- package/dist/src/types/comparison-coercion.d.ts.map +1 -0
- package/dist/src/types/comparison-coercion.js +162 -0
- package/dist/src/types/comparison-coercion.js.map +1 -0
- package/dist/src/types/index.d.ts +2 -2
- package/dist/src/types/index.d.ts.map +1 -1
- package/dist/src/types/index.js +2 -2
- package/dist/src/types/index.js.map +1 -1
- package/dist/src/types/logical-type.d.ts +7 -0
- package/dist/src/types/logical-type.d.ts.map +1 -1
- package/dist/src/types/logical-type.js.map +1 -1
- package/dist/src/types/validation.d.ts +56 -0
- package/dist/src/types/validation.d.ts.map +1 -1
- package/dist/src/types/validation.js +68 -0
- package/dist/src/types/validation.js.map +1 -1
- package/dist/src/util/coercion.d.ts +8 -0
- package/dist/src/util/coercion.d.ts.map +1 -1
- package/dist/src/util/coercion.js +8 -0
- package/dist/src/util/coercion.js.map +1 -1
- package/dist/src/util/comparison.d.ts +41 -3
- package/dist/src/util/comparison.d.ts.map +1 -1
- package/dist/src/util/comparison.js +52 -5
- package/dist/src/util/comparison.js.map +1 -1
- package/dist/src/util/key-serializer.d.ts +46 -0
- package/dist/src/util/key-serializer.d.ts.map +1 -1
- package/dist/src/util/key-serializer.js +47 -0
- package/dist/src/util/key-serializer.js.map +1 -1
- package/dist/src/util/value-set.d.ts +21 -0
- package/dist/src/util/value-set.d.ts.map +1 -0
- package/dist/src/util/value-set.js +22 -0
- package/dist/src/util/value-set.js.map +1 -0
- package/dist/src/vtab/best-access-plan.d.ts +87 -0
- package/dist/src/vtab/best-access-plan.d.ts.map +1 -1
- package/dist/src/vtab/best-access-plan.js +87 -0
- package/dist/src/vtab/best-access-plan.js.map +1 -1
- package/dist/src/vtab/capabilities.d.ts +1 -1
- package/dist/src/vtab/events.d.ts +5 -1
- package/dist/src/vtab/events.d.ts.map +1 -1
- package/dist/src/vtab/events.js.map +1 -1
- package/dist/src/vtab/memory/layer/alter-column.d.ts +147 -0
- package/dist/src/vtab/memory/layer/alter-column.d.ts.map +1 -0
- package/dist/src/vtab/memory/layer/alter-column.js +214 -0
- package/dist/src/vtab/memory/layer/alter-column.js.map +1 -0
- package/dist/src/vtab/memory/layer/base.d.ts +6 -4
- package/dist/src/vtab/memory/layer/base.d.ts.map +1 -1
- package/dist/src/vtab/memory/layer/base.js +10 -7
- package/dist/src/vtab/memory/layer/base.js.map +1 -1
- package/dist/src/vtab/memory/layer/interface.d.ts +2 -2
- package/dist/src/vtab/memory/layer/interface.d.ts.map +1 -1
- package/dist/src/vtab/memory/layer/manager.d.ts +232 -39
- package/dist/src/vtab/memory/layer/manager.d.ts.map +1 -1
- package/dist/src/vtab/memory/layer/manager.js +657 -519
- package/dist/src/vtab/memory/layer/manager.js.map +1 -1
- package/dist/src/vtab/memory/layer/scan-layer.d.ts.map +1 -1
- package/dist/src/vtab/memory/layer/scan-layer.js +32 -5
- package/dist/src/vtab/memory/layer/scan-layer.js.map +1 -1
- package/dist/src/vtab/memory/layer/transaction.d.ts +111 -16
- package/dist/src/vtab/memory/layer/transaction.d.ts.map +1 -1
- package/dist/src/vtab/memory/layer/transaction.js +154 -51
- package/dist/src/vtab/memory/layer/transaction.js.map +1 -1
- package/dist/src/vtab/memory/module.d.ts +14 -2
- package/dist/src/vtab/memory/module.d.ts.map +1 -1
- package/dist/src/vtab/memory/module.js +53 -40
- package/dist/src/vtab/memory/module.js.map +1 -1
- package/dist/src/vtab/memory/table.d.ts +1 -1
- package/dist/src/vtab/memory/table.d.ts.map +1 -1
- package/dist/src/vtab/memory/table.js +15 -3
- package/dist/src/vtab/memory/table.js.map +1 -1
- package/dist/src/vtab/module.d.ts +51 -21
- package/dist/src/vtab/module.d.ts.map +1 -1
- package/dist/src/vtab/table.d.ts +10 -2
- package/dist/src/vtab/table.d.ts.map +1 -1
- package/dist/src/vtab/table.js +1 -1
- package/dist/src/vtab/table.js.map +1 -1
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rule-anti-join-fk-empty.d.ts","sourceRoot":"","sources":["../../../../../src/planner/rules/subquery/rule-anti-join-fk-empty.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAU7D,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,GAAG,QAAQ,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"rule-anti-join-fk-empty.d.ts","sourceRoot":"","sources":["../../../../../src/planner/rules/subquery/rule-anti-join-fk-empty.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAU7D,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,GAAG,QAAQ,GAAG,IAAI,CAuDzF"}
|
|
@@ -33,7 +33,7 @@ import { createLogger } from '../../../common/logger.js';
|
|
|
33
33
|
import { JoinNode, extractEquiPairsFromCondition } from '../../nodes/join-node.js';
|
|
34
34
|
import { EmptyRelationNode } from '../../nodes/empty-relation-node.js';
|
|
35
35
|
import { normalizePredicate } from '../../analysis/predicate-normalizer.js';
|
|
36
|
-
import { lookupCoveringFK, isRowPreservingPathToTable,
|
|
36
|
+
import { lookupCoveringFK, isRowPreservingPathToTable, resolveTableColumnMapping, mapColumnsToTable } from '../../util/ind-utils.js';
|
|
37
37
|
import { isAndOfColumnEqualities } from '../join/rule-join-elimination.js';
|
|
38
38
|
import { PlanNodeCharacteristics } from '../../framework/characteristics.js';
|
|
39
39
|
const log = createLogger('optimizer:rule:anti-join-fk-empty');
|
|
@@ -52,12 +52,20 @@ export function ruleAntiJoinFkEmpty(node, _context) {
|
|
|
52
52
|
const pairs = extractEquiPairsFromCondition(node.condition, leftAttrs, rightAttrs);
|
|
53
53
|
if (pairs.length === 0)
|
|
54
54
|
return null;
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
// Equi-pairs index each side's OUTPUT columns; the FK lookup speaks base-table
|
|
56
|
+
// column indices. Translate before comparing — a projection on either side can
|
|
57
|
+
// rename, reorder, or drop columns, and a derived column has no table origin
|
|
58
|
+
// at all (mapColumnsToTable then declines).
|
|
59
|
+
const leftMapping = resolveTableColumnMapping(node.left);
|
|
60
|
+
const rightMapping = resolveTableColumnMapping(node.right);
|
|
61
|
+
if (!leftMapping || !rightMapping)
|
|
58
62
|
return null;
|
|
59
|
-
const childEquiCols = pairs.map(p => p.left);
|
|
60
|
-
const parentEquiCols = pairs.map(p => p.right);
|
|
63
|
+
const childEquiCols = mapColumnsToTable(pairs.map(p => p.left), leftMapping);
|
|
64
|
+
const parentEquiCols = mapColumnsToTable(pairs.map(p => p.right), rightMapping);
|
|
65
|
+
if (!childEquiCols || !parentEquiCols)
|
|
66
|
+
return null;
|
|
67
|
+
const leftSchema = leftMapping.schema;
|
|
68
|
+
const rightSchema = rightMapping.schema;
|
|
61
69
|
const match = lookupCoveringFK(leftSchema, rightSchema, childEquiCols, parentEquiCols);
|
|
62
70
|
if (!match)
|
|
63
71
|
return null;
|
|
@@ -66,8 +74,10 @@ export function ruleAntiJoinFkEmpty(node, _context) {
|
|
|
66
74
|
if (match.nullable)
|
|
67
75
|
return null;
|
|
68
76
|
// The parent side must expose the full base-table row set — otherwise the
|
|
69
|
-
// IND `L.fk ⊆ R.pk` doesn't guarantee a match in the filtered relation.
|
|
70
|
-
|
|
77
|
+
// IND `L.fk ⊆ R.pk` doesn't guarantee a match in the filtered relation. A
|
|
78
|
+
// projection is peeled (it never drops rows); its column renaming is already
|
|
79
|
+
// accounted for by the mapping above.
|
|
80
|
+
if (!isRowPreservingPathToTable(node.right, { throughProject: true }))
|
|
71
81
|
return null;
|
|
72
82
|
// Refuse to fold to Empty when either participating subtree carries a write —
|
|
73
83
|
// the anti-join collapses to EmptyRelation(L's attrs), dropping both sides.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rule-anti-join-fk-empty.js","sourceRoot":"","sources":["../../../../../src/planner/rules/subquery/rule-anti-join-fk-empty.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAGzD,OAAO,EAAE,QAAQ,EAAE,6BAA6B,EAAE,MAAM,0BAA0B,CAAC;AACnF,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AACvE,OAAO,EAAE,kBAAkB,EAAE,MAAM,wCAAwC,CAAC;AAC5E,OAAO,EAAE,gBAAgB,EAAE,0BAA0B,EAAE,
|
|
1
|
+
{"version":3,"file":"rule-anti-join-fk-empty.js","sourceRoot":"","sources":["../../../../../src/planner/rules/subquery/rule-anti-join-fk-empty.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
* SemiJoin(L, R, p)
|
|
9
9
|
* where p is an AND-of-column-equalities,
|
|
10
10
|
* L's equi columns form a declared FK referencing R's PK, and
|
|
11
|
-
* R is a row-preserving path to its base table
|
|
11
|
+
* R is a row-preserving path to its base table (projections included — the
|
|
12
|
+
* uncorrelated `IN` arm hands the join the subquery's SELECT list verbatim,
|
|
13
|
+
* so R is typically `Project(parent)`).
|
|
12
14
|
*
|
|
13
15
|
* Rewrite:
|
|
14
16
|
* - FK columns all NOT NULL → replace the SemiJoin with L (every L row matches
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rule-semi-join-fk-trivial.d.ts","sourceRoot":"","sources":["../../../../../src/planner/rules/subquery/rule-semi-join-fk-trivial.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"rule-semi-join-fk-trivial.d.ts","sourceRoot":"","sources":["../../../../../src/planner/rules/subquery/rule-semi-join-fk-trivial.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAA6B,MAAM,0BAA0B,CAAC;AACpF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAc7D,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,GAAG,QAAQ,GAAG,IAAI,CA8D3F"}
|
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
* SemiJoin(L, R, p)
|
|
9
9
|
* where p is an AND-of-column-equalities,
|
|
10
10
|
* L's equi columns form a declared FK referencing R's PK, and
|
|
11
|
-
* R is a row-preserving path to its base table
|
|
11
|
+
* R is a row-preserving path to its base table (projections included — the
|
|
12
|
+
* uncorrelated `IN` arm hands the join the subquery's SELECT list verbatim,
|
|
13
|
+
* so R is typically `Project(parent)`).
|
|
12
14
|
*
|
|
13
15
|
* Rewrite:
|
|
14
16
|
* - FK columns all NOT NULL → replace the SemiJoin with L (every L row matches
|
|
@@ -27,7 +29,7 @@ import { FilterNode } from '../../nodes/filter.js';
|
|
|
27
29
|
import { UnaryOpNode, BinaryOpNode } from '../../nodes/scalar.js';
|
|
28
30
|
import { ColumnReferenceNode } from '../../nodes/reference.js';
|
|
29
31
|
import { normalizePredicate } from '../../analysis/predicate-normalizer.js';
|
|
30
|
-
import { lookupCoveringFK, isRowPreservingPathToTable,
|
|
32
|
+
import { lookupCoveringFK, isRowPreservingPathToTable, resolveTableColumnMapping, mapColumnsToTable } from '../../util/ind-utils.js';
|
|
31
33
|
import { isAndOfColumnEqualities } from '../join/rule-join-elimination.js';
|
|
32
34
|
import { PlanNodeCharacteristics } from '../../framework/characteristics.js';
|
|
33
35
|
const log = createLogger('optimizer:rule:semi-join-fk-trivial');
|
|
@@ -46,18 +48,31 @@ export function ruleSemiJoinFkTrivial(node, _context) {
|
|
|
46
48
|
const pairs = extractEquiPairsFromCondition(node.condition, leftAttrs, rightAttrs);
|
|
47
49
|
if (pairs.length === 0)
|
|
48
50
|
return null;
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
// Equi-pairs index each side's OUTPUT columns; the FK lookup speaks base-table
|
|
52
|
+
// column indices. Translate before comparing — a projection on either side can
|
|
53
|
+
// rename, reorder, or drop columns, and a derived column has no table origin
|
|
54
|
+
// at all (mapColumnsToTable then declines).
|
|
55
|
+
const leftMapping = resolveTableColumnMapping(node.left);
|
|
56
|
+
const rightMapping = resolveTableColumnMapping(node.right);
|
|
57
|
+
if (!leftMapping || !rightMapping)
|
|
52
58
|
return null;
|
|
53
|
-
const
|
|
54
|
-
const
|
|
59
|
+
const childOutputCols = pairs.map(p => p.left);
|
|
60
|
+
const childEquiCols = mapColumnsToTable(childOutputCols, leftMapping);
|
|
61
|
+
const parentEquiCols = mapColumnsToTable(pairs.map(p => p.right), rightMapping);
|
|
62
|
+
if (!childEquiCols || !parentEquiCols)
|
|
63
|
+
return null;
|
|
64
|
+
const leftSchema = leftMapping.schema;
|
|
65
|
+
const rightSchema = rightMapping.schema;
|
|
55
66
|
const match = lookupCoveringFK(leftSchema, rightSchema, childEquiCols, parentEquiCols);
|
|
56
67
|
if (!match)
|
|
57
68
|
return null;
|
|
58
69
|
// The parent side must be the full table — if rows were filtered out, the
|
|
59
70
|
// IND inclusion doesn't preserve "every L row has a match" under filtering.
|
|
60
|
-
|
|
71
|
+
// A projection is peeled: `extractUncorrelatedIn` hands the join the IN
|
|
72
|
+
// subquery verbatim, so R is a `Project` over the parent table, and a
|
|
73
|
+
// projection never drops rows. Its column renaming is already accounted for
|
|
74
|
+
// by the mapping above.
|
|
75
|
+
if (!isRowPreservingPathToTable(node.right, { throughProject: true }))
|
|
61
76
|
return null;
|
|
62
77
|
// Refuse to drop the R side when it carries a write — the rewrite replaces
|
|
63
78
|
// the semi-join with L (or Filter(L)) and the R subtree is dropped entirely.
|
|
@@ -70,8 +85,10 @@ export function ruleSemiJoinFkTrivial(node, _context) {
|
|
|
70
85
|
return node.left;
|
|
71
86
|
}
|
|
72
87
|
// Nullable FK: rows with NULL in any FK column never match in the semi-join.
|
|
73
|
-
// Replace the join with `Filter(L, fk IS NOT NULL AND …)`.
|
|
74
|
-
|
|
88
|
+
// Replace the join with `Filter(L, fk IS NOT NULL AND …)`. The predicate is
|
|
89
|
+
// built over L's OUTPUT columns (`childOutputCols`), not the base-table
|
|
90
|
+
// indices the FK lookup consumed.
|
|
91
|
+
const predicate = buildIsNotNullPredicate(node.scope, leftAttrs, childOutputCols);
|
|
75
92
|
if (!predicate)
|
|
76
93
|
return null;
|
|
77
94
|
log('Trivializing semi-join over nullable FK %s → %s to Filter(L, fk IS NOT NULL)', leftSchema.name, rightSchema.name);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rule-semi-join-fk-trivial.js","sourceRoot":"","sources":["../../../../../src/planner/rules/subquery/rule-semi-join-fk-trivial.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"rule-semi-join-fk-trivial.js","sourceRoot":"","sources":["../../../../../src/planner/rules/subquery/rule-semi-join-fk-trivial.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,21 +1,39 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Rule: Subquery Decorrelation
|
|
3
3
|
*
|
|
4
|
-
* Transforms
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Transforms EXISTS and IN subqueries in WHERE-clause FilterNode predicates
|
|
5
|
+
* into equivalent semi/anti joins, enabling hash join selection and
|
|
6
|
+
* eliminating per-row re-execution (correlated) or set-probe evaluation
|
|
7
|
+
* (uncorrelated) of the inner query.
|
|
7
8
|
*
|
|
8
9
|
* Transformations:
|
|
9
10
|
* Filter[EXISTS(correlated)](outer) → SemiJoin[corr_pred](outer, inner)
|
|
10
11
|
* Filter[NOT EXISTS(correlated)](outer) → AntiJoin[corr_pred](outer, inner)
|
|
11
12
|
* Filter[col IN (correlated subquery)](outer) → SemiJoin[col = inner.col](outer, inner)
|
|
13
|
+
* Filter[col IN (uncorrelated subquery)](outer) → SemiJoin[col = inner.col0](outer, inner)
|
|
12
14
|
*
|
|
13
15
|
* Applicability:
|
|
14
16
|
* - FilterNode with top-level ExistsNode, NOT ExistsNode, or InNode (subquery variant)
|
|
15
|
-
* -
|
|
16
|
-
*
|
|
17
|
+
* - Correlated arms: subquery references outer attributes and the correlation
|
|
18
|
+
* predicate is a simple equi-join condition (col = col across inner/outer)
|
|
19
|
+
* - Uncorrelated IN arm: bare outer column on the left of IN, deterministic
|
|
20
|
+
* read-only inner, hashable synthesized equi-pair, and agreeing IN/`=`
|
|
21
|
+
* collation resolutions (see `extractUncorrelatedIn`). The inner tree is used
|
|
22
|
+
* VERBATIM as the join's right side — no descent, so inner LIMIT / DISTINCT /
|
|
23
|
+
* set-ops / CTE bodies are preserved as-is.
|
|
17
24
|
*
|
|
18
|
-
*
|
|
25
|
+
* Why the uncorrelated IN rewrite is sound in WHERE position ONLY: `x IN S` is
|
|
26
|
+
* three-valued — it yields NULL (not FALSE) when `x` is NULL, or when there is
|
|
27
|
+
* no match and `S` contains a NULL. A FilterNode predicate collapses NULL to
|
|
28
|
+
* "drop the row", and a semi join also drops a row with no match (NULL never
|
|
29
|
+
* equals under `=`), so under a WHERE filter the two agree on every case.
|
|
30
|
+
* Projection-position IN must keep its three-valued answer and stays on the
|
|
31
|
+
* runtime set-probe path (`emitIn`), which also remains the fallback whenever
|
|
32
|
+
* any gate declines.
|
|
33
|
+
*
|
|
34
|
+
* NOT IN is deferred due to NULL semantics complexity (the parser builds it as
|
|
35
|
+
* UnaryOp(NOT) over the InNode, so it never matches these top-level-conjunct
|
|
36
|
+
* shapes).
|
|
19
37
|
*
|
|
20
38
|
* A SECOND ANCHOR (`ruleExistsInSelectDecorrelation`, ProjectNode) handles the
|
|
21
39
|
* same subqueries appearing in a SELECT list, where a semi/anti join cannot be
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rule-subquery-decorrelation.d.ts","sourceRoot":"","sources":["../../../../../src/planner/rules/subquery/rule-subquery-decorrelation.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"rule-subquery-decorrelation.d.ts","sourceRoot":"","sources":["../../../../../src/planner/rules/subquery/rule-subquery-decorrelation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAIH,OAAO,EAAoB,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACtE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAqe7D,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,GAAG,QAAQ,GAAG,IAAI,CAkB/F;AAmCD,wBAAgB,+BAA+B,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,GAAG,QAAQ,GAAG,IAAI,CAkCrG"}
|
|
@@ -1,21 +1,39 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Rule: Subquery Decorrelation
|
|
3
3
|
*
|
|
4
|
-
* Transforms
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Transforms EXISTS and IN subqueries in WHERE-clause FilterNode predicates
|
|
5
|
+
* into equivalent semi/anti joins, enabling hash join selection and
|
|
6
|
+
* eliminating per-row re-execution (correlated) or set-probe evaluation
|
|
7
|
+
* (uncorrelated) of the inner query.
|
|
7
8
|
*
|
|
8
9
|
* Transformations:
|
|
9
10
|
* Filter[EXISTS(correlated)](outer) → SemiJoin[corr_pred](outer, inner)
|
|
10
11
|
* Filter[NOT EXISTS(correlated)](outer) → AntiJoin[corr_pred](outer, inner)
|
|
11
12
|
* Filter[col IN (correlated subquery)](outer) → SemiJoin[col = inner.col](outer, inner)
|
|
13
|
+
* Filter[col IN (uncorrelated subquery)](outer) → SemiJoin[col = inner.col0](outer, inner)
|
|
12
14
|
*
|
|
13
15
|
* Applicability:
|
|
14
16
|
* - FilterNode with top-level ExistsNode, NOT ExistsNode, or InNode (subquery variant)
|
|
15
|
-
* -
|
|
16
|
-
*
|
|
17
|
+
* - Correlated arms: subquery references outer attributes and the correlation
|
|
18
|
+
* predicate is a simple equi-join condition (col = col across inner/outer)
|
|
19
|
+
* - Uncorrelated IN arm: bare outer column on the left of IN, deterministic
|
|
20
|
+
* read-only inner, hashable synthesized equi-pair, and agreeing IN/`=`
|
|
21
|
+
* collation resolutions (see `extractUncorrelatedIn`). The inner tree is used
|
|
22
|
+
* VERBATIM as the join's right side — no descent, so inner LIMIT / DISTINCT /
|
|
23
|
+
* set-ops / CTE bodies are preserved as-is.
|
|
17
24
|
*
|
|
18
|
-
*
|
|
25
|
+
* Why the uncorrelated IN rewrite is sound in WHERE position ONLY: `x IN S` is
|
|
26
|
+
* three-valued — it yields NULL (not FALSE) when `x` is NULL, or when there is
|
|
27
|
+
* no match and `S` contains a NULL. A FilterNode predicate collapses NULL to
|
|
28
|
+
* "drop the row", and a semi join also drops a row with no match (NULL never
|
|
29
|
+
* equals under `=`), so under a WHERE filter the two agree on every case.
|
|
30
|
+
* Projection-position IN must keep its three-valued answer and stays on the
|
|
31
|
+
* runtime set-probe path (`emitIn`), which also remains the fallback whenever
|
|
32
|
+
* any gate declines.
|
|
33
|
+
*
|
|
34
|
+
* NOT IN is deferred due to NULL semantics complexity (the parser builds it as
|
|
35
|
+
* UnaryOp(NOT) over the InNode, so it never matches these top-level-conjunct
|
|
36
|
+
* shapes).
|
|
19
37
|
*
|
|
20
38
|
* A SECOND ANCHOR (`ruleExistsInSelectDecorrelation`, ProjectNode) handles the
|
|
21
39
|
* same subqueries appearing in a SELECT list, where a semi/anti join cannot be
|
|
@@ -41,6 +59,9 @@ import { PlanNodeType } from '../../nodes/plan-node-type.js';
|
|
|
41
59
|
import { splitConjuncts, combineConjuncts } from '../../analysis/predicate-conjuncts.js';
|
|
42
60
|
import { isEquiCorrelation, collectDefinedAttrIds, referencesAnyAttr } from '../../analysis/equi-correlation.js';
|
|
43
61
|
import { PlanNodeCharacteristics } from '../../framework/characteristics.js';
|
|
62
|
+
import { extractEquiPairs } from '../join/equi-pair-extractor.js';
|
|
63
|
+
import { resolveComparisonCollation, resolveInCollationForNode } from '../../analysis/comparison-collation.js';
|
|
64
|
+
import { coerceComparisonSet } from '../../building/coercion.js';
|
|
44
65
|
const log = createLogger('optimizer:rule:subquery-decorrelation');
|
|
45
66
|
/**
|
|
46
67
|
* Identify a decorrelation candidate from a single conjunct.
|
|
@@ -49,7 +70,7 @@ function identifyCandidate(node) {
|
|
|
49
70
|
// EXISTS(subquery)
|
|
50
71
|
if (node instanceof ExistsNode) {
|
|
51
72
|
if (isCorrelatedSubquery(node.subquery)) {
|
|
52
|
-
return { subqueryNode: node, joinType: 'semi'
|
|
73
|
+
return { subqueryNode: node, joinType: 'semi' };
|
|
53
74
|
}
|
|
54
75
|
return null;
|
|
55
76
|
}
|
|
@@ -58,17 +79,20 @@ function identifyCandidate(node) {
|
|
|
58
79
|
if (node.operand instanceof ExistsNode) {
|
|
59
80
|
const exists = node.operand;
|
|
60
81
|
if (isCorrelatedSubquery(exists.subquery)) {
|
|
61
|
-
return { subqueryNode: exists, joinType: 'anti'
|
|
82
|
+
return { subqueryNode: exists, joinType: 'anti' };
|
|
62
83
|
}
|
|
63
84
|
}
|
|
64
85
|
return null;
|
|
65
86
|
}
|
|
66
|
-
// col IN (correlated
|
|
87
|
+
// col IN (subquery) — correlated and uncorrelated take different extractions
|
|
67
88
|
if (node instanceof InNode && node.source && !node.values) {
|
|
68
89
|
if (isCorrelatedSubquery(node.source)) {
|
|
69
|
-
return { subqueryNode: node, joinType: 'semi'
|
|
90
|
+
return { subqueryNode: node, joinType: 'semi' };
|
|
70
91
|
}
|
|
71
|
-
|
|
92
|
+
// Uncorrelated filter-position IN: semi join ≡ IN under WHERE's NULL
|
|
93
|
+
// collapse (see the rule header). Gates live in extractUncorrelatedIn;
|
|
94
|
+
// any decline leaves the InNode on the runtime set-probe path.
|
|
95
|
+
return { subqueryNode: node, joinType: 'semi', uncorrelatedIn: true };
|
|
72
96
|
}
|
|
73
97
|
return null;
|
|
74
98
|
}
|
|
@@ -159,15 +183,13 @@ function extractInCorrelation(inNode, outerAttrIds) {
|
|
|
159
183
|
if (innerAttrs.length === 0)
|
|
160
184
|
return null;
|
|
161
185
|
const innerFirstAttr = innerAttrs[0];
|
|
162
|
-
// Build the equi-join condition: outer.col = inner.firstCol
|
|
163
|
-
// We need a BinaryOpNode with = between the outer column ref and an inner column ref
|
|
164
|
-
const innerColRef = new ColumnReferenceNode(outerColRef.scope, outerColRef.expression, // reuse expression for formatting
|
|
165
|
-
innerFirstAttr.type, innerFirstAttr.id, 0 // column index in the inner relation
|
|
166
|
-
);
|
|
167
|
-
const equiCondition = new BinaryOpNode(outerColRef.scope, { type: 'binary', operator: '=', left: outerColRef.expression, right: innerColRef.expression }, outerColRef, innerColRef);
|
|
168
186
|
// Walk through the subquery to find any additional correlation filters
|
|
169
187
|
let current = subqueryRoot;
|
|
170
|
-
// Skip Project and Alias nodes
|
|
188
|
+
// Skip Project and Alias nodes.
|
|
189
|
+
// NOTE: every other root (Distinct, SetOperation, Sort, CTE body, Aggregate, …)
|
|
190
|
+
// is caught only by the post-build external-reference backstop in
|
|
191
|
+
// decorrelateOneConjunct — widening this descent without keeping that backstop
|
|
192
|
+
// would silently bury the correlation inside the join's right side again.
|
|
171
193
|
while (current.nodeType === PlanNodeType.Project || current.nodeType === PlanNodeType.Alias) {
|
|
172
194
|
const children = current.getChildren();
|
|
173
195
|
const source = children[0];
|
|
@@ -175,9 +197,44 @@ function extractInCorrelation(inNode, outerAttrIds) {
|
|
|
175
197
|
break;
|
|
176
198
|
current = source;
|
|
177
199
|
}
|
|
200
|
+
// Gate: a LIMIT/OFFSET reached by the descent sits ABOVE the correlated
|
|
201
|
+
// filter, so it applies per outer row — a single semi join cannot express
|
|
202
|
+
// that. (A LIMIT *below* the correlated filter, inside a derived table, is
|
|
203
|
+
// uncorrelated and never reached here.)
|
|
204
|
+
if (current.nodeType === PlanNodeType.LimitOffset)
|
|
205
|
+
return null;
|
|
206
|
+
// Whatever the descent landed on becomes the join's right side (minus the
|
|
207
|
+
// correlated filter, when one was found).
|
|
208
|
+
const innerSource = current instanceof FilterNode ? current.source : current;
|
|
209
|
+
// Gate: the right side must actually expose the IN comparison target. A bare
|
|
210
|
+
// column projection preserves the source column's attribute id, but a
|
|
211
|
+
// COMPUTED projection (`select b.x + 0 …`) mints a fresh id that nothing
|
|
212
|
+
// below defines — the join condition would reference an attribute no side
|
|
213
|
+
// provides. Mirrors the SELECT-list sibling's key-attribute lookup.
|
|
214
|
+
const innerKeyIndex = innerSource.getAttributes().findIndex(a => a.id === innerFirstAttr.id);
|
|
215
|
+
if (innerKeyIndex < 0)
|
|
216
|
+
return null;
|
|
217
|
+
// Build the equi-join condition: outer.col = inner.firstCol. The inner
|
|
218
|
+
// reference gets its OWN AST — reusing the outer column's expression made
|
|
219
|
+
// EXPLAIN render every decorrelated IN condition as the nonsense `a.x = a.x`.
|
|
220
|
+
const innerColRef = new ColumnReferenceNode(outerColRef.scope, { type: 'column', name: innerFirstAttr.name }, innerFirstAttr.type, innerFirstAttr.id, innerKeyIndex);
|
|
221
|
+
// Reconcile the two operands exactly as a hand-written `=` would be. Building the
|
|
222
|
+
// BinaryOpNode raw bypassed the plan-build coercion every other comparison site gets,
|
|
223
|
+
// so `json_col in (select text_col …)` compared a native JS object against a string
|
|
224
|
+
// and never matched, and `int_col in (select text_col …)` compared a number against a
|
|
225
|
+
// string. `coerceComparisonSet` is the IN-shaped helper — the same one the IN
|
|
226
|
+
// value-list and simple-CASE builders call, and the same one `extractUncorrelatedIn`
|
|
227
|
+
// calls, so the two decorrelation arms and the runtime set probe cannot split.
|
|
228
|
+
//
|
|
229
|
+
// The now-CastNode-wrapped side demotes this conjunct out of `extractEquiPairs`
|
|
230
|
+
// (which requires two bare column references) into the join's residual — the correct
|
|
231
|
+
// destination, since the residual is where `=`'s own semantics apply, and the join
|
|
232
|
+
// still keys on the genuine correlation pair.
|
|
233
|
+
const [coercedOuter, [coercedInner]] = coerceComparisonSet(outerColRef.scope, outerColRef, [innerColRef]);
|
|
234
|
+
const equiCondition = new BinaryOpNode(outerColRef.scope, { type: 'binary', operator: '=', left: coercedOuter.expression, right: coercedInner.expression }, coercedOuter, coercedInner);
|
|
178
235
|
// If there's a filter with additional correlation, extract it
|
|
179
236
|
if (current instanceof FilterNode) {
|
|
180
|
-
const innerAttrIds = collectDefinedAttrIds(
|
|
237
|
+
const innerAttrIds = collectDefinedAttrIds(innerSource);
|
|
181
238
|
const conjuncts = splitConjuncts(current.predicate);
|
|
182
239
|
const additionalCorrelation = [];
|
|
183
240
|
const innerOnly = [];
|
|
@@ -199,77 +256,193 @@ function extractInCorrelation(inNode, outerAttrIds) {
|
|
|
199
256
|
const correlationCondition = combineConjuncts(allCorrelation);
|
|
200
257
|
const residualInnerFilter = combineConjuncts(innerOnly);
|
|
201
258
|
return {
|
|
202
|
-
innerSource
|
|
259
|
+
innerSource,
|
|
203
260
|
correlationCondition,
|
|
204
261
|
residualInnerFilter,
|
|
205
262
|
};
|
|
206
263
|
}
|
|
207
264
|
// No inner filter — the only correlation is the IN condition itself
|
|
208
265
|
return {
|
|
209
|
-
innerSource
|
|
266
|
+
innerSource,
|
|
210
267
|
correlationCondition: equiCondition,
|
|
211
268
|
residualInnerFilter: null,
|
|
212
269
|
};
|
|
213
270
|
}
|
|
214
|
-
|
|
215
|
-
|
|
271
|
+
/**
|
|
272
|
+
* Recognize an uncorrelated filter-position `col IN (subquery)` and build the
|
|
273
|
+
* semi-join right side + condition. Every gate declines to `null`, leaving the
|
|
274
|
+
* InNode on the runtime set-probe path (`emitIn`) — the O(K + N·log K) floor;
|
|
275
|
+
* a decline only costs the better plan, never correctness.
|
|
276
|
+
*/
|
|
277
|
+
function extractUncorrelatedIn(inNode, outerAttrIds) {
|
|
278
|
+
const right = inNode.source;
|
|
279
|
+
if (!right)
|
|
280
|
+
return null;
|
|
281
|
+
// Gate: bare outer column on the left of IN. A non-column left side would
|
|
282
|
+
// fail the equi-pair gate below anyway; requiring it up front keeps the
|
|
283
|
+
// decline reason in one place.
|
|
284
|
+
if (!(inNode.condition instanceof ColumnReferenceNode))
|
|
285
|
+
return null;
|
|
286
|
+
const outerColRef = inNode.condition;
|
|
287
|
+
if (!outerAttrIds.has(outerColRef.attributeId))
|
|
216
288
|
return null;
|
|
289
|
+
// Gate: mirror the runtime set probe's own admission (emitIn's uncorrelated
|
|
290
|
+
// arm requires a functional source) so the two paths accept the same shapes.
|
|
291
|
+
// A non-deterministic inner must keep its per-outer-row evaluation
|
|
292
|
+
// semantics; side effects were already refused by the caller.
|
|
293
|
+
if (!PlanNodeCharacteristics.isFunctional(right))
|
|
294
|
+
return null;
|
|
295
|
+
// Build-time validation guarantees an IN subquery exposes exactly one column.
|
|
296
|
+
const innerAttr = right.getAttributes()[0];
|
|
297
|
+
if (!innerAttr)
|
|
298
|
+
return null;
|
|
299
|
+
// Synthesize `outer.col = inner.col0`, giving the inner reference its OWN
|
|
300
|
+
// AST rather than reusing the outer column's expression (which is why the
|
|
301
|
+
// correlated arm's condition renders as the nonsense `a.x = a.x` in EXPLAIN).
|
|
302
|
+
const innerColRef = new ColumnReferenceNode(outerColRef.scope, { type: 'column', name: innerAttr.name }, innerAttr.type, innerAttr.id, 0 // column index in the inner relation
|
|
303
|
+
);
|
|
304
|
+
// Reconcile the operands exactly as a hand-written `=` would be, for the same
|
|
305
|
+
// reason `extractInCorrelation` does: this raw BinaryOpNode bypasses the coercion
|
|
306
|
+
// `buildExpression` applies. When a cast IS inserted (a JSON/TEXT or numeric/TEXT
|
|
307
|
+
// pair), the wrapper fails the equi-pair gate below and the conjunct declines to
|
|
308
|
+
// the set-probe path — whose `inMembershipKeys` performs the same conversion per
|
|
309
|
+
// row. That decline is the point: a hash join keyed on the RAW values would rank a
|
|
310
|
+
// number against a numeric-looking string by storage class and match nothing,
|
|
311
|
+
// disagreeing with every other spelling of the comparison.
|
|
312
|
+
// NOTE: the decline costs the hash-join shape for cross-type uncorrelated INs — the
|
|
313
|
+
// set probe materializes the inner side once and probes per outer row, which is fine
|
|
314
|
+
// at current inner sizes. If a cross-type `col in (select …)` over a large inner ever
|
|
315
|
+
// shows up as slow, the move is a coercion-aware equi-pair (key both sides on the
|
|
316
|
+
// cast result), not reverting the coercion.
|
|
317
|
+
const [coercedOuter, [coercedInner]] = coerceComparisonSet(outerColRef.scope, outerColRef, [innerColRef]);
|
|
318
|
+
const condition = new BinaryOpNode(outerColRef.scope, { type: 'binary', operator: '=', left: coercedOuter.expression, right: coercedInner.expression }, coercedOuter, coercedInner);
|
|
319
|
+
// Gate (the O(N×K)-floor guardrail): the synthesized condition must survive
|
|
320
|
+
// the SAME extraction rule-join-physical-selection uses, as exactly one
|
|
321
|
+
// equi-pair with no residual. That is that rule's only structural
|
|
322
|
+
// precondition, so the join always reaches its hash/merge/NL cost
|
|
323
|
+
// comparison with a usable hashable pair — the inner side is materialized
|
|
324
|
+
// once (hash build) rather than re-driven per outer row. This also folds in
|
|
325
|
+
// the collation-conflict and semantic-ordering-agreement declines (a mixed
|
|
326
|
+
// TIMESPAN/TEXT pair keeps the set-probe path, whose semanticKeyTransform
|
|
327
|
+
// handles it).
|
|
328
|
+
// NOTE: nested loop can still win the cost comparison when the OUTER is
|
|
329
|
+
// tiny (L ≤ ~2 under current COST_CONSTANTS); rule-nested-loop-right-cache
|
|
330
|
+
// then wraps the uncorrelated right in a CacheNode whose abandon threshold
|
|
331
|
+
// is the subject of backlog/bug-cache-threshold-abandon-cliff. A one-row
|
|
332
|
+
// outer scanning the inner once is not a cliff, so that interaction is
|
|
333
|
+
// confined to the harmless quadrant.
|
|
334
|
+
const rightAttrIds = new Set(right.getAttributes().map(a => a.id));
|
|
335
|
+
const extraction = extractEquiPairs(condition, outerAttrIds, rightAttrIds);
|
|
336
|
+
if (!extraction || extraction.equiPairs.length !== 1 || extraction.residual)
|
|
337
|
+
return null;
|
|
338
|
+
// Gate: the IN membership collation and the synthesized `=`'s collation
|
|
339
|
+
// must both resolve (no conflict) to the SAME name — a divergence would
|
|
340
|
+
// silently change which rows match. `inRhsTypes` reduces a subquery RHS to
|
|
341
|
+
// its single output column's type, the same operand pair the `=` sees, so
|
|
342
|
+
// agreement is expected; assert it rather than assume it.
|
|
343
|
+
const inResolution = resolveInCollationForNode(inNode);
|
|
344
|
+
const eqResolution = resolveComparisonCollation(inNode.condition.getType(), innerAttr.type);
|
|
345
|
+
if (inResolution.kind !== 'resolved' || eqResolution.kind !== 'resolved'
|
|
346
|
+
|| inResolution.name !== eqResolution.name) {
|
|
347
|
+
return null;
|
|
348
|
+
}
|
|
349
|
+
return { right, condition };
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Decorrelate the first rewritable EXISTS/IN conjunct of one FilterNode.
|
|
353
|
+
* Returns the semi/anti join (with any remaining conjuncts re-wrapped in a
|
|
354
|
+
* FilterNode above it), or null when no conjunct rewrites.
|
|
355
|
+
*/
|
|
356
|
+
function decorrelateOneConjunct(node) {
|
|
217
357
|
const outerSource = node.source;
|
|
218
358
|
const outerAttrIds = new Set(outerSource.getAttributes().map(a => a.id));
|
|
219
359
|
// Split the filter predicate into conjuncts
|
|
220
360
|
const conjuncts = splitConjuncts(node.predicate);
|
|
221
|
-
// Find the first decorrelation candidate
|
|
222
|
-
let candidateIndex = -1;
|
|
223
|
-
let candidate = null;
|
|
224
361
|
for (let i = 0; i < conjuncts.length; i++) {
|
|
225
|
-
candidate = identifyCandidate(conjuncts[i]);
|
|
226
|
-
if (candidate)
|
|
227
|
-
|
|
228
|
-
|
|
362
|
+
const candidate = identifyCandidate(conjuncts[i]);
|
|
363
|
+
if (!candidate)
|
|
364
|
+
continue;
|
|
365
|
+
// Decorrelating EXISTS/IN into a semi/anti join changes how many times the
|
|
366
|
+
// inner subquery's subtree executes (per outer row → once per matching outer
|
|
367
|
+
// row in the semi-join driver). Refuse on impure inners so DML-bearing
|
|
368
|
+
// subqueries keep their declared per-row firing.
|
|
369
|
+
const innerRoot = candidate.subqueryNode instanceof ExistsNode
|
|
370
|
+
? candidate.subqueryNode.subquery
|
|
371
|
+
: candidate.subqueryNode.source;
|
|
372
|
+
if (innerRoot && PlanNodeCharacteristics.subtreeHasSideEffects(innerRoot)) {
|
|
373
|
+
log('Decorrelation skipped: inner subquery has side effects');
|
|
374
|
+
continue;
|
|
229
375
|
}
|
|
376
|
+
log('Found %s decorrelation candidate in filter predicate', candidate.joinType);
|
|
377
|
+
// Extract the join right side and condition based on the candidate arm
|
|
378
|
+
let joinRight;
|
|
379
|
+
let joinCondition;
|
|
380
|
+
if (candidate.uncorrelatedIn) {
|
|
381
|
+
const rewrite = extractUncorrelatedIn(candidate.subqueryNode, outerAttrIds);
|
|
382
|
+
if (!rewrite) {
|
|
383
|
+
log('Uncorrelated IN gates declined; conjunct keeps the set-probe path');
|
|
384
|
+
continue;
|
|
385
|
+
}
|
|
386
|
+
joinRight = rewrite.right;
|
|
387
|
+
joinCondition = rewrite.condition;
|
|
388
|
+
}
|
|
389
|
+
else {
|
|
390
|
+
const extraction = candidate.subqueryNode instanceof ExistsNode
|
|
391
|
+
? extractExistsCorrelation(candidate.subqueryNode.subquery, outerAttrIds)
|
|
392
|
+
: extractInCorrelation(candidate.subqueryNode, outerAttrIds);
|
|
393
|
+
if (!extraction) {
|
|
394
|
+
log('Could not extract simple equi-correlation; skipping conjunct');
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
// If there are residual inner-only predicates, wrap the inner in a FilterNode
|
|
398
|
+
joinRight = extraction.residualInnerFilter
|
|
399
|
+
? new FilterNode(extraction.innerSource.scope, extraction.innerSource, extraction.residualInnerFilter)
|
|
400
|
+
: extraction.innerSource;
|
|
401
|
+
joinCondition = extraction.correlationCondition;
|
|
402
|
+
// Correctness backstop: every correlation must have been LIFTED into the
|
|
403
|
+
// join condition. Anything still referencing an outer scope from inside
|
|
404
|
+
// the right side would be driven as if uncorrelated (a semi join does not
|
|
405
|
+
// re-establish the outer row context), so decline and leave the conjunct
|
|
406
|
+
// on the per-row path. Mirrors the SELECT-list sibling's
|
|
407
|
+
// `collectExternalReferences(distinctRight)` check.
|
|
408
|
+
if (collectExternalReferences(joinRight).size !== 0) {
|
|
409
|
+
log('Right side retains external references after extraction; skipping conjunct');
|
|
410
|
+
continue;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
// Build the semi/anti join
|
|
414
|
+
const joinNode = new JoinNode(outerSource.scope, outerSource, joinRight, candidate.joinType, joinCondition);
|
|
415
|
+
log('Decorrelated %s subquery into %s JOIN', candidate.subqueryNode.nodeType, candidate.joinType.toUpperCase());
|
|
416
|
+
// If there are remaining conjuncts in the original filter, wrap in a new FilterNode
|
|
417
|
+
const remainingConjuncts = conjuncts.filter((_, j) => j !== i);
|
|
418
|
+
if (remainingConjuncts.length > 0) {
|
|
419
|
+
const residualPredicate = combineConjuncts(remainingConjuncts);
|
|
420
|
+
return new FilterNode(node.scope, joinNode, residualPredicate);
|
|
421
|
+
}
|
|
422
|
+
return joinNode;
|
|
230
423
|
}
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
// row in the semi-join driver). Refuse on impure inners so DML-bearing
|
|
236
|
-
// subqueries keep their declared per-row firing.
|
|
237
|
-
const innerRoot = candidate.subqueryNode instanceof ExistsNode
|
|
238
|
-
? candidate.subqueryNode.subquery
|
|
239
|
-
: candidate.subqueryNode.source;
|
|
240
|
-
if (innerRoot && PlanNodeCharacteristics.subtreeHasSideEffects(innerRoot)) {
|
|
241
|
-
log('Decorrelation skipped: inner subquery has side effects');
|
|
242
|
-
return null;
|
|
243
|
-
}
|
|
244
|
-
log('Found %s decorrelation candidate in filter predicate', candidate.joinType);
|
|
245
|
-
// Extract correlation info based on subquery type
|
|
246
|
-
let extraction = null;
|
|
247
|
-
if (candidate.subqueryNode instanceof ExistsNode) {
|
|
248
|
-
extraction = extractExistsCorrelation(candidate.subqueryNode.subquery, outerAttrIds);
|
|
249
|
-
}
|
|
250
|
-
else if (candidate.subqueryNode instanceof InNode) {
|
|
251
|
-
extraction = extractInCorrelation(candidate.subqueryNode, outerAttrIds);
|
|
252
|
-
}
|
|
253
|
-
if (!extraction) {
|
|
254
|
-
log('Could not extract simple equi-correlation; skipping decorrelation');
|
|
424
|
+
return null;
|
|
425
|
+
}
|
|
426
|
+
export function ruleSubqueryDecorrelation(node, _context) {
|
|
427
|
+
if (!(node instanceof FilterNode))
|
|
255
428
|
return null;
|
|
429
|
+
// The engine never re-offers a rule its own output (see pass.ts), so loop
|
|
430
|
+
// internally: each iteration decorrelates ONE conjunct, and a residual
|
|
431
|
+
// FilterNode output feeds the next iteration. Two IN conjuncts in one WHERE
|
|
432
|
+
// thus become two stacked semi joins (the second joins against the first as
|
|
433
|
+
// its outer). Terminates: every successful iteration removes one conjunct.
|
|
434
|
+
let result = null;
|
|
435
|
+
let current = node;
|
|
436
|
+
for (;;) {
|
|
437
|
+
const rewritten = decorrelateOneConjunct(current);
|
|
438
|
+
if (!rewritten)
|
|
439
|
+
break;
|
|
440
|
+
result = rewritten;
|
|
441
|
+
if (!(rewritten instanceof FilterNode))
|
|
442
|
+
break;
|
|
443
|
+
current = rewritten;
|
|
256
444
|
}
|
|
257
|
-
|
|
258
|
-
// Build the inner side: if there are residual inner-only predicates, wrap in FilterNode
|
|
259
|
-
let joinRight = innerSource;
|
|
260
|
-
if (residualInnerFilter) {
|
|
261
|
-
joinRight = new FilterNode(innerSource.scope, innerSource, residualInnerFilter);
|
|
262
|
-
}
|
|
263
|
-
// Build the semi/anti join
|
|
264
|
-
const joinNode = new JoinNode(outerSource.scope, outerSource, joinRight, candidate.joinType, correlationCondition);
|
|
265
|
-
log('Decorrelated %s subquery into %s JOIN', candidate.subqueryNode.nodeType, candidate.joinType.toUpperCase());
|
|
266
|
-
// If there are remaining conjuncts in the original filter, wrap in a new FilterNode
|
|
267
|
-
const remainingConjuncts = conjuncts.filter((_, i) => i !== candidateIndex);
|
|
268
|
-
if (remainingConjuncts.length > 0) {
|
|
269
|
-
const residualPredicate = combineConjuncts(remainingConjuncts);
|
|
270
|
-
return new FilterNode(node.scope, joinNode, residualPredicate);
|
|
271
|
-
}
|
|
272
|
-
return joinNode;
|
|
445
|
+
return result;
|
|
273
446
|
}
|
|
274
447
|
/* ────────────────────────────────────────────────────────────────────────────
|
|
275
448
|
* SELECT-list EXISTS / IN decorrelation (existence-flag left join)
|