@quereus/quereus 4.11.0 → 4.12.1

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 (451) hide show
  1. package/dist/src/common/type-inference.d.ts +12 -6
  2. package/dist/src/common/type-inference.d.ts.map +1 -1
  3. package/dist/src/common/type-inference.js +12 -24
  4. package/dist/src/common/type-inference.js.map +1 -1
  5. package/dist/src/core/database-assertions.d.ts.map +1 -1
  6. package/dist/src/core/database-assertions.js +7 -5
  7. package/dist/src/core/database-assertions.js.map +1 -1
  8. package/dist/src/core/database-events.d.ts +4 -3
  9. package/dist/src/core/database-events.d.ts.map +1 -1
  10. package/dist/src/core/database-events.js +4 -3
  11. package/dist/src/core/database-events.js.map +1 -1
  12. package/dist/src/core/database-materialized-views-analysis.d.ts +1 -3
  13. package/dist/src/core/database-materialized-views-analysis.d.ts.map +1 -1
  14. package/dist/src/core/database-materialized-views-analysis.js +1 -11
  15. package/dist/src/core/database-materialized-views-analysis.js.map +1 -1
  16. package/dist/src/core/database-materialized-views-plan-builders.d.ts.map +1 -1
  17. package/dist/src/core/database-materialized-views-plan-builders.js +12 -10
  18. package/dist/src/core/database-materialized-views-plan-builders.js.map +1 -1
  19. package/dist/src/core/database.d.ts.map +1 -1
  20. package/dist/src/core/database.js +13 -2
  21. package/dist/src/core/database.js.map +1 -1
  22. package/dist/src/core/derived-row-validator.d.ts.map +1 -1
  23. package/dist/src/core/derived-row-validator.js +14 -2
  24. package/dist/src/core/derived-row-validator.js.map +1 -1
  25. package/dist/src/core/param.d.ts +9 -0
  26. package/dist/src/core/param.d.ts.map +1 -1
  27. package/dist/src/core/param.js +21 -1
  28. package/dist/src/core/param.js.map +1 -1
  29. package/dist/src/core/statement.d.ts.map +1 -1
  30. package/dist/src/core/statement.js +38 -15
  31. package/dist/src/core/statement.js.map +1 -1
  32. package/dist/src/func/builtins/aggregate.d.ts.map +1 -1
  33. package/dist/src/func/builtins/aggregate.js +7 -2
  34. package/dist/src/func/builtins/aggregate.js.map +1 -1
  35. package/dist/src/func/builtins/builtin-window-functions.d.ts.map +1 -1
  36. package/dist/src/func/builtins/builtin-window-functions.js +27 -11
  37. package/dist/src/func/builtins/builtin-window-functions.js.map +1 -1
  38. package/dist/src/func/builtins/datetime.d.ts.map +1 -1
  39. package/dist/src/func/builtins/datetime.js +8 -3
  40. package/dist/src/func/builtins/datetime.js.map +1 -1
  41. package/dist/src/func/builtins/explain.d.ts.map +1 -1
  42. package/dist/src/func/builtins/explain.js +9 -29
  43. package/dist/src/func/builtins/explain.js.map +1 -1
  44. package/dist/src/func/builtins/json-tvf.d.ts.map +1 -1
  45. package/dist/src/func/builtins/json-tvf.js +14 -7
  46. package/dist/src/func/builtins/json-tvf.js.map +1 -1
  47. package/dist/src/func/builtins/return-types.d.ts +29 -0
  48. package/dist/src/func/builtins/return-types.d.ts.map +1 -1
  49. package/dist/src/func/builtins/return-types.js +4 -1
  50. package/dist/src/func/builtins/return-types.js.map +1 -1
  51. package/dist/src/func/builtins/scalar.d.ts.map +1 -1
  52. package/dist/src/func/builtins/scalar.js +22 -67
  53. package/dist/src/func/builtins/scalar.js.map +1 -1
  54. package/dist/src/func/builtins/schema.d.ts.map +1 -1
  55. package/dist/src/func/builtins/schema.js +26 -16
  56. package/dist/src/func/builtins/schema.js.map +1 -1
  57. package/dist/src/index.d.ts +1 -1
  58. package/dist/src/index.d.ts.map +1 -1
  59. package/dist/src/index.js +6 -1
  60. package/dist/src/index.js.map +1 -1
  61. package/dist/src/parser/parser.d.ts.map +1 -1
  62. package/dist/src/parser/parser.js +8 -5
  63. package/dist/src/parser/parser.js.map +1 -1
  64. package/dist/src/parser/visitor.d.ts +16 -0
  65. package/dist/src/parser/visitor.d.ts.map +1 -1
  66. package/dist/src/parser/visitor.js +16 -0
  67. package/dist/src/parser/visitor.js.map +1 -1
  68. package/dist/src/planner/analysis/assertion-plan.d.ts +33 -0
  69. package/dist/src/planner/analysis/assertion-plan.d.ts.map +1 -0
  70. package/dist/src/planner/analysis/assertion-plan.js +39 -0
  71. package/dist/src/planner/analysis/assertion-plan.js.map +1 -0
  72. package/dist/src/planner/analysis/binary-operator-class.d.ts +55 -0
  73. package/dist/src/planner/analysis/binary-operator-class.d.ts.map +1 -0
  74. package/dist/src/planner/analysis/binary-operator-class.js +57 -0
  75. package/dist/src/planner/analysis/binary-operator-class.js.map +1 -0
  76. package/dist/src/planner/analysis/binding-extractor.d.ts +9 -3
  77. package/dist/src/planner/analysis/binding-extractor.d.ts.map +1 -1
  78. package/dist/src/planner/analysis/binding-extractor.js +11 -16
  79. package/dist/src/planner/analysis/binding-extractor.js.map +1 -1
  80. package/dist/src/planner/analysis/change-scope.d.ts.map +1 -1
  81. package/dist/src/planner/analysis/change-scope.js +19 -6
  82. package/dist/src/planner/analysis/change-scope.js.map +1 -1
  83. package/dist/src/planner/analysis/check-extraction.d.ts +1 -7
  84. package/dist/src/planner/analysis/check-extraction.d.ts.map +1 -1
  85. package/dist/src/planner/analysis/check-extraction.js.map +1 -1
  86. package/dist/src/planner/analysis/comparison-collation.d.ts +17 -0
  87. package/dist/src/planner/analysis/comparison-collation.d.ts.map +1 -1
  88. package/dist/src/planner/analysis/comparison-collation.js +17 -5
  89. package/dist/src/planner/analysis/comparison-collation.js.map +1 -1
  90. package/dist/src/planner/analysis/constraint-extractor.d.ts.map +1 -1
  91. package/dist/src/planner/analysis/constraint-extractor.js +18 -18
  92. package/dist/src/planner/analysis/constraint-extractor.js.map +1 -1
  93. package/dist/src/planner/analysis/coverage-prover.d.ts +11 -7
  94. package/dist/src/planner/analysis/coverage-prover.d.ts.map +1 -1
  95. package/dist/src/planner/analysis/coverage-prover.js +11 -7
  96. package/dist/src/planner/analysis/coverage-prover.js.map +1 -1
  97. package/dist/src/planner/analysis/equi-correlation.d.ts +1 -1
  98. package/dist/src/planner/analysis/equi-correlation.js +1 -1
  99. package/dist/src/planner/analysis/expression-fingerprint.d.ts.map +1 -1
  100. package/dist/src/planner/analysis/expression-fingerprint.js +0 -2
  101. package/dist/src/planner/analysis/expression-fingerprint.js.map +1 -1
  102. package/dist/src/planner/analysis/key-filter.d.ts.map +1 -1
  103. package/dist/src/planner/analysis/key-filter.js +2 -3
  104. package/dist/src/planner/analysis/key-filter.js.map +1 -1
  105. package/dist/src/planner/analysis/relation-key.d.ts +104 -0
  106. package/dist/src/planner/analysis/relation-key.d.ts.map +1 -0
  107. package/dist/src/planner/analysis/relation-key.js +121 -0
  108. package/dist/src/planner/analysis/relation-key.js.map +1 -0
  109. package/dist/src/planner/analysis/scalar-param-usage.d.ts.map +1 -1
  110. package/dist/src/planner/analysis/scalar-param-usage.js +12 -4
  111. package/dist/src/planner/analysis/scalar-param-usage.js.map +1 -1
  112. package/dist/src/planner/analysis/set-op-type-merge.d.ts +9 -3
  113. package/dist/src/planner/analysis/set-op-type-merge.d.ts.map +1 -1
  114. package/dist/src/planner/analysis/set-op-type-merge.js +22 -8
  115. package/dist/src/planner/analysis/set-op-type-merge.js.map +1 -1
  116. package/dist/src/planner/building/alter-table.d.ts.map +1 -1
  117. package/dist/src/planner/building/alter-table.js +31 -23
  118. package/dist/src/planner/building/alter-table.js.map +1 -1
  119. package/dist/src/planner/building/analyze.d.ts.map +1 -1
  120. package/dist/src/planner/building/analyze.js +21 -0
  121. package/dist/src/planner/building/analyze.js.map +1 -1
  122. package/dist/src/planner/building/block.d.ts.map +1 -1
  123. package/dist/src/planner/building/block.js +222 -82
  124. package/dist/src/planner/building/block.js.map +1 -1
  125. package/dist/src/planner/building/constraint-builder.d.ts.map +1 -1
  126. package/dist/src/planner/building/constraint-builder.js +5 -3
  127. package/dist/src/planner/building/constraint-builder.js.map +1 -1
  128. package/dist/src/planner/building/delete.js +1 -1
  129. package/dist/src/planner/building/delete.js.map +1 -1
  130. package/dist/src/planner/building/expression.d.ts.map +1 -1
  131. package/dist/src/planner/building/expression.js +11 -3
  132. package/dist/src/planner/building/expression.js.map +1 -1
  133. package/dist/src/planner/building/foreign-key-builder.d.ts +16 -6
  134. package/dist/src/planner/building/foreign-key-builder.d.ts.map +1 -1
  135. package/dist/src/planner/building/foreign-key-builder.js +43 -20
  136. package/dist/src/planner/building/foreign-key-builder.js.map +1 -1
  137. package/dist/src/planner/building/generated-column-scope.d.ts +58 -0
  138. package/dist/src/planner/building/generated-column-scope.d.ts.map +1 -0
  139. package/dist/src/planner/building/generated-column-scope.js +117 -0
  140. package/dist/src/planner/building/generated-column-scope.js.map +1 -0
  141. package/dist/src/planner/building/insert.d.ts.map +1 -1
  142. package/dist/src/planner/building/insert.js +20 -28
  143. package/dist/src/planner/building/insert.js.map +1 -1
  144. package/dist/src/planner/building/select-aggregates.d.ts +73 -132
  145. package/dist/src/planner/building/select-aggregates.d.ts.map +1 -1
  146. package/dist/src/planner/building/select-aggregates.js +405 -81
  147. package/dist/src/planner/building/select-aggregates.js.map +1 -1
  148. package/dist/src/planner/building/select-modifiers.d.ts +37 -8
  149. package/dist/src/planner/building/select-modifiers.d.ts.map +1 -1
  150. package/dist/src/planner/building/select-modifiers.js +8 -9
  151. package/dist/src/planner/building/select-modifiers.js.map +1 -1
  152. package/dist/src/planner/building/select-ordinal.d.ts +6 -0
  153. package/dist/src/planner/building/select-ordinal.d.ts.map +1 -1
  154. package/dist/src/planner/building/select-ordinal.js +1 -1
  155. package/dist/src/planner/building/select-ordinal.js.map +1 -1
  156. package/dist/src/planner/building/select-window.d.ts +2 -2
  157. package/dist/src/planner/building/select-window.d.ts.map +1 -1
  158. package/dist/src/planner/building/select-window.js +82 -101
  159. package/dist/src/planner/building/select-window.js.map +1 -1
  160. package/dist/src/planner/building/select.d.ts.map +1 -1
  161. package/dist/src/planner/building/select.js +78 -33
  162. package/dist/src/planner/building/select.js.map +1 -1
  163. package/dist/src/planner/building/update.d.ts.map +1 -1
  164. package/dist/src/planner/building/update.js +14 -8
  165. package/dist/src/planner/building/update.js.map +1 -1
  166. package/dist/src/planner/building/view-mutation-builder.d.ts.map +1 -1
  167. package/dist/src/planner/building/view-mutation-builder.js +329 -14
  168. package/dist/src/planner/building/view-mutation-builder.js.map +1 -1
  169. package/dist/src/planner/building/with.d.ts +6 -0
  170. package/dist/src/planner/building/with.d.ts.map +1 -1
  171. package/dist/src/planner/building/with.js +52 -3
  172. package/dist/src/planner/building/with.js.map +1 -1
  173. package/dist/src/planner/cache/correlation-detector.d.ts.map +1 -1
  174. package/dist/src/planner/cache/correlation-detector.js +44 -17
  175. package/dist/src/planner/cache/correlation-detector.js.map +1 -1
  176. package/dist/src/planner/framework/physical-utils.d.ts +6 -0
  177. package/dist/src/planner/framework/physical-utils.d.ts.map +1 -1
  178. package/dist/src/planner/framework/physical-utils.js +6 -0
  179. package/dist/src/planner/framework/physical-utils.js.map +1 -1
  180. package/dist/src/planner/mutation/lens-enforcement.d.ts +123 -0
  181. package/dist/src/planner/mutation/lens-enforcement.d.ts.map +1 -1
  182. package/dist/src/planner/mutation/lens-enforcement.js +362 -5
  183. package/dist/src/planner/mutation/lens-enforcement.js.map +1 -1
  184. package/dist/src/planner/mutation/mutation-diagnostic.d.ts +1 -1
  185. package/dist/src/planner/mutation/mutation-diagnostic.d.ts.map +1 -1
  186. package/dist/src/planner/mutation/mutation-diagnostic.js.map +1 -1
  187. package/dist/src/planner/nodes/alter-table-node.d.ts +15 -11
  188. package/dist/src/planner/nodes/alter-table-node.d.ts.map +1 -1
  189. package/dist/src/planner/nodes/alter-table-node.js +5 -0
  190. package/dist/src/planner/nodes/alter-table-node.js.map +1 -1
  191. package/dist/src/planner/nodes/bloom-join-node.d.ts.map +1 -1
  192. package/dist/src/planner/nodes/bloom-join-node.js +7 -0
  193. package/dist/src/planner/nodes/bloom-join-node.js.map +1 -1
  194. package/dist/src/planner/nodes/constraint-check-node.d.ts +16 -0
  195. package/dist/src/planner/nodes/constraint-check-node.d.ts.map +1 -1
  196. package/dist/src/planner/nodes/constraint-check-node.js.map +1 -1
  197. package/dist/src/planner/nodes/plan-node-type.d.ts +1 -1
  198. package/dist/src/planner/nodes/plan-node-type.d.ts.map +1 -1
  199. package/dist/src/planner/nodes/plan-node-type.js +1 -1
  200. package/dist/src/planner/nodes/plan-node-type.js.map +1 -1
  201. package/dist/src/planner/nodes/pragma.d.ts.map +1 -1
  202. package/dist/src/planner/nodes/pragma.js +5 -2
  203. package/dist/src/planner/nodes/pragma.js.map +1 -1
  204. package/dist/src/planner/nodes/reference.d.ts.map +1 -1
  205. package/dist/src/planner/nodes/reference.js +9 -1
  206. package/dist/src/planner/nodes/reference.js.map +1 -1
  207. package/dist/src/planner/nodes/scalar.d.ts.map +1 -1
  208. package/dist/src/planner/nodes/scalar.js +117 -91
  209. package/dist/src/planner/nodes/scalar.js.map +1 -1
  210. package/dist/src/planner/nodes/sequence-node.d.ts +50 -0
  211. package/dist/src/planner/nodes/sequence-node.d.ts.map +1 -0
  212. package/dist/src/planner/nodes/sequence-node.js +96 -0
  213. package/dist/src/planner/nodes/sequence-node.js.map +1 -0
  214. package/dist/src/planner/nodes/values-node.d.ts +8 -0
  215. package/dist/src/planner/nodes/values-node.d.ts.map +1 -1
  216. package/dist/src/planner/nodes/values-node.js +29 -6
  217. package/dist/src/planner/nodes/values-node.js.map +1 -1
  218. package/dist/src/planner/nodes/view-mutation-node.d.ts +43 -1
  219. package/dist/src/planner/nodes/view-mutation-node.d.ts.map +1 -1
  220. package/dist/src/planner/nodes/view-mutation-node.js +27 -6
  221. package/dist/src/planner/nodes/view-mutation-node.js.map +1 -1
  222. package/dist/src/planner/nodes/window-node.d.ts +33 -4
  223. package/dist/src/planner/nodes/window-node.d.ts.map +1 -1
  224. package/dist/src/planner/nodes/window-node.js +80 -36
  225. package/dist/src/planner/nodes/window-node.js.map +1 -1
  226. package/dist/src/planner/planning-context.d.ts +38 -1
  227. package/dist/src/planner/planning-context.d.ts.map +1 -1
  228. package/dist/src/planner/rules/access/rule-key-set-seek.d.ts.map +1 -1
  229. package/dist/src/planner/rules/access/rule-key-set-seek.js +8 -3
  230. package/dist/src/planner/rules/access/rule-key-set-seek.js.map +1 -1
  231. package/dist/src/planner/rules/join/rule-fanout-lookup-join.js +18 -12
  232. package/dist/src/planner/rules/join/rule-fanout-lookup-join.js.map +1 -1
  233. package/dist/src/planner/rules/retrieve/rule-grow-retrieve.js +4 -3
  234. package/dist/src/planner/rules/retrieve/rule-grow-retrieve.js.map +1 -1
  235. package/dist/src/planner/rules/window/rule-monotonic-window.d.ts.map +1 -1
  236. package/dist/src/planner/rules/window/rule-monotonic-window.js +6 -0
  237. package/dist/src/planner/rules/window/rule-monotonic-window.js.map +1 -1
  238. package/dist/src/planner/scopes/param.d.ts.map +1 -1
  239. package/dist/src/planner/scopes/param.js +10 -5
  240. package/dist/src/planner/scopes/param.js.map +1 -1
  241. package/dist/src/planner/util/ind-utils.d.ts +25 -8
  242. package/dist/src/planner/util/ind-utils.d.ts.map +1 -1
  243. package/dist/src/planner/util/ind-utils.js +53 -10
  244. package/dist/src/planner/util/ind-utils.js.map +1 -1
  245. package/dist/src/planner/util/key-utils.d.ts +7 -7
  246. package/dist/src/planner/util/key-utils.js +9 -9
  247. package/dist/src/planner/util/key-utils.js.map +1 -1
  248. package/dist/src/runtime/delta-executor.d.ts.map +1 -1
  249. package/dist/src/runtime/delta-executor.js +7 -2
  250. package/dist/src/runtime/delta-executor.js.map +1 -1
  251. package/dist/src/runtime/emit/add-constraint.d.ts.map +1 -1
  252. package/dist/src/runtime/emit/add-constraint.js +50 -15
  253. package/dist/src/runtime/emit/add-constraint.js.map +1 -1
  254. package/dist/src/runtime/emit/alter-schema-event.d.ts +2 -43
  255. package/dist/src/runtime/emit/alter-schema-event.d.ts.map +1 -1
  256. package/dist/src/runtime/emit/alter-schema-event.js +2 -53
  257. package/dist/src/runtime/emit/alter-schema-event.js.map +1 -1
  258. package/dist/src/runtime/emit/alter-table.d.ts.map +1 -1
  259. package/dist/src/runtime/emit/alter-table.js +59 -24
  260. package/dist/src/runtime/emit/alter-table.js.map +1 -1
  261. package/dist/src/runtime/emit/analyze.d.ts.map +1 -1
  262. package/dist/src/runtime/emit/analyze.js +7 -0
  263. package/dist/src/runtime/emit/analyze.js.map +1 -1
  264. package/dist/src/runtime/emit/assertion-rename-helpers.d.ts.map +1 -1
  265. package/dist/src/runtime/emit/assertion-rename-helpers.js +2 -3
  266. package/dist/src/runtime/emit/assertion-rename-helpers.js.map +1 -1
  267. package/dist/src/runtime/emit/binary.d.ts +5 -0
  268. package/dist/src/runtime/emit/binary.d.ts.map +1 -1
  269. package/dist/src/runtime/emit/binary.js +22 -22
  270. package/dist/src/runtime/emit/binary.js.map +1 -1
  271. package/dist/src/runtime/emit/block.d.ts.map +1 -1
  272. package/dist/src/runtime/emit/block.js +9 -3
  273. package/dist/src/runtime/emit/block.js.map +1 -1
  274. package/dist/src/runtime/emit/constraint-check.d.ts.map +1 -1
  275. package/dist/src/runtime/emit/constraint-check.js +5 -6
  276. package/dist/src/runtime/emit/constraint-check.js.map +1 -1
  277. package/dist/src/runtime/emit/create-assertion.d.ts.map +1 -1
  278. package/dist/src/runtime/emit/create-assertion.js +71 -70
  279. package/dist/src/runtime/emit/create-assertion.js.map +1 -1
  280. package/dist/src/runtime/emit/create-index.d.ts.map +1 -1
  281. package/dist/src/runtime/emit/create-index.js +8 -3
  282. package/dist/src/runtime/emit/create-index.js.map +1 -1
  283. package/dist/src/runtime/emit/create-table.d.ts.map +1 -1
  284. package/dist/src/runtime/emit/create-table.js +20 -10
  285. package/dist/src/runtime/emit/create-table.js.map +1 -1
  286. package/dist/src/runtime/emit/create-view.d.ts.map +1 -1
  287. package/dist/src/runtime/emit/create-view.js +51 -44
  288. package/dist/src/runtime/emit/create-view.js.map +1 -1
  289. package/dist/src/runtime/emit/ddl-event-scope.d.ts +71 -0
  290. package/dist/src/runtime/emit/ddl-event-scope.d.ts.map +1 -0
  291. package/dist/src/runtime/emit/ddl-event-scope.js +80 -0
  292. package/dist/src/runtime/emit/ddl-event-scope.js.map +1 -0
  293. package/dist/src/runtime/emit/dml-executor.d.ts.map +1 -1
  294. package/dist/src/runtime/emit/dml-executor.js +23 -23
  295. package/dist/src/runtime/emit/dml-executor.js.map +1 -1
  296. package/dist/src/runtime/emit/drop-assertion.d.ts.map +1 -1
  297. package/dist/src/runtime/emit/drop-assertion.js +23 -17
  298. package/dist/src/runtime/emit/drop-assertion.js.map +1 -1
  299. package/dist/src/runtime/emit/drop-index.d.ts.map +1 -1
  300. package/dist/src/runtime/emit/drop-index.js +7 -2
  301. package/dist/src/runtime/emit/drop-index.js.map +1 -1
  302. package/dist/src/runtime/emit/drop-table.d.ts.map +1 -1
  303. package/dist/src/runtime/emit/drop-table.js +16 -10
  304. package/dist/src/runtime/emit/drop-table.js.map +1 -1
  305. package/dist/src/runtime/emit/drop-view.d.ts.map +1 -1
  306. package/dist/src/runtime/emit/drop-view.js +50 -43
  307. package/dist/src/runtime/emit/drop-view.js.map +1 -1
  308. package/dist/src/runtime/emit/insert.d.ts.map +1 -1
  309. package/dist/src/runtime/emit/insert.js +2 -1
  310. package/dist/src/runtime/emit/insert.js.map +1 -1
  311. package/dist/src/runtime/emit/materialized-view.d.ts.map +1 -1
  312. package/dist/src/runtime/emit/materialized-view.js +87 -67
  313. package/dist/src/runtime/emit/materialized-view.js.map +1 -1
  314. package/dist/src/runtime/emit/schema-declarative.d.ts +8 -0
  315. package/dist/src/runtime/emit/schema-declarative.d.ts.map +1 -1
  316. package/dist/src/runtime/emit/schema-declarative.js +128 -25
  317. package/dist/src/runtime/emit/schema-declarative.js.map +1 -1
  318. package/dist/src/runtime/emit/sequence.d.ts +17 -0
  319. package/dist/src/runtime/emit/sequence.d.ts.map +1 -0
  320. package/dist/src/runtime/emit/sequence.js +40 -0
  321. package/dist/src/runtime/emit/sequence.js.map +1 -0
  322. package/dist/src/runtime/emit/set-object-tags.d.ts.map +1 -1
  323. package/dist/src/runtime/emit/set-object-tags.js +40 -32
  324. package/dist/src/runtime/emit/set-object-tags.js.map +1 -1
  325. package/dist/src/runtime/emit/table-valued-function.d.ts.map +1 -1
  326. package/dist/src/runtime/emit/table-valued-function.js +3 -2
  327. package/dist/src/runtime/emit/table-valued-function.js.map +1 -1
  328. package/dist/src/runtime/emit/update.js +3 -3
  329. package/dist/src/runtime/emit/update.js.map +1 -1
  330. package/dist/src/runtime/emit/view-mutation.d.ts.map +1 -1
  331. package/dist/src/runtime/emit/view-mutation.js +45 -3
  332. package/dist/src/runtime/emit/view-mutation.js.map +1 -1
  333. package/dist/src/runtime/foreign-key-actions.d.ts.map +1 -1
  334. package/dist/src/runtime/foreign-key-actions.js +9 -10
  335. package/dist/src/runtime/foreign-key-actions.js.map +1 -1
  336. package/dist/src/runtime/register.d.ts.map +1 -1
  337. package/dist/src/runtime/register.js +3 -2
  338. package/dist/src/runtime/register.js.map +1 -1
  339. package/dist/src/runtime/row-constraints.d.ts +14 -8
  340. package/dist/src/runtime/row-constraints.d.ts.map +1 -1
  341. package/dist/src/runtime/row-constraints.js +41 -15
  342. package/dist/src/runtime/row-constraints.js.map +1 -1
  343. package/dist/src/runtime/strict-representation.d.ts +11 -6
  344. package/dist/src/runtime/strict-representation.d.ts.map +1 -1
  345. package/dist/src/runtime/strict-representation.js +1 -44
  346. package/dist/src/runtime/strict-representation.js.map +1 -1
  347. package/dist/src/schema/assertion.d.ts +25 -2
  348. package/dist/src/schema/assertion.d.ts.map +1 -1
  349. package/dist/src/schema/assertion.js.map +1 -1
  350. package/dist/src/schema/catalog-rendering.d.ts +38 -0
  351. package/dist/src/schema/catalog-rendering.d.ts.map +1 -0
  352. package/dist/src/schema/catalog-rendering.js +171 -0
  353. package/dist/src/schema/catalog-rendering.js.map +1 -0
  354. package/dist/src/schema/catalog.d.ts +69 -3
  355. package/dist/src/schema/catalog.d.ts.map +1 -1
  356. package/dist/src/schema/catalog.js +124 -19
  357. package/dist/src/schema/catalog.js.map +1 -1
  358. package/dist/src/schema/column-source-resolver.d.ts +9 -5
  359. package/dist/src/schema/column-source-resolver.d.ts.map +1 -1
  360. package/dist/src/schema/column-source-resolver.js +9 -5
  361. package/dist/src/schema/column-source-resolver.js.map +1 -1
  362. package/dist/src/schema/constraint-builder.d.ts +13 -10
  363. package/dist/src/schema/constraint-builder.d.ts.map +1 -1
  364. package/dist/src/schema/constraint-builder.js +45 -17
  365. package/dist/src/schema/constraint-builder.js.map +1 -1
  366. package/dist/src/schema/declared-schema-manager.d.ts +50 -0
  367. package/dist/src/schema/declared-schema-manager.d.ts.map +1 -1
  368. package/dist/src/schema/declared-schema-manager.js +52 -0
  369. package/dist/src/schema/declared-schema-manager.js.map +1 -1
  370. package/dist/src/schema/expr-scope/frame.d.ts +65 -0
  371. package/dist/src/schema/expr-scope/frame.d.ts.map +1 -0
  372. package/dist/src/schema/expr-scope/frame.js +104 -0
  373. package/dist/src/schema/expr-scope/frame.js.map +1 -0
  374. package/dist/src/schema/expr-scope/walk.d.ts +40 -0
  375. package/dist/src/schema/expr-scope/walk.d.ts.map +1 -0
  376. package/dist/src/schema/expr-scope/walk.js +260 -0
  377. package/dist/src/schema/expr-scope/walk.js.map +1 -0
  378. package/dist/src/schema/generated-column-refs.d.ts +41 -0
  379. package/dist/src/schema/generated-column-refs.d.ts.map +1 -0
  380. package/dist/src/schema/generated-column-refs.js +124 -0
  381. package/dist/src/schema/generated-column-refs.js.map +1 -0
  382. package/dist/src/schema/lens-ack.d.ts.map +1 -1
  383. package/dist/src/schema/lens-ack.js +5 -2
  384. package/dist/src/schema/lens-ack.js.map +1 -1
  385. package/dist/src/schema/lens-compiler.d.ts.map +1 -1
  386. package/dist/src/schema/lens-compiler.js +9 -1
  387. package/dist/src/schema/lens-compiler.js.map +1 -1
  388. package/dist/src/schema/lens-prover.d.ts +7 -2
  389. package/dist/src/schema/lens-prover.d.ts.map +1 -1
  390. package/dist/src/schema/lens-prover.js +105 -11
  391. package/dist/src/schema/lens-prover.js.map +1 -1
  392. package/dist/src/schema/lens.d.ts +12 -0
  393. package/dist/src/schema/lens.d.ts.map +1 -1
  394. package/dist/src/schema/lens.js +10 -0
  395. package/dist/src/schema/lens.js.map +1 -1
  396. package/dist/src/schema/manager.d.ts +35 -1
  397. package/dist/src/schema/manager.d.ts.map +1 -1
  398. package/dist/src/schema/manager.js +125 -26
  399. package/dist/src/schema/manager.js.map +1 -1
  400. package/dist/src/schema/rename/self-qualifier-strip.d.ts +16 -10
  401. package/dist/src/schema/rename/self-qualifier-strip.d.ts.map +1 -1
  402. package/dist/src/schema/rename/self-qualifier-strip.js +30 -213
  403. package/dist/src/schema/rename/self-qualifier-strip.js.map +1 -1
  404. package/dist/src/schema/rename-rewriter.d.ts +4 -3
  405. package/dist/src/schema/rename-rewriter.d.ts.map +1 -1
  406. package/dist/src/schema/rename-rewriter.js +4 -3
  407. package/dist/src/schema/rename-rewriter.js.map +1 -1
  408. package/dist/src/schema/schema-differ.d.ts +54 -6
  409. package/dist/src/schema/schema-differ.d.ts.map +1 -1
  410. package/dist/src/schema/schema-differ.js +108 -42
  411. package/dist/src/schema/schema-differ.js.map +1 -1
  412. package/dist/src/schema/schema-hasher.d.ts +12 -0
  413. package/dist/src/schema/schema-hasher.d.ts.map +1 -1
  414. package/dist/src/schema/schema-hasher.js +28 -10
  415. package/dist/src/schema/schema-hasher.js.map +1 -1
  416. package/dist/src/schema/table.d.ts +74 -19
  417. package/dist/src/schema/table.d.ts.map +1 -1
  418. package/dist/src/schema/table.js +133 -71
  419. package/dist/src/schema/table.js.map +1 -1
  420. package/dist/src/types/comparison-coercion.d.ts +5 -1
  421. package/dist/src/types/comparison-coercion.d.ts.map +1 -1
  422. package/dist/src/types/comparison-coercion.js +21 -5
  423. package/dist/src/types/comparison-coercion.js.map +1 -1
  424. package/dist/src/types/representation.d.ts +65 -0
  425. package/dist/src/types/representation.d.ts.map +1 -0
  426. package/dist/src/types/representation.js +63 -0
  427. package/dist/src/types/representation.js.map +1 -0
  428. package/dist/src/types/validation.d.ts +76 -22
  429. package/dist/src/types/validation.d.ts.map +1 -1
  430. package/dist/src/types/validation.js +110 -31
  431. package/dist/src/types/validation.js.map +1 -1
  432. package/dist/src/vtab/best-access-plan.d.ts +10 -0
  433. package/dist/src/vtab/best-access-plan.d.ts.map +1 -1
  434. package/dist/src/vtab/best-access-plan.js +16 -0
  435. package/dist/src/vtab/best-access-plan.js.map +1 -1
  436. package/dist/src/vtab/capabilities.d.ts +72 -4
  437. package/dist/src/vtab/capabilities.d.ts.map +1 -1
  438. package/dist/src/vtab/memory/layer/manager.d.ts.map +1 -1
  439. package/dist/src/vtab/memory/layer/manager.js +17 -13
  440. package/dist/src/vtab/memory/layer/manager.js.map +1 -1
  441. package/dist/src/vtab/module.d.ts +13 -7
  442. package/dist/src/vtab/module.d.ts.map +1 -1
  443. package/package.json +3 -3
  444. package/dist/src/planner/nodes/array-index-node.d.ts +0 -22
  445. package/dist/src/planner/nodes/array-index-node.d.ts.map +0 -1
  446. package/dist/src/planner/nodes/array-index-node.js +0 -41
  447. package/dist/src/planner/nodes/array-index-node.js.map +0 -1
  448. package/dist/src/runtime/emit/array-index.d.ts +0 -5
  449. package/dist/src/runtime/emit/array-index.d.ts.map +0 -1
  450. package/dist/src/runtime/emit/array-index.js +0 -22
  451. package/dist/src/runtime/emit/array-index.js.map +0 -1
