@quereus/quereus 3.3.0 → 4.1.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 +204 -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 +716 -27
- 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 +13 -0
- package/dist/src/func/registration.d.ts.map +1 -1
- package/dist/src/func/registration.js +5 -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 +20 -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 +17 -16
|
@@ -1,29 +1,55 @@
|
|
|
1
|
-
import { createTableToString, createViewToString, createIndexToString, createAssertionToString, columnDefToString, quoteIdentifier, expressionToString } from '../emit/ast-stringify.js';
|
|
1
|
+
import { createTableToString, createViewToString, createMaterializedViewToString, createIndexToString, createAssertionToString, columnDefToString, quoteIdentifier, expressionToString, tagsBodyToString, tableConstraintsToString, constraintBodyToCanonicalString, createIndexBodyToCanonicalString, indexedColumnBareName, viewDefinitionToCanonicalString, astToString } from '../emit/ast-stringify.js';
|
|
2
|
+
import { computeBodyHash, normalizeBackingModuleName, canonicalBackingModuleArgs } from './view.js';
|
|
2
3
|
import { QuereusError } from '../common/errors.js';
|
|
3
4
|
import { StatusCode } from '../common/types.js';
|
|
4
5
|
import { createLogger } from '../common/logger.js';
|
|
6
|
+
import { validateReservedTags } from './reserved-tags.js';
|
|
7
|
+
import { raiseReservedTagDiagnostics } from './reserved-tags-policy.js';
|
|
8
|
+
import { renameColumnInAst, renameColumnInCheckExpression, renameTableInAst } from './rename-rewriter.js';
|
|
9
|
+
import { cloneExpr, cloneQueryExpr } from '../planner/mutation/scope-transform.js';
|
|
10
|
+
import { normalizeCollationName } from '../util/comparison.js';
|
|
11
|
+
import { inferType } from '../types/registry.js';
|
|
12
|
+
import { resolveDefaultCollation } from './table.js';
|
|
5
13
|
const log = createLogger('schema:differ');
|
|
6
14
|
const warnLog = log.extend('warn');
|
|
7
15
|
/** Reserved tag namespace prefix used for differ-recognized hints. */
|
|
8
16
|
const QUEREUS_TAG_PREFIX = 'quereus.';
|
|
9
|
-
/** Recognized tag keys under the `quereus.*` namespace. */
|
|
10
|
-
const KNOWN_QUEREUS_KEYS = new Set(['quereus.id', 'quereus.previous_name']);
|
|
11
17
|
/**
|
|
12
18
|
* Computes the difference between declared schema and actual catalog
|
|
13
19
|
*/
|
|
14
|
-
export function computeSchemaDiff(declaredSchema, actualCatalog, policy = 'allow'
|
|
20
|
+
export function computeSchemaDiff(declaredSchema, actualCatalog, policy = 'allow',
|
|
21
|
+
/**
|
|
22
|
+
* Session `default_collation` used to resolve an omitted COLLATE on the *declared*
|
|
23
|
+
* side, matching how the CREATE path resolves it for a fresh `apply`. Defaults to
|
|
24
|
+
* `'BINARY'` so direct callers (and the existing test suite, which runs under the
|
|
25
|
+
* BINARY session default) keep byte-for-byte identical diffs. The emitters thread
|
|
26
|
+
* the live `default_collation` session option.
|
|
27
|
+
*/
|
|
28
|
+
defaultCollation = 'BINARY') {
|
|
15
29
|
const diff = {
|
|
16
30
|
tablesToCreate: [],
|
|
17
31
|
tablesToDrop: [],
|
|
18
32
|
tablesToAlter: [],
|
|
33
|
+
maintainedModuleMigrations: [],
|
|
19
34
|
viewsToCreate: [],
|
|
20
35
|
viewsToDrop: [],
|
|
21
36
|
indexesToCreate: [],
|
|
22
37
|
indexesToDrop: [],
|
|
23
38
|
assertionsToCreate: [],
|
|
24
39
|
assertionsToDrop: [],
|
|
40
|
+
viewTagsChanges: [],
|
|
41
|
+
indexTagsChanges: [],
|
|
25
42
|
renames: [],
|
|
43
|
+
lensToAttach: [],
|
|
44
|
+
lensToDetach: [],
|
|
26
45
|
};
|
|
46
|
+
// Logical schema: the per-table diff is attach/detach-lens, never
|
|
47
|
+
// create/drop-table. A logical schema's actual catalog views ARE its lens
|
|
48
|
+
// bodies (a logical schema has no user views), so compare declared logical
|
|
49
|
+
// tables against the registered views. Basis storage is untouched.
|
|
50
|
+
if (declaredSchema.isLogical) {
|
|
51
|
+
return computeLogicalSchemaDiff(declaredSchema, actualCatalog, diff);
|
|
52
|
+
}
|
|
27
53
|
const targetSchemaName = actualCatalog.schemaName;
|
|
28
54
|
// Extract schema-level default module settings
|
|
29
55
|
const defaultVtabModule = declaredSchema.using?.defaultVtabModule;
|
|
@@ -31,38 +57,111 @@ export function computeSchemaDiff(declaredSchema, actualCatalog, policy = 'allow
|
|
|
31
57
|
// Build maps of declared items
|
|
32
58
|
const declaredTables = new Map();
|
|
33
59
|
const declaredViews = new Map();
|
|
60
|
+
const declaredMaterializedViews = new Map();
|
|
34
61
|
const declaredIndexes = new Map();
|
|
35
62
|
const declaredAssertions = new Map();
|
|
63
|
+
// Reserved-tag shape/site validation flows through the SAME typed registry
|
|
64
|
+
// (`validateReservedTags`) as the lens-compile / mutation / advertisement
|
|
65
|
+
// paths — there is no second differ-local allow-list. Severity is unified:
|
|
66
|
+
// an unknown / mis-sited / malformed `quereus.*` key is a hard error here too
|
|
67
|
+
// (Decision 2 of the ticket), so a physical-schema typo fails `apply`/`diff`
|
|
68
|
+
// loudly instead of silently soft-warning. We accumulate every declared
|
|
69
|
+
// object's diagnostics across the whole schema, then raise once — BEFORE the
|
|
70
|
+
// throw-y rename resolution below, so a tag typo surfaces deterministically
|
|
71
|
+
// rather than being masked by a rename conflict. Sites per Decision 3:
|
|
72
|
+
// table → physical-table (also the basis-table/advertisement position),
|
|
73
|
+
// column → physical-column, view / materialized view → view-ddl,
|
|
74
|
+
// index → physical-index, table constraint (named or not) → physical-constraint.
|
|
75
|
+
// Assertions carry no `tags` field (no site). The rename hints
|
|
76
|
+
// (quereus.id / quereus.previous_name) are first-class specs valid at each of
|
|
77
|
+
// these physical sites; an MV's hint validates (over-permissive: the differ
|
|
78
|
+
// supports no MV rename and simply ignores it — harmless, see Decision 1).
|
|
79
|
+
const tagDiagnostics = [];
|
|
36
80
|
for (const item of declaredSchema.items) {
|
|
37
81
|
switch (item.type) {
|
|
38
82
|
case 'declaredTable':
|
|
39
83
|
declaredTables.set(item.tableStmt.table.name.toLowerCase(), item);
|
|
40
|
-
|
|
84
|
+
tagDiagnostics.push(...validateReservedTags(item.tableStmt.tags, 'physical-table'));
|
|
41
85
|
for (const col of item.tableStmt.columns) {
|
|
42
|
-
|
|
86
|
+
tagDiagnostics.push(...validateReservedTags(col.tags, 'physical-column'));
|
|
43
87
|
}
|
|
44
88
|
for (const c of item.tableStmt.constraints ?? []) {
|
|
45
|
-
|
|
46
|
-
|
|
89
|
+
// Validate every table constraint's tags, named or not. A table-level
|
|
90
|
+
// constraint consumes a trailing `WITH TAGS` unconditionally (the
|
|
91
|
+
// parser only defers it to the column for *unnamed inline column*
|
|
92
|
+
// constraints), so an unnamed table constraint CAN carry a reserved
|
|
93
|
+
// tag — gating validation on `c.name` would leave a typo there as a
|
|
94
|
+
// silent no-op, the exact escape the unified hard-error posture
|
|
95
|
+
// exists to close. Rename detection still keys off named constraints
|
|
96
|
+
// only; this is validation-only and harmless on unnamed ones.
|
|
97
|
+
tagDiagnostics.push(...validateReservedTags(c.tags, 'physical-constraint'));
|
|
98
|
+
}
|
|
99
|
+
// Inline *named* column constraints carry their own trailing `WITH TAGS`
|
|
100
|
+
// on `cc.tags` at the SAME physical-constraint site as a table-level
|
|
101
|
+
// constraint (the parser lifts a trailing tag onto the constraint only
|
|
102
|
+
// when it is named; an unnamed inline constraint defers its tags to the
|
|
103
|
+
// column, so `cc.tags` is undefined there and validateReservedTags returns
|
|
104
|
+
// [] — a harmless no-op, no `cc.name` guard needed). Validate regardless of
|
|
105
|
+
// constraint *kind* (validation is independent of the lifecycle lift that
|
|
106
|
+
// handles only check/unique/fk) so a typo'd or mis-sited reserved key on
|
|
107
|
+
// e.g. `qty integer constraint chk check (qty>0) with tags (...)` fails
|
|
108
|
+
// here too. Appended LAST (after the table-level constraint loop) so the
|
|
109
|
+
// accumulated diagnostic order is identical to the direct CREATE path:
|
|
110
|
+
// table → columns → table-constraints → column-constraints.
|
|
111
|
+
for (const col of item.tableStmt.columns) {
|
|
112
|
+
for (const cc of col.constraints ?? []) {
|
|
113
|
+
tagDiagnostics.push(...validateReservedTags(cc.tags, 'physical-constraint'));
|
|
114
|
+
}
|
|
47
115
|
}
|
|
48
116
|
break;
|
|
49
117
|
case 'declaredView':
|
|
50
118
|
declaredViews.set(item.viewStmt.view.name.toLowerCase(), item);
|
|
51
|
-
|
|
119
|
+
tagDiagnostics.push(...validateReservedTags(item.viewStmt.tags, 'view-ddl'));
|
|
120
|
+
break;
|
|
121
|
+
case 'declaredMaterializedView':
|
|
122
|
+
declaredMaterializedViews.set(item.viewStmt.view.name.toLowerCase(), item);
|
|
123
|
+
tagDiagnostics.push(...validateReservedTags(item.viewStmt.tags, 'view-ddl'));
|
|
52
124
|
break;
|
|
53
125
|
case 'declaredIndex':
|
|
54
126
|
declaredIndexes.set(item.indexStmt.index.name.toLowerCase(), item);
|
|
55
|
-
|
|
127
|
+
tagDiagnostics.push(...validateReservedTags(item.indexStmt.tags, 'physical-index'));
|
|
56
128
|
break;
|
|
57
129
|
case 'declaredAssertion':
|
|
58
130
|
declaredAssertions.set(item.assertionStmt.name.toLowerCase(), item);
|
|
59
131
|
break;
|
|
60
132
|
}
|
|
61
133
|
}
|
|
134
|
+
raiseReservedTagDiagnostics(tagDiagnostics, {
|
|
135
|
+
log: (d) => warnLog('reserved tag advisory (%s) on %s: %s', d.reason, d.site, d.message),
|
|
136
|
+
});
|
|
137
|
+
// Normalize every declared `materialized view` into the TABLE category: a
|
|
138
|
+
// declared table whose `maintained` clause carries the body (no declared
|
|
139
|
+
// columns — the body owns the shape). This is what dissolves the standalone MV
|
|
140
|
+
// comparison: a maintained table is now compared per name against a declared
|
|
141
|
+
// table or maintained clause in ONE category, so a table↔maintained transition
|
|
142
|
+
// becomes an attach/detach alter, never a cross-category drop+create. The
|
|
143
|
+
// original `declaredMaterializedViews` map is retained for the CREATE branch
|
|
144
|
+
// (a fresh maintained table renders the `create materialized view` sugar, since
|
|
145
|
+
// a column-less `create table … maintained as` has no parseable DDL).
|
|
146
|
+
for (const [name, declaredMv] of declaredMaterializedViews) {
|
|
147
|
+
if (declaredTables.has(name)) {
|
|
148
|
+
throw new QuereusError(`'${name}' is declared as both a table and a materialized view`, StatusCode.ERROR);
|
|
149
|
+
}
|
|
150
|
+
declaredTables.set(name, materializedViewToDeclaredTable(declaredMv));
|
|
151
|
+
}
|
|
62
152
|
// Build maps of actual items
|
|
63
153
|
const actualTables = new Map(actualCatalog.tables.map(t => [t.name.toLowerCase(), t]));
|
|
64
154
|
const actualViews = new Map(actualCatalog.views.map(v => [v.name.toLowerCase(), v]));
|
|
65
|
-
|
|
155
|
+
// Exclude *exposed implicit covering indexes* (`CatalogIndex.implicit` — the
|
|
156
|
+
// secondary BTree backing a UNIQUE constraint tagged
|
|
157
|
+
// `quereus.expose_implicit_index`). The catalog surfaces them for introspection
|
|
158
|
+
// (`schema()` / `index_info()`), but their lifecycle belongs to the originating
|
|
159
|
+
// UNIQUE constraint (the named-constraint diff path), NOT to `CREATE/DROP INDEX`.
|
|
160
|
+
// Filtering here keeps them out of ALL three downstream index consumers in one
|
|
161
|
+
// place — rename resolution, the create/body loop, and the orphan-drop loop — so a
|
|
162
|
+
// converged schema with an exposed implicit index diffs empty (no phantom
|
|
163
|
+
// `DROP INDEX IF EXISTS`). See `catalog.ts` `CatalogIndex.implicit`.
|
|
164
|
+
const actualIndexes = new Map(actualCatalog.indexes.filter(i => !i.implicit).map(i => [i.name.toLowerCase(), i]));
|
|
66
165
|
// Resolve renames per-kind. Each call returns:
|
|
67
166
|
// - rename ops (oldName -> newName)
|
|
68
167
|
// - matched pairs (declaredKey -> actual): for the alter-diff loop later
|
|
@@ -101,13 +200,99 @@ export function computeSchemaDiff(declaredSchema, actualCatalog, policy = 'allow
|
|
|
101
200
|
policy,
|
|
102
201
|
});
|
|
103
202
|
diff.renames.push(...indexRenames.renames);
|
|
104
|
-
//
|
|
203
|
+
// Pre-pass: resolve every name-matched declared table's column renames, keyed by
|
|
204
|
+
// declared (new) table name (lowercased). This gives the per-table alter loop
|
|
205
|
+
// cross-table visibility of a *parent* table's column renames, so the FK branch
|
|
206
|
+
// of `reconciledDeclaredBody` can inverse-rename an FK's referenced PARENT column
|
|
207
|
+
// (its `foreignKey.table` carries the parent's declared name at diff time — the
|
|
208
|
+
// same lookup key). A self-referential FK falls out for free: the parent is the
|
|
209
|
+
// current table, so `map.get(currentTable)` matches `diff.columnsToRename`. Pure
|
|
210
|
+
// creates (no matched actual) contribute nothing. NOTE: this re-resolves the
|
|
211
|
+
// current table's renames a second time (once here, once in its own
|
|
212
|
+
// `computeTableAlterDiff`); see `resolveColumnRenames` for why that's accepted.
|
|
213
|
+
const columnRenamesByTable = new Map();
|
|
214
|
+
for (const [name, declaredTable] of declaredTables) {
|
|
215
|
+
const matchedActual = tableRenames.pairs.get(name);
|
|
216
|
+
if (!matchedActual)
|
|
217
|
+
continue;
|
|
218
|
+
const renames = resolveColumnRenames(declaredTable, matchedActual, policy).renames;
|
|
219
|
+
if (renames.length > 0) {
|
|
220
|
+
columnRenamesByTable.set(name, renames.map(r => ({ oldName: r.oldName, newName: r.newName })));
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
// Declared-side column-existence resolver for the scope-aware seeded column
|
|
224
|
+
// rewrites in the reconcilers (the optional `ResolveColumnInSource` arg of
|
|
225
|
+
// `renameColumnInCheckExpression`). The forward rename propagation passes a
|
|
226
|
+
// live schemaManager lookup (see `rewriteTableForColumnRename` in
|
|
227
|
+
// runtime/emit/alter-table.ts); the diff side has no live catalog for the
|
|
228
|
+
// post-rename world, so it answers from the DECLARED column sets instead:
|
|
229
|
+
// the inverse walk's match target (`oldCol`) is the rename's NEW column
|
|
230
|
+
// name, and the question being answered is "in the declared world, does
|
|
231
|
+
// this inner FROM source expose that name (so the unqualified ref binds
|
|
232
|
+
// there, not to the owning seed)?". The walk's `realSources` carry OLD
|
|
233
|
+
// table names when the inverse table-rename pass has pre-normalized
|
|
234
|
+
// qualifiers (and DECLARED names when it hasn't — `columnReconciledViewStmt`),
|
|
235
|
+
// hence the old→new table-name mapping before the declared lookup: an
|
|
236
|
+
// already-declared name simply misses the rename find and passes through.
|
|
237
|
+
// Cross-schema sources answer false (the catalog is single-schema) —
|
|
238
|
+
// conservative where the forward path's live lookup could say yes; worst
|
|
239
|
+
// case a benign drop+recreate.
|
|
240
|
+
const targetSchemaLower = targetSchemaName.toLowerCase();
|
|
241
|
+
const resolveDeclaredColumn = (schema, table, column) => {
|
|
242
|
+
if (schema !== targetSchemaLower)
|
|
243
|
+
return false;
|
|
244
|
+
const declaredName = tableRenames.renames.find(r => r.oldName.toLowerCase() === table)?.newName.toLowerCase() ?? table;
|
|
245
|
+
const dt = declaredTables.get(declaredName);
|
|
246
|
+
return dt?.tableStmt.columns.some(c => c.name.toLowerCase() === column) ?? false;
|
|
247
|
+
};
|
|
248
|
+
// Tables: creates / alters. `dropSet` accumulates the destructive
|
|
249
|
+
// backing-module moves here (the live incarnation must be dropped) AND the
|
|
250
|
+
// orphan drops below — both flow through `orderDropsByFKDependency` together.
|
|
251
|
+
// `maintainedModuleRecreates` counts those module-move drop+create pairs so the
|
|
252
|
+
// require-hint guard can exclude them (a deliberate recreate of a matched
|
|
253
|
+
// object, not an ambiguous unhinted rename — mirroring viewRecreates /
|
|
254
|
+
// indexRecreates).
|
|
255
|
+
const dropSet = new Set();
|
|
256
|
+
let maintainedModuleRecreates = 0;
|
|
105
257
|
for (const [name, declaredTable] of declaredTables) {
|
|
106
258
|
const tableStmt = declaredTable.tableStmt;
|
|
107
259
|
const matchedActual = tableRenames.pairs.get(name);
|
|
108
260
|
if (matchedActual) {
|
|
109
261
|
// Either a rename match or a name-based match — compute alter diff against the matched actual.
|
|
110
|
-
|
|
262
|
+
// Thread the table renames (all `kind: 'table'` from this resolver), the
|
|
263
|
+
// schema name, and the cross-table column-rename map so the constraint body
|
|
264
|
+
// comparison can reconcile a renamed local column / FK-parent-table /
|
|
265
|
+
// FK-referenced-parent-column against the actual (pre-rename) catalog body.
|
|
266
|
+
const alterDiff = computeTableAlterDiff(declaredTable, matchedActual, policy, tableRenames.renames, targetSchemaName, columnRenamesByTable, resolveDeclaredColumn, defaultCollation);
|
|
267
|
+
if (alterDiff.maintainedModuleMigration) {
|
|
268
|
+
// Destructive backing-module move: drop the live incarnation (via the
|
|
269
|
+
// shared drop ordering) and recreate into the newly declared module
|
|
270
|
+
// (re-materializing the body). The recreate subsumes any concurrent body /
|
|
271
|
+
// tag / shape op, so the alter diff is SUPPRESSED — no `tablesToAlter`
|
|
272
|
+
// entry for this table. Gated at apply (allow_destructive); surfaced
|
|
273
|
+
// unconditionally by `diff schema`.
|
|
274
|
+
//
|
|
275
|
+
// Rename-coincident case: when the same apply BOTH renames this
|
|
276
|
+
// maintained table (hinted match, `matchedActual.name !== name`) AND moves
|
|
277
|
+
// its backing module, the table RENAME op is preserved in `diff.renames`
|
|
278
|
+
// (dependents over the old name retarget via the ALTER … RENAME primitive —
|
|
279
|
+
// see reconciledDeclaredViewDefinition). At apply that rename runs FIRST,
|
|
280
|
+
// moving the live incarnation to the NEW declared name, so the drop must
|
|
281
|
+
// target the NEW name `name` — dropping the old `matchedActual.name` would
|
|
282
|
+
// no-op (it was just renamed away) and the recreate, which renders under
|
|
283
|
+
// `name`, would then collide ("already exists"). For a plain name match the
|
|
284
|
+
// two names are identical.
|
|
285
|
+
const dropName = matchedActual.name.toLowerCase() !== name ? name : matchedActual.name.toLowerCase();
|
|
286
|
+
dropSet.add(dropName);
|
|
287
|
+
diff.tablesToCreate.push(renderFreshTableCreate(name, tableStmt, declaredMaterializedViews, targetSchemaName, defaultVtabModule, defaultVtabArgs));
|
|
288
|
+
diff.maintainedModuleMigrations.push({
|
|
289
|
+
name: tableStmt.table.name,
|
|
290
|
+
fromModule: alterDiff.maintainedModuleMigration.fromModule,
|
|
291
|
+
toModule: alterDiff.maintainedModuleMigration.toModule,
|
|
292
|
+
});
|
|
293
|
+
maintainedModuleRecreates++;
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
111
296
|
// If this was a rename, set the alter target to the new name (post-rename)
|
|
112
297
|
if (matchedActual.name.toLowerCase() !== name) {
|
|
113
298
|
alterDiff.tableName = tableStmt.table.name;
|
|
@@ -117,17 +302,22 @@ export function computeSchemaDiff(declaredSchema, actualCatalog, policy = 'allow
|
|
|
117
302
|
|| alterDiff.columnsToAlter.length > 0
|
|
118
303
|
|| alterDiff.columnsToRename.length > 0
|
|
119
304
|
|| (alterDiff.constraintsToRename?.length ?? 0) > 0
|
|
120
|
-
|| alterDiff.
|
|
305
|
+
|| (alterDiff.constraintsToDrop?.length ?? 0) > 0
|
|
306
|
+
|| (alterDiff.constraintsToAdd?.length ?? 0) > 0
|
|
307
|
+
|| alterDiff.primaryKeyChange
|
|
308
|
+
|| alterDiff.tableTagsChange !== undefined
|
|
309
|
+
|| (alterDiff.constraintTagsChanges?.length ?? 0) > 0
|
|
310
|
+
|| alterDiff.setMaintained !== undefined
|
|
311
|
+
|| alterDiff.dropMaintained === true) {
|
|
121
312
|
diff.tablesToAlter.push(alterDiff);
|
|
122
313
|
}
|
|
123
314
|
}
|
|
124
315
|
else {
|
|
125
|
-
|
|
126
|
-
diff.tablesToCreate.push(createTableToString(effectiveStmt));
|
|
316
|
+
diff.tablesToCreate.push(renderFreshTableCreate(name, tableStmt, declaredMaterializedViews, targetSchemaName, defaultVtabModule, defaultVtabArgs));
|
|
127
317
|
}
|
|
128
318
|
}
|
|
129
|
-
// Tables: drops (skip those consumed by a rename)
|
|
130
|
-
|
|
319
|
+
// Tables: drops (skip those consumed by a rename). The module-move drops added
|
|
320
|
+
// above stay in `dropSet`; the orphan drops join them here.
|
|
131
321
|
for (const [name] of actualTables) {
|
|
132
322
|
if (tableRenames.consumedActuals.has(name))
|
|
133
323
|
continue;
|
|
@@ -135,10 +325,64 @@ export function computeSchemaDiff(declaredSchema, actualCatalog, policy = 'allow
|
|
|
135
325
|
dropSet.add(name);
|
|
136
326
|
}
|
|
137
327
|
diff.tablesToDrop = orderDropsByFKDependency(dropSet, actualTables);
|
|
138
|
-
// Views: creates / drops
|
|
328
|
+
// Views: creates / drops / definition-change recreates / hinted-rename
|
|
329
|
+
// recreates / in-place tag changes.
|
|
330
|
+
// A matched view (name- OR rename-matched) whose canonical definition drifted
|
|
331
|
+
// (explicit column list, body, or the `insert defaults` clause — see
|
|
332
|
+
// `viewDefinitionToCanonicalString`) drops+recreates: a plain view is
|
|
333
|
+
// data-less, so the recreate is free, and it carries the declared tags — so a
|
|
334
|
+
// definition change SUPPRESSES any separate SET TAGS (the same mutual
|
|
335
|
+
// exclusion the MV/index paths use). The declared definition is compared raw
|
|
336
|
+
// first; on mismatch a rename-RECONCILED render (every in-diff table/column
|
|
337
|
+
// rename inverse-applied NEW→OLD — see `reconciledDeclaredViewDefinition`)
|
|
338
|
+
// re-compares, so a dependent view over a source renamed in this same diff
|
|
339
|
+
// does not churn a spurious recreate. That reconciliation is
|
|
340
|
+
// correctness-critical, not just churn: `generateMigrationDDL` emits view
|
|
341
|
+
// creates BEFORE the table-alter block where RENAME COLUMN lives, and CREATE
|
|
342
|
+
// VIEW plans its body at create time — an unreconciled recreate naming the
|
|
343
|
+
// NEW column would fail at apply. A RENAME-matched view (hinted via
|
|
344
|
+
// quereus.id / quereus.previous_name) resolves to drop(actual old name) +
|
|
345
|
+
// create(declared new name) whether or not its definition changed — its
|
|
346
|
+
// `kind: 'view'` rename op is metadata only (no ALTER VIEW … RENAME TO
|
|
347
|
+
// primitive), so the convergence DDL must come from these buckets. A
|
|
348
|
+
// definition-UNCHANGED hinted rename renders the recreate with the in-diff
|
|
349
|
+
// COLUMN renames inverse-applied while keeping declared TABLE names (table
|
|
350
|
+
// renames run before creates; column renames run after — see
|
|
351
|
+
// `columnReconciledViewStmt`); the recreate carries the declared tags, so a
|
|
352
|
+
// rename + tag drift converges through it and the in-place SET TAGS branch
|
|
353
|
+
// below never double-emits (renames `continue` past it). A pure name match
|
|
354
|
+
// whose definition is unchanged but tags drifted still takes the in-place
|
|
355
|
+
// `ALTER VIEW … SET TAGS`.
|
|
356
|
+
let viewRecreates = 0; // deliberate drop+create pairs, excluded from the require-hint counts
|
|
139
357
|
for (const [name, declaredView] of declaredViews) {
|
|
140
|
-
|
|
358
|
+
const matchedActual = viewRenames.pairs.get(name);
|
|
359
|
+
if (!matchedActual) {
|
|
141
360
|
diff.viewsToCreate.push(createViewToString(declaredView.viewStmt));
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
const stmt = declaredView.viewStmt;
|
|
364
|
+
// The body string carries any trailing `with defaults (…)` clause, so the
|
|
365
|
+
// canonical definition (and its rename-reconciled form) covers defaults drift.
|
|
366
|
+
let definitionDrifted = viewDefinitionToCanonicalString(stmt.columns, stmt.select) !== matchedActual.definition;
|
|
367
|
+
if (definitionDrifted && (tableRenames.renames.length > 0 || columnRenamesByTable.size > 0)) {
|
|
368
|
+
definitionDrifted = reconciledDeclaredViewDefinition(stmt.columns, stmt.select, tableRenames.renames, columnRenamesByTable, targetSchemaName, resolveDeclaredColumn) !== matchedActual.definition;
|
|
369
|
+
}
|
|
370
|
+
if (definitionDrifted) {
|
|
371
|
+
diff.viewsToDrop.push(matchedActual.name);
|
|
372
|
+
diff.viewsToCreate.push(createViewToString(stmt));
|
|
373
|
+
viewRecreates++;
|
|
374
|
+
continue;
|
|
375
|
+
}
|
|
376
|
+
if (matchedActual.name.toLowerCase() !== name) {
|
|
377
|
+
// Hinted rename, definition unchanged → drop(old) + recreate(declared),
|
|
378
|
+
// rendered with in-diff column renames inverse-applied (NEW→OLD).
|
|
379
|
+
diff.viewsToDrop.push(matchedActual.name);
|
|
380
|
+
diff.viewsToCreate.push(createViewToString(columnReconciledViewStmt(stmt, columnRenamesByTable, targetSchemaName, resolveDeclaredColumn)));
|
|
381
|
+
viewRecreates++;
|
|
382
|
+
continue;
|
|
383
|
+
}
|
|
384
|
+
if (tagsDrifted(stmt.tags, matchedActual.tags)) {
|
|
385
|
+
diff.viewTagsChanges.push({ name: stmt.view.name, tags: desiredTagSet(stmt.tags) });
|
|
142
386
|
}
|
|
143
387
|
}
|
|
144
388
|
for (const [name] of actualViews) {
|
|
@@ -147,11 +391,81 @@ export function computeSchemaDiff(declaredSchema, actualCatalog, policy = 'allow
|
|
|
147
391
|
if (!declaredViews.has(name))
|
|
148
392
|
diff.viewsToDrop.push(name);
|
|
149
393
|
}
|
|
150
|
-
//
|
|
394
|
+
// (Maintained tables are no longer compared here. They are normalized into the
|
|
395
|
+
// table category above, so the unified table loop recognizes a body change as a
|
|
396
|
+
// re-attach (`set maintained as`), a table↔maintained transition as an
|
|
397
|
+
// attach/detach, and an undeclared live maintained table as a `drop table` —
|
|
398
|
+
// see `computeTableAlterDiff` / `applyMaintainedTransition`. A backing-module
|
|
399
|
+
// change on a both-maintained name-match IS detected (see `backingModuleDrifted`)
|
|
400
|
+
// and routed to a destructive drop+recreate in the table loop above —
|
|
401
|
+
// incarnation-minting, so gated at apply on `allow_destructive`.)
|
|
402
|
+
// Indexes: creates / drops / body-change recreates / hinted-rename recreates /
|
|
403
|
+
// in-place tag changes.
|
|
404
|
+
// A name-matched index whose canonical body drifted (UNIQUE-ness, column
|
|
405
|
+
// set/order/direction, partial WHERE) drops+recreates — the same drop+recreate
|
|
406
|
+
// shape MVs use, since an index has no in-place "redefine" primitive. The
|
|
407
|
+
// recreate carries the declared tags, so a body change SUPPRESSES any separate
|
|
408
|
+
// SET TAGS for that index (mutually exclusive per object, mirroring the MV
|
|
409
|
+
// precedence above). A RENAME-matched index (hinted) resolves to drop(actual
|
|
410
|
+
// old name) + create(declared new name) whether or not its body changed — the
|
|
411
|
+
// `kind: 'index'` rename op is metadata only (no ALTER INDEX … RENAME TO
|
|
412
|
+
// primitive), so the convergence DDL must come from these buckets; the
|
|
413
|
+
// rebuild cost on a pure rename is the documented tradeoff. A body-UNCHANGED
|
|
414
|
+
// hinted rename renders the recreate with the in-diff COLUMN renames
|
|
415
|
+
// inverse-applied while keeping declared TABLE names (see
|
|
416
|
+
// `columnReconciledIndexStmt`); the recreate carries the declared tags, so the
|
|
417
|
+
// in-place SET TAGS branch below never double-emits (renames `continue` past
|
|
418
|
+
// it). A pure name match whose body is unchanged but tags drifted still takes
|
|
419
|
+
// the in-place `ALTER INDEX … SET TAGS`.
|
|
420
|
+
let indexRecreates = 0; // deliberate drop+create pairs, excluded from the require-hint counts
|
|
151
421
|
for (const [name, declaredIndex] of declaredIndexes) {
|
|
152
|
-
|
|
422
|
+
const matchedActual = indexRenames.pairs.get(name);
|
|
423
|
+
if (!matchedActual) {
|
|
424
|
+
const effectiveStmt = applyIndexDefaults(declaredIndex.indexStmt, targetSchemaName);
|
|
425
|
+
diff.indexesToCreate.push(createIndexToString(effectiveStmt));
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
// Body comparison (canonical: name / tags excluded; per-column collation
|
|
429
|
+
// included via both-sides pre-resolution). Schema qualification does not affect
|
|
430
|
+
// the body render, so compare the raw declared stmt. The declared side resolves
|
|
431
|
+
// each column's effective collation against the matched declared table (so an
|
|
432
|
+
// inherited/BINARY collation that is unchanged does not churn, while a real
|
|
433
|
+
// collation change recreates), then inverse-applies the index table's in-diff
|
|
434
|
+
// column renames so a same-named index over a column renamed in this same diff
|
|
435
|
+
// matches the actual (pre-rename) body instead of churning a spurious
|
|
436
|
+
// drop+recreate (the column rename rides the table-alter channel). The rename
|
|
437
|
+
// lookup is keyed by the index's *declared* (post-rename) table name — exactly
|
|
438
|
+
// how `columnRenamesByTable` is keyed — so a table renamed in the same diff still
|
|
439
|
+
// resolves its column renames. ALL in-diff table renames are threaded too, so
|
|
440
|
+
// both a table-qualified self-reference and a cross-table reference in the
|
|
441
|
+
// partial WHERE predicate reconcile alongside the column renames (the own-table
|
|
442
|
+
// rename's remaining special role — seeding the column rewrites — is resolved
|
|
443
|
+
// inside declaredIndexCanonicalBody). The drop targets the actual
|
|
444
|
+
// (pre-rename) name and the recreate carries the declared (post-rename) name, so
|
|
445
|
+
// a genuine body change on a rename-matched index resolves to a correct
|
|
446
|
+
// drop+recreate that supersedes the no-op rename op.
|
|
447
|
+
const declaredTableForIndex = declaredTables.get(declaredIndex.indexStmt.table.name.toLowerCase());
|
|
448
|
+
const indexColRenames = columnRenamesByTable.get(declaredIndex.indexStmt.table.name.toLowerCase()) ?? [];
|
|
449
|
+
const declaredBody = declaredIndexCanonicalBody(declaredIndex.indexStmt, declaredTableForIndex, indexColRenames, tableRenames.renames, targetSchemaName, defaultCollation);
|
|
450
|
+
if (declaredBody !== matchedActual.definition) {
|
|
451
|
+
diff.indexesToDrop.push(matchedActual.name);
|
|
153
452
|
const effectiveStmt = applyIndexDefaults(declaredIndex.indexStmt, targetSchemaName);
|
|
154
453
|
diff.indexesToCreate.push(createIndexToString(effectiveStmt));
|
|
454
|
+
indexRecreates++;
|
|
455
|
+
continue;
|
|
456
|
+
}
|
|
457
|
+
if (matchedActual.name.toLowerCase() !== name) {
|
|
458
|
+
// Hinted rename, body unchanged → drop(old) + recreate(declared),
|
|
459
|
+
// rendered with in-diff column renames inverse-applied (NEW→OLD).
|
|
460
|
+
diff.indexesToDrop.push(matchedActual.name);
|
|
461
|
+
const reconciledStmt = columnReconciledIndexStmt(declaredIndex.indexStmt, indexColRenames, columnRenamesByTable, targetSchemaName);
|
|
462
|
+
diff.indexesToCreate.push(createIndexToString(applyIndexDefaults(reconciledStmt, targetSchemaName)));
|
|
463
|
+
indexRecreates++;
|
|
464
|
+
continue;
|
|
465
|
+
}
|
|
466
|
+
// Body unchanged → in-place tag change (pure name match only — renames `continue` above).
|
|
467
|
+
if (tagsDrifted(declaredIndex.indexStmt.tags, matchedActual.tags)) {
|
|
468
|
+
diff.indexTagsChanges.push({ name: declaredIndex.indexStmt.index.name, tags: desiredTagSet(declaredIndex.indexStmt.tags) });
|
|
155
469
|
}
|
|
156
470
|
}
|
|
157
471
|
for (const [name] of actualIndexes) {
|
|
@@ -161,11 +475,17 @@ export function computeSchemaDiff(declaredSchema, actualCatalog, policy = 'allow
|
|
|
161
475
|
diff.indexesToDrop.push(name);
|
|
162
476
|
}
|
|
163
477
|
// Apply 'require-hint' policy: any unhinted name change is an error rather
|
|
164
|
-
// than a silent drop+create.
|
|
478
|
+
// than a silent drop+create. A body-change OR hinted-rename recreate counts as
|
|
479
|
+
// both a create and a drop, which would falsely trip the unhinted-rename guard
|
|
480
|
+
// — so exclude those from the view/index counts (the constraint path does the
|
|
481
|
+
// same with its pure counts): both are deliberate drop+create pairs of a
|
|
482
|
+
// matched object, not an ambiguous unhinted rename. A backing-module move is the
|
|
483
|
+
// table-side analogue (matched-object recreate, not a rename), so subtract
|
|
484
|
+
// `maintainedModuleRecreates` from both table counts.
|
|
165
485
|
if (policy === 'require-hint') {
|
|
166
|
-
enforceRequireHint('table', diff.tablesToCreate.length, diff.tablesToDrop.length);
|
|
167
|
-
enforceRequireHint('view', diff.viewsToCreate.length, diff.viewsToDrop.length);
|
|
168
|
-
enforceRequireHint('index', diff.indexesToCreate.length, diff.indexesToDrop.length);
|
|
486
|
+
enforceRequireHint('table', diff.tablesToCreate.length - maintainedModuleRecreates, diff.tablesToDrop.length - maintainedModuleRecreates);
|
|
487
|
+
enforceRequireHint('view', diff.viewsToCreate.length - viewRecreates, diff.viewsToDrop.length - viewRecreates);
|
|
488
|
+
enforceRequireHint('index', diff.indexesToCreate.length - indexRecreates, diff.indexesToDrop.length - indexRecreates);
|
|
169
489
|
}
|
|
170
490
|
// Assertions (no rename support — names are explicitly part of the contract).
|
|
171
491
|
const actualAssertions = new Map(actualCatalog.assertions.map(a => [a.name.toLowerCase(), a]));
|
|
@@ -181,6 +501,36 @@ export function computeSchemaDiff(declaredSchema, actualCatalog, policy = 'allow
|
|
|
181
501
|
}
|
|
182
502
|
return diff;
|
|
183
503
|
}
|
|
504
|
+
/**
|
|
505
|
+
* Computes the diff for a **logical** declared schema. The per-table unit is
|
|
506
|
+
* attach/detach-lens, not create/drop-table:
|
|
507
|
+
* - a declared logical table not currently registered → attach,
|
|
508
|
+
* - a registered lens body absent from the declaration → detach.
|
|
509
|
+
*
|
|
510
|
+
* Crucially, this never populates `tablesToDrop` — a logical removal detaches
|
|
511
|
+
* the lens and leaves basis storage intact (see `docs/lens.md` § Deployment).
|
|
512
|
+
* Physical buckets stay empty; `generateMigrationDDL` emits nothing for a
|
|
513
|
+
* logical diff (attach/detach happens in the lens compiler at apply time).
|
|
514
|
+
*/
|
|
515
|
+
function computeLogicalSchemaDiff(declaredSchema, actualCatalog, diff) {
|
|
516
|
+
const declaredLogical = new Set();
|
|
517
|
+
for (const item of declaredSchema.items) {
|
|
518
|
+
if (item.type === 'declaredTable') {
|
|
519
|
+
declaredLogical.add(item.tableStmt.table.name.toLowerCase());
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
// In a logical schema, registered views are exclusively lens bodies.
|
|
523
|
+
const actualLens = new Set(actualCatalog.views.map(v => v.name.toLowerCase()));
|
|
524
|
+
for (const name of declaredLogical) {
|
|
525
|
+
if (!actualLens.has(name))
|
|
526
|
+
diff.lensToAttach.push(name);
|
|
527
|
+
}
|
|
528
|
+
for (const name of actualLens) {
|
|
529
|
+
if (!declaredLogical.has(name))
|
|
530
|
+
diff.lensToDetach.push(name);
|
|
531
|
+
}
|
|
532
|
+
return diff;
|
|
533
|
+
}
|
|
184
534
|
/**
|
|
185
535
|
* Generic resolver: pair declared and actual objects by name first, then by
|
|
186
536
|
* `quereus.id` and `quereus.previous_name` tag hints to detect renames.
|
|
@@ -287,26 +637,28 @@ function readQuereusHint(tags, key) {
|
|
|
287
637
|
const trimmed = v.trim();
|
|
288
638
|
return trimmed.length > 0 ? trimmed : undefined;
|
|
289
639
|
}
|
|
290
|
-
/**
|
|
291
|
-
* Soft-warn on unrecognized `quereus.*` tag keys so future versions can add
|
|
292
|
-
* keys without breaking older parsers.
|
|
293
|
-
*/
|
|
294
|
-
function warnUnknownQuereusKeys(tags, subject, subjectName) {
|
|
295
|
-
if (!tags)
|
|
296
|
-
return;
|
|
297
|
-
for (const key of Object.keys(tags)) {
|
|
298
|
-
if (!key.startsWith(QUEREUS_TAG_PREFIX))
|
|
299
|
-
continue;
|
|
300
|
-
if (KNOWN_QUEREUS_KEYS.has(key))
|
|
301
|
-
continue;
|
|
302
|
-
warnLog('Unknown reserved tag key %s on %s %s', key, subject, subjectName);
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
640
|
function enforceRequireHint(kind, creates, drops) {
|
|
306
641
|
if (creates > 0 && drops > 0) {
|
|
307
642
|
throw new QuereusError(`rename_policy = 'require-hint': ${kind} drops and creates both present (${drops} drop / ${creates} create); add 'quereus.previous_name' or 'quereus.id' to hint renames, or use 'allow' / 'deny'.`, StatusCode.ERROR);
|
|
308
643
|
}
|
|
309
644
|
}
|
|
645
|
+
/**
|
|
646
|
+
* Renders the fresh-create DDL for a declared table — shared by the create branch
|
|
647
|
+
* and the destructive backing-module move's recreate, so both re-materialize a
|
|
648
|
+
* maintained body into the declared module through one renderer. A maintained
|
|
649
|
+
* table declared via the `materialized view` sugar renders that sugar (its
|
|
650
|
+
* column-less normalized table form has no parseable `create table` DDL); a
|
|
651
|
+
* declared-shape maintained table and a plain table render the `create table …`
|
|
652
|
+
* form (carrying any `maintained as` clause).
|
|
653
|
+
*/
|
|
654
|
+
function renderFreshTableCreate(name, tableStmt, declaredMaterializedViews, targetSchemaName, defaultVtabModule, defaultVtabArgs) {
|
|
655
|
+
const declaredMv = declaredMaterializedViews.get(name);
|
|
656
|
+
if (declaredMv) {
|
|
657
|
+
return createMaterializedViewToString(declaredMv.viewStmt);
|
|
658
|
+
}
|
|
659
|
+
const effectiveStmt = applyTableDefaults(tableStmt, targetSchemaName, defaultVtabModule, defaultVtabArgs);
|
|
660
|
+
return createTableToString(effectiveStmt);
|
|
661
|
+
}
|
|
310
662
|
/**
|
|
311
663
|
* Applies schema-level defaults (schema name, default vtab module) to a table statement
|
|
312
664
|
*/
|
|
@@ -371,17 +723,541 @@ function applyIndexDefaults(indexStmt, targetSchemaName) {
|
|
|
371
723
|
}
|
|
372
724
|
return result;
|
|
373
725
|
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
726
|
+
/**
|
|
727
|
+
* Reads an index column's EXPLICIT per-column collation as-written, covering both
|
|
728
|
+
* indexed-column forms: the plain `col.collation` and the parser's collate-folded
|
|
729
|
+
* form (`col COLLATE x`, whose collation lives on `col.expr.collation`). Returns
|
|
730
|
+
* undefined when the column carries no explicit COLLATE — it then inherits the
|
|
731
|
+
* table column's collation (see {@link declaredColumnCollation}).
|
|
732
|
+
*/
|
|
733
|
+
function explicitIndexColumnCollation(col) {
|
|
734
|
+
if (col.collation)
|
|
735
|
+
return col.collation;
|
|
736
|
+
if (col.expr?.type === 'collate')
|
|
737
|
+
return col.expr.collation;
|
|
738
|
+
return undefined;
|
|
739
|
+
}
|
|
740
|
+
/**
|
|
741
|
+
* Reads a declared table column's effective collation from its `collate` column
|
|
742
|
+
* constraint, normalized (uppercase), defaulting to `'BINARY'`. Mirrors the
|
|
743
|
+
* engine's resolution of a table column's collation ({@link extractDeclaredCollation}).
|
|
744
|
+
* Returns `'BINARY'` when no declared table is supplied (an index on a non-declared
|
|
745
|
+
* table — not a coherent name-match) or the column is not found in it.
|
|
746
|
+
*/
|
|
747
|
+
function declaredColumnCollation(declaredTable, columnName, defaultCollation) {
|
|
748
|
+
if (!declaredTable)
|
|
749
|
+
return 'BINARY';
|
|
750
|
+
const lower = columnName.toLowerCase();
|
|
751
|
+
const col = declaredTable.tableStmt.columns.find(c => c.name.toLowerCase() === lower);
|
|
752
|
+
return col ? extractDeclaredCollation(col, defaultCollation) : 'BINARY';
|
|
753
|
+
}
|
|
754
|
+
/**
|
|
755
|
+
* Renders the canonical body of a DECLARED index, pre-resolving each column's
|
|
756
|
+
* effective collation the same way the engine does at create/import time
|
|
757
|
+
* (`buildIndexSchema` / `importIndex`): explicit index COLLATE, else the declared
|
|
758
|
+
* table column's collation, else BINARY — normalized. The resolved value is placed
|
|
759
|
+
* on a normalized plain-form {@link AST.IndexedColumn} (`{ name, collation,
|
|
760
|
+
* direction }`) so {@link createIndexBodyToCanonicalString} reads it off
|
|
761
|
+
* `col.collation`, matching the actual side's lift (`indexToCanonicalDDL`). Because
|
|
762
|
+
* both sides feed an identically-resolved collation, an unchanged inherited/BINARY
|
|
763
|
+
* collation renders identically (no churn) while a genuine collation change diverges
|
|
764
|
+
* (drop+recreate).
|
|
765
|
+
*
|
|
766
|
+
* A genuine expression-index column (no resolvable bare name) is passed through
|
|
767
|
+
* untouched — there is no column collation to resolve and the renderer falls back to
|
|
768
|
+
* `expressionToString`. `declaredTable` is the matched declared table (looked up by
|
|
769
|
+
* the index's table reference); undefined falls back to explicit-or-BINARY.
|
|
770
|
+
*
|
|
771
|
+
* **Column-rename reconciliation.** `colRenames` are the in-diff column renames of
|
|
772
|
+
* the index's table (keyed by the declared/new table name in `computeSchemaDiff`'s
|
|
773
|
+
* pre-pass). The actual catalog body still renders the *pre-rename* column names at
|
|
774
|
+
* diff time, while the declared side renders the *new* names — so a same-named index
|
|
775
|
+
* over a column renamed in this same diff would otherwise churn a spurious
|
|
776
|
+
* drop+recreate. To reconcile, each resolved bare name is inverse-mapped from its NEW
|
|
777
|
+
* name back to its OLD name (case-insensitive) so a pure rename matches the actual
|
|
778
|
+
* body (no churn) while a genuine body edit layered on the rename still differs
|
|
779
|
+
* (recreate). The indexed-column list carries bare names (no qualifiers) and the body
|
|
780
|
+
* excludes the `on <table>` reference, so a *table* rename alone never churns the
|
|
781
|
+
* column list — but the partial WHERE predicate CAN embed table names: the index
|
|
782
|
+
* table as a qualifier (`where t.active = 1`) or, in principle, another table
|
|
783
|
+
* inside a subquery — so ALL in-diff table renames (`tableRenames`) are
|
|
784
|
+
* reconciled there (see below). The cross-table case is currently unreachable
|
|
785
|
+
* end-to-end (the memory backend rejects subqueries — and any cross-table ref —
|
|
786
|
+
* in partial-index predicates at create time, so no actual catalog index can
|
|
787
|
+
* carry one), but the all-renames scope is kept for symmetry with the forward
|
|
788
|
+
* rewriter and future backends.
|
|
789
|
+
*
|
|
790
|
+
* **Ordering is load-bearing**: the effective collation is resolved from the *new*
|
|
791
|
+
* (declared) column name FIRST — the declared table's `ColumnDef` is keyed by the new
|
|
792
|
+
* name — and only THEN is the emitted name inverse-renamed to its old form. Reversing
|
|
793
|
+
* this would look up the declared column's collation under the old name and miss it.
|
|
794
|
+
*
|
|
795
|
+
* The partial-index `where` predicate is reconciled the same way the constraint CHECK
|
|
796
|
+
* path is: EVERY in-diff table rename is inverse-rewritten NEW→OLD first (via
|
|
797
|
+
* {@link renameTableInAst} — the exact inverse of the forward rewriter the executed
|
|
798
|
+
* rename migration runs over ALL tables, so the diff-side reconcile and the migration
|
|
799
|
+
* cannot drift), THEN each renamed column is inverse-rewritten NEW→OLD via
|
|
800
|
+
* {@link renameColumnInCheckExpression} seeded with the OLD table name (qualifiers
|
|
801
|
+
* are pre-normalized to OLD by that point; unqualified refs resolve via the seed
|
|
802
|
+
* either way). The index table's OWN rename retains one special role: it supplies
|
|
803
|
+
* that seed. Sequential inverse application of multiple renames is order-independent
|
|
804
|
+
* because `resolveRenames` makes chains/swaps unrepresentable — no inverse output
|
|
805
|
+
* (oldName) can match another inverse input (newName). This keeps a partial index
|
|
806
|
+
* over a renamed column AND/OR a qualified self-reference under a renamed table from
|
|
807
|
+
* churning, while a genuine predicate edit layered on either rename still differs
|
|
808
|
+
* (recreate). `schemaName` is the default schema for both rewriters. Known accepted
|
|
809
|
+
* edge (symmetric with the forward path): the rewriters are scope-naive about a
|
|
810
|
+
* subquery alias that happens to equal a renamed table's new name — worst case a
|
|
811
|
+
* spurious (valid) recreate.
|
|
812
|
+
*/
|
|
813
|
+
function declaredIndexCanonicalBody(indexStmt, declaredTable, colRenames, tableRenames, schemaName, defaultCollation) {
|
|
814
|
+
const columns = indexStmt.columns.map(col => {
|
|
815
|
+
const bareName = indexedColumnBareName(col);
|
|
816
|
+
if (!bareName)
|
|
817
|
+
return col;
|
|
818
|
+
// Collation resolves on the DECLARED (new) name — see the ordering note above.
|
|
819
|
+
// An inherited (no explicit index COLLATE) column resolves the table column's
|
|
820
|
+
// collation under the session default, matching the actual catalog index built
|
|
821
|
+
// from a default-resolved table column — so a non-BINARY default doesn't churn.
|
|
822
|
+
const effective = normalizeCollationName(explicitIndexColumnCollation(col) || declaredColumnCollation(declaredTable, bareName, defaultCollation) || 'BINARY');
|
|
823
|
+
// THEN inverse-rename the emitted name to its old (actual-catalog) form.
|
|
824
|
+
const oldName = colRenames.find(r => r.newName.toLowerCase() === bareName.toLowerCase())?.oldName ?? bareName;
|
|
825
|
+
return { name: oldName, collation: effective, direction: col.direction };
|
|
826
|
+
});
|
|
827
|
+
// Reconcile the partial-WHERE predicate: inverse-rewrite EVERY in-diff table
|
|
828
|
+
// rename NEW→OLD first (self-qualifier or a cross-table reference in a
|
|
829
|
+
// subquery alike — mirroring the forward rewriter's all-tables walk), then
|
|
830
|
+
// each renamed column NEW→OLD, over a clone (the rewriters mutate in place;
|
|
831
|
+
// indexStmt backs the recreate DDL). Skip the clone when nothing can match.
|
|
832
|
+
// The column rewrites are seeded with the OLD table name because the
|
|
833
|
+
// qualifier pass has already normalized qualified refs to it (with no own-
|
|
834
|
+
// table rename, OLD == declared, so the seed is unchanged).
|
|
835
|
+
let where = indexStmt.where;
|
|
836
|
+
if (where && (colRenames.length > 0 || tableRenames.length > 0)) {
|
|
837
|
+
const clone = cloneExpr(where);
|
|
838
|
+
for (const r of tableRenames) {
|
|
839
|
+
renameTableInAst(clone, r.newName, r.oldName, schemaName);
|
|
840
|
+
}
|
|
841
|
+
// The index's OWN table rename retains one special role: seeding the
|
|
842
|
+
// column rewrites with that table's OLD name (matched by the declared/
|
|
843
|
+
// NEW table name, exactly the lookup the call site used to do).
|
|
844
|
+
const ownRename = tableRenames.find(r => r.newName.toLowerCase() === indexStmt.table.name.toLowerCase());
|
|
845
|
+
const seedTableName = ownRename?.oldName ?? indexStmt.table.name;
|
|
846
|
+
for (const r of colRenames) {
|
|
847
|
+
renameColumnInCheckExpression(clone, seedTableName, r.newName, r.oldName, schemaName);
|
|
848
|
+
}
|
|
849
|
+
where = clone;
|
|
850
|
+
}
|
|
851
|
+
return createIndexBodyToCanonicalString({ ...indexStmt, columns, where });
|
|
852
|
+
}
|
|
853
|
+
/**
|
|
854
|
+
* Renders the declared view definition's canonical string with the in-diff
|
|
855
|
+
* renames inverse-applied — each renamed identifier rewritten from its declared
|
|
856
|
+
* NEW name back to the ACTUAL (pre-rename) name the catalog still carries at
|
|
857
|
+
* diff time. The view analogue of {@link reconciledDeclaredBody} (constraints)
|
|
858
|
+
* and {@link declaredIndexCanonicalBody}: comparing this against the actual
|
|
859
|
+
* definition distinguishes a *pure source rename* (matches after reconciliation
|
|
860
|
+
* → no recreate; the rename ops alone converge the view at apply, via the live
|
|
861
|
+
* rename propagation) from a *genuine definition edit* (still differs →
|
|
862
|
+
* drop+recreate). Callers short-circuit the raw-equal compare and call this
|
|
863
|
+
* only on mismatch with renames present.
|
|
864
|
+
*
|
|
865
|
+
* The explicit column list names the VIEW's own output columns — stable
|
|
866
|
+
* identity untouched by source renames — so it passes through unchanged; the
|
|
867
|
+
* body (including its trailing `with defaults (…)` clause) reconciles via
|
|
868
|
+
* {@link inverseRenamedViewParts} with ALL in-diff table renames threaded.
|
|
869
|
+
*/
|
|
870
|
+
function reconciledDeclaredViewDefinition(columns, select, tableRenames,
|
|
871
|
+
/** Declared (new) table name (lowercased) → that table's column renames. */
|
|
872
|
+
columnRenamesByTable, schemaName,
|
|
873
|
+
/** Declared-side column-existence resolver for the seeded defaults-expr rewrites (see `computeSchemaDiff`). */
|
|
874
|
+
resolveDeclaredColumn) {
|
|
875
|
+
const reconciledSelect = inverseRenamedViewParts(select, tableRenames, columnRenamesByTable, schemaName, resolveDeclaredColumn);
|
|
876
|
+
return viewDefinitionToCanonicalString(columns, reconciledSelect);
|
|
877
|
+
}
|
|
878
|
+
/**
|
|
879
|
+
* Core inverse-rename pass shared by {@link reconciledDeclaredViewDefinition}
|
|
880
|
+
* (which threads ALL in-diff table renames) and {@link columnReconciledViewStmt}
|
|
881
|
+
* (which passes none — its render must keep declared table names): clones the
|
|
882
|
+
* declared select and `insert defaults` clause (the rewriters mutate in place;
|
|
883
|
+
* the declared stmt backs the declared-schema store / recreate DDL) and
|
|
884
|
+
* rewrites the in-diff renames NEW→OLD.
|
|
885
|
+
*
|
|
886
|
+
* Reconciled body: inverse table renames over the select clone for ALL threaded
|
|
887
|
+
* renames FIRST — so both a direct FROM reference and a cross-table reference in
|
|
888
|
+
* a subquery normalize to OLD names — THEN each renamed table's column renames
|
|
889
|
+
* NEW→OLD, seeded with that table's OLD name (qualifiers are pre-normalized to
|
|
890
|
+
* OLD by the table pass; with no own-table rename — in particular with no table
|
|
891
|
+
* pass at all — the seed is the DECLARED name the qualifiers still carry).
|
|
892
|
+
* Sequential inverse application is order-independent: `resolveRenames` makes
|
|
893
|
+
* chains/swaps unrepresentable, so no inverse output (oldName) can match another
|
|
894
|
+
* inverse input (newName).
|
|
895
|
+
*
|
|
896
|
+
* The trailing `with defaults (…)` clause now rides inside `select`
|
|
897
|
+
* ({@link AST.SelectStmt.defaults}), so the scope-aware body walks descend it
|
|
898
|
+
* directly: each entry's `expr` inverse-renames in the select's FROM scope frame
|
|
899
|
+
* and each entry's `column` target rides the same synthetic-probe rewrite as a
|
|
900
|
+
* `with inverse` target, so the body reconciliation here covers the defaults for
|
|
901
|
+
* free. The `resolveDeclaredColumn` resolver is threaded into that body walk so
|
|
902
|
+
* an unqualified ref inside a defaults-expr subquery that binds a like-named
|
|
903
|
+
* column on its own FROM is NOT false-captured by the enclosing FROM seed
|
|
904
|
+
* (declared-side parity with the forward live-lookup walk; see `computeSchemaDiff`).
|
|
905
|
+
*/
|
|
906
|
+
function inverseRenamedViewParts(select, tableRenames,
|
|
907
|
+
/** Declared (new) table name (lowercased) → that table's column renames. */
|
|
908
|
+
columnRenamesByTable, schemaName,
|
|
909
|
+
/** Declared-side column-existence resolver for the scope-aware body walk (see `computeSchemaDiff`). */
|
|
910
|
+
resolveDeclaredColumn) {
|
|
911
|
+
const selectClone = cloneQueryExpr(select);
|
|
912
|
+
for (const r of tableRenames) {
|
|
913
|
+
renameTableInAst(selectClone, r.newName, r.oldName, schemaName);
|
|
914
|
+
}
|
|
915
|
+
for (const [declaredTableName, colRenames] of columnRenamesByTable) {
|
|
916
|
+
const ownRename = tableRenames.find(r => r.newName.toLowerCase() === declaredTableName);
|
|
917
|
+
const seedTableName = ownRename?.oldName ?? declaredTableName;
|
|
918
|
+
for (const r of colRenames) {
|
|
919
|
+
renameColumnInAst(selectClone, seedTableName, r.newName, r.oldName, schemaName, resolveDeclaredColumn);
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
return selectClone;
|
|
923
|
+
}
|
|
924
|
+
/**
|
|
925
|
+
* Declared {@link AST.CreateViewStmt} with the in-diff COLUMN renames
|
|
926
|
+
* inverse-applied (NEW→OLD); table references untouched. Renders the recreate
|
|
927
|
+
* DDL of a hinted-RENAME-matched view whose definition is otherwise unchanged:
|
|
928
|
+
* in migration order the create runs AFTER `ALTER TABLE … RENAME TO` (declared
|
|
929
|
+
* table names are already live) but BEFORE `ALTER TABLE … RENAME COLUMN` (a
|
|
930
|
+
* body naming the NEW column would fail to plan at create time). After the
|
|
931
|
+
* create, the live column-rename propagation rewrites the fresh body, so the
|
|
932
|
+
* post-apply state and a re-diff converge. Unlike
|
|
933
|
+
* {@link reconciledDeclaredViewDefinition} there is NO inverse table pass (it
|
|
934
|
+
* would name a table that no longer exists at create time) — the shared
|
|
935
|
+
* {@link inverseRenamedViewParts} core runs with no table renames, seeding the
|
|
936
|
+
* column rewrites with each table's DECLARED name, which the body's qualifiers
|
|
937
|
+
* still carry. Identity when the diff carries no column renames.
|
|
938
|
+
*/
|
|
939
|
+
function columnReconciledViewStmt(stmt,
|
|
940
|
+
/** Declared (new) table name (lowercased) → that table's column renames. */
|
|
941
|
+
columnRenamesByTable, schemaName,
|
|
942
|
+
/** Declared-side column-existence resolver for the seeded defaults-expr rewrites (see `computeSchemaDiff`). */
|
|
943
|
+
resolveDeclaredColumn) {
|
|
944
|
+
if (columnRenamesByTable.size === 0)
|
|
945
|
+
return stmt;
|
|
946
|
+
// The reconciled body carries any trailing `with defaults (…)` clause inline.
|
|
947
|
+
const reconciledSelect = inverseRenamedViewParts(stmt.select, [], columnRenamesByTable, schemaName, resolveDeclaredColumn);
|
|
948
|
+
return { ...stmt, select: reconciledSelect };
|
|
949
|
+
}
|
|
950
|
+
/**
|
|
951
|
+
* Declared {@link AST.CreateIndexStmt} with the in-diff COLUMN renames
|
|
952
|
+
* inverse-applied (NEW→OLD); table references untouched. The index analogue of
|
|
953
|
+
* {@link columnReconciledViewStmt}, rendering the recreate DDL of a
|
|
954
|
+
* hinted-RENAME-matched index whose body is otherwise unchanged. Indexed-column
|
|
955
|
+
* bare names map NEW→OLD via the index table's own renames (both indexed-column
|
|
956
|
+
* forms — plain and the parser's collate-folded `col COLLATE x`). The partial
|
|
957
|
+
* WHERE predicate reuses the walk shape of {@link declaredIndexCanonicalBody}
|
|
958
|
+
* minus its inverse table pass: the own table's renames via the
|
|
959
|
+
* CHECK-expression entry point seeded with the DECLARED table name (qualifiers
|
|
960
|
+
* still carry it — no table pass pre-normalized them), other tables' renames
|
|
961
|
+
* via the plain scope-aware walk (a cross-table predicate reference is
|
|
962
|
+
* unreachable today — the memory backend rejects it at create time — kept for
|
|
963
|
+
* symmetry with the canonical-body reconciler). After the create, the live
|
|
964
|
+
* column-rename propagation rewrites the indexed columns and predicate, so a
|
|
965
|
+
* re-diff converges. Identity when the diff carries no column renames.
|
|
966
|
+
*/
|
|
967
|
+
function columnReconciledIndexStmt(stmt,
|
|
968
|
+
/** The index's own table's in-diff column renames. */
|
|
969
|
+
colRenames,
|
|
970
|
+
/** Declared (new) table name (lowercased) → that table's column renames. */
|
|
971
|
+
columnRenamesByTable, schemaName) {
|
|
972
|
+
if (columnRenamesByTable.size === 0)
|
|
973
|
+
return stmt;
|
|
974
|
+
const columns = stmt.columns.map(col => {
|
|
975
|
+
const bareName = indexedColumnBareName(col);
|
|
976
|
+
if (!bareName)
|
|
977
|
+
return col;
|
|
978
|
+
const rename = colRenames.find(r => r.newName.toLowerCase() === bareName.toLowerCase());
|
|
979
|
+
if (!rename)
|
|
980
|
+
return col;
|
|
981
|
+
if (col.name)
|
|
982
|
+
return { ...col, name: rename.oldName };
|
|
983
|
+
// Collate-folded form: the bare name lives on col.expr.expr.name.
|
|
984
|
+
const collate = col.expr;
|
|
985
|
+
const inner = collate.expr;
|
|
986
|
+
return { ...col, expr: { ...collate, expr: { ...inner, name: rename.oldName } } };
|
|
987
|
+
});
|
|
988
|
+
let where = stmt.where;
|
|
989
|
+
if (where) {
|
|
990
|
+
const clone = cloneExpr(where);
|
|
991
|
+
for (const r of colRenames) {
|
|
992
|
+
renameColumnInCheckExpression(clone, stmt.table.name, r.newName, r.oldName, schemaName);
|
|
993
|
+
}
|
|
994
|
+
const ownTableLower = stmt.table.name.toLowerCase();
|
|
995
|
+
for (const [declaredTableName, renames] of columnRenamesByTable) {
|
|
996
|
+
if (declaredTableName === ownTableLower)
|
|
997
|
+
continue;
|
|
998
|
+
for (const r of renames) {
|
|
999
|
+
renameColumnInAst(clone, declaredTableName, r.newName, r.oldName, schemaName);
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
where = clone;
|
|
1003
|
+
}
|
|
1004
|
+
return { ...stmt, columns, where };
|
|
1005
|
+
}
|
|
1006
|
+
/**
|
|
1007
|
+
* Converts a column-level constraint carrying a name into the equivalent
|
|
1008
|
+
* table-level {@link AST.TableConstraint}, so it can be stringified into an
|
|
1009
|
+
* `ADD CONSTRAINT` fragment and diffed by name alongside table-level constraints.
|
|
1010
|
+
* Returns undefined for constraint kinds that are not lifecycle-managed named
|
|
1011
|
+
* constraints (NOT NULL / NULL / DEFAULT / COLLATE / GENERATED / PRIMARY KEY).
|
|
1012
|
+
*/
|
|
1013
|
+
function columnConstraintToTableConstraint(columnName, cc) {
|
|
1014
|
+
switch (cc.type) {
|
|
1015
|
+
case 'check':
|
|
1016
|
+
if (!cc.expr)
|
|
1017
|
+
return undefined;
|
|
1018
|
+
return { type: 'check', name: cc.name, expr: cc.expr, operations: cc.operations, onConflict: cc.onConflict, tags: cc.tags };
|
|
1019
|
+
case 'unique':
|
|
1020
|
+
return { type: 'unique', name: cc.name, columns: [{ name: columnName }], onConflict: cc.onConflict, tags: cc.tags };
|
|
1021
|
+
case 'foreignKey':
|
|
1022
|
+
if (!cc.foreignKey)
|
|
1023
|
+
return undefined;
|
|
1024
|
+
return { type: 'foreignKey', name: cc.name, columns: [{ name: columnName }], foreignKey: cc.foreignKey, tags: cc.tags };
|
|
1025
|
+
default:
|
|
1026
|
+
return undefined;
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
/**
|
|
1030
|
+
* Gathers declared *user-named* CHECK / UNIQUE / FOREIGN KEY constraints from a
|
|
1031
|
+
* declared table — both table-level and column-level (carrying an explicit name)
|
|
1032
|
+
* — keyed by lowercased name. PRIMARY KEY is excluded (handled by
|
|
1033
|
+
* `primaryKeyChange`); engine-synthesized `_`-prefixed names are excluded to stay
|
|
1034
|
+
* symmetric with the catalog's `namedConstraints`. On a name collision the first
|
|
1035
|
+
* wins (a duplicate user constraint name is a separate validation concern).
|
|
1036
|
+
*
|
|
1037
|
+
* `schemaName` is the schema this table is being diffed under (the differ runs
|
|
1038
|
+
* per schema — it is the CHILD schema for any FK declared here). It is threaded
|
|
1039
|
+
* into the canonical-body render so an FK's explicit own-schema qualifier folds
|
|
1040
|
+
* out symmetrically with the actual-catalog side (see
|
|
1041
|
+
* {@link constraintBodyToCanonicalString}); a genuine cross-schema parent stays a
|
|
1042
|
+
* body-change channel.
|
|
1043
|
+
*/
|
|
1044
|
+
function collectDeclaredNamedConstraints(declaredTable, schemaName) {
|
|
1045
|
+
const out = new Map();
|
|
1046
|
+
const add = (name, tags, tc) => {
|
|
1047
|
+
if (!name)
|
|
1048
|
+
return;
|
|
1049
|
+
const lower = name.toLowerCase();
|
|
1050
|
+
if (lower.startsWith('_'))
|
|
1051
|
+
return;
|
|
1052
|
+
if (out.has(lower))
|
|
1053
|
+
return;
|
|
1054
|
+
out.set(lower, { name, tags, ddl: tableConstraintsToString([tc]), definition: constraintBodyToCanonicalString(tc, schemaName), bodyAst: tc });
|
|
382
1055
|
};
|
|
383
|
-
|
|
384
|
-
|
|
1056
|
+
for (const c of declaredTable.tableStmt.constraints ?? []) {
|
|
1057
|
+
if (c.type === 'primaryKey')
|
|
1058
|
+
continue;
|
|
1059
|
+
add(c.name, c.tags, c);
|
|
1060
|
+
}
|
|
1061
|
+
for (const col of declaredTable.tableStmt.columns) {
|
|
1062
|
+
for (const cc of col.constraints ?? []) {
|
|
1063
|
+
if (!cc.name)
|
|
1064
|
+
continue;
|
|
1065
|
+
const tc = columnConstraintToTableConstraint(col.name, cc);
|
|
1066
|
+
if (tc)
|
|
1067
|
+
add(cc.name, cc.tags, tc);
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
return out;
|
|
1071
|
+
}
|
|
1072
|
+
/**
|
|
1073
|
+
* Inverse-applies the in-diff column renames to a constraint's column list: maps
|
|
1074
|
+
* each `{ name }` entry from its NEW name back to its OLD name (case-insensitive).
|
|
1075
|
+
* Used to reconcile a UNIQUE column set / an FK's local (child) column set against
|
|
1076
|
+
* the actual catalog body, which still renders the pre-rename names at diff time.
|
|
1077
|
+
* Mutates the supplied (already-cloned) array in place.
|
|
1078
|
+
*/
|
|
1079
|
+
function inverseRenameConstraintColumns(columns, colRenames) {
|
|
1080
|
+
if (!columns)
|
|
1081
|
+
return;
|
|
1082
|
+
for (const col of columns) {
|
|
1083
|
+
const lower = col.name.toLowerCase();
|
|
1084
|
+
const r = colRenames.find(cr => cr.newName.toLowerCase() === lower);
|
|
1085
|
+
if (r)
|
|
1086
|
+
col.name = r.oldName;
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
/**
|
|
1090
|
+
* String-list variant of {@link inverseRenameConstraintColumns}: inverse-applies
|
|
1091
|
+
* column renames to a bare `string[]` (an FK's referenced PARENT column list,
|
|
1092
|
+
* which is `string[]` rather than `{ name }[]`), mapping each entry from its NEW
|
|
1093
|
+
* name back to its OLD name (case-insensitive). Used to reconcile an FK whose
|
|
1094
|
+
* *parent* table renamed a referenced column — the parent's column renames are
|
|
1095
|
+
* threaded in from `computeSchemaDiff`'s pre-pass. Mutates the supplied
|
|
1096
|
+
* (already-cloned) array in place; an undefined / elided list is a no-op (so a
|
|
1097
|
+
* `references parent` with no column list never synthesizes one).
|
|
1098
|
+
*/
|
|
1099
|
+
function inverseRenameStringColumns(columns, colRenames) {
|
|
1100
|
+
if (!columns)
|
|
1101
|
+
return;
|
|
1102
|
+
for (let i = 0; i < columns.length; i++) {
|
|
1103
|
+
const lower = columns[i].toLowerCase();
|
|
1104
|
+
const r = colRenames.find(cr => cr.newName.toLowerCase() === lower);
|
|
1105
|
+
if (r)
|
|
1106
|
+
columns[i] = r.oldName;
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
/**
|
|
1110
|
+
* Renders the declared constraint's canonical body with the in-diff renames
|
|
1111
|
+
* inverse-applied — i.e. each renamed identifier rewritten from its NEW name
|
|
1112
|
+
* back to the ACTUAL (pre-rename) name the catalog still carries at diff time.
|
|
1113
|
+
* Comparing this against `actual.definition` lets the body-change detector
|
|
1114
|
+
* distinguish a *pure rename* (bodies match after reconciliation → no churn) from
|
|
1115
|
+
* a *genuine body edit* (still differ → drop+recreate). A body edit layered on a
|
|
1116
|
+
* rename survives the reconciliation, so the existing rename-vs-body precedence is
|
|
1117
|
+
* preserved.
|
|
1118
|
+
*
|
|
1119
|
+
* Reconciles only what each kind needs (surgical clone, never the whole tree):
|
|
1120
|
+
* - CHECK: inverse table renames on ALL in-diff renamed tables FIRST —
|
|
1121
|
+
* mirroring the forward path (`rewriteTableForTableRename` walks
|
|
1122
|
+
* every table's CHECKs), so both a qualified self-reference and a
|
|
1123
|
+
* cross-table reference inside a subquery reconcile — then inverse
|
|
1124
|
+
* column renames, in two passes mirroring the forward
|
|
1125
|
+
* `rewriteTableForColumnRename` branch split: the OWNING table's
|
|
1126
|
+
* renames via the seeded CHECK rewriter (OLD-table seed — correct
|
|
1127
|
+
* unconditionally, since qualifiers are pre-normalized to OLD by
|
|
1128
|
+
* the qualifier pass — plus the declared-side scope resolver, so
|
|
1129
|
+
* an unqualified inner-subquery ref binding to a like-named column
|
|
1130
|
+
* on its own FROM source is not falsely captured by the seed),
|
|
1131
|
+
* then OTHER tables' renames via the plain scope-aware walk (no
|
|
1132
|
+
* seed, no resolver — exactly the forward non-owning branch).
|
|
1133
|
+
* Within each pass, sequential inverse application is
|
|
1134
|
+
* order-independent: `resolveRenames` makes rename chains/swaps
|
|
1135
|
+
* unrepresentable, so no inverse output (oldName) can match
|
|
1136
|
+
* another inverse input (newName). BETWEEN the passes order
|
|
1137
|
+
* matters — owning first (see the cross-table loop's comment).
|
|
1138
|
+
* - UNIQUE: inverse column renames on the column list.
|
|
1139
|
+
* - FK: inverse column renames on the LOCAL (child) column list, inverse
|
|
1140
|
+
* column renames on the referenced PARENT column list (via the parent
|
|
1141
|
+
* table's column renames, keyed by the declared parent name in
|
|
1142
|
+
* `columnRenamesByTable`), AND inverse table renames on the referenced
|
|
1143
|
+
* parent `foreignKey.table`. A parent-table rename and a parent-column
|
|
1144
|
+
* rename in the same diff reconcile together: look up the parent's
|
|
1145
|
+
* column renames by the *new* parent name first, then rewrite the
|
|
1146
|
+
* table name back to its old form.
|
|
1147
|
+
*/
|
|
1148
|
+
function reconciledDeclaredBody(d, colRenames, tableRenames, tableName, schemaName,
|
|
1149
|
+
/** Declared (new) table name (lowercased) → that table's column renames; for the FK parent-column reconcile. */
|
|
1150
|
+
columnRenamesByTable,
|
|
1151
|
+
/** Declared-side column-existence resolver for the seeded CHECK rewrites (see `computeSchemaDiff`). */
|
|
1152
|
+
resolveDeclaredColumn) {
|
|
1153
|
+
const tc = d.bodyAst;
|
|
1154
|
+
switch (tc.type) {
|
|
1155
|
+
case 'check': {
|
|
1156
|
+
if (!tc.expr)
|
|
1157
|
+
return d.definition;
|
|
1158
|
+
// cloneExpr: the rewriters mutate in place; bodyAst backs ddl/definition.
|
|
1159
|
+
const clone = { ...tc, expr: cloneExpr(tc.expr) };
|
|
1160
|
+
// Qualifiers first: any qualified reference in the declared CHECK carries
|
|
1161
|
+
// a NEW table name (a self-reference after the owning table's rename, or a
|
|
1162
|
+
// cross-table reference inside a subquery after THAT table's rename);
|
|
1163
|
+
// inverse-rewrite every in-diff rename to its OLD name so the body matches
|
|
1164
|
+
// the actual catalog and the OLD-seeded column rewrites below see the
|
|
1165
|
+
// owning table's OLD qualifier. Sequential in-place application is safe:
|
|
1166
|
+
// `resolveRenames` makes chains and swaps unrepresentable (every newName is
|
|
1167
|
+
// absent from the actual catalog while every oldName is present), so no
|
|
1168
|
+
// rename's inverse output can match another's inverse input — order is
|
|
1169
|
+
// immaterial and equivalent to simultaneous substitution.
|
|
1170
|
+
for (const r of tableRenames) {
|
|
1171
|
+
renameTableInAst(clone.expr, r.newName, r.oldName, schemaName);
|
|
1172
|
+
}
|
|
1173
|
+
for (const r of colRenames) {
|
|
1174
|
+
// Inverse: rewrite the declared NEW column name back to its OLD name.
|
|
1175
|
+
// The declared-side resolver keeps the seeded walk scope-aware — an
|
|
1176
|
+
// unqualified ref inside a subquery whose own FROM source exposes the
|
|
1177
|
+
// NEW name (in the declared world) binds there, not to the owning
|
|
1178
|
+
// seed, so it is NOT inverse-rewritten — mirroring the forward seeded
|
|
1179
|
+
// call in `rewriteTableForColumnRename` (owning-table branch).
|
|
1180
|
+
renameColumnInCheckExpression(clone.expr, tableName, r.newName, r.oldName, schemaName, resolveDeclaredColumn);
|
|
1181
|
+
}
|
|
1182
|
+
// Cross-table column renames: a subquery in this CHECK may reference
|
|
1183
|
+
// ANOTHER table whose column was renamed in this same diff; the forward
|
|
1184
|
+
// propagation rewrites those refs via the plain scope-aware walk (no seed
|
|
1185
|
+
// frame, no resolver — `rewriteTableForColumnRename`'s non-owning branch),
|
|
1186
|
+
// so the inverse uses the same walker: an unqualified ref only rewrites
|
|
1187
|
+
// when the renamed table sits in an enclosing FROM frame, which is exactly
|
|
1188
|
+
// right for subquery references. The map key is the DECLARED (new) table
|
|
1189
|
+
// name; the qualifier pass above already rewrote the clone's references to
|
|
1190
|
+
// OLD names, so map the walk's table seed back. The owning table's entry
|
|
1191
|
+
// is skipped — its renames are `colRenames`, handled by the seeded loop
|
|
1192
|
+
// above (`tableName` is the ACTUAL/old owning name, so the comparison
|
|
1193
|
+
// holds even when the owning table was itself renamed). ORDER MATTERS:
|
|
1194
|
+
// owning-seeded inverse FIRST. With the reverse order, a compound diff
|
|
1195
|
+
// (owning `qty→cap` + referenced `lim.cap→capacity`) has this loop turn
|
|
1196
|
+
// the inner `capacity` back into `cap`, which the owning inverse then
|
|
1197
|
+
// falsely captures; owning-first leaves the inner ref spelled `capacity`
|
|
1198
|
+
// (no match) until this loop fixes it.
|
|
1199
|
+
for (const [declaredTableName, renames] of columnRenamesByTable) {
|
|
1200
|
+
const ownRename = tableRenames.find(r => r.newName.toLowerCase() === declaredTableName);
|
|
1201
|
+
const seedTableName = ownRename?.oldName ?? declaredTableName;
|
|
1202
|
+
if (seedTableName.toLowerCase() === tableName.toLowerCase())
|
|
1203
|
+
continue;
|
|
1204
|
+
for (const r of renames) {
|
|
1205
|
+
renameColumnInAst(clone.expr, seedTableName, r.newName, r.oldName, schemaName);
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
return constraintBodyToCanonicalString(clone);
|
|
1209
|
+
}
|
|
1210
|
+
case 'unique': {
|
|
1211
|
+
const clone = { ...tc, columns: tc.columns?.map(c => ({ ...c })) };
|
|
1212
|
+
inverseRenameConstraintColumns(clone.columns, colRenames);
|
|
1213
|
+
return constraintBodyToCanonicalString(clone);
|
|
1214
|
+
}
|
|
1215
|
+
case 'foreignKey': {
|
|
1216
|
+
const clone = {
|
|
1217
|
+
...tc,
|
|
1218
|
+
columns: tc.columns?.map(c => ({ ...c })),
|
|
1219
|
+
foreignKey: tc.foreignKey
|
|
1220
|
+
? { ...tc.foreignKey, columns: tc.foreignKey.columns ? [...tc.foreignKey.columns] : undefined }
|
|
1221
|
+
: tc.foreignKey,
|
|
1222
|
+
};
|
|
1223
|
+
// Local (child) columns live on THIS table → inverse column rename.
|
|
1224
|
+
inverseRenameConstraintColumns(clone.columns, colRenames);
|
|
1225
|
+
if (clone.foreignKey) {
|
|
1226
|
+
// Referenced PARENT columns → inverse column rename via the parent
|
|
1227
|
+
// table's renames, looked up by the DECLARED (new) parent name —
|
|
1228
|
+
// BEFORE the table inverse-rename below rewrites that name to its old
|
|
1229
|
+
// form. Absent from the map (e.g. a freshly created parent) ⇒ no-op.
|
|
1230
|
+
const parentLower = clone.foreignKey.table.toLowerCase();
|
|
1231
|
+
const parentColRenames = columnRenamesByTable.get(parentLower);
|
|
1232
|
+
if (parentColRenames)
|
|
1233
|
+
inverseRenameStringColumns(clone.foreignKey.columns, parentColRenames);
|
|
1234
|
+
// Parent table reference → inverse table rename (newTable → oldTable).
|
|
1235
|
+
// The parent SCHEMA is NOT a rename channel (renames are within-schema),
|
|
1236
|
+
// so `foreignKey.schema` rides the clone untouched; the canonical render
|
|
1237
|
+
// folds an own-schema qualifier out against `schemaName` (the child schema).
|
|
1238
|
+
const tr = tableRenames.find(r => r.newName.toLowerCase() === parentLower);
|
|
1239
|
+
if (tr)
|
|
1240
|
+
clone.foreignKey = { ...clone.foreignKey, table: tr.oldName };
|
|
1241
|
+
}
|
|
1242
|
+
return constraintBodyToCanonicalString(clone, schemaName);
|
|
1243
|
+
}
|
|
1244
|
+
default:
|
|
1245
|
+
return d.definition;
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
/**
|
|
1249
|
+
* Resolves a declared table's column renames against its matched actual catalog
|
|
1250
|
+
* table — the map-building + {@link resolveRenames} step shared by both the
|
|
1251
|
+
* `computeSchemaDiff` pre-pass (which keeps only `.renames`, keyed by declared
|
|
1252
|
+
* table name, so the FK branch of {@link reconciledDeclaredBody} can inverse-
|
|
1253
|
+
* rename a parent's referenced column cross-table) and {@link computeTableAlterDiff}
|
|
1254
|
+
* (which uses the full `{ renames, pairs, consumedActuals }` for add/drop/alter).
|
|
1255
|
+
* The current table's renames are therefore resolved twice per diff — once in the
|
|
1256
|
+
* pre-pass and once in its own alter-diff. Accepted: `resolveRenames` over a
|
|
1257
|
+
* table's columns is O(columns) with no I/O, and threading the full result through
|
|
1258
|
+
* the loop for a micro-optimization would widen the blast radius.
|
|
1259
|
+
*/
|
|
1260
|
+
function resolveColumnRenames(declaredTable, actualTable, policy) {
|
|
385
1261
|
const declaredColumns = new Map();
|
|
386
1262
|
for (const col of declaredTable.tableStmt.columns) {
|
|
387
1263
|
declaredColumns.set(col.name.toLowerCase(), col);
|
|
@@ -390,7 +1266,7 @@ function computeTableAlterDiff(declaredTable, actualTable, policy) {
|
|
|
390
1266
|
for (const col of actualTable.columns) {
|
|
391
1267
|
actualColumns.set(col.name.toLowerCase(), col);
|
|
392
1268
|
}
|
|
393
|
-
|
|
1269
|
+
return resolveRenames({
|
|
394
1270
|
kind: 'constraint', // unused for ColumnRenameOp; kind only flows into RenameOp not surfaced here
|
|
395
1271
|
declared: declaredColumns,
|
|
396
1272
|
actual: actualColumns,
|
|
@@ -400,21 +1276,83 @@ function computeTableAlterDiff(declaredTable, actualTable, policy) {
|
|
|
400
1276
|
getActualTags: a => a.tags,
|
|
401
1277
|
policy,
|
|
402
1278
|
});
|
|
1279
|
+
}
|
|
1280
|
+
function computeTableAlterDiff(declaredTable, actualTable, policy,
|
|
1281
|
+
/** Table renames detected in this same diff (used to reconcile FK parent-table refs). */
|
|
1282
|
+
tableRenames,
|
|
1283
|
+
/** Schema name — the default schema for the CHECK column-rename rewriter. */
|
|
1284
|
+
schemaName,
|
|
1285
|
+
/**
|
|
1286
|
+
* Declared (new) table name (lowercased) → that table's column renames, for the
|
|
1287
|
+
* cross-table FK referenced-parent-column reconcile in {@link reconciledDeclaredBody}.
|
|
1288
|
+
*/
|
|
1289
|
+
columnRenamesByTable,
|
|
1290
|
+
/** Declared-side column-existence resolver for the scope-aware CHECK reconcile (see `computeSchemaDiff`). */
|
|
1291
|
+
resolveDeclaredColumn,
|
|
1292
|
+
/** Session `default_collation` for resolving an omitted COLLATE on the declared side. */
|
|
1293
|
+
defaultCollation) {
|
|
1294
|
+
const diff = {
|
|
1295
|
+
// Default to actual's name; caller may override to declared name when this is a rename target.
|
|
1296
|
+
tableName: actualTable.name,
|
|
1297
|
+
columnsToAdd: [],
|
|
1298
|
+
columnsToDrop: [],
|
|
1299
|
+
columnsToAlter: [],
|
|
1300
|
+
columnsToRename: [],
|
|
1301
|
+
};
|
|
1302
|
+
const declaredMaintained = declaredTable.tableStmt.maintained;
|
|
1303
|
+
const liveMaintained = actualTable.maintained;
|
|
1304
|
+
// Backing-module drift on a both-maintained name-match → a destructive,
|
|
1305
|
+
// incarnation-minting move (drop+recreate). Detected here, where both module
|
|
1306
|
+
// sides are in scope (declared on the table stmt; live on the maintained
|
|
1307
|
+
// descriptor); `computeSchemaDiff` routes it to drop+recreate instead of
|
|
1308
|
+
// pushing this alter. Compare ONLY when BOTH sides are maintained — a
|
|
1309
|
+
// table↔maintained transition is an attach/detach (handled by
|
|
1310
|
+
// applyMaintainedTransition), never a module move — and use the SAME
|
|
1311
|
+
// normalization the live catalog already applied (absent/`mem` ⇒ `memory`;
|
|
1312
|
+
// args stable-sorted, absent ⇒ ''), so the two spellings of the memory default
|
|
1313
|
+
// never drift. The module is deliberately separate from `bodyHash`, so the body
|
|
1314
|
+
// re-attach path stays untouched.
|
|
1315
|
+
const moduleMigration = backingModuleDrifted(declaredMaintained, liveMaintained, declaredTable.tableStmt.moduleName, declaredTable.tableStmt.moduleArgs);
|
|
1316
|
+
if (moduleMigration)
|
|
1317
|
+
diff.maintainedModuleMigration = moduleMigration;
|
|
1318
|
+
// MV-sugar / implicit maintained form: the body owns the shape, so the differ
|
|
1319
|
+
// stays PLAN-FREE — it never derives or compares this table's columns /
|
|
1320
|
+
// constraints / PK. A genuine shape mismatch surfaces at apply's attach shape
|
|
1321
|
+
// check (the sited, authoritative check), not here. Only table tags and the
|
|
1322
|
+
// derivation transition matter. (The declared-shape table form carries columns
|
|
1323
|
+
// and falls through to the full comparison below.)
|
|
1324
|
+
if (declaredMaintained && declaredTable.tableStmt.columns.length === 0) {
|
|
1325
|
+
if (tagsDrifted(declaredTable.tableStmt.tags, actualTable.tags)) {
|
|
1326
|
+
diff.tableTagsChange = desiredTagSet(declaredTable.tableStmt.tags);
|
|
1327
|
+
}
|
|
1328
|
+
applyMaintainedTransition(diff, declaredMaintained, liveMaintained, tableRenames, columnRenamesByTable, schemaName, resolveDeclaredColumn);
|
|
1329
|
+
markMaintainedTagRoute(diff, liveMaintained);
|
|
1330
|
+
return diff;
|
|
1331
|
+
}
|
|
1332
|
+
// Detect column renames first so subsequent add/drop/alter operate on the
|
|
1333
|
+
// post-rename column set.
|
|
1334
|
+
const colRenames = resolveColumnRenames(declaredTable, actualTable, policy);
|
|
403
1335
|
for (const r of colRenames.renames) {
|
|
404
1336
|
diff.columnsToRename.push({ oldName: r.oldName, newName: r.newName });
|
|
405
1337
|
}
|
|
406
|
-
// Find columns to add (store full column definition for DDL generation)
|
|
1338
|
+
// Find columns to add (store full column definition for DDL generation).
|
|
1339
|
+
// Emit an EXPLICIT resolved COLLATE when the declared column omits one and the
|
|
1340
|
+
// session `default_collation` resolves to a non-BINARY collation for its type
|
|
1341
|
+
// (see {@link withResolvedAddColumnCollation}) — keeps the migration both
|
|
1342
|
+
// self-contained (lands the same collation under any executing session default)
|
|
1343
|
+
// and idempotent (matches the catalog column the engine's ADD COLUMN now creates).
|
|
407
1344
|
for (const col of declaredTable.tableStmt.columns) {
|
|
408
1345
|
if (!colRenames.pairs.has(col.name.toLowerCase())) {
|
|
409
|
-
diff.columnsToAdd.push(columnDefToString(col));
|
|
1346
|
+
diff.columnsToAdd.push(columnDefToString(withResolvedAddColumnCollation(col, defaultCollation)));
|
|
410
1347
|
}
|
|
411
1348
|
}
|
|
412
1349
|
// Find columns to drop (skip those consumed by a rename)
|
|
1350
|
+
const declaredColumnNames = new Set(declaredTable.tableStmt.columns.map(c => c.name.toLowerCase()));
|
|
413
1351
|
for (const col of actualTable.columns) {
|
|
414
1352
|
const ln = col.name.toLowerCase();
|
|
415
1353
|
if (colRenames.consumedActuals.has(ln))
|
|
416
1354
|
continue;
|
|
417
|
-
if (!
|
|
1355
|
+
if (!declaredColumnNames.has(ln)) {
|
|
418
1356
|
diff.columnsToDrop.push(col.name);
|
|
419
1357
|
}
|
|
420
1358
|
}
|
|
@@ -423,7 +1361,7 @@ function computeTableAlterDiff(declaredTable, actualTable, policy) {
|
|
|
423
1361
|
const matched = colRenames.pairs.get(col.name.toLowerCase());
|
|
424
1362
|
if (!matched)
|
|
425
1363
|
continue;
|
|
426
|
-
const change = computeColumnAttributeChange(col, matched);
|
|
1364
|
+
const change = computeColumnAttributeChange(col, matched, defaultCollation);
|
|
427
1365
|
if (change) {
|
|
428
1366
|
diff.columnsToAlter.push(change);
|
|
429
1367
|
}
|
|
@@ -432,15 +1370,15 @@ function computeTableAlterDiff(declaredTable, actualTable, policy) {
|
|
|
432
1370
|
if (policy === 'require-hint') {
|
|
433
1371
|
enforceRequireHint(`column (${actualTable.name})`, diff.columnsToAdd.length, diff.columnsToDrop.length);
|
|
434
1372
|
}
|
|
435
|
-
//
|
|
436
|
-
//
|
|
437
|
-
//
|
|
438
|
-
//
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
1373
|
+
// Constraint lifecycle (CHECK / UNIQUE / FOREIGN KEY) by name: rename / drop /
|
|
1374
|
+
// add. We gather declared *user-named* constraints from BOTH the table-level
|
|
1375
|
+
// `constraints` list AND column-level constraints carrying an explicit name
|
|
1376
|
+
// (e.g. `qty int constraint chk_qty check (qty > 0)`) — the actual catalog's
|
|
1377
|
+
// `namedConstraints` already merges both. PRIMARY KEY constraints are excluded
|
|
1378
|
+
// (PK changes flow through `primaryKeyChange`); auto-prefixed (`_`) names are
|
|
1379
|
+
// excluded to stay symmetric with the catalog (see catalog.ts) so an unnamed
|
|
1380
|
+
// declared constraint never churns add/drop against its synthesized actual name.
|
|
1381
|
+
const declaredNamedConstraints = collectDeclaredNamedConstraints(declaredTable, schemaName);
|
|
444
1382
|
const actualNamedConstraints = new Map();
|
|
445
1383
|
for (const c of actualTable.namedConstraints ?? []) {
|
|
446
1384
|
actualNamedConstraints.set(c.name.toLowerCase(), c);
|
|
@@ -455,20 +1393,293 @@ function computeTableAlterDiff(declaredTable, actualTable, policy) {
|
|
|
455
1393
|
getActualTags: a => a.tags,
|
|
456
1394
|
policy,
|
|
457
1395
|
});
|
|
458
|
-
|
|
459
|
-
|
|
1396
|
+
// Adds / drops / body-change recreates. A declared constraint is matched (by
|
|
1397
|
+
// name or by rename hint) to at most one actual via `constraintRenames.pairs`:
|
|
1398
|
+
// - no match → create (ADD declared fragment)
|
|
1399
|
+
// - match, same body → no-op here (rename, if any, handled below; tags below)
|
|
1400
|
+
// - match, body changed → drop the old + add the declared (drop+recreate); a
|
|
1401
|
+
// constraint body change has no in-place "redefine"
|
|
1402
|
+
// primitive, and re-creation re-validates existing rows
|
|
1403
|
+
// against the new rule.
|
|
1404
|
+
// Precedence: when a constraint was rename-matched AND its body changed, prefer
|
|
1405
|
+
// the drop+recreate (under the declared name/def) and SUPPRESS the RENAME — one
|
|
1406
|
+
// coherent op, and the new body must re-validate regardless.
|
|
1407
|
+
const constraintsToAdd = [];
|
|
1408
|
+
const constraintsToDrop = [];
|
|
1409
|
+
const renamesSuppressedByBodyChange = new Set(); // declared lower-names
|
|
1410
|
+
const bodyChangedNames = new Set(); // declared lower-names recreated
|
|
1411
|
+
// Counts that feed the `require-hint` guard: only a PURE create + PURE drop
|
|
1412
|
+
// (the unhinted-rename shape) trips it. A same-constraint body-change drop+add
|
|
1413
|
+
// is a deliberate recreate, not an ambiguous rename, so it is excluded here.
|
|
1414
|
+
let pureCreateCount = 0;
|
|
1415
|
+
let pureDropCount = 0;
|
|
1416
|
+
for (const [lower, d] of declaredNamedConstraints) {
|
|
1417
|
+
const matchedActual = constraintRenames.pairs.get(lower);
|
|
1418
|
+
if (!matchedActual) {
|
|
1419
|
+
constraintsToAdd.push(d.ddl); // create (name- or rename-unmatched)
|
|
1420
|
+
pureCreateCount++;
|
|
1421
|
+
continue;
|
|
1422
|
+
}
|
|
1423
|
+
// Body comparison. The actual side (`matchedActual.definition`) renders the
|
|
1424
|
+
// PRE-rename identifier names (a column/parent-table rename has not landed at
|
|
1425
|
+
// diff time); the declared side uses the NEW names. So a string mismatch that
|
|
1426
|
+
// is purely a renamed identifier — already emitted as a rename in this same
|
|
1427
|
+
// diff — must NOT churn a drop+recreate. Short-circuit the common no-rename
|
|
1428
|
+
// case (raw strings equal) first; only then reconcile the declared body back
|
|
1429
|
+
// to the old names and re-compare. A genuine body edit still differs after
|
|
1430
|
+
// reconciliation, so the drop+recreate (and its rename-suppression) is kept.
|
|
1431
|
+
if (d.definition !== matchedActual.definition &&
|
|
1432
|
+
reconciledDeclaredBody(d, diff.columnsToRename, tableRenames, actualTable.name, schemaName, columnRenamesByTable, resolveDeclaredColumn) !== matchedActual.definition) {
|
|
1433
|
+
constraintsToDrop.push(matchedActual.name); // drop old
|
|
1434
|
+
constraintsToAdd.push(d.ddl); // add new (declared name + tags)
|
|
1435
|
+
bodyChangedNames.add(lower);
|
|
1436
|
+
if (matchedActual.name.toLowerCase() !== lower)
|
|
1437
|
+
renamesSuppressedByBodyChange.add(lower);
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
// Renames, minus any subsumed by a same-constraint body change.
|
|
1441
|
+
const effectiveConstraintRenames = constraintRenames.renames.filter(r => !renamesSuppressedByBodyChange.has(r.newName.toLowerCase()));
|
|
1442
|
+
if (effectiveConstraintRenames.length > 0) {
|
|
1443
|
+
diff.constraintsToRename = effectiveConstraintRenames.map(r => ({ oldName: r.oldName, newName: r.newName }));
|
|
1444
|
+
}
|
|
1445
|
+
// Drops: actual constraint neither declared nor consumed by a rename → DROP.
|
|
1446
|
+
// (A rename-consumed actual whose body also changed was already dropped above;
|
|
1447
|
+
// it stays in `consumedActuals`, so it is skipped here — no double drop.)
|
|
1448
|
+
for (const [lower, a] of actualNamedConstraints) {
|
|
1449
|
+
if (constraintRenames.consumedActuals.has(lower))
|
|
1450
|
+
continue;
|
|
1451
|
+
if (!declaredNamedConstraints.has(lower)) {
|
|
1452
|
+
constraintsToDrop.push(a.name);
|
|
1453
|
+
pureDropCount++;
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
if (constraintsToAdd.length > 0)
|
|
1457
|
+
diff.constraintsToAdd = constraintsToAdd;
|
|
1458
|
+
if (constraintsToDrop.length > 0)
|
|
1459
|
+
diff.constraintsToDrop = constraintsToDrop;
|
|
1460
|
+
// Apply require-hint to constraints within this table (a PURE add + a PURE drop
|
|
1461
|
+
// with no rename hint is the ambiguous case the policy guards — body-change
|
|
1462
|
+
// recreates are excluded from the counts).
|
|
1463
|
+
if (policy === 'require-hint') {
|
|
1464
|
+
enforceRequireHint(`constraint (${actualTable.name})`, pureCreateCount, pureDropCount);
|
|
1465
|
+
}
|
|
1466
|
+
// Detect named-constraint tag drift (name-matched constraints only — a
|
|
1467
|
+
// renamed constraint is not addressable by ALTER CONSTRAINT, see the runtime
|
|
1468
|
+
// emitter). Whole-set replacement; rename hints excluded from the compare. A
|
|
1469
|
+
// constraint that is being body-change-recreated is skipped: its recreate
|
|
1470
|
+
// fragment already carries the declared tags, so a separate SET TAGS would be
|
|
1471
|
+
// redundant (and would target a constraint that no longer exists at that point).
|
|
1472
|
+
const constraintTagsChanges = [];
|
|
1473
|
+
for (const [name, declaredConstraint] of declaredNamedConstraints) {
|
|
1474
|
+
if (bodyChangedNames.has(name))
|
|
1475
|
+
continue;
|
|
1476
|
+
const actualConstraint = actualNamedConstraints.get(name);
|
|
1477
|
+
if (!actualConstraint)
|
|
1478
|
+
continue; // a rename/create — not a same-name tag change
|
|
1479
|
+
if (tagsDrifted(declaredConstraint.tags, actualConstraint.tags)) {
|
|
1480
|
+
constraintTagsChanges.push({
|
|
1481
|
+
constraintName: declaredConstraint.name,
|
|
1482
|
+
tags: desiredTagSet(declaredConstraint.tags),
|
|
1483
|
+
});
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
if (constraintTagsChanges.length > 0) {
|
|
1487
|
+
diff.constraintTagsChanges = constraintTagsChanges;
|
|
1488
|
+
}
|
|
1489
|
+
// Detect table-level tag drift (whole-set replacement; rename hints excluded).
|
|
1490
|
+
if (tagsDrifted(declaredTable.tableStmt.tags, actualTable.tags)) {
|
|
1491
|
+
diff.tableTagsChange = desiredTagSet(declaredTable.tableStmt.tags);
|
|
460
1492
|
}
|
|
461
1493
|
// Detect PK changes
|
|
462
1494
|
const declaredPk = extractDeclaredPK(declaredTable);
|
|
463
1495
|
const actualPk = actualTable.primaryKey;
|
|
464
|
-
|
|
1496
|
+
// Inverse-rename declared PK column names (new → old) so a pure PK-column rename
|
|
1497
|
+
// — already emitted as RENAME COLUMN — does not also churn an ALTER PRIMARY KEY.
|
|
1498
|
+
// Mirrors the constraint-body reconciliation (reconciledDeclaredBody). A PK
|
|
1499
|
+
// references only THIS table's own columns, so `diff.columnsToRename` suffices —
|
|
1500
|
+
// no cross-table `columnRenamesByTable` / table renames (unlike the FK body case).
|
|
1501
|
+
// Clone first: inverseRenameConstraintColumns mutates in place, and declaredPk
|
|
1502
|
+
// backs the NEW names carried in `newPkColumns`.
|
|
1503
|
+
const reconciledDeclaredPk = declaredPk.map(c => ({ ...c }));
|
|
1504
|
+
inverseRenameConstraintColumns(reconciledDeclaredPk, diff.columnsToRename);
|
|
1505
|
+
if (!pkSequencesEqual(reconciledDeclaredPk, actualPk)) {
|
|
465
1506
|
diff.primaryKeyChange = {
|
|
466
1507
|
oldPkColumns: actualPk.map(pk => pk.columnName),
|
|
467
|
-
newPkColumns: declaredPk,
|
|
1508
|
+
newPkColumns: declaredPk, // keep NEW (declared) names for the genuine-change DDL
|
|
468
1509
|
};
|
|
469
1510
|
}
|
|
1511
|
+
// Derivation transition LAST, so a re-attach with a concurrent shape change can
|
|
1512
|
+
// observe the column / PK / constraint ops recorded above (→ detach-reshape-attach).
|
|
1513
|
+
applyMaintainedTransition(diff, declaredMaintained, liveMaintained, tableRenames, columnRenamesByTable, schemaName, resolveDeclaredColumn);
|
|
1514
|
+
markMaintainedTagRoute(diff, liveMaintained);
|
|
470
1515
|
return diff;
|
|
471
1516
|
}
|
|
1517
|
+
/**
|
|
1518
|
+
* Route a maintained table's tag change through `ALTER MATERIALIZED VIEW` (see
|
|
1519
|
+
* {@link TableAlterDiff.maintainedTags}): set the flag when a tag change exists on
|
|
1520
|
+
* a table that is live-maintained and NOT detached early in this same diff (so it
|
|
1521
|
+
* is still maintained at the moment the alter block's SET TAGS runs).
|
|
1522
|
+
*/
|
|
1523
|
+
function markMaintainedTagRoute(diff, liveMaintained) {
|
|
1524
|
+
if (diff.tableTagsChange !== undefined && liveMaintained && !diff.dropMaintained) {
|
|
1525
|
+
diff.maintainedTags = true;
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
/**
|
|
1529
|
+
* Detects a backing-module move on a name-matched maintained table: the declared
|
|
1530
|
+
* `using <module>(args)` clause normalizes to a different backing than the live
|
|
1531
|
+
* one. Returns `{ fromModule, toModule }` (normalized labels) on drift, else
|
|
1532
|
+
* `undefined`. Fires ONLY when BOTH sides are maintained — a table↔maintained
|
|
1533
|
+
* transition is an attach/detach, not a module move. The name half uses
|
|
1534
|
+
* {@link normalizeBackingModuleName} (absent/`mem` ⇒ `memory`, lowercased) and
|
|
1535
|
+
* the args half {@link canonicalBackingModuleArgs} (stable sorted-key render,
|
|
1536
|
+
* absent ⇒ ''), so the two spellings of the memory default (`using memory()` /
|
|
1537
|
+
* `using mem()` / omitted) never register as drift.
|
|
1538
|
+
*/
|
|
1539
|
+
function backingModuleDrifted(declaredMaintained, liveMaintained, declaredModuleName, declaredModuleArgs) {
|
|
1540
|
+
if (!declaredMaintained || !liveMaintained)
|
|
1541
|
+
return undefined;
|
|
1542
|
+
const declName = normalizeBackingModuleName(declaredModuleName);
|
|
1543
|
+
const liveName = normalizeBackingModuleName(liveMaintained.backingModuleName);
|
|
1544
|
+
const declArgs = canonicalBackingModuleArgs(declaredModuleArgs);
|
|
1545
|
+
const liveArgs = canonicalBackingModuleArgs(liveMaintained.backingModuleArgs);
|
|
1546
|
+
if (declName === liveName && declArgs === liveArgs)
|
|
1547
|
+
return undefined;
|
|
1548
|
+
return {
|
|
1549
|
+
fromModule: backingModuleLabel(liveName, liveArgs),
|
|
1550
|
+
toModule: backingModuleLabel(declName, declArgs),
|
|
1551
|
+
};
|
|
1552
|
+
}
|
|
1553
|
+
/** Renders a normalized backing-module identity as `name` or `name(args)` for diagnostics. */
|
|
1554
|
+
function backingModuleLabel(name, canonicalArgs) {
|
|
1555
|
+
return canonicalArgs ? `${name}(${canonicalArgs})` : name;
|
|
1556
|
+
}
|
|
1557
|
+
/**
|
|
1558
|
+
* Recognize the maintained-table (derivation) transition for a name-matched table
|
|
1559
|
+
* and record the attach / detach / re-attach op on `diff`. PLAN-FREE: it compares
|
|
1560
|
+
* only the canonical body hash (declared `maintained` clause vs the live
|
|
1561
|
+
* `derivation.bodyHash`), never the body's derived shape.
|
|
1562
|
+
*
|
|
1563
|
+
* declared maintained, live plain → attach (set maintained as)
|
|
1564
|
+
* declared plain, live maintained → detach (drop maintained)
|
|
1565
|
+
* both maintained, body hash equal → no-op (idempotent — no phantom re-attach)
|
|
1566
|
+
* both maintained, body hash drift → re-attach: a single `set maintained as`
|
|
1567
|
+
* when the table shape is unchanged (content reconcile / refresh — NOT a
|
|
1568
|
+
* recreate, so the table incarnation and unrelated rows survive); a
|
|
1569
|
+
* detach → column ops → attach when the declared shape ALSO drifted (the new
|
|
1570
|
+
* column's values arrive via the attach reconcile).
|
|
1571
|
+
*
|
|
1572
|
+
* A pure in-diff source rename is inverse-applied before the hash compare so a
|
|
1573
|
+
* rename never churns a spurious re-attach (mirrors the view/MV body reconcile).
|
|
1574
|
+
* A backing-module change is deliberately NOT compared here — it is a destructive
|
|
1575
|
+
* incarnation-minting move (drop+recreate), detected separately by
|
|
1576
|
+
* {@link backingModuleDrifted} in `computeTableAlterDiff` and routed by
|
|
1577
|
+
* `computeSchemaDiff`, never folded into this non-destructive body transition.
|
|
1578
|
+
*/
|
|
1579
|
+
function applyMaintainedTransition(diff, declaredMaintained, liveMaintained, tableRenames, columnRenamesByTable, schemaName,
|
|
1580
|
+
/** Declared-side column-existence resolver for the seeded defaults-expr rewrites (see `computeSchemaDiff`). */
|
|
1581
|
+
resolveDeclaredColumn) {
|
|
1582
|
+
if (!declaredMaintained && !liveMaintained)
|
|
1583
|
+
return;
|
|
1584
|
+
if (declaredMaintained && !liveMaintained) {
|
|
1585
|
+
// Attach: derived content reconciles the live (plain) table's rows. Any
|
|
1586
|
+
// `with defaults (…)` rides inside the body select. Carry the DECLARED rename
|
|
1587
|
+
// list (the verb renames + records explicit; `undefined` ⇒ stays implicit).
|
|
1588
|
+
diff.setMaintained = { columns: declaredMaintained.columns, select: declaredMaintained.select };
|
|
1589
|
+
return;
|
|
1590
|
+
}
|
|
1591
|
+
if (!declaredMaintained && liveMaintained) {
|
|
1592
|
+
// Detach: rows stay, maintenance stops, the table becomes writable.
|
|
1593
|
+
diff.dropMaintained = true;
|
|
1594
|
+
return;
|
|
1595
|
+
}
|
|
1596
|
+
// Both maintained — compare the canonical body hash (reconciling in-diff renames
|
|
1597
|
+
// so a pure source rename converges via the rename propagation, not a re-attach).
|
|
1598
|
+
if (maintainedBodyMatches(declaredMaintained, liveMaintained.bodyHash, tableRenames, columnRenamesByTable, schemaName, resolveDeclaredColumn)) {
|
|
1599
|
+
return; // no derivation change → no churn
|
|
1600
|
+
}
|
|
1601
|
+
// Body drift → re-attach. A concurrent shape change (column / PK / constraint
|
|
1602
|
+
// ops already on `diff`) means detach → reshape → re-attach; an unchanged shape
|
|
1603
|
+
// re-attaches in place (content reconcile / refresh). Carry the DECLARED rename
|
|
1604
|
+
// list so an explicit rename-list change (`(a, b)` → `(a, c)`) reshapes the
|
|
1605
|
+
// backing in place via the verb instead of erroring at the strict attach shape
|
|
1606
|
+
// check; `undefined` ⇒ the verb follows the body (implicit reshape).
|
|
1607
|
+
if (alterDiffHasShapeOps(diff))
|
|
1608
|
+
diff.dropMaintained = true;
|
|
1609
|
+
diff.setMaintained = { columns: declaredMaintained.columns, select: declaredMaintained.select };
|
|
1610
|
+
}
|
|
1611
|
+
/**
|
|
1612
|
+
* True when the declared maintained body canonicalizes to the live
|
|
1613
|
+
* `derivation.bodyHash` — i.e. an unchanged derivation (no re-attach).
|
|
1614
|
+
*
|
|
1615
|
+
* The recorded form is the as-authored `maintained.columns`: `undefined` for an
|
|
1616
|
+
* implicit body (MV sugar / `create table … maintained as` / the re-attach verb,
|
|
1617
|
+
* which now records implicit) and the names array for an explicit rename list
|
|
1618
|
+
* (`mv (a, b)` / `maintained (a, b) as`). Both the create path AND the re-attach
|
|
1619
|
+
* verb record the same implicit form for a body whose natural names already equal
|
|
1620
|
+
* the table columns, so a single as-authored variant suffices — there is no
|
|
1621
|
+
* create-vs-attach representation gap to absorb. The variant is also re-compared
|
|
1622
|
+
* under in-diff rename reconciliation so a pure source rename converges via the
|
|
1623
|
+
* rename propagation rather than churning a re-attach. A genuine body / clause edit
|
|
1624
|
+
* (including an explicit rename-list change b → c) fails the compare, so it is not
|
|
1625
|
+
* masked.
|
|
1626
|
+
*/
|
|
1627
|
+
function maintainedBodyMatches(declared, liveBodyHash, tableRenames, columnRenamesByTable, schemaName,
|
|
1628
|
+
/** Declared-side column-existence resolver for the seeded defaults-expr rewrites (see `computeSchemaDiff`). */
|
|
1629
|
+
resolveDeclaredColumn) {
|
|
1630
|
+
const hasRenames = tableRenames.length > 0 || columnRenamesByTable.size > 0;
|
|
1631
|
+
// The single as-authored form: implicit (`undefined`) for a sugar / verb-attached
|
|
1632
|
+
// body, or the explicit declared rename list. Both create and re-attach record the
|
|
1633
|
+
// implicit form for a same-name body, so there is no live-names fallback to add.
|
|
1634
|
+
// The body string carries any trailing `with defaults (…)` clause, so a
|
|
1635
|
+
// defaults-only edit fails the compare and a pure source rename converges.
|
|
1636
|
+
const variants = [declared.columns];
|
|
1637
|
+
for (const columns of variants) {
|
|
1638
|
+
if (computeBodyHash(viewDefinitionToCanonicalString(columns, declared.select)) === liveBodyHash)
|
|
1639
|
+
return true;
|
|
1640
|
+
if (hasRenames
|
|
1641
|
+
&& computeBodyHash(reconciledDeclaredViewDefinition(columns, declared.select, tableRenames, columnRenamesByTable, schemaName, resolveDeclaredColumn)) === liveBodyHash) {
|
|
1642
|
+
return true;
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
return false;
|
|
1646
|
+
}
|
|
1647
|
+
/** True when a table-alter diff carries any structural (column / PK / constraint) op. */
|
|
1648
|
+
function alterDiffHasShapeOps(diff) {
|
|
1649
|
+
return diff.columnsToAdd.length > 0
|
|
1650
|
+
|| diff.columnsToDrop.length > 0
|
|
1651
|
+
|| diff.columnsToAlter.length > 0
|
|
1652
|
+
|| diff.columnsToRename.length > 0
|
|
1653
|
+
|| !!diff.primaryKeyChange
|
|
1654
|
+
|| (diff.constraintsToAdd?.length ?? 0) > 0
|
|
1655
|
+
|| (diff.constraintsToDrop?.length ?? 0) > 0
|
|
1656
|
+
|| (diff.constraintsToRename?.length ?? 0) > 0;
|
|
1657
|
+
}
|
|
1658
|
+
/**
|
|
1659
|
+
* Normalize a declared `materialized view` item into the table category: a
|
|
1660
|
+
* declared table whose `maintained` clause carries the body and whose declared
|
|
1661
|
+
* column list is empty (the body owns the shape). This is what lets the differ
|
|
1662
|
+
* compare a maintained declaration per name in ONE category — a table↔maintained
|
|
1663
|
+
* transition becomes an attach/detach alter, never a cross-category drop+create.
|
|
1664
|
+
*/
|
|
1665
|
+
function materializedViewToDeclaredTable(declaredMv) {
|
|
1666
|
+
const mv = declaredMv.viewStmt;
|
|
1667
|
+
return {
|
|
1668
|
+
type: 'declaredTable',
|
|
1669
|
+
tableStmt: {
|
|
1670
|
+
type: 'createTable',
|
|
1671
|
+
table: mv.view,
|
|
1672
|
+
ifNotExists: false,
|
|
1673
|
+
columns: [],
|
|
1674
|
+
constraints: [],
|
|
1675
|
+
moduleName: mv.moduleName,
|
|
1676
|
+
moduleArgs: mv.moduleArgs,
|
|
1677
|
+
tags: mv.tags,
|
|
1678
|
+
// Any `with defaults (…)` rides inside mv.select.
|
|
1679
|
+
maintained: { columns: mv.columns, select: mv.select },
|
|
1680
|
+
},
|
|
1681
|
+
};
|
|
1682
|
+
}
|
|
472
1683
|
/**
|
|
473
1684
|
* Extract a declared column's effective nullability from its AST constraints.
|
|
474
1685
|
* Returns undefined when no explicit NULL/NOT NULL is present (session default applies).
|
|
@@ -493,6 +1704,42 @@ function extractDeclaredDefault(col) {
|
|
|
493
1704
|
const d = col.constraints.find(c => c.type === 'default');
|
|
494
1705
|
return d?.expr ?? null;
|
|
495
1706
|
}
|
|
1707
|
+
/**
|
|
1708
|
+
* Extract a declared column's effective collation from its COLLATE constraint,
|
|
1709
|
+
* canonicalized (uppercase). When no COLLATE is declared, resolves the
|
|
1710
|
+
* `defaultCollation` exactly as the engine's CREATE path does
|
|
1711
|
+
* ({@link resolveDefaultCollation}) — so absent COLLATE and an explicit
|
|
1712
|
+
* `COLLATE <default>` compare equal against the actual catalog collation, and an
|
|
1713
|
+
* `apply schema` under a non-BINARY default stays idempotent (the live catalog
|
|
1714
|
+
* column already carries the resolved default; resolving the declared side to the
|
|
1715
|
+
* same value avoids a spurious `SET COLLATE`). `defaultCollation` is threaded from
|
|
1716
|
+
* the live session — the cross-session rehydrate concern stays fixed-BINARY on the
|
|
1717
|
+
* `importTable` path, not here.
|
|
1718
|
+
*/
|
|
1719
|
+
function extractDeclaredCollation(col, defaultCollation) {
|
|
1720
|
+
const c = col.constraints?.find(c => c.type === 'collate');
|
|
1721
|
+
if (c)
|
|
1722
|
+
return c.collation ? normalizeCollationName(c.collation) : 'BINARY';
|
|
1723
|
+
return resolveDefaultCollation(inferType(col.dataType), defaultCollation);
|
|
1724
|
+
}
|
|
1725
|
+
/**
|
|
1726
|
+
* Returns `col` unchanged when it already declares an explicit COLLATE, or when the
|
|
1727
|
+
* session `default_collation` resolves to BINARY for its type; otherwise returns a
|
|
1728
|
+
* shallow clone with an explicit `{ type: 'collate', collation: <resolved> }`
|
|
1729
|
+
* constraint appended. Used by the ADD COLUMN emission so generated DDL carries an
|
|
1730
|
+
* explicit non-BINARY collation rather than relying on the executing session's
|
|
1731
|
+
* default — this is what makes `apply schema` idempotent and `diff schema` output
|
|
1732
|
+
* portable across sessions with different defaults. Never mutates the declared AST
|
|
1733
|
+
* (clones the `constraints` array).
|
|
1734
|
+
*/
|
|
1735
|
+
function withResolvedAddColumnCollation(col, defaultCollation) {
|
|
1736
|
+
if (col.constraints?.some(c => c.type === 'collate'))
|
|
1737
|
+
return col;
|
|
1738
|
+
const resolved = resolveDefaultCollation(inferType(col.dataType), defaultCollation);
|
|
1739
|
+
if (resolved === 'BINARY')
|
|
1740
|
+
return col;
|
|
1741
|
+
return { ...col, constraints: [...(col.constraints ?? []), { type: 'collate', collation: resolved }] };
|
|
1742
|
+
}
|
|
496
1743
|
/**
|
|
497
1744
|
* Structural equality for DEFAULT expressions. Compares AST shape by
|
|
498
1745
|
* JSON serialization with a stable key order — adequate for literals
|
|
@@ -514,7 +1761,38 @@ function stableStringify(v) {
|
|
|
514
1761
|
const keys = Object.keys(obj).filter(k => k !== 'loc').sort();
|
|
515
1762
|
return `{${keys.map(k => JSON.stringify(k) + ':' + stableStringify(obj[k])).join(',')}}`;
|
|
516
1763
|
}
|
|
517
|
-
|
|
1764
|
+
/**
|
|
1765
|
+
* Rename-hint keys excluded from tag-drift comparison: they drive rename
|
|
1766
|
+
* detection ({@link resolveRenames}), not data state, so a tag set carrying only
|
|
1767
|
+
* a hint must not churn out a `SET TAGS` after the rename completes. All other
|
|
1768
|
+
* reserved tags (`quereus.lens.*`, `quereus.expose_implicit_index`, …) are real
|
|
1769
|
+
* schema state and ARE compared.
|
|
1770
|
+
*/
|
|
1771
|
+
const RENAME_HINT_KEYS = new Set([QUEREUS_TAG_PREFIX + 'id', QUEREUS_TAG_PREFIX + 'previous_name']);
|
|
1772
|
+
/** A tag record with the rename-hint keys stripped, for drift comparison. */
|
|
1773
|
+
function tagsForDriftCompare(tags) {
|
|
1774
|
+
const out = {};
|
|
1775
|
+
if (tags) {
|
|
1776
|
+
for (const [k, v] of Object.entries(tags)) {
|
|
1777
|
+
if (!RENAME_HINT_KEYS.has(k.toLowerCase()))
|
|
1778
|
+
out[k] = v;
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
return out;
|
|
1782
|
+
}
|
|
1783
|
+
/**
|
|
1784
|
+
* True when the declared and actual tag sets differ (order-independent, rename
|
|
1785
|
+
* hints ignored). On drift the differ emits a `SET TAGS` carrying the full
|
|
1786
|
+
* declared set (hints included — they are stored verbatim).
|
|
1787
|
+
*/
|
|
1788
|
+
function tagsDrifted(declared, actual) {
|
|
1789
|
+
return stableStringify(tagsForDriftCompare(declared)) !== stableStringify(tagsForDriftCompare(actual));
|
|
1790
|
+
}
|
|
1791
|
+
/** The whole-set replacement value to emit for a drifted tag site (declared, or `{}`). */
|
|
1792
|
+
function desiredTagSet(declared) {
|
|
1793
|
+
return declared ? { ...declared } : {};
|
|
1794
|
+
}
|
|
1795
|
+
function computeColumnAttributeChange(declared, actual, defaultCollation) {
|
|
518
1796
|
const change = { columnName: declared.name };
|
|
519
1797
|
let any = false;
|
|
520
1798
|
// Nullability — only compare when explicitly declared; session default handles unspecified.
|
|
@@ -542,6 +1820,19 @@ function computeColumnAttributeChange(declared, actual) {
|
|
|
542
1820
|
change.defaultValue = null;
|
|
543
1821
|
any = true;
|
|
544
1822
|
}
|
|
1823
|
+
// Collation — declared COLLATE (default BINARY) vs actual, case-insensitive.
|
|
1824
|
+
// Absent and BINARY are equal, so a column that never mentions COLLATE never
|
|
1825
|
+
// churns a diff against an actual BINARY column.
|
|
1826
|
+
const declaredCollation = extractDeclaredCollation(declared, defaultCollation);
|
|
1827
|
+
if (declaredCollation !== (actual.collation || 'BINARY').toUpperCase()) {
|
|
1828
|
+
change.collation = declaredCollation;
|
|
1829
|
+
any = true;
|
|
1830
|
+
}
|
|
1831
|
+
// Tag drift — whole-set replacement (rename hints excluded from the compare).
|
|
1832
|
+
if (tagsDrifted(declared.tags, actual.tags)) {
|
|
1833
|
+
change.tags = desiredTagSet(declared.tags);
|
|
1834
|
+
any = true;
|
|
1835
|
+
}
|
|
545
1836
|
return any ? change : undefined;
|
|
546
1837
|
}
|
|
547
1838
|
function extractDeclaredPK(declaredTable) {
|
|
@@ -632,22 +1923,32 @@ export function generateMigrationDDL(diff, schemaName) {
|
|
|
632
1923
|
const schemaPrefix = (schemaName && schemaName !== 'main') ? `${quoteIdentifier(schemaName)}.` : '';
|
|
633
1924
|
// Renames first — they free old names for subsequent creates and re-target
|
|
634
1925
|
// dependents (handled inside ALTER TABLE ... RENAME by the rename rewriter).
|
|
635
|
-
//
|
|
636
|
-
//
|
|
637
|
-
//
|
|
638
|
-
//
|
|
639
|
-
//
|
|
1926
|
+
// Only tables have a rename primitive. The other RenameOp kinds are metadata
|
|
1927
|
+
// here: a hinted view/index rename's convergence DDL is emitted by
|
|
1928
|
+
// computeSchemaDiff itself as drop(old) + recreate(declared) through the
|
|
1929
|
+
// standard viewsToDrop/viewsToCreate / indexesToDrop/indexesToCreate buckets
|
|
1930
|
+
// (whether or not the definition also changed), and a constraint rename rides
|
|
1931
|
+
// the table-alter channel (RENAME CONSTRAINT below).
|
|
640
1932
|
for (const r of diff.renames) {
|
|
641
1933
|
if (r.kind === 'table') {
|
|
642
1934
|
statements.push(`ALTER TABLE ${schemaPrefix}${quoteIdentifier(r.oldName)} RENAME TO ${quoteIdentifier(r.newName)}`);
|
|
643
1935
|
}
|
|
644
|
-
//
|
|
645
|
-
// drop+recreate via the standard buckets.
|
|
1936
|
+
// Non-table rename ops emit no DDL here — see the note above.
|
|
646
1937
|
}
|
|
647
1938
|
// Drop assertions first (they may reference tables)
|
|
648
1939
|
for (const name of diff.assertionsToDrop) {
|
|
649
1940
|
statements.push(`DROP ASSERTION IF EXISTS ${schemaPrefix}${quoteIdentifier(name)}`);
|
|
650
1941
|
}
|
|
1942
|
+
// Detach maintained tables (`drop maintained`) EARLY — where MV drops ran
|
|
1943
|
+
// before — so a detach precedes any column op on that table AND any drop of its
|
|
1944
|
+
// former source in the same migration. This is the reshape leg of a re-attach
|
|
1945
|
+
// (detach → column ops → re-attach) and the standalone detach transition; the
|
|
1946
|
+
// flip's cross-table detach-v2 / attach-v1 pair falls out of this ordering.
|
|
1947
|
+
for (const alter of diff.tablesToAlter) {
|
|
1948
|
+
if (alter.dropMaintained) {
|
|
1949
|
+
statements.push(`ALTER TABLE ${schemaPrefix}${quoteIdentifier(alter.tableName)} DROP MAINTAINED`);
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
651
1952
|
// Drop items (reverse order)
|
|
652
1953
|
for (const tableName of diff.tablesToDrop) {
|
|
653
1954
|
statements.push(`DROP TABLE IF EXISTS ${schemaPrefix}${quoteIdentifier(tableName)}`);
|
|
@@ -658,7 +1959,9 @@ export function generateMigrationDDL(diff, schemaName) {
|
|
|
658
1959
|
for (const indexName of diff.indexesToDrop) {
|
|
659
1960
|
statements.push(`DROP INDEX IF EXISTS ${schemaPrefix}${quoteIdentifier(indexName)}`);
|
|
660
1961
|
}
|
|
661
|
-
// Create new items
|
|
1962
|
+
// Create new items. A fresh maintained table rides `tablesToCreate` (rendered
|
|
1963
|
+
// as the `create materialized view` sugar) and re-materializes as part of its
|
|
1964
|
+
// create.
|
|
662
1965
|
statements.push(...diff.tablesToCreate);
|
|
663
1966
|
statements.push(...diff.viewsToCreate);
|
|
664
1967
|
statements.push(...diff.indexesToCreate);
|
|
@@ -669,7 +1972,10 @@ export function generateMigrationDDL(diff, schemaName) {
|
|
|
669
1972
|
// → ADD COLUMN
|
|
670
1973
|
// → ALTER COLUMN (type, then default, then nullability — so SET NOT NULL
|
|
671
1974
|
// can rely on an already-populated DEFAULT for backfill)
|
|
1975
|
+
// → RENAME CONSTRAINT, then DROP CONSTRAINT (free / remove a name before any
|
|
1976
|
+
// re-add; a UNIQUE drop precedes the PK change so it can't strand a PK dep)
|
|
672
1977
|
// → ALTER PRIMARY KEY
|
|
1978
|
+
// → ADD CONSTRAINT (after the PK change and the column adds it may reference)
|
|
673
1979
|
// → DROP COLUMN (last, so NOT NULL relaxation never blocks subsequent drops)
|
|
674
1980
|
for (const alter of diff.tablesToAlter) {
|
|
675
1981
|
const quotedTable = `${schemaPrefix}${quoteIdentifier(alter.tableName)}`;
|
|
@@ -684,6 +1990,11 @@ export function generateMigrationDDL(diff, schemaName) {
|
|
|
684
1990
|
if (colAlter.dataType !== undefined) {
|
|
685
1991
|
statements.push(`ALTER TABLE ${quotedTable} ALTER COLUMN ${quotedCol} SET DATA TYPE ${colAlter.dataType}`);
|
|
686
1992
|
}
|
|
1993
|
+
// SET COLLATE right after SET DATA TYPE (both are comparison-domain
|
|
1994
|
+
// changes), before DEFAULT / NOT NULL.
|
|
1995
|
+
if (colAlter.collation !== undefined) {
|
|
1996
|
+
statements.push(`ALTER TABLE ${quotedTable} ALTER COLUMN ${quotedCol} SET COLLATE ${colAlter.collation}`);
|
|
1997
|
+
}
|
|
687
1998
|
if (colAlter.defaultValue !== undefined) {
|
|
688
1999
|
if (colAlter.defaultValue === null) {
|
|
689
2000
|
statements.push(`ALTER TABLE ${quotedTable} ALTER COLUMN ${quotedCol} DROP DEFAULT`);
|
|
@@ -698,6 +2009,15 @@ export function generateMigrationDDL(diff, schemaName) {
|
|
|
698
2009
|
: `ALTER TABLE ${quotedTable} ALTER COLUMN ${quotedCol} DROP NOT NULL`);
|
|
699
2010
|
}
|
|
700
2011
|
}
|
|
2012
|
+
// Constraint lifecycle: RENAME (free a name) then DROP (remove a stale /
|
|
2013
|
+
// conflicting constraint), both BEFORE re-adds and before the PK change so a
|
|
2014
|
+
// dropped UNIQUE can't strand a PK dependency.
|
|
2015
|
+
for (const r of alter.constraintsToRename ?? []) {
|
|
2016
|
+
statements.push(`ALTER TABLE ${quotedTable} RENAME CONSTRAINT ${quoteIdentifier(r.oldName)} TO ${quoteIdentifier(r.newName)}`);
|
|
2017
|
+
}
|
|
2018
|
+
for (const name of alter.constraintsToDrop ?? []) {
|
|
2019
|
+
statements.push(`ALTER TABLE ${quotedTable} DROP CONSTRAINT ${quoteIdentifier(name)}`);
|
|
2020
|
+
}
|
|
701
2021
|
if (alter.primaryKeyChange) {
|
|
702
2022
|
const pkCols = alter.primaryKeyChange.newPkColumns
|
|
703
2023
|
.map(c => {
|
|
@@ -709,9 +2029,71 @@ export function generateMigrationDDL(diff, schemaName) {
|
|
|
709
2029
|
.join(', ');
|
|
710
2030
|
statements.push(`ALTER TABLE ${quotedTable} ALTER PRIMARY KEY (${pkCols})`);
|
|
711
2031
|
}
|
|
2032
|
+
// ADD CONSTRAINT after the PK change (a new UNIQUE / FK may align with the new
|
|
2033
|
+
// key) and after the column adds it may reference. CHECK adds apply in-place;
|
|
2034
|
+
// UNIQUE / FK adds depend on module ADD CONSTRAINT support (see constraintsToAdd).
|
|
2035
|
+
for (const frag of alter.constraintsToAdd ?? []) {
|
|
2036
|
+
statements.push(`ALTER TABLE ${quotedTable} ADD ${frag}`);
|
|
2037
|
+
}
|
|
712
2038
|
for (const colName of alter.columnsToDrop) {
|
|
713
2039
|
statements.push(`ALTER TABLE ${quotedTable} DROP COLUMN ${quoteIdentifier(colName)}`);
|
|
714
2040
|
}
|
|
2041
|
+
// Tags phase — last, so a SET TAGS lands on the post-structural column /
|
|
2042
|
+
// constraint set (a tag set emitted alongside a RENAME COLUMN targets the
|
|
2043
|
+
// post-rename name). Whole-set replacement; an empty set clears.
|
|
2044
|
+
if (alter.tableTagsChange !== undefined) {
|
|
2045
|
+
// A maintained table's tag edit must use ALTER MATERIALIZED VIEW (fires
|
|
2046
|
+
// materialized_view_modified so a store catalog re-persists; the ALTER
|
|
2047
|
+
// TABLE handler rejects a tag action on a maintained table). See
|
|
2048
|
+
// `TableAlterDiff.maintainedTags`.
|
|
2049
|
+
const tagVerb = alter.maintainedTags ? 'ALTER MATERIALIZED VIEW' : 'ALTER TABLE';
|
|
2050
|
+
statements.push(`${tagVerb} ${quotedTable} SET TAGS ${tagsBodyToString(alter.tableTagsChange)}`);
|
|
2051
|
+
}
|
|
2052
|
+
for (const colAlter of alter.columnsToAlter) {
|
|
2053
|
+
if (colAlter.tags === undefined)
|
|
2054
|
+
continue;
|
|
2055
|
+
statements.push(`ALTER TABLE ${quotedTable} ALTER COLUMN ${quoteIdentifier(colAlter.columnName)} SET TAGS ${tagsBodyToString(colAlter.tags)}`);
|
|
2056
|
+
}
|
|
2057
|
+
for (const ctc of alter.constraintTagsChanges ?? []) {
|
|
2058
|
+
statements.push(`ALTER TABLE ${quotedTable} ALTER CONSTRAINT ${quoteIdentifier(ctc.constraintName)} SET TAGS ${tagsBodyToString(ctc.tags)}`);
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
2061
|
+
// Attach / re-attach maintained tables (`set maintained as`) LATE — where MV
|
|
2062
|
+
// creates ran — after every table create/alter, so the target's final shape and
|
|
2063
|
+
// the body's sources already exist (and any reshape leg's `drop maintained` +
|
|
2064
|
+
// column ops have run). A same-shape re-attach reconciles content in place (a
|
|
2065
|
+
// refresh, not a recreate). Rendered via `astToString` over a synthetic
|
|
2066
|
+
// `set maintained as` action so the body QueryExpr round-trips exactly.
|
|
2067
|
+
for (const alter of diff.tablesToAlter) {
|
|
2068
|
+
if (!alter.setMaintained)
|
|
2069
|
+
continue;
|
|
2070
|
+
const stmt = {
|
|
2071
|
+
type: 'alterTable',
|
|
2072
|
+
table: schemaName && schemaName !== 'main'
|
|
2073
|
+
? { type: 'identifier', name: alter.tableName, schema: schemaName }
|
|
2074
|
+
: { type: 'identifier', name: alter.tableName },
|
|
2075
|
+
action: {
|
|
2076
|
+
// Any `with defaults (…)` rides inside the body select. The `(cols)`
|
|
2077
|
+
// rename list rides too when the DECLARED form is explicit (`astToString`
|
|
2078
|
+
// renders it; `undefined` ⇒ the bare `set maintained as` implicit form).
|
|
2079
|
+
type: 'setMaintained',
|
|
2080
|
+
columns: alter.setMaintained.columns,
|
|
2081
|
+
select: alter.setMaintained.select,
|
|
2082
|
+
},
|
|
2083
|
+
};
|
|
2084
|
+
statements.push(astToString(stmt));
|
|
2085
|
+
}
|
|
2086
|
+
// In-place tag changes on views / indexes. These are leaf metadata writes (no
|
|
2087
|
+
// dependency ordering vs the table-alter block). The `?? []` keeps
|
|
2088
|
+
// generateMigrationDDL robust against hand-built diffs (some tests construct
|
|
2089
|
+
// partial SchemaDiff literals), mirroring `constraintTagsChanges`. A maintained
|
|
2090
|
+
// table's tag-only change rides the table-alter block above (`SET TAGS`), not a
|
|
2091
|
+
// separate bucket.
|
|
2092
|
+
for (const vtc of diff.viewTagsChanges ?? []) {
|
|
2093
|
+
statements.push(`ALTER VIEW ${schemaPrefix}${quoteIdentifier(vtc.name)} SET TAGS ${tagsBodyToString(vtc.tags)}`);
|
|
2094
|
+
}
|
|
2095
|
+
for (const itc of diff.indexTagsChanges ?? []) {
|
|
2096
|
+
statements.push(`ALTER INDEX ${schemaPrefix}${quoteIdentifier(itc.name)} SET TAGS ${tagsBodyToString(itc.tags)}`);
|
|
715
2097
|
}
|
|
716
2098
|
return statements;
|
|
717
2099
|
}
|