@quereus/quereus 4.3.2 → 4.4.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 (396) hide show
  1. package/README.md +5 -0
  2. package/dist/src/common/types.d.ts +9 -0
  3. package/dist/src/common/types.d.ts.map +1 -1
  4. package/dist/src/common/types.js.map +1 -1
  5. package/dist/src/core/database-events.d.ts +49 -0
  6. package/dist/src/core/database-events.d.ts.map +1 -1
  7. package/dist/src/core/database-events.js +148 -0
  8. package/dist/src/core/database-events.js.map +1 -1
  9. package/dist/src/core/database-external-changes.d.ts +2 -2
  10. package/dist/src/core/database-external-changes.d.ts.map +1 -1
  11. package/dist/src/core/database-external-changes.js +12 -9
  12. package/dist/src/core/database-external-changes.js.map +1 -1
  13. package/dist/src/core/database-materialized-views-apply.d.ts +156 -14
  14. package/dist/src/core/database-materialized-views-apply.d.ts.map +1 -1
  15. package/dist/src/core/database-materialized-views-apply.js +370 -65
  16. package/dist/src/core/database-materialized-views-apply.js.map +1 -1
  17. package/dist/src/core/database-materialized-views-plan-builders.d.ts +66 -1
  18. package/dist/src/core/database-materialized-views-plan-builders.d.ts.map +1 -1
  19. package/dist/src/core/database-materialized-views-plan-builders.js +368 -8
  20. package/dist/src/core/database-materialized-views-plan-builders.js.map +1 -1
  21. package/dist/src/core/database-materialized-views-plans.d.ts +257 -30
  22. package/dist/src/core/database-materialized-views-plans.d.ts.map +1 -1
  23. package/dist/src/core/database-materialized-views.d.ts +99 -42
  24. package/dist/src/core/database-materialized-views.d.ts.map +1 -1
  25. package/dist/src/core/database-materialized-views.js +198 -121
  26. package/dist/src/core/database-materialized-views.js.map +1 -1
  27. package/dist/src/core/database.d.ts +5 -0
  28. package/dist/src/core/database.d.ts.map +1 -1
  29. package/dist/src/core/database.js +41 -18
  30. package/dist/src/core/database.js.map +1 -1
  31. package/dist/src/core/internal-statement-cache.d.ts +18 -0
  32. package/dist/src/core/internal-statement-cache.d.ts.map +1 -0
  33. package/dist/src/core/internal-statement-cache.js +211 -0
  34. package/dist/src/core/internal-statement-cache.js.map +1 -0
  35. package/dist/src/func/builtins/aggregate.d.ts +13 -12
  36. package/dist/src/func/builtins/aggregate.d.ts.map +1 -1
  37. package/dist/src/func/builtins/aggregate.js +146 -62
  38. package/dist/src/func/builtins/aggregate.js.map +1 -1
  39. package/dist/src/func/builtins/builtin-window-functions.d.ts.map +1 -1
  40. package/dist/src/func/builtins/builtin-window-functions.js +63 -48
  41. package/dist/src/func/builtins/builtin-window-functions.js.map +1 -1
  42. package/dist/src/func/registration.d.ts +19 -1
  43. package/dist/src/func/registration.d.ts.map +1 -1
  44. package/dist/src/func/registration.js +41 -0
  45. package/dist/src/func/registration.js.map +1 -1
  46. package/dist/src/index.d.ts +8 -8
  47. package/dist/src/index.d.ts.map +1 -1
  48. package/dist/src/index.js +12 -11
  49. package/dist/src/index.js.map +1 -1
  50. package/dist/src/parser/parser.d.ts +6 -0
  51. package/dist/src/parser/parser.d.ts.map +1 -1
  52. package/dist/src/parser/parser.js +45 -34
  53. package/dist/src/parser/parser.js.map +1 -1
  54. package/dist/src/planner/analysis/comparison-collation.d.ts +2 -0
  55. package/dist/src/planner/analysis/comparison-collation.d.ts.map +1 -1
  56. package/dist/src/planner/analysis/comparison-collation.js +1 -1
  57. package/dist/src/planner/analysis/comparison-collation.js.map +1 -1
  58. package/dist/src/planner/analysis/equi-correlation.d.ts +26 -0
  59. package/dist/src/planner/analysis/equi-correlation.d.ts.map +1 -0
  60. package/dist/src/planner/analysis/equi-correlation.js +62 -0
  61. package/dist/src/planner/analysis/equi-correlation.js.map +1 -0
  62. package/dist/src/planner/analysis/expression-fingerprint.d.ts.map +1 -1
  63. package/dist/src/planner/analysis/expression-fingerprint.js +29 -0
  64. package/dist/src/planner/analysis/expression-fingerprint.js.map +1 -1
  65. package/dist/src/planner/analysis/query-rewrite-matcher.d.ts +60 -18
  66. package/dist/src/planner/analysis/query-rewrite-matcher.d.ts.map +1 -1
  67. package/dist/src/planner/analysis/query-rewrite-matcher.js +95 -42
  68. package/dist/src/planner/analysis/query-rewrite-matcher.js.map +1 -1
  69. package/dist/src/planner/analysis/sat-checker.d.ts.map +1 -1
  70. package/dist/src/planner/analysis/sat-checker.js +43 -4
  71. package/dist/src/planner/analysis/sat-checker.js.map +1 -1
  72. package/dist/src/planner/analysis/scalar-subqueries.d.ts +28 -0
  73. package/dist/src/planner/analysis/scalar-subqueries.d.ts.map +1 -0
  74. package/dist/src/planner/analysis/scalar-subqueries.js +60 -0
  75. package/dist/src/planner/analysis/scalar-subqueries.js.map +1 -0
  76. package/dist/src/planner/building/delete.d.ts.map +1 -1
  77. package/dist/src/planner/building/delete.js +10 -3
  78. package/dist/src/planner/building/delete.js.map +1 -1
  79. package/dist/src/planner/building/expression.d.ts.map +1 -1
  80. package/dist/src/planner/building/expression.js +87 -6
  81. package/dist/src/planner/building/expression.js.map +1 -1
  82. package/dist/src/planner/building/foreign-key-builder.d.ts +44 -1
  83. package/dist/src/planner/building/foreign-key-builder.d.ts.map +1 -1
  84. package/dist/src/planner/building/foreign-key-builder.js +54 -0
  85. package/dist/src/planner/building/foreign-key-builder.js.map +1 -1
  86. package/dist/src/planner/building/insert.d.ts.map +1 -1
  87. package/dist/src/planner/building/insert.js +9 -12
  88. package/dist/src/planner/building/insert.js.map +1 -1
  89. package/dist/src/planner/building/select.d.ts.map +1 -1
  90. package/dist/src/planner/building/select.js +15 -1
  91. package/dist/src/planner/building/select.js.map +1 -1
  92. package/dist/src/planner/building/update.d.ts.map +1 -1
  93. package/dist/src/planner/building/update.js +13 -4
  94. package/dist/src/planner/building/update.js.map +1 -1
  95. package/dist/src/planner/building/with.d.ts.map +1 -1
  96. package/dist/src/planner/building/with.js +5 -8
  97. package/dist/src/planner/building/with.js.map +1 -1
  98. package/dist/src/planner/cache/materialization-advisory.d.ts +33 -0
  99. package/dist/src/planner/cache/materialization-advisory.d.ts.map +1 -1
  100. package/dist/src/planner/cache/materialization-advisory.js +164 -28
  101. package/dist/src/planner/cache/materialization-advisory.js.map +1 -1
  102. package/dist/src/planner/cache/reference-graph.d.ts +0 -4
  103. package/dist/src/planner/cache/reference-graph.d.ts.map +1 -1
  104. package/dist/src/planner/cache/reference-graph.js +5 -13
  105. package/dist/src/planner/cache/reference-graph.js.map +1 -1
  106. package/dist/src/planner/cost/index.d.ts +28 -7
  107. package/dist/src/planner/cost/index.d.ts.map +1 -1
  108. package/dist/src/planner/cost/index.js +26 -0
  109. package/dist/src/planner/cost/index.js.map +1 -1
  110. package/dist/src/planner/framework/pass.js +3 -3
  111. package/dist/src/planner/nodes/cache-node.d.ts +18 -2
  112. package/dist/src/planner/nodes/cache-node.d.ts.map +1 -1
  113. package/dist/src/planner/nodes/cache-node.js +42 -3
  114. package/dist/src/planner/nodes/cache-node.js.map +1 -1
  115. package/dist/src/planner/nodes/cte-node.d.ts +15 -1
  116. package/dist/src/planner/nodes/cte-node.d.ts.map +1 -1
  117. package/dist/src/planner/nodes/cte-node.js +12 -2
  118. package/dist/src/planner/nodes/cte-node.js.map +1 -1
  119. package/dist/src/planner/nodes/dml-executor-node.d.ts +0 -9
  120. package/dist/src/planner/nodes/dml-executor-node.d.ts.map +1 -1
  121. package/dist/src/planner/nodes/dml-executor-node.js.map +1 -1
  122. package/dist/src/planner/nodes/recursive-cte-node.d.ts +30 -1
  123. package/dist/src/planner/nodes/recursive-cte-node.d.ts.map +1 -1
  124. package/dist/src/planner/nodes/recursive-cte-node.js +30 -3
  125. package/dist/src/planner/nodes/recursive-cte-node.js.map +1 -1
  126. package/dist/src/planner/nodes/scalar.d.ts.map +1 -1
  127. package/dist/src/planner/nodes/scalar.js +26 -3
  128. package/dist/src/planner/nodes/scalar.js.map +1 -1
  129. package/dist/src/planner/optimizer.d.ts.map +1 -1
  130. package/dist/src/planner/optimizer.js +129 -8
  131. package/dist/src/planner/optimizer.js.map +1 -1
  132. package/dist/src/planner/rules/access/rule-select-access-path.d.ts.map +1 -1
  133. package/dist/src/planner/rules/access/rule-select-access-path.js +43 -18
  134. package/dist/src/planner/rules/access/rule-select-access-path.js.map +1 -1
  135. package/dist/src/planner/rules/cache/rule-cte-optimization.d.ts.map +1 -1
  136. package/dist/src/planner/rules/cache/rule-cte-optimization.js +8 -1
  137. package/dist/src/planner/rules/cache/rule-cte-optimization.js.map +1 -1
  138. package/dist/src/planner/rules/cache/rule-materialized-view-rewrite.d.ts.map +1 -1
  139. package/dist/src/planner/rules/cache/rule-materialized-view-rewrite.js +72 -54
  140. package/dist/src/planner/rules/cache/rule-materialized-view-rewrite.js.map +1 -1
  141. package/dist/src/planner/rules/cache/rule-mutating-subquery-cache.d.ts.map +1 -1
  142. package/dist/src/planner/rules/cache/rule-mutating-subquery-cache.js +6 -0
  143. package/dist/src/planner/rules/cache/rule-mutating-subquery-cache.js.map +1 -1
  144. package/dist/src/planner/rules/cache/rule-nested-loop-right-cache.d.ts +25 -0
  145. package/dist/src/planner/rules/cache/rule-nested-loop-right-cache.d.ts.map +1 -0
  146. package/dist/src/planner/rules/cache/rule-nested-loop-right-cache.js +176 -0
  147. package/dist/src/planner/rules/cache/rule-nested-loop-right-cache.js.map +1 -0
  148. package/dist/src/planner/rules/cache/rule-scalar-subquery-cache.d.ts +31 -0
  149. package/dist/src/planner/rules/cache/rule-scalar-subquery-cache.d.ts.map +1 -0
  150. package/dist/src/planner/rules/cache/rule-scalar-subquery-cache.js +68 -0
  151. package/dist/src/planner/rules/cache/rule-scalar-subquery-cache.js.map +1 -0
  152. package/dist/src/planner/rules/join/rule-fanout-lookup-join.d.ts.map +1 -1
  153. package/dist/src/planner/rules/join/rule-fanout-lookup-join.js +1 -54
  154. package/dist/src/planner/rules/join/rule-fanout-lookup-join.js.map +1 -1
  155. package/dist/src/planner/rules/predicate/rule-predicate-inference-equivalence.js +8 -1
  156. package/dist/src/planner/rules/predicate/rule-predicate-inference-equivalence.js.map +1 -1
  157. package/dist/src/planner/rules/subquery/rule-scalar-agg-decorrelation.d.ts +191 -0
  158. package/dist/src/planner/rules/subquery/rule-scalar-agg-decorrelation.d.ts.map +1 -0
  159. package/dist/src/planner/rules/subquery/rule-scalar-agg-decorrelation.js +627 -0
  160. package/dist/src/planner/rules/subquery/rule-scalar-agg-decorrelation.js.map +1 -0
  161. package/dist/src/planner/rules/subquery/rule-subquery-decorrelation.d.ts +10 -1
  162. package/dist/src/planner/rules/subquery/rule-subquery-decorrelation.d.ts.map +1 -1
  163. package/dist/src/planner/rules/subquery/rule-subquery-decorrelation.js +242 -51
  164. package/dist/src/planner/rules/subquery/rule-subquery-decorrelation.js.map +1 -1
  165. package/dist/src/runtime/cache/shared-cache.d.ts +7 -0
  166. package/dist/src/runtime/cache/shared-cache.d.ts.map +1 -1
  167. package/dist/src/runtime/cache/shared-cache.js +34 -1
  168. package/dist/src/runtime/cache/shared-cache.js.map +1 -1
  169. package/dist/src/runtime/deferred-constraint-queue.d.ts.map +1 -1
  170. package/dist/src/runtime/deferred-constraint-queue.js +10 -0
  171. package/dist/src/runtime/deferred-constraint-queue.js.map +1 -1
  172. package/dist/src/runtime/emit/aggregate-setup.d.ts +49 -0
  173. package/dist/src/runtime/emit/aggregate-setup.d.ts.map +1 -0
  174. package/dist/src/runtime/emit/aggregate-setup.js +99 -0
  175. package/dist/src/runtime/emit/aggregate-setup.js.map +1 -0
  176. package/dist/src/runtime/emit/aggregate.d.ts +2 -3
  177. package/dist/src/runtime/emit/aggregate.d.ts.map +1 -1
  178. package/dist/src/runtime/emit/aggregate.js +10 -84
  179. package/dist/src/runtime/emit/aggregate.js.map +1 -1
  180. package/dist/src/runtime/emit/alter-table.d.ts.map +1 -1
  181. package/dist/src/runtime/emit/alter-table.js +348 -165
  182. package/dist/src/runtime/emit/alter-table.js.map +1 -1
  183. package/dist/src/runtime/emit/asof-scan.d.ts.map +1 -1
  184. package/dist/src/runtime/emit/asof-scan.js +6 -0
  185. package/dist/src/runtime/emit/asof-scan.js.map +1 -1
  186. package/dist/src/runtime/emit/between.d.ts.map +1 -1
  187. package/dist/src/runtime/emit/between.js +32 -3
  188. package/dist/src/runtime/emit/between.js.map +1 -1
  189. package/dist/src/runtime/emit/binary.d.ts.map +1 -1
  190. package/dist/src/runtime/emit/binary.js +22 -2
  191. package/dist/src/runtime/emit/binary.js.map +1 -1
  192. package/dist/src/runtime/emit/bloom-join.d.ts.map +1 -1
  193. package/dist/src/runtime/emit/bloom-join.js +24 -3
  194. package/dist/src/runtime/emit/bloom-join.js.map +1 -1
  195. package/dist/src/runtime/emit/cache.d.ts +7 -3
  196. package/dist/src/runtime/emit/cache.d.ts.map +1 -1
  197. package/dist/src/runtime/emit/cache.js +23 -6
  198. package/dist/src/runtime/emit/cache.js.map +1 -1
  199. package/dist/src/runtime/emit/cast.d.ts.map +1 -1
  200. package/dist/src/runtime/emit/cast.js +10 -23
  201. package/dist/src/runtime/emit/cast.js.map +1 -1
  202. package/dist/src/runtime/emit/constraint-check.d.ts.map +1 -1
  203. package/dist/src/runtime/emit/constraint-check.js +41 -44
  204. package/dist/src/runtime/emit/constraint-check.js.map +1 -1
  205. package/dist/src/runtime/emit/create-view.d.ts.map +1 -1
  206. package/dist/src/runtime/emit/create-view.js +6 -0
  207. package/dist/src/runtime/emit/create-view.js.map +1 -1
  208. package/dist/src/runtime/emit/cte.d.ts.map +1 -1
  209. package/dist/src/runtime/emit/cte.js +43 -20
  210. package/dist/src/runtime/emit/cte.js.map +1 -1
  211. package/dist/src/runtime/emit/distinct.d.ts.map +1 -1
  212. package/dist/src/runtime/emit/distinct.js +6 -3
  213. package/dist/src/runtime/emit/distinct.js.map +1 -1
  214. package/dist/src/runtime/emit/dml-executor.d.ts.map +1 -1
  215. package/dist/src/runtime/emit/dml-executor.js +241 -115
  216. package/dist/src/runtime/emit/dml-executor.js.map +1 -1
  217. package/dist/src/runtime/emit/hash-aggregate.d.ts.map +1 -1
  218. package/dist/src/runtime/emit/hash-aggregate.js +16 -65
  219. package/dist/src/runtime/emit/hash-aggregate.js.map +1 -1
  220. package/dist/src/runtime/emit/insert.d.ts.map +1 -1
  221. package/dist/src/runtime/emit/insert.js +18 -7
  222. package/dist/src/runtime/emit/insert.js.map +1 -1
  223. package/dist/src/runtime/emit/join.js +5 -0
  224. package/dist/src/runtime/emit/join.js.map +1 -1
  225. package/dist/src/runtime/emit/materialized-view-helpers.d.ts +14 -1
  226. package/dist/src/runtime/emit/materialized-view-helpers.d.ts.map +1 -1
  227. package/dist/src/runtime/emit/materialized-view-helpers.js +38 -7
  228. package/dist/src/runtime/emit/materialized-view-helpers.js.map +1 -1
  229. package/dist/src/runtime/emit/materialized-view.d.ts.map +1 -1
  230. package/dist/src/runtime/emit/materialized-view.js +47 -6
  231. package/dist/src/runtime/emit/materialized-view.js.map +1 -1
  232. package/dist/src/runtime/emit/merge-join.d.ts.map +1 -1
  233. package/dist/src/runtime/emit/merge-join.js +18 -8
  234. package/dist/src/runtime/emit/merge-join.js.map +1 -1
  235. package/dist/src/runtime/emit/recursive-cte.d.ts.map +1 -1
  236. package/dist/src/runtime/emit/recursive-cte.js +59 -1
  237. package/dist/src/runtime/emit/recursive-cte.js.map +1 -1
  238. package/dist/src/runtime/emit/set-object-tags.d.ts +8 -0
  239. package/dist/src/runtime/emit/set-object-tags.d.ts.map +1 -1
  240. package/dist/src/runtime/emit/set-object-tags.js +8 -0
  241. package/dist/src/runtime/emit/set-object-tags.js.map +1 -1
  242. package/dist/src/runtime/emit/set-operation.d.ts.map +1 -1
  243. package/dist/src/runtime/emit/set-operation.js +7 -4
  244. package/dist/src/runtime/emit/set-operation.js.map +1 -1
  245. package/dist/src/runtime/emit/sort.d.ts.map +1 -1
  246. package/dist/src/runtime/emit/sort.js +5 -2
  247. package/dist/src/runtime/emit/sort.js.map +1 -1
  248. package/dist/src/runtime/emit/subquery.d.ts.map +1 -1
  249. package/dist/src/runtime/emit/subquery.js +113 -13
  250. package/dist/src/runtime/emit/subquery.js.map +1 -1
  251. package/dist/src/runtime/emit/temporal-arithmetic.d.ts +7 -0
  252. package/dist/src/runtime/emit/temporal-arithmetic.d.ts.map +1 -1
  253. package/dist/src/runtime/emit/temporal-arithmetic.js +15 -7
  254. package/dist/src/runtime/emit/temporal-arithmetic.js.map +1 -1
  255. package/dist/src/runtime/emit/update.d.ts.map +1 -1
  256. package/dist/src/runtime/emit/update.js +44 -3
  257. package/dist/src/runtime/emit/update.js.map +1 -1
  258. package/dist/src/runtime/emit/window.d.ts.map +1 -1
  259. package/dist/src/runtime/emit/window.js +72 -55
  260. package/dist/src/runtime/emit/window.js.map +1 -1
  261. package/dist/src/runtime/foreign-key-actions.d.ts +36 -1
  262. package/dist/src/runtime/foreign-key-actions.d.ts.map +1 -1
  263. package/dist/src/runtime/foreign-key-actions.js +238 -116
  264. package/dist/src/runtime/foreign-key-actions.js.map +1 -1
  265. package/dist/src/runtime/parallel-driver.d.ts +20 -1
  266. package/dist/src/runtime/parallel-driver.d.ts.map +1 -1
  267. package/dist/src/runtime/parallel-driver.js +23 -1
  268. package/dist/src/runtime/parallel-driver.js.map +1 -1
  269. package/dist/src/runtime/types.d.ts +54 -0
  270. package/dist/src/runtime/types.d.ts.map +1 -1
  271. package/dist/src/runtime/types.js.map +1 -1
  272. package/dist/src/schema/catalog-persistability.d.ts +58 -0
  273. package/dist/src/schema/catalog-persistability.d.ts.map +1 -0
  274. package/dist/src/schema/catalog-persistability.js +87 -0
  275. package/dist/src/schema/catalog-persistability.js.map +1 -0
  276. package/dist/src/schema/catalog.d.ts +17 -1
  277. package/dist/src/schema/catalog.d.ts.map +1 -1
  278. package/dist/src/schema/catalog.js +25 -3
  279. package/dist/src/schema/catalog.js.map +1 -1
  280. package/dist/src/schema/constraint-builder.d.ts +20 -22
  281. package/dist/src/schema/constraint-builder.d.ts.map +1 -1
  282. package/dist/src/schema/constraint-builder.js +43 -36
  283. package/dist/src/schema/constraint-builder.js.map +1 -1
  284. package/dist/src/schema/ddl-generator.d.ts +23 -0
  285. package/dist/src/schema/ddl-generator.d.ts.map +1 -1
  286. package/dist/src/schema/ddl-generator.js +27 -0
  287. package/dist/src/schema/ddl-generator.js.map +1 -1
  288. package/dist/src/schema/function.d.ts +109 -1
  289. package/dist/src/schema/function.d.ts.map +1 -1
  290. package/dist/src/schema/function.js.map +1 -1
  291. package/dist/src/schema/manager.d.ts +28 -0
  292. package/dist/src/schema/manager.d.ts.map +1 -1
  293. package/dist/src/schema/manager.js +82 -14
  294. package/dist/src/schema/manager.js.map +1 -1
  295. package/dist/src/schema/schema-differ.d.ts.map +1 -1
  296. package/dist/src/schema/schema-differ.js +51 -12
  297. package/dist/src/schema/schema-differ.js.map +1 -1
  298. package/dist/src/schema/unique-enforcement.d.ts +31 -0
  299. package/dist/src/schema/unique-enforcement.d.ts.map +1 -1
  300. package/dist/src/schema/unique-enforcement.js +37 -1
  301. package/dist/src/schema/unique-enforcement.js.map +1 -1
  302. package/dist/src/schema/window-function.d.ts +30 -10
  303. package/dist/src/schema/window-function.d.ts.map +1 -1
  304. package/dist/src/schema/window-function.js +20 -16
  305. package/dist/src/schema/window-function.js.map +1 -1
  306. package/dist/src/types/cast-semantics.d.ts +43 -0
  307. package/dist/src/types/cast-semantics.d.ts.map +1 -0
  308. package/dist/src/types/cast-semantics.js +59 -0
  309. package/dist/src/types/cast-semantics.js.map +1 -0
  310. package/dist/src/types/index.d.ts +1 -1
  311. package/dist/src/types/index.d.ts.map +1 -1
  312. package/dist/src/types/index.js +1 -1
  313. package/dist/src/types/index.js.map +1 -1
  314. package/dist/src/types/json-type.d.ts.map +1 -1
  315. package/dist/src/types/json-type.js +31 -10
  316. package/dist/src/types/json-type.js.map +1 -1
  317. package/dist/src/types/logical-type.d.ts +22 -0
  318. package/dist/src/types/logical-type.d.ts.map +1 -1
  319. package/dist/src/types/logical-type.js.map +1 -1
  320. package/dist/src/types/temporal-types.d.ts.map +1 -1
  321. package/dist/src/types/temporal-types.js +33 -11
  322. package/dist/src/types/temporal-types.js.map +1 -1
  323. package/dist/src/types/validation.d.ts +39 -1
  324. package/dist/src/types/validation.d.ts.map +1 -1
  325. package/dist/src/types/validation.js +60 -0
  326. package/dist/src/types/validation.js.map +1 -1
  327. package/dist/src/util/ast-spine-clone.d.ts +23 -0
  328. package/dist/src/util/ast-spine-clone.d.ts.map +1 -0
  329. package/dist/src/util/ast-spine-clone.js +41 -0
  330. package/dist/src/util/ast-spine-clone.js.map +1 -0
  331. package/dist/src/util/coercion.d.ts +13 -0
  332. package/dist/src/util/coercion.d.ts.map +1 -1
  333. package/dist/src/util/coercion.js +10 -1
  334. package/dist/src/util/coercion.js.map +1 -1
  335. package/dist/src/util/comparison.d.ts +89 -0
  336. package/dist/src/util/comparison.d.ts.map +1 -1
  337. package/dist/src/util/comparison.js +156 -27
  338. package/dist/src/util/comparison.js.map +1 -1
  339. package/dist/src/vtab/manifest.d.ts +7 -0
  340. package/dist/src/vtab/manifest.d.ts.map +1 -1
  341. package/dist/src/vtab/memory/connection.d.ts +14 -1
  342. package/dist/src/vtab/memory/connection.d.ts.map +1 -1
  343. package/dist/src/vtab/memory/connection.js +20 -2
  344. package/dist/src/vtab/memory/connection.js.map +1 -1
  345. package/dist/src/vtab/memory/layer/base.d.ts +6 -1
  346. package/dist/src/vtab/memory/layer/base.d.ts.map +1 -1
  347. package/dist/src/vtab/memory/layer/base.js +14 -5
  348. package/dist/src/vtab/memory/layer/base.js.map +1 -1
  349. package/dist/src/vtab/memory/layer/connection.d.ts +17 -0
  350. package/dist/src/vtab/memory/layer/connection.d.ts.map +1 -1
  351. package/dist/src/vtab/memory/layer/connection.js +26 -0
  352. package/dist/src/vtab/memory/layer/connection.js.map +1 -1
  353. package/dist/src/vtab/memory/layer/manager.d.ts +96 -13
  354. package/dist/src/vtab/memory/layer/manager.d.ts.map +1 -1
  355. package/dist/src/vtab/memory/layer/manager.js +595 -119
  356. package/dist/src/vtab/memory/layer/manager.js.map +1 -1
  357. package/dist/src/vtab/memory/layer/plan-filter.d.ts +41 -20
  358. package/dist/src/vtab/memory/layer/plan-filter.d.ts.map +1 -1
  359. package/dist/src/vtab/memory/layer/plan-filter.js +57 -27
  360. package/dist/src/vtab/memory/layer/plan-filter.js.map +1 -1
  361. package/dist/src/vtab/memory/layer/row-convert.d.ts +21 -0
  362. package/dist/src/vtab/memory/layer/row-convert.d.ts.map +1 -0
  363. package/dist/src/vtab/memory/layer/row-convert.js +30 -0
  364. package/dist/src/vtab/memory/layer/row-convert.js.map +1 -0
  365. package/dist/src/vtab/memory/layer/scan-layer.d.ts +6 -3
  366. package/dist/src/vtab/memory/layer/scan-layer.d.ts.map +1 -1
  367. package/dist/src/vtab/memory/layer/scan-layer.js +69 -36
  368. package/dist/src/vtab/memory/layer/scan-layer.js.map +1 -1
  369. package/dist/src/vtab/memory/layer/transaction.d.ts +127 -13
  370. package/dist/src/vtab/memory/layer/transaction.d.ts.map +1 -1
  371. package/dist/src/vtab/memory/layer/transaction.js +214 -65
  372. package/dist/src/vtab/memory/layer/transaction.js.map +1 -1
  373. package/dist/src/vtab/memory/module.d.ts.map +1 -1
  374. package/dist/src/vtab/memory/module.js +9 -2
  375. package/dist/src/vtab/memory/module.js.map +1 -1
  376. package/dist/src/vtab/memory/table.d.ts.map +1 -1
  377. package/dist/src/vtab/memory/table.js +3 -2
  378. package/dist/src/vtab/memory/table.js.map +1 -1
  379. package/dist/src/vtab/memory/types.d.ts +27 -1
  380. package/dist/src/vtab/memory/types.d.ts.map +1 -1
  381. package/dist/src/vtab/memory/types.js +16 -1
  382. package/dist/src/vtab/memory/types.js.map +1 -1
  383. package/dist/src/vtab/memory/utils/primary-key.d.ts +9 -0
  384. package/dist/src/vtab/memory/utils/primary-key.d.ts.map +1 -1
  385. package/dist/src/vtab/memory/utils/primary-key.js +11 -0
  386. package/dist/src/vtab/memory/utils/primary-key.js.map +1 -1
  387. package/dist/src/vtab/module.d.ts +63 -0
  388. package/dist/src/vtab/module.d.ts.map +1 -1
  389. package/dist/src/vtab/table.d.ts +24 -6
  390. package/dist/src/vtab/table.d.ts.map +1 -1
  391. package/dist/src/vtab/table.js.map +1 -1
  392. package/package.json +3 -3
  393. package/dist/src/planner/rules/cache/rule-in-subquery-cache.d.ts +0 -19
  394. package/dist/src/planner/rules/cache/rule-in-subquery-cache.d.ts.map +0 -1
  395. package/dist/src/planner/rules/cache/rule-in-subquery-cache.js +0 -53
  396. package/dist/src/planner/rules/cache/rule-in-subquery-cache.js.map +0 -1
