@quereus/quereus 4.5.0 → 4.6.0

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 (468) hide show
  1. package/README.md +4 -3
  2. package/dist/src/core/database-events.d.ts +99 -1
  3. package/dist/src/core/database-events.d.ts.map +1 -1
  4. package/dist/src/core/database-events.js +219 -3
  5. package/dist/src/core/database-events.js.map +1 -1
  6. package/dist/src/core/database-materialized-views-plan-builders.d.ts.map +1 -1
  7. package/dist/src/core/database-materialized-views-plan-builders.js +29 -2
  8. package/dist/src/core/database-materialized-views-plan-builders.js.map +1 -1
  9. package/dist/src/core/database-transaction.d.ts +9 -0
  10. package/dist/src/core/database-transaction.d.ts.map +1 -1
  11. package/dist/src/core/database-transaction.js +9 -0
  12. package/dist/src/core/database-transaction.js.map +1 -1
  13. package/dist/src/core/database.d.ts +7 -1
  14. package/dist/src/core/database.d.ts.map +1 -1
  15. package/dist/src/core/database.js +20 -5
  16. package/dist/src/core/database.js.map +1 -1
  17. package/dist/src/func/builtins/aggregate.js +11 -11
  18. package/dist/src/func/builtins/aggregate.js.map +1 -1
  19. package/dist/src/func/builtins/builtin-window-functions.d.ts.map +1 -1
  20. package/dist/src/func/builtins/builtin-window-functions.js +15 -85
  21. package/dist/src/func/builtins/builtin-window-functions.js.map +1 -1
  22. package/dist/src/func/builtins/conversion.d.ts.map +1 -1
  23. package/dist/src/func/builtins/conversion.js +10 -9
  24. package/dist/src/func/builtins/conversion.js.map +1 -1
  25. package/dist/src/func/builtins/datetime.d.ts.map +1 -1
  26. package/dist/src/func/builtins/datetime.js +28 -10
  27. package/dist/src/func/builtins/datetime.js.map +1 -1
  28. package/dist/src/func/builtins/json.d.ts.map +1 -1
  29. package/dist/src/func/builtins/json.js +23 -17
  30. package/dist/src/func/builtins/json.js.map +1 -1
  31. package/dist/src/func/builtins/mutation.js +2 -2
  32. package/dist/src/func/builtins/mutation.js.map +1 -1
  33. package/dist/src/func/builtins/return-types.d.ts +327 -0
  34. package/dist/src/func/builtins/return-types.d.ts.map +1 -0
  35. package/dist/src/func/builtins/return-types.js +43 -0
  36. package/dist/src/func/builtins/return-types.js.map +1 -0
  37. package/dist/src/func/builtins/scalar.d.ts.map +1 -1
  38. package/dist/src/func/builtins/scalar.js +138 -71
  39. package/dist/src/func/builtins/scalar.js.map +1 -1
  40. package/dist/src/func/builtins/schema.d.ts.map +1 -1
  41. package/dist/src/func/builtins/schema.js +11 -3
  42. package/dist/src/func/builtins/schema.js.map +1 -1
  43. package/dist/src/func/builtins/string.d.ts.map +1 -1
  44. package/dist/src/func/builtins/string.js +4 -2
  45. package/dist/src/func/builtins/string.js.map +1 -1
  46. package/dist/src/func/builtins/timespan.d.ts +3 -0
  47. package/dist/src/func/builtins/timespan.d.ts.map +1 -1
  48. package/dist/src/func/builtins/timespan.js +15 -11
  49. package/dist/src/func/builtins/timespan.js.map +1 -1
  50. package/dist/src/func/registration.d.ts +35 -1
  51. package/dist/src/func/registration.d.ts.map +1 -1
  52. package/dist/src/func/registration.js +235 -41
  53. package/dist/src/func/registration.js.map +1 -1
  54. package/dist/src/index.d.ts +11 -4
  55. package/dist/src/index.d.ts.map +1 -1
  56. package/dist/src/index.js +12 -3
  57. package/dist/src/index.js.map +1 -1
  58. package/dist/src/parser/parser.d.ts +46 -0
  59. package/dist/src/parser/parser.d.ts.map +1 -1
  60. package/dist/src/parser/parser.js +160 -44
  61. package/dist/src/parser/parser.js.map +1 -1
  62. package/dist/src/planner/analysis/check-extraction.d.ts.map +1 -1
  63. package/dist/src/planner/analysis/check-extraction.js +37 -4
  64. package/dist/src/planner/analysis/check-extraction.js.map +1 -1
  65. package/dist/src/planner/analysis/comparison-collation.d.ts +20 -22
  66. package/dist/src/planner/analysis/comparison-collation.d.ts.map +1 -1
  67. package/dist/src/planner/analysis/comparison-collation.js +29 -26
  68. package/dist/src/planner/analysis/comparison-collation.js.map +1 -1
  69. package/dist/src/planner/analysis/constraint-extractor.d.ts +0 -7
  70. package/dist/src/planner/analysis/constraint-extractor.d.ts.map +1 -1
  71. package/dist/src/planner/analysis/constraint-extractor.js +59 -59
  72. package/dist/src/planner/analysis/constraint-extractor.js.map +1 -1
  73. package/dist/src/planner/building/alter-table.d.ts.map +1 -1
  74. package/dist/src/planner/building/alter-table.js +65 -27
  75. package/dist/src/planner/building/alter-table.js.map +1 -1
  76. package/dist/src/planner/building/coercion.d.ts +21 -45
  77. package/dist/src/planner/building/coercion.d.ts.map +1 -1
  78. package/dist/src/planner/building/coercion.js +42 -91
  79. package/dist/src/planner/building/coercion.js.map +1 -1
  80. package/dist/src/planner/building/expression.d.ts +17 -0
  81. package/dist/src/planner/building/expression.d.ts.map +1 -1
  82. package/dist/src/planner/building/expression.js +28 -17
  83. package/dist/src/planner/building/expression.js.map +1 -1
  84. package/dist/src/planner/building/function-call.d.ts.map +1 -1
  85. package/dist/src/planner/building/function-call.js +7 -2
  86. package/dist/src/planner/building/function-call.js.map +1 -1
  87. package/dist/src/planner/building/select-aggregates.d.ts +1 -1
  88. package/dist/src/planner/building/select-aggregates.d.ts.map +1 -1
  89. package/dist/src/planner/building/select-aggregates.js +53 -2
  90. package/dist/src/planner/building/select-aggregates.js.map +1 -1
  91. package/dist/src/planner/building/select-context.d.ts +7 -0
  92. package/dist/src/planner/building/select-context.d.ts.map +1 -1
  93. package/dist/src/planner/building/select-context.js +10 -40
  94. package/dist/src/planner/building/select-context.js.map +1 -1
  95. package/dist/src/planner/building/select.d.ts +37 -1
  96. package/dist/src/planner/building/select.d.ts.map +1 -1
  97. package/dist/src/planner/building/select.js +112 -51
  98. package/dist/src/planner/building/select.js.map +1 -1
  99. package/dist/src/planner/cost/conjunct-cost.d.ts +40 -0
  100. package/dist/src/planner/cost/conjunct-cost.d.ts.map +1 -1
  101. package/dist/src/planner/cost/conjunct-cost.js +45 -0
  102. package/dist/src/planner/cost/conjunct-cost.js.map +1 -1
  103. package/dist/src/planner/cost/index.d.ts +10 -0
  104. package/dist/src/planner/cost/index.d.ts.map +1 -1
  105. package/dist/src/planner/cost/index.js +15 -0
  106. package/dist/src/planner/cost/index.js.map +1 -1
  107. package/dist/src/planner/nodes/aggregate-node.d.ts +6 -0
  108. package/dist/src/planner/nodes/aggregate-node.d.ts.map +1 -1
  109. package/dist/src/planner/nodes/aggregate-node.js +10 -2
  110. package/dist/src/planner/nodes/aggregate-node.js.map +1 -1
  111. package/dist/src/planner/nodes/alias-node.d.ts.map +1 -1
  112. package/dist/src/planner/nodes/alias-node.js +2 -1
  113. package/dist/src/planner/nodes/alias-node.js.map +1 -1
  114. package/dist/src/planner/nodes/alter-table-node.d.ts +24 -8
  115. package/dist/src/planner/nodes/alter-table-node.d.ts.map +1 -1
  116. package/dist/src/planner/nodes/alter-table-node.js.map +1 -1
  117. package/dist/src/planner/nodes/asof-scan-node.d.ts.map +1 -1
  118. package/dist/src/planner/nodes/asof-scan-node.js +4 -1
  119. package/dist/src/planner/nodes/asof-scan-node.js.map +1 -1
  120. package/dist/src/planner/nodes/asserted-keys-node.d.ts.map +1 -1
  121. package/dist/src/planner/nodes/asserted-keys-node.js +2 -1
  122. package/dist/src/planner/nodes/asserted-keys-node.js.map +1 -1
  123. package/dist/src/planner/nodes/bloom-join-node.d.ts.map +1 -1
  124. package/dist/src/planner/nodes/bloom-join-node.js +8 -3
  125. package/dist/src/planner/nodes/bloom-join-node.js.map +1 -1
  126. package/dist/src/planner/nodes/cache-node.d.ts.map +1 -1
  127. package/dist/src/planner/nodes/cache-node.js +2 -1
  128. package/dist/src/planner/nodes/cache-node.js.map +1 -1
  129. package/dist/src/planner/nodes/distinct-node.d.ts +6 -0
  130. package/dist/src/planner/nodes/distinct-node.d.ts.map +1 -1
  131. package/dist/src/planner/nodes/distinct-node.js +10 -2
  132. package/dist/src/planner/nodes/distinct-node.js.map +1 -1
  133. package/dist/src/planner/nodes/eager-prefetch-node.d.ts.map +1 -1
  134. package/dist/src/planner/nodes/eager-prefetch-node.js +2 -1
  135. package/dist/src/planner/nodes/eager-prefetch-node.js.map +1 -1
  136. package/dist/src/planner/nodes/fanout-lookup-join-node.d.ts +5 -0
  137. package/dist/src/planner/nodes/fanout-lookup-join-node.d.ts.map +1 -1
  138. package/dist/src/planner/nodes/fanout-lookup-join-node.js +13 -6
  139. package/dist/src/planner/nodes/fanout-lookup-join-node.js.map +1 -1
  140. package/dist/src/planner/nodes/filter.d.ts.map +1 -1
  141. package/dist/src/planner/nodes/filter.js +12 -5
  142. package/dist/src/planner/nodes/filter.js.map +1 -1
  143. package/dist/src/planner/nodes/hash-aggregate.d.ts +6 -0
  144. package/dist/src/planner/nodes/hash-aggregate.d.ts.map +1 -1
  145. package/dist/src/planner/nodes/hash-aggregate.js +10 -2
  146. package/dist/src/planner/nodes/hash-aggregate.js.map +1 -1
  147. package/dist/src/planner/nodes/join-node.d.ts +20 -1
  148. package/dist/src/planner/nodes/join-node.d.ts.map +1 -1
  149. package/dist/src/planner/nodes/join-node.js +30 -9
  150. package/dist/src/planner/nodes/join-node.js.map +1 -1
  151. package/dist/src/planner/nodes/join-utils.d.ts +21 -0
  152. package/dist/src/planner/nodes/join-utils.d.ts.map +1 -1
  153. package/dist/src/planner/nodes/join-utils.js +26 -0
  154. package/dist/src/planner/nodes/join-utils.js.map +1 -1
  155. package/dist/src/planner/nodes/key-set-semi-join-node.d.ts.map +1 -1
  156. package/dist/src/planner/nodes/key-set-semi-join-node.js +5 -1
  157. package/dist/src/planner/nodes/key-set-semi-join-node.js.map +1 -1
  158. package/dist/src/planner/nodes/lens-auxiliary-access-node.d.ts.map +1 -1
  159. package/dist/src/planner/nodes/lens-auxiliary-access-node.js +2 -1
  160. package/dist/src/planner/nodes/lens-auxiliary-access-node.js.map +1 -1
  161. package/dist/src/planner/nodes/limit-offset.d.ts +6 -0
  162. package/dist/src/planner/nodes/limit-offset.d.ts.map +1 -1
  163. package/dist/src/planner/nodes/limit-offset.js +10 -2
  164. package/dist/src/planner/nodes/limit-offset.js.map +1 -1
  165. package/dist/src/planner/nodes/merge-join-node.d.ts.map +1 -1
  166. package/dist/src/planner/nodes/merge-join-node.js +8 -3
  167. package/dist/src/planner/nodes/merge-join-node.js.map +1 -1
  168. package/dist/src/planner/nodes/ordinal-slice-node.d.ts +5 -0
  169. package/dist/src/planner/nodes/ordinal-slice-node.d.ts.map +1 -1
  170. package/dist/src/planner/nodes/ordinal-slice-node.js +9 -2
  171. package/dist/src/planner/nodes/ordinal-slice-node.js.map +1 -1
  172. package/dist/src/planner/nodes/plan-node.d.ts +24 -0
  173. package/dist/src/planner/nodes/plan-node.d.ts.map +1 -1
  174. package/dist/src/planner/nodes/plan-node.js.map +1 -1
  175. package/dist/src/planner/nodes/project-node.d.ts.map +1 -1
  176. package/dist/src/planner/nodes/project-node.js +2 -1
  177. package/dist/src/planner/nodes/project-node.js.map +1 -1
  178. package/dist/src/planner/nodes/reference.d.ts.map +1 -1
  179. package/dist/src/planner/nodes/reference.js +3 -2
  180. package/dist/src/planner/nodes/reference.js.map +1 -1
  181. package/dist/src/planner/nodes/retrieve-node.d.ts.map +1 -1
  182. package/dist/src/planner/nodes/retrieve-node.js +2 -1
  183. package/dist/src/planner/nodes/retrieve-node.js.map +1 -1
  184. package/dist/src/planner/nodes/scalar.d.ts.map +1 -1
  185. package/dist/src/planner/nodes/scalar.js +5 -18
  186. package/dist/src/planner/nodes/scalar.js.map +1 -1
  187. package/dist/src/planner/nodes/sort.d.ts.map +1 -1
  188. package/dist/src/planner/nodes/sort.js +3 -1
  189. package/dist/src/planner/nodes/sort.js.map +1 -1
  190. package/dist/src/planner/nodes/stream-aggregate.d.ts +6 -0
  191. package/dist/src/planner/nodes/stream-aggregate.d.ts.map +1 -1
  192. package/dist/src/planner/nodes/stream-aggregate.js +10 -2
  193. package/dist/src/planner/nodes/stream-aggregate.js.map +1 -1
  194. package/dist/src/planner/nodes/table-access-nodes.d.ts.map +1 -1
  195. package/dist/src/planner/nodes/table-access-nodes.js +5 -0
  196. package/dist/src/planner/nodes/table-access-nodes.js.map +1 -1
  197. package/dist/src/planner/nodes/window-function.d.ts.map +1 -1
  198. package/dist/src/planner/nodes/window-function.js +8 -0
  199. package/dist/src/planner/nodes/window-function.js.map +1 -1
  200. package/dist/src/planner/nodes/window-node.d.ts +1 -1
  201. package/dist/src/planner/nodes/window-node.d.ts.map +1 -1
  202. package/dist/src/planner/nodes/window-node.js +3 -1
  203. package/dist/src/planner/nodes/window-node.js.map +1 -1
  204. package/dist/src/planner/optimizer.d.ts +9 -1
  205. package/dist/src/planner/optimizer.d.ts.map +1 -1
  206. package/dist/src/planner/optimizer.js +77 -6
  207. package/dist/src/planner/optimizer.js.map +1 -1
  208. package/dist/src/planner/rules/access/rule-key-set-seek.d.ts.map +1 -1
  209. package/dist/src/planner/rules/access/rule-key-set-seek.js +3 -68
  210. package/dist/src/planner/rules/access/rule-key-set-seek.js.map +1 -1
  211. package/dist/src/planner/rules/access/rule-select-access-path.d.ts +19 -0
  212. package/dist/src/planner/rules/access/rule-select-access-path.d.ts.map +1 -1
  213. package/dist/src/planner/rules/access/rule-select-access-path.js +24 -3
  214. package/dist/src/planner/rules/access/rule-select-access-path.js.map +1 -1
  215. package/dist/src/planner/rules/cache/rule-cte-optimization.d.ts.map +1 -1
  216. package/dist/src/planner/rules/cache/rule-cte-optimization.js +9 -0
  217. package/dist/src/planner/rules/cache/rule-cte-optimization.js.map +1 -1
  218. package/dist/src/planner/rules/cache/rule-nested-loop-right-cache.d.ts.map +1 -1
  219. package/dist/src/planner/rules/cache/rule-nested-loop-right-cache.js +6 -4
  220. package/dist/src/planner/rules/cache/rule-nested-loop-right-cache.js.map +1 -1
  221. package/dist/src/planner/rules/join/equi-pair-extractor.d.ts +5 -31
  222. package/dist/src/planner/rules/join/equi-pair-extractor.d.ts.map +1 -1
  223. package/dist/src/planner/rules/join/equi-pair-extractor.js +4 -44
  224. package/dist/src/planner/rules/join/equi-pair-extractor.js.map +1 -1
  225. package/dist/src/planner/rules/join/index-nested-loop.d.ts +49 -0
  226. package/dist/src/planner/rules/join/index-nested-loop.d.ts.map +1 -0
  227. package/dist/src/planner/rules/join/index-nested-loop.js +285 -0
  228. package/dist/src/planner/rules/join/index-nested-loop.js.map +1 -0
  229. package/dist/src/planner/rules/join/rule-fanout-batched-outer.js +8 -8
  230. package/dist/src/planner/rules/join/rule-join-physical-selection.d.ts +10 -4
  231. package/dist/src/planner/rules/join/rule-join-physical-selection.d.ts.map +1 -1
  232. package/dist/src/planner/rules/join/rule-join-physical-selection.js +83 -27
  233. package/dist/src/planner/rules/join/rule-join-physical-selection.js.map +1 -1
  234. package/dist/src/planner/rules/join/rule-monotonic-merge-join.js +4 -4
  235. package/dist/src/planner/rules/join/rule-monotonic-merge-join.js.map +1 -1
  236. package/dist/src/planner/rules/predicate/rule-filter-conjunct-ordering.d.ts +21 -8
  237. package/dist/src/planner/rules/predicate/rule-filter-conjunct-ordering.d.ts.map +1 -1
  238. package/dist/src/planner/rules/predicate/rule-filter-conjunct-ordering.js +70 -14
  239. package/dist/src/planner/rules/predicate/rule-filter-conjunct-ordering.js.map +1 -1
  240. package/dist/src/planner/rules/predicate/rule-filter-selectivity.d.ts +42 -17
  241. package/dist/src/planner/rules/predicate/rule-filter-selectivity.d.ts.map +1 -1
  242. package/dist/src/planner/rules/predicate/rule-filter-selectivity.js +84 -154
  243. package/dist/src/planner/rules/predicate/rule-filter-selectivity.js.map +1 -1
  244. package/dist/src/planner/rules/predicate/rule-join-predicate-pushdown.d.ts +73 -0
  245. package/dist/src/planner/rules/predicate/rule-join-predicate-pushdown.d.ts.map +1 -0
  246. package/dist/src/planner/rules/predicate/rule-join-predicate-pushdown.js +205 -0
  247. package/dist/src/planner/rules/predicate/rule-join-predicate-pushdown.js.map +1 -0
  248. package/dist/src/planner/rules/predicate/rule-predicate-inference-equivalence.d.ts +14 -11
  249. package/dist/src/planner/rules/predicate/rule-predicate-inference-equivalence.d.ts.map +1 -1
  250. package/dist/src/planner/rules/predicate/rule-predicate-inference-equivalence.js +21 -85
  251. package/dist/src/planner/rules/predicate/rule-predicate-inference-equivalence.js.map +1 -1
  252. package/dist/src/planner/rules/predicate/rule-predicate-pushdown.d.ts +7 -1
  253. package/dist/src/planner/rules/predicate/rule-predicate-pushdown.d.ts.map +1 -1
  254. package/dist/src/planner/rules/predicate/rule-predicate-pushdown.js +7 -1
  255. package/dist/src/planner/rules/predicate/rule-predicate-pushdown.js.map +1 -1
  256. package/dist/src/planner/rules/shared/access-leaf.d.ts +39 -0
  257. package/dist/src/planner/rules/shared/access-leaf.d.ts.map +1 -0
  258. package/dist/src/planner/rules/shared/access-leaf.js +79 -0
  259. package/dist/src/planner/rules/shared/access-leaf.js.map +1 -0
  260. package/dist/src/planner/rules/subquery/rule-anti-join-fk-empty.d.ts.map +1 -1
  261. package/dist/src/planner/rules/subquery/rule-anti-join-fk-empty.js +18 -8
  262. package/dist/src/planner/rules/subquery/rule-anti-join-fk-empty.js.map +1 -1
  263. package/dist/src/planner/rules/subquery/rule-semi-join-fk-trivial.d.ts +3 -1
  264. package/dist/src/planner/rules/subquery/rule-semi-join-fk-trivial.d.ts.map +1 -1
  265. package/dist/src/planner/rules/subquery/rule-semi-join-fk-trivial.js +27 -10
  266. package/dist/src/planner/rules/subquery/rule-semi-join-fk-trivial.js.map +1 -1
  267. package/dist/src/planner/rules/subquery/rule-subquery-decorrelation.d.ts.map +1 -1
  268. package/dist/src/planner/rules/subquery/rule-subquery-decorrelation.js +69 -11
  269. package/dist/src/planner/rules/subquery/rule-subquery-decorrelation.js.map +1 -1
  270. package/dist/src/planner/rules/window/rule-monotonic-window.d.ts +17 -7
  271. package/dist/src/planner/rules/window/rule-monotonic-window.d.ts.map +1 -1
  272. package/dist/src/planner/rules/window/rule-monotonic-window.js +124 -87
  273. package/dist/src/planner/rules/window/rule-monotonic-window.js.map +1 -1
  274. package/dist/src/planner/scopes/aliased.d.ts.map +1 -1
  275. package/dist/src/planner/scopes/aliased.js +6 -0
  276. package/dist/src/planner/scopes/aliased.js.map +1 -1
  277. package/dist/src/planner/stats/catalog-stats.d.ts +44 -13
  278. package/dist/src/planner/stats/catalog-stats.d.ts.map +1 -1
  279. package/dist/src/planner/stats/catalog-stats.js +137 -61
  280. package/dist/src/planner/stats/catalog-stats.js.map +1 -1
  281. package/dist/src/planner/stats/conjunct-selectivity.d.ts +48 -0
  282. package/dist/src/planner/stats/conjunct-selectivity.d.ts.map +1 -0
  283. package/dist/src/planner/stats/conjunct-selectivity.js +168 -0
  284. package/dist/src/planner/stats/conjunct-selectivity.js.map +1 -0
  285. package/dist/src/planner/stats/index.d.ts +22 -3
  286. package/dist/src/planner/stats/index.d.ts.map +1 -1
  287. package/dist/src/planner/stats/index.js.map +1 -1
  288. package/dist/src/planner/stats/table-cardinality.d.ts +28 -0
  289. package/dist/src/planner/stats/table-cardinality.d.ts.map +1 -0
  290. package/dist/src/planner/stats/table-cardinality.js +29 -0
  291. package/dist/src/planner/stats/table-cardinality.js.map +1 -0
  292. package/dist/src/planner/util/column-origins.d.ts +29 -8
  293. package/dist/src/planner/util/column-origins.d.ts.map +1 -1
  294. package/dist/src/planner/util/column-origins.js +74 -14
  295. package/dist/src/planner/util/column-origins.js.map +1 -1
  296. package/dist/src/planner/util/fd-utils.d.ts +32 -8
  297. package/dist/src/planner/util/fd-utils.d.ts.map +1 -1
  298. package/dist/src/planner/util/fd-utils.js +46 -9
  299. package/dist/src/planner/util/fd-utils.js.map +1 -1
  300. package/dist/src/planner/util/ind-utils.d.ts +56 -17
  301. package/dist/src/planner/util/ind-utils.d.ts.map +1 -1
  302. package/dist/src/planner/util/ind-utils.js +82 -21
  303. package/dist/src/planner/util/ind-utils.js.map +1 -1
  304. package/dist/src/planner/util/key-utils.d.ts +20 -2
  305. package/dist/src/planner/util/key-utils.d.ts.map +1 -1
  306. package/dist/src/planner/util/key-utils.js +37 -3
  307. package/dist/src/planner/util/key-utils.js.map +1 -1
  308. package/dist/src/planner/util/row-estimates.d.ts +28 -0
  309. package/dist/src/planner/util/row-estimates.d.ts.map +1 -0
  310. package/dist/src/planner/util/row-estimates.js +40 -0
  311. package/dist/src/planner/util/row-estimates.js.map +1 -0
  312. package/dist/src/planner/util/row-population.d.ts +42 -0
  313. package/dist/src/planner/util/row-population.d.ts.map +1 -0
  314. package/dist/src/planner/util/row-population.js +71 -0
  315. package/dist/src/planner/util/row-population.js.map +1 -0
  316. package/dist/src/runtime/emit/add-constraint.d.ts.map +1 -1
  317. package/dist/src/runtime/emit/add-constraint.js +39 -1
  318. package/dist/src/runtime/emit/add-constraint.js.map +1 -1
  319. package/dist/src/runtime/emit/aggregate.d.ts.map +1 -1
  320. package/dist/src/runtime/emit/aggregate.js +4 -4
  321. package/dist/src/runtime/emit/aggregate.js.map +1 -1
  322. package/dist/src/runtime/emit/alter-schema-event.d.ts +42 -0
  323. package/dist/src/runtime/emit/alter-schema-event.d.ts.map +1 -0
  324. package/dist/src/runtime/emit/alter-schema-event.js +28 -0
  325. package/dist/src/runtime/emit/alter-schema-event.js.map +1 -0
  326. package/dist/src/runtime/emit/alter-table.d.ts.map +1 -1
  327. package/dist/src/runtime/emit/alter-table.js +380 -154
  328. package/dist/src/runtime/emit/alter-table.js.map +1 -1
  329. package/dist/src/runtime/emit/analyze.d.ts.map +1 -1
  330. package/dist/src/runtime/emit/analyze.js +35 -3
  331. package/dist/src/runtime/emit/analyze.js.map +1 -1
  332. package/dist/src/runtime/emit/cast.d.ts.map +1 -1
  333. package/dist/src/runtime/emit/cast.js +4 -19
  334. package/dist/src/runtime/emit/cast.js.map +1 -1
  335. package/dist/src/runtime/emit/dml-executor.d.ts.map +1 -1
  336. package/dist/src/runtime/emit/dml-executor.js +64 -27
  337. package/dist/src/runtime/emit/dml-executor.js.map +1 -1
  338. package/dist/src/runtime/emit/drop-index.d.ts.map +1 -1
  339. package/dist/src/runtime/emit/drop-index.js +4 -3
  340. package/dist/src/runtime/emit/drop-index.js.map +1 -1
  341. package/dist/src/runtime/emit/hash-aggregate.d.ts.map +1 -1
  342. package/dist/src/runtime/emit/hash-aggregate.js +2 -2
  343. package/dist/src/runtime/emit/hash-aggregate.js.map +1 -1
  344. package/dist/src/runtime/emit/join.d.ts.map +1 -1
  345. package/dist/src/runtime/emit/join.js +8 -67
  346. package/dist/src/runtime/emit/join.js.map +1 -1
  347. package/dist/src/runtime/emit/key-set-semi-join.d.ts.map +1 -1
  348. package/dist/src/runtime/emit/key-set-semi-join.js +6 -0
  349. package/dist/src/runtime/emit/key-set-semi-join.js.map +1 -1
  350. package/dist/src/runtime/emit/operand-comparator.d.ts +30 -0
  351. package/dist/src/runtime/emit/operand-comparator.d.ts.map +1 -1
  352. package/dist/src/runtime/emit/operand-comparator.js +34 -0
  353. package/dist/src/runtime/emit/operand-comparator.js.map +1 -1
  354. package/dist/src/runtime/emit/schema-declarative.d.ts.map +1 -1
  355. package/dist/src/runtime/emit/schema-declarative.js +30 -0
  356. package/dist/src/runtime/emit/schema-declarative.js.map +1 -1
  357. package/dist/src/runtime/emit/subquery.d.ts.map +1 -1
  358. package/dist/src/runtime/emit/subquery.js +153 -52
  359. package/dist/src/runtime/emit/subquery.js.map +1 -1
  360. package/dist/src/runtime/emit/window.js +36 -23
  361. package/dist/src/runtime/emit/window.js.map +1 -1
  362. package/dist/src/schema/catalog-persistability.d.ts +19 -16
  363. package/dist/src/schema/catalog-persistability.d.ts.map +1 -1
  364. package/dist/src/schema/catalog-persistability.js +76 -20
  365. package/dist/src/schema/catalog-persistability.js.map +1 -1
  366. package/dist/src/schema/catalog.d.ts +67 -2
  367. package/dist/src/schema/catalog.d.ts.map +1 -1
  368. package/dist/src/schema/catalog.js +194 -3
  369. package/dist/src/schema/catalog.js.map +1 -1
  370. package/dist/src/schema/column.d.ts +26 -3
  371. package/dist/src/schema/column.d.ts.map +1 -1
  372. package/dist/src/schema/column.js.map +1 -1
  373. package/dist/src/schema/ddl-generator.d.ts.map +1 -1
  374. package/dist/src/schema/ddl-generator.js +19 -6
  375. package/dist/src/schema/ddl-generator.js.map +1 -1
  376. package/dist/src/schema/function.d.ts +32 -1
  377. package/dist/src/schema/function.d.ts.map +1 -1
  378. package/dist/src/schema/function.js +10 -2
  379. package/dist/src/schema/function.js.map +1 -1
  380. package/dist/src/schema/manager.d.ts +84 -7
  381. package/dist/src/schema/manager.d.ts.map +1 -1
  382. package/dist/src/schema/manager.js +153 -53
  383. package/dist/src/schema/manager.js.map +1 -1
  384. package/dist/src/schema/schema-differ.d.ts.map +1 -1
  385. package/dist/src/schema/schema-differ.js +104 -28
  386. package/dist/src/schema/schema-differ.js.map +1 -1
  387. package/dist/src/schema/table.d.ts +149 -1
  388. package/dist/src/schema/table.d.ts.map +1 -1
  389. package/dist/src/schema/table.js +248 -1
  390. package/dist/src/schema/table.js.map +1 -1
  391. package/dist/src/schema/unique-enforcement.d.ts +5 -3
  392. package/dist/src/schema/unique-enforcement.d.ts.map +1 -1
  393. package/dist/src/schema/unique-enforcement.js +5 -3
  394. package/dist/src/schema/unique-enforcement.js.map +1 -1
  395. package/dist/src/types/builtin-types.d.ts +16 -0
  396. package/dist/src/types/builtin-types.d.ts.map +1 -1
  397. package/dist/src/types/builtin-types.js +46 -8
  398. package/dist/src/types/builtin-types.js.map +1 -1
  399. package/dist/src/types/cast-semantics.d.ts +42 -0
  400. package/dist/src/types/cast-semantics.d.ts.map +1 -1
  401. package/dist/src/types/cast-semantics.js +64 -0
  402. package/dist/src/types/cast-semantics.js.map +1 -1
  403. package/dist/src/types/comparison-coercion.d.ts +101 -0
  404. package/dist/src/types/comparison-coercion.d.ts.map +1 -0
  405. package/dist/src/types/comparison-coercion.js +162 -0
  406. package/dist/src/types/comparison-coercion.js.map +1 -0
  407. package/dist/src/types/index.d.ts +2 -2
  408. package/dist/src/types/index.d.ts.map +1 -1
  409. package/dist/src/types/index.js +2 -2
  410. package/dist/src/types/index.js.map +1 -1
  411. package/dist/src/types/logical-type.d.ts +7 -0
  412. package/dist/src/types/logical-type.d.ts.map +1 -1
  413. package/dist/src/types/logical-type.js.map +1 -1
  414. package/dist/src/types/validation.d.ts +56 -0
  415. package/dist/src/types/validation.d.ts.map +1 -1
  416. package/dist/src/types/validation.js +68 -0
  417. package/dist/src/types/validation.js.map +1 -1
  418. package/dist/src/util/coercion.d.ts +8 -0
  419. package/dist/src/util/coercion.d.ts.map +1 -1
  420. package/dist/src/util/coercion.js +8 -0
  421. package/dist/src/util/coercion.js.map +1 -1
  422. package/dist/src/util/comparison.d.ts +16 -3
  423. package/dist/src/util/comparison.d.ts.map +1 -1
  424. package/dist/src/util/comparison.js +21 -5
  425. package/dist/src/util/comparison.js.map +1 -1
  426. package/dist/src/util/key-serializer.d.ts +46 -0
  427. package/dist/src/util/key-serializer.d.ts.map +1 -1
  428. package/dist/src/util/key-serializer.js +47 -0
  429. package/dist/src/util/key-serializer.js.map +1 -1
  430. package/dist/src/util/value-set.d.ts +21 -0
  431. package/dist/src/util/value-set.d.ts.map +1 -0
  432. package/dist/src/util/value-set.js +22 -0
  433. package/dist/src/util/value-set.js.map +1 -0
  434. package/dist/src/vtab/capabilities.d.ts +1 -1
  435. package/dist/src/vtab/events.d.ts +5 -1
  436. package/dist/src/vtab/events.d.ts.map +1 -1
  437. package/dist/src/vtab/events.js.map +1 -1
  438. package/dist/src/vtab/memory/layer/alter-column.d.ts +147 -0
  439. package/dist/src/vtab/memory/layer/alter-column.d.ts.map +1 -0
  440. package/dist/src/vtab/memory/layer/alter-column.js +214 -0
  441. package/dist/src/vtab/memory/layer/alter-column.js.map +1 -0
  442. package/dist/src/vtab/memory/layer/base.d.ts.map +1 -1
  443. package/dist/src/vtab/memory/layer/base.js +4 -3
  444. package/dist/src/vtab/memory/layer/base.js.map +1 -1
  445. package/dist/src/vtab/memory/layer/interface.d.ts +2 -2
  446. package/dist/src/vtab/memory/layer/interface.d.ts.map +1 -1
  447. package/dist/src/vtab/memory/layer/manager.d.ts +176 -25
  448. package/dist/src/vtab/memory/layer/manager.d.ts.map +1 -1
  449. package/dist/src/vtab/memory/layer/manager.js +572 -495
  450. package/dist/src/vtab/memory/layer/manager.js.map +1 -1
  451. package/dist/src/vtab/memory/layer/scan-layer.d.ts.map +1 -1
  452. package/dist/src/vtab/memory/layer/scan-layer.js +32 -5
  453. package/dist/src/vtab/memory/layer/scan-layer.js.map +1 -1
  454. package/dist/src/vtab/memory/layer/transaction.d.ts +101 -15
  455. package/dist/src/vtab/memory/layer/transaction.d.ts.map +1 -1
  456. package/dist/src/vtab/memory/layer/transaction.js +133 -45
  457. package/dist/src/vtab/memory/layer/transaction.js.map +1 -1
  458. package/dist/src/vtab/memory/module.d.ts.map +1 -1
  459. package/dist/src/vtab/memory/module.js +2 -1
  460. package/dist/src/vtab/memory/module.js.map +1 -1
  461. package/dist/src/vtab/memory/table.d.ts.map +1 -1
  462. package/dist/src/vtab/memory/table.js +11 -6
  463. package/dist/src/vtab/memory/table.js.map +1 -1
  464. package/dist/src/vtab/module.d.ts +51 -21
  465. package/dist/src/vtab/module.d.ts.map +1 -1
  466. package/dist/src/vtab/table.d.ts +1 -1
  467. package/dist/src/vtab/table.js +1 -1
  468. package/package.json +3 -3
