@quereus/quereus 4.4.0 → 4.5.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 (491) hide show
  1. package/README.md +6 -1
  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-materialized-views-analysis.d.ts.map +1 -1
  10. package/dist/src/core/database-materialized-views-analysis.js +1 -0
  11. package/dist/src/core/database-materialized-views-analysis.js.map +1 -1
  12. package/dist/src/core/database-materialized-views-plan-builders.d.ts +2 -1
  13. package/dist/src/core/database-materialized-views-plan-builders.d.ts.map +1 -1
  14. package/dist/src/core/database-materialized-views-plan-builders.js +26 -5
  15. package/dist/src/core/database-materialized-views-plan-builders.js.map +1 -1
  16. package/dist/src/core/database-materialized-views-plans.d.ts +6 -3
  17. package/dist/src/core/database-materialized-views-plans.d.ts.map +1 -1
  18. package/dist/src/core/database-materialized-views.d.ts +3 -2
  19. package/dist/src/core/database-materialized-views.d.ts.map +1 -1
  20. package/dist/src/core/database-materialized-views.js +26 -5
  21. package/dist/src/core/database-materialized-views.js.map +1 -1
  22. package/dist/src/core/database.d.ts +5 -0
  23. package/dist/src/core/database.d.ts.map +1 -1
  24. package/dist/src/core/database.js +21 -1
  25. package/dist/src/core/database.js.map +1 -1
  26. package/dist/src/core/internal-statement-cache.d.ts +18 -0
  27. package/dist/src/core/internal-statement-cache.d.ts.map +1 -0
  28. package/dist/src/core/internal-statement-cache.js +211 -0
  29. package/dist/src/core/internal-statement-cache.js.map +1 -0
  30. package/dist/src/func/builtins/aggregate.d.ts +13 -12
  31. package/dist/src/func/builtins/aggregate.d.ts.map +1 -1
  32. package/dist/src/func/builtins/aggregate.js +45 -67
  33. package/dist/src/func/builtins/aggregate.js.map +1 -1
  34. package/dist/src/func/builtins/builtin-window-functions.d.ts.map +1 -1
  35. package/dist/src/func/builtins/builtin-window-functions.js +63 -48
  36. package/dist/src/func/builtins/builtin-window-functions.js.map +1 -1
  37. package/dist/src/func/builtins/scalar.d.ts.map +1 -1
  38. package/dist/src/func/builtins/scalar.js +92 -3
  39. package/dist/src/func/builtins/scalar.js.map +1 -1
  40. package/dist/src/func/registration.d.ts +18 -1
  41. package/dist/src/func/registration.d.ts.map +1 -1
  42. package/dist/src/func/registration.js +26 -0
  43. package/dist/src/func/registration.js.map +1 -1
  44. package/dist/src/index.d.ts +10 -10
  45. package/dist/src/index.d.ts.map +1 -1
  46. package/dist/src/index.js +13 -12
  47. package/dist/src/index.js.map +1 -1
  48. package/dist/src/parser/parser.d.ts +6 -0
  49. package/dist/src/parser/parser.d.ts.map +1 -1
  50. package/dist/src/parser/parser.js +45 -34
  51. package/dist/src/parser/parser.js.map +1 -1
  52. package/dist/src/planner/analysis/coarsened-key.d.ts +4 -3
  53. package/dist/src/planner/analysis/coarsened-key.d.ts.map +1 -1
  54. package/dist/src/planner/analysis/coarsened-key.js +37 -3
  55. package/dist/src/planner/analysis/coarsened-key.js.map +1 -1
  56. package/dist/src/planner/analysis/comparison-collation.d.ts +35 -3
  57. package/dist/src/planner/analysis/comparison-collation.d.ts.map +1 -1
  58. package/dist/src/planner/analysis/comparison-collation.js +37 -3
  59. package/dist/src/planner/analysis/comparison-collation.js.map +1 -1
  60. package/dist/src/planner/analysis/coverage-prover.d.ts +13 -2
  61. package/dist/src/planner/analysis/coverage-prover.d.ts.map +1 -1
  62. package/dist/src/planner/analysis/coverage-prover.js +10 -14
  63. package/dist/src/planner/analysis/coverage-prover.js.map +1 -1
  64. package/dist/src/planner/analysis/expression-fingerprint.d.ts.map +1 -1
  65. package/dist/src/planner/analysis/expression-fingerprint.js +29 -0
  66. package/dist/src/planner/analysis/expression-fingerprint.js.map +1 -1
  67. package/dist/src/planner/analysis/predicate-conjuncts.d.ts +10 -4
  68. package/dist/src/planner/analysis/predicate-conjuncts.d.ts.map +1 -1
  69. package/dist/src/planner/analysis/predicate-conjuncts.js +28 -7
  70. package/dist/src/planner/analysis/predicate-conjuncts.js.map +1 -1
  71. package/dist/src/planner/analysis/query-rewrite-matcher.d.ts +6 -0
  72. package/dist/src/planner/analysis/query-rewrite-matcher.d.ts.map +1 -1
  73. package/dist/src/planner/analysis/query-rewrite-matcher.js +19 -29
  74. package/dist/src/planner/analysis/query-rewrite-matcher.js.map +1 -1
  75. package/dist/src/planner/analysis/sat-checker.d.ts.map +1 -1
  76. package/dist/src/planner/analysis/sat-checker.js +43 -4
  77. package/dist/src/planner/analysis/sat-checker.js.map +1 -1
  78. package/dist/src/planner/analysis/scalar-subqueries.d.ts +22 -3
  79. package/dist/src/planner/analysis/scalar-subqueries.d.ts.map +1 -1
  80. package/dist/src/planner/analysis/scalar-subqueries.js +32 -3
  81. package/dist/src/planner/analysis/scalar-subqueries.js.map +1 -1
  82. package/dist/src/planner/analysis/set-op-type-merge.d.ts +64 -0
  83. package/dist/src/planner/analysis/set-op-type-merge.d.ts.map +1 -0
  84. package/dist/src/planner/analysis/set-op-type-merge.js +97 -0
  85. package/dist/src/planner/analysis/set-op-type-merge.js.map +1 -0
  86. package/dist/src/planner/building/coercion.d.ts +78 -0
  87. package/dist/src/planner/building/coercion.d.ts.map +1 -0
  88. package/dist/src/planner/building/coercion.js +137 -0
  89. package/dist/src/planner/building/coercion.js.map +1 -0
  90. package/dist/src/planner/building/constraint-builder.d.ts.map +1 -1
  91. package/dist/src/planner/building/constraint-builder.js +5 -18
  92. package/dist/src/planner/building/constraint-builder.js.map +1 -1
  93. package/dist/src/planner/building/delete.d.ts.map +1 -1
  94. package/dist/src/planner/building/delete.js +10 -3
  95. package/dist/src/planner/building/delete.js.map +1 -1
  96. package/dist/src/planner/building/expression.d.ts.map +1 -1
  97. package/dist/src/planner/building/expression.js +13 -35
  98. package/dist/src/planner/building/expression.js.map +1 -1
  99. package/dist/src/planner/building/foreign-key-builder.d.ts +44 -1
  100. package/dist/src/planner/building/foreign-key-builder.d.ts.map +1 -1
  101. package/dist/src/planner/building/foreign-key-builder.js +54 -0
  102. package/dist/src/planner/building/foreign-key-builder.js.map +1 -1
  103. package/dist/src/planner/building/function-call.d.ts.map +1 -1
  104. package/dist/src/planner/building/function-call.js +8 -0
  105. package/dist/src/planner/building/function-call.js.map +1 -1
  106. package/dist/src/planner/building/insert.d.ts.map +1 -1
  107. package/dist/src/planner/building/insert.js +9 -12
  108. package/dist/src/planner/building/insert.js.map +1 -1
  109. package/dist/src/planner/building/select-compound.d.ts.map +1 -1
  110. package/dist/src/planner/building/select-compound.js +10 -6
  111. package/dist/src/planner/building/select-compound.js.map +1 -1
  112. package/dist/src/planner/building/update.d.ts.map +1 -1
  113. package/dist/src/planner/building/update.js +13 -4
  114. package/dist/src/planner/building/update.js.map +1 -1
  115. package/dist/src/planner/cost/conjunct-cost.d.ts +42 -0
  116. package/dist/src/planner/cost/conjunct-cost.d.ts.map +1 -0
  117. package/dist/src/planner/cost/conjunct-cost.js +62 -0
  118. package/dist/src/planner/cost/conjunct-cost.js.map +1 -0
  119. package/dist/src/planner/framework/pass.js +3 -3
  120. package/dist/src/planner/mutation/propagate.d.ts.map +1 -1
  121. package/dist/src/planner/mutation/propagate.js +1 -0
  122. package/dist/src/planner/mutation/propagate.js.map +1 -1
  123. package/dist/src/planner/nodes/async-gather-node.d.ts.map +1 -1
  124. package/dist/src/planner/nodes/async-gather-node.js +19 -8
  125. package/dist/src/planner/nodes/async-gather-node.js.map +1 -1
  126. package/dist/src/planner/nodes/bloom-join-node.d.ts +2 -0
  127. package/dist/src/planner/nodes/bloom-join-node.d.ts.map +1 -1
  128. package/dist/src/planner/nodes/bloom-join-node.js +10 -5
  129. package/dist/src/planner/nodes/bloom-join-node.js.map +1 -1
  130. package/dist/src/planner/nodes/dml-executor-node.d.ts +0 -9
  131. package/dist/src/planner/nodes/dml-executor-node.d.ts.map +1 -1
  132. package/dist/src/planner/nodes/dml-executor-node.js.map +1 -1
  133. package/dist/src/planner/nodes/join-node.d.ts +5 -0
  134. package/dist/src/planner/nodes/join-node.d.ts.map +1 -1
  135. package/dist/src/planner/nodes/join-node.js +8 -1
  136. package/dist/src/planner/nodes/join-node.js.map +1 -1
  137. package/dist/src/planner/nodes/join-utils.d.ts +57 -0
  138. package/dist/src/planner/nodes/join-utils.d.ts.map +1 -1
  139. package/dist/src/planner/nodes/join-utils.js +47 -0
  140. package/dist/src/planner/nodes/join-utils.js.map +1 -1
  141. package/dist/src/planner/nodes/key-set-semi-join-node.d.ts +87 -0
  142. package/dist/src/planner/nodes/key-set-semi-join-node.d.ts.map +1 -0
  143. package/dist/src/planner/nodes/key-set-semi-join-node.js +137 -0
  144. package/dist/src/planner/nodes/key-set-semi-join-node.js.map +1 -0
  145. package/dist/src/planner/nodes/merge-join-node.d.ts +2 -0
  146. package/dist/src/planner/nodes/merge-join-node.d.ts.map +1 -1
  147. package/dist/src/planner/nodes/merge-join-node.js +14 -6
  148. package/dist/src/planner/nodes/merge-join-node.js.map +1 -1
  149. package/dist/src/planner/nodes/plan-node-type.d.ts +1 -0
  150. package/dist/src/planner/nodes/plan-node-type.d.ts.map +1 -1
  151. package/dist/src/planner/nodes/plan-node-type.js +1 -0
  152. package/dist/src/planner/nodes/plan-node-type.js.map +1 -1
  153. package/dist/src/planner/nodes/scalar.d.ts.map +1 -1
  154. package/dist/src/planner/nodes/scalar.js +26 -3
  155. package/dist/src/planner/nodes/scalar.js.map +1 -1
  156. package/dist/src/planner/nodes/set-operation-node.d.ts +68 -16
  157. package/dist/src/planner/nodes/set-operation-node.d.ts.map +1 -1
  158. package/dist/src/planner/nodes/set-operation-node.js +147 -26
  159. package/dist/src/planner/nodes/set-operation-node.js.map +1 -1
  160. package/dist/src/planner/nodes/table-access-nodes.d.ts +19 -1
  161. package/dist/src/planner/nodes/table-access-nodes.d.ts.map +1 -1
  162. package/dist/src/planner/nodes/table-access-nodes.js +13 -2
  163. package/dist/src/planner/nodes/table-access-nodes.js.map +1 -1
  164. package/dist/src/planner/optimizer.d.ts.map +1 -1
  165. package/dist/src/planner/optimizer.js +54 -12
  166. package/dist/src/planner/optimizer.js.map +1 -1
  167. package/dist/src/planner/rules/access/rule-key-set-seek.d.ts +68 -0
  168. package/dist/src/planner/rules/access/rule-key-set-seek.d.ts.map +1 -0
  169. package/dist/src/planner/rules/access/rule-key-set-seek.js +408 -0
  170. package/dist/src/planner/rules/access/rule-key-set-seek.js.map +1 -0
  171. package/dist/src/planner/rules/access/rule-monotonic-range-access.d.ts.map +1 -1
  172. package/dist/src/planner/rules/access/rule-monotonic-range-access.js +2 -2
  173. package/dist/src/planner/rules/access/rule-monotonic-range-access.js.map +1 -1
  174. package/dist/src/planner/rules/access/rule-select-access-path.d.ts +27 -0
  175. package/dist/src/planner/rules/access/rule-select-access-path.d.ts.map +1 -1
  176. package/dist/src/planner/rules/access/rule-select-access-path.js +74 -29
  177. package/dist/src/planner/rules/access/rule-select-access-path.js.map +1 -1
  178. package/dist/src/planner/rules/cache/rule-materialized-view-rewrite.d.ts.map +1 -1
  179. package/dist/src/planner/rules/cache/rule-materialized-view-rewrite.js +23 -4
  180. package/dist/src/planner/rules/cache/rule-materialized-view-rewrite.js.map +1 -1
  181. package/dist/src/planner/rules/cache/rule-scalar-subquery-cache.d.ts +10 -8
  182. package/dist/src/planner/rules/cache/rule-scalar-subquery-cache.d.ts.map +1 -1
  183. package/dist/src/planner/rules/cache/rule-scalar-subquery-cache.js +10 -8
  184. package/dist/src/planner/rules/cache/rule-scalar-subquery-cache.js.map +1 -1
  185. package/dist/src/planner/rules/join/equi-pair-extractor.d.ts +67 -41
  186. package/dist/src/planner/rules/join/equi-pair-extractor.d.ts.map +1 -1
  187. package/dist/src/planner/rules/join/equi-pair-extractor.js +72 -36
  188. package/dist/src/planner/rules/join/equi-pair-extractor.js.map +1 -1
  189. package/dist/src/planner/rules/join/rule-join-physical-selection.d.ts.map +1 -1
  190. package/dist/src/planner/rules/join/rule-join-physical-selection.js +14 -2
  191. package/dist/src/planner/rules/join/rule-join-physical-selection.js.map +1 -1
  192. package/dist/src/planner/rules/join/rule-monotonic-merge-join.d.ts.map +1 -1
  193. package/dist/src/planner/rules/join/rule-monotonic-merge-join.js +11 -1
  194. package/dist/src/planner/rules/join/rule-monotonic-merge-join.js.map +1 -1
  195. package/dist/src/planner/rules/predicate/rule-empty-relation-folding.d.ts +1 -1
  196. package/dist/src/planner/rules/predicate/rule-empty-relation-folding.js +1 -1
  197. package/dist/src/planner/rules/predicate/rule-filter-conjunct-ordering.d.ts +30 -0
  198. package/dist/src/planner/rules/predicate/rule-filter-conjunct-ordering.d.ts.map +1 -0
  199. package/dist/src/planner/rules/predicate/rule-filter-conjunct-ordering.js +63 -0
  200. package/dist/src/planner/rules/predicate/rule-filter-conjunct-ordering.js.map +1 -0
  201. package/dist/src/planner/rules/predicate/rule-filter-selectivity.d.ts +17 -1
  202. package/dist/src/planner/rules/predicate/rule-filter-selectivity.d.ts.map +1 -1
  203. package/dist/src/planner/rules/predicate/rule-filter-selectivity.js +206 -12
  204. package/dist/src/planner/rules/predicate/rule-filter-selectivity.js.map +1 -1
  205. package/dist/src/planner/rules/predicate/rule-predicate-inference-equivalence.js +8 -1
  206. package/dist/src/planner/rules/predicate/rule-predicate-inference-equivalence.js.map +1 -1
  207. package/dist/src/planner/rules/predicate/rule-predicate-pushdown.d.ts +3 -1
  208. package/dist/src/planner/rules/predicate/rule-predicate-pushdown.d.ts.map +1 -1
  209. package/dist/src/planner/rules/predicate/rule-predicate-pushdown.js +13 -1
  210. package/dist/src/planner/rules/predicate/rule-predicate-pushdown.js.map +1 -1
  211. package/dist/src/planner/rules/retrieve/rule-grow-retrieve.d.ts.map +1 -1
  212. package/dist/src/planner/rules/retrieve/rule-grow-retrieve.js +18 -3
  213. package/dist/src/planner/rules/retrieve/rule-grow-retrieve.js.map +1 -1
  214. package/dist/src/planner/rules/shared/index-style-context.d.ts +17 -0
  215. package/dist/src/planner/rules/shared/index-style-context.d.ts.map +1 -1
  216. package/dist/src/planner/rules/shared/index-style-context.js +7 -0
  217. package/dist/src/planner/rules/shared/index-style-context.js.map +1 -1
  218. package/dist/src/planner/rules/subquery/rule-scalar-agg-decorrelation.js +4 -0
  219. package/dist/src/planner/rules/subquery/rule-scalar-agg-decorrelation.js.map +1 -1
  220. package/dist/src/planner/rules/subquery/rule-subquery-decorrelation.d.ts +24 -6
  221. package/dist/src/planner/rules/subquery/rule-subquery-decorrelation.d.ts.map +1 -1
  222. package/dist/src/planner/rules/subquery/rule-subquery-decorrelation.js +175 -60
  223. package/dist/src/planner/rules/subquery/rule-subquery-decorrelation.js.map +1 -1
  224. package/dist/src/planner/stats/catalog-stats.d.ts +52 -0
  225. package/dist/src/planner/stats/catalog-stats.d.ts.map +1 -1
  226. package/dist/src/planner/stats/catalog-stats.js +151 -5
  227. package/dist/src/planner/stats/catalog-stats.js.map +1 -1
  228. package/dist/src/planner/stats/index.d.ts +15 -0
  229. package/dist/src/planner/stats/index.d.ts.map +1 -1
  230. package/dist/src/planner/stats/index.js.map +1 -1
  231. package/dist/src/planner/stats/selectivity-combine.d.ts +53 -0
  232. package/dist/src/planner/stats/selectivity-combine.d.ts.map +1 -0
  233. package/dist/src/planner/stats/selectivity-combine.js +75 -0
  234. package/dist/src/planner/stats/selectivity-combine.js.map +1 -0
  235. package/dist/src/planner/util/column-origins.d.ts +58 -0
  236. package/dist/src/planner/util/column-origins.d.ts.map +1 -0
  237. package/dist/src/planner/util/column-origins.js +90 -0
  238. package/dist/src/planner/util/column-origins.js.map +1 -0
  239. package/dist/src/runtime/deferred-constraint-queue.d.ts +40 -0
  240. package/dist/src/runtime/deferred-constraint-queue.d.ts.map +1 -1
  241. package/dist/src/runtime/deferred-constraint-queue.js +91 -0
  242. package/dist/src/runtime/deferred-constraint-queue.js.map +1 -1
  243. package/dist/src/runtime/emit/aggregate-setup.d.ts +49 -0
  244. package/dist/src/runtime/emit/aggregate-setup.d.ts.map +1 -0
  245. package/dist/src/runtime/emit/aggregate-setup.js +99 -0
  246. package/dist/src/runtime/emit/aggregate-setup.js.map +1 -0
  247. package/dist/src/runtime/emit/aggregate.d.ts.map +1 -1
  248. package/dist/src/runtime/emit/aggregate.js +4 -69
  249. package/dist/src/runtime/emit/aggregate.js.map +1 -1
  250. package/dist/src/runtime/emit/alter-table.d.ts.map +1 -1
  251. package/dist/src/runtime/emit/alter-table.js +354 -165
  252. package/dist/src/runtime/emit/alter-table.js.map +1 -1
  253. package/dist/src/runtime/emit/asof-scan.d.ts.map +1 -1
  254. package/dist/src/runtime/emit/asof-scan.js +11 -0
  255. package/dist/src/runtime/emit/asof-scan.js.map +1 -1
  256. package/dist/src/runtime/emit/between.d.ts.map +1 -1
  257. package/dist/src/runtime/emit/between.js +7 -12
  258. package/dist/src/runtime/emit/between.js.map +1 -1
  259. package/dist/src/runtime/emit/binary.d.ts.map +1 -1
  260. package/dist/src/runtime/emit/binary.js +24 -25
  261. package/dist/src/runtime/emit/binary.js.map +1 -1
  262. package/dist/src/runtime/emit/bloom-join.d.ts.map +1 -1
  263. package/dist/src/runtime/emit/bloom-join.js +8 -16
  264. package/dist/src/runtime/emit/bloom-join.js.map +1 -1
  265. package/dist/src/runtime/emit/case.d.ts.map +1 -1
  266. package/dist/src/runtime/emit/case.js +40 -6
  267. package/dist/src/runtime/emit/case.js.map +1 -1
  268. package/dist/src/runtime/emit/cast.d.ts.map +1 -1
  269. package/dist/src/runtime/emit/cast.js +10 -23
  270. package/dist/src/runtime/emit/cast.js.map +1 -1
  271. package/dist/src/runtime/emit/constraint-check.d.ts.map +1 -1
  272. package/dist/src/runtime/emit/constraint-check.js +72 -51
  273. package/dist/src/runtime/emit/constraint-check.js.map +1 -1
  274. package/dist/src/runtime/emit/create-view.d.ts.map +1 -1
  275. package/dist/src/runtime/emit/create-view.js +6 -0
  276. package/dist/src/runtime/emit/create-view.js.map +1 -1
  277. package/dist/src/runtime/emit/distinct.d.ts.map +1 -1
  278. package/dist/src/runtime/emit/distinct.js +6 -3
  279. package/dist/src/runtime/emit/distinct.js.map +1 -1
  280. package/dist/src/runtime/emit/dml-executor.d.ts.map +1 -1
  281. package/dist/src/runtime/emit/dml-executor.js +188 -84
  282. package/dist/src/runtime/emit/dml-executor.js.map +1 -1
  283. package/dist/src/runtime/emit/filter.d.ts +18 -0
  284. package/dist/src/runtime/emit/filter.d.ts.map +1 -1
  285. package/dist/src/runtime/emit/filter.js +52 -10
  286. package/dist/src/runtime/emit/filter.js.map +1 -1
  287. package/dist/src/runtime/emit/hash-aggregate.d.ts.map +1 -1
  288. package/dist/src/runtime/emit/hash-aggregate.js +16 -65
  289. package/dist/src/runtime/emit/hash-aggregate.js.map +1 -1
  290. package/dist/src/runtime/emit/insert.d.ts.map +1 -1
  291. package/dist/src/runtime/emit/insert.js +18 -7
  292. package/dist/src/runtime/emit/insert.js.map +1 -1
  293. package/dist/src/runtime/emit/join-key-extractor.d.ts +46 -0
  294. package/dist/src/runtime/emit/join-key-extractor.d.ts.map +1 -0
  295. package/dist/src/runtime/emit/join-key-extractor.js +60 -0
  296. package/dist/src/runtime/emit/join-key-extractor.js.map +1 -0
  297. package/dist/src/runtime/emit/join.d.ts.map +1 -1
  298. package/dist/src/runtime/emit/join.js +37 -11
  299. package/dist/src/runtime/emit/join.js.map +1 -1
  300. package/dist/src/runtime/emit/key-set-semi-join.d.ts +29 -0
  301. package/dist/src/runtime/emit/key-set-semi-join.d.ts.map +1 -0
  302. package/dist/src/runtime/emit/key-set-semi-join.js +143 -0
  303. package/dist/src/runtime/emit/key-set-semi-join.js.map +1 -0
  304. package/dist/src/runtime/emit/materialized-view-helpers.d.ts +14 -1
  305. package/dist/src/runtime/emit/materialized-view-helpers.d.ts.map +1 -1
  306. package/dist/src/runtime/emit/materialized-view-helpers.js +33 -3
  307. package/dist/src/runtime/emit/materialized-view-helpers.js.map +1 -1
  308. package/dist/src/runtime/emit/materialized-view.d.ts.map +1 -1
  309. package/dist/src/runtime/emit/materialized-view.js +47 -6
  310. package/dist/src/runtime/emit/materialized-view.js.map +1 -1
  311. package/dist/src/runtime/emit/merge-join.d.ts.map +1 -1
  312. package/dist/src/runtime/emit/merge-join.js +32 -15
  313. package/dist/src/runtime/emit/merge-join.js.map +1 -1
  314. package/dist/src/runtime/emit/operand-comparator.d.ts +40 -0
  315. package/dist/src/runtime/emit/operand-comparator.d.ts.map +1 -0
  316. package/dist/src/runtime/emit/operand-comparator.js +59 -0
  317. package/dist/src/runtime/emit/operand-comparator.js.map +1 -0
  318. package/dist/src/runtime/emit/scan.d.ts.map +1 -1
  319. package/dist/src/runtime/emit/scan.js +11 -2
  320. package/dist/src/runtime/emit/scan.js.map +1 -1
  321. package/dist/src/runtime/emit/set-object-tags.d.ts +8 -0
  322. package/dist/src/runtime/emit/set-object-tags.d.ts.map +1 -1
  323. package/dist/src/runtime/emit/set-object-tags.js +8 -0
  324. package/dist/src/runtime/emit/set-object-tags.js.map +1 -1
  325. package/dist/src/runtime/emit/set-operation.d.ts.map +1 -1
  326. package/dist/src/runtime/emit/set-operation.js +7 -4
  327. package/dist/src/runtime/emit/set-operation.js.map +1 -1
  328. package/dist/src/runtime/emit/sort.d.ts.map +1 -1
  329. package/dist/src/runtime/emit/sort.js +5 -2
  330. package/dist/src/runtime/emit/sort.js.map +1 -1
  331. package/dist/src/runtime/emit/subquery.d.ts.map +1 -1
  332. package/dist/src/runtime/emit/subquery.js +113 -13
  333. package/dist/src/runtime/emit/subquery.js.map +1 -1
  334. package/dist/src/runtime/emit/temporal-arithmetic.d.ts +7 -0
  335. package/dist/src/runtime/emit/temporal-arithmetic.d.ts.map +1 -1
  336. package/dist/src/runtime/emit/temporal-arithmetic.js +15 -7
  337. package/dist/src/runtime/emit/temporal-arithmetic.js.map +1 -1
  338. package/dist/src/runtime/emit/update.d.ts.map +1 -1
  339. package/dist/src/runtime/emit/update.js +44 -3
  340. package/dist/src/runtime/emit/update.js.map +1 -1
  341. package/dist/src/runtime/emit/window.d.ts.map +1 -1
  342. package/dist/src/runtime/emit/window.js +72 -55
  343. package/dist/src/runtime/emit/window.js.map +1 -1
  344. package/dist/src/runtime/foreign-key-actions.d.ts +36 -1
  345. package/dist/src/runtime/foreign-key-actions.d.ts.map +1 -1
  346. package/dist/src/runtime/foreign-key-actions.js +238 -116
  347. package/dist/src/runtime/foreign-key-actions.js.map +1 -1
  348. package/dist/src/runtime/parallel-driver.d.ts +10 -2
  349. package/dist/src/runtime/parallel-driver.d.ts.map +1 -1
  350. package/dist/src/runtime/parallel-driver.js +12 -2
  351. package/dist/src/runtime/parallel-driver.js.map +1 -1
  352. package/dist/src/runtime/register.d.ts.map +1 -1
  353. package/dist/src/runtime/register.js +2 -0
  354. package/dist/src/runtime/register.js.map +1 -1
  355. package/dist/src/runtime/types.d.ts +25 -0
  356. package/dist/src/runtime/types.d.ts.map +1 -1
  357. package/dist/src/runtime/types.js.map +1 -1
  358. package/dist/src/schema/catalog-persistability.d.ts +58 -0
  359. package/dist/src/schema/catalog-persistability.d.ts.map +1 -0
  360. package/dist/src/schema/catalog-persistability.js +87 -0
  361. package/dist/src/schema/catalog-persistability.js.map +1 -0
  362. package/dist/src/schema/catalog.d.ts +17 -1
  363. package/dist/src/schema/catalog.d.ts.map +1 -1
  364. package/dist/src/schema/catalog.js +25 -3
  365. package/dist/src/schema/catalog.js.map +1 -1
  366. package/dist/src/schema/constraint-builder.d.ts +20 -22
  367. package/dist/src/schema/constraint-builder.d.ts.map +1 -1
  368. package/dist/src/schema/constraint-builder.js +43 -36
  369. package/dist/src/schema/constraint-builder.js.map +1 -1
  370. package/dist/src/schema/ddl-generator.d.ts +23 -0
  371. package/dist/src/schema/ddl-generator.d.ts.map +1 -1
  372. package/dist/src/schema/ddl-generator.js +27 -0
  373. package/dist/src/schema/ddl-generator.js.map +1 -1
  374. package/dist/src/schema/function.d.ts +39 -1
  375. package/dist/src/schema/function.d.ts.map +1 -1
  376. package/dist/src/schema/function.js.map +1 -1
  377. package/dist/src/schema/manager.d.ts +28 -0
  378. package/dist/src/schema/manager.d.ts.map +1 -1
  379. package/dist/src/schema/manager.js +82 -14
  380. package/dist/src/schema/manager.js.map +1 -1
  381. package/dist/src/schema/schema-differ.d.ts.map +1 -1
  382. package/dist/src/schema/schema-differ.js +51 -12
  383. package/dist/src/schema/schema-differ.js.map +1 -1
  384. package/dist/src/schema/unique-enforcement.d.ts +31 -0
  385. package/dist/src/schema/unique-enforcement.d.ts.map +1 -1
  386. package/dist/src/schema/unique-enforcement.js +37 -1
  387. package/dist/src/schema/unique-enforcement.js.map +1 -1
  388. package/dist/src/schema/window-function.d.ts +30 -10
  389. package/dist/src/schema/window-function.d.ts.map +1 -1
  390. package/dist/src/schema/window-function.js +20 -16
  391. package/dist/src/schema/window-function.js.map +1 -1
  392. package/dist/src/types/builtin-types.d.ts.map +1 -1
  393. package/dist/src/types/builtin-types.js +30 -4
  394. package/dist/src/types/builtin-types.js.map +1 -1
  395. package/dist/src/types/cast-semantics.d.ts +43 -0
  396. package/dist/src/types/cast-semantics.d.ts.map +1 -0
  397. package/dist/src/types/cast-semantics.js +59 -0
  398. package/dist/src/types/cast-semantics.js.map +1 -0
  399. package/dist/src/types/index.d.ts +1 -1
  400. package/dist/src/types/index.d.ts.map +1 -1
  401. package/dist/src/types/index.js +1 -1
  402. package/dist/src/types/index.js.map +1 -1
  403. package/dist/src/types/json-type.d.ts.map +1 -1
  404. package/dist/src/types/json-type.js +31 -10
  405. package/dist/src/types/json-type.js.map +1 -1
  406. package/dist/src/types/logical-type.d.ts +22 -0
  407. package/dist/src/types/logical-type.d.ts.map +1 -1
  408. package/dist/src/types/logical-type.js.map +1 -1
  409. package/dist/src/types/temporal-types.d.ts.map +1 -1
  410. package/dist/src/types/temporal-types.js +33 -11
  411. package/dist/src/types/temporal-types.js.map +1 -1
  412. package/dist/src/types/validation.d.ts +39 -1
  413. package/dist/src/types/validation.d.ts.map +1 -1
  414. package/dist/src/types/validation.js +60 -0
  415. package/dist/src/types/validation.js.map +1 -1
  416. package/dist/src/util/ast-spine-clone.d.ts +23 -0
  417. package/dist/src/util/ast-spine-clone.d.ts.map +1 -0
  418. package/dist/src/util/ast-spine-clone.js +41 -0
  419. package/dist/src/util/ast-spine-clone.js.map +1 -0
  420. package/dist/src/util/coercion.d.ts +13 -0
  421. package/dist/src/util/coercion.d.ts.map +1 -1
  422. package/dist/src/util/coercion.js +10 -1
  423. package/dist/src/util/coercion.js.map +1 -1
  424. package/dist/src/util/comparison.d.ts +114 -0
  425. package/dist/src/util/comparison.d.ts.map +1 -1
  426. package/dist/src/util/comparison.js +187 -27
  427. package/dist/src/util/comparison.js.map +1 -1
  428. package/dist/src/vtab/best-access-plan.d.ts +87 -0
  429. package/dist/src/vtab/best-access-plan.d.ts.map +1 -1
  430. package/dist/src/vtab/best-access-plan.js +87 -0
  431. package/dist/src/vtab/best-access-plan.js.map +1 -1
  432. package/dist/src/vtab/manifest.d.ts +7 -0
  433. package/dist/src/vtab/manifest.d.ts.map +1 -1
  434. package/dist/src/vtab/memory/connection.d.ts +14 -1
  435. package/dist/src/vtab/memory/connection.d.ts.map +1 -1
  436. package/dist/src/vtab/memory/connection.js +20 -2
  437. package/dist/src/vtab/memory/connection.js.map +1 -1
  438. package/dist/src/vtab/memory/layer/base.d.ts +12 -5
  439. package/dist/src/vtab/memory/layer/base.d.ts.map +1 -1
  440. package/dist/src/vtab/memory/layer/base.js +20 -9
  441. package/dist/src/vtab/memory/layer/base.js.map +1 -1
  442. package/dist/src/vtab/memory/layer/connection.d.ts +17 -0
  443. package/dist/src/vtab/memory/layer/connection.d.ts.map +1 -1
  444. package/dist/src/vtab/memory/layer/connection.js +26 -0
  445. package/dist/src/vtab/memory/layer/connection.js.map +1 -1
  446. package/dist/src/vtab/memory/layer/manager.d.ts +156 -31
  447. package/dist/src/vtab/memory/layer/manager.d.ts.map +1 -1
  448. package/dist/src/vtab/memory/layer/manager.js +695 -158
  449. package/dist/src/vtab/memory/layer/manager.js.map +1 -1
  450. package/dist/src/vtab/memory/layer/plan-filter.d.ts +41 -20
  451. package/dist/src/vtab/memory/layer/plan-filter.d.ts.map +1 -1
  452. package/dist/src/vtab/memory/layer/plan-filter.js +57 -27
  453. package/dist/src/vtab/memory/layer/plan-filter.js.map +1 -1
  454. package/dist/src/vtab/memory/layer/row-convert.d.ts +21 -0
  455. package/dist/src/vtab/memory/layer/row-convert.d.ts.map +1 -0
  456. package/dist/src/vtab/memory/layer/row-convert.js +30 -0
  457. package/dist/src/vtab/memory/layer/row-convert.js.map +1 -0
  458. package/dist/src/vtab/memory/layer/scan-layer.d.ts +6 -3
  459. package/dist/src/vtab/memory/layer/scan-layer.d.ts.map +1 -1
  460. package/dist/src/vtab/memory/layer/scan-layer.js +48 -34
  461. package/dist/src/vtab/memory/layer/scan-layer.js.map +1 -1
  462. package/dist/src/vtab/memory/layer/transaction.d.ts +142 -19
  463. package/dist/src/vtab/memory/layer/transaction.d.ts.map +1 -1
  464. package/dist/src/vtab/memory/layer/transaction.js +237 -73
  465. package/dist/src/vtab/memory/layer/transaction.js.map +1 -1
  466. package/dist/src/vtab/memory/module.d.ts +14 -2
  467. package/dist/src/vtab/memory/module.d.ts.map +1 -1
  468. package/dist/src/vtab/memory/module.js +60 -41
  469. package/dist/src/vtab/memory/module.js.map +1 -1
  470. package/dist/src/vtab/memory/table.d.ts +1 -1
  471. package/dist/src/vtab/memory/table.d.ts.map +1 -1
  472. package/dist/src/vtab/memory/table.js +12 -4
  473. package/dist/src/vtab/memory/table.js.map +1 -1
  474. package/dist/src/vtab/memory/types.d.ts +27 -1
  475. package/dist/src/vtab/memory/types.d.ts.map +1 -1
  476. package/dist/src/vtab/memory/types.js +16 -1
  477. package/dist/src/vtab/memory/types.js.map +1 -1
  478. package/dist/src/vtab/memory/utils/primary-key.d.ts +9 -0
  479. package/dist/src/vtab/memory/utils/primary-key.d.ts.map +1 -1
  480. package/dist/src/vtab/memory/utils/primary-key.js +11 -0
  481. package/dist/src/vtab/memory/utils/primary-key.js.map +1 -1
  482. package/dist/src/vtab/module.d.ts +63 -0
  483. package/dist/src/vtab/module.d.ts.map +1 -1
  484. package/dist/src/vtab/table.d.ts +33 -7
  485. package/dist/src/vtab/table.d.ts.map +1 -1
  486. package/dist/src/vtab/table.js.map +1 -1
  487. package/package.json +3 -3
  488. package/dist/src/planner/rules/cache/rule-in-subquery-cache.d.ts +0 -26
  489. package/dist/src/planner/rules/cache/rule-in-subquery-cache.d.ts.map +0 -1
  490. package/dist/src/planner/rules/cache/rule-in-subquery-cache.js +0 -66
  491. package/dist/src/planner/rules/cache/rule-in-subquery-cache.js.map +0 -1
