@quereus/quereus 4.20.1 → 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/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,2550 @@
|
|
|
1
|
+
-- Test declarative schema full lifecycle
|
|
2
|
+
|
|
3
|
+
-- Step 1: Declare a schema with a table
|
|
4
|
+
declare schema main {
|
|
5
|
+
table users {
|
|
6
|
+
id INTEGER PRIMARY KEY,
|
|
7
|
+
name TEXT NOT NULL,
|
|
8
|
+
email TEXT
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
-- run
|
|
13
|
+
|
|
14
|
+
-- Step 2: Diff against empty database (should show CREATE TABLE as DDL row)
|
|
15
|
+
diff schema main;
|
|
16
|
+
→ [{"ddl": "create table users (id INTEGER primary key, name TEXT not null, email TEXT)"}]
|
|
17
|
+
|
|
18
|
+
-- Step 3: Apply the schema (creates the table, returns nothing)
|
|
19
|
+
apply schema main;
|
|
20
|
+
|
|
21
|
+
-- run
|
|
22
|
+
|
|
23
|
+
-- Step 4: Verify table was created
|
|
24
|
+
select count(*) from users;
|
|
25
|
+
→ [{"count(*)": 0}]
|
|
26
|
+
|
|
27
|
+
-- Step 5: Diff again - should now be empty (no rows) since schema matches
|
|
28
|
+
diff schema main;
|
|
29
|
+
→ []
|
|
30
|
+
|
|
31
|
+
-- Step 6: Declare schema with seed data
|
|
32
|
+
declare schema main {
|
|
33
|
+
table users {
|
|
34
|
+
id INTEGER PRIMARY KEY,
|
|
35
|
+
name TEXT NOT NULL,
|
|
36
|
+
email TEXT
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
seed users (
|
|
40
|
+
(1, 'Alice', 'alice@example.com'),
|
|
41
|
+
(2, 'Bob', 'bob@example.com')
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
-- run
|
|
46
|
+
|
|
47
|
+
-- Step 7: Apply with seed (no rows returned)
|
|
48
|
+
apply schema main with seed;
|
|
49
|
+
|
|
50
|
+
-- run
|
|
51
|
+
|
|
52
|
+
-- Step 8: Verify seed data was inserted
|
|
53
|
+
select * from users order by id;
|
|
54
|
+
→ [{"id": 1, "name": "Alice", "email": "alice@example.com"}, {"id": 2, "name": "Bob", "email": "bob@example.com"}]
|
|
55
|
+
|
|
56
|
+
-- Step 9: Test forward references - declare tables out of order with foreign keys
|
|
57
|
+
declare schema main {
|
|
58
|
+
table posts {
|
|
59
|
+
id INTEGER PRIMARY KEY,
|
|
60
|
+
user_id INTEGER NOT NULL,
|
|
61
|
+
title TEXT NOT NULL,
|
|
62
|
+
content TEXT,
|
|
63
|
+
constraint fk_user foreign key (user_id) references users(id)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
table users {
|
|
67
|
+
id INTEGER PRIMARY KEY,
|
|
68
|
+
name TEXT NOT NULL,
|
|
69
|
+
email TEXT
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
table comments {
|
|
73
|
+
id INTEGER PRIMARY KEY,
|
|
74
|
+
post_id INTEGER NOT NULL,
|
|
75
|
+
user_id INTEGER NOT NULL,
|
|
76
|
+
text TEXT NOT NULL,
|
|
77
|
+
constraint fk_post foreign key (post_id) references posts(id),
|
|
78
|
+
constraint fk_comment_user foreign key (user_id) references users(id)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
-- run
|
|
83
|
+
|
|
84
|
+
-- Step 10: Diff should show posts and comments to create (users already exists)
|
|
85
|
+
diff schema main;
|
|
86
|
+
→ [{"ddl": "create table posts (id INTEGER primary key, user_id INTEGER not null, title TEXT not null, content TEXT, constraint fk_user foreign key (user_id) references users(id))"}, {"ddl": "create table comments (id INTEGER primary key, post_id INTEGER not null, user_id INTEGER not null, text TEXT not null, constraint fk_post foreign key (post_id) references posts(id), constraint fk_comment_user foreign key (user_id) references users(id))"}]
|
|
87
|
+
|
|
88
|
+
-- Step 11: Apply the forward-referenced schema
|
|
89
|
+
apply schema main;
|
|
90
|
+
|
|
91
|
+
-- run
|
|
92
|
+
|
|
93
|
+
-- Step 12: Verify all three tables exist
|
|
94
|
+
select count(*) from users;
|
|
95
|
+
→ [{"count(*)": 2}]
|
|
96
|
+
|
|
97
|
+
select count(*) from posts;
|
|
98
|
+
→ [{"count(*)": 0}]
|
|
99
|
+
|
|
100
|
+
select count(*) from comments;
|
|
101
|
+
→ [{"count(*)": 0}]
|
|
102
|
+
|
|
103
|
+
-- Step 13: Insert data respecting foreign key order
|
|
104
|
+
insert into posts (id, user_id, title, content) values (1, 1, 'First Post', 'This is the content');
|
|
105
|
+
insert into comments (id, post_id, user_id, text) values (1, 1, 2, 'Great post!');
|
|
106
|
+
|
|
107
|
+
select p.title, c.text, u.name as commenter
|
|
108
|
+
from posts p
|
|
109
|
+
join comments c on p.id = c.post_id
|
|
110
|
+
join users u on c.user_id = u.id;
|
|
111
|
+
→ [{"title": "First Post", "text": "Great post!", "commenter": "Bob"}]
|
|
112
|
+
|
|
113
|
+
-- Step 14: Test table removal via redeclaration (only keep users)
|
|
114
|
+
declare schema main {
|
|
115
|
+
table users {
|
|
116
|
+
id INTEGER PRIMARY KEY,
|
|
117
|
+
name TEXT NOT NULL,
|
|
118
|
+
email TEXT
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
-- run
|
|
123
|
+
|
|
124
|
+
-- Step 15: Diff should show DROP statements for posts and comments
|
|
125
|
+
-- (comments must be dropped before posts because it FK-references posts)
|
|
126
|
+
diff schema main;
|
|
127
|
+
→ [{"ddl": "DROP TABLE IF EXISTS comments"}, {"ddl": "DROP TABLE IF EXISTS posts"}]
|
|
128
|
+
|
|
129
|
+
-- Step 16: Apply to drop extra tables
|
|
130
|
+
apply schema main;
|
|
131
|
+
|
|
132
|
+
-- run
|
|
133
|
+
|
|
134
|
+
-- Step 17: Verify posts and comments are gone
|
|
135
|
+
select count(*) from users;
|
|
136
|
+
→ [{"count(*)": 2}]
|
|
137
|
+
|
|
138
|
+
select count(*) from posts;
|
|
139
|
+
-- error: Table 'posts' not found in schema path
|
|
140
|
+
|
|
141
|
+
select count(*) from comments;
|
|
142
|
+
-- error: Table 'comments' not found in schema path
|
|
143
|
+
|
|
144
|
+
-- Step 18: Test composite schema with multiple seeds and forward refs
|
|
145
|
+
-- Declare a completely new schema (replacing the previous one)
|
|
146
|
+
declare schema main {
|
|
147
|
+
table categories {
|
|
148
|
+
id INTEGER PRIMARY KEY,
|
|
149
|
+
name TEXT NOT NULL
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
table products {
|
|
153
|
+
id INTEGER PRIMARY KEY,
|
|
154
|
+
category_id INTEGER NOT NULL,
|
|
155
|
+
name TEXT NOT NULL,
|
|
156
|
+
price REAL NOT NULL,
|
|
157
|
+
constraint fk_category foreign key (category_id) references categories(id)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
seed categories (
|
|
161
|
+
(1, 'Electronics'),
|
|
162
|
+
(2, 'Books'),
|
|
163
|
+
(3, 'Clothing')
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
seed products (
|
|
167
|
+
(1, 1, 'Laptop', 999.99),
|
|
168
|
+
(2, 1, 'Mouse', 29.99),
|
|
169
|
+
(3, 2, 'SQL Guide', 49.99)
|
|
170
|
+
)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
-- run
|
|
174
|
+
|
|
175
|
+
-- Step 19: Apply complete replacement (drops users, creates categories/products)
|
|
176
|
+
apply schema main with seed;
|
|
177
|
+
|
|
178
|
+
-- run
|
|
179
|
+
|
|
180
|
+
-- Step 20: Verify relational integrity with join
|
|
181
|
+
select p.name as product, c.name as category, p.price
|
|
182
|
+
from products p
|
|
183
|
+
join categories c on p.category_id = c.id
|
|
184
|
+
order by c.id, p.id;
|
|
185
|
+
→ [{"product": "Laptop", "category": "Electronics", "price": 999.99}, {"product": "Mouse", "category": "Electronics", "price": 29.99}, {"product": "SQL Guide", "category": "Books", "price": 49.99}]
|
|
186
|
+
|
|
187
|
+
-- Step 21: Verify old tables are gone
|
|
188
|
+
select count(*) from users;
|
|
189
|
+
-- error: not found in schema path:
|
|
190
|
+
|
|
191
|
+
-- Step 22: Test trailing commas in table definitions
|
|
192
|
+
declare schema main {
|
|
193
|
+
table config {
|
|
194
|
+
config_key TEXT PRIMARY KEY,
|
|
195
|
+
value TEXT,
|
|
196
|
+
description TEXT,
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
-- run
|
|
201
|
+
|
|
202
|
+
apply schema main with seed;
|
|
203
|
+
|
|
204
|
+
-- run
|
|
205
|
+
|
|
206
|
+
select count(*) from config;
|
|
207
|
+
→ [{"count(*)": 0}]
|
|
208
|
+
|
|
209
|
+
-- Verify products table was dropped
|
|
210
|
+
select count(*) from products;
|
|
211
|
+
-- error: not found in schema path:
|
|
212
|
+
|
|
213
|
+
-- Verify categories table was dropped
|
|
214
|
+
select count(*) from categories;
|
|
215
|
+
-- error: not found in schema path:
|
|
216
|
+
|
|
217
|
+
-- Step 23: Test explicit main schema qualification
|
|
218
|
+
select count(*) from main.config;
|
|
219
|
+
→ [{"count(*)": 0}]
|
|
220
|
+
|
|
221
|
+
-- Step 24: Verify trailing comma test passed
|
|
222
|
+
declare schema main {
|
|
223
|
+
table test_trailing {
|
|
224
|
+
a INTEGER PRIMARY KEY,
|
|
225
|
+
b TEXT,
|
|
226
|
+
c TEXT,
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
-- run
|
|
231
|
+
|
|
232
|
+
apply schema main;
|
|
233
|
+
|
|
234
|
+
-- run
|
|
235
|
+
|
|
236
|
+
select count(*) from test_trailing;
|
|
237
|
+
→ [{"count(*)": 0}]
|
|
238
|
+
|
|
239
|
+
-- Step 25: Test custom schema with forward references and CHECK constraints
|
|
240
|
+
declare schema Test2 {
|
|
241
|
+
table A {
|
|
242
|
+
Col1 TEXT,
|
|
243
|
+
constraint Col1InB check on insert, update (exists (select 1 from B where new.Col1 = B.Col1))
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
table B {
|
|
247
|
+
Col1 TEXT,
|
|
248
|
+
primary key (Col1)
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
seed B (
|
|
252
|
+
('valid1'),
|
|
253
|
+
('valid2')
|
|
254
|
+
)
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
-- run
|
|
258
|
+
|
|
259
|
+
apply schema Test2 with seed;
|
|
260
|
+
|
|
261
|
+
-- run
|
|
262
|
+
|
|
263
|
+
-- Verify B has seed data
|
|
264
|
+
select * from Test2.B order by Col1;
|
|
265
|
+
→ [{"Col1": "valid1"}, {"Col1": "valid2"}]
|
|
266
|
+
|
|
267
|
+
-- Insert into A with value that exists in B (should succeed)
|
|
268
|
+
insert into Test2.A (Col1) values ('valid1');
|
|
269
|
+
|
|
270
|
+
select * from Test2.A;
|
|
271
|
+
→ [{"Col1": "valid1"}]
|
|
272
|
+
|
|
273
|
+
-- Insert into A with value that doesn't exist in B (should fail)
|
|
274
|
+
insert into Test2.A (Col1) values ('invalid');
|
|
275
|
+
-- error: CHECK constraint failed
|
|
276
|
+
|
|
277
|
+
-- Verify schema isolation: Test2 and main are separate
|
|
278
|
+
select count(*) from Test2.A;
|
|
279
|
+
→ [{"count(*)": 1}]
|
|
280
|
+
|
|
281
|
+
select count(*) from main.test_trailing;
|
|
282
|
+
→ [{"count(*)": 0}]
|
|
283
|
+
|
|
284
|
+
-- Step 26: Test views in declarative schema
|
|
285
|
+
declare schema main {
|
|
286
|
+
table users {
|
|
287
|
+
id INTEGER PRIMARY KEY,
|
|
288
|
+
name TEXT NOT NULL,
|
|
289
|
+
email TEXT,
|
|
290
|
+
active INTEGER DEFAULT 1
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
table posts {
|
|
294
|
+
id INTEGER PRIMARY KEY,
|
|
295
|
+
user_id INTEGER NOT NULL,
|
|
296
|
+
title TEXT NOT NULL,
|
|
297
|
+
content TEXT,
|
|
298
|
+
constraint fk_user foreign key (user_id) references users(id)
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
view active_users AS select id, name, email from users where active = 1
|
|
302
|
+
|
|
303
|
+
view user_posts AS select
|
|
304
|
+
u.name as author,
|
|
305
|
+
p.title,
|
|
306
|
+
p.content
|
|
307
|
+
from users u
|
|
308
|
+
join posts p on u.id = p.user_id
|
|
309
|
+
where u.active = 1
|
|
310
|
+
|
|
311
|
+
seed users (
|
|
312
|
+
(1, 'Alice', 'alice@example.com', 1),
|
|
313
|
+
(2, 'Bob', 'bob@example.com', 1),
|
|
314
|
+
(3, 'Charlie', 'charlie@example.com', 0)
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
seed posts (
|
|
318
|
+
(1, 1, 'First Post', 'Hello world'),
|
|
319
|
+
(2, 2, 'Second Post', 'Another post'),
|
|
320
|
+
(3, 3, 'Inactive Post', 'This should not appear in view')
|
|
321
|
+
)
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
-- run
|
|
325
|
+
|
|
326
|
+
-- Step 27: Diff should show tables and views to create
|
|
327
|
+
diff schema main;
|
|
328
|
+
→ [{"ddl": "DROP TABLE IF EXISTS test_trailing"}, {"ddl": "create table users (id INTEGER primary key, name TEXT not null, email TEXT, active INTEGER default 1)"}, {"ddl": "create table posts (id INTEGER primary key, user_id INTEGER not null, title TEXT not null, content TEXT, constraint fk_user foreign key (user_id) references users(id))"}, {"ddl": "create view active_users as select id, name, email from users where active = 1"}, {"ddl": "create view user_posts as select u.name as author, p.title, p.content from users as u inner join posts as p on u.id = p.user_id where u.active = 1"}]
|
|
329
|
+
|
|
330
|
+
-- Step 28: Apply schema with views
|
|
331
|
+
apply schema main with seed;
|
|
332
|
+
|
|
333
|
+
-- run
|
|
334
|
+
|
|
335
|
+
-- Step 29: Test views work correctly
|
|
336
|
+
select * from active_users order by id;
|
|
337
|
+
→ [{"id": 1, "name": "Alice", "email": "alice@example.com"}, {"id": 2, "name": "Bob", "email": "bob@example.com"}]
|
|
338
|
+
|
|
339
|
+
select * from user_posts order by author;
|
|
340
|
+
→ [{"author": "Alice", "title": "First Post", "content": "Hello world"}, {"author": "Bob", "title": "Second Post", "content": "Another post"}]
|
|
341
|
+
|
|
342
|
+
-- Step 30: Test view with explicit column names
|
|
343
|
+
declare schema main {
|
|
344
|
+
table users {
|
|
345
|
+
id INTEGER PRIMARY KEY,
|
|
346
|
+
name TEXT NOT NULL,
|
|
347
|
+
email TEXT,
|
|
348
|
+
active INTEGER DEFAULT 1
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
view user_summary (user_id, full_name, contact) AS select id, name, email from users where active = 1
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
-- run
|
|
355
|
+
|
|
356
|
+
-- Step 31: Diff should show view changes (drop old views, create new one)
|
|
357
|
+
diff schema main;
|
|
358
|
+
→ [{"ddl": "DROP TABLE IF EXISTS posts"}, {"ddl": "DROP VIEW IF EXISTS active_users"}, {"ddl": "DROP VIEW IF EXISTS user_posts"}, {"ddl": "create view user_summary (user_id, full_name, contact) as select id, name, email from users where active = 1"}]
|
|
359
|
+
|
|
360
|
+
-- Step 32: Apply view changes
|
|
361
|
+
apply schema main;
|
|
362
|
+
|
|
363
|
+
-- run
|
|
364
|
+
|
|
365
|
+
-- Step 33: Test new view with explicit columns
|
|
366
|
+
select * from user_summary order by user_id;
|
|
367
|
+
→ [{"user_id": 1, "full_name": "Alice", "contact": "alice@example.com"}, {"user_id": 2, "full_name": "Bob", "contact": "bob@example.com"}]
|
|
368
|
+
|
|
369
|
+
-- Step 33.1: Declarative apply rejects a conflicting-collation FK. The differ emits
|
|
370
|
+
-- CREATE TABLE in dependency order (parent before child), so the child's CREATE
|
|
371
|
+
-- re-executes through the same declaration-time collation validation as a direct
|
|
372
|
+
-- CREATE TABLE and the apply fails. Isolated in schema Test3 so `main` is
|
|
373
|
+
-- untouched. Ticket fk-collation-conflict-create-time-validation.
|
|
374
|
+
declare schema Test3 {
|
|
375
|
+
table cfk_parent {
|
|
376
|
+
k TEXT COLLATE RTRIM PRIMARY KEY
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
table cfk_child {
|
|
380
|
+
id INTEGER PRIMARY KEY,
|
|
381
|
+
ref TEXT COLLATE NOCASE,
|
|
382
|
+
constraint fk_bad foreign key (ref) references cfk_parent(k)
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
-- run
|
|
387
|
+
|
|
388
|
+
apply schema Test3;
|
|
389
|
+
-- error: conflicting collations
|
|
390
|
+
|
|
391
|
+
-- Step 34: Test view removal
|
|
392
|
+
declare schema main {
|
|
393
|
+
table users {
|
|
394
|
+
id INTEGER PRIMARY KEY,
|
|
395
|
+
name TEXT NOT NULL,
|
|
396
|
+
email TEXT,
|
|
397
|
+
active INTEGER DEFAULT 1
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
-- run
|
|
402
|
+
|
|
403
|
+
-- Step 35: Diff should show view to drop
|
|
404
|
+
diff schema main;
|
|
405
|
+
→ [{"ddl": "DROP VIEW IF EXISTS user_summary"}]
|
|
406
|
+
|
|
407
|
+
-- Step 36: Apply to remove view
|
|
408
|
+
apply schema main;
|
|
409
|
+
|
|
410
|
+
-- run
|
|
411
|
+
|
|
412
|
+
-- Step 37: Verify view is gone
|
|
413
|
+
select count(*) from user_summary;
|
|
414
|
+
-- error: not found in schema path:
|
|
415
|
+
|
|
416
|
+
-- Step 38: Test indexes in declarative schema
|
|
417
|
+
-- Note: Using a unique table name to avoid DROP TABLE bug where data persists
|
|
418
|
+
declare schema main {
|
|
419
|
+
table products_with_indexes {
|
|
420
|
+
id INTEGER PRIMARY KEY,
|
|
421
|
+
name TEXT NOT NULL,
|
|
422
|
+
category TEXT,
|
|
423
|
+
price REAL
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
index idx_products_name on products_with_indexes(name)
|
|
427
|
+
index idx_products_category on products_with_indexes(category, price)
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
-- run
|
|
431
|
+
|
|
432
|
+
-- Step 39: Diff should show table and indexes to create
|
|
433
|
+
diff schema main;
|
|
434
|
+
→ [{"ddl": "DROP TABLE IF EXISTS users"}, {"ddl": "create table products_with_indexes (id INTEGER primary key, name TEXT not null, category TEXT, price REAL)"}, {"ddl": "create index idx_products_name on products_with_indexes (name)"}, {"ddl": "create index idx_products_category on products_with_indexes (category, price)"}]
|
|
435
|
+
|
|
436
|
+
-- Step 40: Apply schema with indexes
|
|
437
|
+
apply schema main;
|
|
438
|
+
|
|
439
|
+
-- run
|
|
440
|
+
|
|
441
|
+
-- Step 41: Verify table was created
|
|
442
|
+
select count(*) from products_with_indexes;
|
|
443
|
+
→ [{"count(*)": 0}]
|
|
444
|
+
|
|
445
|
+
-- Step 42: Insert test data
|
|
446
|
+
insert into products_with_indexes values (1, 'Laptop', 'Electronics', 999.99);
|
|
447
|
+
insert into products_with_indexes values (2, 'Book', 'Books', 29.99);
|
|
448
|
+
insert into products_with_indexes values (3, 'Shirt', 'Clothing', 19.99);
|
|
449
|
+
|
|
450
|
+
-- Step 43: Verify data
|
|
451
|
+
select * from products_with_indexes order by name;
|
|
452
|
+
→ [{"id": 2, "name": "Book", "category": "Books", "price": 29.99}, {"id": 1, "name": "Laptop", "category": "Electronics", "price": 999.99}, {"id": 3, "name": "Shirt", "category": "Clothing", "price": 19.99}]
|
|
453
|
+
|
|
454
|
+
-- Step 44: Diff again - should be empty since schema matches
|
|
455
|
+
diff schema main;
|
|
456
|
+
→ []
|
|
457
|
+
|
|
458
|
+
-- Step 45: Test index removal
|
|
459
|
+
declare schema main {
|
|
460
|
+
table products_with_indexes {
|
|
461
|
+
id INTEGER PRIMARY KEY,
|
|
462
|
+
name TEXT NOT NULL,
|
|
463
|
+
category TEXT,
|
|
464
|
+
price REAL
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
index idx_products_name on products_with_indexes(name)
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
-- run
|
|
471
|
+
|
|
472
|
+
-- Step 46: Diff should show one index to drop
|
|
473
|
+
diff schema main;
|
|
474
|
+
→ [{"ddl": "DROP INDEX IF EXISTS idx_products_category"}]
|
|
475
|
+
|
|
476
|
+
-- Step 47: Apply to remove index
|
|
477
|
+
apply schema main;
|
|
478
|
+
|
|
479
|
+
-- run
|
|
480
|
+
|
|
481
|
+
-- Step 48: Diff again - should be empty
|
|
482
|
+
diff schema main;
|
|
483
|
+
|
|
484
|
+
-- Test NOT EXISTS in CHECK constraint (regression test for operator spacing)
|
|
485
|
+
-- This tests that "NOT EXISTS" is properly stringified with a space between NOT and EXISTS
|
|
486
|
+
-- The bug was that "not exists" was being concatenated as "notexists" without a space
|
|
487
|
+
|
|
488
|
+
declare schema check_not_exists {
|
|
489
|
+
table B (x int);
|
|
490
|
+
table A (
|
|
491
|
+
x int check (not exists(select 1 from B))
|
|
492
|
+
)
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
-- run
|
|
496
|
+
|
|
497
|
+
-- The key test: DDL should have "not exists" with a space, not "notexists"
|
|
498
|
+
diff schema check_not_exists;
|
|
499
|
+
→ [{"ddl": "create table check_not_exists.B (x int)"}, {"ddl": "create table check_not_exists.A (x int check (not exists (select 1 from B)))"}]
|
|
500
|
+
|
|
501
|
+
-- This should apply without parse errors (would fail with "notexists" syntax error)
|
|
502
|
+
apply schema check_not_exists;
|
|
503
|
+
|
|
504
|
+
-- run
|
|
505
|
+
|
|
506
|
+
-- Step 49: Test schema-level default vtab module
|
|
507
|
+
-- When using (default_vtab_module = 'memory') is specified, tables without explicit USING
|
|
508
|
+
-- should inherit the schema-level default module
|
|
509
|
+
declare schema test_default_module using (default_vtab_module = 'memory') {
|
|
510
|
+
table items {
|
|
511
|
+
id INTEGER PRIMARY KEY,
|
|
512
|
+
name TEXT NOT NULL
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
-- run
|
|
517
|
+
|
|
518
|
+
-- Diff should show the table with USING memory clause applied
|
|
519
|
+
diff schema test_default_module;
|
|
520
|
+
→ [{"ddl": "create table test_default_module.items (id INTEGER primary key, name TEXT not null) using memory"}]
|
|
521
|
+
|
|
522
|
+
-- Apply the schema
|
|
523
|
+
apply schema test_default_module;
|
|
524
|
+
|
|
525
|
+
-- run
|
|
526
|
+
|
|
527
|
+
-- Verify table works
|
|
528
|
+
insert into test_default_module.items values (1, 'Test Item');
|
|
529
|
+
select * from test_default_module.items;
|
|
530
|
+
→ [{"id": 1, "name": "Test Item"}]
|
|
531
|
+
|
|
532
|
+
-- Step 50: Test that explicit USING clause takes precedence over schema default
|
|
533
|
+
declare schema test_explicit_module using (default_vtab_module = 'memory') {
|
|
534
|
+
table default_table {
|
|
535
|
+
id INTEGER PRIMARY KEY,
|
|
536
|
+
name TEXT
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
table explicit_table using memory {
|
|
540
|
+
id INTEGER PRIMARY KEY,
|
|
541
|
+
value TEXT
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
-- run
|
|
546
|
+
|
|
547
|
+
-- Both tables should use memory module (one from default, one explicit)
|
|
548
|
+
diff schema test_explicit_module;
|
|
549
|
+
→ [{"ddl": "create table test_explicit_module.default_table (id INTEGER primary key, name TEXT) using memory"}, {"ddl": "create table test_explicit_module.explicit_table (id INTEGER primary key, value TEXT) using memory"}]
|
|
550
|
+
|
|
551
|
+
apply schema test_explicit_module;
|
|
552
|
+
|
|
553
|
+
-- run
|
|
554
|
+
|
|
555
|
+
-- Verify both tables work
|
|
556
|
+
insert into test_explicit_module.default_table values (1, 'Default');
|
|
557
|
+
insert into test_explicit_module.explicit_table values (1, 'Explicit');
|
|
558
|
+
select dt.name, et.value from test_explicit_module.default_table as dt cross join test_explicit_module.explicit_table as et;
|
|
559
|
+
→ [{"name": "Default", "value": "Explicit"}]
|
|
560
|
+
|
|
561
|
+
-- Step 51: Test WITH CONTEXT in declared table
|
|
562
|
+
-- This tests that mutation context variables are properly parsed for tables inside declare schema blocks
|
|
563
|
+
declare schema test_mutation_context using (default_vtab_module = 'memory') {
|
|
564
|
+
table secure_items (
|
|
565
|
+
id INTEGER PRIMARY KEY,
|
|
566
|
+
value TEXT,
|
|
567
|
+
CONSTRAINT auth_check CHECK (context.allowed = 1)
|
|
568
|
+
) with context (allowed INTEGER)
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
-- run
|
|
572
|
+
|
|
573
|
+
-- Diff should include the WITH CONTEXT clause
|
|
574
|
+
diff schema test_mutation_context;
|
|
575
|
+
→ [{"ddl": "create table test_mutation_context.secure_items (id INTEGER primary key, value TEXT, constraint auth_check check (context.allowed = 1)) using memory with context (allowed INTEGER)"}]
|
|
576
|
+
|
|
577
|
+
-- Apply the schema
|
|
578
|
+
apply schema test_mutation_context;
|
|
579
|
+
|
|
580
|
+
-- run
|
|
581
|
+
|
|
582
|
+
-- Insert with allowed = 1 should succeed
|
|
583
|
+
insert into test_mutation_context.secure_items (id, value)
|
|
584
|
+
with context allowed = 1
|
|
585
|
+
values (1, 'test value');
|
|
586
|
+
|
|
587
|
+
select * from test_mutation_context.secure_items;
|
|
588
|
+
→ [{"id": 1, "value": "test value"}]
|
|
589
|
+
|
|
590
|
+
-- Insert with allowed = 0 should fail the CHECK constraint
|
|
591
|
+
insert into test_mutation_context.secure_items (id, value)
|
|
592
|
+
with context allowed = 0
|
|
593
|
+
values (2, 'should fail');
|
|
594
|
+
-- error: CHECK constraint failed
|
|
595
|
+
|
|
596
|
+
-- Verify only the first row exists
|
|
597
|
+
select count(*) from test_mutation_context.secure_items;
|
|
598
|
+
→ [{"count(*)": 1}]
|
|
599
|
+
|
|
600
|
+
-- Step 52: Test WITH CONTEXT with default values in declared table
|
|
601
|
+
declare schema test_context_defaults using (default_vtab_module = 'memory') {
|
|
602
|
+
table orders (
|
|
603
|
+
id INTEGER PRIMARY KEY,
|
|
604
|
+
amount INTEGER DEFAULT base_amount + tax
|
|
605
|
+
) with context (base_amount INTEGER, tax INTEGER)
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
-- run
|
|
609
|
+
|
|
610
|
+
apply schema test_context_defaults;
|
|
611
|
+
|
|
612
|
+
-- run
|
|
613
|
+
|
|
614
|
+
-- Insert using context for defaults
|
|
615
|
+
insert into test_context_defaults.orders (id)
|
|
616
|
+
with context base_amount = 100, tax = 15
|
|
617
|
+
values (1);
|
|
618
|
+
|
|
619
|
+
select * from test_context_defaults.orders;
|
|
620
|
+
→ [{"id": 1, "amount": 115}]
|
|
621
|
+
|
|
622
|
+
-- Step 53: Test boolean and blob seed data types
|
|
623
|
+
-- Regression: boolean and Uint8Array values were silently NULLed in seed data interpolation
|
|
624
|
+
declare schema test_seed_types using (default_vtab_module = 'memory') {
|
|
625
|
+
table typed_data {
|
|
626
|
+
id INTEGER PRIMARY KEY,
|
|
627
|
+
flag INTEGER,
|
|
628
|
+
data BLOB
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
seed typed_data (
|
|
632
|
+
(1, true, X'cafebabe'),
|
|
633
|
+
(2, false, X'deadbeef')
|
|
634
|
+
)
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
-- run
|
|
638
|
+
|
|
639
|
+
apply schema test_seed_types with seed;
|
|
640
|
+
|
|
641
|
+
-- run
|
|
642
|
+
|
|
643
|
+
-- Verify boolean values were converted to 0/1 (not NULL)
|
|
644
|
+
select id, flag from test_seed_types.typed_data order by id;
|
|
645
|
+
→ [{"id": 1, "flag": 1}, {"id": 2, "flag": 0}]
|
|
646
|
+
|
|
647
|
+
-- Verify blob values were preserved (not NULL)
|
|
648
|
+
select id, typeof(data), length(data) from test_seed_types.typed_data order by id;
|
|
649
|
+
→ [{"id": 1, "typeof(data)": "blob", "length(data)": 4}, {"id": 2, "typeof(data)": "blob", "length(data)": 4}]
|
|
650
|
+
|
|
651
|
+
-- Step 54: Test assertions in declarative schema (using main since assertions live in main)
|
|
652
|
+
-- First clean up: redeclare main as empty to clear leftover tables
|
|
653
|
+
declare schema main {
|
|
654
|
+
table assert_accounts {
|
|
655
|
+
id INTEGER PRIMARY KEY,
|
|
656
|
+
balance INTEGER NOT NULL
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
assertion positive_balance check (not exists (select 1 from assert_accounts where balance < 0))
|
|
660
|
+
|
|
661
|
+
seed assert_accounts (
|
|
662
|
+
(1, 100),
|
|
663
|
+
(2, 200)
|
|
664
|
+
)
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
-- run
|
|
668
|
+
|
|
669
|
+
-- Step 55: Diff should show drops for old main objects plus CREATE TABLE and CREATE ASSERTION
|
|
670
|
+
diff schema main;
|
|
671
|
+
→ [{"ddl": "DROP TABLE IF EXISTS products_with_indexes"}, {"ddl": "DROP INDEX IF EXISTS idx_products_name"}, {"ddl": "create table assert_accounts (id INTEGER primary key, balance INTEGER not null)"}, {"ddl": "create assertion positive_balance check (not exists (select 1 from assert_accounts where balance < 0))"}]
|
|
672
|
+
|
|
673
|
+
-- Step 56: Apply creates the table and assertion
|
|
674
|
+
apply schema main with seed;
|
|
675
|
+
|
|
676
|
+
-- run
|
|
677
|
+
|
|
678
|
+
-- Step 57: Verify seed data
|
|
679
|
+
select * from assert_accounts order by id;
|
|
680
|
+
→ [{"id": 1, "balance": 100}, {"id": 2, "balance": 200}]
|
|
681
|
+
|
|
682
|
+
-- Step 58: Diff should be empty now
|
|
683
|
+
diff schema main;
|
|
684
|
+
→ []
|
|
685
|
+
|
|
686
|
+
-- Step 59: Assertion is enforced — commit with violation fails
|
|
687
|
+
begin;
|
|
688
|
+
insert into assert_accounts values (3, -50);
|
|
689
|
+
commit;
|
|
690
|
+
-- error: Integrity assertion failed: positive_balance
|
|
691
|
+
|
|
692
|
+
-- Step 60: Verify state unchanged
|
|
693
|
+
select count(*) from assert_accounts;
|
|
694
|
+
→ [{"count(*)": 2}]
|
|
695
|
+
|
|
696
|
+
-- Step 61: Valid insert works
|
|
697
|
+
begin;
|
|
698
|
+
insert into assert_accounts values (3, 50);
|
|
699
|
+
commit;
|
|
700
|
+
|
|
701
|
+
-- run
|
|
702
|
+
|
|
703
|
+
select count(*) from assert_accounts;
|
|
704
|
+
→ [{"count(*)": 3}]
|
|
705
|
+
|
|
706
|
+
-- Step 62: Redeclare schema without the assertion — diff shows DROP ASSERTION
|
|
707
|
+
declare schema main {
|
|
708
|
+
table assert_accounts {
|
|
709
|
+
id INTEGER PRIMARY KEY,
|
|
710
|
+
balance INTEGER NOT NULL
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
-- run
|
|
715
|
+
|
|
716
|
+
diff schema main;
|
|
717
|
+
→ [{"ddl": "DROP ASSERTION IF EXISTS positive_balance"}]
|
|
718
|
+
|
|
719
|
+
-- Step 63: Apply removes the assertion
|
|
720
|
+
apply schema main;
|
|
721
|
+
|
|
722
|
+
-- run
|
|
723
|
+
|
|
724
|
+
-- Step 64: Violation no longer fails (assertion removed)
|
|
725
|
+
begin;
|
|
726
|
+
insert into assert_accounts values (4, -99);
|
|
727
|
+
commit;
|
|
728
|
+
|
|
729
|
+
-- run
|
|
730
|
+
|
|
731
|
+
select count(*) from assert_accounts;
|
|
732
|
+
→ [{"count(*)": 4}]
|
|
733
|
+
|
|
734
|
+
-- Step 65: Multiple assertions in one declaration
|
|
735
|
+
declare schema main {
|
|
736
|
+
table assert_items {
|
|
737
|
+
id INTEGER PRIMARY KEY,
|
|
738
|
+
qty INTEGER NOT NULL,
|
|
739
|
+
price INTEGER NOT NULL
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
assertion qty_positive check (not exists (select 1 from assert_items where qty < 0))
|
|
743
|
+
assertion price_positive check (not exists (select 1 from assert_items where price < 0))
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
-- run
|
|
747
|
+
|
|
748
|
+
diff schema main;
|
|
749
|
+
→ [{"ddl": "DROP TABLE IF EXISTS assert_accounts"}, {"ddl": "create table assert_items (id INTEGER primary key, qty INTEGER not null, price INTEGER not null)"}, {"ddl": "create assertion qty_positive check (not exists (select 1 from assert_items where qty < 0))"}, {"ddl": "create assertion price_positive check (not exists (select 1 from assert_items where price < 0))"}]
|
|
750
|
+
|
|
751
|
+
apply schema main;
|
|
752
|
+
|
|
753
|
+
-- run
|
|
754
|
+
|
|
755
|
+
-- Both assertions enforced
|
|
756
|
+
begin;
|
|
757
|
+
insert into assert_items values (1, -1, 10);
|
|
758
|
+
commit;
|
|
759
|
+
-- error: Integrity assertion failed: qty_positive
|
|
760
|
+
|
|
761
|
+
begin;
|
|
762
|
+
insert into assert_items values (1, 1, -10);
|
|
763
|
+
commit;
|
|
764
|
+
-- error: Integrity assertion failed: price_positive
|
|
765
|
+
|
|
766
|
+
-- Valid insert
|
|
767
|
+
begin;
|
|
768
|
+
insert into assert_items values (1, 5, 10);
|
|
769
|
+
commit;
|
|
770
|
+
|
|
771
|
+
-- run
|
|
772
|
+
|
|
773
|
+
select * from assert_items;
|
|
774
|
+
→ [{"id": 1, "qty": 5, "price": 10}]
|
|
775
|
+
|
|
776
|
+
-- ===================================================================
|
|
777
|
+
-- Redeclaring an assertion's CHECK body must be visible to `diff` / `apply` —
|
|
778
|
+
-- regression for bug-assertion-body-drift-invisible-to-diff. Uses its own
|
|
779
|
+
-- schema so it does not disturb the assert_accounts / assert_items row
|
|
780
|
+
-- counts asserted above.
|
|
781
|
+
-- ===================================================================
|
|
782
|
+
|
|
783
|
+
declare schema assert_drift {
|
|
784
|
+
table t (
|
|
785
|
+
x INTEGER PRIMARY KEY
|
|
786
|
+
)
|
|
787
|
+
|
|
788
|
+
assertion range_check check (not exists (select 1 from t where x < 0))
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
-- run
|
|
792
|
+
|
|
793
|
+
apply schema assert_drift;
|
|
794
|
+
|
|
795
|
+
-- run
|
|
796
|
+
|
|
797
|
+
diff schema assert_drift;
|
|
798
|
+
→ []
|
|
799
|
+
|
|
800
|
+
-- Redeclare with a changed CHECK body (forbid x < 0 → forbid x < -100). The
|
|
801
|
+
-- diff must show a drop+recreate pair for the drifted assertion, not [].
|
|
802
|
+
declare schema assert_drift {
|
|
803
|
+
table t (
|
|
804
|
+
x INTEGER PRIMARY KEY
|
|
805
|
+
)
|
|
806
|
+
|
|
807
|
+
assertion range_check check (not exists (select 1 from t where x < -100))
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
-- run
|
|
811
|
+
|
|
812
|
+
diff schema assert_drift;
|
|
813
|
+
→ [{"ddl": "DROP ASSERTION IF EXISTS assert_drift.range_check"}, {"ddl": "create assertion assert_drift.range_check check (not exists (select 1 from t where x < -100))"}]
|
|
814
|
+
|
|
815
|
+
apply schema assert_drift;
|
|
816
|
+
|
|
817
|
+
-- run
|
|
818
|
+
|
|
819
|
+
-- Idempotent: re-diff is empty.
|
|
820
|
+
diff schema assert_drift;
|
|
821
|
+
→ []
|
|
822
|
+
|
|
823
|
+
-- A row illegal under the OLD rule (x < 0) but legal under the NEW rule
|
|
824
|
+
-- (only x < -100 is forbidden) commits — the new rule is the one enforced.
|
|
825
|
+
begin;
|
|
826
|
+
insert into assert_drift.t values (-50);
|
|
827
|
+
commit;
|
|
828
|
+
|
|
829
|
+
-- run
|
|
830
|
+
|
|
831
|
+
select * from assert_drift.t;
|
|
832
|
+
→ [{"x": -50}]
|
|
833
|
+
|
|
834
|
+
-- A row illegal under the NEW rule fails at commit.
|
|
835
|
+
begin;
|
|
836
|
+
insert into assert_drift.t values (-150);
|
|
837
|
+
commit;
|
|
838
|
+
-- error: Integrity assertion failed: assert_drift.range_check
|
|
839
|
+
|
|
840
|
+
select count(*) as cnt from assert_drift.t;
|
|
841
|
+
→ [{"cnt": 1}]
|
|
842
|
+
|
|
843
|
+
-- ===================================================================
|
|
844
|
+
-- Drift interacting with a same-apply ADD COLUMN: the recreated assertion names
|
|
845
|
+
-- a column the same migration adds. `create assertion` plans its body at create
|
|
846
|
+
-- time (same strictness as a view body), so the create MUST run after the
|
|
847
|
+
-- table-alter block — assertion creates are emitted last, after every ADD COLUMN
|
|
848
|
+
-- and maintained re-attach. Assertion DROPs still lead the whole migration.
|
|
849
|
+
-- ===================================================================
|
|
850
|
+
|
|
851
|
+
declare schema assert_addcol {
|
|
852
|
+
table t (
|
|
853
|
+
x INTEGER PRIMARY KEY
|
|
854
|
+
)
|
|
855
|
+
|
|
856
|
+
assertion range_check check (not exists (select 1 from t where x < 0))
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
-- run
|
|
860
|
+
|
|
861
|
+
apply schema assert_addcol;
|
|
862
|
+
|
|
863
|
+
-- run
|
|
864
|
+
|
|
865
|
+
declare schema assert_addcol {
|
|
866
|
+
table t (
|
|
867
|
+
x INTEGER PRIMARY KEY,
|
|
868
|
+
y INTEGER
|
|
869
|
+
)
|
|
870
|
+
|
|
871
|
+
assertion range_check check (not exists (select 1 from t where y < 0))
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
-- run
|
|
875
|
+
|
|
876
|
+
diff schema assert_addcol;
|
|
877
|
+
→ [{"ddl": "DROP ASSERTION IF EXISTS assert_addcol.range_check"}, {"ddl": "ALTER TABLE assert_addcol.t ADD COLUMN y INTEGER"}, {"ddl": "create assertion assert_addcol.range_check check (not exists (select 1 from t where y < 0))"}]
|
|
878
|
+
|
|
879
|
+
apply schema assert_addcol;
|
|
880
|
+
|
|
881
|
+
-- run
|
|
882
|
+
|
|
883
|
+
diff schema assert_addcol;
|
|
884
|
+
→ []
|
|
885
|
+
|
|
886
|
+
-- The recreated rule enforces against the freshly added column.
|
|
887
|
+
begin;
|
|
888
|
+
insert into assert_addcol.t values (1, -5);
|
|
889
|
+
commit;
|
|
890
|
+
-- error: Integrity assertion failed: assert_addcol.range_check
|
|
891
|
+
|
|
892
|
+
begin;
|
|
893
|
+
insert into assert_addcol.t values (2, 5);
|
|
894
|
+
commit;
|
|
895
|
+
|
|
896
|
+
-- run
|
|
897
|
+
|
|
898
|
+
select * from assert_addcol.t;
|
|
899
|
+
→ [{"x": 2, "y": 5}]
|
|
900
|
+
|
|
901
|
+
-- ===================================================================
|
|
902
|
+
-- Table rename in the same apply as an assertion whose body follows the new
|
|
903
|
+
-- name. Assertion CHECK bodies are never rename-reconciled by the differ, so
|
|
904
|
+
-- the unchanged-in-spirit assertion reads as drifted and churns a
|
|
905
|
+
-- drop+recreate. That is correct-but-avoidable, and it must still APPLY
|
|
906
|
+
-- cleanly: renames are emitted before assertion drops/creates.
|
|
907
|
+
-- ===================================================================
|
|
908
|
+
|
|
909
|
+
declare schema assert_rename {
|
|
910
|
+
table t (
|
|
911
|
+
x INTEGER PRIMARY KEY
|
|
912
|
+
)
|
|
913
|
+
|
|
914
|
+
assertion range_check check (not exists (select 1 from t where x < 0))
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
-- run
|
|
918
|
+
|
|
919
|
+
apply schema assert_rename;
|
|
920
|
+
|
|
921
|
+
-- run
|
|
922
|
+
|
|
923
|
+
declare schema assert_rename {
|
|
924
|
+
table t2 (
|
|
925
|
+
x INTEGER PRIMARY KEY
|
|
926
|
+
) with tags ("quereus.previous_name" = 't')
|
|
927
|
+
|
|
928
|
+
assertion range_check check (not exists (select 1 from t2 where x < 0))
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
-- run
|
|
932
|
+
|
|
933
|
+
diff schema assert_rename;
|
|
934
|
+
→ [{"ddl": "ALTER TABLE assert_rename.t RENAME TO t2"}, {"ddl": "DROP ASSERTION IF EXISTS assert_rename.range_check"}, {"ddl": "create assertion assert_rename.range_check check (not exists (select 1 from t2 where x < 0))"}]
|
|
935
|
+
|
|
936
|
+
apply schema assert_rename;
|
|
937
|
+
|
|
938
|
+
-- run
|
|
939
|
+
|
|
940
|
+
diff schema assert_rename;
|
|
941
|
+
→ []
|
|
942
|
+
|
|
943
|
+
-- The rebound assertion enforces against the renamed table.
|
|
944
|
+
begin;
|
|
945
|
+
insert into assert_rename.t2 values (-1);
|
|
946
|
+
commit;
|
|
947
|
+
-- error: Integrity assertion failed: assert_rename.range_check
|
|
948
|
+
|
|
949
|
+
-- ===================================================================
|
|
950
|
+
-- Declarative `unique index` — parser admits the UNIQUE qualifier and
|
|
951
|
+
-- the diff stringifies it back as `create unique index` so that the
|
|
952
|
+
-- apply path enforces uniqueness the same way a top-level
|
|
953
|
+
-- `CREATE UNIQUE INDEX` does.
|
|
954
|
+
-- ===================================================================
|
|
955
|
+
|
|
956
|
+
declare schema decl_uniq {
|
|
957
|
+
table T (
|
|
958
|
+
id INTEGER PRIMARY KEY,
|
|
959
|
+
email TEXT NOT NULL,
|
|
960
|
+
a TEXT,
|
|
961
|
+
b TEXT
|
|
962
|
+
)
|
|
963
|
+
|
|
964
|
+
unique index ix_T_email on T(email)
|
|
965
|
+
unique index ix_T_a_b on T(a, b)
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
-- run
|
|
969
|
+
|
|
970
|
+
-- Diff stringifies the declared indexes with the `unique` qualifier.
|
|
971
|
+
diff schema decl_uniq;
|
|
972
|
+
→ [{"ddl": "create table decl_uniq.T (id INTEGER primary key, email TEXT not null, a TEXT, b TEXT)"}, {"ddl": "create unique index decl_uniq.ix_T_email on decl_uniq.T (email)"}, {"ddl": "create unique index decl_uniq.ix_T_a_b on decl_uniq.T (a, b)"}]
|
|
973
|
+
|
|
974
|
+
apply schema decl_uniq;
|
|
975
|
+
|
|
976
|
+
-- run
|
|
977
|
+
|
|
978
|
+
-- Apply was idempotent; re-diff is empty.
|
|
979
|
+
diff schema decl_uniq;
|
|
980
|
+
→ []
|
|
981
|
+
|
|
982
|
+
-- Single-column unique enforcement.
|
|
983
|
+
insert into decl_uniq.T values (1, 'x@y', 'p', 'q');
|
|
984
|
+
insert into decl_uniq.T values (2, 'x@y', 'r', 's');
|
|
985
|
+
-- error: UNIQUE
|
|
986
|
+
|
|
987
|
+
-- Multi-column unique enforcement (compound key collides on (p,q)).
|
|
988
|
+
insert into decl_uniq.T values (3, 'a@y', 'p', 'q');
|
|
989
|
+
-- error: UNIQUE
|
|
990
|
+
|
|
991
|
+
-- Distinct compound key is accepted.
|
|
992
|
+
insert into decl_uniq.T values (4, 'b@y', 'p', 'r');
|
|
993
|
+
|
|
994
|
+
select count(*) as cnt from decl_uniq.T;
|
|
995
|
+
→ [{"cnt": 2}]
|
|
996
|
+
|
|
997
|
+
-- Regression for github.com/gotchoices/quereus/issues/22 — prefix NOT against
|
|
998
|
+
-- a subquery predicate inside CHECK, through the declare/apply round-trip.
|
|
999
|
+
-- This exercises stringify → re-parse and must produce a semantically
|
|
1000
|
+
-- equivalent CHECK constraint (not one that rejects every row).
|
|
1001
|
+
declare schema decl_not_in
|
|
1002
|
+
{
|
|
1003
|
+
table Block (Code text primary key);
|
|
1004
|
+
table TIn (
|
|
1005
|
+
Id int, Color text,
|
|
1006
|
+
primary key (Id),
|
|
1007
|
+
constraint NoBlocked check (Color not in (select Code from Block))
|
|
1008
|
+
);
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
-- run
|
|
1012
|
+
|
|
1013
|
+
apply schema decl_not_in;
|
|
1014
|
+
|
|
1015
|
+
-- run
|
|
1016
|
+
|
|
1017
|
+
insert into decl_not_in.Block (Code) values ('r');
|
|
1018
|
+
insert into decl_not_in.Block (Code) values ('y');
|
|
1019
|
+
insert into decl_not_in.TIn (Id, Color) values (1, 'g');
|
|
1020
|
+
select Color from decl_not_in.TIn;
|
|
1021
|
+
→ [{"Color":"g"}]
|
|
1022
|
+
|
|
1023
|
+
insert into decl_not_in.TIn (Id, Color) values (2, 'r');
|
|
1024
|
+
-- error: CHECK constraint failed: NoBlocked
|
|
1025
|
+
|
|
1026
|
+
drop table decl_not_in.TIn;
|
|
1027
|
+
drop table decl_not_in.Block;
|
|
1028
|
+
|
|
1029
|
+
-- ===================================================================
|
|
1030
|
+
-- Regression for github.com/gotchoices/quereus/issues/23 — CHECK
|
|
1031
|
+
-- `on delete` operations clause survives the declare/apply round-trip.
|
|
1032
|
+
-- The stringifier used to drop the `operations` list, so the constraint
|
|
1033
|
+
-- defaulted to the all-ops mask (INSERT+UPDATE+DELETE) and fired on
|
|
1034
|
+
-- INSERT instead of only on DELETE.
|
|
1035
|
+
-- ===================================================================
|
|
1036
|
+
|
|
1037
|
+
declare schema decl_check_ops
|
|
1038
|
+
{
|
|
1039
|
+
table NoDelete (
|
|
1040
|
+
Id int,
|
|
1041
|
+
primary key (Id),
|
|
1042
|
+
constraint NoDeleteEver check on delete (false)
|
|
1043
|
+
);
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
-- run
|
|
1047
|
+
|
|
1048
|
+
apply schema decl_check_ops;
|
|
1049
|
+
|
|
1050
|
+
-- run
|
|
1051
|
+
|
|
1052
|
+
-- INSERT must succeed (operations list excludes insert).
|
|
1053
|
+
insert into decl_check_ops.NoDelete (Id) values (1);
|
|
1054
|
+
select Id from decl_check_ops.NoDelete;
|
|
1055
|
+
→ [{"Id":1}]
|
|
1056
|
+
|
|
1057
|
+
-- DELETE must fail.
|
|
1058
|
+
delete from decl_check_ops.NoDelete where Id = 1;
|
|
1059
|
+
-- error: CHECK constraint failed: NoDeleteEver
|
|
1060
|
+
|
|
1061
|
+
drop table decl_check_ops.NoDelete;
|
|
1062
|
+
|
|
1063
|
+
-- CHECK `on update` (the other half of the default-mask, distinct from
|
|
1064
|
+
-- the on-delete case so a mistakenly-defaulted mask still surfaces).
|
|
1065
|
+
declare schema decl_check_upd
|
|
1066
|
+
{
|
|
1067
|
+
table OnUpdOnly (
|
|
1068
|
+
Id int,
|
|
1069
|
+
Val int,
|
|
1070
|
+
primary key (Id),
|
|
1071
|
+
constraint NoNegOnUpdate check on update (new.Val >= 0)
|
|
1072
|
+
);
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
-- run
|
|
1076
|
+
|
|
1077
|
+
apply schema decl_check_upd;
|
|
1078
|
+
|
|
1079
|
+
-- run
|
|
1080
|
+
|
|
1081
|
+
-- INSERT must succeed regardless of the bad value (constraint excludes insert).
|
|
1082
|
+
insert into decl_check_upd.OnUpdOnly (Id, Val) values (1, -5);
|
|
1083
|
+
select Val from decl_check_upd.OnUpdOnly;
|
|
1084
|
+
→ [{"Val":-5}]
|
|
1085
|
+
|
|
1086
|
+
-- UPDATE must fail when the new value violates the constraint.
|
|
1087
|
+
update decl_check_upd.OnUpdOnly set Val = -1 where Id = 1;
|
|
1088
|
+
-- error: CHECK constraint failed: NoNegOnUpdate
|
|
1089
|
+
|
|
1090
|
+
-- UPDATE that satisfies the constraint succeeds.
|
|
1091
|
+
update decl_check_upd.OnUpdOnly set Val = 7 where Id = 1;
|
|
1092
|
+
select Val from decl_check_upd.OnUpdOnly;
|
|
1093
|
+
→ [{"Val":7}]
|
|
1094
|
+
|
|
1095
|
+
drop table decl_check_upd.OnUpdOnly;
|
|
1096
|
+
|
|
1097
|
+
-- ===================================================================
|
|
1098
|
+
-- Regression for github.com/gotchoices/quereus/issues/21 — compound
|
|
1099
|
+
-- SELECT tail survives the declare/apply round-trip. The stringifier
|
|
1100
|
+
-- read `stmt.union` / `stmt.unionAll` (dead fields the parser never
|
|
1101
|
+
-- populates) instead of `stmt.compound`, silently dropping every leg
|
|
1102
|
+
-- beyond the first. Views are declared in `main` since declarative
|
|
1103
|
+
-- views in non-main schemas are emitted without a schema prefix (a
|
|
1104
|
+
-- separate concern outside this fix).
|
|
1105
|
+
-- ===================================================================
|
|
1106
|
+
|
|
1107
|
+
declare schema main {
|
|
1108
|
+
view rgb_codes as
|
|
1109
|
+
select 'r' as Code, 'Red' as Name
|
|
1110
|
+
union all select 'g' as Code, 'Green' as Name
|
|
1111
|
+
union all select 'b' as Code, 'Blue' as Name
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
-- run
|
|
1115
|
+
|
|
1116
|
+
apply schema main;
|
|
1117
|
+
|
|
1118
|
+
-- run
|
|
1119
|
+
|
|
1120
|
+
-- All three legs must be present; previously only 'r' survived.
|
|
1121
|
+
select Code from rgb_codes order by Code;
|
|
1122
|
+
→ [{"Code":"b"}, {"Code":"g"}, {"Code":"r"}]
|
|
1123
|
+
|
|
1124
|
+
drop view rgb_codes;
|
|
1125
|
+
|
|
1126
|
+
-- UNION (DISTINCT) — duplicate row collapses.
|
|
1127
|
+
declare schema main {
|
|
1128
|
+
view distinct_union as
|
|
1129
|
+
select 1 as N union select 2 as N union select 1 as N
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
-- run
|
|
1133
|
+
|
|
1134
|
+
apply schema main;
|
|
1135
|
+
|
|
1136
|
+
-- run
|
|
1137
|
+
|
|
1138
|
+
select N from distinct_union order by N;
|
|
1139
|
+
→ [{"N": 1}, {"N": 2}]
|
|
1140
|
+
|
|
1141
|
+
drop view distinct_union;
|
|
1142
|
+
|
|
1143
|
+
-- INTERSECT — only common row survives. (Compound operators are
|
|
1144
|
+
-- right-associative in this parser, so the test uses single-leg sides
|
|
1145
|
+
-- to keep the test focused on the stringifier emitting `intersect`.)
|
|
1146
|
+
declare schema main {
|
|
1147
|
+
view two_only as
|
|
1148
|
+
select 2 as N intersect select 2 as N
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
-- run
|
|
1152
|
+
|
|
1153
|
+
apply schema main;
|
|
1154
|
+
|
|
1155
|
+
-- run
|
|
1156
|
+
|
|
1157
|
+
select N from two_only;
|
|
1158
|
+
→ [{"N": 2}]
|
|
1159
|
+
|
|
1160
|
+
drop view two_only;
|
|
1161
|
+
|
|
1162
|
+
-- EXCEPT — left minus right.
|
|
1163
|
+
declare schema main {
|
|
1164
|
+
view one_only as
|
|
1165
|
+
select 1 as N except select 2 as N
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
-- run
|
|
1169
|
+
|
|
1170
|
+
apply schema main;
|
|
1171
|
+
|
|
1172
|
+
-- run
|
|
1173
|
+
|
|
1174
|
+
select N from one_only;
|
|
1175
|
+
→ [{"N": 1}]
|
|
1176
|
+
|
|
1177
|
+
drop view one_only;
|
|
1178
|
+
|
|
1179
|
+
-- Cross-fix smoke: CHECK whose expression is a compound subquery. Both
|
|
1180
|
+
-- the operations list and the compound tail must survive the round-trip
|
|
1181
|
+
-- for the constraint to evaluate as written.
|
|
1182
|
+
declare schema decl_check_cmpd
|
|
1183
|
+
{
|
|
1184
|
+
table Palette (
|
|
1185
|
+
Color text primary key,
|
|
1186
|
+
constraint InPalette check (Color in (
|
|
1187
|
+
select 'r' as c union all
|
|
1188
|
+
select 'g' union all
|
|
1189
|
+
select 'b'
|
|
1190
|
+
))
|
|
1191
|
+
);
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
-- run
|
|
1195
|
+
|
|
1196
|
+
apply schema decl_check_cmpd;
|
|
1197
|
+
|
|
1198
|
+
-- run
|
|
1199
|
+
|
|
1200
|
+
insert into decl_check_cmpd.Palette values ('g');
|
|
1201
|
+
select Color from decl_check_cmpd.Palette;
|
|
1202
|
+
→ [{"Color":"g"}]
|
|
1203
|
+
|
|
1204
|
+
insert into decl_check_cmpd.Palette values ('z');
|
|
1205
|
+
-- error: CHECK constraint failed: InPalette
|
|
1206
|
+
|
|
1207
|
+
drop table decl_check_cmpd.Palette;
|
|
1208
|
+
|
|
1209
|
+
-- ===================================================================
|
|
1210
|
+
-- Exposed implicit covering index — a UNIQUE constraint tagged
|
|
1211
|
+
-- quereus.expose_implicit_index surfaces its secondary BTree for
|
|
1212
|
+
-- introspection (schema() / index_info()), but the differ must NOT
|
|
1213
|
+
-- manage it as a standalone index. A converged schema diffs EMPTY (no
|
|
1214
|
+
-- phantom DROP INDEX IF EXISTS uq_expo_vin) across both the memory and
|
|
1215
|
+
-- store backends (ticket
|
|
1216
|
+
-- declarative-differ-spurious-drop-exposed-implicit-index).
|
|
1217
|
+
-- ===================================================================
|
|
1218
|
+
|
|
1219
|
+
declare schema decl_expo_idx
|
|
1220
|
+
{
|
|
1221
|
+
table ExpoTbl (
|
|
1222
|
+
id INTEGER PRIMARY KEY,
|
|
1223
|
+
vin TEXT,
|
|
1224
|
+
constraint uq_expo_vin unique (vin) with tags ("quereus.expose_implicit_index" = true)
|
|
1225
|
+
);
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
-- run
|
|
1229
|
+
|
|
1230
|
+
apply schema decl_expo_idx;
|
|
1231
|
+
|
|
1232
|
+
-- run
|
|
1233
|
+
|
|
1234
|
+
-- Converged: re-diff is empty — no spurious DROP INDEX for the exposed
|
|
1235
|
+
-- implicit covering index (was the bug this ticket fixes).
|
|
1236
|
+
diff schema decl_expo_idx;
|
|
1237
|
+
→ []
|
|
1238
|
+
|
|
1239
|
+
-- UNIQUE enforcement still holds (the constraint, not the index, governs it).
|
|
1240
|
+
insert into decl_expo_idx.ExpoTbl values (1, 'VIN001');
|
|
1241
|
+
insert into decl_expo_idx.ExpoTbl values (2, 'VIN001');
|
|
1242
|
+
-- error: UNIQUE
|
|
1243
|
+
|
|
1244
|
+
drop table decl_expo_idx.ExpoTbl;
|
|
1245
|
+
|
|
1246
|
+
-- ===================================================================
|
|
1247
|
+
-- View `with defaults (col = expr, …)` survives the declare/diff/apply
|
|
1248
|
+
-- round-trip: the diff renders the clause in the create DDL, the apply
|
|
1249
|
+
-- re-parses that DDL, and an insert through the applied view supplies the
|
|
1250
|
+
-- defaulted column. Declared in `main` since declarative views in
|
|
1251
|
+
-- non-main schemas are emitted without a schema prefix (see the
|
|
1252
|
+
-- compound-tail blocks above). `main` is empty at this point in the file
|
|
1253
|
+
-- (the last `apply schema main` reconciled it down to a single view,
|
|
1254
|
+
-- since dropped), so the diff is exactly the two creates.
|
|
1255
|
+
-- ===================================================================
|
|
1256
|
+
|
|
1257
|
+
declare schema main {
|
|
1258
|
+
table decl_dfl (
|
|
1259
|
+
id INTEGER PRIMARY KEY,
|
|
1260
|
+
name TEXT,
|
|
1261
|
+
created INTEGER NOT NULL
|
|
1262
|
+
)
|
|
1263
|
+
|
|
1264
|
+
view decl_dfl_v as select id, name from decl_dfl with defaults (created = 424242)
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
-- run
|
|
1268
|
+
|
|
1269
|
+
-- The rendered create DDL carries the clause in canonical form.
|
|
1270
|
+
diff schema main;
|
|
1271
|
+
→ [{"ddl": "create table decl_dfl (id INTEGER primary key, name TEXT, created INTEGER not null)"}, {"ddl": "create view decl_dfl_v as select id, name from decl_dfl with defaults (created = 424242)"}]
|
|
1272
|
+
|
|
1273
|
+
apply schema main;
|
|
1274
|
+
|
|
1275
|
+
-- run
|
|
1276
|
+
|
|
1277
|
+
-- Write-through: the applied view supplies the omitted not-null column.
|
|
1278
|
+
insert into decl_dfl_v values (1, 'x');
|
|
1279
|
+
select * from decl_dfl order by id;
|
|
1280
|
+
→ [{"id":1,"name":"x","created":424242}]
|
|
1281
|
+
|
|
1282
|
+
-- Converged: an identical re-declare diffs empty (declared and actual agree,
|
|
1283
|
+
-- clause included).
|
|
1284
|
+
diff schema main;
|
|
1285
|
+
→ []
|
|
1286
|
+
|
|
1287
|
+
drop view decl_dfl_v;
|
|
1288
|
+
drop table decl_dfl;
|
|
1289
|
+
|
|
1290
|
+
-- ===================================================================
|
|
1291
|
+
-- View definition drift on a NAME-MATCHED view (ticket
|
|
1292
|
+
-- view-insert-defaults-declarative-drift-undetected): a changed /
|
|
1293
|
+
-- removed / added `with defaults` clause and a changed body each
|
|
1294
|
+
-- diff as DROP VIEW + CREATE VIEW. Previously all four diffed EMPTY —
|
|
1295
|
+
-- a stale default survived apply silently. Tag-only drift still takes
|
|
1296
|
+
-- the in-place SET TAGS path (declarative-equivalence unit tests).
|
|
1297
|
+
-- `main` is empty again at this point in the file.
|
|
1298
|
+
-- ===================================================================
|
|
1299
|
+
|
|
1300
|
+
create table vd_t (id integer primary key, name text, created integer not null);
|
|
1301
|
+
create view vd_v as select id, name from vd_t with defaults (created = 111);
|
|
1302
|
+
|
|
1303
|
+
-- run
|
|
1304
|
+
|
|
1305
|
+
-- Baseline: write-through uses the original default.
|
|
1306
|
+
insert into vd_v values (1, 'a');
|
|
1307
|
+
select created from vd_t where id = 1;
|
|
1308
|
+
→ [{"created": 111}]
|
|
1309
|
+
|
|
1310
|
+
-- Clause changed (111 → 222) on the name-matched view.
|
|
1311
|
+
declare schema main {
|
|
1312
|
+
table vd_t (
|
|
1313
|
+
id integer primary key,
|
|
1314
|
+
name text,
|
|
1315
|
+
created integer not null
|
|
1316
|
+
)
|
|
1317
|
+
|
|
1318
|
+
view vd_v as select id, name from vd_t with defaults (created = 222)
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
-- run
|
|
1322
|
+
|
|
1323
|
+
diff schema main;
|
|
1324
|
+
→ [{"ddl": "DROP VIEW IF EXISTS vd_v"}, {"ddl": "create view vd_v as select id, name from vd_t with defaults (created = 222)"}]
|
|
1325
|
+
|
|
1326
|
+
apply schema main;
|
|
1327
|
+
|
|
1328
|
+
-- run
|
|
1329
|
+
|
|
1330
|
+
-- Write-through now uses the NEW default (it kept writing 111 before this fix).
|
|
1331
|
+
insert into vd_v values (2, 'b');
|
|
1332
|
+
select created from vd_t where id = 2;
|
|
1333
|
+
→ [{"created": 222}]
|
|
1334
|
+
|
|
1335
|
+
-- Converged: re-diff is empty.
|
|
1336
|
+
diff schema main;
|
|
1337
|
+
→ []
|
|
1338
|
+
|
|
1339
|
+
-- Clause removed: drift again → drop+recreate; an insert omitting the
|
|
1340
|
+
-- NOT NULL column now fails (nothing supplies it anymore).
|
|
1341
|
+
declare schema main {
|
|
1342
|
+
table vd_t (
|
|
1343
|
+
id integer primary key,
|
|
1344
|
+
name text,
|
|
1345
|
+
created integer not null
|
|
1346
|
+
)
|
|
1347
|
+
|
|
1348
|
+
view vd_v as select id, name from vd_t
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
-- run
|
|
1352
|
+
|
|
1353
|
+
diff schema main;
|
|
1354
|
+
→ [{"ddl": "DROP VIEW IF EXISTS vd_v"}, {"ddl": "create view vd_v as select id, name from vd_t"}]
|
|
1355
|
+
|
|
1356
|
+
apply schema main;
|
|
1357
|
+
|
|
1358
|
+
-- run
|
|
1359
|
+
|
|
1360
|
+
insert into vd_v values (3, 'c');
|
|
1361
|
+
-- error: NOT NULL constraint failed: vd_t.created
|
|
1362
|
+
|
|
1363
|
+
diff schema main;
|
|
1364
|
+
→ []
|
|
1365
|
+
|
|
1366
|
+
-- Clause added (the inverse migration): drift → drop+recreate; the
|
|
1367
|
+
-- defaulted write-through works again with the new value.
|
|
1368
|
+
declare schema main {
|
|
1369
|
+
table vd_t (
|
|
1370
|
+
id integer primary key,
|
|
1371
|
+
name text,
|
|
1372
|
+
created integer not null
|
|
1373
|
+
)
|
|
1374
|
+
|
|
1375
|
+
view vd_v as select id, name from vd_t with defaults (created = 333)
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
-- run
|
|
1379
|
+
|
|
1380
|
+
diff schema main;
|
|
1381
|
+
→ [{"ddl": "DROP VIEW IF EXISTS vd_v"}, {"ddl": "create view vd_v as select id, name from vd_t with defaults (created = 333)"}]
|
|
1382
|
+
|
|
1383
|
+
apply schema main;
|
|
1384
|
+
|
|
1385
|
+
-- run
|
|
1386
|
+
|
|
1387
|
+
insert into vd_v values (4, 'd');
|
|
1388
|
+
select created from vd_t where id = 4;
|
|
1389
|
+
→ [{"created": 333}]
|
|
1390
|
+
|
|
1391
|
+
-- Body changed (added a predicate), same name and clause: drift → drop+recreate.
|
|
1392
|
+
declare schema main {
|
|
1393
|
+
table vd_t (
|
|
1394
|
+
id integer primary key,
|
|
1395
|
+
name text,
|
|
1396
|
+
created integer not null
|
|
1397
|
+
)
|
|
1398
|
+
|
|
1399
|
+
view vd_v as select id, name from vd_t where id > 2 with defaults (created = 333)
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
-- run
|
|
1403
|
+
|
|
1404
|
+
diff schema main;
|
|
1405
|
+
→ [{"ddl": "DROP VIEW IF EXISTS vd_v"}, {"ddl": "create view vd_v as select id, name from vd_t where id > 2 with defaults (created = 333)"}]
|
|
1406
|
+
|
|
1407
|
+
apply schema main;
|
|
1408
|
+
|
|
1409
|
+
-- run
|
|
1410
|
+
|
|
1411
|
+
-- The new body filters: rows 1 (id=1) and 2 (id=2) are excluded; row 3 errored.
|
|
1412
|
+
select id from vd_v order by id;
|
|
1413
|
+
→ [{"id": 4}]
|
|
1414
|
+
|
|
1415
|
+
diff schema main;
|
|
1416
|
+
→ []
|
|
1417
|
+
|
|
1418
|
+
drop view vd_v;
|
|
1419
|
+
drop table vd_t;
|
|
1420
|
+
|
|
1421
|
+
-- ---------------------------------------------------------------------------
|
|
1422
|
+
-- Maintained-table re-attach convergence (explicit rename-list reshape).
|
|
1423
|
+
-- A rename-list change on an EXPLICIT maintained table re-attaches in place via
|
|
1424
|
+
-- the carried `set maintained (cols) as` op — the backing column is renamed, rows
|
|
1425
|
+
-- are relabeled (not rebuilt), and a re-diff converges. Ticket
|
|
1426
|
+
-- maintained-reattach-explicit-rename-list-reshape.
|
|
1427
|
+
-- ---------------------------------------------------------------------------
|
|
1428
|
+
declare schema main {
|
|
1429
|
+
table mvr_t (id INTEGER PRIMARY KEY, x INTEGER NOT NULL)
|
|
1430
|
+
materialized view mvr_mv (a, b) as select id, x from mvr_t
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
-- run
|
|
1434
|
+
|
|
1435
|
+
apply schema main;
|
|
1436
|
+
|
|
1437
|
+
-- run
|
|
1438
|
+
|
|
1439
|
+
insert into mvr_t values (1, 10), (2, 20);
|
|
1440
|
+
select a, b from mvr_mv order by a;
|
|
1441
|
+
→ [{"a": 1, "b": 10}, {"a": 2, "b": 20}]
|
|
1442
|
+
|
|
1443
|
+
-- Rename-list change b → c: an in-place re-attach carrying the declared list.
|
|
1444
|
+
declare schema main {
|
|
1445
|
+
table mvr_t (id INTEGER PRIMARY KEY, x INTEGER NOT NULL)
|
|
1446
|
+
materialized view mvr_mv (a, c) as select id, x from mvr_t
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
-- run
|
|
1450
|
+
|
|
1451
|
+
diff schema main;
|
|
1452
|
+
→ [{"ddl": "alter table mvr_mv set maintained (a, c) as select id, x from mvr_t"}]
|
|
1453
|
+
|
|
1454
|
+
apply schema main;
|
|
1455
|
+
|
|
1456
|
+
-- run
|
|
1457
|
+
|
|
1458
|
+
-- The backing column is relabeled b → c with values intact (a rename, not a rebuild).
|
|
1459
|
+
select a, c from mvr_mv order by a;
|
|
1460
|
+
→ [{"a": 1, "c": 10}, {"a": 2, "c": 20}]
|
|
1461
|
+
|
|
1462
|
+
-- Maintenance is live on the relabeled backing.
|
|
1463
|
+
insert into mvr_t values (3, 30);
|
|
1464
|
+
select c from mvr_mv where a = 3;
|
|
1465
|
+
→ [{"c": 30}]
|
|
1466
|
+
|
|
1467
|
+
-- Converged: re-diff wants nothing further.
|
|
1468
|
+
diff schema main;
|
|
1469
|
+
→ []
|
|
1470
|
+
|
|
1471
|
+
drop table mvr_mv;
|
|
1472
|
+
drop table mvr_t;
|
|
1473
|
+
|
|
1474
|
+
-- ===================================================================
|
|
1475
|
+
-- Seed application is idempotent (ticket
|
|
1476
|
+
-- declarative-seed-reopen-pk-collision). Re-applying `apply schema …
|
|
1477
|
+
-- with seed` over a table that already holds its seed rows must not
|
|
1478
|
+
-- collide on the seed PKs.
|
|
1479
|
+
-- The store package covers the reopen-without-rehydrate crash directly;
|
|
1480
|
+
-- this memory-backed case pins the in-engine SEMANTICS: the second apply
|
|
1481
|
+
-- uses INSERT … ON CONFLICT (<pk>) DO NOTHING — existing rows (including
|
|
1482
|
+
-- user-edited seed rows) are left untouched, and non-seed rows survive
|
|
1483
|
+
-- (no full-reset DELETE).
|
|
1484
|
+
-- ===================================================================
|
|
1485
|
+
|
|
1486
|
+
declare schema decl_seed_idem using (default_vtab_module = 'memory') {
|
|
1487
|
+
table meta {
|
|
1488
|
+
id INTEGER PRIMARY KEY,
|
|
1489
|
+
name TEXT NOT NULL
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
seed meta (
|
|
1493
|
+
(1, 'AllSite'),
|
|
1494
|
+
(2, 'Other')
|
|
1495
|
+
)
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
-- run
|
|
1499
|
+
|
|
1500
|
+
-- First apply seeds the freshly-created table.
|
|
1501
|
+
apply schema decl_seed_idem with seed;
|
|
1502
|
+
|
|
1503
|
+
-- run
|
|
1504
|
+
|
|
1505
|
+
select id, name from decl_seed_idem.meta order by id;
|
|
1506
|
+
→ [{"id": 1, "name": "AllSite"}, {"id": 2, "name": "Other"}]
|
|
1507
|
+
|
|
1508
|
+
-- A user adds a non-seed row and edits a seeded row.
|
|
1509
|
+
insert into decl_seed_idem.meta values (3, 'UserAdded');
|
|
1510
|
+
update decl_seed_idem.meta set name = 'Edited' where id = 2;
|
|
1511
|
+
select id, name from decl_seed_idem.meta order by id;
|
|
1512
|
+
→ [{"id": 1, "name": "AllSite"}, {"id": 2, "name": "Edited"}, {"id": 3, "name": "UserAdded"}]
|
|
1513
|
+
|
|
1514
|
+
-- Re-apply the SAME schema with seed: no DDL diff, but the seed re-runs.
|
|
1515
|
+
-- ON CONFLICT (<pk>) DO NOTHING skips existing PKs, so user edits and
|
|
1516
|
+
-- non-seed rows survive unchanged.
|
|
1517
|
+
apply schema decl_seed_idem with seed;
|
|
1518
|
+
|
|
1519
|
+
-- run
|
|
1520
|
+
|
|
1521
|
+
select id, name from decl_seed_idem.meta order by id;
|
|
1522
|
+
→ [{"id": 1, "name": "AllSite"}, {"id": 2, "name": "Edited"}, {"id": 3, "name": "UserAdded"}]
|
|
1523
|
+
|
|
1524
|
+
drop table decl_seed_idem.meta;
|
|
1525
|
+
|
|
1526
|
+
-- ===================================================================
|
|
1527
|
+
-- Seed re-application must NOT fire ON DELETE CASCADE for an unchanged
|
|
1528
|
+
-- seed parent (ticket seed-reseed-or-replace-cascade-tradeoff). This is
|
|
1529
|
+
-- the headline reason the seed insert skips an existing PK (via ON
|
|
1530
|
+
-- CONFLICT (<pk>) DO NOTHING) rather than using OR REPLACE: OR REPLACE
|
|
1531
|
+
-- is delete-then-insert on a PK conflict, so a reseed of an
|
|
1532
|
+
-- already-seeded parent would delete the parent row, fire ON DELETE
|
|
1533
|
+
-- CASCADE, and destroy child rows — even when the replacement values are
|
|
1534
|
+
-- identical. DO NOTHING skips the existing parent entirely (no delete),
|
|
1535
|
+
-- so the cascade never fires and children survive.
|
|
1536
|
+
-- ===================================================================
|
|
1537
|
+
|
|
1538
|
+
pragma foreign_keys = true;
|
|
1539
|
+
|
|
1540
|
+
declare schema decl_seed_cascade using (default_vtab_module = 'memory') {
|
|
1541
|
+
table parent {
|
|
1542
|
+
id INTEGER PRIMARY KEY,
|
|
1543
|
+
name TEXT
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1546
|
+
table child {
|
|
1547
|
+
id INTEGER PRIMARY KEY,
|
|
1548
|
+
parent_id INTEGER,
|
|
1549
|
+
constraint fk_parent foreign key (parent_id) references parent(id) on delete cascade
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
seed parent (
|
|
1553
|
+
(1, 'P1')
|
|
1554
|
+
)
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
-- run
|
|
1558
|
+
|
|
1559
|
+
apply schema decl_seed_cascade with seed;
|
|
1560
|
+
|
|
1561
|
+
-- run
|
|
1562
|
+
|
|
1563
|
+
-- A user adds a child referencing the seeded parent.
|
|
1564
|
+
insert into decl_seed_cascade.child values (10, 1);
|
|
1565
|
+
select id, parent_id from decl_seed_cascade.child order by id;
|
|
1566
|
+
→ [{"id": 10, "parent_id": 1}]
|
|
1567
|
+
|
|
1568
|
+
-- Re-apply the seed: ON CONFLICT (<pk>) DO NOTHING skips the
|
|
1569
|
+
-- already-present parent (1) with no delete, so ON DELETE CASCADE never
|
|
1570
|
+
-- fires and the child survives.
|
|
1571
|
+
apply schema decl_seed_cascade with seed;
|
|
1572
|
+
|
|
1573
|
+
-- run
|
|
1574
|
+
|
|
1575
|
+
select id, parent_id from decl_seed_cascade.child order by id;
|
|
1576
|
+
→ [{"id": 10, "parent_id": 1}]
|
|
1577
|
+
|
|
1578
|
+
-- The seed parent is still present and unchanged.
|
|
1579
|
+
select id, name from decl_seed_cascade.parent order by id;
|
|
1580
|
+
→ [{"id": 1, "name": "P1"}]
|
|
1581
|
+
|
|
1582
|
+
drop table decl_seed_cascade.child;
|
|
1583
|
+
drop table decl_seed_cascade.parent;
|
|
1584
|
+
|
|
1585
|
+
-- ===================================================================
|
|
1586
|
+
-- A malformed seed row aborts on first apply instead of vanishing
|
|
1587
|
+
-- (ticket seed-or-ignore-masks-malformed-rows). The seed insert uses
|
|
1588
|
+
-- `ON CONFLICT (<pk>) DO NOTHING`, which suppresses ONLY the seed-PK
|
|
1589
|
+
-- already-present conflict (the idempotency cases above). A row that
|
|
1590
|
+
-- violates a CHECK, a NOT NULL column, or a child-side FK is NOT a PK
|
|
1591
|
+
-- conflict, so it aborts with a clear error rather than being silently
|
|
1592
|
+
-- dropped the way `INSERT OR IGNORE` used to drop it.
|
|
1593
|
+
-- ===================================================================
|
|
1594
|
+
|
|
1595
|
+
-- Positive control: valid seed rows on a CHECK-constrained table apply
|
|
1596
|
+
-- cleanly, and a re-apply stays idempotent (the PK-targeted conflict
|
|
1597
|
+
-- clause skips the already-present rows, it does not re-run the CHECK
|
|
1598
|
+
-- into an error).
|
|
1599
|
+
declare schema decl_seed_good_check using (default_vtab_module = 'memory') {
|
|
1600
|
+
table t {
|
|
1601
|
+
id INTEGER PRIMARY KEY,
|
|
1602
|
+
n INTEGER,
|
|
1603
|
+
constraint positive check (n > 0)
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
seed t (
|
|
1607
|
+
(1, 5),
|
|
1608
|
+
(2, 10)
|
|
1609
|
+
)
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
-- run
|
|
1613
|
+
|
|
1614
|
+
apply schema decl_seed_good_check with seed;
|
|
1615
|
+
|
|
1616
|
+
-- run
|
|
1617
|
+
|
|
1618
|
+
select id, n from decl_seed_good_check.t order by id;
|
|
1619
|
+
→ [{"id": 1, "n": 5}, {"id": 2, "n": 10}]
|
|
1620
|
+
|
|
1621
|
+
-- Idempotent reseed: existing seed PKs are skipped, no CHECK re-fires.
|
|
1622
|
+
apply schema decl_seed_good_check with seed;
|
|
1623
|
+
|
|
1624
|
+
-- run
|
|
1625
|
+
|
|
1626
|
+
select id, n from decl_seed_good_check.t order by id;
|
|
1627
|
+
→ [{"id": 1, "n": 5}, {"id": 2, "n": 10}]
|
|
1628
|
+
|
|
1629
|
+
drop table decl_seed_good_check.t;
|
|
1630
|
+
|
|
1631
|
+
-- CHECK violation: a seed row failing `check (n < 10)` aborts the apply
|
|
1632
|
+
-- (was silently dropped under OR IGNORE). (Seed literals must be bare —
|
|
1633
|
+
-- the parser rejects unary-minus — so the violation uses an over-large
|
|
1634
|
+
-- positive value rather than a negative one.)
|
|
1635
|
+
declare schema decl_seed_bad_check using (default_vtab_module = 'memory') {
|
|
1636
|
+
table t {
|
|
1637
|
+
id INTEGER PRIMARY KEY,
|
|
1638
|
+
n INTEGER,
|
|
1639
|
+
constraint small check (n < 10)
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1642
|
+
seed t (
|
|
1643
|
+
(1, 50)
|
|
1644
|
+
)
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
-- run
|
|
1648
|
+
|
|
1649
|
+
apply schema decl_seed_bad_check with seed;
|
|
1650
|
+
-- error: CHECK constraint failed
|
|
1651
|
+
|
|
1652
|
+
-- NOT NULL violation: a seed row supplying NULL for a NOT NULL column
|
|
1653
|
+
-- aborts the apply.
|
|
1654
|
+
declare schema decl_seed_bad_nn using (default_vtab_module = 'memory') {
|
|
1655
|
+
table t {
|
|
1656
|
+
id INTEGER PRIMARY KEY,
|
|
1657
|
+
name TEXT NOT NULL
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
seed t (
|
|
1661
|
+
(1, NULL)
|
|
1662
|
+
)
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1665
|
+
-- run
|
|
1666
|
+
|
|
1667
|
+
apply schema decl_seed_bad_nn with seed;
|
|
1668
|
+
-- error: NOT NULL constraint failed
|
|
1669
|
+
|
|
1670
|
+
-- Child-side FK violation: a seed child row referencing a missing parent
|
|
1671
|
+
-- aborts the apply (the FK existence check is synthesized as a CHECK).
|
|
1672
|
+
pragma foreign_keys = true;
|
|
1673
|
+
|
|
1674
|
+
declare schema decl_seed_bad_fk using (default_vtab_module = 'memory') {
|
|
1675
|
+
table parent {
|
|
1676
|
+
id INTEGER PRIMARY KEY
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
table child {
|
|
1680
|
+
id INTEGER PRIMARY KEY,
|
|
1681
|
+
parent_id INTEGER NOT NULL,
|
|
1682
|
+
constraint fk_parent foreign key (parent_id) references parent(id)
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1685
|
+
seed child (
|
|
1686
|
+
(1, 999)
|
|
1687
|
+
)
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1690
|
+
-- run
|
|
1691
|
+
|
|
1692
|
+
apply schema decl_seed_bad_fk with seed;
|
|
1693
|
+
-- error: CHECK constraint failed
|
|
1694
|
+
|
|
1695
|
+
-- Composite PRIMARY KEY: the conflict clause names every PK column
|
|
1696
|
+
-- (`on conflict (a, b) do nothing`). A reseed over an already-present
|
|
1697
|
+
-- composite key is skipped (idempotent), a user edit to a non-PK column
|
|
1698
|
+
-- survives, and a non-seed row is preserved — the multi-column analogue
|
|
1699
|
+
-- of decl_seed_idem.
|
|
1700
|
+
declare schema decl_seed_composite using (default_vtab_module = 'memory') {
|
|
1701
|
+
table pair {
|
|
1702
|
+
a INTEGER,
|
|
1703
|
+
b INTEGER,
|
|
1704
|
+
label TEXT,
|
|
1705
|
+
primary key (a, b)
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1708
|
+
seed pair (
|
|
1709
|
+
(1, 1, 'one-one'),
|
|
1710
|
+
(1, 2, 'one-two')
|
|
1711
|
+
)
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
-- run
|
|
1715
|
+
|
|
1716
|
+
apply schema decl_seed_composite with seed;
|
|
1717
|
+
|
|
1718
|
+
-- run
|
|
1719
|
+
|
|
1720
|
+
select a, b, label from decl_seed_composite.pair order by a, b;
|
|
1721
|
+
→ [{"a": 1, "b": 1, "label": "one-one"}, {"a": 1, "b": 2, "label": "one-two"}]
|
|
1722
|
+
|
|
1723
|
+
-- A user edits a non-PK column on a seeded row and adds a non-seed row.
|
|
1724
|
+
update decl_seed_composite.pair set label = 'edited' where a = 1 and b = 2;
|
|
1725
|
+
insert into decl_seed_composite.pair values (2, 1, 'user-added');
|
|
1726
|
+
|
|
1727
|
+
-- Reseed: the composite-key conflict is skipped (no overwrite of the
|
|
1728
|
+
-- edit), and the non-seed row survives.
|
|
1729
|
+
apply schema decl_seed_composite with seed;
|
|
1730
|
+
|
|
1731
|
+
-- run
|
|
1732
|
+
|
|
1733
|
+
select a, b, label from decl_seed_composite.pair order by a, b;
|
|
1734
|
+
→ [{"a": 1, "b": 1, "label": "one-one"}, {"a": 1, "b": 2, "label": "edited"}, {"a": 2, "b": 1, "label": "user-added"}]
|
|
1735
|
+
|
|
1736
|
+
drop table decl_seed_composite.pair;
|
|
1737
|
+
|
|
1738
|
+
-- Secondary UNIQUE collision: two seed rows share a UNIQUE value but have
|
|
1739
|
+
-- distinct PKs (a typo in the seed data). The seed insert targets the PK
|
|
1740
|
+
-- with `ON CONFLICT (<pk>) DO NOTHING`, so the second row's conflict is on
|
|
1741
|
+
-- the `email` UNIQUE constraint, NOT the targeted PK — it must abort the
|
|
1742
|
+
-- apply rather than vanish silently (ticket
|
|
1743
|
+
-- seed-secondary-unique-silently-skipped). Previously the PK-targeted
|
|
1744
|
+
-- clause short-circuited and swallowed any unique conflict.
|
|
1745
|
+
declare schema decl_seed_dup_unique using (default_vtab_module = 'memory') {
|
|
1746
|
+
table t {
|
|
1747
|
+
id INTEGER PRIMARY KEY,
|
|
1748
|
+
email TEXT,
|
|
1749
|
+
constraint uq_email unique (email)
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
seed t (
|
|
1753
|
+
(1, 'dup@x'),
|
|
1754
|
+
(2, 'dup@x')
|
|
1755
|
+
)
|
|
1756
|
+
}
|
|
1757
|
+
|
|
1758
|
+
-- run
|
|
1759
|
+
|
|
1760
|
+
apply schema decl_seed_dup_unique with seed;
|
|
1761
|
+
-- error: UNIQUE constraint failed
|
|
1762
|
+
|
|
1763
|
+
-- Comparison-domain ALTER ordering. A column whose declared type AND collation both
|
|
1764
|
+
-- drift needs `SET DATA TYPE` and `SET COLLATE` in the order the engine accepts:
|
|
1765
|
+
-- `SET DATA TYPE` carries the column's CURRENT collation into the new type, and
|
|
1766
|
+
-- DATE / TIME / DATETIME / TIMESPAN / JSON accept BINARY only, so narrowing a
|
|
1767
|
+
-- `TEXT COLLATE NOCASE` column to `DATE` must emit `SET COLLATE BINARY` FIRST — the
|
|
1768
|
+
-- other order is rejected engine-side and aborts the whole migration
|
|
1769
|
+
-- (bug-retype-keeps-illegal-collation). Widening back keeps the opposite order: NOCASE
|
|
1770
|
+
-- can only land once the column is TEXT again.
|
|
1771
|
+
declare schema decl_collate_retype using (default_vtab_module = 'memory') {
|
|
1772
|
+
table t {
|
|
1773
|
+
id INTEGER PRIMARY KEY,
|
|
1774
|
+
d TEXT COLLATE NOCASE
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1778
|
+
-- run
|
|
1779
|
+
|
|
1780
|
+
apply schema decl_collate_retype;
|
|
1781
|
+
insert into decl_collate_retype.t values (1, '2024-01-01'), (2, '2024-02-01');
|
|
1782
|
+
|
|
1783
|
+
-- run
|
|
1784
|
+
|
|
1785
|
+
-- Narrowing: TEXT COLLATE NOCASE → DATE.
|
|
1786
|
+
declare schema decl_collate_retype using (default_vtab_module = 'memory') {
|
|
1787
|
+
table t {
|
|
1788
|
+
id INTEGER PRIMARY KEY,
|
|
1789
|
+
d DATE
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1793
|
+
-- run
|
|
1794
|
+
|
|
1795
|
+
diff schema decl_collate_retype;
|
|
1796
|
+
→ [{"ddl": "ALTER TABLE decl_collate_retype.t ALTER COLUMN d SET COLLATE BINARY"}, {"ddl": "ALTER TABLE decl_collate_retype.t ALTER COLUMN d SET DATA TYPE DATE"}]
|
|
1797
|
+
|
|
1798
|
+
apply schema decl_collate_retype;
|
|
1799
|
+
|
|
1800
|
+
-- run
|
|
1801
|
+
|
|
1802
|
+
-- Converged (no residual drift) and the rows survived the retype.
|
|
1803
|
+
diff schema decl_collate_retype;
|
|
1804
|
+
→ []
|
|
1805
|
+
|
|
1806
|
+
select id, d from decl_collate_retype.t order by d;
|
|
1807
|
+
→ [{"id": 1, "d": "2024-01-01"}, {"id": 2, "d": "2024-02-01"}]
|
|
1808
|
+
|
|
1809
|
+
-- Widening back: DATE → TEXT COLLATE NOCASE, retype first this time.
|
|
1810
|
+
declare schema decl_collate_retype using (default_vtab_module = 'memory') {
|
|
1811
|
+
table t {
|
|
1812
|
+
id INTEGER PRIMARY KEY,
|
|
1813
|
+
d TEXT COLLATE NOCASE
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
-- run
|
|
1818
|
+
|
|
1819
|
+
diff schema decl_collate_retype;
|
|
1820
|
+
→ [{"ddl": "ALTER TABLE decl_collate_retype.t ALTER COLUMN d SET DATA TYPE TEXT"}, {"ddl": "ALTER TABLE decl_collate_retype.t ALTER COLUMN d SET COLLATE NOCASE"}]
|
|
1821
|
+
|
|
1822
|
+
apply schema decl_collate_retype;
|
|
1823
|
+
|
|
1824
|
+
-- run
|
|
1825
|
+
|
|
1826
|
+
diff schema decl_collate_retype;
|
|
1827
|
+
→ []
|
|
1828
|
+
|
|
1829
|
+
select id, d from decl_collate_retype.t order by id;
|
|
1830
|
+
→ [{"id": 1, "d": "2024-01-01"}, {"id": 2, "d": "2024-02-01"}]
|
|
1831
|
+
|
|
1832
|
+
drop table decl_collate_retype.t;
|
|
1833
|
+
|
|
1834
|
+
-- run
|
|
1835
|
+
|
|
1836
|
+
-- ---------------------------------------------------------------------------
|
|
1837
|
+
-- Views and materialized views in a NON-main declared schema. The apply must
|
|
1838
|
+
-- land both in the declared schema, their bodies must resolve their unqualified
|
|
1839
|
+
-- source names against that schema (independent of the session's schema path),
|
|
1840
|
+
-- and a re-diff immediately after apply must be empty.
|
|
1841
|
+
-- Ticket `bug-declared-materialized-view-non-main-schema`.
|
|
1842
|
+
declare schema nm_view_pol using (default_vtab_module = 'memory') {
|
|
1843
|
+
table nt {
|
|
1844
|
+
id INTEGER PRIMARY KEY,
|
|
1845
|
+
x INTEGER
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
view nv as select id, x from nt
|
|
1849
|
+
|
|
1850
|
+
materialized view nmv as select id, x from nt
|
|
1851
|
+
}
|
|
1852
|
+
|
|
1853
|
+
-- run
|
|
1854
|
+
|
|
1855
|
+
apply schema nm_view_pol;
|
|
1856
|
+
|
|
1857
|
+
-- run
|
|
1858
|
+
|
|
1859
|
+
-- Converged immediately: the stored (unqualified) bodies match the declared ones.
|
|
1860
|
+
diff schema nm_view_pol;
|
|
1861
|
+
→ []
|
|
1862
|
+
|
|
1863
|
+
insert into nm_view_pol.nt values (1, 10), (2, 20);
|
|
1864
|
+
|
|
1865
|
+
-- The view body resolves `nt` in its home schema even though the session path is
|
|
1866
|
+
-- the default (`main, temp`).
|
|
1867
|
+
select * from nm_view_pol.nv order by id;
|
|
1868
|
+
→ [{"id": 1, "x": 10}, {"id": 2, "x": 20}]
|
|
1869
|
+
|
|
1870
|
+
-- The MV was filled at apply and row-time maintained through the insert above.
|
|
1871
|
+
select * from nm_view_pol.nmv order by id;
|
|
1872
|
+
→ [{"id": 1, "x": 10}, {"id": 2, "x": 20}]
|
|
1873
|
+
|
|
1874
|
+
-- run
|
|
1875
|
+
|
|
1876
|
+
-- Refresh re-plans the body — still against the MV's home schema.
|
|
1877
|
+
refresh materialized view nm_view_pol.nmv;
|
|
1878
|
+
|
|
1879
|
+
select * from nm_view_pol.nmv order by id;
|
|
1880
|
+
→ [{"id": 1, "x": 10}, {"id": 2, "x": 20}]
|
|
1881
|
+
|
|
1882
|
+
-- Still converged after use.
|
|
1883
|
+
diff schema nm_view_pol;
|
|
1884
|
+
→ []
|
|
1885
|
+
|
|
1886
|
+
drop materialized view nm_view_pol.nmv;
|
|
1887
|
+
drop view nm_view_pol.nv;
|
|
1888
|
+
drop table nm_view_pol.nt;
|
|
1889
|
+
|
|
1890
|
+
-- run
|
|
1891
|
+
|
|
1892
|
+
-- ---------------------------------------------------------------------------
|
|
1893
|
+
-- Assertion in a NON-main declared schema. The apply must land the assertion in
|
|
1894
|
+
-- the declared schema (not main), its body must resolve the unqualified table
|
|
1895
|
+
-- name against that schema, a re-diff immediately after apply must be empty,
|
|
1896
|
+
-- and a write to an unrelated main-schema table must still commit.
|
|
1897
|
+
-- Ticket `bug-declared-assertion-ignores-target-schema`.
|
|
1898
|
+
declare schema nm_assert_pol using (default_vtab_module = 'memory') {
|
|
1899
|
+
table at_t {
|
|
1900
|
+
id INTEGER PRIMARY KEY,
|
|
1901
|
+
x INTEGER NOT NULL
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1904
|
+
assertion a1 check (not exists (select 1 from at_t where x < 0))
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1907
|
+
-- run
|
|
1908
|
+
|
|
1909
|
+
apply schema nm_assert_pol;
|
|
1910
|
+
|
|
1911
|
+
-- run
|
|
1912
|
+
|
|
1913
|
+
-- Converged immediately: the assertion landed in its declared schema.
|
|
1914
|
+
diff schema nm_assert_pol;
|
|
1915
|
+
→ []
|
|
1916
|
+
|
|
1917
|
+
-- The assertion lives in the declared schema, not main.
|
|
1918
|
+
select schema_name, name from assertion_info() where name = 'a1';
|
|
1919
|
+
→ [{"schema_name": "nm_assert_pol", "name": "a1"}]
|
|
1920
|
+
|
|
1921
|
+
-- A conforming row commits (the body's unqualified `at_t` resolves in the
|
|
1922
|
+
-- assertion's home schema even though the session path is `main, temp`).
|
|
1923
|
+
insert into nm_assert_pol.at_t values (1, 10);
|
|
1924
|
+
|
|
1925
|
+
select id, x from nm_assert_pol.at_t order by id;
|
|
1926
|
+
→ [{"id": 1, "x": 10}]
|
|
1927
|
+
|
|
1928
|
+
-- run
|
|
1929
|
+
|
|
1930
|
+
-- A violating row fails at commit.
|
|
1931
|
+
begin;
|
|
1932
|
+
insert into nm_assert_pol.at_t values (2, -5);
|
|
1933
|
+
commit;
|
|
1934
|
+
-- error: Integrity assertion failed: nm_assert_pol.a1
|
|
1935
|
+
|
|
1936
|
+
select count(*) as cnt from nm_assert_pol.at_t;
|
|
1937
|
+
→ [{"cnt": 1}]
|
|
1938
|
+
|
|
1939
|
+
-- An unrelated main-schema write still commits: compiling the non-main
|
|
1940
|
+
-- assertion at commit time no longer fails against the session path.
|
|
1941
|
+
create table nm_unrelated (id integer primary key);
|
|
1942
|
+
insert into nm_unrelated values (1);
|
|
1943
|
+
|
|
1944
|
+
select id from nm_unrelated;
|
|
1945
|
+
→ [{"id": 1}]
|
|
1946
|
+
|
|
1947
|
+
-- run
|
|
1948
|
+
|
|
1949
|
+
-- Redeclare the schema WITHOUT the assertion: the diff emits a schema-qualified
|
|
1950
|
+
-- drop, and applying it removes the assertion from its home schema.
|
|
1951
|
+
declare schema nm_assert_pol using (default_vtab_module = 'memory') {
|
|
1952
|
+
table at_t {
|
|
1953
|
+
id INTEGER PRIMARY KEY,
|
|
1954
|
+
x INTEGER NOT NULL
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1957
|
+
|
|
1958
|
+
-- run
|
|
1959
|
+
|
|
1960
|
+
diff schema nm_assert_pol;
|
|
1961
|
+
→ [{"ddl": "DROP ASSERTION IF EXISTS nm_assert_pol.a1"}]
|
|
1962
|
+
|
|
1963
|
+
apply schema nm_assert_pol;
|
|
1964
|
+
|
|
1965
|
+
-- run
|
|
1966
|
+
|
|
1967
|
+
select count(*) as cnt from assertion_info() where name = 'a1';
|
|
1968
|
+
→ [{"cnt": 0}]
|
|
1969
|
+
|
|
1970
|
+
diff schema nm_assert_pol;
|
|
1971
|
+
→ []
|
|
1972
|
+
|
|
1973
|
+
drop table nm_unrelated;
|
|
1974
|
+
drop table nm_assert_pol.at_t;
|
|
1975
|
+
|
|
1976
|
+
-- ===================================================================
|
|
1977
|
+
-- Seed-apply against a quoted (non-bare) schema name (ticket
|
|
1978
|
+
-- bug-seed-schema-name-not-quoted). The generated seed INSERT used to
|
|
1979
|
+
-- splice schemaName/tableName into the target unquoted, so any schema
|
|
1980
|
+
-- name needing quoting broke the apply. Two distinct failure shapes:
|
|
1981
|
+
-- a colon in the name reaches the parser as a bare token and derails
|
|
1982
|
+
-- statement parsing, while a name whose leading run looks like a broken
|
|
1983
|
+
-- number (e.g. "4ee6...") is rejected by the LEXER as a truncated
|
|
1984
|
+
-- scientific-notation literal before the parser ever sees it. Both are
|
|
1985
|
+
-- fixed by quoting the whole name. A table name needing quoting takes
|
|
1986
|
+
-- the same path (third block).
|
|
1987
|
+
-- ===================================================================
|
|
1988
|
+
|
|
1989
|
+
-- Symptom 1: colon in the schema name, no digit-e-nondigit run.
|
|
1990
|
+
declare schema "scope:alpha" using (default_vtab_module = 'memory') {
|
|
1991
|
+
table meta {
|
|
1992
|
+
id INTEGER PRIMARY KEY,
|
|
1993
|
+
label TEXT NOT NULL
|
|
1994
|
+
}
|
|
1995
|
+
|
|
1996
|
+
seed meta (
|
|
1997
|
+
(1, 'first')
|
|
1998
|
+
)
|
|
1999
|
+
}
|
|
2000
|
+
|
|
2001
|
+
-- run
|
|
2002
|
+
|
|
2003
|
+
apply schema "scope:alpha" with seed;
|
|
2004
|
+
|
|
2005
|
+
-- run
|
|
2006
|
+
|
|
2007
|
+
select id, label from "scope:alpha".meta order by id;
|
|
2008
|
+
→ [{"id": 1, "label": "first"}]
|
|
2009
|
+
|
|
2010
|
+
-- Idempotent reseed stays a no-op on the existing PK.
|
|
2011
|
+
apply schema "scope:alpha" with seed;
|
|
2012
|
+
|
|
2013
|
+
-- run
|
|
2014
|
+
|
|
2015
|
+
select id, label from "scope:alpha".meta order by id;
|
|
2016
|
+
→ [{"id": 1, "label": "first"}]
|
|
2017
|
+
|
|
2018
|
+
drop table "scope:alpha".meta;
|
|
2019
|
+
|
|
2020
|
+
-- Symptom 2: no colon, but a leading digit-e-nondigit run the lexer
|
|
2021
|
+
-- misreads as a truncated scientific-notation number literal. The
|
|
2022
|
+
-- leading digit is what makes the name non-bare: an interior run
|
|
2023
|
+
-- (item4ee6) is a valid identifier and never needed quoting.
|
|
2024
|
+
declare schema "4ee6item" using (default_vtab_module = 'memory') {
|
|
2025
|
+
table meta {
|
|
2026
|
+
id INTEGER PRIMARY KEY,
|
|
2027
|
+
label TEXT NOT NULL
|
|
2028
|
+
}
|
|
2029
|
+
|
|
2030
|
+
seed meta (
|
|
2031
|
+
(1, 'first')
|
|
2032
|
+
)
|
|
2033
|
+
}
|
|
2034
|
+
|
|
2035
|
+
-- run
|
|
2036
|
+
|
|
2037
|
+
apply schema "4ee6item" with seed;
|
|
2038
|
+
|
|
2039
|
+
-- run
|
|
2040
|
+
|
|
2041
|
+
select id, label from "4ee6item".meta order by id;
|
|
2042
|
+
→ [{"id": 1, "label": "first"}]
|
|
2043
|
+
|
|
2044
|
+
apply schema "4ee6item" with seed;
|
|
2045
|
+
|
|
2046
|
+
-- run
|
|
2047
|
+
|
|
2048
|
+
select id, label from "4ee6item".meta order by id;
|
|
2049
|
+
→ [{"id": 1, "label": "first"}]
|
|
2050
|
+
|
|
2051
|
+
drop table "4ee6item".meta;
|
|
2052
|
+
|
|
2053
|
+
-- Both symptoms in one name (the shape the original report hit: a UUID
|
|
2054
|
+
-- suffix after a colon), plus a table name that itself needs quoting —
|
|
2055
|
+
-- the seed INSERT target quotes both halves.
|
|
2056
|
+
declare schema "recording:1fec5b46-014d-4e77-9dc1-b44df5513e88" using (default_vtab_module = 'memory') {
|
|
2057
|
+
table "row data" {
|
|
2058
|
+
id INTEGER PRIMARY KEY,
|
|
2059
|
+
label TEXT NOT NULL
|
|
2060
|
+
}
|
|
2061
|
+
|
|
2062
|
+
seed "row data" (
|
|
2063
|
+
(1, 'first')
|
|
2064
|
+
)
|
|
2065
|
+
}
|
|
2066
|
+
|
|
2067
|
+
-- run
|
|
2068
|
+
|
|
2069
|
+
apply schema "recording:1fec5b46-014d-4e77-9dc1-b44df5513e88" with seed;
|
|
2070
|
+
|
|
2071
|
+
-- run
|
|
2072
|
+
|
|
2073
|
+
select id, label from "recording:1fec5b46-014d-4e77-9dc1-b44df5513e88"."row data" order by id;
|
|
2074
|
+
→ [{"id": 1, "label": "first"}]
|
|
2075
|
+
|
|
2076
|
+
drop table "recording:1fec5b46-014d-4e77-9dc1-b44df5513e88"."row data";
|
|
2077
|
+
|
|
2078
|
+
-- ===================================
|
|
2079
|
+
-- apply schema options: unhonored keys are rejected, not dropped
|
|
2080
|
+
-- ===================================
|
|
2081
|
+
|
|
2082
|
+
create table opt_probe (id integer primary key);
|
|
2083
|
+
|
|
2084
|
+
declare schema main {
|
|
2085
|
+
table opt_probe {
|
|
2086
|
+
id integer primary key
|
|
2087
|
+
}
|
|
2088
|
+
}
|
|
2089
|
+
|
|
2090
|
+
-- run
|
|
2091
|
+
|
|
2092
|
+
-- `dry_run` / `validate_only` promise a preview the engine never had.
|
|
2093
|
+
apply schema main options (dry_run = true);
|
|
2094
|
+
-- error: Unsupported apply schema option 'dry_run'
|
|
2095
|
+
|
|
2096
|
+
apply schema main options (validate_only = false);
|
|
2097
|
+
-- error: Unsupported apply schema option 'validate_only'
|
|
2098
|
+
|
|
2099
|
+
-- An unrecognized key is an error too, rather than a silent no-op.
|
|
2100
|
+
apply schema main options (allow_destructiv = true);
|
|
2101
|
+
-- error: Unsupported apply schema option 'allow_destructiv'
|
|
2102
|
+
|
|
2103
|
+
-- So is a value that is not a boolean literal.
|
|
2104
|
+
apply schema main options (allow_destructive = 'yes');
|
|
2105
|
+
-- error: Expected 'true' or 'false'
|
|
2106
|
+
|
|
2107
|
+
-- The supported options still apply cleanly.
|
|
2108
|
+
apply schema main options (allow_destructive = true, rename_policy = 'deny');
|
|
2109
|
+
|
|
2110
|
+
-- run
|
|
2111
|
+
|
|
2112
|
+
drop table opt_probe;
|
|
2113
|
+
|
|
2114
|
+
-- ===================================================================
|
|
2115
|
+
-- Unnamed CHECK / UNIQUE / FOREIGN KEY constraints are diffed by body
|
|
2116
|
+
-- (ticket declarative-differ-ignores-unnamed-constraint-additions). The
|
|
2117
|
+
-- differ used to key constraints only by name, so adding or removing an
|
|
2118
|
+
-- unnamed one produced an empty diff and an upgraded database never
|
|
2119
|
+
-- gained (or lost) the rule a fresh create at the new version has. An
|
|
2120
|
+
-- added one lands under a reserved `_`-prefixed name (so it stays
|
|
2121
|
+
-- body-matched and is droppable); a CREATE-time one stored with no name
|
|
2122
|
+
-- cannot be dropped by any statement, so its removal is refused.
|
|
2123
|
+
-- ===================================================================
|
|
2124
|
+
|
|
2125
|
+
declare schema main {
|
|
2126
|
+
table uc_p { id integer primary key }
|
|
2127
|
+
table uc_t { id integer primary key, a integer null, b integer null }
|
|
2128
|
+
}
|
|
2129
|
+
|
|
2130
|
+
-- run
|
|
2131
|
+
|
|
2132
|
+
apply schema main;
|
|
2133
|
+
|
|
2134
|
+
-- run
|
|
2135
|
+
|
|
2136
|
+
insert into uc_p values (5);
|
|
2137
|
+
insert into uc_t values (1, 5, 5);
|
|
2138
|
+
|
|
2139
|
+
-- Table-level unnamed CHECK: ADD under `_check_<n>`, enforced, converged.
|
|
2140
|
+
declare schema main {
|
|
2141
|
+
table uc_p { id integer primary key }
|
|
2142
|
+
table uc_t { id integer primary key, a integer null, b integer null, check (a > 0) }
|
|
2143
|
+
}
|
|
2144
|
+
|
|
2145
|
+
-- run
|
|
2146
|
+
|
|
2147
|
+
diff schema main;
|
|
2148
|
+
→ [{"ddl": "ALTER TABLE uc_t ADD constraint _check_0 check (a > 0)"}]
|
|
2149
|
+
|
|
2150
|
+
apply schema main;
|
|
2151
|
+
|
|
2152
|
+
-- run
|
|
2153
|
+
|
|
2154
|
+
insert into uc_t values (2, -1, null);
|
|
2155
|
+
-- error: CHECK constraint failed
|
|
2156
|
+
|
|
2157
|
+
diff schema main;
|
|
2158
|
+
→ []
|
|
2159
|
+
|
|
2160
|
+
-- The same rule spelled column-level has the same body — not a change.
|
|
2161
|
+
declare schema main {
|
|
2162
|
+
table uc_p { id integer primary key }
|
|
2163
|
+
table uc_t { id integer primary key, a integer null check (a > 0), b integer null }
|
|
2164
|
+
}
|
|
2165
|
+
|
|
2166
|
+
-- run
|
|
2167
|
+
|
|
2168
|
+
diff schema main;
|
|
2169
|
+
→ []
|
|
2170
|
+
|
|
2171
|
+
-- Table- and column-level unnamed UNIQUE, and an unnamed FOREIGN KEY.
|
|
2172
|
+
declare schema main {
|
|
2173
|
+
table uc_p { id integer primary key }
|
|
2174
|
+
table uc_t {
|
|
2175
|
+
id integer primary key,
|
|
2176
|
+
a integer null check (a > 0),
|
|
2177
|
+
b integer null unique,
|
|
2178
|
+
unique (a),
|
|
2179
|
+
foreign key (b) references uc_p(id)
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
|
|
2183
|
+
-- run
|
|
2184
|
+
|
|
2185
|
+
diff schema main;
|
|
2186
|
+
→ [{"ddl": "ALTER TABLE uc_t ADD constraint _uc_a unique (a)"}, {"ddl": "ALTER TABLE uc_t ADD constraint _fk_uc_t_b foreign key (b) references uc_p(id)"}, {"ddl": "ALTER TABLE uc_t ADD constraint _uc_b unique (b)"}]
|
|
2187
|
+
|
|
2188
|
+
apply schema main;
|
|
2189
|
+
|
|
2190
|
+
-- run
|
|
2191
|
+
|
|
2192
|
+
insert into uc_t values (3, 5, null);
|
|
2193
|
+
-- error: UNIQUE constraint failed
|
|
2194
|
+
|
|
2195
|
+
insert into uc_t values (4, 6, 5);
|
|
2196
|
+
-- error: UNIQUE constraint failed
|
|
2197
|
+
|
|
2198
|
+
insert into uc_t values (5, 7, 99);
|
|
2199
|
+
-- error: _fk_uc_t_b
|
|
2200
|
+
|
|
2201
|
+
diff schema main;
|
|
2202
|
+
→ []
|
|
2203
|
+
|
|
2204
|
+
-- Removal: every upgrade-added constraint carries its reserved name, so all drop.
|
|
2205
|
+
declare schema main {
|
|
2206
|
+
table uc_p { id integer primary key }
|
|
2207
|
+
table uc_t { id integer primary key, a integer null, b integer null }
|
|
2208
|
+
}
|
|
2209
|
+
|
|
2210
|
+
-- run
|
|
2211
|
+
|
|
2212
|
+
diff schema main;
|
|
2213
|
+
→ [{"ddl": "ALTER TABLE uc_t DROP CONSTRAINT _check_0"}, {"ddl": "ALTER TABLE uc_t DROP CONSTRAINT _uc_a"}, {"ddl": "ALTER TABLE uc_t DROP CONSTRAINT _uc_b"}, {"ddl": "ALTER TABLE uc_t DROP CONSTRAINT _fk_uc_t_b"}]
|
|
2214
|
+
|
|
2215
|
+
apply schema main;
|
|
2216
|
+
|
|
2217
|
+
-- run
|
|
2218
|
+
|
|
2219
|
+
insert into uc_t values (6, -1, 99);
|
|
2220
|
+
insert into uc_t values (7, 5, 99);
|
|
2221
|
+
|
|
2222
|
+
diff schema main;
|
|
2223
|
+
→ []
|
|
2224
|
+
|
|
2225
|
+
-- A named constraint on a column the diff adds rides the ADD COLUMN inline
|
|
2226
|
+
-- only (it used to be ADDed a second time, failing the apply).
|
|
2227
|
+
declare schema main {
|
|
2228
|
+
table uc_p { id integer primary key }
|
|
2229
|
+
table uc_t { id integer primary key, a integer null, b integer null, c integer null constraint cpos check (c > 0) }
|
|
2230
|
+
}
|
|
2231
|
+
|
|
2232
|
+
-- run
|
|
2233
|
+
|
|
2234
|
+
diff schema main;
|
|
2235
|
+
→ [{"ddl": "ALTER TABLE uc_t ADD COLUMN c integer null constraint cpos check (c > 0)"}]
|
|
2236
|
+
|
|
2237
|
+
apply schema main;
|
|
2238
|
+
|
|
2239
|
+
-- run
|
|
2240
|
+
|
|
2241
|
+
insert into uc_t values (8, null, null, -1);
|
|
2242
|
+
-- error: CHECK constraint failed: cpos
|
|
2243
|
+
|
|
2244
|
+
diff schema main;
|
|
2245
|
+
→ []
|
|
2246
|
+
|
|
2247
|
+
-- Same for an unnamed one; the engine names it `_check_d`, as CREATE TABLE does.
|
|
2248
|
+
declare schema main {
|
|
2249
|
+
table uc_p { id integer primary key }
|
|
2250
|
+
table uc_t { id integer primary key, a integer null, b integer null, c integer null constraint cpos check (c > 0), d integer null check (d > 0) }
|
|
2251
|
+
}
|
|
2252
|
+
|
|
2253
|
+
-- run
|
|
2254
|
+
|
|
2255
|
+
diff schema main;
|
|
2256
|
+
→ [{"ddl": "ALTER TABLE uc_t ADD COLUMN d integer null check (d > 0)"}]
|
|
2257
|
+
|
|
2258
|
+
apply schema main;
|
|
2259
|
+
|
|
2260
|
+
-- run
|
|
2261
|
+
|
|
2262
|
+
insert into uc_t values (9, null, null, null, -1);
|
|
2263
|
+
-- error: CHECK constraint failed: _check_d
|
|
2264
|
+
|
|
2265
|
+
diff schema main;
|
|
2266
|
+
→ []
|
|
2267
|
+
|
|
2268
|
+
-- An unnamed column-level CHECK's body change: drop + re-add under the same name.
|
|
2269
|
+
declare schema main {
|
|
2270
|
+
table uc_p { id integer primary key }
|
|
2271
|
+
table uc_t { id integer primary key, a integer null, b integer null, c integer null constraint cpos check (c > 0), d integer null check (d > 1) }
|
|
2272
|
+
}
|
|
2273
|
+
|
|
2274
|
+
-- run
|
|
2275
|
+
|
|
2276
|
+
diff schema main;
|
|
2277
|
+
→ [{"ddl": "ALTER TABLE uc_t DROP CONSTRAINT _check_d"}, {"ddl": "ALTER TABLE uc_t ADD constraint _check_d check (d > 1)"}]
|
|
2278
|
+
|
|
2279
|
+
apply schema main;
|
|
2280
|
+
|
|
2281
|
+
-- run
|
|
2282
|
+
|
|
2283
|
+
insert into uc_t values (10, null, null, null, 1);
|
|
2284
|
+
-- error: CHECK constraint failed: _check_d
|
|
2285
|
+
|
|
2286
|
+
diff schema main;
|
|
2287
|
+
→ []
|
|
2288
|
+
|
|
2289
|
+
drop table uc_t;
|
|
2290
|
+
drop table uc_p;
|
|
2291
|
+
|
|
2292
|
+
-- Created fresh: a column-level unnamed CHECK / FK is stored under its
|
|
2293
|
+
-- auto-name and drops; a table-level CHECK and any UNIQUE are stored with
|
|
2294
|
+
-- no name, so dropping one from the declaration is refused, not ignored.
|
|
2295
|
+
declare schema main {
|
|
2296
|
+
table uc_p { id integer primary key }
|
|
2297
|
+
table uc_f {
|
|
2298
|
+
id integer primary key,
|
|
2299
|
+
a integer null check (a < 100),
|
|
2300
|
+
b integer null references uc_p(id),
|
|
2301
|
+
check (a > 0),
|
|
2302
|
+
unique (a)
|
|
2303
|
+
}
|
|
2304
|
+
}
|
|
2305
|
+
|
|
2306
|
+
-- run
|
|
2307
|
+
|
|
2308
|
+
apply schema main;
|
|
2309
|
+
|
|
2310
|
+
-- run
|
|
2311
|
+
|
|
2312
|
+
declare schema main {
|
|
2313
|
+
table uc_p { id integer primary key }
|
|
2314
|
+
table uc_f { id integer primary key, a integer null, b integer null, check (a > 0), unique (a) }
|
|
2315
|
+
}
|
|
2316
|
+
|
|
2317
|
+
-- run
|
|
2318
|
+
|
|
2319
|
+
diff schema main;
|
|
2320
|
+
→ [{"ddl": "ALTER TABLE uc_f DROP CONSTRAINT _check_a"}, {"ddl": "ALTER TABLE uc_f DROP CONSTRAINT _fk_uc_f_b"}]
|
|
2321
|
+
|
|
2322
|
+
apply schema main;
|
|
2323
|
+
|
|
2324
|
+
-- run
|
|
2325
|
+
|
|
2326
|
+
declare schema main {
|
|
2327
|
+
table uc_p { id integer primary key }
|
|
2328
|
+
table uc_f { id integer primary key, a integer null, b integer null, unique (a) }
|
|
2329
|
+
}
|
|
2330
|
+
|
|
2331
|
+
-- run
|
|
2332
|
+
|
|
2333
|
+
diff schema main;
|
|
2334
|
+
-- error: Cannot drop unnamed constraint 'check (a > 0)' from table 'main.uc_f'
|
|
2335
|
+
|
|
2336
|
+
apply schema main;
|
|
2337
|
+
-- error: created without a name, so no statement can drop it
|
|
2338
|
+
|
|
2339
|
+
declare schema main {
|
|
2340
|
+
table uc_p { id integer primary key }
|
|
2341
|
+
table uc_f { id integer primary key, a integer null, b integer null, check (a > 0) }
|
|
2342
|
+
}
|
|
2343
|
+
|
|
2344
|
+
-- run
|
|
2345
|
+
|
|
2346
|
+
diff schema main;
|
|
2347
|
+
-- error: Cannot drop unnamed constraint 'unique (a)' from table 'main.uc_f'
|
|
2348
|
+
|
|
2349
|
+
-- Still declaring both: converged.
|
|
2350
|
+
declare schema main {
|
|
2351
|
+
table uc_p { id integer primary key }
|
|
2352
|
+
table uc_f { id integer primary key, a integer null, b integer null, check (a > 0), unique (a) }
|
|
2353
|
+
}
|
|
2354
|
+
|
|
2355
|
+
-- run
|
|
2356
|
+
|
|
2357
|
+
diff schema main;
|
|
2358
|
+
→ []
|
|
2359
|
+
|
|
2360
|
+
drop table uc_f;
|
|
2361
|
+
|
|
2362
|
+
-- A dropped column takes its unnamed UNIQUE with it (DROP COLUMN prunes a
|
|
2363
|
+
-- UNIQUE over the column), so that removal is not refused.
|
|
2364
|
+
declare schema main {
|
|
2365
|
+
table uc_p { id integer primary key }
|
|
2366
|
+
table uc_g { id integer primary key, a integer null, b integer null, unique (a) }
|
|
2367
|
+
}
|
|
2368
|
+
|
|
2369
|
+
-- run
|
|
2370
|
+
|
|
2371
|
+
apply schema main;
|
|
2372
|
+
|
|
2373
|
+
-- run
|
|
2374
|
+
|
|
2375
|
+
declare schema main {
|
|
2376
|
+
table uc_p { id integer primary key }
|
|
2377
|
+
table uc_g { id integer primary key, b integer null }
|
|
2378
|
+
}
|
|
2379
|
+
|
|
2380
|
+
-- run
|
|
2381
|
+
|
|
2382
|
+
diff schema main;
|
|
2383
|
+
→ [{"ddl": "ALTER TABLE uc_g DROP COLUMN a"}]
|
|
2384
|
+
|
|
2385
|
+
apply schema main;
|
|
2386
|
+
|
|
2387
|
+
-- run
|
|
2388
|
+
|
|
2389
|
+
diff schema main;
|
|
2390
|
+
→ []
|
|
2391
|
+
|
|
2392
|
+
drop table uc_g;
|
|
2393
|
+
|
|
2394
|
+
-- Identical unnamed CHECKs are a multiset: a third one is exactly one ADD.
|
|
2395
|
+
declare schema main {
|
|
2396
|
+
table uc_p { id integer primary key }
|
|
2397
|
+
table uc_d { id integer primary key, a integer null, check (a > 0), check (a > 0) }
|
|
2398
|
+
}
|
|
2399
|
+
|
|
2400
|
+
-- run
|
|
2401
|
+
|
|
2402
|
+
apply schema main;
|
|
2403
|
+
|
|
2404
|
+
-- run
|
|
2405
|
+
|
|
2406
|
+
declare schema main {
|
|
2407
|
+
table uc_p { id integer primary key }
|
|
2408
|
+
table uc_d { id integer primary key, a integer null, check (a > 0), check (a > 0), check (a > 0) }
|
|
2409
|
+
}
|
|
2410
|
+
|
|
2411
|
+
-- run
|
|
2412
|
+
|
|
2413
|
+
diff schema main;
|
|
2414
|
+
→ [{"ddl": "ALTER TABLE uc_d ADD constraint _check_2 check (a > 0)"}]
|
|
2415
|
+
|
|
2416
|
+
apply schema main;
|
|
2417
|
+
|
|
2418
|
+
-- run
|
|
2419
|
+
|
|
2420
|
+
diff schema main;
|
|
2421
|
+
→ []
|
|
2422
|
+
|
|
2423
|
+
-- Taking one away drops the named one, keeping the two no DROP can reach.
|
|
2424
|
+
declare schema main {
|
|
2425
|
+
table uc_p { id integer primary key }
|
|
2426
|
+
table uc_d { id integer primary key, a integer null, check (a > 0), check (a > 0) }
|
|
2427
|
+
}
|
|
2428
|
+
|
|
2429
|
+
-- run
|
|
2430
|
+
|
|
2431
|
+
diff schema main;
|
|
2432
|
+
→ [{"ddl": "ALTER TABLE uc_d DROP CONSTRAINT _check_2"}]
|
|
2433
|
+
|
|
2434
|
+
apply schema main;
|
|
2435
|
+
|
|
2436
|
+
-- run
|
|
2437
|
+
|
|
2438
|
+
diff schema main;
|
|
2439
|
+
→ []
|
|
2440
|
+
|
|
2441
|
+
drop table uc_d;
|
|
2442
|
+
|
|
2443
|
+
-- A column rename the unnamed constraints reference is a rename only: the
|
|
2444
|
+
-- bodies are matched with the rename inverse-applied, so nothing churns.
|
|
2445
|
+
create table uc_r (id integer primary key, amount integer null, pid integer null, check (amount > 0), unique (amount), foreign key (pid) references uc_p(id));
|
|
2446
|
+
|
|
2447
|
+
declare schema main {
|
|
2448
|
+
table uc_p { id integer primary key }
|
|
2449
|
+
table uc_r {
|
|
2450
|
+
id integer primary key,
|
|
2451
|
+
qty integer null with tags ("quereus.previous_name" = 'amount'),
|
|
2452
|
+
parent_id integer null with tags ("quereus.previous_name" = 'pid'),
|
|
2453
|
+
check (qty > 0),
|
|
2454
|
+
unique (qty),
|
|
2455
|
+
foreign key (parent_id) references uc_p(id)
|
|
2456
|
+
}
|
|
2457
|
+
}
|
|
2458
|
+
|
|
2459
|
+
-- run
|
|
2460
|
+
|
|
2461
|
+
diff schema main;
|
|
2462
|
+
→ [{"ddl": "ALTER TABLE uc_r RENAME COLUMN amount TO qty"}, {"ddl": "ALTER TABLE uc_r RENAME COLUMN pid TO parent_id"}]
|
|
2463
|
+
|
|
2464
|
+
apply schema main;
|
|
2465
|
+
|
|
2466
|
+
-- run
|
|
2467
|
+
|
|
2468
|
+
diff schema main;
|
|
2469
|
+
→ []
|
|
2470
|
+
|
|
2471
|
+
insert into uc_r values (1, -1, null);
|
|
2472
|
+
-- error: CHECK constraint failed
|
|
2473
|
+
|
|
2474
|
+
drop table uc_r;
|
|
2475
|
+
|
|
2476
|
+
-- Renaming the parent table an unnamed FK references is the table rename
|
|
2477
|
+
-- only: the FK body is matched with the parent rename inverse-applied.
|
|
2478
|
+
create table uc_k (id integer primary key, pid integer null references uc_p(id));
|
|
2479
|
+
|
|
2480
|
+
declare schema main {
|
|
2481
|
+
table uc_p2 { id integer primary key } with tags ("quereus.previous_name" = 'uc_p')
|
|
2482
|
+
table uc_k { id integer primary key, pid integer null references uc_p2(id) }
|
|
2483
|
+
}
|
|
2484
|
+
|
|
2485
|
+
-- run
|
|
2486
|
+
|
|
2487
|
+
diff schema main;
|
|
2488
|
+
→ [{"ddl": "ALTER TABLE uc_p RENAME TO uc_p2"}]
|
|
2489
|
+
|
|
2490
|
+
apply schema main;
|
|
2491
|
+
|
|
2492
|
+
-- run
|
|
2493
|
+
|
|
2494
|
+
diff schema main;
|
|
2495
|
+
→ []
|
|
2496
|
+
|
|
2497
|
+
drop table uc_k;
|
|
2498
|
+
alter table uc_p2 rename to uc_p;
|
|
2499
|
+
|
|
2500
|
+
-- A declared reserved `_` name is unnamed-class: ADDed under that name, then
|
|
2501
|
+
-- matched by body (so dropping the name from the declaration is no change),
|
|
2502
|
+
-- and a body edit drops it and ADDs the new body under a fresh `_check_<n>`.
|
|
2503
|
+
create table uc_x (id integer primary key, a integer null);
|
|
2504
|
+
|
|
2505
|
+
declare schema main {
|
|
2506
|
+
table uc_p { id integer primary key }
|
|
2507
|
+
table uc_x { id integer primary key, a integer null, constraint _pos check (a > 0) }
|
|
2508
|
+
}
|
|
2509
|
+
|
|
2510
|
+
-- run
|
|
2511
|
+
|
|
2512
|
+
diff schema main;
|
|
2513
|
+
→ [{"ddl": "ALTER TABLE uc_x ADD constraint _pos check (a > 0)"}]
|
|
2514
|
+
|
|
2515
|
+
apply schema main;
|
|
2516
|
+
|
|
2517
|
+
-- run
|
|
2518
|
+
|
|
2519
|
+
declare schema main {
|
|
2520
|
+
table uc_p { id integer primary key }
|
|
2521
|
+
table uc_x { id integer primary key, a integer null, check (a > 0) }
|
|
2522
|
+
}
|
|
2523
|
+
|
|
2524
|
+
-- run
|
|
2525
|
+
|
|
2526
|
+
diff schema main;
|
|
2527
|
+
→ []
|
|
2528
|
+
|
|
2529
|
+
declare schema main {
|
|
2530
|
+
table uc_p { id integer primary key }
|
|
2531
|
+
table uc_x { id integer primary key, a integer null, check (a > 1) }
|
|
2532
|
+
}
|
|
2533
|
+
|
|
2534
|
+
-- run
|
|
2535
|
+
|
|
2536
|
+
diff schema main;
|
|
2537
|
+
→ [{"ddl": "ALTER TABLE uc_x DROP CONSTRAINT _pos"}, {"ddl": "ALTER TABLE uc_x ADD constraint _check_1 check (a > 1)"}]
|
|
2538
|
+
|
|
2539
|
+
apply schema main;
|
|
2540
|
+
|
|
2541
|
+
-- run
|
|
2542
|
+
|
|
2543
|
+
diff schema main;
|
|
2544
|
+
→ []
|
|
2545
|
+
|
|
2546
|
+
insert into uc_x values (1, 1);
|
|
2547
|
+
-- error: CHECK constraint failed: _check_1
|
|
2548
|
+
|
|
2549
|
+
drop table uc_x;
|
|
2550
|
+
drop table uc_p;
|