@quereus/quereus 2.8.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (454) hide show
  1. package/README.md +224 -222
  2. package/dist/src/core/database-assertions.d.ts +36 -16
  3. package/dist/src/core/database-assertions.d.ts.map +1 -1
  4. package/dist/src/core/database-assertions.js +222 -118
  5. package/dist/src/core/database-assertions.js.map +1 -1
  6. package/dist/src/core/database-transaction.d.ts +96 -13
  7. package/dist/src/core/database-transaction.d.ts.map +1 -1
  8. package/dist/src/core/database-transaction.js +294 -35
  9. package/dist/src/core/database-transaction.js.map +1 -1
  10. package/dist/src/core/database-watchers.d.ts +58 -0
  11. package/dist/src/core/database-watchers.d.ts.map +1 -0
  12. package/dist/src/core/database-watchers.js +206 -0
  13. package/dist/src/core/database-watchers.js.map +1 -0
  14. package/dist/src/core/database.d.ts +78 -5
  15. package/dist/src/core/database.d.ts.map +1 -1
  16. package/dist/src/core/database.js +120 -20
  17. package/dist/src/core/database.js.map +1 -1
  18. package/dist/src/core/statement.d.ts +9 -0
  19. package/dist/src/core/statement.d.ts.map +1 -1
  20. package/dist/src/core/statement.js +29 -0
  21. package/dist/src/core/statement.js.map +1 -1
  22. package/dist/src/core/table-handle.d.ts +45 -0
  23. package/dist/src/core/table-handle.d.ts.map +1 -0
  24. package/dist/src/core/table-handle.js +54 -0
  25. package/dist/src/core/table-handle.js.map +1 -0
  26. package/dist/src/emit/ast-stringify.d.ts.map +1 -1
  27. package/dist/src/emit/ast-stringify.js +0 -3
  28. package/dist/src/emit/ast-stringify.js.map +1 -1
  29. package/dist/src/func/builtins/conversion.d.ts.map +1 -1
  30. package/dist/src/func/builtins/conversion.js +12 -1
  31. package/dist/src/func/builtins/conversion.js.map +1 -1
  32. package/dist/src/func/builtins/explain.d.ts.map +1 -1
  33. package/dist/src/func/builtins/explain.js +22 -8
  34. package/dist/src/func/builtins/explain.js.map +1 -1
  35. package/dist/src/func/builtins/generation.d.ts.map +1 -1
  36. package/dist/src/func/builtins/generation.js +26 -1
  37. package/dist/src/func/builtins/generation.js.map +1 -1
  38. package/dist/src/func/builtins/index.d.ts.map +1 -1
  39. package/dist/src/func/builtins/index.js +5 -1
  40. package/dist/src/func/builtins/index.js.map +1 -1
  41. package/dist/src/func/builtins/json-tvf.d.ts.map +1 -1
  42. package/dist/src/func/builtins/json-tvf.js +16 -2
  43. package/dist/src/func/builtins/json-tvf.js.map +1 -1
  44. package/dist/src/func/builtins/schema.d.ts +4 -0
  45. package/dist/src/func/builtins/schema.d.ts.map +1 -1
  46. package/dist/src/func/builtins/schema.js +270 -11
  47. package/dist/src/func/builtins/schema.js.map +1 -1
  48. package/dist/src/func/registration.d.ts +19 -1
  49. package/dist/src/func/registration.d.ts.map +1 -1
  50. package/dist/src/func/registration.js +8 -3
  51. package/dist/src/func/registration.js.map +1 -1
  52. package/dist/src/index.d.ts +7 -1
  53. package/dist/src/index.d.ts.map +1 -1
  54. package/dist/src/index.js +5 -0
  55. package/dist/src/index.js.map +1 -1
  56. package/dist/src/parser/ast.d.ts +3 -2
  57. package/dist/src/parser/ast.d.ts.map +1 -1
  58. package/dist/src/parser/parser.d.ts.map +1 -1
  59. package/dist/src/parser/parser.js +25 -8
  60. package/dist/src/parser/parser.js.map +1 -1
  61. package/dist/src/planner/analysis/assertion-classifier.d.ts +71 -0
  62. package/dist/src/planner/analysis/assertion-classifier.d.ts.map +1 -0
  63. package/dist/src/planner/analysis/assertion-classifier.js +286 -0
  64. package/dist/src/planner/analysis/assertion-classifier.js.map +1 -0
  65. package/dist/src/planner/analysis/assertion-hoist-cache.d.ts +34 -0
  66. package/dist/src/planner/analysis/assertion-hoist-cache.d.ts.map +1 -0
  67. package/dist/src/planner/analysis/assertion-hoist-cache.js +119 -0
  68. package/dist/src/planner/analysis/assertion-hoist-cache.js.map +1 -0
  69. package/dist/src/planner/analysis/binding-extractor.d.ts +58 -0
  70. package/dist/src/planner/analysis/binding-extractor.d.ts.map +1 -0
  71. package/dist/src/planner/analysis/binding-extractor.js +110 -0
  72. package/dist/src/planner/analysis/binding-extractor.js.map +1 -0
  73. package/dist/src/planner/analysis/change-scope.d.ts +184 -0
  74. package/dist/src/planner/analysis/change-scope.d.ts.map +1 -0
  75. package/dist/src/planner/analysis/change-scope.js +825 -0
  76. package/dist/src/planner/analysis/change-scope.js.map +1 -0
  77. package/dist/src/planner/analysis/check-extraction.d.ts +29 -0
  78. package/dist/src/planner/analysis/check-extraction.d.ts.map +1 -0
  79. package/dist/src/planner/analysis/check-extraction.js +420 -0
  80. package/dist/src/planner/analysis/check-extraction.js.map +1 -0
  81. package/dist/src/planner/analysis/constraint-extractor.d.ts +47 -7
  82. package/dist/src/planner/analysis/constraint-extractor.d.ts.map +1 -1
  83. package/dist/src/planner/analysis/constraint-extractor.js +169 -92
  84. package/dist/src/planner/analysis/constraint-extractor.js.map +1 -1
  85. package/dist/src/planner/analysis/partial-unique-extraction.d.ts +68 -0
  86. package/dist/src/planner/analysis/partial-unique-extraction.d.ts.map +1 -0
  87. package/dist/src/planner/analysis/partial-unique-extraction.js +347 -0
  88. package/dist/src/planner/analysis/partial-unique-extraction.js.map +1 -0
  89. package/dist/src/planner/analysis/predicate-conjuncts.d.ts +14 -0
  90. package/dist/src/planner/analysis/predicate-conjuncts.d.ts.map +1 -0
  91. package/dist/src/planner/analysis/predicate-conjuncts.js +31 -0
  92. package/dist/src/planner/analysis/predicate-conjuncts.js.map +1 -0
  93. package/dist/src/planner/analysis/predicate-shape.d.ts +52 -0
  94. package/dist/src/planner/analysis/predicate-shape.d.ts.map +1 -0
  95. package/dist/src/planner/analysis/predicate-shape.js +119 -0
  96. package/dist/src/planner/analysis/predicate-shape.js.map +1 -0
  97. package/dist/src/planner/analysis/sat-checker.d.ts +43 -0
  98. package/dist/src/planner/analysis/sat-checker.d.ts.map +1 -0
  99. package/dist/src/planner/analysis/sat-checker.js +393 -0
  100. package/dist/src/planner/analysis/sat-checker.js.map +1 -0
  101. package/dist/src/planner/building/foreign-key-builder.d.ts.map +1 -1
  102. package/dist/src/planner/building/foreign-key-builder.js +3 -2
  103. package/dist/src/planner/building/foreign-key-builder.js.map +1 -1
  104. package/dist/src/planner/building/select.js +14 -2
  105. package/dist/src/planner/building/select.js.map +1 -1
  106. package/dist/src/planner/building/table.d.ts.map +1 -1
  107. package/dist/src/planner/building/table.js +1 -1
  108. package/dist/src/planner/building/table.js.map +1 -1
  109. package/dist/src/planner/building/update.d.ts.map +1 -1
  110. package/dist/src/planner/building/update.js +10 -6
  111. package/dist/src/planner/building/update.js.map +1 -1
  112. package/dist/src/planner/framework/characteristics.d.ts +13 -2
  113. package/dist/src/planner/framework/characteristics.d.ts.map +1 -1
  114. package/dist/src/planner/framework/characteristics.js +31 -5
  115. package/dist/src/planner/framework/characteristics.js.map +1 -1
  116. package/dist/src/planner/framework/pass.d.ts.map +1 -1
  117. package/dist/src/planner/framework/pass.js +46 -16
  118. package/dist/src/planner/framework/pass.js.map +1 -1
  119. package/dist/src/planner/framework/physical-utils.d.ts +21 -9
  120. package/dist/src/planner/framework/physical-utils.d.ts.map +1 -1
  121. package/dist/src/planner/framework/physical-utils.js +47 -31
  122. package/dist/src/planner/framework/physical-utils.js.map +1 -1
  123. package/dist/src/planner/nodes/aggregate-node.d.ts +25 -0
  124. package/dist/src/planner/nodes/aggregate-node.d.ts.map +1 -1
  125. package/dist/src/planner/nodes/aggregate-node.js +75 -8
  126. package/dist/src/planner/nodes/aggregate-node.js.map +1 -1
  127. package/dist/src/planner/nodes/alias-node.d.ts.map +1 -1
  128. package/dist/src/planner/nodes/alias-node.js +8 -1
  129. package/dist/src/planner/nodes/alias-node.js.map +1 -1
  130. package/dist/src/planner/nodes/asof-scan-node.d.ts +137 -0
  131. package/dist/src/planner/nodes/asof-scan-node.d.ts.map +1 -0
  132. package/dist/src/planner/nodes/asof-scan-node.js +237 -0
  133. package/dist/src/planner/nodes/asof-scan-node.js.map +1 -0
  134. package/dist/src/planner/nodes/bloom-join-node.d.ts.map +1 -1
  135. package/dist/src/planner/nodes/bloom-join-node.js +19 -9
  136. package/dist/src/planner/nodes/bloom-join-node.js.map +1 -1
  137. package/dist/src/planner/nodes/constraint-check-node.d.ts +3 -0
  138. package/dist/src/planner/nodes/constraint-check-node.d.ts.map +1 -1
  139. package/dist/src/planner/nodes/constraint-check-node.js.map +1 -1
  140. package/dist/src/planner/nodes/distinct-node.d.ts.map +1 -1
  141. package/dist/src/planner/nodes/distinct-node.js +17 -6
  142. package/dist/src/planner/nodes/distinct-node.js.map +1 -1
  143. package/dist/src/planner/nodes/empty-relation-node.d.ts +27 -0
  144. package/dist/src/planner/nodes/empty-relation-node.d.ts.map +1 -0
  145. package/dist/src/planner/nodes/empty-relation-node.js +61 -0
  146. package/dist/src/planner/nodes/empty-relation-node.js.map +1 -0
  147. package/dist/src/planner/nodes/filter.d.ts.map +1 -1
  148. package/dist/src/planner/nodes/filter.js +67 -5
  149. package/dist/src/planner/nodes/filter.js.map +1 -1
  150. package/dist/src/planner/nodes/function.d.ts +11 -1
  151. package/dist/src/planner/nodes/function.d.ts.map +1 -1
  152. package/dist/src/planner/nodes/function.js +94 -1
  153. package/dist/src/planner/nodes/function.js.map +1 -1
  154. package/dist/src/planner/nodes/hash-aggregate.d.ts +1 -1
  155. package/dist/src/planner/nodes/hash-aggregate.d.ts.map +1 -1
  156. package/dist/src/planner/nodes/hash-aggregate.js +10 -6
  157. package/dist/src/planner/nodes/hash-aggregate.js.map +1 -1
  158. package/dist/src/planner/nodes/join-node.d.ts.map +1 -1
  159. package/dist/src/planner/nodes/join-node.js +21 -10
  160. package/dist/src/planner/nodes/join-node.js.map +1 -1
  161. package/dist/src/planner/nodes/join-utils.d.ts +42 -1
  162. package/dist/src/planner/nodes/join-utils.d.ts.map +1 -1
  163. package/dist/src/planner/nodes/join-utils.js +132 -0
  164. package/dist/src/planner/nodes/join-utils.js.map +1 -1
  165. package/dist/src/planner/nodes/limit-offset.d.ts.map +1 -1
  166. package/dist/src/planner/nodes/limit-offset.js +8 -1
  167. package/dist/src/planner/nodes/limit-offset.js.map +1 -1
  168. package/dist/src/planner/nodes/merge-join-node.d.ts.map +1 -1
  169. package/dist/src/planner/nodes/merge-join-node.js +22 -9
  170. package/dist/src/planner/nodes/merge-join-node.js.map +1 -1
  171. package/dist/src/planner/nodes/ordinal-slice-node.d.ts +50 -0
  172. package/dist/src/planner/nodes/ordinal-slice-node.d.ts.map +1 -0
  173. package/dist/src/planner/nodes/ordinal-slice-node.js +130 -0
  174. package/dist/src/planner/nodes/ordinal-slice-node.js.map +1 -0
  175. package/dist/src/planner/nodes/plan-node-type.d.ts +3 -0
  176. package/dist/src/planner/nodes/plan-node-type.d.ts.map +1 -1
  177. package/dist/src/planner/nodes/plan-node-type.js +3 -0
  178. package/dist/src/planner/nodes/plan-node-type.js.map +1 -1
  179. package/dist/src/planner/nodes/plan-node.d.ts +316 -5
  180. package/dist/src/planner/nodes/plan-node.d.ts.map +1 -1
  181. package/dist/src/planner/nodes/plan-node.js +49 -0
  182. package/dist/src/planner/nodes/plan-node.js.map +1 -1
  183. package/dist/src/planner/nodes/project-node.d.ts.map +1 -1
  184. package/dist/src/planner/nodes/project-node.js +78 -28
  185. package/dist/src/planner/nodes/project-node.js.map +1 -1
  186. package/dist/src/planner/nodes/reference.d.ts +27 -2
  187. package/dist/src/planner/nodes/reference.d.ts.map +1 -1
  188. package/dist/src/planner/nodes/reference.js +117 -1
  189. package/dist/src/planner/nodes/reference.js.map +1 -1
  190. package/dist/src/planner/nodes/retrieve-node.d.ts +9 -1
  191. package/dist/src/planner/nodes/retrieve-node.d.ts.map +1 -1
  192. package/dist/src/planner/nodes/retrieve-node.js +21 -0
  193. package/dist/src/planner/nodes/retrieve-node.js.map +1 -1
  194. package/dist/src/planner/nodes/returning-node.d.ts.map +1 -1
  195. package/dist/src/planner/nodes/returning-node.js +64 -28
  196. package/dist/src/planner/nodes/returning-node.js.map +1 -1
  197. package/dist/src/planner/nodes/scalar.d.ts +8 -1
  198. package/dist/src/planner/nodes/scalar.d.ts.map +1 -1
  199. package/dist/src/planner/nodes/scalar.js +112 -1
  200. package/dist/src/planner/nodes/scalar.js.map +1 -1
  201. package/dist/src/planner/nodes/set-operation-node.d.ts +2 -1
  202. package/dist/src/planner/nodes/set-operation-node.d.ts.map +1 -1
  203. package/dist/src/planner/nodes/set-operation-node.js +24 -0
  204. package/dist/src/planner/nodes/set-operation-node.js.map +1 -1
  205. package/dist/src/planner/nodes/single-row.d.ts.map +1 -1
  206. package/dist/src/planner/nodes/single-row.js +3 -1
  207. package/dist/src/planner/nodes/single-row.js.map +1 -1
  208. package/dist/src/planner/nodes/sort.d.ts.map +1 -1
  209. package/dist/src/planner/nodes/sort.js +28 -1
  210. package/dist/src/planner/nodes/sort.js.map +1 -1
  211. package/dist/src/planner/nodes/stream-aggregate.d.ts +1 -1
  212. package/dist/src/planner/nodes/stream-aggregate.d.ts.map +1 -1
  213. package/dist/src/planner/nodes/stream-aggregate.js +10 -8
  214. package/dist/src/planner/nodes/stream-aggregate.js.map +1 -1
  215. package/dist/src/planner/nodes/table-access-nodes.d.ts +40 -5
  216. package/dist/src/planner/nodes/table-access-nodes.d.ts.map +1 -1
  217. package/dist/src/planner/nodes/table-access-nodes.js +113 -18
  218. package/dist/src/planner/nodes/table-access-nodes.js.map +1 -1
  219. package/dist/src/planner/nodes/table-function-call.d.ts +4 -1
  220. package/dist/src/planner/nodes/table-function-call.d.ts.map +1 -1
  221. package/dist/src/planner/nodes/table-function-call.js +224 -14
  222. package/dist/src/planner/nodes/table-function-call.js.map +1 -1
  223. package/dist/src/planner/nodes/update-node.d.ts +1 -3
  224. package/dist/src/planner/nodes/update-node.d.ts.map +1 -1
  225. package/dist/src/planner/nodes/update-node.js +3 -9
  226. package/dist/src/planner/nodes/update-node.js.map +1 -1
  227. package/dist/src/planner/nodes/window-node.d.ts +61 -2
  228. package/dist/src/planner/nodes/window-node.d.ts.map +1 -1
  229. package/dist/src/planner/nodes/window-node.js +71 -3
  230. package/dist/src/planner/nodes/window-node.js.map +1 -1
  231. package/dist/src/planner/optimizer-tuning.d.ts +38 -1
  232. package/dist/src/planner/optimizer-tuning.d.ts.map +1 -1
  233. package/dist/src/planner/optimizer-tuning.js +6 -0
  234. package/dist/src/planner/optimizer-tuning.js.map +1 -1
  235. package/dist/src/planner/optimizer.d.ts.map +1 -1
  236. package/dist/src/planner/optimizer.js +278 -0
  237. package/dist/src/planner/optimizer.js.map +1 -1
  238. package/dist/src/planner/rules/access/rule-asof-strategy-select.d.ts +30 -0
  239. package/dist/src/planner/rules/access/rule-asof-strategy-select.d.ts.map +1 -0
  240. package/dist/src/planner/rules/access/rule-asof-strategy-select.js +112 -0
  241. package/dist/src/planner/rules/access/rule-asof-strategy-select.js.map +1 -0
  242. package/dist/src/planner/rules/access/rule-monotonic-limit-pushdown.d.ts +33 -0
  243. package/dist/src/planner/rules/access/rule-monotonic-limit-pushdown.d.ts.map +1 -0
  244. package/dist/src/planner/rules/access/rule-monotonic-limit-pushdown.js +162 -0
  245. package/dist/src/planner/rules/access/rule-monotonic-limit-pushdown.js.map +1 -0
  246. package/dist/src/planner/rules/access/rule-monotonic-range-access.d.ts +29 -0
  247. package/dist/src/planner/rules/access/rule-monotonic-range-access.d.ts.map +1 -0
  248. package/dist/src/planner/rules/access/rule-monotonic-range-access.js +175 -0
  249. package/dist/src/planner/rules/access/rule-monotonic-range-access.js.map +1 -0
  250. package/dist/src/planner/rules/access/rule-select-access-path.d.ts.map +1 -1
  251. package/dist/src/planner/rules/access/rule-select-access-path.js +53 -17
  252. package/dist/src/planner/rules/access/rule-select-access-path.js.map +1 -1
  253. package/dist/src/planner/rules/aggregate/rule-groupby-fd-simplification.d.ts +30 -0
  254. package/dist/src/planner/rules/aggregate/rule-groupby-fd-simplification.d.ts.map +1 -0
  255. package/dist/src/planner/rules/aggregate/rule-groupby-fd-simplification.js +116 -0
  256. package/dist/src/planner/rules/aggregate/rule-groupby-fd-simplification.js.map +1 -0
  257. package/dist/src/planner/rules/distinct/rule-distinct-elimination.d.ts +7 -7
  258. package/dist/src/planner/rules/distinct/rule-distinct-elimination.d.ts.map +1 -1
  259. package/dist/src/planner/rules/distinct/rule-distinct-elimination.js +18 -16
  260. package/dist/src/planner/rules/distinct/rule-distinct-elimination.js.map +1 -1
  261. package/dist/src/planner/rules/join/equi-pair-extractor.d.ts +61 -0
  262. package/dist/src/planner/rules/join/equi-pair-extractor.d.ts.map +1 -0
  263. package/dist/src/planner/rules/join/equi-pair-extractor.js +155 -0
  264. package/dist/src/planner/rules/join/equi-pair-extractor.js.map +1 -0
  265. package/dist/src/planner/rules/join/rule-join-elimination.d.ts +56 -0
  266. package/dist/src/planner/rules/join/rule-join-elimination.d.ts.map +1 -0
  267. package/dist/src/planner/rules/join/rule-join-elimination.js +326 -0
  268. package/dist/src/planner/rules/join/rule-join-elimination.js.map +1 -0
  269. package/dist/src/planner/rules/join/rule-join-greedy-commute.d.ts.map +1 -1
  270. package/dist/src/planner/rules/join/rule-join-greedy-commute.js +10 -2
  271. package/dist/src/planner/rules/join/rule-join-greedy-commute.js.map +1 -1
  272. package/dist/src/planner/rules/join/rule-join-physical-selection.d.ts.map +1 -1
  273. package/dist/src/planner/rules/join/rule-join-physical-selection.js +2 -122
  274. package/dist/src/planner/rules/join/rule-join-physical-selection.js.map +1 -1
  275. package/dist/src/planner/rules/join/rule-lateral-top1-asof.d.ts +21 -0
  276. package/dist/src/planner/rules/join/rule-lateral-top1-asof.d.ts.map +1 -0
  277. package/dist/src/planner/rules/join/rule-lateral-top1-asof.js +405 -0
  278. package/dist/src/planner/rules/join/rule-lateral-top1-asof.js.map +1 -0
  279. package/dist/src/planner/rules/join/rule-monotonic-merge-join.d.ts +31 -0
  280. package/dist/src/planner/rules/join/rule-monotonic-merge-join.d.ts.map +1 -0
  281. package/dist/src/planner/rules/join/rule-monotonic-merge-join.js +113 -0
  282. package/dist/src/planner/rules/join/rule-monotonic-merge-join.js.map +1 -0
  283. package/dist/src/planner/rules/predicate/rule-aggregate-predicate-pushdown.d.ts +20 -0
  284. package/dist/src/planner/rules/predicate/rule-aggregate-predicate-pushdown.d.ts.map +1 -0
  285. package/dist/src/planner/rules/predicate/rule-aggregate-predicate-pushdown.js +181 -0
  286. package/dist/src/planner/rules/predicate/rule-aggregate-predicate-pushdown.js.map +1 -0
  287. package/dist/src/planner/rules/predicate/rule-empty-relation-folding.d.ts +46 -0
  288. package/dist/src/planner/rules/predicate/rule-empty-relation-folding.d.ts.map +1 -0
  289. package/dist/src/planner/rules/predicate/rule-empty-relation-folding.js +156 -0
  290. package/dist/src/planner/rules/predicate/rule-empty-relation-folding.js.map +1 -0
  291. package/dist/src/planner/rules/predicate/rule-filter-contradiction.d.ts +30 -0
  292. package/dist/src/planner/rules/predicate/rule-filter-contradiction.d.ts.map +1 -0
  293. package/dist/src/planner/rules/predicate/rule-filter-contradiction.js +60 -0
  294. package/dist/src/planner/rules/predicate/rule-filter-contradiction.js.map +1 -0
  295. package/dist/src/planner/rules/predicate/rule-predicate-inference-equivalence.d.ts +45 -0
  296. package/dist/src/planner/rules/predicate/rule-predicate-inference-equivalence.d.ts.map +1 -0
  297. package/dist/src/planner/rules/predicate/rule-predicate-inference-equivalence.js +210 -0
  298. package/dist/src/planner/rules/predicate/rule-predicate-inference-equivalence.js.map +1 -0
  299. package/dist/src/planner/rules/predicate/rule-sargable-range-rewrite.d.ts +29 -0
  300. package/dist/src/planner/rules/predicate/rule-sargable-range-rewrite.d.ts.map +1 -0
  301. package/dist/src/planner/rules/predicate/rule-sargable-range-rewrite.js +161 -0
  302. package/dist/src/planner/rules/predicate/rule-sargable-range-rewrite.js.map +1 -0
  303. package/dist/src/planner/rules/sort/rule-orderby-fd-pruning.d.ts +39 -0
  304. package/dist/src/planner/rules/sort/rule-orderby-fd-pruning.d.ts.map +1 -0
  305. package/dist/src/planner/rules/sort/rule-orderby-fd-pruning.js +91 -0
  306. package/dist/src/planner/rules/sort/rule-orderby-fd-pruning.js.map +1 -0
  307. package/dist/src/planner/rules/subquery/rule-anti-join-fk-empty.d.ts +35 -0
  308. package/dist/src/planner/rules/subquery/rule-anti-join-fk-empty.d.ts.map +1 -0
  309. package/dist/src/planner/rules/subquery/rule-anti-join-fk-empty.js +74 -0
  310. package/dist/src/planner/rules/subquery/rule-anti-join-fk-empty.js.map +1 -0
  311. package/dist/src/planner/rules/subquery/rule-semi-join-fk-trivial.d.ts +27 -0
  312. package/dist/src/planner/rules/subquery/rule-semi-join-fk-trivial.d.ts.map +1 -0
  313. package/dist/src/planner/rules/subquery/rule-semi-join-fk-trivial.js +103 -0
  314. package/dist/src/planner/rules/subquery/rule-semi-join-fk-trivial.js.map +1 -0
  315. package/dist/src/planner/rules/subquery/rule-subquery-decorrelation.d.ts.map +1 -1
  316. package/dist/src/planner/rules/subquery/rule-subquery-decorrelation.js +1 -25
  317. package/dist/src/planner/rules/subquery/rule-subquery-decorrelation.js.map +1 -1
  318. package/dist/src/planner/rules/window/rule-monotonic-window.d.ts +47 -0
  319. package/dist/src/planner/rules/window/rule-monotonic-window.d.ts.map +1 -0
  320. package/dist/src/planner/rules/window/rule-monotonic-window.js +341 -0
  321. package/dist/src/planner/rules/window/rule-monotonic-window.js.map +1 -0
  322. package/dist/src/planner/scopes/global.js +2 -2
  323. package/dist/src/planner/scopes/global.js.map +1 -1
  324. package/dist/src/planner/type-utils.d.ts.map +1 -1
  325. package/dist/src/planner/type-utils.js +11 -0
  326. package/dist/src/planner/type-utils.js.map +1 -1
  327. package/dist/src/planner/util/fd-utils.d.ts +245 -0
  328. package/dist/src/planner/util/fd-utils.d.ts.map +1 -0
  329. package/dist/src/planner/util/fd-utils.js +1416 -0
  330. package/dist/src/planner/util/fd-utils.js.map +1 -0
  331. package/dist/src/planner/util/ind-utils.d.ts +79 -0
  332. package/dist/src/planner/util/ind-utils.d.ts.map +1 -0
  333. package/dist/src/planner/util/ind-utils.js +146 -0
  334. package/dist/src/planner/util/ind-utils.js.map +1 -0
  335. package/dist/src/planner/util/key-utils.d.ts +75 -14
  336. package/dist/src/planner/util/key-utils.d.ts.map +1 -1
  337. package/dist/src/planner/util/key-utils.js +234 -57
  338. package/dist/src/planner/util/key-utils.js.map +1 -1
  339. package/dist/src/runtime/context-helpers.d.ts +9 -0
  340. package/dist/src/runtime/context-helpers.d.ts.map +1 -1
  341. package/dist/src/runtime/context-helpers.js +5 -0
  342. package/dist/src/runtime/context-helpers.js.map +1 -1
  343. package/dist/src/runtime/delta-executor.d.ts +134 -0
  344. package/dist/src/runtime/delta-executor.d.ts.map +1 -0
  345. package/dist/src/runtime/delta-executor.js +382 -0
  346. package/dist/src/runtime/delta-executor.js.map +1 -0
  347. package/dist/src/runtime/emit/alter-table.d.ts.map +1 -1
  348. package/dist/src/runtime/emit/alter-table.js +52 -16
  349. package/dist/src/runtime/emit/alter-table.js.map +1 -1
  350. package/dist/src/runtime/emit/asof-scan.d.ts +10 -0
  351. package/dist/src/runtime/emit/asof-scan.d.ts.map +1 -0
  352. package/dist/src/runtime/emit/asof-scan.js +467 -0
  353. package/dist/src/runtime/emit/asof-scan.js.map +1 -0
  354. package/dist/src/runtime/emit/constraint-check.d.ts.map +1 -1
  355. package/dist/src/runtime/emit/constraint-check.js +20 -0
  356. package/dist/src/runtime/emit/constraint-check.js.map +1 -1
  357. package/dist/src/runtime/emit/create-assertion.d.ts.map +1 -1
  358. package/dist/src/runtime/emit/create-assertion.js +3 -2
  359. package/dist/src/runtime/emit/create-assertion.js.map +1 -1
  360. package/dist/src/runtime/emit/dml-executor.d.ts.map +1 -1
  361. package/dist/src/runtime/emit/dml-executor.js +40 -13
  362. package/dist/src/runtime/emit/dml-executor.js.map +1 -1
  363. package/dist/src/runtime/emit/drop-assertion.js +1 -1
  364. package/dist/src/runtime/emit/drop-assertion.js.map +1 -1
  365. package/dist/src/runtime/emit/empty-relation.d.ts +5 -0
  366. package/dist/src/runtime/emit/empty-relation.d.ts.map +1 -0
  367. package/dist/src/runtime/emit/empty-relation.js +11 -0
  368. package/dist/src/runtime/emit/empty-relation.js.map +1 -0
  369. package/dist/src/runtime/emit/ordinal-slice.d.ts +13 -0
  370. package/dist/src/runtime/emit/ordinal-slice.d.ts.map +1 -0
  371. package/dist/src/runtime/emit/ordinal-slice.js +89 -0
  372. package/dist/src/runtime/emit/ordinal-slice.js.map +1 -0
  373. package/dist/src/runtime/emit/returning.d.ts.map +1 -1
  374. package/dist/src/runtime/emit/returning.js +9 -4
  375. package/dist/src/runtime/emit/returning.js.map +1 -1
  376. package/dist/src/runtime/emit/scan.d.ts +19 -3
  377. package/dist/src/runtime/emit/scan.d.ts.map +1 -1
  378. package/dist/src/runtime/emit/scan.js +12 -8
  379. package/dist/src/runtime/emit/scan.js.map +1 -1
  380. package/dist/src/runtime/emit/schema-declarative.d.ts.map +1 -1
  381. package/dist/src/runtime/emit/schema-declarative.js +91 -14
  382. package/dist/src/runtime/emit/schema-declarative.js.map +1 -1
  383. package/dist/src/runtime/emit/window.d.ts.map +1 -1
  384. package/dist/src/runtime/emit/window.js +732 -37
  385. package/dist/src/runtime/emit/window.js.map +1 -1
  386. package/dist/src/runtime/foreign-key-actions.d.ts +16 -0
  387. package/dist/src/runtime/foreign-key-actions.d.ts.map +1 -1
  388. package/dist/src/runtime/foreign-key-actions.js +86 -5
  389. package/dist/src/runtime/foreign-key-actions.js.map +1 -1
  390. package/dist/src/runtime/register.d.ts.map +1 -1
  391. package/dist/src/runtime/register.js +6 -0
  392. package/dist/src/runtime/register.js.map +1 -1
  393. package/dist/src/schema/assertion.d.ts +8 -0
  394. package/dist/src/schema/assertion.d.ts.map +1 -1
  395. package/dist/src/schema/catalog.d.ts +10 -0
  396. package/dist/src/schema/catalog.d.ts.map +1 -1
  397. package/dist/src/schema/catalog.js +29 -6
  398. package/dist/src/schema/catalog.js.map +1 -1
  399. package/dist/src/schema/change-events.d.ts +5 -1
  400. package/dist/src/schema/change-events.d.ts.map +1 -1
  401. package/dist/src/schema/change-events.js.map +1 -1
  402. package/dist/src/schema/function.d.ts +89 -1
  403. package/dist/src/schema/function.d.ts.map +1 -1
  404. package/dist/src/schema/function.js +31 -0
  405. package/dist/src/schema/function.js.map +1 -1
  406. package/dist/src/schema/manager.d.ts +43 -0
  407. package/dist/src/schema/manager.d.ts.map +1 -1
  408. package/dist/src/schema/manager.js +105 -4
  409. package/dist/src/schema/manager.js.map +1 -1
  410. package/dist/src/schema/rename-rewriter.d.ts.map +1 -1
  411. package/dist/src/schema/rename-rewriter.js +303 -102
  412. package/dist/src/schema/rename-rewriter.js.map +1 -1
  413. package/dist/src/schema/schema-differ.d.ts +18 -1
  414. package/dist/src/schema/schema-differ.d.ts.map +1 -1
  415. package/dist/src/schema/schema-differ.js +307 -42
  416. package/dist/src/schema/schema-differ.js.map +1 -1
  417. package/dist/src/schema/table.d.ts +21 -2
  418. package/dist/src/schema/table.d.ts.map +1 -1
  419. package/dist/src/schema/table.js +17 -8
  420. package/dist/src/schema/table.js.map +1 -1
  421. package/dist/src/types/logical-type.d.ts +11 -0
  422. package/dist/src/types/logical-type.d.ts.map +1 -1
  423. package/dist/src/types/logical-type.js.map +1 -1
  424. package/dist/src/types/temporal-types.d.ts.map +1 -1
  425. package/dist/src/types/temporal-types.js +32 -0
  426. package/dist/src/types/temporal-types.js.map +1 -1
  427. package/dist/src/util/ast-literal.d.ts +11 -0
  428. package/dist/src/util/ast-literal.d.ts.map +1 -0
  429. package/dist/src/util/ast-literal.js +26 -0
  430. package/dist/src/util/ast-literal.js.map +1 -0
  431. package/dist/src/vtab/best-access-plan.d.ts +41 -0
  432. package/dist/src/vtab/best-access-plan.d.ts.map +1 -1
  433. package/dist/src/vtab/best-access-plan.js +29 -0
  434. package/dist/src/vtab/best-access-plan.js.map +1 -1
  435. package/dist/src/vtab/events.d.ts +9 -0
  436. package/dist/src/vtab/events.d.ts.map +1 -1
  437. package/dist/src/vtab/events.js +19 -0
  438. package/dist/src/vtab/events.js.map +1 -1
  439. package/dist/src/vtab/filter-info.d.ts +14 -0
  440. package/dist/src/vtab/filter-info.d.ts.map +1 -1
  441. package/dist/src/vtab/memory/layer/manager.d.ts.map +1 -1
  442. package/dist/src/vtab/memory/layer/manager.js +24 -5
  443. package/dist/src/vtab/memory/layer/manager.js.map +1 -1
  444. package/dist/src/vtab/memory/module.d.ts +39 -1
  445. package/dist/src/vtab/memory/module.d.ts.map +1 -1
  446. package/dist/src/vtab/memory/module.js +206 -44
  447. package/dist/src/vtab/memory/module.js.map +1 -1
  448. package/dist/src/vtab/memory/utils/predicate.d.ts +2 -1
  449. package/dist/src/vtab/memory/utils/predicate.d.ts.map +1 -1
  450. package/dist/src/vtab/memory/utils/predicate.js +32 -1
  451. package/dist/src/vtab/memory/utils/predicate.js.map +1 -1
  452. package/dist/src/vtab/module.d.ts +24 -0
  453. package/dist/src/vtab/module.d.ts.map +1 -1
  454. package/package.json +3 -3