@@ -14,6 +14,7 @@ import { isAggregateFunctionSchema } from '../../schema/function.js';
14
14
  import { expressionToString, expressionToIdentityString } from '../../emit/ast-stringify.js';
15
15
  import { buildOrdinalAwareExpression, resolveOrdinalReference } from './select-ordinal.js';
16
16
  import { collectDefinedAttrIds } from '../analysis/equi-correlation.js';
17
+ import { walkAstNodes } from '../analysis/predicate-shape.js';
17
18
  import { PlanNodeType } from '../nodes/plan-node-type.js';
18
19
  /**
19
20
  * Processes GROUP BY, aggregates, and HAVING clauses
@@ -32,14 +33,17 @@ export function buildAggregatePhase(input, stmt, selectContext, aggregates, hasA
32
33
  hasHavingOnlyAggregates = true;
33
34
  }
34
35
  }
35
- // Detect aggregate function references in ORDER BY. They are only legal when
36
- // the query is otherwise an aggregate query (has aggregates in SELECT/HAVING
37
- // or has a GROUP BY). When legal, any ORDER BY aggregate not already present
38
- // in the SELECT or HAVING aggregate list must be added to the AggregateNode
39
- // so it is computed and available to the post-aggregate sort.
40
- const orderByHasAggregates = orderByContainsAggregates(stmt.orderBy, selectContext, selectList);
36
+ // Decide where this query's ORDER BY has to run (see
37
+ // `orderByNeedsPostAggregateSort`). Aggregate function references in ORDER BY are
38
+ // only legal when the query is otherwise an aggregate query (has aggregates in
39
+ // SELECT/HAVING or has a GROUP BY). When legal, any ORDER BY aggregate not already
40
+ // present in the SELECT or HAVING aggregate list must be added to the AggregateNode
41
+ // so it is computed and available to the post-aggregate sort. An ORDER BY that only
42
+ // names a SELECT-list alias adds nothing here — the alias resolves through a scope
43
+ // later — so `hasOrderByOnlyAggregates` stays false for it.
44
+ const needsPostAggregateSort = orderByNeedsPostAggregateSort(stmt.orderBy, selectContext, selectList);
41
45
  let hasOrderByOnlyAggregates = false;
42
- if (orderByHasAggregates && (hasAggregates || hasGroupBy)) {
46
+ if (needsPostAggregateSort && (hasAggregates || hasGroupBy)) {
43
47
  const orderByAggs = collectOrderByAggregates(stmt.orderBy, selectContext, aggregates);
44
48
  if (orderByAggs.length > 0) {
45
49
  aggregates.push(...orderByAggs);
@@ -72,10 +76,10 @@ export function buildAggregatePhase(input, stmt, selectContext, aggregates, hasA
72
76
  // After (optional) early HAVING filter we continue with the existing pipeline
73
77
  // ----------------------------------------------------------------------------
74
78
  // Handle pre-aggregate sorting for ORDER BY without GROUP BY. Skip when the
75
- // ORDER BY contains aggregates — those need to run against the post-aggregate
76
- // row(s), not the per-input rows.
77
- const preAggregateSort = Boolean(hasAggregates && !hasGroupBy && stmt.orderBy && stmt.orderBy.length > 0 && !orderByHasAggregates);
78
- currentInput = handlePreAggregateSort(currentInput, stmt, selectContext, hasAggregates, !!hasGroupBy, orderByHasAggregates, selectList);
79
+ // ORDER BY names an aggregate or a SELECT-list alias — neither can be evaluated
80
+ // against the per-input rows; they need the post-aggregate row(s).
81
+ const preAggregateSort = Boolean(hasAggregates && !hasGroupBy && stmt.orderBy && stmt.orderBy.length > 0 && !needsPostAggregateSort);
82
+ currentInput = handlePreAggregateSort(currentInput, stmt, selectContext, preAggregateSort, selectList);
79
83
  // Build GROUP BY expressions, resolving 1-based positional references against the SELECT list.
80
84
  const groupByExpressions = stmt.groupBy ?
81
85
  stmt.groupBy.map(expr => buildOrdinalAwareExpression(selectContext, expr, selectList, 'GROUP BY', false)) : [];
@@ -85,8 +89,20 @@ export function buildAggregatePhase(input, stmt, selectContext, aggregates, hasA
85
89
  // Create AggregateNode
86
90
  const aggregateNode = new AggregateNode(selectContext.scope, currentInput, groupByExpressions, aggregates);
87
91
  currentInput = aggregateNode;
92
+ // A GROUPED query's post-aggregate expressions — HAVING below, the rebuilt SELECT
93
+ // list, a window phase's specifications and function arguments, the sort keys —
94
+ // are all built against scopes that fall through to the pre-aggregate select scope,
95
+ // so a legal spelling of a grouping key can bind to a base-table attribute the
96
+ // grouped row does not carry. Every such expression reaches the fix through ONE
97
+ // entry point, {@link redirectPostAggregate}, off this one context — built here,
98
+ // the moment the AggregateNode exists, so no post-aggregate site can run before it
99
+ // does. An aggregate query with no GROUP BY has no grouping keys to redirect onto
100
+ // and gets none.
101
+ const groupedRedirectContext = groupByExpressions.length > 0
102
+ ? buildGroupedRedirectContext(groupByExpressions, aggregateNode.getAttributes(), aggregateNode.getRelations()[0])
103
+ : undefined;
88
104
  // Create aggregate output scope
89
- const aggregateOutputScope = createAggregateOutputScope(selectContext.scope, currentInput, groupByExpressions, aggregates);
105
+ const aggregateOutputScope = createAggregateOutputScope(selectContext.scope, currentInput, groupByExpressions, aggregates, projections);
90
106
  // Build the aggregates planning context entries so downstream builders
91
107
  // (final projection, ORDER BY) can resolve aggregate function references
92
108
  // to ColumnReferenceNodes against the AggregateNode output.
@@ -104,7 +120,7 @@ export function buildAggregatePhase(input, stmt, selectContext, aggregates, hasA
104
120
  // Handle HAVING clause *after* aggregation only when we did not already push
105
121
  // it below the AggregateNode.
106
122
  if (stmt.having && !shouldPushHavingBelowAggregate) {
107
- currentInput = buildHavingFilter(currentInput, stmt.having, selectContext, aggregateOutputScope, aggregates, groupByExpressions);
123
+ currentInput = buildHavingFilter(currentInput, stmt.having, selectContext, aggregateOutputScope, aggregates, groupByExpressions, groupedRedirectContext);
108
124
  }
109
125
  // Determine if final projection is needed.
110
126
  // Force a final projection when HAVING-only or ORDER-BY-only aggregates were
@@ -134,32 +150,27 @@ export function buildAggregatePhase(input, stmt, selectContext, aggregates, hasA
134
150
  groupByExpressions,
135
151
  hasHavingOnlyAggregates,
136
152
  hasOrderByOnlyAggregates,
137
- orderByHasAggregates,
153
+ orderByNeedsPostAggregateSort: needsPostAggregateSort,
138
154
  aggregatesContext,
155
+ groupedRedirectContext,
139
156
  };
140
157
  }
141
158
  /**
142
- * Handles pre-aggregate sorting for special cases
159
+ * Sorts the per-input rows BELOW the AggregateNode when the caller decided this
160
+ * query takes the pre-aggregate sort (see the `preAggregateSort` computation in
161
+ * {@link buildAggregatePhase}); otherwise passes `input` through untouched.
143
162
  */
