@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,13 @@
1
+ -- adapted from SQLite test/window1.test, test/window4.test, test/window9.test
2
+ -- Named window definitions via the WINDOW clause; Quereus does not yet
3
+ -- support window-name references. Document the rejection.
4
+
5
+ create table nw (id integer primary key, val integer, grp text);
6
+ insert into nw values (1, 10, 'A'), (2, 20, 'A');
7
+
8
+ select id, val, grp,
9
+ row_number() over w as rn
10
+ from nw window w as (partition by grp order by id) order by id;
11
+ -- error: Window name references are not yet supported
12
+
13
+ drop table nw;
@@ -0,0 +1,13 @@
1
+ -- adapted from SQLite test/window1.test, test/window3.test, test/window6.test
2
+ -- nth_value(expr, n) is not currently implemented as a window function in
3
+ -- Quereus. Document the rejection.
4
+
5
+ create table nv (id integer primary key, val integer, grp text);
6
+ insert into nv values (1, 10, 'A'), (2, 20, 'A');
7
+
8
+ select id, val, grp,
9
+ nth_value(val, 1) over (partition by grp order by id) as nth1
10
+ from nv order by id;
11
+ -- error: Unknown window function: nth_value
12
+
13
+ drop table nv;
@@ -0,0 +1,78 @@
1
+ -- subqueries.sqllogic - Testing various subquery forms
2
+
3
+ -- Setup tables
4
+ CREATE TABLE outer_t (id INTEGER PRIMARY KEY, grp TEXT);
5
+ CREATE TABLE inner_t (id INTEGER PRIMARY KEY, val INTEGER, outer_id INTEGER NULL);
6
+
7
+ INSERT INTO outer_t VALUES (1, 'A'), (2, 'B'), (3, 'A');
8
+ INSERT INTO inner_t VALUES (10, 100, 1), (20, 200, 2), (30, 300, 1), (40, 400, 3), (50, 500, null);
9
+
10
+ -- Scalar Subquery (Uncorrelated)
11
+ SELECT (SELECT max(val) FROM inner_t);
12
+ → [{"(select max(val) from inner_t)":500}]
13
+ SELECT id, (SELECT count(*) FROM inner_t) as inner_count FROM outer_t ORDER BY id;
14
+ → [{"id":1,"inner_count":5},{"id":2,"inner_count":5},{"id":3,"inner_count":5}]
15
+
16
+ -- Scalar Subquery (Correlated)
17
+ SELECT id, (SELECT sum(val) FROM inner_t WHERE inner_t.outer_id = outer_t.id) as grp_sum FROM outer_t ORDER BY id;
18
+ → [{"id":1,"grp_sum":400},{"id":2,"grp_sum":200},{"id":3,"grp_sum":400}]
19
+
20
+ -- EXISTS Subquery (Uncorrelated)
21
+ SELECT id FROM outer_t WHERE EXISTS (SELECT 1 FROM inner_t WHERE val > 1000);
22
+ → []
23
+ SELECT id FROM outer_t WHERE EXISTS (SELECT 1 FROM inner_t WHERE val < 200);
24
+ → [{"id":1},{"id":2},{"id":3}]
25
+
26
+ -- EXISTS Subquery (Correlated)
27
+ SELECT id FROM outer_t WHERE EXISTS (SELECT 1 FROM inner_t WHERE inner_t.outer_id = outer_t.id AND val > 250) ORDER BY id;
28
+ → [{"id":1},{"id":3}]
29
+ SELECT id FROM outer_t WHERE NOT EXISTS (SELECT 1 FROM inner_t WHERE inner_t.outer_id = outer_t.id);
30
+ → []
31
+
32
+ -- IN Subquery (Uncorrelated)
33
+ SELECT id FROM outer_t WHERE id IN (SELECT outer_id FROM inner_t WHERE val > 300) ORDER BY id;
34
+ → [{"id":3}]
35
+ SELECT id FROM outer_t WHERE id NOT IN (SELECT outer_id FROM inner_t WHERE val < 300) ORDER BY id;
36
+ → [{"id":3}]
37
+
38
+ -- IN Subquery (Correlated - less common, but syntactically valid)
39
+ SELECT id FROM outer_t WHERE outer_t.grp IN (SELECT o2.grp FROM outer_t o2 WHERE o2.id = outer_t.id) ORDER BY id;
40
+ → [{"id":1},{"id":2},{"id":3}]
41
+
42
+ -- Comparison Subquery (Uncorrelated)
43
+ SELECT id FROM outer_t WHERE id = (SELECT min(outer_id) FROM inner_t);
44
+ → [{"id":1}]
45
+ SELECT id FROM outer_t WHERE id > (SELECT avg(outer_id) FROM inner_t WHERE outer_id IS NOT NULL) ORDER BY id;
46
+ → [{"id":2},{"id":3}]
47
+
48
+ -- Comparison Subquery (Correlated)
49
+ SELECT id FROM outer_t WHERE outer_t.id = (SELECT i.outer_id FROM inner_t i WHERE i.val = 400 AND i.outer_id = outer_t.id);
50
+ → [{"id":3}]
51
+
52
+ -- Subquery in SELECT list returning multiple rows (Error expected)
53
+ SELECT id, (SELECT val FROM inner_t WHERE inner_t.outer_id = outer_t.id) FROM outer_t;
54
+ -- error: Scalar subquery returned more than one row
55
+
56
+ -- Subqueries in ORDER BY (testing async expression handling)
57
+ -- Uncorrelated scalar subquery in ORDER BY
58
+ SELECT id, grp FROM outer_t ORDER BY (SELECT max(val) FROM inner_t), id;
59
+ → [{"id":1,"grp":"A"},{"id":2,"grp":"B"},{"id":3,"grp":"A"}]
60
+
61
+ -- Correlated scalar subquery in ORDER BY
62
+ SELECT id, grp FROM outer_t ORDER BY (SELECT sum(val) FROM inner_t WHERE inner_t.outer_id = outer_t.id) DESC, id;
63
+ → [{"id":1,"grp":"A"},{"id":3,"grp":"A"},{"id":2,"grp":"B"}]
64
+
65
+ -- Complex ORDER BY with both regular columns and subqueries
66
+ SELECT id, grp FROM outer_t ORDER BY grp, (SELECT count(*) FROM inner_t WHERE inner_t.outer_id = outer_t.id) DESC;
67
+ → [{"id":1,"grp":"A"},{"id":3,"grp":"A"},{"id":2,"grp":"B"}]
68
+
69
+ -- ORDER BY with subquery that may return NULL
70
+ SELECT id, grp FROM outer_t ORDER BY (SELECT min(val) FROM inner_t WHERE inner_t.outer_id = outer_t.id + 100) NULLS LAST, id;
71
+ → [{"id":1,"grp":"A"},{"id":2,"grp":"B"},{"id":3,"grp":"A"}]
72
+
73
+ -- Subquery source (derived table) with alias and qualified SELECT *
74
+ SELECT s.* FROM (SELECT id, grp FROM outer_t WHERE id > 1) s ORDER BY id;
75
+ → [{"id":2,"grp":"B"},{"id":3,"grp":"A"}]
76
+
77
+ DROP TABLE outer_t;
78
+ DROP TABLE inner_t;
@@ -0,0 +1,93 @@
1
+ -- adapted from SQLite test/subquery.test, test/subquery2.test, test/exists.test
2
+ -- Subquery surface gaps: scalar in UPDATE SET, dynamic LIMIT/OFFSET via scalar
3
+ -- subquery, scalar subquery with DISTINCT/LIMIT in projection, EXISTS in SELECT
4
+ -- list (not just WHERE), and EXISTS with arithmetic correlation predicates.
5
+
6
+ create table sqx_outer (id integer primary key, val integer);
7
+ create table sqx_inner (id integer primary key, ref_id integer, amount integer);
8
+ insert into sqx_outer values (1, 0), (2, 0), (3, 0);
9
+ insert into sqx_inner values (10, 1, 50), (20, 1, 30), (30, 2, 40);
10
+
11
+ -- =============================================================================
12
+ -- Correlated scalar subquery in UPDATE SET clause
13
+ -- =============================================================================
14
+
15
+ update sqx_outer set val = (select coalesce(sum(amount), 0) from sqx_inner where sqx_inner.ref_id = sqx_outer.id);
16
+ select id, val from sqx_outer order by id;
17
+ → [{"id":1,"val":80},{"id":2,"val":40},{"id":3,"val":0}]
18
+
19
+ -- =============================================================================
20
+ -- Dynamic LIMIT / OFFSET via scalar subquery
21
+ -- =============================================================================
22
+
23
+ create table sqx_limits (lim integer primary key, off integer);
24
+ insert into sqx_limits values (2, 0), (3, 1);
25
+
26
+ -- LIMIT from scalar subquery
27
+ select id from sqx_outer order by id limit (select lim from sqx_limits where lim = 2);
28
+ → [{"id":1},{"id":2}]
29
+
30
+ -- LIMIT and OFFSET both from scalar subqueries
31
+ select id from sqx_outer order by id limit (select lim from sqx_limits where lim = 3) offset (select off from sqx_limits where lim = 3);
32
+ → [{"id":2},{"id":3}]
33
+
34
+ drop table sqx_limits;
35
+
36
+ -- =============================================================================
37
+ -- Scalar subquery in projection with DISTINCT + ORDER BY + LIMIT
38
+ -- =============================================================================
39
+
40
+ create table sqx_dups (id integer primary key, v integer);
41
+ insert into sqx_dups values (1, 100), (2, 100), (3, 200), (4, 200), (5, 300);
42
+
43
+ -- DISTINCT inside scalar subquery: pick the second-smallest distinct value
44
+ select (select distinct v from sqx_dups order by v limit 1 offset 1) as second_distinct;
45
+ → [{"second_distinct":200}]
46
+
47
+ -- Scalar subquery with constant projection + ORDER BY + LIMIT
48
+ select (select 'k' from sqx_dups order by v limit 1) as constant_proj;
49
+ → [{"constant_proj":"k"}]
50
+
51
+ -- Scalar subquery with ORDER BY + LIMIT past end yields null
52
+ select (select v from sqx_dups order by v limit 1 offset 100) as past_end;
53
+ → [{"past_end":null}]
54
+
55
+ drop table sqx_dups;
56
+
57
+ -- =============================================================================
58
+ -- EXISTS in SELECT projection (not just WHERE)
59
+ -- =============================================================================
60
+
61
+ select id, exists (select 1 from sqx_inner where sqx_inner.ref_id = sqx_outer.id) as has_child from sqx_outer order by id;
62
+ → [{"id":1,"has_child":true},{"id":2,"has_child":true},{"id":3,"has_child":false}]
63
+
64
+ -- NOT EXISTS in SELECT projection
65
+ select id, not exists (select 1 from sqx_inner where sqx_inner.ref_id = sqx_outer.id) as is_leaf from sqx_outer order by id;
66
+ → [{"id":1,"is_leaf":false},{"id":2,"is_leaf":false},{"id":3,"is_leaf":true}]
67
+
68
+ -- EXISTS inside CASE expression in projection
69
+ select id, case when exists (select 1 from sqx_inner where sqx_inner.ref_id = sqx_outer.id and amount > 45) then 'large' else 'small' end as bucket from sqx_outer order by id;
70
+ → [{"id":1,"bucket":"large"},{"id":2,"bucket":"small"},{"id":3,"bucket":"small"}]
71
+
72
+ -- =============================================================================
73
+ -- EXISTS with arithmetic correlation predicate (not just plain equality)
74
+ -- =============================================================================
75
+
76
+ create table sqx_a (id integer primary key, x integer);
77
+ create table sqx_b (id integer primary key, y integer);
78
+ insert into sqx_a values (1, 10), (2, 20), (3, 30);
79
+ insert into sqx_b values (10, 0), (20, 10), (30, 20);
80
+
81
+ -- EXISTS where correlation is `b.y = a.x - 10`
82
+ select sqx_a.id from sqx_a where exists (select 1 from sqx_b where sqx_b.y = sqx_a.x - 10) order by sqx_a.id;
83
+ → [{"id":1},{"id":2},{"id":3}]
84
+
85
+ -- EXISTS where correlation includes a function call
86
+ select sqx_a.id from sqx_a where exists (select 1 from sqx_b where sqx_b.y = abs(sqx_a.x - 30)) order by sqx_a.id;
87
+ → [{"id":1},{"id":2},{"id":3}]
88
+
89
+ drop table sqx_a;
90
+ drop table sqx_b;
91
+
92
+ drop table sqx_inner;
93
+ drop table sqx_outer;
@@ -0,0 +1,58 @@
1
+ -- and-or-short-circuit.sqllogic - AND/OR short-circuit deferral end-to-end
2
+ -- Complements test/and-or-short-circuit.spec.ts (which owns the side-effect /
3
+ -- non-evaluation and emit-note assertions via a counting UDF). This file pins
4
+ -- three-valued-logic parity between the eager two-param path (pure-scalar RHS)
5
+ -- and the deferred short-circuit path (subquery-wrapped RHS) end-to-end through
6
+ -- the SQL runner, for every {true,false,null}² operand pair.
7
+
8
+ -- true=1, false=0, null=null (isTruthy maps 1->true, 0->false). Row id = combo.
9
+ create table tv (id integer primary key, a integer null, b integer null);
10
+ insert into tv values
11
+ (1, 1, 1), (2, 1, 0), (3, 1, null),
12
+ (4, 0, 1), (5, 0, 0), (6, 0, null),
13
+ (7, null, 1), (8, null, 0), (9, null, null);
14
+
15
+ -- =============================================================================
16
+ -- AND: eager (column RHS) vs deferred (correlated subquery RHS) must match
17
+ -- =============================================================================
18
+
19
+ select id, (a and b) as r from tv order by id;
20
+ → [{"id":1,"r":true},{"id":2,"r":false},{"id":3,"r":null},{"id":4,"r":false},{"id":5,"r":false},{"id":6,"r":false},{"id":7,"r":null},{"id":8,"r":false},{"id":9,"r":null}]
21
+
22
+ select id, (a and (select b from tv t2 where t2.id = tv.id)) as r from tv order by id;
23
+ → [{"id":1,"r":true},{"id":2,"r":false},{"id":3,"r":null},{"id":4,"r":false},{"id":5,"r":false},{"id":6,"r":false},{"id":7,"r":null},{"id":8,"r":false},{"id":9,"r":null}]
24
+
25
+ -- =============================================================================
26
+ -- OR: eager vs deferred must match
27
+ -- =============================================================================
28
+
29
+ select id, (a or b) as r from tv order by id;
30
+ → [{"id":1,"r":true},{"id":2,"r":true},{"id":3,"r":true},{"id":4,"r":true},{"id":5,"r":false},{"id":6,"r":null},{"id":7,"r":true},{"id":8,"r":null},{"id":9,"r":null}]
31
+
32
+ select id, (a or (select b from tv t2 where t2.id = tv.id)) as r from tv order by id;
33
+ → [{"id":1,"r":true},{"id":2,"r":true},{"id":3,"r":true},{"id":4,"r":true},{"id":5,"r":false},{"id":6,"r":null},{"id":7,"r":true},{"id":8,"r":null},{"id":9,"r":null}]
34
+
35
+ -- =============================================================================
36
+ -- XOR is untouched (both operands always required); subquery RHS still correct
37
+ -- =============================================================================
38
+
39
+ select id, (a xor b) as r from tv order by id;
40
+ → [{"id":1,"r":false},{"id":2,"r":true},{"id":3,"r":null},{"id":4,"r":true},{"id":5,"r":false},{"id":6,"r":null},{"id":7,"r":null},{"id":8,"r":null},{"id":9,"r":null}]
41
+
42
+ select id, (a xor (select b from tv t2 where t2.id = tv.id)) as r from tv order by id;
43
+ → [{"id":1,"r":false},{"id":2,"r":true},{"id":3,"r":null},{"id":4,"r":true},{"id":5,"r":false},{"id":6,"r":null},{"id":7,"r":null},{"id":8,"r":null},{"id":9,"r":null}]
44
+
45
+ -- =============================================================================
46
+ -- Deferred RHS in a WHERE-clause OR (the headline use case), plus a nested
47
+ -- short-circuit where the inner OR is itself the deferred right operand.
48
+ -- =============================================================================
49
+
50
+ -- true OR (subquery) short-circuits to true; false/null OR (subquery=b) resolves b.
51
+ select id from tv where a = 1 or (select b from tv t2 where t2.id = tv.id) = 1 order by id;
52
+ → [{"id":1},{"id":2},{"id":3},{"id":4},{"id":7}]
53
+
54
+ -- a and (b or (subquery)): outer AND defers the (b or subquery) group.
55
+ select id, (a = 1 and (b = 1 or (select count(*) from tv t2 where t2.id = tv.id) > 5)) as r from tv order by id;
56
+ → [{"id":1,"r":true},{"id":2,"r":false},{"id":3,"r":null},{"id":4,"r":false},{"id":5,"r":false},{"id":6,"r":false},{"id":7,"r":null},{"id":8,"r":false},{"id":9,"r":null}]
57
+
58
+ drop table tv;
@@ -0,0 +1,174 @@
1
+ -- in-subquery-caching.sqllogic - Testing uncorrelated IN subquery materialization
2
+ -- via the emitIn set probe (once-per-execution lookup set; see
3
+ -- quereus-in-subquery-set-probe). Correlated / non-deterministic sources keep the
4
+ -- per-outer-row streaming path.
5
+
6
+ -- Setup tables
7
+ CREATE TABLE items (id INTEGER PRIMARY KEY, category TEXT, val INTEGER);
8
+ CREATE TABLE categories (name TEXT PRIMARY KEY);
9
+
10
+ INSERT INTO categories VALUES ('A'), ('B'), ('C');
11
+ INSERT INTO items VALUES (1, 'A', 10), (2, 'A', 20), (3, 'B', 30), (4, 'C', 40), (5, 'A', 50);
12
+
13
+ -- Basic uncorrelated IN subquery (should be cached)
14
+ SELECT id FROM items WHERE category IN (SELECT name FROM categories WHERE name <> 'C') ORDER BY id;
15
+ → [{"id":1},{"id":2},{"id":3},{"id":5}]
16
+
17
+ -- IN subquery with NULLs in the subquery result set
18
+ -- SQL three-valued logic: if value not found and NULLs present, result is NULL (not FALSE)
19
+ CREATE TABLE nullable_ids (id INTEGER PRIMARY KEY, val INTEGER NULL);
20
+ INSERT INTO nullable_ids VALUES (1, 1), (2, NULL), (3, 3);
21
+
22
+ -- id=2 is not in {1, NULL, 3}, but NULL is present, so result is NULL (not TRUE) — excluded from WHERE
23
+ SELECT id FROM items WHERE id IN (SELECT val FROM nullable_ids) ORDER BY id;
24
+ → [{"id":1},{"id":3}]
25
+
26
+ -- NOT IN with NULLs — any NULL in subquery makes NOT IN return NULL for non-matching rows
27
+ -- Only rows whose id IS in the set (after ignoring NULLs) would return FALSE for IN, i.e., TRUE for NOT IN
28
+ -- But since NULL is present, non-matching rows get NULL (not FALSE) for IN, so NOT IN is NULL (not TRUE)
29
+ SELECT id FROM items WHERE id NOT IN (SELECT val FROM nullable_ids) ORDER BY id;
30
+ → []
31
+
32
+ -- IN subquery returning empty set (should return no rows)
33
+ SELECT id FROM items WHERE id IN (SELECT val FROM nullable_ids WHERE val > 100) ORDER BY id;
34
+ → []
35
+
36
+ -- Correlated IN subquery — must take the streaming (per-outer-row) path, not the
37
+ -- set probe (should still work correctly)
38
+ SELECT c.name FROM categories c WHERE c.name IN (SELECT i.category FROM items i WHERE i.val > 20 AND i.category = c.name) ORDER BY c.name;
39
+ → [{"name":"A"},{"name":"B"},{"name":"C"}]
40
+
41
+ -- Uncorrelated IN subquery inside a CTE-based query (the motivating pattern)
42
+ WITH RECURSIVE
43
+ nums(n) AS (
44
+ SELECT 1
45
+ UNION ALL
46
+ SELECT n + 1 FROM nums WHERE n < 5
47
+ )
48
+ SELECT id FROM items WHERE id IN (SELECT n FROM nums) ORDER BY id;
49
+ → [{"id":1},{"id":2},{"id":3},{"id":4},{"id":5}]
50
+
51
+ -- Nested: uncorrelated IN subquery referencing a CTE that itself is recursive
52
+ WITH RECURSIVE
53
+ tree(id) AS (
54
+ SELECT id FROM items WHERE id = 1
55
+ UNION ALL
56
+ SELECT i.id FROM items i JOIN tree t ON i.id = t.id + 1 WHERE i.id <= 3
57
+ )
58
+ SELECT val FROM items WHERE id IN (SELECT id FROM tree) ORDER BY val;
59
+ → [{"val":10},{"val":20},{"val":30}]
60
+
61
+ -- IN in projection position (not a WHERE filter): same emitter / set-probe path,
62
+ -- must yield the three-valued boolean per row. id 4 (category 'C') is not in the
63
+ -- filtered set {'A','B'}, and the subquery has no NULLs, so → false (0).
64
+ SELECT id, category IN (SELECT name FROM categories WHERE name <> 'C') AS in_ab FROM items ORDER BY id;
65
+ → [{"id":1,"in_ab":true},{"id":2,"in_ab":true},{"id":3,"in_ab":true},{"id":4,"in_ab":false},{"id":5,"in_ab":true}]
66
+
67
+ -- NOCASE collation: membership uses the condition column's collation. The set
68
+ -- probe keys the lookup set under the same collation as the streaming path, so
69
+ -- 'Apple' matches 'apple' and 'BANANA' matches 'banana'.
70
+ CREATE TABLE nc (id INTEGER PRIMARY KEY, tag TEXT COLLATE NOCASE);
71
+ INSERT INTO nc VALUES (1, 'Apple'), (2, 'BANANA'), (3, 'cherry');
72
+ CREATE TABLE nc_want (w TEXT);
73
+ INSERT INTO nc_want VALUES ('apple'), ('banana');
74
+
75
+ SELECT id FROM nc WHERE tag IN (SELECT w FROM nc_want) ORDER BY id;
76
+ → [{"id":1},{"id":2}]
77
+
78
+ DROP TABLE nc_want;
79
+ DROP TABLE nc;
80
+
81
+ -- Self-referencing DELETE: the subquery reads the same table being deleted from.
82
+ -- Materialize-once makes this a deterministic pre-statement snapshot — the set of
83
+ -- ids to delete is computed before any row is removed.
84
+ CREATE TABLE sref (id INTEGER PRIMARY KEY, keep INTEGER);
85
+ INSERT INTO sref VALUES (1, 1), (2, 0), (3, 1), (4, 0);
86
+
87
+ DELETE FROM sref WHERE id IN (SELECT id FROM sref WHERE keep = 0);
88
+ SELECT id FROM sref ORDER BY id;
89
+ → [{"id":1},{"id":3}]
90
+
91
+ DROP TABLE sref;
92
+
93
+ -- UPDATE / DELETE row counts filtered by an uncorrelated IN subquery over a
94
+ -- larger set (exercises the set probe on many candidate rows).
95
+ CREATE TABLE dml_outer (id INTEGER PRIMARY KEY, mark INTEGER);
96
+ INSERT INTO dml_outer VALUES (1, 0), (2, 0), (3, 0), (4, 0), (5, 0), (6, 0);
97
+ CREATE TABLE dml_keep (id INTEGER PRIMARY KEY);
98
+ INSERT INTO dml_keep VALUES (2), (4), (6);
99
+
100
+ UPDATE dml_outer SET mark = 1 WHERE id IN (SELECT id FROM dml_keep);
101
+ SELECT id, mark FROM dml_outer ORDER BY id;
102
+ → [{"id":1,"mark":0},{"id":2,"mark":1},{"id":3,"mark":0},{"id":4,"mark":1},{"id":5,"mark":0},{"id":6,"mark":1}]
103
+
104
+ DELETE FROM dml_outer WHERE id IN (SELECT id FROM dml_keep);
105
+ SELECT id, mark FROM dml_outer ORDER BY id;
106
+ → [{"id":1,"mark":0},{"id":3,"mark":0},{"id":5,"mark":0}]
107
+
108
+ DROP TABLE dml_keep;
109
+ DROP TABLE dml_outer;
110
+
111
+ -- BLOB values in an uncorrelated IN subquery: the set-probe path builds an
112
+ -- in-memory BTree keyed on the raw scalar value, which used to crash on any
113
+ -- non-empty BLOB (bug-in-subquery-blob-values-crash — inheritree freezes each
114
+ -- stored entry by default, and Object.freeze throws on a non-empty
115
+ -- Uint8Array, which is exactly what a BLOB scalar is).
116
+ CREATE TABLE blob_items (id INTEGER PRIMARY KEY, b BLOB NULL);
117
+ INSERT INTO blob_items VALUES (1, x'6162'), (2, x'6164'), (3, null);
118
+
119
+ CREATE TABLE blob_set (v BLOB NULL);
120
+ INSERT INTO blob_set VALUES (x'6162'), (x'6169');
121
+
122
+ -- select-list position: byte-equality hit, non-match, condition-NULL → NULL
123
+ SELECT id, b IN (SELECT v FROM blob_set) AS in_set FROM blob_items ORDER BY id;
124
+ → [{"id":1,"in_set":true},{"id":2,"in_set":false},{"id":3,"in_set":null}]
125
+
126
+ -- NOT IN mirrors IN
127
+ SELECT id, b NOT IN (SELECT v FROM blob_set) AS notin_set FROM blob_items ORDER BY id;
128
+ → [{"id":1,"notin_set":false},{"id":2,"notin_set":true},{"id":3,"notin_set":null}]
129
+
130
+ -- WHERE position is rewritten to a semi join and never touches the set-probe
131
+ -- tree — must agree with the select-list result above.
132
+ SELECT id FROM blob_items WHERE b IN (SELECT v FROM blob_set) ORDER BY id;
133
+ → [{"id":1}]
134
+
135
+ -- CASE WHEN wrapping the IN — exercises the set probe through a branch
136
+ SELECT id, CASE WHEN b IN (SELECT v FROM blob_set) THEN 'y' ELSE 'n' END AS r FROM blob_items ORDER BY id;
137
+ → [{"id":1,"r":"y"},{"id":2,"r":"n"},{"id":3,"r":"n"}]
138
+
139
+ -- Inner result set contains a NULL: a miss becomes NULL instead of false
140
+ CREATE TABLE blob_set_n (v BLOB NULL);
141
+ INSERT INTO blob_set_n VALUES (x'6162'), (null);
142
+
143
+ SELECT id, b IN (SELECT v FROM blob_set_n) AS in_set FROM blob_items ORDER BY id;
144
+ → [{"id":1,"in_set":true},{"id":2,"in_set":null},{"id":3,"in_set":null}]
145
+
146
+ SELECT id, b NOT IN (SELECT v FROM blob_set_n) AS notin_set FROM blob_items ORDER BY id;
147
+ → [{"id":1,"notin_set":false},{"id":2,"notin_set":null},{"id":3,"notin_set":null}]
148
+
149
+ -- TEXT vs BLOB never match, even with byte-identical content — cross
150
+ -- storage-class comparison (BLOB sorts above TEXT; see compareSqlValuesFast).
151
+ SELECT ('ab' IN (SELECT v FROM blob_set)) AS text_in_blobs;
152
+ → [{"text_in_blobs":false}]
153
+
154
+ -- Correlated inner takes the streaming path, not the set-probe tree — this
155
+ -- shape never built a BTree even before the fix, kept here for parity.
156
+ CREATE TABLE blob_outer (id INTEGER PRIMARY KEY, tag TEXT, b BLOB NULL);
157
+ INSERT INTO blob_outer VALUES (1, 'x', x'6162'), (2, 'x', x'6164'), (3, 'y', x'6162');
158
+
159
+ CREATE TABLE blob_inner (tag TEXT, v BLOB NULL);
160
+ INSERT INTO blob_inner VALUES ('x', x'6162'), ('x', null), ('y', x'6169');
161
+
162
+ SELECT id, b IN (SELECT v FROM blob_inner WHERE tag = blob_outer.tag) AS r FROM blob_outer ORDER BY id;
163
+ → [{"id":1,"r":true},{"id":2,"r":null},{"id":3,"r":false}]
164
+
165
+ DROP TABLE blob_inner;
166
+ DROP TABLE blob_outer;
167
+ DROP TABLE blob_set_n;
168
+ DROP TABLE blob_set;
169
+ DROP TABLE blob_items;
170
+
171
+ -- Cleanup
172
+ DROP TABLE nullable_ids;
173
+ DROP TABLE items;
174
+ DROP TABLE categories;
@@ -0,0 +1,197 @@
1
+ -- 07.7-scalar-agg-decorrelation.sqllogic
2
+ -- Correlated scalar-aggregate subqueries in the SELECT list are decorrelated
3
+ -- into grouped left joins (rule scalar-agg-decorrelation). These tests pin the
4
+ -- observable semantics of both the decorrelated shapes and the shapes the rule
5
+ -- must reject (which stay on the correlated per-row path).
6
+
7
+ -- Setup
8
+ CREATE TABLE p (id INTEGER PRIMARY KEY, k INTEGER NULL, grp TEXT);
9
+ CREATE TABLE c (id INTEGER PRIMARY KEY, pid INTEGER NULL, v INTEGER NULL, tag TEXT);
10
+ CREATE TABLE d (id INTEGER PRIMARY KEY, k INTEGER NULL);
11
+ CREATE TABLE cc (id INTEGER PRIMARY KEY, pid INTEGER NULL, k INTEGER NULL, v INTEGER);
12
+
13
+ INSERT INTO p VALUES (1, 10, 'a'), (2, 20, 'b'), (3, null, 'c'), (4, 10, 'd');
14
+ INSERT INTO c VALUES (1, 1, 5, 'x'), (2, 1, 7, 'y'), (3, 2, null, 'z'), (4, null, 100, 'w');
15
+ INSERT INTO d VALUES (1, 10), (2, 10), (3, 20), (4, null);
16
+ INSERT INTO cc VALUES (1, 1, 10, 1), (2, 1, 99, 1), (3, 4, 10, 1);
17
+
18
+ -- count(*): empty group must yield 0 (the "count bug"), not NULL
19
+ SELECT p.id, (SELECT count(*) FROM c WHERE c.pid = p.id) AS n FROM p ORDER BY p.id;
20
+ → [{"id":1,"n":2},{"id":2,"n":1},{"id":3,"n":0},{"id":4,"n":0}]
21
+
22
+ -- count(v): group exists but all values NULL → 0 via the aggregate itself,
23
+ -- NOT via the empty-group marker (distinguishes join-miss from all-NULL input)
24
+ SELECT p.id, (SELECT count(c.v) FROM c WHERE c.pid = p.id) AS n FROM p ORDER BY p.id;
25
+ → [{"id":1,"n":2},{"id":2,"n":0},{"id":3,"n":0},{"id":4,"n":0}]
26
+
27
+ -- sum: NULL on empty group AND on all-NULL input (no marker needed)
28
+ SELECT p.id, (SELECT sum(c.v) FROM c WHERE c.pid = p.id) AS s FROM p ORDER BY p.id;
29
+ → [{"id":1,"s":12},{"id":2,"s":null},{"id":3,"s":null},{"id":4,"s":null}]
30
+
31
+ -- min / max: NULL on empty group
32
+ SELECT p.id, (SELECT min(c.v) FROM c WHERE c.pid = p.id) AS mn, (SELECT max(c.v) FROM c WHERE c.pid = p.id) AS mx FROM p ORDER BY p.id;
33
+ → [{"id":1,"mn":5,"mx":7},{"id":2,"mn":null,"mx":null},{"id":3,"mn":null,"mx":null},{"id":4,"mn":null,"mx":null}]
34
+
35
+ -- avg: NULL on empty group
36
+ SELECT p.id, (SELECT avg(c.v) FROM c WHERE c.pid = p.id) AS a FROM p ORDER BY p.id;
37
+ → [{"id":1,"a":6},{"id":2,"a":null},{"id":3,"a":null},{"id":4,"a":null}]
38
+
39
+ -- total: 0.0 on empty group (non-NULL empty value → marker CASE path, REAL)
40
+ SELECT p.id, (SELECT total(c.v) FROM c WHERE c.pid = p.id) AS t FROM p ORDER BY p.id;
41
+ → [{"id":1,"t":12},{"id":2,"t":0},{"id":3,"t":0},{"id":4,"t":0}]
42
+
43
+ -- Duplicate outer key values + NULL outer key: two outer rows share k=10 and
44
+ -- each sees the same group; the NULL key never matches (empty value, not join junk)
45
+ SELECT p.id, (SELECT count(*) FROM d WHERE d.k = p.k) AS n FROM p ORDER BY p.id;
46
+ → [{"id":1,"n":2},{"id":2,"n":1},{"id":3,"n":0},{"id":4,"n":2}]
47
+
48
+ -- NULL inner keys: the d.k IS NULL row groups but can never match any outer row
49
+ SELECT p.id, (SELECT sum(d.id) FROM d WHERE d.k = p.k) AS s FROM p ORDER BY p.id;
50
+ → [{"id":1,"s":3},{"id":2,"s":3},{"id":3,"s":null},{"id":4,"s":3}]
51
+
52
+ -- Composite (multi-conjunct) correlation
53
+ SELECT p.id, (SELECT count(*) FROM cc WHERE cc.pid = p.id AND cc.k = p.k) AS n FROM p ORDER BY p.id;
54
+ → [{"id":1,"n":1},{"id":2,"n":0},{"id":3,"n":0},{"id":4,"n":1}]
55
+
56
+ -- Residual inner-only predicate alongside the correlation
57
+ SELECT p.id, (SELECT count(*) FROM c WHERE c.pid = p.id AND c.v > 5) AS n FROM p ORDER BY p.id;
58
+ → [{"id":1,"n":1},{"id":2,"n":0},{"id":3,"n":0},{"id":4,"n":0}]
59
+
60
+ -- Wrapped subquery: only the inner node is substituted, wrapper preserved
61
+ SELECT p.id, coalesce((SELECT sum(c.v) FROM c WHERE c.pid = p.id), -1) AS s FROM p ORDER BY p.id;
62
+ → [{"id":1,"s":12},{"id":2,"s":-1},{"id":3,"s":-1},{"id":4,"s":-1}]
63
+
64
+ -- Subquery inside a scalar expression
65
+ SELECT p.id, 1 + (SELECT count(*) FROM c WHERE c.pid = p.id) AS n FROM p ORDER BY p.id;
66
+ → [{"id":1,"n":3},{"id":2,"n":2},{"id":3,"n":1},{"id":4,"n":1}]
67
+
68
+ -- Multiple correlated subqueries in one SELECT list (independent joins)
69
+ SELECT p.id, (SELECT count(*) FROM c WHERE c.pid = p.id) AS n, (SELECT count(*) FROM d WHERE d.k = p.k) AS m FROM p ORDER BY p.id;
70
+ → [{"id":1,"n":2,"m":2},{"id":2,"n":1,"m":1},{"id":3,"n":0,"m":0},{"id":4,"n":0,"m":2}]
71
+
72
+ -- Outer reference in the aggregate argument (remapped to the equated inner column)
73
+ SELECT p.id, (SELECT sum(c.v + p.id) FROM c WHERE c.pid = p.id) AS s FROM p ORDER BY p.id;
74
+ → [{"id":1,"s":14},{"id":2,"s":null},{"id":3,"s":null},{"id":4,"s":null}]
75
+
76
+ -- group_concat: NULL on empty group (all values NULL also concatenates to NULL)
77
+ SELECT p.id, (SELECT group_concat(c.tag) FROM c WHERE c.pid = p.id) AS g FROM p ORDER BY p.id;
78
+ → [{"id":1,"g":"x,y"},{"id":2,"g":"z"},{"id":3,"g":null},{"id":4,"g":null}]
79
+
80
+ -- json_group_array: empty group matches the correlated/zero-row behavior
81
+ SELECT p.id, (SELECT json_group_array(c.v) FROM c WHERE c.pid = p.id) AS j FROM p ORDER BY p.id;
82
+ → [{"id":1,"j":[5,7]},{"id":2,"j":[null]},{"id":3,"j":null},{"id":4,"j":null}]
83
+
84
+ -- DISTINCT aggregate
85
+ SELECT p.id, (SELECT count(DISTINCT d.k) FROM d WHERE d.k = p.k) AS n FROM p ORDER BY p.id;
86
+ → [{"id":1,"n":1},{"id":2,"n":1},{"id":3,"n":0},{"id":4,"n":1}]
87
+
88
+ -- Rejected shapes: these stay on the correlated per-row path and must keep
89
+ -- today's semantics.
90
+
91
+ -- Non-aggregate LIMIT 1 subquery (aggregate root required)
92
+ SELECT p.id, (SELECT c.v FROM c WHERE c.pid = p.id ORDER BY c.v DESC LIMIT 1) AS v FROM p ORDER BY p.id;
93
+ → [{"id":1,"v":7},{"id":2,"v":null},{"id":3,"v":null},{"id":4,"v":null}]
94
+
95
+ -- Non-aggregate multi-row subquery: the >1-row runtime error is preserved
96
+ SELECT p.id, (SELECT c.v FROM c WHERE c.pid = p.id) AS v FROM p WHERE p.id = 1;
97
+ -- error: Scalar subquery returned more than one row
98
+
99
+ -- Non-equi correlation (rejected: stays correlated, still correct)
100
+ SELECT p.id, (SELECT count(*) FROM c WHERE c.pid < p.id) AS n FROM p ORDER BY p.id;
101
+ → [{"id":1,"n":0},{"id":2,"n":2},{"id":3,"n":3},{"id":4,"n":3}]
102
+
103
+ -- Uncorrelated scalar subquery (untouched)
104
+ SELECT p.id, (SELECT count(*) FROM c) AS n FROM p ORDER BY p.id;
105
+ → [{"id":1,"n":4},{"id":2,"n":4},{"id":3,"n":4},{"id":4,"n":4}]
106
+
107
+ -- GROUP BY inside a scalar subquery. This used to be rejected at build time as a
108
+ -- two-column subquery, because a grouped select list of only aggregates leaked
109
+ -- the grouping key into the output (bug-grouped-aggregate-only-select-returns-extra-column).
110
+ -- It is a legal one-column subquery; the rule still rejects it (grouped body, not
111
+ -- a bare scalar aggregate) so it stays on the correlated per-row path.
112
+ SELECT p.id, (SELECT count(*) FROM cc WHERE cc.pid = p.id GROUP BY cc.pid) AS n FROM p WHERE p.id = 1;
113
+ → [{"id":1,"n":2}]
114
+
115
+ -- An outer row matching no inner row produces *no group at all*, so the scalar
116
+ -- subquery is NULL — not the 0 the ungrouped count(*) would give (ids 2 and 3).
117
+ SELECT p.id, (SELECT count(*) FROM cc WHERE cc.pid = p.id GROUP BY cc.pid) AS n FROM p ORDER BY p.id;
118
+ → [{"id":1,"n":2},{"id":2,"n":null},{"id":3,"n":null},{"id":4,"n":1}]
119
+
120
+ -- Grouping on a column the correlation does not pin can yield several groups,
121
+ -- and the >1-row runtime error still applies.
122
+ SELECT p.id, (SELECT count(*) FROM cc WHERE cc.pid = p.id GROUP BY cc.k) AS n FROM p WHERE p.id = 1;
123
+ -- error: Scalar subquery returned more than one row
124
+
125
+ -- Weak collation on the correlation + outer ref in the aggregate argument:
126
+ -- the remap is not value-faithful, so the rule bails; results stay correct.
127
+ CREATE TABLE nc (id INTEGER PRIMARY KEY, t TEXT COLLATE NOCASE);
128
+ INSERT INTO nc VALUES (1, 'A'), (2, 'a'), (3, 'B');
129
+ SELECT p.grp, (SELECT group_concat(p.grp) FROM nc WHERE nc.t = p.grp) AS g FROM p WHERE p.id = 1;
130
+ → [{"grp":"a","g":"a,a"}]
131
+
132
+ -- ORDER BY anchor (rule scalar-agg-decorrelation-sort): a correlated
133
+ -- scalar-aggregate subquery in a sort key is decorrelated into the same grouped
134
+ -- LEFT join, capped by a pass-through Project so the Sort's output shape is
135
+ -- unchanged. These pin the ordering semantics against the correlated baseline.
136
+
137
+ -- Basic: order by the correlated count (asc), id tiebreak. Empty groups (id 3,4
138
+ -- → 0) sort first.
139
+ SELECT p.id FROM p ORDER BY (SELECT count(*) FROM c WHERE c.pid = p.id), p.id;
140
+ → [{"id":3},{"id":4},{"id":2},{"id":1}]
141
+
142
+ -- DESC + LIMIT above the Sort: the pass-through Project sits between Sort and
143
+ -- Limit, so Limit still caps the correct (unchanged) row set.
144
+ SELECT p.id FROM p ORDER BY (SELECT count(*) FROM c WHERE c.pid = p.id) DESC, p.id LIMIT 2;
145
+ → [{"id":1},{"id":2}]
146
+
147
+ -- Empty-group NULL ordering: sum is NULL on empty/all-NULL groups (id 2,3,4).
148
+ -- NULLS FIRST puts them ahead of id 1 (sum 12).
149
+ SELECT p.id FROM p ORDER BY (SELECT sum(c.v) FROM c WHERE c.pid = p.id) NULLS FIRST, p.id;
150
+ → [{"id":2},{"id":3},{"id":4},{"id":1}]
151
+
152
+ -- Same key, NULLS LAST: the NULLs move to the end.
153
+ SELECT p.id FROM p ORDER BY (SELECT sum(c.v) FROM c WHERE c.pid = p.id) NULLS LAST, p.id;
154
+ → [{"id":1},{"id":2},{"id":3},{"id":4}]
155
+
156
+ -- Same subquery in the SELECT list AND the ORDER BY: two distinct anchors
157
+ -- (Project + Sort) over the same subquery shape; the inner is scanned once and
158
+ -- both resolve correctly (no double-decorrelation / conflict).
159
+ SELECT p.id, (SELECT count(*) FROM c WHERE c.pid = p.id) AS n FROM p ORDER BY (SELECT count(*) FROM c WHERE c.pid = p.id), p.id;
160
+ → [{"id":3,"n":0},{"id":4,"n":0},{"id":2,"n":1},{"id":1,"n":2}]
161
+
162
+ -- Multiple ORDER BY keys, only the second correlated: p.k (plain column) then
163
+ -- the count breaks the k=10 tie (id 1 count 2 vs id 4 count 0).
164
+ SELECT p.id FROM p WHERE p.k IS NOT NULL ORDER BY p.k, (SELECT count(*) FROM c WHERE c.pid = p.id), p.id;
165
+ → [{"id":4},{"id":1},{"id":2}]
166
+
167
+ -- ORDER BY an aggregate scalar subquery over a GROUP BY query. The subquery's
168
+ -- own count() must resolve as its OWN aggregate, not fingerprint-match the outer
169
+ -- query's count() and bind to its output alias (the scope-leak fixed by
170
+ -- fix/order-by-aggregate-subquery-scope-leak — a nested SELECT no longer inherits
171
+ -- the enclosing query's aggregate context).
172
+ -- Correlated form: order groups by children-per-key. d.k matches: k=10→2, k=20→1,
173
+ -- k=null→0 (null = null is never true), so null sorts first, then 20, then 10.
174
+ SELECT p.k, count(*) AS cnt FROM p GROUP BY p.k ORDER BY (SELECT count(*) FROM d WHERE d.k = p.k), p.k;
175
+ → [{"k":null,"cnt":1},{"k":20,"cnt":1},{"k":10,"cnt":2}]
176
+
177
+ -- Uncorrelated form (also broke before the fix): the subquery is a constant 4 for
178
+ -- every group, so the sort collapses to the p.k tiebreak (NULLs first on ASC).
179
+ SELECT p.k, count(*) AS cnt FROM p GROUP BY p.k ORDER BY (SELECT count(*) FROM d), p.k;
180
+ → [{"k":null,"cnt":1},{"k":10,"cnt":2},{"k":20,"cnt":1}]
181
+
182
+ -- Stripping projection: p.k is not selected, so the correlation column is absent
183
+ -- from the Sort's source and the subquery stays correlated — still correct (the
184
+ -- runtime reads p.k from the live base scan below the Project). count(d.k=p.k):
185
+ -- p1/p4→2, p2→1, p3(null)→0. Ascending, id tiebreak.
186
+ SELECT p.id FROM p ORDER BY (SELECT count(*) FROM d WHERE d.k = p.k), p.id;
187
+ → [{"id":3},{"id":2},{"id":1},{"id":4}]
188
+
189
+ -- Bail-but-correct: a non-equi correlation (c.pid < p.id) stays on the
190
+ -- correlated per-row path; the ordering is still correct.
191
+ SELECT p.id FROM p ORDER BY (SELECT count(*) FROM c WHERE c.pid < p.id), p.id;
192
+ → [{"id":1},{"id":2},{"id":3},{"id":4}]
193
+
194
+ -- Shape invariance: the decorrelated Sort feeds a derived table consumed by
195
+ -- SELECT * — only the original columns (id, grp) appear, no leaked join columns.
196
+ SELECT * FROM (SELECT p.id, p.grp FROM p ORDER BY (SELECT count(*) FROM c WHERE c.pid = p.id)) sub ORDER BY sub.id;
197
+ → [{"id":1,"grp":"a"},{"id":2,"grp":"b"},{"id":3,"grp":"c"},{"id":4,"grp":"d"}]