@@ -4,20 +4,21 @@ import { createRowSlot } from '../context-helpers.js';
4
4
  import { QuereusError } from '../../common/errors.js';
5
5
  import { StatusCode } from '../../common/types.js';
6
6
  import { createLogger } from '../../common/logger.js';
7
- import { buildColumnIndexMap, withGeneratedColumnGraph, requireVtabModule, resolveNamedConstraintClass, validateCollationForType } from '../../schema/table.js';
7
+ import { buildColumnIndexMap, withGeneratedColumnGraph, requireVtabModule, resolveNamedConstraintClass, namedConstraintExists, assertConstraintNameFree, validateCollationForType, columnDefToSchema } from '../../schema/table.js';
8
8
  import { validateForeignKeyCollations, buildForeignKeyConstraintSchema, extractColumnLevelCheckConstraints, extractColumnLevelForeignKeys, extractColumnLevelUniqueConstraints } from '../../schema/constraint-builder.js';
9
- import { MemoryTableModule } from '../../vtab/memory/module.js';
10
9
  import { quoteIdentifier, expressionToString, astToString } from '../../emit/ast-stringify.js';
11
10
  import { renameTableInAst, renameColumnInAst, renameColumnInCheckExpression } from '../../schema/rename-rewriter.js';
12
11
  import { assertCatalogObjectPersistable, assertRenameDependentsPersistable } from '../../schema/catalog-persistability.js';
