@quereus/quereus 3.3.0 → 4.1.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 +204 -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 +716 -27
  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 +13 -0
  80. package/dist/src/func/registration.d.ts.map +1 -1
  81. package/dist/src/func/registration.js +5 -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 +20 -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 +17 -16
@@ -0,0 +1,1687 @@
1
+ import { isRelationalNode } from '../nodes/plan-node.js';
2
+ import { checkSatisfiability } from '../analysis/sat-checker.js';
3
+ import { splitConjuncts } from '../analysis/predicate-conjuncts.js';
4
+ import { SetOperationNode } from '../nodes/set-operation-node.js';
5
+ import { buildSelectStmt } from '../building/select.js';
6
+ import { buildExpression } from '../building/expression.js';
7
+ import { FilterNode } from '../nodes/filter.js';
8
+ import { ProjectNode } from '../nodes/project-node.js';
9
+ import { RegisteredScope } from '../scopes/registered.js';
10
+ import { ColumnReferenceNode } from '../nodes/reference.js';
11
+ import { raiseMutationDiagnostic } from './mutation-diagnostic.js';
12
+ import { propagate } from './propagate.js';
13
+ import { MS_UPDATE_KEYS_CTE, isJoinBody, isInnerJoinBody, analyzeJoinView, analyzeMultiSourceInsert, decomposeUpdate, decomposeDelete, buildMultiSourceKeyCapture, capturedSideIndices, withKeyCapture } from './multi-source.js';
14
+ import { cloneExpr, transformExpr } from './scope-transform.js';
15
+ import { unwrapPassthroughSubquery } from '../util/set-op-wrapper.js';
16
+ /**
17
+ * Set-operation membership-column write decomposition — the **first set-op view
18
+ * writability** in the engine (docs/view-updateability.md § Set Operations).
19
+ *
20
+ * The read half (`set-op-membership-read`) reifies a binary set operation's branch
21
+ * provenance as first-class `existence`-sited boolean columns and, per row, computes
22
+ * a runtime membership probe (`inA ≡ tuple ∈ A`). This module delivers the write
23
+ * payoff: a membership column **is** the branch presence, and *writing* it drives the
24
+ * branch's existence:
25
+ *
26
+ * ```sql
27
+ * -- U = select id, x from A union exists left as inA, exists right as inB select id, x from B
28
+ * update U set inB = true where id = 3 -- a row in A only ⇒ INSERT it into B
29
+ * update U set inB = false where id = 3 -- a row in B ⇒ DELETE the matching B row
30
+ * ```
31
+ *
32
+ * **The substrate (Halloween-safe via an up-front capture).** Every affected view
33
+ * row — its data columns AND its membership-probe flags — is captured ONCE
34
+ * (`buildSetOpCapture`), *before* any branch op fires, into the same context-backed
35
+ * key relation the multi-source path uses ({@link MS_UPDATE_KEYS_CTE}). The per-branch
36
+ * ops then read that immutable capture rather than re-scanning the view (which reads
37
+ * the very branches being written), so a branch insert/delete can never perturb the
38
+ * affected set out from under a sibling branch op. The capture rides the existing
39
+ * `ViewMutationNode.identityCapture` side input + the void/drain runtime path — no new
40
+ * runtime substrate (see `runtime/emit/view-mutation.ts`).
41
+ *
42
+ * **A branch is itself a view body — routed per-branch via recursive `propagate`.**
43
+ * Each operand of the set operation is a relational sub-plan (`select … from B`). A
44
+ * **single-source** branch's per-branch op is lowered to an AST `BaseOp` against a
45
+ * **synthetic branch view-like** and run back through {@link propagate} — reusing the
46
+ * single-source spine verbatim (the branch's own σ predicate, renames, and base routing are
47
+ * honored by its spine).
48
+ *
49
+ * A **multi-source (INNER join) branch/leg** ({@link isInnerJoinBody}) is **composed** rather
50
+ * than routed through plain {@link propagate} (`set-op-write-multisource-leg-compose`): plain
51
+ * `propagate` builds NO capture, so its emitted multi-source predicates (`k<side>_<j>`) would
52
+ * bind to the outer set-op capture (view-output columns) — the internal `k.k0_0 isn't a column`
53
+ * error. Instead the fan builds the join branch itself (mirroring `buildViewMutation`'s
54
+ * orchestration): {@link analyzeJoinView} + {@link decomposeUpdate}/{@link decomposeDelete} +
55
+ * {@link buildMultiSourceKeyCapture} build an **inner per-branch base-PK capture** under a fresh
56
+ * `__vmupd_keys$N` name (minted monotonically — two join branches never collide), filtered by the
57
+ * SAME {@link buildMemberExists} predicate (so it scans the OUTER set-op capture), and bubble it
58
+ * up on {@link SetOpWritePlan.nestedCaptures} (materialized outer-first then inner, via
59
+ * `ViewMutationNode.nestedCaptures`). INSERT into a join branch is deferred to
60
+ * `set-op-write-multisource-leg-insert` (needs the plan-level shared-surrogate envelope) — a clean
61
+ * reject + `is_insertable_into = NO`; an OUTER (left/right/full) / cross / non-equi join leg is
62
+ * likewise deferred (a clean classification reject). A branch that bottoms out in a base table emits one base op; a branch
63
+ * that is itself a `SetOperationNode` (a **subtree operand**, `nestable-flagged-set-ops`)
64
+ * recurses *here* for the unambiguous fan-out — a data-column UPDATE, a DELETE, and a
65
+ * `set <subtreeFlag> = false` drop fan out to every member leaf, sharing the ONE up-front
66
+ * capture (`fanBranchDataUpdate` / `fanBranchDelete`, detected via {@link analyzeSetOpBranches}).
67
+ * A **union / union all** subtree fans freely (a leaf ⊆ the subtree). An **`except` /
68
+ * `intersect`** subtree fan is **membership-gated** (`set-op-membership-nested-except`): a leaf
69
+ * can hold rows the subtree excludes, so the recursion AND-s the captured subtree-membership
70
+ * boundary flag (`exists <branch> as <flag>`) into each leaf's member-exists, restricting the
71
+ * fan to genuine members — one conjunct per non-union boundary descended. A flag-less non-union
72
+ * boundary has no boundary probe to gate on and stays deferred (rejected).
73
+ * The genuinely ambiguous inserts into a subtree — `set <subtreeFlag> = true`, a
74
+ * surfaced-inner-flag write, and insert-through routing into a subtree side — have no single
75
+ * deterministic target leaf (product-coordinate addressing) and are rejected, pointing at
76
+ * `set-op-membership-nested`.
77
+ *
78
+ * **A LEFT operand can be a subtree too** (`set-op-leftwrap-write`). A parenthesized LEFT
79
+ * compound operand — `(A∪B) union[…] (C∪D)`, a *parallel-sibling* shape — is lifted by the parser
80
+ * into a `select * from (A∪B) as values_N` passthrough wrapper so the SELECT-level `compound` slot
81
+ * can host the outer operator. {@link buildBranch} **unwraps** that wrapper (via
82
+ * {@link unwrapBranchSelect}, the same {@link unwrapPassthroughSubquery} predicate the read/plan
83
+ * path uses) so the wrapped left operand is a first-class subtree operand — its data cols,
84
+ * `isNested`, and fan-out recursion all derive from the inner compound, exactly as the
85
+ * (always-direct) right compound operand. So the unambiguous fan-out (data UPDATE / DELETE /
86
+ * `set <subtreeFlag> = false`) reaches the LEFT subtree's leaves, while the ambiguous inserts
87
+ * into it stay deferred (`set-op-membership-nested`). The static surfaces walk both operands too.
88
+ *
89
+ * **Per-branch correlation.** A fan-out / membership-delete op identifies the branch's
90
+ * affected rows by a correlated `exists (select 1 from __vmupd_keys k where <k matches
91
+ * the branch row's data tuple>)` — a NULL-safe full-data-tuple match (set operations
92
+ * treat `NULL = NULL` as equal, and the engine has no `IS NOT DISTINCT FROM`, so each
93
+ * column is matched `k.c = b.c or (k.c is null and b.c is null)`). The branch's own
94
+ * columns are qualified with the synthetic branch-view name so {@link propagate}'s
95
+ * single-source correlation (the `__vm_self` self-alias) binds them to the lowered base
96
+ * row; the `k.*` capture columns stay unqualified-to-the-branch and resolve to the
97
+ * injected `__vmupd_keys` relation. The membership-INSERT branch instead reads the
98
+ * captured rows **absent** from the branch via the probe flag (`where not k.<flag>`),
99
+ * so a `set <flag> = true` is a clean no-op for rows already present (and across
100
+ * operators — `except`'s always-false right flag inserts every visible row, `intersect`'s
101
+ * always-true flags insert none).
102
+ *
103
+ * **Scope.** union / union all / except / intersect membership writes, data-column fan-out,
104
+ * delete fan-out, and insert-through, at any nesting depth for the unambiguous fan-out
105
+ * operations (data UPDATE / DELETE / `set <subtreeFlag> = false`). The ambiguous inserts
106
+ * into a subtree (`set <subtreeFlag> = true`, surfaced-inner-flag writes, insert-through into
107
+ * a subtree side) are `set-op-membership-nested` (product-coordinate addressing); non-literal
108
+ * boolean membership writes and the `strict` unspecified-case policy stay deferred.
109
+ */
110
+ /**
111
+ * True iff `selectAst` is a **binary set-operation body carrying ≥1 membership flag**
112
+ * (`<setop> exists <branch> as <name>`) — the shape this write path decomposes. An
113
+ * AST peek (no plan built), the write-side shadow of the read half's combinator. A
114
+ * plain (flag-less) set-op body returns `false` and keeps rejecting `unsupported-set-op`
115
+ * through the single-source spine (there is no membership column to address a branch by).
116
+ * `diff` is excluded (the parser already rejects membership on it).
117
+ */
118
+ export function isSetOpMembershipBody(selectAst) {
119
+ return selectAst.type === 'select'
120
+ && !!selectAst.compound
121
+ && selectAst.compound.op !== 'diff'
122
+ && !!selectAst.compound.existence
123
+ && selectAst.compound.existence.length > 0;
124
+ }
125
+ /**
126
+ * True iff a set-op membership body is reportable-writable by the static surfaces — the
127
+ * no-plan shadow of {@link analyzeSetOpView}'s pre-write rejections: an outer LIMIT/OFFSET
128
+ * (the body is not decomposable — a write would escape the limited window), a non-SELECT
129
+ * right operand, a `select *` leg, or a computed (non-plain-column) leg. Lets the
130
+ * `column_info` / `view_info` static surfaces gate the membership-writable claim on the SAME
131
+ * shape the dynamic write enforces, instead of reporting writable from the membership flag's
132
+ * presence alone.
133
+ *
134
+ * **Recursive** (`nestable-flagged-set-ops`): an operand is branch-writable iff it is a
135
+ * plain-leg leaf OR a (recursively) branch-writable set-op body — so a nested view reports
136
+ * `is_updatable` / `is_deletable` = YES, agreeing with the dynamic accept (data + delete
137
+ * fan-out recurse through a subtree operand). Inserts into a subtree are deferred, so
138
+ * insertability is gated separately on {@link setOpHasSubtreeOperand}.
139
+ */
140
+ export function isSetOpBranchWritable(selectAst) {
141
+ if (selectAst.type !== 'select' || !selectAst.compound)
142
+ return false;
143
+ return isSetOpBodyWritable(selectAst);
144
+ }
145
+ /**
146
+ * True iff a set-op body (its compound + both operands) is recursively branch-writable.
147
+ * Mirrors the dynamic write's pre-write rejections at this level — an outer LIMIT/OFFSET
148
+ * (a write would escape the window), then each operand checked via {@link isOperandWritable}.
149
+ *
150
+ * Threads each operand's **boundary-flag presence** (`exists <branch> as <flag>` declared on
151
+ * THIS compound for that side) into {@link isOperandWritable}: an `except` / `intersect` subtree
152
+ * operand is writable only when its side carries a boundary flag to gate the fan on, mirroring
153
+ * the dynamic `gateFlagForNonUnionSubtree` requirement.
154
+ */
155
+ function isSetOpBodyWritable(selectAst) {
156
+ if (!selectAst.compound)
157
+ return false;
158
+ if (selectAst.limit || selectAst.offset)
159
+ return false;
160
+ const ex = selectAst.compound.existence ?? [];
161
+ const leftFlag = ex.some(e => e.branch === 'left');
162
+ const rightFlag = ex.some(e => e.branch === 'right');
163
+ return isOperandWritable(leftBranchSelect(selectAst), leftFlag)
164
+ && isOperandWritable(selectAst.compound.select, rightFlag);
165
+ }
166
+ /**
167
+ * True iff an operand (a compound leg) is recursively branch-writable: a (recursively)
168
+ * writable set-op body, OR a plain-column leaf (the shape that round-trips a base column
169
+ * through the branch's single-source spine). A non-SELECT operand, a `select *` leg, or a
170
+ * computed leg is non-writable — the dynamic write rejects all three.
171
+ *
172
+ * `hasGatingFlag` is whether the parent compound declared a boundary membership flag for this
173
+ * operand's side. A **union / union all** subtree ignores it (a leaf ⊆ the subtree, so the
174
+ * leaf-presence correlation already implies membership — no gate needed). An **`except` /
175
+ * `intersect`** subtree is writable IFF `hasGatingFlag` (the captured boundary flag gates the
176
+ * fan to genuine members — `set-op-membership-nested-except`); a flag-less non-union boundary
177
+ * stays deferred, so this returns `false`, agreeing with the dynamic reject. Leaf operands
178
+ * ignore `hasGatingFlag`.
179
+ */
180
+ function isOperandWritable(operand, hasGatingFlag) {
181
+ if (operand.type !== 'select')
182
+ return false;
183
+ // Unwrap a parenthesized LEFT compound operand's `select * from (compound)` wrapper so it is
184
+ // classified as the subtree it is, mirroring the dynamic `buildBranch` unwrap — a no-op on a
185
+ // direct operand (`set-op-leftwrap-write`).
186
+ const effective = unwrapBranchSelect(operand);
187
+ if (effective.compound && effective.compound.op !== 'diff') {
188
+ // An except / intersect subtree needs a boundary flag to gate the membership fan on.
189
+ if (!isUnionLikeSubtree(effective.compound.op) && !hasGatingFlag)
190
+ return false;
191
+ return isSetOpBodyWritable(effective);
192
+ }
193
+ // A leaf operand's data columns must be plain (optionally renamed) base columns; on top of
194
+ // that it is writable when it is single-source OR a **multi-source INNER join leg** (the
195
+ // join-leg compose — `set-op-write-multisource-leg-compose`, which builds an inner per-branch
196
+ // capture chained off the outer set-op capture). An OUTER (left/right/full) / cross join leg is
197
+ // deferred, so it reports non-writable here — matching the dynamic `buildBranch` reject exactly.
198
+ // (`isInnerJoinBody` keys only on `joinType`, so a NON-EQUI inner join is admitted and composes,
199
+ // exactly as the standalone join-view path and the flag-less route admit it.) This recurses to
200
+ // leaves at every depth, so a
201
+ // nested join leaf is classified too. Insertability through a join leg is deferred separately
202
+ // (gated in `schema.ts`).
203
+ if (tryBranchColumnNames(effective) === null)
204
+ return false;
205
+ return !isJoinBody(effective) || isInnerJoinBody(effective);
206
+ }
207
+ /**
208
+ * True iff a subtree operand's set operator is **union-like** (`union` / `unionAll`), whose
209
+ * result is a SUPERSET of each operand — every resident leaf row is a member, so the fan-out
210
+ * needs no membership gate. `except` / `intersect` are NOT union-like: a leaf can hold rows the
211
+ * subtree excludes, so their fan-out is gated on the captured subtree-membership boundary flag
212
+ * (`set-op-membership-nested-except`). This helper branches the gate logic (no extra conjunct
213
+ * for union-like, accumulate the boundary flag otherwise); a flag-less non-union boundary
214
+ * remains the lone deferral.
215
+ */
216
+ function isUnionLikeSubtree(op) {
217
+ return op === 'union' || op === 'unionAll';
218
+ }
219
+ /**
220
+ * True iff a membership body has a **subtree (compound) operand** — an inner
221
+ * `SetOperationNode` operand on EITHER side. Insert-through into a multi-leaf subtree has no
222
+ * single deterministic target leaf (product-coordinate addressing — `set-op-membership-nested`),
223
+ * so the static `is_insertable_into` surface gates to `NO` when this holds, while
224
+ * data/delete fan-out (which touches every member leaf) stays writable. A parenthesized LEFT
225
+ * compound operand is lifted into a `select * from (compound)` wrapper, so the left is unwrapped
226
+ * before probing — a parallel-sibling view (`set-op-leftwrap-write`) also reports NO.
227
+ */
228
+ export function setOpHasSubtreeOperand(selectAst) {
229
+ if (selectAst.type !== 'select' || !selectAst.compound)
230
+ return false;
231
+ const left = unwrapBranchSelect(leftBranchSelect(selectAst));
232
+ return isSubtreeOperand(left) || isSubtreeOperand(selectAst.compound.select);
233
+ }
234
+ /** True iff an operand SELECT is itself a (non-diff) set-op subtree. */
235
+ function isSubtreeOperand(operand) {
236
+ return operand.type === 'select' && !!operand.compound && operand.compound.op !== 'diff';
237
+ }
238
+ /**
239
+ * True iff EVERY multi-source (INNER join) leg/branch of this set-op body would accept an
240
+ * implicit INSERT through the plan-level shared-surrogate envelope
241
+ * (`set-op-write-multisource-leg-insert`). A body with NO join leg returns `true` (its
242
+ * single-source legs are the existing insert-through path); a body with ≥1 join leg returns
243
+ * `true` only when every such leg's `analyzeMultiSourceInsert` probe succeeds.
244
+ *
245
+ * A purely structural inner-vs-outer predicate is INSUFFICIENT: an inner equi-join leg can still
246
+ * reject dynamically with `unsupported-decomposition-key` (a composite shared key — the CV shape),
247
+ * `unsupported-join` (a non-equi ON), or `no-default` (the shared key is neither supplied nor
248
+ * defaulted — e.g. the SJ self-join, whose anchor key `emp.mgr` has no default). So this re-derives
249
+ * the branches the same way the dynamic write does (membership via {@link analyzeSetOpView}, else
250
+ * {@link analyzeFlaglessSetOpView}) and probes each `isMultiSource` leg with
251
+ * {@link analyzeMultiSourceInsert} under a synthetic implicit (empty-column) `InsertStmt`, in
252
+ * try/catch — a leg that throws (composite key, non-equi, no-default, uncovered NOT NULL, …) ⇒
253
+ * `false`. The empty `columns` makes `analyzeMultiSourceInsert` use the implicit supply set, and an
254
+ * inner join leg has no existence columns, so the empty `values` is never read.
255
+ *
256
+ * This is only ever reached after {@link isSetOpBranchWritable} / {@link isSetOpFlaglessWritableBody}
257
+ * already passed (an outer-join branch / non-flagless body short-circuits to the conservative
258
+ * all-`NO` row upstream, never reaching here), so the branch re-derivation will not throw on the
259
+ * body shape; only the per-leg `analyzeMultiSourceInsert` probe may. It must not leak a structured
260
+ * diagnostic out of the read TVF, so every probe is caught.
261
+ */
262
+ export function setOpJoinLegsInsertable(ctx, view) {
263
+ const branches = isSetOpMembershipBody(view.selectAst)
264
+ ? analyzeSetOpView(ctx, view).branches
265
+ : analyzeFlaglessSetOpView(ctx, view).legs.map(l => l.branch);
266
+ for (const branch of branches) {
267
+ if (!branch.isMultiSource)
268
+ continue;
269
+ const probeStmt = {
270
+ type: 'insert',
271
+ table: { type: 'identifier', name: branch.view.name },
272
+ columns: [],
273
+ source: { type: 'values', values: [] },
274
+ };
275
+ try {
276
+ analyzeMultiSourceInsert(ctx, branch.view, probeStmt);
277
+ }
278
+ catch {
279
+ return false; // a leg whose insert envelope rejects (composite / non-equi / no-default key)
280
+ }
281
+ }
282
+ return true;
283
+ }
284
+ /**
285
+ * The **surfaced inner-branch membership-flag names** of a (possibly nested) set-op body —
286
+ * every flag declared on a subtree operand, surfaced as a readable-but-non-writable column of
287
+ * the outer view. Returned in the plan's recursive `[L flags] ++ [R flags] ++ [own flags]`
288
+ * attribute layout (`SetOperationNode.buildAttributes`), so this list lands element-for-element
289
+ * on the plan-derived `analysis.surfacedInnerFlagNames` (`viewColNames` minus the leading data
290
+ * cols and trailing own flags). Empty for a binary (non-nested) body. Writing one addresses a
291
+ * branch *inside* a subtree operand (product-coordinate addressing), so `buildUpdate` rejects it
292
+ * with a `set-op-membership-nested` diagnostic and the `column_info` surface reports it
293
+ * `is_updatable = NO`.
294
+ */
295
+ export function surfacedInnerFlagNames(selectAst) {
296
+ const out = [];
297
+ if (selectAst.type === 'select' && selectAst.compound) {
298
+ // Walk BOTH operands in plan layout order: `[L operand surfaced] ++ [R operand surfaced]`
299
+ // (this node's OWN flags are `analysis.flags`, not surfaced-inner, and are excluded here).
300
+ // The unwrap of a parenthesized LEFT compound operand's `select * from (compound)` wrapper
301
+ // (`set-op-leftwrap-write`) lives INSIDE the recursion, so it applies uniformly at every
302
+ // level — pass the raw left/right operands here, not pre-unwrapped.
303
+ collectSubtreeFlagNames(leftBranchSelect(selectAst), out);
304
+ collectSubtreeFlagNames(selectAst.compound.select, out);
305
+ }
306
+ return out;
307
+ }
308
+ /**
309
+ * Collect every membership-flag name `operand` (and its deeper subtree operands) surfaces, in
310
+ * the plan's recursive `[L flags] ++ [R flags] ++ [own flags]` attribute layout
311
+ * (`SetOperationNode.buildAttributes`): descend the LEFT leg, then the RIGHT leg, THEN append
312
+ * this node's OWN flags — so the result lands element-for-element on the plan-derived
313
+ * `analysis.surfacedInnerFlagNames` regardless of which leg declared a flag, at any depth.
314
+ *
315
+ * Each operand is first unwrapped via {@link unwrapBranchSelect} (a no-op on a direct operand)
316
+ * so a parenthesized LEFT compound operand's `select * from (compound)` wrapper is descended too
317
+ * (`set-op-leftwrap-write`) — a flag declared on either leg of a left- OR right-side subtree is
318
+ * reached, and a write to one rejects with the clean `set-op-membership-nested` diagnostic
319
+ * rather than `unknown-view-column`.
320
+ */
321
+ function collectSubtreeFlagNames(operand, out) {
322
+ if (operand.type !== 'select')
323
+ return;
324
+ const effective = unwrapBranchSelect(operand);
325
+ if (!effective.compound || effective.compound.op === 'diff')
326
+ return;
327
+ collectSubtreeFlagNames(leftBranchSelect(effective), out);
328
+ collectSubtreeFlagNames(effective.compound.select, out);
329
+ for (const e of effective.compound.existence ?? [])
330
+ out.push(e.name);
331
+ }
332
+ /** Decompose a set-op membership-column view mutation. Throws a structured diagnostic for unsupported shapes. */
333
+ export function buildSetOpWrite(ctx, view, req) {
334
+ const analysis = analyzeSetOpView(ctx, view);
335
+ switch (req.op) {
336
+ case 'insert': return buildInsertThrough(ctx, view, analysis, req.stmt);
337
+ case 'update': return buildUpdate(ctx, view, analysis, req.stmt);
338
+ case 'delete': return buildDelete(ctx, view, analysis, req.stmt);
339
+ }
340
+ }
341
+ // --- analysis -------------------------------------------------------------
342
+ function analyzeSetOpView(ctx, view) {
343
+ if (view.selectAst.type !== 'select' || !view.selectAst.compound) {
344
+ raiseMutationDiagnostic({
345
+ reason: 'unsupported-set-op',
346
+ table: view.name,
347
+ message: `cannot write through view '${view.name}': not a set-operation body`,
348
+ });
349
+ }
350
+ const sel = view.selectAst;
351
+ const compound = sel.compound;
352
+ if (compound.op === 'diff') {
353
+ raiseMutationDiagnostic({
354
+ reason: 'unsupported-set-op',
355
+ table: view.name,
356
+ message: `cannot write through view '${view.name}': a DIFF (symmetric difference) body has no single addressable branch per row`,
357
+ });
358
+ }
359
+ if (!compound.existence || compound.existence.length === 0) {
360
+ raiseMutationDiagnostic({
361
+ reason: 'unsupported-set-op',
362
+ table: view.name,
363
+ message: `cannot write through view '${view.name}': a set-operation body is writable only through its membership columns; declare 'exists <branch> as <flag>' to address a branch`,
364
+ });
365
+ }
366
+ // LIMIT / OFFSET on the outer compound would put the capture's filter above the
367
+ // window — a write would escape it. Reject (parity with the join / single-source spine).
368
+ if (sel.limit || sel.offset) {
369
+ raiseMutationDiagnostic({
370
+ reason: 'unsupported-limit',
371
+ table: view.name,
372
+ message: `cannot write through view '${view.name}': a LIMIT/OFFSET set-operation body is not decomposable (a write would escape the limited window)`,
373
+ });
374
+ }
375
+ const flags = compound.existence.map(e => ({ name: e.name, side: e.branch }));
376
+ // Plan the body ONCE: its root attributes are the view output columns (data columns
377
+ // then the appended membership flags — `set-op-membership-read`'s combinator surface).
378
+ const root = buildSelectStmt(ctx, sel);
379
+ if (!isRelationalNode(root)) {
380
+ raiseMutationDiagnostic({
381
+ reason: 'no-base-lineage',
382
+ table: view.name,
383
+ message: `cannot write through view '${view.name}': the set-operation body did not produce a relation`,
384
+ });
385
+ }
386
+ const relRoot = root;
387
+ const attrs = relRoot.getAttributes();
388
+ const viewColNames = attrs.map(a => a.name);
389
+ const viewColTypes = attrs.map(a => a.type);
390
+ // Data-column count is the recursive DATA arity of the planned set operation
391
+ // (`SetOperationNode.dataColumnCount()`), NOT `attrs.length - flags.length`: with a nested
392
+ // (flagged) subtree operand, the surfaced inner flags inflate `attrs.length`, so subtracting
393
+ // only the OWN flags would mis-count them as data columns (`nestable-flagged-set-ops`).
394
+ const setOpNode = findSetOpNode(relRoot);
395
+ if (!setOpNode) {
396
+ raiseMutationDiagnostic({
397
+ reason: 'no-base-lineage',
398
+ table: view.name,
399
+ message: `cannot write through view '${view.name}': the set-operation body produced no SetOperationNode`,
400
+ });
401
+ }
402
+ const dataColCount = setOpNode.dataColumnCount();
403
+ if (dataColCount <= 0) {
404
+ raiseMutationDiagnostic({
405
+ reason: 'unsupported-set-op',
406
+ table: view.name,
407
+ message: `cannot write through view '${view.name}': the set operation exposes no data columns alongside its membership flags`,
408
+ });
409
+ }
410
+ const dataColNames = viewColNames.slice(0, dataColCount);
411
+ // Surfaced inner flags sit BETWEEN the data columns and this node's own flags in the
412
+ // `[data] ++ [L flags] ++ [R flags] ++ [own flags]` layout — `viewColNames` minus the
413
+ // leading data columns and the trailing own flags. Empty for a binary (non-nested) body.
414
+ const surfacedInnerFlagNames = viewColNames.slice(dataColCount, viewColNames.length - flags.length);
415
+ // A scope resolving each view output column name to its producing attribute over the
416
+ // planned root — the same shape `createSetOperationScope` builds for the body itself,
417
+ // reused here so the user predicate / capture projections resolve byte-identically.
418
+ // Parented to `ctx.scope` so a user WHERE's parameters (`where id = ?`), CTE refs, and
419
+ // other ambient symbols still resolve — a view output column shadows them (checked
420
+ // first), and a base-only name still fails to resolve (the statement scope exposes no
421
+ // base columns), so the encapsulation guard is unchanged.
422
+ const viewColScope = new RegisteredScope(ctx.scope);
423
+ attrs.forEach((attr, i) => {
424
+ viewColScope.registerSymbol(attr.name.toLowerCase(), (exp, s) => new ColumnReferenceNode(s, exp, attr.type, attr.id, i));
425
+ });
426
+ const branches = [
427
+ buildBranch(view, 'left', leftBranchSelect(sel), dataColCount, flags),
428
+ buildBranch(view, 'right', rightBranchSelect(view, compound.select), dataColCount, flags),
429
+ ];
430
+ return { op: compound.op, root: relRoot, viewColScope, viewColNames, viewColTypes, dataColCount, dataColNames, surfacedInnerFlagNames, flags, branches };
431
+ }
432
+ /**
433
+ * The `SetOperationNode` inside a planned body root — the root itself for a bare compound
434
+ * body, else found by descending the relational spine (a body with an outer ORDER BY wraps
435
+ * the set op in a `SortNode`). Its recursive `dataColumnCount()` is the data arity the
436
+ * surfaced-inner-flag count subtraction (`attrs.length - flags.length`) over-counts.
437
+ */
438
+ function findSetOpNode(node) {
439
+ if (node instanceof SetOperationNode)
440
+ return node;
441
+ for (const child of node.getRelations()) {
442
+ const found = findSetOpNode(child);
443
+ if (found)
444
+ return found;
445
+ }
446
+ return undefined;
447
+ }
448
+ /**
449
+ * Membership-free branch analysis of a nested (subtree) operand: its two inner branches,
450
+ * built without the membership gate `analyzeSetOpView` enforces (a flag-less subtree has no
451
+ * `compound.existence`). The data arity is the OUTER body's (`dataColCount`) — set
452
+ * operations preserve data columns at every depth (the `SetOperationNode` constructor
453
+ * enforces `dataArity(left) === dataArity(right)`), so an inner leaf has exactly the same
454
+ * data columns the outer capture froze. Used by the data/delete fan-out recursion.
455
+ */
456
+ function analyzeSetOpBranches(view, branchView, dataColCount) {
457
+ const sel = branchView.selectAst;
458
+ const compound = sel.compound;
459
+ const innerFlags = (compound.existence ?? []).map(e => ({ name: e.name, side: e.branch }));
460
+ return [
461
+ buildBranch(view, 'left', leftBranchSelect(sel), dataColCount, innerFlags),
462
+ buildBranch(view, 'right', rightBranchSelect(view, compound.select), dataColCount, innerFlags),
463
+ ];
464
+ }
465
+ /** The left operand's SELECT — the compound statement stripped of its outer modifiers. */
466
+ function leftBranchSelect(sel) {
467
+ const { compound: _c, orderBy: _o, limit: _l, offset: _f, ...leftCore } = sel;
468
+ return leftCore;
469
+ }
470
+ /**
471
+ * The **effective** operand SELECT of a (possibly parenthesized-compound) operand: the inner
472
+ * compound when `branchSelect` is a pure `select * from (<compound>) as values_N` passthrough
473
+ * wrapper (the shape the parser lifts a parenthesized LEFT compound operand into,
474
+ * `set-op-leftwrap-arity`), else `branchSelect` unchanged. Shared with the read/plan path
475
+ * (`select-compound.ts`'s `unwrapToSelect`, via the same {@link unwrapPassthroughSubquery}
476
+ * predicate) so neither path drifts on what a pure wrapper is.
477
+ *
478
+ * Threading it through {@link buildBranch} makes the wrapped LEFT operand a first-class subtree
479
+ * operand for the write path's unambiguous fan-out (data UPDATE / DELETE / `set <flag> = false`),
480
+ * exactly as the (always-direct) right compound operand already is (`set-op-leftwrap-write`). The
481
+ * unwrap is a no-op on a direct operand (a leaf SELECT, or the right side's direct compound), so
482
+ * applying it uniformly to both sides is safe. A non-SELECT inner (a `select * from (values…)`)
483
+ * stays the wrapper and is rejected downstream as a `select *` leg.
484
+ */
485
+ function unwrapBranchSelect(branchSelect) {
486
+ const inner = unwrapPassthroughSubquery(branchSelect);
487
+ return inner && inner.type === 'select' ? inner : branchSelect;
488
+ }
489
+ /**
490
+ * The **left-most leaf** SELECT of a (possibly nested, possibly wrapped) operand — the leg whose
491
+ * projection positionally aligns to the set operation's data columns (a `SetOperationNode`
492
+ * preserves its left child's column ids verbatim at every depth). A direct operand IS its own
493
+ * leaf; a nested compound descends its left leg, unwrapping each `select * from (compound)`
494
+ * wrapper. This is what {@link branchColumnNames} reads its data-column names from: a right-spine
495
+ * nested operand's left leg is a direct leaf (so `branchSelect.columns` already named it), but a
496
+ * LEFT-spine nested operand's left leg is itself wrapped (`set-op-leftwrap-write`), so a single
497
+ * `.columns` read there would see the wrapper's `*` — the descent reaches the real leaf instead.
498
+ */
499
+ function leftmostLeafSelect(branchSelect) {
500
+ let cur = unwrapBranchSelect(branchSelect);
501
+ while (cur.compound && cur.compound.op !== 'diff') {
502
+ cur = unwrapBranchSelect(leftBranchSelect(cur));
503
+ }
504
+ return cur;
505
+ }
506
+ /** The right operand's SELECT, stripped of any leg-local ORDER BY / LIMIT / OFFSET. */
507
+ function rightBranchSelect(view, right) {
508
+ if (right.type !== 'select') {
509
+ raiseMutationDiagnostic({
510
+ reason: 'unsupported-set-op',
511
+ table: view.name,
512
+ message: `cannot write through view '${view.name}': the right branch is a ${right.type.toUpperCase()} operand, which is not a recursively-writable body (v1 supports SELECT operands)`,
513
+ });
514
+ }
515
+ const { orderBy: _o, limit: _l, offset: _f, ...core } = right;
516
+ return core;
517
+ }
518
+ /** Build one recursively-writable branch view-like from an operand SELECT. */
519
+ function buildBranch(view, side, branchSelect, dataColCount, flags) {
520
+ // Unwrap a parenthesized LEFT compound operand's `select * from (<compound>)` wrapper to its
521
+ // inner compound, so a wrapped left operand is a first-class subtree operand (its data cols,
522
+ // `isNested`, and recursion all derive from the inner) — `set-op-leftwrap-write`. A no-op on a
523
+ // direct operand.
524
+ const effectiveSelect = unwrapBranchSelect(branchSelect);
525
+ // A subtree operand carries its own (non-diff) compound; its fan-out recurses to leaves.
526
+ const isNested = !!effectiveSelect.compound && effectiveSelect.compound.op !== 'diff';
527
+ // A non-nested **multi-source (INNER join) leg** is now writable for UPDATE / DELETE
528
+ // (`set-op-write-multisource-leg-compose`): its data/delete fan builds an inner per-branch
529
+ // base-PK capture chained off the outer set-op capture (a fresh `__vmupd_keys$N` name), so
530
+ // the join branch decomposes through the multi-source machinery without colliding with the
531
+ // outer capture. An OUTER (left/right/full) / cross join leg is deferred — reject it cleanly
532
+ // here (never routing it to `propagate`, which would mishandle the un-composed capture).
533
+ // (`isInnerJoinBody` keys only on `joinType`, so a NON-EQUI inner join is admitted and
534
+ // composes here, exactly as the standalone join-view path admits it — not rejected.) A nested
535
+ // (subtree) operand is NOT classified here: its join leaves are reached as non-nested branches
536
+ // via `analyzeSetOpBranches` → `buildBranch` and classified at that depth, so this covers every
537
+ // leaf at every nesting level.
538
+ const isMultiSource = !isNested && isInnerJoinBody(effectiveSelect);
539
+ if (!isNested && !isMultiSource && isJoinBody(effectiveSelect)) {
540
+ raiseMutationDiagnostic({
541
+ reason: 'unsupported-set-op',
542
+ table: view.name,
543
+ message: `cannot write through view '${view.name}': the ${side} branch is an OUTER (left/right/full) or cross join leg, which is not a plain INNER join — its set-op write composition is deferred (set-op-write-multisource-leg-compose ships inner-join legs)`,
544
+ });
545
+ }
546
+ const dataColNames = branchColumnNames(view, side, effectiveSelect);
547
+ if (dataColNames.length !== dataColCount) {
548
+ raiseMutationDiagnostic({
549
+ reason: 'unsupported-set-op',
550
+ table: view.name,
551
+ message: `cannot write through view '${view.name}': the ${side} branch projects ${dataColNames.length} columns but the set operation exposes ${dataColCount} data columns`,
552
+ });
553
+ }
554
+ const branchView = {
555
+ name: `__setop_${side}`,
556
+ schemaName: view.schemaName,
557
+ selectAst: effectiveSelect,
558
+ };
559
+ const flag = flags.find(f => f.side === side);
560
+ return { side, view: branchView, dataColNames, isNested, isMultiSource, ...(flag ? { flag } : {}) };
561
+ }
562
+ /**
563
+ * The branch operand's projected column names (positional). v1 admits plain column
564
+ * references (with optional rename) — the shape that round-trips a base column through
565
+ * the branch's single-source spine. A `select *` leg or a computed projection is
566
+ * rejected (the nested / computed-branch generality is deferred): a `*` has no static
567
+ * name list to align positionally, and a computed leg column has no base column to
568
+ * write a fanned-out value into.
569
+ */
570
+ function branchColumnNames(view, side, branchSelect) {
571
+ // The data-column names are the left-most leaf's projection (a set op preserves its left
572
+ // child's column ids at every depth) — descend through a nested / left-wrapped operand so a
573
+ // LEFT-spine compound branch derives names from its real leaf, not a wrapper's `*`.
574
+ const leaf = leftmostLeafSelect(branchSelect);
575
+ const names = tryBranchColumnNames(leaf);
576
+ if (names)
577
+ return names;
578
+ // `tryBranchColumnNames` returned `null` ⇒ a `*` or computed leg; re-derive the
579
+ // specific reason for the per-side diagnostic (the shared probe only yields the
580
+ // boolean, so the static surface and this path cannot drift on what counts as writable).
581
+ for (const rc of leaf.columns) {
582
+ if (rc.type === 'all') {
583
+ raiseMutationDiagnostic({
584
+ reason: 'unsupported-set-op',
585
+ table: view.name,
586
+ message: `cannot write through view '${view.name}': the ${side} branch uses 'select *'; list its columns explicitly so each maps to a writable branch column`,
587
+ });
588
+ }
589
+ if (rc.expr.type !== 'column') {
590
+ raiseMutationDiagnostic({
591
+ reason: 'unsupported-set-op',
592
+ table: view.name,
593
+ message: `cannot write through view '${view.name}': the ${side} branch projects a computed column; v1 supports plain (optionally renamed) base columns in a writable branch`,
594
+ });
595
+ }
596
+ }
597
+ // Unreachable: `tryBranchColumnNames` returns `null` only for a `*`/computed leg, both
598
+ // handled above. Guard defensively rather than returning a wrong (empty) name list.
599
+ raiseMutationDiagnostic({
600
+ reason: 'unsupported-set-op',
601
+ table: view.name,
602
+ message: `cannot write through view '${view.name}': the ${side} branch is not a writable plain-column projection`,
603
+ });
604
+ }
605
+ /**
606
+ * The non-throwing core of {@link branchColumnNames}: the branch operand's projected
607
+ * plain-column names (positional, honoring a leg rename via `rc.alias ?? rc.expr.name`),
608
+ * or `null` when the leg is not a writable shape — a `select *` (`rc.type === 'all'`, no
609
+ * static name list to align positionally) or a computed (`rc.expr.type !== 'column'`)
610
+ * projection (no base column to write a fanned-out value into). Shared by the dynamic
611
+ * write path and the static {@link isSetOpBranchWritable} probe so neither can drift on
612
+ * what a writable leg is.
613
+ */
614
+ function tryBranchColumnNames(branchSelect) {
615
+ const names = [];
616
+ for (const rc of branchSelect.columns) {
617
+ if (rc.type === 'all' || rc.expr.type !== 'column')
618
+ return null;
619
+ names.push(rc.alias ?? rc.expr.name);
620
+ }
621
+ return names;
622
+ }
623
+ // --- capture --------------------------------------------------------------
624
+ /**
625
+ * Build the up-front affected-row capture: `Project_{all view cols}(Filter_{userWhere}
626
+ * (setOpRoot))`, materialized ONCE before any branch op fires. Every probe-driven write
627
+ * reads it back through {@link MS_UPDATE_KEYS_CTE}, so the data columns AND the
628
+ * membership-probe flags are frozen at their pre-mutation values (Halloween-safe). The
629
+ * capture column shape (one per view output column, by name) is what each branch op's
630
+ * `k.<col>` reference and the membership-INSERT projection resolve against.
631
+ */
632
+ function buildSetOpCapture(ctx, analysis, where) {
633
+ const scope = analysis.viewColScope;
634
+ const filtered = where
635
+ ? new FilterNode(scope, analysis.root, buildExpression({ ...ctx, scope }, cloneExpr(where)))
636
+ : analysis.root;
637
+ const projections = analysis.viewColNames.map(name => ({
638
+ node: buildExpression({ ...ctx, scope }, { type: 'column', name }),
639
+ alias: name,
640
+ }));
641
+ const source = new ProjectNode(scope, filtered, projections, undefined, undefined, false);
642
+ const keyColumns = analysis.viewColNames.map((name, i) => ({ name, type: analysis.viewColTypes[i] }));
643
+ return { source, descriptor: {}, keyColumns };
644
+ }
645
+ /** Build a fresh {@link JoinLegFan} over the up-front outer set-op capture. */
646
+ function makeJoinLegFan(outerCapture) {
647
+ const captures = [];
648
+ let counter = 0;
649
+ return { outerCapture, captures, mintName: () => `${MS_UPDATE_KEYS_CTE}$${++counter}` };
650
+ }
651
+ /**
652
+ * Compose one multi-source (INNER join) branch's data UPDATE / DELETE: build the branch base ops
653
+ * via the multi-source decomposer (referencing a fresh inner `__vmupd_keys$N` capture) PLUS the
654
+ * inner per-branch base-PK capture itself, filtered by the branch's `memberExists` predicate (the
655
+ * SAME data-tuple match against the OUTER set-op capture the single-source fan builds), and record
656
+ * the inner capture into the fan accumulator. The inner capture is built under a context with the
657
+ * OUTER capture injected (so its `memberExists` filter's `from __vmupd_keys` resolves), and over
658
+ * the branch's planned join body (`analyzeJoinView`) — so the join leg decomposes through the
659
+ * multi-source machinery without colliding with the outer capture.
660
+ *
661
+ * `decompose` selects UPDATE vs DELETE — both build `BaseOp[]` against the base tables whose
662
+ * predicates read back the fresh inner capture; the inner capture's identity is the branch's
663
+ * affected (captured-and-member-matched) rows. `branchStmt.where` IS the `memberExists` predicate,
664
+ * reused verbatim as the inner capture's filter.
665
+ */
666
+ function fanMultiSourceBranch(ctx, branch, branchStmt, fan, decompose) {
667
+ const joinAnalysis = analyzeJoinView(ctx, branch.view);
668
+ const name = fan.mintName();
669
+ const baseOps = decompose(joinAnalysis, name);
670
+ const sides = capturedSideIndices(baseOps, joinAnalysis);
671
+ // Inject the OUTER set-op capture so the inner capture's `memberExists` filter (`exists (…
672
+ // from __vmupd_keys k …)`) resolves against it; the inner capture is built over the branch's
673
+ // planned join body under its own fresh name.
674
+ const ctxWithOuter = withKeyCapture(ctx, fan.outerCapture);
675
+ const capture = buildMultiSourceKeyCapture(ctxWithOuter, branch.view, branchStmt.where, joinAnalysis, sides, undefined, name);
676
+ fan.captures.push(capture);
677
+ return baseOps;
678
+ }
679
+ function buildUpdate(ctx, view, analysis, stmt) {
680
+ rejectReturning(view, stmt.returning);
681
+ const flips = new Map();
682
+ const dataAssignments = [];
683
+ for (const asg of stmt.assignments) {
684
+ const flag = analysis.flags.find(f => f.name.toLowerCase() === asg.column.toLowerCase());
685
+ if (flag) {
686
+ const value = asBooleanLiteral(asg.value);
687
+ if (value === undefined) {
688
+ raiseMutationDiagnostic({
689
+ reason: 'unsupported-set-op',
690
+ column: asg.column,
691
+ table: view.name,
692
+ message: `cannot write through view '${view.name}': the membership column '${asg.column}' must be assigned a boolean literal (true/false); a per-row branch on a non-literal value is deferred`,
693
+ });
694
+ }
695
+ const existing = flips.get(flag.side);
696
+ if (existing !== undefined && existing !== value) {
697
+ raiseMutationDiagnostic({
698
+ reason: 'conflicting-assignment',
699
+ column: asg.column,
700
+ table: view.name,
701
+ message: `cannot write through view '${view.name}': the ${flag.side} branch's membership is assigned both true and false in one statement`,
702
+ });
703
+ }
704
+ flips.set(flag.side, value);
705
+ continue;
706
+ }
707
+ // A surfaced inner flag (`inB`/`inC`) IS a view column, but writing it addresses a
708
+ // branch INSIDE a subtree operand (product-coordinate addressing) — deferred to
709
+ // `set-op-membership-nested`. Reject with a clean diagnostic (NOT `unknown-view-column`,
710
+ // which would mislead — the name resolves).
711
+ if (analysis.surfacedInnerFlagNames.some(n => n.toLowerCase() === asg.column.toLowerCase())) {
712
+ raiseMutationDiagnostic({
713
+ reason: 'unsupported-set-op',
714
+ column: asg.column,
715
+ table: view.name,
716
+ message: `cannot write through view '${view.name}': '${asg.column}' is a surfaced inner-branch membership flag of a nested set operation; writing it addresses a branch inside a subtree operand (product-coordinate addressing) — deferred to set-op-membership-nested`,
717
+ });
718
+ }
719
+ const position = analysis.dataColNames.findIndex(n => n.toLowerCase() === asg.column.toLowerCase());
720
+ if (position < 0) {
721
+ raiseMutationDiagnostic({
722
+ reason: 'unknown-view-column',
723
+ column: asg.column,
724
+ table: view.name,
725
+ message: `cannot write through view '${view.name}': '${asg.column}' is not a data or membership column of the set operation`,
726
+ suggestion: `view '${view.name}' exposes: ${analysis.viewColNames.join(', ')}.`,
727
+ });
728
+ }
729
+ dataAssignments.push({ position, value: asg.value });
730
+ }
731
+ // Contradiction: a `false` flip removes the row from its branch, but a data
732
+ // assignment fans out to every member branch (including that one). The two effects on
733
+ // the same branch contradict (write a column of a row being deleted). Reject rather
734
+ // than silently pick one (parity with the join-existence write's `set npCol, hasB=false`).
735
+ const anyFalseFlip = [...flips.values()].some(v => v === false);
736
+ if (anyFalseFlip && dataAssignments.length > 0) {
737
+ raiseMutationDiagnostic({
738
+ reason: 'conflicting-assignment',
739
+ table: view.name,
740
+ message: `cannot write through view '${view.name}': a membership-flag write removes a branch (= false) while the same statement also writes a data column that fans out to that branch — the two effects contradict`,
741
+ });
742
+ }
743
+ const capture = buildSetOpCapture(ctx, analysis, stmt.where);
744
+ // A multi-source (INNER join) branch the fan reaches builds its own inner per-branch capture
745
+ // chained off this outer capture; the fan accumulates them (`set-op-write-multisource-leg-compose`).
746
+ const fan = makeJoinLegFan(capture);
747
+ const baseOps = [];
748
+ // A `set <joinFlag> = true` membership flip into a multi-source (INNER join) branch is built via
749
+ // the plan-level shared-surrogate envelope and spliced as a nested `ViewMutationNode` child; the
750
+ // accumulator threads into `buildBranchMembershipInsert` (mirror of the `fan` threading) and
751
+ // rides the plan (`set-op-write-multisource-leg-insert`). The nested envelope source reads the
752
+ // OUTER `__vmupd_keys` capture (built above for every UPDATE), so `buildSetOpMutation`'s
753
+ // capture-injected `opCtx` resolves its `from __vmupd_keys`.
754
+ const joinLegInserts = [];
755
+ // Data fan-out: update the row in every member leaf, recursing through a subtree operand
756
+ // to its leaves. The full-data-tuple `exists` correlation restricts each leaf update to
757
+ // the rows actually present there (a non-member leaf matches no row), so the per-branch
758
+ // membership is honored without an explicit flag gate.
759
+ if (dataAssignments.length > 0) {
760
+ for (const branch of analysis.branches) {
761
+ baseOps.push(...fanBranchDataUpdate(ctx, view, analysis, branch, dataAssignments, stmt, fan));
762
+ }
763
+ }
764
+ // Membership flips. `= true` inserts into the branch (rejected for a subtree — a
765
+ // multi-leaf insert has no single target leaf); `= false` is a delete fan-out (recurses
766
+ // through a subtree to drop the row from its resident leaves).
767
+ for (const branch of analysis.branches) {
768
+ const flip = flips.get(branch.side);
769
+ if (flip === undefined)
770
+ continue;
771
+ if (flip) {
772
+ baseOps.push(...buildBranchMembershipInsert(ctx, view, analysis, branch, dataAssignments, stmt, joinLegInserts));
773
+ }
774
+ else {
775
+ baseOps.push(...fanBranchDelete(ctx, view, analysis, branch, stmt, fan));
776
+ }
777
+ }
778
+ if (baseOps.length === 0 && joinLegInserts.length === 0) {
779
+ // Unreachable: the parser requires ≥1 assignment, and every assignment routes to a
780
+ // flip or a data fan-out above. Guard defensively. (A lone join-branch `= true` flip
781
+ // produces no base op of its own — it splices a nested envelope child — so it is
782
+ // admitted via the `joinLegInserts` arm.)
783
+ raiseMutationDiagnostic({
784
+ reason: 'unsupported-set-op',
785
+ table: view.name,
786
+ message: `cannot write through view '${view.name}': the update names no writable set-operation column`,
787
+ });
788
+ }
789
+ return {
790
+ baseOps,
791
+ capture,
792
+ nestedCaptures: fan.captures.length > 0 ? fan.captures : undefined,
793
+ joinLegInserts: joinLegInserts.length > 0 ? joinLegInserts : undefined,
794
+ };
795
+ }
796
+ /**
797
+ * The captured subtree-membership boundary flag to gate a delete / data fan-out into an
798
+ * `except` / `intersect` subtree operand on — `branch.flag.name`, the `exists <branch> as
799
+ * <flag>` the OUTER compound declared for this side (a view output column, present in the
800
+ * capture, so `k.<flag>` probes "is this captured row a member of the subtree").
801
+ *
802
+ * Gating the recursion on this flag restores soundness: for `except` / `intersect` a leaf can
803
+ * hold rows the subtree EXCLUDES (e.g. a row in both B and C is absent from `B except C`); if
804
+ * an OUTER operand makes that row visible it enters the capture, and a blind fan-out would
805
+ * delete / mutate it in the inner leaves even though it is NOT a subtree member. AND-ing
806
+ * `k.<flag>` into the leaf member-exists restricts the fan to genuine members, making the
807
+ * nested fan behave exactly like the proven binary `except` / `intersect` fan.
808
+ *
809
+ * A **flag-less** non-union boundary (`A union[inA] (B except C)` — no `inSub`) surfaces no
810
+ * boundary probe column to gate on, so it stays **deferred**: reject cleanly, naming
811
+ * `set-op-membership-nested-except` (kept greppable as the remaining deferral).
812
+ */
813
+ function gateFlagForNonUnionSubtree(view, branch) {
814
+ if (branch.flag)
815
+ return branch.flag.name;
816
+ const op = branch.view.selectAst.compound?.op;
817
+ raiseMutationDiagnostic({
818
+ reason: 'unsupported-set-op',
819
+ table: view.name,
820
+ message: `cannot write through view '${view.name}': a delete / data fan-out through a flag-less ${(op ?? 'set').toUpperCase()} subtree operand is deferred — without a declared subtree-membership flag ('exists <branch> as <flag>') there is no captured boundary probe to gate the fan on, so it could touch leaf rows the subtree excludes (set-op-membership-nested-except)`,
821
+ });
822
+ }
823
+ /**
824
+ * Accumulate the membership gate for descending into a nested (subtree) `branch`: a **union /
825
+ * union all** subtree adds nothing (a leaf ⊆ the subtree, so leaf-presence already implies
826
+ * membership), an **`except` / `intersect`** subtree contributes its captured boundary flag
827
+ * (`set-op-membership-nested-except`; a flag-less non-union boundary throws, staying deferred).
828
+ * Shared by {@link fanBranchDataUpdate} and {@link fanBranchDelete} so the two fan paths cannot
829
+ * drift on the gate logic.
830
+ */
831
+ function accumulateInnerGate(view, branch, gateFlags) {
832
+ const subOp = branch.view.selectAst.compound.op;
833
+ return isUnionLikeSubtree(subOp)
834
+ ? gateFlags
835
+ : [...gateFlags, gateFlagForNonUnionSubtree(view, branch)];
836
+ }
837
+ /**
838
+ * Fan a data-column UPDATE out to one branch — recursing through a nested (subtree) operand
839
+ * to its member leaves, else updating the leaf's member rows (matched via the shared capture).
840
+ *
841
+ * The recursion reuses the SINGLE up-front capture: a subtree's leaves share the outer's data
842
+ * columns (nesting preserves them), so "update the leaf rows whose data tuple ∈ `__vmupd_keys`"
843
+ * is the same frozen-capture correlation rebuilt against each inner branch — no second capture.
844
+ * The positional `dataAssignments` fan unchanged (each re-mapped to the leaf's own column name
845
+ * at that data position via `branch.dataColNames[da.position]`); the value is cloned fresh at
846
+ * each leaf (its refs resolve against that leaf's columns when leg names match — the v1 caveat).
847
+ */
848
+ function fanBranchDataUpdate(ctx, view, analysis, branch, dataAssignments, stmt, fan, gateFlags = []) {
849
+ if (branch.isNested) {
850
+ const innerGate = accumulateInnerGate(view, branch, gateFlags);
851
+ const baseOps = [];
852
+ for (const inner of analyzeSetOpBranches(view, branch.view, analysis.dataColCount)) {
853
+ baseOps.push(...fanBranchDataUpdate(ctx, view, analysis, inner, dataAssignments, stmt, fan, innerGate));
854
+ }
855
+ return baseOps;
856
+ }
857
+ const assignments = dataAssignments.map(da => ({
858
+ column: branch.dataColNames[da.position],
859
+ value: cloneExpr(da.value),
860
+ }));
861
+ const updateStmt = {
862
+ type: 'update',
863
+ table: { type: 'identifier', name: branch.view.name },
864
+ assignments,
865
+ where: buildMemberExists(analysis, branch, gateFlags),
866
+ contextValues: stmt.contextValues,
867
+ schemaPath: stmt.schemaPath,
868
+ loc: stmt.loc,
869
+ };
870
+ // A multi-source (INNER join) branch routes through the join decomposer + an inner per-branch
871
+ // capture (chained off the outer set-op capture) rather than plain `propagate` (which builds
872
+ // no capture and would collide the inner capture with the outer). § set-op-write-multisource-leg-compose.
873
+ if (branch.isMultiSource) {
874
+ return fanMultiSourceBranch(ctx, branch, updateStmt, fan, (joinAnalysis, name) => decomposeUpdate(ctx, branch.view, joinAnalysis, updateStmt, undefined, name));
875
+ }
876
+ return propagate(ctx, branch.view, { op: 'update', stmt: updateStmt });
877
+ }
878
+ /**
879
+ * `set <flag> = true`: insert the captured rows that are **absent** from this branch
880
+ * (`where not k.<flag>`) into it. Composed same-statement data assignments flow into the
881
+ * inserted projection (the new value); every other data column reads the captured row.
882
+ */
883
+ function buildBranchMembershipInsert(ctx, view, analysis, branch, dataAssignments, stmt, joinLegInserts) {
884
+ if (branch.isNested) {
885
+ // `set <subtreeFlag> = true` would insert the row into a multi-leaf subtree (B∪C) —
886
+ // "which leaf?" has no single deterministic answer (product-coordinate addressing).
887
+ // Deferred to `set-op-membership-nested`. (The `= false` flip routes to the delete
888
+ // fan-out, which IS unambiguous — it touches every member leaf.)
889
+ raiseMutationDiagnostic({
890
+ reason: 'unsupported-set-op',
891
+ column: branch.flag?.name,
892
+ table: view.name,
893
+ message: `cannot write through view '${view.name}': 'set ${branch.flag?.name ?? '<flag>'} = true' inserts into a multi-leaf subtree operand, which has no single deterministic target leaf (product-coordinate addressing) — deferred to set-op-membership-nested`,
894
+ });
895
+ }
896
+ if (!branch.flag) {
897
+ // Unreachable on the flip path (a flip targets a declared flag's side), but guard.
898
+ raiseMutationDiagnostic({
899
+ reason: 'unsupported-set-op',
900
+ table: view.name,
901
+ message: `cannot write through view '${view.name}': the ${branch.side} branch has no membership flag to insert through`,
902
+ });
903
+ }
904
+ const assignedByPosition = new Map();
905
+ for (const da of dataAssignments)
906
+ assignedByPosition.set(da.position, da.value);
907
+ const projections = analysis.dataColNames.map((uName, i) => {
908
+ const assigned = assignedByPosition.get(i);
909
+ const expr = assigned !== undefined
910
+ ? qualifyDataRefsWithCapture(view, analysis, assigned)
911
+ : { type: 'column', name: uName, table: 'k' };
912
+ return { type: 'column', expr, alias: branch.dataColNames[i] };
913
+ });
914
+ const source = {
915
+ type: 'select',
916
+ columns: projections,
917
+ from: [{ type: 'table', table: { type: 'identifier', name: MS_UPDATE_KEYS_CTE }, alias: 'k' }],
918
+ // Only the captured rows NOT already in this branch — the probe makes a redundant
919
+ // `= true` a clean no-op (and folds the per-operator semantics: `except`'s right
920
+ // flag is always false ⇒ insert all, `intersect`'s flags are always true ⇒ none).
921
+ where: { type: 'unary', operator: 'NOT', expr: { type: 'column', name: branch.flag.name, table: 'k' } },
922
+ };
923
+ const insertStmt = {
924
+ type: 'insert',
925
+ table: { type: 'identifier', name: branch.view.name },
926
+ columns: [...branch.dataColNames],
927
+ source,
928
+ contextValues: stmt.contextValues,
929
+ schemaPath: stmt.schemaPath,
930
+ loc: stmt.loc,
931
+ };
932
+ // A `set <flag> = true` flip into a multi-source (INNER join) branch is built via the
933
+ // plan-level shared-surrogate envelope (`buildMultiSourceInsert`) and spliced as a nested
934
+ // `ViewMutationNode` child — record the descriptor and produce NO base op of our own
935
+ // (`set-op-write-multisource-leg-insert`). The `source` SELECT reads the OUTER `__vmupd_keys`
936
+ // capture (`where not k.<flag>`), so the envelope reuses it verbatim; `buildSetOpMutation`
937
+ // passes the capture-injected context so its `from __vmupd_keys` resolves. A single-source
938
+ // branch lowers through `propagate`.
939
+ if (branch.isMultiSource) {
940
+ joinLegInserts.push({ view: branch.view, stmt: insertStmt });
941
+ return [];
942
+ }
943
+ return propagate(ctx, branch.view, { op: 'insert', stmt: insertStmt });
944
+ }
945
+ /**
946
+ * Fan a DELETE out to one branch — recursing through a nested (subtree) operand to its member
947
+ * leaves, else deleting the leaf's matching rows (every captured row present there). Serves
948
+ * both the `delete from V` fan-out and the `set <subtreeFlag> = false` subtree drop (a
949
+ * delete fan-out into the subtree's leaves), so it takes either originating statement and
950
+ * reads only its shared `contextValues` / `schemaPath` / `loc`. Reuses the SINGLE up-front
951
+ * capture (the same frozen-data-tuple correlation, rebuilt against each inner branch).
952
+ */
953
+ function fanBranchDelete(ctx, view, analysis, branch, stmt, fan, gateFlags = []) {
954
+ if (branch.isNested) {
955
+ const innerGate = accumulateInnerGate(view, branch, gateFlags);
956
+ const baseOps = [];
957
+ for (const inner of analyzeSetOpBranches(view, branch.view, analysis.dataColCount)) {
958
+ baseOps.push(...fanBranchDelete(ctx, view, analysis, inner, stmt, fan, innerGate));
959
+ }
960
+ return baseOps;
961
+ }
962
+ const deleteStmt = {
963
+ type: 'delete',
964
+ table: { type: 'identifier', name: branch.view.name },
965
+ where: buildMemberExists(analysis, branch, gateFlags),
966
+ contextValues: stmt.contextValues,
967
+ schemaPath: stmt.schemaPath,
968
+ loc: stmt.loc,
969
+ };
970
+ // A multi-source (INNER join) branch routes through the join decomposer + an inner per-branch
971
+ // capture (chained off the outer set-op capture); a single-source branch lowers via `propagate`.
972
+ if (branch.isMultiSource) {
973
+ return fanMultiSourceBranch(ctx, branch, deleteStmt, fan, (joinAnalysis, name) => decomposeDelete(ctx, branch.view, joinAnalysis, deleteStmt, name));
974
+ }
975
+ return propagate(ctx, branch.view, { op: 'delete', stmt: deleteStmt });
976
+ }
977
+ // --- DELETE (fan-out via the probe) ---------------------------------------
978
+ function buildDelete(ctx, view, analysis, stmt) {
979
+ rejectReturning(view, stmt.returning);
980
+ const capture = buildSetOpCapture(ctx, analysis, stmt.where);
981
+ const fan = makeJoinLegFan(capture);
982
+ const baseOps = [];
983
+ // Delete from every member leaf at every depth — the fan recurses through a subtree
984
+ // operand to its leaves (the full-tuple `exists` correlation restricts each leaf delete to
985
+ // its resident rows, so a non-member leaf matches none). A multi-source (INNER join) branch
986
+ // builds its own inner per-branch capture chained off this outer capture.
987
+ for (const branch of analysis.branches) {
988
+ baseOps.push(...fanBranchDelete(ctx, view, analysis, branch, stmt, fan));
989
+ }
990
+ return { baseOps, capture, nestedCaptures: fan.captures.length > 0 ? fan.captures : undefined };
991
+ }
992
+ // --- INSERT (insert-through, flag-routed) ---------------------------------
993
+ function buildInsertThrough(ctx, view, analysis, stmt) {
994
+ rejectReturning(view, stmt.returning);
995
+ if (stmt.source.type !== 'values') {
996
+ raiseMutationDiagnostic({
997
+ reason: 'unsupported-source',
998
+ table: view.name,
999
+ message: `cannot insert through view '${view.name}': a set-operation insert routes by literal membership flags, so it requires a VALUES source (a SELECT/DML source's per-row routing is deferred)`,
1000
+ });
1001
+ }
1002
+ // Resolve each VALUES position to a data column or a membership flag. An explicit
1003
+ // column list maps by name; an omitted list maps positionally over the view's output
1004
+ // layout (data columns then flags).
1005
+ const layout = resolveInsertLayout(view, analysis, stmt);
1006
+ // The flags route the insert: a true flag activates its branch, a false flag omits it.
1007
+ // The flag value must be a uniform boolean literal across the inserted rows (a per-row
1008
+ // mix is deferred), mirroring the join-existence insert directive.
1009
+ const activeSides = new Set();
1010
+ for (const flag of analysis.flags) {
1011
+ const pos = layout.flagPositions.get(flag.name.toLowerCase());
1012
+ if (pos === undefined) {
1013
+ raiseMutationDiagnostic({
1014
+ reason: 'unsupported-set-op',
1015
+ column: flag.name,
1016
+ table: view.name,
1017
+ message: `cannot insert through view '${view.name}': the membership flag '${flag.name}' must be supplied to route the insert to its branch (a flag-less multi-branch insert is ambiguous)`,
1018
+ });
1019
+ }
1020
+ if (uniformBooleanFlag(view, stmt.source.values, pos, flag.name))
1021
+ activeSides.add(flag.side);
1022
+ }
1023
+ if (activeSides.size === 0) {
1024
+ raiseMutationDiagnostic({
1025
+ reason: 'unsupported-set-op',
1026
+ table: view.name,
1027
+ message: `cannot insert through view '${view.name}': no membership flag is true, so the inserted row would belong to no branch (set at least one 'exists' flag true)`,
1028
+ });
1029
+ }
1030
+ const baseOps = [];
1031
+ // A multi-source (INNER join) branch's VALUES insert is built via the plan-level
1032
+ // shared-surrogate envelope (`buildMultiSourceInsert`) and spliced as a nested
1033
+ // `ViewMutationNode` child rather than routed through `propagate` (which would raise the
1034
+ // internal `unsupported-multisource-insert`) — `set-op-write-multisource-leg-insert`.
1035
+ const joinLegInserts = [];
1036
+ for (const branch of analysis.branches) {
1037
+ if (!activeSides.has(branch.side))
1038
+ continue;
1039
+ if (branch.isNested) {
1040
+ // Routing a VALUES row into a multi-leaf subtree operand has no single deterministic
1041
+ // target leaf (product-coordinate addressing) — deferred to `set-op-membership-nested`.
1042
+ // A leaf-only active side still inserts normally.
1043
+ raiseMutationDiagnostic({
1044
+ reason: 'unsupported-set-op',
1045
+ table: view.name,
1046
+ message: `cannot insert through view '${view.name}': the active routing flag targets a multi-leaf subtree operand, which has no single deterministic target leaf for a VALUES row (product-coordinate addressing) — deferred to set-op-membership-nested`,
1047
+ });
1048
+ }
1049
+ const values = stmt.source.values.map(row => layout.dataPositions.map(p => cloneExpr(row[p])));
1050
+ const source = { type: 'values', values };
1051
+ const insertStmt = {
1052
+ type: 'insert',
1053
+ table: { type: 'identifier', name: branch.view.name },
1054
+ columns: [...branch.dataColNames],
1055
+ source,
1056
+ onConflict: stmt.onConflict,
1057
+ contextValues: stmt.contextValues,
1058
+ schemaPath: stmt.schemaPath,
1059
+ loc: stmt.loc,
1060
+ };
1061
+ // The VALUES source is self-contained (no outer capture); the nested envelope sources
1062
+ // its values straight from it. A single-source branch lowers through `propagate`.
1063
+ if (branch.isMultiSource) {
1064
+ joinLegInserts.push({ view: branch.view, stmt: insertStmt });
1065
+ }
1066
+ else {
1067
+ baseOps.push(...propagate(ctx, branch.view, { op: 'insert', stmt: insertStmt }));
1068
+ }
1069
+ }
1070
+ // Insert-through reads no existing row, so it needs no capture (self-contained values).
1071
+ return { baseOps, joinLegInserts: joinLegInserts.length > 0 ? joinLegInserts : undefined };
1072
+ }
1073
+ /** Map the insert's column list (explicit or positional) onto data columns + membership flags. */
1074
+ function resolveInsertLayout(view, analysis, stmt) {
1075
+ const flagNames = new Set(analysis.flags.map(f => f.name.toLowerCase()));
1076
+ const dataNames = new Set(analysis.dataColNames.map(n => n.toLowerCase()));
1077
+ const cols = stmt.columns && stmt.columns.length > 0 ? stmt.columns : analysis.viewColNames;
1078
+ const dataByName = new Map(); // data col name → VALUES position
1079
+ const flagPositions = new Map();
1080
+ cols.forEach((rawName, pos) => {
1081
+ const name = rawName.toLowerCase();
1082
+ if (flagNames.has(name)) {
1083
+ flagPositions.set(name, pos);
1084
+ }
1085
+ else if (dataNames.has(name)) {
1086
+ dataByName.set(name, pos);
1087
+ }
1088
+ else {
1089
+ raiseMutationDiagnostic({
1090
+ reason: 'unknown-view-column',
1091
+ column: rawName,
1092
+ table: view.name,
1093
+ message: `cannot insert through view '${view.name}': '${rawName}' is not a data or membership column of the set operation`,
1094
+ suggestion: `view '${view.name}' exposes: ${analysis.viewColNames.join(', ')}.`,
1095
+ });
1096
+ }
1097
+ });
1098
+ // Data columns must be supplied in full (the branches need every data column to build
1099
+ // a row); a missing one is left to the branch's own NOT NULL / default handling only
1100
+ // when truly omittable — v1 requires the data columns be supplied for an insert-through.
1101
+ const dataPositions = analysis.dataColNames.map(name => {
1102
+ const pos = dataByName.get(name.toLowerCase());
1103
+ if (pos === undefined) {
1104
+ raiseMutationDiagnostic({
1105
+ reason: 'no-default',
1106
+ column: name,
1107
+ table: view.name,
1108
+ message: `cannot insert through view '${view.name}': data column '${name}' is not supplied; a set-operation insert-through requires every data column`,
1109
+ });
1110
+ }
1111
+ return pos;
1112
+ });
1113
+ return { dataPositions, flagPositions };
1114
+ }
1115
+ // --- helpers --------------------------------------------------------------
1116
+ /**
1117
+ * The correlated `exists (select 1 from __vmupd_keys k where <k matches the branch row>)`
1118
+ * a fan-out / membership-delete routes on. The match is a NULL-safe full-data-tuple
1119
+ * comparison: each data column is `k.c = b.c or (k.c is null and b.c is null)` (set
1120
+ * operations treat `NULL = NULL` as equal; the engine has no `IS NOT DISTINCT FROM`).
1121
+ * The branch columns are qualified with the synthetic branch-view name so {@link propagate}
1122
+ * lowers them to the target row (`__vm_self`); the `k.*` columns resolve to the injected
1123
+ * `__vmupd_keys` relation.
1124
+ *
1125
+ * In the nested fan-out the OUTER `analysis` is threaded unchanged, so `k.*` keeps naming the
1126
+ * one outer capture's data columns while `branch.*` names the inner leaf's — sound because a
1127
+ * subtree preserves the data columns at every depth (`buildBranch`'s arity check guarantees
1128
+ * `branch.dataColNames.length === analysis.dataColCount`).
1129
+ *
1130
+ * **Membership gate** (`set-op-membership-nested-except`). For a **union / union all** subtree
1131
+ * a leaf's rows ⊆ the subtree's, so the frozen capture selects exactly the leaf rows to touch
1132
+ * and no extra conjunct is needed (`gateFlags` empty). For an **`except` / `intersect`** subtree
1133
+ * a leaf can hold rows the subtree EXCLUDES; if an outer operand makes such a row visible it
1134
+ * enters the capture, and a blind fan-out would touch it in the leaves even though it is NOT a
1135
+ * subtree member. To stay sound, each non-union boundary descended contributes its captured
1136
+ * **subtree-membership boundary flag** (the `exists <branch> as <flag>` the OUTER compound
1137
+ * declared for that side, a view output column present in `__vmupd_keys`); `gateFlags` AND-s a
1138
+ * fresh `k.<flag>` per accumulated boundary into the predicate, restricting the fan to genuine
1139
+ * members. Fresh `ColumnExpr` nodes are built per call because the gate is reused across leaves.
1140
+ */
1141
+ function buildMemberExists(analysis, branch, gateFlags = []) {
1142
+ let pred;
1143
+ for (let i = 0; i < analysis.dataColCount; i++) {
1144
+ const colMatch = nullSafeEqual({ type: 'column', name: analysis.dataColNames[i], table: 'k' }, { type: 'column', name: branch.dataColNames[i], table: branch.view.name });
1145
+ pred = pred ? { type: 'binary', operator: 'AND', left: pred, right: colMatch } : colMatch;
1146
+ }
1147
+ // AND the accumulated subtree-membership boundary flags in. `dataColCount > 0`
1148
+ // (checked in `analyzeSetOpView`) guarantees `pred` is defined here.
1149
+ for (const flag of gateFlags) {
1150
+ const flagRef = { type: 'column', name: flag, table: 'k' };
1151
+ pred = pred ? { type: 'binary', operator: 'AND', left: pred, right: flagRef } : flagRef;
1152
+ }
1153
+ return {
1154
+ type: 'exists',
1155
+ subquery: {
1156
+ type: 'select',
1157
+ columns: [{ type: 'column', expr: { type: 'literal', value: 1 } }],
1158
+ from: [{ type: 'table', table: { type: 'identifier', name: MS_UPDATE_KEYS_CTE }, alias: 'k' }],
1159
+ where: pred,
1160
+ },
1161
+ };
1162
+ }
1163
+ /** `a = b or (a is null and b is null)` — null-safe equality built from primitives. */
1164
+ function nullSafeEqual(a, b) {
1165
+ const eq = { type: 'binary', operator: '=', left: { ...a }, right: { ...b } };
1166
+ const bothNull = {
1167
+ type: 'binary',
1168
+ operator: 'AND',
1169
+ left: { type: 'unary', operator: 'IS NULL', expr: { ...a } },
1170
+ right: { type: 'unary', operator: 'IS NULL', expr: { ...b } },
1171
+ };
1172
+ return { type: 'binary', operator: 'OR', left: eq, right: bothNull };
1173
+ }
1174
+ /**
1175
+ * Rewrite a composed data-assignment value (in view / data-column terms) so its column
1176
+ * references read the captured row (`k.<col>`) — the membership-INSERT projection runs
1177
+ * over `__vmupd_keys`, not the branch. A reference to a membership flag, or to a name
1178
+ * that is neither a data column, is rejected (a data value cannot read a routing flag).
1179
+ */
1180
+ function qualifyDataRefsWithCapture(view, analysis, value) {
1181
+ const dataNames = new Set(analysis.dataColNames.map(n => n.toLowerCase()));
1182
+ return transformExpr(value, (col) => {
1183
+ if (col.table)
1184
+ return undefined; // already qualified (e.g. a correlated outer ref) — leave it
1185
+ if (dataNames.has(col.name.toLowerCase()))
1186
+ return { type: 'column', name: col.name, table: 'k' };
1187
+ raiseMutationDiagnostic({
1188
+ reason: 'unsupported-set-op',
1189
+ column: col.name,
1190
+ table: view.name,
1191
+ message: `cannot write through view '${view.name}': the membership-insert value references '${col.name}', which is not a data column; a data value cannot read a membership flag`,
1192
+ });
1193
+ });
1194
+ }
1195
+ /**
1196
+ * The uniform boolean directive a membership flag supplies on an insert-through — `true`
1197
+ * activates its branch, `false` omits it. Must be the SAME boolean literal across every
1198
+ * inserted row (a per-row branch on the value is deferred — `set-op-membership-nested`).
1199
+ */
1200
+ function uniformBooleanFlag(view, rows, position, flagName) {
1201
+ let flag;
1202
+ for (const row of rows) {
1203
+ const cell = row[position];
1204
+ const b = cell ? asBooleanLiteral(cell) : undefined;
1205
+ if (b === undefined) {
1206
+ raiseMutationDiagnostic({
1207
+ reason: 'unsupported-set-op',
1208
+ column: flagName,
1209
+ table: view.name,
1210
+ message: `cannot insert through view '${view.name}': the membership flag '${flagName}' must be a boolean literal (true/false); a non-literal per-row directive is deferred`,
1211
+ });
1212
+ }
1213
+ if (flag === undefined)
1214
+ flag = b;
1215
+ else if (flag !== b) {
1216
+ raiseMutationDiagnostic({
1217
+ reason: 'unsupported-set-op',
1218
+ column: flagName,
1219
+ table: view.name,
1220
+ message: `cannot insert through view '${view.name}': the membership flag '${flagName}' must be uniform across the inserted rows (a per-row mix of true/false is deferred)`,
1221
+ });
1222
+ }
1223
+ }
1224
+ return flag ?? false;
1225
+ }
1226
+ /**
1227
+ * The boolean value of a literal membership assignment (`true`/`false`, or the numeric
1228
+ * `1`/`0` spellings), or `undefined` for any non-literal / non-boolean value (a per-row
1229
+ * branch on the written value is deferred). Mirrors the join-existence write's gate.
1230
+ */
1231
+ function asBooleanLiteral(expr) {
1232
+ if (expr.type !== 'literal')
1233
+ return undefined;
1234
+ const v = expr.value;
1235
+ if (v === true || v === false)
1236
+ return v;
1237
+ if (v === 1 || v === 1n)
1238
+ return true;
1239
+ if (v === 0 || v === 0n)
1240
+ return false;
1241
+ return undefined;
1242
+ }
1243
+ /** RETURNING through a set-op membership write is not yet recoverable — reject. */
1244
+ function rejectReturning(view, returning) {
1245
+ if (returning && returning.length > 0) {
1246
+ raiseMutationDiagnostic({
1247
+ reason: 'returning-through-view',
1248
+ table: view.name,
1249
+ message: `cannot write through view '${view.name}': RETURNING is not yet supported on a set-operation membership write (the per-branch fan-out yields no single recoverable view row)`,
1250
+ });
1251
+ }
1252
+ }
1253
+ /** Peel Cast/Collate wrappers to expose an underlying AST literal value, else `undefined`. */
1254
+ function peelToLiteral(expr) {
1255
+ let e = expr;
1256
+ while (e.type === 'cast' || e.type === 'collate')
1257
+ e = e.expr;
1258
+ if (e.type !== 'literal')
1259
+ return undefined;
1260
+ const v = e.value;
1261
+ if (v instanceof Promise)
1262
+ return undefined;
1263
+ return v;
1264
+ }
1265
+ /** Classify one leg result column (the shadow of {@link tryBranchColumnNames}, admitting literals). */
1266
+ function legColumnKind(rc) {
1267
+ if (rc.type === 'all')
1268
+ return null;
1269
+ if (rc.expr.type === 'column')
1270
+ return { kind: 'column', name: rc.alias ?? rc.expr.name };
1271
+ const lit = peelToLiteral(rc.expr);
1272
+ if (lit !== undefined)
1273
+ return { kind: 'literal', value: lit };
1274
+ return null; // a computed (non-literal) projection — not a writable / discriminator shape
1275
+ }
1276
+ /** Strip a leg's own ORDER BY / LIMIT / OFFSET (those belong to the outer compound), keeping `compound`. */
1277
+ function stripLegModifiers(sel) {
1278
+ const { orderBy: _o, limit: _l, offset: _f, ...core } = sel;
1279
+ return core;
1280
+ }
1281
+ /** True iff a leg projects ≥1 literal discriminator (a routing constant). */
1282
+ function hasLiteralDiscriminator(leaf) {
1283
+ return leaf.columns.some(rc => legColumnKind(rc)?.kind === 'literal');
1284
+ }
1285
+ /** True iff a leaf SELECT is a writable flag-less leg: single-source, no compound, ≥1 plain/literal column, all admitted. */
1286
+ function isWritableLeafLeg(leaf) {
1287
+ if (leaf.compound)
1288
+ return false;
1289
+ // A multi-source (join) leg is writable for UPDATE / DELETE when it is an INNER join
1290
+ // (`set-op-write-multisource-leg-compose`, which builds an inner per-branch capture chained
1291
+ // off the outer set-op capture). `isInnerJoinBody` keys only on `joinType`, so a non-equi
1292
+ // (theta) INNER join is admitted here exactly as on the membership and standalone paths. An
1293
+ // OUTER (left/right/full) / cross join leg is deferred: falling to `false` here drops the
1294
+ // WHOLE body out of the flag-less route (via `flaglessShape`'s per-leg walk) → conservative
1295
+ // all-`NO` static surface + the single-source spine's clean `unsupported-set-op` reject.
1296
+ // (Comma joins are unreachable — the reader rejects multiple FROM sources at build time.)
1297
+ if (isJoinBody(leaf) && !isInnerJoinBody(leaf))
1298
+ return false;
1299
+ if (!leaf.columns || leaf.columns.length === 0)
1300
+ return false;
1301
+ return leaf.columns.every(rc => legColumnKind(rc) !== null);
1302
+ }
1303
+ /**
1304
+ * The flag-less writable shape of a set-op body, or `null` when it is not one. A pure AST
1305
+ * peek (no plan built), the write-side shadow of {@link isSetOpBranchWritable} that ADMITS
1306
+ * literal discriminators (which `tryBranchColumnNames` rejects). Returns `null` for any
1307
+ * existence flag anywhere (mutual exclusion with {@link isSetOpMembershipBody}), a `diff`
1308
+ * body, a non-SELECT operand, a `select *` / computed leg, or a shape v1 does not flatten:
1309
+ * - a **union-like** (`union` / `unionAll`) chain of any depth → N flat legs;
1310
+ * - a **binary** `intersect` / `except` (a single depth-1 compound) → 2 legs;
1311
+ * - anything else (a deep / mixed intersect/except chain) → `null` (kept on the existing reject).
1312
+ */
1313
+ function flaglessShape(sel) {
1314
+ if (!sel.compound || sel.compound.op === 'diff')
1315
+ return null;
1316
+ if (sel.compound.existence && sel.compound.existence.length > 0)
1317
+ return null;
1318
+ const topOp = sel.compound.op;
1319
+ const legs = [];
1320
+ let cur = sel;
1321
+ for (;;) {
1322
+ const leftLeg = unwrapBranchSelect(leftBranchSelect(cur));
1323
+ if (!isWritableLeafLeg(leftLeg))
1324
+ return null;
1325
+ legs.push(leftLeg);
1326
+ const right = cur.compound.select;
1327
+ if (right.type !== 'select')
1328
+ return null;
1329
+ const rightEff = unwrapBranchSelect(stripLegModifiers(right));
1330
+ if (!rightEff.compound) {
1331
+ if (!isWritableLeafLeg(rightEff))
1332
+ return null;
1333
+ legs.push(rightEff);
1334
+ // A union / union all needs ≥1 literal discriminator to route an insert honestly —
1335
+ // without one every leg is consistent with every row, so the routing is ambiguous
1336
+ // (the flag-less analog of the membership path's "a flag-less multi-branch insert is
1337
+ // ambiguous"). Such a body stays on the existing phase-1 reject. `intersect` / `except`
1338
+ // route by the OPERATOR (every leg / the left operand), so they need no discriminator.
1339
+ if (isUnionLikeSubtree(topOp) && !legs.some(hasLiteralDiscriminator))
1340
+ return null;
1341
+ return { op: topOp, legSelects: legs };
1342
+ }
1343
+ // The chain continues: only a uniform union-like chain may descend past depth 1; an
1344
+ // intersect / except is supported only as a single binary compound (its associativity
1345
+ // matters and is deferred for chains).
1346
+ if (rightEff.compound.op !== topOp || !isUnionLikeSubtree(topOp))
1347
+ return null;
1348
+ if (rightEff.compound.existence && rightEff.compound.existence.length > 0)
1349
+ return null;
1350
+ cur = rightEff;
1351
+ }
1352
+ }
1353
+ /**
1354
+ * True iff `selectAst` is a flag-less set-op body writable through predicate-honest branch
1355
+ * dispatch (`set-op-flagless-predicate-honest-writes`). Mutually exclusive with
1356
+ * {@link isSetOpMembershipBody} (any `exists … as <flag>` takes the membership path); a
1357
+ * non-decomposable shape (outer LIMIT/OFFSET, `select *` / computed leg, deep intersect/except)
1358
+ * returns `false` and keeps rejecting `unsupported-set-op` through the single-source spine.
1359
+ */
1360
+ export function isSetOpFlaglessWritableBody(selectAst) {
1361
+ if (selectAst.type !== 'select' || !selectAst.compound)
1362
+ return false;
1363
+ if (selectAst.limit || selectAst.offset)
1364
+ return false;
1365
+ return flaglessShape(selectAst) !== null;
1366
+ }
1367
+ /**
1368
+ * The flag-less view's **discriminator** column names — every data column projected as a
1369
+ * literal in ANY leg (read-only, `no-inverse` on UPDATE). The view's column names come from
1370
+ * the left-most leg (a set op takes its left child's names); a position is a discriminator
1371
+ * iff some leg pins it with a literal projection. Used by the `column_info` static surface
1372
+ * to report the discriminator `is_updatable = NO` (data / plain columns report YES).
1373
+ */
1374
+ export function flaglessDiscriminatorColumnNames(selectAst) {
1375
+ if (selectAst.type !== 'select')
1376
+ return [];
1377
+ const shape = flaglessShape(selectAst);
1378
+ if (!shape)
1379
+ return [];
1380
+ const first = shape.legSelects[0];
1381
+ const out = [];
1382
+ for (let i = 0; i < first.columns.length; i++) {
1383
+ const anyLiteral = shape.legSelects.some(leg => legColumnKind(leg.columns[i])?.kind === 'literal');
1384
+ if (!anyLiteral)
1385
+ continue;
1386
+ const rc = first.columns[i];
1387
+ const name = rc.type === 'column' ? (rc.alias ?? (rc.expr.type === 'column' ? rc.expr.name : undefined)) : undefined;
1388
+ if (name)
1389
+ out.push(name);
1390
+ }
1391
+ return out;
1392
+ }
1393
+ /** Decompose a flag-less set-op view mutation. Throws a structured diagnostic for unsupported shapes. */
1394
+ export function buildFlaglessSetOpWrite(ctx, view, req) {
1395
+ const { analysis, legs } = analyzeFlaglessSetOpView(ctx, view);
1396
+ switch (req.op) {
1397
+ case 'insert': return buildFlaglessInsert(ctx, view, analysis, legs, req.stmt);
1398
+ case 'update': return buildFlaglessUpdate(ctx, view, analysis, legs, req.stmt);
1399
+ case 'delete': return buildFlaglessDelete(ctx, view, analysis, legs, req.stmt);
1400
+ }
1401
+ }
1402
+ function analyzeFlaglessSetOpView(ctx, view) {
1403
+ if (view.selectAst.type !== 'select' || !view.selectAst.compound) {
1404
+ raiseMutationDiagnostic({ reason: 'unsupported-set-op', table: view.name, message: `cannot write through view '${view.name}': not a set-operation body` });
1405
+ }
1406
+ const sel = view.selectAst;
1407
+ const shape = flaglessShape(sel);
1408
+ if (!shape) {
1409
+ raiseMutationDiagnostic({ reason: 'unsupported-set-op', table: view.name, message: `cannot write through view '${view.name}': not a flag-less predicate-honest writable set-operation body` });
1410
+ }
1411
+ // Plan the body ONCE: a flag-less body has no flag columns, so the root attributes ARE
1412
+ // the view's data columns (positionally aligned to every leg's projection).
1413
+ const root = buildSelectStmt(ctx, sel);
1414
+ if (!isRelationalNode(root)) {
1415
+ raiseMutationDiagnostic({ reason: 'no-base-lineage', table: view.name, message: `cannot write through view '${view.name}': the set-operation body did not produce a relation` });
1416
+ }
1417
+ const relRoot = root;
1418
+ const setOpNode = findSetOpNode(relRoot);
1419
+ if (!setOpNode) {
1420
+ raiseMutationDiagnostic({ reason: 'no-base-lineage', table: view.name, message: `cannot write through view '${view.name}': the set-operation body produced no SetOperationNode` });
1421
+ }
1422
+ const dataColCount = setOpNode.dataColumnCount();
1423
+ const attrs = relRoot.getAttributes();
1424
+ const dataColNames = attrs.map(a => a.name);
1425
+ const dataColTypes = attrs.map(a => a.type);
1426
+ // A scope resolving each view data-column name to its producing attribute over the planned
1427
+ // root — reused by `buildSetOpCapture` for the user-predicate / capture projections.
1428
+ const viewColScope = new RegisteredScope(ctx.scope);
1429
+ attrs.forEach((attr, i) => {
1430
+ viewColScope.registerSymbol(attr.name.toLowerCase(), (exp, s) => new ColumnReferenceNode(s, exp, attr.type, attr.id, i));
1431
+ });
1432
+ const legs = shape.legSelects.map((legSel, i) => buildFlaglessLeg(ctx, view, legSel, i, dataColNames));
1433
+ // A `SetOpAnalysis`-shaped carrier so the membership fan helpers / capture builder are
1434
+ // reused verbatim. `branches` is a 2-tuple by type; the flag-less fan iterates `legs`
1435
+ // (which may be > 2) and never reads `branches`, so the first two legs satisfy the type.
1436
+ const branches = [legs[0].branch, legs[1].branch];
1437
+ const analysis = {
1438
+ op: shape.op, root: relRoot, viewColScope,
1439
+ viewColNames: dataColNames, viewColTypes: dataColTypes,
1440
+ dataColCount, dataColNames, surfacedInnerFlagNames: [], flags: [], branches,
1441
+ };
1442
+ return { analysis, legs };
1443
+ }
1444
+ /**
1445
+ * Build one flag-less leg: a synthetic branch view-like (its columns re-aliased to the view
1446
+ * data-column names so the member-exists correlation and the base ops align positionally),
1447
+ * plus the plan-time routing oracle inputs (the leg's planned σ-forwarded bindings + the
1448
+ * synthesized literal-discriminator bindings).
1449
+ */
1450
+ function buildFlaglessLeg(ctx, view, legSel, index, dataColNames) {
1451
+ const dataColCount = dataColNames.length;
1452
+ const kinds = legSel.columns.map(legColumnKind);
1453
+ if (kinds.length !== dataColCount || kinds.some(k => k === null)) {
1454
+ raiseMutationDiagnostic({
1455
+ reason: 'unsupported-set-op', table: view.name,
1456
+ message: `cannot write through view '${view.name}': a leg projects ${kinds.length} columns (expected ${dataColCount}) or a non-writable (\`select *\` / computed) column`,
1457
+ });
1458
+ }
1459
+ // Re-alias every leg column to the view data-column name at its position, so the synthetic
1460
+ // branch view exposes exactly the view's data columns (a plain base column round-trips
1461
+ // through `propagate`; a literal discriminator resolves to its constant). This makes the
1462
+ // member-exists `b.<col>` references and the base ops align regardless of the leg's own
1463
+ // aliases / base-column names.
1464
+ const aliasedColumns = legSel.columns.map((rc, i) => ({
1465
+ type: 'column', expr: rc.expr, alias: dataColNames[i],
1466
+ }));
1467
+ const effectiveSelect = { ...legSel, columns: aliasedColumns };
1468
+ const branchView = { name: `__setop_leg${index}`, schemaName: view.schemaName, selectAst: effectiveSelect };
1469
+ // A flag-less join leg is always an INNER join here: `isWritableLeafLeg` (the recognizer
1470
+ // `flaglessShape` gated on) admits only single-source or INNER-join legs, so an outer / cross
1471
+ // leg never reaches this builder (the body falls out of the flag-less route).
1472
+ const branch = { side: index === 0 ? 'left' : 'right', view: branchView, dataColNames: [...dataColNames], isNested: false, isMultiSource: isJoinBody(legSel) };
1473
+ const plainPositions = kinds.flatMap((k, i) => k.kind === 'column' ? [i] : []);
1474
+ const discriminatorPositions = kinds.flatMap((k, i) => k.kind === 'literal' ? [i] : []);
1475
+ // Plan the leg body for the oracle: its output attributes carry the σ-forwarded constant
1476
+ // bindings / domains (the pre-existing half — a `where color='red'` over a `color`-projecting
1477
+ // leg forwards `∅ → color='red'` to the output column). The synthesized discriminator
1478
+ // bindings (Option B) close the projected-literal gap.
1479
+ const planned = buildSelectStmt(ctx, effectiveSelect);
1480
+ if (!isRelationalNode(planned)) {
1481
+ raiseMutationDiagnostic({ reason: 'no-base-lineage', table: view.name, message: `cannot write through view '${view.name}': a leg did not produce a relation` });
1482
+ }
1483
+ const legRoot = planned;
1484
+ const legAttrs = legRoot.getAttributes();
1485
+ const attrIdToIndex = new Map();
1486
+ legAttrs.forEach((a, i) => attrIdToIndex.set(a.id, i));
1487
+ const scope = new RegisteredScope(ctx.scope);
1488
+ legAttrs.forEach((attr, i) => {
1489
+ scope.registerSymbol(dataColNames[i].toLowerCase(), (exp, s) => new ColumnReferenceNode(s, exp, attr.type, attr.id, i));
1490
+ });
1491
+ const discriminatorBindings = [];
1492
+ kinds.forEach((k, i) => {
1493
+ if (k.kind === 'literal' && k.value !== null) {
1494
+ discriminatorBindings.push({ attrs: [i], value: { kind: 'literal', value: k.value } });
1495
+ }
1496
+ });
1497
+ const physical = legRoot.physical;
1498
+ const bindings = [...(physical.constantBindings ?? []), ...discriminatorBindings];
1499
+ const domains = physical.domainConstraints ?? [];
1500
+ const sigmaConjuncts = collectFilterConjuncts(legRoot);
1501
+ return {
1502
+ branch, plainPositions, discriminatorPositions, scope, bindings, domains, sigmaConjuncts,
1503
+ attrIndex: (attrId) => attrIdToIndex.get(attrId),
1504
+ getCollation: (col) => legAttrs[col]?.type.collationName,
1505
+ };
1506
+ }
1507
+ /**
1508
+ * Collect every leg-body σ predicate as a flat conjunct list: each `FilterNode.predicate` in
1509
+ * the leg's relational subtree (walked via `getRelations()`) split into its AND-conjuncts. Every
1510
+ * collected conjunct is a fact that must hold for a row resident in this leg, so feeding them all
1511
+ * to the oracle can only ever push the verdict toward `unsat` on a real contradiction (the checker
1512
+ * never emits a false `unsat`). A conjunct referencing a column this leg does not project resolves
1513
+ * to `attrIndex → undefined` and contributes nothing.
1514
+ */
1515
+ function collectFilterConjuncts(root) {
1516
+ const out = [];
1517
+ const visit = (node) => {
1518
+ if (node instanceof FilterNode)
1519
+ out.push(...splitConjuncts(node.predicate));
1520
+ for (const child of node.getRelations())
1521
+ visit(child);
1522
+ };
1523
+ visit(root);
1524
+ return out;
1525
+ }
1526
+ /**
1527
+ * The per-leg branch oracle (`set-op-flagless-predicate-honest-writes`): is a row satisfying
1528
+ * `predicate` possible in this leg? Feeds the mutation predicate (in view data-column terms,
1529
+ * resolved against the leg's planned attributes) as conjuncts, alongside the leg's σ-forwarded
1530
+ * + literal-discriminator bindings, into {@link checkSatisfiability}. `unsat` ⇒ skip the leg;
1531
+ * `sat` / `unknown` ⇒ include it (the checker never emits a false `unsat`). A `predicate` of
1532
+ * `undefined` (no WHERE / no supplied values) is unconstrained ⇒ `sat`.
1533
+ */
1534
+ function legConsistency(ctx, leg, predicate) {
1535
+ let conjuncts = [];
1536
+ if (predicate) {
1537
+ const node = buildExpression({ ...ctx, scope: leg.scope }, cloneExpr(predicate));
1538
+ conjuncts = splitConjuncts(node);
1539
+ }
1540
+ // Fold the leg's own σ conjuncts in alongside the mutation predicate: a range σ on a
1541
+ // projected column (`where x < 5`) is invisible to the bindings/domains the leg forwards,
1542
+ // so a supplied value that provably violates it (`x = 7`) only contradicts here ⇒ `unsat`
1543
+ // ⇒ skip the leg (no phantom base row). Non-projected-column conjuncts are inert.
1544
+ return checkSatisfiability([...conjuncts, ...leg.sigmaConjuncts], leg.domains, leg.bindings, leg.attrIndex, leg.getCollation);
1545
+ }
1546
+ function resolveFlaglessInsertLayout(view, analysis, stmt) {
1547
+ const cols = stmt.columns && stmt.columns.length > 0 ? stmt.columns : analysis.dataColNames;
1548
+ const map = new Map();
1549
+ cols.forEach((rawName, vi) => {
1550
+ const pos = analysis.dataColNames.findIndex(n => n.toLowerCase() === rawName.toLowerCase());
1551
+ if (pos < 0) {
1552
+ raiseMutationDiagnostic({
1553
+ reason: 'unknown-view-column', column: rawName, table: view.name,
1554
+ message: `cannot insert through view '${view.name}': '${rawName}' is not a column of the set operation`,
1555
+ suggestion: `view '${view.name}' exposes: ${analysis.dataColNames.join(', ')}.`,
1556
+ });
1557
+ }
1558
+ map.set(pos, vi);
1559
+ });
1560
+ return { valueIndexByDataPos: map };
1561
+ }
1562
+ /** The existence predicate of one VALUES row: `∧ <dataCol> = <suppliedValue>` over the supplied columns. */
1563
+ function rowExistencePredicate(analysis, layout, row) {
1564
+ let pred;
1565
+ for (const [pos, vi] of layout.valueIndexByDataPos) {
1566
+ const eq = {
1567
+ type: 'binary', operator: '=',
1568
+ left: { type: 'column', name: analysis.dataColNames[pos] },
1569
+ right: row[vi],
1570
+ };
1571
+ pred = pred ? { type: 'binary', operator: 'AND', left: pred, right: eq } : eq;
1572
+ }
1573
+ return pred;
1574
+ }
1575
+ /** The legs a flag-less INSERT routes into by operator: `except` inserts the left operand only. */
1576
+ function fanLegsForInsert(op, legs) {
1577
+ return op === 'except' ? [legs[0]] : legs;
1578
+ }
1579
+ function buildFlaglessInsert(ctx, view, analysis, legs, stmt) {
1580
+ rejectReturning(view, stmt.returning);
1581
+ if (stmt.source.type !== 'values') {
1582
+ raiseMutationDiagnostic({
1583
+ reason: 'unsupported-source', table: view.name,
1584
+ message: `cannot insert through view '${view.name}': a flag-less set-operation insert routes by the supplied discriminator values, so it requires a VALUES source (a SELECT/DML source's per-row routing is deferred)`,
1585
+ });
1586
+ }
1587
+ const values = stmt.source.values;
1588
+ const layout = resolveFlaglessInsertLayout(view, analysis, stmt);
1589
+ const baseOps = [];
1590
+ // A multi-source (INNER join) leg's INSERT is built via the plan-level shared-surrogate
1591
+ // envelope (`buildMultiSourceInsert`) and spliced as a nested `ViewMutationNode` child rather
1592
+ // than routed through `propagate` (which would raise the internal `unsupported-multisource-insert`)
1593
+ // — `set-op-write-multisource-leg-insert`. The VALUES source is self-contained.
1594
+ const joinLegInserts = [];
1595
+ for (const leg of fanLegsForInsert(analysis.op, legs)) {
1596
+ // Per-row routing: a row inserts into this leg unless provably inconsistent with it.
1597
+ const rows = values.filter(row => legConsistency(ctx, leg, rowExistencePredicate(analysis, layout, row)) !== 'unsat');
1598
+ if (rows.length === 0)
1599
+ continue;
1600
+ // Only the leg's PLAIN (writable) columns that are supplied flow into the base insert;
1601
+ // the literal discriminators are determined by the leg projection / σ (omitted base
1602
+ // columns are recovered by the leg's `where`-constant FD insert-defaulting).
1603
+ const supplied = leg.plainPositions.filter(pos => layout.valueIndexByDataPos.has(pos));
1604
+ if (supplied.length === 0)
1605
+ continue; // an all-literal / unsupplied leg has no base row to write
1606
+ const colNames = supplied.map(pos => analysis.dataColNames[pos]);
1607
+ const valueRows = rows.map(row => supplied.map(pos => cloneExpr(row[layout.valueIndexByDataPos.get(pos)])));
1608
+ const source = { type: 'values', values: valueRows };
1609
+ const insertStmt = {
1610
+ type: 'insert',
1611
+ table: { type: 'identifier', name: leg.branch.view.name },
1612
+ columns: colNames,
1613
+ source,
1614
+ onConflict: stmt.onConflict,
1615
+ contextValues: stmt.contextValues,
1616
+ schemaPath: stmt.schemaPath,
1617
+ loc: stmt.loc,
1618
+ };
1619
+ if (leg.branch.isMultiSource) {
1620
+ joinLegInserts.push({ view: leg.branch.view, stmt: insertStmt });
1621
+ }
1622
+ else {
1623
+ baseOps.push(...propagate(ctx, leg.branch.view, { op: 'insert', stmt: insertStmt }));
1624
+ }
1625
+ }
1626
+ if (baseOps.length === 0 && joinLegInserts.length === 0) {
1627
+ raiseMutationDiagnostic({
1628
+ reason: 'unsupported-set-op', table: view.name,
1629
+ message: `cannot insert through view '${view.name}': the supplied values are consistent with no writable leg (the row would belong to no branch)`,
1630
+ });
1631
+ }
1632
+ // Insert-through reads no existing row, so it needs no capture (self-contained values).
1633
+ return { baseOps, joinLegInserts: joinLegInserts.length > 0 ? joinLegInserts : undefined };
1634
+ }
1635
+ // --- flag-less DELETE / data-UPDATE (fan to the consistent legs) ----------
1636
+ /** The legs a flag-less DELETE / data-UPDATE fans to: every consistent leg (`except` fans the left only). */
1637
+ function fanLegsForFanOut(ctx, op, legs, where) {
1638
+ const consistent = legs.filter(l => legConsistency(ctx, l, where) !== 'unsat');
1639
+ if (op === 'except')
1640
+ return consistent.includes(legs[0]) ? [legs[0]] : [];
1641
+ return consistent;
1642
+ }
1643
+ function buildFlaglessDelete(ctx, view, analysis, legs, stmt) {
1644
+ rejectReturning(view, stmt.returning);
1645
+ const capture = buildSetOpCapture(ctx, analysis, stmt.where);
1646
+ const fan = makeJoinLegFan(capture);
1647
+ const baseOps = [];
1648
+ for (const leg of fanLegsForFanOut(ctx, analysis.op, legs, stmt.where)) {
1649
+ baseOps.push(...fanBranchDelete(ctx, view, analysis, leg.branch, stmt, fan));
1650
+ }
1651
+ return { baseOps, capture, nestedCaptures: fan.captures.length > 0 ? fan.captures : undefined };
1652
+ }
1653
+ function buildFlaglessUpdate(ctx, view, analysis, legs, stmt) {
1654
+ rejectReturning(view, stmt.returning);
1655
+ // A literal discriminator is read-only (it has no base inverse). Reject a write to one
1656
+ // up front with `no-inverse` (Finding 5) — NOT silently routed.
1657
+ const discriminators = new Set();
1658
+ for (const leg of legs)
1659
+ for (const p of leg.discriminatorPositions)
1660
+ discriminators.add(p);
1661
+ const dataAssignments = [];
1662
+ for (const asg of stmt.assignments) {
1663
+ const position = analysis.dataColNames.findIndex(n => n.toLowerCase() === asg.column.toLowerCase());
1664
+ if (position < 0) {
1665
+ raiseMutationDiagnostic({
1666
+ reason: 'unknown-view-column', column: asg.column, table: view.name,
1667
+ message: `cannot write through view '${view.name}': '${asg.column}' is not a column of the set operation`,
1668
+ suggestion: `view '${view.name}' exposes: ${analysis.dataColNames.join(', ')}.`,
1669
+ });
1670
+ }
1671
+ if (discriminators.has(position)) {
1672
+ raiseMutationDiagnostic({
1673
+ reason: 'no-inverse', column: asg.column, table: view.name,
1674
+ message: `cannot write through view '${view.name}': '${asg.column}' is a literal discriminator column (it routes rows to a branch and has no base-column inverse) — it is read-only`,
1675
+ });
1676
+ }
1677
+ dataAssignments.push({ position, value: asg.value });
1678
+ }
1679
+ const capture = buildSetOpCapture(ctx, analysis, stmt.where);
1680
+ const fan = makeJoinLegFan(capture);
1681
+ const baseOps = [];
1682
+ for (const leg of fanLegsForFanOut(ctx, analysis.op, legs, stmt.where)) {
1683
+ baseOps.push(...fanBranchDataUpdate(ctx, view, analysis, leg.branch, dataAssignments, stmt, fan));
1684
+ }
1685
+ return { baseOps, capture, nestedCaptures: fan.captures.length > 0 ? fan.captures : undefined };
1686
+ }
1687
+ //# sourceMappingURL=set-op.js.map