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