13
- import { tryFoldLiteral } from '../../parser/utils.js';
12
+ import { assertUniqueConstraintIndexNameFree, assertUniqueConstraintNotDuplicated, uniqueConstraintColumnSetKey } from '../../schema/catalog.js';
14
13
  import { isTruthy } from '../../util/comparison.js';
15
- import { assertDdlTransactionPolicy } from './ddl-transaction-policy.js';
16
- import { validateAndParse } from '../../types/validation.js';
14
+ import { assertDdlTransactionPolicy, isExplicitTransactionOpen } from './ddl-transaction-policy.js';
15
+ import { emitAlterSchemaEvent } from './alter-schema-event.js';
16
+ import { foldDefaultToType, validateAndParse } from '../../types/validation.js';
17
17
  import { snapshotStaleMaterializedViews, propagateTableRenameToMaterializedViews, propagateColumnRenameToMaterializedViews, restoreUnaffectedMaterializedViews, attachMaintainedDerivation, detachMaintainedDerivation, } from './materialized-view-helpers.js';
18
18
  import { isMaintainedTable } from '../../schema/derivation.js';
19
19
  import { inferType } from '../../types/registry.js';
20
20
  const log = createLogger('runtime:emit:alter-table');
21
+ const warnLog = log.extend('warn');
21
22
  /** A scheduled sub-program resolved to a callback the emitter invokes per row. */
