@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,1594 @@
1
+ import { columnsFormDeclaredKey } from './table.js';
2
+ import { QuereusError } from '../common/errors.js';
3
+ import { StatusCode } from '../common/types.js';
4
+ import { astToString } from '../emit/ast-stringify.js';
5
+ import { buildLogicalConstraints } from './lens.js';
6
+ import { computeSchemaHash } from './schema-hasher.js';
7
+ import { validateReservedTags } from './reserved-tags.js';
8
+ import { raiseReservedTagDiagnostics } from './reserved-tags-policy.js';
9
+ import { proveLens, collectColumnRefNames } from './lens-prover.js';
10
+ import { applyAckGovernance, resolveEscalationPolicy } from './lens-ack.js';
11
+ import { createLogger } from '../common/logger.js';
12
+ import { addInd, MAX_INDS_PER_NODE } from '../planner/util/fd-utils.js';
13
+ const log = createLogger('schema:lens-compiler');
14
+ /**
15
+ * Lens compiler — the `apply schema X` step for a **logical** schema.
16
+ *
17
+ * For each declared logical table it: builds the logical spec, aligns it against
18
+ * the basis schema (the default name-based aligner), produces the inlined
19
+ * effective view body, populates the lens slot, and registers the body as an
20
+ * ordinary `ViewSchema`. The query processor then sees a view; reads ride the
21
+ * standard view-resolution path and writes ride view-updateability.
22
+ *
23
+ * v1 is **single-source, name-based** (see `docs/lens.md` § The Default Mapper):
24
+ * a logical table maps to a name-matching basis table, and each logical column
25
+ * to a name-matching basis column. Type/nullability conformance and the n-way
26
+ * decomposition shape are deferred to the prover / decomposition tickets.
27
+ */
28
+ /**
29
+ * Deploys (or re-deploys) a logical schema's lens slots + compiled view bodies.
30
+ *
31
+ * Re-deploy semantics are **clear-and-rebuild**: every existing lens view + slot
32
+ * in the logical schema is dropped, then rebuilt from the current declaration.
33
+ * This is how asymmetric removal falls out for free — a logical table dropped
34
+ * from the declaration is simply not rebuilt (its view + slot vanish), and the
35
+ * basis is never touched (logical removals never cascade to basis storage; see
36
+ * `docs/lens.md` § Deployment).
37
+ */
38
+ export function deployLogicalSchema(db, declaredSchema, logicalSchemaName) {
39
+ validateLogicalDeclaration(declaredSchema, logicalSchemaName);
40
+ const schemaManager = db.schemaManager;
41
+ const logicalSchema = schemaManager.getSchemaOrFail(logicalSchemaName);
42
+ // The lens block (explicit `over Y` basis + sparse overrides), if declared.
43
+ // Re-read from source on every deploy — that is what lets a rename and a
44
+ // later column-add compose without attribute-ID plumbing (docs/lens.md § D2).
45
+ const lensDecl = db.declaredSchemaManager.getLensDeclaration(logicalSchemaName);
46
+ const overridesByTable = indexOverrides(lensDecl, declaredSchema, logicalSchemaName);
47
+ // Infer the basis lazily, only when there is ≥1 logical table to align. An
48
+ // empty logical declaration (e.g. re-applying X after all its tables are
49
+ // removed) is a pure detach-everything operation and must NOT fail on basis
50
+ // resolution — removal never depends on the basis (asymmetric removal).
51
+ let basis;
52
+ const resolveBasis = () => {
53
+ if (lensDecl) {
54
+ // Explicit `over Y` binding resolves the foundation's default-basis
55
+ // ambiguity (docs/lens.md § D4); `inferDefaultBasis` is NOT consulted.
56
+ const basisSchema = schemaManager.getSchema(lensDecl.basisSchema);
57
+ if (!basisSchema) {
58
+ throw new QuereusError(`lens: basis schema '${lensDecl.basisSchema}' for logical schema '${logicalSchemaName}' does not exist (declared via 'declare lens for ${logicalSchemaName} over ${lensDecl.basisSchema}')`, StatusCode.ERROR);
59
+ }
60
+ return { schema: basisSchema, schemaName: basisSchema.name };
61
+ }
62
+ return inferDefaultBasis(schemaManager, logicalSchemaName);
63
+ };
64
+ // Compile everything FIRST (basis alignment can throw — name mismatch, etc.).
65
+ // Only after every table aligns successfully do we mutate the catalog, so a
66
+ // failed re-apply leaves the existing lens state untouched (atomic deploy).
67
+ // Module mapping advertisements are collected once per deploy (the first time a
68
+ // basis is resolved) and filtered per logical table by the resolver.
69
+ let allAdvertisements;
70
+ const compiled = [];
71
+ // Prover accumulators (docs/lens.md § Coverage checklist). Errors aggregate
72
+ // across every table and throw atomically below — before any catalog mutation,
73
+ // preserving the existing atomic-deploy property. Warnings flow to the report.
74
+ const proveErrors = [];
75
+ const proveWarnings = [];
76
+ const acknowledged = [];
77
+ const obligationsByTable = new Map();
78
+ for (const item of declaredSchema.items) {
79
+ if (item.type !== 'declaredTable')
80
+ continue;
81
+ basis ??= resolveBasis();
82
+ allAdvertisements ??= collectAdvertisements(db, basis.schema);
83
+ const logicalTable = schemaManager.buildLogicalTableSchema(item.tableStmt, logicalSchema.name);
84
+ const override = overridesByTable.get(logicalTable.name.toLowerCase());
85
+ // Resolve (collect → select primary → validate) the advertisement BEFORE body
86
+ // compilation, so a malformed advertisement aborts the deploy atomically. The
87
+ // resolved advertisement is **stored** on the slot, not yet synthesized — the
88
+ // v1 name-match / override body producer below is unchanged by this ticket;
89
+ // `lens-multi-source-decomposition` reads the slot to build the n-way body.
90
+ const { advertisement, auxiliaryAccess } = resolveAdvertisement(allAdvertisements, logicalTable, basis, db, logicalSchemaName, override !== undefined);
91
+ let compiledBody;
92
+ let provenance;
93
+ let effectiveColumns;
94
+ // Whether the body came from `compileDecompositionBody` (the synthesized
95
+ // n-way decomposition). Only that body actually carries the advertised
96
+ // `anchor ⋈ member` joins the existence-anchor IND describes, so it is the
97
+ // only body the IND injection may attach to (R2 gate below).
98
+ let fromDecomposition = false;
99
+ if (override) {
100
+ const merged = compileOverrideBody(logicalTable, logicalSchemaName, basis.schemaName, schemaManager, override, advertisement);
101
+ compiledBody = merged.body;
102
+ provenance = merged.provenance;
103
+ effectiveColumns = merged.effectiveColumns;
104
+ // Override ⊕ advertisement composition (docs/lens.md § Override-vs-advertisement):
105
+ // a *sparse* override (one that relies on gap-fill) must not re-anchor or
106
+ // reference relations outside the advertised decomposition. A *full*
107
+ // hand-authored override (no gap-fill) bypasses the advertisement and is
108
+ // not conflict-checked. Gap-fill *execution* from the advertisement lands
109
+ // in `lens-multi-source-decomposition`; this ticket validates the conflict.
110
+ if (advertisement && provenance.some(p => p.source === 'default')) {
111
+ validateOverrideAdvertisementConflict(advertisement, override, basis.schemaName, schemaManager, logicalSchemaName, logicalTable.name);
112
+ }
113
+ }
114
+ else if (advertisement) {
115
+ // A resolved primary-storage advertisement → synthesize the n-way `get`
116
+ // join body from the decomposition (docs/lens.md § The Default Mapper).
117
+ compiledBody = compileDecompositionBody(logicalTable, logicalSchemaName, basis, advertisement, db);
118
+ provenance = logicalTable.columns.map(c => ({ logicalColumn: c.name, source: 'default' }));
119
+ effectiveColumns = logicalTable.columns.map(c => c.name);
120
+ fromDecomposition = true;
121
+ }
122
+ else {
123
+ compiledBody = compileDefaultBody(logicalTable, logicalSchemaName, basis.schema, basis.schemaName);
124
+ provenance = logicalTable.columns.map(c => ({ logicalColumn: c.name, source: 'default' }));
125
+ effectiveColumns = logicalTable.columns.map(c => c.name);
126
+ }
127
+ // Annotate provenance with advertisement-backed member info (introspection).
128
+ if (advertisement)
129
+ annotateProvenanceWithAdvertisement(provenance, advertisement);
130
+ // Inject the existence-anchor IND surface from a primary-storage
131
+ // advertisement (lens-multi-source-ind-injection): one relation-IND per
132
+ // mandatory non-anchor member, threaded to the prover via the slot so the
133
+ // mandatory inner-joins are provably row-loss-free and the put fan-out is
134
+ // sound against a derived existence fact. See docs/lens.md § The module
135
+ // mapping advertisement and docs/optimizer.md § Inclusion Dependency Tracking.
136
+ //
137
+ // Gated (R2) to the synthesized-decomposition body: only
138
+ // `compileDecompositionBody` emits the advertised `anchor ⋈ member` joins the
139
+ // IND describes. A full hand-authored override and the single-source default
140
+ // body carry no such join, so injecting there would describe joins absent from
141
+ // `compiledBody` — leave `injectedInds` undefined. (The future sparse-override
142
+ // gap-fill body — `lens-multi-source-decomposition` — will carry the joins too;
143
+ // extend the gate to it then.)
144
+ const injectedInds = fromDecomposition
145
+ ? computeExistenceAnchorInds(advertisement, basis, db)
146
+ : [];
147
+ const slot = {
148
+ logicalTable,
149
+ defaultBasis: { schemaName: basis.schemaName },
150
+ override: override?.select,
151
+ compiledBody,
152
+ columnProvenance: provenance,
153
+ attachedConstraints: buildLogicalConstraints(logicalTable),
154
+ advertisement,
155
+ auxiliaryAccess,
156
+ injectedInds: injectedInds.length > 0 ? injectedInds : undefined,
157
+ };
158
+ // PoC: validate the reserved `quereus.*` tag namespace shape + site on the
159
+ // logical table and its constraints, INSIDE the compile-first loop so an
160
+ // invalid tag fails the deploy atomically (before any catalog mutation).
161
+ // Only shape/site is checked here — no reserved tag's semantics are read.
162
+ validateLensTags(slot);
163
+ // Prove the slot and classify its constraints (docs/lens.md § Coverage
164
+ // checklist). The verdict is recorded on the slot (obligations + readOnly)
165
+ // and its diagnostics are aggregated into the deploy report. Errors are
166
+ // thrown atomically after every table is proved (below), preserving the
167
+ // atomic-deploy contract; warnings never block.
168
+ const prove = proveLens(slot, db);
169
+ slot.obligations = prove.obligations;
170
+ slot.readOnly = prove.readOnly;
171
+ // Acknowledgment + escalation governance (docs/lens.md § Acknowledging
172
+ // advisories). Coded+sited advisories the prover emitted become
173
+ // acknowledgeable in source (the `quereus.lens.ack.<code>` tag, with a
174
+ // recorded fingerprint that re-surfaces them on material change), and the
175
+ // per-table escalation policy promotes specific codes to blocking errors.
176
+ // Escalation errors aggregate with the prover's and throw atomically below.
177
+ const governance = applyAckGovernance(slot, prove.warnings, resolveEscalationPolicy(logicalTable));
178
+ proveErrors.push(...prove.errors, ...governance.errors);
179
+ proveWarnings.push(...governance.warnings);
180
+ acknowledged.push(...governance.acknowledged);
181
+ obligationsByTable.set(logicalTable.name.toLowerCase(), prove.obligations);
182
+ const view = {
183
+ name: logicalTable.name,
184
+ schemaName: logicalSchema.name,
185
+ sql: astToString(compiledBody),
186
+ selectAst: compiledBody,
187
+ // Pin the consumer-facing column names to the *logical* declaration
188
+ // (the contract), independent of the basis
189
+ // column casing. Equivalent to `create view T(<logical cols>) as
190
+ // <body>`: `select * from X.T` then surfaces the logical names, not
191
+ // whatever the basis happens to spell them. Write-through is
192
+ // unaffected (positional passthrough).
193
+ columns: effectiveColumns,
194
+ tags: logicalTable.tags,
195
+ };
196
+ compiled.push({ slot, view });
197
+ }
198
+ // Atomic block: any prover error aborts the deploy BEFORE the catalog mutation
199
+ // below, so a failed re-apply leaves the prior lens state untouched. Every
200
+ // blocking diagnostic is listed (sited) in one error.
201
+ if (proveErrors.length > 0) {
202
+ throw new QuereusError(formatProveErrors(logicalSchemaName, proveErrors), StatusCode.ERROR);
203
+ }
204
+ // Clear-and-rebuild: drop all current lens views + slots, then register the
205
+ // freshly compiled set. A logical schema's views are exclusively lens bodies,
206
+ // so dropping every view is safe and implements detach for tables removed
207
+ // from the declaration (logical removals never touch basis storage).
208
+ for (const view of Array.from(logicalSchema.getAllViews())) {
209
+ logicalSchema.removeView(view.name);
210
+ }
211
+ logicalSchema.clearLensSlots();
212
+ for (const { slot, view } of compiled) {
213
+ logicalSchema.addLensSlot(slot);
214
+ logicalSchema.addView(view);
215
+ log('Deployed lens for %s.%s over %s', logicalSchemaName, slot.logicalTable.name, slot.defaultBasis.schemaName);
216
+ }
217
+ // The lens-slot set just changed (the only slot-mutating site that fires no
218
+ // `SchemaChangeEvent`), so the lens basis-FK gate is stale — reset it directly,
219
+ // sibling to the snapshot rotation. The next basis write rebuilds it and reflects
220
+ // any added / removed logical FK in this deploy.
221
+ schemaManager.invalidateLensFkGate();
222
+ // Capture + rotate the deployed-basis snapshot AFTER a successful catalog
223
+ // mutation, so an aborted re-apply leaves the prior snapshot untouched. The
224
+ // snapshot is the source of truth the `quereus_basis_backfill` differ reads
225
+ // (docs/lens.md § The deployed basis representation).
226
+ const snapshot = buildDeploymentSnapshot(db, compiled, basis, logicalSchemaName);
227
+ db.declaredSchemaManager.rotateDeployedLensSnapshot(logicalSchemaName, snapshot);
228
+ // Errors are already thrown above; a returned report carries only advisories.
229
+ // Persist it on the manager — the stable hook the sibling acknowledgment ticket
230
+ // reads to fingerprint / tally / expand advisories (docs/lens.md § Acknowledging
231
+ // advisories). `apply schema` returning these as result rows is deferred to that
232
+ // ticket (converting the universally-used void statement to relational is a
233
+ // separate, high-blast-radius change); the report is fully produced here.
234
+ const report = { errors: [], warnings: proveWarnings, acknowledged, obligationsByTable };
235
+ db.declaredSchemaManager.setDeployedLensReport(logicalSchemaName, report);
236
+ return report;
237
+ }
238
+ /**
239
+ * Formats the aggregated blocking diagnostics into one atomic deploy error. Each
240
+ * diagnostic's `message` is included verbatim (so a caller / test matching a
241
+ * specific sited substring still matches), prefixed by its stable code.
242
+ */
243
+ function formatProveErrors(logicalSchemaName, errors) {
244
+ const lines = errors.map(e => ` - [${e.code}] ${e.message}`);
245
+ return `lens: deploy of logical schema '${logicalSchemaName}' blocked by ${errors.length} error(s):\n${lines.join('\n')}`;
246
+ }
247
+ /**
248
+ * Builds the {@link LensDeploymentSnapshot} for a just-completed deploy: per
249
+ * logical table, the compiled get-body, its logical columns, and the
250
+ * per-column basis backing (relation + column) derived from the effective body.
251
+ * Also records `basisHash = computeSchemaHash(basis declared schema)` — the
252
+ * migration-safety record. An empty deploy (every logical table removed, no
253
+ * basis resolved) yields an empty-tables snapshot that still rotates, so the
254
+ * detach is reflected.
255
+ */
256
+ function buildDeploymentSnapshot(db, compiled, basis, logicalSchemaName) {
257
+ const priorBasisName = db.declaredSchemaManager.getDeployedLensSnapshots(logicalSchemaName)?.current?.basisSchemaName;
258
+ const basisSchemaName = basis?.schemaName ?? priorBasisName ?? '';
259
+ const basisDeclared = basisSchemaName
260
+ ? db.declaredSchemaManager.getDeclaredSchema(basisSchemaName)
261
+ : undefined;
262
+ const basisHash = basisDeclared ? computeSchemaHash(basisDeclared) : '';
263
+ const tables = new Map();
264
+ if (basis) {
265
+ for (const { slot } of compiled) {
266
+ const relationBacking = deriveRelationBacking(slot.compiledBody, slot.columnProvenance, basis, db.schemaManager);
267
+ const logicalColumns = slot.columnProvenance.map(p => p.logicalColumn);
268
+ const surrogateMemberKeys = deriveSurrogateMemberKeys(slot, basis);
269
+ tables.set(slot.logicalTable.name.toLowerCase(), {
270
+ logicalTable: slot.logicalTable.name,
271
+ getBody: slot.compiledBody,
272
+ logicalColumns,
273
+ relationBacking,
274
+ surrogateMemberKeys,
275
+ });
276
+ }
277
+ }
278
+ return { basisSchemaName, basisHash, tables };
279
+ }
280
+ /** Lowercased `schema.table` identity for an override source's basis relation. */
281
+ function sourceRelKey(src) {
282
+ return `${src.table.schemaName.toLowerCase()}.${src.table.name.toLowerCase()}`;
283
+ }
284
+ /**
285
+ * The basis columns an engine-generated skeleton insert must supply: NOT NULL,
286
+ * no default, non-generated. A column that is nullable, defaulted, or generated
287
+ * has its own value source, so a skeleton may soundly omit it; these have none,
288
+ * so omitting one would fail an unguarded NOT NULL constraint. Walks the member's
289
+ * *full* schema so it also flags required columns the lens maps to no logical
290
+ * column (which never appear in the relation's `(basisColumn → logicalColumn)`
291
+ * pairs). See `LensRelationBacking.requiredBasisColumns`.
292
+ */
293
+ function requiredBasisColumnsOf(table) {
294
+ return table.columns
295
+ .filter(c => c.notNull && c.defaultValue === null && !c.generated)
296
+ .map(c => c.name);
297
+ }
298
+ /**
299
+ * Derives, per basis relation, the `(basisColumn → logicalColumn)` pairs it backs
300
+ * for one compiled effective body — the record a later re-decomposition diffs and
301
+ * backfills (docs/lens.md § The deployed basis representation). Two contributions:
302
+ *
303
+ * 1. **Projection.** The body's projection is exactly the logical
304
+ * columns, in declaration order (see `compileDefaultBody` / `compileOverrideBody`);
305
+ * each plain column reference resolves to its FROM source. A computed
306
+ * (non-column) projection has no single basis backing and is omitted.
307
+ * 2. **Shared join keys.** A columnar split joins its members on a shared key but
308
+ * projects it only once (from the anchor). The other members carry the key
309
+ * column too and must be backfilled with it, else a `not null` key fails. So
310
+ * every join-key column equated to a *projected* logical column is threaded to
311
+ * its member, mapped to that logical column.
312
+ */
313
+ function deriveRelationBacking(body, provenance, basis, schemaManager) {
314
+ const { sources } = collectOverrideSources(body.from, basis.schemaName, schemaManager);
315
+ const byRef = new Map();
316
+ for (const s of sources)
317
+ byRef.set(s.refName.toLowerCase(), s);
318
+ const single = sources.length === 1 ? sources[0] : undefined;
319
+ const result = new Map();
320
+ const add = (src, basisColumn, logicalColumn) => {
321
+ const key = sourceRelKey(src);
322
+ let rb = result.get(key);
323
+ if (!rb) {
324
+ rb = {
325
+ relationId: key,
326
+ basisRelation: { schema: src.table.schemaName, table: src.table.name },
327
+ columns: [],
328
+ requiredBasisColumns: requiredBasisColumnsOf(src.table),
329
+ };
330
+ result.set(key, rb);
331
+ }
332
+ const cols = rb.columns;
333
+ if (!cols.some(c => c.basisColumn.toLowerCase() === basisColumn.toLowerCase())) {
334
+ cols.push({ basisColumn, logicalColumn });
335
+ }
336
+ };
337
+ // 1. Projection.
338
+ const projected = [];
339
+ for (let i = 0; i < provenance.length && i < body.columns.length; i++) {
340
+ const rc = body.columns[i];
341
+ if (rc.type !== 'column')
342
+ continue;
343
+ const expr = rc.expr;
344
+ if (expr.type !== 'column')
345
+ continue; // computed → no single backing relation
346
+ const colExpr = expr;
347
+ const src = colExpr.table ? byRef.get(colExpr.table.toLowerCase()) : single;
348
+ if (!src)
349
+ continue; // unqualified ref over a multi-source FROM, or an opaque source
350
+ projected.push({ logical: provenance[i].logicalColumn, src, basisColumn: colExpr.name });
351
+ add(src, colExpr.name, provenance[i].logicalColumn);
352
+ }
353
+ // 2. Shared join keys — thread each equated key column to a projected logical column.
354
+ for (const cls of collectJoinKeyEquivalences(body.from, basis.schemaName, schemaManager)) {
355
+ let logical;
356
+ for (const m of cls) {
357
+ const proj = projected.find(p => sourceRelKey(p.src) === sourceRelKey(m.src)
358
+ && p.basisColumn.toLowerCase() === m.column.toLowerCase());
359
+ if (proj) {
360
+ logical = proj.logical;
361
+ break;
362
+ }
363
+ }
364
+ if (!logical)
365
+ continue; // key not surfaced as a logical column → leave to multi-source
366
+ for (const m of cls)
367
+ add(m.src, m.column, logical);
368
+ }
369
+ return result;
370
+ }
371
+ /**
372
+ * Collects join-key equivalence classes from a body's FROM. Each `using (cols)`
373
+ * join contributes, per column, the equated key columns across its left/right
374
+ * subtree sources; each `on a.x = b.y` conjunct contributes that pair. Overlapping
375
+ * classes are harmless — threading dedups per relation.
376
+ */
377
+ function collectJoinKeyEquivalences(from, basisSchemaName, schemaManager) {
378
+ const classes = [];
379
+ if (!from)
380
+ return classes;
381
+ const resolveTableSources = (node) => {
382
+ const out = [];
383
+ const walk = (n) => {
384
+ if (n.type === 'table') {
385
+ const schemaName = n.table.schema ?? basisSchemaName;
386
+ const tbl = schemaManager.getSchema(schemaName)?.getTable(n.table.name);
387
+ if (tbl)
388
+ out.push({ table: tbl, refName: n.alias ?? tbl.name });
389
+ }
390
+ else if (n.type === 'join') {
391
+ walk(n.left);
392
+ walk(n.right);
393
+ }
394
+ };
395
+ walk(node);
396
+ return out;
397
+ };
398
+ const memberFor = (src, colName) => {
399
+ const idx = src.table.columnIndexMap.get(colName.toLowerCase());
400
+ if (idx === undefined)
401
+ return undefined;
402
+ return { src, column: src.table.columns[idx].name };
403
+ };
404
+ const walkJoin = (node) => {
405
+ if (node.type !== 'join')
406
+ return;
407
+ walkJoin(node.left);
408
+ walkJoin(node.right);
409
+ const subtree = [...resolveTableSources(node.left), ...resolveTableSources(node.right)];
410
+ if (node.columns) {
411
+ // USING (cols): equate each named column across every subtree source carrying it.
412
+ for (const col of node.columns) {
413
+ const members = subtree.map(s => memberFor(s, col)).filter((m) => m !== undefined);
414
+ if (members.length > 1)
415
+ classes.push(members);
416
+ }
417
+ }
418
+ else if (node.condition) {
419
+ // ON a.x = b.y [and ...]: best-effort equality-conjunct extraction.
420
+ const byRef = new Map();
421
+ for (const s of subtree)
422
+ byRef.set(s.refName.toLowerCase(), s);
423
+ for (const [left, right] of collectEqualityPairs(node.condition)) {
424
+ if (!left.table || !right.table)
425
+ continue;
426
+ const ls = byRef.get(left.table.toLowerCase());
427
+ const rs = byRef.get(right.table.toLowerCase());
428
+ if (!ls || !rs)
429
+ continue;
430
+ const lm = memberFor(ls, left.name);
431
+ const rm = memberFor(rs, right.name);
432
+ if (lm && rm)
433
+ classes.push([lm, rm]);
434
+ }
435
+ }
436
+ };
437
+ for (const f of from)
438
+ walkJoin(f);
439
+ return classes;
440
+ }
441
+ /** Extracts `col = col` conjuncts from an ON condition (best-effort, AND-only). */
442
+ function collectEqualityPairs(expr) {
443
+ const pairs = [];
444
+ const walk = (e) => {
445
+ if (e.type !== 'binary')
446
+ return;
447
+ const bin = e;
448
+ if (bin.operator.toUpperCase() === 'AND') {
449
+ walk(bin.left);
450
+ walk(bin.right);
451
+ }
452
+ else if (bin.operator === '=' && bin.left.type === 'column' && bin.right.type === 'column') {
453
+ pairs.push([bin.left, bin.right]);
454
+ }
455
+ };
456
+ walk(expr);
457
+ return pairs;
458
+ }
459
+ /**
460
+ * The basis-relation keys (`schema.table`, lowercased) of a slot's surrogate-keyed
461
+ * decomposition members, or undefined when there is no advertisement / the shared
462
+ * key is a logical-tuple. Lets the differ defer an unsound multi-member surrogate
463
+ * split (see `docs/lens.md` § The Default Mapper — evaluate-once-and-thread).
464
+ */
465
+ function deriveSurrogateMemberKeys(slot, basis) {
466
+ const storage = slot.advertisement?.storage;
467
+ if (!storage || storage.sharedKey.kind !== 'surrogate')
468
+ return undefined;
469
+ const keys = new Set();
470
+ for (const m of storage.members) {
471
+ const schemaName = (m.relation.schema || basis.schemaName).toLowerCase();
472
+ keys.add(`${schemaName}.${m.relation.table.toLowerCase()}`);
473
+ }
474
+ return keys;
475
+ }
476
+ /**
477
+ * PoC wiring for `reserved-tags.ts`: validates the reserved `quereus.*` tag
478
+ * namespace on one lens slot's logical table (`logical-table` site), each of its
479
+ * logical columns (`logical-column` site — the home of `quereus.lens.writable`),
480
+ * and each of its attached constraints (`logical-constraint` site).
481
+ *
482
+ * Severity policy is the registry's; this caller only routes it: a
483
+ * `severity:'error'` diagnostic (unknown key, mis-sited key, malformed enum/CSV
484
+ * value) throws a {@link QuereusError} with the sited message — consistent with
485
+ * the other compile-time lens errors, and atomic because validation runs before
486
+ * catalog mutation. `severity:'warning'` diagnostics (e.g. an empty
487
+ * `quereus.lens.ack` rationale) are logged via the existing `log` channel; the
488
+ * deploy-summary warning channel (`docs/lens.md:169`) is `3-lens-prover` Phase
489
+ * C's to build. Errors take precedence — warnings only log when none fail.
490
+ *
491
+ * This validates shape/site only — no reserved tag carries behavior read here.
492
+ */
493
+ function validateLensTags(slot) {
494
+ const diagnostics = [
495
+ ...validateReservedTags(slot.logicalTable.tags, 'logical-table'),
496
+ ];
497
+ // Each logical column's tags at the `logical-column` site (the home of
498
+ // `quereus.lens.writable`). This also closes a pre-existing gap: a typo'd /
499
+ // mis-sited `quereus.*` key on a logical column was previously never validated.
500
+ for (const col of slot.logicalTable.columns) {
501
+ if (col.tags)
502
+ diagnostics.push(...validateReservedTags(col.tags, 'logical-column'));
503
+ }
504
+ for (const constraint of slot.attachedConstraints) {
505
+ const tags = constraint.kind === 'primaryKey' ? undefined : constraint.constraint.tags;
506
+ if (tags)
507
+ diagnostics.push(...validateReservedTags(tags, 'logical-constraint'));
508
+ }
509
+ raiseReservedTagDiagnostics(diagnostics, {
510
+ log: (diag) => log('lens advisory (%s) on %s.%s: %s', diag.reason, slot.logicalTable.schemaName, slot.logicalTable.name, diag.message),
511
+ });
512
+ }
513
+ /**
514
+ * Rejects every physical construct under a logical declared schema, naming the
515
+ * offending construct and the logical-schema context. Tags are allowed (they
516
+ * are engine-facing and survive into the compiled view).
517
+ */
518
+ export function validateLogicalDeclaration(declaredSchema, logicalSchemaName) {
519
+ const ctx = `logical schema '${logicalSchemaName}'`;
520
+ for (const item of declaredSchema.items) {
521
+ switch (item.type) {
522
+ case 'declaredTable': {
523
+ if (item.tableStmt.moduleName) {
524
+ throw new QuereusError(`lens: module association 'using ${item.tableStmt.moduleName}(...)' on table '${item.tableStmt.table.name}' is not allowed in ${ctx}; logical tables declare columns and constraints only`, StatusCode.ERROR);
525
+ }
526
+ break;
527
+ }
528
+ case 'declaredIndex': {
529
+ const kind = item.indexStmt.isUnique ? 'unique index' : 'index';
530
+ throw new QuereusError(`lens: ${kind} '${item.indexStmt.index.name}' is not allowed in ${ctx}; indexes are a basis-layer construct (a materialized view over the basis)`, StatusCode.ERROR);
531
+ }
532
+ case 'declaredMaterializedView': {
533
+ throw new QuereusError(`lens: materialized view '${item.viewStmt.view.name}' is not allowed in ${ctx}; materialized views are a basis-layer construct`, StatusCode.ERROR);
534
+ }
535
+ // declaredView / declaredSeed / declaredAssertion / declareIgnored:
536
+ // not part of the logical-table surface in v1 — neither rejected nor
537
+ // processed by the lens compiler (only declaredTable becomes a slot).
538
+ }
539
+ }
540
+ }
541
+ /**
542
+ * Infers the default basis for a logical schema (MVP binding — there is no
543
+ * `declare lens for X over Y` yet). The basis is the single registered
544
+ * **physical** schema that contains ≥1 table, excluding the logical schema
545
+ * itself and `temp`. See `docs/lens.md` § Default-basis inference.
546
+ */
547
+ export function inferDefaultBasis(schemaManager, logicalSchemaName) {
548
+ const lowerLogical = logicalSchemaName.toLowerCase();
549
+ const candidates = [];
550
+ for (const schema of schemaManager._getAllSchemas()) {
551
+ const lowerName = schema.name.toLowerCase();
552
+ if (lowerName === lowerLogical)
553
+ continue;
554
+ if (lowerName === 'temp')
555
+ continue;
556
+ if (schema.kind !== 'physical')
557
+ continue;
558
+ let hasTable = false;
559
+ for (const _t of schema.getAllTables()) {
560
+ hasTable = true;
561
+ break;
562
+ }
563
+ if (!hasTable)
564
+ continue;
565
+ candidates.push({ schema, schemaName: schema.name });
566
+ }
567
+ if (candidates.length === 1) {
568
+ return candidates[0];
569
+ }
570
+ throw new QuereusError(`lens: cannot infer a default basis for logical schema '${logicalSchemaName}' (found ${candidates.length} candidates); supply 'declare lens for ${logicalSchemaName} over <basis>'`, StatusCode.ERROR);
571
+ }
572
+ /**
573
+ * The default name-based aligner: produces `select <logical columns> from B.T'`
574
+ * for one logical table `L.T` over basis schema `B`.
575
+ *
576
+ * - The basis table is matched by name (case-insensitive).
577
+ * - Each logical column is matched to a basis column by name (case-insensitive).
578
+ * - The projection lists exactly the logical columns, in declaration order, so
579
+ * a basis table with extra columns is correctly projected down.
580
+ *
581
+ * The empty-key (singleton) case needs no special path: a `primary key ()`
582
+ * logical table over a `primary key ()` basis table is an ordinary single-source
583
+ * projection.
584
+ */
585
+ export function compileDefaultBody(logicalTable, logicalSchemaName, basisSchema, basisSchemaName) {
586
+ const logicalName = logicalTable.name;
587
+ const basisTable = basisSchema.getTable(logicalName);
588
+ if (!basisTable) {
589
+ throw new QuereusError(`lens: logical table '${logicalSchemaName}.${logicalName}' has no basis backing`, StatusCode.ERROR);
590
+ }
591
+ const columns = [];
592
+ for (const col of logicalTable.columns) {
593
+ const basisColIdx = basisTable.columnIndexMap.get(col.name.toLowerCase());
594
+ if (basisColIdx === undefined) {
595
+ throw new QuereusError(`lens: logical column '${logicalSchemaName}.${logicalName}.${col.name}' has no basis backing`, StatusCode.ERROR);
596
+ }
597
+ // Single source → an unqualified column reference is unambiguous. Reference
598
+ // the basis column by its actual name; the consumer-facing column *names*
599
+ // (and casing) are pinned to the logical declaration via the registered
600
+ // view's explicit column list (see `deployLogicalSchema`), so the basis
601
+ // spelling never leaks through `select * from Logical.T`.
602
+ const basisColName = basisTable.columns[basisColIdx].name;
603
+ columns.push({
604
+ type: 'column',
605
+ expr: { type: 'column', name: basisColName },
606
+ });
607
+ }
608
+ return {
609
+ type: 'select',
610
+ columns,
611
+ from: [{
612
+ type: 'table',
613
+ table: { type: 'identifier', name: basisTable.name, schema: basisSchemaName },
614
+ }],
615
+ };
616
+ }
617
+ // ===========================================================================
618
+ // n-way decomposition synthesis (docs/lens.md § The Default Mapper)
619
+ // ===========================================================================
620
+ //
621
+ // When a logical table is backed by a resolved primary-storage advertisement,
622
+ // the body producer synthesizes the `get` join instead of the single-source
623
+ // name aligner: a left-deep equi-join rooted at the existence anchor, mandatory
624
+ // members inner-joined and optional members outer-joined, EAV pivot members
625
+ // projected as correlated scalar subqueries (not joined), and the projection
626
+ // resolving each logical column to its advertised backing expression. Read
627
+ // direction only — the `put` fan-out + IND injection are sibling tickets; a
628
+ // multi-source body remains write-rejected by view-updateability.
629
+ /**
630
+ * Synthesizes the n-way `get` body for a logical table backed by a resolved
631
+ * primary-storage advertisement.
632
+ *
633
+ * The synthesized FROM is a left-deep join tree rooted at `anchorRelationId`:
634
+ * every other non-EAV member is inner-joined (`presence:'mandatory'`) or
635
+ * outer-joined (`presence:'optional'`) onto the anchor via a positional
636
+ * key-equi-join over `sharedKey.keyColumnsByRelation`. Optional members are
637
+ * outer-joined so a logical row missing an optional component survives with that
638
+ * component's columns null (inner-joining everywhere would silently drop rows).
639
+ *
640
+ * The empty-key (singleton) case is not a special path: an empty per-member key
641
+ * column list makes the equi-join conjunction vacuously true (`on 1 = 1`), so a
642
+ * `primary key ()` table over a 0-or-1-row anchor reads 0-or-1 row.
643
+ *
644
+ * EAV pivot members are NOT join members (joining a triple store would multiply
645
+ * rows); each EAV-backed logical column is projected as a correlated scalar
646
+ * subquery keyed by the attribute literal.
647
+ */
648
+ function compileDecompositionBody(logicalTable, logicalSchemaName, basis, advertisement, db) {
649
+ const storage = advertisement.storage;
650
+ if (!storage) {
651
+ // A primary advertisement always carries a storage shape (validated at
652
+ // resolution); guard defensively rather than emit an unsound body.
653
+ throw new QuereusError(`lens: decomposition for logical table '${logicalSchemaName}.${logicalTable.name}' has a primary advertisement with no storage shape`, StatusCode.ERROR);
654
+ }
655
+ const logicalName = logicalTable.name;
656
+ // Resolve every member's basis table (re-resolved here for the synthesized
657
+ // FROM's actual table name + schema casing; existence is validated already).
658
+ const memberTables = new Map();
659
+ for (const member of storage.members) {
660
+ const table = resolveBasisRelation(db, member, basis);
661
+ if (!table) {
662
+ throw new QuereusError(`lens: decomposition for logical table '${logicalSchemaName}.${logicalName}' references basis relation '${member.relation.schema}.${member.relation.table}' (member '${member.relationId}'), which does not exist`, StatusCode.ERROR);
663
+ }
664
+ memberTables.set(member.relationId, table);
665
+ }
666
+ const anchor = storage.members.find(m => m.relationId === storage.anchorRelationId);
667
+ if (!anchor) {
668
+ throw new QuereusError(`lens: decomposition for logical table '${logicalSchemaName}.${logicalName}' names anchor '${storage.anchorRelationId}', which is not among the members`, StatusCode.ERROR);
669
+ }
670
+ const anchorTable = memberTables.get(anchor.relationId);
671
+ const anchorKeys = storage.sharedKey.keyColumnsByRelation.get(anchor.relationId) ?? [];
672
+ // The join set: the anchor (root) + every non-EAV member. EAV pivot members
673
+ // back columns via a correlated subquery, never a join.
674
+ const joinedMembers = new Set([anchor.relationId]);
675
+ for (const member of storage.members) {
676
+ if (member.relationId === anchor.relationId)
677
+ continue;
678
+ if (member.attributePivot)
679
+ continue; // EAV → subquery, not joined
680
+ joinedMembers.add(member.relationId);
681
+ }
682
+ // Build the left-deep join tree, anchor first.
683
+ let from = memberTableSource(anchorTable, basis.schemaName, anchor.relationId);
684
+ for (const member of storage.members) {
685
+ if (!joinedMembers.has(member.relationId) || member.relationId === anchor.relationId)
686
+ continue;
687
+ const memberTable = memberTables.get(member.relationId);
688
+ const memberKeys = storage.sharedKey.keyColumnsByRelation.get(member.relationId) ?? [];
689
+ from = {
690
+ type: 'join',
691
+ joinType: member.presence === 'mandatory' ? 'inner' : 'left',
692
+ left: from,
693
+ right: memberTableSource(memberTable, basis.schemaName, member.relationId),
694
+ condition: buildKeyEquiJoin(anchor.relationId, anchorKeys, member.relationId, memberKeys),
695
+ };
696
+ }
697
+ // Projection: each logical column → its advertised backing expression.
698
+ const aliasOf = (relationId) => joinedMembers.has(relationId) ? relationId : undefined;
699
+ const eavAnchor = anchorKeys.length > 0
700
+ ? { alias: anchor.relationId, keyColumn: anchorKeys[0] }
701
+ : undefined;
702
+ const columns = [];
703
+ for (const col of logicalTable.columns) {
704
+ const res = resolveAdvertisedColumn(col.name, storage, basis.schemaName, aliasOf, eavAnchor);
705
+ let expr;
706
+ if (res.kind === 'expr') {
707
+ expr = res.expr;
708
+ }
709
+ else if (res.kind === 'unreachable') {
710
+ throw new QuereusError(`lens: decomposition for logical table '${logicalSchemaName}.${logicalName}' maps column '${col.name}' to member '${res.member}', which is not part of the synthesized join (an EAV pivot member backs columns through its attribute pivot, not a direct column mapping)`, StatusCode.ERROR);
711
+ }
712
+ else {
713
+ // Name-match against a join member (anchor first), qualified by the
714
+ // member's alias — the decomposition analogue of the single-source path.
715
+ const nm = nameMatchAgainstMembers(col.name, storage, memberTables, joinedMembers, anchor.relationId);
716
+ if (!nm) {
717
+ throw new QuereusError(`lens: decomposition for logical table '${logicalSchemaName}.${logicalName}' cannot resolve column '${col.name}': it is not mapped by any advertised member, not EAV-backed, and no decomposition member has a same-named column`, StatusCode.ERROR);
718
+ }
719
+ expr = nm;
720
+ }
721
+ columns.push({ type: 'column', expr, alias: col.name });
722
+ }
723
+ return { type: 'select', columns, from: [from] };
724
+ }
725
+ /**
726
+ * Computes the existence-anchor inclusion dependencies for a decomposition
727
+ * (`lens-multi-source-ind-injection`, docs/lens.md § The module mapping
728
+ * advertisement). For each **mandatory**, non-anchor, non-EAV member, injects one
729
+ * IND asserting the existence **anchor's** shared-key tuple is included in that
730
+ * member's key — the propagated fact that lets the prover discharge the
731
+ * no-row-loss obligation of the anchor-rooted inner join against a threaded
732
+ * existence fact rather than re-deriving decomposition structure. The surrogate
733
+ * join carries no declared SQL FK, so `seedTableForeignKeyInds` / `lookupCoveringFK`
734
+ * are structurally blind to it; this is the `IndTarget.kind:'relation'` producer
735
+ * reserved by Wave 1.
736
+ *
737
+ * Direction + soundness. `compileDecompositionBody` builds a left-deep join
738
+ * **rooted at the anchor**, inner-joining each mandatory member (`anchor ⋈ member`);
739
+ * the logical entities are the anchor rows (one per logical row). The no-row-loss
740
+ * obligation the prover discharges is therefore "no anchor row is dropped"
741
+ * (`tSide = anchor`, `lookup = member`), which it reads from an IND **on the
742
+ * anchor** of the form `anchor.key ⊆ member.key`. `presence:'mandatory'` ("every
743
+ * logical row has it") guarantees exactly that: every anchor (= logical) row has a
744
+ * matching member row on the shared key ⇒ `anchor.key ⊆ member.key`, total
745
+ * (`nullRejecting:false`) — exactly the existence fact the anchor-rooted inner
746
+ * join's row-preservation obligation needs. The converse (`member ⊆ anchor`) is
747
+ * **intentionally not asserted**: no stated property guarantees member→anchor
748
+ * referential integrity, so emitting it would over-claim (a mandatory-member row
749
+ * whose key is absent from the anchor is simply filtered by the inner join — reads
750
+ * stay correct, but the converse fact would be false).
751
+ *
752
+ * Guards (over-claim is unsound — Wave 1's § Enforcement readiness):
753
+ * - **mandatory only** — an optional member is outer-joined; its absence is
754
+ * exactly what the outer join preserves, so an IND would over-claim.
755
+ * - **total only** (`nullRejecting:false`) — a mandatory member's existence is
756
+ * total: every logical row has it.
757
+ * - **EAV pivots excluded** — they are projected as correlated subqueries, never
758
+ * inner-joined, so there is no row-loss obligation to discharge.
759
+ * - **empty key → none** — a singleton (`primary key ()`) has no witnessing key
760
+ * tuple; existence is the anchor's own 0-or-1-row property.
761
+ *
762
+ * `cols` are the **anchor's** shared-key column indices on the anchor's basis
763
+ * relation; `target.targetCols` the **member's** key column indices on the member's
764
+ * basis relation; `target.relationId` is the member (not the anchor). Anchor/member
765
+ * key columns pair positionally, matching the get-synthesis equi-join. Uses `addInd`
766
+ * + `MAX_INDS_PER_NODE` for dedup/cap consistency with the Wave-1 FK seeding;
767
+ * multiple mandatory members produce distinct INDs (same `cols` = anchor key,
768
+ * different `target.relationId`), so dedup is unaffected.
769
+ */
770
+ function computeExistenceAnchorInds(advertisement, basis, db) {
771
+ const storage = advertisement.storage;
772
+ if (!storage)
773
+ return [];
774
+ const anchor = storage.members.find(m => m.relationId === storage.anchorRelationId);
775
+ if (!anchor)
776
+ return []; // validated at resolution; defensive
777
+ const anchorTable = resolveBasisRelation(db, anchor, basis);
778
+ if (!anchorTable)
779
+ return []; // validated at resolution; defensive
780
+ const anchorKeyIdx = mapKeyColumnsToIndices(storage.sharedKey.keyColumnsByRelation.get(anchor.relationId) ?? [], anchorTable);
781
+ // Empty anchor key (singleton / `primary key ()`): no witnessing tuple to thread.
782
+ if (!anchorKeyIdx || anchorKeyIdx.length === 0)
783
+ return [];
784
+ let inds = [];
785
+ for (const member of storage.members) {
786
+ if (member.relationId === anchor.relationId)
787
+ continue;
788
+ if (member.presence !== 'mandatory')
789
+ continue; // optional → outer-joined → no IND (over-claim guard)
790
+ if (member.attributePivot)
791
+ continue; // EAV pivot → projected as subquery, never inner-joined
792
+ const memberTable = resolveBasisRelation(db, member, basis);
793
+ if (!memberTable)
794
+ continue; // validated at resolution; defensive
795
+ const memberKeyIdx = mapKeyColumnsToIndices(storage.sharedKey.keyColumnsByRelation.get(member.relationId) ?? [], memberTable);
796
+ if (!memberKeyIdx)
797
+ continue;
798
+ // Pair positionally up to the shorter list (matching the get-synthesis equi-join).
799
+ const n = Math.min(memberKeyIdx.length, anchorKeyIdx.length);
800
+ if (n === 0)
801
+ continue; // member has no key tuple to witness inclusion
802
+ // `anchor.key ⊆ member.key`, total: THIS = anchor (cols = anchor key indices),
803
+ // target = the MEMBER (the totality direction `mandatory` guarantees and the
804
+ // anchor-rooted inner join's no-row-loss obligation consumes). See doc above.
805
+ inds = addInd(inds, {
806
+ cols: anchorKeyIdx.slice(0, n),
807
+ target: { kind: 'relation', relationId: member.relationId, targetCols: memberKeyIdx.slice(0, n) },
808
+ nullRejecting: false,
809
+ }, { cap: MAX_INDS_PER_NODE });
810
+ }
811
+ return inds;
812
+ }
813
+ /**
814
+ * Maps shared-key column names to their indices on `table`, preserving order.
815
+ * Returns undefined if any column is missing — the advertisement validator
816
+ * already guarantees existence, so this is a defensive guard.
817
+ */
818
+ function mapKeyColumnsToIndices(keys, table) {
819
+ const idx = [];
820
+ for (const k of keys) {
821
+ const i = table.columnIndexMap.get(k.toLowerCase());
822
+ if (i === undefined)
823
+ return undefined;
824
+ idx.push(i);
825
+ }
826
+ return idx;
827
+ }
828
+ /** A FROM `table` source for one decomposition member, aliased by its relationId. */
829
+ function memberTableSource(table, basisSchemaName, alias) {
830
+ return {
831
+ type: 'table',
832
+ table: { type: 'identifier', name: table.name, schema: table.schemaName || basisSchemaName },
833
+ alias,
834
+ };
835
+ }
836
+ /**
837
+ * Builds the per-member key-equi-join ON condition: a positional conjunction of
838
+ * `member.kᵢ = anchor.kᵢ` over the two relations' shared-key column lists (paired
839
+ * by index, since a surrogate may be spelled differently per relation). An empty
840
+ * key column list (the `primary key ()` singleton) yields the vacuously-true
841
+ * `1 = 1` — no singleton-specific branch (docs/lens.md § The Default Mapper).
842
+ */
843
+ function buildKeyEquiJoin(anchorAlias, anchorKeys, memberAlias, memberKeys) {
844
+ const n = Math.min(anchorKeys.length, memberKeys.length);
845
+ const eqs = [];
846
+ for (let i = 0; i < n; i++) {
847
+ eqs.push({
848
+ type: 'binary',
849
+ operator: '=',
850
+ left: { type: 'column', name: memberKeys[i], table: memberAlias },
851
+ right: { type: 'column', name: anchorKeys[i], table: anchorAlias },
852
+ });
853
+ }
854
+ if (eqs.length === 0) {
855
+ // Singleton / empty key: vacuously true (`on 1 = 1`).
856
+ return {
857
+ type: 'binary',
858
+ operator: '=',
859
+ left: { type: 'literal', value: 1 },
860
+ right: { type: 'literal', value: 1 },
861
+ };
862
+ }
863
+ return eqs.reduce((acc, e) => ({ type: 'binary', operator: 'AND', left: acc, right: e }));
864
+ }
865
+ /**
866
+ * Resolves one logical column to its advertised backing expression, re-qualified
867
+ * to the caller-supplied alias for the backing member. Shared by the pure
868
+ * decomposition body (`compileDecompositionBody`) and the override gap-fill path
869
+ * (`compileOverrideBody`), so both honor the same precedence:
870
+ *
871
+ * 1. **Explicit per-member mapping** wins → the mapped `basisExpr`, re-qualified
872
+ * to the member's alias. When the member is not reachable from the caller's
873
+ * FROM (`aliasOf` returns undefined), reports `unreachable`.
874
+ * 2. **EAV attribute pivot** (exactly one pivot member + an entity correlation
875
+ * key) → a correlated scalar subquery keyed by the attribute literal.
876
+ * 3. Otherwise `none` — the caller falls back to its own name-match path.
877
+ *
878
+ * Matches `annotateProvenanceWithAdvertisement`'s attribution order (explicit
879
+ * mapping, then the sole EAV member), so the synthesized projection and the
880
+ * `quereus_effective_lens` provenance stay consistent.
881
+ */
882
+ function resolveAdvertisedColumn(logicalColumn, storage, basisSchemaName, aliasOf, eavAnchor) {
883
+ const lc = logicalColumn.toLowerCase();
884
+ // 1. Explicit per-member mapping.
885
+ for (const member of storage.members) {
886
+ const mapping = member.columns.find(m => m.logicalColumn.toLowerCase() === lc);
887
+ if (!mapping)
888
+ continue;
889
+ const alias = aliasOf(member.relationId);
890
+ if (!alias)
891
+ return { kind: 'unreachable', member: member.relationId };
892
+ return { kind: 'expr', expr: requalifyColumnRefs(mapping.basisExpr, alias) };
893
+ }
894
+ // 2. EAV attribute pivot — a correlated scalar subquery keyed by the attribute
895
+ // literal (only with exactly one pivot member + an entity correlation key).
896
+ const eavMembers = storage.members.filter(m => m.attributePivot);
897
+ if (eavMembers.length === 1 && eavAnchor) {
898
+ return { kind: 'expr', expr: buildEavSubquery(eavMembers[0], logicalColumn, basisSchemaName, eavAnchor) };
899
+ }
900
+ // 3. Not advertised → caller falls back to name-match.
901
+ return { kind: 'none' };
902
+ }
903
+ /**
904
+ * Builds the correlated scalar subquery for one EAV-backed logical column:
905
+ * `(select p.<value> from <pivot> p where p.<entity> = anchor.<key> and
906
+ * p.<attribute> = '<logicalColumn>')`. Keeps every EAV column independently
907
+ * nullable (a logical row may have a triple for some attributes and not others)
908
+ * and rides the existing scalar-subquery read path with no new runtime.
909
+ */
910
+ function buildEavSubquery(pivot, logicalColumn, basisSchemaName, eavAnchor) {
911
+ const piv = pivot.attributePivot;
912
+ const pivotAlias = pivot.relationId;
913
+ const pivotSchema = pivot.relation.schema || basisSchemaName;
914
+ const query = {
915
+ type: 'select',
916
+ columns: [{ type: 'column', expr: { type: 'column', name: piv.valueColumn, table: pivotAlias } }],
917
+ from: [{
918
+ type: 'table',
919
+ table: { type: 'identifier', name: pivot.relation.table, schema: pivotSchema },
920
+ alias: pivotAlias,
921
+ }],
922
+ where: {
923
+ type: 'binary',
924
+ operator: 'AND',
925
+ left: {
926
+ type: 'binary',
927
+ operator: '=',
928
+ left: { type: 'column', name: piv.entityColumn, table: pivotAlias },
929
+ right: { type: 'column', name: eavAnchor.keyColumn, table: eavAnchor.alias },
930
+ },
931
+ right: {
932
+ type: 'binary',
933
+ operator: '=',
934
+ left: { type: 'column', name: piv.attributeColumn, table: pivotAlias },
935
+ right: { type: 'literal', value: logicalColumn },
936
+ },
937
+ },
938
+ };
939
+ return { type: 'subquery', query };
940
+ }
941
+ /**
942
+ * Deep-clones a member's `basisExpr` and re-qualifies every `column` reference in
943
+ * it to `alias` (the member's alias in the synthesized FROM). The stored mapping
944
+ * references the member's own columns, possibly bare or self-qualified; this
945
+ * rewrites them all to the alias so the projection is unambiguous over the join.
946
+ * Reuses the reflective walk shape of `collectColumnRefNames`, rewriting rather
947
+ * than collecting.
948
+ */
949
+ function requalifyColumnRefs(expr, alias) {
950
+ const rewrite = (node) => {
951
+ if (Array.isArray(node))
952
+ return node.map(rewrite);
953
+ if (node && typeof node === 'object' && 'type' in node) {
954
+ const src = node;
955
+ const out = {};
956
+ for (const key of Object.keys(src))
957
+ out[key] = rewrite(src[key]);
958
+ if (src.type === 'column') {
959
+ out.table = alias;
960
+ out.schema = undefined;
961
+ }
962
+ return out;
963
+ }
964
+ return node;
965
+ };
966
+ return rewrite(expr);
967
+ }
968
+ /**
969
+ * Resolves a name-match logical column against the decomposition's join members
970
+ * (anchor first, then advertisement order): the first member whose basis table
971
+ * carries a same-named column, qualified by that member's alias. Returns
972
+ * undefined when no join member has the column (the caller errors precisely).
973
+ */
974
+ function nameMatchAgainstMembers(logicalColumn, storage, memberTables, joinedMembers, anchorRelationId) {
975
+ const lc = logicalColumn.toLowerCase();
976
+ const order = [anchorRelationId, ...storage.members.map(m => m.relationId).filter(id => id !== anchorRelationId)];
977
+ for (const relationId of order) {
978
+ if (!joinedMembers.has(relationId))
979
+ continue;
980
+ const table = memberTables.get(relationId);
981
+ const idx = table?.columnIndexMap.get(lc);
982
+ if (idx !== undefined) {
983
+ return { type: 'column', name: table.columns[idx].name, table: relationId };
984
+ }
985
+ }
986
+ return undefined;
987
+ }
988
+ /**
989
+ * Indexes a lens block's overrides by lowercased logical-table name, validating
990
+ * that each names a logical table the declaration actually carries. The check
991
+ * is skipped for an empty declaration (a pure detach-everything re-apply, which
992
+ * never aligns a basis).
993
+ */
994
+ function indexOverrides(lensDecl, declaredSchema, logicalSchemaName) {
995
+ const byTable = new Map();
996
+ if (!lensDecl)
997
+ return byTable;
998
+ const declaredTableNames = new Set();
999
+ for (const item of declaredSchema.items) {
1000
+ if (item.type === 'declaredTable')
1001
+ declaredTableNames.add(item.tableStmt.table.name.toLowerCase());
1002
+ }
1003
+ for (const ov of lensDecl.overrides) {
1004
+ const key = ov.table.toLowerCase();
1005
+ if (declaredTableNames.size > 0 && !declaredTableNames.has(key)) {
1006
+ throw new QuereusError(`lens: override 'view ${ov.table} as ...' references logical table '${logicalSchemaName}.${ov.table}', which is not declared in logical schema '${logicalSchemaName}'`, StatusCode.ERROR);
1007
+ }
1008
+ byTable.set(key, ov);
1009
+ }
1010
+ return byTable;
1011
+ }
1012
+ /**
1013
+ * Composes one effective **read** body for a logical table that has a
1014
+ * `declare lens` override, per docs/lens.md § D2:
1015
+ *
1016
+ * covered columns (override projection) ⊕ default-mapper gap-fill.
1017
+ *
1018
+ * Coverage is read **by name** from the override's output column names (alias or
1019
+ * bare name, and `*`-expansion of FROM-source columns). Each uncovered logical
1020
+ * column is gap-filled from a same-named basis column of the override's FROM.
1021
+ * When a gap is not reachable from the FROM (basis lacks the column, or a partial
1022
+ * cross-basis join), the compile errors rather than emit an unsound body — every
1023
+ * logical column must map to basis. The composition is recomputed on every deploy.
1024
+ */
1025
+ function compileOverrideBody(logicalTable, logicalSchemaName, basisSchemaName, schemaManager, override, advertisement) {
1026
+ const select = override.select;
1027
+ const logicalName = logicalTable.name;
1028
+ // Every FROM source the override names must live in the declared basis — an
1029
+ // override referencing a *different* existing schema (e.g. `Z.Foo` while the
1030
+ // lens is `over Y`) would silently re-anchor the body to Z (docs/lens.md § D4).
1031
+ validateOverrideBasisSources(select, basisSchemaName, logicalSchemaName, logicalName);
1032
+ // Resolve FROM-source basis tables once — used for both `*` expansion and
1033
+ // gap-fill. Opaque sources (subquery / function / unresolvable table) are
1034
+ // tracked so a gap that actually needs them errors precisely.
1035
+ const { sources, hasOpaqueSource } = collectOverrideSources(select.from, basisSchemaName, schemaManager);
1036
+ const qualify = sources.length > 1;
1037
+ // Advertisement-driven gap-fill needs to map an advertised member relationId to
1038
+ // its reference name (alias / table name) in the override's FROM. A member the
1039
+ // override's FROM does not include resolves to undefined (gap-fill then falls
1040
+ // back to name-match, or errors precisely).
1041
+ const overrideSourceByRel = new Map();
1042
+ for (const s of sources) {
1043
+ overrideSourceByRel.set(`${s.table.schemaName.toLowerCase()}.${s.table.name.toLowerCase()}`, s.refName);
1044
+ }
1045
+ const overrideAliasOf = (relationId) => {
1046
+ const member = advertisement?.storage?.members.find(m => m.relationId === relationId);
1047
+ if (!member)
1048
+ return undefined;
1049
+ const key = `${(member.relation.schema || basisSchemaName).toLowerCase()}.${member.relation.table.toLowerCase()}`;
1050
+ return overrideSourceByRel.get(key);
1051
+ };
1052
+ // Coverage map: lowercased output-column name -> the expression producing it,
1053
+ // plus the override column's `with inverse` clause when authored — carried per
1054
+ // covered column into the composed body so the write path consumes it there
1055
+ // (docs/view-updateability.md § Authored inverses; a gap-filled column never
1056
+ // has one).
1057
+ const coverage = new Map();
1058
+ let hasStar = false;
1059
+ let starTable;
1060
+ for (const col of select.columns) {
1061
+ if (col.type === 'all') {
1062
+ hasStar = true;
1063
+ if (col.table)
1064
+ starTable = col.table.toLowerCase();
1065
+ continue;
1066
+ }
1067
+ const outName = deriveColumnOutputName(col);
1068
+ if (!outName) {
1069
+ // A computed (non-column) projection term without an alias maps to no
1070
+ // logical column — it would be silently dropped, then the same-named
1071
+ // logical column gap-filled from the basis (a wrong, surprising read).
1072
+ throw new QuereusError(`lens: override for logical table '${logicalSchemaName}.${logicalName}' has a computed projection term '${astToString(col.expr)}' with no output name; add an alias (... as <name>) so it maps to a logical column`, StatusCode.ERROR);
1073
+ }
1074
+ coverage.set(outName.toLowerCase(), { expr: col.expr, inverse: col.inverse });
1075
+ }
1076
+ // `*` covers every FROM-source column not already explicitly covered.
1077
+ if (hasStar) {
1078
+ for (const src of sources) {
1079
+ if (starTable && src.refName.toLowerCase() !== starTable)
1080
+ continue;
1081
+ for (const c of src.table.columns) {
1082
+ const key = c.name.toLowerCase();
1083
+ if (!coverage.has(key))
1084
+ coverage.set(key, { expr: columnRef(c.name, qualify ? src.refName : undefined) });
1085
+ }
1086
+ }
1087
+ }
1088
+ const composed = [];
1089
+ const provenance = [];
1090
+ const effectiveColumns = [];
1091
+ for (const col of logicalTable.columns) {
1092
+ const key = col.name.toLowerCase();
1093
+ const covered = coverage.get(key);
1094
+ if (covered !== undefined) {
1095
+ composed.push({ type: 'column', expr: covered.expr, alias: col.name, inverse: covered.inverse });
1096
+ provenance.push({ logicalColumn: col.name, source: 'override' });
1097
+ }
1098
+ else {
1099
+ // Advertisement-driven gap-fill (richer than name-match): resolve the
1100
+ // uncovered column against the advertised member mapping, re-qualified to
1101
+ // its alias in the override's FROM. Fall back to today's FROM name-match
1102
+ // when the advertisement does not back it; error precisely when the
1103
+ // advertisement backs it from a member the FROM omits and name-match
1104
+ // cannot reach it either (the same fidelity-boundary discipline as gapFillError).
1105
+ let resolved;
1106
+ let unreachableMember;
1107
+ if (advertisement?.storage) {
1108
+ const res = resolveAdvertisedColumn(col.name, advertisement.storage, basisSchemaName, overrideAliasOf, undefined);
1109
+ if (res.kind === 'expr') {
1110
+ resolved = res.expr;
1111
+ }
1112
+ else if (res.kind === 'unreachable') {
1113
+ unreachableMember = res.member;
1114
+ }
1115
+ }
1116
+ if (!resolved) {
1117
+ const ref = gapFillRef(col.name, sources, qualify);
1118
+ if (ref) {
1119
+ resolved = ref;
1120
+ }
1121
+ else if (unreachableMember) {
1122
+ throw new QuereusError(`lens: override for logical table '${logicalSchemaName}.${logicalName}' leaves column '${col.name}' to advertisement gap-fill, but its backing member '${unreachableMember}' is absent from the override's FROM; cover it explicitly or include the member relation in the FROM`, StatusCode.ERROR);
1123
+ }
1124
+ else {
1125
+ throw new QuereusError(gapFillError(logicalSchemaName, logicalName, col.name, sources, hasOpaqueSource), StatusCode.ERROR);
1126
+ }
1127
+ }
1128
+ composed.push({ type: 'column', expr: resolved, alias: col.name });
1129
+ provenance.push({ logicalColumn: col.name, source: 'default' });
1130
+ }
1131
+ effectiveColumns.push(col.name);
1132
+ }
1133
+ // Preserve every non-projection clause of the override (where/group/having/
1134
+ // order/limit/distinct/with — the filter shape, etc.); replace only the
1135
+ // projection with the composed logical-column list.
1136
+ const body = { ...select, columns: composed };
1137
+ return { body, provenance, effectiveColumns };
1138
+ }
1139
+ /** Walks an override's FROM tree, collecting introspectable basis-table sources. */
1140
+ function collectOverrideSources(from, basisSchemaName, schemaManager) {
1141
+ const sources = [];
1142
+ let hasOpaqueSource = false;
1143
+ const walk = (node) => {
1144
+ switch (node.type) {
1145
+ case 'table': {
1146
+ const schemaName = node.table.schema ?? basisSchemaName;
1147
+ const tbl = schemaManager.getSchema(schemaName)?.getTable(node.table.name);
1148
+ if (!tbl) {
1149
+ hasOpaqueSource = true;
1150
+ return;
1151
+ }
1152
+ sources.push({ table: tbl, refName: node.alias ?? tbl.name });
1153
+ break;
1154
+ }
1155
+ case 'join': {
1156
+ walk(node.left);
1157
+ walk(node.right);
1158
+ break;
1159
+ }
1160
+ default:
1161
+ // subquerySource / functionSource — not introspectable in v1.
1162
+ hasOpaqueSource = true;
1163
+ break;
1164
+ }
1165
+ };
1166
+ if (from)
1167
+ for (const f of from)
1168
+ walk(f);
1169
+ return { sources, hasOpaqueSource };
1170
+ }
1171
+ /**
1172
+ * Validates that every `table` source reachable from an override's body resolves
1173
+ * to the declared basis schema. A table qualified with a *different* existing
1174
+ * schema would otherwise bind there silently (re-anchoring the lens off its
1175
+ * `over Y` basis); reject it at deploy time. Unqualified tables default to the
1176
+ * basis and are fine; tables qualified with the basis name are fine.
1177
+ *
1178
+ * The walk is reflective over the entire override `select` AST, so it descends
1179
+ * into subquery-source FROM trees, function-source argument subqueries, `with`
1180
+ * CTE bodies, compound (`union`/`intersect`/…) legs, and scalar/`where`/`in`/
1181
+ * `exists` subqueries — a cross-basis `z.Foo` in any of those nested positions is
1182
+ * flagged too, not only top-level `table`/`join` sources. Descent is *not* gated
1183
+ * on a `type` discriminant: some containers that hold nested SELECTs are plain
1184
+ * wrappers without one — notably `compound` (`{ op, select }`) and `orderBy`
1185
+ * clauses — so a type-gated walk would skip the tables nested under them.
1186
+ *
1187
+ * No CTE-name / alias scope tracking is needed. The check fires only on a `table`
1188
+ * node carrying an explicit, non-basis *schema qualifier*; CTE references and
1189
+ * FROM aliases are always bare (SQL has no `schema.cte` form) and the compiler
1190
+ * resolves a bare FROM table to the basis (see `collectOverrideSources`), so a
1191
+ * bare name is always either the basis or a CTE — never a cross-basis relation.
1192
+ * Only a schema-qualified table can be cross-basis, and that can never name a
1193
+ * CTE/alias, so the walk needs no in-scope-name set to avoid false positives.
1194
+ */
1195
+ function validateOverrideBasisSources(select, basisSchemaName, logicalSchemaName, logicalName) {
1196
+ const lowerBasis = basisSchemaName.toLowerCase();
1197
+ const stack = [select];
1198
+ while (stack.length > 0) {
1199
+ const node = stack.pop();
1200
+ if (!node || typeof node !== 'object')
1201
+ continue;
1202
+ if (node.type === 'table') {
1203
+ const source = node;
1204
+ const schema = source.table.schema;
1205
+ if (schema && schema.toLowerCase() !== lowerBasis) {
1206
+ throw new QuereusError(`lens: override for logical table '${logicalSchemaName}.${logicalName}' references basis relation '${schema}.${source.table.name}' outside the declared basis '${basisSchemaName}' (the lens is declared 'over ${basisSchemaName}'); an override's FROM may only reference the declared basis`, StatusCode.ERROR);
1207
+ }
1208
+ }
1209
+ // Reflective descent: push every nested object/array element. Arrays are
1210
+ // transparent here — Object.values yields their elements.
1211
+ for (const value of Object.values(node)) {
1212
+ if (value && typeof value === 'object')
1213
+ stack.push(value);
1214
+ }
1215
+ }
1216
+ }
1217
+ /** Output name a result column contributes: its alias, or the bare column name. */
1218
+ function deriveColumnOutputName(col) {
1219
+ if (col.alias)
1220
+ return col.alias;
1221
+ return col.expr.type === 'column' ? col.expr.name : undefined;
1222
+ }
1223
+ /** Builds a column reference, optionally qualified by a source ref name. */
1224
+ function columnRef(name, refName) {
1225
+ return refName ? { type: 'column', name, table: refName } : { type: 'column', name };
1226
+ }
1227
+ /** Finds a same-named basis column across the override's FROM sources. */
1228
+ function gapFillRef(colName, sources, qualify) {
1229
+ const lower = colName.toLowerCase();
1230
+ for (const src of sources) {
1231
+ const idx = src.table.columnIndexMap.get(lower);
1232
+ if (idx !== undefined) {
1233
+ // Reference the basis column by its actual name (casing); the logical
1234
+ // alias is applied by the caller.
1235
+ return columnRef(src.table.columns[idx].name, qualify ? src.refName : undefined);
1236
+ }
1237
+ }
1238
+ return undefined;
1239
+ }
1240
+ /** Diagnostic for an uncovered logical column the FROM can't gap-fill. */
1241
+ function gapFillError(logicalSchemaName, logicalName, colName, sources, hasOpaqueSource) {
1242
+ const sourceDesc = sources.length === 0
1243
+ ? 'the override FROM exposes no introspectable basis table'
1244
+ : `basis source(s) ${sources.map(s => s.table.name).join(', ')} have no column '${colName}'`;
1245
+ if (sources.length > 1 || hasOpaqueSource) {
1246
+ // Cross-basis / partial-coverage fidelity boundary (docs/lens.md § D2).
1247
+ return `lens: override for logical table '${logicalSchemaName}.${logicalName}' covers only some columns; uncovered column '${colName}' is not reachable from the override's FROM (${sourceDesc}) — it would need a basis source the single-source mapper cannot join in. Cover it explicitly.`;
1248
+ }
1249
+ // Every logical column must map to basis (docs/lens.md § Gap-fill fidelity boundary).
1250
+ return `lens: override for logical table '${logicalSchemaName}.${logicalName}' leaves column '${colName}' uncovered and ${sourceDesc} to gap-fill it from — cover it explicitly in the override projection (every logical column must map to basis).`;
1251
+ }
1252
+ // ===========================================================================
1253
+ // Module mapping advertisement resolution (docs/lens.md § The Default Mapper)
1254
+ // ===========================================================================
1255
+ //
1256
+ // The protocol seam: a virtual-table module advertises how a set of its basis
1257
+ // relations decomposes a logical table (columnar split / EAV / column-family /
1258
+ // nd-tree). This compiler **resolves** (collects + selects the single primary +
1259
+ // validates) and **stores** the advertisement on the lens slot; it does NOT
1260
+ // synthesize the n-way body — that is `lens-multi-source-decomposition`, which
1261
+ // reads `slot.advertisement`. Validation aborts the deploy atomically (before
1262
+ // any catalog mutation), aggregating every problem with a named site, matching
1263
+ // the contract `validateLensTags` already follows.
1264
+ /**
1265
+ * Collects the mapping advertisements every module owning ≥1 table in `basis`
1266
+ * recognizes, deduplicated by the advertisement `id`. A generic module
1267
+ * (memory/store) returns tag-derived advertisements; a generic module that scans
1268
+ * the whole schema may be hit once per distinct module instance, so the same
1269
+ * tag-derived advertisement can appear twice — the `id` dedup collapses those.
1270
+ */
1271
+ function collectAdvertisements(db, basis) {
1272
+ const modules = new Set();
1273
+ for (const table of basis.getAllTables()) {
1274
+ if (table.vtabModule)
1275
+ modules.add(table.vtabModule);
1276
+ }
1277
+ const byId = new Map();
1278
+ for (const module of modules) {
1279
+ const ads = module.getMappingAdvertisements?.(db, basis) ?? [];
1280
+ for (const ad of ads) {
1281
+ if (!byId.has(ad.id))
1282
+ byId.set(ad.id, ad);
1283
+ }
1284
+ }
1285
+ return Array.from(byId.values());
1286
+ }
1287
+ /**
1288
+ * Resolves the advertisements for one logical table: filters to the table,
1289
+ * selects the single `primary-storage` (accommodation #5 — two is an error),
1290
+ * keeps the rest as `auxiliary-access`, and validates the primary's structural
1291
+ * coherence. Returns the resolved slot fields; throws (aggregated) on any
1292
+ * validation failure so the deploy aborts before catalog mutation.
1293
+ *
1294
+ * `hasOverride` relaxes the per-column coverage check: when an override exists
1295
+ * its own coverage validation (`compileOverrideBody`) owns the column-coverage
1296
+ * verdict, so the advertisement is only checked for internal coherence.
1297
+ */
1298
+ function resolveAdvertisement(all, logicalTable, basis, db, logicalSchemaName, hasOverride) {
1299
+ const lower = logicalTable.name.toLowerCase();
1300
+ const matching = all.filter(a => a.logicalTable.toLowerCase() === lower);
1301
+ if (matching.length === 0)
1302
+ return {};
1303
+ const primaries = matching.filter(a => a.role === 'primary-storage');
1304
+ const auxiliaries = matching.filter(a => a.role === 'auxiliary-access');
1305
+ const errors = [];
1306
+ if (primaries.length > 1) {
1307
+ errors.push(`has ${primaries.length} primary-storage advertisements (${primaries.map(p => `'${p.id}'`).join(', ')}); at most one is allowed`);
1308
+ }
1309
+ const advertisement = primaries[0];
1310
+ if (advertisement) {
1311
+ validatePrimaryAdvertisement(advertisement, logicalTable, basis, db, hasOverride, errors);
1312
+ }
1313
+ for (const aux of auxiliaries) {
1314
+ validateAuxiliaryAdvertisement(aux, basis, db, errors);
1315
+ }
1316
+ if (errors.length > 0) {
1317
+ throw new QuereusError(`lens: advertisement for logical table '${logicalSchemaName}.${logicalTable.name}' is invalid: ${errors.join('; ')}`, StatusCode.ERROR);
1318
+ }
1319
+ return {
1320
+ advertisement,
1321
+ auxiliaryAccess: auxiliaries.length > 0 ? auxiliaries : undefined,
1322
+ };
1323
+ }
1324
+ /**
1325
+ * Validates a `primary-storage` advertisement's structural coherence. Each check
1326
+ * pushes a sited message to `errors` (aggregated by the caller); none mutate the
1327
+ * catalog. See `docs/lens.md` § The Default Mapper for the field semantics.
1328
+ */
1329
+ function validatePrimaryAdvertisement(ad, logicalTable, basis, db, hasOverride, errors) {
1330
+ const storage = ad.storage;
1331
+ if (!storage) {
1332
+ errors.push(`role 'primary-storage' requires a storage shape`);
1333
+ return;
1334
+ }
1335
+ // The IND existence-anchor contract: id == anchorRelationId (so the INDs the
1336
+ // synthesis ticket injects, with IndTarget.kind:'relation'.relationId, and the
1337
+ // join it builds agree on the anchor).
1338
+ if (ad.id !== storage.anchorRelationId) {
1339
+ errors.push(`advertisement id '${ad.id}' must equal storage.anchorRelationId '${storage.anchorRelationId}'`);
1340
+ }
1341
+ const memberByRelationId = new Map();
1342
+ for (const member of storage.members)
1343
+ memberByRelationId.set(member.relationId, member);
1344
+ if (!memberByRelationId.has(storage.anchorRelationId)) {
1345
+ errors.push(`anchor '${storage.anchorRelationId}' is not among the members (${storage.members.map(m => `'${m.relationId}'`).join(', ') || 'none'})`);
1346
+ }
1347
+ // Resolve each member's basis table; validate column / pivot existence.
1348
+ const memberTables = new Map();
1349
+ for (const member of storage.members) {
1350
+ const table = resolveBasisRelation(db, member, basis);
1351
+ if (!table) {
1352
+ errors.push(`member '${member.relationId}' references basis relation '${member.relation.schema}.${member.relation.table}', which does not exist`);
1353
+ continue;
1354
+ }
1355
+ memberTables.set(member.relationId, table);
1356
+ for (const mapping of member.columns) {
1357
+ for (const refName of collectColumnRefNames(mapping.basisExpr)) {
1358
+ if (!table.columnIndexMap.has(refName.toLowerCase())) {
1359
+ errors.push(`member '${member.relationId}' maps logical column '${mapping.logicalColumn}' to basis expression referencing column '${refName}', which does not exist on '${table.name}'`);
1360
+ }
1361
+ }
1362
+ }
1363
+ if (member.attributePivot) {
1364
+ for (const [role, col] of [
1365
+ ['entity', member.attributePivot.entityColumn],
1366
+ ['attribute', member.attributePivot.attributeColumn],
1367
+ ['value', member.attributePivot.valueColumn],
1368
+ ]) {
1369
+ if (!table.columnIndexMap.has(col.toLowerCase())) {
1370
+ errors.push(`member '${member.relationId}' attributePivot ${role} column '${col}' does not exist on '${table.name}'`);
1371
+ }
1372
+ }
1373
+ }
1374
+ }
1375
+ // Shared key: surrogate ⇒ the anchor's shared-key column declares a DEFAULT (the
1376
+ // engine evaluates it once per row and threads it via the EC — it chooses no ID
1377
+ // policy of its own); logical-tuple ⇒ the supplied logical PK, with each member's
1378
+ // key columns matching the logical PK arity.
1379
+ const sharedKey = storage.sharedKey;
1380
+ if (sharedKey.kind === 'surrogate') {
1381
+ // The surrogate's value must come from the anchor key column's declared
1382
+ // `default` (replacing the retired engine-invented `integer-auto` mint): an
1383
+ // INSERT evaluates it once per row, and the EC threads the captured value to
1384
+ // every member. A single-column anchor key whose column has no default is a
1385
+ // deploy-time error — there is nowhere for the surrogate to come from.
1386
+ const anchorKeys = sharedKey.keyColumnsByRelation.get(storage.anchorRelationId) ?? [];
1387
+ const anchorTable = memberTables.get(storage.anchorRelationId);
1388
+ if (anchorTable && anchorKeys.length === 1) {
1389
+ const keyCol = anchorTable.columns.find(c => c.name.toLowerCase() === anchorKeys[0].toLowerCase());
1390
+ if (keyCol && keyCol.defaultValue === null) {
1391
+ errors.push(`shared key is 'surrogate' but anchor '${storage.anchorRelationId}' key column '${anchorKeys[0]}' declares no DEFAULT; a surrogate's value comes from that column's default (e.g. \`default (coalesce((select max(${anchorKeys[0]}) from ${anchorTable.name}), 0) + mutation_ordinal())\`) — the engine no longer auto-generates one`);
1392
+ }
1393
+ }
1394
+ // A surrogate is not tied to the logical PK arity, but the equi-join pairs
1395
+ // each member's key columns positionally with the anchor's, so they must all
1396
+ // share one arity. Validate it here: an under-arity member would otherwise
1397
+ // silently under-join (`buildKeyEquiJoin` pairs by `Math.min`) rather than
1398
+ // error, multiplying rows instead of stitching them.
1399
+ const anchorArity = sharedKey.keyColumnsByRelation.get(storage.anchorRelationId)?.length;
1400
+ if (anchorArity !== undefined) {
1401
+ for (const member of storage.members) {
1402
+ const keyCols = sharedKey.keyColumnsByRelation.get(member.relationId);
1403
+ if (keyCols && keyCols.length !== anchorArity) {
1404
+ errors.push(`shared key is 'surrogate' but member '${member.relationId}' has ${keyCols.length} key column(s), not the anchor '${storage.anchorRelationId}' surrogate arity (${anchorArity})`);
1405
+ }
1406
+ }
1407
+ }
1408
+ }
1409
+ if (sharedKey.kind === 'logical-tuple') {
1410
+ const pkArity = logicalTable.primaryKeyDefinition.length;
1411
+ for (const member of storage.members) {
1412
+ const keyCols = sharedKey.keyColumnsByRelation.get(member.relationId);
1413
+ if (keyCols && keyCols.length !== pkArity) {
1414
+ errors.push(`shared key is 'logical-tuple' but member '${member.relationId}' has ${keyCols.length} key column(s), not the logical primary key's arity (${pkArity})`);
1415
+ }
1416
+ }
1417
+ }
1418
+ // keyColumnsByRelation covers every member and each named column exists.
1419
+ for (const member of storage.members) {
1420
+ const keyCols = sharedKey.keyColumnsByRelation.get(member.relationId);
1421
+ if (keyCols === undefined) {
1422
+ errors.push(`shared key has no key columns for member '${member.relationId}'`);
1423
+ continue;
1424
+ }
1425
+ const table = memberTables.get(member.relationId);
1426
+ if (!table)
1427
+ continue; // already reported as a missing relation
1428
+ for (const col of keyCols) {
1429
+ if (!table.columnIndexMap.has(col.toLowerCase())) {
1430
+ errors.push(`shared key column '${col}' for member '${member.relationId}' does not exist on '${table.name}'`);
1431
+ }
1432
+ }
1433
+ }
1434
+ // Stitch-key / EAV-conflict-target uniqueness (docs/lens.md § The `put`
1435
+ // fan-out). The put fan-out cedes the matched rows to the matched
1436
+ // UPDATE only through the materialize INSERT's `on conflict (<target>) do nothing`,
1437
+ // which the runtime fires solely on a declared PK / UNIQUE violation; the get side
1438
+ // is sound only when that same target is 1:1 (a non-unique columnar stitch key
1439
+ // multiplies the equi-join, a non-unique `(entity, attr)` makes the EAV correlated
1440
+ // subquery multi-valued). So every member's materialize conflict target must equal a
1441
+ // declared PRIMARY KEY or non-partial UNIQUE on its basis table. Validated once here
1442
+ // at deploy — the only place that governs both directions of the lens — so the
1443
+ // plan-time materialize builders may rely on it without re-checking. The anchor is
1444
+ // validated too: its own stitch key must be unique for the logical-PK / surrogate
1445
+ // identity to be 1:1.
1446
+ for (const member of storage.members) {
1447
+ const table = memberTables.get(member.relationId);
1448
+ if (!table)
1449
+ continue; // missing relation already reported above
1450
+ if (member.attributePivot) {
1451
+ // EAV: the conflict target is `(entity, attribute)`, NOT the stitch key
1452
+ // (`entity` alone, which is deliberately one-to-many across attributes).
1453
+ const target = [member.attributePivot.entityColumn, member.attributePivot.attributeColumn];
1454
+ const idx = resolveColumnIndices(table, target);
1455
+ if (idx && !columnsFormDeclaredKey(table, idx)) {
1456
+ errors.push(`EAV pivot member '${member.relationId}' conflict target (${target.join(', ')}) is not a declared PRIMARY KEY or UNIQUE constraint on '${table.name}'; the get-side correlated subquery requires (entity, attribute) single-valued and the per-attribute materialize INSERT cedes matched triples via \`on conflict (${target.join(', ')}) do nothing\` — declare a PRIMARY KEY / UNIQUE on those columns`);
1457
+ }
1458
+ continue;
1459
+ }
1460
+ const keyCols = sharedKey.keyColumnsByRelation.get(member.relationId) ?? [];
1461
+ if (keyCols.length === 0)
1462
+ continue; // singleton (`primary key ()`) — no stitch, no materialize path
1463
+ const idx = resolveColumnIndices(table, keyCols);
1464
+ if (idx && !columnsFormDeclaredKey(table, idx)) {
1465
+ errors.push(`member '${member.relationId}' stitch key (${keyCols.join(', ')}) is not a declared PRIMARY KEY or UNIQUE constraint on basis relation '${table.name}'; the decomposition equi-join requires a 1:1 stitch and the optional-member materialize INSERT's \`on conflict (${keyCols.join(', ')}) do nothing\` only cedes matched rows against a declared unique — declare a PRIMARY KEY / UNIQUE on those columns`);
1466
+ }
1467
+ }
1468
+ // Column coverage (only when there is no override — an override's own coverage
1469
+ // validation owns the verdict otherwise). Every logical column must be backed
1470
+ // by exactly one member mapping, or by an EAV pivot member, or be coverable by
1471
+ // name-match against the basis. Otherwise the advertisement claims the table
1472
+ // but leaves the column unbacked and uncovered → error, naming the column.
1473
+ if (!hasOverride) {
1474
+ const backedBy = buildColumnBackingMap(storage);
1475
+ const hasEavMember = storage.members.some(m => m.attributePivot);
1476
+ const nameMatchTable = basis.schema.getTable(logicalTable.name);
1477
+ for (const col of logicalTable.columns) {
1478
+ const lc = col.name.toLowerCase();
1479
+ if (backedBy.get(lc) === 'ambiguous') {
1480
+ errors.push(`logical column '${col.name}' is backed by more than one member mapping (must be exactly one)`);
1481
+ continue;
1482
+ }
1483
+ if (backedBy.has(lc))
1484
+ continue; // exactly one member maps it
1485
+ if (hasEavMember)
1486
+ continue; // an EAV pivot member backs it generically
1487
+ if (nameMatchTable?.columnIndexMap.has(lc))
1488
+ continue; // left to name-match
1489
+ errors.push(`logical column '${col.name}' is left unbacked by the advertisement and is not coverable by name-match (cover it with a member mapping, an EAV pivot, or a name-matching basis column)`);
1490
+ }
1491
+ }
1492
+ }
1493
+ /**
1494
+ * Minimal validation for an `auxiliary-access` advertisement: every member
1495
+ * relation must resolve. The advertised predicate forms are not validated here —
1496
+ * the read-path consumer (`rule-lens-auxiliary-access`, `lens-access-shape-path-selection`)
1497
+ * matches them at plan time through its recognizer registry and silently degrades
1498
+ * to scan for any form it cannot serve, so an unknown form is never an error.
1499
+ */
1500
+ function validateAuxiliaryAdvertisement(ad, basis, db, errors) {
1501
+ if (!ad.storage)
1502
+ return; // an auxiliary may carry access-only shape
1503
+ for (const member of ad.storage.members) {
1504
+ if (!resolveBasisRelation(db, member, basis)) {
1505
+ errors.push(`auxiliary advertisement '${ad.id}' member '${member.relationId}' references basis relation '${member.relation.schema}.${member.relation.table}', which does not exist`);
1506
+ }
1507
+ }
1508
+ }
1509
+ /**
1510
+ * Builds `logicalColumn(lower) -> member relationId | 'ambiguous'` from the
1511
+ * explicit per-member column mappings (EAV pivots are handled separately by the
1512
+ * caller). A column mapped by two members is `'ambiguous'`.
1513
+ */
1514
+ function buildColumnBackingMap(storage) {
1515
+ const backedBy = new Map();
1516
+ for (const member of storage.members) {
1517
+ for (const mapping of member.columns) {
1518
+ const lc = mapping.logicalColumn.toLowerCase();
1519
+ backedBy.set(lc, backedBy.has(lc) ? 'ambiguous' : member.relationId);
1520
+ }
1521
+ }
1522
+ return backedBy;
1523
+ }
1524
+ /**
1525
+ * Resolves a list of column names to their indices on `table` via `columnIndexMap`.
1526
+ * Returns `undefined` if any name is unresolved — the caller (key-column / pivot
1527
+ * existence loops) already reports an unresolved name, so the uniqueness check just
1528
+ * skips rather than double-reporting.
1529
+ */
1530
+ function resolveColumnIndices(table, names) {
1531
+ const out = [];
1532
+ for (const n of names) {
1533
+ const i = table.columnIndexMap.get(n.toLowerCase());
1534
+ if (i === undefined)
1535
+ return undefined; // unresolved name already reported elsewhere
1536
+ out.push(i);
1537
+ }
1538
+ return out;
1539
+ }
1540
+ /** Resolves a member's {@link BasisRelationRef} to a concrete basis table. */
1541
+ function resolveBasisRelation(db, member, basis) {
1542
+ const schemaName = member.relation.schema || basis.schemaName;
1543
+ const schema = schemaName.toLowerCase() === basis.schemaName.toLowerCase()
1544
+ ? basis.schema
1545
+ : db.schemaManager.getSchema(schemaName);
1546
+ return schema?.getTable(member.relation.table);
1547
+ }
1548
+ /**
1549
+ * Annotates each provenance entry with the member `relationId` that backs its
1550
+ * logical column, when the resolved advertisement maps it. Explicit mappings win;
1551
+ * a column with no explicit mapping is attributed to the sole EAV pivot member
1552
+ * when one exists. Surfaced by `quereus_effective_lens`.
1553
+ */
1554
+ function annotateProvenanceWithAdvertisement(provenance, ad) {
1555
+ const storage = ad.storage;
1556
+ if (!storage)
1557
+ return;
1558
+ const backedBy = buildColumnBackingMap(storage);
1559
+ const eavMembers = storage.members.filter(m => m.attributePivot);
1560
+ const soleEav = eavMembers.length === 1 ? eavMembers[0].relationId : undefined;
1561
+ for (const p of provenance) {
1562
+ const explicit = backedBy.get(p.logicalColumn.toLowerCase());
1563
+ if (explicit && explicit !== 'ambiguous') {
1564
+ p.advertisedBy = explicit;
1565
+ }
1566
+ else if (soleEav) {
1567
+ p.advertisedBy = soleEav;
1568
+ }
1569
+ }
1570
+ }
1571
+ /**
1572
+ * Override ⊕ advertisement conflict (docs/lens.md § Override-vs-advertisement
1573
+ * composition): a *sparse* override (one that relies on gap-fill) may correct an
1574
+ * advertised column mapping, but must NOT re-anchor or reference basis relations
1575
+ * outside the advertised decomposition. Every introspectable basis-table source
1576
+ * in the override's FROM must be one of the advertisement's member relations;
1577
+ * otherwise the developer is silently re-anchoring and must instead author a full
1578
+ * hand-authored body (which bypasses the advertisement entirely). Opaque sources
1579
+ * (subquery / function) are not introspectable and do not trip the check.
1580
+ */
1581
+ function validateOverrideAdvertisementConflict(ad, override, basisSchemaName, schemaManager, logicalSchemaName, logicalName) {
1582
+ const storage = ad.storage;
1583
+ if (!storage)
1584
+ return;
1585
+ const memberRelations = new Set(storage.members.map(m => `${(m.relation.schema || basisSchemaName).toLowerCase()}.${m.relation.table.toLowerCase()}`));
1586
+ const { sources } = collectOverrideSources(override.select.from, basisSchemaName, schemaManager);
1587
+ for (const src of sources) {
1588
+ const key = `${src.table.schemaName.toLowerCase()}.${src.table.name.toLowerCase()}`;
1589
+ if (!memberRelations.has(key)) {
1590
+ throw new QuereusError(`lens: override for logical table '${logicalSchemaName}.${logicalName}' references basis relation '${src.table.schemaName}.${src.table.name}', which is not part of the advertised decomposition (anchor '${storage.anchorRelationId}', members: ${storage.members.map(m => `'${m.relation.table}'`).join(', ')}); a partial override may not re-anchor or change the shared key — cover every logical column explicitly to author a full body that bypasses the advertisement, or align the override's FROM with the decomposition`, StatusCode.ERROR);
1591
+ }
1592
+ }
1593
+ }
1594
+ //# sourceMappingURL=lens-compiler.js.map