@quereus/quereus 3.3.0 → 4.0.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 (900) hide show
  1. package/README.md +7 -0
  2. package/dist/src/common/datatype.d.ts +12 -0
  3. package/dist/src/common/datatype.d.ts.map +1 -1
  4. package/dist/src/common/datatype.js.map +1 -1
  5. package/dist/src/common/types.d.ts +24 -0
  6. package/dist/src/common/types.d.ts.map +1 -1
  7. package/dist/src/common/types.js.map +1 -1
  8. package/dist/src/core/database-assertions.d.ts +37 -9
  9. package/dist/src/core/database-assertions.d.ts.map +1 -1
  10. package/dist/src/core/database-assertions.js +62 -110
  11. package/dist/src/core/database-assertions.js.map +1 -1
  12. package/dist/src/core/database-events.d.ts +163 -0
  13. package/dist/src/core/database-events.d.ts.map +1 -1
  14. package/dist/src/core/database-events.js +235 -21
  15. package/dist/src/core/database-events.js.map +1 -1
  16. package/dist/src/core/database-external-changes.d.ts +28 -0
  17. package/dist/src/core/database-external-changes.d.ts.map +1 -0
  18. package/dist/src/core/database-external-changes.js +242 -0
  19. package/dist/src/core/database-external-changes.js.map +1 -0
  20. package/dist/src/core/database-internal.d.ts +50 -1
  21. package/dist/src/core/database-internal.d.ts.map +1 -1
  22. package/dist/src/core/database-materialized-views.d.ts +1253 -0
  23. package/dist/src/core/database-materialized-views.d.ts.map +1 -0
  24. package/dist/src/core/database-materialized-views.js +3064 -0
  25. package/dist/src/core/database-materialized-views.js.map +1 -0
  26. package/dist/src/core/database-options.d.ts +4 -0
  27. package/dist/src/core/database-options.d.ts.map +1 -1
  28. package/dist/src/core/database-options.js +10 -0
  29. package/dist/src/core/database-options.js.map +1 -1
  30. package/dist/src/core/database-transaction.d.ts +19 -3
  31. package/dist/src/core/database-transaction.d.ts.map +1 -1
  32. package/dist/src/core/database-transaction.js +30 -3
  33. package/dist/src/core/database-transaction.js.map +1 -1
  34. package/dist/src/core/database-watchers.d.ts +19 -0
  35. package/dist/src/core/database-watchers.d.ts.map +1 -1
  36. package/dist/src/core/database-watchers.js +63 -3
  37. package/dist/src/core/database-watchers.js.map +1 -1
  38. package/dist/src/core/database.d.ts +203 -11
  39. package/dist/src/core/database.d.ts.map +1 -1
  40. package/dist/src/core/database.js +493 -29
  41. package/dist/src/core/database.js.map +1 -1
  42. package/dist/src/core/derived-row-validator.d.ts +137 -0
  43. package/dist/src/core/derived-row-validator.d.ts.map +1 -0
  44. package/dist/src/core/derived-row-validator.js +314 -0
  45. package/dist/src/core/derived-row-validator.js.map +1 -0
  46. package/dist/src/core/statement.d.ts.map +1 -1
  47. package/dist/src/core/statement.js +30 -9
  48. package/dist/src/core/statement.js.map +1 -1
  49. package/dist/src/emit/ast-stringify.d.ts +135 -1
  50. package/dist/src/emit/ast-stringify.d.ts.map +1 -1
  51. package/dist/src/emit/ast-stringify.js +793 -118
  52. package/dist/src/emit/ast-stringify.js.map +1 -1
  53. package/dist/src/func/builtins/aggregate.d.ts.map +1 -1
  54. package/dist/src/func/builtins/aggregate.js +11 -10
  55. package/dist/src/func/builtins/aggregate.js.map +1 -1
  56. package/dist/src/func/builtins/builtin-window-functions.d.ts.map +1 -1
  57. package/dist/src/func/builtins/builtin-window-functions.js +32 -0
  58. package/dist/src/func/builtins/builtin-window-functions.js.map +1 -1
  59. package/dist/src/func/builtins/explain.d.ts +3 -0
  60. package/dist/src/func/builtins/explain.d.ts.map +1 -1
  61. package/dist/src/func/builtins/explain.js +229 -0
  62. package/dist/src/func/builtins/explain.js.map +1 -1
  63. package/dist/src/func/builtins/index.d.ts.map +1 -1
  64. package/dist/src/func/builtins/index.js +10 -2
  65. package/dist/src/func/builtins/index.js.map +1 -1
  66. package/dist/src/func/builtins/json.d.ts.map +1 -1
  67. package/dist/src/func/builtins/json.js +3 -2
  68. package/dist/src/func/builtins/json.js.map +1 -1
  69. package/dist/src/func/builtins/mutation.d.ts +2 -0
  70. package/dist/src/func/builtins/mutation.d.ts.map +1 -0
  71. package/dist/src/func/builtins/mutation.js +53 -0
  72. package/dist/src/func/builtins/mutation.js.map +1 -0
  73. package/dist/src/func/builtins/schema.d.ts +2 -0
  74. package/dist/src/func/builtins/schema.d.ts.map +1 -1
  75. package/dist/src/func/builtins/schema.js +713 -26
  76. package/dist/src/func/builtins/schema.js.map +1 -1
  77. package/dist/src/func/builtins/string.js +1 -1
  78. package/dist/src/func/builtins/string.js.map +1 -1
  79. package/dist/src/func/registration.d.ts +9 -0
  80. package/dist/src/func/registration.d.ts.map +1 -1
  81. package/dist/src/func/registration.js +4 -0
  82. package/dist/src/func/registration.js.map +1 -1
  83. package/dist/src/index.d.ts +25 -6
  84. package/dist/src/index.d.ts.map +1 -1
  85. package/dist/src/index.js +27 -3
  86. package/dist/src/index.js.map +1 -1
  87. package/dist/src/parser/ast.d.ts +353 -21
  88. package/dist/src/parser/ast.d.ts.map +1 -1
  89. package/dist/src/parser/index.d.ts +14 -1
  90. package/dist/src/parser/index.d.ts.map +1 -1
  91. package/dist/src/parser/index.js +19 -0
  92. package/dist/src/parser/index.js.map +1 -1
  93. package/dist/src/parser/lexer.d.ts +9 -0
  94. package/dist/src/parser/lexer.d.ts.map +1 -1
  95. package/dist/src/parser/lexer.js +9 -0
  96. package/dist/src/parser/lexer.js.map +1 -1
  97. package/dist/src/parser/parser.d.ts +276 -7
  98. package/dist/src/parser/parser.d.ts.map +1 -1
  99. package/dist/src/parser/parser.js +1387 -469
  100. package/dist/src/parser/parser.js.map +1 -1
  101. package/dist/src/parser/visitor.d.ts.map +1 -1
  102. package/dist/src/parser/visitor.js +12 -8
  103. package/dist/src/parser/visitor.js.map +1 -1
  104. package/dist/src/planner/analysis/assertion-classifier.d.ts.map +1 -1
  105. package/dist/src/planner/analysis/assertion-classifier.js +4 -0
  106. package/dist/src/planner/analysis/assertion-classifier.js.map +1 -1
  107. package/dist/src/planner/analysis/assertion-hoist-cache.d.ts.map +1 -1
  108. package/dist/src/planner/analysis/assertion-hoist-cache.js +8 -4
  109. package/dist/src/planner/analysis/assertion-hoist-cache.js.map +1 -1
  110. package/dist/src/planner/analysis/authored-inverse.d.ts +22 -0
  111. package/dist/src/planner/analysis/authored-inverse.d.ts.map +1 -0
  112. package/dist/src/planner/analysis/authored-inverse.js +267 -0
  113. package/dist/src/planner/analysis/authored-inverse.js.map +1 -0
  114. package/dist/src/planner/analysis/change-scope.d.ts +34 -4
  115. package/dist/src/planner/analysis/change-scope.d.ts.map +1 -1
  116. package/dist/src/planner/analysis/change-scope.js +108 -7
  117. package/dist/src/planner/analysis/change-scope.js.map +1 -1
  118. package/dist/src/planner/analysis/check-extraction.d.ts +36 -2
  119. package/dist/src/planner/analysis/check-extraction.d.ts.map +1 -1
  120. package/dist/src/planner/analysis/check-extraction.js +174 -46
  121. package/dist/src/planner/analysis/check-extraction.js.map +1 -1
  122. package/dist/src/planner/analysis/coarsened-key.d.ts +109 -0
  123. package/dist/src/planner/analysis/coarsened-key.d.ts.map +1 -0
  124. package/dist/src/planner/analysis/coarsened-key.js +228 -0
  125. package/dist/src/planner/analysis/coarsened-key.js.map +1 -0
  126. package/dist/src/planner/analysis/comparison-collation.d.ts +216 -0
  127. package/dist/src/planner/analysis/comparison-collation.d.ts.map +1 -0
  128. package/dist/src/planner/analysis/comparison-collation.js +341 -0
  129. package/dist/src/planner/analysis/comparison-collation.js.map +1 -0
  130. package/dist/src/planner/analysis/constraint-extractor.d.ts +3 -1
  131. package/dist/src/planner/analysis/constraint-extractor.d.ts.map +1 -1
  132. package/dist/src/planner/analysis/constraint-extractor.js +192 -9
  133. package/dist/src/planner/analysis/constraint-extractor.js.map +1 -1
  134. package/dist/src/planner/analysis/coverage-prover.d.ts +321 -0
  135. package/dist/src/planner/analysis/coverage-prover.d.ts.map +1 -0
  136. package/dist/src/planner/analysis/coverage-prover.js +1038 -0
  137. package/dist/src/planner/analysis/coverage-prover.js.map +1 -0
  138. package/dist/src/planner/analysis/key-filter.d.ts +22 -0
  139. package/dist/src/planner/analysis/key-filter.d.ts.map +1 -0
  140. package/dist/src/planner/analysis/key-filter.js +105 -0
  141. package/dist/src/planner/analysis/key-filter.js.map +1 -0
  142. package/dist/src/planner/analysis/partial-unique-extraction.d.ts +36 -1
  143. package/dist/src/planner/analysis/partial-unique-extraction.d.ts.map +1 -1
  144. package/dist/src/planner/analysis/partial-unique-extraction.js +148 -22
  145. package/dist/src/planner/analysis/partial-unique-extraction.js.map +1 -1
  146. package/dist/src/planner/analysis/predicate-normalizer.d.ts.map +1 -1
  147. package/dist/src/planner/analysis/predicate-normalizer.js +30 -1
  148. package/dist/src/planner/analysis/predicate-normalizer.js.map +1 -1
  149. package/dist/src/planner/analysis/predicate-shape.d.ts +36 -1
  150. package/dist/src/planner/analysis/predicate-shape.d.ts.map +1 -1
  151. package/dist/src/planner/analysis/predicate-shape.js +51 -13
  152. package/dist/src/planner/analysis/predicate-shape.js.map +1 -1
  153. package/dist/src/planner/analysis/query-rewrite-matcher.d.ts +314 -0
  154. package/dist/src/planner/analysis/query-rewrite-matcher.d.ts.map +1 -0
  155. package/dist/src/planner/analysis/query-rewrite-matcher.js +1081 -0
  156. package/dist/src/planner/analysis/query-rewrite-matcher.js.map +1 -0
  157. package/dist/src/planner/analysis/scalar-invertibility.d.ts +92 -0
  158. package/dist/src/planner/analysis/scalar-invertibility.d.ts.map +1 -0
  159. package/dist/src/planner/analysis/scalar-invertibility.js +129 -0
  160. package/dist/src/planner/analysis/scalar-invertibility.js.map +1 -0
  161. package/dist/src/planner/analysis/update-lineage.d.ts +196 -0
  162. package/dist/src/planner/analysis/update-lineage.d.ts.map +1 -0
  163. package/dist/src/planner/analysis/update-lineage.js +322 -0
  164. package/dist/src/planner/analysis/update-lineage.js.map +1 -0
  165. package/dist/src/planner/analysis/view-complement.d.ts +42 -0
  166. package/dist/src/planner/analysis/view-complement.d.ts.map +1 -0
  167. package/dist/src/planner/analysis/view-complement.js +54 -0
  168. package/dist/src/planner/analysis/view-complement.js.map +1 -0
  169. package/dist/src/planner/building/alter-table.d.ts +1 -1
  170. package/dist/src/planner/building/alter-table.d.ts.map +1 -1
  171. package/dist/src/planner/building/alter-table.js +211 -2
  172. package/dist/src/planner/building/alter-table.js.map +1 -1
  173. package/dist/src/planner/building/block.d.ts.map +1 -1
  174. package/dist/src/planner/building/block.js +18 -1
  175. package/dist/src/planner/building/block.js.map +1 -1
  176. package/dist/src/planner/building/constraint-builder.d.ts +33 -5
  177. package/dist/src/planner/building/constraint-builder.d.ts.map +1 -1
  178. package/dist/src/planner/building/constraint-builder.js +63 -28
  179. package/dist/src/planner/building/constraint-builder.js.map +1 -1
  180. package/dist/src/planner/building/create-view.d.ts +9 -0
  181. package/dist/src/planner/building/create-view.d.ts.map +1 -1
  182. package/dist/src/planner/building/create-view.js +41 -12
  183. package/dist/src/planner/building/create-view.js.map +1 -1
  184. package/dist/src/planner/building/ddl.d.ts.map +1 -1
  185. package/dist/src/planner/building/ddl.js +94 -0
  186. package/dist/src/planner/building/ddl.js.map +1 -1
  187. package/dist/src/planner/building/declare-schema.d.ts +1 -0
  188. package/dist/src/planner/building/declare-schema.d.ts.map +1 -1
  189. package/dist/src/planner/building/declare-schema.js +4 -1
  190. package/dist/src/planner/building/declare-schema.js.map +1 -1
  191. package/dist/src/planner/building/default-scope.d.ts +26 -0
  192. package/dist/src/planner/building/default-scope.d.ts.map +1 -0
  193. package/dist/src/planner/building/default-scope.js +41 -0
  194. package/dist/src/planner/building/default-scope.js.map +1 -0
  195. package/dist/src/planner/building/delete.d.ts +19 -1
  196. package/dist/src/planner/building/delete.d.ts.map +1 -1
  197. package/dist/src/planner/building/delete.js +109 -30
  198. package/dist/src/planner/building/delete.js.map +1 -1
  199. package/dist/src/planner/building/dml-target.d.ts +118 -0
  200. package/dist/src/planner/building/dml-target.d.ts.map +1 -0
  201. package/dist/src/planner/building/dml-target.js +282 -0
  202. package/dist/src/planner/building/dml-target.js.map +1 -0
  203. package/dist/src/planner/building/drop-index.d.ts.map +1 -1
  204. package/dist/src/planner/building/drop-index.js +4 -1
  205. package/dist/src/planner/building/drop-index.js.map +1 -1
  206. package/dist/src/planner/building/drop-view.d.ts.map +1 -1
  207. package/dist/src/planner/building/drop-view.js +4 -2
  208. package/dist/src/planner/building/drop-view.js.map +1 -1
  209. package/dist/src/planner/building/expression.d.ts.map +1 -1
  210. package/dist/src/planner/building/expression.js +60 -21
  211. package/dist/src/planner/building/expression.js.map +1 -1
  212. package/dist/src/planner/building/foreign-key-builder.d.ts +30 -0
  213. package/dist/src/planner/building/foreign-key-builder.d.ts.map +1 -1
  214. package/dist/src/planner/building/foreign-key-builder.js +160 -129
  215. package/dist/src/planner/building/foreign-key-builder.js.map +1 -1
  216. package/dist/src/planner/building/insert.d.ts +45 -2
  217. package/dist/src/planner/building/insert.d.ts.map +1 -1
  218. package/dist/src/planner/building/insert.js +257 -88
  219. package/dist/src/planner/building/insert.js.map +1 -1
  220. package/dist/src/planner/building/lens-auxiliary-access.d.ts +22 -0
  221. package/dist/src/planner/building/lens-auxiliary-access.d.ts.map +1 -0
  222. package/dist/src/planner/building/lens-auxiliary-access.js +132 -0
  223. package/dist/src/planner/building/lens-auxiliary-access.js.map +1 -0
  224. package/dist/src/planner/building/materialized-view.d.ts +16 -0
  225. package/dist/src/planner/building/materialized-view.d.ts.map +1 -0
  226. package/dist/src/planner/building/materialized-view.js +57 -0
  227. package/dist/src/planner/building/materialized-view.js.map +1 -0
  228. package/dist/src/planner/building/returning-star.d.ts +32 -0
  229. package/dist/src/planner/building/returning-star.d.ts.map +1 -0
  230. package/dist/src/planner/building/returning-star.js +45 -0
  231. package/dist/src/planner/building/returning-star.js.map +1 -0
  232. package/dist/src/planner/building/select-aggregates.d.ts.map +1 -1
  233. package/dist/src/planner/building/select-aggregates.js +47 -0
  234. package/dist/src/planner/building/select-aggregates.js.map +1 -1
  235. package/dist/src/planner/building/select-compound.d.ts.map +1 -1
  236. package/dist/src/planner/building/select-compound.js +84 -11
  237. package/dist/src/planner/building/select-compound.js.map +1 -1
  238. package/dist/src/planner/building/select-context.d.ts +10 -2
  239. package/dist/src/planner/building/select-context.d.ts.map +1 -1
  240. package/dist/src/planner/building/select-context.js +7 -1
  241. package/dist/src/planner/building/select-context.js.map +1 -1
  242. package/dist/src/planner/building/select-modifiers.js +6 -0
  243. package/dist/src/planner/building/select-modifiers.js.map +1 -1
  244. package/dist/src/planner/building/select-ordinal.d.ts +18 -0
  245. package/dist/src/planner/building/select-ordinal.d.ts.map +1 -1
  246. package/dist/src/planner/building/select-ordinal.js +30 -0
  247. package/dist/src/planner/building/select-ordinal.js.map +1 -1
  248. package/dist/src/planner/building/select-projections.d.ts +8 -2
  249. package/dist/src/planner/building/select-projections.d.ts.map +1 -1
  250. package/dist/src/planner/building/select-projections.js +26 -4
  251. package/dist/src/planner/building/select-projections.js.map +1 -1
  252. package/dist/src/planner/building/select-window.d.ts.map +1 -1
  253. package/dist/src/planner/building/select-window.js +8 -5
  254. package/dist/src/planner/building/select-window.js.map +1 -1
  255. package/dist/src/planner/building/select.d.ts.map +1 -1
  256. package/dist/src/planner/building/select.js +164 -59
  257. package/dist/src/planner/building/select.js.map +1 -1
  258. package/dist/src/planner/building/set-object-tags.d.ts +7 -0
  259. package/dist/src/planner/building/set-object-tags.d.ts.map +1 -0
  260. package/dist/src/planner/building/set-object-tags.js +38 -0
  261. package/dist/src/planner/building/set-object-tags.js.map +1 -0
  262. package/dist/src/planner/building/tag-diagnostics.d.ts +27 -0
  263. package/dist/src/planner/building/tag-diagnostics.d.ts.map +1 -0
  264. package/dist/src/planner/building/tag-diagnostics.js +37 -0
  265. package/dist/src/planner/building/tag-diagnostics.js.map +1 -0
  266. package/dist/src/planner/building/update.d.ts +18 -1
  267. package/dist/src/planner/building/update.d.ts.map +1 -1
  268. package/dist/src/planner/building/update.js +134 -58
  269. package/dist/src/planner/building/update.js.map +1 -1
  270. package/dist/src/planner/building/view-mutation-builder.d.ts +15 -0
  271. package/dist/src/planner/building/view-mutation-builder.d.ts.map +1 -0
  272. package/dist/src/planner/building/view-mutation-builder.js +1158 -0
  273. package/dist/src/planner/building/view-mutation-builder.js.map +1 -0
  274. package/dist/src/planner/building/with.d.ts +11 -0
  275. package/dist/src/planner/building/with.d.ts.map +1 -1
  276. package/dist/src/planner/building/with.js +48 -10
  277. package/dist/src/planner/building/with.js.map +1 -1
  278. package/dist/src/planner/cost/index.d.ts +83 -0
  279. package/dist/src/planner/cost/index.d.ts.map +1 -1
  280. package/dist/src/planner/cost/index.js +114 -0
  281. package/dist/src/planner/cost/index.js.map +1 -1
  282. package/dist/src/planner/framework/characteristics.d.ts +38 -4
  283. package/dist/src/planner/framework/characteristics.d.ts.map +1 -1
  284. package/dist/src/planner/framework/characteristics.js +50 -6
  285. package/dist/src/planner/framework/characteristics.js.map +1 -1
  286. package/dist/src/planner/framework/pass.d.ts.map +1 -1
  287. package/dist/src/planner/framework/pass.js +2 -1
  288. package/dist/src/planner/framework/pass.js.map +1 -1
  289. package/dist/src/planner/framework/registry.d.ts +39 -1
  290. package/dist/src/planner/framework/registry.d.ts.map +1 -1
  291. package/dist/src/planner/framework/registry.js +18 -2
  292. package/dist/src/planner/framework/registry.js.map +1 -1
  293. package/dist/src/planner/mutation/backward-body.d.ts +131 -0
  294. package/dist/src/planner/mutation/backward-body.d.ts.map +1 -0
  295. package/dist/src/planner/mutation/backward-body.js +135 -0
  296. package/dist/src/planner/mutation/backward-body.js.map +1 -0
  297. package/dist/src/planner/mutation/cte-flatten.d.ts +17 -0
  298. package/dist/src/planner/mutation/cte-flatten.d.ts.map +1 -0
  299. package/dist/src/planner/mutation/cte-flatten.js +364 -0
  300. package/dist/src/planner/mutation/cte-flatten.js.map +1 -0
  301. package/dist/src/planner/mutation/decomposition.d.ts +273 -0
  302. package/dist/src/planner/mutation/decomposition.d.ts.map +1 -0
  303. package/dist/src/planner/mutation/decomposition.js +1719 -0
  304. package/dist/src/planner/mutation/decomposition.js.map +1 -0
  305. package/dist/src/planner/mutation/lens-enforcement.d.ts +165 -0
  306. package/dist/src/planner/mutation/lens-enforcement.d.ts.map +1 -0
  307. package/dist/src/planner/mutation/lens-enforcement.js +745 -0
  308. package/dist/src/planner/mutation/lens-enforcement.js.map +1 -0
  309. package/dist/src/planner/mutation/multi-source.d.ts +568 -0
  310. package/dist/src/planner/mutation/multi-source.d.ts.map +1 -0
  311. package/dist/src/planner/mutation/multi-source.js +2915 -0
  312. package/dist/src/planner/mutation/multi-source.js.map +1 -0
  313. package/dist/src/planner/mutation/mutation-diagnostic.d.ts +37 -0
  314. package/dist/src/planner/mutation/mutation-diagnostic.d.ts.map +1 -0
  315. package/dist/src/planner/mutation/mutation-diagnostic.js +24 -0
  316. package/dist/src/planner/mutation/mutation-diagnostic.js.map +1 -0
  317. package/dist/src/planner/mutation/mutation-tags.d.ts +33 -0
  318. package/dist/src/planner/mutation/mutation-tags.d.ts.map +1 -0
  319. package/dist/src/planner/mutation/mutation-tags.js +31 -0
  320. package/dist/src/planner/mutation/mutation-tags.js.map +1 -0
  321. package/dist/src/planner/mutation/propagate.d.ts +97 -0
  322. package/dist/src/planner/mutation/propagate.d.ts.map +1 -0
  323. package/dist/src/planner/mutation/propagate.js +220 -0
  324. package/dist/src/planner/mutation/propagate.js.map +1 -0
  325. package/dist/src/planner/mutation/scope-transform.d.ts +181 -0
  326. package/dist/src/planner/mutation/scope-transform.d.ts.map +1 -0
  327. package/dist/src/planner/mutation/scope-transform.js +574 -0
  328. package/dist/src/planner/mutation/scope-transform.js.map +1 -0
  329. package/dist/src/planner/mutation/set-op.d.ts +242 -0
  330. package/dist/src/planner/mutation/set-op.d.ts.map +1 -0
  331. package/dist/src/planner/mutation/set-op.js +1687 -0
  332. package/dist/src/planner/mutation/set-op.js.map +1 -0
  333. package/dist/src/planner/mutation/single-source.d.ts +261 -0
  334. package/dist/src/planner/mutation/single-source.d.ts.map +1 -0
  335. package/dist/src/planner/mutation/single-source.js +1096 -0
  336. package/dist/src/planner/mutation/single-source.js.map +1 -0
  337. package/dist/src/planner/nodes/aggregate-node.js +3 -3
  338. package/dist/src/planner/nodes/aggregate-node.js.map +1 -1
  339. package/dist/src/planner/nodes/alias-node.d.ts.map +1 -1
  340. package/dist/src/planner/nodes/alias-node.js +5 -1
  341. package/dist/src/planner/nodes/alias-node.js.map +1 -1
  342. package/dist/src/planner/nodes/alter-table-node.d.ts +124 -1
  343. package/dist/src/planner/nodes/alter-table-node.d.ts.map +1 -1
  344. package/dist/src/planner/nodes/alter-table-node.js +27 -0
  345. package/dist/src/planner/nodes/alter-table-node.js.map +1 -1
  346. package/dist/src/planner/nodes/analyze-node.d.ts +2 -1
  347. package/dist/src/planner/nodes/analyze-node.d.ts.map +1 -1
  348. package/dist/src/planner/nodes/analyze-node.js +18 -1
  349. package/dist/src/planner/nodes/analyze-node.js.map +1 -1
  350. package/dist/src/planner/nodes/asserted-keys-node.d.ts +43 -0
  351. package/dist/src/planner/nodes/asserted-keys-node.d.ts.map +1 -0
  352. package/dist/src/planner/nodes/asserted-keys-node.js +99 -0
  353. package/dist/src/planner/nodes/asserted-keys-node.js.map +1 -0
  354. package/dist/src/planner/nodes/async-gather-node.d.ts.map +1 -1
  355. package/dist/src/planner/nodes/async-gather-node.js +33 -8
  356. package/dist/src/planner/nodes/async-gather-node.js.map +1 -1
  357. package/dist/src/planner/nodes/bloom-join-node.d.ts.map +1 -1
  358. package/dist/src/planner/nodes/bloom-join-node.js +2 -1
  359. package/dist/src/planner/nodes/bloom-join-node.js.map +1 -1
  360. package/dist/src/planner/nodes/create-view-node.d.ts +7 -2
  361. package/dist/src/planner/nodes/create-view-node.d.ts.map +1 -1
  362. package/dist/src/planner/nodes/create-view-node.js +4 -1
  363. package/dist/src/planner/nodes/create-view-node.js.map +1 -1
  364. package/dist/src/planner/nodes/declarative-schema.d.ts +13 -1
  365. package/dist/src/planner/nodes/declarative-schema.d.ts.map +1 -1
  366. package/dist/src/planner/nodes/declarative-schema.js +32 -0
  367. package/dist/src/planner/nodes/declarative-schema.js.map +1 -1
  368. package/dist/src/planner/nodes/distinct-node.d.ts.map +1 -1
  369. package/dist/src/planner/nodes/distinct-node.js +2 -0
  370. package/dist/src/planner/nodes/distinct-node.js.map +1 -1
  371. package/dist/src/planner/nodes/dml-executor-node.d.ts +29 -1
  372. package/dist/src/planner/nodes/dml-executor-node.d.ts.map +1 -1
  373. package/dist/src/planner/nodes/dml-executor-node.js +27 -3
  374. package/dist/src/planner/nodes/dml-executor-node.js.map +1 -1
  375. package/dist/src/planner/nodes/eager-prefetch-node.d.ts.map +1 -1
  376. package/dist/src/planner/nodes/eager-prefetch-node.js +2 -0
  377. package/dist/src/planner/nodes/eager-prefetch-node.js.map +1 -1
  378. package/dist/src/planner/nodes/envelope-scan-node.d.ts +42 -0
  379. package/dist/src/planner/nodes/envelope-scan-node.d.ts.map +1 -0
  380. package/dist/src/planner/nodes/envelope-scan-node.js +62 -0
  381. package/dist/src/planner/nodes/envelope-scan-node.js.map +1 -0
  382. package/dist/src/planner/nodes/fanout-lookup-join-node.d.ts.map +1 -1
  383. package/dist/src/planner/nodes/fanout-lookup-join-node.js +11 -1
  384. package/dist/src/planner/nodes/fanout-lookup-join-node.js.map +1 -1
  385. package/dist/src/planner/nodes/filter.d.ts.map +1 -1
  386. package/dist/src/planner/nodes/filter.js +63 -13
  387. package/dist/src/planner/nodes/filter.js.map +1 -1
  388. package/dist/src/planner/nodes/join-node.d.ts +41 -1
  389. package/dist/src/planner/nodes/join-node.d.ts.map +1 -1
  390. package/dist/src/planner/nodes/join-node.js +78 -8
  391. package/dist/src/planner/nodes/join-node.js.map +1 -1
  392. package/dist/src/planner/nodes/join-utils.d.ts +33 -6
  393. package/dist/src/planner/nodes/join-utils.d.ts.map +1 -1
  394. package/dist/src/planner/nodes/join-utils.js +124 -9
  395. package/dist/src/planner/nodes/join-utils.js.map +1 -1
  396. package/dist/src/planner/nodes/lens-auxiliary-access-node.d.ts +104 -0
  397. package/dist/src/planner/nodes/lens-auxiliary-access-node.d.ts.map +1 -0
  398. package/dist/src/planner/nodes/lens-auxiliary-access-node.js +91 -0
  399. package/dist/src/planner/nodes/lens-auxiliary-access-node.js.map +1 -0
  400. package/dist/src/planner/nodes/limit-offset.d.ts.map +1 -1
  401. package/dist/src/planner/nodes/limit-offset.js +4 -5
  402. package/dist/src/planner/nodes/limit-offset.js.map +1 -1
  403. package/dist/src/planner/nodes/materialized-view-nodes.d.ts +69 -0
  404. package/dist/src/planner/nodes/materialized-view-nodes.d.ts.map +1 -0
  405. package/dist/src/planner/nodes/materialized-view-nodes.js +111 -0
  406. package/dist/src/planner/nodes/materialized-view-nodes.js.map +1 -0
  407. package/dist/src/planner/nodes/merge-join-node.d.ts.map +1 -1
  408. package/dist/src/planner/nodes/merge-join-node.js +2 -1
  409. package/dist/src/planner/nodes/merge-join-node.js.map +1 -1
  410. package/dist/src/planner/nodes/ordinal-slice-node.d.ts.map +1 -1
  411. package/dist/src/planner/nodes/ordinal-slice-node.js +2 -0
  412. package/dist/src/planner/nodes/ordinal-slice-node.js.map +1 -1
  413. package/dist/src/planner/nodes/plan-node-type.d.ts +9 -0
  414. package/dist/src/planner/nodes/plan-node-type.d.ts.map +1 -1
  415. package/dist/src/planner/nodes/plan-node-type.js +9 -0
  416. package/dist/src/planner/nodes/plan-node-type.js.map +1 -1
  417. package/dist/src/planner/nodes/plan-node.d.ts +265 -5
  418. package/dist/src/planner/nodes/plan-node.d.ts.map +1 -1
  419. package/dist/src/planner/nodes/plan-node.js.map +1 -1
  420. package/dist/src/planner/nodes/pragma.d.ts +2 -1
  421. package/dist/src/planner/nodes/pragma.d.ts.map +1 -1
  422. package/dist/src/planner/nodes/pragma.js +12 -0
  423. package/dist/src/planner/nodes/pragma.js.map +1 -1
  424. package/dist/src/planner/nodes/project-node.d.ts +14 -1
  425. package/dist/src/planner/nodes/project-node.d.ts.map +1 -1
  426. package/dist/src/planner/nodes/project-node.js +85 -11
  427. package/dist/src/planner/nodes/project-node.js.map +1 -1
  428. package/dist/src/planner/nodes/reference.d.ts.map +1 -1
  429. package/dist/src/planner/nodes/reference.js +62 -27
  430. package/dist/src/planner/nodes/reference.js.map +1 -1
  431. package/dist/src/planner/nodes/retrieve-node.d.ts.map +1 -1
  432. package/dist/src/planner/nodes/retrieve-node.js +7 -0
  433. package/dist/src/planner/nodes/retrieve-node.js.map +1 -1
  434. package/dist/src/planner/nodes/returning-node.d.ts.map +1 -1
  435. package/dist/src/planner/nodes/returning-node.js +10 -3
  436. package/dist/src/planner/nodes/returning-node.js.map +1 -1
  437. package/dist/src/planner/nodes/scalar.d.ts +20 -0
  438. package/dist/src/planner/nodes/scalar.d.ts.map +1 -1
  439. package/dist/src/planner/nodes/scalar.js +71 -14
  440. package/dist/src/planner/nodes/scalar.js.map +1 -1
  441. package/dist/src/planner/nodes/set-object-tags-node.d.ts +39 -0
  442. package/dist/src/planner/nodes/set-object-tags-node.d.ts.map +1 -0
  443. package/dist/src/planner/nodes/set-object-tags-node.js +41 -0
  444. package/dist/src/planner/nodes/set-object-tags-node.js.map +1 -0
  445. package/dist/src/planner/nodes/set-operation-node.d.ts +123 -1
  446. package/dist/src/planner/nodes/set-operation-node.d.ts.map +1 -1
  447. package/dist/src/planner/nodes/set-operation-node.js +291 -18
  448. package/dist/src/planner/nodes/set-operation-node.js.map +1 -1
  449. package/dist/src/planner/nodes/single-row.d.ts.map +1 -1
  450. package/dist/src/planner/nodes/single-row.js +3 -0
  451. package/dist/src/planner/nodes/single-row.js.map +1 -1
  452. package/dist/src/planner/nodes/sort.d.ts.map +1 -1
  453. package/dist/src/planner/nodes/sort.js +7 -6
  454. package/dist/src/planner/nodes/sort.js.map +1 -1
  455. package/dist/src/planner/nodes/subquery.d.ts +2 -0
  456. package/dist/src/planner/nodes/subquery.d.ts.map +1 -1
  457. package/dist/src/planner/nodes/subquery.js +18 -2
  458. package/dist/src/planner/nodes/subquery.js.map +1 -1
  459. package/dist/src/planner/nodes/table-access-nodes.d.ts.map +1 -1
  460. package/dist/src/planner/nodes/table-access-nodes.js +23 -3
  461. package/dist/src/planner/nodes/table-access-nodes.js.map +1 -1
  462. package/dist/src/planner/nodes/table-function-call.js +6 -0
  463. package/dist/src/planner/nodes/table-function-call.js.map +1 -1
  464. package/dist/src/planner/nodes/values-node.d.ts +1 -0
  465. package/dist/src/planner/nodes/values-node.d.ts.map +1 -1
  466. package/dist/src/planner/nodes/values-node.js +16 -6
  467. package/dist/src/planner/nodes/values-node.js.map +1 -1
  468. package/dist/src/planner/nodes/view-mutation-node.d.ts +259 -0
  469. package/dist/src/planner/nodes/view-mutation-node.d.ts.map +1 -0
  470. package/dist/src/planner/nodes/view-mutation-node.js +273 -0
  471. package/dist/src/planner/nodes/view-mutation-node.js.map +1 -0
  472. package/dist/src/planner/nodes/window-function.d.ts +17 -1
  473. package/dist/src/planner/nodes/window-function.d.ts.map +1 -1
  474. package/dist/src/planner/nodes/window-function.js +15 -1
  475. package/dist/src/planner/nodes/window-function.js.map +1 -1
  476. package/dist/src/planner/nodes/window-node.js +2 -2
  477. package/dist/src/planner/nodes/window-node.js.map +1 -1
  478. package/dist/src/planner/optimizer.d.ts.map +1 -1
  479. package/dist/src/planner/optimizer.js +372 -39
  480. package/dist/src/planner/optimizer.js.map +1 -1
  481. package/dist/src/planner/planning-context.d.ts +1 -1
  482. package/dist/src/planner/planning-context.d.ts.map +1 -1
  483. package/dist/src/planner/rules/access/lens-access-form-matcher.d.ts +70 -0
  484. package/dist/src/planner/rules/access/lens-access-form-matcher.d.ts.map +1 -0
  485. package/dist/src/planner/rules/access/lens-access-form-matcher.js +156 -0
  486. package/dist/src/planner/rules/access/lens-access-form-matcher.js.map +1 -0
  487. package/dist/src/planner/rules/access/rule-lens-auxiliary-access.d.ts +31 -0
  488. package/dist/src/planner/rules/access/rule-lens-auxiliary-access.d.ts.map +1 -0
  489. package/dist/src/planner/rules/access/rule-lens-auxiliary-access.js +176 -0
  490. package/dist/src/planner/rules/access/rule-lens-auxiliary-access.js.map +1 -0
  491. package/dist/src/planner/rules/access/rule-select-access-path.d.ts.map +1 -1
  492. package/dist/src/planner/rules/access/rule-select-access-path.js +435 -37
  493. package/dist/src/planner/rules/access/rule-select-access-path.js.map +1 -1
  494. package/dist/src/planner/rules/aggregate/rule-groupby-fd-simplification.d.ts.map +1 -1
  495. package/dist/src/planner/rules/aggregate/rule-groupby-fd-simplification.js +9 -0
  496. package/dist/src/planner/rules/aggregate/rule-groupby-fd-simplification.js.map +1 -1
  497. package/dist/src/planner/rules/cache/rule-materialized-view-rewrite.d.ts +39 -0
  498. package/dist/src/planner/rules/cache/rule-materialized-view-rewrite.d.ts.map +1 -0
  499. package/dist/src/planner/rules/cache/rule-materialized-view-rewrite.js +616 -0
  500. package/dist/src/planner/rules/cache/rule-materialized-view-rewrite.js.map +1 -0
  501. package/dist/src/planner/rules/cache/rule-scalar-cse.d.ts.map +1 -1
  502. package/dist/src/planner/rules/cache/rule-scalar-cse.js +8 -1
  503. package/dist/src/planner/rules/cache/rule-scalar-cse.js.map +1 -1
  504. package/dist/src/planner/rules/join/equi-pair-extractor.d.ts +36 -0
  505. package/dist/src/planner/rules/join/equi-pair-extractor.d.ts.map +1 -1
  506. package/dist/src/planner/rules/join/equi-pair-extractor.js +38 -1
  507. package/dist/src/planner/rules/join/equi-pair-extractor.js.map +1 -1
  508. package/dist/src/planner/rules/join/rule-fanout-batched-outer.d.ts.map +1 -1
  509. package/dist/src/planner/rules/join/rule-fanout-batched-outer.js +10 -0
  510. package/dist/src/planner/rules/join/rule-fanout-batched-outer.js.map +1 -1
  511. package/dist/src/planner/rules/join/rule-fanout-lookup-join.d.ts.map +1 -1
  512. package/dist/src/planner/rules/join/rule-fanout-lookup-join.js +19 -1
  513. package/dist/src/planner/rules/join/rule-fanout-lookup-join.js.map +1 -1
  514. package/dist/src/planner/rules/join/rule-inner-join-existence-recovery.d.ts +130 -0
  515. package/dist/src/planner/rules/join/rule-inner-join-existence-recovery.d.ts.map +1 -0
  516. package/dist/src/planner/rules/join/rule-inner-join-existence-recovery.js +206 -0
  517. package/dist/src/planner/rules/join/rule-inner-join-existence-recovery.js.map +1 -0
  518. package/dist/src/planner/rules/join/rule-join-elimination.d.ts +67 -14
  519. package/dist/src/planner/rules/join/rule-join-elimination.d.ts.map +1 -1
  520. package/dist/src/planner/rules/join/rule-join-elimination.js +81 -25
  521. package/dist/src/planner/rules/join/rule-join-elimination.js.map +1 -1
  522. package/dist/src/planner/rules/join/rule-join-existence-pruning.d.ts +84 -0
  523. package/dist/src/planner/rules/join/rule-join-existence-pruning.d.ts.map +1 -0
  524. package/dist/src/planner/rules/join/rule-join-existence-pruning.js +138 -0
  525. package/dist/src/planner/rules/join/rule-join-existence-pruning.js.map +1 -0
  526. package/dist/src/planner/rules/join/rule-join-greedy-commute.d.ts.map +1 -1
  527. package/dist/src/planner/rules/join/rule-join-greedy-commute.js +9 -1
  528. package/dist/src/planner/rules/join/rule-join-greedy-commute.js.map +1 -1
  529. package/dist/src/planner/rules/join/rule-join-physical-selection.d.ts.map +1 -1
  530. package/dist/src/planner/rules/join/rule-join-physical-selection.js +12 -1
  531. package/dist/src/planner/rules/join/rule-join-physical-selection.js.map +1 -1
  532. package/dist/src/planner/rules/join/rule-lateral-top1-asof.d.ts.map +1 -1
  533. package/dist/src/planner/rules/join/rule-lateral-top1-asof.js +4 -0
  534. package/dist/src/planner/rules/join/rule-lateral-top1-asof.js.map +1 -1
  535. package/dist/src/planner/rules/join/rule-monotonic-merge-join.d.ts.map +1 -1
  536. package/dist/src/planner/rules/join/rule-monotonic-merge-join.js +4 -0
  537. package/dist/src/planner/rules/join/rule-monotonic-merge-join.js.map +1 -1
  538. package/dist/src/planner/rules/join/rule-quickpick-enumeration.d.ts.map +1 -1
  539. package/dist/src/planner/rules/join/rule-quickpick-enumeration.js +10 -0
  540. package/dist/src/planner/rules/join/rule-quickpick-enumeration.js.map +1 -1
  541. package/dist/src/planner/rules/join/rule-semijoin-existence-recovery.d.ts +286 -0
  542. package/dist/src/planner/rules/join/rule-semijoin-existence-recovery.d.ts.map +1 -0
  543. package/dist/src/planner/rules/join/rule-semijoin-existence-recovery.js +548 -0
  544. package/dist/src/planner/rules/join/rule-semijoin-existence-recovery.js.map +1 -0
  545. package/dist/src/planner/rules/parallel/rule-async-gather-union-all.d.ts.map +1 -1
  546. package/dist/src/planner/rules/parallel/rule-async-gather-union-all.js +9 -1
  547. package/dist/src/planner/rules/parallel/rule-async-gather-union-all.js.map +1 -1
  548. package/dist/src/planner/rules/parallel/rule-async-gather-zip-by-key.d.ts.map +1 -1
  549. package/dist/src/planner/rules/parallel/rule-async-gather-zip-by-key.js +7 -0
  550. package/dist/src/planner/rules/parallel/rule-async-gather-zip-by-key.js.map +1 -1
  551. package/dist/src/planner/rules/parallel/rule-eager-prefetch-probe.d.ts.map +1 -1
  552. package/dist/src/planner/rules/parallel/rule-eager-prefetch-probe.js +10 -1
  553. package/dist/src/planner/rules/parallel/rule-eager-prefetch-probe.js.map +1 -1
  554. package/dist/src/planner/rules/predicate/rule-aggregate-predicate-pushdown.d.ts.map +1 -1
  555. package/dist/src/planner/rules/predicate/rule-aggregate-predicate-pushdown.js +9 -0
  556. package/dist/src/planner/rules/predicate/rule-aggregate-predicate-pushdown.js.map +1 -1
  557. package/dist/src/planner/rules/predicate/rule-empty-relation-folding.d.ts.map +1 -1
  558. package/dist/src/planner/rules/predicate/rule-empty-relation-folding.js +18 -0
  559. package/dist/src/planner/rules/predicate/rule-empty-relation-folding.js.map +1 -1
  560. package/dist/src/planner/rules/predicate/rule-filter-contradiction.d.ts.map +1 -1
  561. package/dist/src/planner/rules/predicate/rule-filter-contradiction.js +7 -0
  562. package/dist/src/planner/rules/predicate/rule-filter-contradiction.js.map +1 -1
  563. package/dist/src/planner/rules/predicate/rule-predicate-inference-equivalence.d.ts.map +1 -1
  564. package/dist/src/planner/rules/predicate/rule-predicate-inference-equivalence.js +9 -0
  565. package/dist/src/planner/rules/predicate/rule-predicate-inference-equivalence.js.map +1 -1
  566. package/dist/src/planner/rules/predicate/rule-predicate-pushdown.js +13 -3
  567. package/dist/src/planner/rules/predicate/rule-predicate-pushdown.js.map +1 -1
  568. package/dist/src/planner/rules/retrieve/rule-projection-pruning.d.ts.map +1 -1
  569. package/dist/src/planner/rules/retrieve/rule-projection-pruning.js +14 -0
  570. package/dist/src/planner/rules/retrieve/rule-projection-pruning.js.map +1 -1
  571. package/dist/src/planner/rules/sort/rule-orderby-fd-pruning.d.ts +1 -1
  572. package/dist/src/planner/rules/sort/rule-orderby-fd-pruning.js +4 -4
  573. package/dist/src/planner/rules/sort/rule-orderby-fd-pruning.js.map +1 -1
  574. package/dist/src/planner/rules/subquery/rule-anti-join-fk-empty.d.ts.map +1 -1
  575. package/dist/src/planner/rules/subquery/rule-anti-join-fk-empty.js +8 -0
  576. package/dist/src/planner/rules/subquery/rule-anti-join-fk-empty.js.map +1 -1
  577. package/dist/src/planner/rules/subquery/rule-semi-join-fk-trivial.d.ts.map +1 -1
  578. package/dist/src/planner/rules/subquery/rule-semi-join-fk-trivial.js +7 -0
  579. package/dist/src/planner/rules/subquery/rule-semi-join-fk-trivial.js.map +1 -1
  580. package/dist/src/planner/rules/subquery/rule-subquery-decorrelation.d.ts.map +1 -1
  581. package/dist/src/planner/rules/subquery/rule-subquery-decorrelation.js +12 -0
  582. package/dist/src/planner/rules/subquery/rule-subquery-decorrelation.js.map +1 -1
  583. package/dist/src/planner/type-utils.d.ts +14 -0
  584. package/dist/src/planner/type-utils.d.ts.map +1 -1
  585. package/dist/src/planner/type-utils.js +66 -21
  586. package/dist/src/planner/type-utils.js.map +1 -1
  587. package/dist/src/planner/util/fd-utils.d.ts +177 -43
  588. package/dist/src/planner/util/fd-utils.d.ts.map +1 -1
  589. package/dist/src/planner/util/fd-utils.js +396 -101
  590. package/dist/src/planner/util/fd-utils.js.map +1 -1
  591. package/dist/src/planner/util/ind-utils.d.ts +27 -1
  592. package/dist/src/planner/util/ind-utils.d.ts.map +1 -1
  593. package/dist/src/planner/util/ind-utils.js +80 -6
  594. package/dist/src/planner/util/ind-utils.js.map +1 -1
  595. package/dist/src/planner/util/key-utils.d.ts.map +1 -1
  596. package/dist/src/planner/util/key-utils.js +81 -12
  597. package/dist/src/planner/util/key-utils.js.map +1 -1
  598. package/dist/src/planner/util/set-op-wrapper.d.ts +37 -0
  599. package/dist/src/planner/util/set-op-wrapper.d.ts.map +1 -0
  600. package/dist/src/planner/util/set-op-wrapper.js +82 -0
  601. package/dist/src/planner/util/set-op-wrapper.js.map +1 -0
  602. package/dist/src/planner/validation/plan-validator.d.ts.map +1 -1
  603. package/dist/src/planner/validation/plan-validator.js +1 -0
  604. package/dist/src/planner/validation/plan-validator.js.map +1 -1
  605. package/dist/src/runtime/context-helpers.d.ts +13 -1
  606. package/dist/src/runtime/context-helpers.d.ts.map +1 -1
  607. package/dist/src/runtime/context-helpers.js +7 -1
  608. package/dist/src/runtime/context-helpers.js.map +1 -1
  609. package/dist/src/runtime/delta-executor.d.ts +30 -1
  610. package/dist/src/runtime/delta-executor.d.ts.map +1 -1
  611. package/dist/src/runtime/delta-executor.js +29 -4
  612. package/dist/src/runtime/delta-executor.js.map +1 -1
  613. package/dist/src/runtime/emit/add-constraint.d.ts.map +1 -1
  614. package/dist/src/runtime/emit/add-constraint.js +38 -5
  615. package/dist/src/runtime/emit/add-constraint.js.map +1 -1
  616. package/dist/src/runtime/emit/aggregate.d.ts.map +1 -1
  617. package/dist/src/runtime/emit/aggregate.js +10 -8
  618. package/dist/src/runtime/emit/aggregate.js.map +1 -1
  619. package/dist/src/runtime/emit/alter-table.d.ts +1 -1
  620. package/dist/src/runtime/emit/alter-table.d.ts.map +1 -1
  621. package/dist/src/runtime/emit/alter-table.js +664 -108
  622. package/dist/src/runtime/emit/alter-table.js.map +1 -1
  623. package/dist/src/runtime/emit/analyze.d.ts.map +1 -1
  624. package/dist/src/runtime/emit/analyze.js +2 -1
  625. package/dist/src/runtime/emit/analyze.js.map +1 -1
  626. package/dist/src/runtime/emit/asof-scan.d.ts.map +1 -1
  627. package/dist/src/runtime/emit/asof-scan.js +18 -5
  628. package/dist/src/runtime/emit/asof-scan.js.map +1 -1
  629. package/dist/src/runtime/emit/asserted-keys.d.ts +13 -0
  630. package/dist/src/runtime/emit/asserted-keys.d.ts.map +1 -0
  631. package/dist/src/runtime/emit/asserted-keys.js +13 -0
  632. package/dist/src/runtime/emit/asserted-keys.js.map +1 -0
  633. package/dist/src/runtime/emit/between.d.ts.map +1 -1
  634. package/dist/src/runtime/emit/between.js +24 -19
  635. package/dist/src/runtime/emit/between.js.map +1 -1
  636. package/dist/src/runtime/emit/binary.d.ts.map +1 -1
  637. package/dist/src/runtime/emit/binary.js +5 -9
  638. package/dist/src/runtime/emit/binary.js.map +1 -1
  639. package/dist/src/runtime/emit/block.d.ts.map +1 -1
  640. package/dist/src/runtime/emit/block.js +11 -2
  641. package/dist/src/runtime/emit/block.js.map +1 -1
  642. package/dist/src/runtime/emit/bloom-join.d.ts.map +1 -1
  643. package/dist/src/runtime/emit/bloom-join.js +8 -2
  644. package/dist/src/runtime/emit/bloom-join.js.map +1 -1
  645. package/dist/src/runtime/emit/constraint-check.js +15 -0
  646. package/dist/src/runtime/emit/constraint-check.js.map +1 -1
  647. package/dist/src/runtime/emit/create-table.d.ts.map +1 -1
  648. package/dist/src/runtime/emit/create-table.js +8 -0
  649. package/dist/src/runtime/emit/create-table.js.map +1 -1
  650. package/dist/src/runtime/emit/create-view.d.ts.map +1 -1
  651. package/dist/src/runtime/emit/create-view.js +16 -1
  652. package/dist/src/runtime/emit/create-view.js.map +1 -1
  653. package/dist/src/runtime/emit/dml-executor.d.ts +27 -0
  654. package/dist/src/runtime/emit/dml-executor.d.ts.map +1 -1
  655. package/dist/src/runtime/emit/dml-executor.js +413 -193
  656. package/dist/src/runtime/emit/dml-executor.js.map +1 -1
  657. package/dist/src/runtime/emit/drop-table.d.ts.map +1 -1
  658. package/dist/src/runtime/emit/drop-table.js +10 -0
  659. package/dist/src/runtime/emit/drop-table.js.map +1 -1
  660. package/dist/src/runtime/emit/drop-view.d.ts.map +1 -1
  661. package/dist/src/runtime/emit/drop-view.js +17 -0
  662. package/dist/src/runtime/emit/drop-view.js.map +1 -1
  663. package/dist/src/runtime/emit/envelope-scan.d.ts +13 -0
  664. package/dist/src/runtime/emit/envelope-scan.d.ts.map +1 -0
  665. package/dist/src/runtime/emit/envelope-scan.js +22 -0
  666. package/dist/src/runtime/emit/envelope-scan.js.map +1 -0
  667. package/dist/src/runtime/emit/join.d.ts +10 -2
  668. package/dist/src/runtime/emit/join.d.ts.map +1 -1
  669. package/dist/src/runtime/emit/join.js +128 -38
  670. package/dist/src/runtime/emit/join.js.map +1 -1
  671. package/dist/src/runtime/emit/lens-auxiliary-access.d.ts +16 -0
  672. package/dist/src/runtime/emit/lens-auxiliary-access.d.ts.map +1 -0
  673. package/dist/src/runtime/emit/lens-auxiliary-access.js +16 -0
  674. package/dist/src/runtime/emit/lens-auxiliary-access.js.map +1 -0
  675. package/dist/src/runtime/emit/materialized-view-helpers.d.ts +640 -0
  676. package/dist/src/runtime/emit/materialized-view-helpers.d.ts.map +1 -0
  677. package/dist/src/runtime/emit/materialized-view-helpers.js +2576 -0
  678. package/dist/src/runtime/emit/materialized-view-helpers.js.map +1 -0
  679. package/dist/src/runtime/emit/materialized-view.d.ts +31 -0
  680. package/dist/src/runtime/emit/materialized-view.d.ts.map +1 -0
  681. package/dist/src/runtime/emit/materialized-view.js +187 -0
  682. package/dist/src/runtime/emit/materialized-view.js.map +1 -0
  683. package/dist/src/runtime/emit/merge-join.d.ts.map +1 -1
  684. package/dist/src/runtime/emit/merge-join.js +15 -3
  685. package/dist/src/runtime/emit/merge-join.js.map +1 -1
  686. package/dist/src/runtime/emit/project.d.ts.map +1 -1
  687. package/dist/src/runtime/emit/project.js +10 -5
  688. package/dist/src/runtime/emit/project.js.map +1 -1
  689. package/dist/src/runtime/emit/schema-declarative.d.ts +1 -0
  690. package/dist/src/runtime/emit/schema-declarative.d.ts.map +1 -1
  691. package/dist/src/runtime/emit/schema-declarative.js +101 -5
  692. package/dist/src/runtime/emit/schema-declarative.js.map +1 -1
  693. package/dist/src/runtime/emit/set-object-tags.d.ts +16 -0
  694. package/dist/src/runtime/emit/set-object-tags.d.ts.map +1 -0
  695. package/dist/src/runtime/emit/set-object-tags.js +57 -0
  696. package/dist/src/runtime/emit/set-object-tags.js.map +1 -0
  697. package/dist/src/runtime/emit/set-operation.d.ts.map +1 -1
  698. package/dist/src/runtime/emit/set-operation.js +140 -24
  699. package/dist/src/runtime/emit/set-operation.js.map +1 -1
  700. package/dist/src/runtime/emit/subquery.d.ts.map +1 -1
  701. package/dist/src/runtime/emit/subquery.js +110 -5
  702. package/dist/src/runtime/emit/subquery.js.map +1 -1
  703. package/dist/src/runtime/emit/unary.d.ts.map +1 -1
  704. package/dist/src/runtime/emit/unary.js +34 -6
  705. package/dist/src/runtime/emit/unary.js.map +1 -1
  706. package/dist/src/runtime/emit/view-mutation.d.ts +70 -0
  707. package/dist/src/runtime/emit/view-mutation.d.ts.map +1 -0
  708. package/dist/src/runtime/emit/view-mutation.js +299 -0
  709. package/dist/src/runtime/emit/view-mutation.js.map +1 -0
  710. package/dist/src/runtime/emit/window.js +29 -5
  711. package/dist/src/runtime/emit/window.js.map +1 -1
  712. package/dist/src/runtime/foreign-key-actions.d.ts +66 -3
  713. package/dist/src/runtime/foreign-key-actions.d.ts.map +1 -1
  714. package/dist/src/runtime/foreign-key-actions.js +580 -172
  715. package/dist/src/runtime/foreign-key-actions.js.map +1 -1
  716. package/dist/src/runtime/parallel-driver.d.ts +4 -1
  717. package/dist/src/runtime/parallel-driver.d.ts.map +1 -1
  718. package/dist/src/runtime/parallel-driver.js +5 -1
  719. package/dist/src/runtime/parallel-driver.js.map +1 -1
  720. package/dist/src/runtime/register.d.ts.map +1 -1
  721. package/dist/src/runtime/register.js +17 -1
  722. package/dist/src/runtime/register.js.map +1 -1
  723. package/dist/src/runtime/types.d.ts +10 -0
  724. package/dist/src/runtime/types.d.ts.map +1 -1
  725. package/dist/src/runtime/types.js.map +1 -1
  726. package/dist/src/schema/basis-backfill.d.ts +63 -0
  727. package/dist/src/schema/basis-backfill.d.ts.map +1 -0
  728. package/dist/src/schema/basis-backfill.js +161 -0
  729. package/dist/src/schema/basis-backfill.js.map +1 -0
  730. package/dist/src/schema/catalog.d.ts +115 -1
  731. package/dist/src/schema/catalog.d.ts.map +1 -1
  732. package/dist/src/schema/catalog.js +249 -22
  733. package/dist/src/schema/catalog.js.map +1 -1
  734. package/dist/src/schema/change-events.d.ts +42 -1
  735. package/dist/src/schema/change-events.d.ts.map +1 -1
  736. package/dist/src/schema/change-events.js.map +1 -1
  737. package/dist/src/schema/column.d.ts +16 -0
  738. package/dist/src/schema/column.d.ts.map +1 -1
  739. package/dist/src/schema/column.js.map +1 -1
  740. package/dist/src/schema/constraint-builder.d.ts +182 -0
  741. package/dist/src/schema/constraint-builder.d.ts.map +1 -0
  742. package/dist/src/schema/constraint-builder.js +424 -0
  743. package/dist/src/schema/constraint-builder.js.map +1 -0
  744. package/dist/src/schema/ddl-generator.d.ts +86 -1
  745. package/dist/src/schema/ddl-generator.d.ts.map +1 -1
  746. package/dist/src/schema/ddl-generator.js +316 -20
  747. package/dist/src/schema/ddl-generator.js.map +1 -1
  748. package/dist/src/schema/declared-schema-manager.d.ts +51 -0
  749. package/dist/src/schema/declared-schema-manager.d.ts.map +1 -1
  750. package/dist/src/schema/declared-schema-manager.js +61 -0
  751. package/dist/src/schema/declared-schema-manager.js.map +1 -1
  752. package/dist/src/schema/derivation.d.ts +106 -0
  753. package/dist/src/schema/derivation.d.ts.map +1 -0
  754. package/dist/src/schema/derivation.js +25 -0
  755. package/dist/src/schema/derivation.js.map +1 -0
  756. package/dist/src/schema/function.d.ts +13 -0
  757. package/dist/src/schema/function.d.ts.map +1 -1
  758. package/dist/src/schema/function.js.map +1 -1
  759. package/dist/src/schema/lens-ack.d.ts +90 -0
  760. package/dist/src/schema/lens-ack.d.ts.map +1 -0
  761. package/dist/src/schema/lens-ack.js +361 -0
  762. package/dist/src/schema/lens-ack.js.map +1 -0
  763. package/dist/src/schema/lens-compiler.d.ts +62 -0
  764. package/dist/src/schema/lens-compiler.d.ts.map +1 -0
  765. package/dist/src/schema/lens-compiler.js +1594 -0
  766. package/dist/src/schema/lens-compiler.js.map +1 -0
  767. package/dist/src/schema/lens-fk-discovery.d.ts +175 -0
  768. package/dist/src/schema/lens-fk-discovery.d.ts.map +1 -0
  769. package/dist/src/schema/lens-fk-discovery.js +336 -0
  770. package/dist/src/schema/lens-fk-discovery.js.map +1 -0
  771. package/dist/src/schema/lens-prover.d.ts +336 -0
  772. package/dist/src/schema/lens-prover.d.ts.map +1 -0
  773. package/dist/src/schema/lens-prover.js +1988 -0
  774. package/dist/src/schema/lens-prover.js.map +1 -0
  775. package/dist/src/schema/lens.d.ts +254 -0
  776. package/dist/src/schema/lens.d.ts.map +1 -0
  777. package/dist/src/schema/lens.js +21 -0
  778. package/dist/src/schema/lens.js.map +1 -0
  779. package/dist/src/schema/manager.d.ts +676 -18
  780. package/dist/src/schema/manager.d.ts.map +1 -1
  781. package/dist/src/schema/manager.js +1573 -238
  782. package/dist/src/schema/manager.js.map +1 -1
  783. package/dist/src/schema/mapping-advertisement-tags.d.ts +39 -0
  784. package/dist/src/schema/mapping-advertisement-tags.d.ts.map +1 -0
  785. package/dist/src/schema/mapping-advertisement-tags.js +216 -0
  786. package/dist/src/schema/mapping-advertisement-tags.js.map +1 -0
  787. package/dist/src/schema/rename-rewriter.d.ts +45 -4
  788. package/dist/src/schema/rename-rewriter.d.ts.map +1 -1
  789. package/dist/src/schema/rename-rewriter.js +412 -19
  790. package/dist/src/schema/rename-rewriter.js.map +1 -1
  791. package/dist/src/schema/reserved-tags-policy.d.ts +32 -0
  792. package/dist/src/schema/reserved-tags-policy.d.ts.map +1 -0
  793. package/dist/src/schema/reserved-tags-policy.js +34 -0
  794. package/dist/src/schema/reserved-tags-policy.js.map +1 -0
  795. package/dist/src/schema/reserved-tags.d.ts +170 -0
  796. package/dist/src/schema/reserved-tags.d.ts.map +1 -0
  797. package/dist/src/schema/reserved-tags.js +507 -0
  798. package/dist/src/schema/reserved-tags.js.map +1 -0
  799. package/dist/src/schema/schema-differ.d.ts +158 -2
  800. package/dist/src/schema/schema-differ.d.ts.map +1 -1
  801. package/dist/src/schema/schema-differ.js +1460 -78
  802. package/dist/src/schema/schema-differ.js.map +1 -1
  803. package/dist/src/schema/schema-hasher.d.ts +8 -3
  804. package/dist/src/schema/schema-hasher.d.ts.map +1 -1
  805. package/dist/src/schema/schema-hasher.js +22 -2
  806. package/dist/src/schema/schema-hasher.js.map +1 -1
  807. package/dist/src/schema/schema.d.ts +25 -1
  808. package/dist/src/schema/schema.d.ts.map +1 -1
  809. package/dist/src/schema/schema.js +36 -2
  810. package/dist/src/schema/schema.js.map +1 -1
  811. package/dist/src/schema/table.d.ts +259 -10
  812. package/dist/src/schema/table.d.ts.map +1 -1
  813. package/dist/src/schema/table.js +309 -26
  814. package/dist/src/schema/table.js.map +1 -1
  815. package/dist/src/schema/unique-enforcement.d.ts +78 -0
  816. package/dist/src/schema/unique-enforcement.d.ts.map +1 -0
  817. package/dist/src/schema/unique-enforcement.js +93 -0
  818. package/dist/src/schema/unique-enforcement.js.map +1 -0
  819. package/dist/src/schema/view.d.ts +83 -2
  820. package/dist/src/schema/view.d.ts.map +1 -1
  821. package/dist/src/schema/view.js +67 -1
  822. package/dist/src/schema/view.js.map +1 -1
  823. package/dist/src/schema/window-function.d.ts +9 -1
  824. package/dist/src/schema/window-function.d.ts.map +1 -1
  825. package/dist/src/schema/window-function.js.map +1 -1
  826. package/dist/src/util/comparison.d.ts +24 -0
  827. package/dist/src/util/comparison.d.ts.map +1 -1
  828. package/dist/src/util/comparison.js +34 -0
  829. package/dist/src/util/comparison.js.map +1 -1
  830. package/dist/src/util/mutation-statement.d.ts.map +1 -1
  831. package/dist/src/util/mutation-statement.js +4 -1
  832. package/dist/src/util/mutation-statement.js.map +1 -1
  833. package/dist/src/util/serialization.d.ts +9 -0
  834. package/dist/src/util/serialization.d.ts.map +1 -1
  835. package/dist/src/util/serialization.js +26 -0
  836. package/dist/src/util/serialization.js.map +1 -1
  837. package/dist/src/vtab/backing-host.d.ts +286 -0
  838. package/dist/src/vtab/backing-host.d.ts.map +1 -0
  839. package/dist/src/vtab/backing-host.js +118 -0
  840. package/dist/src/vtab/backing-host.js.map +1 -0
  841. package/dist/src/vtab/best-access-plan.d.ts +21 -0
  842. package/dist/src/vtab/best-access-plan.d.ts.map +1 -1
  843. package/dist/src/vtab/best-access-plan.js.map +1 -1
  844. package/dist/src/vtab/capabilities.d.ts +5 -5
  845. package/dist/src/vtab/capabilities.d.ts.map +1 -1
  846. package/dist/src/vtab/mapping-advertisement.d.ts +163 -0
  847. package/dist/src/vtab/mapping-advertisement.d.ts.map +1 -0
  848. package/dist/src/vtab/mapping-advertisement.js +2 -0
  849. package/dist/src/vtab/mapping-advertisement.js.map +1 -0
  850. package/dist/src/vtab/memory/index.d.ts +64 -4
  851. package/dist/src/vtab/memory/index.d.ts.map +1 -1
  852. package/dist/src/vtab/memory/index.js +119 -12
  853. package/dist/src/vtab/memory/index.js.map +1 -1
  854. package/dist/src/vtab/memory/layer/base.d.ts +38 -1
  855. package/dist/src/vtab/memory/layer/base.d.ts.map +1 -1
  856. package/dist/src/vtab/memory/layer/base.js +112 -24
  857. package/dist/src/vtab/memory/layer/base.js.map +1 -1
  858. package/dist/src/vtab/memory/layer/manager.d.ts +291 -4
  859. package/dist/src/vtab/memory/layer/manager.d.ts.map +1 -1
  860. package/dist/src/vtab/memory/layer/manager.js +1050 -91
  861. package/dist/src/vtab/memory/layer/manager.js.map +1 -1
  862. package/dist/src/vtab/memory/layer/plan-filter.d.ts.map +1 -1
  863. package/dist/src/vtab/memory/layer/plan-filter.js +35 -6
  864. package/dist/src/vtab/memory/layer/plan-filter.js.map +1 -1
  865. package/dist/src/vtab/memory/layer/scan-layer.d.ts.map +1 -1
  866. package/dist/src/vtab/memory/layer/scan-layer.js +66 -14
  867. package/dist/src/vtab/memory/layer/scan-layer.js.map +1 -1
  868. package/dist/src/vtab/memory/layer/scan-plan.d.ts +14 -0
  869. package/dist/src/vtab/memory/layer/scan-plan.d.ts.map +1 -1
  870. package/dist/src/vtab/memory/layer/scan-plan.js +27 -4
  871. package/dist/src/vtab/memory/layer/scan-plan.js.map +1 -1
  872. package/dist/src/vtab/memory/layer/transaction.d.ts.map +1 -1
  873. package/dist/src/vtab/memory/layer/transaction.js +5 -1
  874. package/dist/src/vtab/memory/layer/transaction.js.map +1 -1
  875. package/dist/src/vtab/memory/module.d.ts +17 -0
  876. package/dist/src/vtab/memory/module.d.ts.map +1 -1
  877. package/dist/src/vtab/memory/module.js +82 -3
  878. package/dist/src/vtab/memory/module.js.map +1 -1
  879. package/dist/src/vtab/memory/table.d.ts.map +1 -1
  880. package/dist/src/vtab/memory/table.js +15 -5
  881. package/dist/src/vtab/memory/table.js.map +1 -1
  882. package/dist/src/vtab/memory/types.d.ts +20 -2
  883. package/dist/src/vtab/memory/types.d.ts.map +1 -1
  884. package/dist/src/vtab/memory/utils/predicate.d.ts.map +1 -1
  885. package/dist/src/vtab/memory/utils/predicate.js +46 -24
  886. package/dist/src/vtab/memory/utils/predicate.js.map +1 -1
  887. package/dist/src/vtab/memory/utils/primary-key-encode.d.ts +31 -0
  888. package/dist/src/vtab/memory/utils/primary-key-encode.d.ts.map +1 -0
  889. package/dist/src/vtab/memory/utils/primary-key-encode.js +101 -0
  890. package/dist/src/vtab/memory/utils/primary-key-encode.js.map +1 -0
  891. package/dist/src/vtab/memory/utils/primary-key.d.ts +8 -0
  892. package/dist/src/vtab/memory/utils/primary-key.d.ts.map +1 -1
  893. package/dist/src/vtab/memory/utils/primary-key.js +12 -5
  894. package/dist/src/vtab/memory/utils/primary-key.js.map +1 -1
  895. package/dist/src/vtab/module.d.ts +203 -4
  896. package/dist/src/vtab/module.d.ts.map +1 -1
  897. package/dist/src/vtab/table.d.ts +9 -0
  898. package/dist/src/vtab/table.d.ts.map +1 -1
  899. package/dist/src/vtab/table.js.map +1 -1
  900. package/package.json +6 -5