@@ -0,0 +1,1416 @@
1
+ /**
2
+ * Functional dependency (FD) and equivalence-class (EC) helpers used by
3
+ * `computePhysical` on relational plan nodes. See `docs/optimizer.md`
4
+ * section "Functional Dependency Tracking" for the propagation table and
5
+ * design rationale.
6
+ */
7
+ import { createLogger } from '../../common/logger.js';
8
+ import { ColumnReferenceNode, ParameterReferenceNode } from '../nodes/reference.js';
9
+ import { BetweenNode, BinaryOpNode, CastNode, CollateNode, LiteralNode, UnaryOpNode } from '../nodes/scalar.js';
10
+ import { InNode } from '../nodes/subquery.js';
11
+ import { compareSqlValues } from '../../util/comparison.js';
12
+ import { flipComparison } from '../analysis/predicate-shape.js';
13
+ const log = createLogger('planner:fd');
14
+ /**
15
+ * Per-node cap on the number of FDs we materialize. The propagation rules
16
+ * are conservative enough that hitting this in practice is rare; the cap
17
+ * is a safety valve for pathological plans.
18
+ */
19
+ export const MAX_FDS_PER_NODE = 64;
20
+ /**
21
+ * Closure of `attrs` under `fds`. Iterative fixed-point.
22
+ *
23
+ * Guarded FDs (`fd.guard !== undefined`) are skipped — they are only valid
24
+ * under a surrounding predicate, and the closure layer has no notion of one.
25
+ * Filter activation strips the guard before the FD reaches closure consumers.
26
+ *
27
+ * O(|fds| × growth) — terminates when no new attribute is added in a pass.
28
+ */
29
+ export function computeClosure(attrs, fds) {
30
+ const closure = new Set(attrs);
31
+ let changed = true;
32
+ while (changed) {
33
+ changed = false;
34
+ for (const fd of fds) {
35
+ if (fd.guard !== undefined)
36
+ continue;
37
+ if (fd.determinants.every(d => closure.has(d))) {
38
+ for (const dep of fd.dependents) {
39
+ if (!closure.has(dep)) {
40
+ closure.add(dep);
41
+ changed = true;
42
+ }
43
+ }
44
+ }
45
+ }
46
+ }
47
+ return closure;
48
+ }
49
+ /**
50
+ * Expand a list of equivalence classes into bi-directional FDs over the same
51
+ * column indices, then concatenate with the existing FDs. For a class
52
+ * `{c0, c1, ..., ck}` this emits `{ci} → {cj}` for every distinct ordered pair
53
+ * — enough for `computeClosure` to derive every member from any one of them.
54
+ */
55
+ export function expandEcsToFds(ecs, fds) {
56
+ const out = fds.slice();
57
+ for (const cls of ecs) {
58
+ if (cls.length < 2)
59
+ continue;
60
+ for (let i = 0; i < cls.length; i++) {
61
+ for (let j = 0; j < cls.length; j++) {
62
+ if (i === j)
63
+ continue;
64
+ out.push({ determinants: [cls[i]], dependents: [cls[j]] });
65
+ }
66
+ }
67
+ }
68
+ return out;
69
+ }
70
+ /** True iff `attrs` determines every attribute in `target` under `fds`. */
71
+ export function determines(attrs, target, fds) {
72
+ if (target.size === 0)
73
+ return true;
74
+ const closure = computeClosure(attrs, fds);
75
+ for (const t of target) {
76
+ if (!closure.has(t))
77
+ return false;
78
+ }
79
+ return true;
80
+ }
81
+ /**
82
+ * Smallest subset of `attrs` whose closure equals the closure of `attrs`.
83
+ * Greedy minimization: try dropping each attribute; keep the drop iff the
84
+ * resulting closure is unchanged. O(|attrs|² × |fds|).
85
+ */
86
+ export function minimalCover(attrs, fds) {
87
+ const fullClosure = computeClosure(attrs, fds);
88
+ const result = new Set(attrs);
89
+ for (const a of [...result]) {
90
+ const trial = new Set(result);
91
+ trial.delete(a);
92
+ const trialClosure = computeClosure(trial, fds);
93
+ if (trialClosure.size === fullClosure.size) {
94
+ let same = true;
95
+ for (const x of fullClosure) {
96
+ if (!trialClosure.has(x)) {
97
+ same = false;
98
+ break;
99
+ }
100
+ }
101
+ if (same)
102
+ result.delete(a);
103
+ }
104
+ }
105
+ return result;
106
+ }
107
+ // Structural-only equality: compares determinants/dependents/guard. The
108
+ // optional `source` provenance tag (`declared-check` vs `assertion`) is NOT
109
+ // compared here, so two structurally-identical FDs from different sources
110
+ // collapse to one in `addFd` / `mergeFds`. The first-merged source wins —
111
+ // table references merge declared-check contributions before hoisted
112
+ // assertion contributions, so `declared-check` is preferred on collisions.
113
+ function fdsEqual(a, b) {
114
+ if (a.determinants.length !== b.determinants.length)
115
+ return false;
116
+ if (a.dependents.length !== b.dependents.length)
117
+ return false;
118
+ const aDet = new Set(a.determinants);
119
+ for (const d of b.determinants)
120
+ if (!aDet.has(d))
121
+ return false;
122
+ const aDep = new Set(a.dependents);
123
+ for (const d of b.dependents)
124
+ if (!aDep.has(d))
125
+ return false;
126
+ return guardsEqual(a.guard, b.guard);
127
+ }
128
+ function guardsEqual(a, b) {
129
+ if (a === b)
130
+ return true;
131
+ if (!a || !b)
132
+ return false;
133
+ if (a.clauses.length !== b.clauses.length)
134
+ return false;
135
+ // Order-insensitive clause comparison.
136
+ const used = new Array(b.clauses.length).fill(false);
137
+ for (const ac of a.clauses) {
138
+ let matched = false;
139
+ for (let i = 0; i < b.clauses.length; i++) {
140
+ if (used[i])
141
+ continue;
142
+ if (guardClauseEquals(ac, b.clauses[i])) {
143
+ used[i] = true;
144
+ matched = true;
145
+ break;
146
+ }
147
+ }
148
+ if (!matched)
149
+ return false;
150
+ }
151
+ return true;
152
+ }
153
+ function guardClauseEquals(a, b) {
154
+ if (a.kind !== b.kind)
155
+ return false;
156
+ if (a.kind === 'eq-literal' && b.kind === 'eq-literal') {
157
+ return a.column === b.column && sqlValueEquals(a.value, b.value);
158
+ }
159
+ if (a.kind === 'eq-column' && b.kind === 'eq-column') {
160
+ // Order-insensitive on left/right.
161
+ return (a.left === b.left && a.right === b.right)
162
+ || (a.left === b.right && a.right === b.left);
163
+ }
164
+ if (a.kind === 'is-null' && b.kind === 'is-null') {
165
+ return a.column === b.column && a.negated === b.negated;
166
+ }
167
+ if (a.kind === 'range' && b.kind === 'range') {
168
+ if (a.column !== b.column)
169
+ return false;
170
+ const aHasMin = a.min !== undefined;
171
+ const bHasMin = b.min !== undefined;
172
+ if (aHasMin !== bHasMin)
173
+ return false;
174
+ if (aHasMin && !sqlValueEquals(a.min, b.min))
175
+ return false;
176
+ if (aHasMin && a.minInclusive !== b.minInclusive)
177
+ return false;
178
+ const aHasMax = a.max !== undefined;
179
+ const bHasMax = b.max !== undefined;
180
+ if (aHasMax !== bHasMax)
181
+ return false;
182
+ if (aHasMax && !sqlValueEquals(a.max, b.max))
183
+ return false;
184
+ if (aHasMax && a.maxInclusive !== b.maxInclusive)
185
+ return false;
186
+ return true;
187
+ }
188
+ if (a.kind === 'or-of' && b.kind === 'or-of') {
189
+ if (a.clauses.length !== b.clauses.length)
190
+ return false;
191
+ // Order-insensitive sub-clause comparison.
192
+ const used = new Array(b.clauses.length).fill(false);
193
+ for (const ac of a.clauses) {
194
+ let matched = false;
195
+ for (let i = 0; i < b.clauses.length; i++) {
196
+ if (used[i])
197
+ continue;
198
+ if (guardClauseEquals(ac, b.clauses[i])) {
199
+ used[i] = true;
200
+ matched = true;
201
+ break;
202
+ }
203
+ }
204
+ if (!matched)
205
+ return false;
206
+ }
207
+ return true;
208
+ }
209
+ return false;
210
+ }
211
+ function determinantsEqual(a, b) {
212
+ if (a.length !== b.length)
213
+ return false;
214
+ const aSet = new Set(a);
215
+ for (const x of b)
216
+ if (!aSet.has(x))
217
+ return false;
218
+ return true;
219
+ }
220
+ function dependentsSubset(sub, sup) {
221
+ const supSet = new Set(sup);
222
+ for (const x of sub)
223
+ if (!supSet.has(x))
224
+ return false;
225
+ return true;
226
+ }
227
+ /**
228
+ * Add a single FD, dropping any existing entry with the same determinants
229
+ * (and same guard) whose dependents are a subset of the new one (subsumption).
230
+ * When the resulting list exceeds the cap, drop FDs whose determinants are
231
+ * not a subset of any `keyHints` entry on the same node.
232
+ *
233
+ * Guard-aware: FDs with different `guard` predicates are kept side-by-side
234
+ * even when their determinants/dependents match — they are logically distinct
235
+ * facts and may be activated by different surrounding predicates.
236
+ */
237
+ export function addFd(fds, next, opts = {}) {
238
+ if (next.dependents.length === 0)
239
+ return fds.slice();
240
+ const result = [];
241
+ let subsumedByExisting = false;
242
+ for (const existing of fds) {
243
+ if (fdsEqual(existing, next)) {
244
+ subsumedByExisting = true;
245
+ result.push(existing);
246
+ continue;
247
+ }
248
+ if (determinantsEqual(existing.determinants, next.determinants) &&
249
+ guardsEqual(existing.guard, next.guard)) {
250
+ // Same determinants and guard: keep whichever has the larger dependent set.
251
+ if (dependentsSubset(existing.dependents, next.dependents)) {
252
+ // existing ⊂ next, drop existing
253
+ continue;
254
+ }
255
+ if (dependentsSubset(next.dependents, existing.dependents)) {
256
+ subsumedByExisting = true;
257
+ }
258
+ }
259
+ result.push(existing);
260
+ }
261
+ if (!subsumedByExisting)
262
+ result.push(next);
263
+ return enforceCap(result, opts);
264
+ }
265
+ function enforceCap(fds, opts) {
266
+ const cap = opts.cap ?? MAX_FDS_PER_NODE;
267
+ if (fds.length <= cap)
268
+ return fds;
269
+ const keyHints = opts.keyHints ?? [];
270
+ const keySet = keyHints.map(k => new Set(k));
271
+ const isSubsetOfAnyKey = (det) => {
272
+ if (keySet.length === 0)
273
+ return false;
274
+ return keySet.some(ks => det.every(d => ks.has(d)));
275
+ };
276
+ const preferred = fds.filter(fd => isSubsetOfAnyKey(fd.determinants));
277
+ const other = fds.filter(fd => !isSubsetOfAnyKey(fd.determinants));
278
+ let kept;
279
+ if (preferred.length >= cap) {
280
+ kept = preferred.slice(0, cap);
281
+ }
282
+ else {
283
+ kept = preferred.concat(other.slice(0, cap - preferred.length));
284
+ }
285
+ log('FD cap reached: dropped %d FD(s) from %d', fds.length - kept.length, fds.length);
286
+ return kept;
287
+ }
288
+ /** Merge two FD lists, applying subsumption via `addFd`. */
289
+ export function mergeFds(a, b, opts = {}) {
290
+ let result = a.slice();
291
+ for (const fd of b) {
292
+ result = addFd(result, fd, opts);
293
+ }
294
+ return result;
295
+ }
296
+ /**
297
+ * Project FDs through a column mapping (oldCol → newCol). FDs whose
298
+ * determinants lose any column are dropped entirely (the projection breaks
299
+ * the determinant set). Dependents that don't survive are filtered out;
300
+ * an FD whose dependents are completely filtered is dropped.
301
+ *
302
+ * Exception: an FD with empty determinants (the singleton "at-most-one-row"
303
+ * marker) survives as long as at least one dependent does — losing some
304
+ * dependent columns to projection doesn't invalidate the at-most-one-row
305
+ * claim on the surviving columns.
306
+ *
307
+ * Guarded FDs additionally require every column referenced in `guard.clauses`
308
+ * to be in the mapping — if any guard column is dropped the guard becomes
309
+ * unobservable and the FD can never be re-activated downstream.
310
+ */
311
+ export function projectFds(fds, mapping) {
312
+ const result = [];
313
+ for (const fd of fds) {
314
+ const newDet = [];
315
+ let miss = false;
316
+ for (const d of fd.determinants) {
317
+ const m = mapping.get(d);
318
+ if (m === undefined) {
319
+ miss = true;
320
+ break;
321
+ }
322
+ newDet.push(m);
323
+ }
324
+ if (miss)
325
+ continue;
326
+ const newDep = [];
327
+ for (const d of fd.dependents) {
328
+ const m = mapping.get(d);
329
+ if (m !== undefined)
330
+ newDep.push(m);
331
+ }
332
+ if (newDep.length === 0)
333
+ continue;
334
+ let newGuard;
335
+ if (fd.guard !== undefined) {
336
+ const mappedGuard = projectGuard(fd.guard, mapping);
337
+ if (!mappedGuard)
338
+ continue;
339
+ newGuard = mappedGuard;
340
+ }
341
+ result.push(newGuard
342
+ ? { determinants: newDet, dependents: newDep, guard: newGuard }
343
+ : { determinants: newDet, dependents: newDep });
344
+ }
345
+ return result;
346
+ }
347
+ function projectGuard(guard, mapping) {
348
+ const clauses = [];
349
+ for (const clause of guard.clauses) {
350
+ const mapped = projectClause(clause, mapping);
351
+ if (mapped === undefined)
352
+ return undefined;
353
+ clauses.push(mapped);
354
+ }
355
+ return { clauses };
356
+ }
357
+ function projectClause(clause, mapping) {
358
+ switch (clause.kind) {
359
+ case 'eq-literal': {
360
+ const m = mapping.get(clause.column);
361
+ if (m === undefined)
362
+ return undefined;
363
+ return { kind: 'eq-literal', column: m, value: clause.value };
364
+ }
365
+ case 'eq-column': {
366
+ const ml = mapping.get(clause.left);
367
+ const mr = mapping.get(clause.right);
368
+ if (ml === undefined || mr === undefined)
369
+ return undefined;
370
+ return { kind: 'eq-column', left: ml, right: mr };
371
+ }
372
+ case 'is-null': {
373
+ const m = mapping.get(clause.column);
374
+ if (m === undefined)
375
+ return undefined;
376
+ return { kind: 'is-null', column: m, negated: clause.negated };
377
+ }
378
+ case 'range': {
379
+ const m = mapping.get(clause.column);
380
+ if (m === undefined)
381
+ return undefined;
382
+ return { ...clause, column: m };
383
+ }
384
+ case 'or-of': {
385
+ const sub = [];
386
+ for (const c of clause.clauses) {
387
+ const mapped = projectClause(c, mapping);
388
+ // Same conservative rule as the rest of projectGuard: if any nested
389
+ // column drops out the whole clause is unrecoverable.
390
+ if (mapped === undefined)
391
+ return undefined;
392
+ sub.push(mapped);
393
+ }
394
+ return { kind: 'or-of', clauses: sub };
395
+ }
396
+ }
397
+ }
398
+ /** Shift all column indices in `fds` (including any `guard` columns) by `offset`. */
399
+ export function shiftFds(fds, offset) {
400
+ if (offset === 0)
401
+ return fds.slice();
402
+ return fds.map(fd => {
403
+ const shifted = {
404
+ determinants: fd.determinants.map(d => d + offset),
405
+ dependents: fd.dependents.map(d => d + offset),
406
+ };
407
+ if (fd.guard !== undefined) {
408
+ return { ...shifted, guard: shiftGuard(fd.guard, offset) };
409
+ }
410
+ return shifted;
411
+ });
412
+ }
413
+ function shiftGuard(guard, offset) {
414
+ return { clauses: guard.clauses.map(c => shiftClause(c, offset)) };
415
+ }
416
+ function shiftClause(clause, offset) {
417
+ switch (clause.kind) {
418
+ case 'eq-literal':
419
+ return { kind: 'eq-literal', column: clause.column + offset, value: clause.value };
420
+ case 'eq-column':
421
+ return { kind: 'eq-column', left: clause.left + offset, right: clause.right + offset };
422
+ case 'is-null':
423
+ return { kind: 'is-null', column: clause.column + offset, negated: clause.negated };
424
+ case 'range':
425
+ return { ...clause, column: clause.column + offset };
426
+ case 'or-of':
427
+ return { kind: 'or-of', clauses: clause.clauses.map(c => shiftClause(c, offset)) };
428
+ }
429
+ }
430
+ /**
431
+ * Return the unconditional twin of `fd` — drop the guard but keep determinants
432
+ * and dependents. Used by Filter activation when the surrounding predicate
433
+ * entails the guard.
434
+ */
435
+ export function stripGuard(fd) {
436
+ if (fd.guard === undefined)
437
+ return fd;
438
+ return { determinants: fd.determinants, dependents: fd.dependents };
439
+ }
440
+ /** Shift all column indices in `classes` by `offset`. */
441
+ export function shiftEquivClasses(classes, offset) {
442
+ if (offset === 0)
443
+ return classes.map(c => c.slice());
444
+ return classes.map(c => c.map(x => x + offset));
445
+ }
446
+ function normalizeClass(cls) {
447
+ const dedup = Array.from(new Set(cls));
448
+ dedup.sort((a, b) => a - b);
449
+ return dedup;
450
+ }
451
+ /**
452
+ * Merge two equivalence-class sets, taking the transitive closure of
453
+ * overlapping classes (union-find style).
454
+ */
455
+ export function mergeEquivClasses(a, b) {
456
+ const classes = [...a, ...b].map(c => normalizeClass(c));
457
+ let merged = true;
458
+ while (merged) {
459
+ merged = false;
460
+ outer: for (let i = 0; i < classes.length; i++) {
461
+ const ci = classes[i];
462
+ const ciSet = new Set(ci);
463
+ for (let j = i + 1; j < classes.length; j++) {
464
+ const cj = classes[j];
465
+ let overlap = false;
466
+ for (const x of cj) {
467
+ if (ciSet.has(x)) {
468
+ overlap = true;
469
+ break;
470
+ }
471
+ }
472
+ if (overlap) {
473
+ classes[i] = normalizeClass([...ci, ...cj]);
474
+ classes.splice(j, 1);
475
+ merged = true;
476
+ break outer;
477
+ }
478
+ }
479
+ }
480
+ }
481
+ return classes.filter(c => c.length >= 2);
482
+ }
483
+ /** Add a new equality `a ≡ b` to an existing class list. */
484
+ export function addEquivalence(classes, a, b) {
485
+ if (a === b)
486
+ return classes.map(c => c.slice());
487
+ return mergeEquivClasses(classes, [[a, b]]);
488
+ }
489
+ /**
490
+ * Build an FD `key → {0..columnCount-1} \ key` from a superkey. The canonical
491
+ * way to encode "K is a unique key on a relation": K determines every other
492
+ * output column. K = ∅ produces the "at-most-one-row" singleton FD.
493
+ *
494
+ * Returns undefined when K covers every column (the all-columns case has no
495
+ * non-trivial encoding — that case is communicated via `RelationType.isSet`
496
+ * instead).
497
+ */
498
+ export function superkeyToFd(key, columnCount) {
499
+ const keySet = new Set(key);
500
+ const dependents = [];
501
+ for (let i = 0; i < columnCount; i++) {
502
+ if (!keySet.has(i))
503
+ dependents.push(i);
504
+ }
505
+ if (dependents.length === 0)
506
+ return undefined;
507
+ return { determinants: key.slice(), dependents };
508
+ }
509
+ /**
510
+ * True iff the closure of `attrs` under `fds` covers `{0..columnCount-1}` —
511
+ * i.e., `attrs` is a superkey of the relation. Replaces the legacy "covers a
512
+ * `uniqueKeys` entry" check; FDs are the canonical surface now.
513
+ */
514
+ export function isSuperkey(attrs, fds, columnCount) {
515
+ if (columnCount <= 0)
516
+ return true;
517
+ const closure = computeClosure(attrs, fds ?? []);
518
+ for (let i = 0; i < columnCount; i++) {
519
+ if (!closure.has(i))
520
+ return false;
521
+ }
522
+ return true;
523
+ }
524
+ /**
525
+ * Enumerate the minimal full-cover key sets discoverable from `fds`: for each
526
+ * FD `K → Y` whose closure covers all columns, return `K` (greedily minimized
527
+ * within `K`). Deduplicated by set equality.
528
+ *
529
+ * Excludes the trivial "all-columns is a superkey" tautology — only FDs with
530
+ * `K ⊊ all_cols` are considered, since the all-cols case is encoded via
531
+ * `RelationType.isSet`.
532
+ */
533
+ export function deriveKeysFromFds(fds, columnCount) {
534
+ if (!fds || fds.length === 0)
535
+ return [];
536
+ const results = [];
537
+ const seen = new Set();
538
+ for (const fd of fds) {
539
+ if (fd.guard !== undefined)
540
+ continue;
541
+ if (fd.determinants.length >= columnCount)
542
+ continue;
543
+ const det = new Set(fd.determinants);
544
+ if (!isSuperkey(det, fds, columnCount))
545
+ continue;
546
+ const minimal = minimalCover(det, fds);
547
+ // Ensure the minimal cover still covers all columns (it should — minimalCover
548
+ // only drops attrs whose removal doesn't change closure).
549
+ const sorted = Array.from(minimal).sort((a, b) => a - b);
550
+ const key = sorted.join(',');
551
+ if (seen.has(key))
552
+ continue;
553
+ seen.add(key);
554
+ results.push(sorted);
555
+ }
556
+ return results;
557
+ }
558
+ /**
559
+ * True iff the FD set encodes any non-trivial key — i.e., there exists some
560
+ * FD whose determinants form a superkey of `columnCount` columns with the
561
+ * determinant set strictly smaller than all columns. This is the FD-surface
562
+ * replacement for "the relation has a known unique key smaller than its full
563
+ * column list" (the old `uniqueKeys.length > 0` check), excluding the
564
+ * tautological all-columns case which carries no information.
565
+ */
566
+ export function hasAnyKey(fds, columnCount) {
567
+ if (!fds || fds.length === 0)
568
+ return false;
569
+ return fds.some(fd => fd.guard === undefined &&
570
+ fd.determinants.length < columnCount &&
571
+ isSuperkey(new Set(fd.determinants), fds, columnCount));
572
+ }
573
+ /**
574
+ * True iff the relation has at-most-one-row — i.e., some FD `∅ → Y` exists
575
+ * whose closure covers every column. Replaces the legacy `[[]]` singleton
576
+ * marker on `uniqueKeys`.
577
+ */
578
+ export function hasSingletonFd(fds, columnCount) {
579
+ if (!fds)
580
+ return false;
581
+ return fds.some(fd => fd.guard === undefined &&
582
+ fd.determinants.length === 0 &&
583
+ isSuperkey(new Set(), fds, columnCount));
584
+ }
585
+ /**
586
+ * Build the singleton FD `∅ → {0..columnCount-1}` that encodes
587
+ * "at-most-one-row". Returns undefined when `columnCount === 0` (no
588
+ * dependents).
589
+ */
590
+ export function singletonFd(columnCount) {
591
+ if (columnCount <= 0)
592
+ return undefined;
593
+ const dependents = [];
594
+ for (let i = 0; i < columnCount; i++)
595
+ dependents.push(i);
596
+ return { determinants: [], dependents };
597
+ }
598
+ /**
599
+ * True iff `attrs` is asserted to be a unique key by the FD set — i.e., there
600
+ * exists some FD whose determinants are a subset of `attrs` and whose closure
601
+ * covers all columns. Stricter than `isSuperkey`: the trivial "all-cols is a
602
+ * superkey of itself" tautology does NOT count, because no FD makes that claim.
603
+ *
604
+ * Use this when you need a positive uniqueness claim (e.g., the
605
+ * sort/window strict-monotonicOn check). For "would attrs functionally
606
+ * determine the rest of the relation under closure?" use `isSuperkey` directly.
607
+ */
608
+ export function isAssertedKey(attrs, fds, columnCount) {
609
+ if (!fds || fds.length === 0)
610
+ return false;
611
+ for (const fd of fds) {
612
+ if (fd.guard !== undefined)
613
+ continue;
614
+ // Determinants must be a subset of attrs.
615
+ let subset = true;
616
+ for (const d of fd.determinants) {
617
+ if (!attrs.has(d)) {
618
+ subset = false;
619
+ break;
620
+ }
621
+ }
622
+ if (!subset)
623
+ continue;
624
+ // Determinants closure must cover all columns.
625
+ if (isSuperkey(new Set(fd.determinants), fds, columnCount))
626
+ return true;
627
+ }
628
+ return false;
629
+ }
630
+ /**
631
+ * Walk `predicate` (assumed to be a normalized conjunction) and extract FDs,
632
+ * equivalence-class contributions, and constant bindings from equality
633
+ * conjuncts.
634
+ *
635
+ * `attrIdToIndex` maps an attribute ID to its column index in the predicate's
636
+ * relation. Equality conjuncts referencing attributes outside this map
637
+ * (correlated subqueries, etc.) are silently ignored.
638
+ *
639
+ * Recognized shapes (per AND-conjunct):
640
+ * - `col = literal` ⇒ FD `∅ → col` + binding `{col} → literal value`.
641
+ * - `col = ?` ⇒ FD `∅ → col` + binding `{col} → parameter ref`.
642
+ * - `col1 = col2` ⇒ FDs `{col1} → {col2}` and `{col2} → {col1}` plus an
643
+ * equivalence pair `[col1, col2]`.
644
+ *
645
+ * Non-equality conjuncts contribute nothing.
646
+ */
647
+ export function extractEqualityFds(predicate, attrIdToIndex) {
648
+ const fds = [];
649
+ const equivPairs = [];
650
+ const constantBindings = [];
651
+ const stack = [predicate];
652
+ while (stack.length > 0) {
653
+ const n = stack.pop();
654
+ if (!(n instanceof BinaryOpNode))
655
+ continue;
656
+ const op = n.expression.operator;
657
+ if (op === 'AND') {
658
+ stack.push(n.left, n.right);
659
+ continue;
660
+ }
661
+ if (op !== '=')
662
+ continue;
663
+ const lIsCol = n.left instanceof ColumnReferenceNode;
664
+ const rIsCol = n.right instanceof ColumnReferenceNode;
665
+ const lConst = constantValueOf(n.left);
666
+ const rConst = constantValueOf(n.right);
667
+ if (lIsCol && rIsCol) {
668
+ const lIdx = attrIdToIndex.get(n.left.attributeId);
669
+ const rIdx = attrIdToIndex.get(n.right.attributeId);
670
+ if (lIdx !== undefined && rIdx !== undefined && lIdx !== rIdx) {
671
+ fds.push({ determinants: [lIdx], dependents: [rIdx] });
672
+ fds.push({ determinants: [rIdx], dependents: [lIdx] });
673
+ equivPairs.push([lIdx, rIdx]);
674
+ }
675
+ continue;
676
+ }
677
+ if (lIsCol && rConst !== undefined) {
678
+ const lIdx = attrIdToIndex.get(n.left.attributeId);
679
+ if (lIdx !== undefined) {
680
+ fds.push({ determinants: [], dependents: [lIdx] });
681
+ constantBindings.push({ attrs: [lIdx], value: rConst });
682
+ }
683
+ continue;
684
+ }
685
+ if (rIsCol && lConst !== undefined) {
686
+ const rIdx = attrIdToIndex.get(n.right.attributeId);
687
+ if (rIdx !== undefined) {
688
+ fds.push({ determinants: [], dependents: [rIdx] });
689
+ constantBindings.push({ attrs: [rIdx], value: lConst });
690
+ }
691
+ continue;
692
+ }
693
+ }
694
+ return { fds, equivPairs, constantBindings };
695
+ }
696
+ function buildPredicateFacts(predicate, attrIdToIndex, isColumnNumeric) {
697
+ const literalEqs = new Map();
698
+ const columnEqs = new Map();
699
+ const isNullCols = new Set();
700
+ const isNotNullCols = new Set();
701
+ const inListEqs = new Map();
702
+ const rangeBounds = new Map();
703
+ const addColumnEq = (a, b) => {
704
+ if (a === b)
705
+ return;
706
+ let aSet = columnEqs.get(a);
707
+ if (!aSet) {
708
+ aSet = new Set();
709
+ columnEqs.set(a, aSet);
710
+ }
711
+ aSet.add(b);
712
+ let bSet = columnEqs.get(b);
713
+ if (!bSet) {
714
+ bSet = new Set();
715
+ columnEqs.set(b, bSet);
716
+ }
717
+ bSet.add(a);
718
+ };
719
+ const columnIndexOf = (n) => {
720
+ if (n instanceof ColumnReferenceNode) {
721
+ return attrIdToIndex.get(n.attributeId);
722
+ }
723
+ return undefined;
724
+ };
725
+ const tightenLowerBound = (col, value, inclusive) => {
726
+ const cur = rangeBounds.get(col);
727
+ if (!cur) {
728
+ rangeBounds.set(col, { min: value, minInclusive: inclusive, maxInclusive: false });
729
+ return;
730
+ }
731
+ if (cur.min === undefined) {
732
+ cur.min = value;
733
+ cur.minInclusive = inclusive;
734
+ return;
735
+ }
736
+ const cmp = compareSqlValues(value, cur.min);
737
+ if (cmp > 0) {
738
+ cur.min = value;
739
+ cur.minInclusive = inclusive;
740
+ }
741
+ else if (cmp === 0 && cur.minInclusive && !inclusive) {
742
+ // Tighten: same value, but new bound is exclusive (excludes the boundary).
743
+ cur.minInclusive = false;
744
+ }
745
+ };
746
+ const tightenUpperBound = (col, value, inclusive) => {
747
+ const cur = rangeBounds.get(col);
748
+ if (!cur) {
749
+ rangeBounds.set(col, { max: value, maxInclusive: inclusive, minInclusive: false });
750
+ return;
751
+ }
752
+ if (cur.max === undefined) {
753
+ cur.max = value;
754
+ cur.maxInclusive = inclusive;
755
+ return;
756
+ }
757
+ const cmp = compareSqlValues(value, cur.max);
758
+ if (cmp < 0) {
759
+ cur.max = value;
760
+ cur.maxInclusive = inclusive;
761
+ }
762
+ else if (cmp === 0 && cur.maxInclusive && !inclusive) {
763
+ cur.maxInclusive = false;
764
+ }
765
+ };
766
+ const recordComparison = (col, op, lit) => {
767
+ switch (op) {
768
+ case '>':
769
+ tightenLowerBound(col, lit, false);
770
+ return;
771
+ case '>=':
772
+ tightenLowerBound(col, lit, true);
773
+ return;
774
+ case '<':
775
+ tightenUpperBound(col, lit, false);
776
+ return;
777
+ case '<=':
778
+ tightenUpperBound(col, lit, true);
779
+ return;
780
+ }
781
+ };
782
+ const stack = [predicate];
783
+ while (stack.length > 0) {
784
+ const n = stack.pop();
785
+ if (n instanceof BinaryOpNode) {
786
+ const op = n.expression.operator;
787
+ if (op === 'AND') {
788
+ stack.push(n.left, n.right);
789
+ continue;
790
+ }
791
+ if (op === '=' || op === '==') {
792
+ const lIdx = columnIndexOf(n.left);
793
+ const rIdx = columnIndexOf(n.right);
794
+ if (lIdx !== undefined && rIdx !== undefined) {
795
+ addColumnEq(lIdx, rIdx);
796
+ continue;
797
+ }
798
+ if (lIdx !== undefined) {
799
+ const lit = literalSqlValueOf(n.right);
800
+ if (lit !== undefined)
801
+ literalEqs.set(lIdx, lit);
802
+ continue;
803
+ }
804
+ if (rIdx !== undefined) {
805
+ const lit = literalSqlValueOf(n.left);
806
+ if (lit !== undefined)
807
+ literalEqs.set(rIdx, lit);
808
+ }
809
+ continue;
810
+ }
811
+ // IS / IS NOT may be written as binary with literal NULL on the right.
812
+ if (op === 'IS' || op === 'IS NOT') {
813
+ const lIdx = columnIndexOf(n.left);
814
+ if (lIdx === undefined)
815
+ continue;
816
+ const lit = literalSqlValueOf(n.right);
817
+ if (lit !== null)
818
+ continue;
819
+ if (op === 'IS')
820
+ isNullCols.add(lIdx);
821
+ else
822
+ isNotNullCols.add(lIdx);
823
+ continue;
824
+ }
825
+ if (op === '<' || op === '<=' || op === '>' || op === '>=') {
826
+ const lIdx = columnIndexOf(n.left);
827
+ const rIdx = columnIndexOf(n.right);
828
+ if (lIdx !== undefined && rIdx === undefined) {
829
+ const lit = literalSqlValueOf(n.right);
830
+ if (lit === undefined || lit === null)
831
+ continue;
832
+ recordComparison(lIdx, op, lit);
833
+ }
834
+ else if (rIdx !== undefined && lIdx === undefined) {
835
+ const lit = literalSqlValueOf(n.left);
836
+ if (lit === undefined || lit === null)
837
+ continue;
838
+ recordComparison(rIdx, flipComparison(op), lit);
839
+ }
840
+ continue;
841
+ }
842
+ continue;
843
+ }
844
+ if (n instanceof BetweenNode) {
845
+ if (n.expression.not === true)
846
+ continue;
847
+ const cIdx = columnIndexOf(n.expr);
848
+ if (cIdx === undefined)
849
+ continue;
850
+ const lo = literalSqlValueOf(n.lower);
851
+ const hi = literalSqlValueOf(n.upper);
852
+ if (lo !== undefined && lo !== null)
853
+ tightenLowerBound(cIdx, lo, true);
854
+ if (hi !== undefined && hi !== null)
855
+ tightenUpperBound(cIdx, hi, true);
856
+ continue;
857
+ }
858
+ if (n instanceof UnaryOpNode) {
859
+ const op = n.expression.operator;
860
+ const cIdx = columnIndexOf(n.operand);
861
+ if (cIdx === undefined)
862
+ continue;
863
+ if (op === 'IS NULL')
864
+ isNullCols.add(cIdx);
865
+ else if (op === 'IS NOT NULL')
866
+ isNotNullCols.add(cIdx);
867
+ // `WHERE NOT col` excludes both NULL and zero rows. Pin `col = 0` so
868
+ // it discharges partial-UC guards rewritten the same way at production.
869
+ // Numeric-only: for TEXT/BLOB/BOOLEAN columns `col = 0` (strict
870
+ // `sqlValueEquals`) is not equivalent to `NOT col` (TEXT `''` and
871
+ // boolean `false` are falsy but compare unequal to integer 0), so the
872
+ // rewrite would falsely discharge a `col = 0` guard for rows the
873
+ // runtime filter actually keeps. `IS NOT NULL` is still recorded —
874
+ // that's sound regardless of type.
875
+ else if (op === 'NOT') {
876
+ if (isColumnNumeric(cIdx))
877
+ literalEqs.set(cIdx, 0);
878
+ isNotNullCols.add(cIdx);
879
+ }
880
+ continue;
881
+ }
882
+ if (n instanceof InNode) {
883
+ // Literal-only IN-list with a column-reference condition: capture
884
+ // the OR-set so `or-of [eq-literal …]` guards can discharge.
885
+ if (n.source !== undefined)
886
+ continue;
887
+ if (!n.values || n.values.length === 0)
888
+ continue;
889
+ const cIdx = columnIndexOf(n.condition);
890
+ if (cIdx === undefined)
891
+ continue;
892
+ const set = new Set();
893
+ let allLiterals = true;
894
+ for (const v of n.values) {
895
+ const lit = literalSqlValueOf(v);
896
+ if (lit === undefined) {
897
+ allLiterals = false;
898
+ break;
899
+ }
900
+ set.add(lit);
901
+ }
902
+ if (!allLiterals)
903
+ continue;
904
+ // If a previous IN on the same column was captured, intersect to keep
905
+ // the strongest set (`col IN (a,b) AND col IN (b,c)` ⇒ {b}).
906
+ const prev = inListEqs.get(cIdx);
907
+ if (prev) {
908
+ const intersected = new Set();
909
+ for (const x of set) {
910
+ for (const y of prev) {
911
+ if (sqlValueEquals(x, y))
912
+ intersected.add(x);
913
+ }
914
+ }
915
+ inListEqs.set(cIdx, intersected);
916
+ }
917
+ else {
918
+ inListEqs.set(cIdx, set);
919
+ }
920
+ // A singleton IN also pins the literal.
921
+ if (set.size === 1) {
922
+ const only = set.values().next().value;
923
+ literalEqs.set(cIdx, only);
924
+ }
925
+ }
926
+ }
927
+ return { literalEqs, columnEqs, isNullCols, isNotNullCols, inListEqs, rangeBounds };
928
+ }
929
+ function literalSqlValueOf(n) {
930
+ let cur = n;
931
+ while (cur instanceof CastNode || cur instanceof CollateNode) {
932
+ cur = cur.operand;
933
+ }
934
+ if (cur instanceof LiteralNode) {
935
+ const v = cur.expression.value;
936
+ if (v instanceof Promise)
937
+ return undefined;
938
+ return v;
939
+ }
940
+ return undefined;
941
+ }
942
+ function ecIndexOf(col, ecs) {
943
+ for (let i = 0; i < ecs.length; i++) {
944
+ if (ecs[i].includes(col))
945
+ return i;
946
+ }
947
+ return undefined;
948
+ }
949
+ function bindingForColumn(col, bindings) {
950
+ for (const b of bindings)
951
+ if (b.attrs.includes(col))
952
+ return b;
953
+ return undefined;
954
+ }
955
+ /**
956
+ * Decide whether the surrounding `predicate` (combined with the source's ECs
957
+ * and constant bindings) entails every clause in `guard`. Conservative — when
958
+ * in doubt, returns `false`.
959
+ *
960
+ * `isColumnNonNullable(col)` reports whether the source's output column is
961
+ * declared NOT NULL; the helper uses it to discharge `is-null negated:true`
962
+ * guards from type information alone.
963
+ *
964
+ * `isColumnNumeric(col)` reports whether the source's output column has a
965
+ * numeric logical type. Used to gate the `NOT col → col = 0` rewrite: only
966
+ * sound for numeric columns since the consumer matches `eq-literal{col, 0}`
967
+ * via strict `sqlValueEquals`, which treats TEXT `''`, BLOB, and boolean
968
+ * `false` as unequal to integer 0.
969
+ */
970
+ export function predicateImpliesGuard(predicate, guard, ecs, bindings, attrIdToIndex, isColumnNonNullable, isColumnNumeric) {
971
+ const facts = buildPredicateFacts(predicate, attrIdToIndex, isColumnNumeric);
972
+ for (const clause of guard.clauses) {
973
+ if (!clauseEntailed(clause, facts, ecs, bindings, isColumnNonNullable)) {
974
+ return false;
975
+ }
976
+ }
977
+ return true;
978
+ }
979
+ function clauseEntailed(clause, facts, ecs, bindings, isColumnNonNullable) {
980
+ switch (clause.kind) {
981
+ case 'eq-literal': {
982
+ const { column, value } = clause;
983
+ // Direct conjunct match.
984
+ const direct = facts.literalEqs.get(column);
985
+ if (direct !== undefined && sqlValueEquals(direct, value))
986
+ return true;
987
+ // Via EC: any column in `column`'s EC pinned to the literal.
988
+ const ecIdx = ecIndexOf(column, ecs);
989
+ if (ecIdx !== undefined) {
990
+ for (const peer of ecs[ecIdx]) {
991
+ const peerLit = facts.literalEqs.get(peer);
992
+ if (peerLit !== undefined && sqlValueEquals(peerLit, value))
993
+ return true;
994
+ }
995
+ }
996
+ // Via binding: source already pins `column` to the same literal.
997
+ const binding = bindingForColumn(column, bindings);
998
+ if (binding && binding.value.kind === 'literal' && sqlValueEquals(binding.value.value, value)) {
999
+ return true;
1000
+ }
1001
+ return false;
1002
+ }
1003
+ case 'eq-column': {
1004
+ const { left, right } = clause;
1005
+ if (left === right)
1006
+ return true;
1007
+ // Same EC.
1008
+ const li = ecIndexOf(left, ecs);
1009
+ const ri = ecIndexOf(right, ecs);
1010
+ if (li !== undefined && li === ri)
1011
+ return true;
1012
+ // Direct conjunct match.
1013
+ const leftPeers = facts.columnEqs.get(left);
1014
+ if (leftPeers && leftPeers.has(right))
1015
+ return true;
1016
+ // Bound to the same ConstantValue (literal or parameter) on both sides.
1017
+ const lBind = bindingForColumn(left, bindings);
1018
+ const rBind = bindingForColumn(right, bindings);
1019
+ if (lBind && rBind && constantValueEquals(lBind.value, rBind.value))
1020
+ return true;
1021
+ return false;
1022
+ }
1023
+ case 'is-null': {
1024
+ const { column, negated } = clause;
1025
+ if (negated) {
1026
+ // "col IS NOT NULL" guard.
1027
+ if (facts.isNotNullCols.has(column))
1028
+ return true;
1029
+ if (isColumnNonNullable(column))
1030
+ return true;
1031
+ return false;
1032
+ }
1033
+ // "col IS NULL" guard.
1034
+ return facts.isNullCols.has(column);
1035
+ }
1036
+ case 'range': {
1037
+ // Try the guard column and every EC peer / binding-shared column —
1038
+ // any of them carrying a subset range discharges the guard.
1039
+ const cands = candidateColumns(clause.column, ecs, bindings);
1040
+ for (const c of cands) {
1041
+ const filter = facts.rangeBounds.get(c);
1042
+ if (filter && filterRangeSubsetOfGuardRange(filter, clause))
1043
+ return true;
1044
+ }
1045
+ return false;
1046
+ }
1047
+ case 'or-of': {
1048
+ // (a) Any sub-clause directly entailed by facts ⇒ OR entailed.
1049
+ for (const sub of clause.clauses) {
1050
+ if (clauseEntailed(sub, facts, ecs, bindings, isColumnNonNullable))
1051
+ return true;
1052
+ }
1053
+ // (b) Pure-IN specialization: every sub-clause is eq-literal on the
1054
+ // same column. Entailed when the filter pins that column to a
1055
+ // subset of the OR-set.
1056
+ return inListEntailed(clause, facts, ecs, bindings);
1057
+ }
1058
+ }
1059
+ }
1060
+ /**
1061
+ * Specialized discharge for `or-of` clauses whose sub-clauses are all
1062
+ * `eq-literal` on the same column (the IN-list shape). Entailed when the
1063
+ * filter pins that column — via direct literal, IN-list, EC peer, or constant
1064
+ * binding — to a subset of the OR-set. Returns false for any other `or-of`
1065
+ * shape.
1066
+ */
1067
+ function inListEntailed(clause, facts, ecs, bindings) {
1068
+ if (clause.clauses.length === 0)
1069
+ return false;
1070
+ let column;
1071
+ const orSet = [];
1072
+ for (const sub of clause.clauses) {
1073
+ if (sub.kind !== 'eq-literal')
1074
+ return false;
1075
+ if (column === undefined)
1076
+ column = sub.column;
1077
+ else if (column !== sub.column)
1078
+ return false;
1079
+ orSet.push(sub.value);
1080
+ }
1081
+ if (column === undefined)
1082
+ return false;
1083
+ const inOrSet = (v) => {
1084
+ for (const x of orSet)
1085
+ if (sqlValueEquals(x, v))
1086
+ return true;
1087
+ return false;
1088
+ };
1089
+ // Try the column itself and every EC peer plus columns bound to the same
1090
+ // ConstantValue (those are pinned to the same literal).
1091
+ const cands = candidateColumns(column, ecs, bindings);
1092
+ for (const c of cands) {
1093
+ const direct = facts.literalEqs.get(c);
1094
+ if (direct !== undefined && inOrSet(direct))
1095
+ return true;
1096
+ const inList = facts.inListEqs.get(c);
1097
+ if (inList !== undefined && inList.size > 0) {
1098
+ let allIn = true;
1099
+ for (const v of inList) {
1100
+ if (!inOrSet(v)) {
1101
+ allIn = false;
1102
+ break;
1103
+ }
1104
+ }
1105
+ if (allIn)
1106
+ return true;
1107
+ }
1108
+ // Source binding pins this candidate to a literal in the OR-set.
1109
+ const binding = bindingForColumn(c, bindings);
1110
+ if (binding && binding.value.kind === 'literal' && inOrSet(binding.value.value)) {
1111
+ return true;
1112
+ }
1113
+ }
1114
+ return false;
1115
+ }
1116
+ /**
1117
+ * True iff every value satisfying `filter` also satisfies `guard`. Per-side
1118
+ * check: a guard side with no bound is trivially satisfied; otherwise the
1119
+ * filter must have a matching bound that is strictly tighter, or equal-and-
1120
+ * compatible on inclusivity. BINARY collation for text comparison — consistent
1121
+ * with how DomainConstraint range subsumption is handled today.
1122
+ */
1123
+ function filterRangeSubsetOfGuardRange(filter, guard) {
1124
+ if (guard.min !== undefined) {
1125
+ if (filter.min === undefined)
1126
+ return false;
1127
+ const cmp = compareSqlValues(filter.min, guard.min);
1128
+ if (cmp < 0)
1129
+ return false;
1130
+ if (cmp === 0 && filter.minInclusive && !guard.minInclusive)
1131
+ return false;
1132
+ }
1133
+ if (guard.max !== undefined) {
1134
+ if (filter.max === undefined)
1135
+ return false;
1136
+ const cmp = compareSqlValues(filter.max, guard.max);
1137
+ if (cmp > 0)
1138
+ return false;
1139
+ if (cmp === 0 && filter.maxInclusive && !guard.maxInclusive)
1140
+ return false;
1141
+ }
1142
+ return true;
1143
+ }
1144
+ function candidateColumns(column, ecs, bindings) {
1145
+ const out = new Set();
1146
+ out.add(column);
1147
+ const ecIdx = ecIndexOf(column, ecs);
1148
+ if (ecIdx !== undefined) {
1149
+ for (const c of ecs[ecIdx])
1150
+ out.add(c);
1151
+ }
1152
+ // Columns sharing a ConstantBinding with `column` are also pinned to the
1153
+ // same value.
1154
+ const ownBinding = bindingForColumn(column, bindings);
1155
+ if (ownBinding) {
1156
+ for (const c of ownBinding.attrs)
1157
+ out.add(c);
1158
+ }
1159
+ return Array.from(out);
1160
+ }
1161
+ /**
1162
+ * If `n` is "constant" relative to the filter's input stream — true for one
1163
+ * full execution — return its `ConstantValue`; otherwise undefined.
1164
+ *
1165
+ * `LiteralNode` is constant at all times. `ParameterReferenceNode` is
1166
+ * constant for the duration of a single execution: the parameter is bound
1167
+ * once before iteration and the same value is observed by every row. That
1168
+ * matches the scope `computePhysical` describes (per-execution properties),
1169
+ * so the EC layer treats parameters and literals uniformly. Subqueries /
1170
+ * correlated expressions remain rejected — they can vary per-row.
1171
+ *
1172
+ * `CastNode` and `CollateNode` are peeled through: they don't change row-wise
1173
+ * behaviour (constant-after-cast is still constant). For a literal under a
1174
+ * cast the inner `SqlValue` is reported as-is — downstream consumers needing
1175
+ * the post-cast value must apply the cast themselves.
1176
+ */
1177
+ function constantValueOf(n) {
1178
+ while (n instanceof CastNode || n instanceof CollateNode) {
1179
+ n = n.operand;
1180
+ }
1181
+ if (n instanceof LiteralNode) {
1182
+ const v = n.expression.value;
1183
+ if (v instanceof Promise)
1184
+ return undefined;
1185
+ return { kind: 'literal', value: v };
1186
+ }
1187
+ if (n instanceof ParameterReferenceNode) {
1188
+ return { kind: 'parameter', paramRef: n.nameOrIndex };
1189
+ }
1190
+ return undefined;
1191
+ }
1192
+ // ---------------------------------------------------------------------------
1193
+ // ConstantBinding helpers
1194
+ // ---------------------------------------------------------------------------
1195
+ // Structural value-equality used by `mergeConstantBindings` to coalesce
1196
+ // bindings on identical values. ConstantBinding.source is NOT compared at
1197
+ // merge time — see `fdsEqual` for the dedup-precedence rule.
1198
+ function constantValueEquals(a, b) {
1199
+ if (a.kind !== b.kind)
1200
+ return false;
1201
+ if (a.kind === 'literal' && b.kind === 'literal') {
1202
+ const av = a.value;
1203
+ const bv = b.value;
1204
+ if (av === bv)
1205
+ return true;
1206
+ // Compare bigint/number/string by their textual form; everything else by identity.
1207
+ if (av instanceof Uint8Array && bv instanceof Uint8Array) {
1208
+ if (av.length !== bv.length)
1209
+ return false;
1210
+ for (let i = 0; i < av.length; i++)
1211
+ if (av[i] !== bv[i])
1212
+ return false;
1213
+ return true;
1214
+ }
1215
+ return false;
1216
+ }
1217
+ if (a.kind === 'parameter' && b.kind === 'parameter') {
1218
+ return a.paramRef === b.paramRef;
1219
+ }
1220
+ return false;
1221
+ }
1222
+ function unionAttrs(a, b) {
1223
+ const out = new Set(a);
1224
+ for (const x of b)
1225
+ out.add(x);
1226
+ return Array.from(out).sort((x, y) => x - y);
1227
+ }
1228
+ function normalizeBinding(b) {
1229
+ const attrs = Array.from(new Set(b.attrs)).sort((x, y) => x - y);
1230
+ return { attrs, value: b.value };
1231
+ }
1232
+ /**
1233
+ * Merge two binding lists, coalescing bindings that share a `ConstantValue`
1234
+ * by unioning their `attrs`. Caps the result at `MAX_FDS_PER_NODE`; later
1235
+ * additions are dropped when the cap is exceeded — bindings sourced from
1236
+ * earlier nodes are preferred since they typically sit closer to keyed
1237
+ * columns. Truncations are logged under `quereus:planner:fd`.
1238
+ */
1239
+ export function mergeConstantBindings(a, b) {
1240
+ const result = a.map(normalizeBinding);
1241
+ for (const raw of b) {
1242
+ const next = normalizeBinding(raw);
1243
+ let merged = false;
1244
+ for (let i = 0; i < result.length; i++) {
1245
+ if (constantValueEquals(result[i].value, next.value)) {
1246
+ result[i] = { attrs: unionAttrs(result[i].attrs, next.attrs), value: result[i].value };
1247
+ merged = true;
1248
+ break;
1249
+ }
1250
+ }
1251
+ if (!merged)
1252
+ result.push(next);
1253
+ }
1254
+ return enforceBindingCap(result);
1255
+ }
1256
+ function enforceBindingCap(bindings) {
1257
+ if (bindings.length <= MAX_FDS_PER_NODE)
1258
+ return bindings;
1259
+ const kept = bindings.slice(0, MAX_FDS_PER_NODE);
1260
+ log('ConstantBinding cap reached: dropped %d binding(s) from %d', bindings.length - kept.length, bindings.length);
1261
+ return kept;
1262
+ }
1263
+ /**
1264
+ * Extend `bindings` over `ecs`: if a binding pins column `c` to value `v` and
1265
+ * `c` is in an equivalence class `{c, c2, ...}`, fold every member of that
1266
+ * class into the binding's `attrs`. This is what lets predicate-inference
1267
+ * rules consume bindings directly without walking ECs.
1268
+ */
1269
+ export function closeConstantBindingsOverEcs(bindings, ecs) {
1270
+ if (bindings.length === 0)
1271
+ return [];
1272
+ if (ecs.length === 0)
1273
+ return bindings.map(normalizeBinding);
1274
+ const result = [];
1275
+ for (const binding of bindings) {
1276
+ const expanded = new Set(binding.attrs);
1277
+ let grew = true;
1278
+ while (grew) {
1279
+ grew = false;
1280
+ for (const cls of ecs) {
1281
+ const overlap = cls.some(c => expanded.has(c));
1282
+ if (!overlap)
1283
+ continue;
1284
+ for (const c of cls) {
1285
+ if (!expanded.has(c)) {
1286
+ expanded.add(c);
1287
+ grew = true;
1288
+ }
1289
+ }
1290
+ }
1291
+ }
1292
+ result.push({ attrs: Array.from(expanded).sort((x, y) => x - y), value: binding.value });
1293
+ }
1294
+ // Two bindings might now alias to the same value/attrs after closure; coalesce.
1295
+ return mergeConstantBindings(result, []);
1296
+ }
1297
+ /**
1298
+ * Project bindings through `mapping` (oldCol → newCol). A binding whose
1299
+ * `attrs` lose every member is dropped; otherwise the surviving members are
1300
+ * remapped.
1301
+ */
1302
+ export function projectConstantBindings(bindings, mapping) {
1303
+ const out = [];
1304
+ for (const binding of bindings) {
1305
+ const mapped = [];
1306
+ for (const c of binding.attrs) {
1307
+ const m = mapping.get(c);
1308
+ if (m !== undefined && !mapped.includes(m))
1309
+ mapped.push(m);
1310
+ }
1311
+ if (mapped.length === 0)
1312
+ continue;
1313
+ out.push({ attrs: mapped.sort((x, y) => x - y), value: binding.value });
1314
+ }
1315
+ return mergeConstantBindings(out, []);
1316
+ }
1317
+ /** Shift `attrs` by `offset` (column-index translation for joins). */
1318
+ export function shiftConstantBindings(bindings, offset) {
1319
+ if (offset === 0)
1320
+ return bindings.map(normalizeBinding);
1321
+ return bindings.map(binding => ({
1322
+ attrs: binding.attrs.map(c => c + offset).sort((x, y) => x - y),
1323
+ value: binding.value,
1324
+ }));
1325
+ }
1326
+ function sqlValueEquals(a, b) {
1327
+ if (a === b)
1328
+ return true;
1329
+ if (a instanceof Uint8Array && b instanceof Uint8Array) {
1330
+ if (a.length !== b.length)
1331
+ return false;
1332
+ for (let i = 0; i < a.length; i++)
1333
+ if (a[i] !== b[i])
1334
+ return false;
1335
+ return true;
1336
+ }
1337
+ return false;
1338
+ }
1339
+ // Structural-only equality. The optional `source` provenance tag is NOT
1340
+ // compared — see `fdsEqual` above for the dedup-precedence rule.
1341
+ function domainConstraintEquals(a, b) {
1342
+ if (a.column !== b.column || a.kind !== b.kind)
1343
+ return false;
1344
+ if (a.kind === 'range' && b.kind === 'range') {
1345
+ const aHasMin = a.min !== undefined;
1346
+ const bHasMin = b.min !== undefined;
1347
+ if (aHasMin !== bHasMin)
1348
+ return false;
1349
+ if (aHasMin && !sqlValueEquals(a.min, b.min))
1350
+ return false;
1351
+ if (aHasMin && a.minInclusive !== b.minInclusive)
1352
+ return false;
1353
+ const aHasMax = a.max !== undefined;
1354
+ const bHasMax = b.max !== undefined;
1355
+ if (aHasMax !== bHasMax)
1356
+ return false;
1357
+ if (aHasMax && !sqlValueEquals(a.max, b.max))
1358
+ return false;
1359
+ if (aHasMax && a.maxInclusive !== b.maxInclusive)
1360
+ return false;
1361
+ return true;
1362
+ }
1363
+ if (a.kind === 'enum' && b.kind === 'enum') {
1364
+ if (a.values.length !== b.values.length)
1365
+ return false;
1366
+ for (let i = 0; i < a.values.length; i++) {
1367
+ if (!sqlValueEquals(a.values[i], b.values[i]))
1368
+ return false;
1369
+ }
1370
+ return true;
1371
+ }
1372
+ return false;
1373
+ }
1374
+ /**
1375
+ * Concatenate two domain-constraint lists, dropping structurally equal
1376
+ * duplicates. We deliberately do NOT intersect overlapping range/enum
1377
+ * constraints on the same column — that's deferred to the
1378
+ * predicate-contradiction-detection ticket. Caps at `MAX_FDS_PER_NODE`.
1379
+ */
1380
+ export function mergeDomainConstraints(a, b) {
1381
+ const result = a.slice();
1382
+ for (const next of b) {
1383
+ if (result.some(existing => domainConstraintEquals(existing, next)))
1384
+ continue;
1385
+ result.push(next);
1386
+ }
1387
+ return enforceDomainCap(result);
1388
+ }
1389
+ function enforceDomainCap(domains) {
1390
+ if (domains.length <= MAX_FDS_PER_NODE)
1391
+ return domains;
1392
+ const kept = domains.slice(0, MAX_FDS_PER_NODE);
1393
+ log('DomainConstraint cap reached: dropped %d domain(s) from %d', domains.length - kept.length, domains.length);
1394
+ return kept;
1395
+ }
1396
+ /**
1397
+ * Project domain constraints through `mapping` (oldCol → newCol). Drops any
1398
+ * constraint whose column is not in the mapping; remaps the rest.
1399
+ */
1400
+ export function projectDomainConstraints(domains, mapping) {
1401
+ const out = [];
1402
+ for (const domain of domains) {
1403
+ const mapped = mapping.get(domain.column);
1404
+ if (mapped === undefined)
1405
+ continue;
1406
+ out.push({ ...domain, column: mapped });
1407
+ }
1408
+ return mergeDomainConstraints(out, []);
1409
+ }
1410
+ /** Shift every domain constraint's `column` by `offset` (join translation). */
1411
+ export function shiftDomainConstraints(domains, offset) {
1412
+ if (offset === 0)
1413
+ return domains.slice();
1414
+ return domains.map(domain => ({ ...domain, column: domain.column + offset }));
1415
+ }
1416
+ //# sourceMappingURL=fd-utils.js.map