@quereus/quereus 4.20.0 → 4.20.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/src/common/types.d.ts +4 -4
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +3 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/schema/catalog-rendering.js +13 -1
- package/dist/src/schema/catalog-rendering.js.map +1 -1
- package/dist/src/schema/catalog.d.ts +35 -2
- package/dist/src/schema/catalog.d.ts.map +1 -1
- package/dist/src/schema/catalog.js +42 -33
- package/dist/src/schema/catalog.js.map +1 -1
- package/dist/src/schema/schema-differ.d.ts +1 -1
- package/dist/src/schema/schema-differ.d.ts.map +1 -1
- package/dist/src/schema/schema-differ.js +195 -33
- package/dist/src/schema/schema-differ.js.map +1 -1
- package/dist/src/schema/unique-enforcement.d.ts +23 -1
- package/dist/src/schema/unique-enforcement.d.ts.map +1 -1
- package/dist/src/schema/unique-enforcement.js +43 -1
- package/dist/src/schema/unique-enforcement.js.map +1 -1
- package/dist/src/vtab/memory/layer/manager.d.ts +15 -12
- package/dist/src/vtab/memory/layer/manager.d.ts.map +1 -1
- package/dist/src/vtab/memory/layer/manager.js +87 -100
- package/dist/src/vtab/memory/layer/manager.js.map +1 -1
- package/package.json +7 -4
- package/test/README.md +173 -0
- package/test/logic/01-basic.sqllogic +39 -0
- package/test/logic/01.1-select-projection-extras.sqllogic +130 -0
- package/test/logic/01.2-strings.sqllogic +43 -0
- package/test/logic/01.5-insert-select.sqllogic +237 -0
- package/test/logic/01.6-update-extras.sqllogic +118 -0
- package/test/logic/01.7-update-from.sqllogic +67 -0
- package/test/logic/01.8-delete-extras.sqllogic +122 -0
- package/test/logic/01.8.1-delete-range-cow.sqllogic +115 -0
- package/test/logic/01.9-query-expr-dml-gates.sqllogic +32 -0
- package/test/logic/01.9-query-expr-dml.sqllogic +171 -0
- package/test/logic/01.9-query-expr-values.sqllogic +111 -0
- package/test/logic/01.9.1-limit-over-dml-subquery.sqllogic +119 -0
- package/test/logic/02-filters.sqllogic +54 -0
- package/test/logic/02-smoke.sqllogic +62 -0
- package/test/logic/02.1-bind-parameters.sqllogic +223 -0
- package/test/logic/03-expressions.sqllogic +190 -0
- package/test/logic/03.1-quoted-identifiers.sqllogic +55 -0
- package/test/logic/03.4-defaults.sqllogic +554 -0
- package/test/logic/03.4.1-default-edge-cases.sqllogic +99 -0
- package/test/logic/03.5-tvf.sqllogic +61 -0
- package/test/logic/03.6-type-system.sqllogic +544 -0
- package/test/logic/03.6.1-numeric-text-comparison-coercion.sqllogic +244 -0
- package/test/logic/03.6.2-value-to-text.sqllogic +224 -0
- package/test/logic/03.7-bigint-mixed-arithmetic.sqllogic +65 -0
- package/test/logic/03.8-not-precedence.sqllogic +255 -0
- package/test/logic/03.9-is-bool-predicate.sqllogic +150 -0
- package/test/logic/03.9.1-is-bool-predicate-partial-index.sqllogic +34 -0
- package/test/logic/04-order-by.sqllogic +38 -0
- package/test/logic/04-transactions.sqllogic +239 -0
- package/test/logic/04a-savepoint-lazy-attach.sqllogic +122 -0
- package/test/logic/05-vtab_memory.sqllogic +50 -0
- package/test/logic/05.0.1-vtab-memory-unique-index-collation.sqllogic +34 -0
- package/test/logic/05.1-composite-pk-range-scan.sqllogic +147 -0
- package/test/logic/05.1.1-secondary-index-range-scan.sqllogic +56 -0
- package/test/logic/05.2-cast-seek-correctness.sqllogic +124 -0
- package/test/logic/06-builtin_functions.sqllogic +149 -0
- package/test/logic/06.1-string-functions.sqllogic +92 -0
- package/test/logic/06.1.3-like-glob-edges.sqllogic +53 -0
- package/test/logic/06.2-math-functions.sqllogic +82 -0
- package/test/logic/06.3-schema.sqllogic +48 -0
- package/test/logic/06.3.1-schema-all-schemas.sqllogic +39 -0
- package/test/logic/06.3.2-schema-foreign-keys.sqllogic +208 -0
- package/test/logic/06.3.3-introspection-tags.sqllogic +202 -0
- package/test/logic/06.3.3.1-introspection-tags-index.sqllogic +74 -0
- package/test/logic/06.3.4-view-info.sqllogic +378 -0
- package/test/logic/06.3.5-column-info.sqllogic +347 -0
- package/test/logic/06.3.6-schema-views.sqllogic +29 -0
- package/test/logic/06.4-schema-search-path.sqllogic +354 -0
- package/test/logic/06.4.1-schema-case-insensitive.sqllogic +83 -0
- package/test/logic/06.4.2-collation-extras.sqllogic +472 -0
- package/test/logic/06.4.2.1-collation-index-access-paths.sqllogic +140 -0
- package/test/logic/06.4.3-write-path-collation.sqllogic +99 -0
- package/test/logic/06.4.4-comparison-collation-precedence.sqllogic +342 -0
- package/test/logic/06.4.5-any-collate-declared-keys.sqllogic +92 -0
- package/test/logic/06.5-polymorphic-types.sqllogic +169 -0
- package/test/logic/06.5.1-conversion-functions.sqllogic +173 -0
- package/test/logic/06.5.2-scalar-minmax.sqllogic +16 -0
- package/test/logic/06.5.3-undeclared-return-type-comparison.sqllogic +244 -0
- package/test/logic/06.5.4-declared-return-type-builtins.sqllogic +288 -0
- package/test/logic/06.6-aggregate-extended.sqllogic +78 -0
- package/test/logic/06.7-json-extended.sqllogic +53 -0
- package/test/logic/06.8-json-path-operators.sqllogic +99 -0
- package/test/logic/06.9-json-canonical-key.sqllogic +65 -0
- package/test/logic/06.9.1-json-coerce-once.sqllogic +417 -0
- package/test/logic/06.9.1.1-json-coerce-once-index.sqllogic +39 -0
- package/test/logic/06.9.2-json-structural-equality.sqllogic +382 -0
- package/test/logic/06.9.2.1-json-structural-equality-index.sqllogic +76 -0
- package/test/logic/06.9.3-json-index-range-seek.sqllogic +205 -0
- package/test/logic/07-aggregates.sqllogic +193 -0
- package/test/logic/07.1-aggregate-filter-clause.sqllogic +12 -0
- package/test/logic/07.2-aggregate-order-by.sqllogic +11 -0
- package/test/logic/07.3-group-by-extras.sqllogic +602 -0
- package/test/logic/07.3.1-group-by-order-by-key.sqllogic +164 -0
- package/test/logic/07.3.2-grouped-select-list-shape.sqllogic +231 -0
- package/test/logic/07.4-group-by-filter-composite-pk.sqllogic +120 -0
- package/test/logic/07.5-sum-mixed-exact-and-float.sqllogic +151 -0
- package/test/logic/07.5-window.sqllogic +1941 -0
- package/test/logic/07.5.1-window-named.sqllogic +13 -0
- package/test/logic/07.5.2-window-nth-value.sqllogic +13 -0
- package/test/logic/07.6-subqueries.sqllogic +78 -0
- package/test/logic/07.6.1-subquery-extras.sqllogic +93 -0
- package/test/logic/07.7-and-or-short-circuit.sqllogic +58 -0
- package/test/logic/07.7-in-subquery-caching.sqllogic +174 -0
- package/test/logic/07.7-scalar-agg-decorrelation.sqllogic +197 -0
- package/test/logic/07.7.1-in-extras.sqllogic +158 -0
- package/test/logic/07.7.1-scalar-agg-decorrelation-filter.sqllogic +105 -0
- package/test/logic/07.7.2-scalar-agg-decorrelation-nested.sqllogic +115 -0
- package/test/logic/07.7.3-exists-in-select-decorrelation.sqllogic +107 -0
- package/test/logic/07.7.4-where-conjunct-ordering.sqllogic +121 -0
- package/test/logic/07.7.5-filter-lost-under-index-order.sqllogic +134 -0
- package/test/logic/07.7.6-correlated-predicate-scope.sqllogic +178 -0
- package/test/logic/07.7.7-join-source-scope-shadowing.sqllogic +137 -0
- package/test/logic/07.7.8-correlated-ref-to-computed-column.sqllogic +165 -0
- package/test/logic/07.7.9-conjunct-monotonicity.sqllogic +200 -0
- package/test/logic/07.8-correlated-subquery-edges.sqllogic +71 -0
- package/test/logic/07.8.1-correlated-in-decorrelation-gates.sqllogic +156 -0
- package/test/logic/07.9-in-value-list.sqllogic +154 -0
- package/test/logic/07.9.1-in-value-list-composite-index.sqllogic +38 -0
- package/test/logic/08-views.sqllogic +64 -0
- package/test/logic/08.1-semi-anti-join.sqllogic +191 -0
- package/test/logic/08.1-view-edge-cases.sqllogic +118 -0
- package/test/logic/08.1.1-uncorrelated-in-semijoin.sqllogic +251 -0
- package/test/logic/08.2-existence-flag-semijoin-recovery.sqllogic +197 -0
- package/test/logic/08.3-existence-flag-inner-recovery.sqllogic +102 -0
- package/test/logic/08.4-key-set-semi-join.sqllogic +447 -0
- package/test/logic/09-set_operations.sqllogic +244 -0
- package/test/logic/09.1-set-op-cross-collation.sqllogic +230 -0
- package/test/logic/10-distinct_datatypes.sqllogic +127 -0
- package/test/logic/10.1-ddl-lifecycle.sqllogic +203 -0
- package/test/logic/10.1.1-create-table-syntax-edges.sqllogic +47 -0
- package/test/logic/10.1.2-ddl-in-transaction.sqllogic +143 -0
- package/test/logic/10.1.2.1-add-constraint-in-transaction.sqllogic +59 -0
- package/test/logic/10.1.3-ddl-drop-in-transaction.sqllogic +187 -0
- package/test/logic/10.1.3.1-drop-constraint-in-transaction.sqllogic +45 -0
- package/test/logic/10.1.4-create-table-duplicate-constraint-names.sqllogic +149 -0
- package/test/logic/10.1.4-ddl-transaction-policy.sqllogic +314 -0
- package/test/logic/10.1.5-alter-add-constraint-minted-name-disambiguation.sqllogic +219 -0
- package/test/logic/10.1.5-create-index-lifecycle.sqllogic +72 -0
- package/test/logic/10.1.6-ddl-transaction-policy-index.sqllogic +143 -0
- package/test/logic/10.1.7-create-table-default-type-check.sqllogic +119 -0
- package/test/logic/10.1.8-alter-add-check-existing-rows.sqllogic +117 -0
- package/test/logic/10.2-column-features.sqllogic +365 -0
- package/test/logic/10.2.1-table-options-rejection.sqllogic +63 -0
- package/test/logic/10.2.2-default-collation-memory.sqllogic +30 -0
- package/test/logic/10.3-function-features.sqllogic +247 -0
- package/test/logic/10.4-schema-scale.sqllogic +168 -0
- package/test/logic/10.4.1-schema-scale-indexes.sqllogic +121 -0
- package/test/logic/10.5-indexes.sqllogic +164 -0
- package/test/logic/10.5.1-partial-indexes.sqllogic +526 -0
- package/test/logic/10.5.2-expression-indexes.sqllogic +34 -0
- package/test/logic/10.5.3-desc-index-ordering.sqllogic +130 -0
- package/test/logic/10.5.4-composite-pk-index-update-phantom.sqllogic +55 -0
- package/test/logic/10.5.5-index-name-uniqueness.sqllogic +76 -0
- package/test/logic/10.5.6-minmax-index-boundary.sqllogic +204 -0
- package/test/logic/10.5.7-implicit-unique-index-lifecycle.sqllogic +1216 -0
- package/test/logic/10.6-distinct-edge-cases.sqllogic +119 -0
- package/test/logic/100-predicate-normalization-edge-cases.sqllogic +238 -0
- package/test/logic/100.1-where-extras.sqllogic +163 -0
- package/test/logic/100.1.1-where-join-on-order-indexed.sqllogic +35 -0
- package/test/logic/101-builtin-mutation-kills.sqllogic +974 -0
- package/test/logic/101-transaction-edge-cases.sqllogic +395 -0
- package/test/logic/102-schema-catalog-edge-cases.sqllogic +252 -0
- package/test/logic/102-unique-constraints.sqllogic +145 -0
- package/test/logic/102.0.1-schema-catalog-indexed-table.sqllogic +32 -0
- package/test/logic/102.1-unique-edge-cases.sqllogic +111 -0
- package/test/logic/102.1.1-unique-index-creation-edge-cases.sqllogic +75 -0
- package/test/logic/102.2-unique-collation.sqllogic +181 -0
- package/test/logic/102.2.1-unique-collation-index-derived.sqllogic +305 -0
- package/test/logic/103-database-options-edge-cases.sqllogic +108 -0
- package/test/logic/103.1-pragma-boolean-values.sqllogic +126 -0
- package/test/logic/104-emit-mutation-kills.sqllogic +236 -0
- package/test/logic/105-vtab-memory-mutation-kills.sqllogic +754 -0
- package/test/logic/105.1-vtab-memory-index-mutation-kills.sqllogic +540 -0
- package/test/logic/106-constraint-extractor-mutation-kills.sqllogic +350 -0
- package/test/logic/107-temporal-arithmetic-mutation-kills.sqllogic +833 -0
- package/test/logic/108-cardinality-estimation.sqllogic +146 -0
- package/test/logic/108.1-analyze-name-resolution.sqllogic +102 -0
- package/test/logic/109-aggregate-physical-selection.sqllogic +103 -0
- package/test/logic/11-joins.sqllogic +86 -0
- package/test/logic/11.1-join-using.sqllogic +267 -0
- package/test/logic/11.2-comma-join.sqllogic +35 -0
- package/test/logic/11.3-index-nested-loop-join.sqllogic +305 -0
- package/test/logic/11.3-mismatched-collation-join.sqllogic +74 -0
- package/test/logic/11.4-hash-join-side-swap.sqllogic +118 -0
- package/test/logic/110-scan-emitter-mutation-kills.sqllogic +121 -0
- package/test/logic/110.1-scan-emitter-index-scan-kills.sqllogic +47 -0
- package/test/logic/12-empty-primary-key.sqllogic +124 -0
- package/test/logic/12-join_padding_order.sqllogic +58 -0
- package/test/logic/12.1-orderby-index-consumption.sqllogic +40 -0
- package/test/logic/13-cte.sqllogic +137 -0
- package/test/logic/13.1-cte-multiple-recursive.sqllogic +104 -0
- package/test/logic/13.10-cte-qualified-name.sqllogic +136 -0
- package/test/logic/13.11-unreferenced-dml-cte.sqllogic +414 -0
- package/test/logic/13.12-nested-dml-cte-rejected.sqllogic +280 -0
- package/test/logic/13.2-cte-bind-params.sqllogic +98 -0
- package/test/logic/13.3-cte-edge-cases.sqllogic +120 -0
- package/test/logic/13.4-cte-extras.sqllogic +98 -0
- package/test/logic/13.5-cte-join-order.sqllogic +166 -0
- package/test/logic/13.6-cte-dml-runs-once.sqllogic +318 -0
- package/test/logic/13.7-cte-sibling-visibility.sqllogic +227 -0
- package/test/logic/13.8-insert-with-clause-visibility.sqllogic +254 -0
- package/test/logic/13.9-schema-authored-cte-isolation.sqllogic +328 -0
- package/test/logic/13.9.1-schema-authored-schema-path-isolation.sqllogic +408 -0
- package/test/logic/14-utilities.sqllogic +168 -0
- package/test/logic/15-timespan.sqllogic +279 -0
- package/test/logic/15.1-semantic-ordering.sqllogic +960 -0
- package/test/logic/15.1.1-json-check-coercion.sqllogic +178 -0
- package/test/logic/15.1.2-semantic-ordering-index.sqllogic +41 -0
- package/test/logic/15.1.3-declared-form-write-contract.sqllogic +519 -0
- package/test/logic/16-epoch.sqllogic +187 -0
- package/test/logic/17-weekday-modifier.sqllogic +78 -0
- package/test/logic/18-json-string-escapes.sqllogic +86 -0
- package/test/logic/18-strftime-extended-formats.sqllogic +68 -0
- package/test/logic/20-empty-single-row.sqllogic +261 -0
- package/test/logic/21-null-edge-cases.sqllogic +227 -0
- package/test/logic/21.1-where-null-comparisons.sqllogic +151 -0
- package/test/logic/21.1.1-where-null-comparisons-secondary-index.sqllogic +35 -0
- package/test/logic/21.2-between-edges.sqllogic +157 -0
- package/test/logic/22-boundary-values.sqllogic +175 -0
- package/test/logic/23-self-joins-duplicates.sqllogic +74 -0
- package/test/logic/24-builtin-branches.sqllogic +611 -0
- package/test/logic/24.1-substr-extras.sqllogic +75 -0
- package/test/logic/24.2-random-extras.sqllogic +39 -0
- package/test/logic/25-aggregate-edge-cases.sqllogic +289 -0
- package/test/logic/25.1-nested-aggregates.sqllogic +74 -0
- package/test/logic/25.2-having-edge-cases.sqllogic +318 -0
- package/test/logic/25.3-aggregate-isnull-empty.sqllogic +16 -0
- package/test/logic/25.4-groupby-fd-constant-empty.sqllogic +55 -0
- package/test/logic/25.5-ungrouped-aggregate-column-free-select-item.sqllogic +196 -0
- package/test/logic/26-join-edge-cases.sqllogic +333 -0
- package/test/logic/26.1-left-join-isnull-on-notnull.sqllogic +26 -0
- package/test/logic/26.2-left-join-on-vs-where.sqllogic +67 -0
- package/test/logic/26.3-join-on-constants.sqllogic +61 -0
- package/test/logic/26.4-join-on-is-null-nullable.sqllogic +44 -0
- package/test/logic/27-window-edge-cases.sqllogic +206 -0
- package/test/logic/27.1-window-groups-frame.sqllogic +13 -0
- package/test/logic/27.2-window-exclude.sqllogic +13 -0
- package/test/logic/27.3-window-json-aggregation.sqllogic +66 -0
- package/test/logic/28-set-ops-sort-edge-cases.sqllogic +444 -0
- package/test/logic/28.1-compound-limit-offset.sqllogic +143 -0
- package/test/logic/28.2-orderby-expression-extras.sqllogic +698 -0
- package/test/logic/28.2-set-op-branch-types.sqllogic +217 -0
- package/test/logic/29-constraint-edge-cases.sqllogic +442 -0
- package/test/logic/29.1-column-level-conflict-clause.sqllogic +171 -0
- package/test/logic/29.2-table-level-pk-conflict-clause.sqllogic +113 -0
- package/test/logic/40-constraints.sqllogic +418 -0
- package/test/logic/40.1-pk-desc-direction.sqllogic +76 -0
- package/test/logic/40.2-check-extras.sqllogic +683 -0
- package/test/logic/41-alter-table.sqllogic +191 -0
- package/test/logic/41-fk-cascade-conflict-and-self-ref.sqllogic +263 -0
- package/test/logic/41-fk-cross-schema.sqllogic +155 -0
- package/test/logic/41-fk-extended-targets.sqllogic +173 -0
- package/test/logic/41-foreign-keys.sqllogic +1001 -0
- package/test/logic/41-generated-column-errors.sqllogic +225 -0
- package/test/logic/41-generated-column-extras.sqllogic +598 -0
- package/test/logic/41-generated-column-scope.sqllogic +329 -0
- package/test/logic/41-generated-columns.sqllogic +361 -0
- package/test/logic/41.1-alter-pk.sqllogic +230 -0
- package/test/logic/41.1-fk-collation-conflict.sqllogic +277 -0
- package/test/logic/41.10-alter-drop-column-foreign-key.sqllogic +274 -0
- package/test/logic/41.10.1-alter-drop-column-unique-index.sqllogic +169 -0
- package/test/logic/41.10.2-alter-drop-column-check-and-assertion.sqllogic +1616 -0
- package/test/logic/41.10.3-alter-drop-column-referencing-fk.sqllogic +292 -0
- package/test/logic/41.10.4-drop-object-expression-dependents.sqllogic +602 -0
- package/test/logic/41.11-deferred-fk-with-rename.sqllogic +232 -0
- package/test/logic/41.12-alter-default-coercion.sqllogic +200 -0
- package/test/logic/41.13-alter-add-column-generated-backfill.sqllogic +566 -0
- package/test/logic/41.14-alter-add-column-subquery-backfill.sqllogic +129 -0
- package/test/logic/41.15-alter-column-default-type-check.sqllogic +178 -0
- package/test/logic/41.16-fk-unenforceable.sqllogic +364 -0
- package/test/logic/41.2-alter-column.sqllogic +382 -0
- package/test/logic/41.2.1-alter-column-retype-deleted-row-memory.sqllogic +32 -0
- package/test/logic/41.2.2-alter-column-retype-index-rebuild.sqllogic +77 -0
- package/test/logic/41.2.3-alter-column-set-not-null-pk-backfill.sqllogic +310 -0
- package/test/logic/41.2.3.1-alter-column-set-not-null-pk-backfill-secondary-index.sqllogic +37 -0
- package/test/logic/41.3-alter-add-column-unique.sqllogic +184 -0
- package/test/logic/41.3-alter-rename-propagation.sqllogic +2352 -0
- package/test/logic/41.3.1-alter-rename-index-propagation.sqllogic +94 -0
- package/test/logic/41.4-alter-add-column-constraints.sqllogic +855 -0
- package/test/logic/41.5-alter-misc.sqllogic +48 -0
- package/test/logic/41.5-cross-schema-foreign-keys.sqllogic +253 -0
- package/test/logic/41.6-alter-drop-rename-constraint.sqllogic +354 -0
- package/test/logic/41.6.1-alter-drop-constraint-index-derived.sqllogic +22 -0
- package/test/logic/41.7-alter-column-collate.sqllogic +105 -0
- package/test/logic/41.7.1-alter-column-collate-unique.sqllogic +45 -0
- package/test/logic/41.7.2-alter-column-collate-unique-store.sqllogic +208 -0
- package/test/logic/41.7.2.1-alter-column-collate-unique-index-store.sqllogic +114 -0
- package/test/logic/41.7.3-alter-column-retype-unique.sqllogic +293 -0
- package/test/logic/41.7.3.1-alter-column-retype-staged-rows-memory.sqllogic +41 -0
- package/test/logic/41.7.3.2-alter-column-retype-unique-index.sqllogic +72 -0
- package/test/logic/41.7.4-alter-column-retype-semantic-memory.sqllogic +445 -0
- package/test/logic/41.7.4.1-alter-column-retype-semantic-index-memory.sqllogic +91 -0
- package/test/logic/41.7.5-alter-column-collate-pk-staged-delete-memory.sqllogic +106 -0
- package/test/logic/41.8-alter-add-constraint-unique-fk.sqllogic +275 -0
- package/test/logic/41.8-alter-savepoint-staged-rows.sqllogic +443 -0
- package/test/logic/41.8.1-alter-rename-savepoint-staged-rows.sqllogic +408 -0
- package/test/logic/41.9-alter-constraint-body-change.sqllogic +180 -0
- package/test/logic/41.9-fk-restrict-batched.sqllogic +290 -0
- package/test/logic/42-committed-snapshot.sqllogic +254 -0
- package/test/logic/42-returning.sqllogic +159 -0
- package/test/logic/42.1-returning-extras.sqllogic +232 -0
- package/test/logic/42.2-returning-stored-row.sqllogic +165 -0
- package/test/logic/43-default-nullability.sqllogic +131 -0
- package/test/logic/43-transition-constraints.sqllogic +341 -0
- package/test/logic/43.1-default-collation.sqllogic +140 -0
- package/test/logic/43.1-notnull-or-conflict.sqllogic +79 -0
- package/test/logic/43.2-deferred-check-new-on-delete.sqllogic +95 -0
- package/test/logic/43.3-nullable-primary-key.sqllogic +587 -0
- package/test/logic/44-determinism-validation.sqllogic +164 -0
- package/test/logic/44-orthogonality.sqllogic +146 -0
- package/test/logic/44.1-nondeterministic-schema.sqllogic +316 -0
- package/test/logic/45-udf-determinism.sqllogic +39 -0
- package/test/logic/46-mutation-context.sqllogic +595 -0
- package/test/logic/47-upsert.sqllogic +255 -0
- package/test/logic/47.1-upsert-conflict-targets.sqllogic +169 -0
- package/test/logic/47.2-replace-and-or-clauses.sqllogic +164 -0
- package/test/logic/47.2.1-pk-replace-secondary-unique.sqllogic +374 -0
- package/test/logic/47.3-engine-or-fail.sqllogic +122 -0
- package/test/logic/47.3-upsert-conflict-target-collation.sqllogic +139 -0
- package/test/logic/47.3.1-upsert-conflict-index-derived-collation.sqllogic +25 -0
- package/test/logic/47.4-upsert-conflict-target-affinity.sqllogic +59 -0
- package/test/logic/47.5-upsert-do-update-constraints.sqllogic +440 -0
- package/test/logic/47.6-upsert-do-update-decomposed.sqllogic +100 -0
- package/test/logic/49-reference-graph.sqllogic +84 -0
- package/test/logic/50-declarative-schema.sqllogic +2550 -0
- package/test/logic/50-metadata-tags.sqllogic +1033 -0
- package/test/logic/50.0.1-metadata-tags-index.sqllogic +141 -0
- package/test/logic/50.1-declare-schema-pk.sqllogic +150 -0
- package/test/logic/50.2-declare-schema-renames.sqllogic +949 -0
- package/test/logic/50.2.1-declare-schema-index-renames.sqllogic +173 -0
- package/test/logic/50.3-declare-schema-duplicate-names.sqllogic +280 -0
- package/test/logic/50.4-declare-schema-apply-restore.sqllogic +192 -0
- package/test/logic/51-lens-foundation.sqllogic +147 -0
- package/test/logic/51-materialized-views.sqllogic +376 -0
- package/test/logic/51.5-materialized-views-coarsened-key.sqllogic +198 -0
- package/test/logic/51.7-maintained-table-attach-detach.sqllogic +468 -0
- package/test/logic/51.8-maintained-table-declared-constraints.sqllogic +440 -0
- package/test/logic/51.85-maintained-table-parent-fk.sqllogic +103 -0
- package/test/logic/51.9-maintained-table-secondary-unique.sqllogic +237 -0
- package/test/logic/51.9.1-maintained-table-partial-unique.sqllogic +36 -0
- package/test/logic/52-lens-overrides.sqllogic +566 -0
- package/test/logic/53-materialized-views-rowtime.sqllogic +1601 -0
- package/test/logic/53-reserved-tags.sqllogic +252 -0
- package/test/logic/53.1-materialized-view-write-through.sqllogic +329 -0
- package/test/logic/53.2-materialized-view-rename-propagation.sqllogic +483 -0
- package/test/logic/53.3-materialized-view-constraint-only-ddl.sqllogic +409 -0
- package/test/logic/53.3.1-materialized-view-index-ddl-irrelevance.sqllogic +29 -0
- package/test/logic/53.4-materialized-view-structural-alter-restore.sqllogic +436 -0
- package/test/logic/54-covering-mv-enforcement.sqllogic +314 -0
- package/test/logic/54.1-covering-mv-partial-enforcement.sqllogic +34 -0
- package/test/logic/55-internal-eviction-reporting.sqllogic +89 -0
- package/test/logic/55-lens-prover.sqllogic +202 -0
- package/test/logic/55.1-eviction-restrict-fk.sqllogic +148 -0
- package/test/logic/55.5-lens-authored-inverse.sqllogic +1468 -0
- package/test/logic/80-grow-retrieve-pass.sqllogic +79 -0
- package/test/logic/81-quickpick.sqllogic +24 -0
- package/test/logic/82-bloom-join.sqllogic +185 -0
- package/test/logic/83-merge-join.sqllogic +280 -0
- package/test/logic/84-asof-scan.sqllogic +238 -0
- package/test/logic/84-key-cardinality.sqllogic +50 -0
- package/test/logic/85-relational-const-folding.sqllogic +51 -0
- package/test/logic/86-scalar-cse.sqllogic +42 -0
- package/test/logic/90-error_paths.sqllogic +114 -0
- package/test/logic/90.1-parse-errors.sqllogic +77 -0
- package/test/logic/90.2-alter-table-errors.sqllogic +53 -0
- package/test/logic/90.2.1-alter-extra-errors.sqllogic +51 -0
- package/test/logic/90.3-expression-errors.sqllogic +43 -0
- package/test/logic/90.4-dml-errors.sqllogic +47 -0
- package/test/logic/90.5-unsupported-join-types.sqllogic +37 -0
- package/test/logic/90.5.1-right-full-join-read.sqllogic +157 -0
- package/test/logic/90.6-select-error-paths.sqllogic +57 -0
- package/test/logic/91-merge-join-edge-cases.sqllogic +159 -0
- package/test/logic/92-hash-aggregate-edge-cases.sqllogic +139 -0
- package/test/logic/93-ddl-view-edge-cases.sqllogic +104 -0
- package/test/logic/93.1-view-error-paths.sqllogic +74 -0
- package/test/logic/93.2-view-mutation-pending.sqllogic +108 -0
- package/test/logic/93.3-view-mutation-or-clause.sqllogic +60 -0
- package/test/logic/93.4-view-mutation.sqllogic +4881 -0
- package/test/logic/93.4.1-view-mutation-partial-unique-cardinality.sqllogic +31 -0
- package/test/logic/93.5-authored-inverse.sqllogic +415 -0
- package/test/logic/93.6-set-op-flagless-write.sqllogic +862 -0
- package/test/logic/94-tvf-edge-cases.sqllogic +58 -0
- package/test/logic/94.1-limit-offset-edge-cases.sqllogic +109 -0
- package/test/logic/94.2-limit-offset-extras.sqllogic +98 -0
- package/test/logic/95-assertions.sqllogic +1417 -0
- package/test/logic/96-subquery-edge-cases.sqllogic +276 -0
- package/test/logic/97-json-function-edge-cases.sqllogic +633 -0
- package/test/logic/97.1-json-blob-and-special-values.sqllogic +92 -0
- package/test/logic/98-temporal-edge-cases.sqllogic +515 -0
- package/test/logic/99-conversion-edge-cases.sqllogic +325 -0
- package/test/logic/99.0.1-schema-index-listing.sqllogic +21 -0
- package/test/logic/99.1-cast-syntax-extras.sqllogic +167 -0
- package/test/logic/drop-unique-index.sqllogic +88 -0
- package/test/logic/pushdown-test.sqllogic +36 -0
|
@@ -0,0 +1,1001 @@
|
|
|
1
|
+
-- foreign-keys.sqllogic - Testing FOREIGN KEY constraint enforcement
|
|
2
|
+
|
|
3
|
+
-- ===================================================================
|
|
4
|
+
-- Setup: Enable foreign key enforcement
|
|
5
|
+
-- ===================================================================
|
|
6
|
+
|
|
7
|
+
PRAGMA foreign_keys = true;
|
|
8
|
+
|
|
9
|
+
-- ===================================================================
|
|
10
|
+
-- Phase 1: Child-side referential validation
|
|
11
|
+
-- ===================================================================
|
|
12
|
+
|
|
13
|
+
-- --- Basic FK validation (child-side INSERT) ---
|
|
14
|
+
|
|
15
|
+
CREATE TABLE authors (
|
|
16
|
+
id INTEGER PRIMARY KEY,
|
|
17
|
+
name TEXT NOT NULL
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
CREATE TABLE books (
|
|
21
|
+
id INTEGER PRIMARY KEY,
|
|
22
|
+
title TEXT NOT NULL,
|
|
23
|
+
author_id INTEGER,
|
|
24
|
+
FOREIGN KEY (author_id) REFERENCES authors(id) ON DELETE RESTRICT ON UPDATE RESTRICT
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
-- Populate parent table
|
|
28
|
+
INSERT INTO authors VALUES (1, 'Alice'), (2, 'Bob');
|
|
29
|
+
SELECT * FROM authors ORDER BY id;
|
|
30
|
+
→ [{"id":1,"name":"Alice"},{"id":2,"name":"Bob"}]
|
|
31
|
+
|
|
32
|
+
-- Valid FK insert (author exists)
|
|
33
|
+
INSERT INTO books VALUES (1, 'Book A', 1);
|
|
34
|
+
SELECT * FROM books;
|
|
35
|
+
→ [{"id":1,"title":"Book A","author_id":1}]
|
|
36
|
+
|
|
37
|
+
-- Invalid FK insert (author_id 99 does not exist)
|
|
38
|
+
INSERT INTO books VALUES (2, 'Book B', 99);
|
|
39
|
+
-- error: CHECK constraint failed: _fk_books_author_id
|
|
40
|
+
|
|
41
|
+
-- Verify state after failed insert
|
|
42
|
+
SELECT count(*) FROM books;
|
|
43
|
+
→ [{"count(*)":1}]
|
|
44
|
+
|
|
45
|
+
-- --- Child-side UPDATE validation ---
|
|
46
|
+
|
|
47
|
+
-- Valid FK update
|
|
48
|
+
UPDATE books SET author_id = 2 WHERE id = 1;
|
|
49
|
+
SELECT author_id FROM books WHERE id = 1;
|
|
50
|
+
→ [{"author_id":2}]
|
|
51
|
+
|
|
52
|
+
-- Invalid FK update (author_id 50 does not exist)
|
|
53
|
+
UPDATE books SET author_id = 50 WHERE id = 1;
|
|
54
|
+
-- error: CHECK constraint failed: _fk_books_author_id
|
|
55
|
+
|
|
56
|
+
-- Verify state after failed update
|
|
57
|
+
SELECT author_id FROM books WHERE id = 1;
|
|
58
|
+
→ [{"author_id":2}]
|
|
59
|
+
|
|
60
|
+
DROP TABLE books;
|
|
61
|
+
DROP TABLE authors;
|
|
62
|
+
|
|
63
|
+
-- ===================================================================
|
|
64
|
+
-- Phase 1: Parent-side RESTRICT and NO ACTION
|
|
65
|
+
-- ===================================================================
|
|
66
|
+
|
|
67
|
+
-- --- RESTRICT on DELETE ---
|
|
68
|
+
|
|
69
|
+
CREATE TABLE departments (
|
|
70
|
+
id INTEGER PRIMARY KEY,
|
|
71
|
+
name TEXT NOT NULL
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
CREATE TABLE employees (
|
|
75
|
+
id INTEGER PRIMARY KEY,
|
|
76
|
+
name TEXT NOT NULL,
|
|
77
|
+
dept_id INTEGER,
|
|
78
|
+
FOREIGN KEY (dept_id) REFERENCES departments(id) ON DELETE RESTRICT ON UPDATE RESTRICT
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
INSERT INTO departments VALUES (1, 'Engineering'), (2, 'Marketing');
|
|
82
|
+
INSERT INTO employees VALUES (1, 'Alice', 1), (2, 'Bob', 1);
|
|
83
|
+
|
|
84
|
+
-- Commit setup data before error test
|
|
85
|
+
SELECT count(*) FROM departments;
|
|
86
|
+
→ [{"count(*)":2}]
|
|
87
|
+
|
|
88
|
+
-- Attempt to delete a referenced department (should fail - RESTRICT; enforced
|
|
89
|
+
-- by the batched statement-end probe since fk-restrict-statement-batch)
|
|
90
|
+
DELETE FROM departments WHERE id = 1;
|
|
91
|
+
-- error: violates RESTRICT from 'employees'
|
|
92
|
+
|
|
93
|
+
-- Verify nothing was deleted
|
|
94
|
+
SELECT count(*) FROM departments;
|
|
95
|
+
→ [{"count(*)":2}]
|
|
96
|
+
|
|
97
|
+
-- Delete an unreferenced department (should succeed)
|
|
98
|
+
DELETE FROM departments WHERE id = 2;
|
|
99
|
+
SELECT count(*) FROM departments;
|
|
100
|
+
→ [{"count(*)":1}]
|
|
101
|
+
|
|
102
|
+
-- --- RESTRICT on UPDATE ---
|
|
103
|
+
|
|
104
|
+
-- Attempt to update referenced PK (should fail - RESTRICT; batched probe)
|
|
105
|
+
UPDATE departments SET id = 99 WHERE id = 1;
|
|
106
|
+
-- error: violates RESTRICT from 'employees'
|
|
107
|
+
|
|
108
|
+
-- Verify nothing changed
|
|
109
|
+
SELECT id FROM departments;
|
|
110
|
+
→ [{"id":1}]
|
|
111
|
+
|
|
112
|
+
DROP TABLE employees;
|
|
113
|
+
DROP TABLE departments;
|
|
114
|
+
|
|
115
|
+
-- --- NO ACTION (treated as RESTRICT) ---
|
|
116
|
+
|
|
117
|
+
CREATE TABLE categories (
|
|
118
|
+
id INTEGER PRIMARY KEY,
|
|
119
|
+
name TEXT NOT NULL
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
CREATE TABLE products (
|
|
123
|
+
id INTEGER PRIMARY KEY,
|
|
124
|
+
name TEXT NOT NULL,
|
|
125
|
+
cat_id INTEGER,
|
|
126
|
+
FOREIGN KEY (cat_id) REFERENCES categories(id) ON DELETE NO ACTION
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
INSERT INTO categories VALUES (1, 'Electronics'), (2, 'Books');
|
|
130
|
+
INSERT INTO products VALUES (1, 'Laptop', 1);
|
|
131
|
+
|
|
132
|
+
SELECT count(*) FROM products;
|
|
133
|
+
→ [{"count(*)":1}]
|
|
134
|
+
|
|
135
|
+
-- NO ACTION → RESTRICT: deleting parent with existing children fails
|
|
136
|
+
DELETE FROM categories WHERE id = 1;
|
|
137
|
+
-- error: violates RESTRICT from 'products'
|
|
138
|
+
|
|
139
|
+
-- Nothing changed
|
|
140
|
+
SELECT count(*) FROM categories WHERE id = 1;
|
|
141
|
+
→ [{"count(*)":1}]
|
|
142
|
+
SELECT count(*) FROM products WHERE cat_id = 1;
|
|
143
|
+
→ [{"count(*)":1}]
|
|
144
|
+
|
|
145
|
+
-- Deleting an unreferenced parent succeeds
|
|
146
|
+
DELETE FROM categories WHERE id = 2;
|
|
147
|
+
SELECT count(*) FROM categories;
|
|
148
|
+
→ [{"count(*)":1}]
|
|
149
|
+
|
|
150
|
+
DROP TABLE products;
|
|
151
|
+
DROP TABLE categories;
|
|
152
|
+
|
|
153
|
+
-- ===================================================================
|
|
154
|
+
-- Phase 2: Cascading actions
|
|
155
|
+
-- ===================================================================
|
|
156
|
+
|
|
157
|
+
-- --- CASCADE DELETE ---
|
|
158
|
+
|
|
159
|
+
CREATE TABLE parent_table (
|
|
160
|
+
id INTEGER PRIMARY KEY,
|
|
161
|
+
data TEXT
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
CREATE TABLE child_table (
|
|
165
|
+
id INTEGER PRIMARY KEY,
|
|
166
|
+
parent_id INTEGER,
|
|
167
|
+
info TEXT,
|
|
168
|
+
FOREIGN KEY (parent_id) REFERENCES parent_table(id) ON DELETE CASCADE
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
INSERT INTO parent_table VALUES (1, 'P1'), (2, 'P2'), (3, 'P3');
|
|
172
|
+
INSERT INTO child_table VALUES (10, 1, 'C1a'), (11, 1, 'C1b'), (20, 2, 'C2a');
|
|
173
|
+
|
|
174
|
+
-- Commit setup
|
|
175
|
+
SELECT count(*) FROM child_table;
|
|
176
|
+
→ [{"count(*)":3}]
|
|
177
|
+
|
|
178
|
+
-- Delete parent row - children should cascade
|
|
179
|
+
DELETE FROM parent_table WHERE id = 1;
|
|
180
|
+
|
|
181
|
+
SELECT count(*) FROM parent_table WHERE id = 1;
|
|
182
|
+
→ [{"count(*)":0}]
|
|
183
|
+
|
|
184
|
+
-- Child rows referencing parent_id=1 should be gone
|
|
185
|
+
SELECT count(*) FROM child_table WHERE parent_id = 1;
|
|
186
|
+
→ [{"count(*)":0}]
|
|
187
|
+
|
|
188
|
+
-- Child rows referencing parent_id=2 should still exist
|
|
189
|
+
SELECT count(*) FROM child_table WHERE parent_id = 2;
|
|
190
|
+
→ [{"count(*)":1}]
|
|
191
|
+
|
|
192
|
+
DROP TABLE child_table;
|
|
193
|
+
DROP TABLE parent_table;
|
|
194
|
+
|
|
195
|
+
-- --- CASCADE UPDATE ---
|
|
196
|
+
|
|
197
|
+
CREATE TABLE ref_parent (
|
|
198
|
+
id INTEGER PRIMARY KEY,
|
|
199
|
+
value TEXT
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
CREATE TABLE ref_child (
|
|
203
|
+
id INTEGER PRIMARY KEY,
|
|
204
|
+
parent_id INTEGER,
|
|
205
|
+
FOREIGN KEY (parent_id) REFERENCES ref_parent(id) ON UPDATE CASCADE
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
INSERT INTO ref_parent VALUES (1, 'A'), (2, 'B');
|
|
209
|
+
INSERT INTO ref_child VALUES (100, 1), (101, 1), (200, 2);
|
|
210
|
+
|
|
211
|
+
-- Commit setup
|
|
212
|
+
SELECT count(*) FROM ref_child;
|
|
213
|
+
→ [{"count(*)":3}]
|
|
214
|
+
|
|
215
|
+
-- Update parent PK - children should cascade
|
|
216
|
+
UPDATE ref_parent SET id = 10 WHERE id = 1;
|
|
217
|
+
|
|
218
|
+
SELECT parent_id FROM ref_child WHERE id = 100;
|
|
219
|
+
→ [{"parent_id":10}]
|
|
220
|
+
SELECT parent_id FROM ref_child WHERE id = 101;
|
|
221
|
+
→ [{"parent_id":10}]
|
|
222
|
+
|
|
223
|
+
-- Non-cascaded child should be unchanged
|
|
224
|
+
SELECT parent_id FROM ref_child WHERE id = 200;
|
|
225
|
+
→ [{"parent_id":2}]
|
|
226
|
+
|
|
227
|
+
DROP TABLE ref_child;
|
|
228
|
+
DROP TABLE ref_parent;
|
|
229
|
+
|
|
230
|
+
-- --- SET NULL on DELETE ---
|
|
231
|
+
|
|
232
|
+
CREATE TABLE sn_parent (
|
|
233
|
+
id INTEGER PRIMARY KEY,
|
|
234
|
+
name TEXT
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
CREATE TABLE sn_child (
|
|
238
|
+
id INTEGER PRIMARY KEY,
|
|
239
|
+
parent_id INTEGER NULL,
|
|
240
|
+
FOREIGN KEY (parent_id) REFERENCES sn_parent(id) ON DELETE SET NULL
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
INSERT INTO sn_parent VALUES (1, 'X'), (2, 'Y');
|
|
244
|
+
INSERT INTO sn_child VALUES (1, 1), (2, 1), (3, 2);
|
|
245
|
+
|
|
246
|
+
-- Commit setup
|
|
247
|
+
Select count(*) FROM sn_child;
|
|
248
|
+
→ [{"count(*)":3}]
|
|
249
|
+
|
|
250
|
+
-- Delete parent - child FK columns should become NULL
|
|
251
|
+
DELETE FROM sn_parent WHERE id = 1;
|
|
252
|
+
|
|
253
|
+
SELECT id, parent_id FROM sn_child ORDER BY id;
|
|
254
|
+
→ [{"id":1,"parent_id":null},{"id":2,"parent_id":null},{"id":3,"parent_id":2}]
|
|
255
|
+
|
|
256
|
+
DROP TABLE sn_child;
|
|
257
|
+
DROP TABLE sn_parent;
|
|
258
|
+
|
|
259
|
+
-- --- SET DEFAULT on DELETE ---
|
|
260
|
+
|
|
261
|
+
CREATE TABLE sd_parent (
|
|
262
|
+
id INTEGER PRIMARY KEY,
|
|
263
|
+
name TEXT
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
CREATE TABLE sd_child (
|
|
267
|
+
id INTEGER PRIMARY KEY,
|
|
268
|
+
parent_id INTEGER DEFAULT 0,
|
|
269
|
+
FOREIGN KEY (parent_id) REFERENCES sd_parent(id) ON DELETE SET DEFAULT
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
INSERT INTO sd_parent VALUES (0, 'Default'), (1, 'One'), (2, 'Two');
|
|
273
|
+
INSERT INTO sd_child VALUES (1, 1), (2, 1), (3, 2);
|
|
274
|
+
|
|
275
|
+
-- Commit setup
|
|
276
|
+
SELECT count(*) FROM sd_child;
|
|
277
|
+
→ [{"count(*)":3}]
|
|
278
|
+
|
|
279
|
+
-- Delete parent - child FK columns should become default (0)
|
|
280
|
+
DELETE FROM sd_parent WHERE id = 1;
|
|
281
|
+
|
|
282
|
+
SELECT id, parent_id FROM sd_child ORDER BY id;
|
|
283
|
+
→ [{"id":1,"parent_id":0},{"id":2,"parent_id":0},{"id":3,"parent_id":2}]
|
|
284
|
+
|
|
285
|
+
DROP TABLE sd_child;
|
|
286
|
+
DROP TABLE sd_parent;
|
|
287
|
+
|
|
288
|
+
-- --- SET NULL on UPDATE ---
|
|
289
|
+
|
|
290
|
+
CREATE TABLE snu_parent (
|
|
291
|
+
id INTEGER PRIMARY KEY,
|
|
292
|
+
name TEXT
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
CREATE TABLE snu_child (
|
|
296
|
+
id INTEGER PRIMARY KEY,
|
|
297
|
+
parent_id INTEGER NULL,
|
|
298
|
+
FOREIGN KEY (parent_id) REFERENCES snu_parent(id) ON UPDATE SET NULL
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
INSERT INTO snu_parent VALUES (1, 'X'), (2, 'Y');
|
|
302
|
+
INSERT INTO snu_child VALUES (1, 1), (2, 1), (3, 2);
|
|
303
|
+
|
|
304
|
+
-- Update parent PK - child FK columns should become NULL
|
|
305
|
+
UPDATE snu_parent SET id = 10 WHERE id = 1;
|
|
306
|
+
|
|
307
|
+
SELECT id, parent_id FROM snu_child ORDER BY id;
|
|
308
|
+
→ [{"id":1,"parent_id":null},{"id":2,"parent_id":null},{"id":3,"parent_id":2}]
|
|
309
|
+
|
|
310
|
+
DROP TABLE snu_child;
|
|
311
|
+
DROP TABLE snu_parent;
|
|
312
|
+
|
|
313
|
+
-- --- SET DEFAULT on UPDATE ---
|
|
314
|
+
|
|
315
|
+
CREATE TABLE sdu_parent (
|
|
316
|
+
id INTEGER PRIMARY KEY,
|
|
317
|
+
name TEXT
|
|
318
|
+
);
|
|
319
|
+
|
|
320
|
+
CREATE TABLE sdu_child (
|
|
321
|
+
id INTEGER PRIMARY KEY,
|
|
322
|
+
parent_id INTEGER DEFAULT 0,
|
|
323
|
+
FOREIGN KEY (parent_id) REFERENCES sdu_parent(id) ON UPDATE SET DEFAULT
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
INSERT INTO sdu_parent VALUES (0, 'Default'), (1, 'One'), (2, 'Two');
|
|
327
|
+
INSERT INTO sdu_child VALUES (1, 1), (2, 1), (3, 2);
|
|
328
|
+
|
|
329
|
+
-- Update parent PK - child FK columns should become default (0)
|
|
330
|
+
UPDATE sdu_parent SET id = 10 WHERE id = 1;
|
|
331
|
+
|
|
332
|
+
SELECT id, parent_id FROM sdu_child ORDER BY id;
|
|
333
|
+
→ [{"id":1,"parent_id":0},{"id":2,"parent_id":0},{"id":3,"parent_id":2}]
|
|
334
|
+
|
|
335
|
+
DROP TABLE sdu_child;
|
|
336
|
+
DROP TABLE sdu_parent;
|
|
337
|
+
|
|
338
|
+
-- ===================================================================
|
|
339
|
+
-- NULL FK values (NULLs don't participate in FK matching)
|
|
340
|
+
-- ===================================================================
|
|
341
|
+
|
|
342
|
+
CREATE TABLE null_parent (
|
|
343
|
+
id INTEGER PRIMARY KEY,
|
|
344
|
+
name TEXT
|
|
345
|
+
);
|
|
346
|
+
|
|
347
|
+
CREATE TABLE null_child (
|
|
348
|
+
id INTEGER PRIMARY KEY,
|
|
349
|
+
parent_id INTEGER NULL,
|
|
350
|
+
FOREIGN KEY (parent_id) REFERENCES null_parent(id) ON DELETE RESTRICT
|
|
351
|
+
);
|
|
352
|
+
|
|
353
|
+
INSERT INTO null_parent VALUES (1, 'A');
|
|
354
|
+
|
|
355
|
+
-- Inserting NULL FK value should succeed (NULLs bypass FK checks)
|
|
356
|
+
INSERT INTO null_child VALUES (1, NULL);
|
|
357
|
+
SELECT * FROM null_child;
|
|
358
|
+
→ [{"id":1,"parent_id":null}]
|
|
359
|
+
|
|
360
|
+
-- Update FK column to NULL should also succeed (MATCH SIMPLE applies to UPDATE too)
|
|
361
|
+
INSERT INTO null_parent VALUES (2, 'B');
|
|
362
|
+
INSERT INTO null_child VALUES (2, 2);
|
|
363
|
+
UPDATE null_child SET parent_id = NULL WHERE id = 2;
|
|
364
|
+
SELECT * FROM null_child ORDER BY id;
|
|
365
|
+
→ [{"id":1,"parent_id":null},{"id":2,"parent_id":null}]
|
|
366
|
+
|
|
367
|
+
DROP TABLE null_child;
|
|
368
|
+
DROP TABLE null_parent;
|
|
369
|
+
|
|
370
|
+
-- --- MATCH SIMPLE: NULL FK with empty parent (RESTRICT) ---
|
|
371
|
+
|
|
372
|
+
CREATE TABLE ms_parent (
|
|
373
|
+
id INTEGER PRIMARY KEY
|
|
374
|
+
);
|
|
375
|
+
|
|
376
|
+
CREATE TABLE ms_child (
|
|
377
|
+
id INTEGER PRIMARY KEY,
|
|
378
|
+
parent_id INTEGER NULL,
|
|
379
|
+
FOREIGN KEY (parent_id) REFERENCES ms_parent(id) ON DELETE RESTRICT
|
|
380
|
+
);
|
|
381
|
+
|
|
382
|
+
-- Insert with NULL FK into empty parent table — must succeed per MATCH SIMPLE
|
|
383
|
+
INSERT INTO ms_child VALUES (1, NULL);
|
|
384
|
+
SELECT * FROM ms_child;
|
|
385
|
+
→ [{"id":1,"parent_id":null}]
|
|
386
|
+
|
|
387
|
+
DROP TABLE ms_child;
|
|
388
|
+
DROP TABLE ms_parent;
|
|
389
|
+
|
|
390
|
+
-- --- MATCH SIMPLE: self-referential FK, first row NULL ---
|
|
391
|
+
|
|
392
|
+
CREATE TABLE ms_tree (
|
|
393
|
+
id INTEGER PRIMARY KEY,
|
|
394
|
+
parent_id INTEGER NULL,
|
|
395
|
+
FOREIGN KEY (parent_id) REFERENCES ms_tree(id) ON DELETE CASCADE
|
|
396
|
+
);
|
|
397
|
+
|
|
398
|
+
-- Insert root with NULL parent_id into empty table — must succeed
|
|
399
|
+
INSERT INTO ms_tree VALUES (1, NULL);
|
|
400
|
+
SELECT * FROM ms_tree;
|
|
401
|
+
→ [{"id":1,"parent_id":null}]
|
|
402
|
+
|
|
403
|
+
DROP TABLE ms_tree;
|
|
404
|
+
|
|
405
|
+
-- --- MATCH SIMPLE: multi-column FK, one NULL column ---
|
|
406
|
+
|
|
407
|
+
CREATE TABLE ms_mc_parent (
|
|
408
|
+
a INTEGER,
|
|
409
|
+
b INTEGER,
|
|
410
|
+
PRIMARY KEY (a, b)
|
|
411
|
+
);
|
|
412
|
+
|
|
413
|
+
CREATE TABLE ms_mc_child (
|
|
414
|
+
id INTEGER PRIMARY KEY,
|
|
415
|
+
ref_a INTEGER NULL,
|
|
416
|
+
ref_b INTEGER NULL,
|
|
417
|
+
FOREIGN KEY (ref_a, ref_b) REFERENCES ms_mc_parent(a, b) ON DELETE RESTRICT
|
|
418
|
+
);
|
|
419
|
+
|
|
420
|
+
-- One NULL column satisfies MATCH SIMPLE even with empty parent
|
|
421
|
+
INSERT INTO ms_mc_child VALUES (1, 42, NULL);
|
|
422
|
+
SELECT * FROM ms_mc_child;
|
|
423
|
+
→ [{"id":1,"ref_a":42,"ref_b":null}]
|
|
424
|
+
|
|
425
|
+
DROP TABLE ms_mc_child;
|
|
426
|
+
DROP TABLE ms_mc_parent;
|
|
427
|
+
|
|
428
|
+
-- --- MATCH SIMPLE: multi-column FK, all NULLs ---
|
|
429
|
+
|
|
430
|
+
CREATE TABLE ms_mc2_parent (
|
|
431
|
+
a INTEGER,
|
|
432
|
+
b INTEGER,
|
|
433
|
+
PRIMARY KEY (a, b)
|
|
434
|
+
);
|
|
435
|
+
|
|
436
|
+
CREATE TABLE ms_mc2_child (
|
|
437
|
+
id INTEGER PRIMARY KEY,
|
|
438
|
+
ref_a INTEGER NULL,
|
|
439
|
+
ref_b INTEGER NULL,
|
|
440
|
+
FOREIGN KEY (ref_a, ref_b) REFERENCES ms_mc2_parent(a, b) ON DELETE RESTRICT
|
|
441
|
+
);
|
|
442
|
+
|
|
443
|
+
-- All NULLs satisfies MATCH SIMPLE
|
|
444
|
+
INSERT INTO ms_mc2_child VALUES (1, NULL, NULL);
|
|
445
|
+
SELECT * FROM ms_mc2_child;
|
|
446
|
+
→ [{"id":1,"ref_a":null,"ref_b":null}]
|
|
447
|
+
|
|
448
|
+
DROP TABLE ms_mc2_child;
|
|
449
|
+
DROP TABLE ms_mc2_parent;
|
|
450
|
+
|
|
451
|
+
-- --- MATCH SIMPLE: multi-column FK, no NULLs, no match (regression guard) ---
|
|
452
|
+
|
|
453
|
+
CREATE TABLE ms_mc3_parent (
|
|
454
|
+
a INTEGER,
|
|
455
|
+
b INTEGER,
|
|
456
|
+
PRIMARY KEY (a, b)
|
|
457
|
+
);
|
|
458
|
+
|
|
459
|
+
CREATE TABLE ms_mc3_child (
|
|
460
|
+
id INTEGER PRIMARY KEY,
|
|
461
|
+
ref_a INTEGER,
|
|
462
|
+
ref_b INTEGER,
|
|
463
|
+
FOREIGN KEY (ref_a, ref_b) REFERENCES ms_mc3_parent(a, b) ON DELETE RESTRICT
|
|
464
|
+
);
|
|
465
|
+
|
|
466
|
+
-- No NULLs, no matching parent row — must fail
|
|
467
|
+
INSERT INTO ms_mc3_child VALUES (1, 1, 2);
|
|
468
|
+
-- error: CHECK constraint failed: _fk_ms_mc3_child_ref_a_ref_b
|
|
469
|
+
|
|
470
|
+
DROP TABLE ms_mc3_child;
|
|
471
|
+
DROP TABLE ms_mc3_parent;
|
|
472
|
+
|
|
473
|
+
-- ===================================================================
|
|
474
|
+
-- Multi-column FK
|
|
475
|
+
-- ===================================================================
|
|
476
|
+
|
|
477
|
+
CREATE TABLE mc_parent (
|
|
478
|
+
a INTEGER,
|
|
479
|
+
b INTEGER,
|
|
480
|
+
data TEXT,
|
|
481
|
+
PRIMARY KEY (a, b)
|
|
482
|
+
);
|
|
483
|
+
|
|
484
|
+
CREATE TABLE mc_child (
|
|
485
|
+
id INTEGER PRIMARY KEY,
|
|
486
|
+
ref_a INTEGER,
|
|
487
|
+
ref_b INTEGER,
|
|
488
|
+
FOREIGN KEY (ref_a, ref_b) REFERENCES mc_parent(a, b) ON DELETE CASCADE
|
|
489
|
+
);
|
|
490
|
+
|
|
491
|
+
INSERT INTO mc_parent VALUES (1, 10, 'P1'), (2, 20, 'P2');
|
|
492
|
+
INSERT INTO mc_child VALUES (1, 1, 10), (2, 2, 20);
|
|
493
|
+
|
|
494
|
+
-- Valid composite FK insert
|
|
495
|
+
SELECT count(*) FROM mc_child;
|
|
496
|
+
→ [{"count(*)":2}]
|
|
497
|
+
|
|
498
|
+
-- Invalid composite FK insert (no matching parent)
|
|
499
|
+
INSERT INTO mc_child VALUES (3, 1, 99);
|
|
500
|
+
-- error: CHECK constraint failed: _fk_mc_child_ref_a_ref_b
|
|
501
|
+
|
|
502
|
+
-- CASCADE DELETE with composite FK
|
|
503
|
+
DELETE FROM mc_parent WHERE a = 1 AND b = 10;
|
|
504
|
+
SELECT count(*) FROM mc_child WHERE ref_a = 1;
|
|
505
|
+
→ [{"count(*)":0}]
|
|
506
|
+
|
|
507
|
+
-- Other child rows intact
|
|
508
|
+
SELECT count(*) FROM mc_child WHERE ref_a = 2;
|
|
509
|
+
→ [{"count(*)":1}]
|
|
510
|
+
|
|
511
|
+
DROP TABLE mc_child;
|
|
512
|
+
DROP TABLE mc_parent;
|
|
513
|
+
|
|
514
|
+
-- ===================================================================
|
|
515
|
+
-- Pragma on/off behavior
|
|
516
|
+
-- ===================================================================
|
|
517
|
+
|
|
518
|
+
PRAGMA foreign_keys = false;
|
|
519
|
+
|
|
520
|
+
CREATE TABLE p_parent (
|
|
521
|
+
id INTEGER PRIMARY KEY,
|
|
522
|
+
name TEXT
|
|
523
|
+
);
|
|
524
|
+
|
|
525
|
+
CREATE TABLE p_child (
|
|
526
|
+
id INTEGER PRIMARY KEY,
|
|
527
|
+
parent_id INTEGER,
|
|
528
|
+
FOREIGN KEY (parent_id) REFERENCES p_parent(id) ON DELETE RESTRICT
|
|
529
|
+
);
|
|
530
|
+
|
|
531
|
+
-- With FK off, should be able to insert orphan row
|
|
532
|
+
INSERT INTO p_child VALUES (1, 999);
|
|
533
|
+
SELECT * FROM p_child;
|
|
534
|
+
→ [{"id":1,"parent_id":999}]
|
|
535
|
+
|
|
536
|
+
-- Turn FK on - existing data is not retroactively checked
|
|
537
|
+
PRAGMA foreign_keys = true;
|
|
538
|
+
|
|
539
|
+
-- New inserts should be checked
|
|
540
|
+
INSERT INTO p_child VALUES (2, 888);
|
|
541
|
+
-- error: CHECK constraint failed: _fk_p_child_parent_id
|
|
542
|
+
|
|
543
|
+
DROP TABLE p_child;
|
|
544
|
+
DROP TABLE p_parent;
|
|
545
|
+
|
|
546
|
+
-- ===================================================================
|
|
547
|
+
-- Column-level FK syntax
|
|
548
|
+
-- ===================================================================
|
|
549
|
+
|
|
550
|
+
PRAGMA foreign_keys = true;
|
|
551
|
+
|
|
552
|
+
CREATE TABLE col_parent (
|
|
553
|
+
id INTEGER PRIMARY KEY,
|
|
554
|
+
name TEXT
|
|
555
|
+
);
|
|
556
|
+
|
|
557
|
+
CREATE TABLE col_child (
|
|
558
|
+
id INTEGER PRIMARY KEY,
|
|
559
|
+
ref_id INTEGER REFERENCES col_parent(id) ON DELETE CASCADE
|
|
560
|
+
);
|
|
561
|
+
|
|
562
|
+
INSERT INTO col_parent VALUES (1, 'A'), (2, 'B');
|
|
563
|
+
INSERT INTO col_child VALUES (10, 1), (20, 2);
|
|
564
|
+
|
|
565
|
+
-- CASCADE DELETE via column-level FK
|
|
566
|
+
DELETE FROM col_parent WHERE id = 1;
|
|
567
|
+
SELECT count(*) FROM col_child WHERE ref_id = 1;
|
|
568
|
+
→ [{"count(*)":0}]
|
|
569
|
+
|
|
570
|
+
-- Remaining data intact
|
|
571
|
+
SELECT count(*) FROM col_child WHERE ref_id = 2;
|
|
572
|
+
→ [{"count(*)":1}]
|
|
573
|
+
|
|
574
|
+
DROP TABLE col_child;
|
|
575
|
+
DROP TABLE col_parent;
|
|
576
|
+
|
|
577
|
+
-- ===================================================================
|
|
578
|
+
-- Cycle detection
|
|
579
|
+
-- ===================================================================
|
|
580
|
+
|
|
581
|
+
PRAGMA foreign_keys = true;
|
|
582
|
+
|
|
583
|
+
CREATE TABLE cycle_a (
|
|
584
|
+
id INTEGER PRIMARY KEY,
|
|
585
|
+
b_id INTEGER
|
|
586
|
+
);
|
|
587
|
+
|
|
588
|
+
CREATE TABLE cycle_b (
|
|
589
|
+
id INTEGER PRIMARY KEY,
|
|
590
|
+
a_id INTEGER,
|
|
591
|
+
FOREIGN KEY (a_id) REFERENCES cycle_a(id) ON DELETE CASCADE
|
|
592
|
+
);
|
|
593
|
+
|
|
594
|
+
-- Add FK from cycle_a to cycle_b after both tables exist
|
|
595
|
+
-- (Note: cycle_a -> cycle_b -> cycle_a would form a cascade cycle)
|
|
596
|
+
-- For now, test that simple cascades don't trigger false cycle detection
|
|
597
|
+
INSERT INTO cycle_a VALUES (1, 10);
|
|
598
|
+
INSERT INTO cycle_b VALUES (10, 1);
|
|
599
|
+
|
|
600
|
+
-- Commit setup
|
|
601
|
+
SELECT count(*) FROM cycle_b;
|
|
602
|
+
→ [{"count(*)":1}]
|
|
603
|
+
|
|
604
|
+
-- Deleting from cycle_a should cascade to cycle_b
|
|
605
|
+
DELETE FROM cycle_a WHERE id = 1;
|
|
606
|
+
SELECT count(*) FROM cycle_b WHERE a_id = 1;
|
|
607
|
+
→ [{"count(*)":0}]
|
|
608
|
+
|
|
609
|
+
DROP TABLE cycle_b;
|
|
610
|
+
DROP TABLE cycle_a;
|
|
611
|
+
|
|
612
|
+
-- ===================================================================
|
|
613
|
+
-- UPSERT DO UPDATE with FK CASCADE on parent table
|
|
614
|
+
-- ===================================================================
|
|
615
|
+
|
|
616
|
+
-- CASCADE UPDATE via UPSERT DO UPDATE on parent
|
|
617
|
+
|
|
618
|
+
CREATE TABLE upsert_parent (
|
|
619
|
+
id INTEGER PRIMARY KEY,
|
|
620
|
+
name TEXT
|
|
621
|
+
);
|
|
622
|
+
|
|
623
|
+
CREATE TABLE upsert_child (
|
|
624
|
+
id INTEGER PRIMARY KEY,
|
|
625
|
+
parent_id INTEGER,
|
|
626
|
+
FOREIGN KEY (parent_id) REFERENCES upsert_parent(id) ON UPDATE CASCADE
|
|
627
|
+
);
|
|
628
|
+
|
|
629
|
+
INSERT INTO upsert_parent VALUES (1, 'Alice'), (2, 'Bob');
|
|
630
|
+
INSERT INTO upsert_child VALUES (10, 1), (20, 1), (30, 2);
|
|
631
|
+
|
|
632
|
+
-- UPSERT that updates parent PK: child rows should cascade
|
|
633
|
+
INSERT INTO upsert_parent (id, name) VALUES (1, 'Alice_updated')
|
|
634
|
+
ON CONFLICT (id) DO UPDATE SET id = 100, name = NEW.name;
|
|
635
|
+
|
|
636
|
+
-- Parent row should have new id
|
|
637
|
+
SELECT id, name FROM upsert_parent WHERE id = 100;
|
|
638
|
+
→ [{"id":100,"name":"Alice_updated"}]
|
|
639
|
+
|
|
640
|
+
-- Child rows should have cascaded to new parent_id
|
|
641
|
+
SELECT id, parent_id FROM upsert_child ORDER BY id;
|
|
642
|
+
→ [{"id":10,"parent_id":100},{"id":20,"parent_id":100},{"id":30,"parent_id":2}]
|
|
643
|
+
|
|
644
|
+
DROP TABLE upsert_child;
|
|
645
|
+
DROP TABLE upsert_parent;
|
|
646
|
+
|
|
647
|
+
-- SET NULL via UPSERT DO UPDATE on parent
|
|
648
|
+
|
|
649
|
+
CREATE TABLE upsert_sn_parent (
|
|
650
|
+
id INTEGER PRIMARY KEY,
|
|
651
|
+
name TEXT
|
|
652
|
+
);
|
|
653
|
+
|
|
654
|
+
CREATE TABLE upsert_sn_child (
|
|
655
|
+
id INTEGER PRIMARY KEY,
|
|
656
|
+
parent_id INTEGER NULL,
|
|
657
|
+
FOREIGN KEY (parent_id) REFERENCES upsert_sn_parent(id) ON UPDATE SET NULL
|
|
658
|
+
);
|
|
659
|
+
|
|
660
|
+
INSERT INTO upsert_sn_parent VALUES (1, 'X'), (2, 'Y');
|
|
661
|
+
INSERT INTO upsert_sn_child VALUES (1, 1), (2, 1), (3, 2);
|
|
662
|
+
|
|
663
|
+
-- UPSERT that updates parent PK: child FK columns should become NULL
|
|
664
|
+
INSERT INTO upsert_sn_parent (id, name) VALUES (1, 'X_new')
|
|
665
|
+
ON CONFLICT (id) DO UPDATE SET id = 10, name = NEW.name;
|
|
666
|
+
|
|
667
|
+
SELECT id, parent_id FROM upsert_sn_child ORDER BY id;
|
|
668
|
+
→ [{"id":1,"parent_id":null},{"id":2,"parent_id":null},{"id":3,"parent_id":2}]
|
|
669
|
+
|
|
670
|
+
DROP TABLE upsert_sn_child;
|
|
671
|
+
DROP TABLE upsert_sn_parent;
|
|
672
|
+
|
|
673
|
+
-- ===================================================================
|
|
674
|
+
-- INSERT OR REPLACE with FK CASCADE on parent table
|
|
675
|
+
-- ===================================================================
|
|
676
|
+
|
|
677
|
+
-- CASCADE DELETE via INSERT OR REPLACE on parent
|
|
678
|
+
|
|
679
|
+
CREATE TABLE repl_del_parent (
|
|
680
|
+
id INTEGER PRIMARY KEY,
|
|
681
|
+
name TEXT
|
|
682
|
+
);
|
|
683
|
+
|
|
684
|
+
CREATE TABLE repl_del_child (
|
|
685
|
+
id INTEGER PRIMARY KEY,
|
|
686
|
+
parent_id INTEGER,
|
|
687
|
+
FOREIGN KEY (parent_id) REFERENCES repl_del_parent(id) ON DELETE CASCADE
|
|
688
|
+
);
|
|
689
|
+
|
|
690
|
+
INSERT INTO repl_del_parent VALUES (1, 'Alice'), (2, 'Bob');
|
|
691
|
+
INSERT INTO repl_del_child VALUES (10, 1), (20, 1), (30, 2);
|
|
692
|
+
|
|
693
|
+
-- REPLACE is semantically delete+insert, so ON DELETE CASCADE should fire
|
|
694
|
+
INSERT OR REPLACE INTO repl_del_parent VALUES (1, 'Alice_replaced');
|
|
695
|
+
|
|
696
|
+
-- Child rows referencing parent_id=1 should be cascade-deleted
|
|
697
|
+
SELECT id, parent_id FROM repl_del_child ORDER BY id;
|
|
698
|
+
→ [{"id":30,"parent_id":2}]
|
|
699
|
+
|
|
700
|
+
DROP TABLE repl_del_child;
|
|
701
|
+
DROP TABLE repl_del_parent;
|
|
702
|
+
|
|
703
|
+
-- SET NULL via INSERT OR REPLACE on parent (ON DELETE SET NULL)
|
|
704
|
+
|
|
705
|
+
CREATE TABLE repl_sn_parent (
|
|
706
|
+
id INTEGER PRIMARY KEY,
|
|
707
|
+
name TEXT
|
|
708
|
+
);
|
|
709
|
+
|
|
710
|
+
CREATE TABLE repl_sn_child (
|
|
711
|
+
id INTEGER PRIMARY KEY,
|
|
712
|
+
parent_id INTEGER NULL,
|
|
713
|
+
FOREIGN KEY (parent_id) REFERENCES repl_sn_parent(id) ON DELETE SET NULL
|
|
714
|
+
);
|
|
715
|
+
|
|
716
|
+
INSERT INTO repl_sn_parent VALUES (1, 'X'), (2, 'Y');
|
|
717
|
+
INSERT INTO repl_sn_child VALUES (1, 1), (2, 1), (3, 2);
|
|
718
|
+
|
|
719
|
+
-- REPLACE parent — child FK columns should become NULL via ON DELETE SET NULL
|
|
720
|
+
INSERT OR REPLACE INTO repl_sn_parent VALUES (1, 'X_replaced');
|
|
721
|
+
|
|
722
|
+
SELECT id, parent_id FROM repl_sn_child ORDER BY id;
|
|
723
|
+
→ [{"id":1,"parent_id":null},{"id":2,"parent_id":null},{"id":3,"parent_id":2}]
|
|
724
|
+
|
|
725
|
+
DROP TABLE repl_sn_child;
|
|
726
|
+
DROP TABLE repl_sn_parent;
|
|
727
|
+
|
|
728
|
+
-- ===================================================================
|
|
729
|
+
-- Phase: Parent-side FK UPDATE column-mask (regression for fix-fk-parent-update-column-mask)
|
|
730
|
+
-- ===================================================================
|
|
731
|
+
-- Updates that don't touch any FK-referenced parent column must NOT trip
|
|
732
|
+
-- the synthesised parent-side NOT EXISTS check.
|
|
733
|
+
|
|
734
|
+
CREATE TABLE pmask_parent (id TEXT PRIMARY KEY, name TEXT NOT NULL);
|
|
735
|
+
CREATE TABLE pmask_child (parent_id TEXT PRIMARY KEY REFERENCES pmask_parent(id));
|
|
736
|
+
|
|
737
|
+
INSERT INTO pmask_parent VALUES ('a', 'x');
|
|
738
|
+
INSERT INTO pmask_child VALUES ('a');
|
|
739
|
+
|
|
740
|
+
-- UPDATE on a non-referenced column succeeds even with referencing child rows
|
|
741
|
+
UPDATE pmask_parent SET name = 'y' WHERE id = 'a';
|
|
742
|
+
|
|
743
|
+
SELECT id, name FROM pmask_parent;
|
|
744
|
+
→ [{"id":"a","name":"y"}]
|
|
745
|
+
|
|
746
|
+
-- UPDATE that touches the referenced PK still trips RESTRICT
|
|
747
|
+
UPDATE pmask_parent SET id = 'b' WHERE id = 'a';
|
|
748
|
+
-- error: violates RESTRICT from 'pmask_child'
|
|
749
|
+
|
|
750
|
+
DROP TABLE pmask_child;
|
|
751
|
+
DROP TABLE pmask_parent;
|
|
752
|
+
|
|
753
|
+
-- Two FKs referencing different parent columns: updating only one must
|
|
754
|
+
-- skip the other parent-side check.
|
|
755
|
+
|
|
756
|
+
CREATE TABLE pmask2_parent (
|
|
757
|
+
id INTEGER PRIMARY KEY,
|
|
758
|
+
code TEXT NOT NULL UNIQUE,
|
|
759
|
+
label TEXT NOT NULL UNIQUE,
|
|
760
|
+
extra TEXT NULL
|
|
761
|
+
);
|
|
762
|
+
CREATE TABLE pmask2_child_code (
|
|
763
|
+
cid INTEGER PRIMARY KEY,
|
|
764
|
+
parent_code TEXT REFERENCES pmask2_parent(code)
|
|
765
|
+
);
|
|
766
|
+
CREATE TABLE pmask2_child_label (
|
|
767
|
+
cid INTEGER PRIMARY KEY,
|
|
768
|
+
parent_label TEXT REFERENCES pmask2_parent(label)
|
|
769
|
+
);
|
|
770
|
+
|
|
771
|
+
INSERT INTO pmask2_parent VALUES (1, 'C1', 'L1', NULL);
|
|
772
|
+
INSERT INTO pmask2_child_code VALUES (10, 'C1');
|
|
773
|
+
INSERT INTO pmask2_child_label VALUES (20, 'L1');
|
|
774
|
+
|
|
775
|
+
-- Updating `label` only: pmask2_child_code's parent-side check (on `code`) must skip;
|
|
776
|
+
-- pmask2_child_label's check fires on `label` change → RESTRICT.
|
|
777
|
+
UPDATE pmask2_parent SET label = 'L2' WHERE id = 1;
|
|
778
|
+
-- error: violates RESTRICT from 'pmask2_child_label'
|
|
779
|
+
|
|
780
|
+
-- Updating `code` only: symmetric — pmask2_child_code's check fires.
|
|
781
|
+
UPDATE pmask2_parent SET code = 'C2' WHERE id = 1;
|
|
782
|
+
-- error: violates RESTRICT from 'pmask2_child_code'
|
|
783
|
+
|
|
784
|
+
-- Updating neither referenced column: both checks must skip.
|
|
785
|
+
UPDATE pmask2_parent SET extra = 'ok' WHERE id = 1;
|
|
786
|
+
|
|
787
|
+
SELECT id, code, label, extra FROM pmask2_parent;
|
|
788
|
+
→ [{"id":1,"code":"C1","label":"L1","extra":"ok"}]
|
|
789
|
+
|
|
790
|
+
DROP TABLE pmask2_child_label;
|
|
791
|
+
DROP TABLE pmask2_child_code;
|
|
792
|
+
DROP TABLE pmask2_parent;
|
|
793
|
+
|
|
794
|
+
-- ===================================================================
|
|
795
|
+
-- Phase: change-detection short-circuit coerces before comparing
|
|
796
|
+
-- (regression for bug-fk-restrict-change-detection-uncoerced)
|
|
797
|
+
-- ===================================================================
|
|
798
|
+
-- The referenced-column-change short-circuit used to compare the stored OLD
|
|
799
|
+
-- value against the raw, uncoerced NEW value, so a value spelled differently
|
|
800
|
+
-- from its stored form (integer 1 written as text '1'; a JSON object written
|
|
801
|
+
-- with reordered keys) read as "changed" and wrongly tripped RESTRICT even
|
|
802
|
+
-- though the stored value would not actually move. Both tables below carry a
|
|
803
|
+
-- RESTRICT child and a CASCADE child on the SAME parent so the gate that
|
|
804
|
+
-- batches pure-RESTRICT statements (see 41.9-fk-restrict-batched.sqllogic)
|
|
805
|
+
-- excludes them: the update runs the per-row transitive pre-walk
|
|
806
|
+
-- (assertNoRestrictedChildrenForParentMutation + the cascade-recursion arm of
|
|
807
|
+
-- assertTransitiveRestrictsForParentMutation), not the batched accumulator.
|
|
808
|
+
|
|
809
|
+
CREATE TABLE crc_parent (id INTEGER PRIMARY KEY, other INTEGER);
|
|
810
|
+
CREATE TABLE crc_restrict_child (cid INTEGER PRIMARY KEY, p_id INTEGER,
|
|
811
|
+
FOREIGN KEY (p_id) REFERENCES crc_parent(id) ON UPDATE RESTRICT);
|
|
812
|
+
CREATE TABLE crc_cascade_child (cid INTEGER PRIMARY KEY, p_id INTEGER,
|
|
813
|
+
FOREIGN KEY (p_id) REFERENCES crc_parent(id) ON UPDATE CASCADE);
|
|
814
|
+
|
|
815
|
+
INSERT INTO crc_parent VALUES (1, 100);
|
|
816
|
+
INSERT INTO crc_restrict_child VALUES (10, 1);
|
|
817
|
+
INSERT INTO crc_cascade_child VALUES (20, 1);
|
|
818
|
+
|
|
819
|
+
-- Rewriting the integer key as the text '1' does not change the stored value:
|
|
820
|
+
-- must NOT trip RESTRICT.
|
|
821
|
+
UPDATE crc_parent SET id = '1' WHERE id = 1;
|
|
822
|
+
SELECT id FROM crc_parent;
|
|
823
|
+
→ [{"id":1}]
|
|
824
|
+
|
|
825
|
+
-- The cascade child must be untouched too — no key change means no
|
|
826
|
+
-- propagating UPDATE should fire (the short-circuit is shared by both arms).
|
|
827
|
+
SELECT p_id FROM crc_cascade_child;
|
|
828
|
+
→ [{"p_id":1}]
|
|
829
|
+
|
|
830
|
+
-- Sanity: a genuine key change with a live RESTRICT child still raises the
|
|
831
|
+
-- error, so the fix cannot silently disable enforcement. Mixed actions are
|
|
832
|
+
-- not batchable, so this is the plan-time synthesized parent-side NOT EXISTS
|
|
833
|
+
-- (ConstraintCheckNode), not the runtime pre-walk's friendlier message.
|
|
834
|
+
UPDATE crc_parent SET id = 2 WHERE id = 1;
|
|
835
|
+
-- error: CHECK constraint failed: _fk_crc_restrict_child_p_id
|
|
836
|
+
|
|
837
|
+
DROP TABLE crc_cascade_child;
|
|
838
|
+
DROP TABLE crc_restrict_child;
|
|
839
|
+
DROP TABLE crc_parent;
|
|
840
|
+
|
|
841
|
+
-- Same short-circuit, JSON-typed key: a value re-typed through the column's
|
|
842
|
+
-- logical type (validateAndParse) rather than compared as raw text — a JSON
|
|
843
|
+
-- object rewritten with different whitespace / key order is the same stored
|
|
844
|
+
-- value.
|
|
845
|
+
|
|
846
|
+
CREATE TABLE crcj_parent (id JSON PRIMARY KEY, other INTEGER);
|
|
847
|
+
CREATE TABLE crcj_restrict_child (cid INTEGER PRIMARY KEY, p_id JSON,
|
|
848
|
+
FOREIGN KEY (p_id) REFERENCES crcj_parent(id) ON UPDATE RESTRICT);
|
|
849
|
+
CREATE TABLE crcj_cascade_child (cid INTEGER PRIMARY KEY, p_id JSON,
|
|
850
|
+
FOREIGN KEY (p_id) REFERENCES crcj_parent(id) ON UPDATE CASCADE);
|
|
851
|
+
|
|
852
|
+
INSERT INTO crcj_parent VALUES ('{"a":1,"b":2}', 100);
|
|
853
|
+
INSERT INTO crcj_restrict_child VALUES (10, '{"a":1,"b":2}');
|
|
854
|
+
INSERT INTO crcj_cascade_child VALUES (20, '{"a":1,"b":2}');
|
|
855
|
+
|
|
856
|
+
-- Reorder-equal JSON key: must NOT trip RESTRICT.
|
|
857
|
+
UPDATE crcj_parent SET id = '{"b": 2, "a": 1}' WHERE other = 100;
|
|
858
|
+
SELECT id FROM crcj_parent;
|
|
859
|
+
→ [{"id":{"b":2,"a":1}}]
|
|
860
|
+
|
|
861
|
+
-- Cascade child untouched (no key change ⇒ no propagating UPDATE).
|
|
862
|
+
SELECT p_id FROM crcj_cascade_child;
|
|
863
|
+
→ [{"p_id":{"a":1,"b":2}}]
|
|
864
|
+
|
|
865
|
+
-- Sanity: a genuine JSON key change with a live RESTRICT child still raises
|
|
866
|
+
-- the error (plan-time NOT EXISTS, as above — mixed actions are not batchable).
|
|
867
|
+
UPDATE crcj_parent SET id = '{"a":9,"b":9}' WHERE other = 100;
|
|
868
|
+
-- error: CHECK constraint failed: _fk_crcj_restrict_child_p_id
|
|
869
|
+
|
|
870
|
+
DROP TABLE crcj_cascade_child;
|
|
871
|
+
DROP TABLE crcj_restrict_child;
|
|
872
|
+
DROP TABLE crcj_parent;
|
|
873
|
+
|
|
874
|
+
-- ON DELETE RESTRICT has no NEW row, so the change-detection short-circuit
|
|
875
|
+
-- never runs for it — guard that the refactor left the delete path alone.
|
|
876
|
+
CREATE TABLE crcd_parent (id INTEGER PRIMARY KEY);
|
|
877
|
+
CREATE TABLE crcd_child (cid INTEGER PRIMARY KEY, p_id INTEGER,
|
|
878
|
+
FOREIGN KEY (p_id) REFERENCES crcd_parent(id) ON DELETE RESTRICT);
|
|
879
|
+
|
|
880
|
+
INSERT INTO crcd_parent VALUES (1);
|
|
881
|
+
INSERT INTO crcd_child VALUES (10, 1);
|
|
882
|
+
|
|
883
|
+
DELETE FROM crcd_parent WHERE id = 1;
|
|
884
|
+
-- error: violates RESTRICT from 'crcd_child'
|
|
885
|
+
|
|
886
|
+
DROP TABLE crcd_child;
|
|
887
|
+
DROP TABLE crcd_parent;
|
|
888
|
+
|
|
889
|
+
-- ===================================================================
|
|
890
|
+
-- Phase: ON UPDATE action gate — untouched referenced column must not
|
|
891
|
+
-- fire the action at all (regression for fk-update-actions-fire-when-key-unchanged)
|
|
892
|
+
-- ===================================================================
|
|
893
|
+
-- executeForeignKeyActions used to re-issue the child DML for CASCADE / SET NULL /
|
|
894
|
+
-- SET DEFAULT after every parent UPDATE, even when the referenced column never
|
|
895
|
+
-- changed. Updating an unrelated parent column wrongly rewrote (or nulled) child
|
|
896
|
+
-- rows that were never re-keyed. Each block below asserts the untouched case is a
|
|
897
|
+
-- no-op and a genuine re-key still fires the action.
|
|
898
|
+
|
|
899
|
+
-- SET DEFAULT: an untouched referenced column must leave the child alone; a
|
|
900
|
+
-- genuine re-key must still re-point the child to the default (a live parent key).
|
|
901
|
+
CREATE TABLE fkgd_parent (id INTEGER PRIMARY KEY, other INTEGER);
|
|
902
|
+
CREATE TABLE fkgd_child (cid INTEGER PRIMARY KEY, p_id INTEGER DEFAULT 99,
|
|
903
|
+
FOREIGN KEY (p_id) REFERENCES fkgd_parent(id) ON UPDATE SET DEFAULT);
|
|
904
|
+
|
|
905
|
+
INSERT INTO fkgd_parent VALUES (1, 100), (99, 200);
|
|
906
|
+
INSERT INTO fkgd_child VALUES (10, 1);
|
|
907
|
+
|
|
908
|
+
UPDATE fkgd_parent SET other = 111 WHERE id = 1;
|
|
909
|
+
SELECT p_id FROM fkgd_child WHERE cid = 10;
|
|
910
|
+
→ [{"p_id":1}]
|
|
911
|
+
|
|
912
|
+
UPDATE fkgd_parent SET id = 7 WHERE id = 1;
|
|
913
|
+
SELECT p_id FROM fkgd_child WHERE cid = 10;
|
|
914
|
+
→ [{"p_id":99}]
|
|
915
|
+
|
|
916
|
+
DROP TABLE fkgd_child;
|
|
917
|
+
DROP TABLE fkgd_parent;
|
|
918
|
+
|
|
919
|
+
-- SET NULL, nullable child column: an untouched referenced column must leave the
|
|
920
|
+
-- child alone; a genuine re-key must still null it out.
|
|
921
|
+
CREATE TABLE fkgn_parent (id INTEGER PRIMARY KEY, other INTEGER);
|
|
922
|
+
CREATE TABLE fkgn_child (cid INTEGER PRIMARY KEY, p_id INTEGER NULL,
|
|
923
|
+
FOREIGN KEY (p_id) REFERENCES fkgn_parent(id) ON UPDATE SET NULL);
|
|
924
|
+
|
|
925
|
+
INSERT INTO fkgn_parent VALUES (1, 100);
|
|
926
|
+
INSERT INTO fkgn_child VALUES (10, 1);
|
|
927
|
+
|
|
928
|
+
UPDATE fkgn_parent SET other = 111 WHERE id = 1;
|
|
929
|
+
SELECT p_id FROM fkgn_child WHERE cid = 10;
|
|
930
|
+
→ [{"p_id":1}]
|
|
931
|
+
|
|
932
|
+
UPDATE fkgn_parent SET id = 7 WHERE id = 1;
|
|
933
|
+
SELECT p_id FROM fkgn_child WHERE cid = 10;
|
|
934
|
+
→ [{"p_id":null}]
|
|
935
|
+
|
|
936
|
+
DROP TABLE fkgn_child;
|
|
937
|
+
DROP TABLE fkgn_parent;
|
|
938
|
+
|
|
939
|
+
-- SET NULL, non-nullable child column: before the fix, an update leaving the
|
|
940
|
+
-- referenced column alone still re-issued the child SET NULL and tripped NOT NULL.
|
|
941
|
+
-- It must now succeed as a no-op on the child. This is the case most likely to
|
|
942
|
+
-- regress unnoticed, since the symptom was an error rather than silently bad data.
|
|
943
|
+
-- NOT NULL is spelled out even though it is Quereus's column default, so the case
|
|
944
|
+
-- keeps testing what it claims if that default ever changes.
|
|
945
|
+
CREATE TABLE fkgnn_parent (id INTEGER PRIMARY KEY, other INTEGER);
|
|
946
|
+
CREATE TABLE fkgnn_child (cid INTEGER PRIMARY KEY, p_id INTEGER NOT NULL,
|
|
947
|
+
FOREIGN KEY (p_id) REFERENCES fkgnn_parent(id) ON UPDATE SET NULL);
|
|
948
|
+
|
|
949
|
+
INSERT INTO fkgnn_parent VALUES (1, 100);
|
|
950
|
+
INSERT INTO fkgnn_child VALUES (10, 1);
|
|
951
|
+
|
|
952
|
+
UPDATE fkgnn_parent SET other = 111 WHERE id = 1;
|
|
953
|
+
SELECT p_id FROM fkgnn_child WHERE cid = 10;
|
|
954
|
+
→ [{"p_id":1}]
|
|
955
|
+
|
|
956
|
+
DROP TABLE fkgnn_child;
|
|
957
|
+
DROP TABLE fkgnn_parent;
|
|
958
|
+
|
|
959
|
+
-- CASCADE: an untouched referenced column must leave the child value alone (a
|
|
960
|
+
-- phantom rewrite-to-same-value used to fire here — see the runtime spec for the
|
|
961
|
+
-- data-change-event assertion this value-only check cannot make); a genuine
|
|
962
|
+
-- re-key must still cascade.
|
|
963
|
+
CREATE TABLE fkgc_parent (id INTEGER PRIMARY KEY, other INTEGER);
|
|
964
|
+
CREATE TABLE fkgc_child (cid INTEGER PRIMARY KEY, p_id INTEGER,
|
|
965
|
+
FOREIGN KEY (p_id) REFERENCES fkgc_parent(id) ON UPDATE CASCADE);
|
|
966
|
+
|
|
967
|
+
INSERT INTO fkgc_parent VALUES (1, 100);
|
|
968
|
+
INSERT INTO fkgc_child VALUES (10, 1);
|
|
969
|
+
|
|
970
|
+
UPDATE fkgc_parent SET other = 111 WHERE id = 1;
|
|
971
|
+
SELECT p_id FROM fkgc_child WHERE cid = 10;
|
|
972
|
+
→ [{"p_id":1}]
|
|
973
|
+
|
|
974
|
+
UPDATE fkgc_parent SET id = 7 WHERE id = 1;
|
|
975
|
+
SELECT p_id FROM fkgc_child WHERE cid = 10;
|
|
976
|
+
→ [{"p_id":7}]
|
|
977
|
+
|
|
978
|
+
DROP TABLE fkgc_child;
|
|
979
|
+
DROP TABLE fkgc_parent;
|
|
980
|
+
|
|
981
|
+
-- Composite CASCADE: the gate asks whether ANY referenced column changed, so a
|
|
982
|
+
-- parent update that moves only the second key column must still cascade, while
|
|
983
|
+
-- one that moves neither must not fire at all.
|
|
984
|
+
CREATE TABLE fkgmc_parent (a INTEGER, b INTEGER, other INTEGER, PRIMARY KEY (a, b));
|
|
985
|
+
CREATE TABLE fkgmc_child (cid INTEGER PRIMARY KEY, ref_a INTEGER, ref_b INTEGER,
|
|
986
|
+
FOREIGN KEY (ref_a, ref_b) REFERENCES fkgmc_parent(a, b) ON UPDATE CASCADE);
|
|
987
|
+
|
|
988
|
+
INSERT INTO fkgmc_parent VALUES (1, 2, 100);
|
|
989
|
+
INSERT INTO fkgmc_child VALUES (10, 1, 2);
|
|
990
|
+
|
|
991
|
+
UPDATE fkgmc_parent SET other = 111 WHERE a = 1 AND b = 2;
|
|
992
|
+
SELECT ref_a, ref_b FROM fkgmc_child WHERE cid = 10;
|
|
993
|
+
→ [{"ref_a":1,"ref_b":2}]
|
|
994
|
+
|
|
995
|
+
UPDATE fkgmc_parent SET b = 20 WHERE a = 1 AND b = 2;
|
|
996
|
+
SELECT ref_a, ref_b FROM fkgmc_child WHERE cid = 10;
|
|
997
|
+
→ [{"ref_a":1,"ref_b":20}]
|
|
998
|
+
|
|
999
|
+
DROP TABLE fkgmc_child;
|
|
1000
|
+
DROP TABLE fkgmc_parent;
|
|
1001
|
+
|