@@ -1,12 +1,16 @@
1
- import { buildColumnIndexMap, columnDefToSchema } from '../../../schema/table.js';
1
+ import { buildColumnIndexMap, columnDefToSchema, resolvePkDefaultConflict, resolveNamedConstraintClass, validateCollationForType } from '../../../schema/table.js';
2
+ import { BTree } from 'inheritree';
2
3
  import { StatusCode } from '../../../common/types.js';
3
4
  import { BaseLayer } from './base.js';
4
5
  import { TransactionLayer } from './transaction.js';
5
6
  import { MemoryTableConnection } from './connection.js';
7
+ import { MemoryVirtualTableConnection } from '../connection.js';
6
8
  import { Latches } from '../../../util/latches.js';
7
9
  import { QuereusError } from '../../../common/errors.js';
8
10
  import { ConflictResolution } from '../../../common/constants.js';
9
- import { compareSqlValues } from '../../../util/comparison.js';
11
+ import { buildUniqueConstraintSchema, buildForeignKeyConstraintSchema, buildCheckConstraintSchema, validateForeignKeyOverExistingRows, maintainedTableUniqueViolationError } from '../../../schema/constraint-builder.js';
12
+ import { uniqueEnforcementCollations } from '../../../schema/unique-enforcement.js';
13
+ import { compareSqlValues, rowsValueIdentical, normalizeCollationName } from '../../../util/comparison.js';
10
14
  import { scanLayer as scanLayerImpl } from './scan-layer.js';
