@quereus/quereus 4.20.1 → 4.20.3

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.
Files changed (380) hide show
  1. package/README.md +2 -0
  2. package/dist/src/schema/schema-differ.d.ts.map +1 -1
  3. package/dist/src/schema/schema-differ.js +17 -69
  4. package/dist/src/schema/schema-differ.js.map +1 -1
  5. package/package.json +7 -4
  6. package/test/README.md +173 -0
  7. package/test/logic/01-basic.sqllogic +39 -0
  8. package/test/logic/01.1-select-projection-extras.sqllogic +130 -0
  9. package/test/logic/01.2-strings.sqllogic +43 -0
  10. package/test/logic/01.5-insert-select.sqllogic +237 -0
  11. package/test/logic/01.6-update-extras.sqllogic +118 -0
  12. package/test/logic/01.7-update-from.sqllogic +67 -0
  13. package/test/logic/01.8-delete-extras.sqllogic +122 -0
  14. package/test/logic/01.8.1-delete-range-cow.sqllogic +115 -0
  15. package/test/logic/01.9-query-expr-dml-gates.sqllogic +32 -0
  16. package/test/logic/01.9-query-expr-dml.sqllogic +171 -0
  17. package/test/logic/01.9-query-expr-values.sqllogic +111 -0
  18. package/test/logic/01.9.1-limit-over-dml-subquery.sqllogic +119 -0
  19. package/test/logic/02-filters.sqllogic +54 -0
  20. package/test/logic/02-smoke.sqllogic +62 -0
  21. package/test/logic/02.1-bind-parameters.sqllogic +223 -0
  22. package/test/logic/03-expressions.sqllogic +190 -0
  23. package/test/logic/03.1-quoted-identifiers.sqllogic +55 -0
  24. package/test/logic/03.4-defaults.sqllogic +554 -0
  25. package/test/logic/03.4.1-default-edge-cases.sqllogic +99 -0
  26. package/test/logic/03.5-tvf.sqllogic +61 -0
  27. package/test/logic/03.6-type-system.sqllogic +544 -0
  28. package/test/logic/03.6.1-numeric-text-comparison-coercion.sqllogic +244 -0
  29. package/test/logic/03.6.2-value-to-text.sqllogic +224 -0
  30. package/test/logic/03.7-bigint-mixed-arithmetic.sqllogic +65 -0
  31. package/test/logic/03.8-not-precedence.sqllogic +255 -0
  32. package/test/logic/03.9-is-bool-predicate.sqllogic +150 -0
  33. package/test/logic/03.9.1-is-bool-predicate-partial-index.sqllogic +34 -0
  34. package/test/logic/04-order-by.sqllogic +38 -0
  35. package/test/logic/04-transactions.sqllogic +239 -0
  36. package/test/logic/04a-savepoint-lazy-attach.sqllogic +122 -0
  37. package/test/logic/05-vtab_memory.sqllogic +50 -0
  38. package/test/logic/05.0.1-vtab-memory-unique-index-collation.sqllogic +34 -0
  39. package/test/logic/05.1-composite-pk-range-scan.sqllogic +147 -0
  40. package/test/logic/05.1.1-secondary-index-range-scan.sqllogic +56 -0
  41. package/test/logic/05.2-cast-seek-correctness.sqllogic +124 -0
  42. package/test/logic/06-builtin_functions.sqllogic +149 -0
  43. package/test/logic/06.1-string-functions.sqllogic +92 -0
  44. package/test/logic/06.1.3-like-glob-edges.sqllogic +53 -0
  45. package/test/logic/06.2-math-functions.sqllogic +82 -0
  46. package/test/logic/06.3-schema.sqllogic +48 -0
  47. package/test/logic/06.3.1-schema-all-schemas.sqllogic +39 -0
  48. package/test/logic/06.3.2-schema-foreign-keys.sqllogic +208 -0
  49. package/test/logic/06.3.3-introspection-tags.sqllogic +202 -0
  50. package/test/logic/06.3.3.1-introspection-tags-index.sqllogic +74 -0
  51. package/test/logic/06.3.4-view-info.sqllogic +378 -0
  52. package/test/logic/06.3.5-column-info.sqllogic +347 -0
  53. package/test/logic/06.3.6-schema-views.sqllogic +29 -0
  54. package/test/logic/06.4-schema-search-path.sqllogic +354 -0
  55. package/test/logic/06.4.1-schema-case-insensitive.sqllogic +83 -0
  56. package/test/logic/06.4.2-collation-extras.sqllogic +472 -0
  57. package/test/logic/06.4.2.1-collation-index-access-paths.sqllogic +140 -0
  58. package/test/logic/06.4.3-write-path-collation.sqllogic +99 -0
  59. package/test/logic/06.4.4-comparison-collation-precedence.sqllogic +342 -0
  60. package/test/logic/06.4.5-any-collate-declared-keys.sqllogic +92 -0
  61. package/test/logic/06.5-polymorphic-types.sqllogic +169 -0
  62. package/test/logic/06.5.1-conversion-functions.sqllogic +173 -0
  63. package/test/logic/06.5.2-scalar-minmax.sqllogic +16 -0
  64. package/test/logic/06.5.3-undeclared-return-type-comparison.sqllogic +244 -0
  65. package/test/logic/06.5.4-declared-return-type-builtins.sqllogic +288 -0
  66. package/test/logic/06.6-aggregate-extended.sqllogic +78 -0
  67. package/test/logic/06.7-json-extended.sqllogic +53 -0
  68. package/test/logic/06.8-json-path-operators.sqllogic +99 -0
  69. package/test/logic/06.9-json-canonical-key.sqllogic +65 -0
  70. package/test/logic/06.9.1-json-coerce-once.sqllogic +417 -0
  71. package/test/logic/06.9.1.1-json-coerce-once-index.sqllogic +39 -0
  72. package/test/logic/06.9.2-json-structural-equality.sqllogic +382 -0
  73. package/test/logic/06.9.2.1-json-structural-equality-index.sqllogic +76 -0
  74. package/test/logic/06.9.3-json-index-range-seek.sqllogic +205 -0
  75. package/test/logic/07-aggregates.sqllogic +193 -0
  76. package/test/logic/07.1-aggregate-filter-clause.sqllogic +12 -0
  77. package/test/logic/07.2-aggregate-order-by.sqllogic +11 -0
  78. package/test/logic/07.3-group-by-extras.sqllogic +602 -0
  79. package/test/logic/07.3.1-group-by-order-by-key.sqllogic +164 -0
  80. package/test/logic/07.3.2-grouped-select-list-shape.sqllogic +231 -0
  81. package/test/logic/07.4-group-by-filter-composite-pk.sqllogic +120 -0
  82. package/test/logic/07.5-sum-mixed-exact-and-float.sqllogic +151 -0
  83. package/test/logic/07.5-window.sqllogic +1941 -0
  84. package/test/logic/07.5.1-window-named.sqllogic +13 -0
  85. package/test/logic/07.5.2-window-nth-value.sqllogic +13 -0
  86. package/test/logic/07.6-subqueries.sqllogic +78 -0
  87. package/test/logic/07.6.1-subquery-extras.sqllogic +93 -0
  88. package/test/logic/07.7-and-or-short-circuit.sqllogic +58 -0
  89. package/test/logic/07.7-in-subquery-caching.sqllogic +174 -0
  90. package/test/logic/07.7-scalar-agg-decorrelation.sqllogic +197 -0
  91. package/test/logic/07.7.1-in-extras.sqllogic +158 -0
  92. package/test/logic/07.7.1-scalar-agg-decorrelation-filter.sqllogic +105 -0
  93. package/test/logic/07.7.2-scalar-agg-decorrelation-nested.sqllogic +115 -0
  94. package/test/logic/07.7.3-exists-in-select-decorrelation.sqllogic +107 -0
  95. package/test/logic/07.7.4-where-conjunct-ordering.sqllogic +121 -0
  96. package/test/logic/07.7.5-filter-lost-under-index-order.sqllogic +134 -0
  97. package/test/logic/07.7.6-correlated-predicate-scope.sqllogic +178 -0
  98. package/test/logic/07.7.7-join-source-scope-shadowing.sqllogic +137 -0
  99. package/test/logic/07.7.8-correlated-ref-to-computed-column.sqllogic +165 -0
  100. package/test/logic/07.7.9-conjunct-monotonicity.sqllogic +200 -0
  101. package/test/logic/07.8-correlated-subquery-edges.sqllogic +71 -0
  102. package/test/logic/07.8.1-correlated-in-decorrelation-gates.sqllogic +156 -0
  103. package/test/logic/07.9-in-value-list.sqllogic +154 -0
  104. package/test/logic/07.9.1-in-value-list-composite-index.sqllogic +38 -0
  105. package/test/logic/08-views.sqllogic +64 -0
  106. package/test/logic/08.1-semi-anti-join.sqllogic +191 -0
  107. package/test/logic/08.1-view-edge-cases.sqllogic +118 -0
  108. package/test/logic/08.1.1-uncorrelated-in-semijoin.sqllogic +251 -0
  109. package/test/logic/08.2-existence-flag-semijoin-recovery.sqllogic +197 -0
  110. package/test/logic/08.3-existence-flag-inner-recovery.sqllogic +102 -0
  111. package/test/logic/08.4-key-set-semi-join.sqllogic +447 -0
  112. package/test/logic/09-set_operations.sqllogic +244 -0
  113. package/test/logic/09.1-set-op-cross-collation.sqllogic +230 -0
  114. package/test/logic/10-distinct_datatypes.sqllogic +127 -0
  115. package/test/logic/10.1-ddl-lifecycle.sqllogic +203 -0
  116. package/test/logic/10.1.1-create-table-syntax-edges.sqllogic +47 -0
  117. package/test/logic/10.1.2-ddl-in-transaction.sqllogic +143 -0
  118. package/test/logic/10.1.2.1-add-constraint-in-transaction.sqllogic +59 -0
  119. package/test/logic/10.1.3-ddl-drop-in-transaction.sqllogic +187 -0
  120. package/test/logic/10.1.3.1-drop-constraint-in-transaction.sqllogic +45 -0
  121. package/test/logic/10.1.4-create-table-duplicate-constraint-names.sqllogic +149 -0
  122. package/test/logic/10.1.4-ddl-transaction-policy.sqllogic +314 -0
  123. package/test/logic/10.1.5-alter-add-constraint-minted-name-disambiguation.sqllogic +219 -0
  124. package/test/logic/10.1.5-create-index-lifecycle.sqllogic +72 -0
  125. package/test/logic/10.1.6-ddl-transaction-policy-index.sqllogic +143 -0
  126. package/test/logic/10.1.7-create-table-default-type-check.sqllogic +119 -0
  127. package/test/logic/10.1.8-alter-add-check-existing-rows.sqllogic +117 -0
  128. package/test/logic/10.2-column-features.sqllogic +365 -0
  129. package/test/logic/10.2.1-table-options-rejection.sqllogic +63 -0
  130. package/test/logic/10.2.2-default-collation-memory.sqllogic +30 -0
  131. package/test/logic/10.3-function-features.sqllogic +247 -0
  132. package/test/logic/10.4-schema-scale.sqllogic +168 -0
  133. package/test/logic/10.4.1-schema-scale-indexes.sqllogic +121 -0
  134. package/test/logic/10.5-indexes.sqllogic +164 -0
  135. package/test/logic/10.5.1-partial-indexes.sqllogic +526 -0
  136. package/test/logic/10.5.2-expression-indexes.sqllogic +34 -0
  137. package/test/logic/10.5.3-desc-index-ordering.sqllogic +130 -0
  138. package/test/logic/10.5.4-composite-pk-index-update-phantom.sqllogic +55 -0
  139. package/test/logic/10.5.5-index-name-uniqueness.sqllogic +76 -0
  140. package/test/logic/10.5.6-minmax-index-boundary.sqllogic +204 -0
  141. package/test/logic/10.5.7-implicit-unique-index-lifecycle.sqllogic +1216 -0
  142. package/test/logic/10.6-distinct-edge-cases.sqllogic +119 -0
  143. package/test/logic/100-predicate-normalization-edge-cases.sqllogic +238 -0
  144. package/test/logic/100.1-where-extras.sqllogic +163 -0
  145. package/test/logic/100.1.1-where-join-on-order-indexed.sqllogic +35 -0
  146. package/test/logic/101-builtin-mutation-kills.sqllogic +974 -0
  147. package/test/logic/101-transaction-edge-cases.sqllogic +395 -0
  148. package/test/logic/102-schema-catalog-edge-cases.sqllogic +252 -0
  149. package/test/logic/102-unique-constraints.sqllogic +145 -0
  150. package/test/logic/102.0.1-schema-catalog-indexed-table.sqllogic +32 -0
  151. package/test/logic/102.1-unique-edge-cases.sqllogic +111 -0
  152. package/test/logic/102.1.1-unique-index-creation-edge-cases.sqllogic +75 -0
  153. package/test/logic/102.2-unique-collation.sqllogic +181 -0
  154. package/test/logic/102.2.1-unique-collation-index-derived.sqllogic +305 -0
  155. package/test/logic/103-database-options-edge-cases.sqllogic +108 -0
  156. package/test/logic/103.1-pragma-boolean-values.sqllogic +126 -0
  157. package/test/logic/104-emit-mutation-kills.sqllogic +236 -0
  158. package/test/logic/105-vtab-memory-mutation-kills.sqllogic +754 -0
  159. package/test/logic/105.1-vtab-memory-index-mutation-kills.sqllogic +540 -0
  160. package/test/logic/106-constraint-extractor-mutation-kills.sqllogic +350 -0
  161. package/test/logic/107-temporal-arithmetic-mutation-kills.sqllogic +833 -0
  162. package/test/logic/108-cardinality-estimation.sqllogic +146 -0
  163. package/test/logic/108.1-analyze-name-resolution.sqllogic +102 -0
  164. package/test/logic/109-aggregate-physical-selection.sqllogic +103 -0
  165. package/test/logic/11-joins.sqllogic +86 -0
  166. package/test/logic/11.1-join-using.sqllogic +267 -0
  167. package/test/logic/11.2-comma-join.sqllogic +35 -0
  168. package/test/logic/11.3-index-nested-loop-join.sqllogic +305 -0
  169. package/test/logic/11.3-mismatched-collation-join.sqllogic +74 -0
  170. package/test/logic/11.4-hash-join-side-swap.sqllogic +118 -0
  171. package/test/logic/110-scan-emitter-mutation-kills.sqllogic +121 -0
  172. package/test/logic/110.1-scan-emitter-index-scan-kills.sqllogic +47 -0
  173. package/test/logic/12-empty-primary-key.sqllogic +124 -0
  174. package/test/logic/12-join_padding_order.sqllogic +58 -0
  175. package/test/logic/12.1-orderby-index-consumption.sqllogic +40 -0
  176. package/test/logic/13-cte.sqllogic +137 -0
  177. package/test/logic/13.1-cte-multiple-recursive.sqllogic +104 -0
  178. package/test/logic/13.10-cte-qualified-name.sqllogic +136 -0
  179. package/test/logic/13.11-unreferenced-dml-cte.sqllogic +414 -0
  180. package/test/logic/13.12-nested-dml-cte-rejected.sqllogic +280 -0
  181. package/test/logic/13.2-cte-bind-params.sqllogic +98 -0
  182. package/test/logic/13.3-cte-edge-cases.sqllogic +120 -0
  183. package/test/logic/13.4-cte-extras.sqllogic +98 -0
  184. package/test/logic/13.5-cte-join-order.sqllogic +166 -0
  185. package/test/logic/13.6-cte-dml-runs-once.sqllogic +318 -0
  186. package/test/logic/13.7-cte-sibling-visibility.sqllogic +227 -0
  187. package/test/logic/13.8-insert-with-clause-visibility.sqllogic +254 -0
  188. package/test/logic/13.9-schema-authored-cte-isolation.sqllogic +328 -0
  189. package/test/logic/13.9.1-schema-authored-schema-path-isolation.sqllogic +408 -0
  190. package/test/logic/14-utilities.sqllogic +168 -0
  191. package/test/logic/15-timespan.sqllogic +279 -0
  192. package/test/logic/15.1-semantic-ordering.sqllogic +960 -0
  193. package/test/logic/15.1.1-json-check-coercion.sqllogic +178 -0
  194. package/test/logic/15.1.2-semantic-ordering-index.sqllogic +41 -0
  195. package/test/logic/15.1.3-declared-form-write-contract.sqllogic +519 -0
  196. package/test/logic/16-epoch.sqllogic +187 -0
  197. package/test/logic/17-weekday-modifier.sqllogic +78 -0
  198. package/test/logic/18-json-string-escapes.sqllogic +86 -0
  199. package/test/logic/18-strftime-extended-formats.sqllogic +68 -0
  200. package/test/logic/20-empty-single-row.sqllogic +261 -0
  201. package/test/logic/21-null-edge-cases.sqllogic +227 -0
  202. package/test/logic/21.1-where-null-comparisons.sqllogic +151 -0
  203. package/test/logic/21.1.1-where-null-comparisons-secondary-index.sqllogic +35 -0
  204. package/test/logic/21.2-between-edges.sqllogic +157 -0
  205. package/test/logic/22-boundary-values.sqllogic +175 -0
  206. package/test/logic/23-self-joins-duplicates.sqllogic +74 -0
  207. package/test/logic/24-builtin-branches.sqllogic +611 -0
  208. package/test/logic/24.1-substr-extras.sqllogic +75 -0
  209. package/test/logic/24.2-random-extras.sqllogic +39 -0
  210. package/test/logic/25-aggregate-edge-cases.sqllogic +289 -0
  211. package/test/logic/25.1-nested-aggregates.sqllogic +74 -0
  212. package/test/logic/25.2-having-edge-cases.sqllogic +318 -0
  213. package/test/logic/25.3-aggregate-isnull-empty.sqllogic +16 -0
  214. package/test/logic/25.4-groupby-fd-constant-empty.sqllogic +55 -0
  215. package/test/logic/25.5-ungrouped-aggregate-column-free-select-item.sqllogic +196 -0
  216. package/test/logic/26-join-edge-cases.sqllogic +333 -0
  217. package/test/logic/26.1-left-join-isnull-on-notnull.sqllogic +26 -0
  218. package/test/logic/26.2-left-join-on-vs-where.sqllogic +67 -0
  219. package/test/logic/26.3-join-on-constants.sqllogic +61 -0
  220. package/test/logic/26.4-join-on-is-null-nullable.sqllogic +44 -0
  221. package/test/logic/27-window-edge-cases.sqllogic +206 -0
  222. package/test/logic/27.1-window-groups-frame.sqllogic +13 -0
  223. package/test/logic/27.2-window-exclude.sqllogic +13 -0
  224. package/test/logic/27.3-window-json-aggregation.sqllogic +66 -0
  225. package/test/logic/28-set-ops-sort-edge-cases.sqllogic +444 -0
  226. package/test/logic/28.1-compound-limit-offset.sqllogic +143 -0
  227. package/test/logic/28.2-orderby-expression-extras.sqllogic +698 -0
  228. package/test/logic/28.2-set-op-branch-types.sqllogic +217 -0
  229. package/test/logic/29-constraint-edge-cases.sqllogic +442 -0
  230. package/test/logic/29.1-column-level-conflict-clause.sqllogic +171 -0
  231. package/test/logic/29.2-table-level-pk-conflict-clause.sqllogic +113 -0
  232. package/test/logic/40-constraints.sqllogic +418 -0
  233. package/test/logic/40.1-pk-desc-direction.sqllogic +76 -0
  234. package/test/logic/40.2-check-extras.sqllogic +683 -0
  235. package/test/logic/41-alter-table.sqllogic +191 -0
  236. package/test/logic/41-fk-cascade-conflict-and-self-ref.sqllogic +263 -0
  237. package/test/logic/41-fk-cross-schema.sqllogic +155 -0
  238. package/test/logic/41-fk-extended-targets.sqllogic +173 -0
  239. package/test/logic/41-foreign-keys.sqllogic +1001 -0
  240. package/test/logic/41-generated-column-errors.sqllogic +225 -0
  241. package/test/logic/41-generated-column-extras.sqllogic +598 -0
  242. package/test/logic/41-generated-column-scope.sqllogic +329 -0
  243. package/test/logic/41-generated-columns.sqllogic +361 -0
  244. package/test/logic/41.1-alter-pk.sqllogic +230 -0
  245. package/test/logic/41.1-fk-collation-conflict.sqllogic +277 -0
  246. package/test/logic/41.10-alter-drop-column-foreign-key.sqllogic +274 -0
  247. package/test/logic/41.10.1-alter-drop-column-unique-index.sqllogic +169 -0
  248. package/test/logic/41.10.2-alter-drop-column-check-and-assertion.sqllogic +1616 -0
  249. package/test/logic/41.10.3-alter-drop-column-referencing-fk.sqllogic +292 -0
  250. package/test/logic/41.10.4-drop-object-expression-dependents.sqllogic +602 -0
  251. package/test/logic/41.11-deferred-fk-with-rename.sqllogic +232 -0
  252. package/test/logic/41.12-alter-default-coercion.sqllogic +200 -0
  253. package/test/logic/41.13-alter-add-column-generated-backfill.sqllogic +566 -0
  254. package/test/logic/41.14-alter-add-column-subquery-backfill.sqllogic +129 -0
  255. package/test/logic/41.15-alter-column-default-type-check.sqllogic +178 -0
  256. package/test/logic/41.16-fk-unenforceable.sqllogic +364 -0
  257. package/test/logic/41.2-alter-column.sqllogic +382 -0
  258. package/test/logic/41.2.1-alter-column-retype-deleted-row-memory.sqllogic +32 -0
  259. package/test/logic/41.2.2-alter-column-retype-index-rebuild.sqllogic +77 -0
  260. package/test/logic/41.2.3-alter-column-set-not-null-pk-backfill.sqllogic +310 -0
  261. package/test/logic/41.2.3.1-alter-column-set-not-null-pk-backfill-secondary-index.sqllogic +37 -0
  262. package/test/logic/41.3-alter-add-column-unique.sqllogic +184 -0
  263. package/test/logic/41.3-alter-rename-propagation.sqllogic +2352 -0
  264. package/test/logic/41.3.1-alter-rename-index-propagation.sqllogic +94 -0
  265. package/test/logic/41.4-alter-add-column-constraints.sqllogic +855 -0
  266. package/test/logic/41.5-alter-misc.sqllogic +48 -0
  267. package/test/logic/41.5-cross-schema-foreign-keys.sqllogic +253 -0
  268. package/test/logic/41.6-alter-drop-rename-constraint.sqllogic +354 -0
  269. package/test/logic/41.6.1-alter-drop-constraint-index-derived.sqllogic +22 -0
  270. package/test/logic/41.7-alter-column-collate.sqllogic +105 -0
  271. package/test/logic/41.7.1-alter-column-collate-unique.sqllogic +45 -0
  272. package/test/logic/41.7.2-alter-column-collate-unique-store.sqllogic +208 -0
  273. package/test/logic/41.7.2.1-alter-column-collate-unique-index-store.sqllogic +114 -0
  274. package/test/logic/41.7.3-alter-column-retype-unique.sqllogic +293 -0
  275. package/test/logic/41.7.3.1-alter-column-retype-staged-rows-memory.sqllogic +41 -0
  276. package/test/logic/41.7.3.2-alter-column-retype-unique-index.sqllogic +72 -0
  277. package/test/logic/41.7.4-alter-column-retype-semantic-memory.sqllogic +445 -0
  278. package/test/logic/41.7.4.1-alter-column-retype-semantic-index-memory.sqllogic +91 -0
  279. package/test/logic/41.7.5-alter-column-collate-pk-staged-delete-memory.sqllogic +106 -0
  280. package/test/logic/41.8-alter-add-constraint-unique-fk.sqllogic +275 -0
  281. package/test/logic/41.8-alter-savepoint-staged-rows.sqllogic +443 -0
  282. package/test/logic/41.8.1-alter-rename-savepoint-staged-rows.sqllogic +408 -0
  283. package/test/logic/41.9-alter-constraint-body-change.sqllogic +180 -0
  284. package/test/logic/41.9-fk-restrict-batched.sqllogic +290 -0
  285. package/test/logic/42-committed-snapshot.sqllogic +254 -0
  286. package/test/logic/42-returning.sqllogic +159 -0
  287. package/test/logic/42.1-returning-extras.sqllogic +232 -0
  288. package/test/logic/42.2-returning-stored-row.sqllogic +165 -0
  289. package/test/logic/43-default-nullability.sqllogic +131 -0
  290. package/test/logic/43-transition-constraints.sqllogic +341 -0
  291. package/test/logic/43.1-default-collation.sqllogic +140 -0
  292. package/test/logic/43.1-notnull-or-conflict.sqllogic +79 -0
  293. package/test/logic/43.2-deferred-check-new-on-delete.sqllogic +95 -0
  294. package/test/logic/43.3-nullable-primary-key.sqllogic +587 -0
  295. package/test/logic/44-determinism-validation.sqllogic +164 -0
  296. package/test/logic/44-orthogonality.sqllogic +146 -0
  297. package/test/logic/44.1-nondeterministic-schema.sqllogic +316 -0
  298. package/test/logic/45-udf-determinism.sqllogic +39 -0
  299. package/test/logic/46-mutation-context.sqllogic +595 -0
  300. package/test/logic/47-upsert.sqllogic +255 -0
  301. package/test/logic/47.1-upsert-conflict-targets.sqllogic +169 -0
  302. package/test/logic/47.2-replace-and-or-clauses.sqllogic +164 -0
  303. package/test/logic/47.2.1-pk-replace-secondary-unique.sqllogic +374 -0
  304. package/test/logic/47.3-engine-or-fail.sqllogic +122 -0
  305. package/test/logic/47.3-upsert-conflict-target-collation.sqllogic +139 -0
  306. package/test/logic/47.3.1-upsert-conflict-index-derived-collation.sqllogic +25 -0
  307. package/test/logic/47.4-upsert-conflict-target-affinity.sqllogic +59 -0
  308. package/test/logic/47.5-upsert-do-update-constraints.sqllogic +440 -0
  309. package/test/logic/47.6-upsert-do-update-decomposed.sqllogic +100 -0
  310. package/test/logic/49-reference-graph.sqllogic +84 -0
  311. package/test/logic/50-declarative-schema.sqllogic +2550 -0
  312. package/test/logic/50-metadata-tags.sqllogic +1033 -0
  313. package/test/logic/50.0.1-metadata-tags-index.sqllogic +141 -0
  314. package/test/logic/50.1-declare-schema-pk.sqllogic +150 -0
  315. package/test/logic/50.2-declare-schema-renames.sqllogic +949 -0
  316. package/test/logic/50.2.1-declare-schema-index-renames.sqllogic +210 -0
  317. package/test/logic/50.3-declare-schema-duplicate-names.sqllogic +280 -0
  318. package/test/logic/50.4-declare-schema-apply-restore.sqllogic +192 -0
  319. package/test/logic/51-lens-foundation.sqllogic +147 -0
  320. package/test/logic/51-materialized-views.sqllogic +376 -0
  321. package/test/logic/51.5-materialized-views-coarsened-key.sqllogic +198 -0
  322. package/test/logic/51.7-maintained-table-attach-detach.sqllogic +468 -0
  323. package/test/logic/51.8-maintained-table-declared-constraints.sqllogic +440 -0
  324. package/test/logic/51.85-maintained-table-parent-fk.sqllogic +103 -0
  325. package/test/logic/51.9-maintained-table-secondary-unique.sqllogic +237 -0
  326. package/test/logic/51.9.1-maintained-table-partial-unique.sqllogic +36 -0
  327. package/test/logic/52-lens-overrides.sqllogic +566 -0
  328. package/test/logic/53-materialized-views-rowtime.sqllogic +1601 -0
  329. package/test/logic/53-reserved-tags.sqllogic +252 -0
  330. package/test/logic/53.1-materialized-view-write-through.sqllogic +329 -0
  331. package/test/logic/53.2-materialized-view-rename-propagation.sqllogic +483 -0
  332. package/test/logic/53.3-materialized-view-constraint-only-ddl.sqllogic +409 -0
  333. package/test/logic/53.3.1-materialized-view-index-ddl-irrelevance.sqllogic +29 -0
  334. package/test/logic/53.4-materialized-view-structural-alter-restore.sqllogic +436 -0
  335. package/test/logic/54-covering-mv-enforcement.sqllogic +314 -0
  336. package/test/logic/54.1-covering-mv-partial-enforcement.sqllogic +34 -0
  337. package/test/logic/55-internal-eviction-reporting.sqllogic +89 -0
  338. package/test/logic/55-lens-prover.sqllogic +202 -0
  339. package/test/logic/55.1-eviction-restrict-fk.sqllogic +148 -0
  340. package/test/logic/55.5-lens-authored-inverse.sqllogic +1468 -0
  341. package/test/logic/80-grow-retrieve-pass.sqllogic +79 -0
  342. package/test/logic/81-quickpick.sqllogic +24 -0
  343. package/test/logic/82-bloom-join.sqllogic +185 -0
  344. package/test/logic/83-merge-join.sqllogic +280 -0
  345. package/test/logic/84-asof-scan.sqllogic +238 -0
  346. package/test/logic/84-key-cardinality.sqllogic +50 -0
  347. package/test/logic/85-relational-const-folding.sqllogic +51 -0
  348. package/test/logic/86-scalar-cse.sqllogic +42 -0
  349. package/test/logic/90-error_paths.sqllogic +114 -0
  350. package/test/logic/90.1-parse-errors.sqllogic +77 -0
  351. package/test/logic/90.2-alter-table-errors.sqllogic +53 -0
  352. package/test/logic/90.2.1-alter-extra-errors.sqllogic +51 -0
  353. package/test/logic/90.3-expression-errors.sqllogic +43 -0
  354. package/test/logic/90.4-dml-errors.sqllogic +47 -0
  355. package/test/logic/90.5-unsupported-join-types.sqllogic +37 -0
  356. package/test/logic/90.5.1-right-full-join-read.sqllogic +157 -0
  357. package/test/logic/90.6-select-error-paths.sqllogic +57 -0
  358. package/test/logic/91-merge-join-edge-cases.sqllogic +159 -0
  359. package/test/logic/92-hash-aggregate-edge-cases.sqllogic +139 -0
  360. package/test/logic/93-ddl-view-edge-cases.sqllogic +104 -0
  361. package/test/logic/93.1-view-error-paths.sqllogic +74 -0
  362. package/test/logic/93.2-view-mutation-pending.sqllogic +108 -0
  363. package/test/logic/93.3-view-mutation-or-clause.sqllogic +60 -0
  364. package/test/logic/93.4-view-mutation.sqllogic +4881 -0
  365. package/test/logic/93.4.1-view-mutation-partial-unique-cardinality.sqllogic +31 -0
  366. package/test/logic/93.5-authored-inverse.sqllogic +415 -0
  367. package/test/logic/93.6-set-op-flagless-write.sqllogic +862 -0
  368. package/test/logic/94-tvf-edge-cases.sqllogic +58 -0
  369. package/test/logic/94.1-limit-offset-edge-cases.sqllogic +109 -0
  370. package/test/logic/94.2-limit-offset-extras.sqllogic +98 -0
  371. package/test/logic/95-assertions.sqllogic +1417 -0
  372. package/test/logic/96-subquery-edge-cases.sqllogic +276 -0
  373. package/test/logic/97-json-function-edge-cases.sqllogic +633 -0
  374. package/test/logic/97.1-json-blob-and-special-values.sqllogic +92 -0
  375. package/test/logic/98-temporal-edge-cases.sqllogic +515 -0
  376. package/test/logic/99-conversion-edge-cases.sqllogic +325 -0
  377. package/test/logic/99.0.1-schema-index-listing.sqllogic +21 -0
  378. package/test/logic/99.1-cast-syntax-extras.sqllogic +167 -0
  379. package/test/logic/drop-unique-index.sqllogic +88 -0
  380. 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
+