@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,12 +1,16 @@
|
|
|
1
|
-
import { buildColumnIndexMap, columnDefToSchema } from '../../../schema/table.js';
|
|
1
|
+
import { buildColumnIndexMap, columnDefToSchema, resolvePkDefaultConflict, resolveNamedConstraintClass, validateCollationForType } from '../../../schema/table.js';
|
|
2
|
+
import { BTree } from 'inheritree';
|
|
2
3
|
import { StatusCode } from '../../../common/types.js';
|
|
3
4
|
import { BaseLayer } from './base.js';
|
|
4
5
|
import { TransactionLayer } from './transaction.js';
|
|
5
6
|
import { MemoryTableConnection } from './connection.js';
|
|
7
|
+
import { MemoryVirtualTableConnection } from '../connection.js';
|
|
6
8
|
import { Latches } from '../../../util/latches.js';
|
|
7
9
|
import { QuereusError } from '../../../common/errors.js';
|
|
8
10
|
import { ConflictResolution } from '../../../common/constants.js';
|
|
9
|
-
import {
|
|
11
|
+
import { buildUniqueConstraintSchema, buildForeignKeyConstraintSchema, buildCheckConstraintSchema, validateForeignKeyOverExistingRows, maintainedTableUniqueViolationError } from '../../../schema/constraint-builder.js';
|
|
12
|
+
import { uniqueEnforcementCollations } from '../../../schema/unique-enforcement.js';
|
|
13
|
+
import { compareSqlValues, rowsValueIdentical, normalizeCollationName } from '../../../util/comparison.js';
|
|
10
14
|
import { scanLayer as scanLayerImpl } from './scan-layer.js';
|
|
11
15
|
import { createPrimaryKeyFunctions, buildPrimaryKeyFromValues } from '../utils/primary-key.js';
|
|
12
16
|
import { createMemoryTableLoggers } from '../utils/logging.js';
|
|
@@ -28,6 +32,15 @@ export class MemoryTableManager {
|
|
|
28
32
|
isReadOnly;
|
|
29
33
|
tableSchema;
|
|
30
34
|
primaryKeyFunctions;
|
|
35
|
+
/**
|
|
36
|
+
* Implicit covering structures: constraint identity → the auto-index that
|
|
37
|
+
* realizes it. The physical structure is the synchronously-maintained
|
|
38
|
+
* secondary BTree; this association lets `findIndexForConstraint` and
|
|
39
|
+
* introspection speak the materialized-view vocabulary (an `origin`) for the
|
|
40
|
+
* implicit structure, the same way an explicit covering MV is described.
|
|
41
|
+
* Keyed by constraint name when present, else by the auto-index name.
|
|
42
|
+
*/
|
|
43
|
+
implicitCoveringStructures = new Map();
|
|
31
44
|
/** Optional event emitter for mutation and schema hooks */
|
|
32
45
|
eventEmitter;
|
|
33
46
|
constructor(db, _moduleName, schemaName, tableName, initialSchema, readOnly = false, eventEmitter) {
|
|
@@ -38,7 +51,17 @@ export class MemoryTableManager {
|
|
|
38
51
|
this.tableSchema = initialSchema;
|
|
39
52
|
this.isReadOnly = readOnly;
|
|
40
53
|
this.eventEmitter = eventEmitter;
|
|
41
|
-
|
|
54
|
+
// Phase D (docs/lens.md § Departures — Auto-index for unique/PK): the legacy
|
|
55
|
+
// eager auto-index for UNIQUE constraints is a *physical*-schema behavior. A
|
|
56
|
+
// logical schema's UNIQUE contributes only a key/FD to the optimizer and an
|
|
57
|
+
// enforced boundary constraint — it creates NO structure; any covering index
|
|
58
|
+
// is an explicit basis-layer materialized view. Logical tables are never
|
|
59
|
+
// module-backed (a MemoryTableManager is never constructed for one), so this
|
|
60
|
+
// path is already unreachable for them — we gate explicitly regardless, so the
|
|
61
|
+
// separation is enforced at the source rather than relying on that invariant.
|
|
62
|
+
if (!this.isLogicalSchema()) {
|
|
63
|
+
this.ensureUniqueConstraintIndexes();
|
|
64
|
+
}
|
|
42
65
|
this.initializePrimaryKeyFunctions();
|
|
43
66
|
this.baseLayer = new BaseLayer(this.tableSchema);
|
|
44
67
|
this._currentCommittedLayer = this.baseLayer;
|
|
@@ -46,10 +69,27 @@ export class MemoryTableManager {
|
|
|
46
69
|
initializePrimaryKeyFunctions() {
|
|
47
70
|
this.primaryKeyFunctions = createPrimaryKeyFunctions(this.tableSchema);
|
|
48
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* One-bit guard on `Schema.kind` (docs/lens.md § Departures): true when this
|
|
74
|
+
* table belongs to a logical schema. Prefers the table's own `isLogical` flag
|
|
75
|
+
* and falls back to the owning schema's `kind`, so the gate holds even if a
|
|
76
|
+
* logical TableSchema were ever (incorrectly) handed to a memory manager.
|
|
77
|
+
*/
|
|
78
|
+
isLogicalSchema() {
|
|
79
|
+
if (this.tableSchema.isLogical === true)
|
|
80
|
+
return true;
|
|
81
|
+
return this.db.schemaManager.getSchema(this.schemaName)?.kind === 'logical';
|
|
82
|
+
}
|
|
49
83
|
/**
|
|
50
84
|
* Auto-creates secondary indexes for UNIQUE constraints that don't already
|
|
51
85
|
* have a matching index. This mirrors standard SQL behavior where UNIQUE
|
|
52
86
|
* constraints imply an index for efficient enforcement.
|
|
87
|
+
*
|
|
88
|
+
* Alongside each such index, records an *implicit covering structure*
|
|
89
|
+
* descriptor in {@link implicitCoveringStructures} (the materialized-view
|
|
90
|
+
* vocabulary) so the implicit BTree and a future explicit covering MV share
|
|
91
|
+
* one schema shape. The physical structure is unchanged — observation-
|
|
92
|
+
* equivalent, zero behavioral difference.
|
|
53
93
|
*/
|
|
54
94
|
ensureUniqueConstraintIndexes() {
|
|
55
95
|
const uniqueConstraints = this.tableSchema.uniqueConstraints;
|
|
@@ -59,18 +99,34 @@ export class MemoryTableManager {
|
|
|
59
99
|
const newIndexes = [...existingIndexes];
|
|
60
100
|
let added = false;
|
|
61
101
|
for (const uc of uniqueConstraints) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
102
|
+
// Reuse an existing same-column-set index ONLY when its per-column
|
|
103
|
+
// collations are equivalent to the declared column collations — otherwise
|
|
104
|
+
// it would enforce this non-derived UC under the index's collation rather
|
|
105
|
+
// than the declared one. A collation-mismatched index falls through to a
|
|
106
|
+
// distinct `_uc_*` covering index and coexists as its own constraint.
|
|
107
|
+
const matchingIndex = existingIndexes.find(idx => idx.columns.length === uc.columns.length &&
|
|
108
|
+
idx.columns.every((col, i) => col.index === uc.columns[i]) &&
|
|
109
|
+
this.indexCollationsMatchDeclared(idx, uc));
|
|
110
|
+
let indexName;
|
|
111
|
+
if (matchingIndex) {
|
|
112
|
+
indexName = matchingIndex.name;
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
65
115
|
const colNames = uc.columns.map(i => this.tableSchema.columns[i]?.name ?? String(i));
|
|
66
|
-
|
|
116
|
+
indexName = uc.name ?? `_uc_${colNames.join('_')}`;
|
|
67
117
|
newIndexes.push({
|
|
68
118
|
name: indexName,
|
|
69
|
-
|
|
119
|
+
// Carry each column's declared collation so the auto-index — and the
|
|
120
|
+
// `checkUniqueViaIndex` path it backs — enforces UNIQUE under the column's
|
|
121
|
+
// collation (e.g. NOCASE) rather than defaulting to BINARY.
|
|
122
|
+
columns: uc.columns.map(colIdx => ({ index: colIdx, collation: this.tableSchema.columns[colIdx]?.collation })),
|
|
70
123
|
predicate: uc.predicate,
|
|
71
124
|
});
|
|
72
125
|
added = true;
|
|
73
126
|
}
|
|
127
|
+
// Reframe the (auto or pre-existing) secondary index as the implicit
|
|
128
|
+
// covering structure realizing this constraint.
|
|
129
|
+
this.implicitCoveringStructures.set(uc.name ?? indexName, { indexName, origin: 'implicit-from-unique-constraint' });
|
|
74
130
|
}
|
|
75
131
|
if (added) {
|
|
76
132
|
this.tableSchema = {
|
|
@@ -79,6 +135,48 @@ export class MemoryTableManager {
|
|
|
79
135
|
};
|
|
80
136
|
}
|
|
81
137
|
}
|
|
138
|
+
/**
|
|
139
|
+
* Returns the implicit covering structure realizing the given UNIQUE
|
|
140
|
+
* constraint, or undefined when none was synthesized. Part of the unified
|
|
141
|
+
* covering-structure surface the lens layer and introspection consume — the
|
|
142
|
+
* physical structure is the synchronously-maintained secondary BTree named
|
|
143
|
+
* {@link ImplicitCoveringStructure.indexName}.
|
|
144
|
+
*/
|
|
145
|
+
getImplicitCoveringStructure(uc) {
|
|
146
|
+
const indexName = uc.name ?? this.implicitIndexNameFor(uc);
|
|
147
|
+
return this.implicitCoveringStructures.get(indexName);
|
|
148
|
+
}
|
|
149
|
+
/** Conventional auto-index name for an unnamed UNIQUE constraint (mirrors {@link ensureUniqueConstraintIndexes}). */
|
|
150
|
+
implicitIndexNameFor(uc) {
|
|
151
|
+
const colNames = uc.columns.map(i => this.tableSchema.columns[i]?.name ?? String(i));
|
|
152
|
+
return `_uc_${colNames.join('_')}`;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* True when a same-column-set index's per-column collations are
|
|
156
|
+
* collation-equivalent to the constraint's DECLARED column collations.
|
|
157
|
+
*
|
|
158
|
+
* Gates REUSE of an existing same-column-set index as a non-derived UNIQUE's
|
|
159
|
+
* realizing structure. A non-derived (table-level / column) UNIQUE enforces
|
|
160
|
+
* under the declared column collation, so reusing a finer/coarser-collated
|
|
161
|
+
* same-column-set index (e.g. a BINARY `create unique index` over a NOCASE
|
|
162
|
+
* column) would silently enforce under the index's collation instead. When
|
|
163
|
+
* this returns false the caller builds the distinct `_uc_*` covering index and
|
|
164
|
+
* lets the user index coexist as an independent constraint (matches SQLite,
|
|
165
|
+
* where both indexes enforce).
|
|
166
|
+
*
|
|
167
|
+
* Positions align because the column SET already matched
|
|
168
|
+
* (`idx.columns[i]` ↔ `uc.columns[i]`). A plain index column with no explicit
|
|
169
|
+
* COLLATE has `collation === undefined` and falls back to the declared
|
|
170
|
+
* collation, so the common case stays reuse-safe.
|
|
171
|
+
*/
|
|
172
|
+
indexCollationsMatchDeclared(idx, uc) {
|
|
173
|
+
const columns = this.tableSchema.columns;
|
|
174
|
+
return uc.columns.every((colIdx, i) => {
|
|
175
|
+
const declared = normalizeCollationName(columns[colIdx]?.collation ?? 'BINARY');
|
|
176
|
+
const indexColl = normalizeCollationName(idx.columns[i]?.collation ?? columns[colIdx]?.collation ?? 'BINARY');
|
|
177
|
+
return indexColl === declared;
|
|
178
|
+
});
|
|
179
|
+
}
|
|
82
180
|
/**
|
|
83
181
|
* Get the event emitter if one was provided.
|
|
84
182
|
*/
|
|
@@ -545,12 +643,16 @@ export class MemoryTableManager {
|
|
|
545
643
|
existingRow: existingRow
|
|
546
644
|
};
|
|
547
645
|
}
|
|
548
|
-
// Check UNIQUE constraints against secondary indexes
|
|
549
|
-
|
|
646
|
+
// Check UNIQUE constraints against secondary indexes. Secondary-UNIQUE
|
|
647
|
+
// REPLACE evictions (rows at OTHER PKs) accumulate in `evicted` and are
|
|
648
|
+
// surfaced via `evictedRows` so the DML executor runs the full delete
|
|
649
|
+
// pipeline (change-tracking, row-time MV maintenance, FK cascade, events).
|
|
650
|
+
const evicted = [];
|
|
651
|
+
const ucResult = await this.checkUniqueConstraints(targetLayer, schema, newRowData, primaryKey, onConflict, evicted);
|
|
550
652
|
if (ucResult)
|
|
551
653
|
return ucResult;
|
|
552
654
|
targetLayer.recordUpsert(primaryKey, newRowData, null);
|
|
553
|
-
return { status: 'ok', row: newRowData };
|
|
655
|
+
return { status: 'ok', row: newRowData, evictedRows: evicted.length > 0 ? evicted : undefined };
|
|
554
656
|
}
|
|
555
657
|
async performUpdate(targetLayer, values, oldKeyValues, onConflict) {
|
|
556
658
|
if (!values || !oldKeyValues) {
|
|
@@ -583,17 +685,20 @@ export class MemoryTableManager {
|
|
|
583
685
|
return this.performUpdateWithPrimaryKeyChange(targetLayer, schema, targetPrimaryKey, newPrimaryKey, oldRowData, newRowData, onConflict);
|
|
584
686
|
}
|
|
585
687
|
else {
|
|
586
|
-
// Check UNIQUE constraints if any constrained columns changed
|
|
688
|
+
// Check UNIQUE constraints if any constrained columns changed. A
|
|
689
|
+
// secondary-UNIQUE REPLACE evicts the conflicting row(s) at other PKs;
|
|
690
|
+
// surface them via `evictedRows` for the executor's delete pipeline.
|
|
691
|
+
const evicted = [];
|
|
587
692
|
if (this.uniqueColumnsChanged(schema, oldRowData, newRowData)) {
|
|
588
|
-
const ucResult = this.checkUniqueConstraints(targetLayer, schema, newRowData, targetPrimaryKey, onConflict);
|
|
693
|
+
const ucResult = await this.checkUniqueConstraints(targetLayer, schema, newRowData, targetPrimaryKey, onConflict, evicted);
|
|
589
694
|
if (ucResult)
|
|
590
695
|
return ucResult;
|
|
591
696
|
}
|
|
592
697
|
targetLayer.recordUpsert(targetPrimaryKey, newRowData, oldRowData);
|
|
593
|
-
return { status: 'ok', row: newRowData };
|
|
698
|
+
return { status: 'ok', row: newRowData, evictedRows: evicted.length > 0 ? evicted : undefined };
|
|
594
699
|
}
|
|
595
700
|
}
|
|
596
|
-
performUpdateWithPrimaryKeyChange(targetLayer, schema, oldPrimaryKey, newPrimaryKey, oldRowData, newRowData, onConflict) {
|
|
701
|
+
async performUpdateWithPrimaryKeyChange(targetLayer, schema, oldPrimaryKey, newPrimaryKey, oldRowData, newRowData, onConflict) {
|
|
597
702
|
const existingRowAtNewKey = this.lookupEffectiveRow(newPrimaryKey, targetLayer);
|
|
598
703
|
if (existingRowAtNewKey !== null) {
|
|
599
704
|
const pkAction = onConflict ?? resolvePkDefaultConflict(schema) ?? ConflictResolution.ABORT;
|
|
@@ -615,16 +720,19 @@ export class MemoryTableManager {
|
|
|
615
720
|
existingRow: existingRowAtNewKey
|
|
616
721
|
};
|
|
617
722
|
}
|
|
618
|
-
// Delete old row first, then check UNIQUE constraints at the new position
|
|
723
|
+
// Delete old row first, then check UNIQUE constraints at the new position.
|
|
724
|
+
// A secondary-UNIQUE REPLACE at the new position evicts conflicting row(s)
|
|
725
|
+
// at other PKs; surface them via `evictedRows` for the executor pipeline.
|
|
619
726
|
targetLayer.recordDelete(oldPrimaryKey, oldRowData);
|
|
620
|
-
const
|
|
727
|
+
const evicted = [];
|
|
728
|
+
const ucResult = await this.checkUniqueConstraints(targetLayer, schema, newRowData, newPrimaryKey, onConflict, evicted);
|
|
621
729
|
if (ucResult) {
|
|
622
730
|
// Rollback the delete if constraint check fails
|
|
623
731
|
targetLayer.recordUpsert(oldPrimaryKey, oldRowData, null);
|
|
624
732
|
return ucResult;
|
|
625
733
|
}
|
|
626
734
|
targetLayer.recordUpsert(newPrimaryKey, newRowData, null);
|
|
627
|
-
return { status: 'ok', row: newRowData };
|
|
735
|
+
return { status: 'ok', row: newRowData, evictedRows: evicted.length > 0 ? evicted : undefined };
|
|
628
736
|
}
|
|
629
737
|
async performDelete(targetLayer, oldKeyValues) {
|
|
630
738
|
if (!oldKeyValues) {
|
|
@@ -653,8 +761,13 @@ export class MemoryTableManager {
|
|
|
653
761
|
return true;
|
|
654
762
|
}
|
|
655
763
|
if (uc.predicate) {
|
|
656
|
-
const
|
|
657
|
-
|
|
764
|
+
const covering = this.findIndexForConstraint(this._currentCommittedLayer, uc);
|
|
765
|
+
// For an index the compiled predicate is already on hand; for an MV-covered
|
|
766
|
+
// (or uncovered) constraint, compile the partial predicate ad hoc to learn
|
|
767
|
+
// which columns can transition the row across the predicate's scope.
|
|
768
|
+
const referenced = covering?.kind === 'memory-index'
|
|
769
|
+
? covering.index.predicate?.referencedColumns
|
|
770
|
+
: compilePredicate(uc.predicate, schema.columns).referencedColumns;
|
|
658
771
|
if (referenced) {
|
|
659
772
|
for (const colIdx of referenced) {
|
|
660
773
|
if (compareSqlValues(oldRow[colIdx], newRow[colIdx]) !== 0)
|
|
@@ -668,78 +781,251 @@ export class MemoryTableManager {
|
|
|
668
781
|
/**
|
|
669
782
|
* Checks all UNIQUE constraints for a new/updated row. Returns an UpdateResult
|
|
670
783
|
* if a violation is found (or IGNORE suppresses the insert), or null if all pass.
|
|
671
|
-
* For REPLACE conflicts, the conflicting rows are deleted from the layer
|
|
784
|
+
* For REPLACE conflicts, the conflicting rows are deleted from the layer and
|
|
785
|
+
* pushed onto `evicted` so the DML executor can run the full delete pipeline
|
|
786
|
+
* (change-tracking, row-time MV maintenance, FK cascade, auto-events) for each.
|
|
672
787
|
*/
|
|
673
|
-
checkUniqueConstraints(targetLayer, schema, newRowData, newPrimaryKey, onConflict) {
|
|
788
|
+
async checkUniqueConstraints(targetLayer, schema, newRowData, newPrimaryKey, onConflict, evicted) {
|
|
674
789
|
if (!schema.uniqueConstraints)
|
|
675
790
|
return null;
|
|
676
791
|
for (const uc of schema.uniqueConstraints) {
|
|
677
|
-
const result = this.checkSingleUniqueConstraint(targetLayer, schema, uc, newRowData, newPrimaryKey, onConflict);
|
|
792
|
+
const result = await this.checkSingleUniqueConstraint(targetLayer, schema, uc, newRowData, newPrimaryKey, onConflict, evicted);
|
|
678
793
|
if (result)
|
|
679
794
|
return result;
|
|
680
795
|
}
|
|
681
796
|
return null;
|
|
682
797
|
}
|
|
683
|
-
checkSingleUniqueConstraint(targetLayer, schema, uc, newRowData, newPrimaryKey, onConflict) {
|
|
798
|
+
async checkSingleUniqueConstraint(targetLayer, schema, uc, newRowData, newPrimaryKey, onConflict, evicted, allowMvCovering = true) {
|
|
684
799
|
// SQL semantics: UNIQUE allows multiple NULLs — skip if any constrained column is NULL
|
|
685
800
|
if (uc.columns.some(colIdx => newRowData[colIdx] === null))
|
|
686
801
|
return null;
|
|
687
|
-
// Find the
|
|
688
|
-
const
|
|
689
|
-
// Partial UNIQUE: a row whose predicate is not unambiguously TRUE is
|
|
690
|
-
//
|
|
691
|
-
|
|
802
|
+
// Find the covering structure enforcing this constraint.
|
|
803
|
+
const covering = this.findIndexForConstraint(targetLayer, uc, allowMvCovering);
|
|
804
|
+
// Partial UNIQUE: a row whose predicate is not unambiguously TRUE is outside
|
|
805
|
+
// the structure's scope and contributes nothing to uniqueness. The source-side
|
|
806
|
+
// skip must short-circuit identically regardless of which structure covers the
|
|
807
|
+
// constraint — for the MV path the partial scope is governed by the (aligned)
|
|
808
|
+
// `uc.predicate` (the prover proves the MV's WHERE equivalent to it).
|
|
809
|
+
if (covering?.kind === 'memory-index'
|
|
810
|
+
&& covering.index.predicate
|
|
811
|
+
&& !covering.index.rowMatchesPredicate(newRowData)) {
|
|
812
|
+
return null;
|
|
813
|
+
}
|
|
814
|
+
if (covering?.kind === 'materialized-view'
|
|
815
|
+
&& uc.predicate
|
|
816
|
+
&& compilePredicate(uc.predicate, schema.columns).evaluate(newRowData) !== true) {
|
|
692
817
|
return null;
|
|
693
818
|
}
|
|
694
819
|
// Resolve effective action: statement OR > constraint default > ABORT.
|
|
695
820
|
const effective = onConflict ?? uc.defaultConflict ?? ConflictResolution.ABORT;
|
|
696
|
-
if (
|
|
697
|
-
|
|
821
|
+
if (covering) {
|
|
822
|
+
switch (covering.kind) {
|
|
823
|
+
case 'memory-index':
|
|
824
|
+
return this.checkUniqueViaIndex(targetLayer, schema, uc, covering.index, newRowData, newPrimaryKey, effective, evicted);
|
|
825
|
+
case 'materialized-view':
|
|
826
|
+
return this.checkUniqueViaMaterializedView(targetLayer, schema, uc, covering.view, newRowData, newPrimaryKey, effective, evicted);
|
|
827
|
+
default: {
|
|
828
|
+
const exhaustive = covering;
|
|
829
|
+
throw new QuereusError(`Unknown covering structure: ${JSON.stringify(exhaustive)}`, StatusCode.INTERNAL);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
698
832
|
}
|
|
699
833
|
// Fallback: scan primary tree
|
|
700
|
-
return this.checkUniqueByScanning(targetLayer, schema, uc, newRowData, newPrimaryKey, effective);
|
|
834
|
+
return this.checkUniqueByScanning(targetLayer, schema, uc, newRowData, newPrimaryKey, effective, evicted);
|
|
701
835
|
}
|
|
702
|
-
|
|
836
|
+
/**
|
|
837
|
+
* Resolves the {@link CoveringStructure} enforcing a UNIQUE constraint. Prefers
|
|
838
|
+
* a linked, non-stale row-time covering MV when one is present (the live
|
|
839
|
+
* enforcement path in v1; the sole structure once the auto-index is
|
|
840
|
+
* retired — see {@link CoveringStructure}), falling back to the auto-built
|
|
841
|
+
* `memory-index`. The row-time resolution is a synchronous map lookup with an
|
|
842
|
+
* O(1) negative fast path, so a non-covered table stays on the index path at
|
|
843
|
+
* effectively no cost.
|
|
844
|
+
*
|
|
845
|
+
* `allowMvCovering = false` skips the MV preference: the maintenance-write
|
|
846
|
+
* enforcement path ({@link enforceSecondaryUniqueOnMaintenance}) checks rows
|
|
847
|
+
* THIS table's batch just wrote, and a covering MV over this table is
|
|
848
|
+
* cascade-maintained only after the batch returns — it lags the batch and
|
|
849
|
+
* would miss a same-batch colliding pair. The synchronously-maintained
|
|
850
|
+
* auto-index is exact.
|
|
851
|
+
*/
|
|
852
|
+
findIndexForConstraint(targetLayer, uc, allowMvCovering = true) {
|
|
853
|
+
if (allowMvCovering) {
|
|
854
|
+
const mv = this.db._findRowTimeCoveringStructure(this.schemaName, this._tableName, uc);
|
|
855
|
+
if (mv)
|
|
856
|
+
return { kind: 'materialized-view', view: mv };
|
|
857
|
+
}
|
|
703
858
|
const schema = targetLayer.getSchema();
|
|
704
859
|
if (!schema.indexes)
|
|
705
860
|
return undefined;
|
|
861
|
+
// Resolve the constraint's OWN realizing structure BY NAME — never the
|
|
862
|
+
// column-set scan below, which returns the FIRST same-column-set index and
|
|
863
|
+
// (when several differently-collated indexes cover one column-set) would
|
|
864
|
+
// enforce a UC under the wrong index's collation, generating candidates from
|
|
865
|
+
// that index's wrongly-keyed BTree:
|
|
866
|
+
// - index-derived UNIQUE (`CREATE UNIQUE INDEX`) → its own index name via
|
|
867
|
+
// `uc.derivedFromIndex` (matches store/isolation's by-name resolution
|
|
868
|
+
// through `uniqueEnforcementCollations`).
|
|
869
|
+
// - non-derived UNIQUE (table-level / column) → its own `_uc_*` covering
|
|
870
|
+
// index via `implicitCoveringStructures`. The realization guard only
|
|
871
|
+
// reuses a collation-equivalent same-column-set index, so a pre-existing
|
|
872
|
+
// finer index (e.g. BINARY over a NOCASE column) no longer collapses onto
|
|
873
|
+
// the constraint; resolving by name is robust to `schema.indexes` order
|
|
874
|
+
// (the finer index may be listed earlier, having been created first).
|
|
875
|
+
// Both fall through to the column-set scan only when the name does not
|
|
876
|
+
// resolve (defensive).
|
|
877
|
+
if (uc.derivedFromIndex) {
|
|
878
|
+
const index = targetLayer.getSecondaryIndex?.(uc.derivedFromIndex);
|
|
879
|
+
if (index)
|
|
880
|
+
return { kind: 'memory-index', index };
|
|
881
|
+
}
|
|
882
|
+
else {
|
|
883
|
+
const own = this.getImplicitCoveringStructure(uc);
|
|
884
|
+
if (own) {
|
|
885
|
+
const index = targetLayer.getSecondaryIndex?.(own.indexName);
|
|
886
|
+
if (index)
|
|
887
|
+
return { kind: 'memory-index', index };
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
// Defensive fallback: match the auto-built `_uc_*` covering index by
|
|
891
|
+
// column-set when the by-name resolution above did not land.
|
|
706
892
|
for (const idx of schema.indexes) {
|
|
707
893
|
if (idx.columns.length === uc.columns.length &&
|
|
708
894
|
idx.columns.every((col, i) => col.index === uc.columns[i])) {
|
|
709
|
-
|
|
895
|
+
const index = targetLayer.getSecondaryIndex?.(idx.name);
|
|
896
|
+
return index ? { kind: 'memory-index', index } : undefined;
|
|
710
897
|
}
|
|
711
898
|
}
|
|
712
899
|
return undefined;
|
|
713
900
|
}
|
|
714
|
-
checkUniqueViaIndex(targetLayer, schema, uc, index, newRowData, newPrimaryKey, onConflict) {
|
|
901
|
+
checkUniqueViaIndex(targetLayer, schema, uc, index, newRowData, newPrimaryKey, onConflict, evicted) {
|
|
715
902
|
const indexKey = index.keyFromRow(newRowData);
|
|
716
903
|
const existingPKs = index.getPrimaryKeys(indexKey);
|
|
717
904
|
for (const existingPK of existingPKs) {
|
|
718
905
|
if (this.comparePrimaryKeys(newPrimaryKey, existingPK) === 0)
|
|
719
906
|
continue;
|
|
720
|
-
//
|
|
907
|
+
// Validate the candidate against the live effective row before acting —
|
|
908
|
+
// the same stale-candidate discipline as checkUniqueViaMaterializedView.
|
|
909
|
+
// An index entry's PK can still lag the effective row set *within* a
|
|
910
|
+
// statement (a candidate row deleted/updated internally, or a prior
|
|
911
|
+
// REPLACE eviction whose index removal lands later in the batch), so a
|
|
912
|
+
// candidate whose row is gone, no longer carries the colliding values,
|
|
913
|
+
// or left a partial index's scope is skipped rather than raised as a
|
|
914
|
+
// false conflict (or, worse, REPLACE-evicting an innocent row). The
|
|
915
|
+
// entry now tracks PKs by value (removeEntry drops composite PKs
|
|
916
|
+
// correctly, so it no longer accumulates stale-by-reference members);
|
|
917
|
+
// this live re-check remains as defense-in-depth for that genuine
|
|
918
|
+
// intra-statement lag.
|
|
919
|
+
// Compare under the INDEX's per-column collation (positionally aligned
|
|
920
|
+
// with uc.columns — findIndexForConstraint requires it): the index is
|
|
921
|
+
// the enforcing structure, and an explicit `create unique index …
|
|
922
|
+
// (col collate nocase)` may declare a coarser collation than the
|
|
923
|
+
// column — re-checking under the column's collation would skip the
|
|
924
|
+
// case-variant candidates the index legitimately unifies.
|
|
925
|
+
// This is the authoritative LIVE-index source for the per-column
|
|
926
|
+
// enforcement collation. The shared `uniqueEnforcementCollations(schema,
|
|
927
|
+
// uc)` helper (which store/isolation import, and checkUniqueViaMaterializedView
|
|
928
|
+
// uses) resolves the SAME per-column value, but BY NAME via
|
|
929
|
+
// `uc.derivedFromIndex`. `findIndexForConstraint` now ALSO resolves an
|
|
930
|
+
// index-derived UC by that name (the column-set scan is only the
|
|
931
|
+
// non-derived fallback), so the live `index` handle here IS the UC's own
|
|
932
|
+
// index and `index.specColumns[i]?.collation` is the correct per-column
|
|
933
|
+
// collation even when several same-column-set indexes exist with
|
|
934
|
+
// differing collations. The `(schema, uc)` helper signature still has no
|
|
935
|
+
// MemoryIndex handle, so this site keeps the live-handle read — but the
|
|
936
|
+
// two resolutions now agree on the multi-index shape too. The agreement is
|
|
937
|
+
// pinned by test/unique-enforcement-collation.spec.ts (a real divergence
|
|
938
|
+
// is a finding, not a reason to widen the helper).
|
|
939
|
+
const conflictingRow = this.lookupEffectiveRow(existingPK, targetLayer);
|
|
940
|
+
if (!conflictingRow)
|
|
941
|
+
continue;
|
|
942
|
+
if (!uc.columns.every((col, i) => compareSqlValues(newRowData[col], conflictingRow[col], index.specColumns[i]?.collation ?? schema.columns[col].collation) === 0))
|
|
943
|
+
continue;
|
|
944
|
+
if (index.predicate && !index.rowMatchesPredicate(conflictingRow))
|
|
945
|
+
continue;
|
|
946
|
+
// Found a different live row with the same unique key values
|
|
721
947
|
if (onConflict === ConflictResolution.IGNORE) {
|
|
722
948
|
return { status: 'ok', row: undefined };
|
|
723
949
|
}
|
|
724
950
|
if (onConflict === ConflictResolution.REPLACE) {
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
return null; // Conflict resolved, continue with insert
|
|
951
|
+
targetLayer.recordDelete(existingPK, conflictingRow);
|
|
952
|
+
// Report the eviction so the executor runs its delete pipeline.
|
|
953
|
+
evicted.push(conflictingRow);
|
|
954
|
+
continue; // conflict resolved, keep scanning for further duplicates
|
|
730
955
|
}
|
|
731
956
|
const colNames = uc.columns.map(i => schema.columns[i].name).join(', ');
|
|
732
|
-
const existingRow = this.lookupEffectiveRow(existingPK, targetLayer);
|
|
733
957
|
return {
|
|
734
958
|
status: 'constraint',
|
|
735
959
|
constraint: 'unique',
|
|
736
960
|
message: `UNIQUE constraint failed: ${this._tableName} (${colNames})`,
|
|
737
|
-
existingRow:
|
|
961
|
+
existingRow: conflictingRow
|
|
738
962
|
};
|
|
739
963
|
}
|
|
740
964
|
return null;
|
|
741
965
|
}
|
|
742
|
-
|
|
966
|
+
/**
|
|
967
|
+
* Enforce a UNIQUE constraint through its linked `row-time` covering MV's backing
|
|
968
|
+
* table (mirrors {@link checkUniqueViaIndex}, but the candidates come from the MV
|
|
969
|
+
* rather than a secondary BTree). The backing scan yields candidate conflicting
|
|
970
|
+
* source PKs; each is *validated against the live source row* before acting, since
|
|
971
|
+
* a backing entry can lag a source row deleted/updated internally within the same
|
|
972
|
+
* statement (e.g. the PK-changing-UPDATE delete below, or a prior REPLACE eviction)
|
|
973
|
+
* — the row-time hook only fires for DML-executor row writes, not these internal
|
|
974
|
+
* mutations. A candidate whose source row is gone or no longer matches the UC is
|
|
975
|
+
* stale and skipped, so a false conflict is never raised.
|
|
976
|
+
*
|
|
977
|
+
* On a REPLACE eviction the conflicting **source** row is deleted directly on the
|
|
978
|
+
* transaction layer and pushed onto `evicted`; the DML executor then runs the full
|
|
979
|
+
* delete pipeline for it (change-tracking, FK cascade, auto-events, and the
|
|
980
|
+
* row-time covering-structure maintenance that removes the evicted row's backing
|
|
981
|
+
* entry — so a later same-UC row in the statement never sees a phantom). The
|
|
982
|
+
* executor processes the eviction before the writing row's own bookkeeping, so the
|
|
983
|
+
* backing delete still lands within this statement.
|
|
984
|
+
*/
|
|
985
|
+
async checkUniqueViaMaterializedView(targetLayer, schema, uc, mv, newRowData, newPrimaryKey, onConflict, evicted) {
|
|
986
|
+
const newSourcePk = Array.isArray(newPrimaryKey) ? newPrimaryKey : [newPrimaryKey];
|
|
987
|
+
const conflicts = await this.db._lookupCoveringConflicts(mv, uc, newRowData, newSourcePk);
|
|
988
|
+
// Re-validate under each column's enforcement collation — the index's per-column
|
|
989
|
+
// COLLATE for an index-derived UNIQUE, else the declared column collation
|
|
990
|
+
// (uniqueEnforcementCollations) — mirroring checkUniqueViaIndex, the store's
|
|
991
|
+
// findUniqueConflictViaCoveringMv, and the isolation overlay, so all modules agree.
|
|
992
|
+
// The candidate generation (_lookupCoveringConflicts) narrows under the SOURCE
|
|
993
|
+
// column's DECLARED collation, so for a FINER index (e.g. BINARY over a NOCASE
|
|
994
|
+
// column) it returns a superset this filters down correctly; a finer/incomparable
|
|
995
|
+
// index-derived UNIQUE whose declared candidate set could be a subset is declined
|
|
996
|
+
// upstream by findRowTimeCoveringStructure's collation gate, so only BINARY-floor
|
|
997
|
+
// or equal-collation MVs ever reach here.
|
|
998
|
+
const collations = uniqueEnforcementCollations(schema, uc);
|
|
999
|
+
for (const conflict of conflicts) {
|
|
1000
|
+
const existingPK = buildPrimaryKeyFromValues(conflict.pk, schema.primaryKeyDefinition);
|
|
1001
|
+
if (this.comparePrimaryKeys(newPrimaryKey, existingPK) === 0)
|
|
1002
|
+
continue;
|
|
1003
|
+
// Validate against the live source row: skip stale backing candidates.
|
|
1004
|
+
const conflictingRow = this.lookupEffectiveRow(existingPK, targetLayer);
|
|
1005
|
+
if (!conflictingRow)
|
|
1006
|
+
continue;
|
|
1007
|
+
if (!uc.columns.every((col, i) => compareSqlValues(newRowData[col], conflictingRow[col], collations[i]) === 0))
|
|
1008
|
+
continue;
|
|
1009
|
+
if (onConflict === ConflictResolution.IGNORE) {
|
|
1010
|
+
return { status: 'ok', row: undefined };
|
|
1011
|
+
}
|
|
1012
|
+
if (onConflict === ConflictResolution.REPLACE) {
|
|
1013
|
+
targetLayer.recordDelete(existingPK, conflictingRow);
|
|
1014
|
+
// Report the eviction; the executor maintains the covering backing.
|
|
1015
|
+
evicted.push(conflictingRow);
|
|
1016
|
+
continue; // conflict resolved, keep scanning for further duplicates
|
|
1017
|
+
}
|
|
1018
|
+
const colNames = uc.columns.map(i => schema.columns[i].name).join(', ');
|
|
1019
|
+
return {
|
|
1020
|
+
status: 'constraint',
|
|
1021
|
+
constraint: 'unique',
|
|
1022
|
+
message: `UNIQUE constraint failed: ${this._tableName} (${colNames})`,
|
|
1023
|
+
existingRow: conflictingRow
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
1026
|
+
return null;
|
|
1027
|
+
}
|
|
1028
|
+
checkUniqueByScanning(targetLayer, schema, uc, newRowData, newPrimaryKey, onConflict, evicted) {
|
|
743
1029
|
const primaryTree = targetLayer.getModificationTree('primary');
|
|
744
1030
|
if (!primaryTree)
|
|
745
1031
|
return null;
|
|
@@ -755,7 +1041,7 @@ export class MemoryTableManager {
|
|
|
755
1041
|
continue;
|
|
756
1042
|
if (predicate && predicate.evaluate(existingRow) !== true)
|
|
757
1043
|
continue;
|
|
758
|
-
const allMatch = uc.columns.every(colIdx => compareSqlValues(newRowData[colIdx], existingRow[colIdx]) === 0);
|
|
1044
|
+
const allMatch = uc.columns.every(colIdx => compareSqlValues(newRowData[colIdx], existingRow[colIdx], schema.columns[colIdx].collation) === 0);
|
|
759
1045
|
if (!allMatch)
|
|
760
1046
|
continue;
|
|
761
1047
|
if (onConflict === ConflictResolution.IGNORE) {
|
|
@@ -763,6 +1049,8 @@ export class MemoryTableManager {
|
|
|
763
1049
|
}
|
|
764
1050
|
if (onConflict === ConflictResolution.REPLACE) {
|
|
765
1051
|
targetLayer.recordDelete(existingPK, existingRow);
|
|
1052
|
+
// Report the eviction so the executor runs its delete pipeline.
|
|
1053
|
+
evicted.push(existingRow);
|
|
766
1054
|
return null;
|
|
767
1055
|
}
|
|
768
1056
|
const colNames = uc.columns.map(i => schema.columns[i].name).join(', ');
|
|
@@ -810,8 +1098,271 @@ export class MemoryTableManager {
|
|
|
810
1098
|
}
|
|
811
1099
|
this.baseLayer.primaryTree.insert(row);
|
|
812
1100
|
}
|
|
1101
|
+
/**
|
|
1102
|
+
* Atomically replaces the entire committed contents with `rows` by building a
|
|
1103
|
+
* fresh {@link BaseLayer} and swapping it in under the SchemaChange latch.
|
|
1104
|
+
* Used to (re)materialize a materialized view: callers run the view body to
|
|
1105
|
+
* completion and hand the result rows here. Concurrent readers do NOT block:
|
|
1106
|
+
* each scan reads a base-layer snapshot captured at start-of-call, so an
|
|
1107
|
+
* in-flight scan keeps the pre-swap base while a fresh scan sees the new base
|
|
1108
|
+
* — never a partial state. The swap itself is a single synchronous assignment
|
|
1109
|
+
* performed under the SchemaChange latch (which serializes swaps with `alter
|
|
1110
|
+
* table` and other refreshes, not with readers).
|
|
1111
|
+
*
|
|
1112
|
+
* Throws on a duplicate primary key among `rows` (the caller rolls back).
|
|
1113
|
+
* Callers may pass `onDuplicateKey` to substitute a purpose-built diagnostic
|
|
1114
|
+
* for the duplicate-PK case (e.g. the materialized-view "must be a set"
|
|
1115
|
+
* message); when omitted, the generic backing-table message is thrown. The
|
|
1116
|
+
* factory only controls the wording — duplicate detection still uses the
|
|
1117
|
+
* btree's collation/desc/composite-correct key comparison.
|
|
1118
|
+
*/
|
|
1119
|
+
async replaceBaseLayer(rows, onDuplicateKey) {
|
|
1120
|
+
if (this.isReadOnly) {
|
|
1121
|
+
throw new QuereusError(`Table '${this._tableName}' is read-only`, StatusCode.READONLY);
|
|
1122
|
+
}
|
|
1123
|
+
const lockKey = `MemoryTable.SchemaChange:${this.schemaName}.${this._tableName}`;
|
|
1124
|
+
const release = await Latches.acquire(lockKey);
|
|
1125
|
+
try {
|
|
1126
|
+
// Drain any in-flight transaction layers down to the base so the swap
|
|
1127
|
+
// below isn't shadowed by a committed transaction layer ahead of base.
|
|
1128
|
+
await this.ensureSchemaChangeSafety();
|
|
1129
|
+
const oldBase = this.baseLayer;
|
|
1130
|
+
const newBase = new BaseLayer(this.tableSchema);
|
|
1131
|
+
for (const row of rows) {
|
|
1132
|
+
const key = this.primaryKeyFunctions.extractFromRow(row);
|
|
1133
|
+
const path = newBase.primaryTree.find(key);
|
|
1134
|
+
if (path.on) {
|
|
1135
|
+
throw onDuplicateKey
|
|
1136
|
+
? onDuplicateKey()
|
|
1137
|
+
: new QuereusError(`UNIQUE constraint failed: ${this._tableName} PK.`, StatusCode.CONSTRAINT);
|
|
1138
|
+
}
|
|
1139
|
+
newBase.primaryTree.insert(row);
|
|
1140
|
+
}
|
|
1141
|
+
newBase.rebuildAllSecondaryIndexes();
|
|
1142
|
+
this.baseLayer = newBase;
|
|
1143
|
+
this._currentCommittedLayer = newBase;
|
|
1144
|
+
// Re-point any connection still reading the old base at the new base so
|
|
1145
|
+
// the next statement observes refreshed contents.
|
|
1146
|
+
for (const conn of this.connections.values()) {
|
|
1147
|
+
if (conn.readLayer === oldBase) {
|
|
1148
|
+
conn.readLayer = newBase;
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
finally {
|
|
1153
|
+
release();
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
/**
|
|
1157
|
+
* Privileged **transactional** maintenance write: apply an ordered
|
|
1158
|
+
* {@link MaintenanceOp} batch to a given connection's *pending*
|
|
1159
|
+
* {@link TransactionLayer} (creating it lazily, exactly as a user write would).
|
|
1160
|
+
* The row-time materialized-view maintenance path uses it so a covering
|
|
1161
|
+
* structure's backing table is kept consistent synchronously with each source
|
|
1162
|
+
* row-write — within the same transaction, visible to later reads on this
|
|
1163
|
+
* connection (reads-own-writes), and committed/rolled-back in lockstep with the
|
|
1164
|
+
* source write by the Database's coordinated commit.
|
|
1165
|
+
*
|
|
1166
|
+
* It deliberately bypasses {@link validateMutationPermissions} (which throws
|
|
1167
|
+
* READONLY for MV backing tables) and reuses {@link TransactionLayer.recordUpsert} /
|
|
1168
|
+
* {@link TransactionLayer.recordDelete} so secondary-index and change-tracking
|
|
1169
|
+
* bookkeeping stay correct. No latch is taken: the pending layer is private to
|
|
1170
|
+
* `connection`, only this synchronous path writes it, and the tree mutations are
|
|
1171
|
+
* synchronous — so a multi-row statement's later rows observe earlier rows'
|
|
1172
|
+
* pending writes with no interleaving.
|
|
1173
|
+
*
|
|
1174
|
+
* Declared secondary UNIQUE constraints ARE enforced — post-batch, against the
|
|
1175
|
+
* final effective contents, throwing the maintained-table-attributed
|
|
1176
|
+
* CONSTRAINT error ({@link enforceSecondaryUniqueOnMaintenance}). CHECK / FK
|
|
1177
|
+
* stay engine-validated (see `vtab/backing-host.ts` § Constraint validation).
|
|
1178
|
+
*
|
|
1179
|
+
* Returns the **effective** changes it applied (one {@link BackingRowChange} per
|
|
1180
|
+
* backing row it mutated): a `delete-key` that found a row → `delete`; an `upsert` →
|
|
1181
|
+
* `update` when it replaced an existing row, else `insert`; a `delete-by-prefix` →
|
|
1182
|
+
* one `delete` per matched row; a `replace-all` → the minimal keyed diff between the
|
|
1183
|
+
* new and old contents (insert/update/delete, identical rows skipped). A
|
|
1184
|
+
* `delete-key`/`delete-by-prefix` that matches nothing, an `upsert` whose row is
|
|
1185
|
+
* **value-identical** to the effective existing row (`rowsValueIdentical` — written
|
|
1186
|
+
* nothing, reported nothing; the normative skip in `vtab/backing-host.ts`), or a
|
|
1187
|
+
* `replace-all` whose new contents equal the old — produces nothing. The MV-over-MV
|
|
1188
|
+
* cascade feeds these onward to MVs reading this backing table (see
|
|
1189
|
+
* `database-materialized-views.ts` § cascade).
|
|
1190
|
+
*
|
|
1191
|
+
* Async only because `delete-by-prefix` / `replace-all` reuse the async layer scan to
|
|
1192
|
+
* enumerate the affected (prefix / whole-table) slice; the point ops stay synchronous
|
|
1193
|
+
* within the same pass, so a multi-row statement's later rows still observe earlier
|
|
1194
|
+
* rows' pending writes with no interleaving (no await separates a single op's lookup
|
|
1195
|
+
* from its record).
|
|
1196
|
+
*/
|
|
1197
|
+
async applyMaintenanceToLayer(connection, ops) {
|
|
1198
|
+
const changes = [];
|
|
1199
|
+
if (ops.length === 0)
|
|
1200
|
+
return changes;
|
|
1201
|
+
this.ensureTransactionLayer(connection);
|
|
1202
|
+
const layer = connection.pendingTransactionLayer;
|
|
1203
|
+
for (const op of ops) {
|
|
1204
|
+
switch (op.kind) {
|
|
1205
|
+
case 'delete-key': {
|
|
1206
|
+
const existing = this.lookupEffectiveRow(op.key, layer);
|
|
1207
|
+
if (existing) {
|
|
1208
|
+
layer.recordDelete(op.key, existing);
|
|
1209
|
+
changes.push({ op: 'delete', oldRow: existing });
|
|
1210
|
+
}
|
|
1211
|
+
break;
|
|
1212
|
+
}
|
|
1213
|
+
case 'upsert': {
|
|
1214
|
+
const key = this.primaryKeyFunctions.extractFromRow(op.row);
|
|
1215
|
+
const existing = this.lookupEffectiveRow(key, layer);
|
|
1216
|
+
if (existing && rowsValueIdentical(existing, op.row)) {
|
|
1217
|
+
// Value-identical against the EFFECTIVE row (pending over committed):
|
|
1218
|
+
// nothing changes, so write nothing and report nothing — the
|
|
1219
|
+
// skip-identical upsert contract (vtab/backing-host.ts), the point-op
|
|
1220
|
+
// analogue of the replace-all diff's identical-row skip. Both skips are
|
|
1221
|
+
// byte-faithful (`rowsValueIdentical`, BINARY per column): a collation-equal
|
|
1222
|
+
// / byte-different row (a case-only rewrite under NOCASE) is a real change
|
|
1223
|
+
// that must re-key the stored bytes — collation governs key identity only.
|
|
1224
|
+
break;
|
|
1225
|
+
}
|
|
1226
|
+
layer.recordUpsert(key, op.row, existing);
|
|
1227
|
+
changes.push(existing
|
|
1228
|
+
? { op: 'update', oldRow: existing, newRow: op.row }
|
|
1229
|
+
: { op: 'insert', newRow: op.row });
|
|
1230
|
+
break;
|
|
1231
|
+
}
|
|
1232
|
+
case 'delete-by-prefix': {
|
|
1233
|
+
// Range-scan the primary tree over the half-open interval whose leading
|
|
1234
|
+
// PK columns equal `keyPrefix` (the btree orders by the composite PK,
|
|
1235
|
+
// base-PK columns leading, so the slice is contiguous; `scanLayer`'s
|
|
1236
|
+
// `equalityPrefix` seeks to it and early-terminates on prefix mismatch).
|
|
1237
|
+
// Collect the matched rows first, THEN `recordDelete` each — the same
|
|
1238
|
+
// per-row bookkeeping (secondary indexes, change tracking) the point
|
|
1239
|
+
// `delete-key` arm uses, over a prefix range instead of a point.
|
|
1240
|
+
// Collect-then-delete avoids mutating the tree mid-iteration.
|
|
1241
|
+
const scanPlan = { indexName: 'primary', descending: false, equalityPrefix: op.keyPrefix };
|
|
1242
|
+
const matched = [];
|
|
1243
|
+
for await (const row of scanLayerImpl(layer, scanPlan)) {
|
|
1244
|
+
matched.push({ key: this.primaryKeyFunctions.extractFromRow(row), row });
|
|
1245
|
+
}
|
|
1246
|
+
for (const { key, row } of matched) {
|
|
1247
|
+
layer.recordDelete(key, row);
|
|
1248
|
+
changes.push({ op: 'delete', oldRow: row });
|
|
1249
|
+
}
|
|
1250
|
+
break;
|
|
1251
|
+
}
|
|
1252
|
+
case 'replace-all': {
|
|
1253
|
+
// Wholesale transactional replacement, realized as the minimal keyed diff
|
|
1254
|
+
// (by backing PK) against the layer's current effective rows. Snapshot the
|
|
1255
|
+
// old rows FIRST — the same whole-table effective iteration the
|
|
1256
|
+
// `delete-by-prefix` arm scopes to a prefix — into a PK-keyed btree, so the
|
|
1257
|
+
// diff is computed against a stable before-image regardless of the upserts
|
|
1258
|
+
// applied below. Collation governs KEY identity only: keys are compared with
|
|
1259
|
+
// the table's PK comparator (honoring PK-column collation), so a new row whose
|
|
1260
|
+
// key only differs by collation (e.g. 'apple' vs a stored 'APPLE' under a NOCASE
|
|
1261
|
+
// PK) matches its old row and resolves to an `update` — never a spurious insert +
|
|
1262
|
+
// delete that would leak secondary-index bookkeeping. VALUE fidelity of a paired
|
|
1263
|
+
// row is byte-faithful (`rowsValueIdentical`, below) — one discipline, not two.
|
|
1264
|
+
const oldByKey = new BTree(e => e.key, this.comparePrimaryKeys);
|
|
1265
|
+
for await (const row of scanLayerImpl(layer, { indexName: 'primary', descending: false })) {
|
|
1266
|
+
oldByKey.insert({ key: this.primaryKeyFunctions.extractFromRow(row), row });
|
|
1267
|
+
}
|
|
1268
|
+
// New-row keys (same PK comparator) for the delete pass's membership test.
|
|
1269
|
+
const newKeys = new BTree(k => k, this.comparePrimaryKeys);
|
|
1270
|
+
// Insert/update/skip-identical pass, in new-row order.
|
|
1271
|
+
for (const newRow of op.rows) {
|
|
1272
|
+
const key = this.primaryKeyFunctions.extractFromRow(newRow);
|
|
1273
|
+
newKeys.insert(key);
|
|
1274
|
+
const existing = oldByKey.get(key);
|
|
1275
|
+
if (!existing) {
|
|
1276
|
+
layer.recordUpsert(key, newRow, null);
|
|
1277
|
+
changes.push({ op: 'insert', newRow });
|
|
1278
|
+
}
|
|
1279
|
+
else if (!rowsValueIdentical(existing.row, newRow)) {
|
|
1280
|
+
layer.recordUpsert(key, newRow, existing.row);
|
|
1281
|
+
changes.push({ op: 'update', oldRow: existing.row, newRow });
|
|
1282
|
+
}
|
|
1283
|
+
// else: byte-identical at this key — a true no-op, no emitted change.
|
|
1284
|
+
// The skip is byte-faithful (`rowsValueIdentical`): a collation-equal /
|
|
1285
|
+
// byte-different paired row (a case-only rewrite under a NOCASE PK) is an
|
|
1286
|
+
// `update` that re-keys the stored bytes, matching the point-op upsert skip
|
|
1287
|
+
// and the byte-exact maintenance-equivalence oracle.
|
|
1288
|
+
}
|
|
1289
|
+
// Delete pass: every old key absent from the new set, ascending PK order.
|
|
1290
|
+
// `oldByKey` is a private snapshot, not mutated here, so iterating it while
|
|
1291
|
+
// `recordDelete` mutates the layer's tree is safe.
|
|
1292
|
+
for (const path of oldByKey.ascending(oldByKey.first())) {
|
|
1293
|
+
const entry = oldByKey.at(path);
|
|
1294
|
+
if (newKeys.get(entry.key) !== undefined)
|
|
1295
|
+
continue;
|
|
1296
|
+
layer.recordDelete(entry.key, entry.row);
|
|
1297
|
+
changes.push({ op: 'delete', oldRow: entry.row });
|
|
1298
|
+
}
|
|
1299
|
+
break;
|
|
1300
|
+
}
|
|
1301
|
+
default: {
|
|
1302
|
+
// A new MaintenanceOp must extend this switch; never-assignment makes
|
|
1303
|
+
// that a compile error rather than a silent no-op.
|
|
1304
|
+
const exhaustiveCheck = op;
|
|
1305
|
+
throw new QuereusError(`Unknown maintenance op: ${JSON.stringify(exhaustiveCheck)}`, StatusCode.INTERNAL);
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
await this.enforceSecondaryUniqueOnMaintenance(layer, changes);
|
|
1310
|
+
return changes;
|
|
1311
|
+
}
|
|
1312
|
+
/**
|
|
1313
|
+
* Declared secondary-UNIQUE enforcement for maintenance writes — the
|
|
1314
|
+
* collision-shaped half of the derived-row constraint contract (CHECK / FK
|
|
1315
|
+
* are per-row properties and validate engine-side; see
|
|
1316
|
+
* docs/materialized-views.md § Derived-row constraint validation). The
|
|
1317
|
+
* privileged surface bypasses the DML constraint pipeline, so without this
|
|
1318
|
+
* the batch above would store two derived rows colliding on a declared
|
|
1319
|
+
* UNIQUE silently.
|
|
1320
|
+
*
|
|
1321
|
+
* Runs POST-batch over the effective changes, never per-op: a `replace-all`
|
|
1322
|
+
* diff applies its upserts before its deletes, so an in-flight per-op check
|
|
1323
|
+
* would false-positive against a row the same batch is about to delete
|
|
1324
|
+
* (e.g. the derived set moved a unique value from one primary key to
|
|
1325
|
+
* another). After the batch the layer holds exactly the final contents, so
|
|
1326
|
+
* checking each WRITTEN image against it is exact — and complete: every
|
|
1327
|
+
* pre-existing row entered through DML / ADD CONSTRAINT / earlier validated
|
|
1328
|
+
* maintenance, so any colliding pair includes at least one written image.
|
|
1329
|
+
* A value-identical upsert the batch skipped emitted no change and cannot
|
|
1330
|
+
* introduce a collision (the table's contents did not change at that key).
|
|
1331
|
+
*
|
|
1332
|
+
* Reuses {@link checkSingleUniqueConstraint} (same-PK exclusion, NULL-pass,
|
|
1333
|
+
* partial-predicate scope, per-column collation, auto-index fast path) with
|
|
1334
|
+
* two maintenance-specific postures: the conflict action is forced to ABORT
|
|
1335
|
+
* (a derivation write carries no user OR clause, and a declared
|
|
1336
|
+
* `on conflict replace`/`ignore` default must not silently evict or drop
|
|
1337
|
+
* derived rows — the eviction would diverge the table from its derivation),
|
|
1338
|
+
* and the covering-MV route is bypassed (see
|
|
1339
|
+
* {@link findIndexForConstraint}'s `allowMvCovering`).
|
|
1340
|
+
*
|
|
1341
|
+
* Zero overhead when the table declares no secondary UNIQUE (every MV-sugar
|
|
1342
|
+
* backing, and most maintained tables): one empty-array check.
|
|
1343
|
+
*/
|
|
1344
|
+
async enforceSecondaryUniqueOnMaintenance(layer, changes) {
|
|
1345
|
+
const schema = layer.getSchema();
|
|
1346
|
+
const ucs = schema.uniqueConstraints;
|
|
1347
|
+
if (!ucs || ucs.length === 0 || changes.length === 0)
|
|
1348
|
+
return;
|
|
1349
|
+
// ABORT means the IGNORE/REPLACE arms never fire, so nothing ever lands here.
|
|
1350
|
+
const noEvict = [];
|
|
1351
|
+
for (const change of changes) {
|
|
1352
|
+
if (change.op === 'delete')
|
|
1353
|
+
continue;
|
|
1354
|
+
const newPrimaryKey = this.primaryKeyFromRow(change.newRow);
|
|
1355
|
+
for (const uc of ucs) {
|
|
1356
|
+
const result = await this.checkSingleUniqueConstraint(layer, schema, uc, change.newRow, newPrimaryKey, ConflictResolution.ABORT, noEvict, /*allowMvCovering*/ false);
|
|
1357
|
+
if (result) {
|
|
1358
|
+
const colNames = uc.columns.map(i => schema.columns[i]?.name ?? String(i));
|
|
1359
|
+
throw maintainedTableUniqueViolationError(this.schemaName, this._tableName, uc.name ?? `_uc_${colNames.join('_')}`, colNames, uc.columns.map(i => change.newRow[i]));
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
813
1364
|
// --- Schema Operations (simplified with inherited BTrees) ---
|
|
814
|
-
async addColumn(columnDefAst) {
|
|
1365
|
+
async addColumn(columnDefAst, backfillEvaluator) {
|
|
815
1366
|
if (this.isReadOnly)
|
|
816
1367
|
throw new QuereusError(`Table '${this._tableName}' is read-only`, StatusCode.READONLY);
|
|
817
1368
|
const lockKey = `MemoryTable.SchemaChange:${this.schemaName}.${this._tableName}`;
|
|
@@ -822,7 +1373,11 @@ export class MemoryTableManager {
|
|
|
822
1373
|
// Get default nullability setting from database options
|
|
823
1374
|
const defaultNullability = this.db.options.getStringOption('default_column_nullability');
|
|
824
1375
|
const defaultNotNull = defaultNullability === 'not_null';
|
|
825
|
-
|
|
1376
|
+
// Honor the session `default_collation` for an ADD COLUMN that omits an explicit
|
|
1377
|
+
// COLLATE, matching the CREATE path (and the differ's resolved-COLLATE emission) so
|
|
1378
|
+
// an ADD-COLUMN-ed text column gets the same collation a CREATE-d one would.
|
|
1379
|
+
// resolveDefaultCollation falls non-text types back to BINARY automatically.
|
|
1380
|
+
const newColumnSchema = columnDefToSchema(columnDefAst, defaultNotNull, this.db.options.getStringOption('default_collation'));
|
|
826
1381
|
if (this.tableSchema.columns.some(c => c.name.toLowerCase() === newColumnSchema.name.toLowerCase())) {
|
|
827
1382
|
throw new QuereusError(`Duplicate column name: ${newColumnSchema.name}`, StatusCode.ERROR);
|
|
828
1383
|
}
|
|
@@ -836,13 +1391,19 @@ export class MemoryTableManager {
|
|
|
836
1391
|
defaultIsLiteral = true;
|
|
837
1392
|
}
|
|
838
1393
|
else {
|
|
839
|
-
|
|
1394
|
+
// A non-literal expression default (e.g. `new.<col>`) is written as NULL
|
|
1395
|
+
// here; the engine backfills these rows per-row immediately after.
|
|
1396
|
+
logger.debugLog(`[Add Column] '${newColumnSchema.name}' default is a non-literal expression; existing rows are backfilled by the engine.`);
|
|
840
1397
|
}
|
|
841
1398
|
}
|
|
842
|
-
// Check for NOT NULL constraint (could be explicit or from default behavior)
|
|
843
|
-
// Allow NOT NULL without DEFAULT if table is empty (SQLite-compatible)
|
|
1399
|
+
// Check for NOT NULL constraint (could be explicit or from default behavior).
|
|
1400
|
+
// Allow NOT NULL without DEFAULT if the table is empty (SQLite-compatible).
|
|
1401
|
+
// A non-literal *expression* default (e.g. `new.<col>`) is backfilled per-row by
|
|
1402
|
+
// the engine right after this returns, which then enforces NOT NULL on the
|
|
1403
|
+
// backfilled values — so don't reject it here as "without a DEFAULT".
|
|
844
1404
|
const tableHasRows = this.baseLayer.primaryTree.at(this.baseLayer.primaryTree.first()) !== undefined;
|
|
845
|
-
|
|
1405
|
+
const hasDefaultExpr = !!(defaultConstraint && defaultConstraint.expr);
|
|
1406
|
+
if (newColumnSchema.notNull && defaultValue === null && !defaultIsLiteral && !hasDefaultExpr && tableHasRows) {
|
|
846
1407
|
throw new QuereusError(`Cannot add NOT NULL column '${newColumnSchema.name}' to non-empty table `
|
|
847
1408
|
+ `'${this.schemaName}.${this._tableName}' without a DEFAULT value`, StatusCode.CONSTRAINT);
|
|
848
1409
|
}
|
|
@@ -853,7 +1414,10 @@ export class MemoryTableManager {
|
|
|
853
1414
|
columnIndexMap: buildColumnIndexMap(updatedColumnsSchema),
|
|
854
1415
|
});
|
|
855
1416
|
this.baseLayer.updateSchema(finalNewTableSchema);
|
|
856
|
-
|
|
1417
|
+
// A non-foldable DEFAULT (e.g. `new.<col>`) backfills each existing row from
|
|
1418
|
+
// its own value via the engine-supplied evaluator; a literal/NULL default
|
|
1419
|
+
// uses the single folded `defaultValue` for every row.
|
|
1420
|
+
await this.baseLayer.addColumnToBase(newColumnSchema, defaultValue, backfillEvaluator);
|
|
857
1421
|
this.tableSchema = finalNewTableSchema;
|
|
858
1422
|
this.initializePrimaryKeyFunctions();
|
|
859
1423
|
// Emit schema change event
|
|
@@ -897,11 +1461,36 @@ export class MemoryTableManager {
|
|
|
897
1461
|
...def, index: def.index > colIndex ? def.index - 1 : def.index
|
|
898
1462
|
}));
|
|
899
1463
|
const updatedPrimaryKeyNames = updatedPkDefinition.map(def => updatedColumnsSchema[def.index]?.name).filter(Boolean);
|
|
900
|
-
|
|
1464
|
+
// Prune any UNIQUE constraint over the dropped column. A UNIQUE that includes the
|
|
1465
|
+
// dropped column is removed outright (a UNIQUE missing one of its columns is a
|
|
1466
|
+
// different, stronger constraint, not a silently-narrowed one); the auto-built
|
|
1467
|
+
// covering index it backed is torn down with it (see the index exclusion below).
|
|
1468
|
+
// Remaining constraints have their column indices shifted to track the removed slot.
|
|
1469
|
+
// Without this, dropping a uniquely-constrained column (including the ADD COLUMN +
|
|
1470
|
+
// inline-UNIQUE revert path) would strand a constraint whose column index dangles
|
|
1471
|
+
// past the end of the column array.
|
|
1472
|
+
const oldUniqueConstraints = this.tableSchema.uniqueConstraints ?? [];
|
|
1473
|
+
const droppedUcKeys = oldUniqueConstraints
|
|
1474
|
+
.filter(uc => uc.columns.includes(colIndex))
|
|
1475
|
+
.map(uc => uc.name ?? this.implicitIndexNameFor(uc));
|
|
1476
|
+
const remainingUniqueConstraints = oldUniqueConstraints
|
|
1477
|
+
.filter(uc => !uc.columns.includes(colIndex))
|
|
1478
|
+
.map(uc => ({ ...uc, columns: Object.freeze(uc.columns.map(i => i > colIndex ? i - 1 : i)) }));
|
|
1479
|
+
// Drop the implicit covering index of each removed constraint outright (matched by
|
|
1480
|
+
// the same `uc.name ?? '_uc_<cols>'` convention DROP CONSTRAINT uses, so a user
|
|
1481
|
+
// index that merely shares columns is left untouched), then shift/prune the rest
|
|
1482
|
+
// over the removed slot. A *single*-column covering index collapses to empty and is
|
|
1483
|
+
// filtered by the trailing `length > 0` regardless; the explicit name exclusion is
|
|
1484
|
+
// what tears down a *multi*-column covering index, which would otherwise survive
|
|
1485
|
+
// orphaned — narrowed to its surviving columns — in `index_info` and on every write.
|
|
1486
|
+
const droppedCoveringIndexNames = new Set(droppedUcKeys.map(k => k.toLowerCase()));
|
|
1487
|
+
const updatedIndexes = (this.tableSchema.indexes || [])
|
|
1488
|
+
.filter(idx => !droppedCoveringIndexNames.has(idx.name.toLowerCase()))
|
|
1489
|
+
.map(idx => ({
|
|
901
1490
|
...idx,
|
|
902
1491
|
columns: idx.columns
|
|
903
|
-
.map(ic => ({ ...ic, index: ic.index > colIndex ? ic.index - 1 : ic.index }))
|
|
904
1492
|
.filter(ic => ic.index !== colIndex)
|
|
1493
|
+
.map(ic => ({ ...ic, index: ic.index > colIndex ? ic.index - 1 : ic.index }))
|
|
905
1494
|
})).filter(idx => idx.columns.length > 0);
|
|
906
1495
|
const finalNewTableSchema = Object.freeze({
|
|
907
1496
|
...this.tableSchema,
|
|
@@ -909,11 +1498,18 @@ export class MemoryTableManager {
|
|
|
909
1498
|
columnIndexMap: buildColumnIndexMap(updatedColumnsSchema),
|
|
910
1499
|
primaryKeyDefinition: Object.freeze(updatedPkDefinition),
|
|
911
1500
|
primaryKey: Object.freeze(updatedPrimaryKeyNames),
|
|
912
|
-
indexes: Object.freeze(updatedIndexes)
|
|
1501
|
+
indexes: Object.freeze(updatedIndexes),
|
|
1502
|
+
uniqueConstraints: remainingUniqueConstraints.length > 0
|
|
1503
|
+
? Object.freeze(remainingUniqueConstraints)
|
|
1504
|
+
: undefined,
|
|
913
1505
|
});
|
|
914
1506
|
this.baseLayer.updateSchema(finalNewTableSchema);
|
|
915
1507
|
await this.baseLayer.dropColumnFromBase(colIndex);
|
|
916
1508
|
this.tableSchema = finalNewTableSchema;
|
|
1509
|
+
// The covering-structure records for the dropped constraints are now stale —
|
|
1510
|
+
// clear them (keys computed against the pre-drop column names above).
|
|
1511
|
+
for (const key of droppedUcKeys)
|
|
1512
|
+
this.implicitCoveringStructures.delete(key);
|
|
917
1513
|
this.initializePrimaryKeyFunctions();
|
|
918
1514
|
// Emit schema change event
|
|
919
1515
|
this.eventEmitter?.emitSchemaChange?.({
|
|
@@ -1015,7 +1611,28 @@ export class MemoryTableManager {
|
|
|
1015
1611
|
}
|
|
1016
1612
|
const oldCol = this.tableSchema.columns[colIndex];
|
|
1017
1613
|
let newCol = oldCol;
|
|
1018
|
-
|
|
1614
|
+
// A collation change re-keys any PK / UNIQUE / index that orders by this
|
|
1615
|
+
// column, so it needs the structure re-sort + uniqueness re-validation below.
|
|
1616
|
+
let collationChanged = false;
|
|
1617
|
+
if (change.setCollation !== undefined) {
|
|
1618
|
+
const normalized = validateCollationForType(change.setCollation, oldCol.logicalType, change.columnName);
|
|
1619
|
+
const nameMatches = normalized === (oldCol.collation || 'BINARY');
|
|
1620
|
+
if (nameMatches && oldCol.collationExplicit) {
|
|
1621
|
+
return; // already explicit in the desired collation — nothing to do
|
|
1622
|
+
}
|
|
1623
|
+
// SET COLLATE is a user declaration with the same standing as a
|
|
1624
|
+
// CREATE-time COLLATE clause, so mark the collation explicit (rank 2 in
|
|
1625
|
+
// the comparison lattice) regardless of the column's creation history —
|
|
1626
|
+
// including SET COLLATE binary. When only the name matches but the column
|
|
1627
|
+
// was not yet explicit (a defaulted collation, or one inherited from
|
|
1628
|
+
// session default_collation), flip the flag as a METADATA-ONLY change:
|
|
1629
|
+
// the collation bytes are unchanged, so keep collationChanged false and
|
|
1630
|
+
// skip the physical re-sort / re-key / UNIQUE re-validation below. A
|
|
1631
|
+
// different name takes the full path AND sets the flag.
|
|
1632
|
+
newCol = { ...oldCol, collation: normalized, collationExplicit: true };
|
|
1633
|
+
collationChanged = !nameMatches;
|
|
1634
|
+
}
|
|
1635
|
+
else if (change.setNotNull !== undefined) {
|
|
1019
1636
|
if (change.setNotNull === true && !oldCol.notNull) {
|
|
1020
1637
|
// Tightening: scan for NULLs. If DEFAULT present, backfill first.
|
|
1021
1638
|
const defaultExpr = oldCol.defaultValue;
|
|
@@ -1092,12 +1709,36 @@ export class MemoryTableManager {
|
|
|
1092
1709
|
throw new QuereusError('ALTER COLUMN requires an attribute to change', StatusCode.INTERNAL);
|
|
1093
1710
|
}
|
|
1094
1711
|
const updatedCols = this.tableSchema.columns.map((c, i) => i === colIndex ? newCol : c);
|
|
1712
|
+
// Propagate a collation change into every PK-definition entry and index
|
|
1713
|
+
// column that orders by this column, so their comparators re-key under it.
|
|
1714
|
+
const updatedPkDef = collationChanged
|
|
1715
|
+
? this.tableSchema.primaryKeyDefinition.map(def => def.index === colIndex ? { ...def, collation: newCol.collation } : def)
|
|
1716
|
+
: this.tableSchema.primaryKeyDefinition;
|
|
1717
|
+
const updatedIndexes = (collationChanged && this.tableSchema.indexes)
|
|
1718
|
+
? this.tableSchema.indexes.map(idx => ({
|
|
1719
|
+
...idx,
|
|
1720
|
+
columns: idx.columns.map(ic => ic.index === colIndex ? { ...ic, collation: newCol.collation } : ic),
|
|
1721
|
+
}))
|
|
1722
|
+
: this.tableSchema.indexes;
|
|
1095
1723
|
const finalNewTableSchema = Object.freeze({
|
|
1096
1724
|
...this.tableSchema,
|
|
1097
1725
|
columns: Object.freeze(updatedCols),
|
|
1098
1726
|
columnIndexMap: buildColumnIndexMap(updatedCols),
|
|
1727
|
+
primaryKeyDefinition: Object.freeze(updatedPkDef),
|
|
1728
|
+
indexes: updatedIndexes ? Object.freeze(updatedIndexes) : updatedIndexes,
|
|
1099
1729
|
});
|
|
1100
1730
|
this.baseLayer.updateSchema(finalNewTableSchema);
|
|
1731
|
+
// A collation change re-sorts structures that order by the column and
|
|
1732
|
+
// re-validates uniqueness under the new collation. Rebuild the secondary
|
|
1733
|
+
// indexes FIRST (strict — a UNIQUE collision throws CONSTRAINT) and the
|
|
1734
|
+
// primary tree LAST (also strict — a PK collision throws), so a throw at
|
|
1735
|
+
// either step leaves the live primary tree intact for the catch's rollback.
|
|
1736
|
+
if (collationChanged) {
|
|
1737
|
+
this.baseLayer.rebuildAllSecondaryIndexesStrict();
|
|
1738
|
+
if (updatedPkDef.some(def => def.index === colIndex)) {
|
|
1739
|
+
this.baseLayer.rebuildPrimaryTreeStrict();
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1101
1742
|
this.tableSchema = finalNewTableSchema;
|
|
1102
1743
|
this.initializePrimaryKeyFunctions();
|
|
1103
1744
|
this.eventEmitter?.emitSchemaChange?.({
|
|
@@ -1110,7 +1751,12 @@ export class MemoryTableManager {
|
|
|
1110
1751
|
logger.operation('Alter Column', this._tableName, { columnName: change.columnName });
|
|
1111
1752
|
}
|
|
1112
1753
|
catch (e) {
|
|
1754
|
+
// Restore the prior schema, then rebuild secondary indexes (non-strict) so
|
|
1755
|
+
// a partially-cleared strict rebuild can't strand the index map in an
|
|
1756
|
+
// inconsistent state. The primary tree is only swapped on full success, so
|
|
1757
|
+
// it is already the original here.
|
|
1113
1758
|
this.baseLayer.updateSchema(originalManagerSchema);
|
|
1759
|
+
this.baseLayer.rebuildAllSecondaryIndexes();
|
|
1114
1760
|
this.tableSchema = originalManagerSchema;
|
|
1115
1761
|
this.initializePrimaryKeyFunctions();
|
|
1116
1762
|
logger.error('Alter Column', this._tableName, e);
|
|
@@ -1232,6 +1878,315 @@ export class MemoryTableManager {
|
|
|
1232
1878
|
release();
|
|
1233
1879
|
}
|
|
1234
1880
|
}
|
|
1881
|
+
/**
|
|
1882
|
+
* Drops a named table-level constraint (CHECK / UNIQUE / FOREIGN KEY). Schema-
|
|
1883
|
+
* only — constraints don't change row shape — except that dropping a UNIQUE
|
|
1884
|
+
* also tears down the implicit covering index (the auto-built secondary BTree
|
|
1885
|
+
* named `uc.name ?? '_uc_<cols>'`) so introspection / the declarative differ
|
|
1886
|
+
* don't observe an orphaned index. The class is resolved here (NOTFOUND /
|
|
1887
|
+
* ambiguous), so the engine can route through `module.alterTable` uniformly.
|
|
1888
|
+
*/
|
|
1889
|
+
async dropConstraint(constraintName) {
|
|
1890
|
+
if (this.isReadOnly)
|
|
1891
|
+
throw new QuereusError(`Table '${this._tableName}' is read-only`, StatusCode.READONLY);
|
|
1892
|
+
const lockKey = `MemoryTable.SchemaChange:${this.schemaName}.${this._tableName}`;
|
|
1893
|
+
const release = await Latches.acquire(lockKey);
|
|
1894
|
+
const originalManagerSchema = this.tableSchema;
|
|
1895
|
+
try {
|
|
1896
|
+
await this.ensureSchemaChangeSafety();
|
|
1897
|
+
const cls = resolveNamedConstraintClass(this.tableSchema, constraintName);
|
|
1898
|
+
const lower = constraintName.toLowerCase();
|
|
1899
|
+
let newSchema;
|
|
1900
|
+
let droppedIndexName;
|
|
1901
|
+
if (cls === 'check') {
|
|
1902
|
+
newSchema = Object.freeze({
|
|
1903
|
+
...this.tableSchema,
|
|
1904
|
+
checkConstraints: Object.freeze(this.tableSchema.checkConstraints.filter(c => c.name?.toLowerCase() !== lower)),
|
|
1905
|
+
});
|
|
1906
|
+
}
|
|
1907
|
+
else if (cls === 'foreignKey') {
|
|
1908
|
+
const remaining = (this.tableSchema.foreignKeys ?? []).filter(c => c.name?.toLowerCase() !== lower);
|
|
1909
|
+
newSchema = Object.freeze({
|
|
1910
|
+
...this.tableSchema,
|
|
1911
|
+
foreignKeys: remaining.length > 0 ? Object.freeze(remaining) : undefined,
|
|
1912
|
+
});
|
|
1913
|
+
}
|
|
1914
|
+
else {
|
|
1915
|
+
// UNIQUE — drop the constraint and its implicit covering index.
|
|
1916
|
+
const uc = this.tableSchema.uniqueConstraints.find(c => c.name?.toLowerCase() === lower);
|
|
1917
|
+
const idxName = uc.name ?? this.implicitIndexNameFor(uc);
|
|
1918
|
+
const idxLower = idxName.toLowerCase();
|
|
1919
|
+
const existingIndexes = this.tableSchema.indexes ?? [];
|
|
1920
|
+
const keptIndexes = existingIndexes.filter(i => i.name.toLowerCase() !== idxLower);
|
|
1921
|
+
if (keptIndexes.length !== existingIndexes.length)
|
|
1922
|
+
droppedIndexName = idxName;
|
|
1923
|
+
const remainingUcs = this.tableSchema.uniqueConstraints.filter(c => c.name?.toLowerCase() !== lower);
|
|
1924
|
+
newSchema = Object.freeze({
|
|
1925
|
+
...this.tableSchema,
|
|
1926
|
+
uniqueConstraints: remainingUcs.length > 0 ? Object.freeze(remainingUcs) : undefined,
|
|
1927
|
+
indexes: Object.freeze(keptIndexes),
|
|
1928
|
+
});
|
|
1929
|
+
this.implicitCoveringStructures.delete(uc.name ?? idxName);
|
|
1930
|
+
}
|
|
1931
|
+
this.baseLayer.updateSchema(newSchema);
|
|
1932
|
+
if (droppedIndexName)
|
|
1933
|
+
await this.baseLayer.dropIndexFromBase(droppedIndexName);
|
|
1934
|
+
this.tableSchema = newSchema;
|
|
1935
|
+
this.initializePrimaryKeyFunctions();
|
|
1936
|
+
this.eventEmitter?.emitSchemaChange?.({
|
|
1937
|
+
type: 'alter',
|
|
1938
|
+
objectType: 'table',
|
|
1939
|
+
schemaName: this.schemaName,
|
|
1940
|
+
objectName: this._tableName,
|
|
1941
|
+
});
|
|
1942
|
+
logger.operation('Drop Constraint', this._tableName, { constraintName });
|
|
1943
|
+
}
|
|
1944
|
+
catch (e) {
|
|
1945
|
+
this.baseLayer.updateSchema(originalManagerSchema);
|
|
1946
|
+
this.tableSchema = originalManagerSchema;
|
|
1947
|
+
this.initializePrimaryKeyFunctions();
|
|
1948
|
+
logger.error('Drop Constraint', this._tableName, e);
|
|
1949
|
+
throw e;
|
|
1950
|
+
}
|
|
1951
|
+
finally {
|
|
1952
|
+
release();
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
/**
|
|
1956
|
+
* Renames a named table-level constraint. Schema-only, with one caveat: a
|
|
1957
|
+
* UNIQUE whose implicit covering index is named after the constraint has that
|
|
1958
|
+
* index renamed in lock-step (so the index stays recognized as the
|
|
1959
|
+
* constraint's covering structure rather than surfacing as an orphan).
|
|
1960
|
+
*/
|
|
1961
|
+
async renameConstraint(oldName, newName) {
|
|
1962
|
+
if (this.isReadOnly)
|
|
1963
|
+
throw new QuereusError(`Table '${this._tableName}' is read-only`, StatusCode.READONLY);
|
|
1964
|
+
const lockKey = `MemoryTable.SchemaChange:${this.schemaName}.${this._tableName}`;
|
|
1965
|
+
const release = await Latches.acquire(lockKey);
|
|
1966
|
+
const originalManagerSchema = this.tableSchema;
|
|
1967
|
+
try {
|
|
1968
|
+
await this.ensureSchemaChangeSafety();
|
|
1969
|
+
const cls = resolveNamedConstraintClass(this.tableSchema, oldName);
|
|
1970
|
+
const oldLower = oldName.toLowerCase();
|
|
1971
|
+
let newSchema;
|
|
1972
|
+
let renamedIndex = false;
|
|
1973
|
+
if (cls === 'check') {
|
|
1974
|
+
newSchema = Object.freeze({
|
|
1975
|
+
...this.tableSchema,
|
|
1976
|
+
checkConstraints: Object.freeze(this.tableSchema.checkConstraints.map(c => (c.name?.toLowerCase() === oldLower ? { ...c, name: newName } : c))),
|
|
1977
|
+
});
|
|
1978
|
+
}
|
|
1979
|
+
else if (cls === 'foreignKey') {
|
|
1980
|
+
newSchema = Object.freeze({
|
|
1981
|
+
...this.tableSchema,
|
|
1982
|
+
foreignKeys: Object.freeze(this.tableSchema.foreignKeys.map(c => (c.name?.toLowerCase() === oldLower ? { ...c, name: newName } : c))),
|
|
1983
|
+
});
|
|
1984
|
+
}
|
|
1985
|
+
else {
|
|
1986
|
+
// UNIQUE — rename the constraint and, when present, its implicit covering index.
|
|
1987
|
+
const uc = this.tableSchema.uniqueConstraints.find(c => c.name?.toLowerCase() === oldLower);
|
|
1988
|
+
const oldIdxName = uc.name ?? this.implicitIndexNameFor(uc);
|
|
1989
|
+
const oldIdxLower = oldIdxName.toLowerCase();
|
|
1990
|
+
const newUcs = this.tableSchema.uniqueConstraints.map(c => (c.name?.toLowerCase() === oldLower ? { ...c, name: newName } : c));
|
|
1991
|
+
let indexes = this.tableSchema.indexes ?? [];
|
|
1992
|
+
if (indexes.some(i => i.name.toLowerCase() === oldIdxLower)) {
|
|
1993
|
+
indexes = indexes.map(i => (i.name.toLowerCase() === oldIdxLower ? { ...i, name: newName } : i));
|
|
1994
|
+
renamedIndex = true;
|
|
1995
|
+
}
|
|
1996
|
+
newSchema = Object.freeze({
|
|
1997
|
+
...this.tableSchema,
|
|
1998
|
+
uniqueConstraints: Object.freeze(newUcs),
|
|
1999
|
+
indexes: Object.freeze(indexes),
|
|
2000
|
+
});
|
|
2001
|
+
const rec = this.implicitCoveringStructures.get(uc.name ?? oldIdxName);
|
|
2002
|
+
if (rec) {
|
|
2003
|
+
this.implicitCoveringStructures.delete(uc.name ?? oldIdxName);
|
|
2004
|
+
this.implicitCoveringStructures.set(newName, { ...rec, indexName: renamedIndex ? newName : rec.indexName });
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
2007
|
+
this.baseLayer.updateSchema(newSchema);
|
|
2008
|
+
// A renamed covering index lives under a new key — rebuild secondary indexes
|
|
2009
|
+
// from the post-rename schema so the base layer's index map matches.
|
|
2010
|
+
if (renamedIndex)
|
|
2011
|
+
this.baseLayer.rebuildAllSecondaryIndexes();
|
|
2012
|
+
this.tableSchema = newSchema;
|
|
2013
|
+
this.initializePrimaryKeyFunctions();
|
|
2014
|
+
this.eventEmitter?.emitSchemaChange?.({
|
|
2015
|
+
type: 'alter',
|
|
2016
|
+
objectType: 'table',
|
|
2017
|
+
schemaName: this.schemaName,
|
|
2018
|
+
objectName: this._tableName,
|
|
2019
|
+
});
|
|
2020
|
+
logger.operation('Rename Constraint', this._tableName, { oldName, newName });
|
|
2021
|
+
}
|
|
2022
|
+
catch (e) {
|
|
2023
|
+
this.baseLayer.updateSchema(originalManagerSchema);
|
|
2024
|
+
this.tableSchema = originalManagerSchema;
|
|
2025
|
+
this.initializePrimaryKeyFunctions();
|
|
2026
|
+
logger.error('Rename Constraint', this._tableName, e);
|
|
2027
|
+
throw e;
|
|
2028
|
+
}
|
|
2029
|
+
finally {
|
|
2030
|
+
release();
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
/**
|
|
2034
|
+
* Adds a table-level UNIQUE or FOREIGN KEY constraint to an existing table,
|
|
2035
|
+
* re-validating the current rows against it and failing atomically with
|
|
2036
|
+
* `CONSTRAINT` (no schema mutation) when the data violates it. Mirrors the
|
|
2037
|
+
* latch + `ensureSchemaChangeSafety()` + snapshot/restore scaffolding of
|
|
2038
|
+
* {@link createIndex} / {@link dropConstraint}.
|
|
2039
|
+
*
|
|
2040
|
+
* - UNIQUE builds (or reuses) the implicit covering secondary index; the build
|
|
2041
|
+
* raises `CONSTRAINT` on the first duplicate among in-scope rows (partial
|
|
2042
|
+
* predicate + per-column collation honored, NULLs distinct).
|
|
2043
|
+
* - FOREIGN KEY appends the constraint and runs the pragma-gated existing-row
|
|
2044
|
+
* validation (engine-side enforcement needs no physical structure).
|
|
2045
|
+
* - CHECK appends the constraint (no physical structure, no existing-row scan —
|
|
2046
|
+
* matching the engine's prior in-emitter behavior); it routes here, rather than
|
|
2047
|
+
* being applied catalog-only, so the module-cached schema stays in lock-step
|
|
2048
|
+
* with the catalog and a later `DROP/RENAME CONSTRAINT` resolves it. (The engine
|
|
2049
|
+
* keeps an engine-side fallback in `runtime/emit/add-constraint.ts` only for
|
|
2050
|
+
* modules that omit `alterTable` — which cannot DROP/RENAME a constraint anyway.)
|
|
2051
|
+
*/
|
|
2052
|
+
async addConstraint(constraint) {
|
|
2053
|
+
if (this.isReadOnly)
|
|
2054
|
+
throw new QuereusError(`Table '${this._tableName}' is read-only`, StatusCode.READONLY);
|
|
2055
|
+
const lockKey = `MemoryTable.SchemaChange:${this.schemaName}.${this._tableName}`;
|
|
2056
|
+
const release = await Latches.acquire(lockKey);
|
|
2057
|
+
const originalManagerSchema = this.tableSchema;
|
|
2058
|
+
try {
|
|
2059
|
+
await this.ensureSchemaChangeSafety();
|
|
2060
|
+
if (constraint.type === 'unique') {
|
|
2061
|
+
await this.addUniqueConstraint(constraint);
|
|
2062
|
+
}
|
|
2063
|
+
else if (constraint.type === 'foreignKey') {
|
|
2064
|
+
await this.addForeignKeyConstraint(constraint);
|
|
2065
|
+
}
|
|
2066
|
+
else if (constraint.type === 'check') {
|
|
2067
|
+
this.addCheckConstraint(constraint);
|
|
2068
|
+
}
|
|
2069
|
+
else {
|
|
2070
|
+
throw new QuereusError(`MemoryTable ADD CONSTRAINT does not support constraint type '${constraint.type}'`, StatusCode.UNSUPPORTED);
|
|
2071
|
+
}
|
|
2072
|
+
this.eventEmitter?.emitSchemaChange?.({
|
|
2073
|
+
type: 'alter',
|
|
2074
|
+
objectType: 'table',
|
|
2075
|
+
schemaName: this.schemaName,
|
|
2076
|
+
objectName: this._tableName,
|
|
2077
|
+
});
|
|
2078
|
+
logger.operation('Add Constraint', this._tableName, { type: constraint.type, name: constraint.name });
|
|
2079
|
+
}
|
|
2080
|
+
catch (e) {
|
|
2081
|
+
// Restore the prior schema and rebuild secondary indexes (non-strict) so a
|
|
2082
|
+
// half-built covering index can't strand the base layer's index map.
|
|
2083
|
+
this.baseLayer.updateSchema(originalManagerSchema);
|
|
2084
|
+
this.baseLayer.rebuildAllSecondaryIndexes();
|
|
2085
|
+
this.tableSchema = originalManagerSchema;
|
|
2086
|
+
this.initializePrimaryKeyFunctions();
|
|
2087
|
+
logger.error('Add Constraint', this._tableName, e);
|
|
2088
|
+
throw e;
|
|
2089
|
+
}
|
|
2090
|
+
finally {
|
|
2091
|
+
release();
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
/**
|
|
2095
|
+
* CHECK arm of {@link addConstraint}. Schema-only: a CHECK has no covering
|
|
2096
|
+
* structure and (matching the engine's prior in-emitter behavior) no existing-row
|
|
2097
|
+
* validation, so this just appends the constraint to the cached schema. Enforcement
|
|
2098
|
+
* is engine-side at INSERT/UPDATE plan time. Runs under the same latch / rollback
|
|
2099
|
+
* scaffolding as the other arms (via {@link addConstraint}).
|
|
2100
|
+
*/
|
|
2101
|
+
addCheckConstraint(constraint) {
|
|
2102
|
+
const check = buildCheckConstraintSchema(constraint, this.tableSchema.checkConstraints.length);
|
|
2103
|
+
const newSchema = Object.freeze({
|
|
2104
|
+
...this.tableSchema,
|
|
2105
|
+
checkConstraints: Object.freeze([...this.tableSchema.checkConstraints, check]),
|
|
2106
|
+
});
|
|
2107
|
+
this.baseLayer.updateSchema(newSchema);
|
|
2108
|
+
this.tableSchema = newSchema;
|
|
2109
|
+
}
|
|
2110
|
+
/**
|
|
2111
|
+
* UNIQUE arm of {@link addConstraint}. Builds the covering secondary index the
|
|
2112
|
+
* same way {@link ensureUniqueConstraintIndexes} does (validating existing rows
|
|
2113
|
+
* via `addIndexToBase` → `populateNewIndex`), unless an existing *unique* index
|
|
2114
|
+
* already covers the exact columns — in which case the data is already
|
|
2115
|
+
* validated and we only register the covering structure.
|
|
2116
|
+
*/
|
|
2117
|
+
async addUniqueConstraint(constraint) {
|
|
2118
|
+
const uc = buildUniqueConstraintSchema(constraint, this.tableSchema.columnIndexMap);
|
|
2119
|
+
const columns = this.tableSchema.columns;
|
|
2120
|
+
const existingIndexes = this.tableSchema.indexes ?? [];
|
|
2121
|
+
const appendedUcs = Object.freeze([...(this.tableSchema.uniqueConstraints ?? []), uc]);
|
|
2122
|
+
// Reuse: an existing UNIQUE index over the exact columns already guarantees
|
|
2123
|
+
// uniqueness, so skip the rebuild. A non-unique index gives no such guarantee
|
|
2124
|
+
// — fall through to build-and-validate. The reused index must ALSO be
|
|
2125
|
+
// collation-equivalent to the declared column collations: a finer/coarser
|
|
2126
|
+
// same-column-set index (e.g. a BINARY `create unique index` over a NOCASE
|
|
2127
|
+
// column) enforces under ITS collation, not the declared one, so reusing it
|
|
2128
|
+
// would under-enforce this non-derived UNIQUE. A collation mismatch falls
|
|
2129
|
+
// through to build the distinct `_uc_*` covering index; the user index keeps
|
|
2130
|
+
// enforcing its own (stricter) uniqueness independently (matches SQLite).
|
|
2131
|
+
const matchingUniqueIndex = existingIndexes.find(idx => idx.unique &&
|
|
2132
|
+
idx.columns.length === uc.columns.length &&
|
|
2133
|
+
idx.columns.every((col, i) => col.index === uc.columns[i]) &&
|
|
2134
|
+
this.indexCollationsMatchDeclared(idx, uc));
|
|
2135
|
+
if (matchingUniqueIndex) {
|
|
2136
|
+
const newSchema = Object.freeze({
|
|
2137
|
+
...this.tableSchema,
|
|
2138
|
+
uniqueConstraints: appendedUcs,
|
|
2139
|
+
});
|
|
2140
|
+
this.baseLayer.updateSchema(newSchema);
|
|
2141
|
+
this.tableSchema = newSchema;
|
|
2142
|
+
this.initializePrimaryKeyFunctions();
|
|
2143
|
+
this.implicitCoveringStructures.set(uc.name ?? matchingUniqueIndex.name, { indexName: matchingUniqueIndex.name, origin: 'implicit-from-unique-constraint' });
|
|
2144
|
+
return;
|
|
2145
|
+
}
|
|
2146
|
+
const colNames = uc.columns.map(i => columns[i]?.name ?? String(i));
|
|
2147
|
+
const indexName = uc.name ?? `_uc_${colNames.join('_')}`;
|
|
2148
|
+
const indexSchema = {
|
|
2149
|
+
name: indexName,
|
|
2150
|
+
// Carry per-column collation so enforcement honors e.g. NOCASE (mirrors
|
|
2151
|
+
// ensureUniqueConstraintIndexes). The covering index is NOT flagged unique
|
|
2152
|
+
// — insert-time enforcement routes through `uniqueConstraints`.
|
|
2153
|
+
columns: uc.columns.map(colIdx => ({ index: colIdx, collation: columns[colIdx]?.collation })),
|
|
2154
|
+
predicate: uc.predicate,
|
|
2155
|
+
};
|
|
2156
|
+
const newSchema = Object.freeze({
|
|
2157
|
+
...this.tableSchema,
|
|
2158
|
+
uniqueConstraints: appendedUcs,
|
|
2159
|
+
indexes: Object.freeze([...existingIndexes, indexSchema]),
|
|
2160
|
+
});
|
|
2161
|
+
// Swap the schema FIRST so `addIndexToBase` → `indexEnforcesUnique` sees the
|
|
2162
|
+
// new constraint and rejects duplicates, then populate (throws CONSTRAINT on
|
|
2163
|
+
// the first in-scope duplicate). A throw rolls back via the catch in addConstraint.
|
|
2164
|
+
this.baseLayer.updateSchema(newSchema);
|
|
2165
|
+
await this.baseLayer.addIndexToBase(indexSchema);
|
|
2166
|
+
this.tableSchema = newSchema;
|
|
2167
|
+
this.initializePrimaryKeyFunctions();
|
|
2168
|
+
this.implicitCoveringStructures.set(uc.name ?? indexName, { indexName, origin: 'implicit-from-unique-constraint' });
|
|
2169
|
+
}
|
|
2170
|
+
/**
|
|
2171
|
+
* FOREIGN KEY arm of {@link addConstraint}. Validates existing child rows
|
|
2172
|
+
* against the new FK (pragma-gated; throws CONSTRAINT on an orphan), then
|
|
2173
|
+
* appends it to the cached schema. No physical structure — FK enforcement is
|
|
2174
|
+
* engine-side (synthesized EXISTS checks at plan time).
|
|
2175
|
+
*/
|
|
2176
|
+
async addForeignKeyConstraint(constraint) {
|
|
2177
|
+
const fk = buildForeignKeyConstraintSchema(constraint, this.tableSchema.columnIndexMap, this._tableName, this.schemaName);
|
|
2178
|
+
const newSchema = Object.freeze({
|
|
2179
|
+
...this.tableSchema,
|
|
2180
|
+
foreignKeys: Object.freeze([...(this.tableSchema.foreignKeys ?? []), fk]),
|
|
2181
|
+
});
|
|
2182
|
+
// Validate BEFORE swapping the cached schema — a throw leaves the table
|
|
2183
|
+
// unmodified. The scan only reads (no schema-change latch), so holding our
|
|
2184
|
+
// own latch here is safe; ensureSchemaChangeSafety already drained to base.
|
|
2185
|
+
await validateForeignKeyOverExistingRows(this.db, newSchema, fk);
|
|
2186
|
+
this.baseLayer.updateSchema(newSchema);
|
|
2187
|
+
this.tableSchema = newSchema;
|
|
2188
|
+
this.initializePrimaryKeyFunctions();
|
|
2189
|
+
}
|
|
1235
2190
|
async destroy() {
|
|
1236
2191
|
const lockKey = `MemoryTable.Destroy:${this.schemaName}.${this._tableName}`;
|
|
1237
2192
|
const release = await Latches.acquire(lockKey);
|
|
@@ -1267,8 +2222,41 @@ export class MemoryTableManager {
|
|
|
1267
2222
|
connection.readLayer = this.baseLayer;
|
|
1268
2223
|
}
|
|
1269
2224
|
}
|
|
2225
|
+
// The manager's `connections` map covers only connections still attached to this
|
|
2226
|
+
// manager. A connection can be DETACHED from the map (removed by disconnect after an
|
|
2227
|
+
// autocommit collapse) while remaining REGISTERED in the Database connection registry —
|
|
2228
|
+
// `MemoryTable.ensureConnection` reuses exactly such a connection for a later scan. The
|
|
2229
|
+
// loop above misses it, so after an in-transaction schema change (e.g. ALTER TABLE ADD
|
|
2230
|
+
// COLUMN, now permitted inside an explicit transaction) it keeps reading a stale
|
|
2231
|
+
// pre-change layer carrying the OLD column shape — the materialized-view-source-stale-read
|
|
2232
|
+
// bug. A detached connection always has `pendingTransactionLayer === null` (disconnect
|
|
2233
|
+
// defers while a pending layer is uncommitted), so this never discards in-flight writes.
|
|
2234
|
+
this.repointRegisteredConnections();
|
|
1270
2235
|
logger.debugLog(`Schema change safety check passed for ${this._tableName}. Current committed layer is base.`);
|
|
1271
2236
|
}
|
|
2237
|
+
/**
|
|
2238
|
+
* Re-point every Database-registered {@link MemoryTableConnection} backed by this
|
|
2239
|
+
* manager (including ones detached from {@link connections}) at the current base layer,
|
|
2240
|
+
* when it carries no uncommitted pending layer. Companion to the `connections`-map sweep
|
|
2241
|
+
* in {@link ensureSchemaChangeSafety}: it closes the gap for a connection that lives in
|
|
2242
|
+
* the Database registry but not in the manager's map.
|
|
2243
|
+
*/
|
|
2244
|
+
repointRegisteredConnections() {
|
|
2245
|
+
const qualifiedName = `${this.schemaName}.${this._tableName}`;
|
|
2246
|
+
for (const c of this.db.getConnectionsForTable(qualifiedName)) {
|
|
2247
|
+
if (!(c instanceof MemoryVirtualTableConnection))
|
|
2248
|
+
continue;
|
|
2249
|
+
const mc = c.getMemoryConnection();
|
|
2250
|
+
if (mc.tableManager !== this)
|
|
2251
|
+
continue;
|
|
2252
|
+
if (mc.pendingTransactionLayer !== null)
|
|
2253
|
+
continue;
|
|
2254
|
+
if (mc.readLayer === this.baseLayer)
|
|
2255
|
+
continue;
|
|
2256
|
+
logger.debugLog(`[Schema Safety] Re-pointing registered connection ${mc.connectionId} to base layer`);
|
|
2257
|
+
mc.readLayer = this.baseLayer;
|
|
2258
|
+
}
|
|
2259
|
+
}
|
|
1272
2260
|
/** Consolidates all transaction data into the base layer for schema changes */
|
|
1273
2261
|
async consolidateToBaseLayer() {
|
|
1274
2262
|
const lockKey = `MemoryTable.Consolidate:${this.schemaName}.${this._tableName}`;
|
|
@@ -1310,45 +2298,16 @@ export class MemoryTableManager {
|
|
|
1310
2298
|
allRows.push(primaryTree.at(path));
|
|
1311
2299
|
}
|
|
1312
2300
|
logger.debugLog(`[Consolidate] Collected ${allRows.length} rows from transaction layer. Row widths: ${allRows.map(r => r.length).join(',')}`);
|
|
1313
|
-
//
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
// Now insert collected rows into the base layer
|
|
1320
|
-
for (const row of allRows) {
|
|
1321
|
-
this.baseLayer.primaryTree.insert(row);
|
|
1322
|
-
}
|
|
1323
|
-
// Count base layer rows after
|
|
1324
|
-
let baseCountAfter = 0;
|
|
1325
|
-
for (const _path of this.baseLayer.primaryTree.ascending(this.baseLayer.primaryTree.first())) {
|
|
1326
|
-
baseCountAfter++;
|
|
1327
|
-
}
|
|
1328
|
-
logger.debugLog(`[Consolidate] Base layer has ${baseCountAfter} rows after copy`);
|
|
1329
|
-
// Also need to rebuild secondary indexes in the base layer
|
|
1330
|
-
await this.baseLayer.rebuildAllSecondaryIndexes();
|
|
2301
|
+
// Replace (do not union into) the base primary tree: `allRows` is the layer's
|
|
2302
|
+
// merged view with deletes already applied, so any row deleted in the
|
|
2303
|
+
// transaction layer must be physically removed from the base — otherwise a
|
|
2304
|
+
// later base-direct scan (e.g. a UNIQUE index build) resurrects it. This also
|
|
2305
|
+
// rebuilds the base secondary indexes from the new tree.
|
|
2306
|
+
this.baseLayer.rebuildPrimaryTreeFromRows(allRows);
|
|
1331
2307
|
}
|
|
1332
2308
|
/** Scans a layer according to the given plan, yielding matching rows. */
|
|
1333
2309
|
async *scanLayer(layer, plan) {
|
|
1334
2310
|
yield* scanLayerImpl(layer, plan);
|
|
1335
2311
|
}
|
|
1336
2312
|
}
|
|
1337
|
-
/**
|
|
1338
|
-
* Resolves the per-constraint default conflict action for PK conflicts.
|
|
1339
|
-
* Prefers the table-level `PRIMARY KEY (...) ON CONFLICT <action>` clause
|
|
1340
|
-
* (the constraint's own declaration) over any column-level `defaultConflict`
|
|
1341
|
-
* declared on a PK column (which primarily targets that column's own
|
|
1342
|
-
* constraints and only acts as a fallback for PK conflicts).
|
|
1343
|
-
*/
|
|
1344
|
-
function resolvePkDefaultConflict(schema) {
|
|
1345
|
-
if (schema.primaryKeyDefaultConflict !== undefined)
|
|
1346
|
-
return schema.primaryKeyDefaultConflict;
|
|
1347
|
-
for (const def of schema.primaryKeyDefinition) {
|
|
1348
|
-
const col = schema.columns[def.index];
|
|
1349
|
-
if (col && col.defaultConflict !== undefined)
|
|
1350
|
-
return col.defaultConflict;
|
|
1351
|
-
}
|
|
1352
|
-
return undefined;
|
|
1353
|
-
}
|
|
1354
2313
|
//# sourceMappingURL=manager.js.map
|