@@ -6,13 +6,15 @@
6
6
  * the sole caller in the manager is `registerMaterializedView` → {@link buildMaintenancePlan}.
7
7
  */
8
8
  import { BlockNode } from '../planner/nodes/block.js';
9
+ import { type ScalarPlanNode } from '../planner/nodes/plan-node.js';
9
10
  import { TableReferenceNode } from '../planner/nodes/reference.js';
10
11
  import { Scheduler } from '../runtime/scheduler.js';
11
12
  import { type MaintenanceSourceStats } from '../planner/cost/index.js';
12
13
  import type { MaintainedTableSchema } from '../schema/derivation.js';
13
14
  import type { TableSchema } from '../schema/table.js';
14
15
  import { type AggregateLike } from './database-materialized-views-analysis.js';
15
- import type { MaterializedViewManagerContext, MaintenancePlan, FullRebuildPlan } from './database-materialized-views-plans.js';
16
+ import type { MaterializedViewManagerContext, MaintenancePlan, FullRebuildPlan, BackingPkColumn, DeltaAggregateDescriptor } from './database-materialized-views-plans.js';
17
+ import type { CollationResolver } from '../types/logical-type.js';
16
18
  /**
17
19
  * Build the row-time maintenance plan for an MV — **cost-gated, with a floor, never a
18
20
  * shape allowlist**. The builder tries to match a bounded-delta arm by shape
@@ -86,6 +88,69 @@ export declare function buildInverseProjectionPlan(ctx: MaterializedViewManagerC
86
88
  * the backing is keyed by the group key regardless of whether it is a source key.
87
89
  */
