@quereus/quereus 4.20.0 → 4.20.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (400) hide show
  1. package/README.md +2 -0
  2. package/dist/src/common/types.d.ts +4 -4
  3. package/dist/src/index.d.ts +1 -1
  4. package/dist/src/index.d.ts.map +1 -1
  5. package/dist/src/index.js +3 -2
  6. package/dist/src/index.js.map +1 -1
  7. package/dist/src/schema/catalog-rendering.js +13 -1
  8. package/dist/src/schema/catalog-rendering.js.map +1 -1
  9. package/dist/src/schema/catalog.d.ts +35 -2
  10. package/dist/src/schema/catalog.d.ts.map +1 -1
  11. package/dist/src/schema/catalog.js +42 -33
  12. package/dist/src/schema/catalog.js.map +1 -1
  13. package/dist/src/schema/schema-differ.d.ts +1 -1
  14. package/dist/src/schema/schema-differ.d.ts.map +1 -1
  15. package/dist/src/schema/schema-differ.js +195 -33
  16. package/dist/src/schema/schema-differ.js.map +1 -1
  17. package/dist/src/schema/unique-enforcement.d.ts +23 -1
  18. package/dist/src/schema/unique-enforcement.d.ts.map +1 -1
  19. package/dist/src/schema/unique-enforcement.js +43 -1
  20. package/dist/src/schema/unique-enforcement.js.map +1 -1
  21. package/dist/src/vtab/memory/layer/manager.d.ts +15 -12
  22. package/dist/src/vtab/memory/layer/manager.d.ts.map +1 -1
  23. package/dist/src/vtab/memory/layer/manager.js +87 -100
  24. package/dist/src/vtab/memory/layer/manager.js.map +1 -1
  25. package/package.json +7 -4
  26. package/test/README.md +173 -0
  27. package/test/logic/01-basic.sqllogic +39 -0
  28. package/test/logic/01.1-select-projection-extras.sqllogic +130 -0
  29. package/test/logic/01.2-strings.sqllogic +43 -0
  30. package/test/logic/01.5-insert-select.sqllogic +237 -0
  31. package/test/logic/01.6-update-extras.sqllogic +118 -0
  32. package/test/logic/01.7-update-from.sqllogic +67 -0
  33. package/test/logic/01.8-delete-extras.sqllogic +122 -0
  34. package/test/logic/01.8.1-delete-range-cow.sqllogic +115 -0
  35. package/test/logic/01.9-query-expr-dml-gates.sqllogic +32 -0
  36. package/test/logic/01.9-query-expr-dml.sqllogic +171 -0
  37. package/test/logic/01.9-query-expr-values.sqllogic +111 -0
  38. package/test/logic/01.9.1-limit-over-dml-subquery.sqllogic +119 -0
  39. package/test/logic/02-filters.sqllogic +54 -0
  40. package/test/logic/02-smoke.sqllogic +62 -0
  41. package/test/logic/02.1-bind-parameters.sqllogic +223 -0
  42. package/test/logic/03-expressions.sqllogic +190 -0
  43. package/test/logic/03.1-quoted-identifiers.sqllogic +55 -0
  44. package/test/logic/03.4-defaults.sqllogic +554 -0
  45. package/test/logic/03.4.1-default-edge-cases.sqllogic +99 -0
  46. package/test/logic/03.5-tvf.sqllogic +61 -0
  47. package/test/logic/03.6-type-system.sqllogic +544 -0
  48. package/test/logic/03.6.1-numeric-text-comparison-coercion.sqllogic +244 -0
  49. package/test/logic/03.6.2-value-to-text.sqllogic +224 -0
  50. package/test/logic/03.7-bigint-mixed-arithmetic.sqllogic +65 -0
  51. package/test/logic/03.8-not-precedence.sqllogic +255 -0
  52. package/test/logic/03.9-is-bool-predicate.sqllogic +150 -0
  53. package/test/logic/03.9.1-is-bool-predicate-partial-index.sqllogic +34 -0
  54. package/test/logic/04-order-by.sqllogic +38 -0
  55. package/test/logic/04-transactions.sqllogic +239 -0
  56. package/test/logic/04a-savepoint-lazy-attach.sqllogic +122 -0
  57. package/test/logic/05-vtab_memory.sqllogic +50 -0
  58. package/test/logic/05.0.1-vtab-memory-unique-index-collation.sqllogic +34 -0
  59. package/test/logic/05.1-composite-pk-range-scan.sqllogic +147 -0
  60. package/test/logic/05.1.1-secondary-index-range-scan.sqllogic +56 -0
  61. package/test/logic/05.2-cast-seek-correctness.sqllogic +124 -0
  62. package/test/logic/06-builtin_functions.sqllogic +149 -0
  63. package/test/logic/06.1-string-functions.sqllogic +92 -0
  64. package/test/logic/06.1.3-like-glob-edges.sqllogic +53 -0
  65. package/test/logic/06.2-math-functions.sqllogic +82 -0
  66. package/test/logic/06.3-schema.sqllogic +48 -0
  67. package/test/logic/06.3.1-schema-all-schemas.sqllogic +39 -0
  68. package/test/logic/06.3.2-schema-foreign-keys.sqllogic +208 -0
  69. package/test/logic/06.3.3-introspection-tags.sqllogic +202 -0
  70. package/test/logic/06.3.3.1-introspection-tags-index.sqllogic +74 -0
  71. package/test/logic/06.3.4-view-info.sqllogic +378 -0
  72. package/test/logic/06.3.5-column-info.sqllogic +347 -0
  73. package/test/logic/06.3.6-schema-views.sqllogic +29 -0
  74. package/test/logic/06.4-schema-search-path.sqllogic +354 -0
  75. package/test/logic/06.4.1-schema-case-insensitive.sqllogic +83 -0
  76. package/test/logic/06.4.2-collation-extras.sqllogic +472 -0
  77. package/test/logic/06.4.2.1-collation-index-access-paths.sqllogic +140 -0
  78. package/test/logic/06.4.3-write-path-collation.sqllogic +99 -0
  79. package/test/logic/06.4.4-comparison-collation-precedence.sqllogic +342 -0
  80. package/test/logic/06.4.5-any-collate-declared-keys.sqllogic +92 -0
  81. package/test/logic/06.5-polymorphic-types.sqllogic +169 -0
  82. package/test/logic/06.5.1-conversion-functions.sqllogic +173 -0
  83. package/test/logic/06.5.2-scalar-minmax.sqllogic +16 -0
  84. package/test/logic/06.5.3-undeclared-return-type-comparison.sqllogic +244 -0
  85. package/test/logic/06.5.4-declared-return-type-builtins.sqllogic +288 -0
  86. package/test/logic/06.6-aggregate-extended.sqllogic +78 -0
  87. package/test/logic/06.7-json-extended.sqllogic +53 -0
  88. package/test/logic/06.8-json-path-operators.sqllogic +99 -0
  89. package/test/logic/06.9-json-canonical-key.sqllogic +65 -0
  90. package/test/logic/06.9.1-json-coerce-once.sqllogic +417 -0
  91. package/test/logic/06.9.1.1-json-coerce-once-index.sqllogic +39 -0
  92. package/test/logic/06.9.2-json-structural-equality.sqllogic +382 -0
  93. package/test/logic/06.9.2.1-json-structural-equality-index.sqllogic +76 -0
  94. package/test/logic/06.9.3-json-index-range-seek.sqllogic +205 -0
  95. package/test/logic/07-aggregates.sqllogic +193 -0
  96. package/test/logic/07.1-aggregate-filter-clause.sqllogic +12 -0
  97. package/test/logic/07.2-aggregate-order-by.sqllogic +11 -0
  98. package/test/logic/07.3-group-by-extras.sqllogic +602 -0
  99. package/test/logic/07.3.1-group-by-order-by-key.sqllogic +164 -0
  100. package/test/logic/07.3.2-grouped-select-list-shape.sqllogic +231 -0
  101. package/test/logic/07.4-group-by-filter-composite-pk.sqllogic +120 -0
  102. package/test/logic/07.5-sum-mixed-exact-and-float.sqllogic +151 -0
  103. package/test/logic/07.5-window.sqllogic +1941 -0
  104. package/test/logic/07.5.1-window-named.sqllogic +13 -0
  105. package/test/logic/07.5.2-window-nth-value.sqllogic +13 -0
  106. package/test/logic/07.6-subqueries.sqllogic +78 -0
  107. package/test/logic/07.6.1-subquery-extras.sqllogic +93 -0
  108. package/test/logic/07.7-and-or-short-circuit.sqllogic +58 -0
  109. package/test/logic/07.7-in-subquery-caching.sqllogic +174 -0
  110. package/test/logic/07.7-scalar-agg-decorrelation.sqllogic +197 -0
  111. package/test/logic/07.7.1-in-extras.sqllogic +158 -0
  112. package/test/logic/07.7.1-scalar-agg-decorrelation-filter.sqllogic +105 -0
  113. package/test/logic/07.7.2-scalar-agg-decorrelation-nested.sqllogic +115 -0
  114. package/test/logic/07.7.3-exists-in-select-decorrelation.sqllogic +107 -0
  115. package/test/logic/07.7.4-where-conjunct-ordering.sqllogic +121 -0
  116. package/test/logic/07.7.5-filter-lost-under-index-order.sqllogic +134 -0
  117. package/test/logic/07.7.6-correlated-predicate-scope.sqllogic +178 -0
  118. package/test/logic/07.7.7-join-source-scope-shadowing.sqllogic +137 -0
  119. package/test/logic/07.7.8-correlated-ref-to-computed-column.sqllogic +165 -0
  120. package/test/logic/07.7.9-conjunct-monotonicity.sqllogic +200 -0
  121. package/test/logic/07.8-correlated-subquery-edges.sqllogic +71 -0
  122. package/test/logic/07.8.1-correlated-in-decorrelation-gates.sqllogic +156 -0
  123. package/test/logic/07.9-in-value-list.sqllogic +154 -0
  124. package/test/logic/07.9.1-in-value-list-composite-index.sqllogic +38 -0
  125. package/test/logic/08-views.sqllogic +64 -0
  126. package/test/logic/08.1-semi-anti-join.sqllogic +191 -0
  127. package/test/logic/08.1-view-edge-cases.sqllogic +118 -0
  128. package/test/logic/08.1.1-uncorrelated-in-semijoin.sqllogic +251 -0
  129. package/test/logic/08.2-existence-flag-semijoin-recovery.sqllogic +197 -0
  130. package/test/logic/08.3-existence-flag-inner-recovery.sqllogic +102 -0
  131. package/test/logic/08.4-key-set-semi-join.sqllogic +447 -0
  132. package/test/logic/09-set_operations.sqllogic +244 -0
  133. package/test/logic/09.1-set-op-cross-collation.sqllogic +230 -0
  134. package/test/logic/10-distinct_datatypes.sqllogic +127 -0
  135. package/test/logic/10.1-ddl-lifecycle.sqllogic +203 -0
  136. package/test/logic/10.1.1-create-table-syntax-edges.sqllogic +47 -0
  137. package/test/logic/10.1.2-ddl-in-transaction.sqllogic +143 -0
  138. package/test/logic/10.1.2.1-add-constraint-in-transaction.sqllogic +59 -0
  139. package/test/logic/10.1.3-ddl-drop-in-transaction.sqllogic +187 -0
  140. package/test/logic/10.1.3.1-drop-constraint-in-transaction.sqllogic +45 -0
  141. package/test/logic/10.1.4-create-table-duplicate-constraint-names.sqllogic +149 -0
  142. package/test/logic/10.1.4-ddl-transaction-policy.sqllogic +314 -0
  143. package/test/logic/10.1.5-alter-add-constraint-minted-name-disambiguation.sqllogic +219 -0
  144. package/test/logic/10.1.5-create-index-lifecycle.sqllogic +72 -0
  145. package/test/logic/10.1.6-ddl-transaction-policy-index.sqllogic +143 -0
  146. package/test/logic/10.1.7-create-table-default-type-check.sqllogic +119 -0
  147. package/test/logic/10.1.8-alter-add-check-existing-rows.sqllogic +117 -0
  148. package/test/logic/10.2-column-features.sqllogic +365 -0
  149. package/test/logic/10.2.1-table-options-rejection.sqllogic +63 -0
  150. package/test/logic/10.2.2-default-collation-memory.sqllogic +30 -0
  151. package/test/logic/10.3-function-features.sqllogic +247 -0
  152. package/test/logic/10.4-schema-scale.sqllogic +168 -0
  153. package/test/logic/10.4.1-schema-scale-indexes.sqllogic +121 -0
  154. package/test/logic/10.5-indexes.sqllogic +164 -0
  155. package/test/logic/10.5.1-partial-indexes.sqllogic +526 -0
  156. package/test/logic/10.5.2-expression-indexes.sqllogic +34 -0
  157. package/test/logic/10.5.3-desc-index-ordering.sqllogic +130 -0
  158. package/test/logic/10.5.4-composite-pk-index-update-phantom.sqllogic +55 -0
  159. package/test/logic/10.5.5-index-name-uniqueness.sqllogic +76 -0
  160. package/test/logic/10.5.6-minmax-index-boundary.sqllogic +204 -0
  161. package/test/logic/10.5.7-implicit-unique-index-lifecycle.sqllogic +1216 -0
  162. package/test/logic/10.6-distinct-edge-cases.sqllogic +119 -0
  163. package/test/logic/100-predicate-normalization-edge-cases.sqllogic +238 -0
  164. package/test/logic/100.1-where-extras.sqllogic +163 -0
  165. package/test/logic/100.1.1-where-join-on-order-indexed.sqllogic +35 -0
  166. package/test/logic/101-builtin-mutation-kills.sqllogic +974 -0
  167. package/test/logic/101-transaction-edge-cases.sqllogic +395 -0
  168. package/test/logic/102-schema-catalog-edge-cases.sqllogic +252 -0
  169. package/test/logic/102-unique-constraints.sqllogic +145 -0
  170. package/test/logic/102.0.1-schema-catalog-indexed-table.sqllogic +32 -0
  171. package/test/logic/102.1-unique-edge-cases.sqllogic +111 -0
  172. package/test/logic/102.1.1-unique-index-creation-edge-cases.sqllogic +75 -0
  173. package/test/logic/102.2-unique-collation.sqllogic +181 -0
  174. package/test/logic/102.2.1-unique-collation-index-derived.sqllogic +305 -0
  175. package/test/logic/103-database-options-edge-cases.sqllogic +108 -0
  176. package/test/logic/103.1-pragma-boolean-values.sqllogic +126 -0
  177. package/test/logic/104-emit-mutation-kills.sqllogic +236 -0
  178. package/test/logic/105-vtab-memory-mutation-kills.sqllogic +754 -0
  179. package/test/logic/105.1-vtab-memory-index-mutation-kills.sqllogic +540 -0
  180. package/test/logic/106-constraint-extractor-mutation-kills.sqllogic +350 -0
  181. package/test/logic/107-temporal-arithmetic-mutation-kills.sqllogic +833 -0
  182. package/test/logic/108-cardinality-estimation.sqllogic +146 -0
  183. package/test/logic/108.1-analyze-name-resolution.sqllogic +102 -0
  184. package/test/logic/109-aggregate-physical-selection.sqllogic +103 -0
  185. package/test/logic/11-joins.sqllogic +86 -0
  186. package/test/logic/11.1-join-using.sqllogic +267 -0
  187. package/test/logic/11.2-comma-join.sqllogic +35 -0
  188. package/test/logic/11.3-index-nested-loop-join.sqllogic +305 -0
  189. package/test/logic/11.3-mismatched-collation-join.sqllogic +74 -0
  190. package/test/logic/11.4-hash-join-side-swap.sqllogic +118 -0
  191. package/test/logic/110-scan-emitter-mutation-kills.sqllogic +121 -0
  192. package/test/logic/110.1-scan-emitter-index-scan-kills.sqllogic +47 -0
  193. package/test/logic/12-empty-primary-key.sqllogic +124 -0
  194. package/test/logic/12-join_padding_order.sqllogic +58 -0
  195. package/test/logic/12.1-orderby-index-consumption.sqllogic +40 -0
  196. package/test/logic/13-cte.sqllogic +137 -0
  197. package/test/logic/13.1-cte-multiple-recursive.sqllogic +104 -0
  198. package/test/logic/13.10-cte-qualified-name.sqllogic +136 -0
  199. package/test/logic/13.11-unreferenced-dml-cte.sqllogic +414 -0
  200. package/test/logic/13.12-nested-dml-cte-rejected.sqllogic +280 -0
  201. package/test/logic/13.2-cte-bind-params.sqllogic +98 -0
  202. package/test/logic/13.3-cte-edge-cases.sqllogic +120 -0
  203. package/test/logic/13.4-cte-extras.sqllogic +98 -0
  204. package/test/logic/13.5-cte-join-order.sqllogic +166 -0
  205. package/test/logic/13.6-cte-dml-runs-once.sqllogic +318 -0
  206. package/test/logic/13.7-cte-sibling-visibility.sqllogic +227 -0
  207. package/test/logic/13.8-insert-with-clause-visibility.sqllogic +254 -0
  208. package/test/logic/13.9-schema-authored-cte-isolation.sqllogic +328 -0
  209. package/test/logic/13.9.1-schema-authored-schema-path-isolation.sqllogic +408 -0
  210. package/test/logic/14-utilities.sqllogic +168 -0
  211. package/test/logic/15-timespan.sqllogic +279 -0
  212. package/test/logic/15.1-semantic-ordering.sqllogic +960 -0
  213. package/test/logic/15.1.1-json-check-coercion.sqllogic +178 -0
  214. package/test/logic/15.1.2-semantic-ordering-index.sqllogic +41 -0
  215. package/test/logic/15.1.3-declared-form-write-contract.sqllogic +519 -0
  216. package/test/logic/16-epoch.sqllogic +187 -0
  217. package/test/logic/17-weekday-modifier.sqllogic +78 -0
  218. package/test/logic/18-json-string-escapes.sqllogic +86 -0
  219. package/test/logic/18-strftime-extended-formats.sqllogic +68 -0
  220. package/test/logic/20-empty-single-row.sqllogic +261 -0
  221. package/test/logic/21-null-edge-cases.sqllogic +227 -0
  222. package/test/logic/21.1-where-null-comparisons.sqllogic +151 -0
  223. package/test/logic/21.1.1-where-null-comparisons-secondary-index.sqllogic +35 -0
  224. package/test/logic/21.2-between-edges.sqllogic +157 -0
  225. package/test/logic/22-boundary-values.sqllogic +175 -0
  226. package/test/logic/23-self-joins-duplicates.sqllogic +74 -0
  227. package/test/logic/24-builtin-branches.sqllogic +611 -0
  228. package/test/logic/24.1-substr-extras.sqllogic +75 -0
  229. package/test/logic/24.2-random-extras.sqllogic +39 -0
  230. package/test/logic/25-aggregate-edge-cases.sqllogic +289 -0
  231. package/test/logic/25.1-nested-aggregates.sqllogic +74 -0
  232. package/test/logic/25.2-having-edge-cases.sqllogic +318 -0
  233. package/test/logic/25.3-aggregate-isnull-empty.sqllogic +16 -0
  234. package/test/logic/25.4-groupby-fd-constant-empty.sqllogic +55 -0
  235. package/test/logic/25.5-ungrouped-aggregate-column-free-select-item.sqllogic +196 -0
  236. package/test/logic/26-join-edge-cases.sqllogic +333 -0
  237. package/test/logic/26.1-left-join-isnull-on-notnull.sqllogic +26 -0
  238. package/test/logic/26.2-left-join-on-vs-where.sqllogic +67 -0
  239. package/test/logic/26.3-join-on-constants.sqllogic +61 -0
  240. package/test/logic/26.4-join-on-is-null-nullable.sqllogic +44 -0
  241. package/test/logic/27-window-edge-cases.sqllogic +206 -0
  242. package/test/logic/27.1-window-groups-frame.sqllogic +13 -0
  243. package/test/logic/27.2-window-exclude.sqllogic +13 -0
  244. package/test/logic/27.3-window-json-aggregation.sqllogic +66 -0
  245. package/test/logic/28-set-ops-sort-edge-cases.sqllogic +444 -0
  246. package/test/logic/28.1-compound-limit-offset.sqllogic +143 -0
  247. package/test/logic/28.2-orderby-expression-extras.sqllogic +698 -0
  248. package/test/logic/28.2-set-op-branch-types.sqllogic +217 -0
  249. package/test/logic/29-constraint-edge-cases.sqllogic +442 -0
  250. package/test/logic/29.1-column-level-conflict-clause.sqllogic +171 -0
  251. package/test/logic/29.2-table-level-pk-conflict-clause.sqllogic +113 -0
  252. package/test/logic/40-constraints.sqllogic +418 -0
  253. package/test/logic/40.1-pk-desc-direction.sqllogic +76 -0
  254. package/test/logic/40.2-check-extras.sqllogic +683 -0
  255. package/test/logic/41-alter-table.sqllogic +191 -0
  256. package/test/logic/41-fk-cascade-conflict-and-self-ref.sqllogic +263 -0
  257. package/test/logic/41-fk-cross-schema.sqllogic +155 -0
  258. package/test/logic/41-fk-extended-targets.sqllogic +173 -0
  259. package/test/logic/41-foreign-keys.sqllogic +1001 -0
  260. package/test/logic/41-generated-column-errors.sqllogic +225 -0
  261. package/test/logic/41-generated-column-extras.sqllogic +598 -0
  262. package/test/logic/41-generated-column-scope.sqllogic +329 -0
  263. package/test/logic/41-generated-columns.sqllogic +361 -0
  264. package/test/logic/41.1-alter-pk.sqllogic +230 -0
  265. package/test/logic/41.1-fk-collation-conflict.sqllogic +277 -0
  266. package/test/logic/41.10-alter-drop-column-foreign-key.sqllogic +274 -0
  267. package/test/logic/41.10.1-alter-drop-column-unique-index.sqllogic +169 -0
  268. package/test/logic/41.10.2-alter-drop-column-check-and-assertion.sqllogic +1616 -0
  269. package/test/logic/41.10.3-alter-drop-column-referencing-fk.sqllogic +292 -0
  270. package/test/logic/41.10.4-drop-object-expression-dependents.sqllogic +602 -0
  271. package/test/logic/41.11-deferred-fk-with-rename.sqllogic +232 -0
  272. package/test/logic/41.12-alter-default-coercion.sqllogic +200 -0
  273. package/test/logic/41.13-alter-add-column-generated-backfill.sqllogic +566 -0
  274. package/test/logic/41.14-alter-add-column-subquery-backfill.sqllogic +129 -0
  275. package/test/logic/41.15-alter-column-default-type-check.sqllogic +178 -0
  276. package/test/logic/41.16-fk-unenforceable.sqllogic +364 -0
  277. package/test/logic/41.2-alter-column.sqllogic +382 -0
  278. package/test/logic/41.2.1-alter-column-retype-deleted-row-memory.sqllogic +32 -0
  279. package/test/logic/41.2.2-alter-column-retype-index-rebuild.sqllogic +77 -0
  280. package/test/logic/41.2.3-alter-column-set-not-null-pk-backfill.sqllogic +310 -0
  281. package/test/logic/41.2.3.1-alter-column-set-not-null-pk-backfill-secondary-index.sqllogic +37 -0
  282. package/test/logic/41.3-alter-add-column-unique.sqllogic +184 -0
  283. package/test/logic/41.3-alter-rename-propagation.sqllogic +2352 -0
  284. package/test/logic/41.3.1-alter-rename-index-propagation.sqllogic +94 -0
  285. package/test/logic/41.4-alter-add-column-constraints.sqllogic +855 -0
  286. package/test/logic/41.5-alter-misc.sqllogic +48 -0
  287. package/test/logic/41.5-cross-schema-foreign-keys.sqllogic +253 -0
  288. package/test/logic/41.6-alter-drop-rename-constraint.sqllogic +354 -0
  289. package/test/logic/41.6.1-alter-drop-constraint-index-derived.sqllogic +22 -0
  290. package/test/logic/41.7-alter-column-collate.sqllogic +105 -0
  291. package/test/logic/41.7.1-alter-column-collate-unique.sqllogic +45 -0
  292. package/test/logic/41.7.2-alter-column-collate-unique-store.sqllogic +208 -0
  293. package/test/logic/41.7.2.1-alter-column-collate-unique-index-store.sqllogic +114 -0
  294. package/test/logic/41.7.3-alter-column-retype-unique.sqllogic +293 -0
  295. package/test/logic/41.7.3.1-alter-column-retype-staged-rows-memory.sqllogic +41 -0
  296. package/test/logic/41.7.3.2-alter-column-retype-unique-index.sqllogic +72 -0
  297. package/test/logic/41.7.4-alter-column-retype-semantic-memory.sqllogic +445 -0
  298. package/test/logic/41.7.4.1-alter-column-retype-semantic-index-memory.sqllogic +91 -0
  299. package/test/logic/41.7.5-alter-column-collate-pk-staged-delete-memory.sqllogic +106 -0
  300. package/test/logic/41.8-alter-add-constraint-unique-fk.sqllogic +275 -0
  301. package/test/logic/41.8-alter-savepoint-staged-rows.sqllogic +443 -0
  302. package/test/logic/41.8.1-alter-rename-savepoint-staged-rows.sqllogic +408 -0
  303. package/test/logic/41.9-alter-constraint-body-change.sqllogic +180 -0
  304. package/test/logic/41.9-fk-restrict-batched.sqllogic +290 -0
  305. package/test/logic/42-committed-snapshot.sqllogic +254 -0
  306. package/test/logic/42-returning.sqllogic +159 -0
  307. package/test/logic/42.1-returning-extras.sqllogic +232 -0
  308. package/test/logic/42.2-returning-stored-row.sqllogic +165 -0
  309. package/test/logic/43-default-nullability.sqllogic +131 -0
  310. package/test/logic/43-transition-constraints.sqllogic +341 -0
  311. package/test/logic/43.1-default-collation.sqllogic +140 -0
  312. package/test/logic/43.1-notnull-or-conflict.sqllogic +79 -0
  313. package/test/logic/43.2-deferred-check-new-on-delete.sqllogic +95 -0
  314. package/test/logic/43.3-nullable-primary-key.sqllogic +587 -0
  315. package/test/logic/44-determinism-validation.sqllogic +164 -0
  316. package/test/logic/44-orthogonality.sqllogic +146 -0
  317. package/test/logic/44.1-nondeterministic-schema.sqllogic +316 -0
  318. package/test/logic/45-udf-determinism.sqllogic +39 -0
  319. package/test/logic/46-mutation-context.sqllogic +595 -0
  320. package/test/logic/47-upsert.sqllogic +255 -0
  321. package/test/logic/47.1-upsert-conflict-targets.sqllogic +169 -0
  322. package/test/logic/47.2-replace-and-or-clauses.sqllogic +164 -0
  323. package/test/logic/47.2.1-pk-replace-secondary-unique.sqllogic +374 -0
  324. package/test/logic/47.3-engine-or-fail.sqllogic +122 -0
  325. package/test/logic/47.3-upsert-conflict-target-collation.sqllogic +139 -0
  326. package/test/logic/47.3.1-upsert-conflict-index-derived-collation.sqllogic +25 -0
  327. package/test/logic/47.4-upsert-conflict-target-affinity.sqllogic +59 -0
  328. package/test/logic/47.5-upsert-do-update-constraints.sqllogic +440 -0
  329. package/test/logic/47.6-upsert-do-update-decomposed.sqllogic +100 -0
  330. package/test/logic/49-reference-graph.sqllogic +84 -0
  331. package/test/logic/50-declarative-schema.sqllogic +2550 -0
  332. package/test/logic/50-metadata-tags.sqllogic +1033 -0
  333. package/test/logic/50.0.1-metadata-tags-index.sqllogic +141 -0
  334. package/test/logic/50.1-declare-schema-pk.sqllogic +150 -0
  335. package/test/logic/50.2-declare-schema-renames.sqllogic +949 -0
  336. package/test/logic/50.2.1-declare-schema-index-renames.sqllogic +173 -0
  337. package/test/logic/50.3-declare-schema-duplicate-names.sqllogic +280 -0
  338. package/test/logic/50.4-declare-schema-apply-restore.sqllogic +192 -0
  339. package/test/logic/51-lens-foundation.sqllogic +147 -0
  340. package/test/logic/51-materialized-views.sqllogic +376 -0
  341. package/test/logic/51.5-materialized-views-coarsened-key.sqllogic +198 -0
  342. package/test/logic/51.7-maintained-table-attach-detach.sqllogic +468 -0
  343. package/test/logic/51.8-maintained-table-declared-constraints.sqllogic +440 -0
  344. package/test/logic/51.85-maintained-table-parent-fk.sqllogic +103 -0
  345. package/test/logic/51.9-maintained-table-secondary-unique.sqllogic +237 -0
  346. package/test/logic/51.9.1-maintained-table-partial-unique.sqllogic +36 -0
  347. package/test/logic/52-lens-overrides.sqllogic +566 -0
  348. package/test/logic/53-materialized-views-rowtime.sqllogic +1601 -0
  349. package/test/logic/53-reserved-tags.sqllogic +252 -0
  350. package/test/logic/53.1-materialized-view-write-through.sqllogic +329 -0
  351. package/test/logic/53.2-materialized-view-rename-propagation.sqllogic +483 -0
  352. package/test/logic/53.3-materialized-view-constraint-only-ddl.sqllogic +409 -0
  353. package/test/logic/53.3.1-materialized-view-index-ddl-irrelevance.sqllogic +29 -0
  354. package/test/logic/53.4-materialized-view-structural-alter-restore.sqllogic +436 -0
  355. package/test/logic/54-covering-mv-enforcement.sqllogic +314 -0
  356. package/test/logic/54.1-covering-mv-partial-enforcement.sqllogic +34 -0
  357. package/test/logic/55-internal-eviction-reporting.sqllogic +89 -0
  358. package/test/logic/55-lens-prover.sqllogic +202 -0
  359. package/test/logic/55.1-eviction-restrict-fk.sqllogic +148 -0
  360. package/test/logic/55.5-lens-authored-inverse.sqllogic +1468 -0
  361. package/test/logic/80-grow-retrieve-pass.sqllogic +79 -0
  362. package/test/logic/81-quickpick.sqllogic +24 -0
  363. package/test/logic/82-bloom-join.sqllogic +185 -0
  364. package/test/logic/83-merge-join.sqllogic +280 -0
  365. package/test/logic/84-asof-scan.sqllogic +238 -0
  366. package/test/logic/84-key-cardinality.sqllogic +50 -0
  367. package/test/logic/85-relational-const-folding.sqllogic +51 -0
  368. package/test/logic/86-scalar-cse.sqllogic +42 -0
  369. package/test/logic/90-error_paths.sqllogic +114 -0
  370. package/test/logic/90.1-parse-errors.sqllogic +77 -0
  371. package/test/logic/90.2-alter-table-errors.sqllogic +53 -0
  372. package/test/logic/90.2.1-alter-extra-errors.sqllogic +51 -0
  373. package/test/logic/90.3-expression-errors.sqllogic +43 -0
  374. package/test/logic/90.4-dml-errors.sqllogic +47 -0
  375. package/test/logic/90.5-unsupported-join-types.sqllogic +37 -0
  376. package/test/logic/90.5.1-right-full-join-read.sqllogic +157 -0
  377. package/test/logic/90.6-select-error-paths.sqllogic +57 -0
  378. package/test/logic/91-merge-join-edge-cases.sqllogic +159 -0
  379. package/test/logic/92-hash-aggregate-edge-cases.sqllogic +139 -0
  380. package/test/logic/93-ddl-view-edge-cases.sqllogic +104 -0
  381. package/test/logic/93.1-view-error-paths.sqllogic +74 -0
  382. package/test/logic/93.2-view-mutation-pending.sqllogic +108 -0
  383. package/test/logic/93.3-view-mutation-or-clause.sqllogic +60 -0
  384. package/test/logic/93.4-view-mutation.sqllogic +4881 -0
  385. package/test/logic/93.4.1-view-mutation-partial-unique-cardinality.sqllogic +31 -0
  386. package/test/logic/93.5-authored-inverse.sqllogic +415 -0
  387. package/test/logic/93.6-set-op-flagless-write.sqllogic +862 -0
  388. package/test/logic/94-tvf-edge-cases.sqllogic +58 -0
  389. package/test/logic/94.1-limit-offset-edge-cases.sqllogic +109 -0
  390. package/test/logic/94.2-limit-offset-extras.sqllogic +98 -0
  391. package/test/logic/95-assertions.sqllogic +1417 -0
  392. package/test/logic/96-subquery-edge-cases.sqllogic +276 -0
  393. package/test/logic/97-json-function-edge-cases.sqllogic +633 -0
  394. package/test/logic/97.1-json-blob-and-special-values.sqllogic +92 -0
  395. package/test/logic/98-temporal-edge-cases.sqllogic +515 -0
  396. package/test/logic/99-conversion-edge-cases.sqllogic +325 -0
  397. package/test/logic/99.0.1-schema-index-listing.sqllogic +21 -0
  398. package/test/logic/99.1-cast-syntax-extras.sqllogic +167 -0
  399. package/test/logic/drop-unique-index.sqllogic +88 -0
  400. package/test/logic/pushdown-test.sqllogic +36 -0
