@quereus/quereus 3.3.0 → 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 -110
- 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 +793 -118
- 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 +276 -7
- package/dist/src/parser/parser.d.ts.map +1 -1
- package/dist/src/parser/parser.js +1387 -469
- 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/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 +108 -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 +3 -1
- package/dist/src/planner/analysis/constraint-extractor.d.ts.map +1 -1
- package/dist/src/planner/analysis/constraint-extractor.js +192 -9
- 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 +109 -30
- 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 +47 -0
- 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/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.js +3 -3
- 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 +18 -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/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 +124 -9
- 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.map +1 -1
- package/dist/src/planner/nodes/limit-offset.js +4 -5
- 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 +85 -11
- 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 +62 -27
- 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 +291 -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 +7 -6
- package/dist/src/planner/nodes/sort.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 +1 -0
- package/dist/src/planner/nodes/values-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/values-node.js +16 -6
- 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 +2 -2
- 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-groupby-fd-simplification.d.ts.map +1 -1
- package/dist/src/planner/rules/aggregate/rule-groupby-fd-simplification.js +9 -0
- 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/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 +38 -1
- 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.d.ts.map +1 -1
- package/dist/src/planner/rules/join/rule-fanout-lookup-join.js +19 -1
- 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 +9 -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 +12 -1
- package/dist/src/planner/rules/join/rule-join-physical-selection.js.map +1 -1
- package/dist/src/planner/rules/join/rule-lateral-top1-asof.d.ts.map +1 -1
- package/dist/src/planner/rules/join/rule-lateral-top1-asof.js +4 -0
- 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 +9 -0
- 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-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 +1 -1
- package/dist/src/planner/rules/sort/rule-orderby-fd-pruning.js +4 -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/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 +177 -43
- package/dist/src/planner/util/fd-utils.d.ts.map +1 -1
- package/dist/src/planner/util/fd-utils.js +396 -101
- 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.map +1 -1
- package/dist/src/planner/util/key-utils.js +81 -12
- 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 +29 -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 +18 -5
- 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 +5 -9
- 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 +8 -2
- package/dist/src/runtime/emit/bloom-join.js.map +1 -1
- package/dist/src/runtime/emit/constraint-check.js +15 -0
- 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/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 +15 -3
- 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/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,1038 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Coverage prover — recognizes when an explicit materialized view *covers* a
|
|
3
|
+
* UNIQUE constraint, i.e. its materialized row set is observation-equivalent to
|
|
4
|
+
* the set of rows the constraint governs, keyed so a point lookup answers the
|
|
5
|
+
* uniqueness question. Pure analysis: it records a constraint↔structure link
|
|
6
|
+
* (see `runtime/emit/materialized-view.ts`); **nothing enforces through the MV's
|
|
7
|
+
* backing table in this ticket** (that needs row-time write-through maintenance
|
|
8
|
+
* — see `docs/materialized-views.md` § Covering structures, the soundness note).
|
|
9
|
+
*
|
|
10
|
+
* Shape — the body, after optimization, walks down to a single constrained base
|
|
11
|
+
* table `T` through a chain of:
|
|
12
|
+
*
|
|
13
|
+
* TableReference(T) → optional Filter(P) → Project(...) → optional Sort
|
|
14
|
+
*
|
|
15
|
+
* (physical access nodes such as IndexScan / SeqScan are transparent links in
|
|
16
|
+
* the chain). A binary **join** is admitted when `T` provably contributes
|
|
17
|
+
* *exactly one* MV row per governed `T` row — see the 1:1 decomposition below.
|
|
18
|
+
* Aggregation, DISTINCT, set operations, `FanOutLookupJoin`, and `AsofScan` are
|
|
19
|
+
* always `NotCovers('shape')`.
|
|
20
|
+
*
|
|
21
|
+
* Soundness is paramount: a false `Covers` would (once the lens layer routes
|
|
22
|
+
* enforcement through the structure) silently miss conflicts. Every check is
|
|
23
|
+
* conservative — a false `NotCovers` only forgoes an optimization.
|
|
24
|
+
*
|
|
25
|
+
* ---
|
|
26
|
+
*
|
|
27
|
+
* **The 1:1 join decomposition.** "Exactly one MV row per governed `T` row"
|
|
28
|
+
* splits into two independent obligations, each proven by a distinct surface:
|
|
29
|
+
*
|
|
30
|
+
* - **No row loss (≥1).** Proven one of two ways during the plan walk:
|
|
31
|
+
*
|
|
32
|
+
* 1. *Row preservation* — `T` sits on the row-*preserving* side of the join:
|
|
33
|
+
* a `left` join with `T` in the left subtree, or a `right` join with `T`
|
|
34
|
+
* in the right subtree.
|
|
35
|
+
* 2. *Referential integrity* — an `inner`/`cross` join whose equi-pairs are
|
|
36
|
+
* an inclusion dependency from the `T`-side relation to the lookup table's
|
|
37
|
+
* **primary key**, over a lookup side that exposes the parent's *full* row
|
|
38
|
+
* set (`innerJoinRetainsConstrainedTable`). Enforced RI then makes every
|
|
39
|
+
* `T` row match exactly one lookup row, so the inner join loses nothing.
|
|
40
|
+
* This obligation is now **IND-derived** (Wave 2): it first consults the
|
|
41
|
+
* propagated `PhysicalProperties.inds` surface on the `T`-side subtree
|
|
42
|
+
* (`indDerivedNoRowLoss`) and falls back to the original structural
|
|
43
|
+
* NOT-NULL-FK-on-`T` check (`lookupCoveringFK`, `!match.nullable`) when no
|
|
44
|
+
* IND discharges it. Both gate on the same preconditions, so they agree on
|
|
45
|
+
* every single-FK shape; the IND path additionally proves no-row-loss
|
|
46
|
+
* across multi-hop FK chains (`T → M → P`) whose threaded IND a single
|
|
47
|
+
* `lookupCoveringFK` call cannot see. Both lean on the engine treating
|
|
48
|
+
* declared FKs as inclusion dependencies — see the RI soundness note below.
|
|
49
|
+
*
|
|
50
|
+
* Every other join type/position (`inner`/`cross` *without* a covering FK/IND;
|
|
51
|
+
* `semi`/`anti` filter; `full` injects lookup-only rows; `T` on the dropping
|
|
52
|
+
* side) is rejected as `shape`. FDs encode uniqueness, not existence, so
|
|
53
|
+
* obligation (1) cannot be FD-derived; obligation (2) is discharged from the
|
|
54
|
+
* propagated IND surface (the Wave-1 over-claim-free guarantee licenses trusting
|
|
55
|
+
* it) with the structural FK-schema read as fallback.
|
|
56
|
+
*
|
|
57
|
+
* - **No fan-out (≤1).** `T`'s primary key must be a unique key of the
|
|
58
|
+
* **topmost join's output relation**, read through `isUnique`. The optimizer
|
|
59
|
+
* propagates join key-preservation into the join's `physical.fds`
|
|
60
|
+
* (`analyzeJoinKeyCoverage` → `propagateJoinFds`): for `T LEFT JOIN L on
|
|
61
|
+
* T.fk = L.ukey` it emits `T.pk → all_join_cols` *iff* the equi-pairs cover a
|
|
62
|
+
* unique key of `L`, i.e. iff each `T` row matches ≤1 `L` row. The moment the
|
|
63
|
+
* lookup side can multiply a `T` row, no preserved-key FD is emitted and
|
|
64
|
+
* `T.pk` is not a superkey of the join output ⇒ `NotCovers('fanout')`.
|
|
65
|
+
*
|
|
66
|
+
* **Why the join frame, not the projected `root`.** The check is deliberately
|
|
67
|
+
* against the topmost join node, where the lookup columns are still present.
|
|
68
|
+
* A fanning `left` join still carries `T`'s own PK FD `T.pk → T-cols` (from
|
|
69
|
+
* the left input's `physical.fds`); once the lookup columns are *projected
|
|
70
|
+
* away* in `root`, that FD would make `T.pk` a derived key of the narrowed
|
|
71
|
+
* relation and silently mask the fan-out (the duplicate `T` rows survive
|
|
72
|
+
* projection without `DISTINCT`). At the join frame the retained lookup
|
|
73
|
+
* columns witness the fan-out, so `isUnique` is faithful regardless of what
|
|
74
|
+
* the projection keeps.
|
|
75
|
+
*
|
|
76
|
+
* Both obligations are required and neither implies the other: a `left` join to
|
|
77
|
+
* a *non-unique* lookup key is row-preserving but fans out (caught by the fan-out
|
|
78
|
+
* gate); an `inner` join to a unique *non-FK* (or nullable-FK) lookup key does
|
|
79
|
+
* not fan out but can lose `T` rows (caught by the no-row-loss gate). A NOT-NULL
|
|
80
|
+
* FK→PK inner join satisfies both at once: the FK target is the PK, so it is
|
|
81
|
+
* unique (no fan-out) *and* every `T` row matches (no row loss).
|
|
82
|
+
*
|
|
83
|
+
* **Referential-integrity soundness (load-bearing).** Obligation (2) is sound
|
|
84
|
+
* only because Quereus *enforces* referential integrity: `pragma foreign_keys`
|
|
85
|
+
* defaults on, and the optimizer treats every declared FK as a hard inclusion
|
|
86
|
+
* dependency (`child.fk ⊆ parent.pk` — see `util/ind-utils.ts`). The INNER
|
|
87
|
+
* branch of `rule-join-elimination` already relies on exactly this invariant to
|
|
88
|
+
* drop an FK→PK join, so admitting the same shape here introduces no *new*
|
|
89
|
+
* assumption. If FKs were advisory — or RI is disabled and orphan child rows are
|
|
90
|
+
* inserted — both this admit path *and* inner join elimination would be unsound
|
|
91
|
+
* together; that is a global optimizer assumption, not one this prover owns.
|
|
92
|
+
*
|
|
93
|
+
* **NOT the `extractBindings` `'row'` classification.** A tempting-but-wrong
|
|
94
|
+
* signal is the binding extractor's `'row'` class (`binding-extractor.ts`,
|
|
95
|
+
* `analyzeRowSpecific`). That is *equality-pinned* — it fires only when equality
|
|
96
|
+
* constraints cover `T`'s key at the reference, and reports a bare join scan as
|
|
97
|
+
* `'global'`. The sound realization of "exactly one MV row per source row" is
|
|
98
|
+
* `T`'s primary key being preserved as a key of the join output — the FD-surface
|
|
99
|
+
* fact `isUnique` already consumes — so `binding-extractor.ts` needs no change.
|
|
100
|
+
*
|
|
101
|
+
* The v1 projection / ordering / predicate checks are frame-correct for a join
|
|
102
|
+
* body: the covering columns all belong to `T` (UC + PK), the lookup-side
|
|
103
|
+
* attributes simply are not in `baseAttrToCol` and are ignored, and the join `ON`
|
|
104
|
+
* lives in the AST `from` clause (not `WHERE`). The AST `ORDER BY` / `WHERE`
|
|
105
|
+
* column resolution is **qualifier-aware** (`makeBodyColumnResolver`): `alias.col`
|
|
106
|
+
* resolves to a `T` column only when `alias` denotes `T`'s reference, and a bare
|
|
107
|
+
* `col` only when unambiguous across the join's sources. A term on a lookup-side
|
|
108
|
+
* column therefore fails on its own terms (`ordering-mismatch` for an `ORDER BY`,
|
|
109
|
+
* `predicate-entailment` for a `WHERE`) rather than mis-mapping onto a same-named
|
|
110
|
+
* `T` column — so a 1:1 join whose lookup key shares a UC column name (e.g.
|
|
111
|
+
* `line_items ⋈ products on l.sku = p.sku`) now covers, instead of being rejected
|
|
112
|
+
* by the former bare-name collision guard.
|
|
113
|
+
*
|
|
114
|
+
* **Cross-schema qualifier resolution is (schema, table)-aware.** Qualifier
|
|
115
|
+
* matching keys on the reference's `(schema, table)` pair, not its table name
|
|
116
|
+
* alone: a `schema.table.col` ORDER BY / WHERE term whose *table* name equals
|
|
117
|
+
* `T`'s but whose *schema* denotes a different schema (e.g. a lookup `s2.t` joined
|
|
118
|
+
* against base `main.t`, both named `t`) resolves to `undefined` instead of
|
|
119
|
+
* collapsing onto base `T`'s same-named column and yielding a false `Covers`. This
|
|
120
|
+
* is **defense-in-depth**: the binder rejects *every* 3-part `schema.table.column`
|
|
121
|
+
* reference in expression context before the prover runs (see
|
|
122
|
+
* `AliasedScope.resolveSymbol` / `resolveColumn`), so a real materialized view can
|
|
123
|
+
* never carry such a term — the guard is reachable only by feeding `proveCoverage`
|
|
124
|
+
* a hand-built `selectAst` (which is what the dedicated unit test does). The two
|
|
125
|
+
* SQL-reachable orderings (`order by uc` bare, `order by t.uc` 2-part) both
|
|
126
|
+
* resolve to base `T`'s column, a genuine cover, and are unchanged.
|
|
127
|
+
*
|
|
128
|
+
* **Join elimination, not handled here.** When the optimizer eliminates a
|
|
129
|
+
* key-preserving lookup join (lookup columns unprojected + FK→PK alignment, see
|
|
130
|
+
* `rule-join-elimination.ts`) the body collapses to a single-source chain and the
|
|
131
|
+
* v1 path covers it with no join-specific code. This module handles the residual
|
|
132
|
+
* cases where the join survives the optimizer but is still provably 1:1.
|
|
133
|
+
*
|
|
134
|
+
* Inner/cross covering via enforced referential integrity is handled (obligation
|
|
135
|
+
* (2) above). Full-outer covering remains deferred (it injects lookup-only rows
|
|
136
|
+
* that have no governed `T` row).
|
|
137
|
+
*
|
|
138
|
+
* ---
|
|
139
|
+
*
|
|
140
|
+
* Two different "coverage" questions live in this module; keep them apart:
|
|
141
|
+
*
|
|
142
|
+
* 1. **Base-table covering** (`proveCoverage`, above) — does an explicit MV's
|
|
143
|
+
* materialized row set cover a `unique` constraint on a *base table* `T`,
|
|
144
|
+
* keyed so a point lookup answers the uniqueness question and the base PK is
|
|
145
|
+
* reconstructible so a conflicting row can be identified? Requires literal
|
|
146
|
+
* projection of every UC column + the source PK, an `order by` permutation of
|
|
147
|
+
* the UC columns, and predicate/NULL-skip alignment.
|
|
148
|
+
*
|
|
149
|
+
* 2. **Output-relation effective key** (`proveEffectiveKeyUnique`, below) — is
|
|
150
|
+
* the body's *own output relation* provably unique on the declared key
|
|
151
|
+
* columns, via its effective key (declared keys, FD-closure-derived keys, or
|
|
152
|
+
* the all-columns/set fallback, all read through the unified `isUnique`
|
|
153
|
+
* surface)? This is the obligation primitive the lens prover consumes for its
|
|
154
|
+
* `obligation: proved` class — e.g. a `group by x, y` body whose output is
|
|
155
|
+
* intrinsically one row per `(x, y)` vacuously satisfies a logical
|
|
156
|
+
* `unique(x, y)`, so no runtime enforcement structure is needed.
|
|
157
|
+
*
|
|
158
|
+
* **Why (2) is NOT folded into (1).** An FD-derived output key cannot prove a
|
|
159
|
+
* *base-table* constraint, and folding it in would be unsound. A `group by x`
|
|
160
|
+
* body's output is *always* unique on `x` — whether or not `T` satisfies
|
|
161
|
+
* `unique(x)` — because grouping collapses base-row duplicates: two base rows
|
|
162
|
+
* with `x = 5` (a base-constraint violation) still yield exactly one output row
|
|
163
|
+
* for `x = 5`. Output-key uniqueness is therefore silent about base duplicates;
|
|
164
|
+
* that masking is the whole problem. Aggregating bodies also drop the base PK, so
|
|
165
|
+
* the "identify the conflicting base row" half of the v1 covering contract (for
|
|
166
|
+
* REPLACE / IGNORE conflict resolution) is unrecoverable. (2) is thus a proof
|
|
167
|
+
* about the *derived (output) relation's own* constraint, deliberately kept out
|
|
168
|
+
* of `proveCoverage` to preserve the v1 soundness boundary and leave the
|
|
169
|
+
* eager-link path (`linkCoveredUniqueConstraints`) untouched. Whether a covering
|
|
170
|
+
* *enforcement* structure can ever be FD-derived (detection-only, ABORT) is a
|
|
171
|
+
* separate concern of the row-time-enforcement / lens tickets, not this one.
|
|
172
|
+
*/
|
|
173
|
+
import { PlanNodeType } from '../nodes/plan-node-type.js';
|
|
174
|
+
import { TableReferenceNode, ColumnReferenceNode } from '../nodes/reference.js';
|
|
175
|
+
import { FilterNode } from '../nodes/filter.js';
|
|
176
|
+
import { JoinNode, extractEquiPairsFromCondition } from '../nodes/join-node.js';
|
|
177
|
+
import { BloomJoinNode } from '../nodes/bloom-join-node.js';
|
|
178
|
+
import { MergeJoinNode } from '../nodes/merge-join-node.js';
|
|
179
|
+
import { SeqScanNode, IndexScanNode } from '../nodes/table-access-nodes.js';
|
|
180
|
+
import { AliasNode } from '../nodes/alias-node.js';
|
|
181
|
+
import { SortNode } from '../nodes/sort.js';
|
|
182
|
+
import { RetrieveNode } from '../nodes/retrieve-node.js';
|
|
183
|
+
import { BinaryOpNode } from '../nodes/scalar.js';
|
|
184
|
+
import { CapabilityDetectors } from '../framework/characteristics.js';
|
|
185
|
+
import { recognizeConjunctiveClauses, guardClausesEntail } from './partial-unique-extraction.js';
|
|
186
|
+
import { normalizePredicate } from './predicate-normalizer.js';
|
|
187
|
+
import { isUnique } from '../util/fd-utils.js';
|
|
188
|
+
import { lookupCoveringFK } from '../util/ind-utils.js';
|
|
189
|
+
const COVERS = { covers: true };
|
|
190
|
+
function notCovers(reason) {
|
|
191
|
+
return { covers: false, reason };
|
|
192
|
+
}
|
|
193
|
+
/** Shared empty lookup-name set for single-source bodies (no join frame). */
|
|
194
|
+
const EMPTY_NAMES = new Set();
|
|
195
|
+
/**
|
|
196
|
+
* Row-preserving / single-source pass-through node types that may appear between
|
|
197
|
+
* the projection and the table reference after optimization. They neither change
|
|
198
|
+
* which base rows are present (Filter is handled separately — its predicate is
|
|
199
|
+
* captured) nor split into multiple sources.
|
|
200
|
+
*
|
|
201
|
+
* Row-*dropping* nodes are deliberately excluded — notably `OrdinalSlice` (a
|
|
202
|
+
* pushed-down LIMIT/OFFSET) and `LimitOffset` itself, which materialize only a
|
|
203
|
+
* prefix of the governed rows and so can never cover. A row cap is rejected up
|
|
204
|
+
* front from the AST (see `proveCoverage`); the exclusion here is the structural
|
|
205
|
+
* backstop should the cap ever reach the plan walk by another path.
|
|
206
|
+
*/
|
|
207
|
+
const PASS_THROUGH = new Set([
|
|
208
|
+
PlanNodeType.Sort,
|
|
209
|
+
PlanNodeType.Project,
|
|
210
|
+
PlanNodeType.Retrieve,
|
|
211
|
+
PlanNodeType.Alias,
|
|
212
|
+
// A lens-boundary marker is a pure row-preserving single-source pass-through
|
|
213
|
+
// (it only contributes FDs); transparent to the coverage shape walk.
|
|
214
|
+
PlanNodeType.AssertedKeys,
|
|
215
|
+
PlanNodeType.SeqScan,
|
|
216
|
+
PlanNodeType.IndexScan,
|
|
217
|
+
PlanNodeType.IndexSeek,
|
|
218
|
+
PlanNodeType.TableSeek,
|
|
219
|
+
]);
|
|
220
|
+
/**
|
|
221
|
+
* Binary (left/right/inner/cross/semi/anti) join node types the shape walk may
|
|
222
|
+
* descend through. These all implement `JoinCapable` (logical `JoinNode`,
|
|
223
|
+
* `BloomJoinNode` = `HashJoin`, `MergeJoinNode`), so `CapabilityDetectors.isJoin`
|
|
224
|
+
* exposes `getJoinType` / `getLeftSource` / `getRightSource`. `FanOutLookupJoin`
|
|
225
|
+
* and `AsofScan` are deliberately absent — they are not `JoinCapable` and fall
|
|
226
|
+
* through to the walk's `shape` rejection.
|
|
227
|
+
*/
|
|
228
|
+
const BINARY_JOIN_TYPES = new Set([
|
|
229
|
+
PlanNodeType.Join,
|
|
230
|
+
PlanNodeType.NestedLoopJoin,
|
|
231
|
+
PlanNodeType.HashJoin,
|
|
232
|
+
PlanNodeType.MergeJoin,
|
|
233
|
+
]);
|
|
234
|
+
/**
|
|
235
|
+
* Decides whether `mv` covers `uc` on `baseTable`. `root` is the optimized body
|
|
236
|
+
* relation (`db.getPlan(body).getRelations()[0]`); the body's declared `order by`
|
|
237
|
+
* comes from `mv.selectAst`. See the module doc for the recognition rules.
|
|
238
|
+
*/
|
|
239
|
+
export function proveCoverage(root, mv, uc, baseTable, opts = {}) {
|
|
240
|
+
const bodyAst = mv.derivation.selectAst;
|
|
241
|
+
// ---- Row cap: a LIMIT/OFFSET body materializes only a prefix of the
|
|
242
|
+
// governed rows, so it can never be observation-equivalent. Read from the
|
|
243
|
+
// AST (the faithful source): the optimizer may push the cap into an
|
|
244
|
+
// `OrdinalSlice` over an ordinal-seek-capable leaf, which the shape walk
|
|
245
|
+
// would otherwise traverse as a transparent link. ----
|
|
246
|
+
if (bodyAst.type === 'select' && (bodyAst.limit !== undefined || bodyAst.offset !== undefined)) {
|
|
247
|
+
return notCovers('shape');
|
|
248
|
+
}
|
|
249
|
+
// ---- Shape: walk down to the constrained base table `T` via the shared
|
|
250
|
+
// descent (`walkToConstrainedBase`) — single-source pass-throughs are
|
|
251
|
+
// transparent links and a binary join is descended into `T`'s side iff
|
|
252
|
+
// it provably keeps every governed `T` row (the no-row-loss obligation).
|
|
253
|
+
// The topmost join (if any) is captured for the fan-out gate below. The
|
|
254
|
+
// *predicate* is taken from the AST further down (the optimizer may absorb
|
|
255
|
+
// a WHERE into an index range seek and drop the FilterNode). ----
|
|
256
|
+
const walk = walkToConstrainedBase(root, baseTable, opts);
|
|
257
|
+
if (!walk.ok)
|
|
258
|
+
return notCovers(walk.reason);
|
|
259
|
+
const tableRef = walk.tableRef;
|
|
260
|
+
const topJoin = walk.topJoin;
|
|
261
|
+
// ---- Projection coverage: map output attributes back to base columns via
|
|
262
|
+
// stable attribute IDs (a bare column reference preserves the source
|
|
263
|
+
// attribute's id through Project/Sort/scan nodes), keeping the first
|
|
264
|
+
// covering output index for the collation gate below. ----
|
|
265
|
+
const baseAttrToCol = new Map();
|
|
266
|
+
tableRef.getAttributes().forEach((attr, i) => baseAttrToCol.set(attr.id, i));
|
|
267
|
+
const coveredBaseCols = new Map();
|
|
268
|
+
root.getAttributes().forEach((attr, outIdx) => {
|
|
269
|
+
const col = baseAttrToCol.get(attr.id);
|
|
270
|
+
if (col !== undefined && !coveredBaseCols.has(col))
|
|
271
|
+
coveredBaseCols.set(col, outIdx);
|
|
272
|
+
});
|
|
273
|
+
// ---- Collation gate: the projected output column must carry the SAME collation
|
|
274
|
+
// as the constrained base column. The backing key inherits the OUTPUT
|
|
275
|
+
// collation (`buildBackingTableSchema`), so a mismatched link would let a
|
|
276
|
+
// coarser-keyed backing (e.g. NOCASE) answer a finer (BINARY) constraint's
|
|
277
|
+
// uniqueness question — collation-equal/byte-different rows would merge in
|
|
278
|
+
// the structure while the constraint must keep them distinct. Defense-in-depth
|
|
279
|
+
// today: a collation-changing projection mints a fresh attribute id and
|
|
280
|
+
// already fails the coverage maps above; this gate makes the requirement
|
|
281
|
+
// explicit so no future id-preserving surface can link across a mismatch. ----
|
|
282
|
+
const outputColumns = root.getType().columns;
|
|
283
|
+
const collationMatches = (baseCol) => {
|
|
284
|
+
const outIdx = coveredBaseCols.get(baseCol);
|
|
285
|
+
if (outIdx === undefined)
|
|
286
|
+
return true; // absence is the missing-*-column reject's job
|
|
287
|
+
const outColl = (outputColumns[outIdx]?.type.collationName ?? 'BINARY').toUpperCase();
|
|
288
|
+
const baseColl = (baseTable.columns[baseCol]?.collation ?? 'BINARY').toUpperCase();
|
|
289
|
+
return outColl === baseColl;
|
|
290
|
+
};
|
|
291
|
+
for (const col of uc.columns) {
|
|
292
|
+
if (!coveredBaseCols.has(col))
|
|
293
|
+
return notCovers('missing-uc-column');
|
|
294
|
+
if (!collationMatches(col))
|
|
295
|
+
return notCovers('collation-mismatch');
|
|
296
|
+
}
|
|
297
|
+
for (const pk of baseTable.primaryKeyDefinition) {
|
|
298
|
+
if (!coveredBaseCols.has(pk.index))
|
|
299
|
+
return notCovers('missing-pk-column');
|
|
300
|
+
if (!collationMatches(pk.index))
|
|
301
|
+
return notCovers('collation-mismatch');
|
|
302
|
+
}
|
|
303
|
+
// ---- Lookup-side column names in the join's output frame (a `T` attribute is
|
|
304
|
+
// one whose id is a key of `baseAttrToCol`); empty for a single-source
|
|
305
|
+
// body. Feeds the qualifier-aware AST resolver's unqualified-name
|
|
306
|
+
// ambiguity check below. ----
|
|
307
|
+
const lookupNames = topJoin !== undefined ? lookupColumnNames(topJoin, baseAttrToCol) : EMPTY_NAMES;
|
|
308
|
+
// ---- Multi-source (join body) no-fan-out gate: `T`'s primary key must remain
|
|
309
|
+
// a unique key of the topmost join's output. Vacuous — and v1 behavior
|
|
310
|
+
// unchanged — for a single-source chain (`topJoin` absent). ----
|
|
311
|
+
if (topJoin !== undefined) {
|
|
312
|
+
const noFanout = proveJoinNoFanout(topJoin, tableRef, baseTable);
|
|
313
|
+
if (!noFanout.covers)
|
|
314
|
+
return noFanout;
|
|
315
|
+
}
|
|
316
|
+
// ---- Qualifier-aware AST column resolution. An ORDER BY / WHERE term
|
|
317
|
+
// `alias.col` resolves to a base-table `T` column only when `alias`
|
|
318
|
+
// denotes `T`'s reference; an unqualified `col` only when `T` has it and
|
|
319
|
+
// no lookup-side column shares the name. A term resolving to a lookup
|
|
320
|
+
// column is then handled on its own terms below (ORDER BY ⇒
|
|
321
|
+
// `ordering-mismatch`, WHERE ⇒ `predicate-entailment`), never mis-mapped
|
|
322
|
+
// onto `T`. For a single-source body this is plain bare-name resolution. ----
|
|
323
|
+
const resolveBodyColumn = makeBodyColumnResolver(bodyAst, baseTable, lookupNames);
|
|
324
|
+
// ---- Ordering: the body's declared ORDER BY columns must be a permutation of
|
|
325
|
+
// the UC columns. The prover never invents an ordering — a missing one
|
|
326
|
+
// fails. Read from the body AST rather than `mv.ordering`: the optimizer
|
|
327
|
+
// drops the Sort (leaving `physical.ordering` empty) whenever an index
|
|
328
|
+
// scan already supplies the order, so the AST is the faithful source. ----
|
|
329
|
+
const orderingBaseCols = bodyOrderByColumns(bodyAst, resolveBodyColumn);
|
|
330
|
+
if (orderingBaseCols === undefined)
|
|
331
|
+
return notCovers('ordering-mismatch');
|
|
332
|
+
if (!isPermutation(orderingBaseCols, uc.columns))
|
|
333
|
+
return notCovers('ordering-mismatch');
|
|
334
|
+
// ---- Predicate alignment: the materialized set (rows where the body's WHERE
|
|
335
|
+
// holds) must equal the governed set (rows where uc.predicate holds,
|
|
336
|
+
// NULL-excluded). The WHERE is read from the AST (see shape note). ----
|
|
337
|
+
const bodyWhere = bodyAst.type === 'select' ? bodyAst.where : undefined;
|
|
338
|
+
return provePredicateAlignment(bodyWhere, uc, baseTable, resolveBodyColumn);
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Walk `root` down to the constrained base table `T`, proving **no row loss**
|
|
342
|
+
* at every join descended through. Single-source pass-throughs (Filter + the
|
|
343
|
+
* physical access nodes in {@link PASS_THROUGH}) are transparent links; a binary
|
|
344
|
+
* join is descended into `T`'s side iff `T` provably keeps every governed row:
|
|
345
|
+
*
|
|
346
|
+
* - **row-preservation** — `T` on the preserving side of an outer join
|
|
347
|
+
* (`left`→left subtree, `right`→right subtree); or
|
|
348
|
+
* - **referential integrity** — an `inner`/`cross` join whose equi-pairs are a
|
|
349
|
+
* NOT-NULL FK / non-null IND from `T` to the lookup table's PK, so enforced RI
|
|
350
|
+
* makes every `T` row match exactly one lookup row
|
|
351
|
+
* (`innerJoinRetainsConstrainedTable`).
|
|
352
|
+
*
|
|
353
|
+
* Every other join type/position (a fanning lookup, `semi`/`anti`, `full`, `T`
|
|
354
|
+
* on the dropping side, a self-join, aggregation/DISTINCT/set-op) returns
|
|
355
|
+
* `{ ok: false, reason: 'shape' }`. The **no-fan-out** (≤1) obligation is NOT
|
|
356
|
+
* checked here — callers run {@link proveJoinNoFanout} on the returned `topJoin`
|
|
357
|
+
* (see {@link proveOneToOneJoin}); `proveCoverage` keeps its own fan-out gate so
|
|
358
|
+
* its diagnostic ordering is unchanged. Shared by `proveCoverage` and the
|
|
359
|
+
* materialized-view 1:1-join gate so the join soundness logic lives in one place.
|
|
360
|
+
*/
|
|
361
|
+
export function walkToConstrainedBase(root, baseTable, opts = {}) {
|
|
362
|
+
let tableRef;
|
|
363
|
+
let topJoin;
|
|
364
|
+
let node = root;
|
|
365
|
+
while (node) {
|
|
366
|
+
if (node instanceof TableReferenceNode) {
|
|
367
|
+
tableRef = node;
|
|
368
|
+
break;
|
|
369
|
+
}
|
|
370
|
+
if (BINARY_JOIN_TYPES.has(node.nodeType) && CapabilityDetectors.isJoin(node)) {
|
|
371
|
+
const left = node.getLeftSource();
|
|
372
|
+
const right = node.getRightSource();
|
|
373
|
+
const joinType = node.getJoinType();
|
|
374
|
+
const leftHasT = subtreeContainsConstrainedTable(left, baseTable);
|
|
375
|
+
const rightHasT = subtreeContainsConstrainedTable(right, baseTable);
|
|
376
|
+
// `T` on both sides (self-join) or neither ⇒ ambiguous / not our table.
|
|
377
|
+
if (leftHasT === rightHasT)
|
|
378
|
+
return { ok: false, reason: 'shape' };
|
|
379
|
+
const tSide = leftHasT ? left : right;
|
|
380
|
+
const lookupSide = leftHasT ? right : left;
|
|
381
|
+
const rowPreserving = (leftHasT && joinType === 'left') || (rightHasT && joinType === 'right');
|
|
382
|
+
if (!rowPreserving) {
|
|
383
|
+
const fkRetained = (joinType === 'inner' || joinType === 'cross')
|
|
384
|
+
&& innerJoinRetainsConstrainedTable(node, tSide, lookupSide, baseTable, opts.structuralOnly === true);
|
|
385
|
+
if (!fkRetained)
|
|
386
|
+
return { ok: false, reason: 'shape' };
|
|
387
|
+
}
|
|
388
|
+
if (topJoin === undefined)
|
|
389
|
+
topJoin = node;
|
|
390
|
+
node = tSide;
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
393
|
+
if (node instanceof FilterNode || PASS_THROUGH.has(node.nodeType)) {
|
|
394
|
+
const relations = node.getRelations();
|
|
395
|
+
if (relations.length !== 1)
|
|
396
|
+
return { ok: false, reason: 'shape' };
|
|
397
|
+
node = relations[0];
|
|
398
|
+
continue;
|
|
399
|
+
}
|
|
400
|
+
return { ok: false, reason: 'shape' };
|
|
401
|
+
}
|
|
402
|
+
if (!tableRef)
|
|
403
|
+
return { ok: false, reason: 'shape' };
|
|
404
|
+
if (tableRef.tableSchema.name.toLowerCase() !== baseTable.name.toLowerCase()
|
|
405
|
+
|| tableRef.tableSchema.schemaName.toLowerCase() !== baseTable.schemaName.toLowerCase()) {
|
|
406
|
+
return { ok: false, reason: 'shape' };
|
|
407
|
+
}
|
|
408
|
+
return { ok: true, tableRef, topJoin };
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Prove that `root`'s body is **provably 1:1 on `baseTable`** — exactly one
|
|
412
|
+
* output row per governed `T` row. Composes the two independent obligations the
|
|
413
|
+
* coverage prover splits a 1:1 join into: no-row-loss (≥1, via
|
|
414
|
+
* {@link walkToConstrainedBase}'s descent) and no-fan-out (≤1, via
|
|
415
|
+
* {@link proveJoinNoFanout} on the captured `topJoin`). For a single-source body
|
|
416
|
+
* (no join) the fan-out gate is vacuous and this succeeds iff the chain walks to
|
|
417
|
+
* `T`. The materialized-view 1:1-join gate calls this on the analyzed body to
|
|
418
|
+
* admit a join shape; `proveCoverage` reuses the same primitives directly so the
|
|
419
|
+
* join soundness logic is not duplicated.
|
|
420
|
+
*/
|
|
421
|
+
export function proveOneToOneJoin(root, baseTable, opts = {}) {
|
|
422
|
+
const walk = walkToConstrainedBase(root, baseTable, opts);
|
|
423
|
+
if (!walk.ok)
|
|
424
|
+
return walk;
|
|
425
|
+
if (walk.topJoin !== undefined) {
|
|
426
|
+
const noFanout = proveJoinNoFanout(walk.topJoin, walk.tableRef, baseTable);
|
|
427
|
+
if (!noFanout.covers)
|
|
428
|
+
return { ok: false, reason: noFanout.reason };
|
|
429
|
+
}
|
|
430
|
+
return walk;
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* "Body proves it": true iff the body's output relation is provably unique on
|
|
434
|
+
* `keyColumns` (output-column indices) via its effective key — declared keys,
|
|
435
|
+
* FD-closure-derived keys, or the set/all-columns fallback, all read through the
|
|
436
|
+
* unified `isUnique` surface. This is the obligation primitive the lens prover
|
|
437
|
+
* consumes for its `obligation: proved` class (e.g. a `group by x, y` body
|
|
438
|
+
* proving a logical `unique(x, y)`).
|
|
439
|
+
*
|
|
440
|
+
* `root` MUST be the optimized body relation (the same node `proveCoverage`
|
|
441
|
+
* receives: `db.getPlan(body).getRelations()[0]`), so `physical.fds` is
|
|
442
|
+
* populated — the group-key FD (`propagateAggregateFds`) and projected
|
|
443
|
+
* source-key FDs live there.
|
|
444
|
+
*
|
|
445
|
+
* Soundness notes (why the v1 base-table covering checks do NOT apply here):
|
|
446
|
+
* - Ordering: irrelevant — a proof of intrinsic uniqueness needs no ordered
|
|
447
|
+
* point-lookup path, so the canonical `group by` body (no ORDER BY) qualifies.
|
|
448
|
+
* - PK reconstructibility / observation-equivalence: irrelevant — there is no
|
|
449
|
+
* enforcement and no base row to identify; the constraint is on the output.
|
|
450
|
+
* - NULL-skip: composes trivially by subsumption. `isUnique` proves *strict*
|
|
451
|
+
* key-uniqueness (NULL treated as a value); SQL `unique` is NULL-permissive
|
|
452
|
+
* (weaker), so strict-unique ⟹ `unique` holds. No extra NULL handling.
|
|
453
|
+
* - Superkey semantics are correct: if the body's real key is a subset of
|
|
454
|
+
* `keyColumns`, the (stronger) constraint on the smaller set still implies the
|
|
455
|
+
* declared one — `isUnique` already returns true for any superset of a key.
|
|
456
|
+
*
|
|
457
|
+
* `keyColumns` are **body-output** column indices; the lens prover owns the
|
|
458
|
+
* logical-column → output-column mapping (this primitive does no base-table
|
|
459
|
+
* attribute-id translation — that was a v1 mechanism for the base frame and does
|
|
460
|
+
* not apply to the output frame). Delegates uniqueness entirely to `isUnique`
|
|
461
|
+
* (DRY); the value this adds is the named obligation seam, the diagnostic result
|
|
462
|
+
* shape, and the load-bearing soundness documentation above.
|
|
463
|
+
*/
|
|
464
|
+
export function proveEffectiveKeyUnique(root, keyColumns) {
|
|
465
|
+
const columnCount = root.getType().columns.length;
|
|
466
|
+
for (const c of keyColumns) {
|
|
467
|
+
if (c < 0 || c >= columnCount)
|
|
468
|
+
return { proved: false, reason: 'out-of-frame' };
|
|
469
|
+
}
|
|
470
|
+
return isUnique(keyColumns, root) ? { proved: true } : { proved: false, reason: 'not-a-key' };
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* Verifies the body predicate `P` is observation-equivalent (over the governed
|
|
474
|
+
* rows) to the constraint's scope:
|
|
475
|
+
*
|
|
476
|
+
* - soundness — `P` entails every required clause (`uc.predicate` clauses
|
|
477
|
+
* plus an `is not null` per nullable UC column), so the materialized set is
|
|
478
|
+
* contained in the governed set; and
|
|
479
|
+
* - completeness — `P` adds no restriction beyond those clauses (a NOT-NULL on
|
|
480
|
+
* any UC column is always allowed, since UNIQUE already ignores NULL rows),
|
|
481
|
+
* so the materialized set is not a strict subset that would miss conflicts.
|
|
482
|
+
*
|
|
483
|
+
* `resolveBodyColumn` resolves the body WHERE's column references (qualifier-aware
|
|
484
|
+
* for join bodies). `uc.predicate` is a constraint on `T`, so it always resolves
|
|
485
|
+
* by bare name against `baseTable` (the default).
|
|
486
|
+
*/
|
|
487
|
+
function provePredicateAlignment(bodyWhere, uc, baseTable, resolveBodyColumn) {
|
|
488
|
+
// Required clauses (the governed scope).
|
|
489
|
+
const requiredClauses = [];
|
|
490
|
+
if (uc.predicate) {
|
|
491
|
+
const ucClauses = recognizeConjunctiveClauses(uc.predicate, baseTable);
|
|
492
|
+
if (ucClauses === undefined)
|
|
493
|
+
return notCovers('predicate-entailment');
|
|
494
|
+
requiredClauses.push(...ucClauses);
|
|
495
|
+
}
|
|
496
|
+
const nullableUcCols = uc.columns.filter(c => baseTable.columns[c]?.notNull !== true);
|
|
497
|
+
for (const c of nullableUcCols) {
|
|
498
|
+
requiredClauses.push({ kind: 'is-null', column: c, negated: true });
|
|
499
|
+
}
|
|
500
|
+
// Recognize P. An unrecognized conjunct makes the materialized set unbounded
|
|
501
|
+
// from the prover's view — reject (we can prove neither containment direction).
|
|
502
|
+
// A WHERE term on a lookup column resolves to `undefined` via the qualifier-
|
|
503
|
+
// aware resolver ⇒ unrecognized ⇒ this same rejection path (predicate-entailment).
|
|
504
|
+
let pClauses = [];
|
|
505
|
+
if (bodyWhere) {
|
|
506
|
+
const clauses = recognizeConjunctiveClauses(bodyWhere, baseTable, resolveBodyColumn);
|
|
507
|
+
if (clauses === undefined) {
|
|
508
|
+
return notCovers(uc.predicate || nullableUcCols.length === 0 ? 'predicate-entailment' : 'missing-null-skip');
|
|
509
|
+
}
|
|
510
|
+
pClauses = clauses;
|
|
511
|
+
}
|
|
512
|
+
// Soundness: P entails every required clause (per-clause for a precise reason).
|
|
513
|
+
for (const rc of requiredClauses) {
|
|
514
|
+
if (!guardClausesEntail(pClauses, [rc])) {
|
|
515
|
+
return notCovers(rc.kind === 'is-null' && rc.negated ? 'missing-null-skip' : 'predicate-entailment');
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
// Completeness: every clause of P is allowed (entailed by the required scope,
|
|
519
|
+
// widened by a permissible NOT-NULL on any UC column). A restriction beyond
|
|
520
|
+
// that would drop governed rows and miss conflicts.
|
|
521
|
+
const allowedForCompleteness = [...requiredClauses];
|
|
522
|
+
for (const c of uc.columns) {
|
|
523
|
+
allowedForCompleteness.push({ kind: 'is-null', column: c, negated: true });
|
|
524
|
+
}
|
|
525
|
+
if (!guardClausesEntail(allowedForCompleteness, pClauses)) {
|
|
526
|
+
return notCovers('predicate-entailment');
|
|
527
|
+
}
|
|
528
|
+
return COVERS;
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* True iff `node`'s subtree contains a `TableReferenceNode` over `baseTable`
|
|
532
|
+
* (matched by lowercased schema + name). Walks `getRelations()` recursively, so
|
|
533
|
+
* it descends through physical access / Retrieve wrappers and nested joins. A
|
|
534
|
+
* self-join of `T` makes *both* of a join's subtrees report true — the walk
|
|
535
|
+
* treats that as ambiguous (`shape`).
|
|
536
|
+
*/
|
|
537
|
+
function subtreeContainsConstrainedTable(node, baseTable) {
|
|
538
|
+
if (node instanceof TableReferenceNode) {
|
|
539
|
+
return node.tableSchema.name.toLowerCase() === baseTable.name.toLowerCase()
|
|
540
|
+
&& node.tableSchema.schemaName.toLowerCase() === baseTable.schemaName.toLowerCase();
|
|
541
|
+
}
|
|
542
|
+
for (const rel of node.getRelations()) {
|
|
543
|
+
if (subtreeContainsConstrainedTable(rel, baseTable))
|
|
544
|
+
return true;
|
|
545
|
+
}
|
|
546
|
+
return false;
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* The `TableReferenceNode` over `baseTable` somewhere in `node`'s subtree, or
|
|
550
|
+
* `undefined`. Like `subtreeContainsConstrainedTable` but returns the node so
|
|
551
|
+
* `T`'s stable attribute ids can be mapped to its base column indices. (A
|
|
552
|
+
* self-join is already rejected upstream, so the first match is unambiguous.)
|
|
553
|
+
*/
|
|
554
|
+
function findConstrainedTableRef(node, baseTable) {
|
|
555
|
+
if (node instanceof TableReferenceNode) {
|
|
556
|
+
return node.tableSchema.name.toLowerCase() === baseTable.name.toLowerCase()
|
|
557
|
+
&& node.tableSchema.schemaName.toLowerCase() === baseTable.schemaName.toLowerCase()
|
|
558
|
+
? node : undefined;
|
|
559
|
+
}
|
|
560
|
+
for (const rel of node.getRelations()) {
|
|
561
|
+
const found = findConstrainedTableRef(rel, baseTable);
|
|
562
|
+
if (found)
|
|
563
|
+
return found;
|
|
564
|
+
}
|
|
565
|
+
return undefined;
|
|
566
|
+
}
|
|
567
|
+
/**
|
|
568
|
+
* The leaf `TableReferenceNode` of `node` **iff** the path down to it exposes the
|
|
569
|
+
* table's *full* row set — nothing filters, seeks, limits, or deduplicates rows.
|
|
570
|
+
* Returns `undefined` otherwise.
|
|
571
|
+
*
|
|
572
|
+
* This is the optimized-plan analogue of `ind-utils.ts`'s
|
|
573
|
+
* `isRowPreservingPathToTable` (which recognizes the *logical*-plan shape:
|
|
574
|
+
* bare TableReference / Retrieve-of-bare-table / Alias / Sort). After physical
|
|
575
|
+
* access selection a full scan is a `SeqScan`/`IndexScan` over the table — so we
|
|
576
|
+
* additionally admit those, but only when **not range-bounded** (`rangeBoundedOn`
|
|
577
|
+
* unset; a bounded scan drops rows). `IndexSeek`/`TableSeek` (row-reducing
|
|
578
|
+
* seeks), `Filter`, `LimitOffset`, `Distinct`, `Project`, joins, aggregates, …
|
|
579
|
+
* all disqualify by falling through to `undefined`.
|
|
580
|
+
*
|
|
581
|
+
* Required for the inner-join FK admit path: the lookup (parent) side must
|
|
582
|
+
* produce the parent's full row set, else a `T` row whose parent was filtered
|
|
583
|
+
* out would be dropped despite the FK guarantee — re-introducing row loss.
|
|
584
|
+
*/
|
|
585
|
+
function resolveFullScanTableRef(node) {
|
|
586
|
+
let n = node;
|
|
587
|
+
for (;;) {
|
|
588
|
+
if (n instanceof TableReferenceNode)
|
|
589
|
+
return n;
|
|
590
|
+
if (n instanceof SeqScanNode || n instanceof IndexScanNode) {
|
|
591
|
+
if (n.rangeBoundedOn)
|
|
592
|
+
return undefined;
|
|
593
|
+
n = n.source;
|
|
594
|
+
continue;
|
|
595
|
+
}
|
|
596
|
+
if (n instanceof AliasNode || n instanceof SortNode || n instanceof RetrieveNode) {
|
|
597
|
+
const rels = n.getRelations();
|
|
598
|
+
if (rels.length !== 1)
|
|
599
|
+
return undefined;
|
|
600
|
+
n = rels[0];
|
|
601
|
+
continue;
|
|
602
|
+
}
|
|
603
|
+
return undefined;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* The count of column-to-column equality conjuncts in `cond` (after
|
|
608
|
+
* normalization) when `cond` is a pure conjunction of them — `a.x = b.y AND …`
|
|
609
|
+
* with no other operator and no non-column operand — or `undefined` otherwise.
|
|
610
|
+
* The inner-join no-row-loss proof needs this: a residual non-equi conjunct (or
|
|
611
|
+
* an equality to a literal/expression) can drop `T` rows the FK→PK guarantee
|
|
612
|
+
* assumes survive, so any such condition disqualifies. The count lets the caller
|
|
613
|
+
* confirm every conjunct produced a cross-side equi-pair (see
|
|
614
|
+
* `pureJoinEquiAttrPairs`): a column equality whose operands sit on the *same*
|
|
615
|
+
* side is a single-relation filter that `extractEquiPairsFromCondition` silently
|
|
616
|
+
* drops yet still restricts the join's row set.
|
|
617
|
+
*/
|
|
618
|
+
function pureColumnEquiConjunctCount(cond) {
|
|
619
|
+
const stack = [normalizePredicate(cond)];
|
|
620
|
+
let count = 0;
|
|
621
|
+
while (stack.length) {
|
|
622
|
+
const n = stack.pop();
|
|
623
|
+
if (n instanceof BinaryOpNode) {
|
|
624
|
+
const op = n.expression.operator;
|
|
625
|
+
if (op === 'AND') {
|
|
626
|
+
stack.push(n.left, n.right);
|
|
627
|
+
continue;
|
|
628
|
+
}
|
|
629
|
+
if (op === '=' && n.left instanceof ColumnReferenceNode && n.right instanceof ColumnReferenceNode) {
|
|
630
|
+
count++;
|
|
631
|
+
continue;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
return undefined;
|
|
635
|
+
}
|
|
636
|
+
return count;
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* Equi-pairs of a binary join in attribute-id form, or `undefined` when the join
|
|
640
|
+
* carries anything beyond an equi-only condition (which could drop `T` rows and
|
|
641
|
+
* so breaks the no-row-loss proof). Physical joins (`BloomJoin`/`MergeJoin`)
|
|
642
|
+
* pre-extract their equi-pairs and stash any remainder in `residualCondition`; a
|
|
643
|
+
* logical `JoinNode` carries a single `condition` that must be a pure
|
|
644
|
+
* AND-of-column-equalities *every one of which crosses the two join sides*. A
|
|
645
|
+
* same-side column equality (e.g. `c.x = c.y`) passes the pure-equi shape but is
|
|
646
|
+
* a single-relation filter `extractEquiPairsFromCondition` drops — so we reject
|
|
647
|
+
* unless the extracted cross-side pair count matches the conjunct count, rather
|
|
648
|
+
* than leaning on predicate pushdown to have hoisted it below the join. A bare
|
|
649
|
+
* cross join (no condition / no equi-pairs) yields an empty list, which the
|
|
650
|
+
* caller treats as "no FK to align".
|
|
651
|
+
*/
|
|
652
|
+
export function pureJoinEquiAttrPairs(join) {
|
|
653
|
+
if (join instanceof BloomJoinNode || join instanceof MergeJoinNode) {
|
|
654
|
+
return join.residualCondition === undefined ? join.equiPairs : undefined;
|
|
655
|
+
}
|
|
656
|
+
if (join instanceof JoinNode) {
|
|
657
|
+
if (!join.condition)
|
|
658
|
+
return [];
|
|
659
|
+
const conjunctCount = pureColumnEquiConjunctCount(join.condition);
|
|
660
|
+
if (conjunctCount === undefined)
|
|
661
|
+
return undefined;
|
|
662
|
+
const leftAttrs = join.left.getAttributes();
|
|
663
|
+
const rightAttrs = join.right.getAttributes();
|
|
664
|
+
const pairs = extractEquiPairsFromCondition(join.condition, leftAttrs, rightAttrs)
|
|
665
|
+
.map(p => ({ leftAttrId: leftAttrs[p.left].id, rightAttrId: rightAttrs[p.right].id }));
|
|
666
|
+
// A conjunct that produced no cross-side pair is a same-side filter that
|
|
667
|
+
// restricts rows without aligning `T` to the lookup — disqualify.
|
|
668
|
+
if (pairs.length !== conjunctCount)
|
|
669
|
+
return undefined;
|
|
670
|
+
return pairs;
|
|
671
|
+
}
|
|
672
|
+
return undefined;
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* No-row-loss proof for an `inner`/`cross` join: every governed `T` row is
|
|
676
|
+
* retained because the join's equi-pairs are an inclusion dependency from the
|
|
677
|
+
* `T`-side relation to the lookup table's primary key, and Quereus enforces
|
|
678
|
+
* referential integrity (declared FKs are treated as inclusion dependencies; see
|
|
679
|
+
* the module doc's soundness note).
|
|
680
|
+
*
|
|
681
|
+
* Two preconditions are shared by both proof paths and gate up front:
|
|
682
|
+
* - **equi-only join** (`pureJoinEquiAttrPairs`) — a residual/non-equi conjunct
|
|
683
|
+
* could fail for the matched lookup row and drop the `T` row.
|
|
684
|
+
* - **full parent row set** (`resolveFullScanTableRef` on the lookup side) — a
|
|
685
|
+
* filtered/seeked lookup side could omit the parent row a `T` row references,
|
|
686
|
+
* re-introducing row loss regardless of any inclusion guarantee.
|
|
687
|
+
*
|
|
688
|
+
* Two interchangeable derivations then discharge the inclusion obligation:
|
|
689
|
+
* 1. **IND-derived** (`indDerivedNoRowLoss`, Wave 2, tried first) — a propagated
|
|
690
|
+
* non-`nullRejecting` IND on the `T`-side subtree whose `(cols → targetCols)`
|
|
691
|
+
* pairing matches the join's equi-pairs and whose target is the lookup
|
|
692
|
+
* parent's key. This composes across multi-hop FK chains (`T → M → P`) where
|
|
693
|
+
* a single `lookupCoveringFK` call sees only one hop.
|
|
694
|
+
* 2. **Structural fallback** (`lookupCoveringFK`, `!match.nullable`) — a NOT-NULL
|
|
695
|
+
* FK declared *on `T`* to the lookup table's PK. Retained verbatim so a
|
|
696
|
+
* missing IND never regresses an existing optimization.
|
|
697
|
+
*
|
|
698
|
+
* Both gate on the same preconditions and the same non-null inclusion to the
|
|
699
|
+
* parent's key, so they cannot disagree on the single-FK corpus (the seeded IND
|
|
700
|
+
* mirrors `lookupCoveringFK` exactly: same PK-cover validation, same nullability
|
|
701
|
+
* bit). The complementary no-fan-out (≤1) obligation is unchanged — it is the
|
|
702
|
+
* join-frame `isUnique(T.pk)` gate in `proveJoinNoFanout`, which a FK→PK join also
|
|
703
|
+
* satisfies (the PK side's key is covered by the equi-pairs).
|
|
704
|
+
*/
|
|
705
|
+
function innerJoinRetainsConstrainedTable(join, tSide, lookupSide, baseTable, structuralOnly = false) {
|
|
706
|
+
const equiPairs = pureJoinEquiAttrPairs(join);
|
|
707
|
+
if (!equiPairs || equiPairs.length === 0)
|
|
708
|
+
return false;
|
|
709
|
+
// Shared precondition: the lookup side must expose the parent's full row set.
|
|
710
|
+
// COMPLETENESS LIMITATION (bushy lookup side, under-claim-safe): a bushy
|
|
711
|
+
// `T ⋈ (M ⋈ P)` makes `lookupSide` a join, so `resolveFullScanTableRef` returns
|
|
712
|
+
// undefined and the two-hop cover is silently lost (falls back to structural ⇒
|
|
713
|
+
// NotCovers). The empty-table PoC stays left-deep empirically, so this is
|
|
714
|
+
// unreachable today; a cost-based reorder on real statistics could go bushy. To
|
|
715
|
+
// extend: prove no-row-loss when the bushy lookup side carries a matching IND
|
|
716
|
+
// surface, or normalize the join to left-deep before proving — either path must
|
|
717
|
+
// preserve the over-claim-free guarantee.
|
|
718
|
+
const lookupRef = resolveFullScanTableRef(lookupSide);
|
|
719
|
+
if (!lookupRef)
|
|
720
|
+
return false;
|
|
721
|
+
// (1) IND-derived path (Wave 2), tried first — proves multi-hop chains the
|
|
722
|
+
// single-call structural check below cannot see. The `structuralOnly`
|
|
723
|
+
// verification seam disables it so the equivalence test can assert the two
|
|
724
|
+
// derivations agree on every single-FK shape.
|
|
725
|
+
if (!structuralOnly && indDerivedNoRowLoss(equiPairs, tSide, lookupRef))
|
|
726
|
+
return true;
|
|
727
|
+
// (2) Structural fallback (unchanged): a NOT-NULL FK declared on `T` to the
|
|
728
|
+
// lookup table's PK.
|
|
729
|
+
const tRef = findConstrainedTableRef(tSide, baseTable);
|
|
730
|
+
if (!tRef)
|
|
731
|
+
return false;
|
|
732
|
+
// Stable attribute id → base column index on each side.
|
|
733
|
+
const tAttrToCol = new Map();
|
|
734
|
+
tRef.getAttributes().forEach((a, i) => tAttrToCol.set(a.id, i));
|
|
735
|
+
const lookupAttrToCol = new Map();
|
|
736
|
+
lookupRef.getAttributes().forEach((a, i) => lookupAttrToCol.set(a.id, i));
|
|
737
|
+
// Split every equi-pair into (T-FK column, lookup-PK column). A pair that does
|
|
738
|
+
// not connect `T` to the lookup table cleanly (e.g. references a third source)
|
|
739
|
+
// is unprovable ⇒ reject.
|
|
740
|
+
const fkCols = [];
|
|
741
|
+
const pkCols = [];
|
|
742
|
+
for (const p of equiPairs) {
|
|
743
|
+
let fkCol = tAttrToCol.get(p.leftAttrId);
|
|
744
|
+
let pkCol = lookupAttrToCol.get(p.rightAttrId);
|
|
745
|
+
if (fkCol === undefined || pkCol === undefined) {
|
|
746
|
+
fkCol = tAttrToCol.get(p.rightAttrId);
|
|
747
|
+
pkCol = lookupAttrToCol.get(p.leftAttrId);
|
|
748
|
+
}
|
|
749
|
+
if (fkCol === undefined || pkCol === undefined)
|
|
750
|
+
return false;
|
|
751
|
+
fkCols.push(fkCol);
|
|
752
|
+
pkCols.push(pkCol);
|
|
753
|
+
}
|
|
754
|
+
const match = lookupCoveringFK(baseTable, lookupRef.tableSchema, fkCols, pkCols);
|
|
755
|
+
return match !== undefined && !match.nullable;
|
|
756
|
+
}
|
|
757
|
+
/**
|
|
758
|
+
* IND-derived no-row-loss proof (Wave 2). Discharges the inner/cross join's
|
|
759
|
+
* inclusion obligation from the **propagated** IND surface (`PhysicalProperties.inds`)
|
|
760
|
+
* on the `T`-side subtree, rather than re-reading the FK declaration on `T`.
|
|
761
|
+
*
|
|
762
|
+
* Admit when there is an IND on `tSide` whose `(cols → targetCols)` positional
|
|
763
|
+
* pairing equals (as a set) the join's `(tSide-output-col → lookup-base-col)`
|
|
764
|
+
* equi-pairs, with:
|
|
765
|
+
* - `nullRejecting === false` — a NULL-rejecting (nullable-FK) IND can drop `T`
|
|
766
|
+
* rows whose `cols` tuple is NULL, exactly the reason the structural path
|
|
767
|
+
* requires `!match.nullable`;
|
|
768
|
+
* - `target.kind === 'table'` matching the lookup parent's `(schema, table)`; and
|
|
769
|
+
* - the IND's `targetCols` (a key of the parent — seeded INDs target the parent
|
|
770
|
+
* PK) aligned positionally with the lookup-side equi-columns.
|
|
771
|
+
*
|
|
772
|
+
* Soundness. The IND `tSide.cols ⊆ parent.targetCols` (total, since not
|
|
773
|
+
* null-rejecting) guarantees, for every `tSide` row, a parent row `p` with
|
|
774
|
+
* `p.targetCols = tSide.cols`. When the join's equi-pairs are exactly that
|
|
775
|
+
* `(cols → targetCols)` pairing, `p` satisfies every equi-condition, so the row is
|
|
776
|
+
* retained — no row loss. The lookup-side full-row-set precondition (checked by the
|
|
777
|
+
* caller) ensures `p` is actually present in the lookup scan.
|
|
778
|
+
*
|
|
779
|
+
* Equivalence with the structural path. The IND set on a bare `T`-side is exactly
|
|
780
|
+
* `seedTableForeignKeyInds(T)` — one total/NOT-NULL IND per declared FK→parent-PK,
|
|
781
|
+
* with the same PK-cover and nullability validation `lookupCoveringFK` applies. The
|
|
782
|
+
* set-equality match below mirrors `lookupCoveringFK`'s "equi-pairs are exactly the
|
|
783
|
+
* FK columns paired to the whole parent PK" requirement, so the two paths admit the
|
|
784
|
+
* identical single-FK shapes. The IND path's additional reach is **composition**:
|
|
785
|
+
* a `T → M → P` chain carries a threaded IND (`M.cols ⊆ P.pk`) on the `T ⋈ M`
|
|
786
|
+
* sub-frame via Wave-1 join propagation, which discharges the outer `⋈ P` join no
|
|
787
|
+
* single `lookupCoveringFK(T, P, …)` call can prove.
|
|
788
|
+
*/
|
|
789
|
+
function indDerivedNoRowLoss(equiPairs, tSide, lookupRef) {
|
|
790
|
+
const inds = tSide.physical?.inds;
|
|
791
|
+
if (!inds || inds.length === 0)
|
|
792
|
+
return false;
|
|
793
|
+
// Map each equi-pair to (tSide-output-col, lookup-base-col). The IND `cols`
|
|
794
|
+
// live in `tSide`'s output frame; the lookup base-col aligns with the target's
|
|
795
|
+
// `targetCols` (output index = base column index at the lookup TableReference).
|
|
796
|
+
const tSideAttrToCol = new Map();
|
|
797
|
+
tSide.getAttributes().forEach((a, i) => tSideAttrToCol.set(a.id, i));
|
|
798
|
+
const lookupAttrToCol = new Map();
|
|
799
|
+
lookupRef.getAttributes().forEach((a, i) => lookupAttrToCol.set(a.id, i));
|
|
800
|
+
const joinPairs = [];
|
|
801
|
+
for (const p of equiPairs) {
|
|
802
|
+
let tCol = tSideAttrToCol.get(p.leftAttrId);
|
|
803
|
+
let lCol = lookupAttrToCol.get(p.rightAttrId);
|
|
804
|
+
if (tCol === undefined || lCol === undefined) {
|
|
805
|
+
tCol = tSideAttrToCol.get(p.rightAttrId);
|
|
806
|
+
lCol = lookupAttrToCol.get(p.leftAttrId);
|
|
807
|
+
}
|
|
808
|
+
// A pair not cleanly split across tSide / lookup (e.g. a third source) is
|
|
809
|
+
// unprovable from this IND surface ⇒ abstain (caller falls back to structural).
|
|
810
|
+
if (tCol === undefined || lCol === undefined)
|
|
811
|
+
return false;
|
|
812
|
+
joinPairs.push([tCol, lCol]);
|
|
813
|
+
}
|
|
814
|
+
const lookupSchema = lookupRef.tableSchema.schemaName.toLowerCase();
|
|
815
|
+
const lookupTable = lookupRef.tableSchema.name.toLowerCase();
|
|
816
|
+
// COMPLETENESS LIMITATION (single-IND match, under-claim-safe): this matches
|
|
817
|
+
// *one* IND to *all* of the join's equi-pairs. A join whose equi-pairs are
|
|
818
|
+
// jointly covered by two INDs (no single IND covers them) abstains here. To
|
|
819
|
+
// extend: admit when a *set* of INDs jointly set-covers the equi-pairs, provided
|
|
820
|
+
// every contributing IND is non-nullRejecting and targets the same lookup-parent
|
|
821
|
+
// key. Under-claim only — never produces a false Covers.
|
|
822
|
+
for (const ind of inds) {
|
|
823
|
+
if (ind.nullRejecting)
|
|
824
|
+
continue;
|
|
825
|
+
if (ind.target.kind !== 'table')
|
|
826
|
+
continue;
|
|
827
|
+
if (ind.target.schema.toLowerCase() !== lookupSchema)
|
|
828
|
+
continue;
|
|
829
|
+
if (ind.target.table.toLowerCase() !== lookupTable)
|
|
830
|
+
continue;
|
|
831
|
+
if (indPairsMatchJoinPairs(ind, joinPairs))
|
|
832
|
+
return true;
|
|
833
|
+
}
|
|
834
|
+
return false;
|
|
835
|
+
}
|
|
836
|
+
/**
|
|
837
|
+
* True iff the IND's positional `(cols[j] → target.targetCols[j])` pairing, taken
|
|
838
|
+
* as a set, equals the join's `(tSideCol → lookupCol)` equi-pairs. Set (not
|
|
839
|
+
* ordered) comparison so the equi-pair extraction order is irrelevant; lengths
|
|
840
|
+
* must match so the join equates *exactly* the IND's columns to the parent's key
|
|
841
|
+
* — the same all-of-the-FK-columns, all-of-the-PK requirement `lookupCoveringFK`
|
|
842
|
+
* enforces, keeping the two derivations in lockstep on the single-FK corpus.
|
|
843
|
+
*/
|
|
844
|
+
function indPairsMatchJoinPairs(ind, joinPairs) {
|
|
845
|
+
if (ind.cols.length !== joinPairs.length)
|
|
846
|
+
return false;
|
|
847
|
+
if (ind.target.targetCols.length !== ind.cols.length)
|
|
848
|
+
return false;
|
|
849
|
+
const indSet = new Set();
|
|
850
|
+
for (let j = 0; j < ind.cols.length; j++) {
|
|
851
|
+
indSet.add(`${ind.cols[j]}:${ind.target.targetCols[j]}`);
|
|
852
|
+
}
|
|
853
|
+
if (indSet.size !== ind.cols.length)
|
|
854
|
+
return false; // duplicate IND pair ⇒ not a clean match
|
|
855
|
+
for (const [tCol, lCol] of joinPairs) {
|
|
856
|
+
if (!indSet.has(`${tCol}:${lCol}`))
|
|
857
|
+
return false;
|
|
858
|
+
}
|
|
859
|
+
return true;
|
|
860
|
+
}
|
|
861
|
+
/**
|
|
862
|
+
* Lowercased names of the lookup-side columns in `topJoin`'s output frame — a
|
|
863
|
+
* `T` attribute is exactly one whose id is a key of `baseAttrToCol`, so every
|
|
864
|
+
* other join-output attribute belongs to a lookup side. Feeds the qualifier-aware
|
|
865
|
+
* resolver's unqualified-name ambiguity check.
|
|
866
|
+
*/
|
|
867
|
+
function lookupColumnNames(topJoin, baseAttrToCol) {
|
|
868
|
+
const names = new Set();
|
|
869
|
+
for (const attr of topJoin.getAttributes()) {
|
|
870
|
+
if (!baseAttrToCol.has(attr.id))
|
|
871
|
+
names.add(attr.name.toLowerCase());
|
|
872
|
+
}
|
|
873
|
+
return names;
|
|
874
|
+
}
|
|
875
|
+
/**
|
|
876
|
+
* No-fan-out (≤1) gate for a join body: `T`'s primary key must be a unique key of
|
|
877
|
+
* `topJoin`'s output relation (`isUnique`), mapped into the join output frame via
|
|
878
|
+
* stable attribute ids. Checked at the join frame rather than the projected
|
|
879
|
+
* `root` — see the module doc ("Why the join frame, not the projected root").
|
|
880
|
+
*
|
|
881
|
+
* The complementary no-row-loss (≥1) obligation is the structural side/type gate
|
|
882
|
+
* in the shape walk; name-resolution safety is now the qualifier-aware resolver
|
|
883
|
+
* (`makeBodyColumnResolver`), which made the former bare-name collision guard
|
|
884
|
+
* unnecessary.
|
|
885
|
+
*/
|
|
886
|
+
export function proveJoinNoFanout(topJoin, tableRef, baseTable) {
|
|
887
|
+
const joinAttrToIndex = new Map();
|
|
888
|
+
topJoin.getAttributes().forEach((a, i) => joinAttrToIndex.set(a.id, i));
|
|
889
|
+
const tAttrs = tableRef.getAttributes();
|
|
890
|
+
const pkInJoinFrame = [];
|
|
891
|
+
for (const pk of baseTable.primaryKeyDefinition) {
|
|
892
|
+
const attrId = tAttrs[pk.index]?.id;
|
|
893
|
+
const joinIdx = attrId !== undefined ? joinAttrToIndex.get(attrId) : undefined;
|
|
894
|
+
if (joinIdx === undefined)
|
|
895
|
+
return notCovers('fanout');
|
|
896
|
+
pkInJoinFrame.push(joinIdx);
|
|
897
|
+
}
|
|
898
|
+
if (!isUnique(pkInJoinFrame, topJoin))
|
|
899
|
+
return notCovers('fanout');
|
|
900
|
+
return COVERS;
|
|
901
|
+
}
|
|
902
|
+
/**
|
|
903
|
+
* Builds the qualifier-aware {@link ColumnIndexResolver} the AST ORDER BY / WHERE
|
|
904
|
+
* checks use to map a column reference to a base-table `T` column index:
|
|
905
|
+
*
|
|
906
|
+
* - **Qualified** (`alias.col` / `table.col` / `schema.table.col`) — a `T`
|
|
907
|
+
* column only when the qualifier denotes `T`'s reference (its alias, or its
|
|
908
|
+
* table name when unaliased, collected from the body FROM clause) *and*, when
|
|
909
|
+
* the reference also carries a `schema` qualifier, that schema is `T`'s schema.
|
|
910
|
+
* Matching is thus (schema, table)-aware: a `schema.table.col` whose table
|
|
911
|
+
* name collides with `T`'s but whose schema denotes a different schema resolves
|
|
912
|
+
* to `undefined` rather than mis-mapping onto a same-named `T` column. A bare
|
|
913
|
+
* `table.col` keeps today's table-name match (the unqualified schema is
|
|
914
|
+
* resolved against the schema search path at plan time, so `t.col` still
|
|
915
|
+
* denotes `T`). A qualifier denoting a lookup source (or any unknown qualifier)
|
|
916
|
+
* yields `undefined`.
|
|
917
|
+
* - **Unqualified** (`col`) — a `T` column only when `T` has it *and* no
|
|
918
|
+
* lookup-side column shares the name (`lookupNames`). An ambiguous bare name
|
|
919
|
+
* would be a plan-time error for a real body, but resolving to `undefined`
|
|
920
|
+
* here is the sound fallback regardless.
|
|
921
|
+
*
|
|
922
|
+
* `undefined` means "not a (resolvable) `T` column", which the ORDER BY check
|
|
923
|
+
* turns into `ordering-mismatch` and the WHERE recognizer turns into an
|
|
924
|
+
* unrecognized conjunct (⇒ `predicate-entailment`). For a single-source body
|
|
925
|
+
* `lookupNames` is empty and `T`'s sole qualifier is in the set, so this reduces
|
|
926
|
+
* to bare-name resolution — v1 behavior unchanged.
|
|
927
|
+
*/
|
|
928
|
+
function makeBodyColumnResolver(selectAst, baseTable, lookupNames) {
|
|
929
|
+
const tQualifiers = collectBaseTableQualifiers(selectAst, baseTable);
|
|
930
|
+
const baseSchema = baseTable.schemaName.toLowerCase();
|
|
931
|
+
return (expr) => {
|
|
932
|
+
const ref = columnRefParts(expr);
|
|
933
|
+
if (ref === undefined)
|
|
934
|
+
return undefined;
|
|
935
|
+
if (ref.qualifier !== undefined) {
|
|
936
|
+
// (schema, table)-aware: a present schema must denote `T`'s schema; an
|
|
937
|
+
// absent schema falls back to the table-name match (the schema search path
|
|
938
|
+
// resolves the bare qualifier to `T` at plan time).
|
|
939
|
+
if (ref.schema !== undefined && ref.schema !== baseSchema)
|
|
940
|
+
return undefined;
|
|
941
|
+
return tQualifiers.has(ref.qualifier) ? baseTable.columnIndexMap.get(ref.name) : undefined;
|
|
942
|
+
}
|
|
943
|
+
if (lookupNames.has(ref.name))
|
|
944
|
+
return undefined;
|
|
945
|
+
return baseTable.columnIndexMap.get(ref.name);
|
|
946
|
+
};
|
|
947
|
+
}
|
|
948
|
+
/**
|
|
949
|
+
* The `(schema?, qualifier?, name)` of a column reference (all lowercased), or
|
|
950
|
+
* `undefined` when `expr` is not a column reference the prover resolves. A
|
|
951
|
+
* `ColumnExpr` carries an optional `table` qualifier *and* an optional `schema`
|
|
952
|
+
* qualifier — both are surfaced so qualifier matching can be (schema, table)-aware
|
|
953
|
+
* (see `makeBodyColumnResolver`). A bare `IdentifierExpr` has neither, and a
|
|
954
|
+
* schema-qualified identifier is rejected (matches `columnIndexFromExpr`).
|
|
955
|
+
*
|
|
956
|
+
* Note: the binder rejects every 3-part `schema.table.column` reference before a
|
|
957
|
+
* plan (let alone an MV) exists (see the module doc § cross-schema), so a present
|
|
958
|
+
* `schema` here is only reachable through a hand-built `selectAst` — the resolver
|
|
959
|
+
* still honors it for defense-in-depth.
|
|
960
|
+
*/
|
|
961
|
+
function columnRefParts(expr) {
|
|
962
|
+
if (expr.type === 'column') {
|
|
963
|
+
const c = expr;
|
|
964
|
+
return { schema: c.schema?.toLowerCase(), qualifier: c.table?.toLowerCase(), name: c.name.toLowerCase() };
|
|
965
|
+
}
|
|
966
|
+
if (expr.type === 'identifier') {
|
|
967
|
+
const id = expr;
|
|
968
|
+
if (id.schema)
|
|
969
|
+
return undefined;
|
|
970
|
+
return { name: id.name.toLowerCase() };
|
|
971
|
+
}
|
|
972
|
+
return undefined;
|
|
973
|
+
}
|
|
974
|
+
/**
|
|
975
|
+
* The set of lowercased FROM-clause qualifiers (alias, or table name when
|
|
976
|
+
* unaliased) that denote the base table `T`. Walks the body FROM clause through
|
|
977
|
+
* nested joins. Subquery / function sources cannot be `T` (the shape walk binds
|
|
978
|
+
* `T` to a `TableReferenceNode`), so their alias is intentionally absent — a
|
|
979
|
+
* reference qualified by it resolves to `undefined` (a lookup/derived column).
|
|
980
|
+
*/
|
|
981
|
+
function collectBaseTableQualifiers(selectAst, baseTable) {
|
|
982
|
+
const out = new Set();
|
|
983
|
+
if (selectAst.type !== 'select' || !selectAst.from)
|
|
984
|
+
return out;
|
|
985
|
+
const stack = [...selectAst.from];
|
|
986
|
+
while (stack.length > 0) {
|
|
987
|
+
const f = stack.pop();
|
|
988
|
+
if (f.type === 'join') {
|
|
989
|
+
stack.push(f.left, f.right);
|
|
990
|
+
continue;
|
|
991
|
+
}
|
|
992
|
+
if (f.type === 'table') {
|
|
993
|
+
const ts = f;
|
|
994
|
+
const denotesT = ts.table.name.toLowerCase() === baseTable.name.toLowerCase()
|
|
995
|
+
&& (ts.table.schema === undefined || ts.table.schema.toLowerCase() === baseTable.schemaName.toLowerCase());
|
|
996
|
+
if (denotesT)
|
|
997
|
+
out.add((ts.alias ?? ts.table.name).toLowerCase());
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
return out;
|
|
1001
|
+
}
|
|
1002
|
+
/**
|
|
1003
|
+
* Base-table column indices named by the body's `ORDER BY`, in order, or
|
|
1004
|
+
* `undefined` when there is no `ORDER BY`, the body is not a plain SELECT, or any
|
|
1005
|
+
* ordering term does not resolve to a `T` column via `resolve` (the prover never
|
|
1006
|
+
* invents an ordering). `resolve` is qualifier-aware for join bodies, so an
|
|
1007
|
+
* `ORDER BY` on a *lookup*-side column yields `undefined` here ⇒ the caller
|
|
1008
|
+
* reports `ordering-mismatch`, the correct reason (it is not a `T` ordering).
|
|
1009
|
+
*/
|
|
1010
|
+
function bodyOrderByColumns(selectAst, resolve) {
|
|
1011
|
+
if (selectAst.type !== 'select')
|
|
1012
|
+
return undefined;
|
|
1013
|
+
const orderBy = selectAst.orderBy;
|
|
1014
|
+
if (!orderBy || orderBy.length === 0)
|
|
1015
|
+
return undefined;
|
|
1016
|
+
const cols = [];
|
|
1017
|
+
for (const term of orderBy) {
|
|
1018
|
+
const col = resolve(term.expr);
|
|
1019
|
+
if (col === undefined)
|
|
1020
|
+
return undefined;
|
|
1021
|
+
cols.push(col);
|
|
1022
|
+
}
|
|
1023
|
+
return cols;
|
|
1024
|
+
}
|
|
1025
|
+
/** True when `a` and `b` contain the same column indices (order-insensitive, distinct). */
|
|
1026
|
+
function isPermutation(a, b) {
|
|
1027
|
+
if (a.length !== b.length)
|
|
1028
|
+
return false;
|
|
1029
|
+
const setA = new Set(a);
|
|
1030
|
+
const setB = new Set(b);
|
|
1031
|
+
if (setA.size !== a.length || setB.size !== b.length)
|
|
1032
|
+
return false;
|
|
1033
|
+
for (const x of setA)
|
|
1034
|
+
if (!setB.has(x))
|
|
1035
|
+
return false;
|
|
1036
|
+
return true;
|
|
1037
|
+
}
|
|
1038
|
+
//# sourceMappingURL=coverage-prover.js.map
|