@quereus/quereus 4.20.0 → 4.20.2
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 +2 -0
- package/dist/src/common/types.d.ts +4 -4
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +3 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/schema/catalog-rendering.js +13 -1
- package/dist/src/schema/catalog-rendering.js.map +1 -1
- package/dist/src/schema/catalog.d.ts +35 -2
- package/dist/src/schema/catalog.d.ts.map +1 -1
- package/dist/src/schema/catalog.js +42 -33
- package/dist/src/schema/catalog.js.map +1 -1
- package/dist/src/schema/schema-differ.d.ts +1 -1
- package/dist/src/schema/schema-differ.d.ts.map +1 -1
- package/dist/src/schema/schema-differ.js +195 -33
- package/dist/src/schema/schema-differ.js.map +1 -1
- package/dist/src/schema/unique-enforcement.d.ts +23 -1
- package/dist/src/schema/unique-enforcement.d.ts.map +1 -1
- package/dist/src/schema/unique-enforcement.js +43 -1
- package/dist/src/schema/unique-enforcement.js.map +1 -1
- package/dist/src/vtab/memory/layer/manager.d.ts +15 -12
- package/dist/src/vtab/memory/layer/manager.d.ts.map +1 -1
- package/dist/src/vtab/memory/layer/manager.js +87 -100
- package/dist/src/vtab/memory/layer/manager.js.map +1 -1
- package/package.json +7 -4
- package/test/README.md +173 -0
- package/test/logic/01-basic.sqllogic +39 -0
- package/test/logic/01.1-select-projection-extras.sqllogic +130 -0
- package/test/logic/01.2-strings.sqllogic +43 -0
- package/test/logic/01.5-insert-select.sqllogic +237 -0
- package/test/logic/01.6-update-extras.sqllogic +118 -0
- package/test/logic/01.7-update-from.sqllogic +67 -0
- package/test/logic/01.8-delete-extras.sqllogic +122 -0
- package/test/logic/01.8.1-delete-range-cow.sqllogic +115 -0
- package/test/logic/01.9-query-expr-dml-gates.sqllogic +32 -0
- package/test/logic/01.9-query-expr-dml.sqllogic +171 -0
- package/test/logic/01.9-query-expr-values.sqllogic +111 -0
- package/test/logic/01.9.1-limit-over-dml-subquery.sqllogic +119 -0
- package/test/logic/02-filters.sqllogic +54 -0
- package/test/logic/02-smoke.sqllogic +62 -0
- package/test/logic/02.1-bind-parameters.sqllogic +223 -0
- package/test/logic/03-expressions.sqllogic +190 -0
- package/test/logic/03.1-quoted-identifiers.sqllogic +55 -0
- package/test/logic/03.4-defaults.sqllogic +554 -0
- package/test/logic/03.4.1-default-edge-cases.sqllogic +99 -0
- package/test/logic/03.5-tvf.sqllogic +61 -0
- package/test/logic/03.6-type-system.sqllogic +544 -0
- package/test/logic/03.6.1-numeric-text-comparison-coercion.sqllogic +244 -0
- package/test/logic/03.6.2-value-to-text.sqllogic +224 -0
- package/test/logic/03.7-bigint-mixed-arithmetic.sqllogic +65 -0
- package/test/logic/03.8-not-precedence.sqllogic +255 -0
- package/test/logic/03.9-is-bool-predicate.sqllogic +150 -0
- package/test/logic/03.9.1-is-bool-predicate-partial-index.sqllogic +34 -0
- package/test/logic/04-order-by.sqllogic +38 -0
- package/test/logic/04-transactions.sqllogic +239 -0
- package/test/logic/04a-savepoint-lazy-attach.sqllogic +122 -0
- package/test/logic/05-vtab_memory.sqllogic +50 -0
- package/test/logic/05.0.1-vtab-memory-unique-index-collation.sqllogic +34 -0
- package/test/logic/05.1-composite-pk-range-scan.sqllogic +147 -0
- package/test/logic/05.1.1-secondary-index-range-scan.sqllogic +56 -0
- package/test/logic/05.2-cast-seek-correctness.sqllogic +124 -0
- package/test/logic/06-builtin_functions.sqllogic +149 -0
- package/test/logic/06.1-string-functions.sqllogic +92 -0
- package/test/logic/06.1.3-like-glob-edges.sqllogic +53 -0
- package/test/logic/06.2-math-functions.sqllogic +82 -0
- package/test/logic/06.3-schema.sqllogic +48 -0
- package/test/logic/06.3.1-schema-all-schemas.sqllogic +39 -0
- package/test/logic/06.3.2-schema-foreign-keys.sqllogic +208 -0
- package/test/logic/06.3.3-introspection-tags.sqllogic +202 -0
- package/test/logic/06.3.3.1-introspection-tags-index.sqllogic +74 -0
- package/test/logic/06.3.4-view-info.sqllogic +378 -0
- package/test/logic/06.3.5-column-info.sqllogic +347 -0
- package/test/logic/06.3.6-schema-views.sqllogic +29 -0
- package/test/logic/06.4-schema-search-path.sqllogic +354 -0
- package/test/logic/06.4.1-schema-case-insensitive.sqllogic +83 -0
- package/test/logic/06.4.2-collation-extras.sqllogic +472 -0
- package/test/logic/06.4.2.1-collation-index-access-paths.sqllogic +140 -0
- package/test/logic/06.4.3-write-path-collation.sqllogic +99 -0
- package/test/logic/06.4.4-comparison-collation-precedence.sqllogic +342 -0
- package/test/logic/06.4.5-any-collate-declared-keys.sqllogic +92 -0
- package/test/logic/06.5-polymorphic-types.sqllogic +169 -0
- package/test/logic/06.5.1-conversion-functions.sqllogic +173 -0
- package/test/logic/06.5.2-scalar-minmax.sqllogic +16 -0
- package/test/logic/06.5.3-undeclared-return-type-comparison.sqllogic +244 -0
- package/test/logic/06.5.4-declared-return-type-builtins.sqllogic +288 -0
- package/test/logic/06.6-aggregate-extended.sqllogic +78 -0
- package/test/logic/06.7-json-extended.sqllogic +53 -0
- package/test/logic/06.8-json-path-operators.sqllogic +99 -0
- package/test/logic/06.9-json-canonical-key.sqllogic +65 -0
- package/test/logic/06.9.1-json-coerce-once.sqllogic +417 -0
- package/test/logic/06.9.1.1-json-coerce-once-index.sqllogic +39 -0
- package/test/logic/06.9.2-json-structural-equality.sqllogic +382 -0
- package/test/logic/06.9.2.1-json-structural-equality-index.sqllogic +76 -0
- package/test/logic/06.9.3-json-index-range-seek.sqllogic +205 -0
- package/test/logic/07-aggregates.sqllogic +193 -0
- package/test/logic/07.1-aggregate-filter-clause.sqllogic +12 -0
- package/test/logic/07.2-aggregate-order-by.sqllogic +11 -0
- package/test/logic/07.3-group-by-extras.sqllogic +602 -0
- package/test/logic/07.3.1-group-by-order-by-key.sqllogic +164 -0
- package/test/logic/07.3.2-grouped-select-list-shape.sqllogic +231 -0
- package/test/logic/07.4-group-by-filter-composite-pk.sqllogic +120 -0
- package/test/logic/07.5-sum-mixed-exact-and-float.sqllogic +151 -0
- package/test/logic/07.5-window.sqllogic +1941 -0
- package/test/logic/07.5.1-window-named.sqllogic +13 -0
- package/test/logic/07.5.2-window-nth-value.sqllogic +13 -0
- package/test/logic/07.6-subqueries.sqllogic +78 -0
- package/test/logic/07.6.1-subquery-extras.sqllogic +93 -0
- package/test/logic/07.7-and-or-short-circuit.sqllogic +58 -0
- package/test/logic/07.7-in-subquery-caching.sqllogic +174 -0
- package/test/logic/07.7-scalar-agg-decorrelation.sqllogic +197 -0
- package/test/logic/07.7.1-in-extras.sqllogic +158 -0
- package/test/logic/07.7.1-scalar-agg-decorrelation-filter.sqllogic +105 -0
- package/test/logic/07.7.2-scalar-agg-decorrelation-nested.sqllogic +115 -0
- package/test/logic/07.7.3-exists-in-select-decorrelation.sqllogic +107 -0
- package/test/logic/07.7.4-where-conjunct-ordering.sqllogic +121 -0
- package/test/logic/07.7.5-filter-lost-under-index-order.sqllogic +134 -0
- package/test/logic/07.7.6-correlated-predicate-scope.sqllogic +178 -0
- package/test/logic/07.7.7-join-source-scope-shadowing.sqllogic +137 -0
- package/test/logic/07.7.8-correlated-ref-to-computed-column.sqllogic +165 -0
- package/test/logic/07.7.9-conjunct-monotonicity.sqllogic +200 -0
- package/test/logic/07.8-correlated-subquery-edges.sqllogic +71 -0
- package/test/logic/07.8.1-correlated-in-decorrelation-gates.sqllogic +156 -0
- package/test/logic/07.9-in-value-list.sqllogic +154 -0
- package/test/logic/07.9.1-in-value-list-composite-index.sqllogic +38 -0
- package/test/logic/08-views.sqllogic +64 -0
- package/test/logic/08.1-semi-anti-join.sqllogic +191 -0
- package/test/logic/08.1-view-edge-cases.sqllogic +118 -0
- package/test/logic/08.1.1-uncorrelated-in-semijoin.sqllogic +251 -0
- package/test/logic/08.2-existence-flag-semijoin-recovery.sqllogic +197 -0
- package/test/logic/08.3-existence-flag-inner-recovery.sqllogic +102 -0
- package/test/logic/08.4-key-set-semi-join.sqllogic +447 -0
- package/test/logic/09-set_operations.sqllogic +244 -0
- package/test/logic/09.1-set-op-cross-collation.sqllogic +230 -0
- package/test/logic/10-distinct_datatypes.sqllogic +127 -0
- package/test/logic/10.1-ddl-lifecycle.sqllogic +203 -0
- package/test/logic/10.1.1-create-table-syntax-edges.sqllogic +47 -0
- package/test/logic/10.1.2-ddl-in-transaction.sqllogic +143 -0
- package/test/logic/10.1.2.1-add-constraint-in-transaction.sqllogic +59 -0
- package/test/logic/10.1.3-ddl-drop-in-transaction.sqllogic +187 -0
- package/test/logic/10.1.3.1-drop-constraint-in-transaction.sqllogic +45 -0
- package/test/logic/10.1.4-create-table-duplicate-constraint-names.sqllogic +149 -0
- package/test/logic/10.1.4-ddl-transaction-policy.sqllogic +314 -0
- package/test/logic/10.1.5-alter-add-constraint-minted-name-disambiguation.sqllogic +219 -0
- package/test/logic/10.1.5-create-index-lifecycle.sqllogic +72 -0
- package/test/logic/10.1.6-ddl-transaction-policy-index.sqllogic +143 -0
- package/test/logic/10.1.7-create-table-default-type-check.sqllogic +119 -0
- package/test/logic/10.1.8-alter-add-check-existing-rows.sqllogic +117 -0
- package/test/logic/10.2-column-features.sqllogic +365 -0
- package/test/logic/10.2.1-table-options-rejection.sqllogic +63 -0
- package/test/logic/10.2.2-default-collation-memory.sqllogic +30 -0
- package/test/logic/10.3-function-features.sqllogic +247 -0
- package/test/logic/10.4-schema-scale.sqllogic +168 -0
- package/test/logic/10.4.1-schema-scale-indexes.sqllogic +121 -0
- package/test/logic/10.5-indexes.sqllogic +164 -0
- package/test/logic/10.5.1-partial-indexes.sqllogic +526 -0
- package/test/logic/10.5.2-expression-indexes.sqllogic +34 -0
- package/test/logic/10.5.3-desc-index-ordering.sqllogic +130 -0
- package/test/logic/10.5.4-composite-pk-index-update-phantom.sqllogic +55 -0
- package/test/logic/10.5.5-index-name-uniqueness.sqllogic +76 -0
- package/test/logic/10.5.6-minmax-index-boundary.sqllogic +204 -0
- package/test/logic/10.5.7-implicit-unique-index-lifecycle.sqllogic +1216 -0
- package/test/logic/10.6-distinct-edge-cases.sqllogic +119 -0
- package/test/logic/100-predicate-normalization-edge-cases.sqllogic +238 -0
- package/test/logic/100.1-where-extras.sqllogic +163 -0
- package/test/logic/100.1.1-where-join-on-order-indexed.sqllogic +35 -0
- package/test/logic/101-builtin-mutation-kills.sqllogic +974 -0
- package/test/logic/101-transaction-edge-cases.sqllogic +395 -0
- package/test/logic/102-schema-catalog-edge-cases.sqllogic +252 -0
- package/test/logic/102-unique-constraints.sqllogic +145 -0
- package/test/logic/102.0.1-schema-catalog-indexed-table.sqllogic +32 -0
- package/test/logic/102.1-unique-edge-cases.sqllogic +111 -0
- package/test/logic/102.1.1-unique-index-creation-edge-cases.sqllogic +75 -0
- package/test/logic/102.2-unique-collation.sqllogic +181 -0
- package/test/logic/102.2.1-unique-collation-index-derived.sqllogic +305 -0
- package/test/logic/103-database-options-edge-cases.sqllogic +108 -0
- package/test/logic/103.1-pragma-boolean-values.sqllogic +126 -0
- package/test/logic/104-emit-mutation-kills.sqllogic +236 -0
- package/test/logic/105-vtab-memory-mutation-kills.sqllogic +754 -0
- package/test/logic/105.1-vtab-memory-index-mutation-kills.sqllogic +540 -0
- package/test/logic/106-constraint-extractor-mutation-kills.sqllogic +350 -0
- package/test/logic/107-temporal-arithmetic-mutation-kills.sqllogic +833 -0
- package/test/logic/108-cardinality-estimation.sqllogic +146 -0
- package/test/logic/108.1-analyze-name-resolution.sqllogic +102 -0
- package/test/logic/109-aggregate-physical-selection.sqllogic +103 -0
- package/test/logic/11-joins.sqllogic +86 -0
- package/test/logic/11.1-join-using.sqllogic +267 -0
- package/test/logic/11.2-comma-join.sqllogic +35 -0
- package/test/logic/11.3-index-nested-loop-join.sqllogic +305 -0
- package/test/logic/11.3-mismatched-collation-join.sqllogic +74 -0
- package/test/logic/11.4-hash-join-side-swap.sqllogic +118 -0
- package/test/logic/110-scan-emitter-mutation-kills.sqllogic +121 -0
- package/test/logic/110.1-scan-emitter-index-scan-kills.sqllogic +47 -0
- package/test/logic/12-empty-primary-key.sqllogic +124 -0
- package/test/logic/12-join_padding_order.sqllogic +58 -0
- package/test/logic/12.1-orderby-index-consumption.sqllogic +40 -0
- package/test/logic/13-cte.sqllogic +137 -0
- package/test/logic/13.1-cte-multiple-recursive.sqllogic +104 -0
- package/test/logic/13.10-cte-qualified-name.sqllogic +136 -0
- package/test/logic/13.11-unreferenced-dml-cte.sqllogic +414 -0
- package/test/logic/13.12-nested-dml-cte-rejected.sqllogic +280 -0
- package/test/logic/13.2-cte-bind-params.sqllogic +98 -0
- package/test/logic/13.3-cte-edge-cases.sqllogic +120 -0
- package/test/logic/13.4-cte-extras.sqllogic +98 -0
- package/test/logic/13.5-cte-join-order.sqllogic +166 -0
- package/test/logic/13.6-cte-dml-runs-once.sqllogic +318 -0
- package/test/logic/13.7-cte-sibling-visibility.sqllogic +227 -0
- package/test/logic/13.8-insert-with-clause-visibility.sqllogic +254 -0
- package/test/logic/13.9-schema-authored-cte-isolation.sqllogic +328 -0
- package/test/logic/13.9.1-schema-authored-schema-path-isolation.sqllogic +408 -0
- package/test/logic/14-utilities.sqllogic +168 -0
- package/test/logic/15-timespan.sqllogic +279 -0
- package/test/logic/15.1-semantic-ordering.sqllogic +960 -0
- package/test/logic/15.1.1-json-check-coercion.sqllogic +178 -0
- package/test/logic/15.1.2-semantic-ordering-index.sqllogic +41 -0
- package/test/logic/15.1.3-declared-form-write-contract.sqllogic +519 -0
- package/test/logic/16-epoch.sqllogic +187 -0
- package/test/logic/17-weekday-modifier.sqllogic +78 -0
- package/test/logic/18-json-string-escapes.sqllogic +86 -0
- package/test/logic/18-strftime-extended-formats.sqllogic +68 -0
- package/test/logic/20-empty-single-row.sqllogic +261 -0
- package/test/logic/21-null-edge-cases.sqllogic +227 -0
- package/test/logic/21.1-where-null-comparisons.sqllogic +151 -0
- package/test/logic/21.1.1-where-null-comparisons-secondary-index.sqllogic +35 -0
- package/test/logic/21.2-between-edges.sqllogic +157 -0
- package/test/logic/22-boundary-values.sqllogic +175 -0
- package/test/logic/23-self-joins-duplicates.sqllogic +74 -0
- package/test/logic/24-builtin-branches.sqllogic +611 -0
- package/test/logic/24.1-substr-extras.sqllogic +75 -0
- package/test/logic/24.2-random-extras.sqllogic +39 -0
- package/test/logic/25-aggregate-edge-cases.sqllogic +289 -0
- package/test/logic/25.1-nested-aggregates.sqllogic +74 -0
- package/test/logic/25.2-having-edge-cases.sqllogic +318 -0
- package/test/logic/25.3-aggregate-isnull-empty.sqllogic +16 -0
- package/test/logic/25.4-groupby-fd-constant-empty.sqllogic +55 -0
- package/test/logic/25.5-ungrouped-aggregate-column-free-select-item.sqllogic +196 -0
- package/test/logic/26-join-edge-cases.sqllogic +333 -0
- package/test/logic/26.1-left-join-isnull-on-notnull.sqllogic +26 -0
- package/test/logic/26.2-left-join-on-vs-where.sqllogic +67 -0
- package/test/logic/26.3-join-on-constants.sqllogic +61 -0
- package/test/logic/26.4-join-on-is-null-nullable.sqllogic +44 -0
- package/test/logic/27-window-edge-cases.sqllogic +206 -0
- package/test/logic/27.1-window-groups-frame.sqllogic +13 -0
- package/test/logic/27.2-window-exclude.sqllogic +13 -0
- package/test/logic/27.3-window-json-aggregation.sqllogic +66 -0
- package/test/logic/28-set-ops-sort-edge-cases.sqllogic +444 -0
- package/test/logic/28.1-compound-limit-offset.sqllogic +143 -0
- package/test/logic/28.2-orderby-expression-extras.sqllogic +698 -0
- package/test/logic/28.2-set-op-branch-types.sqllogic +217 -0
- package/test/logic/29-constraint-edge-cases.sqllogic +442 -0
- package/test/logic/29.1-column-level-conflict-clause.sqllogic +171 -0
- package/test/logic/29.2-table-level-pk-conflict-clause.sqllogic +113 -0
- package/test/logic/40-constraints.sqllogic +418 -0
- package/test/logic/40.1-pk-desc-direction.sqllogic +76 -0
- package/test/logic/40.2-check-extras.sqllogic +683 -0
- package/test/logic/41-alter-table.sqllogic +191 -0
- package/test/logic/41-fk-cascade-conflict-and-self-ref.sqllogic +263 -0
- package/test/logic/41-fk-cross-schema.sqllogic +155 -0
- package/test/logic/41-fk-extended-targets.sqllogic +173 -0
- package/test/logic/41-foreign-keys.sqllogic +1001 -0
- package/test/logic/41-generated-column-errors.sqllogic +225 -0
- package/test/logic/41-generated-column-extras.sqllogic +598 -0
- package/test/logic/41-generated-column-scope.sqllogic +329 -0
- package/test/logic/41-generated-columns.sqllogic +361 -0
- package/test/logic/41.1-alter-pk.sqllogic +230 -0
- package/test/logic/41.1-fk-collation-conflict.sqllogic +277 -0
- package/test/logic/41.10-alter-drop-column-foreign-key.sqllogic +274 -0
- package/test/logic/41.10.1-alter-drop-column-unique-index.sqllogic +169 -0
- package/test/logic/41.10.2-alter-drop-column-check-and-assertion.sqllogic +1616 -0
- package/test/logic/41.10.3-alter-drop-column-referencing-fk.sqllogic +292 -0
- package/test/logic/41.10.4-drop-object-expression-dependents.sqllogic +602 -0
- package/test/logic/41.11-deferred-fk-with-rename.sqllogic +232 -0
- package/test/logic/41.12-alter-default-coercion.sqllogic +200 -0
- package/test/logic/41.13-alter-add-column-generated-backfill.sqllogic +566 -0
- package/test/logic/41.14-alter-add-column-subquery-backfill.sqllogic +129 -0
- package/test/logic/41.15-alter-column-default-type-check.sqllogic +178 -0
- package/test/logic/41.16-fk-unenforceable.sqllogic +364 -0
- package/test/logic/41.2-alter-column.sqllogic +382 -0
- package/test/logic/41.2.1-alter-column-retype-deleted-row-memory.sqllogic +32 -0
- package/test/logic/41.2.2-alter-column-retype-index-rebuild.sqllogic +77 -0
- package/test/logic/41.2.3-alter-column-set-not-null-pk-backfill.sqllogic +310 -0
- package/test/logic/41.2.3.1-alter-column-set-not-null-pk-backfill-secondary-index.sqllogic +37 -0
- package/test/logic/41.3-alter-add-column-unique.sqllogic +184 -0
- package/test/logic/41.3-alter-rename-propagation.sqllogic +2352 -0
- package/test/logic/41.3.1-alter-rename-index-propagation.sqllogic +94 -0
- package/test/logic/41.4-alter-add-column-constraints.sqllogic +855 -0
- package/test/logic/41.5-alter-misc.sqllogic +48 -0
- package/test/logic/41.5-cross-schema-foreign-keys.sqllogic +253 -0
- package/test/logic/41.6-alter-drop-rename-constraint.sqllogic +354 -0
- package/test/logic/41.6.1-alter-drop-constraint-index-derived.sqllogic +22 -0
- package/test/logic/41.7-alter-column-collate.sqllogic +105 -0
- package/test/logic/41.7.1-alter-column-collate-unique.sqllogic +45 -0
- package/test/logic/41.7.2-alter-column-collate-unique-store.sqllogic +208 -0
- package/test/logic/41.7.2.1-alter-column-collate-unique-index-store.sqllogic +114 -0
- package/test/logic/41.7.3-alter-column-retype-unique.sqllogic +293 -0
- package/test/logic/41.7.3.1-alter-column-retype-staged-rows-memory.sqllogic +41 -0
- package/test/logic/41.7.3.2-alter-column-retype-unique-index.sqllogic +72 -0
- package/test/logic/41.7.4-alter-column-retype-semantic-memory.sqllogic +445 -0
- package/test/logic/41.7.4.1-alter-column-retype-semantic-index-memory.sqllogic +91 -0
- package/test/logic/41.7.5-alter-column-collate-pk-staged-delete-memory.sqllogic +106 -0
- package/test/logic/41.8-alter-add-constraint-unique-fk.sqllogic +275 -0
- package/test/logic/41.8-alter-savepoint-staged-rows.sqllogic +443 -0
- package/test/logic/41.8.1-alter-rename-savepoint-staged-rows.sqllogic +408 -0
- package/test/logic/41.9-alter-constraint-body-change.sqllogic +180 -0
- package/test/logic/41.9-fk-restrict-batched.sqllogic +290 -0
- package/test/logic/42-committed-snapshot.sqllogic +254 -0
- package/test/logic/42-returning.sqllogic +159 -0
- package/test/logic/42.1-returning-extras.sqllogic +232 -0
- package/test/logic/42.2-returning-stored-row.sqllogic +165 -0
- package/test/logic/43-default-nullability.sqllogic +131 -0
- package/test/logic/43-transition-constraints.sqllogic +341 -0
- package/test/logic/43.1-default-collation.sqllogic +140 -0
- package/test/logic/43.1-notnull-or-conflict.sqllogic +79 -0
- package/test/logic/43.2-deferred-check-new-on-delete.sqllogic +95 -0
- package/test/logic/43.3-nullable-primary-key.sqllogic +587 -0
- package/test/logic/44-determinism-validation.sqllogic +164 -0
- package/test/logic/44-orthogonality.sqllogic +146 -0
- package/test/logic/44.1-nondeterministic-schema.sqllogic +316 -0
- package/test/logic/45-udf-determinism.sqllogic +39 -0
- package/test/logic/46-mutation-context.sqllogic +595 -0
- package/test/logic/47-upsert.sqllogic +255 -0
- package/test/logic/47.1-upsert-conflict-targets.sqllogic +169 -0
- package/test/logic/47.2-replace-and-or-clauses.sqllogic +164 -0
- package/test/logic/47.2.1-pk-replace-secondary-unique.sqllogic +374 -0
- package/test/logic/47.3-engine-or-fail.sqllogic +122 -0
- package/test/logic/47.3-upsert-conflict-target-collation.sqllogic +139 -0
- package/test/logic/47.3.1-upsert-conflict-index-derived-collation.sqllogic +25 -0
- package/test/logic/47.4-upsert-conflict-target-affinity.sqllogic +59 -0
- package/test/logic/47.5-upsert-do-update-constraints.sqllogic +440 -0
- package/test/logic/47.6-upsert-do-update-decomposed.sqllogic +100 -0
- package/test/logic/49-reference-graph.sqllogic +84 -0
- package/test/logic/50-declarative-schema.sqllogic +2550 -0
- package/test/logic/50-metadata-tags.sqllogic +1033 -0
- package/test/logic/50.0.1-metadata-tags-index.sqllogic +141 -0
- package/test/logic/50.1-declare-schema-pk.sqllogic +150 -0
- package/test/logic/50.2-declare-schema-renames.sqllogic +949 -0
- package/test/logic/50.2.1-declare-schema-index-renames.sqllogic +173 -0
- package/test/logic/50.3-declare-schema-duplicate-names.sqllogic +280 -0
- package/test/logic/50.4-declare-schema-apply-restore.sqllogic +192 -0
- package/test/logic/51-lens-foundation.sqllogic +147 -0
- package/test/logic/51-materialized-views.sqllogic +376 -0
- package/test/logic/51.5-materialized-views-coarsened-key.sqllogic +198 -0
- package/test/logic/51.7-maintained-table-attach-detach.sqllogic +468 -0
- package/test/logic/51.8-maintained-table-declared-constraints.sqllogic +440 -0
- package/test/logic/51.85-maintained-table-parent-fk.sqllogic +103 -0
- package/test/logic/51.9-maintained-table-secondary-unique.sqllogic +237 -0
- package/test/logic/51.9.1-maintained-table-partial-unique.sqllogic +36 -0
- package/test/logic/52-lens-overrides.sqllogic +566 -0
- package/test/logic/53-materialized-views-rowtime.sqllogic +1601 -0
- package/test/logic/53-reserved-tags.sqllogic +252 -0
- package/test/logic/53.1-materialized-view-write-through.sqllogic +329 -0
- package/test/logic/53.2-materialized-view-rename-propagation.sqllogic +483 -0
- package/test/logic/53.3-materialized-view-constraint-only-ddl.sqllogic +409 -0
- package/test/logic/53.3.1-materialized-view-index-ddl-irrelevance.sqllogic +29 -0
- package/test/logic/53.4-materialized-view-structural-alter-restore.sqllogic +436 -0
- package/test/logic/54-covering-mv-enforcement.sqllogic +314 -0
- package/test/logic/54.1-covering-mv-partial-enforcement.sqllogic +34 -0
- package/test/logic/55-internal-eviction-reporting.sqllogic +89 -0
- package/test/logic/55-lens-prover.sqllogic +202 -0
- package/test/logic/55.1-eviction-restrict-fk.sqllogic +148 -0
- package/test/logic/55.5-lens-authored-inverse.sqllogic +1468 -0
- package/test/logic/80-grow-retrieve-pass.sqllogic +79 -0
- package/test/logic/81-quickpick.sqllogic +24 -0
- package/test/logic/82-bloom-join.sqllogic +185 -0
- package/test/logic/83-merge-join.sqllogic +280 -0
- package/test/logic/84-asof-scan.sqllogic +238 -0
- package/test/logic/84-key-cardinality.sqllogic +50 -0
- package/test/logic/85-relational-const-folding.sqllogic +51 -0
- package/test/logic/86-scalar-cse.sqllogic +42 -0
- package/test/logic/90-error_paths.sqllogic +114 -0
- package/test/logic/90.1-parse-errors.sqllogic +77 -0
- package/test/logic/90.2-alter-table-errors.sqllogic +53 -0
- package/test/logic/90.2.1-alter-extra-errors.sqllogic +51 -0
- package/test/logic/90.3-expression-errors.sqllogic +43 -0
- package/test/logic/90.4-dml-errors.sqllogic +47 -0
- package/test/logic/90.5-unsupported-join-types.sqllogic +37 -0
- package/test/logic/90.5.1-right-full-join-read.sqllogic +157 -0
- package/test/logic/90.6-select-error-paths.sqllogic +57 -0
- package/test/logic/91-merge-join-edge-cases.sqllogic +159 -0
- package/test/logic/92-hash-aggregate-edge-cases.sqllogic +139 -0
- package/test/logic/93-ddl-view-edge-cases.sqllogic +104 -0
- package/test/logic/93.1-view-error-paths.sqllogic +74 -0
- package/test/logic/93.2-view-mutation-pending.sqllogic +108 -0
- package/test/logic/93.3-view-mutation-or-clause.sqllogic +60 -0
- package/test/logic/93.4-view-mutation.sqllogic +4881 -0
- package/test/logic/93.4.1-view-mutation-partial-unique-cardinality.sqllogic +31 -0
- package/test/logic/93.5-authored-inverse.sqllogic +415 -0
- package/test/logic/93.6-set-op-flagless-write.sqllogic +862 -0
- package/test/logic/94-tvf-edge-cases.sqllogic +58 -0
- package/test/logic/94.1-limit-offset-edge-cases.sqllogic +109 -0
- package/test/logic/94.2-limit-offset-extras.sqllogic +98 -0
- package/test/logic/95-assertions.sqllogic +1417 -0
- package/test/logic/96-subquery-edge-cases.sqllogic +276 -0
- package/test/logic/97-json-function-edge-cases.sqllogic +633 -0
- package/test/logic/97.1-json-blob-and-special-values.sqllogic +92 -0
- package/test/logic/98-temporal-edge-cases.sqllogic +515 -0
- package/test/logic/99-conversion-edge-cases.sqllogic +325 -0
- package/test/logic/99.0.1-schema-index-listing.sqllogic +21 -0
- package/test/logic/99.1-cast-syntax-extras.sqllogic +167 -0
- package/test/logic/drop-unique-index.sqllogic +88 -0
- package/test/logic/pushdown-test.sqllogic +36 -0
|
@@ -0,0 +1,1216 @@
|
|
|
1
|
+
-- 10.5.7-implicit-unique-index-lifecycle.sqllogic
|
|
2
|
+
--
|
|
3
|
+
-- requires-capability: standalone-index-ddl
|
|
4
|
+
--
|
|
5
|
+
-- The implicit covering structure a declared UNIQUE constraint builds for
|
|
6
|
+
-- enforcement is named after that constraint (or `_uc_<cols>` when unnamed), but
|
|
7
|
+
-- it is NOT a user-addressable index: its lifecycle belongs to the constraint.
|
|
8
|
+
-- `DROP INDEX <that name>` therefore raises `no such index` (IF EXISTS: no-op)
|
|
9
|
+
-- and `CREATE INDEX <that name>` on the same table is refused as a duplicate.
|
|
10
|
+
-- Removing the structure means dropping the constraint.
|
|
11
|
+
--
|
|
12
|
+
-- Runs on BOTH backends deliberately: memory materializes the structure as a
|
|
13
|
+
-- real index entry, the store does not, and the guard reads `uniqueConstraints`
|
|
14
|
+
-- (present on both) so the observable behavior must match.
|
|
15
|
+
|
|
16
|
+
-- ============================================================
|
|
17
|
+
-- 1. Named UNIQUE constraint — DROP INDEX refused, enforcement intact
|
|
18
|
+
-- ============================================================
|
|
19
|
+
create table iul_a (id integer primary key, email text, constraint uq_email unique (email));
|
|
20
|
+
insert into iul_a values (1, 'a@x');
|
|
21
|
+
-- run
|
|
22
|
+
|
|
23
|
+
drop index uq_email;
|
|
24
|
+
-- error: no such index
|
|
25
|
+
|
|
26
|
+
drop index if exists uq_email;
|
|
27
|
+
-- run
|
|
28
|
+
|
|
29
|
+
-- Enforcement survives both attempts.
|
|
30
|
+
insert into iul_a values (2, 'a@x');
|
|
31
|
+
-- error: unique
|
|
32
|
+
|
|
33
|
+
select count(*) as c from iul_a;
|
|
34
|
+
→ [{"c":1}]
|
|
35
|
+
|
|
36
|
+
-- The constraint is still registered.
|
|
37
|
+
select count(*) as c from unique_constraint_info('iul_a') where name = 'uq_email';
|
|
38
|
+
→ [{"c":1}]
|
|
39
|
+
|
|
40
|
+
-- ============================================================
|
|
41
|
+
-- 2. Named UNIQUE constraint — CREATE INDEX of the same name on the same table
|
|
42
|
+
-- ============================================================
|
|
43
|
+
create index uq_email on iul_a (email);
|
|
44
|
+
-- error: already exists on table iul_a
|
|
45
|
+
|
|
46
|
+
-- IF NOT EXISTS skips silently rather than erroring...
|
|
47
|
+
create index if not exists uq_email on iul_a (email);
|
|
48
|
+
-- run
|
|
49
|
+
|
|
50
|
+
-- ...and adds nothing: the name is still only the constraint's backing structure.
|
|
51
|
+
create index uq_email on iul_a (email);
|
|
52
|
+
-- error: already exists on table iul_a
|
|
53
|
+
|
|
54
|
+
-- ============================================================
|
|
55
|
+
-- 3. Unnamed UNIQUE constraint — same rules under the auto-name `_uc_<cols>`
|
|
56
|
+
-- ============================================================
|
|
57
|
+
create table iul_u (id integer primary key, email text, unique (email));
|
|
58
|
+
insert into iul_u values (1, 'u@x');
|
|
59
|
+
-- run
|
|
60
|
+
|
|
61
|
+
drop index _uc_email;
|
|
62
|
+
-- error: no such index
|
|
63
|
+
|
|
64
|
+
drop index if exists _uc_email;
|
|
65
|
+
-- run
|
|
66
|
+
|
|
67
|
+
create index _uc_email on iul_u (email);
|
|
68
|
+
-- error: already exists on table iul_u
|
|
69
|
+
|
|
70
|
+
insert into iul_u values (2, 'u@x');
|
|
71
|
+
-- error: unique
|
|
72
|
+
|
|
73
|
+
-- ============================================================
|
|
74
|
+
-- 4. EXPOSED implicit structure — addressable for TAGS, still not for lifecycle
|
|
75
|
+
-- ============================================================
|
|
76
|
+
create table iul_e (id integer primary key, vin text,
|
|
77
|
+
constraint uq_vin unique (vin) with tags ("quereus.expose_implicit_index" = true));
|
|
78
|
+
insert into iul_e values (1, 'VIN1');
|
|
79
|
+
-- run
|
|
80
|
+
|
|
81
|
+
-- Exposure makes it visible and tag-addressable...
|
|
82
|
+
alter index uq_vin add tags (purpose = 'lookup');
|
|
83
|
+
select json_extract(tags, '$.purpose') as purpose from schema() where type = 'index' and name = 'uq_vin';
|
|
84
|
+
→ [{"purpose":"lookup"}]
|
|
85
|
+
|
|
86
|
+
-- ...but its lifecycle still belongs to the constraint.
|
|
87
|
+
drop index uq_vin;
|
|
88
|
+
-- error: no such index
|
|
89
|
+
|
|
90
|
+
drop index if exists uq_vin;
|
|
91
|
+
-- run
|
|
92
|
+
|
|
93
|
+
create index uq_vin on iul_e (vin);
|
|
94
|
+
-- error: already exists on table iul_e
|
|
95
|
+
|
|
96
|
+
insert into iul_e values (2, 'VIN1');
|
|
97
|
+
-- error: unique
|
|
98
|
+
|
|
99
|
+
-- Still exposed after the refused drops.
|
|
100
|
+
select count(*) as c from schema() where type = 'index' and name = 'uq_vin';
|
|
101
|
+
→ [{"c":1}]
|
|
102
|
+
|
|
103
|
+
-- It describes itself as UNIQUE, on every backend and on every read surface.
|
|
104
|
+
-- Rendered DDL exists to re-parse into the object it describes, and a plain
|
|
105
|
+
-- `CREATE INDEX` does not re-parse into something that enforces uniqueness.
|
|
106
|
+
-- Memory materializes the structure without a `unique` flag and the store
|
|
107
|
+
-- synthesizes it from the constraint, so both routes go through one enumeration
|
|
108
|
+
-- (`displayIndexesForTable`, which renders via `indexSchemaForDisplay`) rather
|
|
109
|
+
-- than each read surface deciding for itself.
|
|
110
|
+
-- Uniqueness *enforcement* stays the constraint's either way.
|
|
111
|
+
select sql like 'CREATE UNIQUE INDEX%' as is_unique_ddl from schema() where type = 'index' and name = 'uq_vin';
|
|
112
|
+
→ [{"is_unique_ddl":true}]
|
|
113
|
+
|
|
114
|
+
select "unique" as u from index_info('iul_e') where index_name = 'uq_vin';
|
|
115
|
+
→ [{"u":1}]
|
|
116
|
+
|
|
117
|
+
-- Negative pin: the helper must not over-apply. A PLAIN (unexposed) UNIQUE
|
|
118
|
+
-- constraint still surfaces no index row at all...
|
|
119
|
+
create table iul_p (id integer primary key, sku text, constraint uq_sku unique (sku));
|
|
120
|
+
-- run
|
|
121
|
+
|
|
122
|
+
select count(*) as c from schema() where type = 'index' and name = 'uq_sku';
|
|
123
|
+
→ [{"c":0}]
|
|
124
|
+
|
|
125
|
+
select count(*) as c from index_info('iul_p');
|
|
126
|
+
→ [{"c":0}]
|
|
127
|
+
|
|
128
|
+
-- ...and an ordinary non-unique index still renders without UNIQUE and reports 0.
|
|
129
|
+
create table iul_o (id integer primary key, label text);
|
|
130
|
+
create index ix_label on iul_o (label);
|
|
131
|
+
-- run
|
|
132
|
+
|
|
133
|
+
select sql like 'CREATE INDEX%' as is_plain_ddl from schema() where type = 'index' and name = 'ix_label';
|
|
134
|
+
→ [{"is_plain_ddl":true}]
|
|
135
|
+
|
|
136
|
+
select "unique" as u from index_info('iul_o') where index_name = 'ix_label';
|
|
137
|
+
→ [{"u":0}]
|
|
138
|
+
|
|
139
|
+
-- Shapes the named single-column pin above does not reach. Each is exposed, so
|
|
140
|
+
-- each must describe itself as UNIQUE on both backends.
|
|
141
|
+
-- (a) An UNNAMED constraint: the structure takes the auto-name `_uc_<cols>`.
|
|
142
|
+
create table iul_n (id integer primary key, code text,
|
|
143
|
+
unique (code) with tags ("quereus.expose_implicit_index" = true));
|
|
144
|
+
-- run
|
|
145
|
+
|
|
146
|
+
select sql like 'CREATE UNIQUE INDEX%' as is_unique_ddl from schema() where type = 'index' and name = '_uc_code';
|
|
147
|
+
→ [{"is_unique_ddl":true}]
|
|
148
|
+
|
|
149
|
+
select "unique" as u from index_info('iul_n') where index_name = '_uc_code';
|
|
150
|
+
→ [{"u":1}]
|
|
151
|
+
|
|
152
|
+
-- (b) A MULTI-COLUMN constraint under a MIXED-CASE name. The exposure map is
|
|
153
|
+
-- lowercase-keyed, so an unfolded lookup would render this one without UNIQUE;
|
|
154
|
+
-- and every column of the structure must carry the same answer.
|
|
155
|
+
create table iul_m (id integer primary key, a integer not null, b integer not null,
|
|
156
|
+
constraint UQ_AB unique (a, b) with tags ("quereus.expose_implicit_index" = true));
|
|
157
|
+
-- run
|
|
158
|
+
|
|
159
|
+
select sql like 'CREATE UNIQUE INDEX%' as is_unique_ddl from schema() where type = 'index' and name = 'UQ_AB';
|
|
160
|
+
→ [{"is_unique_ddl":true}]
|
|
161
|
+
|
|
162
|
+
select count(*) as c from index_info('iul_m') where index_name = 'UQ_AB' and "unique" = 1;
|
|
163
|
+
→ [{"c":2}]
|
|
164
|
+
|
|
165
|
+
-- ============================================================
|
|
166
|
+
-- 5. Mixed-case constraint name — the guards fold case like every index compare
|
|
167
|
+
-- ============================================================
|
|
168
|
+
create table iul_z (id integer primary key, email text, constraint UQ_Email unique (email));
|
|
169
|
+
insert into iul_z values (1, 'z@x');
|
|
170
|
+
-- run
|
|
171
|
+
|
|
172
|
+
drop index UQ_Email;
|
|
173
|
+
-- error: no such index
|
|
174
|
+
|
|
175
|
+
drop index uq_email;
|
|
176
|
+
-- error: no such index
|
|
177
|
+
|
|
178
|
+
create index uq_email on iul_z (email);
|
|
179
|
+
-- error: already exists on table iul_z
|
|
180
|
+
|
|
181
|
+
insert into iul_z values (2, 'z@x');
|
|
182
|
+
-- error: unique
|
|
183
|
+
|
|
184
|
+
-- ============================================================
|
|
185
|
+
-- 6. An implicit name on ANOTHER table is a real, droppable index
|
|
186
|
+
-- ============================================================
|
|
187
|
+
-- Constraint names are unique per TABLE, so `uq_shared` may be a constraint on
|
|
188
|
+
-- iul_b and an ordinary index on iul_c at the same time. The owner scan must
|
|
189
|
+
-- skip past iul_b's implicit match and keep going, not stop at it.
|
|
190
|
+
create table iul_b (id integer primary key, email text, constraint uq_shared unique (email));
|
|
191
|
+
create table iul_c (id integer primary key, email text);
|
|
192
|
+
create index uq_shared on iul_c (email);
|
|
193
|
+
insert into iul_b values (1, 'b@x');
|
|
194
|
+
insert into iul_c values (1, 'c@x'), (2, 'c2@x');
|
|
195
|
+
-- run
|
|
196
|
+
|
|
197
|
+
select id from iul_c where email = 'c2@x';
|
|
198
|
+
→ [{"id":2}]
|
|
199
|
+
|
|
200
|
+
-- Resolves to iul_c's real index, not iul_b's backing structure.
|
|
201
|
+
drop index uq_shared;
|
|
202
|
+
-- run
|
|
203
|
+
|
|
204
|
+
-- iul_b's constraint is untouched and still enforcing.
|
|
205
|
+
select count(*) as c from unique_constraint_info('iul_b') where name = 'uq_shared';
|
|
206
|
+
→ [{"c":1}]
|
|
207
|
+
|
|
208
|
+
insert into iul_b values (2, 'b@x');
|
|
209
|
+
-- error: unique
|
|
210
|
+
|
|
211
|
+
-- iul_c's rows survive the index drop and are still readable by the column.
|
|
212
|
+
select id from iul_c where email = 'c2@x';
|
|
213
|
+
→ [{"id":2}]
|
|
214
|
+
|
|
215
|
+
-- The name is free again on iul_c.
|
|
216
|
+
create index uq_shared on iul_c (email);
|
|
217
|
+
-- run
|
|
218
|
+
|
|
219
|
+
drop index uq_shared;
|
|
220
|
+
-- run
|
|
221
|
+
|
|
222
|
+
-- ============================================================
|
|
223
|
+
-- 6a. `schema()` / `index_info()` hide the backing structure on BOTH backends
|
|
224
|
+
-- ============================================================
|
|
225
|
+
-- Named constraint (iul_a's uq_email): absent from schema()...
|
|
226
|
+
select count(*) as c from schema() where type = 'index' and name = 'uq_email' and tbl_name = 'iul_a';
|
|
227
|
+
→ [{"c":0}]
|
|
228
|
+
|
|
229
|
+
-- ...and from index_info().
|
|
230
|
+
select count(*) as c from index_info('iul_a') where index_name = 'uq_email';
|
|
231
|
+
→ [{"c":0}]
|
|
232
|
+
|
|
233
|
+
-- Unnamed constraint's auto-name (iul_u's _uc_email): same.
|
|
234
|
+
select count(*) as c from schema() where type = 'index' and name = '_uc_email' and tbl_name = 'iul_u';
|
|
235
|
+
→ [{"c":0}]
|
|
236
|
+
|
|
237
|
+
select count(*) as c from index_info('iul_u') where index_name = '_uc_email';
|
|
238
|
+
→ [{"c":0}]
|
|
239
|
+
|
|
240
|
+
-- Mixed-case constraint name (iul_z's UQ_Email): the predicate folds case.
|
|
241
|
+
select count(*) as c from schema() where type = 'index' and lower(name) = 'uq_email' and tbl_name = 'iul_z';
|
|
242
|
+
→ [{"c":0}]
|
|
243
|
+
|
|
244
|
+
select count(*) as c from index_info('iul_z') where lower(index_name) = 'uq_email';
|
|
245
|
+
→ [{"c":0}]
|
|
246
|
+
|
|
247
|
+
-- The EXPOSED implicit structure (iul_e's uq_vin) keeps showing up, with tags,
|
|
248
|
+
-- via index_info() too — it already does via schema() per section 4 above.
|
|
249
|
+
select count(*) as c from index_info('iul_e') where index_name = 'uq_vin';
|
|
250
|
+
→ [{"c":1}]
|
|
251
|
+
|
|
252
|
+
select json_extract(tags, '$.purpose') as purpose from index_info('iul_e') where index_name = 'uq_vin';
|
|
253
|
+
→ [{"purpose":"lookup"}]
|
|
254
|
+
|
|
255
|
+
-- `create unique index` synthesizes a constraint for enforcement, but the index
|
|
256
|
+
-- is the user's own object (not an implicit covering structure) and must keep
|
|
257
|
+
-- showing up on both introspection surfaces.
|
|
258
|
+
create table iul_d (id integer primary key, email text);
|
|
259
|
+
insert into iul_d values (1, 'd@x');
|
|
260
|
+
create unique index uq_d_email on iul_d (email);
|
|
261
|
+
-- run
|
|
262
|
+
|
|
263
|
+
select count(*) as c from schema() where type = 'index' and name = 'uq_d_email';
|
|
264
|
+
→ [{"c":1}]
|
|
265
|
+
|
|
266
|
+
select count(*) as c from index_info('iul_d') where index_name = 'uq_d_email';
|
|
267
|
+
→ [{"c":1}]
|
|
268
|
+
|
|
269
|
+
drop table iul_d;
|
|
270
|
+
-- run
|
|
271
|
+
|
|
272
|
+
-- ============================================================
|
|
273
|
+
-- 7. DROP CONSTRAINT is the way to remove the structure
|
|
274
|
+
-- ============================================================
|
|
275
|
+
alter table iul_a drop constraint uq_email;
|
|
276
|
+
-- run
|
|
277
|
+
|
|
278
|
+
select count(*) as c from unique_constraint_info('iul_a') where name = 'uq_email';
|
|
279
|
+
→ [{"c":0}]
|
|
280
|
+
|
|
281
|
+
-- Duplicate accepted once the constraint (and its backing structure) is gone.
|
|
282
|
+
insert into iul_a values (2, 'a@x');
|
|
283
|
+
select count(*) as c from iul_a;
|
|
284
|
+
→ [{"c":2}]
|
|
285
|
+
|
|
286
|
+
-- ...and the name is now an ordinary one on that table.
|
|
287
|
+
create index uq_email on iul_a (email);
|
|
288
|
+
-- run
|
|
289
|
+
|
|
290
|
+
drop index uq_email;
|
|
291
|
+
-- run
|
|
292
|
+
|
|
293
|
+
-- ============================================================
|
|
294
|
+
-- 8. The strict-DDL-policy gate skips implicit structures the same way
|
|
295
|
+
-- ============================================================
|
|
296
|
+
-- `emitDropIndex` resolves the owning table before the `ddl_transaction_policy`
|
|
297
|
+
-- gate fires, so its scan must apply the same skip. A name held ONLY by a
|
|
298
|
+
-- constraint's structure has no droppable owner, so the statement fails with
|
|
299
|
+
-- `no such index` and never reaches the gate; a real index of that name on
|
|
300
|
+
-- another table IS found past the skip, so the gate does fire for it.
|
|
301
|
+
|
|
302
|
+
pragma ddl_transaction_policy = 'strict';
|
|
303
|
+
-- run
|
|
304
|
+
|
|
305
|
+
-- Allowed in autocommit; iul_b still carries the `uq_shared` constraint.
|
|
306
|
+
create index uq_shared on iul_c (email);
|
|
307
|
+
-- run
|
|
308
|
+
|
|
309
|
+
begin;
|
|
310
|
+
-- run
|
|
311
|
+
drop index uq_shared;
|
|
312
|
+
-- error: ddl_transaction_policy = strict
|
|
313
|
+
rollback;
|
|
314
|
+
-- run
|
|
315
|
+
|
|
316
|
+
begin;
|
|
317
|
+
-- run
|
|
318
|
+
drop index UQ_Email;
|
|
319
|
+
-- error: no such index
|
|
320
|
+
rollback;
|
|
321
|
+
-- run
|
|
322
|
+
|
|
323
|
+
pragma ddl_transaction_policy = 'permissive';
|
|
324
|
+
-- run
|
|
325
|
+
|
|
326
|
+
-- The gate refused rather than dropped: the index is still there.
|
|
327
|
+
drop index uq_shared;
|
|
328
|
+
-- run
|
|
329
|
+
|
|
330
|
+
drop table iul_c;
|
|
331
|
+
drop table iul_b;
|
|
332
|
+
drop table iul_z;
|
|
333
|
+
drop table iul_e;
|
|
334
|
+
drop table iul_u;
|
|
335
|
+
drop table iul_a;
|
|
336
|
+
-- run
|
|
337
|
+
|
|
338
|
+
-- ============================================================
|
|
339
|
+
-- 9. The same rule from the CONSTRAINT side — declaring second is refused too
|
|
340
|
+
-- ============================================================
|
|
341
|
+
-- Sections 2/3/5 cover `CREATE INDEX` onto a name a UNIQUE constraint already
|
|
342
|
+
-- holds. The mirror image — declaring a UNIQUE constraint onto a name an index on
|
|
343
|
+
-- the SAME table already holds — is refused identically, on every authoring path.
|
|
344
|
+
-- Left unguarded it silently reclassifies the user's index as a hidden backing
|
|
345
|
+
-- structure: it disappears from schema() / index_info(), stops being droppable,
|
|
346
|
+
-- and (store-backed) its CREATE INDEX line is dropped from the persisted catalog.
|
|
347
|
+
|
|
348
|
+
create table iul_ac (id integer primary key, a text, b text);
|
|
349
|
+
create index cs_named on iul_ac (b);
|
|
350
|
+
insert into iul_ac values (1, 'x', 'p'), (2, 'y', 'q');
|
|
351
|
+
-- run
|
|
352
|
+
|
|
353
|
+
-- 9a. ALTER TABLE ADD CONSTRAINT
|
|
354
|
+
alter table iul_ac add constraint cs_named unique (a);
|
|
355
|
+
-- error: would collide with existing index 'cs_named'
|
|
356
|
+
|
|
357
|
+
-- The index is untouched by the refusal: still visible, still resolving rows.
|
|
358
|
+
select count(*) as c from schema() where type = 'index' and name = 'cs_named';
|
|
359
|
+
→ [{"c":1}]
|
|
360
|
+
|
|
361
|
+
select id from iul_ac where b = 'q';
|
|
362
|
+
→ [{"id":2}]
|
|
363
|
+
|
|
364
|
+
-- No duplicate index entry was left behind either.
|
|
365
|
+
select count(*) as c from index_info('iul_ac') where index_name = 'cs_named';
|
|
366
|
+
→ [{"c":1}]
|
|
367
|
+
|
|
368
|
+
-- Case folds, like every other index-name compare.
|
|
369
|
+
alter table iul_ac add constraint CS_Named unique (a);
|
|
370
|
+
-- error: would collide with existing index 'cs_named'
|
|
371
|
+
|
|
372
|
+
-- Rejected even when the index is over the SAME column, where the two structures
|
|
373
|
+
-- would physically coincide: accepting it would silently hide the user's index.
|
|
374
|
+
create index cs_same on iul_ac (a);
|
|
375
|
+
-- run
|
|
376
|
+
|
|
377
|
+
alter table iul_ac add constraint cs_same unique (a);
|
|
378
|
+
-- error: would collide with existing index 'cs_same'
|
|
379
|
+
|
|
380
|
+
-- A DIFFERENTLY-named constraint over an already-indexed column stays legal —
|
|
381
|
+
-- reuse matches on columns, not on names.
|
|
382
|
+
alter table iul_ac add constraint cs_other unique (a);
|
|
383
|
+
-- run
|
|
384
|
+
|
|
385
|
+
insert into iul_ac values (3, 'x', 'r');
|
|
386
|
+
-- error: unique
|
|
387
|
+
|
|
388
|
+
-- 9b. Unnamed UNIQUE — the auto-name `_uc_<cols>` is guarded the same way
|
|
389
|
+
create table iul_au (id integer primary key, a text);
|
|
390
|
+
create index _uc_a on iul_au (a);
|
|
391
|
+
-- run
|
|
392
|
+
|
|
393
|
+
alter table iul_au add unique (a);
|
|
394
|
+
-- error: would collide with existing index '_uc_a'
|
|
395
|
+
|
|
396
|
+
select count(*) as c from schema() where type = 'index' and name = '_uc_a';
|
|
397
|
+
→ [{"c":1}]
|
|
398
|
+
|
|
399
|
+
-- Multi-column: the auto-name joins the declared column names with `_`.
|
|
400
|
+
create table iul_am (id integer primary key, a text, b text, c text);
|
|
401
|
+
create index _uc_a_b on iul_am (c);
|
|
402
|
+
-- run
|
|
403
|
+
|
|
404
|
+
alter table iul_am add unique (a, b);
|
|
405
|
+
-- error: would collide with existing index '_uc_a_b'
|
|
406
|
+
|
|
407
|
+
select count(*) as c from schema() where type = 'index' and name = '_uc_a_b';
|
|
408
|
+
→ [{"c":1}]
|
|
409
|
+
|
|
410
|
+
-- A free multi-column auto-name still installs and enforces.
|
|
411
|
+
alter table iul_am add unique (b, c);
|
|
412
|
+
insert into iul_am values (1, 'p', 'q', 'r');
|
|
413
|
+
-- run
|
|
414
|
+
|
|
415
|
+
insert into iul_am values (2, 'z', 'q', 'r');
|
|
416
|
+
-- error: unique
|
|
417
|
+
|
|
418
|
+
-- 9c. ALTER TABLE RENAME CONSTRAINT
|
|
419
|
+
create table iul_rc (id integer primary key, a text, b text);
|
|
420
|
+
create index rc_taken on iul_rc (b);
|
|
421
|
+
alter table iul_rc add constraint rc_uq unique (a);
|
|
422
|
+
-- run
|
|
423
|
+
|
|
424
|
+
alter table iul_rc rename constraint rc_uq to rc_taken;
|
|
425
|
+
-- error: would collide with existing index 'rc_taken'
|
|
426
|
+
|
|
427
|
+
-- Neither object moved: the constraint keeps its name and the index keeps its own.
|
|
428
|
+
select count(*) as c from unique_constraint_info('iul_rc') where name = 'rc_uq';
|
|
429
|
+
→ [{"c":1}]
|
|
430
|
+
|
|
431
|
+
select count(*) as c from schema() where type = 'index' and name = 'rc_taken';
|
|
432
|
+
→ [{"c":1}]
|
|
433
|
+
|
|
434
|
+
-- A free name still renames, and a case-only rename is still a no-op (it must not
|
|
435
|
+
-- trip over the constraint's OWN backing structure).
|
|
436
|
+
alter table iul_rc rename constraint rc_uq to rc_moved;
|
|
437
|
+
-- run
|
|
438
|
+
|
|
439
|
+
alter table iul_rc rename constraint rc_moved to RC_Moved;
|
|
440
|
+
-- run
|
|
441
|
+
|
|
442
|
+
select count(*) as c from unique_constraint_info('iul_rc') where lower(name) = 'rc_moved';
|
|
443
|
+
→ [{"c":1}]
|
|
444
|
+
|
|
445
|
+
-- 9d. ALTER TABLE ADD COLUMN … unique
|
|
446
|
+
create table iul_cc (id integer primary key, a text);
|
|
447
|
+
create index _uc_c on iul_cc (a);
|
|
448
|
+
create index cc_named on iul_cc (a);
|
|
449
|
+
-- run
|
|
450
|
+
|
|
451
|
+
-- Unnamed inline unique: auto-name comes from the not-yet-existing column's name.
|
|
452
|
+
alter table iul_cc add column c text unique;
|
|
453
|
+
-- error: would collide with existing index '_uc_c'
|
|
454
|
+
|
|
455
|
+
-- Named inline unique.
|
|
456
|
+
alter table iul_cc add column d text constraint cc_named unique;
|
|
457
|
+
-- error: would collide with existing index 'cc_named'
|
|
458
|
+
|
|
459
|
+
-- The refusals left the table exactly as it was — neither column was added.
|
|
460
|
+
select count(*) as c from table_info('iul_cc') where name in ('c', 'd');
|
|
461
|
+
→ [{"c":0}]
|
|
462
|
+
|
|
463
|
+
-- A free name adds the column and its constraint normally.
|
|
464
|
+
alter table iul_cc add column e text unique;
|
|
465
|
+
insert into iul_cc values (1, 'p', 'q');
|
|
466
|
+
-- run
|
|
467
|
+
|
|
468
|
+
insert into iul_cc values (2, 'p2', 'q');
|
|
469
|
+
-- error: unique
|
|
470
|
+
|
|
471
|
+
-- A column declaring several inline `unique` constraints is checked per constraint,
|
|
472
|
+
-- so the colliding one is refused whichever position it holds.
|
|
473
|
+
create table iul_cm (id integer primary key, a text);
|
|
474
|
+
create index _uc_m on iul_cm (a);
|
|
475
|
+
-- run
|
|
476
|
+
|
|
477
|
+
alter table iul_cm add column m text constraint cm_free unique unique;
|
|
478
|
+
-- error: would collide with existing index '_uc_m'
|
|
479
|
+
|
|
480
|
+
select count(*) as c from table_info('iul_cm') where name = 'm';
|
|
481
|
+
→ [{"c":0}]
|
|
482
|
+
|
|
483
|
+
-- 9e. `create unique index` still synthesizes its constraint beside its index
|
|
484
|
+
-- (`derivedFromIndex`), which the guard must not mistake for a collision.
|
|
485
|
+
create table iul_ui (id integer primary key, a text);
|
|
486
|
+
create unique index ui_a on iul_ui (a);
|
|
487
|
+
insert into iul_ui values (1, 'k');
|
|
488
|
+
-- run
|
|
489
|
+
|
|
490
|
+
select count(*) as c from schema() where type = 'index' and name = 'ui_a';
|
|
491
|
+
→ [{"c":1}]
|
|
492
|
+
|
|
493
|
+
insert into iul_ui values (2, 'k');
|
|
494
|
+
-- error: unique
|
|
495
|
+
|
|
496
|
+
-- 9f. ALTER TABLE RENAME COLUMN — the `_uc_<cols>` name MOVES with the column
|
|
497
|
+
-- The auto-name is derived from the covered columns' CURRENT names and recorded
|
|
498
|
+
-- nowhere, so renaming a covered column renames the backing structure. Two halves:
|
|
499
|
+
-- the structure must follow (or it is orphaned under the old name — surfacing as a
|
|
500
|
+
-- user index, droppable, and no longer recognized as the constraint's), and the
|
|
501
|
+
-- statement must be refused when the post-rename name is already an index here.
|
|
502
|
+
-- Column names are file-unique so a `drop index` below cannot resolve to some other
|
|
503
|
+
-- table's index of the same name (see section 6).
|
|
504
|
+
create table iul_rk (id integer primary key, rka text, rkb text, unique (rka));
|
|
505
|
+
insert into iul_rk values (1, 'x', 'p'), (2, 'y', 'q');
|
|
506
|
+
-- run
|
|
507
|
+
|
|
508
|
+
alter table iul_rk rename column rka to rkz;
|
|
509
|
+
-- run
|
|
510
|
+
|
|
511
|
+
-- The structure followed the column: nothing surfaces under either name.
|
|
512
|
+
select count(*) as c from schema() where type = 'index' and tbl_name = 'iul_rk';
|
|
513
|
+
→ [{"c":0}]
|
|
514
|
+
|
|
515
|
+
select count(*) as c from index_info('iul_rk');
|
|
516
|
+
→ [{"c":0}]
|
|
517
|
+
|
|
518
|
+
drop index _uc_rka;
|
|
519
|
+
-- error: no such index
|
|
520
|
+
|
|
521
|
+
drop index _uc_rkz;
|
|
522
|
+
-- error: no such index
|
|
523
|
+
|
|
524
|
+
-- ...and the constraint still enforces, now over the new column name.
|
|
525
|
+
insert into iul_rk values (3, 'x', 'r');
|
|
526
|
+
-- error: unique
|
|
527
|
+
|
|
528
|
+
-- Multi-column: the auto-name joins the NEW column names.
|
|
529
|
+
create table iul_rm (id integer primary key, rma text, rmb text, unique (rma, rmb));
|
|
530
|
+
insert into iul_rm values (1, 'x', 'p');
|
|
531
|
+
-- run
|
|
532
|
+
|
|
533
|
+
alter table iul_rm rename column rma to rmz;
|
|
534
|
+
-- run
|
|
535
|
+
|
|
536
|
+
select count(*) as c from index_info('iul_rm');
|
|
537
|
+
→ [{"c":0}]
|
|
538
|
+
|
|
539
|
+
drop index _uc_rmz_rmb;
|
|
540
|
+
-- error: no such index
|
|
541
|
+
|
|
542
|
+
insert into iul_rm values (2, 'x', 'p');
|
|
543
|
+
-- error: unique
|
|
544
|
+
|
|
545
|
+
-- Collision: the post-rename name is already an index on this table.
|
|
546
|
+
create table iul_rx (id integer primary key, rxa text, rxb text, unique (rxa));
|
|
547
|
+
create index _uc_rxz on iul_rx (rxb);
|
|
548
|
+
insert into iul_rx values (1, 'x', 'p'), (2, 'y', 'q');
|
|
549
|
+
-- run
|
|
550
|
+
|
|
551
|
+
alter table iul_rx rename column rxa to rxz;
|
|
552
|
+
-- error: would collide with existing index '_uc_rxz'
|
|
553
|
+
|
|
554
|
+
-- Nothing moved: the column keeps its old name...
|
|
555
|
+
select count(*) as c from table_info('iul_rx') where name = 'rxa';
|
|
556
|
+
→ [{"c":1}]
|
|
557
|
+
|
|
558
|
+
select count(*) as c from table_info('iul_rx') where name = 'rxz';
|
|
559
|
+
→ [{"c":0}]
|
|
560
|
+
|
|
561
|
+
-- ...the user's index is still listed and still resolves rows...
|
|
562
|
+
select count(*) as c from schema() where type = 'index' and name = '_uc_rxz';
|
|
563
|
+
→ [{"c":1}]
|
|
564
|
+
|
|
565
|
+
select id from iul_rx where rxb = 'q';
|
|
566
|
+
→ [{"id":2}]
|
|
567
|
+
|
|
568
|
+
-- ...the constraint still enforces over the old column name...
|
|
569
|
+
insert into iul_rx values (3, 'x', 'r');
|
|
570
|
+
-- error: unique
|
|
571
|
+
|
|
572
|
+
-- ...and the index is still the user's to drop.
|
|
573
|
+
drop index _uc_rxz;
|
|
574
|
+
-- run
|
|
575
|
+
|
|
576
|
+
-- A case-only rename derives a case-equal name, which must not be read as a
|
|
577
|
+
-- collision with the constraint's OWN structure.
|
|
578
|
+
create table iul_rf (id integer primary key, rfa text, unique (rfa));
|
|
579
|
+
insert into iul_rf values (1, 'x');
|
|
580
|
+
-- run
|
|
581
|
+
|
|
582
|
+
alter table iul_rf rename column rfa to RFA;
|
|
583
|
+
-- run
|
|
584
|
+
|
|
585
|
+
select count(*) as c from index_info('iul_rf');
|
|
586
|
+
→ [{"c":0}]
|
|
587
|
+
|
|
588
|
+
insert into iul_rf values (2, 'x');
|
|
589
|
+
-- error: unique
|
|
590
|
+
|
|
591
|
+
-- An EXPOSED implicit structure follows the rename too — it stays visible, under the
|
|
592
|
+
-- NEW derived name (exposure changes visibility, not who owns the name).
|
|
593
|
+
create table iul_rp (id integer primary key, rpa text,
|
|
594
|
+
unique (rpa) with tags ("quereus.expose_implicit_index" = true));
|
|
595
|
+
insert into iul_rp values (1, 'x');
|
|
596
|
+
-- run
|
|
597
|
+
|
|
598
|
+
alter table iul_rp rename column rpa to rpz;
|
|
599
|
+
-- run
|
|
600
|
+
|
|
601
|
+
select index_name from index_info('iul_rp');
|
|
602
|
+
→ [{"index_name":"_uc_rpz"}]
|
|
603
|
+
|
|
604
|
+
insert into iul_rp values (2, 'x');
|
|
605
|
+
-- error: unique
|
|
606
|
+
|
|
607
|
+
-- A NAMED UNIQUE's structure takes the CONSTRAINT's name, which no column rename
|
|
608
|
+
-- moves — so neither arm applies to it.
|
|
609
|
+
create table iul_rn (id integer primary key, rna text, rnb text, constraint rn_uq unique (rna));
|
|
610
|
+
create index _uc_rnz on iul_rn (rnb);
|
|
611
|
+
insert into iul_rn values (1, 'x', 'p');
|
|
612
|
+
-- run
|
|
613
|
+
|
|
614
|
+
alter table iul_rn rename column rna to rnz;
|
|
615
|
+
-- run
|
|
616
|
+
|
|
617
|
+
-- The index the unnamed case would have collided with is untouched...
|
|
618
|
+
select count(*) as c from schema() where type = 'index' and name = '_uc_rnz' and tbl_name = 'iul_rn';
|
|
619
|
+
→ [{"c":1}]
|
|
620
|
+
|
|
621
|
+
-- ...and the constraint's own structure is still hidden and still enforcing.
|
|
622
|
+
select count(*) as c from index_info('iul_rn') where index_name = 'rn_uq';
|
|
623
|
+
→ [{"c":0}]
|
|
624
|
+
|
|
625
|
+
insert into iul_rn values (2, 'x', 'q');
|
|
626
|
+
-- error: unique
|
|
627
|
+
|
|
628
|
+
-- The re-derived name is also the key DROP COLUMN tears the structure down by, so a
|
|
629
|
+
-- rename followed by dropping a covered column must leave no orphan behind. Multi-column
|
|
630
|
+
-- on purpose: a single-column structure collapses to zero key columns and is dropped by
|
|
631
|
+
-- the shift helper regardless, so only this shape proves the key was re-derived.
|
|
632
|
+
create table iul_rd (id integer primary key, rda text, rdb text, unique (rda, rdb));
|
|
633
|
+
insert into iul_rd values (1, 'x', 'p');
|
|
634
|
+
-- run
|
|
635
|
+
|
|
636
|
+
alter table iul_rd rename column rda to rdz;
|
|
637
|
+
-- run
|
|
638
|
+
|
|
639
|
+
alter table iul_rd drop column rdz;
|
|
640
|
+
-- run
|
|
641
|
+
|
|
642
|
+
select count(*) as c from index_info('iul_rd');
|
|
643
|
+
→ [{"c":0}]
|
|
644
|
+
|
|
645
|
+
select count(*) as c from schema() where type = 'index' and tbl_name = 'iul_rd';
|
|
646
|
+
→ [{"c":0}]
|
|
647
|
+
|
|
648
|
+
-- The constraint covered the dropped column, so it went with it — the survivor is free.
|
|
649
|
+
insert into iul_rd values (2, 'p');
|
|
650
|
+
-- run
|
|
651
|
+
|
|
652
|
+
select count(*) as c from iul_rd;
|
|
653
|
+
→ [{"c":2}]
|
|
654
|
+
|
|
655
|
+
-- Renaming INSIDE an open transaction that has already staged a row: the structure moves
|
|
656
|
+
-- to its new name under the transaction's own layer too, so the staged row stays visible
|
|
657
|
+
-- to it, enforcement never lapses, and the commit does not drop the staged write.
|
|
658
|
+
create table iul_rs (id integer primary key, rsa text, unique (rsa));
|
|
659
|
+
insert into iul_rs values (1, 'x');
|
|
660
|
+
-- run
|
|
661
|
+
|
|
662
|
+
begin;
|
|
663
|
+
-- run
|
|
664
|
+
insert into iul_rs values (2, 'y');
|
|
665
|
+
-- run
|
|
666
|
+
alter table iul_rs rename column rsa to rsz;
|
|
667
|
+
-- run
|
|
668
|
+
|
|
669
|
+
insert into iul_rs values (3, 'y');
|
|
670
|
+
-- error: unique
|
|
671
|
+
|
|
672
|
+
commit;
|
|
673
|
+
-- run
|
|
674
|
+
|
|
675
|
+
select id, rsz from iul_rs order by id;
|
|
676
|
+
→ [{"id":1,"rsz":"x"},{"id":2,"rsz":"y"}]
|
|
677
|
+
|
|
678
|
+
select count(*) as c from index_info('iul_rs');
|
|
679
|
+
→ [{"c":0}]
|
|
680
|
+
|
|
681
|
+
insert into iul_rs values (4, 'x');
|
|
682
|
+
-- error: unique
|
|
683
|
+
|
|
684
|
+
drop table iul_rs;
|
|
685
|
+
drop table iul_rd;
|
|
686
|
+
drop table iul_rn;
|
|
687
|
+
drop table iul_rp;
|
|
688
|
+
drop table iul_rf;
|
|
689
|
+
drop table iul_rx;
|
|
690
|
+
drop table iul_rm;
|
|
691
|
+
drop table iul_rk;
|
|
692
|
+
drop table iul_ui;
|
|
693
|
+
drop table iul_cm;
|
|
694
|
+
drop table iul_cc;
|
|
695
|
+
drop table iul_rc;
|
|
696
|
+
drop table iul_am;
|
|
697
|
+
drop table iul_au;
|
|
698
|
+
drop table iul_ac;
|
|
699
|
+
-- run
|
|
700
|
+
|
|
701
|
+
-- ============================================================
|
|
702
|
+
-- 10. Duplicate UNIQUE constraints — one rule, every declaration site
|
|
703
|
+
-- ============================================================
|
|
704
|
+
-- An unnamed UNIQUE has no name to compare, so before this guard the only thing that
|
|
705
|
+
-- ever noticed a repeat was the backing-structure name — which the memory backend
|
|
706
|
+
-- materializes and the store backend does not, so one statement got three different
|
|
707
|
+
-- answers. The test is the constraint's COLUMN SET, which both backends carry, and it
|
|
708
|
+
-- runs ahead of the index-name check so the message names the constraint rather than
|
|
709
|
+
-- an index the user never created.
|
|
710
|
+
|
|
711
|
+
-- 10a. ALTER TABLE ADD CONSTRAINT
|
|
712
|
+
create table dup_ac (id integer primary key, c integer, d integer);
|
|
713
|
+
alter table dup_ac add unique (c);
|
|
714
|
+
-- run
|
|
715
|
+
|
|
716
|
+
alter table dup_ac add unique (c);
|
|
717
|
+
-- error: an equivalent UNIQUE constraint on (c) already exists
|
|
718
|
+
|
|
719
|
+
-- Case folds, like every other column-name compare.
|
|
720
|
+
alter table dup_ac add unique (C);
|
|
721
|
+
-- error: an equivalent UNIQUE constraint on (C) already exists
|
|
722
|
+
|
|
723
|
+
-- Exactly one constraint survives the refusals, and it still enforces.
|
|
724
|
+
select count(*) as c from unique_constraint_info('dup_ac');
|
|
725
|
+
→ [{"c":1}]
|
|
726
|
+
|
|
727
|
+
insert into dup_ac values (1, 5, 7);
|
|
728
|
+
-- run
|
|
729
|
+
|
|
730
|
+
insert into dup_ac values (2, 5, 8);
|
|
731
|
+
-- error: unique
|
|
732
|
+
|
|
733
|
+
-- Column ORDER is not identity: `unique (c, d)` and `unique (d, c)` are one rule, even
|
|
734
|
+
-- though their derived structure names (`_uc_c_d` / `_uc_d_c`) differ.
|
|
735
|
+
alter table dup_ac add unique (c, d);
|
|
736
|
+
-- run
|
|
737
|
+
|
|
738
|
+
alter table dup_ac add unique (d, c);
|
|
739
|
+
-- error: an equivalent UNIQUE constraint on (d, c) already exists
|
|
740
|
+
|
|
741
|
+
select count(distinct id) as c from unique_constraint_info('dup_ac');
|
|
742
|
+
→ [{"c":2}]
|
|
743
|
+
|
|
744
|
+
-- 10b. ALTER TABLE ADD COLUMN … unique, repeated within ONE statement
|
|
745
|
+
create table dup_cc (id integer primary key, a text);
|
|
746
|
+
-- run
|
|
747
|
+
|
|
748
|
+
alter table dup_cc add column m text unique unique;
|
|
749
|
+
-- error: an equivalent UNIQUE constraint on (m) already exists
|
|
750
|
+
|
|
751
|
+
-- The refusal left the table exactly as it was — the column was never added.
|
|
752
|
+
select count(*) as c from table_info('dup_cc') where name = 'm';
|
|
753
|
+
→ [{"c":0}]
|
|
754
|
+
|
|
755
|
+
-- A single inline `unique` still adds the column and enforces.
|
|
756
|
+
alter table dup_cc add column n text unique;
|
|
757
|
+
insert into dup_cc values (1, 'p', 'q');
|
|
758
|
+
-- run
|
|
759
|
+
|
|
760
|
+
insert into dup_cc values (2, 'p2', 'q');
|
|
761
|
+
-- error: unique
|
|
762
|
+
|
|
763
|
+
-- 10c. CREATE TABLE — refused before any storage is created
|
|
764
|
+
create table dup_ct (id integer primary key, c integer, unique (c), unique (c));
|
|
765
|
+
-- error: an equivalent UNIQUE constraint on (c) already exists
|
|
766
|
+
|
|
767
|
+
select count(*) as c from schema() where type = 'table' and name = 'dup_ct';
|
|
768
|
+
→ [{"c":0}]
|
|
769
|
+
|
|
770
|
+
-- Column-level and table-level spellings of the same rule collide too.
|
|
771
|
+
create table dup_ct (id integer primary key, c integer unique, unique (c));
|
|
772
|
+
-- error: an equivalent UNIQUE constraint on (c) already exists
|
|
773
|
+
|
|
774
|
+
-- ...as do two column-level ones on one column.
|
|
775
|
+
create table dup_ct (id integer primary key, c integer unique unique);
|
|
776
|
+
-- error: an equivalent UNIQUE constraint on (c) already exists
|
|
777
|
+
|
|
778
|
+
-- Multi-column, declared in opposite orders — still one rule.
|
|
779
|
+
create table dup_ct (id integer primary key, c integer, d integer, unique (c, d), unique (d, c));
|
|
780
|
+
-- error: an equivalent UNIQUE constraint on (d, c) already exists
|
|
781
|
+
|
|
782
|
+
-- The name is free the whole time: a single declaration creates normally.
|
|
783
|
+
create table dup_ct (id integer primary key, c integer, d integer, unique (c), unique (d));
|
|
784
|
+
insert into dup_ct values (1, 1, 1);
|
|
785
|
+
-- run
|
|
786
|
+
|
|
787
|
+
insert into dup_ct values (2, 1, 2);
|
|
788
|
+
-- error: unique
|
|
789
|
+
|
|
790
|
+
-- 10d. CARVE-OUT — a named UNIQUE beside an unnamed one over the same columns is legal
|
|
791
|
+
-- Both are addressable and droppable by name, so the state is redundant, not broken.
|
|
792
|
+
create table dup_nm (id integer primary key, c integer, unique (c));
|
|
793
|
+
alter table dup_nm add constraint nm_u1 unique (c);
|
|
794
|
+
-- run
|
|
795
|
+
|
|
796
|
+
select count(distinct id) as c from unique_constraint_info('dup_nm');
|
|
797
|
+
→ [{"c":2}]
|
|
798
|
+
|
|
799
|
+
-- A SECOND named one is legal as well — a distinct name is a distinct identity.
|
|
800
|
+
alter table dup_nm add constraint nm_u2 unique (c);
|
|
801
|
+
-- run
|
|
802
|
+
|
|
803
|
+
-- ...but a repeated NAME is still refused, by the name guard rather than this one.
|
|
804
|
+
alter table dup_nm add constraint nm_u2 unique (c);
|
|
805
|
+
-- error: a constraint with that name already exists
|
|
806
|
+
|
|
807
|
+
-- The named ones drop by name, leaving the unnamed one enforcing.
|
|
808
|
+
alter table dup_nm drop constraint nm_u1;
|
|
809
|
+
alter table dup_nm drop constraint nm_u2;
|
|
810
|
+
insert into dup_nm values (1, 3);
|
|
811
|
+
-- run
|
|
812
|
+
|
|
813
|
+
insert into dup_nm values (2, 3);
|
|
814
|
+
-- error: unique
|
|
815
|
+
|
|
816
|
+
select count(distinct id) as c from unique_constraint_info('dup_nm');
|
|
817
|
+
→ [{"c":1}]
|
|
818
|
+
|
|
819
|
+
-- 10e. CARVE-OUT — partial UNIQUEs over the same columns are different rules
|
|
820
|
+
-- A partial UNIQUE only governs rows inside its predicate, so two with different
|
|
821
|
+
-- predicates are genuinely different constraints, and neither duplicates a full one.
|
|
822
|
+
create table dup_pt (id integer primary key, c integer, s text);
|
|
823
|
+
create unique index dup_pt_pos on dup_pt (c) where s = 'pos';
|
|
824
|
+
create unique index dup_pt_neg on dup_pt (c) where s = 'neg';
|
|
825
|
+
-- run
|
|
826
|
+
|
|
827
|
+
select count(distinct id) as c from unique_constraint_info('dup_pt') where partial = 1;
|
|
828
|
+
→ [{"c":2}]
|
|
829
|
+
|
|
830
|
+
-- A full UNIQUE over the same column is still accepted beside them, and builds its
|
|
831
|
+
-- OWN backing structure rather than adopting either partial index — a partial index
|
|
832
|
+
-- only covers rows inside its predicate, so it cannot back a rule that covers every row.
|
|
833
|
+
alter table dup_pt add unique (c);
|
|
834
|
+
-- run
|
|
835
|
+
|
|
836
|
+
select count(distinct id) as c from unique_constraint_info('dup_pt');
|
|
837
|
+
→ [{"c":3}]
|
|
838
|
+
|
|
839
|
+
-- ...and it enforces across the WHOLE table, not just the rows either partial
|
|
840
|
+
-- index's predicate admits.
|
|
841
|
+
insert into dup_pt values (1, 5, 'a');
|
|
842
|
+
-- run
|
|
843
|
+
|
|
844
|
+
insert into dup_pt values (2, 5, 'b');
|
|
845
|
+
-- error: unique
|
|
846
|
+
|
|
847
|
+
-- ...and now it is the full one that refuses the repeat.
|
|
848
|
+
alter table dup_pt add unique (c);
|
|
849
|
+
-- error: an equivalent UNIQUE constraint on (c) already exists
|
|
850
|
+
|
|
851
|
+
-- 10e-bis. The full UNIQUE keeps enforcing after the index it ADOPTED is dropped,
|
|
852
|
+
-- and still refuses to fall back onto a same-column PARTIAL index.
|
|
853
|
+
-- `add unique (c)` reuses `reuse_full` (unique, unfiltered, same collation) instead of
|
|
854
|
+
-- building its own structure. Dropping that index leaves the constraint with no named
|
|
855
|
+
-- structure, so enforcement resolves the covering index by column set — where a partial
|
|
856
|
+
-- index over (c) is the only remaining candidate. It must be refused: with it adopted,
|
|
857
|
+
-- both rows below sit outside `s = 'pos'` and the duplicate `c = 7` goes unnoticed.
|
|
858
|
+
create table reuse_pt (id integer primary key, c integer, s text);
|
|
859
|
+
create unique index reuse_part on reuse_pt (c) where s = 'pos';
|
|
860
|
+
create unique index reuse_full on reuse_pt (c);
|
|
861
|
+
alter table reuse_pt add unique (c);
|
|
862
|
+
drop index reuse_full;
|
|
863
|
+
-- run
|
|
864
|
+
|
|
865
|
+
insert into reuse_pt values (1, 7, 'a');
|
|
866
|
+
-- run
|
|
867
|
+
|
|
868
|
+
insert into reuse_pt values (2, 7, 'b');
|
|
869
|
+
-- error: unique
|
|
870
|
+
|
|
871
|
+
select count(*) as c from reuse_pt;
|
|
872
|
+
→ [{"c":1}]
|
|
873
|
+
|
|
874
|
+
-- The partial index still enforces its own narrower rule alongside.
|
|
875
|
+
insert into reuse_pt values (3, 8, 'pos');
|
|
876
|
+
-- run
|
|
877
|
+
|
|
878
|
+
insert into reuse_pt values (4, 8, 'pos');
|
|
879
|
+
-- error: unique
|
|
880
|
+
|
|
881
|
+
-- 10f. A column name is compared as a whole name, never as text spliced together
|
|
882
|
+
-- A quoted identifier may contain anything, including whatever character the guard
|
|
883
|
+
-- uses to concatenate a column set. `unique ("a,b")` covers ONE column and must not
|
|
884
|
+
-- read as the pair `(a, b)`; refusing it would turn away a legal declaration.
|
|
885
|
+
create table dup_sep (id integer primary key, "a,b" integer, a integer, b integer, unique (a, b));
|
|
886
|
+
alter table dup_sep add unique ("a,b");
|
|
887
|
+
-- run
|
|
888
|
+
|
|
889
|
+
select count(distinct id) as c from unique_constraint_info('dup_sep');
|
|
890
|
+
→ [{"c":2}]
|
|
891
|
+
|
|
892
|
+
-- Both enforce, independently.
|
|
893
|
+
insert into dup_sep values (1, 1, 1, 1);
|
|
894
|
+
-- run
|
|
895
|
+
|
|
896
|
+
insert into dup_sep values (2, 2, 1, 1);
|
|
897
|
+
-- error: unique
|
|
898
|
+
|
|
899
|
+
insert into dup_sep values (3, 1, 3, 3);
|
|
900
|
+
-- error: unique
|
|
901
|
+
|
|
902
|
+
-- ...and the repeat of the single-column one is still refused.
|
|
903
|
+
alter table dup_sep add unique ("a,b");
|
|
904
|
+
-- error: an equivalent UNIQUE constraint on (a,b) already exists
|
|
905
|
+
|
|
906
|
+
drop table dup_sep;
|
|
907
|
+
drop table dup_pt;
|
|
908
|
+
drop table dup_nm;
|
|
909
|
+
drop table dup_ct;
|
|
910
|
+
drop table dup_cc;
|
|
911
|
+
drop table dup_ac;
|
|
912
|
+
-- run
|
|
913
|
+
|
|
914
|
+
-- ============================================================
|
|
915
|
+
-- 11. Two UNIQUE constraints may not derive ONE backing structure name
|
|
916
|
+
-- ============================================================
|
|
917
|
+
-- A UNIQUE's backing structure is named after the constraint, or `_uc_<cols>` when
|
|
918
|
+
-- unnamed. Nothing in that rule stops two constraints on one table deriving the SAME
|
|
919
|
+
-- name. Two spellings reach it: a constraint name written with the engine's reserved
|
|
920
|
+
-- `_uc_` prefix, and the `_`-joined auto-name colliding on ordinary column names
|
|
921
|
+
-- (`unique (a_b)` beside `unique (a, b)` — both derive `_uc_a_b`). Left unguarded the
|
|
922
|
+
-- second constraint shares the first's structure and is then enforced by one keyed on
|
|
923
|
+
-- the OTHER constraint's columns, so it silently stops rejecting duplicates.
|
|
924
|
+
--
|
|
925
|
+
-- Refused on every authoring path. The comparison reads `uniqueConstraints`, which
|
|
926
|
+
-- BOTH backends carry, rather than a materialized index (memory has one, store does
|
|
927
|
+
-- not) — so the message below is identical on both, which is the point of this section.
|
|
928
|
+
|
|
929
|
+
-- 11a. CREATE TABLE — a constraint name carrying the reserved `_uc_` prefix
|
|
930
|
+
create table cn_ct (id integer primary key, cn_c integer, cn_b integer,
|
|
931
|
+
constraint _uc_cn_c unique (cn_b), unique (cn_c));
|
|
932
|
+
-- error: both derive backing structure name '_uc_cn_c'
|
|
933
|
+
|
|
934
|
+
-- Refused before any storage is created.
|
|
935
|
+
select count(*) as c from schema() where type = 'table' and name = 'cn_ct';
|
|
936
|
+
→ [{"c":0}]
|
|
937
|
+
|
|
938
|
+
-- Case folds, like every other index-name compare.
|
|
939
|
+
create table cn_ct (id integer primary key, cn_c integer, cn_b integer,
|
|
940
|
+
constraint _UC_CN_C unique (cn_b), unique (cn_c));
|
|
941
|
+
-- error: both derive backing structure name
|
|
942
|
+
|
|
943
|
+
-- Two inline `unique` constraints on ONE column, the written name coinciding with the
|
|
944
|
+
-- auto-name the other derives. The CREATE TABLE twin of the ADD COLUMN case in 11d —
|
|
945
|
+
-- neither constraint can be seen on a table, so only comparing the declaration against
|
|
946
|
+
-- itself catches it.
|
|
947
|
+
create table cn_ct (id integer primary key, cn_c integer constraint _uc_cn_c unique unique);
|
|
948
|
+
-- error: both derive backing structure name '_uc_cn_c'
|
|
949
|
+
|
|
950
|
+
select count(*) as c from schema() where type = 'table' and name = 'cn_ct';
|
|
951
|
+
→ [{"c":0}]
|
|
952
|
+
|
|
953
|
+
-- 11b. CREATE TABLE — ordinary column names, no reserved prefix anywhere
|
|
954
|
+
-- The auto-name joins the covered column names with `_`, so the single column `cnq_cnr`
|
|
955
|
+
-- derives exactly what the pair `(cnq, cnr)` derives.
|
|
956
|
+
create table cn_ab (id integer primary key, cnq_cnr integer, cnq integer, cnr integer,
|
|
957
|
+
unique (cnq_cnr), unique (cnq, cnr));
|
|
958
|
+
-- error: both derive backing structure name '_uc_cnq_cnr'
|
|
959
|
+
|
|
960
|
+
select count(*) as c from schema() where type = 'table' and name = 'cn_ab';
|
|
961
|
+
→ [{"c":0}]
|
|
962
|
+
|
|
963
|
+
-- Column-level and table-level spellings meet in the same comparison.
|
|
964
|
+
create table cn_ab (id integer primary key, cnq_cnr integer unique, cnq integer, cnr integer,
|
|
965
|
+
unique (cnq, cnr));
|
|
966
|
+
-- error: both derive backing structure name '_uc_cnq_cnr'
|
|
967
|
+
|
|
968
|
+
-- Neither declaration alone is a problem: free names still create and enforce.
|
|
969
|
+
create table cn_ab (id integer primary key, cnq_cnr integer, cnq integer, cnr integer,
|
|
970
|
+
unique (cnq_cnr), unique (cnq));
|
|
971
|
+
insert into cn_ab values (1, 1, 1, 1);
|
|
972
|
+
-- run
|
|
973
|
+
|
|
974
|
+
insert into cn_ab values (2, 1, 2, 2);
|
|
975
|
+
-- error: unique
|
|
976
|
+
|
|
977
|
+
insert into cn_ab values (3, 3, 1, 3);
|
|
978
|
+
-- error: unique
|
|
979
|
+
|
|
980
|
+
-- 11c. ALTER TABLE ADD CONSTRAINT — colliding with another CONSTRAINT's structure
|
|
981
|
+
-- (not an index), so the refusal must come from the constraint comparison on BOTH
|
|
982
|
+
-- backends rather than from memory's materialized index entry.
|
|
983
|
+
create table cn_ac (id integer primary key, cn_c integer, cn_b integer, unique (cn_c));
|
|
984
|
+
-- run
|
|
985
|
+
|
|
986
|
+
alter table cn_ac add constraint _uc_cn_c unique (cn_b);
|
|
987
|
+
-- error: would collide with the backing structure of the UNIQUE constraint on (cn_c)
|
|
988
|
+
|
|
989
|
+
-- Nothing was installed by the refusal.
|
|
990
|
+
select count(distinct id) as c from unique_constraint_info('cn_ac');
|
|
991
|
+
→ [{"c":1}]
|
|
992
|
+
|
|
993
|
+
-- The mirror spelling: the reserved-prefix NAME is on the table and the auto-name is
|
|
994
|
+
-- the one being declared. The message names the other constraint the way it was written.
|
|
995
|
+
create table cn_am (id integer primary key, cn_m integer, cn_n integer,
|
|
996
|
+
constraint _uc_cn_m unique (cn_n));
|
|
997
|
+
-- run
|
|
998
|
+
|
|
999
|
+
alter table cn_am add unique (cn_m);
|
|
1000
|
+
-- error: would collide with the backing structure of the UNIQUE constraint '_uc_cn_m'
|
|
1001
|
+
|
|
1002
|
+
select count(distinct id) as c from unique_constraint_info('cn_am');
|
|
1003
|
+
→ [{"c":1}]
|
|
1004
|
+
|
|
1005
|
+
-- 11d. ALTER TABLE ADD COLUMN … unique
|
|
1006
|
+
create table cn_cc (id integer primary key, cn_q integer, constraint _uc_c unique (cn_q));
|
|
1007
|
+
-- run
|
|
1008
|
+
|
|
1009
|
+
alter table cn_cc add column c text unique;
|
|
1010
|
+
-- error: would collide with the backing structure of the UNIQUE constraint '_uc_c'
|
|
1011
|
+
|
|
1012
|
+
-- The refusal left the table exactly as it was — the column was never added.
|
|
1013
|
+
select count(*) as c from table_info('cn_cc') where name = 'c';
|
|
1014
|
+
→ [{"c":0}]
|
|
1015
|
+
|
|
1016
|
+
-- Two inline `unique` constraints on ONE new column can collide with EACH OTHER, and
|
|
1017
|
+
-- neither is on the table yet — so only comparing them against one another catches it.
|
|
1018
|
+
create table cn_ci (id integer primary key, cn_r integer);
|
|
1019
|
+
-- run
|
|
1020
|
+
|
|
1021
|
+
alter table cn_ci add column c text constraint _uc_c unique unique;
|
|
1022
|
+
-- error: both derive backing structure name '_uc_c'
|
|
1023
|
+
|
|
1024
|
+
select count(*) as c from table_info('cn_ci') where name = 'c';
|
|
1025
|
+
→ [{"c":0}]
|
|
1026
|
+
|
|
1027
|
+
-- 11e. ALTER TABLE RENAME COLUMN — the rename is what makes the two names meet
|
|
1028
|
+
-- `_uc_<cols>` is derived from the covered columns' CURRENT names, so renaming a
|
|
1029
|
+
-- covered column moves the structure onto a name another constraint already derives.
|
|
1030
|
+
create table cn_rk (id integer primary key, cnk_cnm integer, cnk integer, cnx integer,
|
|
1031
|
+
unique (cnk_cnm), unique (cnk, cnx));
|
|
1032
|
+
insert into cn_rk values (1, 1, 1, 1);
|
|
1033
|
+
-- run
|
|
1034
|
+
|
|
1035
|
+
alter table cn_rk rename column cnx to cnm;
|
|
1036
|
+
-- error: would collide with the backing structure of the UNIQUE constraint on (cnk_cnm)
|
|
1037
|
+
|
|
1038
|
+
-- Nothing moved: the column keeps its old name...
|
|
1039
|
+
select count(*) as c from table_info('cn_rk') where name = 'cnx';
|
|
1040
|
+
→ [{"c":1}]
|
|
1041
|
+
|
|
1042
|
+
select count(*) as c from table_info('cn_rk') where name = 'cnm';
|
|
1043
|
+
→ [{"c":0}]
|
|
1044
|
+
|
|
1045
|
+
-- ...and both constraints still enforce, each under its own structure.
|
|
1046
|
+
insert into cn_rk values (2, 1, 2, 2);
|
|
1047
|
+
-- error: unique
|
|
1048
|
+
|
|
1049
|
+
insert into cn_rk values (3, 3, 1, 1);
|
|
1050
|
+
-- error: unique
|
|
1051
|
+
|
|
1052
|
+
-- 11f. ALTER TABLE RENAME CONSTRAINT onto another constraint's derived name
|
|
1053
|
+
create table cn_rn (id integer primary key, cn_m integer, cn_n integer,
|
|
1054
|
+
unique (cn_m), constraint cn_u unique (cn_n));
|
|
1055
|
+
-- run
|
|
1056
|
+
|
|
1057
|
+
alter table cn_rn rename constraint cn_u to _uc_cn_m;
|
|
1058
|
+
-- error: would collide with the backing structure of the UNIQUE constraint on (cn_m)
|
|
1059
|
+
|
|
1060
|
+
-- Neither constraint moved.
|
|
1061
|
+
select count(*) as c from unique_constraint_info('cn_rn') where name = 'cn_u';
|
|
1062
|
+
→ [{"c":1}]
|
|
1063
|
+
|
|
1064
|
+
-- A free name still renames.
|
|
1065
|
+
alter table cn_rn rename constraint cn_u to cn_u_moved;
|
|
1066
|
+
-- run
|
|
1067
|
+
|
|
1068
|
+
select count(*) as c from unique_constraint_info('cn_rn') where name = 'cn_u_moved';
|
|
1069
|
+
→ [{"c":1}]
|
|
1070
|
+
|
|
1071
|
+
-- 11g. NEGATIVE CONTROLS — legal shapes that must keep working
|
|
1072
|
+
-- Two DIFFERENTLY-named UNIQUEs over the same columns: distinct names, distinct
|
|
1073
|
+
-- structures. Redundant, not broken.
|
|
1074
|
+
create table cn_ok1 (id integer primary key, cn_c1 integer,
|
|
1075
|
+
constraint cn_u1 unique (cn_c1), constraint cn_u2 unique (cn_c1));
|
|
1076
|
+
insert into cn_ok1 values (1, 5);
|
|
1077
|
+
-- run
|
|
1078
|
+
|
|
1079
|
+
select count(distinct id) as c from unique_constraint_info('cn_ok1');
|
|
1080
|
+
→ [{"c":2}]
|
|
1081
|
+
|
|
1082
|
+
insert into cn_ok1 values (2, 5);
|
|
1083
|
+
-- error: unique
|
|
1084
|
+
|
|
1085
|
+
-- A named UNIQUE beside an unnamed one over the same columns — `_uc_<cols>` and the
|
|
1086
|
+
-- name are different names, so nothing collides.
|
|
1087
|
+
create table cn_ok2 (id integer primary key, cn_c2 integer,
|
|
1088
|
+
unique (cn_c2), constraint cn_u3 unique (cn_c2));
|
|
1089
|
+
insert into cn_ok2 values (1, 5);
|
|
1090
|
+
-- run
|
|
1091
|
+
|
|
1092
|
+
select count(distinct id) as c from unique_constraint_info('cn_ok2');
|
|
1093
|
+
→ [{"c":2}]
|
|
1094
|
+
|
|
1095
|
+
insert into cn_ok2 values (2, 5);
|
|
1096
|
+
-- error: unique
|
|
1097
|
+
|
|
1098
|
+
-- A UNIQUE whose columns are already covered by a user index of a DIFFERENT name:
|
|
1099
|
+
-- structure reuse matches on COLUMNS, never on names, so the declaration stands.
|
|
1100
|
+
create table cn_ok3 (id integer primary key, cn_c3 integer);
|
|
1101
|
+
create index cn_ok3_i on cn_ok3 (cn_c3);
|
|
1102
|
+
alter table cn_ok3 add unique (cn_c3);
|
|
1103
|
+
insert into cn_ok3 values (1, 5);
|
|
1104
|
+
-- run
|
|
1105
|
+
|
|
1106
|
+
select count(*) as c from schema() where type = 'index' and name = 'cn_ok3_i';
|
|
1107
|
+
→ [{"c":1}]
|
|
1108
|
+
|
|
1109
|
+
insert into cn_ok3 values (2, 5);
|
|
1110
|
+
-- error: unique
|
|
1111
|
+
|
|
1112
|
+
-- A constraint synthesized by `create unique index` is SKIPPED by the constraint
|
|
1113
|
+
-- comparison: its backing structure IS that index, already compared by the index arm,
|
|
1114
|
+
-- so matching it here would report one collision twice under two messages. The proof is
|
|
1115
|
+
-- the message — a declaration onto that name is refused as an INDEX collision.
|
|
1116
|
+
create table cn_ok4 (id integer primary key, cn_c4 integer, cn_d4 integer);
|
|
1117
|
+
create unique index _uc_cn_d4 on cn_ok4 (cn_c4);
|
|
1118
|
+
insert into cn_ok4 values (1, 5, 6);
|
|
1119
|
+
-- run
|
|
1120
|
+
|
|
1121
|
+
alter table cn_ok4 add unique (cn_d4);
|
|
1122
|
+
-- error: would collide with existing index '_uc_cn_d4'
|
|
1123
|
+
|
|
1124
|
+
insert into cn_ok4 values (2, 5, 7);
|
|
1125
|
+
-- error: unique
|
|
1126
|
+
|
|
1127
|
+
-- Same for a PARTIAL one. Only `create unique index … where` produces a partial UNIQUE
|
|
1128
|
+
-- constraint today, so a partial's structure is always its own index and the index arm
|
|
1129
|
+
-- owns the refusal — but the name may still not be shared.
|
|
1130
|
+
create table cn_ok5 (id integer primary key, cn_c5 integer, cn_s5 text, cn_d5 integer);
|
|
1131
|
+
create unique index _uc_cn_d5 on cn_ok5 (cn_c5) where cn_s5 = 'pos';
|
|
1132
|
+
-- run
|
|
1133
|
+
|
|
1134
|
+
alter table cn_ok5 add unique (cn_d5);
|
|
1135
|
+
-- error: would collide with existing index '_uc_cn_d5'
|
|
1136
|
+
|
|
1137
|
+
-- The partial index still enforces its own narrower rule.
|
|
1138
|
+
insert into cn_ok5 values (1, 5, 'pos', 1);
|
|
1139
|
+
-- run
|
|
1140
|
+
|
|
1141
|
+
insert into cn_ok5 values (2, 5, 'pos', 2);
|
|
1142
|
+
-- error: unique
|
|
1143
|
+
|
|
1144
|
+
insert into cn_ok5 values (3, 5, 'neg', 3);
|
|
1145
|
+
-- run
|
|
1146
|
+
|
|
1147
|
+
drop table cn_ok5;
|
|
1148
|
+
drop table cn_ok4;
|
|
1149
|
+
drop table cn_ok3;
|
|
1150
|
+
drop table cn_ok2;
|
|
1151
|
+
drop table cn_ok1;
|
|
1152
|
+
drop table cn_rn;
|
|
1153
|
+
drop table cn_rk;
|
|
1154
|
+
drop table cn_ci;
|
|
1155
|
+
drop table cn_cc;
|
|
1156
|
+
drop table cn_am;
|
|
1157
|
+
drop table cn_ac;
|
|
1158
|
+
drop table cn_ab;
|
|
1159
|
+
-- run
|
|
1160
|
+
|
|
1161
|
+
-- ============================================================
|
|
1162
|
+
-- 12. `apply schema` reaches the same guard
|
|
1163
|
+
-- ============================================================
|
|
1164
|
+
-- The differ emits `ALTER TABLE … ADD <constraint>` through the normal statement
|
|
1165
|
+
-- pipeline, so it inherits the ADD CONSTRAINT arm above rather than needing its
|
|
1166
|
+
-- own check. Runs last, against an otherwise-empty main schema, so the
|
|
1167
|
+
-- declaration below is the whole of main and the diff generates no drops.
|
|
1168
|
+
|
|
1169
|
+
create table as_t (id integer primary key, a text, b text);
|
|
1170
|
+
create index as_foo on as_t (b);
|
|
1171
|
+
insert into as_t values (1, 'x', 'p'), (2, 'y', 'q');
|
|
1172
|
+
-- run
|
|
1173
|
+
|
|
1174
|
+
declare schema main {
|
|
1175
|
+
table as_t (id integer primary key, a text, b text, constraint as_foo unique (a))
|
|
1176
|
+
index as_foo on as_t (b)
|
|
1177
|
+
}
|
|
1178
|
+
-- run
|
|
1179
|
+
|
|
1180
|
+
apply schema main;
|
|
1181
|
+
-- error: would collide with existing index 'as_foo'
|
|
1182
|
+
|
|
1183
|
+
-- The migration stopped at the offending statement; the index is intact.
|
|
1184
|
+
select count(*) as c from schema() where type = 'index' and name = 'as_foo';
|
|
1185
|
+
→ [{"c":1}]
|
|
1186
|
+
|
|
1187
|
+
select id from as_t where b = 'q';
|
|
1188
|
+
→ [{"id":2}]
|
|
1189
|
+
|
|
1190
|
+
drop table as_t;
|
|
1191
|
+
-- run
|
|
1192
|
+
|
|
1193
|
+
-- The CREATE TABLE arm reaches its guard the same way: a declared table carrying the
|
|
1194
|
+
-- same plain UNIQUE twice is emitted as an ordinary CREATE TABLE and refused there.
|
|
1195
|
+
declare schema main {
|
|
1196
|
+
table as_dup (id integer primary key, c integer, unique (c), unique (c))
|
|
1197
|
+
}
|
|
1198
|
+
-- run
|
|
1199
|
+
|
|
1200
|
+
apply schema main;
|
|
1201
|
+
-- error: an equivalent UNIQUE constraint on (c) already exists
|
|
1202
|
+
|
|
1203
|
+
select count(*) as c from schema() where type = 'table' and name = 'as_dup';
|
|
1204
|
+
→ [{"c":0}]
|
|
1205
|
+
|
|
1206
|
+
-- ...and so does a declared table whose two UNIQUEs derive one backing structure name.
|
|
1207
|
+
declare schema main {
|
|
1208
|
+
table as_bn (id integer primary key, as_c integer, as_b integer, constraint _uc_as_c unique (as_b), unique (as_c))
|
|
1209
|
+
}
|
|
1210
|
+
-- run
|
|
1211
|
+
|
|
1212
|
+
apply schema main;
|
|
1213
|
+
-- error: both derive backing structure name '_uc_as_c'
|
|
1214
|
+
|
|
1215
|
+
select count(*) as c from schema() where type = 'table' and name = 'as_bn';
|
|
1216
|
+
→ [{"c":0}]
|