88
90
  export declare function buildAggregateResidualPlan(ctx: MaterializedViewManagerContext, mv: MaintainedTableSchema, analyzed: BlockNode, tableRef: TableReferenceNode, sourceBase: string, aggregate: AggregateLike): MaintenancePlan | null;
91
+ /**
92
+ * Build the {@link DeltaAggregateDescriptor} for a single-source aggregate body, or
93
+ * return `undefined` when any column fails the delta eligibility gate (the arm then
94
+ * stays a plain residual — a gate failure is never an error). Function-generic by
95
+ * construction: every judgment reads the aggregate call node's registry-resolved
96
+ * schema and its DECLARED {@link AggregateAlgebra} — never an aggregate-name list.
97
+ *
98
+ * The gate, per stored backing column:
99
+ * - a **group-key passthrough** column must resolve (transitively) to a GROUP BY
100
+ * source column that also backs a backing-PK position (so its flush value is the
101
+ * group key itself);
102
+ * - a directly **delta-maintainable aggregate** column must be a plain call (`agg(x)` /
103
+ * `agg()` — no DISTINCT, FILTER, ORDER BY, multi-arg) whose declared algebra has
104
+ * `merge` + `negate` + `decode`, with a **bare source column** argument (the delta
105
+ * steps the raw value);
106
+ * - **exact value domain**: the aggregate's declared return type is INTEGER-physical
107
+ * (count-shaped — exact regardless of argument), OR its argument column's static
108
+ * type is INTEGER-physical (integer sum). A REAL/NUMERIC/TEXT sum drifts under
109
+ * repeated add/subtract and would diverge byte-exactly from the live re-evaluation
110
+ * the oracle compares against — such a column disqualifies the whole MV (residual);
111
+ * - a **tighten-only** column (min/max, and any UDAF whose merge is a join-semilattice
112
+ * like `bit_or`/`bool_or`) — a plain call declaring `merge` + `decode` but NO `negate`
113
+ * — is admitted with `deltaClass: 'tighten'`. It needs no exact-value-domain gate
114
+ * (merge is idempotent selection, not accumulating arithmetic → no drift) and is never
115
+ * retraction-safe: an insert `merge`s the new value in cheaply, but a retracted group
116
+ * re-derives wholesale from the residual (via the descriptor's `hasTighten`) rather
117
+ * than trusting the arithmetic;
118
+ * - a **decomposition-maintained** column (the avg class) — a plain call whose algebra
119
+ * declares `decompose` (a scalar formula over sibling partials, e.g. avg ≡ sum/count)
120
+ * but is not itself directly maintainable — is admitted only when EVERY partial it
121
+ * names ({@link resolveDecomposePartial}) is also stored as a sibling column of this
122
+ * same body and is itself delta-maintainable. It carries no independent accumulation;
123
+ * at flush its value is `combine([finalized(partial) …])`. If any partial is missing
124
+ * or not delta-maintainable the whole MV falls to residual (avg is the first client of
125
+ * this class, not a special case);
126
+ * - any other column shape (a constant, an expression over the group key, …) fails.
127
+ *
128
+ * Whole-body requirements:
129
+ * - **no post-aggregate filter** (HAVING, or an outer WHERE over the aggregate
130
+ * output): such a filter breaks the arithmetic invariant "stored row == the
131
+ * group's full accumulator" — a filtered-out group has contributions but no
132
+ * stored row, so a later delta would rebuild it from the identity and understate
133
+ * the aggregate. The residual re-evaluates the filter per group and stays exact;
134
+ * - a **count(*) multiplicity witness** — a zero-arg delta-maintainable column with
135
+ * `decodeExact` (its stored value must reconstruct exactly, since it decides group
136
+ * emptiness under retraction). Without it, emptiness cannot be told from an
137
+ * all-NULL sum;
138
+ * - **BINARY collations** on every backing-PK (group key) column: the flush's stored-
139
+ * row read is the host's `scanEffective` equality-prefix seek, whose prefix compare
140
+ * is binary (scan-layer.ts). Unlike the prefix-delete arm there is no source-PK
141
+ * uniqueness collapsing collation classes to one byte form here, so a non-BINARY
142
+ * group key could miss a collation-equal / byte-different stored row — residual
143
+ * instead;
144
+ * - a body WHERE (if any) must compile to a single-source-row predicate
145
+ * (`compilePredicate`) so per-row contributions can be scoped exactly.
146
+ *
147
+ * Per-column `retractionSafe` (see {@link DeltaAggregateColumn}) is recorded rather
148
+ * than gated: a not-retraction-safe descriptor still serves the bulk-insert fast path,
149
+ * and the flush re-derives only the retracted groups through the residual.
150
+ */
151
+ export declare function buildDeltaAggregateDescriptor(mv: MaintainedTableSchema, analyzed: BlockNode, tableRef: TableReferenceNode, groupColumns: readonly number[], backingPkDefinition: ReadonlyArray<BackingPkColumn>, backingPkSourceCols: readonly number[], rootAttrs: ReadonlyArray<{
152
+ id: number;
153
+ } | undefined>, sourceAttrToCol: Map<number, number>, producingByAttrId: Map<number, ScalarPlanNode>, collationResolver: CollationResolver): DeltaAggregateDescriptor | undefined;
89
154
  /**
90
155
  * Build a `'join-residual'` plan for a provably-1:1 row-preserving **inner/cross join**
91
156
  * body (`select … from T join P on T.fk = P.id`), or return `null` on a shape mismatch
@@ -1 +1 @@
1
- {"version":3,"file":"database-materialized-views-plan-builders.d.ts","sourceRoot":"","sources":["../../../src/core/database-materialized-views-plan-builders.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"database-materialized-views-plan-builders.d.ts","sourceRoot":"","sources":["../../../src/core/database-materialized-views-plan-builders.ts"],"names":[],"mappings":"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"}
@@ -8,6 +8,10 @@
8
8
  import { QuereusError } from '../common/errors.js';
9
9
  import { StatusCode } from '../common/types.js';
10
10
  import { ColumnReferenceNode } from '../planner/nodes/reference.js';
11
+ import { AggregateFunctionCallNode } from '../planner/nodes/aggregate-function.js';
12
+ import { isAggregateFunctionSchema } from '../schema/function.js';
13
+ import { bindAggregateSchema } from '../func/registration.js';
14
+ import { PhysicalType } from '../types/logical-type.js';
11
15
  import { checkDeterministic } from '../planner/validation/determinism-validator.js';
12
16
  import { emitPlanNode } from '../runtime/emitters.js';
13
17
  import { EmissionContext } from '../runtime/emission-context.js';
@@ -21,11 +25,17 @@ import { CapabilityDetectors } from '../planner/framework/characteristics.js';
21
25
  import { selectMaintenanceStrategy, isFullRebuildPathological, seqScanCost, filterCost, projectCost, } from '../planner/cost/index.js';
22
26
  import { tryResolveBackingHost } from '../runtime/emit/materialized-view-helpers.js';
23
27
  import { compilePredicate } from '../vtab/memory/utils/predicate.js';
24
- import { findNonReplicableFunction, findNonReplicableCollation, findNonDeterministic, normalizeCollation, findAggregate, containsNodeType, containsAnyJoin, countNodeType, countJoins, findTableFunctionCall, collectTableRefs, collectProducingExprs, resolveTransitiveSourceCol, bodyWhereReferencesLookup, bodyWhereIsNonDeterministic, relationalAttributes, rootRelationalNode, cannotMaterialize, nonReplicableDerivationError, nonReplicableCollationDerivationError, isSingleRowEvaluable, compileSourceRowEvaluator, } from './database-materialized-views-analysis.js';
28
+ import { findNonReplicableFunction, findNonReplicableCollation, findNonDeterministic, normalizeCollation, isBinaryCollation, findAggregate, containsNodeType, containsAnyJoin, countNodeType, countJoins, findTableFunctionCall, collectTableRefs, collectProducingExprs, resolveTransitiveSourceCol, bodyWhereReferencesLookup, bodyWhereIsNonDeterministic, relationalAttributes, rootRelationalNode, cannotMaterialize, nonReplicableDerivationError, nonReplicableCollationDerivationError, isSingleRowEvaluable, compileSourceRowEvaluator, } from './database-materialized-views-analysis.js';
25
29
  import { BINARY_COLLATION } from '../util/comparison.js';
26
30
  /** Fallback source row estimate when the StatsProvider has no count (mirrors the
27
31
  * optimizer's naive default). Only feeds the create-time maintenance cost gate. */
