@quereus/quereus 4.11.0 → 4.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/common/type-inference.d.ts +12 -6
- package/dist/src/common/type-inference.d.ts.map +1 -1
- package/dist/src/common/type-inference.js +12 -24
- package/dist/src/common/type-inference.js.map +1 -1
- package/dist/src/core/database-assertions.d.ts.map +1 -1
- package/dist/src/core/database-assertions.js +7 -5
- package/dist/src/core/database-assertions.js.map +1 -1
- package/dist/src/core/database-events.d.ts +4 -3
- package/dist/src/core/database-events.d.ts.map +1 -1
- package/dist/src/core/database-events.js +4 -3
- package/dist/src/core/database-events.js.map +1 -1
- package/dist/src/core/database-materialized-views-analysis.d.ts +1 -3
- package/dist/src/core/database-materialized-views-analysis.d.ts.map +1 -1
- package/dist/src/core/database-materialized-views-analysis.js +1 -11
- package/dist/src/core/database-materialized-views-analysis.js.map +1 -1
- package/dist/src/core/database-materialized-views-plan-builders.d.ts.map +1 -1
- package/dist/src/core/database-materialized-views-plan-builders.js +12 -10
- package/dist/src/core/database-materialized-views-plan-builders.js.map +1 -1
- package/dist/src/core/database.d.ts.map +1 -1
- package/dist/src/core/database.js +13 -2
- package/dist/src/core/database.js.map +1 -1
- package/dist/src/core/derived-row-validator.d.ts.map +1 -1
- package/dist/src/core/derived-row-validator.js +14 -2
- package/dist/src/core/derived-row-validator.js.map +1 -1
- package/dist/src/core/param.d.ts +9 -0
- package/dist/src/core/param.d.ts.map +1 -1
- package/dist/src/core/param.js +21 -1
- package/dist/src/core/param.js.map +1 -1
- package/dist/src/core/statement.d.ts.map +1 -1
- package/dist/src/core/statement.js +38 -15
- package/dist/src/core/statement.js.map +1 -1
- package/dist/src/func/builtins/aggregate.d.ts.map +1 -1
- package/dist/src/func/builtins/aggregate.js +7 -2
- 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 +27 -11
- package/dist/src/func/builtins/builtin-window-functions.js.map +1 -1
- package/dist/src/func/builtins/datetime.d.ts.map +1 -1
- package/dist/src/func/builtins/datetime.js +8 -3
- package/dist/src/func/builtins/datetime.js.map +1 -1
- package/dist/src/func/builtins/explain.d.ts.map +1 -1
- package/dist/src/func/builtins/explain.js +9 -29
- package/dist/src/func/builtins/explain.js.map +1 -1
- package/dist/src/func/builtins/json-tvf.d.ts.map +1 -1
- package/dist/src/func/builtins/json-tvf.js +14 -7
- package/dist/src/func/builtins/json-tvf.js.map +1 -1
- package/dist/src/func/builtins/return-types.d.ts +29 -0
- package/dist/src/func/builtins/return-types.d.ts.map +1 -1
- package/dist/src/func/builtins/return-types.js +4 -1
- package/dist/src/func/builtins/return-types.js.map +1 -1
- package/dist/src/func/builtins/scalar.d.ts.map +1 -1
- package/dist/src/func/builtins/scalar.js +22 -67
- package/dist/src/func/builtins/scalar.js.map +1 -1
- package/dist/src/func/builtins/schema.d.ts.map +1 -1
- package/dist/src/func/builtins/schema.js +26 -16
- package/dist/src/func/builtins/schema.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +6 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/parser/parser.d.ts.map +1 -1
- package/dist/src/parser/parser.js +8 -5
- package/dist/src/parser/parser.js.map +1 -1
- package/dist/src/parser/visitor.d.ts +16 -0
- package/dist/src/parser/visitor.d.ts.map +1 -1
- package/dist/src/parser/visitor.js +16 -0
- package/dist/src/parser/visitor.js.map +1 -1
- package/dist/src/planner/analysis/assertion-plan.d.ts +33 -0
- package/dist/src/planner/analysis/assertion-plan.d.ts.map +1 -0
- package/dist/src/planner/analysis/assertion-plan.js +39 -0
- package/dist/src/planner/analysis/assertion-plan.js.map +1 -0
- package/dist/src/planner/analysis/binary-operator-class.d.ts +55 -0
- package/dist/src/planner/analysis/binary-operator-class.d.ts.map +1 -0
- package/dist/src/planner/analysis/binary-operator-class.js +57 -0
- package/dist/src/planner/analysis/binary-operator-class.js.map +1 -0
- package/dist/src/planner/analysis/binding-extractor.d.ts +9 -3
- package/dist/src/planner/analysis/binding-extractor.d.ts.map +1 -1
- package/dist/src/planner/analysis/binding-extractor.js +11 -16
- package/dist/src/planner/analysis/binding-extractor.js.map +1 -1
- package/dist/src/planner/analysis/change-scope.d.ts.map +1 -1
- package/dist/src/planner/analysis/change-scope.js +19 -6
- package/dist/src/planner/analysis/change-scope.js.map +1 -1
- package/dist/src/planner/analysis/check-extraction.d.ts +1 -7
- package/dist/src/planner/analysis/check-extraction.d.ts.map +1 -1
- package/dist/src/planner/analysis/check-extraction.js.map +1 -1
- package/dist/src/planner/analysis/comparison-collation.d.ts +17 -0
- package/dist/src/planner/analysis/comparison-collation.d.ts.map +1 -1
- package/dist/src/planner/analysis/comparison-collation.js +17 -5
- package/dist/src/planner/analysis/comparison-collation.js.map +1 -1
- package/dist/src/planner/analysis/constraint-extractor.d.ts.map +1 -1
- package/dist/src/planner/analysis/constraint-extractor.js +18 -18
- package/dist/src/planner/analysis/constraint-extractor.js.map +1 -1
- package/dist/src/planner/analysis/coverage-prover.d.ts +11 -7
- package/dist/src/planner/analysis/coverage-prover.d.ts.map +1 -1
- package/dist/src/planner/analysis/coverage-prover.js +11 -7
- package/dist/src/planner/analysis/coverage-prover.js.map +1 -1
- package/dist/src/planner/analysis/equi-correlation.d.ts +1 -1
- package/dist/src/planner/analysis/equi-correlation.js +1 -1
- package/dist/src/planner/analysis/expression-fingerprint.d.ts.map +1 -1
- package/dist/src/planner/analysis/expression-fingerprint.js +0 -2
- package/dist/src/planner/analysis/expression-fingerprint.js.map +1 -1
- package/dist/src/planner/analysis/key-filter.d.ts.map +1 -1
- package/dist/src/planner/analysis/key-filter.js +2 -3
- package/dist/src/planner/analysis/key-filter.js.map +1 -1
- package/dist/src/planner/analysis/relation-key.d.ts +104 -0
- package/dist/src/planner/analysis/relation-key.d.ts.map +1 -0
- package/dist/src/planner/analysis/relation-key.js +121 -0
- package/dist/src/planner/analysis/relation-key.js.map +1 -0
- package/dist/src/planner/analysis/scalar-param-usage.d.ts.map +1 -1
- package/dist/src/planner/analysis/scalar-param-usage.js +12 -4
- package/dist/src/planner/analysis/scalar-param-usage.js.map +1 -1
- package/dist/src/planner/analysis/set-op-type-merge.d.ts +9 -3
- package/dist/src/planner/analysis/set-op-type-merge.d.ts.map +1 -1
- package/dist/src/planner/analysis/set-op-type-merge.js +22 -8
- package/dist/src/planner/analysis/set-op-type-merge.js.map +1 -1
- package/dist/src/planner/building/alter-table.d.ts.map +1 -1
- package/dist/src/planner/building/alter-table.js +31 -23
- package/dist/src/planner/building/alter-table.js.map +1 -1
- package/dist/src/planner/building/analyze.d.ts.map +1 -1
- package/dist/src/planner/building/analyze.js +21 -0
- package/dist/src/planner/building/analyze.js.map +1 -1
- package/dist/src/planner/building/block.d.ts.map +1 -1
- package/dist/src/planner/building/block.js +222 -82
- package/dist/src/planner/building/block.js.map +1 -1
- package/dist/src/planner/building/constraint-builder.d.ts.map +1 -1
- package/dist/src/planner/building/constraint-builder.js +5 -3
- package/dist/src/planner/building/constraint-builder.js.map +1 -1
- package/dist/src/planner/building/delete.js +1 -1
- package/dist/src/planner/building/delete.js.map +1 -1
- package/dist/src/planner/building/expression.d.ts.map +1 -1
- package/dist/src/planner/building/expression.js +11 -3
- package/dist/src/planner/building/expression.js.map +1 -1
- package/dist/src/planner/building/foreign-key-builder.d.ts +16 -6
- package/dist/src/planner/building/foreign-key-builder.d.ts.map +1 -1
- package/dist/src/planner/building/foreign-key-builder.js +43 -20
- package/dist/src/planner/building/foreign-key-builder.js.map +1 -1
- package/dist/src/planner/building/generated-column-scope.d.ts +58 -0
- package/dist/src/planner/building/generated-column-scope.d.ts.map +1 -0
- package/dist/src/planner/building/generated-column-scope.js +117 -0
- package/dist/src/planner/building/generated-column-scope.js.map +1 -0
- package/dist/src/planner/building/insert.d.ts.map +1 -1
- package/dist/src/planner/building/insert.js +20 -28
- package/dist/src/planner/building/insert.js.map +1 -1
- package/dist/src/planner/building/select-aggregates.d.ts +73 -132
- package/dist/src/planner/building/select-aggregates.d.ts.map +1 -1
- package/dist/src/planner/building/select-aggregates.js +405 -81
- package/dist/src/planner/building/select-aggregates.js.map +1 -1
- package/dist/src/planner/building/select-modifiers.d.ts +37 -8
- package/dist/src/planner/building/select-modifiers.d.ts.map +1 -1
- package/dist/src/planner/building/select-modifiers.js +8 -9
- package/dist/src/planner/building/select-modifiers.js.map +1 -1
- package/dist/src/planner/building/select-ordinal.d.ts +6 -0
- package/dist/src/planner/building/select-ordinal.d.ts.map +1 -1
- package/dist/src/planner/building/select-ordinal.js +1 -1
- package/dist/src/planner/building/select-ordinal.js.map +1 -1
- package/dist/src/planner/building/select-window.d.ts +2 -2
- package/dist/src/planner/building/select-window.d.ts.map +1 -1
- package/dist/src/planner/building/select-window.js +82 -101
- 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 +78 -33
- package/dist/src/planner/building/select.js.map +1 -1
- package/dist/src/planner/building/update.d.ts.map +1 -1
- package/dist/src/planner/building/update.js +14 -8
- package/dist/src/planner/building/update.js.map +1 -1
- package/dist/src/planner/building/view-mutation-builder.d.ts.map +1 -1
- package/dist/src/planner/building/view-mutation-builder.js +329 -14
- package/dist/src/planner/building/view-mutation-builder.js.map +1 -1
- package/dist/src/planner/building/with.d.ts +6 -0
- package/dist/src/planner/building/with.d.ts.map +1 -1
- package/dist/src/planner/building/with.js +52 -3
- package/dist/src/planner/building/with.js.map +1 -1
- package/dist/src/planner/cache/correlation-detector.d.ts.map +1 -1
- package/dist/src/planner/cache/correlation-detector.js +44 -17
- package/dist/src/planner/cache/correlation-detector.js.map +1 -1
- package/dist/src/planner/framework/physical-utils.d.ts +6 -0
- package/dist/src/planner/framework/physical-utils.d.ts.map +1 -1
- package/dist/src/planner/framework/physical-utils.js +6 -0
- package/dist/src/planner/framework/physical-utils.js.map +1 -1
- package/dist/src/planner/mutation/lens-enforcement.d.ts +123 -0
- package/dist/src/planner/mutation/lens-enforcement.d.ts.map +1 -1
- package/dist/src/planner/mutation/lens-enforcement.js +362 -5
- package/dist/src/planner/mutation/lens-enforcement.js.map +1 -1
- package/dist/src/planner/mutation/mutation-diagnostic.d.ts +1 -1
- package/dist/src/planner/mutation/mutation-diagnostic.d.ts.map +1 -1
- package/dist/src/planner/mutation/mutation-diagnostic.js.map +1 -1
- package/dist/src/planner/nodes/alter-table-node.d.ts +15 -11
- package/dist/src/planner/nodes/alter-table-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/alter-table-node.js +5 -0
- package/dist/src/planner/nodes/alter-table-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 +7 -0
- package/dist/src/planner/nodes/bloom-join-node.js.map +1 -1
- package/dist/src/planner/nodes/constraint-check-node.d.ts +16 -0
- package/dist/src/planner/nodes/constraint-check-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/constraint-check-node.js.map +1 -1
- package/dist/src/planner/nodes/plan-node-type.d.ts +1 -1
- package/dist/src/planner/nodes/plan-node-type.d.ts.map +1 -1
- package/dist/src/planner/nodes/plan-node-type.js +1 -1
- package/dist/src/planner/nodes/plan-node-type.js.map +1 -1
- package/dist/src/planner/nodes/pragma.d.ts.map +1 -1
- package/dist/src/planner/nodes/pragma.js +5 -2
- package/dist/src/planner/nodes/pragma.js.map +1 -1
- package/dist/src/planner/nodes/reference.d.ts.map +1 -1
- package/dist/src/planner/nodes/reference.js +9 -1
- package/dist/src/planner/nodes/reference.js.map +1 -1
- package/dist/src/planner/nodes/scalar.d.ts.map +1 -1
- package/dist/src/planner/nodes/scalar.js +117 -91
- package/dist/src/planner/nodes/scalar.js.map +1 -1
- package/dist/src/planner/nodes/sequence-node.d.ts +50 -0
- package/dist/src/planner/nodes/sequence-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/sequence-node.js +96 -0
- package/dist/src/planner/nodes/sequence-node.js.map +1 -0
- package/dist/src/planner/nodes/values-node.d.ts +8 -0
- package/dist/src/planner/nodes/values-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/values-node.js +29 -6
- package/dist/src/planner/nodes/values-node.js.map +1 -1
- package/dist/src/planner/nodes/view-mutation-node.d.ts +43 -1
- package/dist/src/planner/nodes/view-mutation-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/view-mutation-node.js +27 -6
- package/dist/src/planner/nodes/view-mutation-node.js.map +1 -1
- package/dist/src/planner/nodes/window-node.d.ts +33 -4
- package/dist/src/planner/nodes/window-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/window-node.js +80 -36
- package/dist/src/planner/nodes/window-node.js.map +1 -1
- package/dist/src/planner/planning-context.d.ts +38 -1
- package/dist/src/planner/planning-context.d.ts.map +1 -1
- package/dist/src/planner/rules/access/rule-key-set-seek.d.ts.map +1 -1
- package/dist/src/planner/rules/access/rule-key-set-seek.js +8 -3
- package/dist/src/planner/rules/access/rule-key-set-seek.js.map +1 -1
- package/dist/src/planner/rules/join/rule-fanout-lookup-join.js +18 -12
- package/dist/src/planner/rules/join/rule-fanout-lookup-join.js.map +1 -1
- package/dist/src/planner/rules/retrieve/rule-grow-retrieve.js +4 -3
- package/dist/src/planner/rules/retrieve/rule-grow-retrieve.js.map +1 -1
- package/dist/src/planner/rules/window/rule-monotonic-window.d.ts.map +1 -1
- package/dist/src/planner/rules/window/rule-monotonic-window.js +6 -0
- package/dist/src/planner/rules/window/rule-monotonic-window.js.map +1 -1
- package/dist/src/planner/scopes/param.d.ts.map +1 -1
- package/dist/src/planner/scopes/param.js +10 -5
- package/dist/src/planner/scopes/param.js.map +1 -1
- package/dist/src/planner/util/ind-utils.d.ts +25 -8
- package/dist/src/planner/util/ind-utils.d.ts.map +1 -1
- package/dist/src/planner/util/ind-utils.js +53 -10
- package/dist/src/planner/util/ind-utils.js.map +1 -1
- package/dist/src/planner/util/key-utils.d.ts +7 -7
- package/dist/src/planner/util/key-utils.js +9 -9
- package/dist/src/planner/util/key-utils.js.map +1 -1
- package/dist/src/runtime/delta-executor.d.ts.map +1 -1
- package/dist/src/runtime/delta-executor.js +7 -2
- 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 +50 -15
- package/dist/src/runtime/emit/add-constraint.js.map +1 -1
- package/dist/src/runtime/emit/alter-schema-event.d.ts +2 -43
- package/dist/src/runtime/emit/alter-schema-event.d.ts.map +1 -1
- package/dist/src/runtime/emit/alter-schema-event.js +2 -53
- package/dist/src/runtime/emit/alter-schema-event.js.map +1 -1
- package/dist/src/runtime/emit/alter-table.d.ts.map +1 -1
- package/dist/src/runtime/emit/alter-table.js +59 -24
- 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 +7 -0
- package/dist/src/runtime/emit/analyze.js.map +1 -1
- package/dist/src/runtime/emit/assertion-rename-helpers.d.ts.map +1 -1
- package/dist/src/runtime/emit/assertion-rename-helpers.js +2 -3
- package/dist/src/runtime/emit/assertion-rename-helpers.js.map +1 -1
- package/dist/src/runtime/emit/binary.d.ts +5 -0
- package/dist/src/runtime/emit/binary.d.ts.map +1 -1
- package/dist/src/runtime/emit/binary.js +22 -22
- 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 +9 -3
- package/dist/src/runtime/emit/block.js.map +1 -1
- package/dist/src/runtime/emit/constraint-check.d.ts.map +1 -1
- package/dist/src/runtime/emit/constraint-check.js +5 -6
- package/dist/src/runtime/emit/constraint-check.js.map +1 -1
- package/dist/src/runtime/emit/create-assertion.d.ts.map +1 -1
- package/dist/src/runtime/emit/create-assertion.js +71 -70
- package/dist/src/runtime/emit/create-assertion.js.map +1 -1
- package/dist/src/runtime/emit/create-index.d.ts.map +1 -1
- package/dist/src/runtime/emit/create-index.js +8 -3
- package/dist/src/runtime/emit/create-index.js.map +1 -1
- package/dist/src/runtime/emit/create-table.d.ts.map +1 -1
- package/dist/src/runtime/emit/create-table.js +20 -10
- 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 +51 -44
- package/dist/src/runtime/emit/create-view.js.map +1 -1
- package/dist/src/runtime/emit/ddl-event-scope.d.ts +71 -0
- package/dist/src/runtime/emit/ddl-event-scope.d.ts.map +1 -0
- package/dist/src/runtime/emit/ddl-event-scope.js +80 -0
- package/dist/src/runtime/emit/ddl-event-scope.js.map +1 -0
- package/dist/src/runtime/emit/dml-executor.d.ts.map +1 -1
- package/dist/src/runtime/emit/dml-executor.js +23 -23
- package/dist/src/runtime/emit/dml-executor.js.map +1 -1
- package/dist/src/runtime/emit/drop-assertion.d.ts.map +1 -1
- package/dist/src/runtime/emit/drop-assertion.js +23 -17
- package/dist/src/runtime/emit/drop-assertion.js.map +1 -1
- package/dist/src/runtime/emit/drop-index.d.ts.map +1 -1
- package/dist/src/runtime/emit/drop-index.js +7 -2
- package/dist/src/runtime/emit/drop-index.js.map +1 -1
- package/dist/src/runtime/emit/drop-table.d.ts.map +1 -1
- package/dist/src/runtime/emit/drop-table.js +16 -10
- 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 +50 -43
- package/dist/src/runtime/emit/drop-view.js.map +1 -1
- package/dist/src/runtime/emit/insert.d.ts.map +1 -1
- package/dist/src/runtime/emit/insert.js +2 -1
- package/dist/src/runtime/emit/insert.js.map +1 -1
- package/dist/src/runtime/emit/materialized-view.d.ts.map +1 -1
- package/dist/src/runtime/emit/materialized-view.js +87 -67
- package/dist/src/runtime/emit/materialized-view.js.map +1 -1
- package/dist/src/runtime/emit/schema-declarative.d.ts +8 -0
- package/dist/src/runtime/emit/schema-declarative.d.ts.map +1 -1
- package/dist/src/runtime/emit/schema-declarative.js +128 -25
- package/dist/src/runtime/emit/schema-declarative.js.map +1 -1
- package/dist/src/runtime/emit/sequence.d.ts +17 -0
- package/dist/src/runtime/emit/sequence.d.ts.map +1 -0
- package/dist/src/runtime/emit/sequence.js +40 -0
- package/dist/src/runtime/emit/sequence.js.map +1 -0
- package/dist/src/runtime/emit/set-object-tags.d.ts.map +1 -1
- package/dist/src/runtime/emit/set-object-tags.js +40 -32
- package/dist/src/runtime/emit/set-object-tags.js.map +1 -1
- package/dist/src/runtime/emit/table-valued-function.d.ts.map +1 -1
- package/dist/src/runtime/emit/table-valued-function.js +3 -2
- package/dist/src/runtime/emit/table-valued-function.js.map +1 -1
- package/dist/src/runtime/emit/update.js +3 -3
- package/dist/src/runtime/emit/update.js.map +1 -1
- package/dist/src/runtime/emit/view-mutation.d.ts.map +1 -1
- package/dist/src/runtime/emit/view-mutation.js +45 -3
- package/dist/src/runtime/emit/view-mutation.js.map +1 -1
- package/dist/src/runtime/foreign-key-actions.d.ts.map +1 -1
- package/dist/src/runtime/foreign-key-actions.js +9 -10
- package/dist/src/runtime/foreign-key-actions.js.map +1 -1
- package/dist/src/runtime/register.d.ts.map +1 -1
- package/dist/src/runtime/register.js +3 -2
- package/dist/src/runtime/register.js.map +1 -1
- package/dist/src/runtime/row-constraints.d.ts +14 -8
- package/dist/src/runtime/row-constraints.d.ts.map +1 -1
- package/dist/src/runtime/row-constraints.js +41 -15
- package/dist/src/runtime/row-constraints.js.map +1 -1
- package/dist/src/runtime/strict-representation.d.ts +11 -6
- package/dist/src/runtime/strict-representation.d.ts.map +1 -1
- package/dist/src/runtime/strict-representation.js +1 -44
- package/dist/src/runtime/strict-representation.js.map +1 -1
- package/dist/src/schema/assertion.d.ts +25 -2
- package/dist/src/schema/assertion.d.ts.map +1 -1
- package/dist/src/schema/assertion.js.map +1 -1
- package/dist/src/schema/catalog-rendering.d.ts +38 -0
- package/dist/src/schema/catalog-rendering.d.ts.map +1 -0
- package/dist/src/schema/catalog-rendering.js +171 -0
- package/dist/src/schema/catalog-rendering.js.map +1 -0
- package/dist/src/schema/catalog.d.ts +69 -3
- package/dist/src/schema/catalog.d.ts.map +1 -1
- package/dist/src/schema/catalog.js +124 -19
- package/dist/src/schema/catalog.js.map +1 -1
- package/dist/src/schema/column-source-resolver.d.ts +9 -5
- package/dist/src/schema/column-source-resolver.d.ts.map +1 -1
- package/dist/src/schema/column-source-resolver.js +9 -5
- package/dist/src/schema/column-source-resolver.js.map +1 -1
- package/dist/src/schema/constraint-builder.d.ts +13 -10
- package/dist/src/schema/constraint-builder.d.ts.map +1 -1
- package/dist/src/schema/constraint-builder.js +45 -17
- package/dist/src/schema/constraint-builder.js.map +1 -1
- package/dist/src/schema/declared-schema-manager.d.ts +50 -0
- package/dist/src/schema/declared-schema-manager.d.ts.map +1 -1
- package/dist/src/schema/declared-schema-manager.js +52 -0
- package/dist/src/schema/declared-schema-manager.js.map +1 -1
- package/dist/src/schema/expr-scope/frame.d.ts +65 -0
- package/dist/src/schema/expr-scope/frame.d.ts.map +1 -0
- package/dist/src/schema/expr-scope/frame.js +104 -0
- package/dist/src/schema/expr-scope/frame.js.map +1 -0
- package/dist/src/schema/expr-scope/walk.d.ts +40 -0
- package/dist/src/schema/expr-scope/walk.d.ts.map +1 -0
- package/dist/src/schema/expr-scope/walk.js +260 -0
- package/dist/src/schema/expr-scope/walk.js.map +1 -0
- package/dist/src/schema/generated-column-refs.d.ts +41 -0
- package/dist/src/schema/generated-column-refs.d.ts.map +1 -0
- package/dist/src/schema/generated-column-refs.js +124 -0
- package/dist/src/schema/generated-column-refs.js.map +1 -0
- package/dist/src/schema/lens-ack.d.ts.map +1 -1
- package/dist/src/schema/lens-ack.js +5 -2
- package/dist/src/schema/lens-ack.js.map +1 -1
- package/dist/src/schema/lens-compiler.d.ts.map +1 -1
- package/dist/src/schema/lens-compiler.js +9 -1
- package/dist/src/schema/lens-compiler.js.map +1 -1
- package/dist/src/schema/lens-prover.d.ts +7 -2
- package/dist/src/schema/lens-prover.d.ts.map +1 -1
- package/dist/src/schema/lens-prover.js +105 -11
- package/dist/src/schema/lens-prover.js.map +1 -1
- package/dist/src/schema/lens.d.ts +12 -0
- package/dist/src/schema/lens.d.ts.map +1 -1
- package/dist/src/schema/lens.js +10 -0
- package/dist/src/schema/lens.js.map +1 -1
- package/dist/src/schema/manager.d.ts +35 -1
- package/dist/src/schema/manager.d.ts.map +1 -1
- package/dist/src/schema/manager.js +125 -26
- package/dist/src/schema/manager.js.map +1 -1
- package/dist/src/schema/rename/self-qualifier-strip.d.ts +16 -10
- package/dist/src/schema/rename/self-qualifier-strip.d.ts.map +1 -1
- package/dist/src/schema/rename/self-qualifier-strip.js +30 -213
- package/dist/src/schema/rename/self-qualifier-strip.js.map +1 -1
- package/dist/src/schema/rename-rewriter.d.ts +4 -3
- package/dist/src/schema/rename-rewriter.d.ts.map +1 -1
- package/dist/src/schema/rename-rewriter.js +4 -3
- package/dist/src/schema/rename-rewriter.js.map +1 -1
- package/dist/src/schema/schema-differ.d.ts +54 -6
- package/dist/src/schema/schema-differ.d.ts.map +1 -1
- package/dist/src/schema/schema-differ.js +108 -42
- package/dist/src/schema/schema-differ.js.map +1 -1
- package/dist/src/schema/schema-hasher.d.ts +12 -0
- package/dist/src/schema/schema-hasher.d.ts.map +1 -1
- package/dist/src/schema/schema-hasher.js +28 -10
- package/dist/src/schema/schema-hasher.js.map +1 -1
- package/dist/src/schema/table.d.ts +74 -19
- package/dist/src/schema/table.d.ts.map +1 -1
- package/dist/src/schema/table.js +133 -71
- package/dist/src/schema/table.js.map +1 -1
- package/dist/src/types/comparison-coercion.d.ts +5 -1
- package/dist/src/types/comparison-coercion.d.ts.map +1 -1
- package/dist/src/types/comparison-coercion.js +21 -5
- package/dist/src/types/comparison-coercion.js.map +1 -1
- package/dist/src/types/representation.d.ts +65 -0
- package/dist/src/types/representation.d.ts.map +1 -0
- package/dist/src/types/representation.js +63 -0
- package/dist/src/types/representation.js.map +1 -0
- package/dist/src/types/validation.d.ts +76 -22
- package/dist/src/types/validation.d.ts.map +1 -1
- package/dist/src/types/validation.js +110 -31
- package/dist/src/types/validation.js.map +1 -1
- package/dist/src/vtab/best-access-plan.d.ts +10 -0
- package/dist/src/vtab/best-access-plan.d.ts.map +1 -1
- package/dist/src/vtab/best-access-plan.js +16 -0
- package/dist/src/vtab/best-access-plan.js.map +1 -1
- package/dist/src/vtab/capabilities.d.ts +72 -4
- package/dist/src/vtab/capabilities.d.ts.map +1 -1
- package/dist/src/vtab/memory/layer/manager.d.ts.map +1 -1
- package/dist/src/vtab/memory/layer/manager.js +17 -13
- package/dist/src/vtab/memory/layer/manager.js.map +1 -1
- package/dist/src/vtab/module.d.ts +13 -7
- package/dist/src/vtab/module.d.ts.map +1 -1
- package/package.json +3 -3
- package/dist/src/planner/nodes/array-index-node.d.ts +0 -22
- package/dist/src/planner/nodes/array-index-node.d.ts.map +0 -1
- package/dist/src/planner/nodes/array-index-node.js +0 -41
- package/dist/src/planner/nodes/array-index-node.js.map +0 -1
- package/dist/src/runtime/emit/array-index.d.ts +0 -5
- package/dist/src/runtime/emit/array-index.d.ts.map +0 -1
- package/dist/src/runtime/emit/array-index.js +0 -22
- package/dist/src/runtime/emit/array-index.js.map +0 -1
|
@@ -14,6 +14,7 @@ import { isAggregateFunctionSchema } from '../../schema/function.js';
|
|
|
14
14
|
import { expressionToString, expressionToIdentityString } from '../../emit/ast-stringify.js';
|
|
15
15
|
import { buildOrdinalAwareExpression, resolveOrdinalReference } from './select-ordinal.js';
|
|
16
16
|
import { collectDefinedAttrIds } from '../analysis/equi-correlation.js';
|
|
17
|
+
import { walkAstNodes } from '../analysis/predicate-shape.js';
|
|
17
18
|
import { PlanNodeType } from '../nodes/plan-node-type.js';
|
|
18
19
|
/**
|
|
19
20
|
* Processes GROUP BY, aggregates, and HAVING clauses
|
|
@@ -32,14 +33,17 @@ export function buildAggregatePhase(input, stmt, selectContext, aggregates, hasA
|
|
|
32
33
|
hasHavingOnlyAggregates = true;
|
|
33
34
|
}
|
|
34
35
|
}
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
|
|
36
|
+
// Decide where this query's ORDER BY has to run (see
|
|
37
|
+
// `orderByNeedsPostAggregateSort`). Aggregate function references in ORDER BY are
|
|
38
|
+
// only legal when the query is otherwise an aggregate query (has aggregates in
|
|
39
|
+
// SELECT/HAVING or has a GROUP BY). When legal, any ORDER BY aggregate not already
|
|
40
|
+
// present in the SELECT or HAVING aggregate list must be added to the AggregateNode
|
|
41
|
+
// so it is computed and available to the post-aggregate sort. An ORDER BY that only
|
|
42
|
+
// names a SELECT-list alias adds nothing here — the alias resolves through a scope
|
|
43
|
+
// later — so `hasOrderByOnlyAggregates` stays false for it.
|
|
44
|
+
const needsPostAggregateSort = orderByNeedsPostAggregateSort(stmt.orderBy, selectContext, selectList);
|
|
41
45
|
let hasOrderByOnlyAggregates = false;
|
|
42
|
-
if (
|
|
46
|
+
if (needsPostAggregateSort && (hasAggregates || hasGroupBy)) {
|
|
43
47
|
const orderByAggs = collectOrderByAggregates(stmt.orderBy, selectContext, aggregates);
|
|
44
48
|
if (orderByAggs.length > 0) {
|
|
45
49
|
aggregates.push(...orderByAggs);
|
|
@@ -72,10 +76,10 @@ export function buildAggregatePhase(input, stmt, selectContext, aggregates, hasA
|
|
|
72
76
|
// After (optional) early HAVING filter we continue with the existing pipeline
|
|
73
77
|
// ----------------------------------------------------------------------------
|
|
74
78
|
// Handle pre-aggregate sorting for ORDER BY without GROUP BY. Skip when the
|
|
75
|
-
// ORDER BY
|
|
76
|
-
//
|
|
77
|
-
const preAggregateSort = Boolean(hasAggregates && !hasGroupBy && stmt.orderBy && stmt.orderBy.length > 0 && !
|
|
78
|
-
currentInput = handlePreAggregateSort(currentInput, stmt, selectContext,
|
|
79
|
+
// ORDER BY names an aggregate or a SELECT-list alias — neither can be evaluated
|
|
80
|
+
// against the per-input rows; they need the post-aggregate row(s).
|
|
81
|
+
const preAggregateSort = Boolean(hasAggregates && !hasGroupBy && stmt.orderBy && stmt.orderBy.length > 0 && !needsPostAggregateSort);
|
|
82
|
+
currentInput = handlePreAggregateSort(currentInput, stmt, selectContext, preAggregateSort, selectList);
|
|
79
83
|
// Build GROUP BY expressions, resolving 1-based positional references against the SELECT list.
|
|
80
84
|
const groupByExpressions = stmt.groupBy ?
|
|
81
85
|
stmt.groupBy.map(expr => buildOrdinalAwareExpression(selectContext, expr, selectList, 'GROUP BY', false)) : [];
|
|
@@ -85,8 +89,20 @@ export function buildAggregatePhase(input, stmt, selectContext, aggregates, hasA
|
|
|
85
89
|
// Create AggregateNode
|
|
86
90
|
const aggregateNode = new AggregateNode(selectContext.scope, currentInput, groupByExpressions, aggregates);
|
|
87
91
|
currentInput = aggregateNode;
|
|
92
|
+
// A GROUPED query's post-aggregate expressions — HAVING below, the rebuilt SELECT
|
|
93
|
+
// list, a window phase's specifications and function arguments, the sort keys —
|
|
94
|
+
// are all built against scopes that fall through to the pre-aggregate select scope,
|
|
95
|
+
// so a legal spelling of a grouping key can bind to a base-table attribute the
|
|
96
|
+
// grouped row does not carry. Every such expression reaches the fix through ONE
|
|
97
|
+
// entry point, {@link redirectPostAggregate}, off this one context — built here,
|
|
98
|
+
// the moment the AggregateNode exists, so no post-aggregate site can run before it
|
|
99
|
+
// does. An aggregate query with no GROUP BY has no grouping keys to redirect onto
|
|
100
|
+
// and gets none.
|
|
101
|
+
const groupedRedirectContext = groupByExpressions.length > 0
|
|
102
|
+
? buildGroupedRedirectContext(groupByExpressions, aggregateNode.getAttributes(), aggregateNode.getRelations()[0])
|
|
103
|
+
: undefined;
|
|
88
104
|
// Create aggregate output scope
|
|
89
|
-
const aggregateOutputScope = createAggregateOutputScope(selectContext.scope, currentInput, groupByExpressions, aggregates);
|
|
105
|
+
const aggregateOutputScope = createAggregateOutputScope(selectContext.scope, currentInput, groupByExpressions, aggregates, projections);
|
|
90
106
|
// Build the aggregates planning context entries so downstream builders
|
|
91
107
|
// (final projection, ORDER BY) can resolve aggregate function references
|
|
92
108
|
// to ColumnReferenceNodes against the AggregateNode output.
|
|
@@ -104,7 +120,7 @@ export function buildAggregatePhase(input, stmt, selectContext, aggregates, hasA
|
|
|
104
120
|
// Handle HAVING clause *after* aggregation only when we did not already push
|
|
105
121
|
// it below the AggregateNode.
|
|
106
122
|
if (stmt.having && !shouldPushHavingBelowAggregate) {
|
|
107
|
-
currentInput = buildHavingFilter(currentInput, stmt.having, selectContext, aggregateOutputScope, aggregates, groupByExpressions);
|
|
123
|
+
currentInput = buildHavingFilter(currentInput, stmt.having, selectContext, aggregateOutputScope, aggregates, groupByExpressions, groupedRedirectContext);
|
|
108
124
|
}
|
|
109
125
|
// Determine if final projection is needed.
|
|
110
126
|
// Force a final projection when HAVING-only or ORDER-BY-only aggregates were
|
|
@@ -134,32 +150,27 @@ export function buildAggregatePhase(input, stmt, selectContext, aggregates, hasA
|
|
|
134
150
|
groupByExpressions,
|
|
135
151
|
hasHavingOnlyAggregates,
|
|
136
152
|
hasOrderByOnlyAggregates,
|
|
137
|
-
|
|
153
|
+
orderByNeedsPostAggregateSort: needsPostAggregateSort,
|
|
138
154
|
aggregatesContext,
|
|
155
|
+
groupedRedirectContext,
|
|
139
156
|
};
|
|
140
157
|
}
|
|
141
158
|
/**
|
|
142
|
-
*
|
|
159
|
+
* Sorts the per-input rows BELOW the AggregateNode when the caller decided this
|
|
160
|
+
* query takes the pre-aggregate sort (see the `preAggregateSort` computation in
|
|
161
|
+
* {@link buildAggregatePhase}); otherwise passes `input` through untouched.
|
|
143
162
|
*/
|
|
144
|
-
function handlePreAggregateSort(input, stmt, selectContext,
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
//
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
expression,
|
|
156
|
-
direction: orderByClause.direction,
|
|
157
|
-
nulls: orderByClause.nulls
|
|
158
|
-
};
|
|
159
|
-
});
|
|
160
|
-
return new SortNode(selectContext.scope, input, sortKeys);
|
|
161
|
-
}
|
|
162
|
-
return input;
|
|
163
|
+
function handlePreAggregateSort(input, stmt, selectContext, preAggregateSort, selectList) {
|
|
164
|
+
if (!preAggregateSort)
|
|
165
|
+
return input;
|
|
166
|
+
// This sort sits BELOW the AggregateNode, so a positional reference resolves
|
|
167
|
+
// through the select list (no output attributes exist yet to bind to).
|
|
168
|
+
const sortKeys = stmt.orderBy.map(orderByClause => ({
|
|
169
|
+
expression: buildOrdinalAwareExpression(selectContext, orderByClause.expr, selectList, 'ORDER BY'),
|
|
170
|
+
direction: orderByClause.direction,
|
|
171
|
+
nulls: orderByClause.nulls
|
|
172
|
+
}));
|
|
173
|
+
return new SortNode(selectContext.scope, input, sortKeys);
|
|
163
174
|
}
|
|
164
175
|
/**
|
|
165
176
|
* Builds the coverage test for a grouped query. `groupedOutputAttributes` is the
|
|
@@ -167,7 +178,7 @@ function handlePreAggregateSort(input, stmt, selectContext, hasAggregates, hasGr
|
|
|
167
178
|
* needed only when the expressions to be checked were built against the aggregate
|
|
168
179
|
* output scope.
|
|
169
180
|
*/
|
|
170
|
-
|
|
181
|
+
function buildGroupByCoverage(groupByExpressions, groupedOutputAttributes = []) {
|
|
171
182
|
const attrIds = new Set();
|
|
172
183
|
const fingerprints = new Set();
|
|
173
184
|
for (const expr of groupByExpressions) {
|
|
@@ -183,10 +194,10 @@ export function buildGroupByCoverage(groupByExpressions, groupedOutputAttributes
|
|
|
183
194
|
}
|
|
184
195
|
/**
|
|
185
196
|
* Raises the standard GROUP BY coverage error when `node` reads a column the
|
|
186
|
-
* grouped rows do not carry. Used by the SELECT-list check below; the
|
|
187
|
-
*
|
|
197
|
+
* grouped rows do not carry. Used by the SELECT-list check below; the finished-plan
|
|
198
|
+
* check raises the same message from {@link assertGroupedPlanCoverage}.
|
|
188
199
|
*/
|
|
189
|
-
|
|
200
|
+
function assertGroupByCoverage(node, coverage) {
|
|
190
201
|
const ungrouped = findUngroupedColumnRef(node, coverage);
|
|
191
202
|
if (!ungrouped)
|
|
192
203
|
return;
|
|
@@ -197,7 +208,7 @@ export function assertGroupByCoverage(node, coverage) {
|
|
|
197
208
|
* column index is the key's position in that list, which is its position on the
|
|
198
209
|
* AggregateNode's output row.
|
|
199
210
|
*/
|
|
200
|
-
|
|
211
|
+
function indexGroupKeys(groupByExpressions) {
|
|
201
212
|
const byFingerprint = new Map();
|
|
202
213
|
const byBaseAttrId = new Map();
|
|
203
214
|
groupByExpressions.forEach((expr, index) => {
|
|
@@ -213,13 +224,13 @@ export function indexGroupKeys(groupByExpressions) {
|
|
|
213
224
|
return { byFingerprint, byBaseAttrId };
|
|
214
225
|
}
|
|
215
226
|
/**
|
|
216
|
-
* Builds the {@link
|
|
227
|
+
* Builds the {@link GroupedRedirectContext} for a grouped query.
|
|
217
228
|
* `outputAttributes` is the AggregateNode's attribute list (group keys in GROUP BY
|
|
218
229
|
* order, then the aggregate results); `aggregateInput` is the AggregateNode's
|
|
219
230
|
* source relation, whose whole subtree defines
|
|
220
|
-
* {@link
|
|
231
|
+
* {@link GroupedRedirectContext.aggregateInputAttrIds}.
|
|
221
232
|
*/
|
|
222
|
-
|
|
233
|
+
function buildGroupedRedirectContext(groupByExpressions, outputAttributes, aggregateInput) {
|
|
223
234
|
return {
|
|
224
235
|
groupKeys: indexGroupKeys(groupByExpressions),
|
|
225
236
|
outputAttributes,
|
|
@@ -250,10 +261,35 @@ function isCteDefinition(node) {
|
|
|
250
261
|
return node.nodeType === PlanNodeType.CTE || node.nodeType === PlanNodeType.RecursiveCTE;
|
|
251
262
|
}
|
|
252
263
|
/**
|
|
253
|
-
*
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
*
|
|
264
|
+
* The ONE entry point through which every post-aggregate expression of a GROUPED
|
|
265
|
+
* query reaches the grouping-key redirect: the rebuilt SELECT list, the window
|
|
266
|
+
* phase's specifications and function arguments, the HAVING predicate, and every
|
|
267
|
+
* post-aggregate sort key. A builder that constructs an expression above the
|
|
268
|
+
* AggregateNode calls this rather than remembering the rewrite itself — forgetting
|
|
269
|
+
* is how the SELECT-list and ORDER BY bugs shipped
|
|
270
|
+
* (complete/bug-qualified-group-key-in-select-list-breaks-window-query,
|
|
271
|
+
* complete/bug-order-by-grouping-key-spelling-breaks-window-query).
|
|
272
|
+
*
|
|
273
|
+
* A pass-through when `context` is absent (the query has no GROUP BY keys), and when
|
|
274
|
+
* the expression contains nothing to redirect. That gate is load-bearing, not an
|
|
275
|
+
* optimization: {@link redirectToGroupKeys} matches a subtree by AST text, so under
|
|
276
|
+
* `group by a` an ungated pass would also fingerprint a plain `order by a` that
|
|
277
|
+
* already bound correctly to a projection/window/aggregate OUTPUT attribute — the
|
|
278
|
+
* clearest hazard being an alias that shadows a grouping key (`select upper(a) as a …
|
|
279
|
+
* group by a order by a` must sort by the projected `upper(a)`). Only an expression
|
|
280
|
+
* that actually fell through to a pre-grouping attribute has anything to fix, and
|
|
281
|
+
* only such an expression is walked.
|
|
282
|
+
*/
|
|
283
|
+
export function redirectPostAggregate(expr, context, scope) {
|
|
284
|
+
if (!context || !referencesAggregateInput(expr, context))
|
|
285
|
+
return expr;
|
|
286
|
+
return redirectToGroupKeys(expr, context, scope);
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Rewrites every subtree of a post-aggregate expression that IS a grouping key into
|
|
290
|
+
* a reference to the AggregateNode's output column for that key, so the expression
|
|
291
|
+
* reads the grouped row instead of a base-table column that row does not carry.
|
|
292
|
+
* Reached only through {@link redirectPostAggregate}.
|
|
257
293
|
*
|
|
258
294
|
* Two match rules, in order at each node:
|
|
259
295
|
*
|
|
@@ -294,7 +330,7 @@ function isCteDefinition(node) {
|
|
|
294
330
|
* fixing it means comparing resolved attribute identity rather than text, for both
|
|
295
331
|
* callers at once.
|
|
296
332
|
*/
|
|
297
|
-
|
|
333
|
+
function redirectToGroupKeys(node, context, scope) {
|
|
298
334
|
return redirectNode(node, context, scope);
|
|
299
335
|
}
|
|
300
336
|
/**
|
|
@@ -306,12 +342,24 @@ export function redirectToGroupKeys(node, context, scope) {
|
|
|
306
342
|
*
|
|
307
343
|
* NOTE: this renders an identity fingerprint at every scalar node it visits, and a
|
|
308
344
|
* fingerprint hit inside a subquery then walks that node's subtree again for the guard
|
|
309
|
-
* — so
|
|
310
|
-
*
|
|
311
|
-
* shows up as slow, memoize the fingerprint per node or bail out of the
|
|
312
|
-
* subtrees that contain no `aggregateInputAttrIds` reference at all.
|
|
345
|
+
* — so every grouped query pays a walk of its whole SELECT list, plus its window
|
|
346
|
+
* specifications when it has them, once per prepare. Not measured; if preparing grouped
|
|
347
|
+
* queries ever shows up as slow, memoize the fingerprint per node or bail out of the
|
|
348
|
+
* walk for subtrees that contain no `aggregateInputAttrIds` reference at all.
|
|
349
|
+
*
|
|
350
|
+
* NOTE: the SELECT-list caller hands us its window-function subtrees too, and the window
|
|
351
|
+
* phase then throws each of them away — `rewriteWindowFunctions` (select-window.ts)
|
|
352
|
+
* replaces the whole node with a column reference to the WindowNode's output, looked up
|
|
353
|
+
* by the `AST.WindowFunctionExpr`'s object identity. A `WindowFunctionCallNode` is
|
|
354
|
+
* zero-ary, so this walk could never rebuild one anyway — but it is skipped explicitly
|
|
355
|
+
* below so that lookup's key can never be disturbed and rule 1 can never fingerprint a
|
|
356
|
+
* window function itself. The window phase redirects its specification expressions
|
|
357
|
+
* itself and never passes one in.
|
|
313
358
|
*/
|
|
314
359
|
function redirectNode(node, context, scope, insideSubquery = false) {
|
|
360
|
+
if (CapabilityDetectors.isWindowFunction(node)) {
|
|
361
|
+
return node;
|
|
362
|
+
}
|
|
315
363
|
if ('expression' in node) {
|
|
316
364
|
const expression = node.expression;
|
|
317
365
|
const fingerprintIndex = context.groupKeys.byFingerprint.get(expressionToIdentityString(expression));
|
|
@@ -358,12 +406,96 @@ function readsOnlyAggregateInput(node, context) {
|
|
|
358
406
|
return true;
|
|
359
407
|
}
|
|
360
408
|
/**
|
|
361
|
-
*
|
|
362
|
-
*
|
|
363
|
-
* {@link
|
|
409
|
+
* True when `node` names one of THIS query's pre-grouping columns and the AggregateNode's
|
|
410
|
+
* output row does not carry it — the exact condition that makes a reference above the
|
|
411
|
+
* aggregate either redirectable ({@link referencesAggregateInput}) or, once the redirect
|
|
412
|
+
* has run, an ungrouped column ({@link findUngroupedPostAggregateRef}). The two callers ask
|
|
413
|
+
* it of different subtrees for different purposes, but the predicate is one thing and must
|
|
414
|
+
* stay one thing: a reference the redirect rewrites is by definition one the coverage check
|
|
415
|
+
* would otherwise have rejected.
|
|
416
|
+
*/
|
|
417
|
+
function isPreGroupingReference(node, context) {
|
|
418
|
+
const attrId = node.attributeId;
|
|
419
|
+
return context.aggregateInputAttrIds.has(attrId) && !context.outputAttrIds.has(attrId);
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* True when ANY column reference in `node`'s subtree is a pre-grouping column of this
|
|
423
|
+
* query — i.e. the expression reads something the AggregateNode's output row does not
|
|
424
|
+
* carry, and therefore needs {@link redirectToGroupKeys} run over it.
|
|
425
|
+
*
|
|
426
|
+
* The opposite question to its neighbour {@link readsOnlyAggregateInput} ("does the
|
|
427
|
+
* WHOLE subtree belong to this query's pre-grouping input", which guards a fingerprint
|
|
428
|
+
* match from hijacking a subquery's own identically-spelled column). This one is
|
|
429
|
+
* {@link redirectPostAggregate}'s *gate*: it answers "is there anything here to
|
|
430
|
+
* redirect at all". A subtree whose references all resolved through the projection /
|
|
431
|
+
* window-output / aggregate-output scopes contains none, and must be left alone.
|
|
432
|
+
*
|
|
433
|
+
* Per reference the test is {@link isPreGroupingReference}. CTE definition bodies are
|
|
434
|
+
* skipped for the same reason {@link redirectToGroupKeys} skips them (see
|
|
435
|
+
* {@link isCteDefinition}).
|
|
436
|
+
*/
|
|
437
|
+
function referencesAggregateInput(node, context) {
|
|
438
|
+
if (CapabilityDetectors.isColumnReference(node)) {
|
|
439
|
+
return isPreGroupingReference(node, context);
|
|
440
|
+
}
|
|
441
|
+
for (const child of node.getChildren()) {
|
|
442
|
+
if (isCteDefinition(child))
|
|
443
|
+
continue;
|
|
444
|
+
if (referencesAggregateInput(child, context))
|
|
445
|
+
return true;
|
|
446
|
+
}
|
|
447
|
+
return false;
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Boundary check over a GROUPED query's finished plan: no node ABOVE the
|
|
451
|
+
* AggregateNode may reference an aggregate-input attribute id that is absent from
|
|
452
|
+
* the aggregate's output. Called once per grouped query, from the end of
|
|
453
|
+
* `buildSelectStmt`, walking the operator spine from the query root down to — and
|
|
454
|
+
* stopping at — the AggregateNode itself (whose own input subtree legally reads
|
|
455
|
+
* pre-grouping columns). Each scalar expression hanging off a spine operator gets
|
|
456
|
+
* the subquery-aware per-expression check ({@link assertPostAggregateCoverage}).
|
|
457
|
+
*
|
|
458
|
+
* This is what turns "a builder forgot to call {@link redirectPostAggregate}" from a
|
|
459
|
+
* runtime `No row context found for column …` internal error into the user-facing
|
|
460
|
+
* GROUP BY message at plan time — the SELECT-list and ORDER BY escapes both shipped
|
|
461
|
+
* exactly that way. It also rejects, at plan time, a post-aggregate expression that
|
|
462
|
+
* genuinely reads an ungrouped column (directly, or correlated through a subquery).
|
|
463
|
+
*
|
|
464
|
+
* NOTE: the stop condition is node IDENTITY, so it holds only while `buildSelectStmt`
|
|
465
|
+
* *wraps* the aggregate rather than rebuilding it. A future post-aggregate step that
|
|
466
|
+
* rebuilds the spine through `withChildren` would mint a new AggregateNode, the walk
|
|
467
|
+
* would run straight past it into the aggregate's own input, and every legal
|
|
468
|
+
* pre-grouping reference down there (a WHERE conjunct, a join condition) would be
|
|
469
|
+
* rejected with a user-facing GROUP BY error. If such a step is ever added, locate
|
|
470
|
+
* the aggregate by node id instead of by reference.
|
|
471
|
+
*
|
|
472
|
+
* NOTE: one extra walk of the post-aggregate plan per grouped query per prepare, on
|
|
473
|
+
* top of the redirect's own walk (see {@link redirectNode}). Measured end-to-end, not
|
|
474
|
+
* isolated: compiling a six-column grouped query costs ~0.6-0.8 ms against ~0.5 ms for
|
|
475
|
+
* a comparable ungrouped one, so this walk is bounded well below the surrounding
|
|
476
|
+
* planning cost. Revisit if preparing grouped queries ever shows up as slow.
|
|
477
|
+
*/
|
|
478
|
+
export function assertGroupedPlanCoverage(node, aggregateNode, context) {
|
|
479
|
+
if (node === aggregateNode)
|
|
480
|
+
return;
|
|
481
|
+
for (const child of node.getChildren()) {
|
|
482
|
+
if (child === aggregateNode || isCteDefinition(child))
|
|
483
|
+
continue;
|
|
484
|
+
if (isRelationalNode(child)) {
|
|
485
|
+
assertGroupedPlanCoverage(child, aggregateNode, context);
|
|
486
|
+
}
|
|
487
|
+
else {
|
|
488
|
+
assertPostAggregateCoverage(child, context);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* Raises the GROUP BY coverage error for a post-aggregate expression of a GROUPED
|
|
494
|
+
* query that still reads a pre-grouping column after {@link redirectPostAggregate}
|
|
495
|
+
* has run.
|
|
364
496
|
*
|
|
365
497
|
* Distinct from {@link assertGroupByCoverage} in exactly two ways, both required by
|
|
366
|
-
* the fact that a
|
|
498
|
+
* the fact that a post-aggregate expression may contain a subquery:
|
|
367
499
|
*
|
|
368
500
|
* - it descends into relational children, so a correlated reference buried in a
|
|
369
501
|
* subquery is checked rather than skipped;
|
|
@@ -372,10 +504,10 @@ function readsOnlyAggregateInput(node, context) {
|
|
|
372
504
|
* or a correlated reference to an enclosing query — is legal here and is left alone.
|
|
373
505
|
*
|
|
374
506
|
* The aggregate exemption is likewise narrowed to this query's OWN aggregates; see
|
|
375
|
-
* {@link
|
|
507
|
+
* {@link findUngroupedPostAggregateRef}.
|
|
376
508
|
*/
|
|
377
|
-
|
|
378
|
-
const ungrouped =
|
|
509
|
+
function assertPostAggregateCoverage(node, context) {
|
|
510
|
+
const ungrouped = findUngroupedPostAggregateRef(node, context);
|
|
379
511
|
if (!ungrouped)
|
|
380
512
|
return;
|
|
381
513
|
throw new QuereusError(`Column '${expressionToString(ungrouped.expression)}' must appear in the GROUP BY clause or be used in an aggregate function`, StatusCode.ERROR, undefined, ungrouped.expression.loc?.start.line, ungrouped.expression.loc?.start.column);
|
|
@@ -391,19 +523,17 @@ export function assertGroupedWindowCoverage(node, context) {
|
|
|
391
523
|
* (select max(wg.b) from wg t))` reads an ungrouped column of this query and must say
|
|
392
524
|
* so at plan time, not die at runtime with "No row context found for column b".
|
|
393
525
|
*/
|
|
394
|
-
function
|
|
526
|
+
function findUngroupedPostAggregateRef(node, context, insideSubquery = false) {
|
|
395
527
|
if (!insideSubquery && CapabilityDetectors.isAggregateFunction(node)) {
|
|
396
528
|
return null;
|
|
397
529
|
}
|
|
398
530
|
if (CapabilityDetectors.isColumnReference(node)) {
|
|
399
|
-
|
|
400
|
-
const ungrouped = context.aggregateInputAttrIds.has(attrId) && !context.outputAttrIds.has(attrId);
|
|
401
|
-
return ungrouped ? node : null;
|
|
531
|
+
return isPreGroupingReference(node, context) ? node : null;
|
|
402
532
|
}
|
|
403
533
|
for (const child of node.getChildren()) {
|
|
404
534
|
if (isCteDefinition(child))
|
|
405
535
|
continue;
|
|
406
|
-
const found =
|
|
536
|
+
const found = findUngroupedPostAggregateRef(child, context, insideSubquery || isRelationalNode(child));
|
|
407
537
|
if (found)
|
|
408
538
|
return found;
|
|
409
539
|
}
|
|
@@ -438,6 +568,14 @@ function validateAggregateProjections(projections, hasAggregates, hasGroupBy, gr
|
|
|
438
568
|
* subtree (inner column refs are aggregated), a relational subtree (subqueries
|
|
439
569
|
* resolve their own scope), or any subtree whose AST fingerprint matches a GROUP BY
|
|
440
570
|
* expression (the whole subtree is grouped, e.g. SELECT id+1 ... GROUP BY id+1).
|
|
571
|
+
*
|
|
572
|
+
* Not descending into relational children means a correlated subquery in a grouped
|
|
573
|
+
* SELECT list that reads a genuinely UNGROUPED column of this query
|
|
574
|
+
* (`select a, (select count(*) from wg t where t.b = wg.b) … group by a`) is not
|
|
575
|
+
* rejected here. That is fine: the finished-plan check
|
|
576
|
+
* ({@link assertGroupedPlanCoverage}) IS subquery-aware — it can tell a correlated
|
|
577
|
+
* reference to THIS query from the subquery's own columns, off
|
|
578
|
+
* {@link GroupedRedirectContext} — and rejects it at plan time with the same message.
|
|
441
579
|
*/
|
|
442
580
|
function findUngroupedColumnRef(node, coverage) {
|
|
443
581
|
if (CapabilityDetectors.isAggregateFunction(node)) {
|
|
@@ -465,9 +603,40 @@ function findUngroupedColumnRef(node, coverage) {
|
|
|
465
603
|
return null;
|
|
466
604
|
}
|
|
467
605
|
/**
|
|
468
|
-
*
|
|
606
|
+
* The names a grouped query's OUTPUT row can be referred to by, as a scope. This is
|
|
607
|
+
* what a HAVING predicate and (for a grouped, windowed query) a window specification
|
|
608
|
+
* resolve against, so everything registered here is a name those clauses may use:
|
|
609
|
+
*
|
|
610
|
+
* - each GROUP BY key under its own column name, plus its qualified name when the key
|
|
611
|
+
* was written qualified (`group by i.id, c.id` registers `i.id` and `c.id`; the bare
|
|
612
|
+
* `id` they share becomes ambiguous);
|
|
613
|
+
* - each aggregate under its SELECT-list alias;
|
|
614
|
+
* - each grouping key ALSO under the SELECT-list alias of the column that selects it —
|
|
615
|
+
* `select a as k … group by a` makes `k` name the group key, in `over (order by k)`
|
|
616
|
+
* and in `HAVING` alike. Bare, qualified and computed keys all qualify
|
|
617
|
+
* (`select upper(a) as k … group by upper(a)`). This is SQLite-style permissiveness
|
|
618
|
+
* rather than strict SQL, and it matches how an aggregate's alias already behaves.
|
|
619
|
+
*
|
|
620
|
+
* A registered name SHADOWS a same-named base-table column, because anything not
|
|
621
|
+
* registered here falls through to the pre-aggregate select scope (window specs) or to
|
|
622
|
+
* the source-column fallback in {@link buildHavingFilter} (HAVING). So under
|
|
623
|
+
* `select a as b … group by a`, a bare `b` in either clause is the group key, not the
|
|
624
|
+
* table's own `b` column. Deliberate, and the same rule aggregate aliases already had.
|
|
625
|
+
*
|
|
626
|
+
* NOTE: a select-list alias is the LOWEST-precedence name here — it is skipped when a
|
|
627
|
+
* grouping key's own name or an aggregate's alias already claims it. `select a as b,
|
|
628
|
+
* b, count(*) … group by a, b` therefore keeps resolving a bare `b` to the grouping
|
|
629
|
+
* key `b`, matching both SQLite and PostgreSQL (where an alias never outranks a real
|
|
630
|
+
* column outside ORDER BY). Marking such a collision ambiguous instead was considered
|
|
631
|
+
* and rejected: the grouped SELECT list is itself rebuilt against this scope by
|
|
632
|
+
* {@link buildFinalAggregateProjections}, so an ambiguity minted here would reject the
|
|
633
|
+
* bare `b` in the select list too — and a select-list column must never be affected by
|
|
634
|
+
* a sibling's alias. Revisit only if select-list rebuilding stops going through this
|
|
635
|
+
* scope.
|
|
469
636
|
*/
|
|
470
|
-
function createAggregateOutputScope(parentScope, aggregateNode, groupByExpressions, aggregates
|
|
637
|
+
function createAggregateOutputScope(parentScope, aggregateNode, groupByExpressions, aggregates,
|
|
638
|
+
/** Non-aggregate SELECT-list columns (stars expanded), each carrying its alias. */
|
|
639
|
+
projections) {
|
|
471
640
|
const aggregateOutputScope = new RegisteredScope(parentScope);
|
|
472
641
|
const aggregateAttributes = aggregateNode.getAttributes();
|
|
473
642
|
// Mirror the source-side (FROM/JOIN) naming semantics: a qualified reference
|
|
@@ -553,6 +722,32 @@ function createAggregateOutputScope(parentScope, aggregateNode, groupByExpressio
|
|
|
553
722
|
aggregateOutputScope.registerSymbol(aliasKey, (exp, s) => new ColumnReferenceNode(s, exp, agg.expression.getType(), attr.id, columnIndex));
|
|
554
723
|
}
|
|
555
724
|
});
|
|
725
|
+
// Register each grouping key under the SELECT-list alias of the column that selects
|
|
726
|
+
// it, so `select a as k … group by a` can say `k` in HAVING and in a window
|
|
727
|
+
// specification. Skipped for any alias a grouping key's own name or an aggregate
|
|
728
|
+
// alias already claims (see the NOTE above), and marked ambiguous when two aliases
|
|
729
|
+
// of DIFFERENT grouping keys share one name (`select a as k, b as k … group by a, b`),
|
|
730
|
+
// which nothing else in this scope can arbitrate.
|
|
731
|
+
//
|
|
732
|
+
// The qualified keys count as "already claims" too: a quoted alias may contain a dot
|
|
733
|
+
// (`select wg.a as "wg.a" … group by wg.a`), and registering it would re-register a
|
|
734
|
+
// key this scope already holds — which `registerSymbol` rejects outright, failing a
|
|
735
|
+
// query that plans fine without the alias. See
|
|
736
|
+
// backlog/bug-scope-symbol-keys-collide-with-dotted-column-names for why a flat
|
|
737
|
+
// string key cannot tell a dotted name from a qualified one in the first place.
|
|
738
|
+
const groupKeys = indexGroupKeys(groupByExpressions);
|
|
739
|
+
for (const [aliasKey, keyIndexes] of collectAliasedGroupKeys(projections, groupKeys)) {
|
|
740
|
+
if (bareNameOwners.has(aliasKey) || registeredQualifiedKeys.has(aliasKey))
|
|
741
|
+
continue;
|
|
742
|
+
if (keyIndexes.size > 1) {
|
|
743
|
+
aggregateOutputScope.markAmbiguous(aliasKey);
|
|
744
|
+
continue;
|
|
745
|
+
}
|
|
746
|
+
const [index] = keyIndexes;
|
|
747
|
+
const attr = aggregateAttributes[index];
|
|
748
|
+
const keyExpr = groupByExpressions[index];
|
|
749
|
+
aggregateOutputScope.registerSymbol(aliasKey, (exp, s) => new ColumnReferenceNode(s, exp, keyExpr.getType(), attr.id, index));
|
|
750
|
+
}
|
|
556
751
|
// Note: the aggregate node advertises exactly its GROUP BY + aggregate columns.
|
|
557
752
|
// Source columns for HAVING / correlated access are resolved through the runtime
|
|
558
753
|
// row-descriptor context and the source-column fallback in buildHavingFilter's
|
|
@@ -560,9 +755,62 @@ function createAggregateOutputScope(parentScope, aggregateNode, groupByExpressio
|
|
|
560
755
|
return aggregateOutputScope;
|
|
561
756
|
}
|
|
562
757
|
/**
|
|
563
|
-
*
|
|
758
|
+
* Every SELECT-list alias that names a grouping key, mapped to the GROUP BY position(s)
|
|
759
|
+
* it could mean. More than one position means two aliases of different keys collide on
|
|
760
|
+
* one name.
|
|
761
|
+
*
|
|
762
|
+
* A projection is matched to its key the same two ways {@link redirectToGroupKeys}
|
|
763
|
+
* matches a window expression: by base attribute id for a column reference (so
|
|
764
|
+
* `select wg.a as k … group by a` and the reverse both land), else by identity
|
|
765
|
+
* fingerprint of the whole expression (so `select upper(a) as k … group by upper(a)`
|
|
766
|
+
* lands). A projection matching no key is skipped — in a grouped query it is an
|
|
767
|
+
* ungrouped SELECT column, which `validateAggregateProjections` has already rejected.
|
|
768
|
+
*
|
|
769
|
+
* Aliases are keyed lowercased, matching every other name in the aggregate output scope.
|
|
770
|
+
* Note that an unaliased `select a` carries no alias and contributes nothing, while a
|
|
771
|
+
* star-expanded column carries the source column's name as its alias — which for a
|
|
772
|
+
* grouping key is the name that key already registers, so it costs nothing.
|
|
564
773
|
*/
|
|
565
|
-
function
|
|
774
|
+
function collectAliasedGroupKeys(projections, groupKeys) {
|
|
775
|
+
const aliasTargets = new Map();
|
|
776
|
+
for (const projection of projections) {
|
|
777
|
+
if (!projection.alias)
|
|
778
|
+
continue;
|
|
779
|
+
const index = groupKeyIndexOf(projection.node, groupKeys);
|
|
780
|
+
if (index === undefined)
|
|
781
|
+
continue;
|
|
782
|
+
const aliasKey = projection.alias.toLowerCase();
|
|
783
|
+
let targets = aliasTargets.get(aliasKey);
|
|
784
|
+
if (!targets) {
|
|
785
|
+
targets = new Set();
|
|
786
|
+
aliasTargets.set(aliasKey, targets);
|
|
787
|
+
}
|
|
788
|
+
targets.add(index);
|
|
789
|
+
}
|
|
790
|
+
return aliasTargets;
|
|
791
|
+
}
|
|
792
|
+
/** Which GROUP BY key this SELECT-list expression IS, if any. */
|
|
793
|
+
function groupKeyIndexOf(node, groupKeys) {
|
|
794
|
+
if (CapabilityDetectors.isColumnReference(node)) {
|
|
795
|
+
const byAttrId = groupKeys.byBaseAttrId.get(node.attributeId);
|
|
796
|
+
if (byAttrId !== undefined)
|
|
797
|
+
return byAttrId;
|
|
798
|
+
}
|
|
799
|
+
return groupKeys.byFingerprint.get(expressionToIdentityString(node.expression));
|
|
800
|
+
}
|
|
801
|
+
/**
|
|
802
|
+
* Builds HAVING filter clause.
|
|
803
|
+
*
|
|
804
|
+
* `groupedRedirectContext` is supplied for a GROUPED query. A HAVING reference to a
|
|
805
|
+
* grouping key under a spelling the hybrid scope does not hold (`having wg.a = 'x'`
|
|
806
|
+
* against `group by a`, the key nested in `upper(wg.a)`, a computed key written out
|
|
807
|
+
* again) falls through to a base-table attribute, which happened to read correctly
|
|
808
|
+
* only because this FilterNode sits directly on the AggregateNode's yield where the
|
|
809
|
+
* representative source row is still published. {@link redirectPostAggregate} lands
|
|
810
|
+
* it on the aggregate's own output column instead, so the predicate stays correct
|
|
811
|
+
* wherever the filter ends up relative to a buffering operator.
|
|
812
|
+
*/
|
|
813
|
+
function buildHavingFilter(input, havingClause, selectContext, aggregateOutputScope, aggregates, groupByExpressions, groupedRedirectContext) {
|
|
566
814
|
const aggregateAttributes = input.getAttributes();
|
|
567
815
|
// Create a hybrid scope that first tries the aggregate output scope,
|
|
568
816
|
// then falls back to the original source scope for column resolution.
|
|
@@ -605,7 +853,7 @@ function buildHavingFilter(input, havingClause, selectContext, aggregateOutputSc
|
|
|
605
853
|
};
|
|
606
854
|
})
|
|
607
855
|
};
|
|
608
|
-
const havingExpression = buildExpression(havingContext, havingClause, true);
|
|
856
|
+
const havingExpression = redirectPostAggregate(buildExpression(havingContext, havingClause, true), groupedRedirectContext, hybridScope);
|
|
609
857
|
// Reject HAVING references to non-grouped, non-aggregated columns.
|
|
610
858
|
// With GROUP BY: only GROUP BY columns/expressions and aggregates are allowed.
|
|
611
859
|
// Without GROUP BY (implicit single group, only reachable here when aggregates
|
|
@@ -825,23 +1073,74 @@ function collectOrderByAggregates(orderBy, selectContext, existingAggregates) {
|
|
|
825
1073
|
return dedupeNewAggregates(funcExprs, selectContext, existingAggregates);
|
|
826
1074
|
}
|
|
827
1075
|
/**
|
|
828
|
-
*
|
|
1076
|
+
* Decides whether an ungrouped aggregate query's ORDER BY must be sorted ABOVE the
|
|
1077
|
+
* AggregateNode rather than below it (the pre-aggregate sort extension, which is what
|
|
1078
|
+
* makes `select group_concat(b) from t order by a` concatenate in `a` order).
|
|
1079
|
+
*
|
|
1080
|
+
* Three spellings need the post-aggregate placement, because none of them can be
|
|
1081
|
+
* evaluated against the per-input rows the pre-aggregate sort sees:
|
|
1082
|
+
*
|
|
1083
|
+
* - a spelled-out aggregate call (`order by count(*)`);
|
|
1084
|
+
* - a positional reference standing for one — the literal `1` in `select count(*) as c
|
|
1085
|
+
* from t order by 1` contains no aggregate itself, so it is resolved against the
|
|
1086
|
+
* SELECT list FIRST. Without that the query fails with "Aggregate function count not
|
|
1087
|
+
* allowed in this context";
|
|
1088
|
+
* - a bare (unqualified) column name that is a SELECT-list alias (`select count(*) as c
|
|
1089
|
+
* from t order by c`). The alias only ever exists in the aggregate/projection output
|
|
1090
|
+
* scope, so a pre-aggregate sort fails with "Column not found: c".
|
|
829
1091
|
*
|
|
830
|
-
*
|
|
831
|
-
*
|
|
832
|
-
*
|
|
833
|
-
*
|
|
834
|
-
* routes to the post-aggregate ORDER BY exactly like the spelled-out
|
|
835
|
-
* `order by count(*)`.
|
|
1092
|
+
* The alias arm makes a select-list alias outrank a same-named source column in an
|
|
1093
|
+
* aggregate ORDER BY, matching SQLite and what the non-aggregate and grouped paths
|
|
1094
|
+
* already do. Qualified names (`order by t.c`) are excluded and keep resolving to the
|
|
1095
|
+
* table's column.
|
|
836
1096
|
*/
|
|
837
|
-
function
|
|
1097
|
+
function orderByNeedsPostAggregateSort(orderBy, selectContext, selectList) {
|
|
838
1098
|
if (!orderBy || orderBy.length === 0)
|
|
839
1099
|
return false;
|
|
1100
|
+
const aliases = selectListAliases(selectList);
|
|
840
1101
|
return orderBy.some(clause => {
|
|
841
1102
|
const entry = resolveOrdinalReference(clause.expr, selectList, 'ORDER BY');
|
|
842
|
-
|
|
1103
|
+
if (containsAggregateFunction(entry?.expr ?? clause.expr, selectContext))
|
|
1104
|
+
return true;
|
|
1105
|
+
return mentionsSelectListAlias(clause.expr, aliases);
|
|
843
1106
|
});
|
|
844
1107
|
}
|
|
1108
|
+
/** Lower-cased set of the `as` aliases the SELECT list wrote (stars contribute none). */
|
|
1109
|
+
function selectListAliases(selectList) {
|
|
1110
|
+
const aliases = new Set();
|
|
1111
|
+
for (const entry of selectList) {
|
|
1112
|
+
if (entry.alias)
|
|
1113
|
+
aliases.add(entry.alias.toLowerCase());
|
|
1114
|
+
}
|
|
1115
|
+
return aliases;
|
|
1116
|
+
}
|
|
1117
|
+
/**
|
|
1118
|
+
* True when `expr` mentions any of `aliases` as an unqualified column reference.
|
|
1119
|
+
*
|
|
1120
|
+
* NOTE: `walkAstNodes` descends into subquery operands, so `order by (select … where x
|
|
1121
|
+
* = c)` counts as mentioning `c` even when that `c` is the subquery's own column. The
|
|
1122
|
+
* over-approximation only costs the pre-aggregate ordering extension on a one-row
|
|
1123
|
+
* result; a subquery-aware walk is not worth hand-rolling for it.
|
|
1124
|
+
*
|
|
1125
|
+
* `type: 'column'` is not unique to {@link AST.ColumnExpr}: a subquery's SELECT-list
|
|
1126
|
+
* {@link AST.ResultColumnExpr} carries the same tag with an `expr`/`alias` shape and no
|
|
1127
|
+
* `name`, and the reflective walk reaches those too. Match on the `name` string rather
|
|
1128
|
+
* than on the tag alone.
|
|
1129
|
+
*/
|
|
1130
|
+
function mentionsSelectListAlias(expr, aliases) {
|
|
1131
|
+
if (aliases.size === 0)
|
|
1132
|
+
return false;
|
|
1133
|
+
for (const node of walkAstNodes(expr)) {
|
|
1134
|
+
if (node.type !== 'column')
|
|
1135
|
+
continue;
|
|
1136
|
+
const col = node;
|
|
1137
|
+
if (typeof col.name !== 'string' || col.table)
|
|
1138
|
+
continue;
|
|
1139
|
+
if (aliases.has(col.name.toLowerCase()))
|
|
1140
|
+
return true;
|
|
1141
|
+
}
|
|
1142
|
+
return false;
|
|
1143
|
+
}
|
|
845
1144
|
/**
|
|
846
1145
|
* Given a list of aggregate function call AST nodes, builds aggregate plan nodes
|
|
847
1146
|
* for the entries that are not already present in `existingAggregates` (matched by
|
|
@@ -871,9 +1170,15 @@ function dedupeNewAggregates(funcExprs, selectContext, existingAggregates) {
|
|
|
871
1170
|
return newAggregates;
|
|
872
1171
|
}
|
|
873
1172
|
/**
|
|
874
|
-
* Builds final projections for the complete SELECT list in aggregate context
|
|
1173
|
+
* Builds final projections for the complete SELECT list in aggregate context.
|
|
1174
|
+
*
|
|
1175
|
+
* `groupedContext` is supplied whenever the query HAS a GROUP BY (it is `undefined`
|
|
1176
|
+
* for an aggregate query without one, which has no grouping keys to redirect onto).
|
|
1177
|
+
* It is what makes a select-list reference to a grouping key name the AggregateNode's
|
|
1178
|
+
* OWN group output column, whatever spelling the reference used — see
|
|
1179
|
+
* {@link redirectPostAggregate} and the note on the rebuild below.
|
|
875
1180
|
*/
|
|
876
|
-
export function buildFinalAggregateProjections(stmt, selectContext, aggregateOutputScope, aggregateNode, aggregates, groupByExpressions, starProjectionsByColumn = new Map()) {
|
|
1181
|
+
export function buildFinalAggregateProjections(stmt, selectContext, aggregateOutputScope, aggregateNode, aggregates, groupByExpressions, starProjectionsByColumn = new Map(), groupedContext) {
|
|
877
1182
|
const finalProjections = [];
|
|
878
1183
|
const aggregateAttributes = aggregateNode.getAttributes();
|
|
879
1184
|
// Build context with aggregates so buildFunctionCall can resolve aggregate references
|
|
@@ -902,7 +1207,11 @@ export function buildFinalAggregateProjections(stmt, selectContext, aggregateOut
|
|
|
902
1207
|
// The same index also maps bare source columns that are themselves group keys
|
|
903
1208
|
// straight to the aggregate's group output column, which is what expands
|
|
904
1209
|
// `SELECT *` in a grouped query in source-column order rather than GROUP BY order.
|
|
905
|
-
|
|
1210
|
+
//
|
|
1211
|
+
// A grouped query's `groupedContext` already carries this index; deriving it twice
|
|
1212
|
+
// for one query is how the two copies drift. Only an aggregate query with no GROUP BY
|
|
1213
|
+
// reaches the fallback, and its `groupByExpressions` is empty.
|
|
1214
|
+
const groupKeys = groupedContext?.groupKeys ?? indexGroupKeys(groupByExpressions);
|
|
906
1215
|
for (const column of stmt.columns) {
|
|
907
1216
|
if (column.type === 'all') {
|
|
908
1217
|
for (const starProj of starProjectionsByColumn.get(column) ?? []) {
|
|
@@ -944,7 +1253,22 @@ export function buildFinalAggregateProjections(stmt, selectContext, aggregateOut
|
|
|
944
1253
|
scope: aggregateOutputScope,
|
|
945
1254
|
aggregates: aggregatesContext
|
|
946
1255
|
};
|
|
947
|
-
const
|
|
1256
|
+
const builtNode = buildExpression(finalContext, column.expr, true);
|
|
1257
|
+
// `aggregateOutputScope` holds a grouping key only under the names the query
|
|
1258
|
+
// literally wrote, so every other legal spelling — a qualifier the GROUP BY did
|
|
1259
|
+
// not use (`select wg.a … group by a`), a FROM-alias qualifier, a key nested in
|
|
1260
|
+
// a bigger expression (`upper(wg.a)`) — falls through to the pre-aggregate
|
|
1261
|
+
// scope and binds to a BASE-TABLE attribute the grouped row does not carry.
|
|
1262
|
+
// Redirect each such subtree onto the aggregate's own group output column,
|
|
1263
|
+
// exactly as the window phase does for a window specification.
|
|
1264
|
+
//
|
|
1265
|
+
// For EVERY grouped query, not only windowed ones: reading the base attribute
|
|
1266
|
+
// off the group's representative source row is right only while this projection
|
|
1267
|
+
// is the aggregate's adjacent consumer, and a WindowNode buffering in between
|
|
1268
|
+
// removes that (docs/runtime.md § Corollary: a published source row reaches only
|
|
1269
|
+
// the adjacent consumer). One query shape must not bind two ways depending on
|
|
1270
|
+
// an unrelated clause.
|
|
1271
|
+
const scalarNode = redirectPostAggregate(builtNode, groupedContext, aggregateOutputScope);
|
|
948
1272
|
let attrId = undefined;
|
|
949
1273
|
if (CapabilityDetectors.isColumnReference(scalarNode)) {
|
|
950
1274
|
attrId = scalarNode.attributeId;
|