@@ -0,0 +1,200 @@
1
+ -- conjunct-monotonicity.sqllogic - adding a WHERE conjunct must never widen the result
2
+ --
3
+ -- Regression guard for `bug-primary-key-conjunct-lost-with-correlated-subquery`, and
4
+ -- the generalisation of `bug-filter-conjunct-lost-under-index-order` (pinned next door
5
+ -- in 07.7.5). Both bugs are the same observable class: a conjunct is silently dropped
6
+ -- and the query returns rows the WHERE excluded.
7
+ --
8
+ -- Mechanism of this instance: once `rule-grow-retrieve` equips a Retrieve with an
9
+ -- index-style access plan, that plan is the SOLE authority for what the table access
10
+ -- enforces — `rule-select-access-path` never reads `Retrieve.source`. A correlated
11
+ -- sub-select conjunct is hoisted above the Retrieve as its own Filter, gets rewritten
12
+ -- into a semi join, and join-predicate-pushdown then drops the REMAINING plain conjunct
13
+ -- back down as a bare Filter directly over the already-equipped Retrieve. That fires
14
+ -- grow-retrieve a SECOND time, and its re-probe used to build a request from the new
15
+ -- Filter alone — replacing the committed plan (which was seeking `id = 2`) with an
16
+ -- ordering-only index walk that enforced nothing. Three conjuncts and a CORRELATED
17
+ -- sub-select are both required: with two conjuncts nothing is left over to push back
18
+ -- down, and an uncorrelated sub-select keeps the residual as a single Filter.
19
+ --
20
+ -- Invariant pinned here: the result of a WHERE is monotonically non-increasing in its
21
+ -- conjuncts. For conjuncts K (primary key), N (non-key column) and S (correlated
22
+ -- sub-select), every superset's row set is contained in every subset's. The sweep below
23
+ -- enumerates all seven non-empty subsets, so it covers both known instances and every
24
+ -- shape between them rather than only the one query that was reported.
25
+
26
+ -- =============================================================================
27
+ -- The sweep. Each conjunct discriminates, so a dropped one is always visible:
28
+ -- K = id = 2 -> {2}
29
+ -- N = flag = 1 -> {1,2}
30
+ -- S = exists (select 1 from o o2 where o2.id = o.id
31
+ -- and o2.id < 4) -> {1,2,3}
32
+ -- =============================================================================
33
+
34
+ create table o (id integer primary key, flag integer);
35
+ insert into o values (1, 1), (2, 1), (3, 0), (4, 0);
36
+
37
+ -- Singletons.
38
+
39
+ select id from o where id = 2;
40
+ → [{"id":2}]
41
+
42
+ select id from o where flag = 1;
43
+ → [{"id":1},{"id":2}]
44
+
45
+ select id from o where exists (select 1 from o o2 where o2.id = o.id and o2.id < 4);
46
+ → [{"id":1},{"id":2},{"id":3}]
47
+
48
+ -- Pairs. Each must be contained in both of its singletons.
49
+
50
+ select id from o where id = 2 and flag = 1;
51
+ → [{"id":2}]
52
+
53
+ select id from o where id = 2 and exists (select 1 from o o2 where o2.id = o.id and o2.id < 4);
54
+ → [{"id":2}]
55
+
56
+ select id from o where flag = 1 and exists (select 1 from o o2 where o2.id = o.id and o2.id < 4);
57
+ → [{"id":1},{"id":2}]
58
+
59
+ -- The triple, in every conjunct order. This is the shape that regressed: the answer
60
+ -- must stay {2} no matter which conjunct the planner happens to visit first.
61
+
62
+ select id from o where id = 2 and flag = 1 and exists (select 1 from o o2 where o2.id = o.id and o2.id < 4);
63
+ → [{"id":2}]
64
+
65
+ select id from o where flag = 1 and id = 2 and exists (select 1 from o o2 where o2.id = o.id and o2.id < 4);
66
+ → [{"id":2}]
67
+
68
+ select id from o where exists (select 1 from o o2 where o2.id = o.id and o2.id < 4) and flag = 1 and id = 2;
69
+ → [{"id":2}]
70
+
71
+ select id from o where flag = 1 and exists (select 1 from o o2 where o2.id = o.id and o2.id < 4) and id = 2;
72
+ → [{"id":2}]
73
+
74
+ drop table o;
75
+
76
+ -- =============================================================================
77
+ -- The reported shapes, verbatim. Every row carries flag = 1 here, so the non-key
78
+ -- conjunct cannot mask a dropped key conjunct: dropping `id = 2` yields all three
79
+ -- rows, which is unmistakable.
80
+ -- =============================================================================
81
+
82
+ create table o (id integer primary key, flag integer);
83
+ insert into o values (1, 1), (2, 1), (3, 1);
84
+
85
+ -- Correlated EXISTS on the key column.
86
+
87
+ select id from o where flag = 1 and id = 2 and exists (select 1 from o o2 where o2.id = o.id);
88
+ → [{"id":2}]
89
+
90
+ -- Correlated EXISTS on the NON-key column: a different decorrelation shape.
91
+
92
+ select id from o where flag = 1 and id = 2 and exists (select 1 from o o2 where o2.flag = o.flag);
93
+ → [{"id":2}]
94
+
95
+ -- Correlated scalar sub-select rather than EXISTS.
96
+
97
+ select id from o where flag = 1 and id = 2 and (select count(*) from o o2 where o2.id = o.id) = 1;
98
+ → [{"id":2}]
99
+
100
+ -- The key conjunct written as a single-element IN: extracts as an IN constraint, not '='.
101
+
102
+ select id from o where flag = 1 and id in (2) and exists (select 1 from o o2 where o2.id = o.id);
103
+ → [{"id":2}]
104
+
105
+ -- The sub-select written first.
106
+
107
+ select id from o where exists (select 1 from o o2 where o2.id = o.id) and flag = 1 and id = 2;
108
+ → [{"id":2}]
109
+
110
+ -- BETWEEN: decomposes into a lower and an upper bound sharing ONE source expression.
111
+ -- The de-duplication that keeps the re-probe's unioned constraint list clean must key
112
+ -- on (expression, column, op) — keying on the expression alone collapses these two into
113
+ -- one and silently drops the upper bound.
114
+
115
+ select id from o where flag = 1 and id between 2 and 2 and exists (select 1 from o o2 where o2.id = o.id);
116
+ → [{"id":2}]
117
+
118
+ select id from o where flag = 1 and id between 2 and 3 and exists (select 1 from o o2 where o2.id = o.id);
119
+ → [{"id":2},{"id":3}]
120
+
121
+ -- A range key conjunct rather than an equality: keeps more than one row, so a dropped
122
+ -- predicate cannot be mistaken for a correct answer.
123
+
124
+ select id from o where flag = 1 and id >= 2 and exists (select 1 from o o2 where o2.id = o.id);
125
+ → [{"id":2},{"id":3}]
126
+
127
+ -- Aliased table with qualified column references throughout.
128
+
129
+ select E.id from o E where E.flag = 1 and E.id = 2 and (select count(*) from o T where T.id = E.id) = 1;
130
+ → [{"id":2}]
131
+
132
+ -- =============================================================================
133
+ -- Control shapes that were already correct and must stay so: they exercise the
134
+ -- neighbouring paths through the same rule (two conjuncts, constant conjunct,
135
+ -- UNCORRELATED sub-select, absorbed ORDER BY, pushed LIMIT).
136
+ -- =============================================================================
137
+
138
+ select id from o where id = 2 and exists (select 1 from o o2 where o2.id = o.id);
139
+ → [{"id":2}]
140
+
141
+ select id from o where flag = 1 and id = 2 and 1 = 1;
142
+ → [{"id":2}]
143
+
144
+ select id from o where flag = 1 and id = 2 and (select count(*) from o o2) = 3;
145
+ → [{"id":2}]
146
+
147
+ select id from o where id = 2 order by id;
148
+ → [{"id":2}]
149
+
150
+ select id from o where id = 2 order by id desc;
151
+ → [{"id":2}]
152
+
153
+ select id from o where id = 2 limit 5;
154
+ → [{"id":2}]
155
+
156
+ select id from o where flag = 1 and id = 2 order by id;
157
+ → [{"id":2}]
158
+
159
+ -- The full shape with an ORDER BY on top: the Sort re-probes the equipped Retrieve
160
+ -- through the same code path the Filter does, so it must not clobber the seek either.
161
+
162
+ select id from o where flag = 1 and id = 2 and exists (select 1 from o o2 where o2.id = o.id) order by id;
163
+ → [{"id":2}]
164
+
165
+ select id from o where flag = 1 and id >= 2 and exists (select 1 from o o2 where o2.id = o.id) order by id desc;
166
+ → [{"id":3},{"id":2}]
167
+
168
+ -- LIMIT on top: LimitOffset is the third operation grow-retrieve can translate into an
169
+ -- index-style context, so it re-probes the committed plan as well.
170
+
171
+ select id from o where flag = 1 and id >= 2 and exists (select 1 from o o2 where o2.id = o.id) order by id limit 1;
172
+ → [{"id":2}]
173
+
174
+ drop table o;
175
+
176
+ -- =============================================================================
177
+ -- The mutation planner walks the same Retrieve/access-path rules, so a dropped
178
+ -- conjunct there destroys or corrupts rows the WHERE excluded. This is the arm that
179
+ -- makes the bug data-loss rather than a wrong read.
180
+ -- =============================================================================
181
+
182
+ create table m (id integer primary key, flag integer);
183
+ insert into m values (1, 1), (2, 1), (3, 1);
184
+
185
+ delete from m where flag = 1 and id = 2 and exists (select 1 from m m2 where m2.id = m.id);
186
+
187
+ select id, flag from m order by id;
188
+ → [{"id":1,"flag":1},{"id":3,"flag":1}]
189
+
190
+ drop table m;
191
+
192
+ create table m (id integer primary key, flag integer);
193
+ insert into m values (1, 1), (2, 1), (3, 1);
194
+
195
+ update m set flag = 9 where flag = 1 and id = 2 and exists (select 1 from m m2 where m2.id = m.id);
196
+
197
+ select id, flag from m order by id;
198
+ → [{"id":1,"flag":1},{"id":2,"flag":9},{"id":3,"flag":1}]
199
+
200
+ drop table m;
@@ -0,0 +1,71 @@
1
+ -- correlated-subquery-edges.sqllogic - Correlated subquery edge cases
2
+
3
+ -- Setup tables
4
+ create table o (id integer primary key, cat text);
5
+ insert into o values (1, 'X'), (2, 'Y'), (3, 'X');
6
+
7
+ create table i (id integer primary key, o_id integer null, val integer);
8
+ insert into i values (10, 1, 100), (20, 1, 200), (30, 2, 300), (40, null, 400);
9
+
10
+ -- ===== Correlated subquery with empty correlation =====
11
+
12
+ -- Outer row id=3 has no matching inner rows — sum should be null
13
+ select o.id, (select sum(val) from i where i.o_id = o.id) as total from o order by o.id;
14
+ → [{"id":1,"total":300},{"id":2,"total":300},{"id":3,"total":null}]
15
+
16
+ -- ===== Multi-level correlation =====
17
+
18
+ -- Subquery of subquery referencing outermost table
19
+ -- For o.id=1: inner vals 100,200; min=100; count where val>100 = 1
20
+ -- For o.id=2: inner val 300; min=300; count where val>300 = 0
21
+ -- For o.id=3: no inner rows; count = 0
22
+ select o.id,
23
+ (select count(*) from i i1
24
+ where i1.o_id = o.id
25
+ and i1.val > (select min(val) from i i2 where i2.o_id = o.id)
26
+ ) as cnt
27
+ from o order by o.id;
28
+ → [{"id":1,"cnt":1},{"id":2,"cnt":0},{"id":3,"cnt":0}]
29
+
30
+ -- ===== Correlated EXISTS vs IN equivalence =====
31
+
32
+ -- EXISTS form
33
+ select id from o where exists (select 1 from i where i.o_id = o.id) order by id;
34
+ → [{"id":1},{"id":2}]
35
+
36
+ -- IN form (should return same results)
37
+ select id from o where id in (select o_id from i where o_id is not null) order by id;
38
+ → [{"id":1},{"id":2}]
39
+
40
+ -- ===== Correlated subquery in different positions =====
41
+
42
+ -- In SELECT list
43
+ select o.id, (select max(val) from i where i.o_id = o.id) as max_val from o order by o.id;
44
+ → [{"id":1,"max_val":200},{"id":2,"max_val":300},{"id":3,"max_val":null}]
45
+
46
+ -- In WHERE
47
+ select * from o where (select count(*) from i where i.o_id = o.id) > 1 order by o.id;
48
+ → [{"id":1,"cat":"X"}]
49
+
50
+ -- In HAVING
51
+ select o.cat, count(*) as cnt from o group by o.cat having count(*) > (select 1) order by o.cat;
52
+ → [{"cat":"X","cnt":2}]
53
+
54
+ -- Correlated subquery with aggregation that returns NULL for no-match, coalesced
55
+ select o.id, coalesce((select sum(val) from i where i.o_id = o.id), 0) as total from o order by o.id;
56
+ → [{"id":1,"total":300},{"id":2,"total":300},{"id":3,"total":0}]
57
+
58
+ -- ===== NOT IN with NULLs in subquery =====
59
+
60
+ -- Classic SQL gotcha: NOT IN with NULL in subquery list
61
+ -- i has o_id=NULL, so x NOT IN (..., NULL) is UNKNOWN for all x → empty result
62
+ select * from o where id not in (select o_id from i);
63
+ → []
64
+
65
+ -- NOT IN excluding NULLs works as expected
66
+ select * from o where id not in (select o_id from i where o_id is not null) order by id;
67
+ → [{"id":3,"cat":"X"}]
68
+
69
+ -- Cleanup
70
+ drop table o;
71
+ drop table i;
@@ -0,0 +1,156 @@
1
+ -- 07.8.1-correlated-in-decorrelation-gates.sqllogic
2
+ --
3
+ -- Regression guard for bug-in-decorrelation-inner-shape-unchecked. A correlated
4
+ -- `col IN (subquery)` gets rewritten by rule-subquery-decorrelation into a semi
5
+ -- join (see rule-subquery-decorrelation.ts). Two assumptions in that rewrite were
6
+ -- unchecked: the join condition was built from the subquery's first OUTPUT
7
+ -- column but the join's right side came from wherever a Project/Alias descent
8
+ -- landed (a mismatch for a COMPUTED projection, whose output column has no base
9
+ -- attribute for the right side to expose), and the descent only recognized
10
+ -- Project/Alias, so DISTINCT / LIMIT / set-ops / ORDER BY above the correlated
11
+ -- filter were silently treated as if absent. Both produced a runtime crash:
12
+ -- "No row context found for column ...". The fix adds gates that decline the
13
+ -- rewrite for all of these shapes, falling back to the correct (if slower)
14
+ -- runtime set-probe IN evaluation — every query below must return the right
15
+ -- answer whether the rule fires or declines.
16
+ --
17
+ -- b.id is a PRIMARY KEY, so `b.id = a.id` matches at most one row — DISTINCT and
18
+ -- LIMIT never change which rows the *plain* correlated shapes return; they are
19
+ -- here to pin that the decline is safe, not to change the result set. id=8
20
+ -- exercises COALESCE actually changing the outcome (a genuine NULL-vs-default
21
+ -- distinction), and the UNION cases append a constant unrelated to correlation
22
+ -- so a wrongly-decorrelated plan (which would silently drop the outer-row
23
+ -- context) is caught by a changed expected result.
24
+
25
+ create table a (id integer primary key, x integer null);
26
+ create table b (id integer primary key, x integer null);
27
+
28
+ -- id=3: outer x is NULL (never matches under three-valued IN, regardless of the
29
+ -- inner). id=4: no matching b row (empty subquery). id=5: matching inner
30
+ -- row has NULL x. id=8: matching inner row has NULL x, but outer x = 0 —
31
+ -- distinguishes COALESCE(b.x, 0) from bare b.x.
32
+ insert into a values (1, 10), (2, 20), (3, null), (4, 40), (5, 50), (6, 60), (7, 21), (8, 0);
33
+ insert into b values (1, 10), (2, 20), (3, 30), (5, null), (6, 65), (7, 20), (8, null);
34
+
35
+ -- ===== Plain correlated IN (the baseline the gated shapes are compared to) =====
36
+
37
+ select a.x from a where a.x in (select b.x from b where b.id = a.id) order by a.id;
38
+ → [{"x":10},{"x":20}]
39
+
40
+ -- ===== Computed inner projection: the right side's attribute id is fresh, so =====
41
+ -- ===== the join-key-exposure gate must decline (defect 1) =====
42
+
43
+ select a.x from a where a.x in (select b.x + 0 from b where b.id = a.id) order by a.id;
44
+ → [{"x":10},{"x":20}]
45
+
46
+ -- Computed projection with a residual inner-only predicate alongside the
47
+ -- correlation (b.x > 5 excludes the id=5/id=8 NULL rows via WHERE's NULL drop)
48
+ select a.x from a where a.x in (select b.x * 1 from b where b.id = a.id and b.x > 5) order by a.id;
49
+ → [{"x":10},{"x":20}]
50
+
51
+ -- COALESCE: id=8's inner NULL becomes 0, matching outer x=0 — only correct if
52
+ -- the computed projection's own value (not some other column) is compared
53
+ select a.x from a where a.x in (select coalesce(b.x, 0) from b where b.id = a.id) order by a.id;
54
+ → [{"x":10},{"x":20},{"x":0}]
55
+
56
+ -- ===== DISTINCT / LIMIT above the correlated filter: descent used to stop =====
57
+ -- ===== silently with no inner FilterNode found (defect 2) =====
58
+
59
+ select a.x from a where a.x in (select distinct b.x from b where b.id = a.id) order by a.id;
60
+ → [{"x":10},{"x":20}]
61
+
62
+ select a.x from a where a.x in (select b.x from b where b.id = a.id limit 1) order by a.id;
63
+ → [{"x":10},{"x":20}]
64
+
65
+ select a.x from a where a.x in (select b.x from b where b.id = a.id limit 1 offset 0) order by a.id;
66
+ → [{"x":10},{"x":20}]
67
+
68
+ -- ===== Set operations / ORDER BY above the correlated filter =====
69
+
70
+ -- UNION appends a constant (40) unrelated to any a.id's correlation; id=4 (no
71
+ -- matching b row) picks it up only because IN evaluates per-row, not because
72
+ -- the rewrite silently dropped the correlation
73
+ select a.x from a where a.x in (select b.x from b where b.id = a.id union select 40) order by a.id;
74
+ → [{"x":10},{"x":20},{"x":40}]
75
+
76
+ select a.x from a where a.x in (select b.x from b where b.id = a.id union all select 40) order by a.id;
77
+ → [{"x":10},{"x":20},{"x":40}]
78
+
79
+ select a.x from a where a.x in (select b.x from b where b.id = a.id order by b.x) order by a.id;
80
+ → [{"x":10},{"x":20}]
81
+
82
+ -- ===== The inner *derived table* still decorrelates: only the outer subquery =====
83
+ -- ===== root's own shape gates the rewrite, not an uncorrelated shape nested =====
84
+ -- ===== further inside =====
85
+
86
+ select a.x from a where a.x in (select t.x from (select * from b) t where t.id = a.id) order by a.id;
87
+ → [{"x":10},{"x":20}]
88
+
89
+ -- ===== Two correlated IN conjuncts in one WHERE: exercises the rule's internal =====
90
+ -- ===== per-conjunct loop (each becomes its own stacked semi join) =====
91
+
92
+ select a.x from a where a.x in (select b.x from b where b.id = a.id) and a.x in (select b.x + 0 from b where b.id = a.id) order by a.id;
93
+ → [{"x":10},{"x":20}]
94
+
95
+ -- ===== NOT IN: parses as NOT over the InNode, so it never matches the rule's =====
96
+ -- ===== top-level-conjunct shape and always stays on the per-row path — pinned =====
97
+ -- ===== here for NULL-semantics correctness across the same inner shapes =====
98
+
99
+ select a.x from a where a.x not in (select b.x from b where b.id = a.id) order by a.id;
100
+ → [{"x":40},{"x":60},{"x":21}]
101
+
102
+ select a.x from a where a.x not in (select b.x + 0 from b where b.id = a.id) order by a.id;
103
+ → [{"x":40},{"x":60},{"x":21}]
104
+
105
+ -- ===== NOT EXISTS over the same gated inner shapes (existence only, so the =====
106
+ -- ===== presence/absence of a matching b row is what matters) =====
107
+
108
+ select a.x from a where not exists (select distinct 1 from b where b.id = a.id) order by a.id;
109
+ → [{"x":40}]
110
+
111
+ select a.x from a where not exists (select 1 from b where b.id = a.id limit 1) order by a.id;
112
+ → [{"x":40}]
113
+
114
+ -- ===== More inner roots the Project/Alias descent cannot step through. Each =====
115
+ -- ===== leaves the correlated filter buried below, so the external-reference =====
116
+ -- ===== backstop declines and the per-row path answers. =====
117
+
118
+ -- CTE body inside the subquery
119
+ select a.x from a where a.x in (with c as (select b.x from b where b.id = a.id) select c.x from c) order by a.id;
120
+ → [{"x":10},{"x":20}]
121
+
122
+ -- aggregate root (one row per outer row, so IN sees a single value)
123
+ select a.x from a where a.x in (select max(b.x) from b where b.id = a.id) order by a.id;
124
+ → [{"x":10},{"x":20}]
125
+
126
+ -- grouped root
127
+ select a.x from a where a.x in (select b.x from b where b.id = a.id group by b.x) order by a.id;
128
+ → [{"x":10},{"x":20}]
129
+
130
+ -- window function in the inner projection
131
+ select a.x from a where a.x in (select max(b.x) over () from b where b.id = a.id) order by a.id;
132
+ → [{"x":10},{"x":20}]
133
+
134
+ -- EXCEPT / INTERSECT (the set-op cases beyond UNION)
135
+ select a.x from a where a.x in (select b.x from b where b.id = a.id except select 99) order by a.id;
136
+ → [{"x":10},{"x":20}]
137
+
138
+ select a.x from a where a.x in (select b.x from b where b.id = a.id intersect select 10) order by a.id;
139
+ → [{"x":10}]
140
+
141
+ -- ===== Plain (non-negated) EXISTS over the same gated inner shapes: the =====
142
+ -- ===== external-reference backstop sits on the shared correlated arm, so it =====
143
+ -- ===== gates the EXISTS rewrite too. Every a row with a matching b row. =====
144
+
145
+ select a.x from a where exists (select distinct b.x from b where b.id = a.id) order by a.id;
146
+ → [{"x":10},{"x":20},{"x":null},{"x":50},{"x":60},{"x":21},{"x":0}]
147
+
148
+ select a.x from a where exists (select b.x + 0 from b where b.id = a.id) order by a.id;
149
+ → [{"x":10},{"x":20},{"x":null},{"x":50},{"x":60},{"x":21},{"x":0}]
150
+
151
+ -- UNION arm adds a row unconditionally, so EXISTS is true for every a row
152
+ select a.x from a where exists (select b.x from b where b.id = a.id union select 40) order by a.id;
153
+ → [{"x":10},{"x":20},{"x":null},{"x":40},{"x":50},{"x":60},{"x":21},{"x":0}]
154
+
155
+ drop table a;
156
+ drop table b;
@@ -0,0 +1,154 @@
1
+ -- in-value-list.sqllogic - `WHERE col IN (<value-list>)` set-membership regression.
2
+ --
3
+ -- Regression for the memory-vtab IN multi-seek (plan=5): a duplicate literal
4
+ -- multiplied matching rows (bag, not set) and a NULL list element fell through
5
+ -- to a full-index walk (spurious non-matching rows). The fix lives in
6
+ -- scanLayer's multi-seek branch (src/vtab/memory/layer/scan-layer.ts): skip
7
+ -- NULL/NULL-containing seek keys and dedup yielded rows by primary key.
8
+ --
9
+ -- These assertions are pure set-membership (counts + exact rows), so they hold
10
+ -- on both the memory module (multi-seek path, indexed columns) and the store
11
+ -- module (residual scalar IN filter). Indexed columns below force the multi-seek
12
+ -- path under the default memory module; the non-indexed table exercises the
13
+ -- residual-filter path in the same run.
14
+
15
+ create table t (k integer primary key, v integer unique);
16
+ insert into t values (1, 5), (2, 7);
17
+
18
+ -- ===== Single-value baseline (correct before and after the fix) =====
19
+
20
+ select k, v from t where v in (5);
21
+ → [{"k":1,"v":5}]
22
+
23
+ -- ===== Single-value NULL equality (plan=2 point-seek) returns zero rows =====
24
+ -- `col = null` is UNKNOWN ⇒ no rows. Regression for the point-seek branch of
25
+ -- scanLayer, where a NULL equality key fell through to the full-index walk and
26
+ -- returned every row (fix/in-null-equality-returns-all-rows). Covers the unique
27
+ -- secondary index (v) and the primary key (k) on the memory module, plus the
28
+ -- residual-filter path on the store module.
29
+
30
+ select k, v from t where v in (null);
31
+ → []
32
+
33
+ select k, v from t where v = null;
34
+ → []
35
+
36
+ select k, v from t where k = null;
37
+ → []
38
+
39
+ select k, v from t where k in (null);
40
+ → []
41
+
42
+ -- ===== (a) Duplicate literal must not multiply the matching row =====
43
+
44
+ select k, v from t where v in (5, 5);
45
+ → [{"k":1,"v":5}]
46
+
47
+ -- ===== (b) NULL element contributes no match (no full-scan fallthrough) =====
48
+
49
+ select k, v from t where v in (5, null);
50
+ → [{"k":1,"v":5}]
51
+
52
+ -- ===== (c) Duplicates and NULL combined =====
53
+
54
+ select k, v from t where v in (5, null, 5, 9);
55
+ → [{"k":1,"v":5}]
56
+
57
+ select v from t where v in (5, 5, 9);
58
+ → [{"v":5}]
59
+
60
+ -- A duplicate that matches no row stays empty (not a spurious full scan).
61
+ select k, v from t where v in (9, 9, null) order by k;
62
+ → []
63
+
64
+ -- Two distinct matching values, each duplicated → each row exactly once.
65
+ select k, v from t where v in (5, 5, 7, 7) order by k;
66
+ → [{"k":1,"v":5},{"k":2,"v":7}]
67
+
68
+ -- ===== (d) IN on the PRIMARY KEY column (also multi-seeks) =====
69
+
70
+ select k, v from t where k in (1, 1, 2, null) order by k;
71
+ → [{"k":1,"v":5},{"k":2,"v":7}]
72
+
73
+ select k from t where k in (1, 2, 2) order by k;
74
+ → [{"k":1},{"k":2}]
75
+
76
+ -- ===== (f) Same WHERE with and without DISTINCT must agree =====
77
+
78
+ select v from t where v in (5, null, 5, 9);
79
+ → [{"v":5}]
80
+
81
+ select distinct v from t where v in (5, null, 5, 9);
82
+ → [{"v":5}]
83
+
84
+ select distinct k, v from t where v in (5, 5, 7, 7) order by k;
85
+ → [{"k":1,"v":5},{"k":2,"v":7}]
86
+
87
+ drop table t;
88
+
89
+ -- ===== Fuzz counterexample shape: REAL UNIQUE index, 0 / NULL / dup / 820 =====
90
+
91
+ create table t1 (id integer primary key, c_real2 real unique);
92
+ insert into t1 values (1, 0.0), (2, 820.0), (3, -79.79), (4, 100.0);
93
+
94
+ -- Exactly the rows whose c_real2 ∈ {0, 820}, once each — no duplicate-0 row,
95
+ -- no spurious -79.79/100 row from the NULL element.
96
+ select id, c_real2 from t1 where c_real2 in (0, null, 0, 820) order by id;
97
+ → [{"id":1,"c_real2":0},{"id":2,"c_real2":820}]
98
+
99
+ select distinct id, c_real2 from t1 where c_real2 in (0, null, 0, 820) order by id;
100
+ → [{"id":1,"c_real2":0},{"id":2,"c_real2":820}]
101
+
102
+ drop table t1;
103
+
104
+ -- ===== (e) Composite-index IN (cross-product) with a duplicate and a NULL =====
105
+ -- Lives in 07.9.1-in-value-list-composite-index: the composite seek needs a
106
+ -- standalone `create index`, where every scenario here reaches the multi-seek
107
+ -- through an inline PRIMARY KEY / UNIQUE column.
108
+
109
+ -- ===== Non-indexed column: residual scalar IN filter path =====
110
+ -- w has no index, so IN stays a residual InNode filter rather than a multi-seek.
111
+ -- Duplicates/NULL must behave identically: each base row tested once.
112
+
113
+ create table t2 (k integer primary key, w integer);
114
+ insert into t2 values (1, 5), (2, 7), (3, 5);
115
+
116
+ select k, w from t2 where w in (5, 5) order by k;
117
+ → [{"k":1,"w":5},{"k":3,"w":5}]
118
+
119
+ select k, w from t2 where w in (5, null) order by k;
120
+ → [{"k":1,"w":5},{"k":3,"w":5}]
121
+
122
+ select distinct w from t2 where w in (5, null, 5, 9) order by w;
123
+ → [{"w":5}]
124
+
125
+ drop table t2;
126
+
127
+ -- ===== BLOB constant value list (bug-in-subquery-blob-values-crash) =====
128
+ -- This path pre-builds a BTree at plan-emit time, so a BLOB element crashed
129
+ -- unconditionally, even before any row was scanned: inheritree freezes each
130
+ -- stored entry by default, and Object.freeze throws on a non-empty
131
+ -- Uint8Array, which is exactly what a BLOB scalar is.
132
+
133
+ create table t3 (id integer primary key, b blob null);
134
+ insert into t3 values (1, x'6162'), (2, x'6164'), (3, null);
135
+
136
+ select id from t3 where b in (x'6162', x'6164') order by id;
137
+ → [{"id":1},{"id":2}]
138
+
139
+ -- A NULL list element contributes no match, but turns a miss into NULL
140
+ select id from t3 where b in (x'6162', null) order by id;
141
+ → [{"id":1}]
142
+
143
+ select id, b in (x'6162', null) as r from t3 order by id;
144
+ → [{"id":1,"r":true},{"id":2,"r":null},{"id":3,"r":null}]
145
+
146
+ -- Empty BLOB literal x'' is a distinct, valid zero-length value, not NULL:
147
+ -- a miss against a NULL-bearing list is still NULL, and it matches itself.
148
+ select x'' in (x'6162', null) as r;
149
+ → [{"r":null}]
150
+
151
+ select x'' in (x'', x'6162') as r;
152
+ → [{"r":true}]
153
+
154
+ drop table t3;
@@ -0,0 +1,38 @@
1
+ -- 07.9.1-in-value-list-composite-index.sqllogic
2
+ --
3
+ -- requires-capability: standalone-index-ddl
4
+ --
5
+ -- Carved out of 07.9-in-value-list: every other scenario there reaches the IN
6
+ -- multi-seek path through an inline PRIMARY KEY or UNIQUE column (auto-indexed),
7
+ -- so the file needs no index DDL. This one scenario builds a standalone composite
8
+ -- `create index` to reach the two-column cross-product form of the same seek, and
9
+ -- would otherwise have cost a backend without first-class CREATE INDEX the entire
10
+ -- IN-value-list file.
11
+ --
12
+ -- Subject: the memory-vtab IN multi-seek over a COMPOSITE index — the a×b seek-key
13
+ -- cross-product must drop NULL-bearing tuples and dedup by primary key, so duplicate
14
+ -- list values never multiply matches.
15
+
16
+ -- ===== Composite-index IN (cross-product) with a duplicate and a NULL =====
17
+
18
+ create table c (id integer primary key, a integer, b integer);
19
+ create index idx_ab on c(a, b);
20
+ insert into c values (1, 1, 10), (2, 1, 20), (3, 2, 10), (4, 2, 20);
21
+
22
+ -- Cross-product (a∈{1,1} × b∈{10,null}): the NULL-bearing tuples drop, the
23
+ -- (1,10) duplicate collapses → a single row.
24
+ select id from c where a in (1, 1) and b in (10, null) order by id;
25
+ → [{"id":1}]
26
+
27
+ -- Duplicate values on both columns must not multiply matches.
28
+ select id from c where a in (1, 2, 2) and b in (10, 20, 20) order by id;
29
+ → [{"id":1},{"id":2},{"id":3},{"id":4}]
30
+
31
+ -- Duplicate + NULL collapsing to a two-row subset.
32
+ select id from c where a in (1, 1, 2) and b in (10, null) order by id;
33
+ → [{"id":1},{"id":3}]
34
+
35
+ select distinct id from c where a in (1, 1, 2) and b in (10, null) order by id;
36
+ → [{"id":1},{"id":3}]
37
+
38
+ drop table c;