22
23
  function qualifyTableName(schemaName, tableName) {
23
24
  const prefix = (schemaName && schemaName.toLowerCase() !== 'main')
@@ -28,12 +29,13 @@ function qualifyTableName(schemaName, tableName) {
28
29
  export function emitAlterTable(plan, ctx) {
29
30
  const tableSchema = plan.table.tableSchema;
30
31
  const action = plan.action;
31
- // An ADD COLUMN with a non-foldable DEFAULT carries a backfill scalar; emit it as a
32
- // scheduled sub-program so the scheduler resolves it into a callback the run() body
33
- // evaluates per existing row (via a row slot over the default's row descriptor). When the
34
- // new column also carries a CHECK, its predicates ride alongside as further callbacks,
35
- // evaluated per backfilled row against `[...existingRow, backfilledValue]`. Slot order is
36
- // fixed: backfill first (present whenever checks are), then the checks in order.
32
+ // An ADD COLUMN with a per-row value source — a non-foldable DEFAULT, or a GENERATED
33
+ // ALWAYS AS expression — carries a backfill scalar; emit it as a scheduled sub-program
34
+ // so the scheduler resolves it into a callback the run() body evaluates per existing row
35
+ // (via a row slot over the backfill's row descriptor). When the new column also carries a
36
+ // CHECK, its predicates ride alongside as further callbacks, evaluated per backfilled row
37
+ // against `[...existingRow, backfilledValue]`. Slot order is fixed: backfill first
38
+ // (present whenever checks are), then the checks in order.
37
39
  const backfill = action.type === 'addColumn' ? action.backfill : undefined;
38
40
  const checks = action.type === 'addColumn' ? action.checks : undefined;
39
41
  const params = [
@@ -172,7 +174,7 @@ async function runRenameTable(rctx, tableSchema, schema, newName) {
172
174
  // substitutes `newName`, which is already under test as the record's own name.
173
175
  assertCatalogObjectPersistable(rctx.db, 'materializedView', { ...tableSchema, name: newName });
174
176
  }
175
- assertRenameDependentsPersistable(rctx.db, schema, ast => renameTableInAst(ast, oldName, newName, tableSchema.schemaName));
177
+ assertRenameDependentsPersistable(rctx.db, schema, ast => renameTableInAst(ast, oldName, newName, tableSchema.schemaName), t => rewriteTableForTableRename(t, tableSchema.schemaName.toLowerCase(), oldName, newName));
176
178
  // Clone schema with new name
177
179
  const updatedTableSchema = {
178
180
  ...tableSchema,
@@ -261,6 +263,11 @@ async function runRenameTable(rctx, tableSchema, schema, newName) {
261
263
  // names a vanished table. A module whose `renameTable` already did all its work (or
262
264
  // that keeps no per-name catalog) simply omits the hook.
263
265
  await module.finalizeRename?.(rctx.db, tableSchema.schemaName, oldName, newName);
266
+ // The public schema-change event, for a module with no emitter of its own — see
267
+ // {@link emitAlterSchemaEvent} for why every arm emits at its tail rather than
268
+ // alongside the module call. Names only the NEW table: `DatabaseSchemaChangeEvent`
269
+ // has no old-object-name field, and the emitting backends have the identical gap.
270
+ emitAlterSchemaEvent(rctx, tableSchema, { type: 'alter', objectType: 'table', objectName: newName });
264
271
  log('Renamed table %s.%s to %s', tableSchema.schemaName, oldName, newName);
265
272
  return null;
266
273
  }
@@ -274,7 +281,9 @@ async function runRenameColumn(rctx, tableSchema, schema, oldName, newName) {
274
281
  throw new QuereusError(`Column '${newName}' already exists in table '${tableSchema.name}'`, StatusCode.ERROR);
275
282
  }
276
283
  // Pre-flight, BEFORE the first side effect (`module.alterTable`): the rewritten
277
- // body of every dependent view / materialized view must still be persistable —
284
+ // body of every dependent view / materialized view — and the rewritten record of
285
+ // every dependent table (its CHECK expressions, the referenced-column list of an FK
286
+ // pointing here, its partial-index predicates) — must still be persistable, on the
278
287
  // same unfailable-propagation reasoning as the table-rename arm above. Shares one
279
288
  // resolver with `propagateColumnRename` so the two passes cannot drift apart in
280
289
  // code; what makes them agree at RUNTIME (the resolver reads the LIVE catalog, and
@@ -282,7 +291,8 @@ async function runRenameColumn(rctx, tableSchema, schema, oldName, newName) {
282
291
  // `isTableInUnaliasedScope` skips the renamed table itself and probes only OTHER
283
292
  // sources, whose column sets this rename does not touch.
284
293
  const resolveColumnInSource = buildColumnSourceResolver(rctx.db);
285
- assertRenameDependentsPersistable(rctx.db, schema, ast => renameColumnInAst(ast, tableSchema.name, oldName, newName, tableSchema.schemaName, resolveColumnInSource));
294
+ assertRenameDependentsPersistable(rctx.db, schema, ast => renameColumnInAst(ast, tableSchema.name, oldName, newName, tableSchema.schemaName, resolveColumnInSource), t => rewriteTableForColumnRename(t, tableSchema.schemaName.toLowerCase(), tableSchema.name, oldName, newName, resolveColumnInSource));
295
+ assertRenamedColumnBackingNamesFree(tableSchema, colIndex, oldName, newName);
286
296
  const existingCol = tableSchema.columns[colIndex];
287
297
  // Build a ColumnDef AST for the renamed column (preserving type info)
288
298
  const newColumnDef = {
@@ -333,9 +343,86 @@ async function runRenameColumn(rctx, tableSchema, schema, oldName, newName) {
333
343
  // Propagate the rename into dependent objects (CHECK / FK / partial-index
334
344
  // predicates in this and other tables, view and materialized-view bodies).
335
345
  await propagateColumnRename(rctx, tableSchema.schemaName, tableSchema.name, oldName, newName, preStaleMvs, resolveColumnInSource);
346
+ emitAlterSchemaEvent(rctx, tableSchema, {
347
+ type: 'alter', objectType: 'column',
348
+ objectName: tableSchema.name,
349
+ columnName: newName,
350
+ oldColumnName: oldName,
351
+ });
336
352
  log('Renamed column %s.%s.%s to %s', tableSchema.schemaName, tableSchema.name, oldName, newName);
337
353
  return null;
338
354
  }
355
+ /**
356
+ * Refuses a RENAME COLUMN that would move an unnamed UNIQUE constraint's backing
357
+ * structure onto a name an index on the same table already holds.
358
+ *
359
+ * An unnamed UNIQUE is enforced by a structure named `_uc_<covered column names>`,
360
+ * derived from the LIVE column names and never recorded — so renaming a covered
361
+ * column moves that name. This is the same collision the four declaration paths
362
+ * already refuse (ADD CONSTRAINT, ADD COLUMN … unique, RENAME CONSTRAINT, and
363
+ * `SchemaManager.createIndex` from the index side), reached through a rename instead
364
+ * of a declaration. Left unguarded the user's index is silently reclassified as a
365
+ * hidden backing structure: it vanishes from `schema()` / `index_info()`, `DROP INDEX`
366
+ * answers `no such index`, and store-backed its `CREATE INDEX` line is dropped from
367
+ * the persisted catalog entry — after which the constraint's entries are built into
368
+ * that index's store and, on reopen, it stops rejecting duplicates.
369
+ *
370
+ * Placed before `module.alterTable` for the same reason those paths are: the store's
371
+ * `alterTable` persists, so a later throw would leave the damage on disk.
372
+ *
373
+ * Skips a case-only rename entirely: the derived name folds to the constraint's OWN
374
+ * pre-rename structure, which the memory backend materializes as a real index entry
375
+ * and the name-only guard would otherwise read as a collision (the same reason
376
+ * `runRenameConstraint` gates its check on `oldLower !== newLower`).
377
+ *
378
+ * Named and `derivedFromIndex` constraints are unaffected — neither takes a derived
379
+ * name. Two DIFFERENT unnamed constraints deriving one post-rename name is only
380
+ * reachable through duplicate unnamed UNIQUEs and is not this guard's problem.
381
+ *
382
+ * NOTE: name-only, like the declaration guards, so it also refuses the rename when the
383
+ * constraint is realized by a REUSED same-column-set index and therefore has no
384
+ * `_uc_*` structure to move — a legal rename turned away over a name nothing would
385
+ * have claimed. Deliberate: both backends decide reuse internally and at different
386
+ * times, so a reuse-aware check would make them disagree on which renames are legal.
387
+ * The refusal names both objects and is escapable by renaming the index first. If the
388
+ * false refusal ever bites, gate the check on the constraint actually holding a
389
+ * `_uc_*` structure — which means asking the module, not just `tableSchema.indexes`.
390
+ */
391
+ function assertRenamedColumnBackingNamesFree(tableSchema, colIndex, oldName, newName) {
392
+ if (oldName.toLowerCase() === newName.toLowerCase())
393
+ return;
394
+ for (const uc of tableSchema.uniqueConstraints ?? []) {
395
+ if (uc.name !== undefined || uc.derivedFromIndex !== undefined)
396
+ continue;
397
+ if (!uc.columns.includes(colIndex))
398
+ continue;
399
+ const postRenameColumns = uc.columns.map(i => i === colIndex ? newName : (tableSchema.columns[i]?.name ?? String(i)));
400
+ assertUniqueConstraintIndexNameFree(tableSchema, undefined, postRenameColumns, `rename column '${oldName}' to '${newName}' on table '${tableSchema.name}'`);
401
+ }
402
+ }
403
+ /**
404
+ * Refuses an inline constraint on a new column whose *user-written* name is already taken —
405
+ * by a constraint on the table, or by an earlier inline constraint in the same ADD COLUMN
406
+ * (neither is on `tableSchema` yet). Same within-table rule ADD CONSTRAINT and RENAME
407
+ * CONSTRAINT enforce; see {@link assertConstraintNameFree} for the ordering constraints.
408
+ *
409
+ * Reads the RAW declaration rather than the extracted table constraints: the extractors
410
+ * auto-name an unnamed CHECK `_check_<column>`, and that synthesized name is not user
411
+ * identity — comparing it would refuse two unnamed CHECKs on one new column, which is
412
+ * legal. Only the three classes that occupy a named-constraint array are compared (a name
413
+ * on an inline NOT NULL / DEFAULT / PRIMARY KEY is not stored, so it cannot collide).
414
+ */
415
+ function assertInlineConstraintNamesFree(tableSchema, columnDef) {
416
+ const seen = new Set();
417
+ for (const declared of columnDef.constraints) {
418
+ if (!declared.name)
419
+ continue;
420
+ if (declared.type !== 'check' && declared.type !== 'unique' && declared.type !== 'foreignKey')
421
+ continue;
422
+ assertConstraintNameFree(tableSchema, declared.name, seen);
423
+ seen.add(declared.name.toLowerCase());
424
+ }
425
+ }
339
426
  async function runAddColumn(rctx, tableSchema, schema, columnDef, backfill, backfillCb, checks, checkCbs) {
340
427
  // Validate column doesn't already exist
341
428
  if (tableSchema.columnIndexMap.has(columnDef.name.toLowerCase())) {
@@ -349,21 +436,37 @@ async function runAddColumn(rctx, tableSchema, schema, columnDef, backfill, back
349
436
  // (bind params / bare columns / non-determinism rejected; `new.<column>` accepted)
350
437
  // and, when it does not fold to a literal, compiled into `backfill` — the default
351
438
  // evaluated against the existing row, so `new.<column>` reads that row's sibling.
439
+ // A GENERATED ALWAYS AS expression takes the same `backfill` route (always, since a
440
+ // generated column has no `defaultValue` any module could bulk-write), validated at
441
+ // plan-build with the generated-flavoured determinism check.
352
442
  const defaultConstraint = columnDef.constraints?.find(c => c.type === 'default');
353
- // Folded literal default; undefined when there is no default or it does not fold
354
- // (the latter carries `backfill` instead). Used by the NOT NULL gate below and by
355
- // the batched-event remap's backfill value.
356
- const foldedDefault = defaultConstraint?.expr ? tryFoldLiteral(defaultConstraint.expr) : undefined;
443
+ // Folded literal default, converted to the new column's declared type — the value a
444
+ // fresh INSERT under the same DEFAULT would store, and the same value each module
445
+ // folds for its own rows. Undefined when there is no default (a generated column
446
+ // included) or it does not fold — those carry `backfill` instead. Used by the NOT NULL
447
+ // gate below and by the batched-event remap's backfill value. An unconvertible literal
448
+ // (`integer default 'abc'`) throws MISMATCH here, before anything is mutated.
449
+ const foldedDefault = foldDefaultToType(defaultConstraint?.expr, inferType(columnDef.dataType), columnDef.name);
357
450
  // Call module.alterTable for data + schema update
358
451
  const module = requireVtabModule(tableSchema);
359
452
  if (!module.alterTable) {
360
453
  throw new QuereusError(`Module for table '${tableSchema.name}' does not support ALTER TABLE ADD COLUMN`, StatusCode.UNSUPPORTED);
361
454
  }
362
- // NOT NULL without a usable DEFAULT cannot backfill existing rows. A DEFAULT whose
363
- // folded value is NULL is equivalent to "no DEFAULT" for this purpose. A non-foldable
364
- // expression default (carried in `backfill`) IS usable — its NOT NULL enforcement is
365
- // deferred to the post-backfill scan — so it is not rejected here. If the table is
366
- // non-empty and the default is nullish, reject before mutating any schema or data.
455
+ // NOT NULL without a usable value source cannot backfill existing rows. A DEFAULT whose
456
+ // folded value is NULL is equivalent to "no DEFAULT" for this purpose. A per-row source
457
+ // (carried in `backfill` — a non-foldable expression default or a GENERATED ALWAYS AS
458
+ // expression) IS usable: its NOT NULL enforcement is deferred to the module's per-row
459
+ // backfill, which rejects the ALTER if any row evaluates to NULL. If the table is
460
+ // non-empty and there is no usable source, reject before mutating any schema or data.
461
+ //
462
+ // Mandatoriness is RESOLVED, not read off the statement text: a column is NOT NULL
463
+ // either by an explicit `not null` or by the session `default_column_nullability`
464
+ // (which ships as `not_null`), so `add column x text default null` is a mandatory
465
+ // column with no value for the existing rows and must be refused. `columnDefToSchema`
466
+ // is the same resolver the memory module, the store module and the isolation layer's
467
+ // `deriveAddColumnBackfill` use, so a fourth spelling of the rule cannot drift. It
468
+ // also validates an explicit COLLATE and rejects DEFAULT + GENERATED ALWAYS AS on one
469
+ // column — both previously raised by the module mid-work, now pre-mutation.
367
470
  //
368
471
  // A module may opt out of this engine-generic rejection via the
369
472
  // `delegatesNotNullBackfill` capability (structurally-total modules that
@@ -371,8 +474,9 @@ async function runAddColumn(rctx, tableSchema, schema, columnDef, backfill, back
371
474
  // it declares the capability, the decision is left entirely to its
372
475
  // `alterTable`. Native modules leave it off, so this still fires for them.
373
476
  const delegatesBackfill = module.getCapabilities?.().delegatesNotNullBackfill === true;
374
- const hasNotNull = columnDef.constraints?.some(c => c.type === 'notNull') ?? false;
375
- if (hasNotNull && !delegatesBackfill && !backfill) {
477
+ const defaultNotNull = rctx.db.options.getStringOption('default_column_nullability') === 'not_null';
478
+ const resolvedNotNull = columnDefToSchema(columnDef, defaultNotNull, rctx.db.options.getStringOption('default_collation'), (n) => rctx.db.isCollationRegistered(n)).notNull;
479
+ if (resolvedNotNull && !delegatesBackfill && !backfill) {
376
480
  const defaultIsNullish = !defaultConstraint?.expr || foldedDefault === null;
377
481
  if (defaultIsNullish) {
378
482
  await validateNotNullBackfill(rctx, tableSchema, columnDef.name);
@@ -395,9 +499,37 @@ async function runAddColumn(rctx, tableSchema, schema, columnDef, backfill, back
395
499
  ...inlineChecks,
396
500
  ...extractColumnLevelForeignKeys(columnDef),
397
501
  ];
398
- // A non-foldable default backfills each existing row from its own value. Install a row
399
- // slot over the default's row descriptor; the evaluator the module calls per existing
400
- // row sets the slot to that row, so the default's `new.<col>` refs resolve to it.
502
+ // Refused before the column is materialized, so a duplicate name leaves the table
503
+ // completely untouched rather than relying on the revert path.
504
+ assertInlineConstraintNamesFree(tableSchema, columnDef);
505
+ // An inline `unique` builds an implicit backing structure named after the constraint
506
+ // — or `_uc_<column>` when unnamed — so reject before the column is materialized when
507
+ // that name is already an index on this table, or when the declaration merely repeats
508
+ // a UNIQUE the table already carries. The column does not exist yet, so both tests run
509
+ // off the column definition's own name (which is exactly what
510
+ // `extractColumnLevelUniqueConstraints` put in `columns`). Placed here, ahead of
511
+ // `module.alterTable`, so a refused statement leaves the table completely untouched
512
+ // rather than relying on the revert path.
513
+ //
514
+ // Duplicate-first, index-name-second — see the ordering rationale in
515
+ // `runAddConstraintViaModule`. `declaredColumnSets` carries the unnamed sets claimed by
516
+ // earlier inline constraints in THIS statement (`add column c … unique unique`); none
517
+ // of them is on `tableSchema` yet, so the guard cannot see them any other way.
518
+ const declaredColumnSets = new Set();
519
+ for (const constraint of inlineConstraints) {
520
+ if (constraint.type !== 'unique')
521
+ continue;
522
+ const columnNames = (constraint.columns ?? []).map(c => c.name);
523
+ const operation = `add ${constraint.name ? `constraint '${constraint.name}'` : 'UNIQUE'} column '${columnDef.name}' to table '${tableSchema.name}'`;
524
+ assertUniqueConstraintNotDuplicated(tableSchema, constraint.name, columnNames, operation, declaredColumnSets);
525
+ assertUniqueConstraintIndexNameFree(tableSchema, constraint.name, columnNames, operation);
526
+ if (constraint.name === undefined)
527
+ declaredColumnSets.add(uniqueConstraintColumnSetKey(columnNames));
528
+ }
529
+ // A per-row backfill derives each existing row's value from that row. Install a row slot
530
+ // over the backfill's row descriptor; the evaluator the module calls per existing row
531
+ // sets the slot to that row, so the expression's `new.<col>` refs (and the bare `<col>`
532
+ // refs a GENERATED ALWAYS AS expression uses) resolve to it.
401
533
  const rowSlot = backfill ? createRowSlot(rctx, backfill.rowDescriptor) : undefined;
402
534
  // When the new column carries a CHECK, install a second slot over the existing columns
403
535
  // plus the new column; we evaluate each predicate against `[...existingRow, value]` after
@@ -411,7 +543,15 @@ async function runAddColumn(rctx, tableSchema, schema, columnDef, backfill, back
411
543
  ? async (row) => {
412
544
  rowSlot.set(row);
413
545
  const valueRaw = backfillCb(rctx);
414
- const value = (valueRaw instanceof Promise ? await valueRaw : valueRaw);
546
+ const evaluated = (valueRaw instanceof Promise ? await valueRaw : valueRaw);
547
+ // Convert to the new column's declared type BEFORE the CHECK predicates see it,
548
+ // matching the write path (`emitInsert` coerces at the top of the DML pipeline, so
549
+ // constraint checking and storage both see the declared form). `coerceTo` is unset
550
+ // when the expression's static type already IS the column's type — see
551
+ // `AddColumnBackfill.coerceTo` for why re-converting there would be destructive.
552
+ const value = backfill.coerceTo
553
+ ? validateAndParse(evaluated, backfill.coerceTo, columnDef.name)
554
+ : evaluated;
415
555
  if (checkSlot && checkPredicates.length > 0 && checkCbs) {
416
556
  checkSlot.set([...row, value]);
417
557
  for (let i = 0; i < checkPredicates.length; i++) {
@@ -513,9 +653,10 @@ async function runAddColumn(rctx, tableSchema, schema, columnDef, backfill, back
513
653
  // folding against them is sound and preserves the optimizer's reach.
514
654
  schema.addTable(columnOnlySchema);
515
655
  // Validate the backfilled values against each inline CHECK, for the literal-default
516
- // path only. A per-row (evaluator) default already enforced its CHECKs inside the
517
- // backfill hook above — against the freshly-computed value rather than this scan's
518
- // stale pre-backfill snapshot — and the module enforces NOT NULL there too. Runs
656
+ // path only. A per-row (evaluator) backfill — non-foldable DEFAULT or GENERATED
657
+ // ALWAYS AS — already enforced its CHECKs inside the backfill hook above, against the
658
+ // freshly-computed value rather than this scan's stale pre-backfill snapshot, and the
659
+ // module enforces NOT NULL there too. Runs
519
660
  // before the whole install loop below, so on a column declaring several kinds a
520
661
  // CHECK violation is reported ahead of a UNIQUE or FK one.
521
662
  if (!backfill && inlineChecks.length > 0) {
@@ -577,6 +718,17 @@ async function runAddColumn(rctx, tableSchema, schema, columnDef, backfill, back
577
718
  oldObject: tableSchema,
578
719
  newObject: finalTableSchema,
579
720
  });
721
+ // ONE event for the whole statement, even when the column declared inline constraints.
722
+ // The install loop above makes a second `module.alterTable(addConstraint)` round-trip per
723
+ // inline constraint, and an emitter-backed module therefore announces `alter`/`column`
724
+ // followed by an `alter`/`table` per constraint — an artifact of its internal call
725
+ // pattern, not a second thing the application did. The deliberate divergence from that
726
+ // shape: do not "fix" this into two events.
727
+ emitAlterSchemaEvent(rctx, tableSchema, {
728
+ type: 'alter', objectType: 'column',
729
+ objectName: tableSchema.name,
730
+ columnName: columnDef.name,
731
+ });
580
732
  log('Added column %s to table %s.%s', columnDef.name, tableSchema.schemaName, tableSchema.name);
581
733
  return null;
582
734
  }
@@ -672,9 +824,10 @@ async function validateBackfillAgainstChecks(rctx, columnOnlySchema, newCheckCon
672
824
  }
673
825
  }
674
826
  /**
675
- * Rejects ADD COLUMN ... NOT NULL when no usable DEFAULT is supplied and the
676
- * table already has rows. The pre-mutation form means no rollback is needed —
677
- * the schema and module state are still untouched at this point.
827
+ * Rejects ADD COLUMN ... NOT NULL when no usable value source is supplied (no DEFAULT,
828
+ * a DEFAULT folding to NULL, and no per-row backfill) and the table already has rows.
829
+ * The pre-mutation form means no rollback is needed — the schema and module state are
830
+ * still untouched at this point.
678
831
  */
679
832
  async function validateNotNullBackfill(rctx, tableSchema, newColumnName) {
680
833
  const qualifiedTable = qualifyTableName(tableSchema.schemaName, tableSchema.name);
@@ -792,6 +945,13 @@ async function runDropColumn(rctx, tableSchema, schema, columnName) {
792
945
  oldObject: tableSchema,
793
946
  newObject: finalSchema,
794
947
  });
948
+ // `drop`, not `alter` — the arm removes an object. Matches what an emitter-backed module
949
+ // reports for the same statement.
950
+ emitAlterSchemaEvent(rctx, tableSchema, {
951
+ type: 'drop', objectType: 'column',
952
+ objectName: tableSchema.name,
953
+ columnName,
954
+ });
795
955
  log('Dropped column %s from table %s.%s', columnName, tableSchema.schemaName, tableSchema.name);
796
956
  return null;
797
957
  }
@@ -826,6 +986,10 @@ async function runDropConstraint(rctx, tableSchema, schema, constraintName) {
826
986
  oldObject: tableSchema,
827
987
  newObject: updatedTableSchema,
828
988
  });
989
+ emitAlterSchemaEvent(rctx, tableSchema, {
990
+ type: 'alter', objectType: 'table',
991
+ objectName: tableSchema.name,
992
+ });
829
993
  log('Dropped constraint %s from table %s.%s', constraintName, tableSchema.schemaName, tableSchema.name);
830
994
  return null;
831
995
  }
@@ -847,6 +1011,17 @@ async function runRenameConstraint(rctx, tableSchema, schema, oldName, newName)
847
1011
  if (oldLower !== newLower && namedConstraintExists(tableSchema, newName)) {
848
1012
  throw new QuereusError(`Cannot rename constraint to '${newName}': a constraint with that name already exists in table '${tableSchema.name}'`, StatusCode.CONSTRAINT);
849
1013
  }
1014
+ // A UNIQUE constraint's implicit backing structure is named after the constraint, so
1015
+ // the rename renames that structure too — reject when the new name is already an
1016
+ // index on this table (the mirror of `SchemaManager.createIndex`'s refusal). Gated on
1017
+ // `oldLower !== newLower` for the same reason the collision check above is: on the
1018
+ // memory backend the constraint's OWN backing structure is a materialized index under
1019
+ // the old name, which a case-only rename would otherwise match.
1020
+ if (constraintClass === 'unique' && oldLower !== newLower) {
1021
+ // No columns to pass: the renamed-to name is always a name, so the `_uc_<cols>`
1022
+ // auto-name branch is unreachable from here.
1023
+ assertUniqueConstraintIndexNameFree(tableSchema, newName, [], `rename constraint '${oldName}' to '${newName}' on table '${tableSchema.name}'`);
1024
+ }
850
1025
  const module = requireVtabModule(tableSchema);
851
1026
  if (!module.alterTable) {
852
1027
  throw new QuereusError(`Module for table '${tableSchema.name}' does not support ALTER TABLE RENAME CONSTRAINT`, StatusCode.UNSUPPORTED);
@@ -864,16 +1039,15 @@ async function runRenameConstraint(rctx, tableSchema, schema, oldName, newName)
864
1039
  oldObject: tableSchema,
865
1040
  newObject: updatedTableSchema,
866
1041
  });
1042
+ // A named constraint is not an `objectType` of its own, so the arm reports the TABLE it
1043
+ // reshaped — same as an emitter-backed module does.
1044
+ emitAlterSchemaEvent(rctx, tableSchema, {
1045
+ type: 'alter', objectType: 'table',
1046
+ objectName: tableSchema.name,
1047
+ });
867
1048
  log('Renamed constraint %s.%s.%s to %s', tableSchema.schemaName, tableSchema.name, oldName, newName);
868
1049
  return null;
869
1050
  }
870
- /** True when `name` addresses any named CHECK / UNIQUE / FOREIGN KEY constraint. */
871
- function namedConstraintExists(tableSchema, name) {
872
- const lower = name.toLowerCase();
873
- return (tableSchema.checkConstraints ?? []).some(c => c.name?.toLowerCase() === lower)
874
- || (tableSchema.uniqueConstraints ?? []).some(c => c.name?.toLowerCase() === lower)
875
- || (tableSchema.foreignKeys ?? []).some(c => c.name?.toLowerCase() === lower);
876
- }
877
1051
  /**
878
1052
  * Rejects DROP/RENAME of a UNIQUE constraint that was synthesized from an explicit
879
1053
  * `CREATE UNIQUE INDEX` (`derivedFromIndex` set). That constraint is the index's
@@ -965,6 +1139,14 @@ async function runAlterColumn(rctx, tableSchema, schema, action) {
965
1139
  oldObject: tableSchema,
966
1140
  newObject: updatedTableSchema,
967
1141
  });
1142
+ // All four attribute forms (SET/DROP NOT NULL, SET DATA TYPE, SET/DROP DEFAULT,
1143
+ // SET COLLATE) report the same `alter`/`column` shape — the event says which column
1144
+ // changed, not which attribute.
1145
+ emitAlterSchemaEvent(rctx, tableSchema, {
1146
+ type: 'alter', objectType: 'column',
1147
+ objectName: tableSchema.name,
1148
+ columnName: action.columnName,
1149
+ });
968
1150
  log('Altered column %s.%s.%s', tableSchema.schemaName, tableSchema.name, action.columnName);
969
1151
  return null;
970
1152
  }
@@ -1002,11 +1184,17 @@ function alterColumnEventValueRemap(tableSchema, colIndex, action) {
1002
1184
  };
1003
1185
  }
1004
1186
  if (action.setNotNull === true) {
1005
- // SET NOT NULL backfill: null → the folded literal DEFAULT, the same map the
1006
- // module applies. No usable literal default means the module either found no
1007
- // NULLs (nothing to remap) or rejected the ALTER before reaching here.
1008
- const defaultExpr = tableSchema.columns[colIndex].defaultValue;
1009
- const folded = defaultExpr ? tryFoldLiteral(defaultExpr) : undefined;
1187
+ // SET NOT NULL backfill: null → the folded-and-CONVERTED literal DEFAULT, the same
1188
+ // map the module applies to its rows. No usable literal default means the module
1189
+ // either found no NULLs (nothing to remap) or rejected the ALTER before reaching here.
1190
+ //
1191
+ // NOTE: `foldDefaultToType` throws MISMATCH on an unconvertible literal, and this
1192
+ // runs AFTER the module mutated. Unreachable today — every module folds the same
1193
+ // DEFAULT through the same helper up front and rejects the ALTER there, so a literal
1194
+ // that fails here would already have failed. If a module ever stops folding eagerly,
1195
+ // catch here and return undefined rather than aborting a completed ALTER.
1196
+ const col = tableSchema.columns[colIndex];
1197
+ const folded = foldDefaultToType(col.defaultValue, col.logicalType, col.name);
1010
1198
  if (folded === undefined || folded === null)
1011
1199
  return undefined;
1012
1200
  return (v) => (v === null ? folded : v);
@@ -1100,12 +1288,33 @@ async function runAlterPrimaryKey(rctx, tableSchema, schema, columns) {
1100
1288
  }
1101
1289
  seen.add(pk.index);
1102
1290
  }
1291
+ // Column indices of the key being retired and the one replacing it. Both index the
1292
+ // CURRENT column layout — ALTER PRIMARY KEY adds and drops no column — so they are
1293
+ // also indices into the batched events' row images, which any earlier ALTER in this
1294
+ // transaction has already remapped to that layout.
1295
+ const oldPkIndices = tableSchema.primaryKeyDefinition.map(pk => pk.index);
1296
+ const newPkIndices = newPkDef.map(pk => pk.index);
1103
1297
  // Try native module re-key first
1104
1298
  const module = requireVtabModule(tableSchema);
1105
1299
  if (module.alterTable) {
1106
1300
  try {
1107
1301
  const schemaChangePk = newPkDef.map(pk => ({ index: pk.index, desc: pk.desc ?? false }));
1108
1302
  const updatedTableSchema = await module.alterTable(rctx.db, tableSchema.schemaName, tableSchema.name, { type: 'alterPrimaryKey', newPkColumns: schemaChangePk });
1303
+ // Events this transaction already recorded identify their rows by the RETIRED
1304
+ // key; re-derive each from its own row image so the commit delivers every event
1305
+ // under the key the table has at delivery. AFTER the module call (a module
1306
+ // failure must leave the batch as untouched as the catalog, and the store's
1307
+ // `ddlCommitPendingOps` flushes its queued events into our batch DURING that
1308
+ // call — those must be in the batch before we walk it), BEFORE the catalog swap,
1309
+ // matching where the other ALTER arms call `remapBatchedDataEvents`.
1310
+ //
1311
+ // NOTE: this sits inside the try whose catch falls through to the rebuild, so an
1312
+ // UNSUPPORTED raised BELOW it (only reachable via a `table_modified` listener, as
1313
+ // `schema.addTable` raises no UNSUPPORTED) would re-key twice. The second pass is
1314
+ // idempotent for every event except a PK-moving update, whose image tie-break no
1315
+ // longer recognizes the already-rewritten key. If anything below this line gains
1316
+ // a real UNSUPPORTED path, narrow the try to the `module.alterTable` call.
1317
+ rctx.db._getEventEmitter().rekeyBatchedDataEvents(tableSchema.schemaName, tableSchema.name, oldPkIndices, newPkIndices);
1109
1318
  schema.addTable(updatedTableSchema);
1110
1319
  rctx.db.schemaManager.getChangeNotifier().notifyChange({
1111
1320
  type: 'table_modified',
@@ -1114,39 +1323,96 @@ async function runAlterPrimaryKey(rctx, tableSchema, schema, columns) {
1114
1323
  oldObject: tableSchema,
1115
1324
  newObject: updatedTableSchema,
1116
1325
  });
1326
+ // NOTE: inside the try whose catch falls through to the rebuild — same caveat as
1327
+ // the re-key above. The emit raises no UNSUPPORTED (the gate either emits or
1328
+ // does not), so it cannot itself trigger the fallback.
1329
+ emitAlterSchemaEvent(rctx, tableSchema, {
1330
+ type: 'alter', objectType: 'table',
1331
+ objectName: tableSchema.name,
1332
+ });
1117
1333
  log('Altered primary key of %s.%s (native)', tableSchema.schemaName, tableSchema.name);
1118
1334
  return null;
1119
1335
  }
1120
1336
  catch (e) {
1121
1337
  if (e instanceof QuereusError && e.code === StatusCode.UNSUPPORTED) {
1122
- // Fall through to rebuild
1338
+ // Fall through to rebuild. The swallow is the documented protocol
1339
+ // (docs/module-authoring.md § `alterPrimaryKey`), but a swallowed error must
1340
+ // still leave a trace — this is the only record that the native re-key was
1341
+ // attempted and declined.
1342
+ warnLog('Module %s declined an in-place re-key of %s.%s (UNSUPPORTED: %s); falling back to a shadow-table rebuild', tableSchema.vtabModuleName ?? '<unknown>', tableSchema.schemaName, tableSchema.name, e.message);
1123
1343
  }
1124
1344
  else {
1125
1345
  throw e;
1126
1346
  }
1127
1347
  }
1128
1348
  }
1129
- // Rebuild fallback
1349
+ // Rebuild fallback. Two preconditions, both refusals rather than repairs — see
1350
+ // `rebuildViaShadowTable` for why neither has a correct outcome. Checked here, after the
1351
+ // native attempt, so both entry paths into the rebuild (no `alterTable` hook at all; the
1352
+ // hook raised UNSUPPORTED) are covered by one check each; a module that raised
1353
+ // UNSUPPORTED has by contract mutated nothing, so a refusal here leaves the catalog, the
1354
+ // table and the enclosing transaction untouched. Capability first: it is unconditional,
1355
+ // so it is the more informative answer when both apply.
1356
+ if (!module.renameTable) {
1357
+ throw new QuereusError(`Module '${tableSchema.vtabModuleName ?? '<unknown>'}' does not support ALTER PRIMARY KEY on table `
1358
+ + `'${tableSchema.name}': it cannot re-key in place (it implements no 'alterTable' hook, or the `
1359
+ + `hook declined), and the engine's fallback rebuild finishes by renaming a shadow table over `
1360
+ + `this one — without 'renameTable' the module would keep the rows under the shadow name and `
1361
+ + `the rebuilt table could not be opened.`, StatusCode.UNSUPPORTED);
1362
+ }
1363
+ // NOTE: refused on every `DdlTransactionality` tier, including a module declaring
1364
+ // 'transactional'. None does today; if one appears its DROP + RENAME would roll back
1365
+ // together with the row copy, making this refusal over-broad — exempt that tier then, the
1366
+ // way `assertDdlTransactionPolicy` already does.
1367
+ if (isExplicitTransactionOpen(rctx.db)) {
1368
+ throw new QuereusError(`ALTER PRIMARY KEY on table '${tableSchema.name}' is not allowed inside an explicit transaction: `
1369
+ + `module '${tableSchema.vtabModuleName ?? '<unknown>'}' cannot re-key in place, so the statement `
1370
+ + `would fall back to a shadow-table rebuild whose DROP + RENAME survives ROLLBACK while its row `
1371
+ + `copy does not — a rollback would leave an empty table and destroy rows committed before this `
1372
+ + `transaction began. COMMIT or ROLLBACK first, then re-issue in autocommit mode.`, StatusCode.ERROR);
1373
+ }
1130
1374
  await rebuildTableWithNewShape(rctx, tableSchema, schema, tableSchema.columns.map(c => c.name), newPkDef);
1375
+ // A DEFENSIVE NO-OP on this path today, not the working re-key the native arm above does.
1376
+ // Nothing can be in the batch for this table by now: the rebuild's own events are
1377
+ // suppressed (see `rebuildViaShadowTable`), the ALTER statement writes no rows of its own,
1378
+ // and the explicit-transaction refusal above rules out earlier same-transaction writes.
1379
+ // Kept anyway — it costs one walk of an empty batch, and it is the correct call the moment
1380
+ // that transaction guard is loosened; deleting it would quietly make this arm's
1381
+ // as-of-delivery `key` guarantee depend on the guard staying exactly as it is. Every column
1382
+ // survives the rebuild in order (`survivingColumns` above is the full list), so the indices
1383
+ // would still line up with the event images.
1384
+ rctx.db._getEventEmitter().rekeyBatchedDataEvents(tableSchema.schemaName, tableSchema.name, oldPkIndices, newPkIndices);
1385
+ // The rebuild's own four statements are silent (`rebuildViaShadowTable` runs them inside
1386
+ // `withPublicEventsSuppressed`, which also swallows the inner RENAME TO arm's event). This
1387
+ // emit is OUTSIDE that scope, so the re-key itself reports exactly one `alter`/`table` —
1388
+ // the same shape the native branch above reports.
1389
+ //
1390
+ // NOTE: this arm is the one place where a module WITH its own emitter can end up reporting
1391
+ // nothing — it raised UNSUPPORTED instead of emitting, and the gate below then suppresses
1392
+ // the engine's fallback because the module registration advertises native support. No such
1393
+ // module exists today (memory re-keys in place, the store handles `alterPrimaryKey`
1394
+ // natively), so the path is unreachable. If an emitter-backed module ever declines the
1395
+ // re-key, teach the gate that a rebuild fallback owns the event regardless of the module's
1396
+ // emitter.
1397
+ emitAlterSchemaEvent(rctx, tableSchema, {
1398
+ type: 'alter', objectType: 'table',
1399
+ objectName: tableSchema.name,
1400
+ });
1131
1401
  log('Altered primary key of %s.%s (rebuild)', tableSchema.schemaName, tableSchema.name);
1132
1402
  return null;
1133
1403
  }
1134
1404
  /**
1135
- * Rebuilds a table with a new column projection and/or primary key.
1136
- * For MemoryTable: builds a new table via the module API and copies rows directly,
1137
- * bypassing SQL execution to avoid transaction-layer isolation issues.
1138
- * For other modules: uses shadow-table SQL approach with DROP+RENAME.
1405
+ * Rebuilds a table with a new column projection and/or primary key, via the
1406
+ * shadow-table SQL approach with DROP+RENAME. This is the fallback for a module
1407
+ * whose `alterTable` throws `UNSUPPORTED` for `alterPrimaryKey` (or omits the
1408
+ * hook); the built-in memory and store modules both re-key in place and never
1409
+ * reach it. See `rebuildViaShadowTable` for the two preconditions
1410
+ * `runAlterPrimaryKey` checks before calling this.
1139
1411
  */
1140
1412
  async function rebuildTableWithNewShape(rctx, tableSchema, schema, survivingColumns, newPkDef) {
1141
1413
  const tableName = tableSchema.name;
1142
1414
  const schemaName = tableSchema.schemaName;
1143
- const module = requireVtabModule(tableSchema);
1144
- if (module instanceof MemoryTableModule) {
1145
- await rebuildMemoryTable(rctx, tableSchema, schema, module, survivingColumns, newPkDef);
1146
- }
1147
- else {
1148
- await rebuildViaShadowTable(rctx, tableSchema, schema, survivingColumns, newPkDef);
1149
- }
1415
+ await rebuildViaShadowTable(rctx, tableSchema, schema, survivingColumns, newPkDef);
1150
1416
  const finalSchema = schema.getTable(tableName);
1151
1417
  if (finalSchema) {
1152
1418
  rctx.db.schemaManager.getChangeNotifier().notifyChange({
@@ -1158,87 +1424,6 @@ async function rebuildTableWithNewShape(rctx, tableSchema, schema, survivingColu
1158
1424
  });
1159
1425
  }
1160
1426
  }
1161
- /**
1162
- * MemoryTable rebuild: builds a new table via module.create() and copies rows
1163
- * directly from the old manager, then swaps in the module and catalog.
1164
- */
1165
- async function rebuildMemoryTable(rctx, tableSchema, schema, module, survivingColumns, newPkDef) {
1166
- const tableName = tableSchema.name;
1167
- const schemaName = tableSchema.schemaName;
1168
- const oldKey = `${schemaName}.${tableName}`.toLowerCase();
1169
- const oldMgr = module.tables.get(oldKey);
1170
- if (!oldMgr) {
1171
- throw new QuereusError(`Table '${tableName}' not found in module`, StatusCode.INTERNAL);
1172
- }
1173
- // Build column index mapping: old column index → new column index
1174
- const survivingIndices = [];
1175
- const newColumns = [];
1176
- for (const colName of survivingColumns) {
1177
- const oldIdx = tableSchema.columnIndexMap.get(colName.toLowerCase());
1178
- if (oldIdx === undefined)
1179
- continue;
1180
- survivingIndices.push(oldIdx);
1181
- newColumns.push(tableSchema.columns[oldIdx]);
1182
- }
1183
- // Remap PK indices from old schema to new column order
1184
- const remappedPk = newPkDef.map(pk => {
1185
- const newIdx = survivingIndices.indexOf(pk.index);
1186
- if (newIdx === -1) {
1187
- throw new QuereusError(`PK column index ${pk.index} not in surviving columns`, StatusCode.INTERNAL);
1188
- }
1189
- return { ...pk, index: newIdx };
1190
- });
1191
- // Build new schema
1192
- const newSchema = Object.freeze({
1193
- ...tableSchema,
1194
- columns: Object.freeze(newColumns),
1195
- columnIndexMap: buildColumnIndexMap(newColumns),
1196
- primaryKeyDefinition: Object.freeze(remappedPk),
1197
- indexes: Object.freeze([]),
1198
- });
1199
- // Create the new table via the module API (goes directly to base layer)
1200
- const shadowName = `${tableName}__rekey_${Date.now()}`;
1201
- const shadowSchema = Object.freeze({ ...newSchema, name: shadowName });
1202
- await module.create(rctx.db, shadowSchema);
1203
- const shadowMgr = module.tables.get(`${schemaName}.${shadowName}`.toLowerCase());
1204
- if (!shadowMgr) {
1205
- throw new QuereusError(`Shadow table manager not found after create`, StatusCode.INTERNAL);
1206
- }
1207
- try {
1208
- // Copy rows from old table to new, projecting surviving columns
1209
- const rows = oldMgr.scanAllRows();
1210
- for (const oldRow of rows) {
1211
- const newRow = survivingIndices.map(i => oldRow[i]);
1212
- shadowMgr.insertRow(newRow);
1213
- }
1214
- // Swap: remove old, remove shadow, re-register shadow under old name
1215
- module.tables.delete(oldKey);
1216
- module.tables.delete(`${schemaName}.${shadowName}`.toLowerCase());
1217
- shadowMgr.renameTable(tableName);
1218
- module.tables.set(oldKey, shadowMgr);
1219
- // Update catalog
1220
- schema.removeTable(tableName);
1221
- schema.addTable(shadowMgr.tableSchema);
1222
- // The old manager is now orphaned. Any active VirtualTableConnection bound
1223
- // to it (e.g. from a prior insert in this session) is stale and must not be
1224
- // reused against the rebuilt table — a reused-stale + fresh connection pair
1225
- // leaves two candidates registered for the same table name, which trips
1226
- // DeferredConstraintQueue.findConnection at the next commit. Mirror the
1227
- // drop-table path's cleanup (schema/manager.ts dropTable). The orphaned
1228
- // manager and its pending layer are discarded with the old manager, so no
1229
- // rollback is needed; this intentionally bypasses implicit-transaction
1230
- // deferral, exactly as drop table relies on.
1231
- rctx.db.removeConnectionsForTable(schemaName, tableName);
1232
- }
1233
- catch (e) {
1234
- // Clean up shadow on failure
1235
- try {
1236
- module.tables.delete(`${schemaName}.${shadowName}`.toLowerCase());
1237
- }
1238
- catch { /* ignore */ }
1239
- throw e;
1240
- }
1241
- }
1242
1427
  /**
1243
1428
  * Build the shadow-table CREATE TABLE DDL used by the non-memory rebuild path.
1244
1429
  *
@@ -1345,9 +1530,29 @@ async function runDropMaintained(rctx, tableSchema, schema) {
1345
1530
  return null;
1346
1531
  }
1347
1532
  /**
1348
- * Generic rebuild via shadow table SQL for non-memory modules.
1533
+ * Generic rebuild via shadow table SQL, for a module without a native
1534
+ * `alterPrimaryKey`.
1535
+ *
1536
+ * Two preconditions the caller (`runAlterPrimaryKey`) enforces, because this rebuild has no
1537
+ * correct outcome without them:
1538
+ *
1539
+ * - **The module must implement `renameTable`.** The last step renames the shadow table over
1540
+ * the original; a module that files its rows under the table's name and never hears about
1541
+ * the rename keeps them under the shadow name while the catalog says otherwise, and the
1542
+ * rebuilt table cannot be connected at all.
1543
+ * - **No explicit transaction may be open.** The two halves have different transactional
1544
+ * lifetimes: the schema half (DROP + RENAME) escapes ROLLBACK on every
1545
+ * `DdlTransactionality` tier a built-in module reaches, while the row copy is staged in the
1546
+ * transaction and IS undone. A ROLLBACK would therefore keep the new empty table and
1547
+ * discard the copy of the rows it replaced — destroying data committed before the
1548
+ * transaction began. Making the schema half roll back needs transactional DDL no module
1549
+ * offers; making the data half survive would commit part of the user's transaction behind
1550
+ * their back.
1551
+ *
1552
+ * The four statements are engine scaffolding, not statements the application issued, so the
1553
+ * whole rebuild runs with the PUBLIC event channels suppressed — see the scope inside.
1349
1554
  */
1350
- async function rebuildViaShadowTable(rctx, tableSchema, schema, survivingColumns, newPkDef) {
1555
+ async function rebuildViaShadowTable(rctx, tableSchema, _schema, survivingColumns, newPkDef) {
1351
1556
  const tableName = tableSchema.name;
1352
1557
  const schemaName = tableSchema.schemaName;
1353
1558
  const shadowName = `${tableName}__rekey_${Date.now()}`;
@@ -1355,19 +1560,40 @@ async function rebuildViaShadowTable(rctx, tableSchema, schema, survivingColumns
1355
1560
  const qualifiedTable = qualifyTableName(schemaName, tableName);
1356
1561
  const createDdl = buildShadowTableDdl(tableSchema, shadowName, survivingColumns, newPkDef);
1357
1562
  const projection = survivingColumns.map(c => quoteIdentifier(c)).join(', ');
1358
- try {
1359
- await rctx.db._execWithinTransaction(createDdl);
1360
- await rctx.db._execWithinTransaction(`insert into ${qualifiedShadow} (${projection}) select ${projection} from ${qualifiedTable}`);
1361
- await rctx.db._execWithinTransaction(`drop table ${qualifiedTable}`);
1362
- await rctx.db._execWithinTransaction(`alter table ${qualifiedShadow} rename to ${quoteIdentifier(tableName)}`);
1363
- }
1364
- catch (e) {
1563
+ // The rebuild's statements are ordinary SQL, so without this scope they would raise
1564
+ // ordinary notifications describing the scaffolding rather than what the user asked for:
1565
+ // the copy announces every existing row as a fresh `insert` (relabelled onto the real
1566
+ // table by the trailing rename), and the create/drop pair announces a timestamped table
1567
+ // created and the real one dropped. A re-key changes no row and replaces no table, so all
1568
+ // of that is wrong. Covers the failure cleanup below too — a shadow table nobody was told
1569
+ // about must not announce its own drop.
1570
+ //
1571
+ // Only the PUBLIC channels are suppressed. The internal catalog change notifier keeps
1572
+ // firing throughout (it is what invalidates the optimizer's and the write path's cached
1573
+ // schemas), which is why the rebuilt table is immediately plannable under its new key.
1574
+ //
1575
+ // NOTE: a module whose own emitter defers delivery to its own commit — rather than
1576
+ // emitting during the write — can still leak the copy's inserts, because its events
1577
+ // arrive after this scope has closed. Conditional, not a live defect: no module that
1578
+ // reaches this rebuild behaves that way (memory and the store both re-key in place and
1579
+ // never enter it). If one ever does, suppression has to become name-keyed (covering the
1580
+ // shadow name as well as the real one) or the events have to be dropped out of the batch
1581
+ // after the fact.
1582
+ await rctx.db._getEventEmitter().withPublicEventsSuppressed(async () => {
1365
1583
  try {
1366
- await rctx.db._execWithinTransaction(`drop table if exists ${qualifiedShadow}`);
1584
+ await rctx.db._execWithinTransaction(createDdl);
1585
+ await rctx.db._execWithinTransaction(`insert into ${qualifiedShadow} (${projection}) select ${projection} from ${qualifiedTable}`);
1586
+ await rctx.db._execWithinTransaction(`drop table ${qualifiedTable}`);
1587
+ await rctx.db._execWithinTransaction(`alter table ${qualifiedShadow} rename to ${quoteIdentifier(tableName)}`);
1367
1588
  }
1368
- catch { /* ignore */ }
1369
- throw e;
1370
- }
1589
+ catch (e) {
1590
+ try {
1591
+ await rctx.db._execWithinTransaction(`drop table if exists ${qualifiedShadow}`);
1592
+ }
1593
+ catch { /* ignore */ }
1594
+ throw e;
1595
+ }
1596
+ });
1371
1597
  }
1372
1598
  /**
1373
1599
  * Propagates a table rename into every dependent schema object the catalog