@quereus/quereus 3.2.1 → 4.0.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 +7 -0
- package/dist/src/common/datatype.d.ts +12 -0
- package/dist/src/common/datatype.d.ts.map +1 -1
- package/dist/src/common/datatype.js.map +1 -1
- package/dist/src/common/types.d.ts +24 -0
- package/dist/src/common/types.d.ts.map +1 -1
- package/dist/src/common/types.js.map +1 -1
- package/dist/src/core/database-assertions.d.ts +37 -9
- package/dist/src/core/database-assertions.d.ts.map +1 -1
- package/dist/src/core/database-assertions.js +62 -106
- package/dist/src/core/database-assertions.js.map +1 -1
- package/dist/src/core/database-events.d.ts +163 -0
- package/dist/src/core/database-events.d.ts.map +1 -1
- package/dist/src/core/database-events.js +235 -21
- package/dist/src/core/database-events.js.map +1 -1
- package/dist/src/core/database-external-changes.d.ts +28 -0
- package/dist/src/core/database-external-changes.d.ts.map +1 -0
- package/dist/src/core/database-external-changes.js +242 -0
- package/dist/src/core/database-external-changes.js.map +1 -0
- package/dist/src/core/database-internal.d.ts +50 -1
- package/dist/src/core/database-internal.d.ts.map +1 -1
- package/dist/src/core/database-materialized-views.d.ts +1253 -0
- package/dist/src/core/database-materialized-views.d.ts.map +1 -0
- package/dist/src/core/database-materialized-views.js +3064 -0
- package/dist/src/core/database-materialized-views.js.map +1 -0
- package/dist/src/core/database-options.d.ts +4 -0
- package/dist/src/core/database-options.d.ts.map +1 -1
- package/dist/src/core/database-options.js +10 -0
- package/dist/src/core/database-options.js.map +1 -1
- package/dist/src/core/database-transaction.d.ts +19 -3
- package/dist/src/core/database-transaction.d.ts.map +1 -1
- package/dist/src/core/database-transaction.js +30 -3
- package/dist/src/core/database-transaction.js.map +1 -1
- package/dist/src/core/database-watchers.d.ts +19 -0
- package/dist/src/core/database-watchers.d.ts.map +1 -1
- package/dist/src/core/database-watchers.js +63 -3
- package/dist/src/core/database-watchers.js.map +1 -1
- package/dist/src/core/database.d.ts +203 -11
- package/dist/src/core/database.d.ts.map +1 -1
- package/dist/src/core/database.js +493 -29
- package/dist/src/core/database.js.map +1 -1
- package/dist/src/core/derived-row-validator.d.ts +137 -0
- package/dist/src/core/derived-row-validator.d.ts.map +1 -0
- package/dist/src/core/derived-row-validator.js +314 -0
- package/dist/src/core/derived-row-validator.js.map +1 -0
- package/dist/src/core/statement.d.ts.map +1 -1
- package/dist/src/core/statement.js +30 -9
- package/dist/src/core/statement.js.map +1 -1
- package/dist/src/emit/ast-stringify.d.ts +135 -1
- package/dist/src/emit/ast-stringify.d.ts.map +1 -1
- package/dist/src/emit/ast-stringify.js +795 -120
- package/dist/src/emit/ast-stringify.js.map +1 -1
- package/dist/src/func/builtins/aggregate.d.ts.map +1 -1
- package/dist/src/func/builtins/aggregate.js +11 -10
- 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 +32 -0
- package/dist/src/func/builtins/builtin-window-functions.js.map +1 -1
- package/dist/src/func/builtins/explain.d.ts +3 -0
- package/dist/src/func/builtins/explain.d.ts.map +1 -1
- package/dist/src/func/builtins/explain.js +229 -0
- package/dist/src/func/builtins/explain.js.map +1 -1
- package/dist/src/func/builtins/index.d.ts.map +1 -1
- package/dist/src/func/builtins/index.js +10 -2
- package/dist/src/func/builtins/index.js.map +1 -1
- package/dist/src/func/builtins/json.d.ts.map +1 -1
- package/dist/src/func/builtins/json.js +3 -2
- package/dist/src/func/builtins/json.js.map +1 -1
- package/dist/src/func/builtins/mutation.d.ts +2 -0
- package/dist/src/func/builtins/mutation.d.ts.map +1 -0
- package/dist/src/func/builtins/mutation.js +53 -0
- package/dist/src/func/builtins/mutation.js.map +1 -0
- package/dist/src/func/builtins/schema.d.ts +2 -0
- package/dist/src/func/builtins/schema.d.ts.map +1 -1
- package/dist/src/func/builtins/schema.js +713 -26
- package/dist/src/func/builtins/schema.js.map +1 -1
- package/dist/src/func/builtins/string.js +1 -1
- package/dist/src/func/builtins/string.js.map +1 -1
- package/dist/src/func/registration.d.ts +9 -0
- package/dist/src/func/registration.d.ts.map +1 -1
- package/dist/src/func/registration.js +4 -0
- package/dist/src/func/registration.js.map +1 -1
- package/dist/src/index.d.ts +25 -6
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +27 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/parser/ast.d.ts +353 -21
- package/dist/src/parser/ast.d.ts.map +1 -1
- package/dist/src/parser/index.d.ts +14 -1
- package/dist/src/parser/index.d.ts.map +1 -1
- package/dist/src/parser/index.js +19 -0
- package/dist/src/parser/index.js.map +1 -1
- package/dist/src/parser/lexer.d.ts +9 -0
- package/dist/src/parser/lexer.d.ts.map +1 -1
- package/dist/src/parser/lexer.js +9 -0
- package/dist/src/parser/lexer.js.map +1 -1
- package/dist/src/parser/parser.d.ts +277 -8
- package/dist/src/parser/parser.d.ts.map +1 -1
- package/dist/src/parser/parser.js +1393 -471
- package/dist/src/parser/parser.js.map +1 -1
- package/dist/src/parser/visitor.d.ts.map +1 -1
- package/dist/src/parser/visitor.js +12 -8
- package/dist/src/parser/visitor.js.map +1 -1
- package/dist/src/planner/analysis/assertion-classifier.d.ts.map +1 -1
- package/dist/src/planner/analysis/assertion-classifier.js +4 -0
- package/dist/src/planner/analysis/assertion-classifier.js.map +1 -1
- package/dist/src/planner/analysis/assertion-hoist-cache.d.ts.map +1 -1
- package/dist/src/planner/analysis/assertion-hoist-cache.js +8 -4
- package/dist/src/planner/analysis/assertion-hoist-cache.js.map +1 -1
- package/dist/src/planner/analysis/authored-inverse.d.ts +22 -0
- package/dist/src/planner/analysis/authored-inverse.d.ts.map +1 -0
- package/dist/src/planner/analysis/authored-inverse.js +267 -0
- package/dist/src/planner/analysis/authored-inverse.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 +34 -4
- package/dist/src/planner/analysis/change-scope.d.ts.map +1 -1
- package/dist/src/planner/analysis/change-scope.js +115 -7
- package/dist/src/planner/analysis/change-scope.js.map +1 -1
- package/dist/src/planner/analysis/check-extraction.d.ts +36 -2
- package/dist/src/planner/analysis/check-extraction.d.ts.map +1 -1
- package/dist/src/planner/analysis/check-extraction.js +174 -46
- package/dist/src/planner/analysis/check-extraction.js.map +1 -1
- package/dist/src/planner/analysis/coarsened-key.d.ts +109 -0
- package/dist/src/planner/analysis/coarsened-key.d.ts.map +1 -0
- package/dist/src/planner/analysis/coarsened-key.js +228 -0
- package/dist/src/planner/analysis/coarsened-key.js.map +1 -0
- package/dist/src/planner/analysis/comparison-collation.d.ts +216 -0
- package/dist/src/planner/analysis/comparison-collation.d.ts.map +1 -0
- package/dist/src/planner/analysis/comparison-collation.js +341 -0
- package/dist/src/planner/analysis/comparison-collation.js.map +1 -0
- package/dist/src/planner/analysis/constraint-extractor.d.ts +13 -1
- package/dist/src/planner/analysis/constraint-extractor.d.ts.map +1 -1
- package/dist/src/planner/analysis/constraint-extractor.js +220 -21
- package/dist/src/planner/analysis/constraint-extractor.js.map +1 -1
- package/dist/src/planner/analysis/coverage-prover.d.ts +321 -0
- package/dist/src/planner/analysis/coverage-prover.d.ts.map +1 -0
- package/dist/src/planner/analysis/coverage-prover.js +1038 -0
- package/dist/src/planner/analysis/coverage-prover.js.map +1 -0
- package/dist/src/planner/analysis/key-filter.d.ts +22 -0
- package/dist/src/planner/analysis/key-filter.d.ts.map +1 -0
- package/dist/src/planner/analysis/key-filter.js +105 -0
- package/dist/src/planner/analysis/key-filter.js.map +1 -0
- package/dist/src/planner/analysis/partial-unique-extraction.d.ts +36 -1
- package/dist/src/planner/analysis/partial-unique-extraction.d.ts.map +1 -1
- package/dist/src/planner/analysis/partial-unique-extraction.js +148 -22
- package/dist/src/planner/analysis/partial-unique-extraction.js.map +1 -1
- package/dist/src/planner/analysis/predicate-normalizer.d.ts.map +1 -1
- package/dist/src/planner/analysis/predicate-normalizer.js +30 -1
- package/dist/src/planner/analysis/predicate-normalizer.js.map +1 -1
- package/dist/src/planner/analysis/predicate-shape.d.ts +36 -1
- package/dist/src/planner/analysis/predicate-shape.d.ts.map +1 -1
- package/dist/src/planner/analysis/predicate-shape.js +51 -13
- package/dist/src/planner/analysis/predicate-shape.js.map +1 -1
- package/dist/src/planner/analysis/query-rewrite-matcher.d.ts +314 -0
- package/dist/src/planner/analysis/query-rewrite-matcher.d.ts.map +1 -0
- package/dist/src/planner/analysis/query-rewrite-matcher.js +1081 -0
- package/dist/src/planner/analysis/query-rewrite-matcher.js.map +1 -0
- package/dist/src/planner/analysis/scalar-invertibility.d.ts +92 -0
- package/dist/src/planner/analysis/scalar-invertibility.d.ts.map +1 -0
- package/dist/src/planner/analysis/scalar-invertibility.js +129 -0
- package/dist/src/planner/analysis/scalar-invertibility.js.map +1 -0
- package/dist/src/planner/analysis/update-lineage.d.ts +196 -0
- package/dist/src/planner/analysis/update-lineage.d.ts.map +1 -0
- package/dist/src/planner/analysis/update-lineage.js +322 -0
- package/dist/src/planner/analysis/update-lineage.js.map +1 -0
- package/dist/src/planner/analysis/view-complement.d.ts +42 -0
- package/dist/src/planner/analysis/view-complement.d.ts.map +1 -0
- package/dist/src/planner/analysis/view-complement.js +54 -0
- package/dist/src/planner/analysis/view-complement.js.map +1 -0
- package/dist/src/planner/building/alter-table.d.ts +1 -1
- package/dist/src/planner/building/alter-table.d.ts.map +1 -1
- package/dist/src/planner/building/alter-table.js +211 -2
- package/dist/src/planner/building/alter-table.js.map +1 -1
- package/dist/src/planner/building/block.d.ts.map +1 -1
- package/dist/src/planner/building/block.js +18 -1
- package/dist/src/planner/building/block.js.map +1 -1
- package/dist/src/planner/building/constraint-builder.d.ts +33 -5
- package/dist/src/planner/building/constraint-builder.d.ts.map +1 -1
- package/dist/src/planner/building/constraint-builder.js +63 -28
- package/dist/src/planner/building/constraint-builder.js.map +1 -1
- package/dist/src/planner/building/create-view.d.ts +9 -0
- package/dist/src/planner/building/create-view.d.ts.map +1 -1
- package/dist/src/planner/building/create-view.js +41 -12
- package/dist/src/planner/building/create-view.js.map +1 -1
- package/dist/src/planner/building/ddl.d.ts.map +1 -1
- package/dist/src/planner/building/ddl.js +94 -0
- package/dist/src/planner/building/ddl.js.map +1 -1
- package/dist/src/planner/building/declare-schema.d.ts +1 -0
- package/dist/src/planner/building/declare-schema.d.ts.map +1 -1
- package/dist/src/planner/building/declare-schema.js +4 -1
- package/dist/src/planner/building/declare-schema.js.map +1 -1
- package/dist/src/planner/building/default-scope.d.ts +26 -0
- package/dist/src/planner/building/default-scope.d.ts.map +1 -0
- package/dist/src/planner/building/default-scope.js +41 -0
- package/dist/src/planner/building/default-scope.js.map +1 -0
- package/dist/src/planner/building/delete.d.ts +19 -1
- package/dist/src/planner/building/delete.d.ts.map +1 -1
- package/dist/src/planner/building/delete.js +116 -34
- package/dist/src/planner/building/delete.js.map +1 -1
- package/dist/src/planner/building/dml-target.d.ts +118 -0
- package/dist/src/planner/building/dml-target.d.ts.map +1 -0
- package/dist/src/planner/building/dml-target.js +282 -0
- package/dist/src/planner/building/dml-target.js.map +1 -0
- package/dist/src/planner/building/drop-index.d.ts.map +1 -1
- package/dist/src/planner/building/drop-index.js +4 -1
- package/dist/src/planner/building/drop-index.js.map +1 -1
- package/dist/src/planner/building/drop-view.d.ts.map +1 -1
- package/dist/src/planner/building/drop-view.js +4 -2
- package/dist/src/planner/building/drop-view.js.map +1 -1
- package/dist/src/planner/building/expression.d.ts.map +1 -1
- package/dist/src/planner/building/expression.js +60 -21
- package/dist/src/planner/building/expression.js.map +1 -1
- package/dist/src/planner/building/foreign-key-builder.d.ts +30 -0
- package/dist/src/planner/building/foreign-key-builder.d.ts.map +1 -1
- package/dist/src/planner/building/foreign-key-builder.js +160 -129
- package/dist/src/planner/building/foreign-key-builder.js.map +1 -1
- package/dist/src/planner/building/insert.d.ts +45 -2
- package/dist/src/planner/building/insert.d.ts.map +1 -1
- package/dist/src/planner/building/insert.js +257 -88
- package/dist/src/planner/building/insert.js.map +1 -1
- package/dist/src/planner/building/lens-auxiliary-access.d.ts +22 -0
- package/dist/src/planner/building/lens-auxiliary-access.d.ts.map +1 -0
- package/dist/src/planner/building/lens-auxiliary-access.js +132 -0
- package/dist/src/planner/building/lens-auxiliary-access.js.map +1 -0
- package/dist/src/planner/building/materialized-view.d.ts +16 -0
- package/dist/src/planner/building/materialized-view.d.ts.map +1 -0
- package/dist/src/planner/building/materialized-view.js +57 -0
- package/dist/src/planner/building/materialized-view.js.map +1 -0
- package/dist/src/planner/building/returning-star.d.ts +32 -0
- package/dist/src/planner/building/returning-star.d.ts.map +1 -0
- package/dist/src/planner/building/returning-star.js +45 -0
- package/dist/src/planner/building/returning-star.js.map +1 -0
- package/dist/src/planner/building/select-aggregates.d.ts.map +1 -1
- package/dist/src/planner/building/select-aggregates.js +51 -13
- 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 +84 -11
- package/dist/src/planner/building/select-compound.js.map +1 -1
- package/dist/src/planner/building/select-context.d.ts +10 -2
- package/dist/src/planner/building/select-context.d.ts.map +1 -1
- package/dist/src/planner/building/select-context.js +7 -1
- package/dist/src/planner/building/select-context.js.map +1 -1
- package/dist/src/planner/building/select-modifiers.js +6 -0
- package/dist/src/planner/building/select-modifiers.js.map +1 -1
- package/dist/src/planner/building/select-ordinal.d.ts +18 -0
- package/dist/src/planner/building/select-ordinal.d.ts.map +1 -1
- package/dist/src/planner/building/select-ordinal.js +30 -0
- package/dist/src/planner/building/select-ordinal.js.map +1 -1
- package/dist/src/planner/building/select-projections.d.ts +8 -2
- package/dist/src/planner/building/select-projections.d.ts.map +1 -1
- package/dist/src/planner/building/select-projections.js +26 -4
- package/dist/src/planner/building/select-projections.js.map +1 -1
- package/dist/src/planner/building/select-window.d.ts.map +1 -1
- package/dist/src/planner/building/select-window.js +8 -5
- package/dist/src/planner/building/select-window.js.map +1 -1
- package/dist/src/planner/building/select.d.ts.map +1 -1
- package/dist/src/planner/building/select.js +164 -59
- package/dist/src/planner/building/select.js.map +1 -1
- package/dist/src/planner/building/set-object-tags.d.ts +7 -0
- package/dist/src/planner/building/set-object-tags.d.ts.map +1 -0
- package/dist/src/planner/building/set-object-tags.js +38 -0
- package/dist/src/planner/building/set-object-tags.js.map +1 -0
- package/dist/src/planner/building/tag-diagnostics.d.ts +27 -0
- package/dist/src/planner/building/tag-diagnostics.d.ts.map +1 -0
- package/dist/src/planner/building/tag-diagnostics.js +37 -0
- package/dist/src/planner/building/tag-diagnostics.js.map +1 -0
- package/dist/src/planner/building/update.d.ts +18 -1
- package/dist/src/planner/building/update.d.ts.map +1 -1
- package/dist/src/planner/building/update.js +134 -58
- package/dist/src/planner/building/update.js.map +1 -1
- package/dist/src/planner/building/view-mutation-builder.d.ts +15 -0
- package/dist/src/planner/building/view-mutation-builder.d.ts.map +1 -0
- package/dist/src/planner/building/view-mutation-builder.js +1158 -0
- package/dist/src/planner/building/view-mutation-builder.js.map +1 -0
- package/dist/src/planner/building/with.d.ts +11 -0
- package/dist/src/planner/building/with.d.ts.map +1 -1
- package/dist/src/planner/building/with.js +48 -10
- package/dist/src/planner/building/with.js.map +1 -1
- package/dist/src/planner/cost/index.d.ts +83 -0
- package/dist/src/planner/cost/index.d.ts.map +1 -1
- package/dist/src/planner/cost/index.js +114 -0
- package/dist/src/planner/cost/index.js.map +1 -1
- package/dist/src/planner/framework/characteristics.d.ts +38 -4
- package/dist/src/planner/framework/characteristics.d.ts.map +1 -1
- package/dist/src/planner/framework/characteristics.js +50 -6
- package/dist/src/planner/framework/characteristics.js.map +1 -1
- package/dist/src/planner/framework/pass.d.ts.map +1 -1
- package/dist/src/planner/framework/pass.js +2 -1
- package/dist/src/planner/framework/pass.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/framework/registry.d.ts +39 -1
- package/dist/src/planner/framework/registry.d.ts.map +1 -1
- package/dist/src/planner/framework/registry.js +18 -2
- package/dist/src/planner/framework/registry.js.map +1 -1
- package/dist/src/planner/mutation/backward-body.d.ts +131 -0
- package/dist/src/planner/mutation/backward-body.d.ts.map +1 -0
- package/dist/src/planner/mutation/backward-body.js +135 -0
- package/dist/src/planner/mutation/backward-body.js.map +1 -0
- package/dist/src/planner/mutation/cte-flatten.d.ts +17 -0
- package/dist/src/planner/mutation/cte-flatten.d.ts.map +1 -0
- package/dist/src/planner/mutation/cte-flatten.js +364 -0
- package/dist/src/planner/mutation/cte-flatten.js.map +1 -0
- package/dist/src/planner/mutation/decomposition.d.ts +273 -0
- package/dist/src/planner/mutation/decomposition.d.ts.map +1 -0
- package/dist/src/planner/mutation/decomposition.js +1719 -0
- package/dist/src/planner/mutation/decomposition.js.map +1 -0
- package/dist/src/planner/mutation/lens-enforcement.d.ts +165 -0
- package/dist/src/planner/mutation/lens-enforcement.d.ts.map +1 -0
- package/dist/src/planner/mutation/lens-enforcement.js +745 -0
- package/dist/src/planner/mutation/lens-enforcement.js.map +1 -0
- package/dist/src/planner/mutation/multi-source.d.ts +568 -0
- package/dist/src/planner/mutation/multi-source.d.ts.map +1 -0
- package/dist/src/planner/mutation/multi-source.js +2915 -0
- package/dist/src/planner/mutation/multi-source.js.map +1 -0
- package/dist/src/planner/mutation/mutation-diagnostic.d.ts +37 -0
- package/dist/src/planner/mutation/mutation-diagnostic.d.ts.map +1 -0
- package/dist/src/planner/mutation/mutation-diagnostic.js +24 -0
- package/dist/src/planner/mutation/mutation-diagnostic.js.map +1 -0
- package/dist/src/planner/mutation/mutation-tags.d.ts +33 -0
- package/dist/src/planner/mutation/mutation-tags.d.ts.map +1 -0
- package/dist/src/planner/mutation/mutation-tags.js +31 -0
- package/dist/src/planner/mutation/mutation-tags.js.map +1 -0
- package/dist/src/planner/mutation/propagate.d.ts +97 -0
- package/dist/src/planner/mutation/propagate.d.ts.map +1 -0
- package/dist/src/planner/mutation/propagate.js +220 -0
- package/dist/src/planner/mutation/propagate.js.map +1 -0
- package/dist/src/planner/mutation/scope-transform.d.ts +181 -0
- package/dist/src/planner/mutation/scope-transform.d.ts.map +1 -0
- package/dist/src/planner/mutation/scope-transform.js +574 -0
- package/dist/src/planner/mutation/scope-transform.js.map +1 -0
- package/dist/src/planner/mutation/set-op.d.ts +242 -0
- package/dist/src/planner/mutation/set-op.d.ts.map +1 -0
- package/dist/src/planner/mutation/set-op.js +1687 -0
- package/dist/src/planner/mutation/set-op.js.map +1 -0
- package/dist/src/planner/mutation/single-source.d.ts +261 -0
- package/dist/src/planner/mutation/single-source.d.ts.map +1 -0
- package/dist/src/planner/mutation/single-source.js +1096 -0
- package/dist/src/planner/mutation/single-source.js.map +1 -0
- 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 +11 -9
- 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 +5 -1
- package/dist/src/planner/nodes/alias-node.js.map +1 -1
- package/dist/src/planner/nodes/alter-table-node.d.ts +124 -1
- package/dist/src/planner/nodes/alter-table-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/alter-table-node.js +27 -0
- package/dist/src/planner/nodes/alter-table-node.js.map +1 -1
- package/dist/src/planner/nodes/analyze-node.d.ts +2 -1
- package/dist/src/planner/nodes/analyze-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/analyze-node.js +21 -1
- package/dist/src/planner/nodes/analyze-node.js.map +1 -1
- package/dist/src/planner/nodes/asserted-keys-node.d.ts +43 -0
- package/dist/src/planner/nodes/asserted-keys-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/asserted-keys-node.js +99 -0
- package/dist/src/planner/nodes/asserted-keys-node.js.map +1 -0
- package/dist/src/planner/nodes/async-gather-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/async-gather-node.js +33 -8
- package/dist/src/planner/nodes/async-gather-node.js.map +1 -1
- package/dist/src/planner/nodes/bloom-join-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/bloom-join-node.js +2 -1
- package/dist/src/planner/nodes/bloom-join-node.js.map +1 -1
- package/dist/src/planner/nodes/create-view-node.d.ts +7 -2
- package/dist/src/planner/nodes/create-view-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/create-view-node.js +4 -1
- package/dist/src/planner/nodes/create-view-node.js.map +1 -1
- package/dist/src/planner/nodes/declarative-schema.d.ts +13 -1
- package/dist/src/planner/nodes/declarative-schema.d.ts.map +1 -1
- package/dist/src/planner/nodes/declarative-schema.js +32 -0
- package/dist/src/planner/nodes/declarative-schema.js.map +1 -1
- package/dist/src/planner/nodes/distinct-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/distinct-node.js +2 -0
- package/dist/src/planner/nodes/distinct-node.js.map +1 -1
- package/dist/src/planner/nodes/dml-executor-node.d.ts +29 -1
- package/dist/src/planner/nodes/dml-executor-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/dml-executor-node.js +27 -3
- package/dist/src/planner/nodes/dml-executor-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 -0
- package/dist/src/planner/nodes/eager-prefetch-node.js.map +1 -1
- package/dist/src/planner/nodes/envelope-scan-node.d.ts +42 -0
- package/dist/src/planner/nodes/envelope-scan-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/envelope-scan-node.js +62 -0
- package/dist/src/planner/nodes/envelope-scan-node.js.map +1 -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 +11 -1
- 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 +63 -13
- package/dist/src/planner/nodes/filter.js.map +1 -1
- 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-node.d.ts +41 -1
- package/dist/src/planner/nodes/join-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/join-node.js +78 -8
- package/dist/src/planner/nodes/join-node.js.map +1 -1
- package/dist/src/planner/nodes/join-utils.d.ts +33 -6
- package/dist/src/planner/nodes/join-utils.d.ts.map +1 -1
- package/dist/src/planner/nodes/join-utils.js +131 -10
- package/dist/src/planner/nodes/join-utils.js.map +1 -1
- package/dist/src/planner/nodes/lens-auxiliary-access-node.d.ts +104 -0
- package/dist/src/planner/nodes/lens-auxiliary-access-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/lens-auxiliary-access-node.js +91 -0
- package/dist/src/planner/nodes/lens-auxiliary-access-node.js.map +1 -0
- 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 +52 -3
- package/dist/src/planner/nodes/limit-offset.js.map +1 -1
- package/dist/src/planner/nodes/materialized-view-nodes.d.ts +69 -0
- package/dist/src/planner/nodes/materialized-view-nodes.d.ts.map +1 -0
- package/dist/src/planner/nodes/materialized-view-nodes.js +111 -0
- package/dist/src/planner/nodes/materialized-view-nodes.js.map +1 -0
- package/dist/src/planner/nodes/merge-join-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/merge-join-node.js +2 -1
- package/dist/src/planner/nodes/merge-join-node.js.map +1 -1
- package/dist/src/planner/nodes/ordinal-slice-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/ordinal-slice-node.js +2 -0
- package/dist/src/planner/nodes/ordinal-slice-node.js.map +1 -1
- package/dist/src/planner/nodes/plan-node-type.d.ts +9 -0
- package/dist/src/planner/nodes/plan-node-type.d.ts.map +1 -1
- package/dist/src/planner/nodes/plan-node-type.js +9 -0
- package/dist/src/planner/nodes/plan-node-type.js.map +1 -1
- package/dist/src/planner/nodes/plan-node.d.ts +265 -5
- 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/pragma.d.ts +2 -1
- package/dist/src/planner/nodes/pragma.d.ts.map +1 -1
- package/dist/src/planner/nodes/pragma.js +12 -0
- package/dist/src/planner/nodes/pragma.js.map +1 -1
- package/dist/src/planner/nodes/project-node.d.ts +14 -1
- package/dist/src/planner/nodes/project-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/project-node.js +103 -16
- 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 +63 -30
- 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 +7 -0
- package/dist/src/planner/nodes/retrieve-node.js.map +1 -1
- package/dist/src/planner/nodes/returning-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/returning-node.js +10 -3
- package/dist/src/planner/nodes/returning-node.js.map +1 -1
- package/dist/src/planner/nodes/scalar.d.ts +20 -0
- package/dist/src/planner/nodes/scalar.d.ts.map +1 -1
- package/dist/src/planner/nodes/scalar.js +71 -14
- package/dist/src/planner/nodes/scalar.js.map +1 -1
- package/dist/src/planner/nodes/set-object-tags-node.d.ts +39 -0
- package/dist/src/planner/nodes/set-object-tags-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/set-object-tags-node.js +41 -0
- package/dist/src/planner/nodes/set-object-tags-node.js.map +1 -0
- package/dist/src/planner/nodes/set-operation-node.d.ts +123 -1
- package/dist/src/planner/nodes/set-operation-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/set-operation-node.js +302 -18
- package/dist/src/planner/nodes/set-operation-node.js.map +1 -1
- package/dist/src/planner/nodes/single-row.d.ts.map +1 -1
- package/dist/src/planner/nodes/single-row.js +3 -0
- package/dist/src/planner/nodes/single-row.js.map +1 -1
- package/dist/src/planner/nodes/sort.d.ts.map +1 -1
- package/dist/src/planner/nodes/sort.js +8 -7
- 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/subquery.d.ts +2 -0
- package/dist/src/planner/nodes/subquery.d.ts.map +1 -1
- package/dist/src/planner/nodes/subquery.js +18 -2
- package/dist/src/planner/nodes/subquery.js.map +1 -1
- package/dist/src/planner/nodes/table-access-nodes.d.ts.map +1 -1
- package/dist/src/planner/nodes/table-access-nodes.js +23 -3
- package/dist/src/planner/nodes/table-access-nodes.js.map +1 -1
- package/dist/src/planner/nodes/table-function-call.js +6 -0
- package/dist/src/planner/nodes/table-function-call.js.map +1 -1
- package/dist/src/planner/nodes/values-node.d.ts +3 -1
- package/dist/src/planner/nodes/values-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/values-node.js +26 -0
- package/dist/src/planner/nodes/values-node.js.map +1 -1
- package/dist/src/planner/nodes/view-mutation-node.d.ts +259 -0
- package/dist/src/planner/nodes/view-mutation-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/view-mutation-node.js +273 -0
- package/dist/src/planner/nodes/view-mutation-node.js.map +1 -0
- package/dist/src/planner/nodes/window-function.d.ts +17 -1
- package/dist/src/planner/nodes/window-function.d.ts.map +1 -1
- package/dist/src/planner/nodes/window-function.js +15 -1
- package/dist/src/planner/nodes/window-function.js.map +1 -1
- package/dist/src/planner/nodes/window-node.js +3 -3
- package/dist/src/planner/nodes/window-node.js.map +1 -1
- package/dist/src/planner/optimizer.d.ts.map +1 -1
- package/dist/src/planner/optimizer.js +372 -39
- package/dist/src/planner/optimizer.js.map +1 -1
- package/dist/src/planner/planning-context.d.ts +1 -1
- package/dist/src/planner/planning-context.d.ts.map +1 -1
- package/dist/src/planner/rules/access/lens-access-form-matcher.d.ts +70 -0
- package/dist/src/planner/rules/access/lens-access-form-matcher.d.ts.map +1 -0
- package/dist/src/planner/rules/access/lens-access-form-matcher.js +156 -0
- package/dist/src/planner/rules/access/lens-access-form-matcher.js.map +1 -0
- package/dist/src/planner/rules/access/rule-lens-auxiliary-access.d.ts +31 -0
- package/dist/src/planner/rules/access/rule-lens-auxiliary-access.d.ts.map +1 -0
- package/dist/src/planner/rules/access/rule-lens-auxiliary-access.js +176 -0
- package/dist/src/planner/rules/access/rule-lens-auxiliary-access.js.map +1 -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 +435 -37
- package/dist/src/planner/rules/access/rule-select-access-path.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 +56 -5
- package/dist/src/planner/rules/aggregate/rule-groupby-fd-simplification.js.map +1 -1
- package/dist/src/planner/rules/cache/rule-materialized-view-rewrite.d.ts +39 -0
- package/dist/src/planner/rules/cache/rule-materialized-view-rewrite.d.ts.map +1 -0
- package/dist/src/planner/rules/cache/rule-materialized-view-rewrite.js +616 -0
- package/dist/src/planner/rules/cache/rule-materialized-view-rewrite.js.map +1 -0
- package/dist/src/planner/rules/cache/rule-scalar-cse.d.ts.map +1 -1
- package/dist/src/planner/rules/cache/rule-scalar-cse.js +8 -1
- package/dist/src/planner/rules/cache/rule-scalar-cse.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.d.ts +36 -0
- package/dist/src/planner/rules/join/equi-pair-extractor.d.ts.map +1 -1
- package/dist/src/planner/rules/join/equi-pair-extractor.js +42 -5
- 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.map +1 -1
- package/dist/src/planner/rules/join/rule-fanout-batched-outer.js +10 -0
- package/dist/src/planner/rules/join/rule-fanout-batched-outer.js.map +1 -1
- package/dist/src/planner/rules/join/rule-fanout-lookup-join.js +25 -9
- package/dist/src/planner/rules/join/rule-fanout-lookup-join.js.map +1 -1
- package/dist/src/planner/rules/join/rule-inner-join-existence-recovery.d.ts +130 -0
- package/dist/src/planner/rules/join/rule-inner-join-existence-recovery.d.ts.map +1 -0
- package/dist/src/planner/rules/join/rule-inner-join-existence-recovery.js +206 -0
- package/dist/src/planner/rules/join/rule-inner-join-existence-recovery.js.map +1 -0
- package/dist/src/planner/rules/join/rule-join-elimination.d.ts +67 -14
- package/dist/src/planner/rules/join/rule-join-elimination.d.ts.map +1 -1
- package/dist/src/planner/rules/join/rule-join-elimination.js +81 -25
- package/dist/src/planner/rules/join/rule-join-elimination.js.map +1 -1
- package/dist/src/planner/rules/join/rule-join-existence-pruning.d.ts +84 -0
- package/dist/src/planner/rules/join/rule-join-existence-pruning.d.ts.map +1 -0
- package/dist/src/planner/rules/join/rule-join-existence-pruning.js +138 -0
- package/dist/src/planner/rules/join/rule-join-existence-pruning.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 +19 -1
- 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 +14 -2
- 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 +5 -2
- package/dist/src/planner/rules/join/rule-lateral-top1-asof.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 +4 -0
- package/dist/src/planner/rules/join/rule-monotonic-merge-join.js.map +1 -1
- package/dist/src/planner/rules/join/rule-quickpick-enumeration.d.ts.map +1 -1
- package/dist/src/planner/rules/join/rule-quickpick-enumeration.js +10 -0
- package/dist/src/planner/rules/join/rule-quickpick-enumeration.js.map +1 -1
- package/dist/src/planner/rules/join/rule-semijoin-existence-recovery.d.ts +286 -0
- package/dist/src/planner/rules/join/rule-semijoin-existence-recovery.d.ts.map +1 -0
- package/dist/src/planner/rules/join/rule-semijoin-existence-recovery.js +548 -0
- package/dist/src/planner/rules/join/rule-semijoin-existence-recovery.js.map +1 -0
- package/dist/src/planner/rules/parallel/rule-async-gather-union-all.d.ts.map +1 -1
- package/dist/src/planner/rules/parallel/rule-async-gather-union-all.js +9 -1
- package/dist/src/planner/rules/parallel/rule-async-gather-union-all.js.map +1 -1
- package/dist/src/planner/rules/parallel/rule-async-gather-zip-by-key.d.ts.map +1 -1
- package/dist/src/planner/rules/parallel/rule-async-gather-zip-by-key.js +7 -0
- package/dist/src/planner/rules/parallel/rule-async-gather-zip-by-key.js.map +1 -1
- package/dist/src/planner/rules/parallel/rule-eager-prefetch-probe.d.ts.map +1 -1
- package/dist/src/planner/rules/parallel/rule-eager-prefetch-probe.js +10 -1
- package/dist/src/planner/rules/parallel/rule-eager-prefetch-probe.js.map +1 -1
- package/dist/src/planner/rules/predicate/rule-aggregate-predicate-pushdown.d.ts.map +1 -1
- package/dist/src/planner/rules/predicate/rule-aggregate-predicate-pushdown.js +10 -1
- package/dist/src/planner/rules/predicate/rule-aggregate-predicate-pushdown.js.map +1 -1
- package/dist/src/planner/rules/predicate/rule-empty-relation-folding.d.ts.map +1 -1
- package/dist/src/planner/rules/predicate/rule-empty-relation-folding.js +18 -0
- package/dist/src/planner/rules/predicate/rule-empty-relation-folding.js.map +1 -1
- package/dist/src/planner/rules/predicate/rule-filter-contradiction.d.ts.map +1 -1
- package/dist/src/planner/rules/predicate/rule-filter-contradiction.js +7 -0
- package/dist/src/planner/rules/predicate/rule-filter-contradiction.js.map +1 -1
- 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 +9 -0
- package/dist/src/planner/rules/predicate/rule-predicate-inference-equivalence.js.map +1 -1
- package/dist/src/planner/rules/predicate/rule-predicate-pushdown.js +13 -3
- package/dist/src/planner/rules/predicate/rule-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/retrieve/rule-projection-pruning.d.ts.map +1 -1
- package/dist/src/planner/rules/retrieve/rule-projection-pruning.js +14 -0
- package/dist/src/planner/rules/retrieve/rule-projection-pruning.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/subquery/rule-anti-join-fk-empty.d.ts.map +1 -1
- package/dist/src/planner/rules/subquery/rule-anti-join-fk-empty.js +8 -0
- 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.map +1 -1
- package/dist/src/planner/rules/subquery/rule-semi-join-fk-trivial.js +7 -0
- 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.map +1 -1
- package/dist/src/planner/rules/subquery/rule-subquery-decorrelation.js +12 -0
- package/dist/src/planner/rules/subquery/rule-subquery-decorrelation.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/type-utils.d.ts +14 -0
- package/dist/src/planner/type-utils.d.ts.map +1 -1
- package/dist/src/planner/type-utils.js +66 -21
- package/dist/src/planner/type-utils.js.map +1 -1
- package/dist/src/planner/util/fd-utils.d.ts +228 -36
- package/dist/src/planner/util/fd-utils.d.ts.map +1 -1
- package/dist/src/planner/util/fd-utils.js +501 -84
- package/dist/src/planner/util/fd-utils.js.map +1 -1
- package/dist/src/planner/util/ind-utils.d.ts +27 -1
- package/dist/src/planner/util/ind-utils.d.ts.map +1 -1
- package/dist/src/planner/util/ind-utils.js +80 -6
- package/dist/src/planner/util/ind-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 +182 -33
- package/dist/src/planner/util/key-utils.js.map +1 -1
- package/dist/src/planner/util/set-op-wrapper.d.ts +37 -0
- package/dist/src/planner/util/set-op-wrapper.d.ts.map +1 -0
- package/dist/src/planner/util/set-op-wrapper.js +82 -0
- package/dist/src/planner/util/set-op-wrapper.js.map +1 -0
- package/dist/src/planner/validation/plan-validator.d.ts.map +1 -1
- package/dist/src/planner/validation/plan-validator.js +1 -0
- package/dist/src/planner/validation/plan-validator.js.map +1 -1
- package/dist/src/runtime/context-helpers.d.ts +13 -1
- package/dist/src/runtime/context-helpers.d.ts.map +1 -1
- package/dist/src/runtime/context-helpers.js +7 -1
- package/dist/src/runtime/context-helpers.js.map +1 -1
- package/dist/src/runtime/delta-executor.d.ts +30 -1
- package/dist/src/runtime/delta-executor.d.ts.map +1 -1
- package/dist/src/runtime/delta-executor.js +38 -4
- package/dist/src/runtime/delta-executor.js.map +1 -1
- package/dist/src/runtime/emit/add-constraint.d.ts.map +1 -1
- package/dist/src/runtime/emit/add-constraint.js +38 -5
- 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 +10 -8
- package/dist/src/runtime/emit/aggregate.js.map +1 -1
- package/dist/src/runtime/emit/alter-table.d.ts +1 -1
- package/dist/src/runtime/emit/alter-table.d.ts.map +1 -1
- package/dist/src/runtime/emit/alter-table.js +664 -108
- 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 +2 -1
- 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 +24 -9
- package/dist/src/runtime/emit/asof-scan.js.map +1 -1
- package/dist/src/runtime/emit/asserted-keys.d.ts +13 -0
- package/dist/src/runtime/emit/asserted-keys.d.ts.map +1 -0
- package/dist/src/runtime/emit/asserted-keys.js +13 -0
- package/dist/src/runtime/emit/asserted-keys.js.map +1 -0
- package/dist/src/runtime/emit/between.d.ts.map +1 -1
- package/dist/src/runtime/emit/between.js +24 -19
- 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 +24 -36
- package/dist/src/runtime/emit/binary.js.map +1 -1
- package/dist/src/runtime/emit/block.d.ts.map +1 -1
- package/dist/src/runtime/emit/block.js +11 -2
- package/dist/src/runtime/emit/block.js.map +1 -1
- package/dist/src/runtime/emit/bloom-join.d.ts.map +1 -1
- package/dist/src/runtime/emit/bloom-join.js +12 -4
- 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 +50 -1
- package/dist/src/runtime/emit/constraint-check.js.map +1 -1
- package/dist/src/runtime/emit/create-table.d.ts.map +1 -1
- package/dist/src/runtime/emit/create-table.js +8 -0
- package/dist/src/runtime/emit/create-table.js.map +1 -1
- package/dist/src/runtime/emit/create-view.d.ts.map +1 -1
- package/dist/src/runtime/emit/create-view.js +16 -1
- package/dist/src/runtime/emit/create-view.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/dml-executor.d.ts +27 -0
- package/dist/src/runtime/emit/dml-executor.d.ts.map +1 -1
- package/dist/src/runtime/emit/dml-executor.js +413 -193
- package/dist/src/runtime/emit/dml-executor.js.map +1 -1
- package/dist/src/runtime/emit/drop-table.d.ts.map +1 -1
- package/dist/src/runtime/emit/drop-table.js +10 -0
- package/dist/src/runtime/emit/drop-table.js.map +1 -1
- package/dist/src/runtime/emit/drop-view.d.ts.map +1 -1
- package/dist/src/runtime/emit/drop-view.js +17 -0
- package/dist/src/runtime/emit/drop-view.js.map +1 -1
- package/dist/src/runtime/emit/envelope-scan.d.ts +13 -0
- package/dist/src/runtime/emit/envelope-scan.d.ts.map +1 -0
- package/dist/src/runtime/emit/envelope-scan.js +22 -0
- package/dist/src/runtime/emit/envelope-scan.js.map +1 -0
- package/dist/src/runtime/emit/join.d.ts +10 -2
- package/dist/src/runtime/emit/join.d.ts.map +1 -1
- package/dist/src/runtime/emit/join.js +128 -38
- package/dist/src/runtime/emit/join.js.map +1 -1
- package/dist/src/runtime/emit/lens-auxiliary-access.d.ts +16 -0
- package/dist/src/runtime/emit/lens-auxiliary-access.d.ts.map +1 -0
- package/dist/src/runtime/emit/lens-auxiliary-access.js +16 -0
- package/dist/src/runtime/emit/lens-auxiliary-access.js.map +1 -0
- package/dist/src/runtime/emit/materialized-view-helpers.d.ts +640 -0
- package/dist/src/runtime/emit/materialized-view-helpers.d.ts.map +1 -0
- package/dist/src/runtime/emit/materialized-view-helpers.js +2576 -0
- package/dist/src/runtime/emit/materialized-view-helpers.js.map +1 -0
- package/dist/src/runtime/emit/materialized-view.d.ts +31 -0
- package/dist/src/runtime/emit/materialized-view.d.ts.map +1 -0
- package/dist/src/runtime/emit/materialized-view.js +187 -0
- package/dist/src/runtime/emit/materialized-view.js.map +1 -0
- package/dist/src/runtime/emit/merge-join.d.ts.map +1 -1
- package/dist/src/runtime/emit/merge-join.js +19 -5
- package/dist/src/runtime/emit/merge-join.js.map +1 -1
- package/dist/src/runtime/emit/project.d.ts.map +1 -1
- package/dist/src/runtime/emit/project.js +10 -5
- package/dist/src/runtime/emit/project.js.map +1 -1
- package/dist/src/runtime/emit/schema-declarative.d.ts +1 -0
- package/dist/src/runtime/emit/schema-declarative.d.ts.map +1 -1
- package/dist/src/runtime/emit/schema-declarative.js +101 -5
- package/dist/src/runtime/emit/schema-declarative.js.map +1 -1
- package/dist/src/runtime/emit/set-object-tags.d.ts +16 -0
- package/dist/src/runtime/emit/set-object-tags.d.ts.map +1 -0
- package/dist/src/runtime/emit/set-object-tags.js +57 -0
- package/dist/src/runtime/emit/set-object-tags.js.map +1 -0
- package/dist/src/runtime/emit/set-operation.d.ts.map +1 -1
- package/dist/src/runtime/emit/set-operation.js +140 -24
- package/dist/src/runtime/emit/set-operation.js.map +1 -1
- package/dist/src/runtime/emit/subquery.d.ts.map +1 -1
- package/dist/src/runtime/emit/subquery.js +110 -5
- package/dist/src/runtime/emit/subquery.js.map +1 -1
- package/dist/src/runtime/emit/unary.d.ts.map +1 -1
- package/dist/src/runtime/emit/unary.js +34 -6
- package/dist/src/runtime/emit/unary.js.map +1 -1
- package/dist/src/runtime/emit/view-mutation.d.ts +70 -0
- package/dist/src/runtime/emit/view-mutation.d.ts.map +1 -0
- package/dist/src/runtime/emit/view-mutation.js +299 -0
- package/dist/src/runtime/emit/view-mutation.js.map +1 -0
- package/dist/src/runtime/emit/window.js +29 -5
- package/dist/src/runtime/emit/window.js.map +1 -1
- package/dist/src/runtime/foreign-key-actions.d.ts +66 -3
- package/dist/src/runtime/foreign-key-actions.d.ts.map +1 -1
- package/dist/src/runtime/foreign-key-actions.js +580 -172
- package/dist/src/runtime/foreign-key-actions.js.map +1 -1
- package/dist/src/runtime/parallel-driver.d.ts +4 -1
- package/dist/src/runtime/parallel-driver.d.ts.map +1 -1
- package/dist/src/runtime/parallel-driver.js +5 -1
- 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 +17 -1
- package/dist/src/runtime/register.js.map +1 -1
- package/dist/src/runtime/types.d.ts +10 -0
- package/dist/src/runtime/types.d.ts.map +1 -1
- package/dist/src/runtime/types.js.map +1 -1
- package/dist/src/schema/basis-backfill.d.ts +63 -0
- package/dist/src/schema/basis-backfill.d.ts.map +1 -0
- package/dist/src/schema/basis-backfill.js +161 -0
- package/dist/src/schema/basis-backfill.js.map +1 -0
- package/dist/src/schema/catalog.d.ts +115 -1
- package/dist/src/schema/catalog.d.ts.map +1 -1
- package/dist/src/schema/catalog.js +249 -22
- package/dist/src/schema/catalog.js.map +1 -1
- package/dist/src/schema/change-events.d.ts +42 -1
- package/dist/src/schema/change-events.d.ts.map +1 -1
- package/dist/src/schema/change-events.js.map +1 -1
- package/dist/src/schema/column.d.ts +16 -0
- package/dist/src/schema/column.d.ts.map +1 -1
- package/dist/src/schema/column.js.map +1 -1
- package/dist/src/schema/constraint-builder.d.ts +182 -0
- package/dist/src/schema/constraint-builder.d.ts.map +1 -0
- package/dist/src/schema/constraint-builder.js +424 -0
- package/dist/src/schema/constraint-builder.js.map +1 -0
- package/dist/src/schema/ddl-generator.d.ts +86 -1
- package/dist/src/schema/ddl-generator.d.ts.map +1 -1
- package/dist/src/schema/ddl-generator.js +316 -20
- package/dist/src/schema/ddl-generator.js.map +1 -1
- package/dist/src/schema/declared-schema-manager.d.ts +51 -0
- package/dist/src/schema/declared-schema-manager.d.ts.map +1 -1
- package/dist/src/schema/declared-schema-manager.js +61 -0
- package/dist/src/schema/declared-schema-manager.js.map +1 -1
- package/dist/src/schema/derivation.d.ts +106 -0
- package/dist/src/schema/derivation.d.ts.map +1 -0
- package/dist/src/schema/derivation.js +25 -0
- package/dist/src/schema/derivation.js.map +1 -0
- package/dist/src/schema/function.d.ts +13 -0
- package/dist/src/schema/function.d.ts.map +1 -1
- package/dist/src/schema/function.js.map +1 -1
- package/dist/src/schema/lens-ack.d.ts +90 -0
- package/dist/src/schema/lens-ack.d.ts.map +1 -0
- package/dist/src/schema/lens-ack.js +361 -0
- package/dist/src/schema/lens-ack.js.map +1 -0
- package/dist/src/schema/lens-compiler.d.ts +62 -0
- package/dist/src/schema/lens-compiler.d.ts.map +1 -0
- package/dist/src/schema/lens-compiler.js +1594 -0
- package/dist/src/schema/lens-compiler.js.map +1 -0
- package/dist/src/schema/lens-fk-discovery.d.ts +175 -0
- package/dist/src/schema/lens-fk-discovery.d.ts.map +1 -0
- package/dist/src/schema/lens-fk-discovery.js +336 -0
- package/dist/src/schema/lens-fk-discovery.js.map +1 -0
- package/dist/src/schema/lens-prover.d.ts +336 -0
- package/dist/src/schema/lens-prover.d.ts.map +1 -0
- package/dist/src/schema/lens-prover.js +1988 -0
- package/dist/src/schema/lens-prover.js.map +1 -0
- package/dist/src/schema/lens.d.ts +254 -0
- package/dist/src/schema/lens.d.ts.map +1 -0
- package/dist/src/schema/lens.js +21 -0
- package/dist/src/schema/lens.js.map +1 -0
- package/dist/src/schema/manager.d.ts +676 -18
- package/dist/src/schema/manager.d.ts.map +1 -1
- package/dist/src/schema/manager.js +1573 -238
- package/dist/src/schema/manager.js.map +1 -1
- package/dist/src/schema/mapping-advertisement-tags.d.ts +39 -0
- package/dist/src/schema/mapping-advertisement-tags.d.ts.map +1 -0
- package/dist/src/schema/mapping-advertisement-tags.js +216 -0
- package/dist/src/schema/mapping-advertisement-tags.js.map +1 -0
- package/dist/src/schema/rename-rewriter.d.ts +45 -4
- package/dist/src/schema/rename-rewriter.d.ts.map +1 -1
- package/dist/src/schema/rename-rewriter.js +412 -19
- package/dist/src/schema/rename-rewriter.js.map +1 -1
- package/dist/src/schema/reserved-tags-policy.d.ts +32 -0
- package/dist/src/schema/reserved-tags-policy.d.ts.map +1 -0
- package/dist/src/schema/reserved-tags-policy.js +34 -0
- package/dist/src/schema/reserved-tags-policy.js.map +1 -0
- package/dist/src/schema/reserved-tags.d.ts +170 -0
- package/dist/src/schema/reserved-tags.d.ts.map +1 -0
- package/dist/src/schema/reserved-tags.js +507 -0
- package/dist/src/schema/reserved-tags.js.map +1 -0
- package/dist/src/schema/schema-differ.d.ts +158 -2
- package/dist/src/schema/schema-differ.d.ts.map +1 -1
- package/dist/src/schema/schema-differ.js +1460 -78
- package/dist/src/schema/schema-differ.js.map +1 -1
- package/dist/src/schema/schema-hasher.d.ts +8 -3
- package/dist/src/schema/schema-hasher.d.ts.map +1 -1
- package/dist/src/schema/schema-hasher.js +22 -2
- package/dist/src/schema/schema-hasher.js.map +1 -1
- package/dist/src/schema/schema.d.ts +25 -1
- package/dist/src/schema/schema.d.ts.map +1 -1
- package/dist/src/schema/schema.js +36 -2
- package/dist/src/schema/schema.js.map +1 -1
- package/dist/src/schema/table.d.ts +259 -10
- package/dist/src/schema/table.d.ts.map +1 -1
- package/dist/src/schema/table.js +309 -26
- package/dist/src/schema/table.js.map +1 -1
- package/dist/src/schema/unique-enforcement.d.ts +78 -0
- package/dist/src/schema/unique-enforcement.d.ts.map +1 -0
- package/dist/src/schema/unique-enforcement.js +93 -0
- package/dist/src/schema/unique-enforcement.js.map +1 -0
- package/dist/src/schema/view.d.ts +83 -2
- package/dist/src/schema/view.d.ts.map +1 -1
- package/dist/src/schema/view.js +67 -1
- package/dist/src/schema/view.js.map +1 -1
- package/dist/src/schema/window-function.d.ts +9 -1
- package/dist/src/schema/window-function.d.ts.map +1 -1
- package/dist/src/schema/window-function.js.map +1 -1
- 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 +24 -0
- package/dist/src/util/comparison.d.ts.map +1 -1
- package/dist/src/util/comparison.js +34 -0
- package/dist/src/util/comparison.js.map +1 -1
- package/dist/src/util/mutation-statement.d.ts.map +1 -1
- package/dist/src/util/mutation-statement.js +4 -1
- package/dist/src/util/mutation-statement.js.map +1 -1
- package/dist/src/util/serialization.d.ts +9 -0
- package/dist/src/util/serialization.d.ts.map +1 -1
- package/dist/src/util/serialization.js +26 -0
- package/dist/src/util/serialization.js.map +1 -1
- package/dist/src/vtab/backing-host.d.ts +286 -0
- package/dist/src/vtab/backing-host.d.ts.map +1 -0
- package/dist/src/vtab/backing-host.js +118 -0
- package/dist/src/vtab/backing-host.js.map +1 -0
- package/dist/src/vtab/best-access-plan.d.ts +21 -0
- package/dist/src/vtab/best-access-plan.d.ts.map +1 -1
- package/dist/src/vtab/best-access-plan.js.map +1 -1
- package/dist/src/vtab/capabilities.d.ts +5 -5
- package/dist/src/vtab/capabilities.d.ts.map +1 -1
- package/dist/src/vtab/mapping-advertisement.d.ts +163 -0
- package/dist/src/vtab/mapping-advertisement.d.ts.map +1 -0
- package/dist/src/vtab/mapping-advertisement.js +2 -0
- package/dist/src/vtab/mapping-advertisement.js.map +1 -0
- package/dist/src/vtab/memory/index.d.ts +64 -4
- package/dist/src/vtab/memory/index.d.ts.map +1 -1
- package/dist/src/vtab/memory/index.js +119 -12
- package/dist/src/vtab/memory/index.js.map +1 -1
- package/dist/src/vtab/memory/layer/base.d.ts +38 -1
- package/dist/src/vtab/memory/layer/base.d.ts.map +1 -1
- package/dist/src/vtab/memory/layer/base.js +112 -24
- package/dist/src/vtab/memory/layer/base.js.map +1 -1
- package/dist/src/vtab/memory/layer/manager.d.ts +291 -4
- package/dist/src/vtab/memory/layer/manager.d.ts.map +1 -1
- package/dist/src/vtab/memory/layer/manager.js +1050 -91
- package/dist/src/vtab/memory/layer/manager.js.map +1 -1
- package/dist/src/vtab/memory/layer/plan-filter.d.ts.map +1 -1
- package/dist/src/vtab/memory/layer/plan-filter.js +35 -6
- package/dist/src/vtab/memory/layer/plan-filter.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 +66 -14
- package/dist/src/vtab/memory/layer/scan-layer.js.map +1 -1
- package/dist/src/vtab/memory/layer/scan-plan.d.ts +14 -0
- package/dist/src/vtab/memory/layer/scan-plan.d.ts.map +1 -1
- package/dist/src/vtab/memory/layer/scan-plan.js +27 -4
- package/dist/src/vtab/memory/layer/scan-plan.js.map +1 -1
- package/dist/src/vtab/memory/layer/transaction.d.ts.map +1 -1
- package/dist/src/vtab/memory/layer/transaction.js +5 -1
- package/dist/src/vtab/memory/layer/transaction.js.map +1 -1
- package/dist/src/vtab/memory/module.d.ts +17 -0
- package/dist/src/vtab/memory/module.d.ts.map +1 -1
- package/dist/src/vtab/memory/module.js +82 -3
- package/dist/src/vtab/memory/module.js.map +1 -1
- package/dist/src/vtab/memory/table.d.ts.map +1 -1
- package/dist/src/vtab/memory/table.js +15 -5
- package/dist/src/vtab/memory/table.js.map +1 -1
- package/dist/src/vtab/memory/types.d.ts +20 -2
- package/dist/src/vtab/memory/types.d.ts.map +1 -1
- package/dist/src/vtab/memory/utils/predicate.d.ts.map +1 -1
- package/dist/src/vtab/memory/utils/predicate.js +46 -24
- package/dist/src/vtab/memory/utils/predicate.js.map +1 -1
- package/dist/src/vtab/memory/utils/primary-key-encode.d.ts +31 -0
- package/dist/src/vtab/memory/utils/primary-key-encode.d.ts.map +1 -0
- package/dist/src/vtab/memory/utils/primary-key-encode.js +101 -0
- package/dist/src/vtab/memory/utils/primary-key-encode.js.map +1 -0
- package/dist/src/vtab/memory/utils/primary-key.d.ts +8 -0
- package/dist/src/vtab/memory/utils/primary-key.d.ts.map +1 -1
- package/dist/src/vtab/memory/utils/primary-key.js +12 -5
- package/dist/src/vtab/memory/utils/primary-key.js.map +1 -1
- package/dist/src/vtab/module.d.ts +203 -4
- package/dist/src/vtab/module.d.ts.map +1 -1
- package/dist/src/vtab/table.d.ts +9 -0
- package/dist/src/vtab/table.d.ts.map +1 -1
- package/dist/src/vtab/table.js.map +1 -1
- package/package.json +6 -5
|
@@ -0,0 +1,1081 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Query-rewrite matcher — the read-side dual of the coverage prover. It
|
|
3
|
+
* recognizes when an *arbitrary* scan-projection-filter query fragment (one that
|
|
4
|
+
* never names a materialized view) is **answered from** a covering MV, so the
|
|
5
|
+
* optimizer can scan the MV's backing table with a bounded residual instead of
|
|
6
|
+
* recomputing the body against the base tables.
|
|
7
|
+
*
|
|
8
|
+
* Distinct from `coverage-prover.ts` (which proves a *base-table UNIQUE
|
|
9
|
+
* constraint* is covered, on the write/enforcement path) but sharing its
|
|
10
|
+
* entailment vocabulary: `recognizeConjunctiveClauses` / `guardClausesEntail`
|
|
11
|
+
* (`partial-unique-extraction.ts`). The question answered here is **output-relation
|
|
12
|
+
* subsumption**: does the MV's stored output relation contain a superset (re-
|
|
13
|
+
* coverable via a bounded residual) of the rows the fragment produces, keyed so
|
|
14
|
+
* the residual recovers exactly the fragment's output?
|
|
15
|
+
*
|
|
16
|
+
* Soundness contract (mirrors the coverage prover exactly): **a false NotMatch
|
|
17
|
+
* only forgoes a speedup; a false Match returns wrong rows.** Every check forgoes
|
|
18
|
+
* the rewrite on doubt. The pre-existing recompute-over-base path is correct by
|
|
19
|
+
* construction; the rule only ever replaces it with a provably row-equivalent
|
|
20
|
+
* plan, so the rewrite is non-regressing.
|
|
21
|
+
*
|
|
22
|
+
* This phase delivers the **projection + filter subsumption** shape only.
|
|
23
|
+
* Aggregate rollup (`mv-query-rewrite-aggregate-rollup`) and join subsumption
|
|
24
|
+
* (`mv-query-rewrite-join-subsumption`) are pure additions to this matcher.
|
|
25
|
+
*
|
|
26
|
+
* ## Where the predicates come from (the pristine-fragment requirement)
|
|
27
|
+
*
|
|
28
|
+
* The fragment's WHERE is read from the live plan's `FilterNode` predicate (its
|
|
29
|
+
* `.expression` AST), and the MV's WHERE from `mv.derivation.selectAst.where`. Reading the
|
|
30
|
+
* fragment WHERE from the plan is only sound while the predicate is still an
|
|
31
|
+
* explicit `FilterNode` above the table access — *before* predicate-pushdown
|
|
32
|
+
* absorbs it into a range-bounded scan (where the matcher could no longer see it
|
|
33
|
+
* and would falsely treat the fragment as unfiltered). The rule that drives this
|
|
34
|
+
* matcher therefore fires in the **Structural rewrite pass, before grow-retrieve /
|
|
35
|
+
* predicate-pushdown**, where the fragment is the pristine
|
|
36
|
+
* `Project(Filter?(Retrieve(TableReference)))`. The shape walk additionally
|
|
37
|
+
* rejects any range-bounded physical scan (`SeqScan`/`IndexScan` with
|
|
38
|
+
* `rangeBoundedOn`, or an `IndexSeek`/`TableSeek`) as `'shape'` — defense in depth
|
|
39
|
+
* should an absorbed predicate ever reach the walk by another path.
|
|
40
|
+
*
|
|
41
|
+
* ## Why `.expression` recognition is sound under constant folding
|
|
42
|
+
*
|
|
43
|
+
* A scalar plan node retains its originating AST in `.expression`. Constant
|
|
44
|
+
* folding (which runs before the Structural pass) may make the *plan* more
|
|
45
|
+
* specific than its `.expression` (e.g. folding `1+1` → `2` while `.expression`
|
|
46
|
+
* still reads `amt > 1+1`). Such a divergence only ever makes a clause
|
|
47
|
+
* *unrecognized* (`literalValue` of a non-literal AST returns undefined), which is
|
|
48
|
+
* a conservative NotMatch — it never fabricates a recognized clause weaker than
|
|
49
|
+
* what the plan computes, so it cannot produce a false Match.
|
|
50
|
+
*/
|
|
51
|
+
import { isRelationalNode } from '../nodes/plan-node.js';
|
|
52
|
+
import { ProjectNode } from '../nodes/project-node.js';
|
|
53
|
+
import { FilterNode } from '../nodes/filter.js';
|
|
54
|
+
import { RetrieveNode } from '../nodes/retrieve-node.js';
|
|
55
|
+
import { AliasNode } from '../nodes/alias-node.js';
|
|
56
|
+
import { TableReferenceNode, ColumnReferenceNode } from '../nodes/reference.js';
|
|
57
|
+
import { SeqScanNode, IndexScanNode } from '../nodes/table-access-nodes.js';
|
|
58
|
+
import { BinaryOpNode } from '../nodes/scalar.js';
|
|
59
|
+
import { AggregateNode } from '../nodes/aggregate-node.js';
|
|
60
|
+
import { AggregateFunctionCallNode } from '../nodes/aggregate-function.js';
|
|
61
|
+
import { JoinNode } from '../nodes/join-node.js';
|
|
62
|
+
import { CapabilityDetectors } from '../framework/characteristics.js';
|
|
63
|
+
import { recognizeConjunctiveClauses, guardClausesEntail } from './partial-unique-extraction.js';
|
|
64
|
+
import { proveOneToOneJoin, pureJoinEquiAttrPairs } from './coverage-prover.js';
|
|
65
|
+
import { containsNonDeterministicCall } from './check-extraction.js';
|
|
66
|
+
function fail(reason) {
|
|
67
|
+
return { match: undefined, reason };
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Recognize a query fragment rooted at a `ProjectNode` as a single-source
|
|
71
|
+
* scan-project-filter chain. Walks `Project → Filter? → {Retrieve|Alias|full
|
|
72
|
+
* SeqScan/IndexScan}* → TableReference`. Any other node (Sort/Limit/Distinct/
|
|
73
|
+
* Aggregate/Join/SetOp, or a row-reducing seek / range-bounded scan) ⇒ `'shape'`.
|
|
74
|
+
*/
|
|
75
|
+
export function analyzeQueryFragment(root) {
|
|
76
|
+
if (!(root instanceof ProjectNode))
|
|
77
|
+
return { ok: false, reason: 'shape' };
|
|
78
|
+
// Descend the source chain, collecting WHERE conjuncts, down to the base table.
|
|
79
|
+
const walk = walkScanFilterChain(root.source);
|
|
80
|
+
if (!walk)
|
|
81
|
+
return { ok: false, reason: 'shape' };
|
|
82
|
+
const { tableRef, conjuncts } = walk;
|
|
83
|
+
// Each output column must be a bare column reference into the base table; a
|
|
84
|
+
// computed output is unrecoverable from the backing in v1 (missing-column).
|
|
85
|
+
const outputs = root.projections.map((proj, i) => ({
|
|
86
|
+
attrId: root.getAttributes()[i].id,
|
|
87
|
+
baseCol: proj.node instanceof ColumnReferenceNode ? proj.node.columnIndex : undefined,
|
|
88
|
+
}));
|
|
89
|
+
return {
|
|
90
|
+
ok: true,
|
|
91
|
+
shape: { project: root, tableRef, baseTable: tableRef.tableSchema, outputs, conjuncts },
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Walk a single-source scan-project-filter source chain `Filter? → {Retrieve|Alias|
|
|
96
|
+
* full SeqScan/IndexScan}* → TableReference`, collecting top-level AND-split WHERE
|
|
97
|
+
* conjuncts (plan-node level). Returns the leaf `TableReferenceNode` and conjuncts,
|
|
98
|
+
* or `undefined` for any other node (a row-reducing seek / range-bounded scan,
|
|
99
|
+
* Sort/Limit/Distinct/Aggregate/Join/SetOp). Shared by the projection-filter and
|
|
100
|
+
* aggregate arms so the recognized source shape is identical.
|
|
101
|
+
*/
|
|
102
|
+
function walkScanFilterChain(start) {
|
|
103
|
+
const conjuncts = [];
|
|
104
|
+
let node = start;
|
|
105
|
+
let tableRef;
|
|
106
|
+
while (node) {
|
|
107
|
+
if (node instanceof TableReferenceNode) {
|
|
108
|
+
tableRef = node;
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
if (node instanceof FilterNode) {
|
|
112
|
+
splitConjuncts(node.predicate, conjuncts);
|
|
113
|
+
node = node.source;
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
if (node instanceof RetrieveNode || node instanceof AliasNode) {
|
|
117
|
+
node = singleRelation(node);
|
|
118
|
+
if (!node)
|
|
119
|
+
return undefined;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
// A full (non-range-bounded) physical scan is a row-preserving pass-through;
|
|
123
|
+
// a range-bounded scan has absorbed a predicate we can no longer see (sound
|
|
124
|
+
// only because the rule fires before access selection — this is defensive).
|
|
125
|
+
if (node instanceof SeqScanNode || node instanceof IndexScanNode) {
|
|
126
|
+
if (node.rangeBoundedOn)
|
|
127
|
+
return undefined;
|
|
128
|
+
node = node.source;
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
return undefined;
|
|
132
|
+
}
|
|
133
|
+
if (!tableRef)
|
|
134
|
+
return undefined;
|
|
135
|
+
return { tableRef, conjuncts };
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Decide whether `mv` (backed by `backing`) answers the fragment `shape`. See the
|
|
139
|
+
* module doc for the soundness contract. `isDeterministic` probes the function
|
|
140
|
+
* registry for the determinism gate (a registered MV is already deterministic by
|
|
141
|
+
* construction — the create gate rejects non-deterministic bodies — so this is
|
|
142
|
+
* defense in depth).
|
|
143
|
+
*/
|
|
144
|
+
export function matchFragmentToMv(shape, mv, backing, isDeterministic) {
|
|
145
|
+
const baseTable = shape.baseTable;
|
|
146
|
+
// ---- Candidate gates (a false-positive here only forgoes a speedup). ----
|
|
147
|
+
// Stale: the backing is an unmaintained snapshot — never read it.
|
|
148
|
+
if (mv.derivation.stale === true)
|
|
149
|
+
return fail('no-candidate');
|
|
150
|
+
// Registered + has a live backing table.
|
|
151
|
+
if (!backing)
|
|
152
|
+
return fail('no-candidate');
|
|
153
|
+
// Deterministic body: a random()/now()/volatile-UDF body cannot substitute for
|
|
154
|
+
// live recomputation. Reuses the function-registry determinism metadata.
|
|
155
|
+
if (mvBodyHasNonDeterminism(mv.derivation.selectAst, isDeterministic))
|
|
156
|
+
return fail('no-candidate');
|
|
157
|
+
// Source-schema sanity: the MV must read exactly the one base table the
|
|
158
|
+
// fragment reads (single-source v1). `sourceTables` dedups, so a self-join
|
|
159
|
+
// collapses to one entry — the AST single-`table` FROM check below rejects it.
|
|
160
|
+
const qualified = `${baseTable.schemaName}.${baseTable.name}`.toLowerCase();
|
|
161
|
+
if (mv.derivation.sourceTables.length !== 1 || mv.derivation.sourceTables[0] !== qualified) {
|
|
162
|
+
return fail('source-mismatch');
|
|
163
|
+
}
|
|
164
|
+
// ---- MV body shape (AST): single-source projection + optional filter. ----
|
|
165
|
+
if (mv.derivation.selectAst.type !== 'select')
|
|
166
|
+
return fail('shape');
|
|
167
|
+
const sel = mv.derivation.selectAst;
|
|
168
|
+
if ((sel.groupBy && sel.groupBy.length > 0) || sel.having || sel.distinct
|
|
169
|
+
|| sel.limit !== undefined || sel.offset !== undefined
|
|
170
|
+
|| sel.union || sel.compound) {
|
|
171
|
+
return fail('shape');
|
|
172
|
+
}
|
|
173
|
+
if (!sel.from || sel.from.length !== 1 || sel.from[0].type !== 'table')
|
|
174
|
+
return fail('shape');
|
|
175
|
+
// ---- MV projection → base-column mapping (which backing column holds which
|
|
176
|
+
// base column). A computed select item leaves that backing column unmapped
|
|
177
|
+
// (it cannot answer a passthrough need). ----
|
|
178
|
+
const baseColOfBackingCol = mvProjectionBaseCols(sel.columns, baseTable);
|
|
179
|
+
if (!baseColOfBackingCol)
|
|
180
|
+
return fail('shape');
|
|
181
|
+
const backingColOfBaseCol = new Map();
|
|
182
|
+
baseColOfBackingCol.forEach((baseCol, backingCol) => {
|
|
183
|
+
if (baseCol !== undefined && !backingColOfBaseCol.has(baseCol)) {
|
|
184
|
+
backingColOfBaseCol.set(baseCol, backingCol);
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
// ---- Predicate entailment (containment): the fragment's row set must be a
|
|
188
|
+
// subset of the MV's, i.e. the MV's WHERE `P_mv` is entailed by the
|
|
189
|
+
// fragment's WHERE `P_q` (every MV-required clause is implied by the
|
|
190
|
+
// query). The residual is the conjunction of `P_q` clauses not already
|
|
191
|
+
// entailed by `P_mv`. ----
|
|
192
|
+
const mvClauses = sel.where ? recognizeConjunctiveClauses(sel.where, baseTable) : [];
|
|
193
|
+
if (mvClauses === undefined)
|
|
194
|
+
return fail('predicate-not-entailed');
|
|
195
|
+
const queryClauses = [];
|
|
196
|
+
const residualConjuncts = [];
|
|
197
|
+
const residualClauses = [];
|
|
198
|
+
for (const conjunct of shape.conjuncts) {
|
|
199
|
+
const expr = conjunctExpression(conjunct);
|
|
200
|
+
const clauses = expr ? recognizeConjunctiveClauses(expr, baseTable) : undefined;
|
|
201
|
+
if (!clauses)
|
|
202
|
+
return fail('predicate-not-entailed');
|
|
203
|
+
queryClauses.push(...clauses);
|
|
204
|
+
// A conjunct already entailed by `P_mv` holds for every backing row, so it
|
|
205
|
+
// is dropped from the residual; the rest become the residual filter.
|
|
206
|
+
if (!guardClausesEntail(mvClauses, clauses)) {
|
|
207
|
+
residualConjuncts.push(conjunct);
|
|
208
|
+
residualClauses.push(...clauses);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
if (!guardClausesEntail(queryClauses, mvClauses))
|
|
212
|
+
return fail('predicate-not-entailed');
|
|
213
|
+
// ---- Projection coverage: every fragment output column must be a base column
|
|
214
|
+
// the MV projects. ----
|
|
215
|
+
const outputColumnMap = [];
|
|
216
|
+
for (const out of shape.outputs) {
|
|
217
|
+
if (out.baseCol === undefined)
|
|
218
|
+
return fail('missing-column');
|
|
219
|
+
const backingCol = backingColOfBaseCol.get(out.baseCol);
|
|
220
|
+
if (backingCol === undefined)
|
|
221
|
+
return fail('missing-column');
|
|
222
|
+
outputColumnMap.push({ attrId: out.attrId, backingCol });
|
|
223
|
+
}
|
|
224
|
+
// ---- Residual coverage: every base column the residual references must also be
|
|
225
|
+
// a backing column (so the residual filter can be applied on the scan). ----
|
|
226
|
+
for (const clause of residualClauses) {
|
|
227
|
+
for (const col of clauseColumns(clause)) {
|
|
228
|
+
if (!backingColOfBaseCol.has(col))
|
|
229
|
+
return fail('missing-column');
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return {
|
|
233
|
+
match: {
|
|
234
|
+
mv,
|
|
235
|
+
backing,
|
|
236
|
+
residualClauses,
|
|
237
|
+
residualConjuncts,
|
|
238
|
+
outputColumnMap,
|
|
239
|
+
backingColOfBaseCol,
|
|
240
|
+
},
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Convenience entry point (used by the unit tests): analyze `root` as a fragment
|
|
245
|
+
* and, on success, match it against `mv`. Returns the fragment-analysis failure
|
|
246
|
+
* reason when `root` is not a recognizable scan-project-filter chain.
|
|
247
|
+
*/
|
|
248
|
+
export function matchMaterializedViewRewrite(root, mv, backing, isDeterministic) {
|
|
249
|
+
const frag = analyzeQueryFragment(root);
|
|
250
|
+
if (!frag.ok)
|
|
251
|
+
return fail(frag.reason);
|
|
252
|
+
return matchFragmentToMv(frag.shape, mv, backing, isDeterministic);
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Recognize a query fragment rooted at a logical {@link AggregateNode} (the shape
|
|
256
|
+
* the optimizer presents in the Structural pass, before physical aggregate
|
|
257
|
+
* selection) as a single-source aggregate over a scan-filter chain. Requires
|
|
258
|
+
* **bare-column** GROUP BY expressions and aggregate arguments — a computed group
|
|
259
|
+
* key or aggregate argument (`group by d+1`, `sum(amt*2)`, `group_concat(x, ',')`)
|
|
260
|
+
* is unrecoverable from a stored MV column in v1 ⇒ `aggregate-shape`. Mirrors the
|
|
261
|
+
* row-time aggregate eligibility gate, which likewise requires bare group columns.
|
|
262
|
+
*/
|
|
263
|
+
export function analyzeAggregateFragment(root) {
|
|
264
|
+
if (!(root instanceof AggregateNode))
|
|
265
|
+
return { ok: false, reason: 'aggregate-shape' };
|
|
266
|
+
const walk = walkScanFilterChain(root.source);
|
|
267
|
+
if (!walk)
|
|
268
|
+
return { ok: false, reason: 'aggregate-shape' };
|
|
269
|
+
const { tableRef, conjuncts } = walk;
|
|
270
|
+
const attrs = root.getAttributes();
|
|
271
|
+
const groupCount = root.groupBy.length;
|
|
272
|
+
// GROUP BY key: every expression must be a bare column reference into the base table.
|
|
273
|
+
const groupBaseCols = [];
|
|
274
|
+
for (const gb of root.groupBy) {
|
|
275
|
+
if (!(gb instanceof ColumnReferenceNode))
|
|
276
|
+
return { ok: false, reason: 'aggregate-shape' };
|
|
277
|
+
groupBaseCols.push(gb.columnIndex);
|
|
278
|
+
}
|
|
279
|
+
// Aggregates: each must be an aggregate function over `count(*)` (no arg) or a
|
|
280
|
+
// single bare base column.
|
|
281
|
+
const aggregates = [];
|
|
282
|
+
for (let i = 0; i < root.aggregates.length; i++) {
|
|
283
|
+
const expr = root.aggregates[i].expression;
|
|
284
|
+
if (!(expr instanceof AggregateFunctionCallNode))
|
|
285
|
+
return { ok: false, reason: 'aggregate-shape' };
|
|
286
|
+
let argBaseCol;
|
|
287
|
+
if (expr.args.length === 0) {
|
|
288
|
+
argBaseCol = undefined; // count(*)
|
|
289
|
+
}
|
|
290
|
+
else if (expr.args.length === 1 && expr.args[0] instanceof ColumnReferenceNode) {
|
|
291
|
+
argBaseCol = expr.args[0].columnIndex;
|
|
292
|
+
}
|
|
293
|
+
else {
|
|
294
|
+
return { ok: false, reason: 'aggregate-shape' }; // computed / multi-arg argument
|
|
295
|
+
}
|
|
296
|
+
aggregates.push({
|
|
297
|
+
funcName: expr.functionName.toLowerCase(),
|
|
298
|
+
argBaseCol,
|
|
299
|
+
isDistinct: expr.isDistinct,
|
|
300
|
+
outAttr: attrs[groupCount + i],
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
return {
|
|
304
|
+
ok: true,
|
|
305
|
+
shape: {
|
|
306
|
+
aggregateNode: root,
|
|
307
|
+
tableRef,
|
|
308
|
+
baseTable: tableRef.tableSchema,
|
|
309
|
+
groupBaseCols,
|
|
310
|
+
groupOutAttrs: attrs.slice(0, groupCount),
|
|
311
|
+
aggregates,
|
|
312
|
+
conjuncts,
|
|
313
|
+
},
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Decide whether the grouped MV `mv` (backed by `backing`) answers the aggregate
|
|
318
|
+
* fragment `shape`. See the arm doc above for the two sub-cases and the soundness
|
|
319
|
+
* contract; every check forgoes on doubt (a false NotMatch only forgoes a speedup).
|
|
320
|
+
*/
|
|
321
|
+
export function matchAggregateFragmentToMv(shape, mv, backing, isDeterministic) {
|
|
322
|
+
const baseTable = shape.baseTable;
|
|
323
|
+
// ---- Candidate gates (a false-positive here only forgoes a speedup). ----
|
|
324
|
+
if (mv.derivation.stale === true)
|
|
325
|
+
return fail('no-candidate');
|
|
326
|
+
if (!backing)
|
|
327
|
+
return fail('no-candidate');
|
|
328
|
+
if (mvBodyHasNonDeterminism(mv.derivation.selectAst, isDeterministic))
|
|
329
|
+
return fail('no-candidate');
|
|
330
|
+
const qualified = `${baseTable.schemaName}.${baseTable.name}`.toLowerCase();
|
|
331
|
+
if (mv.derivation.sourceTables.length !== 1 || mv.derivation.sourceTables[0] !== qualified) {
|
|
332
|
+
return fail('source-mismatch');
|
|
333
|
+
}
|
|
334
|
+
// ---- MV body shape: a single-source grouped aggregate, no HAVING/DISTINCT/cap. ----
|
|
335
|
+
if (mv.derivation.selectAst.type !== 'select')
|
|
336
|
+
return fail('aggregate-shape');
|
|
337
|
+
const sel = mv.derivation.selectAst;
|
|
338
|
+
if (!sel.groupBy || sel.groupBy.length === 0)
|
|
339
|
+
return fail('aggregate-shape'); // not a grouped MV
|
|
340
|
+
if (sel.having || sel.distinct || sel.limit !== undefined || sel.offset !== undefined
|
|
341
|
+
|| sel.union || sel.compound) {
|
|
342
|
+
return fail('aggregate-shape');
|
|
343
|
+
}
|
|
344
|
+
if (!sel.from || sel.from.length !== 1 || sel.from[0].type !== 'table')
|
|
345
|
+
return fail('aggregate-shape');
|
|
346
|
+
// ---- MV group key (base columns) — every GROUP BY expr must be a bare column. ----
|
|
347
|
+
const mvGroupBaseCols = [];
|
|
348
|
+
for (const gb of sel.groupBy) {
|
|
349
|
+
const col = baseColumnOfExpr(gb, baseTable);
|
|
350
|
+
if (col === undefined)
|
|
351
|
+
return fail('aggregate-shape'); // computed MV group key
|
|
352
|
+
mvGroupBaseCols.push(col);
|
|
353
|
+
}
|
|
354
|
+
const mvGroupSet = new Set(mvGroupBaseCols);
|
|
355
|
+
// ---- MV stored columns: group-key passthroughs + stored aggregates. ----
|
|
356
|
+
const stored = analyzeMvStoredColumns(sel.columns, baseTable);
|
|
357
|
+
if (!stored)
|
|
358
|
+
return fail('aggregate-shape');
|
|
359
|
+
// Every MV group-key column must be stored as a backing column (so the backing is
|
|
360
|
+
// addressable by the group key — needed for re-grouping and residual filtering).
|
|
361
|
+
for (const gc of mvGroupBaseCols) {
|
|
362
|
+
if (!stored.groupBackingOfBaseCol.has(gc))
|
|
363
|
+
return fail('missing-column');
|
|
364
|
+
}
|
|
365
|
+
// ---- One-row-per-MV-group witness: the backing's primary key must be exactly the
|
|
366
|
+
// MV group key (as backing columns). This is the schema-level form of
|
|
367
|
+
// `coverage-prover.ts`'s `proveEffectiveKeyUnique` — it certifies the backing
|
|
368
|
+
// is a *set* keyed by the group columns, so the exact-key direct scan returns
|
|
369
|
+
// one row per query group and the rollup re-aggregates a set (not a bag). The
|
|
370
|
+
// backing of a grouped MV is maintained keyed on its group columns, so this
|
|
371
|
+
// holds by construction; the check forgoes if a future shape ever diverges. ----
|
|
372
|
+
if (!backingPkIsGroupKey(backing, mvGroupBaseCols, stored.groupBackingOfBaseCol)) {
|
|
373
|
+
return fail('aggregate-shape');
|
|
374
|
+
}
|
|
375
|
+
// ---- Group-key alignment: query key must be a subset of the MV key. ----
|
|
376
|
+
const queryGroupSet = new Set(shape.groupBaseCols);
|
|
377
|
+
for (const gc of queryGroupSet) {
|
|
378
|
+
if (!mvGroupSet.has(gc))
|
|
379
|
+
return fail('group-key-mismatch');
|
|
380
|
+
}
|
|
381
|
+
const exact = queryGroupSet.size === mvGroupSet.size;
|
|
382
|
+
// Query group-key columns must be stored (to re-group / output them).
|
|
383
|
+
const groupKeyBackingCols = [];
|
|
384
|
+
for (const gc of shape.groupBaseCols) {
|
|
385
|
+
const bc = stored.groupBackingOfBaseCol.get(gc);
|
|
386
|
+
if (bc === undefined)
|
|
387
|
+
return fail('missing-column');
|
|
388
|
+
groupKeyBackingCols.push(bc);
|
|
389
|
+
}
|
|
390
|
+
// `backingColOfBaseCol` exposes only the stored group-key columns. The residual
|
|
391
|
+
// coverage check (below) then forgoes any residual conjunct on a non-group column
|
|
392
|
+
// — exactly the columns the MV has already aggregated away.
|
|
393
|
+
const backingColOfBaseCol = new Map(stored.groupBackingOfBaseCol);
|
|
394
|
+
// ---- Aggregate decomposition: a recipe per fragment aggregate. ----
|
|
395
|
+
const recipes = [];
|
|
396
|
+
const outputColumnMap = [];
|
|
397
|
+
for (const qa of shape.aggregates) {
|
|
398
|
+
const recipe = exact
|
|
399
|
+
? recipeForExact(qa, stored.storedAggs)
|
|
400
|
+
: recipeForRollup(qa, stored.storedAggs, baseTable);
|
|
401
|
+
if (!recipe)
|
|
402
|
+
return fail('aggregate-not-decomposable');
|
|
403
|
+
recipes.push(recipe);
|
|
404
|
+
if (exact)
|
|
405
|
+
outputColumnMap.push({ attrId: qa.outAttr.id, backingCol: recipe.backingCols[0] });
|
|
406
|
+
}
|
|
407
|
+
// ---- Predicate alignment (identical containment logic to the foundation), with
|
|
408
|
+
// the aggregate-specific gate that every residual conjunct references only
|
|
409
|
+
// MV group-key columns (so it partitions whole MV groups and commutes with
|
|
410
|
+
// the rollup). A residual on a non-group column resolves to a base column
|
|
411
|
+
// absent from `backingColOfBaseCol` ⇒ `missing-column`. ----
|
|
412
|
+
const mvClauses = sel.where ? recognizeConjunctiveClauses(sel.where, baseTable) : [];
|
|
413
|
+
if (mvClauses === undefined)
|
|
414
|
+
return fail('predicate-not-entailed');
|
|
415
|
+
const queryClauses = [];
|
|
416
|
+
const residualConjuncts = [];
|
|
417
|
+
const residualClauses = [];
|
|
418
|
+
for (const conjunct of shape.conjuncts) {
|
|
419
|
+
const expr = conjunctExpression(conjunct);
|
|
420
|
+
const clauses = expr ? recognizeConjunctiveClauses(expr, baseTable) : undefined;
|
|
421
|
+
if (!clauses)
|
|
422
|
+
return fail('predicate-not-entailed');
|
|
423
|
+
queryClauses.push(...clauses);
|
|
424
|
+
if (!guardClausesEntail(mvClauses, clauses)) {
|
|
425
|
+
residualConjuncts.push(conjunct);
|
|
426
|
+
residualClauses.push(...clauses);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
if (!guardClausesEntail(queryClauses, mvClauses))
|
|
430
|
+
return fail('predicate-not-entailed');
|
|
431
|
+
for (const clause of residualClauses) {
|
|
432
|
+
for (const col of clauseColumns(clause)) {
|
|
433
|
+
if (!backingColOfBaseCol.has(col))
|
|
434
|
+
return fail('missing-column');
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
// Group-key reorder guard. When the query WHERE constant-pins (`g = 1`, `g is
|
|
438
|
+
// null`) or equates (`g1 = g2`) a group-key column AND there are ≥2 group keys,
|
|
439
|
+
// the base's `rule-groupby-fd-simplification` drops the functionally-determined
|
|
440
|
+
// group column and re-emits it as a picker `min` at a *shifted* output position,
|
|
441
|
+
// changing the result's column ORDER. The rewrite preserves the pristine column
|
|
442
|
+
// order, so the two would diverge — forgo to remain a faithful drop-in. (A single
|
|
443
|
+
// group key is never dropped — the rule keeps ≥1 — and range/IN residuals create
|
|
444
|
+
// no determining FD, so both stay eligible. Checks the full query WHERE, not just
|
|
445
|
+
// the residual: a pin entailed by the MV still drives the base's simplification.)
|
|
446
|
+
if (queryGroupSet.size >= 2 && queryClauses.some(c => clausePinsOrEquatesGroupCol(c, queryGroupSet))) {
|
|
447
|
+
return fail('group-key-pinned');
|
|
448
|
+
}
|
|
449
|
+
// A rollup with a residual is sound: the residual coverage check above admits only
|
|
450
|
+
// conjuncts over MV group-key columns, which partition whole MV groups — so the
|
|
451
|
+
// residual `Filter` on the backing scan keeps or drops each backing group entire,
|
|
452
|
+
// and the re-aggregate over the survivors commutes with it (filtering whole groups
|
|
453
|
+
// pre- vs post-rollup is identical). The rule builds that residual `Filter` on the
|
|
454
|
+
// backing scan before the re-aggregate (the same `buildBackingSource` the exact-key
|
|
455
|
+
// and projection-filter arms use). This shape — `group by k` re-aggregating a
|
|
456
|
+
// composite-PK backing with a `WHERE` on a non-grouped PK column — previously
|
|
457
|
+
// tripped a base streaming-aggregate filter-drop bug, which is now fixed
|
|
458
|
+
// (`streaming-aggregate-stale-group-context-shadows-child-filter`); the equivalence
|
|
459
|
+
// harness covers the rollup+residual shapes as the soundness backstop.
|
|
460
|
+
// ---- Exact-key: assemble the output column map for the group-key passthroughs so
|
|
461
|
+
// the foundation's `buildReplacement` can re-emit the whole row directly. ----
|
|
462
|
+
if (exact) {
|
|
463
|
+
const groupOut = [];
|
|
464
|
+
shape.groupBaseCols.forEach((_gc, i) => {
|
|
465
|
+
groupOut.push({ attrId: shape.groupOutAttrs[i].id, backingCol: groupKeyBackingCols[i] });
|
|
466
|
+
});
|
|
467
|
+
// outputColumnMap currently holds the aggregate outputs; prepend the group outputs
|
|
468
|
+
// so the order matches the fragment's [group…, aggregate…] output order.
|
|
469
|
+
outputColumnMap.unshift(...groupOut);
|
|
470
|
+
}
|
|
471
|
+
const rollup = {
|
|
472
|
+
exact,
|
|
473
|
+
groupKeyBackingCols,
|
|
474
|
+
groupOutAttrs: shape.groupOutAttrs,
|
|
475
|
+
aggregates: recipes,
|
|
476
|
+
};
|
|
477
|
+
return {
|
|
478
|
+
match: {
|
|
479
|
+
mv,
|
|
480
|
+
backing,
|
|
481
|
+
residualClauses,
|
|
482
|
+
residualConjuncts,
|
|
483
|
+
outputColumnMap,
|
|
484
|
+
backingColOfBaseCol,
|
|
485
|
+
rollup,
|
|
486
|
+
},
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* Convenience entry point (used by the unit tests): analyze `root` as an aggregate
|
|
491
|
+
* fragment and, on success, match it against `mv`.
|
|
492
|
+
*/
|
|
493
|
+
export function matchAggregateMaterializedViewRewrite(root, mv, backing, isDeterministic) {
|
|
494
|
+
const frag = analyzeAggregateFragment(root);
|
|
495
|
+
if (!frag.ok)
|
|
496
|
+
return fail(frag.reason);
|
|
497
|
+
return matchAggregateFragmentToMv(frag.shape, mv, backing, isDeterministic);
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Recognize a query fragment rooted at a `ProjectNode` whose source descends —
|
|
501
|
+
* through an optional `Filter` (post-join WHERE) and single-source pass-throughs —
|
|
502
|
+
* into a single binary {@link JoinNode} over exactly two distinct base tables. Any
|
|
503
|
+
* other shape (no join, a multi-way join, a non-passthrough node, a row-reducing
|
|
504
|
+
* scan) ⇒ `'shape'`. The fragment is pristine (the rule fires before
|
|
505
|
+
* predicate-pushdown / grow-retrieve), so the join's `ON` condition and the WHERE
|
|
506
|
+
* `Filter` are still explicit.
|
|
507
|
+
*/
|
|
508
|
+
export function analyzeJoinQueryFragment(root) {
|
|
509
|
+
if (!(root instanceof ProjectNode))
|
|
510
|
+
return { ok: false, reason: 'shape' };
|
|
511
|
+
const walk = walkToFragmentJoin(root.source);
|
|
512
|
+
if (!walk)
|
|
513
|
+
return { ok: false, reason: 'shape' };
|
|
514
|
+
const { joinNode, conjuncts } = walk;
|
|
515
|
+
// Exactly two distinct base tables under the join (a self-join or a 3-way join
|
|
516
|
+
// is out of scope — the latter is partial-join matching, deferred).
|
|
517
|
+
const tableRefs = collectBaseTableRefs(joinNode);
|
|
518
|
+
if (tableRefs.length !== 2)
|
|
519
|
+
return { ok: false, reason: 'shape' };
|
|
520
|
+
if (qualifiedOf(tableRefs[0].tableSchema) === qualifiedOf(tableRefs[1].tableSchema)) {
|
|
521
|
+
return { ok: false, reason: 'shape' }; // self-join
|
|
522
|
+
}
|
|
523
|
+
return { ok: true, shape: { project: root, joinNode, tableRefs: [tableRefs[0], tableRefs[1]], conjuncts } };
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Walk a fragment's `Project.source` down to its single binary join, collecting the
|
|
527
|
+
* post-join WHERE conjuncts. Passes through `Filter` (splitting its predicate),
|
|
528
|
+
* `Retrieve`, and `Alias`; returns `undefined` for any other node (so a Distinct /
|
|
529
|
+
* Aggregate / second join above the first ⇒ NotMatch).
|
|
530
|
+
*/
|
|
531
|
+
function walkToFragmentJoin(start) {
|
|
532
|
+
const conjuncts = [];
|
|
533
|
+
let node = start;
|
|
534
|
+
while (node) {
|
|
535
|
+
if (node instanceof JoinNode)
|
|
536
|
+
return { joinNode: node, conjuncts };
|
|
537
|
+
if (node instanceof FilterNode) {
|
|
538
|
+
splitConjuncts(node.predicate, conjuncts);
|
|
539
|
+
node = node.source;
|
|
540
|
+
continue;
|
|
541
|
+
}
|
|
542
|
+
if (node instanceof RetrieveNode || node instanceof AliasNode) {
|
|
543
|
+
node = singleRelation(node);
|
|
544
|
+
if (!node)
|
|
545
|
+
return undefined;
|
|
546
|
+
continue;
|
|
547
|
+
}
|
|
548
|
+
return undefined;
|
|
549
|
+
}
|
|
550
|
+
return undefined;
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* Decide whether the 1:1-join MV `mv` (optimized body `mvBodyRoot`, backed by
|
|
554
|
+
* `backing`) answers the join fragment `shape`. See the arm doc above for the
|
|
555
|
+
* soundness contract; every check forgoes on doubt.
|
|
556
|
+
*
|
|
557
|
+
* `mvBodyRoot` is the MV body's optimized relational root (the rule plans it
|
|
558
|
+
* suppressed and caches it). When the rule could not plan it, pass `undefined` ⇒
|
|
559
|
+
* `no-candidate`.
|
|
560
|
+
*/
|
|
561
|
+
export function matchJoinFragmentToMv(shape, mv, mvBodyRoot, backing, isDeterministic) {
|
|
562
|
+
// ---- Candidate gates (a false-positive here only forgoes a speedup). ----
|
|
563
|
+
if (mv.derivation.stale === true)
|
|
564
|
+
return fail('no-candidate');
|
|
565
|
+
if (!backing)
|
|
566
|
+
return fail('no-candidate');
|
|
567
|
+
if (!mvBodyRoot)
|
|
568
|
+
return fail('no-candidate'); // body did not plan
|
|
569
|
+
if (mvBodyHasNonDeterminism(mv.derivation.selectAst, isDeterministic))
|
|
570
|
+
return fail('no-candidate');
|
|
571
|
+
// ---- Backing alignment: the stored-column map keys each backing column by the
|
|
572
|
+
// MV body's output *position*, which is only sound while the backing columns
|
|
573
|
+
// correspond positionally to the body output (the invariant established when
|
|
574
|
+
// the backing is built from the body). A source `alter` can desynchronize
|
|
575
|
+
// them — `refresh` does not reorder the backing to match a re-planned
|
|
576
|
+
// `select *` body, which interleaves the new column — so a stale mapping would
|
|
577
|
+
// read the wrong backing column or run off its end. Verify positional name
|
|
578
|
+
// alignment up front and forgo (→ correct base recompute) on any mismatch. ----
|
|
579
|
+
if (!backingAlignsWithBody(mvBodyRoot, backing))
|
|
580
|
+
return fail('no-candidate');
|
|
581
|
+
// ---- Source-set match: the MV must read exactly the fragment's two tables. ----
|
|
582
|
+
const qualA = qualifiedOf(shape.tableRefs[0].tableSchema);
|
|
583
|
+
const qualB = qualifiedOf(shape.tableRefs[1].tableSchema);
|
|
584
|
+
const mvSources = new Set(mv.derivation.sourceTables);
|
|
585
|
+
if (mvSources.size !== 2 || !mvSources.has(qualA) || !mvSources.has(qualB)) {
|
|
586
|
+
return fail('source-mismatch');
|
|
587
|
+
}
|
|
588
|
+
// ---- Join equivalence: find a (T, lookup) assignment over which BOTH the
|
|
589
|
+
// fragment and the MV body are provably 1:1, with the same inner/cross join
|
|
590
|
+
// type and the same equi-pairs. A mismatch on any ⇒ NotMatch (shape). ----
|
|
591
|
+
const assignment = findJoinAssignment(shape, mvBodyRoot);
|
|
592
|
+
if (!assignment)
|
|
593
|
+
return fail('shape');
|
|
594
|
+
const { drivingRef, lookupRef, mvDrivingRef, mvLookupRef } = assignment;
|
|
595
|
+
// ---- Stored-column map: each MV backing column (by output position) → the
|
|
596
|
+
// (side, base column) it passes through. A computed MV column is unmapped. ----
|
|
597
|
+
const stored = mvStoredJoinColumns(mvBodyRoot, mvDrivingRef, mvLookupRef);
|
|
598
|
+
// Fragment source attribute id → (side, base column).
|
|
599
|
+
const fragDriving = attrToBaseCol(drivingRef);
|
|
600
|
+
const fragLookup = attrToBaseCol(lookupRef);
|
|
601
|
+
// ---- Projection coverage: every fragment output column must be a bare
|
|
602
|
+
// passthrough of a `T`/`P` column the MV stores. ----
|
|
603
|
+
const outputColumnMap = [];
|
|
604
|
+
const outAttrs = shape.project.getAttributes();
|
|
605
|
+
const projections = shape.project.projections;
|
|
606
|
+
for (let i = 0; i < projections.length; i++) {
|
|
607
|
+
const proj = projections[i];
|
|
608
|
+
if (!(proj.node instanceof ColumnReferenceNode))
|
|
609
|
+
return fail('missing-column'); // computed output
|
|
610
|
+
const backingCol = backingColForAttr(proj.node.attributeId, fragDriving, fragLookup, stored);
|
|
611
|
+
if (backingCol === undefined)
|
|
612
|
+
return fail('missing-column');
|
|
613
|
+
outputColumnMap.push({ attrId: outAttrs[i].id, backingCol });
|
|
614
|
+
}
|
|
615
|
+
// ---- Residual: the MV body has no WHERE (the row-time gate forbids it), so the
|
|
616
|
+
// whole fragment WHERE is residual. Every conjunct must be a subquery-free
|
|
617
|
+
// predicate over stored `T`/`P` columns so it re-binds onto the backing. ----
|
|
618
|
+
const backingColOfSourceAttrId = storedSourceAttrIds(fragDriving, fragLookup, stored);
|
|
619
|
+
for (const conjunct of shape.conjuncts) {
|
|
620
|
+
if (conjunctHasSubquery(conjunct))
|
|
621
|
+
return fail('predicate-not-entailed');
|
|
622
|
+
for (const attrId of conjunctColumnAttrIds(conjunct)) {
|
|
623
|
+
if (!backingColOfSourceAttrId.has(attrId))
|
|
624
|
+
return fail('missing-column');
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
return {
|
|
628
|
+
match: {
|
|
629
|
+
mv,
|
|
630
|
+
backing,
|
|
631
|
+
residualClauses: [],
|
|
632
|
+
residualConjuncts: shape.conjuncts,
|
|
633
|
+
outputColumnMap,
|
|
634
|
+
backingColOfBaseCol: new Map(),
|
|
635
|
+
backingColOfSourceAttrId,
|
|
636
|
+
joinInfo: { drivingTable: drivingRef.tableSchema, lookupTable: lookupRef.tableSchema },
|
|
637
|
+
},
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* Convenience entry point (used by the unit tests): analyze `root` as a join
|
|
642
|
+
* fragment and, on success, match it against `mv` (whose optimized body root is
|
|
643
|
+
* `mvBodyRoot`).
|
|
644
|
+
*/
|
|
645
|
+
export function matchJoinMaterializedViewRewrite(root, mv, mvBodyRoot, backing, isDeterministic) {
|
|
646
|
+
const frag = analyzeJoinQueryFragment(root);
|
|
647
|
+
if (!frag.ok)
|
|
648
|
+
return fail(frag.reason);
|
|
649
|
+
return matchJoinFragmentToMv(frag.shape, mv, mvBodyRoot, backing, isDeterministic);
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* Find a (driving, lookup) assignment of the fragment's two tables over which the
|
|
653
|
+
* fragment join *and* the MV body join are both provably 1:1 (inner/cross),
|
|
654
|
+
* carrying the same equi-pairs in `(driving-col, lookup-col)` terms. Tries each
|
|
655
|
+
* table as the driver; returns the first that discharges every obligation, else
|
|
656
|
+
* `undefined` (⇒ a `shape` NotMatch). A 1:1 FK join determines the driver uniquely,
|
|
657
|
+
* so at most one assignment ever succeeds.
|
|
658
|
+
*/
|
|
659
|
+
function findJoinAssignment(shape, mvBodyRoot) {
|
|
660
|
+
const [a, b] = shape.tableRefs;
|
|
661
|
+
for (const [drivingRef, lookupRef] of [[a, b], [b, a]]) {
|
|
662
|
+
const driving = drivingRef.tableSchema;
|
|
663
|
+
// Both joins must be 1:1 over this driver, via an inner/cross top join.
|
|
664
|
+
const fragProof = proveOneToOneJoin(shape.joinNode, driving);
|
|
665
|
+
if (!fragProof.ok || !fragProof.topJoin || !isInnerOrCross(fragProof.topJoin))
|
|
666
|
+
continue;
|
|
667
|
+
const mvProof = proveOneToOneJoin(mvBodyRoot, driving);
|
|
668
|
+
if (!mvProof.ok || !mvProof.topJoin || !isInnerOrCross(mvProof.topJoin))
|
|
669
|
+
continue;
|
|
670
|
+
// Locate the MV body's matching table references (by qualified name).
|
|
671
|
+
const mvDrivingRef = findTableRef(mvBodyRoot, qualifiedOf(driving));
|
|
672
|
+
const mvLookupRef = findTableRef(mvBodyRoot, qualifiedOf(lookupRef.tableSchema));
|
|
673
|
+
if (!mvDrivingRef || !mvLookupRef)
|
|
674
|
+
continue;
|
|
675
|
+
// Same equi-pairs (as a set, in driving→lookup base-column terms).
|
|
676
|
+
const fragPairs = equiPairsByBaseCol(fragProof.topJoin, drivingRef, lookupRef);
|
|
677
|
+
const mvPairs = equiPairsByBaseCol(mvProof.topJoin, mvDrivingRef, mvLookupRef);
|
|
678
|
+
if (!fragPairs || !mvPairs || !sameStringSet(fragPairs, mvPairs))
|
|
679
|
+
continue;
|
|
680
|
+
return { drivingRef, lookupRef, mvDrivingRef, mvLookupRef };
|
|
681
|
+
}
|
|
682
|
+
return undefined;
|
|
683
|
+
}
|
|
684
|
+
/** True when `join` is an inner or cross join (an outer join is deferred — its
|
|
685
|
+
* null-extended rows make the stored relation differ from an inner-join query). */
|
|
686
|
+
function isInnerOrCross(join) {
|
|
687
|
+
if (!CapabilityDetectors.isJoin(join))
|
|
688
|
+
return false;
|
|
689
|
+
const t = join.getJoinType();
|
|
690
|
+
return t === 'inner' || t === 'cross';
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* The join's equi-pairs as a set of `"drivingCol:lookupCol"` base-column keys, or
|
|
694
|
+
* `undefined` when the join is not a pure equi-join or a pair does not connect the
|
|
695
|
+
* driving and lookup tables cleanly. Shared shape for fragment and MV comparison.
|
|
696
|
+
*/
|
|
697
|
+
function equiPairsByBaseCol(topJoin, drivingRef, lookupRef) {
|
|
698
|
+
const pairs = pureJoinEquiAttrPairs(topJoin);
|
|
699
|
+
if (!pairs || pairs.length === 0)
|
|
700
|
+
return undefined;
|
|
701
|
+
const dCol = attrToBaseCol(drivingRef);
|
|
702
|
+
const lCol = attrToBaseCol(lookupRef);
|
|
703
|
+
const out = new Set();
|
|
704
|
+
for (const p of pairs) {
|
|
705
|
+
let d = dCol.get(p.leftAttrId);
|
|
706
|
+
let l = lCol.get(p.rightAttrId);
|
|
707
|
+
if (d === undefined || l === undefined) {
|
|
708
|
+
d = dCol.get(p.rightAttrId);
|
|
709
|
+
l = lCol.get(p.leftAttrId);
|
|
710
|
+
}
|
|
711
|
+
if (d === undefined || l === undefined)
|
|
712
|
+
return undefined; // not a clean driving↔lookup pair
|
|
713
|
+
out.add(`${d}:${l}`);
|
|
714
|
+
}
|
|
715
|
+
return out;
|
|
716
|
+
}
|
|
717
|
+
function mvStoredJoinColumns(mvBodyRoot, mvDrivingRef, mvLookupRef) {
|
|
718
|
+
const dAttr = attrToBaseCol(mvDrivingRef);
|
|
719
|
+
const lAttr = attrToBaseCol(mvLookupRef);
|
|
720
|
+
const driving = new Map();
|
|
721
|
+
const lookup = new Map();
|
|
722
|
+
// A bare passthrough output column preserves its source attribute id, so the MV
|
|
723
|
+
// output attribute id resolves back to a driving/lookup base column (cf. the
|
|
724
|
+
// projection-coverage technique in `coverage-prover.ts`). A computed column gets
|
|
725
|
+
// a fresh id absent from both maps and is left unmapped.
|
|
726
|
+
mvBodyRoot.getAttributes().forEach((attr, backingCol) => {
|
|
727
|
+
const d = dAttr.get(attr.id);
|
|
728
|
+
if (d !== undefined) {
|
|
729
|
+
if (!driving.has(d))
|
|
730
|
+
driving.set(d, backingCol);
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
const l = lAttr.get(attr.id);
|
|
734
|
+
if (l !== undefined && !lookup.has(l))
|
|
735
|
+
lookup.set(l, backingCol);
|
|
736
|
+
});
|
|
737
|
+
return { driving, lookup };
|
|
738
|
+
}
|
|
739
|
+
/** The backing column storing the fragment source attribute `attrId` (resolved to a
|
|
740
|
+
* driving/lookup base column), or `undefined` when the MV does not store it. */
|
|
741
|
+
function backingColForAttr(attrId, fragDriving, fragLookup, stored) {
|
|
742
|
+
const d = fragDriving.get(attrId);
|
|
743
|
+
if (d !== undefined)
|
|
744
|
+
return stored.driving.get(d);
|
|
745
|
+
const l = fragLookup.get(attrId);
|
|
746
|
+
if (l !== undefined)
|
|
747
|
+
return stored.lookup.get(l);
|
|
748
|
+
return undefined;
|
|
749
|
+
}
|
|
750
|
+
/** Fragment source attribute id → backing column, for every `T`/`P` column the MV
|
|
751
|
+
* stores — the residual re-bind map (`backingColOfSourceAttrId`). */
|
|
752
|
+
function storedSourceAttrIds(fragDriving, fragLookup, stored) {
|
|
753
|
+
const out = new Map();
|
|
754
|
+
fragDriving.forEach((baseCol, attrId) => {
|
|
755
|
+
const bc = stored.driving.get(baseCol);
|
|
756
|
+
if (bc !== undefined)
|
|
757
|
+
out.set(attrId, bc);
|
|
758
|
+
});
|
|
759
|
+
fragLookup.forEach((baseCol, attrId) => {
|
|
760
|
+
const bc = stored.lookup.get(baseCol);
|
|
761
|
+
if (bc !== undefined)
|
|
762
|
+
out.set(attrId, bc);
|
|
763
|
+
});
|
|
764
|
+
return out;
|
|
765
|
+
}
|
|
766
|
+
/** Stable attribute id → base column index for a table reference. */
|
|
767
|
+
function attrToBaseCol(ref) {
|
|
768
|
+
const out = new Map();
|
|
769
|
+
ref.getAttributes().forEach((attr, i) => out.set(attr.id, i));
|
|
770
|
+
return out;
|
|
771
|
+
}
|
|
772
|
+
/** All `TableReferenceNode`s in `node`'s subtree (depth-first, left-to-right). */
|
|
773
|
+
function collectBaseTableRefs(node) {
|
|
774
|
+
if (node instanceof TableReferenceNode)
|
|
775
|
+
return [node];
|
|
776
|
+
const out = [];
|
|
777
|
+
for (const rel of node.getRelations())
|
|
778
|
+
out.push(...collectBaseTableRefs(rel));
|
|
779
|
+
return out;
|
|
780
|
+
}
|
|
781
|
+
/** The first `TableReferenceNode` in `node`'s subtree over the qualified table, or
|
|
782
|
+
* `undefined`. The fragment rejects self-joins upstream, so the first match is
|
|
783
|
+
* unambiguous. */
|
|
784
|
+
function findTableRef(node, qualified) {
|
|
785
|
+
if (node instanceof TableReferenceNode) {
|
|
786
|
+
return qualifiedOf(node.tableSchema) === qualified ? node : undefined;
|
|
787
|
+
}
|
|
788
|
+
for (const rel of node.getRelations()) {
|
|
789
|
+
const found = findTableRef(rel, qualified);
|
|
790
|
+
if (found)
|
|
791
|
+
return found;
|
|
792
|
+
}
|
|
793
|
+
return undefined;
|
|
794
|
+
}
|
|
795
|
+
/** True iff `conjunct` embeds a relational subquery (an Exists/In/scalar-subquery
|
|
796
|
+
* whose child is a relation). Such a predicate is not re-bindable onto a flat
|
|
797
|
+
* backing scan, so the join arm forgoes it (matching the foundation's no-subquery
|
|
798
|
+
* invariant that licenses `sideEffectMode: 'safe'`). */
|
|
799
|
+
function conjunctHasSubquery(node) {
|
|
800
|
+
for (const child of node.getChildren()) {
|
|
801
|
+
if (isRelationalNode(child))
|
|
802
|
+
return true;
|
|
803
|
+
if (conjunctHasSubquery(child))
|
|
804
|
+
return true;
|
|
805
|
+
}
|
|
806
|
+
return false;
|
|
807
|
+
}
|
|
808
|
+
/** Every `ColumnReferenceNode` attribute id referenced (transitively) by a
|
|
809
|
+
* subquery-free conjunct. */
|
|
810
|
+
function conjunctColumnAttrIds(node) {
|
|
811
|
+
const out = [];
|
|
812
|
+
const visit = (n) => {
|
|
813
|
+
if (n instanceof ColumnReferenceNode)
|
|
814
|
+
out.push(n.attributeId);
|
|
815
|
+
for (const child of n.getChildren())
|
|
816
|
+
visit(child);
|
|
817
|
+
};
|
|
818
|
+
visit(node);
|
|
819
|
+
return out;
|
|
820
|
+
}
|
|
821
|
+
/**
|
|
822
|
+
* True iff the backing table's columns correspond positionally — same count, same
|
|
823
|
+
* names in order — to the MV body root's output attributes. This is the invariant
|
|
824
|
+
* the position-keyed stored-column map relies on (the backing is built column-for-
|
|
825
|
+
* column from the body output). A source `alter` followed by `refresh` can break it
|
|
826
|
+
* without marking the MV permanently unusable: the backing's column order and a
|
|
827
|
+
* re-planned `select *` body's output order diverge (a new source column appends to
|
|
828
|
+
* the backing but interleaves in the body). The join arm verifies the invariant
|
|
829
|
+
* here rather than trusting position, so a desynchronized MV forgoes the rewrite
|
|
830
|
+
* (the base recompute stays correct) instead of reading the wrong column.
|
|
831
|
+
*/
|
|
832
|
+
function backingAlignsWithBody(mvBodyRoot, backing) {
|
|
833
|
+
const attrs = mvBodyRoot.getAttributes();
|
|
834
|
+
if (attrs.length !== backing.columns.length)
|
|
835
|
+
return false;
|
|
836
|
+
for (let i = 0; i < attrs.length; i++) {
|
|
837
|
+
if (attrs[i].name.toLowerCase() !== backing.columns[i].name.toLowerCase())
|
|
838
|
+
return false;
|
|
839
|
+
}
|
|
840
|
+
return true;
|
|
841
|
+
}
|
|
842
|
+
/** `schema.table` lowercased — the canonical qualified key matching `sourceTables`. */
|
|
843
|
+
function qualifiedOf(table) {
|
|
844
|
+
return `${table.schemaName}.${table.name}`.toLowerCase();
|
|
845
|
+
}
|
|
846
|
+
/** Set equality over two string sets. */
|
|
847
|
+
function sameStringSet(a, b) {
|
|
848
|
+
if (a.size !== b.size)
|
|
849
|
+
return false;
|
|
850
|
+
for (const x of a)
|
|
851
|
+
if (!b.has(x))
|
|
852
|
+
return false;
|
|
853
|
+
return true;
|
|
854
|
+
}
|
|
855
|
+
/** Decomposable rollup aggregates that re-aggregate as plain `sum`/`min`/`max`. */
|
|
856
|
+
const ROLLUP_SUM_LIKE = new Map([
|
|
857
|
+
['sum', 'sum'], ['min', 'min'], ['max', 'max'],
|
|
858
|
+
]);
|
|
859
|
+
/**
|
|
860
|
+
* Exact-key recipe: the query aggregate must be *exactly* a stored MV aggregate
|
|
861
|
+
* (same function, same argument column, same DISTINCT flag). The stored value is
|
|
862
|
+
* the answer, so this admits any aggregate — including `count(distinct)` /
|
|
863
|
+
* `group_concat` — as a passthrough. Returns a `sum`-kind passthrough recipe whose
|
|
864
|
+
* `backingCols[0]` is the stored column (the kind is unused for exact-key, which the
|
|
865
|
+
* rule answers via `outputColumnMap`).
|
|
866
|
+
*/
|
|
867
|
+
function recipeForExact(qa, stored) {
|
|
868
|
+
const match = stored.find(sa => sa.funcName === qa.funcName && sa.argBaseCol === qa.argBaseCol && sa.isDistinct === qa.isDistinct);
|
|
869
|
+
if (!match)
|
|
870
|
+
return undefined;
|
|
871
|
+
return { outAttr: qa.outAttr, kind: 'sum', backingCols: [match.backingCol] };
|
|
872
|
+
}
|
|
873
|
+
/**
|
|
874
|
+
* Rollup recipe: reconstruct `qa` by re-aggregating the MV's stored partials. Only
|
|
875
|
+
* the decomposable allowlist is admitted; a DISTINCT aggregate never composes.
|
|
876
|
+
*/
|
|
877
|
+
function recipeForRollup(qa, stored, baseTable) {
|
|
878
|
+
if (qa.isDistinct)
|
|
879
|
+
return undefined; // count(distinct …) and friends never compose under rollup.
|
|
880
|
+
const sumLike = ROLLUP_SUM_LIKE.get(qa.funcName);
|
|
881
|
+
if (sumLike) {
|
|
882
|
+
// sum(x) ← sum(stored sum(x)); min/max(x) ← min/max(stored min/max(x)).
|
|
883
|
+
const col = findStored(stored, qa.funcName, qa.argBaseCol);
|
|
884
|
+
return col === undefined ? undefined : { outAttr: qa.outAttr, kind: sumLike, backingCols: [col] };
|
|
885
|
+
}
|
|
886
|
+
if (qa.funcName === 'count') {
|
|
887
|
+
// count(*) ← sum(stored count(*)); count(x) ← sum(stored count(x)). The 'count'
|
|
888
|
+
// kind adds the coalesce-to-0 the rule emits (count over zero rows is 0, not NULL).
|
|
889
|
+
const col = findStored(stored, 'count', qa.argBaseCol);
|
|
890
|
+
return col === undefined ? undefined : { outAttr: qa.outAttr, kind: 'count', backingCols: [col] };
|
|
891
|
+
}
|
|
892
|
+
if (qa.funcName === 'avg') {
|
|
893
|
+
// avg(x) ← sum(stored sum(x)) / sum(stored count). Requires both partials; the
|
|
894
|
+
// count must exclude the same NULLs `avg` does — a stored `count(x)` always
|
|
895
|
+
// qualifies, and a stored `count(*)` only when `x` is declared NOT NULL.
|
|
896
|
+
const sumCol = findStored(stored, 'sum', qa.argBaseCol);
|
|
897
|
+
if (sumCol === undefined)
|
|
898
|
+
return undefined;
|
|
899
|
+
let countCol = findStored(stored, 'count', qa.argBaseCol);
|
|
900
|
+
if (countCol === undefined && qa.argBaseCol !== undefined && baseTable.columns[qa.argBaseCol]?.notNull === true) {
|
|
901
|
+
countCol = findStored(stored, 'count', undefined); // count(*) is sound when x is NOT NULL.
|
|
902
|
+
}
|
|
903
|
+
if (countCol === undefined)
|
|
904
|
+
return undefined;
|
|
905
|
+
return { outAttr: qa.outAttr, kind: 'avg', backingCols: [sumCol, countCol] };
|
|
906
|
+
}
|
|
907
|
+
return undefined; // total / group_concat / unknown ⇒ not decomposable.
|
|
908
|
+
}
|
|
909
|
+
/** The backing column of a stored, non-distinct aggregate `f(argBaseCol)`, or undefined. */
|
|
910
|
+
function findStored(stored, funcName, argBaseCol) {
|
|
911
|
+
const match = stored.find(sa => sa.funcName === funcName && sa.argBaseCol === argBaseCol && !sa.isDistinct);
|
|
912
|
+
return match?.backingCol;
|
|
913
|
+
}
|
|
914
|
+
/**
|
|
915
|
+
* Parse a grouped MV's select list into its group-key passthrough columns and its
|
|
916
|
+
* stored aggregate columns, by backing-column position. A bare column is a group
|
|
917
|
+
* key; an aggregate function call (`count(*)`, or `f(col)`) is a stored aggregate;
|
|
918
|
+
* a `*` or any other computed item is ignored (it answers no group key or aggregate).
|
|
919
|
+
* Returns undefined only for a `table.*` naming a non-base table (defensive).
|
|
920
|
+
*/
|
|
921
|
+
function analyzeMvStoredColumns(columns, baseTable) {
|
|
922
|
+
const groupBackingOfBaseCol = new Map();
|
|
923
|
+
const storedAggs = [];
|
|
924
|
+
for (let backingCol = 0; backingCol < columns.length; backingCol++) {
|
|
925
|
+
const col = columns[backingCol];
|
|
926
|
+
if (col.type === 'all') {
|
|
927
|
+
if (col.table && col.table.toLowerCase() !== baseTable.name.toLowerCase())
|
|
928
|
+
return undefined;
|
|
929
|
+
// A `*` in a grouped body would be an error at create time; ignore defensively.
|
|
930
|
+
continue;
|
|
931
|
+
}
|
|
932
|
+
const expr = col.expr;
|
|
933
|
+
if (expr.type === 'function') {
|
|
934
|
+
const fn = expr;
|
|
935
|
+
const argBaseCol = mvAggregateArgBaseCol(fn, baseTable);
|
|
936
|
+
if (argBaseCol === 'unrecognized')
|
|
937
|
+
continue; // computed/multi-arg ⇒ unusable
|
|
938
|
+
storedAggs.push({
|
|
939
|
+
funcName: fn.name.toLowerCase(),
|
|
940
|
+
argBaseCol,
|
|
941
|
+
isDistinct: fn.distinct === true,
|
|
942
|
+
backingCol,
|
|
943
|
+
});
|
|
944
|
+
continue;
|
|
945
|
+
}
|
|
946
|
+
const baseCol = baseColumnOfExpr(expr, baseTable);
|
|
947
|
+
if (baseCol !== undefined && !groupBackingOfBaseCol.has(baseCol)) {
|
|
948
|
+
groupBackingOfBaseCol.set(baseCol, backingCol);
|
|
949
|
+
}
|
|
950
|
+
// A computed non-aggregate select item is left unmapped (unusable).
|
|
951
|
+
}
|
|
952
|
+
return { groupBackingOfBaseCol, storedAggs };
|
|
953
|
+
}
|
|
954
|
+
/**
|
|
955
|
+
* The base-table column index of an MV aggregate's argument: `undefined` for
|
|
956
|
+
* `count(*)` (no argument), a base column index for a single bare-column argument,
|
|
957
|
+
* or `'unrecognized'` for a computed / multi-argument call (which no fragment
|
|
958
|
+
* aggregate — itself required to be bare — can match).
|
|
959
|
+
*/
|
|
960
|
+
function mvAggregateArgBaseCol(fn, baseTable) {
|
|
961
|
+
if (fn.args.length === 0)
|
|
962
|
+
return undefined;
|
|
963
|
+
if (fn.args.length === 1) {
|
|
964
|
+
const col = baseColumnOfExpr(fn.args[0], baseTable);
|
|
965
|
+
return col === undefined ? 'unrecognized' : col;
|
|
966
|
+
}
|
|
967
|
+
return 'unrecognized';
|
|
968
|
+
}
|
|
969
|
+
/**
|
|
970
|
+
* True iff the backing table's primary key is exactly the MV's group key (mapped to
|
|
971
|
+
* backing columns). The witness that the backing is one row per MV group.
|
|
972
|
+
*/
|
|
973
|
+
function backingPkIsGroupKey(backing, mvGroupBaseCols, groupBackingOfBaseCol) {
|
|
974
|
+
const pk = backing.primaryKeyDefinition;
|
|
975
|
+
if (pk.length !== mvGroupBaseCols.length)
|
|
976
|
+
return false;
|
|
977
|
+
const pkSet = new Set(pk.map(c => c.index));
|
|
978
|
+
if (pkSet.size !== pk.length)
|
|
979
|
+
return false;
|
|
980
|
+
for (const gc of mvGroupBaseCols) {
|
|
981
|
+
const bc = groupBackingOfBaseCol.get(gc);
|
|
982
|
+
if (bc === undefined || !pkSet.has(bc))
|
|
983
|
+
return false;
|
|
984
|
+
}
|
|
985
|
+
return true;
|
|
986
|
+
}
|
|
987
|
+
/** The sole relational child of a single-source pass-through, or undefined. */
|
|
988
|
+
function singleRelation(node) {
|
|
989
|
+
const rels = node.getRelations();
|
|
990
|
+
return rels.length === 1 ? rels[0] : undefined;
|
|
991
|
+
}
|
|
992
|
+
/** Flatten a predicate into its top-level AND conjuncts (plan-node level). */
|
|
993
|
+
function splitConjuncts(predicate, out) {
|
|
994
|
+
if (predicate instanceof BinaryOpNode && predicate.expression.operator === 'AND') {
|
|
995
|
+
splitConjuncts(predicate.left, out);
|
|
996
|
+
splitConjuncts(predicate.right, out);
|
|
997
|
+
return;
|
|
998
|
+
}
|
|
999
|
+
out.push(predicate);
|
|
1000
|
+
}
|
|
1001
|
+
/** The originating AST of a scalar plan node, or undefined when it has none. */
|
|
1002
|
+
function conjunctExpression(node) {
|
|
1003
|
+
const expr = node.expression;
|
|
1004
|
+
return expr && typeof expr === 'object' && 'type' in expr
|
|
1005
|
+
? expr
|
|
1006
|
+
: undefined;
|
|
1007
|
+
}
|
|
1008
|
+
/**
|
|
1009
|
+
* Map each MV backing column (by output position) to the base-table column it
|
|
1010
|
+
* passes through, reading the MV's select list. A `*` expands to every base
|
|
1011
|
+
* column in order; a bare column resolves by name; a computed item leaves that
|
|
1012
|
+
* position `undefined` (unmapped). Returns undefined when a `table.*` form names a
|
|
1013
|
+
* table other than the base (cannot happen for a single-source body, but rejected
|
|
1014
|
+
* defensively).
|
|
1015
|
+
*/
|
|
1016
|
+
function mvProjectionBaseCols(columns, baseTable) {
|
|
1017
|
+
const out = [];
|
|
1018
|
+
for (const col of columns) {
|
|
1019
|
+
if (col.type === 'all') {
|
|
1020
|
+
if (col.table && col.table.toLowerCase() !== baseTable.name.toLowerCase())
|
|
1021
|
+
return undefined;
|
|
1022
|
+
for (let i = 0; i < baseTable.columns.length; i++)
|
|
1023
|
+
out.push(i);
|
|
1024
|
+
continue;
|
|
1025
|
+
}
|
|
1026
|
+
out.push(baseColumnOfExpr(col.expr, baseTable));
|
|
1027
|
+
}
|
|
1028
|
+
return out;
|
|
1029
|
+
}
|
|
1030
|
+
/** Resolve a bare column / identifier expression to a base-table column index. */
|
|
1031
|
+
function baseColumnOfExpr(expr, baseTable) {
|
|
1032
|
+
if (expr.type === 'column') {
|
|
1033
|
+
return baseTable.columnIndexMap.get(expr.name.toLowerCase());
|
|
1034
|
+
}
|
|
1035
|
+
if (expr.type === 'identifier') {
|
|
1036
|
+
const id = expr;
|
|
1037
|
+
if (id.schema)
|
|
1038
|
+
return undefined;
|
|
1039
|
+
return baseTable.columnIndexMap.get(id.name.toLowerCase());
|
|
1040
|
+
}
|
|
1041
|
+
return undefined;
|
|
1042
|
+
}
|
|
1043
|
+
/**
|
|
1044
|
+
* True when `clause` constant-pins (`g = literal`, `g is null`) or equates
|
|
1045
|
+
* (`g1 = g2`) a column in `groupSet` — the predicate shapes that give a group-key
|
|
1046
|
+
* column a determining FD and so drive `rule-groupby-fd-simplification` to drop and
|
|
1047
|
+
* reposition it. Range / IN (`or-of`) clauses create no such FD and return false.
|
|
1048
|
+
*/
|
|
1049
|
+
function clausePinsOrEquatesGroupCol(clause, groupSet) {
|
|
1050
|
+
switch (clause.kind) {
|
|
1051
|
+
case 'eq-literal': return groupSet.has(clause.column);
|
|
1052
|
+
case 'is-null': return groupSet.has(clause.column);
|
|
1053
|
+
case 'eq-column': return groupSet.has(clause.left) || groupSet.has(clause.right);
|
|
1054
|
+
default: return false; // range / or-of: no constant-determining FD
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
/** The base-table column indices a recognized guard clause references. */
|
|
1058
|
+
function clauseColumns(clause) {
|
|
1059
|
+
switch (clause.kind) {
|
|
1060
|
+
case 'eq-literal': return [clause.column];
|
|
1061
|
+
case 'eq-column': return [clause.left, clause.right];
|
|
1062
|
+
case 'is-null': return [clause.column];
|
|
1063
|
+
case 'range': return [clause.column];
|
|
1064
|
+
case 'or-of': return clause.clauses.flatMap(clauseColumns);
|
|
1065
|
+
default: return [];
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
/** True when the MV body's WHERE or any projection expression calls a
|
|
1069
|
+
* non-deterministic function (or embeds a subquery). */
|
|
1070
|
+
function mvBodyHasNonDeterminism(selectAst, isDeterministic) {
|
|
1071
|
+
if (selectAst.type !== 'select')
|
|
1072
|
+
return false;
|
|
1073
|
+
if (selectAst.where && containsNonDeterministicCall(selectAst.where, isDeterministic))
|
|
1074
|
+
return true;
|
|
1075
|
+
for (const col of selectAst.columns) {
|
|
1076
|
+
if (col.type === 'column' && containsNonDeterministicCall(col.expr, isDeterministic))
|
|
1077
|
+
return true;
|
|
1078
|
+
}
|
|
1079
|
+
return false;
|
|
1080
|
+
}
|
|
1081
|
+
//# sourceMappingURL=query-rewrite-matcher.js.map
|