28
32
  const DEFAULT_SOURCE_ROWS = 1000;
33
+ /** Create-time estimate of the fraction of changes to a **tighten-only** (min/max) delta
34
+ * aggregate that retract (a delete / update-old-image) and so re-derive their group from
35
+ * the key-filtered residual instead of pure arithmetic. Feeds the `'delta-aggregate'` cost
36
+ * blend ({@link maintenanceCost}); kept well below 1 so an insert-dominated body still
37
+ * prefers the delta arm, yet non-zero so a tighten body costs more than a pure-group one. */
38
+ const DELTA_TIGHTEN_FALLBACK_RATIO = 0.25;
29
39
  /**
30
40
  * Snapshot a backing table's physical primary key as the maintenance plans consume it: the
31
41
  * declared `(index, desc, collation)` triple plus the collation comparator that name resolves
@@ -123,6 +133,18 @@ export function buildMaintenancePlan(ctx, mv) {
123
133
  }
124
134
  // Try a bounded-delta arm; a shape that fits none falls through to the floor.
125
135
  const boundedDelta = tryBuildBoundedDeltaArm(ctx, mv, analyzed);
136
+ // A residual arm additionally carries the whole-body scheduler — the per-statement
137
+ // degrade-to-rebuild escape: when a statement's distinct affected-key count makes k
138
+ // residual runs cost more than one rebuild (`shouldDegradeToRebuild`), the flush runs
139
+ // this and applies a single `'replace-all'` diff instead. Compiled here (not per arm
140
+ // builder) so the three residual arms share one compilation site; inverse-projection
141
+ // never degrades (always the cheapest arm) and the floor has its own bodyScheduler.
142
+ // NOTE: adds one whole-body optimize+emit per residual-MV (re)registration; if
143
+ // registration latency ever matters (e.g. bulk catalog import), compile this lazily
144
+ // on the first degrade instead.
145
+ if (boundedDelta && boundedDelta.kind !== 'inverse-projection' && boundedDelta.kind !== 'full-rebuild') {
146
+ boundedDelta.fullRebuildScheduler = compileWholeBodyScheduler(ctx, analyzed);
147
+ }
126
148
  const plan = boundedDelta ?? buildFullRebuildPlan(ctx, mv, analyzed);
127
149
  // Precompute the NOT-NULL ordering-seeded PK skew guard once, here at plan build —
128
150
  // which `alter … drop not null` re-runs (it recompiles the row-time plan live), so the
@@ -471,17 +493,33 @@ export function buildAggregateResidualPlan(ctx, mv, analyzed, tableRef, sourceBa
471
493
  const residualScheduler = compileResidual(ctx, analyzed, relKey, groupColumns, 'gk');
472
494
  if (!residualScheduler)
473
495
  return null; // could not parameterize the residual → floor
496
+ // Delta-aggregate fast path: build the descriptor when EVERY stored aggregate
497
+ // column is delta-maintainable by its declared algebra (see the builder below).
498
+ // `undefined` on any gate failure — the arm then stays a plain residual, unchanged.
499
+ const delta = buildDeltaAggregateDescriptor(mv, analyzed, tableRef, groupColumns, backingPkDefinition, backingPkSourceCols, rootAttrs, sourceAttrToCol, producingByAttrId, ctx.getCollationResolver());
474
500
  // ── Cost gate ──
475
501
  // The residual is the structurally-sound incremental arm for an aggregate body;
476
502
  // 'full-rebuild' is the always-correct floor for shapes where the residual is NOT
477
- // sound, so (as with inverse-projection) it is not a competitor here. We still
478
- // record the chosen strategy + cost inputs for parity with the substrate.
479
- const soundStrategies = ['residual-recompute'];
503
+ // sound, so (as with inverse-projection) it is not a competitor here. When the delta
504
+ // descriptor built, 'delta-aggregate' joins the sound set an O(1)-per-change
505
+ // arithmetic RMW, strictly cheaper than the per-group residual under the cost model,
506
+ // so the argmin picks it. The descriptor is attached only when the gate chose it
507
+ // (cost-strategy decoupling: `kind` stays 'residual-recompute' either way).
508
+ const soundStrategies = delta
509
+ ? ['delta-aggregate', 'residual-recompute']
510
+ : ['residual-recompute'];
480
511
  const hasPredicate = mv.derivation.selectAst.type === 'select' && mv.derivation.selectAst.where !== undefined;
481
512
  const sourceStats = estimateMaintenanceStats(ctx, tableRef.tableSchema, backing.columns.length, hasPredicate);
513
+ // A tighten-only (min/max) column re-derives a retracted group from the residual, so the
514
+ // delta arm's cost is no longer pure arithmetic — feed the create-time expected-rescan
515
+ // fraction into the `'delta-aggregate'` cost blend (maintenanceCost). A tighten body then
516
+ // costs strictly more than a pure-group body but, for this insert-dominated estimate, still
517
+ // below the always-residual arm, so the argmin keeps the delta arm.
518
+ if (delta?.hasTighten)
519
+ sourceStats.deltaTightenFallbackRatio = DELTA_TIGHTEN_FALLBACK_RATIO;
482
520
  const estimatedChangeCardinality = Math.max(1, sourceStats.tableRows * 0.01);
483
521
  const chosenStrategy = selectMaintenanceStrategy(soundStrategies, estimatedChangeCardinality, sourceStats);
484
- if (chosenStrategy !== 'residual-recompute') {
522
+ if (!soundStrategies.includes(chosenStrategy)) {
485
523
  throw new QuereusError(`Internal error: cost gate selected unwired strategy '${chosenStrategy}' for materialized view '${mv.name}'`, StatusCode.INTERNAL);
486
524
  }
487
525
  return {
@@ -493,14 +531,326 @@ export function buildAggregateResidualPlan(ctx, mv, analyzed, tableRef, sourceBa
493
531
  chosenStrategy,
494
532
  sourceStats,
495
533
  binding: { kind: 'group', groupColumns: [...groupColumns] },
496
- degradeToRebuild: false,
497
534
  residualScheduler,
498
535
  bindParamPrefix: 'gk',
499
536
  bindColumns: groupColumns,
500
537
  backingPkDefinition,
501
538
  backingPkSourceCols,
539
+ delta: chosenStrategy === 'delta-aggregate' ? delta : undefined,
502
540
  };
503
541
  }
542
+ /**
543
+ * Build the {@link DeltaAggregateDescriptor} for a single-source aggregate body, or
544
+ * return `undefined` when any column fails the delta eligibility gate (the arm then
545
+ * stays a plain residual — a gate failure is never an error). Function-generic by
546
+ * construction: every judgment reads the aggregate call node's registry-resolved
547
+ * schema and its DECLARED {@link AggregateAlgebra} — never an aggregate-name list.
548
+ *
549
+ * The gate, per stored backing column:
550
+ * - a **group-key passthrough** column must resolve (transitively) to a GROUP BY
551
+ * source column that also backs a backing-PK position (so its flush value is the
552
+ * group key itself);
553
+ * - a directly **delta-maintainable aggregate** column must be a plain call (`agg(x)` /
554
+ * `agg()` — no DISTINCT, FILTER, ORDER BY, multi-arg) whose declared algebra has
555
+ * `merge` + `negate` + `decode`, with a **bare source column** argument (the delta
556
+ * steps the raw value);
557
+ * - **exact value domain**: the aggregate's declared return type is INTEGER-physical
558
+ * (count-shaped — exact regardless of argument), OR its argument column's static
559
+ * type is INTEGER-physical (integer sum). A REAL/NUMERIC/TEXT sum drifts under
560
+ * repeated add/subtract and would diverge byte-exactly from the live re-evaluation
561
+ * the oracle compares against — such a column disqualifies the whole MV (residual);
562
+ * - a **tighten-only** column (min/max, and any UDAF whose merge is a join-semilattice
563
+ * like `bit_or`/`bool_or`) — a plain call declaring `merge` + `decode` but NO `negate`
564
+ * — is admitted with `deltaClass: 'tighten'`. It needs no exact-value-domain gate
565
+ * (merge is idempotent selection, not accumulating arithmetic → no drift) and is never
566
+ * retraction-safe: an insert `merge`s the new value in cheaply, but a retracted group
567
+ * re-derives wholesale from the residual (via the descriptor's `hasTighten`) rather
568
+ * than trusting the arithmetic;
569
+ * - a **decomposition-maintained** column (the avg class) — a plain call whose algebra
570
+ * declares `decompose` (a scalar formula over sibling partials, e.g. avg ≡ sum/count)
571
+ * but is not itself directly maintainable — is admitted only when EVERY partial it
572
+ * names ({@link resolveDecomposePartial}) is also stored as a sibling column of this
573
+ * same body and is itself delta-maintainable. It carries no independent accumulation;
574
+ * at flush its value is `combine([finalized(partial) …])`. If any partial is missing
575
+ * or not delta-maintainable the whole MV falls to residual (avg is the first client of
576
+ * this class, not a special case);
577
+ * - any other column shape (a constant, an expression over the group key, …) fails.
578
+ *
579
+ * Whole-body requirements:
580
+ * - **no post-aggregate filter** (HAVING, or an outer WHERE over the aggregate
581
+ * output): such a filter breaks the arithmetic invariant "stored row == the
582
+ * group's full accumulator" — a filtered-out group has contributions but no
583
+ * stored row, so a later delta would rebuild it from the identity and understate
584
+ * the aggregate. The residual re-evaluates the filter per group and stays exact;
585
+ * - a **count(*) multiplicity witness** — a zero-arg delta-maintainable column with
586
+ * `decodeExact` (its stored value must reconstruct exactly, since it decides group
587
+ * emptiness under retraction). Without it, emptiness cannot be told from an
588
+ * all-NULL sum;
589
+ * - **BINARY collations** on every backing-PK (group key) column: the flush's stored-
590
+ * row read is the host's `scanEffective` equality-prefix seek, whose prefix compare
591
+ * is binary (scan-layer.ts). Unlike the prefix-delete arm there is no source-PK
592
+ * uniqueness collapsing collation classes to one byte form here, so a non-BINARY
593
+ * group key could miss a collation-equal / byte-different stored row — residual
594
+ * instead;
595
+ * - a body WHERE (if any) must compile to a single-source-row predicate
596
+ * (`compilePredicate`) so per-row contributions can be scoped exactly.
597
+ *
598
+ * Per-column `retractionSafe` (see {@link DeltaAggregateColumn}) is recorded rather
599
+ * than gated: a not-retraction-safe descriptor still serves the bulk-insert fast path,
600
+ * and the flush re-derives only the retracted groups through the residual.
601
+ */
602
+ export function buildDeltaAggregateDescriptor(mv, analyzed, tableRef, groupColumns, backingPkDefinition, backingPkSourceCols, rootAttrs, sourceAttrToCol, producingByAttrId, collationResolver) {
603
+ const sourceSchema = tableRef.tableSchema;
604
+ const groupColumnSet = new Set(groupColumns);
605
+ // Post-aggregate filter gate: a Filter whose subtree contains the aggregate is a
606
+ // HAVING (or an outer WHERE over the aggregate output). It breaks the arithmetic
607
+ // invariant "stored row == the group's full accumulator" — a filtered-out group has
608
+ // contributions but no stored row, so a later delta would rebuild it from the
609
+ // identity and understate the aggregate. (The body WHERE sits BELOW the aggregate —
610
+ // its Filter subtree contains no aggregate node — and stays delta-eligible via the
611
+ // compiled scope predicate below.)
612
+ if (hasFilterAboveAggregate(analyzed))
613
+ return undefined;
614
+ // Binary-collation gate on the backing PK (the flush's binary equality-prefix read).
615
+ for (const d of backingPkDefinition) {
616
+ if (!isBinaryCollation(d.collation))
617
+ return undefined;
618
+ }
619
+ // Body WHERE → single-row scope predicate (contributions outside it are dropped,
620
+ // mirroring the residual's own WHERE). An uncompilable WHERE shape → residual.
621
+ let predicate;
622
+ const bodyWhere = mv.derivation.selectAst.type === 'select' ? mv.derivation.selectAst.where : undefined;
623
+ if (bodyWhere) {
624
+ try {
625
+ predicate = compilePredicate(bodyWhere, sourceSchema.columns);
626
+ }
627
+ catch {
628
+ return undefined;
629
+ }
630
+ }
631
+ // Chase an output attr's producing chain to its terminal (non-ColumnReference)
632
+ // expression — the aggregate call node for an aggregate output column.
633
+ const resolveTerminalProducing = (attrId) => {
634
+ const seen = new Set();
635
+ let cur = attrId;
636
+ while (!seen.has(cur)) {
637
+ seen.add(cur);
638
+ const expr = producingByAttrId.get(cur);
639
+ if (expr instanceof ColumnReferenceNode) {
640
+ cur = expr.attributeId;
641
+ continue;
642
+ }
643
+ return expr;
644
+ }
645
+ return undefined;
646
+ };
647
+ const aggColumns = [];
648
+ const groupOut = [];
649
+ // Decomposition-maintained columns (the avg class), collected raw here and bound to
650
+ // their sibling stored partials in a second pass below — the partials may project
651
+ // before OR after the decompose column, so they cannot be resolved inline.
652
+ const pendingDecompose = [];
653
+ for (let outCol = 0; outCol < rootAttrs.length; outCol++) {
654
+ const attr = rootAttrs[outCol];
655
+ if (!attr)
656
+ return undefined;
657
+ const sourceCol = resolveTransitiveSourceCol(attr.id, sourceAttrToCol, producingByAttrId);
658
+ if (sourceCol !== undefined) {
659
+ // Group-key passthrough: its flush value is the group key at the matching
660
+ // backing-PK position (covers duplicate projections of a group column).
661
+ if (!groupColumnSet.has(sourceCol))
662
+ return undefined;
663
+ const pkPos = backingPkSourceCols.indexOf(sourceCol);
664
+ if (pkPos < 0)
665
+ return undefined;
666
+ groupOut.push({ backingCol: outCol, pkPos });
667
+ continue;
668
+ }
669
+ const producing = resolveTerminalProducing(attr.id);
670
+ if (!(producing instanceof AggregateFunctionCallNode))
671
+ return undefined;
672
+ // Plain call only: DISTINCT folds duplicates (delta cannot), FILTER/ORDER BY
673
+ // change the contribution set, multi-arg has no single raw value to step.
674
+ if (producing.isDistinct || producing.filter || (producing.orderBy && producing.orderBy.length > 0))
675
+ return undefined;
676
+ if (producing.args.length > 1)
677
+ return undefined;
678
+ const unboundSchema = producing.functionSchema;
679
+ if (!isAggregateFunctionSchema(unboundSchema))
680
+ return undefined;
681
+ // A bare source column argument (both classes below step / decompose over the raw
682
+ // value); a zero-arg (count(*)-shaped) call carries none.
683
+ //
684
+ // Bind the schema to the argument's comparison context ONCE here at plan build:
685
+ // the BOUND schema/algebra are what the delta arm executes (step at statement
686
+ // accumulation, merge/decode/finalize at flush — database-materialized-views-
687
+ // apply.ts), so store maintenance ranks by the same comparator as direct
688
+ // evaluation (min/max over TIMESPAN/JSON/collated text).
689
+ // NOTE (soundness assumption): the flush's decode reconstructs an accumulator from the STORED
690
+ // backing value and merges it against step contributions from SOURCE rows. For
691
+ // min/max the backing column's type is inferReturnType(argType) = argType, so one
692
+ // binding covers both sides. An aggregate whose result type differed from its
693
+ // argument type would need two bindings — none exists today that is both
694
+ // delta-maintainable and comparison-sensitive; state the assumption rather than
695
+ // generalizing for it.
696
+ let argSourceCol;
697
+ const argBindings = [];
698
+ if (producing.args.length === 1) {
699
+ const arg = producing.args[0];
700
+ if (!(arg instanceof ColumnReferenceNode))
701
+ return undefined; // bare column args only
702
+ argSourceCol = resolveTransitiveSourceCol(arg.attributeId, sourceAttrToCol, producingByAttrId);
703
+ if (argSourceCol === undefined)
704
+ return undefined;
705
+ const argType = arg.getType();
706
+ argBindings.push({
707
+ logicalType: argType.logicalType,
708
+ collation: argType.collationName ? collationResolver(argType.collationName) : undefined,
709
+ });
710
+ }
711
+ const schema = bindAggregateSchema(unboundSchema, argBindings);
712
+ const algebra = schema.algebra;
713
+ // Class routing — declaration-driven, never an aggregate-name list:
714
+ // - **directly delta-maintainable** (`merge` + `negate` (retraction) + `decode` —
715
+ // the backing stores finalized values, not accumulators): accumulate it as an
716
+ // ordinary partial;
717
+ // - else a **decomposition-maintained** column whose value is a formula over
718
+ // sibling partials (`algebra.decompose`, e.g. avg ≡ sum/count) — deferred to the
719
+ // pass below that binds each partial to a stored sibling; it accumulates nothing;
720
+ // - anything else disqualifies the whole MV → residual.
721
+ if (algebra?.negate && algebra.decode) {
722
+ // Exact numeric domain: an INTEGER-physical declared result (count-shaped — the
723
+ // accumulator never holds a float) or an INTEGER-physical argument column
724
+ // (integer sum). Anything else may drift under repeated add/subtract.
725
+ // NOTE: if a REAL-domain sum ever needs the fast path, it needs compensated
726
+ // (Kahan) accumulation or a periodic-rescan discipline — a design change, not a
727
+ // relaxation of this gate.
728
+ const resultExact = schema.returnType.logicalType.physicalType === PhysicalType.INTEGER;
729
+ const argExact = argSourceCol !== undefined
730
+ && sourceSchema.columns[argSourceCol]?.logicalType.physicalType === PhysicalType.INTEGER;
731
+ if (!resultExact && !argExact)
732
+ return undefined;
733
+ const retractionSafe = algebra.decodeExact === true
734
+ || (argSourceCol !== undefined && sourceSchema.columns[argSourceCol]?.notNull === true);
735
+ aggColumns.push({
736
+ backingCol: outCol,
737
+ schema,
738
+ algebra,
739
+ deltaClass: 'group',
740
+ argSourceCol,
741
+ isMultiplicity: producing.args.length === 0,
742
+ retractionSafe,
743
+ });
744
+ continue;
745
+ }
746
+ // Tighten-only (join-semilattice): `merge` + `decode` but NO `negate` — min/max, and
747
+ // any UDAF whose merge is idempotent selection (bit_or/bool_or). An insert `merge`s the
748
+ // new value in cheaply (min/max tighten toward the new extreme); a retraction cannot be
749
+ // undone arithmetically, so a retracted group falls back to the residual
750
+ // (retractionSafe:false, and the descriptor's hasTighten routes the whole group there
751
+ // whether or not a row is stored). NO exact-numeric-domain gate: merge is idempotent
752
+ // selection, not accumulating arithmetic, so there is no float drift to guard — a tighten
753
+ // column over any comparable domain (REAL/TEXT included) stays byte-exact vs the live
754
+ // re-evaluation. A negate+decode column that failed the exact-domain gate above already
755
+ // returned undefined, so only a decode-without-negate aggregate reaches here.
756
+ if (algebra?.decode && !algebra.negate) {
757
+ aggColumns.push({
758
+ backingCol: outCol,
759
+ schema,
760
+ algebra,
761
+ deltaClass: 'tighten',
762
+ argSourceCol,
763
+ isMultiplicity: producing.args.length === 0,
764
+ retractionSafe: false,
765
+ });
766
+ continue;
767
+ }
768
+ if (algebra?.decompose) {
769
+ pendingDecompose.push({ backingCol: outCol, argSourceCol, decomposition: algebra.decompose });
770
+ continue;
771
+ }
772
+ return undefined; // neither directly maintainable, tighten-only, nor decomposable → residual
773
+ }
774
+ // Multiplicity witness: a zero-arg delta-maintainable column whose decode is exact
775
+ // (its merged value decides group emptiness, so the reconstruction must survive
776
+ // retraction). Structural — not name-based. A decompose column is never the witness
777
+ // (it accumulates nothing — the requirement is a real stored count(*)).
778
+ const multiplicityIndex = aggColumns.findIndex(c => c.isMultiplicity && c.algebra.decodeExact === true);
779
+ if (multiplicityIndex < 0)
780
+ return undefined;
781
+ if (aggColumns.length === 0)
782
+ return undefined; // defensive — findAggregate implies ≥1
783
+ // Bind each decomposition column's partials to sibling STORED, delta-maintained columns
784
+ // (every `aggColumns` entry already passed the accumulator gate, so it is itself delta-
785
+ // maintainable). A partial that maps to no such sibling disqualifies the whole MV →
786
+ // residual (honest and visible: the user wrote a body without the partials the formula
787
+ // names). The decompose column's `combine` recomputes exactly per group at flush.
788
+ const decomposeColumns = [];
789
+ for (const pd of pendingDecompose) {
790
+ const partialIndices = [];
791
+ for (const p of pd.decomposition.partials) {
792
+ const idx = resolveDecomposePartial(p, pd.argSourceCol, aggColumns, sourceSchema);
793
+ if (idx === undefined)
794
+ return undefined;
795
+ partialIndices.push(idx);
796
+ }
797
+ decomposeColumns.push({ backingCol: pd.backingCol, partialIndices, combine: pd.decomposition.combine });
798
+ }
799
+ return {
800
+ aggColumns,
801
+ decomposeColumns,
802
+ groupColumns: groupOut,
803
+ multiplicityIndex,
804
+ backingColumnCount: rootAttrs.length,
805
+ retractionSafe: aggColumns.every(c => c.retractionSafe),
806
+ hasTighten: aggColumns.some(c => c.deltaClass === 'tighten'),
807
+ predicate,
808
+ };
809
+ }
810
+ /**
811
+ * Resolve one {@link AggregateDecomposition} partial (e.g. avg's `sum` / `count`) to the
812
+ * index of a sibling stored, delta-maintainable aggregate column — an `aggColumns` entry
813
+ * matching the partial's function name and argument. Returns `undefined` when no such
814
+ * sibling is stored (the decompose column is then not maintainable → the MV falls to the
815
+ * residual). The write-side twin of `query-rewrite-matcher.resolveMergeablePartial`.
816
+ *
817
+ * `'same-arg'` targets a sibling over the decompose column's own argument (`f(thisArg)`);
818
+ * `'star'` targets a zero-arg (`count(*)`-shaped) sibling. The **count(*) fallback** mirrors
819
+ * the read-side relaxation from `feat-mv-agg-rollup-retarget`: a stored `count(*)` substitutes
820
+ * for a `count(x)` divisor only when `x` is declared **NOT NULL** — then `count(*)` excludes
821
+ * the same (zero) NULLs `count(x)` would, so the recombine stays exact.
822
+ */
823
+ function resolveDecomposePartial(p, ownerArgSourceCol, aggColumns, sourceSchema) {
824
+ // NOTE: a partial resolves only to an `aggColumns` entry, which already passed the
825
+ // accumulator gate — a BARE source column argument plus the INTEGER-exact-domain check.
826
+ // That is what keeps the decomposed value byte-exact: a partial like `sum(log x)` (a
827
+ // non-bare, float-producing argument) can never be a stored delta-maintainable sibling,
828
+ // so a true geometric mean is NOT delta-decomposable and correctly falls to the residual.
829
+ const targetArg = p.arg === 'star' ? undefined : ownerArgSourceCol;
830
+ const funcName = p.func.toLowerCase();
831
+ const find = (name, argCol) => aggColumns.findIndex(c => c.schema.name.toLowerCase() === name && c.argSourceCol === argCol);
832
+ let idx = find(funcName, targetArg);
833
+ if (idx < 0 && funcName === 'count' && targetArg !== undefined
834
+ && sourceSchema.columns[targetArg]?.notNull === true) {
835
+ idx = find('count', undefined); // count(*) qualifies as the count(x) divisor when x is NOT NULL
836
+ }
837
+ return idx < 0 ? undefined : idx;
838
+ }
839
+ /**
840
+ * True when any Filter node's subtree contains an aggregate node — i.e. the plan
841
+ * filters the AGGREGATE OUTPUT (HAVING, or an outer WHERE over a grouped subquery).
842
+ * The body WHERE is the complement case: its Filter sits below the aggregate, so its
843
+ * subtree contains none. Used by {@link buildDeltaAggregateDescriptor}'s gate.
844
+ */
845
+ function hasFilterAboveAggregate(node) {
846
+ if (node.nodeType === PlanNodeType.Filter && findAggregate(node) !== undefined)
847
+ return true;
848
+ for (const child of node.getChildren()) {
849
+ if (hasFilterAboveAggregate(child))
850
+ return true;
851
+ }
852
+ return false;
853
+ }
504
854
  /**
505
855
  * Build a `'join-residual'` plan for a provably-1:1 row-preserving **inner/cross join**
506
856
  * body (`select … from T join P on T.fk = P.id`), or return `null` on a shape mismatch
@@ -679,7 +1029,6 @@ export function buildJoinResidualPlan(ctx, mv, analyzed, tableRefs) {
679
1029
  chosenStrategy,
680
1030
  sourceStats,
681
1031
  binding: { kind: 'row', keyColumns: [...tPkCols] },
682
- degradeToRebuild: false,
683
1032
  residualScheduler: forwardResidual,
684
1033
  bindParamPrefix: 'pk',
685
1034
  bindColumns: tPkCols,
@@ -866,6 +1215,18 @@ export function compileResidual(ctx, analyzed, relKey, bindColumns, paramPrefix)
866
1215
  const instruction = emitPlanNode(optimized, new EmissionContext(db));
867
1216
  return new Scheduler(instruction);
868
1217
  }
1218
+ /**
1219
+ * Compile the WHOLE optimized body (no key filter, read-side rewrite suppressed) into a
1220
+ * reusable {@link Scheduler} — the residual arms' per-statement degrade-to-rebuild
1221
+ * escape ({@link ResidualArmCommon.fullRebuildScheduler}). Mirrors the floor's own
1222
+ * `bodyScheduler` compilation in {@link buildFullRebuildPlan}: run to completion against
1223
+ * live mid-transaction source state, its rows feed a single `'replace-all'` keyed diff.
1224
+ */
1225
+ function compileWholeBodyScheduler(ctx, analyzed) {
1226
+ const db = ctx;
1227
+ const optimized = db.schemaManager.withSuppressedMaterializedViewRewrite(() => ctx.optimizer.optimize(analyzed, db));
1228
+ return new Scheduler(emitPlanNode(optimized, new EmissionContext(db)));
1229
+ }
869
1230
  /**
870
1231
  * Build a `'prefix-delete'` plan for a single-source lateral-TVF fan-out body
871
1232
  * (`select T.pk…, …, f.* from T cross join lateral tvf(<args over T>) f`), or return
@@ -1025,7 +1386,6 @@ export function buildLateralTvfPrefixDeletePlan(ctx, mv, analyzed, tableRef, sou
1025
1386
  chosenStrategy,
1026
1387
  sourceStats,
1027
1388
  binding: { kind: 'row', keyColumns: [...sourcePkCols] },
1028
- degradeToRebuild: false,
1029
1389
  residualScheduler,
1030
1390
  bindParamPrefix: 'pk',
1031
1391
  bindColumns: sourcePkCols,