144
- function handlePreAggregateSort(input, stmt, selectContext, hasAggregates, hasGroupBy, orderByHasAggregates, selectList) {
145
- // Special handling for ORDER BY with aggregates but no GROUP BY.
146
- // Skip when ORDER BY itself references aggregates — those must run
147
- // post-aggregation, not on the per-row input.
148
- if (hasAggregates && !hasGroupBy && stmt.orderBy && stmt.orderBy.length > 0 && !orderByHasAggregates) {
149
- // Apply ORDER BY before aggregation. This sort sits BELOW the AggregateNode,
150
- // so a positional reference resolves through the select list (no output
151
- // attributes exist yet to bind to).
152
- const sortKeys = stmt.orderBy.map(orderByClause => {
153
- const expression = buildOrdinalAwareExpression(selectContext, orderByClause.expr, selectList, 'ORDER BY');
154
- return {
155
- expression,
156
- direction: orderByClause.direction,
157
- nulls: orderByClause.nulls
158
- };
159
- });
160
- return new SortNode(selectContext.scope, input, sortKeys);
161
- }
162
- return input;
163
+ function handlePreAggregateSort(input, stmt, selectContext, preAggregateSort, selectList) {
164
+ if (!preAggregateSort)
165
+ return input;
166
+ // This sort sits BELOW the AggregateNode, so a positional reference resolves
167
+ // through the select list (no output attributes exist yet to bind to).
168
+ const sortKeys = stmt.orderBy.map(orderByClause => ({
169
+ expression: buildOrdinalAwareExpression(selectContext, orderByClause.expr, selectList, 'ORDER BY'),
170
+ direction: orderByClause.direction,
171
+ nulls: orderByClause.nulls
172
+ }));
173
+ return new SortNode(selectContext.scope, input, sortKeys);
163
174
  }
164
175
  /**
165
176
  * Builds the coverage test for a grouped query. `groupedOutputAttributes` is the
@@ -167,7 +178,7 @@ function handlePreAggregateSort(input, stmt, selectContext, hasAggregates, hasGr
167
178
  * needed only when the expressions to be checked were built against the aggregate
168
179
  * output scope.
169
180
  */
170
- export function buildGroupByCoverage(groupByExpressions, groupedOutputAttributes = []) {
181
+ function buildGroupByCoverage(groupByExpressions, groupedOutputAttributes = []) {
171
182
  const attrIds = new Set();
172
183
  const fingerprints = new Set();
173
184
  for (const expr of groupByExpressions) {
@@ -183,10 +194,10 @@ export function buildGroupByCoverage(groupByExpressions, groupedOutputAttributes
183
194
  }
184
195
  /**
185
196
  * Raises the standard GROUP BY coverage error when `node` reads a column the
186
- * grouped rows do not carry. Used by the SELECT-list check below; the window
187
- * phase raises the same message from {@link assertGroupedWindowCoverage}.
197
+ * grouped rows do not carry. Used by the SELECT-list check below; the finished-plan
198
+ * check raises the same message from {@link assertGroupedPlanCoverage}.
188
199
  */
189
- export function assertGroupByCoverage(node, coverage) {
200
+ function assertGroupByCoverage(node, coverage) {
190
201
  const ungrouped = findUngroupedColumnRef(node, coverage);
191
202
  if (!ungrouped)
192
203
  return;
@@ -197,7 +208,7 @@ export function assertGroupByCoverage(node, coverage) {
197
208
  * column index is the key's position in that list, which is its position on the
198
209
  * AggregateNode's output row.
199
210
  */
200
- export function indexGroupKeys(groupByExpressions) {
211
+ function indexGroupKeys(groupByExpressions) {
201
212
  const byFingerprint = new Map();
202
213
  const byBaseAttrId = new Map();
203
214
  groupByExpressions.forEach((expr, index) => {
@@ -213,13 +224,13 @@ export function indexGroupKeys(groupByExpressions) {
213
224
  return { byFingerprint, byBaseAttrId };
214
225
  }
215
226
  /**
216
- * Builds the {@link GroupedWindowContext} for a grouped, windowed query.
227
+ * Builds the {@link GroupedRedirectContext} for a grouped query.
217
228
  * `outputAttributes` is the AggregateNode's attribute list (group keys in GROUP BY
218
229
  * order, then the aggregate results); `aggregateInput` is the AggregateNode's
219
230
  * source relation, whose whole subtree defines
220
- * {@link GroupedWindowContext.aggregateInputAttrIds}.
231
+ * {@link GroupedRedirectContext.aggregateInputAttrIds}.
221
232
  */
222
- export function buildGroupedWindowContext(groupByExpressions, outputAttributes, aggregateInput) {
233
+ function buildGroupedRedirectContext(groupByExpressions, outputAttributes, aggregateInput) {
223
234
  return {
224
235
  groupKeys: indexGroupKeys(groupByExpressions),
225
236
  outputAttributes,
@@ -250,10 +261,35 @@ function isCteDefinition(node) {
250
261
  return node.nodeType === PlanNodeType.CTE || node.nodeType === PlanNodeType.RecursiveCTE;
251
262
  }
252
263
  /**
253
- * Rewrites every subtree of a window specification / window-function argument that
254
- * IS a grouping key into a reference to the AggregateNode's output column for that
255
- * key, so the expression reads the grouped row instead of a base-table column that
256
- * row does not carry.
264
+ * The ONE entry point through which every post-aggregate expression of a GROUPED
265
+ * query reaches the grouping-key redirect: the rebuilt SELECT list, the window
266
+ * phase's specifications and function arguments, the HAVING predicate, and every
267
+ * post-aggregate sort key. A builder that constructs an expression above the
268
+ * AggregateNode calls this rather than remembering the rewrite itself — forgetting
269
+ * is how the SELECT-list and ORDER BY bugs shipped
270
+ * (complete/bug-qualified-group-key-in-select-list-breaks-window-query,
271
+ * complete/bug-order-by-grouping-key-spelling-breaks-window-query).
272
+ *
273
+ * A pass-through when `context` is absent (the query has no GROUP BY keys), and when
274
+ * the expression contains nothing to redirect. That gate is load-bearing, not an
275
+ * optimization: {@link redirectToGroupKeys} matches a subtree by AST text, so under
276
+ * `group by a` an ungated pass would also fingerprint a plain `order by a` that
277
+ * already bound correctly to a projection/window/aggregate OUTPUT attribute — the
278
+ * clearest hazard being an alias that shadows a grouping key (`select upper(a) as a …
279
+ * group by a order by a` must sort by the projected `upper(a)`). Only an expression
280
+ * that actually fell through to a pre-grouping attribute has anything to fix, and
281
+ * only such an expression is walked.
282
+ */
283
+ export function redirectPostAggregate(expr, context, scope) {
284
+ if (!context || !referencesAggregateInput(expr, context))
285
+ return expr;
286
+ return redirectToGroupKeys(expr, context, scope);
287
+ }
288
+ /**
289
+ * Rewrites every subtree of a post-aggregate expression that IS a grouping key into
290
+ * a reference to the AggregateNode's output column for that key, so the expression
291
+ * reads the grouped row instead of a base-table column that row does not carry.
292
+ * Reached only through {@link redirectPostAggregate}.
257
293
  *
258
294
  * Two match rules, in order at each node:
259
295
  *
@@ -294,7 +330,7 @@ function isCteDefinition(node) {
294
330
  * fixing it means comparing resolved attribute identity rather than text, for both
295
331
  * callers at once.
296
332
  */
297
- export function redirectToGroupKeys(node, context, scope) {
333
+ function redirectToGroupKeys(node, context, scope) {
298
334
  return redirectNode(node, context, scope);
299
335
  }
300
336
  /**
@@ -306,12 +342,24 @@ export function redirectToGroupKeys(node, context, scope) {
306
342
  *
307
343
  * NOTE: this renders an identity fingerprint at every scalar node it visits, and a
308
344
  * fingerprint hit inside a subquery then walks that node's subtree again for the guard
309
- * — so a window specification containing a large subquery costs more to build than
310
- * before, once per prepare of the query. Not measured; if preparing such queries ever
311
- * shows up as slow, memoize the fingerprint per node or bail out of the walk for
312
- * subtrees that contain no `aggregateInputAttrIds` reference at all.
345
+ * — so every grouped query pays a walk of its whole SELECT list, plus its window
346
+ * specifications when it has them, once per prepare. Not measured; if preparing grouped
347
+ * queries ever shows up as slow, memoize the fingerprint per node or bail out of the
348
+ * walk for subtrees that contain no `aggregateInputAttrIds` reference at all.
349
+ *
350
+ * NOTE: the SELECT-list caller hands us its window-function subtrees too, and the window
351
+ * phase then throws each of them away — `rewriteWindowFunctions` (select-window.ts)
352
+ * replaces the whole node with a column reference to the WindowNode's output, looked up
353
+ * by the `AST.WindowFunctionExpr`'s object identity. A `WindowFunctionCallNode` is
354
+ * zero-ary, so this walk could never rebuild one anyway — but it is skipped explicitly
355
+ * below so that lookup's key can never be disturbed and rule 1 can never fingerprint a
356
+ * window function itself. The window phase redirects its specification expressions
357
+ * itself and never passes one in.
313
358
  */
314
359
  function redirectNode(node, context, scope, insideSubquery = false) {
360
+ if (CapabilityDetectors.isWindowFunction(node)) {
361
+ return node;
362
+ }
315
363
  if ('expression' in node) {
316
364
  const expression = node.expression;
317
365
  const fingerprintIndex = context.groupKeys.byFingerprint.get(expressionToIdentityString(expression));
@@ -358,12 +406,96 @@ function readsOnlyAggregateInput(node, context) {
358
406
  return true;
359
407
  }
360
408
  /**
361
- * Raises the GROUP BY coverage error for a window specification / window-function
362
- * argument of a GROUPED query that still reads a pre-grouping column after
363
- * {@link redirectToGroupKeys} has run.
409
+ * True when `node` names one of THIS query's pre-grouping columns and the AggregateNode's
410
+ * output row does not carry it — the exact condition that makes a reference above the
411
+ * aggregate either redirectable ({@link referencesAggregateInput}) or, once the redirect
412
+ * has run, an ungrouped column ({@link findUngroupedPostAggregateRef}). The two callers ask
413
+ * it of different subtrees for different purposes, but the predicate is one thing and must
414
+ * stay one thing: a reference the redirect rewrites is by definition one the coverage check
415
+ * would otherwise have rejected.
416
+ */
417
+ function isPreGroupingReference(node, context) {
418
+ const attrId = node.attributeId;
419
+ return context.aggregateInputAttrIds.has(attrId) && !context.outputAttrIds.has(attrId);
420
+ }
421
+ /**
422
+ * True when ANY column reference in `node`'s subtree is a pre-grouping column of this
423
+ * query — i.e. the expression reads something the AggregateNode's output row does not
424
+ * carry, and therefore needs {@link redirectToGroupKeys} run over it.
425
+ *
426
+ * The opposite question to its neighbour {@link readsOnlyAggregateInput} ("does the
427
+ * WHOLE subtree belong to this query's pre-grouping input", which guards a fingerprint
428
+ * match from hijacking a subquery's own identically-spelled column). This one is
429
+ * {@link redirectPostAggregate}'s *gate*: it answers "is there anything here to
430
+ * redirect at all". A subtree whose references all resolved through the projection /
431
+ * window-output / aggregate-output scopes contains none, and must be left alone.
432
+ *
433
+ * Per reference the test is {@link isPreGroupingReference}. CTE definition bodies are
434
+ * skipped for the same reason {@link redirectToGroupKeys} skips them (see
435
+ * {@link isCteDefinition}).
436
+ */
437
+ function referencesAggregateInput(node, context) {
438
+ if (CapabilityDetectors.isColumnReference(node)) {
439
+ return isPreGroupingReference(node, context);
440
+ }
441
+ for (const child of node.getChildren()) {
442
+ if (isCteDefinition(child))
443
+ continue;
444
+ if (referencesAggregateInput(child, context))
445
+ return true;
446
+ }
447
+ return false;
448
+ }
449
+ /**
450
+ * Boundary check over a GROUPED query's finished plan: no node ABOVE the
451
+ * AggregateNode may reference an aggregate-input attribute id that is absent from
452
+ * the aggregate's output. Called once per grouped query, from the end of
453
+ * `buildSelectStmt`, walking the operator spine from the query root down to — and
454
+ * stopping at — the AggregateNode itself (whose own input subtree legally reads
455
+ * pre-grouping columns). Each scalar expression hanging off a spine operator gets
456
+ * the subquery-aware per-expression check ({@link assertPostAggregateCoverage}).
457
+ *
458
+ * This is what turns "a builder forgot to call {@link redirectPostAggregate}" from a
459
+ * runtime `No row context found for column …` internal error into the user-facing
460
+ * GROUP BY message at plan time — the SELECT-list and ORDER BY escapes both shipped
461
+ * exactly that way. It also rejects, at plan time, a post-aggregate expression that
462
+ * genuinely reads an ungrouped column (directly, or correlated through a subquery).
463
+ *
464
+ * NOTE: the stop condition is node IDENTITY, so it holds only while `buildSelectStmt`
465
+ * *wraps* the aggregate rather than rebuilding it. A future post-aggregate step that
466
+ * rebuilds the spine through `withChildren` would mint a new AggregateNode, the walk
467
+ * would run straight past it into the aggregate's own input, and every legal
468
+ * pre-grouping reference down there (a WHERE conjunct, a join condition) would be
469
+ * rejected with a user-facing GROUP BY error. If such a step is ever added, locate
470
+ * the aggregate by node id instead of by reference.
471
+ *
472
+ * NOTE: one extra walk of the post-aggregate plan per grouped query per prepare, on
473
+ * top of the redirect's own walk (see {@link redirectNode}). Measured end-to-end, not
474
+ * isolated: compiling a six-column grouped query costs ~0.6-0.8 ms against ~0.5 ms for
475
+ * a comparable ungrouped one, so this walk is bounded well below the surrounding
476
+ * planning cost. Revisit if preparing grouped queries ever shows up as slow.
477
+ */
478
+ export function assertGroupedPlanCoverage(node, aggregateNode, context) {
479
+ if (node === aggregateNode)
480
+ return;
481
+ for (const child of node.getChildren()) {
482
+ if (child === aggregateNode || isCteDefinition(child))
483
+ continue;
484
+ if (isRelationalNode(child)) {
485
+ assertGroupedPlanCoverage(child, aggregateNode, context);
486
+ }
487
+ else {
488
+ assertPostAggregateCoverage(child, context);
489
+ }
490
+ }
491
+ }
492
+ /**
493
+ * Raises the GROUP BY coverage error for a post-aggregate expression of a GROUPED
494
+ * query that still reads a pre-grouping column after {@link redirectPostAggregate}
495
+ * has run.
364
496
  *
365
497
  * Distinct from {@link assertGroupByCoverage} in exactly two ways, both required by
366
- * the fact that a window expression may contain a subquery:
498
+ * the fact that a post-aggregate expression may contain a subquery:
367
499
  *
368
500
  * - it descends into relational children, so a correlated reference buried in a
369
501
  * subquery is checked rather than skipped;
@@ -372,10 +504,10 @@ function readsOnlyAggregateInput(node, context) {
372
504
  * or a correlated reference to an enclosing query — is legal here and is left alone.
373
505
  *
374
506
  * The aggregate exemption is likewise narrowed to this query's OWN aggregates; see
375
- * {@link findUngroupedWindowColumnRef}.
507
+ * {@link findUngroupedPostAggregateRef}.
376
508
  */
377
- export function assertGroupedWindowCoverage(node, context) {
378
- const ungrouped = findUngroupedWindowColumnRef(node, context);
509
+ function assertPostAggregateCoverage(node, context) {
510
+ const ungrouped = findUngroupedPostAggregateRef(node, context);
379
511
  if (!ungrouped)
380
512
  return;
381
513
  throw new QuereusError(`Column '${expressionToString(ungrouped.expression)}' must appear in the GROUP BY clause or be used in an aggregate function`, StatusCode.ERROR, undefined, ungrouped.expression.loc?.start.line, ungrouped.expression.loc?.start.column);
@@ -391,19 +523,17 @@ export function assertGroupedWindowCoverage(node, context) {
391
523
  * (select max(wg.b) from wg t))` reads an ungrouped column of this query and must say
392
524
  * so at plan time, not die at runtime with "No row context found for column b".
393
525
  */
394
- function findUngroupedWindowColumnRef(node, context, insideSubquery = false) {
526
+ function findUngroupedPostAggregateRef(node, context, insideSubquery = false) {
395
527
  if (!insideSubquery && CapabilityDetectors.isAggregateFunction(node)) {
396
528
  return null;
397
529
  }
398
530
  if (CapabilityDetectors.isColumnReference(node)) {
399
- const attrId = node.attributeId;
400
- const ungrouped = context.aggregateInputAttrIds.has(attrId) && !context.outputAttrIds.has(attrId);
401
- return ungrouped ? node : null;
531
+ return isPreGroupingReference(node, context) ? node : null;
402
532
  }
403
533
  for (const child of node.getChildren()) {
404
534
  if (isCteDefinition(child))
405
535
  continue;
406
- const found = findUngroupedWindowColumnRef(child, context, insideSubquery || isRelationalNode(child));
536
+ const found = findUngroupedPostAggregateRef(child, context, insideSubquery || isRelationalNode(child));
407
537
  if (found)
408
538
  return found;
409
539
  }
@@ -438,6 +568,14 @@ function validateAggregateProjections(projections, hasAggregates, hasGroupBy, gr
438
568
  * subtree (inner column refs are aggregated), a relational subtree (subqueries
439
569
  * resolve their own scope), or any subtree whose AST fingerprint matches a GROUP BY
440
570
  * expression (the whole subtree is grouped, e.g. SELECT id+1 ... GROUP BY id+1).
571
+ *
572
+ * Not descending into relational children means a correlated subquery in a grouped
573
+ * SELECT list that reads a genuinely UNGROUPED column of this query
574
+ * (`select a, (select count(*) from wg t where t.b = wg.b) … group by a`) is not
575
+ * rejected here. That is fine: the finished-plan check
576
+ * ({@link assertGroupedPlanCoverage}) IS subquery-aware — it can tell a correlated
577
+ * reference to THIS query from the subquery's own columns, off
578
+ * {@link GroupedRedirectContext} — and rejects it at plan time with the same message.
441
579
  */
442
580
  function findUngroupedColumnRef(node, coverage) {
443
581
  if (CapabilityDetectors.isAggregateFunction(node)) {
@@ -465,9 +603,40 @@ function findUngroupedColumnRef(node, coverage) {
465
603
  return null;
466
604
  }
467
605
  /**
468
- * Creates a scope that includes the aggregate output columns
606
+ * The names a grouped query's OUTPUT row can be referred to by, as a scope. This is
607
+ * what a HAVING predicate and (for a grouped, windowed query) a window specification
608
+ * resolve against, so everything registered here is a name those clauses may use:
609
+ *
610
+ * - each GROUP BY key under its own column name, plus its qualified name when the key
611
+ * was written qualified (`group by i.id, c.id` registers `i.id` and `c.id`; the bare
612
+ * `id` they share becomes ambiguous);
613
+ * - each aggregate under its SELECT-list alias;
614
+ * - each grouping key ALSO under the SELECT-list alias of the column that selects it —
615
+ * `select a as k … group by a` makes `k` name the group key, in `over (order by k)`
616
+ * and in `HAVING` alike. Bare, qualified and computed keys all qualify
617
+ * (`select upper(a) as k … group by upper(a)`). This is SQLite-style permissiveness
618
+ * rather than strict SQL, and it matches how an aggregate's alias already behaves.
619
+ *
620
+ * A registered name SHADOWS a same-named base-table column, because anything not
621
+ * registered here falls through to the pre-aggregate select scope (window specs) or to
622
+ * the source-column fallback in {@link buildHavingFilter} (HAVING). So under
623
+ * `select a as b … group by a`, a bare `b` in either clause is the group key, not the
624
+ * table's own `b` column. Deliberate, and the same rule aggregate aliases already had.
625
+ *
626
+ * NOTE: a select-list alias is the LOWEST-precedence name here — it is skipped when a
627
+ * grouping key's own name or an aggregate's alias already claims it. `select a as b,
628
+ * b, count(*) … group by a, b` therefore keeps resolving a bare `b` to the grouping
629
+ * key `b`, matching both SQLite and PostgreSQL (where an alias never outranks a real
630
+ * column outside ORDER BY). Marking such a collision ambiguous instead was considered
631
+ * and rejected: the grouped SELECT list is itself rebuilt against this scope by
632
+ * {@link buildFinalAggregateProjections}, so an ambiguity minted here would reject the
633
+ * bare `b` in the select list too — and a select-list column must never be affected by
634
+ * a sibling's alias. Revisit only if select-list rebuilding stops going through this
635
+ * scope.
469
636
  */
470
- function createAggregateOutputScope(parentScope, aggregateNode, groupByExpressions, aggregates) {
637
+ function createAggregateOutputScope(parentScope, aggregateNode, groupByExpressions, aggregates,
638
+ /** Non-aggregate SELECT-list columns (stars expanded), each carrying its alias. */
639
+ projections) {
471
640
  const aggregateOutputScope = new RegisteredScope(parentScope);
472
641
  const aggregateAttributes = aggregateNode.getAttributes();
473
642
  // Mirror the source-side (FROM/JOIN) naming semantics: a qualified reference
@@ -553,6 +722,32 @@ function createAggregateOutputScope(parentScope, aggregateNode, groupByExpressio
553
722
  aggregateOutputScope.registerSymbol(aliasKey, (exp, s) => new ColumnReferenceNode(s, exp, agg.expression.getType(), attr.id, columnIndex));
554
723
  }
555
724
  });
725
+ // Register each grouping key under the SELECT-list alias of the column that selects
726
+ // it, so `select a as k … group by a` can say `k` in HAVING and in a window
727
+ // specification. Skipped for any alias a grouping key's own name or an aggregate
728
+ // alias already claims (see the NOTE above), and marked ambiguous when two aliases
729
+ // of DIFFERENT grouping keys share one name (`select a as k, b as k … group by a, b`),
730
+ // which nothing else in this scope can arbitrate.
731
+ //
732
+ // The qualified keys count as "already claims" too: a quoted alias may contain a dot
733
+ // (`select wg.a as "wg.a" … group by wg.a`), and registering it would re-register a
734
+ // key this scope already holds — which `registerSymbol` rejects outright, failing a
735
+ // query that plans fine without the alias. See
736
+ // backlog/bug-scope-symbol-keys-collide-with-dotted-column-names for why a flat
737
+ // string key cannot tell a dotted name from a qualified one in the first place.
738
+ const groupKeys = indexGroupKeys(groupByExpressions);
739
+ for (const [aliasKey, keyIndexes] of collectAliasedGroupKeys(projections, groupKeys)) {
740
+ if (bareNameOwners.has(aliasKey) || registeredQualifiedKeys.has(aliasKey))
741
+ continue;
742
+ if (keyIndexes.size > 1) {
743
+ aggregateOutputScope.markAmbiguous(aliasKey);
744
+ continue;
745
+ }
746
+ const [index] = keyIndexes;
747
+ const attr = aggregateAttributes[index];
748
+ const keyExpr = groupByExpressions[index];
749
+ aggregateOutputScope.registerSymbol(aliasKey, (exp, s) => new ColumnReferenceNode(s, exp, keyExpr.getType(), attr.id, index));
750
+ }
556
751
  // Note: the aggregate node advertises exactly its GROUP BY + aggregate columns.
557
752
  // Source columns for HAVING / correlated access are resolved through the runtime
558
753
  // row-descriptor context and the source-column fallback in buildHavingFilter's
@@ -560,9 +755,62 @@ function createAggregateOutputScope(parentScope, aggregateNode, groupByExpressio
560
755
  return aggregateOutputScope;
561
756
  }
562
757
  /**
563
- * Builds HAVING filter clause
758
+ * Every SELECT-list alias that names a grouping key, mapped to the GROUP BY position(s)
759
+ * it could mean. More than one position means two aliases of different keys collide on
760
+ * one name.
761
+ *
762
+ * A projection is matched to its key the same two ways {@link redirectToGroupKeys}
763
+ * matches a window expression: by base attribute id for a column reference (so
764
+ * `select wg.a as k … group by a` and the reverse both land), else by identity
765
+ * fingerprint of the whole expression (so `select upper(a) as k … group by upper(a)`
766
+ * lands). A projection matching no key is skipped — in a grouped query it is an
767
+ * ungrouped SELECT column, which `validateAggregateProjections` has already rejected.
768
+ *
769
+ * Aliases are keyed lowercased, matching every other name in the aggregate output scope.
770
+ * Note that an unaliased `select a` carries no alias and contributes nothing, while a
771
+ * star-expanded column carries the source column's name as its alias — which for a
772
+ * grouping key is the name that key already registers, so it costs nothing.
564
773
  */
565
- function buildHavingFilter(input, havingClause, selectContext, aggregateOutputScope, aggregates, groupByExpressions) {
774
+ function collectAliasedGroupKeys(projections, groupKeys) {
775
+ const aliasTargets = new Map();
776
+ for (const projection of projections) {
777
+ if (!projection.alias)
778
+ continue;
779
+ const index = groupKeyIndexOf(projection.node, groupKeys);
780
+ if (index === undefined)
781
+ continue;
782
+ const aliasKey = projection.alias.toLowerCase();
783
+ let targets = aliasTargets.get(aliasKey);
784
+ if (!targets) {
785
+ targets = new Set();
786
+ aliasTargets.set(aliasKey, targets);
787
+ }
788
+ targets.add(index);
789
+ }
790
+ return aliasTargets;
791
+ }
792
+ /** Which GROUP BY key this SELECT-list expression IS, if any. */
793
+ function groupKeyIndexOf(node, groupKeys) {
794
+ if (CapabilityDetectors.isColumnReference(node)) {
795
+ const byAttrId = groupKeys.byBaseAttrId.get(node.attributeId);
796
+ if (byAttrId !== undefined)
797
+ return byAttrId;
798
+ }
799
+ return groupKeys.byFingerprint.get(expressionToIdentityString(node.expression));
800
+ }
801
+ /**
802
+ * Builds HAVING filter clause.
803
+ *
804
+ * `groupedRedirectContext` is supplied for a GROUPED query. A HAVING reference to a
805
+ * grouping key under a spelling the hybrid scope does not hold (`having wg.a = 'x'`
806
+ * against `group by a`, the key nested in `upper(wg.a)`, a computed key written out
807
+ * again) falls through to a base-table attribute, which happened to read correctly
808
+ * only because this FilterNode sits directly on the AggregateNode's yield where the
809
+ * representative source row is still published. {@link redirectPostAggregate} lands
810
+ * it on the aggregate's own output column instead, so the predicate stays correct
811
+ * wherever the filter ends up relative to a buffering operator.
812
+ */
813
+ function buildHavingFilter(input, havingClause, selectContext, aggregateOutputScope, aggregates, groupByExpressions, groupedRedirectContext) {
566
814
  const aggregateAttributes = input.getAttributes();
567
815
  // Create a hybrid scope that first tries the aggregate output scope,
568
816
  // then falls back to the original source scope for column resolution.
@@ -605,7 +853,7 @@ function buildHavingFilter(input, havingClause, selectContext, aggregateOutputSc
605
853
  };
606
854
  })
607
855
  };
608
- const havingExpression = buildExpression(havingContext, havingClause, true);
856
+ const havingExpression = redirectPostAggregate(buildExpression(havingContext, havingClause, true), groupedRedirectContext, hybridScope);
609
857
  // Reject HAVING references to non-grouped, non-aggregated columns.
610
858
  // With GROUP BY: only GROUP BY columns/expressions and aggregates are allowed.
611
859
  // Without GROUP BY (implicit single group, only reachable here when aggregates
@@ -825,23 +1073,74 @@ function collectOrderByAggregates(orderBy, selectContext, existingAggregates) {
825
1073
  return dedupeNewAggregates(funcExprs, selectContext, existingAggregates);
826
1074
  }
827
1075
  /**
828
- * Returns true if any ORDER BY clause expression contains an aggregate function call.
1076
+ * Decides whether an ungrouped aggregate query's ORDER BY must be sorted ABOVE the
1077
+ * AggregateNode rather than below it (the pre-aggregate sort extension, which is what
1078
+ * makes `select group_concat(b) from t order by a` concatenate in `a` order).
1079
+ *
1080
+ * Three spellings need the post-aggregate placement, because none of them can be
1081
+ * evaluated against the per-input rows the pre-aggregate sort sees:
1082
+ *
1083
+ * - a spelled-out aggregate call (`order by count(*)`);
1084
+ * - a positional reference standing for one — the literal `1` in `select count(*) as c
1085
+ * from t order by 1` contains no aggregate itself, so it is resolved against the
1086
+ * SELECT list FIRST. Without that the query fails with "Aggregate function count not
1087
+ * allowed in this context";
1088
+ * - a bare (unqualified) column name that is a SELECT-list alias (`select count(*) as c
1089
+ * from t order by c`). The alias only ever exists in the aggregate/projection output
1090
+ * scope, so a pre-aggregate sort fails with "Column not found: c".
829
1091
  *
830
- * A positional reference is resolved against the SELECT list FIRST: the literal `1`
831
- * in `select count(*) as c from t order by 1` contains no aggregate itself, but it
832
- * stands for one. Without this the query would take the pre-aggregate sort path and
833
- * fail with "Aggregate function count not allowed in this context"; with it, it
834
- * routes to the post-aggregate ORDER BY exactly like the spelled-out
835
- * `order by count(*)`.
1092
+ * The alias arm makes a select-list alias outrank a same-named source column in an
1093
+ * aggregate ORDER BY, matching SQLite and what the non-aggregate and grouped paths
1094
+ * already do. Qualified names (`order by t.c`) are excluded and keep resolving to the
1095
+ * table's column.
836
1096
  */
837
- function orderByContainsAggregates(orderBy, selectContext, selectList) {
1097
+ function orderByNeedsPostAggregateSort(orderBy, selectContext, selectList) {
838
1098
  if (!orderBy || orderBy.length === 0)
839
1099
  return false;
1100
+ const aliases = selectListAliases(selectList);
840
1101
  return orderBy.some(clause => {
841
1102
  const entry = resolveOrdinalReference(clause.expr, selectList, 'ORDER BY');
842
- return containsAggregateFunction(entry?.expr ?? clause.expr, selectContext);
1103
+ if (containsAggregateFunction(entry?.expr ?? clause.expr, selectContext))
1104
+ return true;
1105
+ return mentionsSelectListAlias(clause.expr, aliases);
843
1106
  });
844
1107
  }
1108
+ /** Lower-cased set of the `as` aliases the SELECT list wrote (stars contribute none). */
1109
+ function selectListAliases(selectList) {
1110
+ const aliases = new Set();
1111
+ for (const entry of selectList) {
1112
+ if (entry.alias)
1113
+ aliases.add(entry.alias.toLowerCase());
1114
+ }
1115
+ return aliases;
1116
+ }
1117
+ /**
1118
+ * True when `expr` mentions any of `aliases` as an unqualified column reference.
1119
+ *
1120
+ * NOTE: `walkAstNodes` descends into subquery operands, so `order by (select … where x
1121
+ * = c)` counts as mentioning `c` even when that `c` is the subquery's own column. The
1122
+ * over-approximation only costs the pre-aggregate ordering extension on a one-row
1123
+ * result; a subquery-aware walk is not worth hand-rolling for it.
1124
+ *
1125
+ * `type: 'column'` is not unique to {@link AST.ColumnExpr}: a subquery's SELECT-list
1126
+ * {@link AST.ResultColumnExpr} carries the same tag with an `expr`/`alias` shape and no
1127
+ * `name`, and the reflective walk reaches those too. Match on the `name` string rather
1128
+ * than on the tag alone.
1129
+ */
1130
+ function mentionsSelectListAlias(expr, aliases) {
1131
+ if (aliases.size === 0)
1132
+ return false;
1133
+ for (const node of walkAstNodes(expr)) {
1134
+ if (node.type !== 'column')
1135
+ continue;
1136
+ const col = node;
1137
+ if (typeof col.name !== 'string' || col.table)
1138
+ continue;
1139
+ if (aliases.has(col.name.toLowerCase()))
1140
+ return true;
1141
+ }
1142
+ return false;
1143
+ }
845
1144
  /**
846
1145
  * Given a list of aggregate function call AST nodes, builds aggregate plan nodes
847
1146
  * for the entries that are not already present in `existingAggregates` (matched by
@@ -871,9 +1170,15 @@ function dedupeNewAggregates(funcExprs, selectContext, existingAggregates) {
871
1170
  return newAggregates;
872
1171
  }
873
1172
  /**
874
- * Builds final projections for the complete SELECT list in aggregate context
1173
+ * Builds final projections for the complete SELECT list in aggregate context.
1174
+ *
1175
+ * `groupedContext` is supplied whenever the query HAS a GROUP BY (it is `undefined`
1176
+ * for an aggregate query without one, which has no grouping keys to redirect onto).
1177
+ * It is what makes a select-list reference to a grouping key name the AggregateNode's
1178
+ * OWN group output column, whatever spelling the reference used — see
1179
+ * {@link redirectPostAggregate} and the note on the rebuild below.
875
1180
  */
876
- export function buildFinalAggregateProjections(stmt, selectContext, aggregateOutputScope, aggregateNode, aggregates, groupByExpressions, starProjectionsByColumn = new Map()) {
1181
+ export function buildFinalAggregateProjections(stmt, selectContext, aggregateOutputScope, aggregateNode, aggregates, groupByExpressions, starProjectionsByColumn = new Map(), groupedContext) {
877
1182
  const finalProjections = [];
878
1183
  const aggregateAttributes = aggregateNode.getAttributes();
879
1184
  // Build context with aggregates so buildFunctionCall can resolve aggregate references
@@ -902,7 +1207,11 @@ export function buildFinalAggregateProjections(stmt, selectContext, aggregateOut
902
1207
  // The same index also maps bare source columns that are themselves group keys
903
1208
  // straight to the aggregate's group output column, which is what expands
904
1209
  // `SELECT *` in a grouped query in source-column order rather than GROUP BY order.
905
- const groupKeys = indexGroupKeys(groupByExpressions);
1210
+ //
1211
+ // A grouped query's `groupedContext` already carries this index; deriving it twice
1212
+ // for one query is how the two copies drift. Only an aggregate query with no GROUP BY
1213
+ // reaches the fallback, and its `groupByExpressions` is empty.
1214
+ const groupKeys = groupedContext?.groupKeys ?? indexGroupKeys(groupByExpressions);
906
1215
  for (const column of stmt.columns) {
907
1216
  if (column.type === 'all') {
908
1217
  for (const starProj of starProjectionsByColumn.get(column) ?? []) {
@@ -944,7 +1253,22 @@ export function buildFinalAggregateProjections(stmt, selectContext, aggregateOut
944
1253
  scope: aggregateOutputScope,
945
1254
  aggregates: aggregatesContext
946
1255
  };
947
- const scalarNode = buildExpression(finalContext, column.expr, true);
1256
+ const builtNode = buildExpression(finalContext, column.expr, true);
1257
+ // `aggregateOutputScope` holds a grouping key only under the names the query
1258
+ // literally wrote, so every other legal spelling — a qualifier the GROUP BY did
1259
+ // not use (`select wg.a … group by a`), a FROM-alias qualifier, a key nested in
1260
+ // a bigger expression (`upper(wg.a)`) — falls through to the pre-aggregate
1261
+ // scope and binds to a BASE-TABLE attribute the grouped row does not carry.
1262
+ // Redirect each such subtree onto the aggregate's own group output column,
1263
+ // exactly as the window phase does for a window specification.
1264
+ //
1265
+ // For EVERY grouped query, not only windowed ones: reading the base attribute
1266
+ // off the group's representative source row is right only while this projection
1267
+ // is the aggregate's adjacent consumer, and a WindowNode buffering in between
1268
+ // removes that (docs/runtime.md § Corollary: a published source row reaches only
1269
+ // the adjacent consumer). One query shape must not bind two ways depending on
1270
+ // an unrelated clause.
1271
+ const scalarNode = redirectPostAggregate(builtNode, groupedContext, aggregateOutputScope);
948
1272
  let attrId = undefined;
949
1273
  if (CapabilityDetectors.isColumnReference(scalarNode)) {
950
1274
  attrId = scalarNode.attributeId;