@@ -5,15 +5,18 @@ import { QuereusError } from '../../common/errors.js';
5
5
  import { StatusCode } from '../../common/types.js';
6
6
  import { createLogger } from '../../common/logger.js';
7
7
  import { buildColumnIndexMap, withGeneratedColumnGraph, requireVtabModule, resolveNamedConstraintClass, validateCollationForType } from '../../schema/table.js';
8
- import { validateForeignKeyOverExistingRows, validateForeignKeyCollations, extractColumnLevelCheckConstraints, extractColumnLevelForeignKeys, extractColumnLevelUniqueConstraints } from '../../schema/constraint-builder.js';
8
+ import { validateForeignKeyCollations, buildForeignKeyConstraintSchema, extractColumnLevelCheckConstraints, extractColumnLevelForeignKeys, extractColumnLevelUniqueConstraints } from '../../schema/constraint-builder.js';
9
9
  import { MemoryTableModule } from '../../vtab/memory/module.js';
10
10
  import { quoteIdentifier, expressionToString, astToString } from '../../emit/ast-stringify.js';
11
11
  import { renameTableInAst, renameColumnInAst, renameColumnInCheckExpression } from '../../schema/rename-rewriter.js';
12
+ import { assertCatalogObjectPersistable, assertRenameDependentsPersistable } from '../../schema/catalog-persistability.js';
12
13
  import { tryFoldLiteral } from '../../parser/utils.js';