11
15
  import { createPrimaryKeyFunctions, buildPrimaryKeyFromValues } from '../utils/primary-key.js';
12
16
  import { createMemoryTableLoggers } from '../utils/logging.js';
@@ -28,6 +32,15 @@ export class MemoryTableManager {
28
32
  isReadOnly;
29
33
  tableSchema;
30
34
  primaryKeyFunctions;
35
+ /**
36
+ * Implicit covering structures: constraint identity → the auto-index that
37
+ * realizes it. The physical structure is the synchronously-maintained
38
+ * secondary BTree; this association lets `findIndexForConstraint` and
39
+ * introspection speak the materialized-view vocabulary (an `origin`) for the
40
+ * implicit structure, the same way an explicit covering MV is described.
41
+ * Keyed by constraint name when present, else by the auto-index name.
42
+ */
43
+ implicitCoveringStructures = new Map();
31
44
  /** Optional event emitter for mutation and schema hooks */
32
45
  eventEmitter;
33
46
  constructor(db, _moduleName, schemaName, tableName, initialSchema, readOnly = false, eventEmitter) {
@@ -38,7 +51,17 @@ export class MemoryTableManager {
38
51
  this.tableSchema = initialSchema;
39
52
  this.isReadOnly = readOnly;
40
53
  this.eventEmitter = eventEmitter;
41
- this.ensureUniqueConstraintIndexes();
54
+ // Phase D (docs/lens.md § Departures — Auto-index for unique/PK): the legacy
55
+ // eager auto-index for UNIQUE constraints is a *physical*-schema behavior. A
56
+ // logical schema's UNIQUE contributes only a key/FD to the optimizer and an
57
+ // enforced boundary constraint — it creates NO structure; any covering index
58
+ // is an explicit basis-layer materialized view. Logical tables are never
59
+ // module-backed (a MemoryTableManager is never constructed for one), so this
60
+ // path is already unreachable for them — we gate explicitly regardless, so the
61
+ // separation is enforced at the source rather than relying on that invariant.
62
+ if (!this.isLogicalSchema()) {
63
+ this.ensureUniqueConstraintIndexes();
64
+ }
42
65
  this.initializePrimaryKeyFunctions();
43
66
  this.baseLayer = new BaseLayer(this.tableSchema);
44
67
  this._currentCommittedLayer = this.baseLayer;
@@ -46,10 +69,27 @@ export class MemoryTableManager {
46
69
  initializePrimaryKeyFunctions() {
47
70
  this.primaryKeyFunctions = createPrimaryKeyFunctions(this.tableSchema);
48
71
  }
72
+ /**
73
+ * One-bit guard on `Schema.kind` (docs/lens.md § Departures): true when this
74
+ * table belongs to a logical schema. Prefers the table's own `isLogical` flag
75
+ * and falls back to the owning schema's `kind`, so the gate holds even if a
76
+ * logical TableSchema were ever (incorrectly) handed to a memory manager.
77
+ */
78
+ isLogicalSchema() {
79
+ if (this.tableSchema.isLogical === true)
80
+ return true;
81
+ return this.db.schemaManager.getSchema(this.schemaName)?.kind === 'logical';
82
+ }
49
83
  /**
50
84
  * Auto-creates secondary indexes for UNIQUE constraints that don't already
51
85
  * have a matching index. This mirrors standard SQL behavior where UNIQUE
52
86
  * constraints imply an index for efficient enforcement.
87
+ *
88
+ * Alongside each such index, records an *implicit covering structure*
89
+ * descriptor in {@link implicitCoveringStructures} (the materialized-view
90
+ * vocabulary) so the implicit BTree and a future explicit covering MV share
91
+ * one schema shape. The physical structure is unchanged — observation-
92
+ * equivalent, zero behavioral difference.
53
93
  */
54
94
  ensureUniqueConstraintIndexes() {
55
95
  const uniqueConstraints = this.tableSchema.uniqueConstraints;
@@ -59,18 +99,34 @@ export class MemoryTableManager {
59
99
  const newIndexes = [...existingIndexes];
60
100
  let added = false;
61
101
  for (const uc of uniqueConstraints) {
62
- const hasMatchingIndex = existingIndexes.some(idx => idx.columns.length === uc.columns.length &&
63
- idx.columns.every((col, i) => col.index === uc.columns[i]));
64
- if (!hasMatchingIndex) {
102
+ // Reuse an existing same-column-set index ONLY when its per-column
103
+ // collations are equivalent to the declared column collations — otherwise
104
+ // it would enforce this non-derived UC under the index's collation rather
105
+ // than the declared one. A collation-mismatched index falls through to a
106
+ // distinct `_uc_*` covering index and coexists as its own constraint.
107
+ const matchingIndex = existingIndexes.find(idx => idx.columns.length === uc.columns.length &&
108
+ idx.columns.every((col, i) => col.index === uc.columns[i]) &&
109
+ this.indexCollationsMatchDeclared(idx, uc));
110
+ let indexName;
111
+ if (matchingIndex) {
112
+ indexName = matchingIndex.name;
113
+ }
114
+ else {
65
115
  const colNames = uc.columns.map(i => this.tableSchema.columns[i]?.name ?? String(i));
66
- const indexName = uc.name ?? `_uc_${colNames.join('_')}`;
116
+ indexName = uc.name ?? `_uc_${colNames.join('_')}`;
67
117
  newIndexes.push({
68
118
  name: indexName,
69
- columns: uc.columns.map(colIdx => ({ index: colIdx })),
119
+ // Carry each column's declared collation so the auto-index and the
120
+ // `checkUniqueViaIndex` path it backs — enforces UNIQUE under the column's
121
+ // collation (e.g. NOCASE) rather than defaulting to BINARY.
122
+ columns: uc.columns.map(colIdx => ({ index: colIdx, collation: this.tableSchema.columns[colIdx]?.collation })),
70
123
  predicate: uc.predicate,
71
124
  });
72
125
  added = true;
73
126
  }
127
+ // Reframe the (auto or pre-existing) secondary index as the implicit
128
+ // covering structure realizing this constraint.
129
+ this.implicitCoveringStructures.set(uc.name ?? indexName, { indexName, origin: 'implicit-from-unique-constraint' });
74
130
  }
75
131
  if (added) {
76
132
  this.tableSchema = {
@@ -79,6 +135,48 @@ export class MemoryTableManager {
79
135
  };
80
136
  }
81
137
  }
138
+ /**
139
+ * Returns the implicit covering structure realizing the given UNIQUE
140
+ * constraint, or undefined when none was synthesized. Part of the unified
141
+ * covering-structure surface the lens layer and introspection consume — the
142
+ * physical structure is the synchronously-maintained secondary BTree named
143
+ * {@link ImplicitCoveringStructure.indexName}.
144
+ */
145
+ getImplicitCoveringStructure(uc) {
146
+ const indexName = uc.name ?? this.implicitIndexNameFor(uc);
147
+ return this.implicitCoveringStructures.get(indexName);
148
+ }
149
+ /** Conventional auto-index name for an unnamed UNIQUE constraint (mirrors {@link ensureUniqueConstraintIndexes}). */
150
+ implicitIndexNameFor(uc) {
151
+ const colNames = uc.columns.map(i => this.tableSchema.columns[i]?.name ?? String(i));
152
+ return `_uc_${colNames.join('_')}`;
153
+ }
154
+ /**
155
+ * True when a same-column-set index's per-column collations are
156
+ * collation-equivalent to the constraint's DECLARED column collations.
157
+ *
158
+ * Gates REUSE of an existing same-column-set index as a non-derived UNIQUE's
159
+ * realizing structure. A non-derived (table-level / column) UNIQUE enforces
160
+ * under the declared column collation, so reusing a finer/coarser-collated
161
+ * same-column-set index (e.g. a BINARY `create unique index` over a NOCASE
162
+ * column) would silently enforce under the index's collation instead. When
163
+ * this returns false the caller builds the distinct `_uc_*` covering index and
164
+ * lets the user index coexist as an independent constraint (matches SQLite,
165
+ * where both indexes enforce).
166
+ *
167
+ * Positions align because the column SET already matched
168
+ * (`idx.columns[i]` ↔ `uc.columns[i]`). A plain index column with no explicit
169
+ * COLLATE has `collation === undefined` and falls back to the declared
170
+ * collation, so the common case stays reuse-safe.
171
+ */
172
+ indexCollationsMatchDeclared(idx, uc) {
173
+ const columns = this.tableSchema.columns;
174
+ return uc.columns.every((colIdx, i) => {
175
+ const declared = normalizeCollationName(columns[colIdx]?.collation ?? 'BINARY');
176
+ const indexColl = normalizeCollationName(idx.columns[i]?.collation ?? columns[colIdx]?.collation ?? 'BINARY');
177
+ return indexColl === declared;
178
+ });
179
+ }
82
180
  /**
83
181
  * Get the event emitter if one was provided.
84
182
  */
@@ -545,12 +643,16 @@ export class MemoryTableManager {
545
643
  existingRow: existingRow
546
644
  };
547
645
  }
548
- // Check UNIQUE constraints against secondary indexes
549
- const ucResult = this.checkUniqueConstraints(targetLayer, schema, newRowData, primaryKey, onConflict);
646
+ // Check UNIQUE constraints against secondary indexes. Secondary-UNIQUE
647
+ // REPLACE evictions (rows at OTHER PKs) accumulate in `evicted` and are
648
+ // surfaced via `evictedRows` so the DML executor runs the full delete
649
+ // pipeline (change-tracking, row-time MV maintenance, FK cascade, events).
650
+ const evicted = [];
651
+ const ucResult = await this.checkUniqueConstraints(targetLayer, schema, newRowData, primaryKey, onConflict, evicted);
550
652
  if (ucResult)
551
653
  return ucResult;
552
654
  targetLayer.recordUpsert(primaryKey, newRowData, null);
553
- return { status: 'ok', row: newRowData };
655
+ return { status: 'ok', row: newRowData, evictedRows: evicted.length > 0 ? evicted : undefined };
554
656
  }
555
657
  async performUpdate(targetLayer, values, oldKeyValues, onConflict) {
556
658
  if (!values || !oldKeyValues) {
@@ -583,17 +685,20 @@ export class MemoryTableManager {
583
685
  return this.performUpdateWithPrimaryKeyChange(targetLayer, schema, targetPrimaryKey, newPrimaryKey, oldRowData, newRowData, onConflict);
584
686
  }
585
687
  else {
586
- // Check UNIQUE constraints if any constrained columns changed
688
+ // Check UNIQUE constraints if any constrained columns changed. A
689
+ // secondary-UNIQUE REPLACE evicts the conflicting row(s) at other PKs;
690
+ // surface them via `evictedRows` for the executor's delete pipeline.
691
+ const evicted = [];
587
692
  if (this.uniqueColumnsChanged(schema, oldRowData, newRowData)) {
588
- const ucResult = this.checkUniqueConstraints(targetLayer, schema, newRowData, targetPrimaryKey, onConflict);
693
+ const ucResult = await this.checkUniqueConstraints(targetLayer, schema, newRowData, targetPrimaryKey, onConflict, evicted);
589
694
  if (ucResult)
590
695
  return ucResult;
591
696
  }
592
697
  targetLayer.recordUpsert(targetPrimaryKey, newRowData, oldRowData);
593
- return { status: 'ok', row: newRowData };
698
+ return { status: 'ok', row: newRowData, evictedRows: evicted.length > 0 ? evicted : undefined };
594
699
  }
595
700
  }
596
- performUpdateWithPrimaryKeyChange(targetLayer, schema, oldPrimaryKey, newPrimaryKey, oldRowData, newRowData, onConflict) {
701
+ async performUpdateWithPrimaryKeyChange(targetLayer, schema, oldPrimaryKey, newPrimaryKey, oldRowData, newRowData, onConflict) {
597
702
  const existingRowAtNewKey = this.lookupEffectiveRow(newPrimaryKey, targetLayer);
598
703
  if (existingRowAtNewKey !== null) {
599
704
  const pkAction = onConflict ?? resolvePkDefaultConflict(schema) ?? ConflictResolution.ABORT;
@@ -615,16 +720,19 @@ export class MemoryTableManager {
615
720
  existingRow: existingRowAtNewKey
616
721
  };
617
722
  }
618
- // Delete old row first, then check UNIQUE constraints at the new position
723
+ // Delete old row first, then check UNIQUE constraints at the new position.
724
+ // A secondary-UNIQUE REPLACE at the new position evicts conflicting row(s)
725
+ // at other PKs; surface them via `evictedRows` for the executor pipeline.
619
726
  targetLayer.recordDelete(oldPrimaryKey, oldRowData);
620
- const ucResult = this.checkUniqueConstraints(targetLayer, schema, newRowData, newPrimaryKey, onConflict);
727
+ const evicted = [];
728
+ const ucResult = await this.checkUniqueConstraints(targetLayer, schema, newRowData, newPrimaryKey, onConflict, evicted);
621
729
  if (ucResult) {
622
730
  // Rollback the delete if constraint check fails
623
731
  targetLayer.recordUpsert(oldPrimaryKey, oldRowData, null);
624
732
  return ucResult;
625
733
  }
626
734
  targetLayer.recordUpsert(newPrimaryKey, newRowData, null);
627
- return { status: 'ok', row: newRowData };
735
+ return { status: 'ok', row: newRowData, evictedRows: evicted.length > 0 ? evicted : undefined };
628
736
  }
629
737
  async performDelete(targetLayer, oldKeyValues) {
630
738
  if (!oldKeyValues) {
@@ -653,8 +761,13 @@ export class MemoryTableManager {
653
761
  return true;
654
762
  }
655
763
  if (uc.predicate) {
656
- const idx = this.findIndexForConstraint(this._currentCommittedLayer, uc);
657
- const referenced = idx?.predicate?.referencedColumns;
764
+ const covering = this.findIndexForConstraint(this._currentCommittedLayer, uc);
765
+ // For an index the compiled predicate is already on hand; for an MV-covered
766
+ // (or uncovered) constraint, compile the partial predicate ad hoc to learn
767
+ // which columns can transition the row across the predicate's scope.
768
+ const referenced = covering?.kind === 'memory-index'
769
+ ? covering.index.predicate?.referencedColumns
770
+ : compilePredicate(uc.predicate, schema.columns).referencedColumns;
658
771
  if (referenced) {
659
772
  for (const colIdx of referenced) {
660
773
  if (compareSqlValues(oldRow[colIdx], newRow[colIdx]) !== 0)
@@ -668,78 +781,251 @@ export class MemoryTableManager {
668
781
  /**
669
782
  * Checks all UNIQUE constraints for a new/updated row. Returns an UpdateResult
670
783
  * if a violation is found (or IGNORE suppresses the insert), or null if all pass.
671
- * For REPLACE conflicts, the conflicting rows are deleted from the layer.
784
+ * For REPLACE conflicts, the conflicting rows are deleted from the layer and
785
+ * pushed onto `evicted` so the DML executor can run the full delete pipeline
786
+ * (change-tracking, row-time MV maintenance, FK cascade, auto-events) for each.
672
787
  */
673
- checkUniqueConstraints(targetLayer, schema, newRowData, newPrimaryKey, onConflict) {
788
+ async checkUniqueConstraints(targetLayer, schema, newRowData, newPrimaryKey, onConflict, evicted) {
674
789
  if (!schema.uniqueConstraints)
675
790
  return null;
676
791
  for (const uc of schema.uniqueConstraints) {
677
- const result = this.checkSingleUniqueConstraint(targetLayer, schema, uc, newRowData, newPrimaryKey, onConflict);
792
+ const result = await this.checkSingleUniqueConstraint(targetLayer, schema, uc, newRowData, newPrimaryKey, onConflict, evicted);
678
793
  if (result)
679
794
  return result;
680
795
  }
681
796
  return null;
682
797
  }
683
- checkSingleUniqueConstraint(targetLayer, schema, uc, newRowData, newPrimaryKey, onConflict) {
798
+ async checkSingleUniqueConstraint(targetLayer, schema, uc, newRowData, newPrimaryKey, onConflict, evicted, allowMvCovering = true) {
684
799
  // SQL semantics: UNIQUE allows multiple NULLs — skip if any constrained column is NULL
685
800
  if (uc.columns.some(colIdx => newRowData[colIdx] === null))
686
801
  return null;
687
- // Find the matching secondary index for this constraint
688
- const index = this.findIndexForConstraint(targetLayer, uc);
689
- // Partial UNIQUE: a row whose predicate is not unambiguously TRUE is
690
- // outside the index's scope and contributes nothing to uniqueness.
691
- if (index?.predicate && !index.rowMatchesPredicate(newRowData)) {
802
+ // Find the covering structure enforcing this constraint.
803
+ const covering = this.findIndexForConstraint(targetLayer, uc, allowMvCovering);
804
+ // Partial UNIQUE: a row whose predicate is not unambiguously TRUE is outside
805
+ // the structure's scope and contributes nothing to uniqueness. The source-side
806
+ // skip must short-circuit identically regardless of which structure covers the
807
+ // constraint — for the MV path the partial scope is governed by the (aligned)
808
+ // `uc.predicate` (the prover proves the MV's WHERE equivalent to it).
809
+ if (covering?.kind === 'memory-index'
810
+ && covering.index.predicate
811
+ && !covering.index.rowMatchesPredicate(newRowData)) {
812
+ return null;
813
+ }
814
+ if (covering?.kind === 'materialized-view'
815
+ && uc.predicate
816
+ && compilePredicate(uc.predicate, schema.columns).evaluate(newRowData) !== true) {
692
817
  return null;
693
818
  }
694
819
  // Resolve effective action: statement OR > constraint default > ABORT.
695
820
  const effective = onConflict ?? uc.defaultConflict ?? ConflictResolution.ABORT;
696
- if (index) {
697
- return this.checkUniqueViaIndex(targetLayer, schema, uc, index, newRowData, newPrimaryKey, effective);
821
+ if (covering) {
822
+ switch (covering.kind) {
823
+ case 'memory-index':
824
+ return this.checkUniqueViaIndex(targetLayer, schema, uc, covering.index, newRowData, newPrimaryKey, effective, evicted);
825
+ case 'materialized-view':
826
+ return this.checkUniqueViaMaterializedView(targetLayer, schema, uc, covering.view, newRowData, newPrimaryKey, effective, evicted);
827
+ default: {
828
+ const exhaustive = covering;
829
+ throw new QuereusError(`Unknown covering structure: ${JSON.stringify(exhaustive)}`, StatusCode.INTERNAL);
830
+ }
831
+ }
698
832
  }
699
833
  // Fallback: scan primary tree
700
- return this.checkUniqueByScanning(targetLayer, schema, uc, newRowData, newPrimaryKey, effective);
834
+ return this.checkUniqueByScanning(targetLayer, schema, uc, newRowData, newPrimaryKey, effective, evicted);
701
835
  }
702
- findIndexForConstraint(targetLayer, uc) {
836
+ /**
837
+ * Resolves the {@link CoveringStructure} enforcing a UNIQUE constraint. Prefers
838
+ * a linked, non-stale row-time covering MV when one is present (the live
839
+ * enforcement path in v1; the sole structure once the auto-index is
840
+ * retired — see {@link CoveringStructure}), falling back to the auto-built
841
+ * `memory-index`. The row-time resolution is a synchronous map lookup with an
842
+ * O(1) negative fast path, so a non-covered table stays on the index path at
843
+ * effectively no cost.
844
+ *
845
+ * `allowMvCovering = false` skips the MV preference: the maintenance-write
846
+ * enforcement path ({@link enforceSecondaryUniqueOnMaintenance}) checks rows
847
+ * THIS table's batch just wrote, and a covering MV over this table is
848
+ * cascade-maintained only after the batch returns — it lags the batch and
849
+ * would miss a same-batch colliding pair. The synchronously-maintained
850
+ * auto-index is exact.
851
+ */
852
+ findIndexForConstraint(targetLayer, uc, allowMvCovering = true) {
853
+ if (allowMvCovering) {
854
+ const mv = this.db._findRowTimeCoveringStructure(this.schemaName, this._tableName, uc);
855
+ if (mv)
856
+ return { kind: 'materialized-view', view: mv };
857
+ }
703
858
  const schema = targetLayer.getSchema();
704
859
  if (!schema.indexes)
705
860
  return undefined;
861
+ // Resolve the constraint's OWN realizing structure BY NAME — never the
862
+ // column-set scan below, which returns the FIRST same-column-set index and
863
+ // (when several differently-collated indexes cover one column-set) would
864
+ // enforce a UC under the wrong index's collation, generating candidates from
865
+ // that index's wrongly-keyed BTree:
866
+ // - index-derived UNIQUE (`CREATE UNIQUE INDEX`) → its own index name via
867
+ // `uc.derivedFromIndex` (matches store/isolation's by-name resolution
868
+ // through `uniqueEnforcementCollations`).
869
+ // - non-derived UNIQUE (table-level / column) → its own `_uc_*` covering
870
+ // index via `implicitCoveringStructures`. The realization guard only
871
+ // reuses a collation-equivalent same-column-set index, so a pre-existing
872
+ // finer index (e.g. BINARY over a NOCASE column) no longer collapses onto
873
+ // the constraint; resolving by name is robust to `schema.indexes` order
874
+ // (the finer index may be listed earlier, having been created first).
875
+ // Both fall through to the column-set scan only when the name does not
876
+ // resolve (defensive).
877
+ if (uc.derivedFromIndex) {
878
+ const index = targetLayer.getSecondaryIndex?.(uc.derivedFromIndex);
879
+ if (index)
880
+ return { kind: 'memory-index', index };
881
+ }
882
+ else {
883
+ const own = this.getImplicitCoveringStructure(uc);
884
+ if (own) {
885
+ const index = targetLayer.getSecondaryIndex?.(own.indexName);
886
+ if (index)
887
+ return { kind: 'memory-index', index };
888
+ }
889
+ }
890
+ // Defensive fallback: match the auto-built `_uc_*` covering index by
891
+ // column-set when the by-name resolution above did not land.
706
892
  for (const idx of schema.indexes) {
707
893
  if (idx.columns.length === uc.columns.length &&
708
894
  idx.columns.every((col, i) => col.index === uc.columns[i])) {
709
- return targetLayer.getSecondaryIndex?.(idx.name);
895
+ const index = targetLayer.getSecondaryIndex?.(idx.name);
896
+ return index ? { kind: 'memory-index', index } : undefined;
710
897
  }
711
898
  }
712
899
  return undefined;
713
900
  }
714
- checkUniqueViaIndex(targetLayer, schema, uc, index, newRowData, newPrimaryKey, onConflict) {
901
+ checkUniqueViaIndex(targetLayer, schema, uc, index, newRowData, newPrimaryKey, onConflict, evicted) {
715
902
  const indexKey = index.keyFromRow(newRowData);
716
903
  const existingPKs = index.getPrimaryKeys(indexKey);
717
904
  for (const existingPK of existingPKs) {
718
905
  if (this.comparePrimaryKeys(newPrimaryKey, existingPK) === 0)
719
906
  continue;
720
- // Found a different row with the same unique key values
907
+ // Validate the candidate against the live effective row before acting —
908
+ // the same stale-candidate discipline as checkUniqueViaMaterializedView.
909
+ // An index entry's PK can still lag the effective row set *within* a
910
+ // statement (a candidate row deleted/updated internally, or a prior
911
+ // REPLACE eviction whose index removal lands later in the batch), so a
912
+ // candidate whose row is gone, no longer carries the colliding values,
913
+ // or left a partial index's scope is skipped rather than raised as a
914
+ // false conflict (or, worse, REPLACE-evicting an innocent row). The
915
+ // entry now tracks PKs by value (removeEntry drops composite PKs
916
+ // correctly, so it no longer accumulates stale-by-reference members);
917
+ // this live re-check remains as defense-in-depth for that genuine
918
+ // intra-statement lag.
919
+ // Compare under the INDEX's per-column collation (positionally aligned
920
+ // with uc.columns — findIndexForConstraint requires it): the index is
921
+ // the enforcing structure, and an explicit `create unique index …
922
+ // (col collate nocase)` may declare a coarser collation than the
923
+ // column — re-checking under the column's collation would skip the
924
+ // case-variant candidates the index legitimately unifies.
925
+ // This is the authoritative LIVE-index source for the per-column
926
+ // enforcement collation. The shared `uniqueEnforcementCollations(schema,
927
+ // uc)` helper (which store/isolation import, and checkUniqueViaMaterializedView
928
+ // uses) resolves the SAME per-column value, but BY NAME via
929
+ // `uc.derivedFromIndex`. `findIndexForConstraint` now ALSO resolves an
930
+ // index-derived UC by that name (the column-set scan is only the
931
+ // non-derived fallback), so the live `index` handle here IS the UC's own
932
+ // index and `index.specColumns[i]?.collation` is the correct per-column
933
+ // collation even when several same-column-set indexes exist with
934
+ // differing collations. The `(schema, uc)` helper signature still has no
935
+ // MemoryIndex handle, so this site keeps the live-handle read — but the
936
+ // two resolutions now agree on the multi-index shape too. The agreement is
937
+ // pinned by test/unique-enforcement-collation.spec.ts (a real divergence
938
+ // is a finding, not a reason to widen the helper).
939
+ const conflictingRow = this.lookupEffectiveRow(existingPK, targetLayer);
940
+ if (!conflictingRow)
941
+ continue;
942
+ if (!uc.columns.every((col, i) => compareSqlValues(newRowData[col], conflictingRow[col], index.specColumns[i]?.collation ?? schema.columns[col].collation) === 0))
943
+ continue;
944
+ if (index.predicate && !index.rowMatchesPredicate(conflictingRow))
945
+ continue;
946
+ // Found a different live row with the same unique key values
721
947
  if (onConflict === ConflictResolution.IGNORE) {
722
948
  return { status: 'ok', row: undefined };
723
949
  }
724
950
  if (onConflict === ConflictResolution.REPLACE) {
725
- const conflictingRow = this.lookupEffectiveRow(existingPK, targetLayer);
726
- if (conflictingRow) {
727
- targetLayer.recordDelete(existingPK, conflictingRow);
728
- }
729
- return null; // Conflict resolved, continue with insert
951
+ targetLayer.recordDelete(existingPK, conflictingRow);
952
+ // Report the eviction so the executor runs its delete pipeline.
953
+ evicted.push(conflictingRow);
954
+ continue; // conflict resolved, keep scanning for further duplicates
730
955
  }
731
956
  const colNames = uc.columns.map(i => schema.columns[i].name).join(', ');
732
- const existingRow = this.lookupEffectiveRow(existingPK, targetLayer);
733
957
  return {
734
958
  status: 'constraint',
735
959
  constraint: 'unique',
736
960
  message: `UNIQUE constraint failed: ${this._tableName} (${colNames})`,
737
- existingRow: existingRow ?? undefined
961
+ existingRow: conflictingRow
738
962
  };
739
963
  }
740
964
  return null;
741
965
  }
742
- checkUniqueByScanning(targetLayer, schema, uc, newRowData, newPrimaryKey, onConflict) {
966
+ /**
967
+ * Enforce a UNIQUE constraint through its linked `row-time` covering MV's backing
968
+ * table (mirrors {@link checkUniqueViaIndex}, but the candidates come from the MV
969
+ * rather than a secondary BTree). The backing scan yields candidate conflicting
970
+ * source PKs; each is *validated against the live source row* before acting, since
971
+ * a backing entry can lag a source row deleted/updated internally within the same
972
+ * statement (e.g. the PK-changing-UPDATE delete below, or a prior REPLACE eviction)
973
+ * — the row-time hook only fires for DML-executor row writes, not these internal
974
+ * mutations. A candidate whose source row is gone or no longer matches the UC is
975
+ * stale and skipped, so a false conflict is never raised.
976
+ *
977
+ * On a REPLACE eviction the conflicting **source** row is deleted directly on the
978
+ * transaction layer and pushed onto `evicted`; the DML executor then runs the full
979
+ * delete pipeline for it (change-tracking, FK cascade, auto-events, and the
980
+ * row-time covering-structure maintenance that removes the evicted row's backing
981
+ * entry — so a later same-UC row in the statement never sees a phantom). The
982
+ * executor processes the eviction before the writing row's own bookkeeping, so the
983
+ * backing delete still lands within this statement.
984
+ */
985
+ async checkUniqueViaMaterializedView(targetLayer, schema, uc, mv, newRowData, newPrimaryKey, onConflict, evicted) {
986
+ const newSourcePk = Array.isArray(newPrimaryKey) ? newPrimaryKey : [newPrimaryKey];
987
+ const conflicts = await this.db._lookupCoveringConflicts(mv, uc, newRowData, newSourcePk);
988
+ // Re-validate under each column's enforcement collation — the index's per-column
989
+ // COLLATE for an index-derived UNIQUE, else the declared column collation
990
+ // (uniqueEnforcementCollations) — mirroring checkUniqueViaIndex, the store's
991
+ // findUniqueConflictViaCoveringMv, and the isolation overlay, so all modules agree.
992
+ // The candidate generation (_lookupCoveringConflicts) narrows under the SOURCE
993
+ // column's DECLARED collation, so for a FINER index (e.g. BINARY over a NOCASE
994
+ // column) it returns a superset this filters down correctly; a finer/incomparable
995
+ // index-derived UNIQUE whose declared candidate set could be a subset is declined
996
+ // upstream by findRowTimeCoveringStructure's collation gate, so only BINARY-floor
997
+ // or equal-collation MVs ever reach here.
998
+ const collations = uniqueEnforcementCollations(schema, uc);
999
+ for (const conflict of conflicts) {
1000
+ const existingPK = buildPrimaryKeyFromValues(conflict.pk, schema.primaryKeyDefinition);
1001
+ if (this.comparePrimaryKeys(newPrimaryKey, existingPK) === 0)
1002
+ continue;
1003
+ // Validate against the live source row: skip stale backing candidates.
1004
+ const conflictingRow = this.lookupEffectiveRow(existingPK, targetLayer);
1005
+ if (!conflictingRow)
1006
+ continue;
1007
+ if (!uc.columns.every((col, i) => compareSqlValues(newRowData[col], conflictingRow[col], collations[i]) === 0))
1008
+ continue;
1009
+ if (onConflict === ConflictResolution.IGNORE) {
1010
+ return { status: 'ok', row: undefined };
1011
+ }
1012
+ if (onConflict === ConflictResolution.REPLACE) {
1013
+ targetLayer.recordDelete(existingPK, conflictingRow);
1014
+ // Report the eviction; the executor maintains the covering backing.
1015
+ evicted.push(conflictingRow);
1016
+ continue; // conflict resolved, keep scanning for further duplicates
1017
+ }
1018
+ const colNames = uc.columns.map(i => schema.columns[i].name).join(', ');
1019
+ return {
1020
+ status: 'constraint',
1021
+ constraint: 'unique',
1022
+ message: `UNIQUE constraint failed: ${this._tableName} (${colNames})`,
1023
+ existingRow: conflictingRow
1024
+ };
1025
+ }
1026
+ return null;
1027
+ }
1028
+ checkUniqueByScanning(targetLayer, schema, uc, newRowData, newPrimaryKey, onConflict, evicted) {
743
1029
  const primaryTree = targetLayer.getModificationTree('primary');
744
1030
  if (!primaryTree)
745
1031
  return null;
@@ -755,7 +1041,7 @@ export class MemoryTableManager {
755
1041
  continue;
756
1042
  if (predicate && predicate.evaluate(existingRow) !== true)
757
1043
  continue;
758
- const allMatch = uc.columns.every(colIdx => compareSqlValues(newRowData[colIdx], existingRow[colIdx]) === 0);
1044
+ const allMatch = uc.columns.every(colIdx => compareSqlValues(newRowData[colIdx], existingRow[colIdx], schema.columns[colIdx].collation) === 0);
759
1045
  if (!allMatch)
760
1046
  continue;
761
1047
  if (onConflict === ConflictResolution.IGNORE) {
@@ -763,6 +1049,8 @@ export class MemoryTableManager {
763
1049
  }
764
1050
  if (onConflict === ConflictResolution.REPLACE) {
765
1051
  targetLayer.recordDelete(existingPK, existingRow);
1052
+ // Report the eviction so the executor runs its delete pipeline.
1053
+ evicted.push(existingRow);
766
1054
  return null;
767
1055
  }
768
1056
  const colNames = uc.columns.map(i => schema.columns[i].name).join(', ');
@@ -810,8 +1098,271 @@ export class MemoryTableManager {
810
1098
  }
811
1099
  this.baseLayer.primaryTree.insert(row);
812
1100
  }
1101
+ /**
1102
+ * Atomically replaces the entire committed contents with `rows` by building a
1103
+ * fresh {@link BaseLayer} and swapping it in under the SchemaChange latch.
1104
+ * Used to (re)materialize a materialized view: callers run the view body to
1105
+ * completion and hand the result rows here. Concurrent readers do NOT block:
1106
+ * each scan reads a base-layer snapshot captured at start-of-call, so an
1107
+ * in-flight scan keeps the pre-swap base while a fresh scan sees the new base
1108
+ * — never a partial state. The swap itself is a single synchronous assignment
1109
+ * performed under the SchemaChange latch (which serializes swaps with `alter
1110
+ * table` and other refreshes, not with readers).
1111
+ *
1112
+ * Throws on a duplicate primary key among `rows` (the caller rolls back).
1113
+ * Callers may pass `onDuplicateKey` to substitute a purpose-built diagnostic
1114
+ * for the duplicate-PK case (e.g. the materialized-view "must be a set"
1115
+ * message); when omitted, the generic backing-table message is thrown. The
1116
+ * factory only controls the wording — duplicate detection still uses the
1117
+ * btree's collation/desc/composite-correct key comparison.
1118
+ */
1119
+ async replaceBaseLayer(rows, onDuplicateKey) {
1120
+ if (this.isReadOnly) {
1121
+ throw new QuereusError(`Table '${this._tableName}' is read-only`, StatusCode.READONLY);
1122
+ }
1123
+ const lockKey = `MemoryTable.SchemaChange:${this.schemaName}.${this._tableName}`;
1124
+ const release = await Latches.acquire(lockKey);
1125
+ try {
1126
+ // Drain any in-flight transaction layers down to the base so the swap
1127
+ // below isn't shadowed by a committed transaction layer ahead of base.
1128
+ await this.ensureSchemaChangeSafety();
1129
+ const oldBase = this.baseLayer;
1130
+ const newBase = new BaseLayer(this.tableSchema);
1131
+ for (const row of rows) {
1132
+ const key = this.primaryKeyFunctions.extractFromRow(row);
1133
+ const path = newBase.primaryTree.find(key);
1134
+ if (path.on) {
1135
+ throw onDuplicateKey
1136
+ ? onDuplicateKey()
1137
+ : new QuereusError(`UNIQUE constraint failed: ${this._tableName} PK.`, StatusCode.CONSTRAINT);
1138
+ }
1139
+ newBase.primaryTree.insert(row);
1140
+ }
1141
+ newBase.rebuildAllSecondaryIndexes();
1142
+ this.baseLayer = newBase;
1143
+ this._currentCommittedLayer = newBase;
1144
+ // Re-point any connection still reading the old base at the new base so
1145
+ // the next statement observes refreshed contents.
1146
+ for (const conn of this.connections.values()) {
1147
+ if (conn.readLayer === oldBase) {
1148
+ conn.readLayer = newBase;
1149
+ }
1150
+ }
1151
+ }
1152
+ finally {
1153
+ release();
1154
+ }
1155
+ }
1156
+ /**
1157
+ * Privileged **transactional** maintenance write: apply an ordered
1158
+ * {@link MaintenanceOp} batch to a given connection's *pending*
1159
+ * {@link TransactionLayer} (creating it lazily, exactly as a user write would).
1160
+ * The row-time materialized-view maintenance path uses it so a covering
1161
+ * structure's backing table is kept consistent synchronously with each source
1162
+ * row-write — within the same transaction, visible to later reads on this
1163
+ * connection (reads-own-writes), and committed/rolled-back in lockstep with the
1164
+ * source write by the Database's coordinated commit.
1165
+ *
1166
+ * It deliberately bypasses {@link validateMutationPermissions} (which throws
1167
+ * READONLY for MV backing tables) and reuses {@link TransactionLayer.recordUpsert} /
1168
+ * {@link TransactionLayer.recordDelete} so secondary-index and change-tracking
1169
+ * bookkeeping stay correct. No latch is taken: the pending layer is private to
1170
+ * `connection`, only this synchronous path writes it, and the tree mutations are
1171
+ * synchronous — so a multi-row statement's later rows observe earlier rows'
1172
+ * pending writes with no interleaving.
1173
+ *
1174
+ * Declared secondary UNIQUE constraints ARE enforced — post-batch, against the
1175
+ * final effective contents, throwing the maintained-table-attributed
1176
+ * CONSTRAINT error ({@link enforceSecondaryUniqueOnMaintenance}). CHECK / FK
1177
+ * stay engine-validated (see `vtab/backing-host.ts` § Constraint validation).
1178
+ *
1179
+ * Returns the **effective** changes it applied (one {@link BackingRowChange} per
1180
+ * backing row it mutated): a `delete-key` that found a row → `delete`; an `upsert` →
1181
+ * `update` when it replaced an existing row, else `insert`; a `delete-by-prefix` →
1182
+ * one `delete` per matched row; a `replace-all` → the minimal keyed diff between the
1183
+ * new and old contents (insert/update/delete, identical rows skipped). A
1184
+ * `delete-key`/`delete-by-prefix` that matches nothing, an `upsert` whose row is
1185
+ * **value-identical** to the effective existing row (`rowsValueIdentical` — written
1186
+ * nothing, reported nothing; the normative skip in `vtab/backing-host.ts`), or a
1187
+ * `replace-all` whose new contents equal the old — produces nothing. The MV-over-MV
1188
+ * cascade feeds these onward to MVs reading this backing table (see
1189
+ * `database-materialized-views.ts` § cascade).
1190
+ *
1191
+ * Async only because `delete-by-prefix` / `replace-all` reuse the async layer scan to
1192
+ * enumerate the affected (prefix / whole-table) slice; the point ops stay synchronous
1193
+ * within the same pass, so a multi-row statement's later rows still observe earlier
1194
+ * rows' pending writes with no interleaving (no await separates a single op's lookup
1195
+ * from its record).
1196
+ */
1197
+ async applyMaintenanceToLayer(connection, ops) {
1198
+ const changes = [];
1199
+ if (ops.length === 0)
1200
+ return changes;
1201
+ this.ensureTransactionLayer(connection);
1202
+ const layer = connection.pendingTransactionLayer;
1203
+ for (const op of ops) {
1204
+ switch (op.kind) {
1205
+ case 'delete-key': {
1206
+ const existing = this.lookupEffectiveRow(op.key, layer);
1207
+ if (existing) {
1208
+ layer.recordDelete(op.key, existing);
1209
+ changes.push({ op: 'delete', oldRow: existing });
1210
+ }
1211
+ break;
1212
+ }
1213
+ case 'upsert': {
1214
+ const key = this.primaryKeyFunctions.extractFromRow(op.row);
1215
+ const existing = this.lookupEffectiveRow(key, layer);
1216
+ if (existing && rowsValueIdentical(existing, op.row)) {
1217
+ // Value-identical against the EFFECTIVE row (pending over committed):
1218
+ // nothing changes, so write nothing and report nothing — the
1219
+ // skip-identical upsert contract (vtab/backing-host.ts), the point-op
1220
+ // analogue of the replace-all diff's identical-row skip. Both skips are
1221
+ // byte-faithful (`rowsValueIdentical`, BINARY per column): a collation-equal
1222
+ // / byte-different row (a case-only rewrite under NOCASE) is a real change
1223
+ // that must re-key the stored bytes — collation governs key identity only.
1224
+ break;
1225
+ }
1226
+ layer.recordUpsert(key, op.row, existing);
1227
+ changes.push(existing
1228
+ ? { op: 'update', oldRow: existing, newRow: op.row }
1229
+ : { op: 'insert', newRow: op.row });
1230
+ break;
1231
+ }
1232
+ case 'delete-by-prefix': {
1233
+ // Range-scan the primary tree over the half-open interval whose leading
1234
+ // PK columns equal `keyPrefix` (the btree orders by the composite PK,
1235
+ // base-PK columns leading, so the slice is contiguous; `scanLayer`'s
1236
+ // `equalityPrefix` seeks to it and early-terminates on prefix mismatch).
1237
+ // Collect the matched rows first, THEN `recordDelete` each — the same
1238
+ // per-row bookkeeping (secondary indexes, change tracking) the point
1239
+ // `delete-key` arm uses, over a prefix range instead of a point.
1240
+ // Collect-then-delete avoids mutating the tree mid-iteration.
1241
+ const scanPlan = { indexName: 'primary', descending: false, equalityPrefix: op.keyPrefix };
1242
+ const matched = [];
1243
+ for await (const row of scanLayerImpl(layer, scanPlan)) {
1244
+ matched.push({ key: this.primaryKeyFunctions.extractFromRow(row), row });
1245
+ }
1246
+ for (const { key, row } of matched) {
1247
+ layer.recordDelete(key, row);
1248
+ changes.push({ op: 'delete', oldRow: row });
1249
+ }
1250
+ break;
1251
+ }
1252
+ case 'replace-all': {
1253
+ // Wholesale transactional replacement, realized as the minimal keyed diff
1254
+ // (by backing PK) against the layer's current effective rows. Snapshot the
1255
+ // old rows FIRST — the same whole-table effective iteration the
1256
+ // `delete-by-prefix` arm scopes to a prefix — into a PK-keyed btree, so the
1257
+ // diff is computed against a stable before-image regardless of the upserts
1258
+ // applied below. Collation governs KEY identity only: keys are compared with
1259
+ // the table's PK comparator (honoring PK-column collation), so a new row whose
1260
+ // key only differs by collation (e.g. 'apple' vs a stored 'APPLE' under a NOCASE
1261
+ // PK) matches its old row and resolves to an `update` — never a spurious insert +
1262
+ // delete that would leak secondary-index bookkeeping. VALUE fidelity of a paired
1263
+ // row is byte-faithful (`rowsValueIdentical`, below) — one discipline, not two.
1264
+ const oldByKey = new BTree(e => e.key, this.comparePrimaryKeys);
1265
+ for await (const row of scanLayerImpl(layer, { indexName: 'primary', descending: false })) {
1266
+ oldByKey.insert({ key: this.primaryKeyFunctions.extractFromRow(row), row });
1267
+ }
1268
+ // New-row keys (same PK comparator) for the delete pass's membership test.
1269
+ const newKeys = new BTree(k => k, this.comparePrimaryKeys);
1270
+ // Insert/update/skip-identical pass, in new-row order.
1271
+ for (const newRow of op.rows) {
1272
+ const key = this.primaryKeyFunctions.extractFromRow(newRow);
1273
+ newKeys.insert(key);
1274
+ const existing = oldByKey.get(key);
1275
+ if (!existing) {
1276
+ layer.recordUpsert(key, newRow, null);
1277
+ changes.push({ op: 'insert', newRow });
1278
+ }
1279
+ else if (!rowsValueIdentical(existing.row, newRow)) {
1280
+ layer.recordUpsert(key, newRow, existing.row);
1281
+ changes.push({ op: 'update', oldRow: existing.row, newRow });
1282
+ }
1283
+ // else: byte-identical at this key — a true no-op, no emitted change.
1284
+ // The skip is byte-faithful (`rowsValueIdentical`): a collation-equal /
1285
+ // byte-different paired row (a case-only rewrite under a NOCASE PK) is an
1286
+ // `update` that re-keys the stored bytes, matching the point-op upsert skip
1287
+ // and the byte-exact maintenance-equivalence oracle.
1288
+ }
1289
+ // Delete pass: every old key absent from the new set, ascending PK order.
1290
+ // `oldByKey` is a private snapshot, not mutated here, so iterating it while
1291
+ // `recordDelete` mutates the layer's tree is safe.
1292
+ for (const path of oldByKey.ascending(oldByKey.first())) {
1293
+ const entry = oldByKey.at(path);
1294
+ if (newKeys.get(entry.key) !== undefined)
1295
+ continue;
1296
+ layer.recordDelete(entry.key, entry.row);
1297
+ changes.push({ op: 'delete', oldRow: entry.row });
1298
+ }
1299
+ break;
1300
+ }
1301
+ default: {
1302
+ // A new MaintenanceOp must extend this switch; never-assignment makes
1303
+ // that a compile error rather than a silent no-op.
1304
+ const exhaustiveCheck = op;
1305
+ throw new QuereusError(`Unknown maintenance op: ${JSON.stringify(exhaustiveCheck)}`, StatusCode.INTERNAL);
1306
+ }
1307
+ }
1308
+ }
1309
+ await this.enforceSecondaryUniqueOnMaintenance(layer, changes);
1310
+ return changes;
1311
+ }
1312
+ /**
1313
+ * Declared secondary-UNIQUE enforcement for maintenance writes — the
1314
+ * collision-shaped half of the derived-row constraint contract (CHECK / FK
1315
+ * are per-row properties and validate engine-side; see
1316
+ * docs/materialized-views.md § Derived-row constraint validation). The
1317
+ * privileged surface bypasses the DML constraint pipeline, so without this
1318
+ * the batch above would store two derived rows colliding on a declared
1319
+ * UNIQUE silently.
1320
+ *
1321
+ * Runs POST-batch over the effective changes, never per-op: a `replace-all`
1322
+ * diff applies its upserts before its deletes, so an in-flight per-op check
1323
+ * would false-positive against a row the same batch is about to delete
1324
+ * (e.g. the derived set moved a unique value from one primary key to
1325
+ * another). After the batch the layer holds exactly the final contents, so
1326
+ * checking each WRITTEN image against it is exact — and complete: every
1327
+ * pre-existing row entered through DML / ADD CONSTRAINT / earlier validated
1328
+ * maintenance, so any colliding pair includes at least one written image.
1329
+ * A value-identical upsert the batch skipped emitted no change and cannot
1330
+ * introduce a collision (the table's contents did not change at that key).
1331
+ *
1332
+ * Reuses {@link checkSingleUniqueConstraint} (same-PK exclusion, NULL-pass,
1333
+ * partial-predicate scope, per-column collation, auto-index fast path) with
1334
+ * two maintenance-specific postures: the conflict action is forced to ABORT
1335
+ * (a derivation write carries no user OR clause, and a declared
1336
+ * `on conflict replace`/`ignore` default must not silently evict or drop
1337
+ * derived rows — the eviction would diverge the table from its derivation),
1338
+ * and the covering-MV route is bypassed (see
1339
+ * {@link findIndexForConstraint}'s `allowMvCovering`).
1340
+ *
1341
+ * Zero overhead when the table declares no secondary UNIQUE (every MV-sugar
1342
+ * backing, and most maintained tables): one empty-array check.
1343
+ */
1344
+ async enforceSecondaryUniqueOnMaintenance(layer, changes) {
1345
+ const schema = layer.getSchema();
1346
+ const ucs = schema.uniqueConstraints;
1347
+ if (!ucs || ucs.length === 0 || changes.length === 0)
1348
+ return;
1349
+ // ABORT means the IGNORE/REPLACE arms never fire, so nothing ever lands here.
1350
+ const noEvict = [];
1351
+ for (const change of changes) {
1352
+ if (change.op === 'delete')
1353
+ continue;
1354
+ const newPrimaryKey = this.primaryKeyFromRow(change.newRow);
1355
+ for (const uc of ucs) {
1356
+ const result = await this.checkSingleUniqueConstraint(layer, schema, uc, change.newRow, newPrimaryKey, ConflictResolution.ABORT, noEvict, /*allowMvCovering*/ false);
1357
+ if (result) {
1358
+ const colNames = uc.columns.map(i => schema.columns[i]?.name ?? String(i));
1359
+ throw maintainedTableUniqueViolationError(this.schemaName, this._tableName, uc.name ?? `_uc_${colNames.join('_')}`, colNames, uc.columns.map(i => change.newRow[i]));
1360
+ }
1361
+ }
1362
+ }
1363
+ }
813
1364
  // --- Schema Operations (simplified with inherited BTrees) ---
814
- async addColumn(columnDefAst) {
1365
+ async addColumn(columnDefAst, backfillEvaluator) {
815
1366
  if (this.isReadOnly)
816
1367
  throw new QuereusError(`Table '${this._tableName}' is read-only`, StatusCode.READONLY);
817
1368
  const lockKey = `MemoryTable.SchemaChange:${this.schemaName}.${this._tableName}`;
@@ -822,7 +1373,11 @@ export class MemoryTableManager {
822
1373
  // Get default nullability setting from database options
823
1374
  const defaultNullability = this.db.options.getStringOption('default_column_nullability');
824
1375
  const defaultNotNull = defaultNullability === 'not_null';
825
- const newColumnSchema = columnDefToSchema(columnDefAst, defaultNotNull);
1376
+ // Honor the session `default_collation` for an ADD COLUMN that omits an explicit
1377
+ // COLLATE, matching the CREATE path (and the differ's resolved-COLLATE emission) so
1378
+ // an ADD-COLUMN-ed text column gets the same collation a CREATE-d one would.
1379
+ // resolveDefaultCollation falls non-text types back to BINARY automatically.
1380
+ const newColumnSchema = columnDefToSchema(columnDefAst, defaultNotNull, this.db.options.getStringOption('default_collation'));
826
1381
  if (this.tableSchema.columns.some(c => c.name.toLowerCase() === newColumnSchema.name.toLowerCase())) {
827
1382
  throw new QuereusError(`Duplicate column name: ${newColumnSchema.name}`, StatusCode.ERROR);
828
1383
  }
@@ -836,13 +1391,19 @@ export class MemoryTableManager {
836
1391
  defaultIsLiteral = true;
837
1392
  }
838
1393
  else {
839
- logger.warn('Add Column', this._tableName, 'Default for new col is expr; existing rows get NULL.', { columnName: newColumnSchema.name });
1394
+ // A non-literal expression default (e.g. `new.<col>`) is written as NULL
1395
+ // here; the engine backfills these rows per-row immediately after.
1396
+ logger.debugLog(`[Add Column] '${newColumnSchema.name}' default is a non-literal expression; existing rows are backfilled by the engine.`);
840
1397
  }
841
1398
  }
842
- // Check for NOT NULL constraint (could be explicit or from default behavior)
843
- // Allow NOT NULL without DEFAULT if table is empty (SQLite-compatible)
1399
+ // Check for NOT NULL constraint (could be explicit or from default behavior).
1400
+ // Allow NOT NULL without DEFAULT if the table is empty (SQLite-compatible).
1401
+ // A non-literal *expression* default (e.g. `new.<col>`) is backfilled per-row by
1402
+ // the engine right after this returns, which then enforces NOT NULL on the
1403
+ // backfilled values — so don't reject it here as "without a DEFAULT".
844
1404
  const tableHasRows = this.baseLayer.primaryTree.at(this.baseLayer.primaryTree.first()) !== undefined;
845
- if (newColumnSchema.notNull && defaultValue === null && !defaultIsLiteral && tableHasRows) {
1405
+ const hasDefaultExpr = !!(defaultConstraint && defaultConstraint.expr);
1406
+ if (newColumnSchema.notNull && defaultValue === null && !defaultIsLiteral && !hasDefaultExpr && tableHasRows) {
846
1407
  throw new QuereusError(`Cannot add NOT NULL column '${newColumnSchema.name}' to non-empty table `
847
1408
  + `'${this.schemaName}.${this._tableName}' without a DEFAULT value`, StatusCode.CONSTRAINT);
848
1409
  }
@@ -853,7 +1414,10 @@ export class MemoryTableManager {
853
1414
  columnIndexMap: buildColumnIndexMap(updatedColumnsSchema),
854
1415
  });
855
1416
  this.baseLayer.updateSchema(finalNewTableSchema);
856
- await this.baseLayer.addColumnToBase(newColumnSchema, defaultValue);
1417
+ // A non-foldable DEFAULT (e.g. `new.<col>`) backfills each existing row from
1418
+ // its own value via the engine-supplied evaluator; a literal/NULL default
1419
+ // uses the single folded `defaultValue` for every row.
1420
+ await this.baseLayer.addColumnToBase(newColumnSchema, defaultValue, backfillEvaluator);
857
1421
  this.tableSchema = finalNewTableSchema;
858
1422
  this.initializePrimaryKeyFunctions();
859
1423
  // Emit schema change event
@@ -897,11 +1461,36 @@ export class MemoryTableManager {
897
1461
  ...def, index: def.index > colIndex ? def.index - 1 : def.index
898
1462
  }));
899
1463
  const updatedPrimaryKeyNames = updatedPkDefinition.map(def => updatedColumnsSchema[def.index]?.name).filter(Boolean);
900
- const updatedIndexes = (this.tableSchema.indexes || []).map(idx => ({
1464
+ // Prune any UNIQUE constraint over the dropped column. A UNIQUE that includes the
1465
+ // dropped column is removed outright (a UNIQUE missing one of its columns is a
1466
+ // different, stronger constraint, not a silently-narrowed one); the auto-built
1467
+ // covering index it backed is torn down with it (see the index exclusion below).
1468
+ // Remaining constraints have their column indices shifted to track the removed slot.
1469
+ // Without this, dropping a uniquely-constrained column (including the ADD COLUMN +
1470
+ // inline-UNIQUE revert path) would strand a constraint whose column index dangles
1471
+ // past the end of the column array.
1472
+ const oldUniqueConstraints = this.tableSchema.uniqueConstraints ?? [];
1473
+ const droppedUcKeys = oldUniqueConstraints
1474
+ .filter(uc => uc.columns.includes(colIndex))
1475
+ .map(uc => uc.name ?? this.implicitIndexNameFor(uc));
1476
+ const remainingUniqueConstraints = oldUniqueConstraints
1477
+ .filter(uc => !uc.columns.includes(colIndex))
1478
+ .map(uc => ({ ...uc, columns: Object.freeze(uc.columns.map(i => i > colIndex ? i - 1 : i)) }));
1479
+ // Drop the implicit covering index of each removed constraint outright (matched by
1480
+ // the same `uc.name ?? '_uc_<cols>'` convention DROP CONSTRAINT uses, so a user
1481
+ // index that merely shares columns is left untouched), then shift/prune the rest
1482
+ // over the removed slot. A *single*-column covering index collapses to empty and is
1483
+ // filtered by the trailing `length > 0` regardless; the explicit name exclusion is
1484
+ // what tears down a *multi*-column covering index, which would otherwise survive
1485
+ // orphaned — narrowed to its surviving columns — in `index_info` and on every write.
1486
+ const droppedCoveringIndexNames = new Set(droppedUcKeys.map(k => k.toLowerCase()));
1487
+ const updatedIndexes = (this.tableSchema.indexes || [])
1488
+ .filter(idx => !droppedCoveringIndexNames.has(idx.name.toLowerCase()))
1489
+ .map(idx => ({
901
1490
  ...idx,
902
1491
  columns: idx.columns
903
- .map(ic => ({ ...ic, index: ic.index > colIndex ? ic.index - 1 : ic.index }))
904
1492
  .filter(ic => ic.index !== colIndex)
1493
+ .map(ic => ({ ...ic, index: ic.index > colIndex ? ic.index - 1 : ic.index }))
905
1494
  })).filter(idx => idx.columns.length > 0);
906
1495
  const finalNewTableSchema = Object.freeze({
907
1496
  ...this.tableSchema,
@@ -909,11 +1498,18 @@ export class MemoryTableManager {
909
1498
  columnIndexMap: buildColumnIndexMap(updatedColumnsSchema),
910
1499
  primaryKeyDefinition: Object.freeze(updatedPkDefinition),
911
1500
  primaryKey: Object.freeze(updatedPrimaryKeyNames),
912
- indexes: Object.freeze(updatedIndexes)
1501
+ indexes: Object.freeze(updatedIndexes),
1502
+ uniqueConstraints: remainingUniqueConstraints.length > 0
1503
+ ? Object.freeze(remainingUniqueConstraints)
1504
+ : undefined,
913
1505
  });
914
1506
  this.baseLayer.updateSchema(finalNewTableSchema);
915
1507
  await this.baseLayer.dropColumnFromBase(colIndex);
916
1508
  this.tableSchema = finalNewTableSchema;
1509
+ // The covering-structure records for the dropped constraints are now stale —
1510
+ // clear them (keys computed against the pre-drop column names above).
1511
+ for (const key of droppedUcKeys)
1512
+ this.implicitCoveringStructures.delete(key);
917
1513
  this.initializePrimaryKeyFunctions();
918
1514
  // Emit schema change event
919
1515
  this.eventEmitter?.emitSchemaChange?.({
@@ -1015,7 +1611,28 @@ export class MemoryTableManager {
1015
1611
  }
1016
1612
  const oldCol = this.tableSchema.columns[colIndex];
1017
1613
  let newCol = oldCol;
1018
- if (change.setNotNull !== undefined) {
1614
+ // A collation change re-keys any PK / UNIQUE / index that orders by this
1615
+ // column, so it needs the structure re-sort + uniqueness re-validation below.
1616
+ let collationChanged = false;
1617
+ if (change.setCollation !== undefined) {
1618
+ const normalized = validateCollationForType(change.setCollation, oldCol.logicalType, change.columnName);
1619
+ const nameMatches = normalized === (oldCol.collation || 'BINARY');
1620
+ if (nameMatches && oldCol.collationExplicit) {
1621
+ return; // already explicit in the desired collation — nothing to do
1622
+ }
1623
+ // SET COLLATE is a user declaration with the same standing as a
1624
+ // CREATE-time COLLATE clause, so mark the collation explicit (rank 2 in
1625
+ // the comparison lattice) regardless of the column's creation history —
1626
+ // including SET COLLATE binary. When only the name matches but the column
1627
+ // was not yet explicit (a defaulted collation, or one inherited from
1628
+ // session default_collation), flip the flag as a METADATA-ONLY change:
1629
+ // the collation bytes are unchanged, so keep collationChanged false and
1630
+ // skip the physical re-sort / re-key / UNIQUE re-validation below. A
1631
+ // different name takes the full path AND sets the flag.
1632
+ newCol = { ...oldCol, collation: normalized, collationExplicit: true };
1633
+ collationChanged = !nameMatches;
1634
+ }
1635
+ else if (change.setNotNull !== undefined) {
1019
1636
  if (change.setNotNull === true && !oldCol.notNull) {
1020
1637
  // Tightening: scan for NULLs. If DEFAULT present, backfill first.
1021
1638
  const defaultExpr = oldCol.defaultValue;
@@ -1092,12 +1709,36 @@ export class MemoryTableManager {
1092
1709
  throw new QuereusError('ALTER COLUMN requires an attribute to change', StatusCode.INTERNAL);
1093
1710
  }
1094
1711
  const updatedCols = this.tableSchema.columns.map((c, i) => i === colIndex ? newCol : c);
1712
+ // Propagate a collation change into every PK-definition entry and index
1713
+ // column that orders by this column, so their comparators re-key under it.
1714
+ const updatedPkDef = collationChanged
1715
+ ? this.tableSchema.primaryKeyDefinition.map(def => def.index === colIndex ? { ...def, collation: newCol.collation } : def)
1716
+ : this.tableSchema.primaryKeyDefinition;
1717
+ const updatedIndexes = (collationChanged && this.tableSchema.indexes)
1718
+ ? this.tableSchema.indexes.map(idx => ({
1719
+ ...idx,
1720
+ columns: idx.columns.map(ic => ic.index === colIndex ? { ...ic, collation: newCol.collation } : ic),
1721
+ }))
1722
+ : this.tableSchema.indexes;
1095
1723
  const finalNewTableSchema = Object.freeze({
1096
1724
  ...this.tableSchema,
1097
1725
  columns: Object.freeze(updatedCols),
1098
1726
  columnIndexMap: buildColumnIndexMap(updatedCols),
1727
+ primaryKeyDefinition: Object.freeze(updatedPkDef),
1728
+ indexes: updatedIndexes ? Object.freeze(updatedIndexes) : updatedIndexes,
1099
1729
  });
1100
1730
  this.baseLayer.updateSchema(finalNewTableSchema);
1731
+ // A collation change re-sorts structures that order by the column and
1732
+ // re-validates uniqueness under the new collation. Rebuild the secondary
1733
+ // indexes FIRST (strict — a UNIQUE collision throws CONSTRAINT) and the
1734
+ // primary tree LAST (also strict — a PK collision throws), so a throw at
1735
+ // either step leaves the live primary tree intact for the catch's rollback.
1736
+ if (collationChanged) {
1737
+ this.baseLayer.rebuildAllSecondaryIndexesStrict();
1738
+ if (updatedPkDef.some(def => def.index === colIndex)) {
1739
+ this.baseLayer.rebuildPrimaryTreeStrict();
1740
+ }
1741
+ }
1101
1742
  this.tableSchema = finalNewTableSchema;
1102
1743
  this.initializePrimaryKeyFunctions();
1103
1744
  this.eventEmitter?.emitSchemaChange?.({
@@ -1110,7 +1751,12 @@ export class MemoryTableManager {
1110
1751
  logger.operation('Alter Column', this._tableName, { columnName: change.columnName });
1111
1752
  }
1112
1753
  catch (e) {
1754
+ // Restore the prior schema, then rebuild secondary indexes (non-strict) so
1755
+ // a partially-cleared strict rebuild can't strand the index map in an
1756
+ // inconsistent state. The primary tree is only swapped on full success, so
1757
+ // it is already the original here.
1113
1758
  this.baseLayer.updateSchema(originalManagerSchema);
1759
+ this.baseLayer.rebuildAllSecondaryIndexes();
1114
1760
  this.tableSchema = originalManagerSchema;
1115
1761
  this.initializePrimaryKeyFunctions();
1116
1762
  logger.error('Alter Column', this._tableName, e);
@@ -1232,6 +1878,315 @@ export class MemoryTableManager {
1232
1878
  release();
1233
1879
  }
1234
1880
  }
1881
+ /**
1882
+ * Drops a named table-level constraint (CHECK / UNIQUE / FOREIGN KEY). Schema-
1883
+ * only — constraints don't change row shape — except that dropping a UNIQUE
1884
+ * also tears down the implicit covering index (the auto-built secondary BTree
1885
+ * named `uc.name ?? '_uc_<cols>'`) so introspection / the declarative differ
1886
+ * don't observe an orphaned index. The class is resolved here (NOTFOUND /
1887
+ * ambiguous), so the engine can route through `module.alterTable` uniformly.
1888
+ */
1889
+ async dropConstraint(constraintName) {
1890
+ if (this.isReadOnly)
1891
+ throw new QuereusError(`Table '${this._tableName}' is read-only`, StatusCode.READONLY);
1892
+ const lockKey = `MemoryTable.SchemaChange:${this.schemaName}.${this._tableName}`;
1893
+ const release = await Latches.acquire(lockKey);
1894
+ const originalManagerSchema = this.tableSchema;
1895
+ try {
1896
+ await this.ensureSchemaChangeSafety();
1897
+ const cls = resolveNamedConstraintClass(this.tableSchema, constraintName);
1898
+ const lower = constraintName.toLowerCase();
1899
+ let newSchema;
1900
+ let droppedIndexName;
1901
+ if (cls === 'check') {
1902
+ newSchema = Object.freeze({
1903
+ ...this.tableSchema,
1904
+ checkConstraints: Object.freeze(this.tableSchema.checkConstraints.filter(c => c.name?.toLowerCase() !== lower)),
1905
+ });
1906
+ }
1907
+ else if (cls === 'foreignKey') {
1908
+ const remaining = (this.tableSchema.foreignKeys ?? []).filter(c => c.name?.toLowerCase() !== lower);
1909
+ newSchema = Object.freeze({
1910
+ ...this.tableSchema,
1911
+ foreignKeys: remaining.length > 0 ? Object.freeze(remaining) : undefined,
1912
+ });
1913
+ }
1914
+ else {
1915
+ // UNIQUE — drop the constraint and its implicit covering index.
1916
+ const uc = this.tableSchema.uniqueConstraints.find(c => c.name?.toLowerCase() === lower);
1917
+ const idxName = uc.name ?? this.implicitIndexNameFor(uc);
1918
+ const idxLower = idxName.toLowerCase();
1919
+ const existingIndexes = this.tableSchema.indexes ?? [];
1920
+ const keptIndexes = existingIndexes.filter(i => i.name.toLowerCase() !== idxLower);
1921
+ if (keptIndexes.length !== existingIndexes.length)
1922
+ droppedIndexName = idxName;
1923
+ const remainingUcs = this.tableSchema.uniqueConstraints.filter(c => c.name?.toLowerCase() !== lower);
1924
+ newSchema = Object.freeze({
1925
+ ...this.tableSchema,
1926
+ uniqueConstraints: remainingUcs.length > 0 ? Object.freeze(remainingUcs) : undefined,
1927
+ indexes: Object.freeze(keptIndexes),
1928
+ });
1929
+ this.implicitCoveringStructures.delete(uc.name ?? idxName);
1930
+ }
1931
+ this.baseLayer.updateSchema(newSchema);
1932
+ if (droppedIndexName)
1933
+ await this.baseLayer.dropIndexFromBase(droppedIndexName);
1934
+ this.tableSchema = newSchema;
1935
+ this.initializePrimaryKeyFunctions();
1936
+ this.eventEmitter?.emitSchemaChange?.({
1937
+ type: 'alter',
1938
+ objectType: 'table',
1939
+ schemaName: this.schemaName,
1940
+ objectName: this._tableName,
1941
+ });
1942
+ logger.operation('Drop Constraint', this._tableName, { constraintName });
1943
+ }
1944
+ catch (e) {
1945
+ this.baseLayer.updateSchema(originalManagerSchema);
1946
+ this.tableSchema = originalManagerSchema;
1947
+ this.initializePrimaryKeyFunctions();
1948
+ logger.error('Drop Constraint', this._tableName, e);
1949
+ throw e;
1950
+ }
1951
+ finally {
1952
+ release();
1953
+ }
1954
+ }
1955
+ /**
1956
+ * Renames a named table-level constraint. Schema-only, with one caveat: a
1957
+ * UNIQUE whose implicit covering index is named after the constraint has that
1958
+ * index renamed in lock-step (so the index stays recognized as the
1959
+ * constraint's covering structure rather than surfacing as an orphan).
1960
+ */
1961
+ async renameConstraint(oldName, newName) {
1962
+ if (this.isReadOnly)
1963
+ throw new QuereusError(`Table '${this._tableName}' is read-only`, StatusCode.READONLY);
1964
+ const lockKey = `MemoryTable.SchemaChange:${this.schemaName}.${this._tableName}`;
1965
+ const release = await Latches.acquire(lockKey);
1966
+ const originalManagerSchema = this.tableSchema;
1967
+ try {
1968
+ await this.ensureSchemaChangeSafety();
1969
+ const cls = resolveNamedConstraintClass(this.tableSchema, oldName);
1970
+ const oldLower = oldName.toLowerCase();
1971
+ let newSchema;
1972
+ let renamedIndex = false;
1973
+ if (cls === 'check') {
1974
+ newSchema = Object.freeze({
1975
+ ...this.tableSchema,
1976
+ checkConstraints: Object.freeze(this.tableSchema.checkConstraints.map(c => (c.name?.toLowerCase() === oldLower ? { ...c, name: newName } : c))),
1977
+ });
1978
+ }
1979
+ else if (cls === 'foreignKey') {
1980
+ newSchema = Object.freeze({
1981
+ ...this.tableSchema,
1982
+ foreignKeys: Object.freeze(this.tableSchema.foreignKeys.map(c => (c.name?.toLowerCase() === oldLower ? { ...c, name: newName } : c))),
1983
+ });
1984
+ }
1985
+ else {
1986
+ // UNIQUE — rename the constraint and, when present, its implicit covering index.
1987
+ const uc = this.tableSchema.uniqueConstraints.find(c => c.name?.toLowerCase() === oldLower);
1988
+ const oldIdxName = uc.name ?? this.implicitIndexNameFor(uc);
1989
+ const oldIdxLower = oldIdxName.toLowerCase();
1990
+ const newUcs = this.tableSchema.uniqueConstraints.map(c => (c.name?.toLowerCase() === oldLower ? { ...c, name: newName } : c));
1991
+ let indexes = this.tableSchema.indexes ?? [];
1992
+ if (indexes.some(i => i.name.toLowerCase() === oldIdxLower)) {
1993
+ indexes = indexes.map(i => (i.name.toLowerCase() === oldIdxLower ? { ...i, name: newName } : i));
1994
+ renamedIndex = true;
1995
+ }
1996
+ newSchema = Object.freeze({
1997
+ ...this.tableSchema,
1998
+ uniqueConstraints: Object.freeze(newUcs),
1999
+ indexes: Object.freeze(indexes),
2000
+ });
2001
+ const rec = this.implicitCoveringStructures.get(uc.name ?? oldIdxName);
2002
+ if (rec) {
2003
+ this.implicitCoveringStructures.delete(uc.name ?? oldIdxName);
2004
+ this.implicitCoveringStructures.set(newName, { ...rec, indexName: renamedIndex ? newName : rec.indexName });
2005
+ }
2006
+ }
2007
+ this.baseLayer.updateSchema(newSchema);
2008
+ // A renamed covering index lives under a new key — rebuild secondary indexes
2009
+ // from the post-rename schema so the base layer's index map matches.
2010
+ if (renamedIndex)
2011
+ this.baseLayer.rebuildAllSecondaryIndexes();
2012
+ this.tableSchema = newSchema;
2013
+ this.initializePrimaryKeyFunctions();
2014
+ this.eventEmitter?.emitSchemaChange?.({
2015
+ type: 'alter',
2016
+ objectType: 'table',
2017
+ schemaName: this.schemaName,
2018
+ objectName: this._tableName,
2019
+ });
2020
+ logger.operation('Rename Constraint', this._tableName, { oldName, newName });
2021
+ }
2022
+ catch (e) {
2023
+ this.baseLayer.updateSchema(originalManagerSchema);
2024
+ this.tableSchema = originalManagerSchema;
2025
+ this.initializePrimaryKeyFunctions();
2026
+ logger.error('Rename Constraint', this._tableName, e);
2027
+ throw e;
2028
+ }
2029
+ finally {
2030
+ release();
2031
+ }
2032
+ }
2033
+ /**
2034
+ * Adds a table-level UNIQUE or FOREIGN KEY constraint to an existing table,
2035
+ * re-validating the current rows against it and failing atomically with
2036
+ * `CONSTRAINT` (no schema mutation) when the data violates it. Mirrors the
2037
+ * latch + `ensureSchemaChangeSafety()` + snapshot/restore scaffolding of
2038
+ * {@link createIndex} / {@link dropConstraint}.
2039
+ *
2040
+ * - UNIQUE builds (or reuses) the implicit covering secondary index; the build
2041
+ * raises `CONSTRAINT` on the first duplicate among in-scope rows (partial
2042
+ * predicate + per-column collation honored, NULLs distinct).
2043
+ * - FOREIGN KEY appends the constraint and runs the pragma-gated existing-row
2044
+ * validation (engine-side enforcement needs no physical structure).
2045
+ * - CHECK appends the constraint (no physical structure, no existing-row scan —
2046
+ * matching the engine's prior in-emitter behavior); it routes here, rather than
2047
+ * being applied catalog-only, so the module-cached schema stays in lock-step
2048
+ * with the catalog and a later `DROP/RENAME CONSTRAINT` resolves it. (The engine
2049
+ * keeps an engine-side fallback in `runtime/emit/add-constraint.ts` only for
2050
+ * modules that omit `alterTable` — which cannot DROP/RENAME a constraint anyway.)
2051
+ */
2052
+ async addConstraint(constraint) {
2053
+ if (this.isReadOnly)
2054
+ throw new QuereusError(`Table '${this._tableName}' is read-only`, StatusCode.READONLY);
2055
+ const lockKey = `MemoryTable.SchemaChange:${this.schemaName}.${this._tableName}`;
2056
+ const release = await Latches.acquire(lockKey);
2057
+ const originalManagerSchema = this.tableSchema;
2058
+ try {
2059
+ await this.ensureSchemaChangeSafety();
2060
+ if (constraint.type === 'unique') {
2061
+ await this.addUniqueConstraint(constraint);
2062
+ }
2063
+ else if (constraint.type === 'foreignKey') {
2064
+ await this.addForeignKeyConstraint(constraint);
2065
+ }
2066
+ else if (constraint.type === 'check') {
2067
+ this.addCheckConstraint(constraint);
2068
+ }
2069
+ else {
2070
+ throw new QuereusError(`MemoryTable ADD CONSTRAINT does not support constraint type '${constraint.type}'`, StatusCode.UNSUPPORTED);
2071
+ }
2072
+ this.eventEmitter?.emitSchemaChange?.({
2073
+ type: 'alter',
2074
+ objectType: 'table',
2075
+ schemaName: this.schemaName,
2076
+ objectName: this._tableName,
2077
+ });
2078
+ logger.operation('Add Constraint', this._tableName, { type: constraint.type, name: constraint.name });
2079
+ }
2080
+ catch (e) {
2081
+ // Restore the prior schema and rebuild secondary indexes (non-strict) so a
2082
+ // half-built covering index can't strand the base layer's index map.
2083
+ this.baseLayer.updateSchema(originalManagerSchema);
2084
+ this.baseLayer.rebuildAllSecondaryIndexes();
2085
+ this.tableSchema = originalManagerSchema;
2086
+ this.initializePrimaryKeyFunctions();
2087
+ logger.error('Add Constraint', this._tableName, e);
2088
+ throw e;
2089
+ }
2090
+ finally {
2091
+ release();
2092
+ }
2093
+ }
2094
+ /**
2095
+ * CHECK arm of {@link addConstraint}. Schema-only: a CHECK has no covering
2096
+ * structure and (matching the engine's prior in-emitter behavior) no existing-row
2097
+ * validation, so this just appends the constraint to the cached schema. Enforcement
2098
+ * is engine-side at INSERT/UPDATE plan time. Runs under the same latch / rollback
2099
+ * scaffolding as the other arms (via {@link addConstraint}).
2100
+ */
2101
+ addCheckConstraint(constraint) {
2102
+ const check = buildCheckConstraintSchema(constraint, this.tableSchema.checkConstraints.length);
2103
+ const newSchema = Object.freeze({
2104
+ ...this.tableSchema,
2105
+ checkConstraints: Object.freeze([...this.tableSchema.checkConstraints, check]),
2106
+ });
2107
+ this.baseLayer.updateSchema(newSchema);
2108
+ this.tableSchema = newSchema;
2109
+ }
2110
+ /**
2111
+ * UNIQUE arm of {@link addConstraint}. Builds the covering secondary index the
2112
+ * same way {@link ensureUniqueConstraintIndexes} does (validating existing rows
2113
+ * via `addIndexToBase` → `populateNewIndex`), unless an existing *unique* index
2114
+ * already covers the exact columns — in which case the data is already
2115
+ * validated and we only register the covering structure.
2116
+ */
2117
+ async addUniqueConstraint(constraint) {
2118
+ const uc = buildUniqueConstraintSchema(constraint, this.tableSchema.columnIndexMap);
2119
+ const columns = this.tableSchema.columns;
2120
+ const existingIndexes = this.tableSchema.indexes ?? [];
2121
+ const appendedUcs = Object.freeze([...(this.tableSchema.uniqueConstraints ?? []), uc]);
2122
+ // Reuse: an existing UNIQUE index over the exact columns already guarantees
2123
+ // uniqueness, so skip the rebuild. A non-unique index gives no such guarantee
2124
+ // — fall through to build-and-validate. The reused index must ALSO be
2125
+ // collation-equivalent to the declared column collations: a finer/coarser
2126
+ // same-column-set index (e.g. a BINARY `create unique index` over a NOCASE
2127
+ // column) enforces under ITS collation, not the declared one, so reusing it
2128
+ // would under-enforce this non-derived UNIQUE. A collation mismatch falls
2129
+ // through to build the distinct `_uc_*` covering index; the user index keeps
2130
+ // enforcing its own (stricter) uniqueness independently (matches SQLite).
2131
+ const matchingUniqueIndex = existingIndexes.find(idx => idx.unique &&
2132
+ idx.columns.length === uc.columns.length &&
2133
+ idx.columns.every((col, i) => col.index === uc.columns[i]) &&
2134
+ this.indexCollationsMatchDeclared(idx, uc));
2135
+ if (matchingUniqueIndex) {
2136
+ const newSchema = Object.freeze({
2137
+ ...this.tableSchema,
2138
+ uniqueConstraints: appendedUcs,
2139
+ });
2140
+ this.baseLayer.updateSchema(newSchema);
2141
+ this.tableSchema = newSchema;
2142
+ this.initializePrimaryKeyFunctions();
2143
+ this.implicitCoveringStructures.set(uc.name ?? matchingUniqueIndex.name, { indexName: matchingUniqueIndex.name, origin: 'implicit-from-unique-constraint' });
2144
+ return;
2145
+ }
2146
+ const colNames = uc.columns.map(i => columns[i]?.name ?? String(i));
2147
+ const indexName = uc.name ?? `_uc_${colNames.join('_')}`;
2148
+ const indexSchema = {
2149
+ name: indexName,
2150
+ // Carry per-column collation so enforcement honors e.g. NOCASE (mirrors
2151
+ // ensureUniqueConstraintIndexes). The covering index is NOT flagged unique
2152
+ // — insert-time enforcement routes through `uniqueConstraints`.
2153
+ columns: uc.columns.map(colIdx => ({ index: colIdx, collation: columns[colIdx]?.collation })),
2154
+ predicate: uc.predicate,
2155
+ };
2156
+ const newSchema = Object.freeze({
2157
+ ...this.tableSchema,
2158
+ uniqueConstraints: appendedUcs,
2159
+ indexes: Object.freeze([...existingIndexes, indexSchema]),
2160
+ });
2161
+ // Swap the schema FIRST so `addIndexToBase` → `indexEnforcesUnique` sees the
2162
+ // new constraint and rejects duplicates, then populate (throws CONSTRAINT on
2163
+ // the first in-scope duplicate). A throw rolls back via the catch in addConstraint.
2164
+ this.baseLayer.updateSchema(newSchema);
2165
+ await this.baseLayer.addIndexToBase(indexSchema);
2166
+ this.tableSchema = newSchema;
2167
+ this.initializePrimaryKeyFunctions();
2168
+ this.implicitCoveringStructures.set(uc.name ?? indexName, { indexName, origin: 'implicit-from-unique-constraint' });
2169
+ }
2170
+ /**
2171
+ * FOREIGN KEY arm of {@link addConstraint}. Validates existing child rows
2172
+ * against the new FK (pragma-gated; throws CONSTRAINT on an orphan), then
2173
+ * appends it to the cached schema. No physical structure — FK enforcement is
2174
+ * engine-side (synthesized EXISTS checks at plan time).
2175
+ */
2176
+ async addForeignKeyConstraint(constraint) {
2177
+ const fk = buildForeignKeyConstraintSchema(constraint, this.tableSchema.columnIndexMap, this._tableName, this.schemaName);
2178
+ const newSchema = Object.freeze({
2179
+ ...this.tableSchema,
2180
+ foreignKeys: Object.freeze([...(this.tableSchema.foreignKeys ?? []), fk]),
2181
+ });
2182
+ // Validate BEFORE swapping the cached schema — a throw leaves the table
2183
+ // unmodified. The scan only reads (no schema-change latch), so holding our
2184
+ // own latch here is safe; ensureSchemaChangeSafety already drained to base.
2185
+ await validateForeignKeyOverExistingRows(this.db, newSchema, fk);
2186
+ this.baseLayer.updateSchema(newSchema);
2187
+ this.tableSchema = newSchema;
2188
+ this.initializePrimaryKeyFunctions();
2189
+ }
1235
2190
  async destroy() {
1236
2191
  const lockKey = `MemoryTable.Destroy:${this.schemaName}.${this._tableName}`;
1237
2192
  const release = await Latches.acquire(lockKey);
@@ -1267,8 +2222,41 @@ export class MemoryTableManager {
1267
2222
  connection.readLayer = this.baseLayer;
1268
2223
  }
1269
2224
  }
2225
+ // The manager's `connections` map covers only connections still attached to this
2226
+ // manager. A connection can be DETACHED from the map (removed by disconnect after an
2227
+ // autocommit collapse) while remaining REGISTERED in the Database connection registry —
2228
+ // `MemoryTable.ensureConnection` reuses exactly such a connection for a later scan. The
2229
+ // loop above misses it, so after an in-transaction schema change (e.g. ALTER TABLE ADD
2230
+ // COLUMN, now permitted inside an explicit transaction) it keeps reading a stale
2231
+ // pre-change layer carrying the OLD column shape — the materialized-view-source-stale-read
2232
+ // bug. A detached connection always has `pendingTransactionLayer === null` (disconnect
2233
+ // defers while a pending layer is uncommitted), so this never discards in-flight writes.
2234
+ this.repointRegisteredConnections();
1270
2235
  logger.debugLog(`Schema change safety check passed for ${this._tableName}. Current committed layer is base.`);
1271
2236
  }
2237
+ /**
2238
+ * Re-point every Database-registered {@link MemoryTableConnection} backed by this
2239
+ * manager (including ones detached from {@link connections}) at the current base layer,
2240
+ * when it carries no uncommitted pending layer. Companion to the `connections`-map sweep
2241
+ * in {@link ensureSchemaChangeSafety}: it closes the gap for a connection that lives in
2242
+ * the Database registry but not in the manager's map.
2243
+ */
2244
+ repointRegisteredConnections() {
2245
+ const qualifiedName = `${this.schemaName}.${this._tableName}`;
2246
+ for (const c of this.db.getConnectionsForTable(qualifiedName)) {
2247
+ if (!(c instanceof MemoryVirtualTableConnection))
2248
+ continue;
2249
+ const mc = c.getMemoryConnection();
2250
+ if (mc.tableManager !== this)
2251
+ continue;
2252
+ if (mc.pendingTransactionLayer !== null)
2253
+ continue;
2254
+ if (mc.readLayer === this.baseLayer)
2255
+ continue;
2256
+ logger.debugLog(`[Schema Safety] Re-pointing registered connection ${mc.connectionId} to base layer`);
2257
+ mc.readLayer = this.baseLayer;
2258
+ }
2259
+ }
1272
2260
  /** Consolidates all transaction data into the base layer for schema changes */
1273
2261
  async consolidateToBaseLayer() {
1274
2262
  const lockKey = `MemoryTable.Consolidate:${this.schemaName}.${this._tableName}`;
@@ -1310,45 +2298,16 @@ export class MemoryTableManager {
1310
2298
  allRows.push(primaryTree.at(path));
1311
2299
  }
1312
2300
  logger.debugLog(`[Consolidate] Collected ${allRows.length} rows from transaction layer. Row widths: ${allRows.map(r => r.length).join(',')}`);
1313
- // Count base layer rows before
1314
- let baseCount = 0;
1315
- for (const _path of this.baseLayer.primaryTree.ascending(this.baseLayer.primaryTree.first())) {
1316
- baseCount++;
1317
- }
1318
- logger.debugLog(`[Consolidate] Base layer had ${baseCount} rows before copy`);
1319
- // Now insert collected rows into the base layer
1320
- for (const row of allRows) {
1321
- this.baseLayer.primaryTree.insert(row);
1322
- }
1323
- // Count base layer rows after
1324
- let baseCountAfter = 0;
1325
- for (const _path of this.baseLayer.primaryTree.ascending(this.baseLayer.primaryTree.first())) {
1326
- baseCountAfter++;
1327
- }
1328
- logger.debugLog(`[Consolidate] Base layer has ${baseCountAfter} rows after copy`);
1329
- // Also need to rebuild secondary indexes in the base layer
1330
- await this.baseLayer.rebuildAllSecondaryIndexes();
2301
+ // Replace (do not union into) the base primary tree: `allRows` is the layer's
2302
+ // merged view with deletes already applied, so any row deleted in the
2303
+ // transaction layer must be physically removed from the base — otherwise a
2304
+ // later base-direct scan (e.g. a UNIQUE index build) resurrects it. This also
2305
+ // rebuilds the base secondary indexes from the new tree.
2306
+ this.baseLayer.rebuildPrimaryTreeFromRows(allRows);
1331
2307
  }
1332
2308
  /** Scans a layer according to the given plan, yielding matching rows. */
1333
2309
  async *scanLayer(layer, plan) {
1334
2310
  yield* scanLayerImpl(layer, plan);
1335
2311
  }
1336
2312
  }
1337
- /**
1338
- * Resolves the per-constraint default conflict action for PK conflicts.
1339
- * Prefers the table-level `PRIMARY KEY (...) ON CONFLICT <action>` clause
1340
- * (the constraint's own declaration) over any column-level `defaultConflict`
1341
- * declared on a PK column (which primarily targets that column's own
1342
- * constraints and only acts as a fallback for PK conflicts).
1343
- */
1344
- function resolvePkDefaultConflict(schema) {
1345
- if (schema.primaryKeyDefaultConflict !== undefined)
1346
- return schema.primaryKeyDefaultConflict;
1347
- for (const def of schema.primaryKeyDefinition) {
1348
- const col = schema.columns[def.index];
1349
- if (col && col.defaultConflict !== undefined)
1350
- return col.defaultConflict;
1351
- }
1352
- return undefined;
1353
- }
1354
2313
  //# sourceMappingURL=manager.js.map