13
14
  import { isTruthy } from '../../util/comparison.js';
14
15
  import { assertDdlTransactionPolicy } from './ddl-transaction-policy.js';
16
+ import { validateAndParse } from '../../types/validation.js';
15
17
  import { snapshotStaleMaterializedViews, propagateTableRenameToMaterializedViews, propagateColumnRenameToMaterializedViews, restoreUnaffectedMaterializedViews, attachMaintainedDerivation, detachMaintainedDerivation, } from './materialized-view-helpers.js';
16
18
  import { isMaintainedTable } from '../../schema/derivation.js';
19
+ import { inferType } from '../../types/registry.js';
17
20
  const log = createLogger('runtime:emit:alter-table');
18
21
  /** A scheduled sub-program resolved to a callback the emitter invokes per row. */
19
22
  function qualifyTableName(schemaName, tableName) {
@@ -156,6 +159,20 @@ async function runRenameTable(rctx, tableSchema, schema, newName) {
156
159
  if (schema.getTable(newName)) {
157
160
  throw new QuereusError(`Table '${newName}' already exists`, StatusCode.ERROR);
158
161
  }
162
+ // Pre-flight, BEFORE the first side effect (`module.renameTable`): every catalog
163
+ // entry this rename would rewrite must still be persistable. Both the rename
164
+ // propagation and the store's catalog write are unfailable (notifier try/catch,
165
+ // then an async persist queue), so without this the statement reports success and
166
+ // the dependent silently diverges from — or vanishes from — the durable catalog.
167
+ if (isMaintainedTable(tableSchema)) {
168
+ // The renamed table is itself a materialized view: its own catalog entry moves
169
+ // (`materialized_view_removed` old → `materialized_view_added` new), so vet the
170
+ // prospective record's new KEY and DDL text. The self-reference rewrite
171
+ // `rewriteTableForTableRename` performs is not applied to the probe — it only
172
+ // substitutes `newName`, which is already under test as the record's own name.
173
+ assertCatalogObjectPersistable(rctx.db, 'materializedView', { ...tableSchema, name: newName });
174
+ }
175
+ assertRenameDependentsPersistable(rctx.db, schema, ast => renameTableInAst(ast, oldName, newName, tableSchema.schemaName));
159
176
  // Clone schema with new name
160
177
  const updatedTableSchema = {
161
178
  ...tableSchema,
@@ -169,6 +186,24 @@ async function runRenameTable(rctx, tableSchema, schema, newName) {
169
186
  if (module.renameTable) {
170
187
  await module.renameTable(rctx.db, tableSchema.schemaName, oldName, newName);
171
188
  }
189
+ // Events this transaction already recorded still carry the OLD name; relabel them so
190
+ // the commit delivers every event under the name the table has at delivery. AFTER the
191
+ // module call (a module failure must leave the batch as untouched as the catalog, and
192
+ // the store's `ddlCommitPendingOps` flushes its queued events into our batch DURING
193
+ // that call — those must be in the batch before we walk it), BEFORE the catalog swap,
194
+ // matching where the other ALTER arms call `remapBatchedDataEvents`.
195
+ //
196
+ // NOTE: batched SCHEMA events are deliberately out of scope here. A schema event
197
+ // records a DDL operation, not current state — relabelling `objectName` without
198
+ // rewriting its `ddl` text would produce an incoherent instruction. How a rename
199
+ // crosses the wire to a peer is `fix/sync-schema-migrations-replicate-empty-ddl`.
200
+ rctx.db._getEventEmitter().renameBatchedEvents(tableSchema.schemaName, oldName, newName);
201
+ // Deferred constraint checks this transaction already parked carry evaluators compiled
202
+ // against the OLD name — their scan leaves would connect to a table that no longer
203
+ // exists (or, on a store backend, to an empty one, yielding a FALSE violation). Tell
204
+ // the queue so it re-points those entries and moves the bucket keyed by the table name.
205
+ // Same placement rationale as the event relabel above.
206
+ rctx.db.getDeferredConstraints().notifyTableRename(tableSchema.schemaName, oldName, newName);
172
207
  // The renamed table's own definition can name itself: a self-referencing FK's
173
208
  // `referencedTable`, a table-qualified CHECK expression, a table-qualified
174
209
  // partial-index predicate. Rewrite those BEFORE the catalog swap and the notify
@@ -238,6 +273,16 @@ async function runRenameColumn(rctx, tableSchema, schema, oldName, newName) {
238
273
  if (oldName.toLowerCase() !== newNameLower && tableSchema.columnIndexMap.has(newNameLower)) {
239
274
  throw new QuereusError(`Column '${newName}' already exists in table '${tableSchema.name}'`, StatusCode.ERROR);
240
275
  }
276
+ // Pre-flight, BEFORE the first side effect (`module.alterTable`): the rewritten
277
+ // body of every dependent view / materialized view must still be persistable —
278
+ // same unfailable-propagation reasoning as the table-rename arm above. Shares one
279
+ // resolver with `propagateColumnRename` so the two passes cannot drift apart in
280
+ // code; what makes them agree at RUNTIME (the resolver reads the LIVE catalog, and
281
+ // this probe runs pre-mutation while the propagation runs post-) is that
282
+ // `isTableInUnaliasedScope` skips the renamed table itself and probes only OTHER
283
+ // sources, whose column sets this rename does not touch.
284
+ const resolveColumnInSource = buildColumnSourceResolver(rctx.db);
285
+ assertRenameDependentsPersistable(rctx.db, schema, ast => renameColumnInAst(ast, tableSchema.name, oldName, newName, tableSchema.schemaName, resolveColumnInSource));
241
286
  const existingCol = tableSchema.columns[colIndex];
242
287
  // Build a ColumnDef AST for the renamed column (preserving type info)
243
288
  const newColumnDef = {
@@ -265,6 +310,12 @@ async function runRenameColumn(rctx, tableSchema, schema, oldName, newName) {
265
310
  columnIndexMap: buildColumnIndexMap(updatedCols),
266
311
  };
267
312
  }
313
+ // A rename moves no value and changes no arity, so the batched events' row images are
314
+ // already right — but their `changedColumns` still names the OLD column. Re-derive it
315
+ // against the new names with an identity row map, so no delivered event names a column
316
+ // the table no longer has. (Modules that emit at commit from their own queue read the
317
+ // current schema then, so they need nothing here.)
318
+ await rctx.db._getEventEmitter().remapBatchedDataEvents(tableSchema.schemaName, tableSchema.name, (row) => row, updatedTableSchema.columns.map(c => c.name));
268
319
  // Update the schema catalog
269
320
  schema.addTable(updatedTableSchema);
270
321
  // Snapshot pre-statement MV staleness BEFORE the notify below: the notify's
@@ -281,7 +332,7 @@ async function runRenameColumn(rctx, tableSchema, schema, oldName, newName) {
281
332
  });
282
333
  // Propagate the rename into dependent objects (CHECK / FK / partial-index
283
334
  // predicates in this and other tables, view and materialized-view bodies).
284
- await propagateColumnRename(rctx, tableSchema.schemaName, tableSchema.name, oldName, newName, preStaleMvs);
335
+ await propagateColumnRename(rctx, tableSchema.schemaName, tableSchema.name, oldName, newName, preStaleMvs, resolveColumnInSource);
285
336
  log('Renamed column %s.%s.%s to %s', tableSchema.schemaName, tableSchema.name, oldName, newName);
286
337
  return null;
287
338
  }
@@ -299,6 +350,10 @@ async function runAddColumn(rctx, tableSchema, schema, columnDef, backfill, back
299
350
  // and, when it does not fold to a literal, compiled into `backfill` — the default
300
351
  // evaluated against the existing row, so `new.<column>` reads that row's sibling.
301
352
  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;
302
357
  // Call module.alterTable for data + schema update
303
358
  const module = requireVtabModule(tableSchema);
304
359
  if (!module.alterTable) {
@@ -318,19 +373,28 @@ async function runAddColumn(rctx, tableSchema, schema, columnDef, backfill, back
318
373
  const delegatesBackfill = module.getCapabilities?.().delegatesNotNullBackfill === true;
319
374
  const hasNotNull = columnDef.constraints?.some(c => c.type === 'notNull') ?? false;
320
375
  if (hasNotNull && !delegatesBackfill && !backfill) {
321
- const folded = defaultConstraint?.expr ? tryFoldLiteral(defaultConstraint.expr) : undefined;
322
- const defaultIsNullish = !defaultConstraint?.expr || folded === null;
376
+ const defaultIsNullish = !defaultConstraint?.expr || foldedDefault === null;
323
377
  if (defaultIsNullish) {
324
378
  await validateNotNullBackfill(rctx, tableSchema, columnDef.name);
325
379
  }
326
380
  }
327
- // Extract column-level CHECK / FK constraints to merge into the engine-side schema below.
328
- // Column-level UNIQUE is handled separately, right after the column is materialized (see
329
- // the inline-UNIQUE block below): it routes through the module's `addConstraint` UNIQUE
330
- // path the same path `ALTER TABLE ADD CONSTRAINT UNIQUE` uses so it is materialized,
331
- // enforced, and (for store-backed modules) persisted, symmetric with CREATE TABLE.
332
- const newCheckConstraints = extractColumnLevelCheckConstraints(columnDef);
333
- const newForeignKeys = extractColumnLevelForeignKeys(columnDef, tableSchema.schemaName);
381
+ // Synthesize the table-level equivalent of every constraint declared inline on the new
382
+ // column. All three kinds go to the module via `addConstraint` below — the same path
383
+ // `ALTER TABLE ADD CONSTRAINT` uses so the module owns them exactly as it owns a
384
+ // constraint declared in CREATE TABLE, and they survive every later structural ALTER
385
+ // (which installs the module's returned schema in the catalog verbatim).
386
+ //
387
+ // Extracted BEFORE the column is materialized so a malformed declaration (e.g. a
388
+ // multi-parent-column FK on a single ADD COLUMN) throws while the table is still
389
+ // untouched. Install order within the loop is UNIQUE → CHECK → FK, so a column
390
+ // declaring several kinds reports the cheapest-to-explain violation first; the
391
+ // literal-default CHECK scan runs ahead of the whole loop (see below).
392
+ const inlineChecks = extractColumnLevelCheckConstraints(columnDef);
393
+ const inlineConstraints = [
394
+ ...extractColumnLevelUniqueConstraints(columnDef),
395
+ ...inlineChecks,
396
+ ...extractColumnLevelForeignKeys(columnDef),
397
+ ];
334
398
  // A non-foldable default backfills each existing row from its own value. Install a row
335
399
  // slot over the default's row descriptor; the evaluator the module calls per existing
336
400
  // row sets the slot to that row, so the default's `new.<col>` refs resolve to it.
@@ -369,185 +433,226 @@ async function runAddColumn(rctx, tableSchema, schema, columnDef, backfill, back
369
433
  // scan below re-reads the table — so the backfill's context does not shadow the
370
434
  // scan's own row context.
371
435
  let updatedTableSchema;
436
+ // Slot the module actually placed the new column at (the module API permits
437
+ // `insertAtIndex`; SQL always appends). Recorded so the revert paths below can apply
438
+ // the inverse event remap.
439
+ let addedColIndex;
372
440
  try {
373
441
  updatedTableSchema = await module.alterTable(rctx.db, tableSchema.schemaName, tableSchema.name, {
374
442
  type: 'addColumn',
375
443
  columnDef,
376
444
  backfillEvaluator,
377
445
  });
446
+ // Events this transaction already batched for the table still describe the
447
+ // pre-ADD column set; insert the backfilled value at the new slot so a listener
448
+ // at commit pairs value i with column i of the schema current at delivery.
449
+ // Covers the engine auto-event path and the store module (which flushed its
450
+ // queued events into the batch during the ALTER); the memory module's own
451
+ // pending-change log is reshaped inside its alterTable. Must run INSIDE this
452
+ // try: the backfill evaluator closes over rowSlot/checkSlot, which the finally
453
+ // below closes the moment the module returns.
454
+ addedColIndex = updatedTableSchema.columnIndexMap.get(columnDef.name.toLowerCase());
455
+ if (addedColIndex !== undefined) {
456
+ const insertAt = addedColIndex;
457
+ await rctx.db._getEventEmitter().remapBatchedDataEvents(tableSchema.schemaName, tableSchema.name, async (row) => {
458
+ // oldRow gets the SAME map as newRow: the literal default, or the backfill
459
+ // evaluator applied to the pre-image itself (the evaluator is a function of
460
+ // a row, and the pre-image is a row), falling back to NULL — the honest
461
+ // "column did not exist yet" placeholder, which errs toward REPORTING a
462
+ // change on the new column rather than suppressing one. Rejected: reusing
463
+ // the newRow result for oldRow (makes oldRow[new] === newRow[new] always,
464
+ // so a diffing consumer never syncs the added column); suppressing the
465
+ // pre-ALTER oldRow (silently turns updates into upserts).
466
+ let value = foldedDefault ?? null;
467
+ if (backfillEvaluator) {
468
+ try {
469
+ value = await backfillEvaluator(row);
470
+ }
471
+ catch {
472
+ // Best-effort: a historical image may fail the evaluator (or its
473
+ // CHECKs) where every live row backfilled cleanly. Never abort
474
+ // the ALTER for an event image.
475
+ value = null;
476
+ }
477
+ }
478
+ return [...row.slice(0, insertAt), value, ...row.slice(insertAt)];
479
+ }, updatedTableSchema.columns.map(c => c.name));
480
+ }
378
481
  }
379
482
  finally {
380
483
  rowSlot?.close();
381
484
  checkSlot?.close();
382
485
  }
383
- // Materialize + enforce any inline column-level UNIQUE(s) on the new column. CREATE TABLE
384
- // routes inline UNIQUE through `extractUniqueConstraints`; `ALTER TABLE ADD CONSTRAINT
385
- // UNIQUE` routes it through `module.alterTable({ addConstraint })`. The imperative ADD
386
- // COLUMN path reaches neither, so without this an inline UNIQUE would be silently dropped —
387
- // never materialized, enforced, or rejected. Convert each into the equivalent table-level
388
- // constraint over the just-added column and feed it to the same addConstraint path, so the
389
- // module builds/reuses its covering structure, validates the existing rows (throwing
390
- // CONSTRAINT on the first duplicate), and (store) persists. Each call returns a schema
391
- // carrying the new column + the unique constraint (+ memory covering index); thread the
392
- // latest forward so the CHECK/FK merge below layers naturally on top.
393
- //
394
- // Ordering: the column is already materialized (so it resolves in `columnIndexMap`), and the
395
- // engine catalog is untouched until the first `schema.addTable` below. So on a UNIQUE failure
396
- // (e.g. a literal DEFAULT that backfills the same value to ≥2 existing rows → immediate
397
- // duplicate) we only drop the just-added column from the module and rethrow no catalog
398
- // restore. The module's own addConstraint already rolled back its half-built covering
399
- // structure before throwing.
400
- const inlineUniqueConstraints = extractColumnLevelUniqueConstraints(columnDef);
401
- for (const uniqueConstraint of inlineUniqueConstraints) {
402
- try {
403
- updatedTableSchema = await module.alterTable(rctx.db, tableSchema.schemaName, tableSchema.name, {
404
- type: 'addConstraint',
405
- constraint: uniqueConstraint,
406
- });
407
- }
408
- catch (err) {
409
- try {
410
- await module.alterTable(rctx.db, tableSchema.schemaName, tableSchema.name, {
411
- type: 'dropColumn',
412
- columnName: columnDef.name,
413
- });
414
- }
415
- catch (revertErr) {
416
- log('Failed to revert ADD COLUMN after inline UNIQUE violation: %s', revertErr.message);
417
- }
418
- throw err;
419
- }
420
- }
421
- // Resolve the new child column index in the freshly returned schema for any FK constraints.
422
- const newColIdx = updatedTableSchema.columnIndexMap.get(columnDef.name.toLowerCase());
423
- const resolvedForeignKeys = newColIdx !== undefined
424
- ? newForeignKeys.map(fk => ({ ...fk, columns: Object.freeze([newColIdx]) }))
425
- : newForeignKeys;
426
- // Merge new column-level CHECK / FK into the table-level constraint sets so the
427
- // existing constraint-builder picks them up for INSERT/UPDATE enforcement.
428
- const mergedChecks = newCheckConstraints.length > 0
429
- ? Object.freeze([...updatedTableSchema.checkConstraints, ...newCheckConstraints])
430
- : updatedTableSchema.checkConstraints;
431
- const mergedForeignKeys = resolvedForeignKeys.length > 0
432
- ? Object.freeze([...(updatedTableSchema.foreignKeys ?? []), ...resolvedForeignKeys])
433
- : updatedTableSchema.foreignKeys;
434
- const enhancedBase = {
435
- ...updatedTableSchema,
436
- checkConstraints: mergedChecks,
437
- foreignKeys: mergedForeignKeys,
438
- };
439
- // Recompute the generated-column dependency graph. If the added column is
440
- // generated and its expression references an unknown column, or any new
441
- // generated-column edges form a cycle, this throws before we register the
442
- // new schema in the catalog.
443
- const enhancedTableSchema = withGeneratedColumnGraph(enhancedBase);
444
- // The optimizer trusts a DECLARED constraint as a proven invariant, which makes
445
- // the existing-row validators below fold away their own work if the new constraint
446
- // is already live:
447
- // - A new FK seeds an inclusion dependency `child.fk ⊆ parent.pk`; the FK
448
- // validator's `not exists` anti-join folds to EmptyRelation under
449
- // `ruleAntiJoinFkEmpty` (+ the INDs seeded at TableReferenceNode).
450
- // - A new CHECK `<p>` seeds a domain constraint on the scan; the CHECK post-scan's
451
- // own `where not (<p>)` folds to EmptyRelation under `ruleFilterContradiction`
452
- // (the domain `<p>` and the predicate `not <p>` are jointly unsatisfiable).
453
- // Either fold makes validation trust the very invariant it is checking and silently
454
- // admit a violating row. So register the new COLUMN with only the PRE-EXISTING
455
- // (already-proven) constraints for the validation pass, then register the full schema
456
- // — with the new FK(s) and CHECK(s) — only once validation passes. This mirrors the
457
- // ADD CONSTRAINT path, which validates before swapping the constraint into the live
458
- // schema. Pre-existing constraints are kept: they held before this ALTER, so folding
459
- // against them is sound and preserves the optimizer's reach.
460
- const hasNewForeignKeys = resolvedForeignKeys.length > 0;
461
- const hasNewChecks = newCheckConstraints.length > 0;
462
- const usesIntermediateSchema = hasNewForeignKeys || hasNewChecks;
463
- const validationSchema = usesIntermediateSchema
464
- ? withGeneratedColumnGraph({
465
- ...enhancedBase,
466
- checkConstraints: updatedTableSchema.checkConstraints,
467
- foreignKeys: updatedTableSchema.foreignKeys,
468
- })
469
- : enhancedTableSchema;
470
- schema.addTable(validationSchema);
471
- // Validate new CHECK constraints against the (already-backfilled) rows AND validate
472
- // existing rows against any new column-level FK, reverting (drop the column + restore
473
- // the original catalog entry) on a violation. Both run inside a single try/revert
474
- // region so that when both a new CHECK and a new FK exist and either fails, the same
475
- // revert path fires.
476
- //
477
- // CHECK is gated on `!backfill`: NOT NULL of a per-row default is enforced by the module
478
- // during backfill (it has the values in-hand and throws before the column is committed),
479
- // and the per-row (evaluator) default path already enforced each CHECK inside the backfill
480
- // hook above (against the freshly-computed value, not a stale snapshot). The CHECK post-scan
481
- // is therefore only correct for the literal-default path, whose values were bulk-written by
482
- // the module without a per-row hook.
483
- //
484
- // FK runs for ALL default kinds. It is a cross-table existence check, not a per-row
485
- // predicate, so it must be a post-`alterTable` scan: the scan sees both the bulk-written
486
- // (literal) and per-row-evaluated backfilled values, and for a self-referential FK it reads
487
- // a consistent post-alter table (a per-row hook would have to query the very table being
488
- // rebuilt). It reuses `validateForeignKeyOverExistingRows` — the same MATCH-SIMPLE,
489
- // pragma-gated validator the ADD CONSTRAINT path calls — so the two paths can never drift.
490
- const runCheckScan = !backfill && newCheckConstraints.length > 0;
491
- if (runCheckScan || hasNewForeignKeys) {
492
- try {
493
- // Reject any new FK whose child/parent column collations declare a same-rank
494
- // conflict (the conflict enforcement would raise at first DML). Pure schema
495
- // check — no row scan, pragma-independent — but kept inside this try/revert
496
- // region so a conflict drops the just-materialized column and restores the
497
- // original catalog, leaving the table untouched. `enhancedTableSchema` carries
498
- // the new column so the child FK column resolves.
499
- for (const fk of resolvedForeignKeys) {
500
- validateForeignKeyCollations(rctx.db, enhancedTableSchema, fk);
501
- }
502
- if (runCheckScan) {
503
- await validateBackfillAgainstChecks(rctx, validationSchema, newCheckConstraints);
504
- }
505
- for (const fk of resolvedForeignKeys) {
506
- // `enhancedTableSchema` supplies only column-name resolution here; the LIVE
507
- // schema the planner reads is `validationSchema`, which omits the new FK(s)
508
- // and CHECK(s), so the anti-join is not folded.
509
- await validateForeignKeyOverExistingRows(rctx.db, enhancedTableSchema, fk);
510
- }
486
+ // The column is materialized; now install the inline constraints. Names of the CHECK /
487
+ // FK ones the module has accepted so far, so a later failure can hand each back to
488
+ // `dropConstraint` before the column itself goes (see {@link revertAddColumn}).
489
+ const installedConstraintNames = [];
490
+ let finalTableSchema;
491
+ try {
492
+ // Recompute the generated-column dependency graph. If the added column is generated
493
+ // and its expression references an unknown column, or any new generated-column edges
494
+ // form a cycle, this throws and the revert below undoes the materialization.
495
+ const columnOnlySchema = withGeneratedColumnGraph(updatedTableSchema);
496
+ // Register the COLUMN-ONLY schema before installing any inline constraint. Two
497
+ // properties depend on this ordering, and both are easy to break:
498
+ //
499
+ // - The module's FK arm validates existing rows with SQL planned against the LIVE
500
+ // catalog, so the new column has to resolve there. Likewise the CHECK backfill
501
+ // scan below.
502
+ // - The new constraint must NOT be live while its own validation runs. The optimizer
503
+ // trusts a DECLARED constraint as a proven invariant: a declared FK seeds the
504
+ // inclusion dependency `child.fk parent.pk` and folds the validator's own
505
+ // `not exists` anti-join to EmptyRelation (`ruleAntiJoinFkEmpty`); a declared CHECK
506
+ // `<p>` seeds a domain constraint that folds the CHECK scan's `where not (<p>)`
507
+ // away (`ruleFilterContradiction`). Either fold makes validation trust the very
508
+ // thing it is checking and silently admit a violating row. The module holds each
509
+ // new constraint in its own cached schema until that constraint's validation
510
+ // passes, and the catalog only learns of them from `finalTableSchema` below.
511
+ //
512
+ // Pre-existing constraints stay live throughout: they held before this ALTER, so
513
+ // folding against them is sound and preserves the optimizer's reach.
514
+ schema.addTable(columnOnlySchema);
515
+ // 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
519
+ // before the whole install loop below, so on a column declaring several kinds a
520
+ // CHECK violation is reported ahead of a UNIQUE or FK one.
521
+ if (!backfill && inlineChecks.length > 0) {
522
+ await validateBackfillAgainstChecks(rctx, columnOnlySchema, inlineChecks);
511
523
  }
512
- catch (err) {
513
- // Revert: drop the column and restore the original catalog entry.
514
- try {
515
- await module.alterTable(rctx.db, tableSchema.schemaName, tableSchema.name, {
516
- type: 'dropColumn',
517
- columnName: columnDef.name,
518
- });
524
+ // Starts as the column-only schema so a column with no inline constraint needs no
525
+ // further work; each accepted constraint replaces it with the module's answer.
526
+ //
527
+ // NOTE: one module round-trip per inline constraint — each takes the module's
528
+ // schema-change latch and, store-backed, writes the table's DDL again. Fine at the
529
+ // counts SQL produces (a column declares 0 or 1 of each kind); if a batched
530
+ // `addConstraint` arm ever appears, or ADD COLUMN becomes hot, hand the whole set
531
+ // over in one call instead.
532
+ let current = columnOnlySchema;
533
+ for (const constraint of inlineConstraints) {
534
+ // A CHECK / FK is dropped by NAME on the revert path, so resolve the name the
535
+ // module will store BEFORE handing the constraint over. UNIQUE needs no entry:
536
+ // an unnamed one has no name to drop by, and the module's own DROP COLUMN
537
+ // prunes a UNIQUE over the dropped column (which CHECK / FK are not).
538
+ let installedName;
539
+ if (constraint.type === 'foreignKey') {
540
+ // Reject a same-rank child/parent collation conflict (which enforcement would
541
+ // raise at the first DML) BEFORE `module.alterTable`, so a rejected ALTER never
542
+ // reaches the module's persistence side effects — mirroring
543
+ // `runAddConstraintViaModule`. Built with the same builder and columnIndexMap
544
+ // the module uses, so the name and column indices are identical to its own.
545
+ const fk = buildForeignKeyConstraintSchema(constraint, columnOnlySchema.columnIndexMap, tableSchema.name, tableSchema.schemaName);
546
+ validateForeignKeyCollations(rctx.db, columnOnlySchema, fk);
547
+ installedName = fk.name;
519
548
  }
520
- catch (revertErr) {
521
- log('Failed to revert ADD COLUMN after constraint violation: %s', revertErr.message);
549
+ else if (constraint.type === 'check') {
550
+ installedName = constraint.name; // always set by the extractor
522
551
  }
523
- schema.addTable(tableSchema);
524
- throw err;
552
+ // The module materializes (UNIQUE's covering structure), validates the existing
553
+ // rows as the kind requires (UNIQUE duplicates, pragma-gated MATCH SIMPLE FK
554
+ // orphans) and — store-backed — persists. Thread the returned schema forward so
555
+ // each constraint layers on the last. The module's answer carries no
556
+ // generated-column bookkeeping of its own, so re-derive it each round.
557
+ const withConstraint = await module.alterTable(rctx.db, tableSchema.schemaName, tableSchema.name, {
558
+ type: 'addConstraint',
559
+ constraint,
560
+ });
561
+ current = withGeneratedColumnGraph(withConstraint);
562
+ if (installedName !== undefined)
563
+ installedConstraintNames.push(installedName);
525
564
  }
565
+ finalTableSchema = current;
526
566
  }
527
- // Validation passed — commit the full schema (with the new FK(s)/CHECK(s)) into the
528
- // catalog. Skipped when no intermediate schema was used, in which case
529
- // `validationSchema === enhancedTableSchema` is already registered.
530
- if (usesIntermediateSchema) {
531
- schema.addTable(enhancedTableSchema);
567
+ catch (err) {
568
+ await revertAddColumn(rctx, tableSchema, schema, columnDef.name, addedColIndex, installedConstraintNames);
569
+ throw err;
532
570
  }
571
+ // Every constraint validated and installed — publish the module's final schema.
572
+ schema.addTable(finalTableSchema);
533
573
  rctx.db.schemaManager.getChangeNotifier().notifyChange({
534
574
  type: 'table_modified',
535
575
  schemaName: tableSchema.schemaName,
536
576
  objectName: tableSchema.name,
537
577
  oldObject: tableSchema,
538
- newObject: enhancedTableSchema,
578
+ newObject: finalTableSchema,
539
579
  });
540
580
  log('Added column %s to table %s.%s', columnDef.name, tableSchema.schemaName, tableSchema.name);
541
581
  return null;
542
582
  }
543
583
  /**
544
- * Runs each new CHECK against existing rows. We rely on the just-registered
545
- * enhanced schema so SQL can resolve the new column. Any row matching
584
+ * Undoes a partially-applied ADD COLUMN, leaving the table exactly as it was: hands each
585
+ * inline CHECK / FK the module already accepted back to `dropConstraint` (newest first),
586
+ * drops the column, un-remaps the batched events, and restores the original catalog entry.
587
+ *
588
+ * The constraints must go before the column: neither built-in module prunes a CHECK / FK
589
+ * over a dropped column, so a stranded one would keep naming a column the table no longer
590
+ * has. (An inline UNIQUE needs no explicit drop — both modules prune a UNIQUE over the
591
+ * dropped column, and an unnamed one has no name to drop by.)
592
+ *
593
+ * Best-effort on the module half: a revert failure is logged, never thrown, so it cannot
594
+ * mask the original violation. Restoring the catalog entry is a no-op when the ALTER failed
595
+ * before registering anything (the original schema is still the live one).
596
+ *
597
+ * NOTE: the hand-back is by NAME, so it assumes a name resolves to the constraint this
598
+ * ALTER installed. A pre-existing constraint can legitimately share an auto-name (nothing
599
+ * rejects `constraint _check_w check (…)` on a table that later gets `add column w …
600
+ * check (…)`; `create table` collides the same way), and today both modules' DROP
601
+ * CONSTRAINT removes every match, which lands on the right end state. If constraint-name
602
+ * resolution ever narrows to a single match, revert must instead identify the installed
603
+ * constraint by identity — otherwise it can drop the pre-existing one and leave ours.
604
+ */
605
+ async function revertAddColumn(rctx, tableSchema, schema, columnName, addedColIndex, installedConstraintNames) {
606
+ try {
607
+ const module = requireVtabModule(tableSchema);
608
+ // Unreachable: runAddColumn requires `alterTable` before materializing anything.
609
+ if (!module.alterTable)
610
+ return;
611
+ for (let i = installedConstraintNames.length - 1; i >= 0; i--) {
612
+ await module.alterTable(rctx.db, tableSchema.schemaName, tableSchema.name, {
613
+ type: 'dropConstraint',
614
+ constraintName: installedConstraintNames[i],
615
+ });
616
+ }
617
+ await module.alterTable(rctx.db, tableSchema.schemaName, tableSchema.name, {
618
+ type: 'dropColumn',
619
+ columnName,
620
+ });
621
+ await remapEventsForRevertedAddColumn(rctx, tableSchema, addedColIndex);
622
+ }
623
+ catch (revertErr) {
624
+ log('Failed to revert ADD COLUMN %s.%s.%s: %s', tableSchema.schemaName, tableSchema.name, columnName, revertErr.message);
625
+ }
626
+ schema.addTable(tableSchema);
627
+ }
628
+ /**
629
+ * Inverse of {@link runAddColumn}'s batched-event remap, for its revert paths: the
630
+ * just-added column has been dropped from the module again, so the batched events must
631
+ * drop the slot too or they keep describing a column the table no longer has. No-op when
632
+ * the forward remap never ran (`addedColIndex` undefined). Best-effort like the forward
633
+ * remap — never masks the original constraint error.
634
+ */
635
+ async function remapEventsForRevertedAddColumn(rctx, tableSchema, addedColIndex) {
636
+ if (addedColIndex === undefined)
637
+ return;
638
+ await rctx.db._getEventEmitter().remapBatchedDataEvents(tableSchema.schemaName, tableSchema.name, (row) => row.filter((_, i) => i !== addedColIndex), tableSchema.columns.map(c => c.name));
639
+ }
640
+ /**
641
+ * Runs each new CHECK against the (already-backfilled) existing rows. Relies on the
642
+ * just-registered column-only schema so SQL can resolve the new column while the CHECK
643
+ * itself is not yet declared — declaring it first would let `ruleFilterContradiction`
644
+ * fold this scan's own `not (<check_expr>)` to EmptyRelation. Any row matching
546
645
  * `not (<check_expr>)` is a violation and aborts the ALTER.
547
646
  */
548
- async function validateBackfillAgainstChecks(rctx, enhancedTableSchema, newCheckConstraints) {
549
- const qualifiedTable = qualifyTableName(enhancedTableSchema.schemaName, enhancedTableSchema.name);
647
+ async function validateBackfillAgainstChecks(rctx, columnOnlySchema, newCheckConstraints) {
648
+ const qualifiedTable = qualifyTableName(columnOnlySchema.schemaName, columnOnlySchema.name);
550
649
  for (const cc of newCheckConstraints) {
650
+ // `extractColumnLevelCheckConstraints` skips an expression-less CHECK, so this
651
+ // cannot fire — but silently skipping a constraint we were asked to validate
652
+ // would admit a violating row, so say so loudly rather than `continue`.
653
+ if (!cc.expr) {
654
+ throw new QuereusError(`CHECK constraint ${cc.name ? `'${cc.name}' ` : ''}on ALTER TABLE ADD COLUMN has no expression`, StatusCode.INTERNAL);
655
+ }
551
656
  const checkSql = expressionToString(cc.expr);
552
657
  const sql = `select 1 from ${qualifiedTable} where not (${checkSql}) limit 1`;
553
658
  const stmt = rctx.db.prepare(sql);
@@ -558,7 +663,7 @@ async function validateBackfillAgainstChecks(rctx, enhancedTableSchema, newCheck
558
663
  break;
559
664
  }
560
665
  if (violated) {
561
- throw new QuereusError(`CHECK constraint ${cc.name ? `'${cc.name}' ` : ''}violated by backfilled rows in ALTER TABLE ADD COLUMN on '${enhancedTableSchema.name}'`, StatusCode.CONSTRAINT);
666
+ throw new QuereusError(`CHECK constraint ${cc.name ? `'${cc.name}' ` : ''}violated by backfilled rows in ALTER TABLE ADD COLUMN on '${columnOnlySchema.name}'`, StatusCode.CONSTRAINT);
562
667
  }
563
668
  }
564
669
  finally {
@@ -668,6 +773,13 @@ async function runDropColumn(rctx, tableSchema, schema, columnName) {
668
773
  type: 'dropColumn',
669
774
  columnName,
670
775
  });
776
+ // Events this transaction already batched for the table still carry the pre-drop
777
+ // arity; drop the slot so a listener at commit pairs value i with column i of the
778
+ // schema current at delivery (and `changedColumns` never names the dropped column).
779
+ // Pure slot filter — no failure mode. Covers the engine auto-event path and the
780
+ // store module (which flushed its queued events into the batch during the ALTER);
781
+ // the memory module's own pending-change log is reshaped inside its alterTable.
782
+ await rctx.db._getEventEmitter().remapBatchedDataEvents(tableSchema.schemaName, tableSchema.name, (row) => row.filter((_, i) => i !== colIndex), updatedTableSchema.columns.map(c => c.name));
671
783
  // Recompute the generated-column dependency graph against the post-drop
672
784
  // column array — old indices in the previous map are invalid.
673
785
  const finalSchema = withGeneratedColumnGraph(updatedTableSchema);
@@ -803,6 +915,16 @@ async function runAlterColumn(rctx, tableSchema, schema, action) {
803
915
  if (action.setCollation !== undefined) {
804
916
  validateCollationForType(action.setCollation, tableSchema.columns[colIndex].logicalType, action.columnName, (n) => rctx.db.isCollationRegistered(n));
805
917
  }
918
+ // SET DATA TYPE: the column keeps its current collation, so the NEW type has to accept it —
919
+ // otherwise the ALTER mints a column shape CREATE TABLE would refuse and generateTableDDL
920
+ // cannot round-trip (a store-backed table with such a column is silently dropped on rehydrate).
921
+ // Same validator, same error text as CREATE TABLE / SET COLLATE. Rejects uniformly whether the
922
+ // collation was user-declared or inherited from `pragma default_collation`: `collationExplicit`
923
+ // is not persisted, so keying on it would coerce before a reopen and reject after one.
924
+ // Remedy: `SET COLLATE binary` first, then retype.
925
+ if (action.setDataType !== undefined) {
926
+ validateCollationForType(tableSchema.columns[colIndex].collation, inferType(action.setDataType), action.columnName, (n) => rctx.db.isCollationRegistered(n));
927
+ }
806
928
  // Route a SET DEFAULT through the same DDL validator CREATE TABLE uses, so the
807
929
  // stored default is consistent with what INSERT will accept: bind params / bare
808
930
  // columns / non-determinism rejected, `new.<column>` accepted (deferred to INSERT
@@ -823,6 +945,18 @@ async function runAlterColumn(rctx, tableSchema, schema, action) {
823
945
  setDefault: action.setDefault,
824
946
  setCollation: action.setCollation,
825
947
  });
948
+ // Events this transaction already batched still carry the PRE-conversion value at
949
+ // the altered column (`SET DATA TYPE`'s normalization, `SET NOT NULL`'s null →
950
+ // DEFAULT backfill); rewrite it so a listener at commit sees the value the
951
+ // committed row holds. The conversion is engine-derivable (the same
952
+ // `validateAndParse` the memory module's converter wraps), so no module-contract
953
+ // change. `SET COLLATE` / `SET DEFAULT` / `DROP NOT NULL` move no stored value and
954
+ // need no remap — in particular the primary-key `SET COLLATE` re-key leaves every
955
+ // event's key and row images valid as-is.
956
+ const eventValueRemap = alterColumnEventValueRemap(tableSchema, colIndex, action);
957
+ if (eventValueRemap) {
958
+ await rctx.db._getEventEmitter().remapBatchedDataEvents(tableSchema.schemaName, tableSchema.name, (row) => row.map((v, i) => i === colIndex ? eventValueRemap(v) : v), updatedTableSchema.columns.map(c => c.name));
959
+ }
826
960
  schema.addTable(updatedTableSchema);
827
961
  rctx.db.schemaManager.getChangeNotifier().notifyChange({
828
962
  type: 'table_modified',
@@ -834,6 +968,51 @@ async function runAlterColumn(rctx, tableSchema, schema, action) {
834
968
  log('Altered column %s.%s.%s', tableSchema.schemaName, tableSchema.name, action.columnName);
835
969
  return null;
836
970
  }
971
+ /**
972
+ * The per-value map {@link runAlterColumn}'s batched-event remap applies at the altered
973
+ * column, or undefined when the ALTER moves no stored value (SET COLLATE, SET DEFAULT,
974
+ * DROP NOT NULL, an alias retype). The returned function is TOTAL — an unconvertible
975
+ * historical event image keeps its raw value rather than aborting the ALTER; the module
976
+ * already validated every value the transaction can actually SEE, so a failure here is
977
+ * confined to a superseded intermediate image.
978
+ */
979
+ function alterColumnEventValueRemap(tableSchema, colIndex, action) {
980
+ if (action.setDataType !== undefined) {
981
+ const newLogicalType = inferType(action.setDataType);
982
+ // Alias retype (`varchar(50)` IS TEXT): schema-only, values untouched.
983
+ if (newLogicalType === tableSchema.columns[colIndex].logicalType)
984
+ return undefined;
985
+ const columnName = tableSchema.columns[colIndex].name;
986
+ return (v) => {
987
+ if (v === null)
988
+ return v; // retype leaves NULLs untouched, matching the module's conversion
989
+ try {
990
+ return validateAndParse(v, newLogicalType, columnName);
991
+ }
992
+ catch {
993
+ // NOTE: the surviving raw value is honest about what the row held, but its
994
+ // JS type no longer matches the column's logical type — the delivered
995
+ // contract is positional (value i belongs to column i), not typed. Only
996
+ // reachable for a superseded intermediate image (e.g. insert 'zzz' →
997
+ // update to '42' → retype to integer delivers oldRow ['zzz']). If a
998
+ // consumer ever type-validates delivered images, revisit: the options are
999
+ // NULL (loses the value) or dropping the image (loses the event).
1000
+ return v;
1001
+ }
1002
+ };
1003
+ }
1004
+ 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;
1010
+ if (folded === undefined || folded === null)
1011
+ return undefined;
1012
+ return (v) => (v === null ? folded : v);
1013
+ }
1014
+ return undefined;
1015
+ }
837
1016
  /**
838
1017
  * Catalog-only metadata-tag mutations. Tags touch no stored row and no physical
839
1018
  * layout, so these never call `module.alterTable` — they delegate to the
@@ -1295,13 +1474,23 @@ function rewriteTableForTableRename(table, renamedSchemaLower, oldName, newName)
1295
1474
  indexes: table.indexes ? Object.freeze(newIndexes) : table.indexes,
1296
1475
  });
1297
1476
  }
1298
- async function propagateColumnRename(rctx, renamedSchemaName, tableName, oldCol, newCol, preStaleMvs) {
1299
- const schemaManager = rctx.db.schemaManager;
1300
- const resolveColumnInSource = (s, t, col) => {
1301
- const targetSchema = schemaManager.getSchema(s);
1477
+ /**
1478
+ * The catalog-backed {@link ResolveColumnInSource} the column-rename rewriters consult
1479
+ * to keep their unqualified-reference walk scope-aware. Built once per statement and
1480
+ * shared by the pre-flight probe in {@link runRenameColumn} and the real propagation
1481
+ * below, so the two cannot drift apart. Note it resolves against the LIVE catalog on
1482
+ * every call, so sharing it does not by itself freeze the answer between the two passes
1483
+ * — see the pre-flight's comment for why they agree anyway.
1484
+ */
1485
+ function buildColumnSourceResolver(db) {
1486
+ return (s, t, col) => {
1487
+ const targetSchema = db.schemaManager.getSchema(s);
1302
1488
  const targetTable = targetSchema?.getTable(t);
1303
1489
  return targetTable?.columnIndexMap.has(col.toLowerCase()) ?? false;
1304
1490
  };
1491
+ }
1492
+ async function propagateColumnRename(rctx, renamedSchemaName, tableName, oldCol, newCol, preStaleMvs, resolveColumnInSource) {
1493
+ const schemaManager = rctx.db.schemaManager;
1305
1494
  for (const schema of schemaManager._getAllSchemas()) {
1306
1495
  await propagateColumnRenameInSchema(rctx.db, schema, renamedSchemaName, tableName, oldCol, newCol, resolveColumnInSource, preStaleMvs);
1307
1496
  }