@quereus/quereus 2.8.0 → 2.9.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 (214) hide show
  1. package/dist/src/emit/ast-stringify.d.ts.map +1 -1
  2. package/dist/src/emit/ast-stringify.js +0 -3
  3. package/dist/src/emit/ast-stringify.js.map +1 -1
  4. package/dist/src/parser/ast.d.ts +3 -2
  5. package/dist/src/parser/ast.d.ts.map +1 -1
  6. package/dist/src/parser/parser.d.ts.map +1 -1
  7. package/dist/src/parser/parser.js +13 -4
  8. package/dist/src/parser/parser.js.map +1 -1
  9. package/dist/src/planner/building/foreign-key-builder.d.ts.map +1 -1
  10. package/dist/src/planner/building/foreign-key-builder.js +3 -2
  11. package/dist/src/planner/building/foreign-key-builder.js.map +1 -1
  12. package/dist/src/planner/building/select.js +14 -2
  13. package/dist/src/planner/building/select.js.map +1 -1
  14. package/dist/src/planner/building/update.d.ts.map +1 -1
  15. package/dist/src/planner/building/update.js +10 -6
  16. package/dist/src/planner/building/update.js.map +1 -1
  17. package/dist/src/planner/framework/characteristics.d.ts +3 -1
  18. package/dist/src/planner/framework/characteristics.d.ts.map +1 -1
  19. package/dist/src/planner/framework/characteristics.js +7 -0
  20. package/dist/src/planner/framework/characteristics.js.map +1 -1
  21. package/dist/src/planner/framework/physical-utils.d.ts +22 -1
  22. package/dist/src/planner/framework/physical-utils.d.ts.map +1 -1
  23. package/dist/src/planner/framework/physical-utils.js +47 -0
  24. package/dist/src/planner/framework/physical-utils.js.map +1 -1
  25. package/dist/src/planner/nodes/alias-node.d.ts.map +1 -1
  26. package/dist/src/planner/nodes/alias-node.js +2 -0
  27. package/dist/src/planner/nodes/alias-node.js.map +1 -1
  28. package/dist/src/planner/nodes/asof-scan-node.d.ts +137 -0
  29. package/dist/src/planner/nodes/asof-scan-node.d.ts.map +1 -0
  30. package/dist/src/planner/nodes/asof-scan-node.js +223 -0
  31. package/dist/src/planner/nodes/asof-scan-node.js.map +1 -0
  32. package/dist/src/planner/nodes/constraint-check-node.d.ts +3 -0
  33. package/dist/src/planner/nodes/constraint-check-node.d.ts.map +1 -1
  34. package/dist/src/planner/nodes/constraint-check-node.js.map +1 -1
  35. package/dist/src/planner/nodes/distinct-node.d.ts.map +1 -1
  36. package/dist/src/planner/nodes/distinct-node.js +7 -0
  37. package/dist/src/planner/nodes/distinct-node.js.map +1 -1
  38. package/dist/src/planner/nodes/filter.d.ts.map +1 -1
  39. package/dist/src/planner/nodes/filter.js +2 -0
  40. package/dist/src/planner/nodes/filter.js.map +1 -1
  41. package/dist/src/planner/nodes/function.d.ts +11 -1
  42. package/dist/src/planner/nodes/function.d.ts.map +1 -1
  43. package/dist/src/planner/nodes/function.js +94 -1
  44. package/dist/src/planner/nodes/function.js.map +1 -1
  45. package/dist/src/planner/nodes/hash-aggregate.d.ts.map +1 -1
  46. package/dist/src/planner/nodes/hash-aggregate.js +2 -0
  47. package/dist/src/planner/nodes/hash-aggregate.js.map +1 -1
  48. package/dist/src/planner/nodes/join-node.d.ts.map +1 -1
  49. package/dist/src/planner/nodes/join-node.js +10 -2
  50. package/dist/src/planner/nodes/join-node.js.map +1 -1
  51. package/dist/src/planner/nodes/join-utils.d.ts +19 -1
  52. package/dist/src/planner/nodes/join-utils.d.ts.map +1 -1
  53. package/dist/src/planner/nodes/join-utils.js +46 -0
  54. package/dist/src/planner/nodes/join-utils.js.map +1 -1
  55. package/dist/src/planner/nodes/limit-offset.d.ts.map +1 -1
  56. package/dist/src/planner/nodes/limit-offset.js +2 -0
  57. package/dist/src/planner/nodes/limit-offset.js.map +1 -1
  58. package/dist/src/planner/nodes/merge-join-node.d.ts.map +1 -1
  59. package/dist/src/planner/nodes/merge-join-node.js +4 -1
  60. package/dist/src/planner/nodes/merge-join-node.js.map +1 -1
  61. package/dist/src/planner/nodes/ordinal-slice-node.d.ts +50 -0
  62. package/dist/src/planner/nodes/ordinal-slice-node.d.ts.map +1 -0
  63. package/dist/src/planner/nodes/ordinal-slice-node.js +127 -0
  64. package/dist/src/planner/nodes/ordinal-slice-node.js.map +1 -0
  65. package/dist/src/planner/nodes/plan-node-type.d.ts +2 -0
  66. package/dist/src/planner/nodes/plan-node-type.d.ts.map +1 -1
  67. package/dist/src/planner/nodes/plan-node-type.js +2 -0
  68. package/dist/src/planner/nodes/plan-node-type.js.map +1 -1
  69. package/dist/src/planner/nodes/plan-node.d.ts +130 -1
  70. package/dist/src/planner/nodes/plan-node.d.ts.map +1 -1
  71. package/dist/src/planner/nodes/plan-node.js +49 -0
  72. package/dist/src/planner/nodes/plan-node.js.map +1 -1
  73. package/dist/src/planner/nodes/project-node.d.ts.map +1 -1
  74. package/dist/src/planner/nodes/project-node.js +6 -1
  75. package/dist/src/planner/nodes/project-node.js.map +1 -1
  76. package/dist/src/planner/nodes/reference.d.ts +4 -1
  77. package/dist/src/planner/nodes/reference.d.ts.map +1 -1
  78. package/dist/src/planner/nodes/reference.js +16 -0
  79. package/dist/src/planner/nodes/reference.js.map +1 -1
  80. package/dist/src/planner/nodes/scalar.d.ts +7 -1
  81. package/dist/src/planner/nodes/scalar.d.ts.map +1 -1
  82. package/dist/src/planner/nodes/scalar.js +100 -1
  83. package/dist/src/planner/nodes/scalar.js.map +1 -1
  84. package/dist/src/planner/nodes/set-operation-node.d.ts +2 -1
  85. package/dist/src/planner/nodes/set-operation-node.d.ts.map +1 -1
  86. package/dist/src/planner/nodes/set-operation-node.js +9 -0
  87. package/dist/src/planner/nodes/set-operation-node.js.map +1 -1
  88. package/dist/src/planner/nodes/sort.d.ts.map +1 -1
  89. package/dist/src/planner/nodes/sort.js +20 -0
  90. package/dist/src/planner/nodes/sort.js.map +1 -1
  91. package/dist/src/planner/nodes/stream-aggregate.d.ts.map +1 -1
  92. package/dist/src/planner/nodes/stream-aggregate.js +2 -0
  93. package/dist/src/planner/nodes/stream-aggregate.js.map +1 -1
  94. package/dist/src/planner/nodes/table-access-nodes.d.ts +37 -2
  95. package/dist/src/planner/nodes/table-access-nodes.d.ts.map +1 -1
  96. package/dist/src/planner/nodes/table-access-nodes.js +87 -10
  97. package/dist/src/planner/nodes/table-access-nodes.js.map +1 -1
  98. package/dist/src/planner/nodes/update-node.d.ts +1 -3
  99. package/dist/src/planner/nodes/update-node.d.ts.map +1 -1
  100. package/dist/src/planner/nodes/update-node.js +3 -9
  101. package/dist/src/planner/nodes/update-node.js.map +1 -1
  102. package/dist/src/planner/nodes/window-node.d.ts +61 -2
  103. package/dist/src/planner/nodes/window-node.d.ts.map +1 -1
  104. package/dist/src/planner/nodes/window-node.js +64 -3
  105. package/dist/src/planner/nodes/window-node.js.map +1 -1
  106. package/dist/src/planner/optimizer-tuning.d.ts +9 -0
  107. package/dist/src/planner/optimizer-tuning.d.ts.map +1 -1
  108. package/dist/src/planner/optimizer-tuning.js +3 -0
  109. package/dist/src/planner/optimizer-tuning.js.map +1 -1
  110. package/dist/src/planner/optimizer.d.ts.map +1 -1
  111. package/dist/src/planner/optimizer.js +91 -0
  112. package/dist/src/planner/optimizer.js.map +1 -1
  113. package/dist/src/planner/rules/access/rule-asof-strategy-select.d.ts +30 -0
  114. package/dist/src/planner/rules/access/rule-asof-strategy-select.d.ts.map +1 -0
  115. package/dist/src/planner/rules/access/rule-asof-strategy-select.js +112 -0
  116. package/dist/src/planner/rules/access/rule-asof-strategy-select.js.map +1 -0
  117. package/dist/src/planner/rules/access/rule-monotonic-limit-pushdown.d.ts +33 -0
  118. package/dist/src/planner/rules/access/rule-monotonic-limit-pushdown.d.ts.map +1 -0
  119. package/dist/src/planner/rules/access/rule-monotonic-limit-pushdown.js +162 -0
  120. package/dist/src/planner/rules/access/rule-monotonic-limit-pushdown.js.map +1 -0
  121. package/dist/src/planner/rules/access/rule-monotonic-range-access.d.ts +29 -0
  122. package/dist/src/planner/rules/access/rule-monotonic-range-access.d.ts.map +1 -0
  123. package/dist/src/planner/rules/access/rule-monotonic-range-access.js +175 -0
  124. package/dist/src/planner/rules/access/rule-monotonic-range-access.js.map +1 -0
  125. package/dist/src/planner/rules/access/rule-select-access-path.d.ts.map +1 -1
  126. package/dist/src/planner/rules/access/rule-select-access-path.js +31 -10
  127. package/dist/src/planner/rules/access/rule-select-access-path.js.map +1 -1
  128. package/dist/src/planner/rules/join/equi-pair-extractor.d.ts +61 -0
  129. package/dist/src/planner/rules/join/equi-pair-extractor.d.ts.map +1 -0
  130. package/dist/src/planner/rules/join/equi-pair-extractor.js +155 -0
  131. package/dist/src/planner/rules/join/equi-pair-extractor.js.map +1 -0
  132. package/dist/src/planner/rules/join/rule-join-physical-selection.d.ts.map +1 -1
  133. package/dist/src/planner/rules/join/rule-join-physical-selection.js +2 -122
  134. package/dist/src/planner/rules/join/rule-join-physical-selection.js.map +1 -1
  135. package/dist/src/planner/rules/join/rule-lateral-top1-asof.d.ts +21 -0
  136. package/dist/src/planner/rules/join/rule-lateral-top1-asof.d.ts.map +1 -0
  137. package/dist/src/planner/rules/join/rule-lateral-top1-asof.js +405 -0
  138. package/dist/src/planner/rules/join/rule-lateral-top1-asof.js.map +1 -0
  139. package/dist/src/planner/rules/join/rule-monotonic-merge-join.d.ts +31 -0
  140. package/dist/src/planner/rules/join/rule-monotonic-merge-join.d.ts.map +1 -0
  141. package/dist/src/planner/rules/join/rule-monotonic-merge-join.js +113 -0
  142. package/dist/src/planner/rules/join/rule-monotonic-merge-join.js.map +1 -0
  143. package/dist/src/planner/rules/window/rule-monotonic-window.d.ts +47 -0
  144. package/dist/src/planner/rules/window/rule-monotonic-window.d.ts.map +1 -0
  145. package/dist/src/planner/rules/window/rule-monotonic-window.js +341 -0
  146. package/dist/src/planner/rules/window/rule-monotonic-window.js.map +1 -0
  147. package/dist/src/runtime/context-helpers.d.ts +9 -0
  148. package/dist/src/runtime/context-helpers.d.ts.map +1 -1
  149. package/dist/src/runtime/context-helpers.js +5 -0
  150. package/dist/src/runtime/context-helpers.js.map +1 -1
  151. package/dist/src/runtime/emit/asof-scan.d.ts +10 -0
  152. package/dist/src/runtime/emit/asof-scan.d.ts.map +1 -0
  153. package/dist/src/runtime/emit/asof-scan.js +467 -0
  154. package/dist/src/runtime/emit/asof-scan.js.map +1 -0
  155. package/dist/src/runtime/emit/constraint-check.d.ts.map +1 -1
  156. package/dist/src/runtime/emit/constraint-check.js +20 -0
  157. package/dist/src/runtime/emit/constraint-check.js.map +1 -1
  158. package/dist/src/runtime/emit/ordinal-slice.d.ts +13 -0
  159. package/dist/src/runtime/emit/ordinal-slice.d.ts.map +1 -0
  160. package/dist/src/runtime/emit/ordinal-slice.js +89 -0
  161. package/dist/src/runtime/emit/ordinal-slice.js.map +1 -0
  162. package/dist/src/runtime/emit/returning.d.ts.map +1 -1
  163. package/dist/src/runtime/emit/returning.js +9 -4
  164. package/dist/src/runtime/emit/returning.js.map +1 -1
  165. package/dist/src/runtime/emit/scan.d.ts +19 -3
  166. package/dist/src/runtime/emit/scan.d.ts.map +1 -1
  167. package/dist/src/runtime/emit/scan.js +12 -8
  168. package/dist/src/runtime/emit/scan.js.map +1 -1
  169. package/dist/src/runtime/emit/schema-declarative.d.ts.map +1 -1
  170. package/dist/src/runtime/emit/schema-declarative.js +91 -14
  171. package/dist/src/runtime/emit/schema-declarative.js.map +1 -1
  172. package/dist/src/runtime/emit/window.d.ts.map +1 -1
  173. package/dist/src/runtime/emit/window.js +732 -37
  174. package/dist/src/runtime/emit/window.js.map +1 -1
  175. package/dist/src/runtime/foreign-key-actions.js +5 -4
  176. package/dist/src/runtime/foreign-key-actions.js.map +1 -1
  177. package/dist/src/runtime/register.d.ts.map +1 -1
  178. package/dist/src/runtime/register.js +4 -0
  179. package/dist/src/runtime/register.js.map +1 -1
  180. package/dist/src/schema/catalog.d.ts +10 -0
  181. package/dist/src/schema/catalog.d.ts.map +1 -1
  182. package/dist/src/schema/catalog.js +29 -6
  183. package/dist/src/schema/catalog.js.map +1 -1
  184. package/dist/src/schema/function.d.ts +24 -0
  185. package/dist/src/schema/function.d.ts.map +1 -1
  186. package/dist/src/schema/function.js.map +1 -1
  187. package/dist/src/schema/manager.d.ts +10 -0
  188. package/dist/src/schema/manager.d.ts.map +1 -1
  189. package/dist/src/schema/manager.js +10 -0
  190. package/dist/src/schema/manager.js.map +1 -1
  191. package/dist/src/schema/schema-differ.d.ts +18 -1
  192. package/dist/src/schema/schema-differ.d.ts.map +1 -1
  193. package/dist/src/schema/schema-differ.js +307 -42
  194. package/dist/src/schema/schema-differ.js.map +1 -1
  195. package/dist/src/types/logical-type.d.ts +11 -0
  196. package/dist/src/types/logical-type.d.ts.map +1 -1
  197. package/dist/src/types/logical-type.js.map +1 -1
  198. package/dist/src/util/ast-literal.d.ts +11 -0
  199. package/dist/src/util/ast-literal.d.ts.map +1 -0
  200. package/dist/src/util/ast-literal.js +26 -0
  201. package/dist/src/util/ast-literal.js.map +1 -0
  202. package/dist/src/vtab/best-access-plan.d.ts +41 -0
  203. package/dist/src/vtab/best-access-plan.d.ts.map +1 -1
  204. package/dist/src/vtab/best-access-plan.js +29 -0
  205. package/dist/src/vtab/best-access-plan.js.map +1 -1
  206. package/dist/src/vtab/filter-info.d.ts +14 -0
  207. package/dist/src/vtab/filter-info.d.ts.map +1 -1
  208. package/dist/src/vtab/memory/module.d.ts +39 -1
  209. package/dist/src/vtab/memory/module.d.ts.map +1 -1
  210. package/dist/src/vtab/memory/module.js +206 -44
  211. package/dist/src/vtab/memory/module.js.map +1 -1
  212. package/dist/src/vtab/module.d.ts +24 -0
  213. package/dist/src/vtab/module.d.ts.map +1 -1
  214. package/package.json +3 -3
@@ -0,0 +1,405 @@
1
+ /**
2
+ * Rule: Recognize lateral-top-1 over asof predicate, replace with AsofScanNode.
3
+ *
4
+ * Pattern (the rule peels through these in any nesting order, ignoring AliasNode):
5
+ *
6
+ * JoinNode (joinType ∈ {inner, left, cross} with `on true` or no condition)
7
+ * left: Left
8
+ * right: AliasNode? | ProjectNode | LimitOffsetNode(LIMIT 1, no OFFSET) | SortNode
9
+ * ...peeled in any nesting order...
10
+ * └─ FilterNode (ANDed: q.K op left.K AND q.P_i = left.P_i ...)
11
+ * └─ <physical right> with monotonicOn(K) and accessCapabilities.asofRight
12
+ *
13
+ * The right side must be correlated against the left (uses outer columns). On
14
+ * a successful match we emit an AsofScanNode preserving the JoinNode's output
15
+ * attribute IDs; otherwise we return null and the existing physical-join
16
+ * selection takes over.
17
+ */
18
+ import { createLogger } from '../../../common/logger.js';
19
+ import { isRelationalNode } from '../../nodes/plan-node.js';
20
+ import { JoinNode } from '../../nodes/join-node.js';
21
+ import { AsofScanNode } from '../../nodes/asof-scan-node.js';
22
+ import { ProjectNode } from '../../nodes/project-node.js';
23
+ import { LimitOffsetNode } from '../../nodes/limit-offset.js';
24
+ import { SortNode } from '../../nodes/sort.js';
25
+ import { FilterNode } from '../../nodes/filter.js';
26
+ import { AliasNode } from '../../nodes/alias-node.js';
27
+ import { BinaryOpNode, LiteralNode } from '../../nodes/scalar.js';
28
+ import { ColumnReferenceNode, TableReferenceNode } from '../../nodes/reference.js';
29
+ import { RetrieveNode } from '../../nodes/retrieve-node.js';
30
+ import { isCorrelatedSubquery } from '../../cache/correlation-detector.js';
31
+ const log = createLogger('optimizer:rule:lateral-top1-asof');
32
+ /**
33
+ * Peel the right subtree of the lateral, looking for the canonical
34
+ * (Project | LimitOffset | Sort | Alias)* → Filter chain.
35
+ */
36
+ function peelLateral(right) {
37
+ let project;
38
+ let limit;
39
+ let sort;
40
+ let cursor = right;
41
+ let safety = 16;
42
+ while (safety-- > 0) {
43
+ if (cursor instanceof AliasNode) {
44
+ cursor = cursor.source;
45
+ continue;
46
+ }
47
+ if (cursor instanceof ProjectNode) {
48
+ if (project)
49
+ return null; // multiple Projects not handled
50
+ project = cursor;
51
+ cursor = cursor.source;
52
+ continue;
53
+ }
54
+ if (cursor instanceof LimitOffsetNode) {
55
+ if (limit)
56
+ return null;
57
+ limit = cursor;
58
+ cursor = cursor.source;
59
+ continue;
60
+ }
61
+ if (cursor instanceof SortNode) {
62
+ if (sort)
63
+ return null;
64
+ sort = cursor;
65
+ cursor = cursor.source;
66
+ continue;
67
+ }
68
+ break;
69
+ }
70
+ if (!limit || !sort || !(cursor instanceof FilterNode))
71
+ return null;
72
+ return { filter: cursor, limit, sort, project };
73
+ }
74
+ /** Verify that the limit is the literal integer 1 and there's no offset. */
75
+ function isLimitOne(limit) {
76
+ if (limit.offset && !isLiteralZeroOrUndefined(limit.offset))
77
+ return false;
78
+ if (!limit.limit)
79
+ return false;
80
+ if (!(limit.limit instanceof LiteralNode))
81
+ return false;
82
+ const v = limit.limit.expression.value;
83
+ if (typeof v === 'number')
84
+ return v === 1;
85
+ if (typeof v === 'bigint')
86
+ return v === 1n;
87
+ return false;
88
+ }
89
+ /** A null-or-zero literal for OFFSET. */
90
+ function isLiteralZeroOrUndefined(node) {
91
+ if (!(node instanceof LiteralNode))
92
+ return false;
93
+ const v = node.expression.value;
94
+ if (v === null || v === undefined)
95
+ return true;
96
+ if (typeof v === 'number')
97
+ return v === 0;
98
+ if (typeof v === 'bigint')
99
+ return v === 0n;
100
+ return false;
101
+ }
102
+ /**
103
+ * Verify the SortNode has a single key that is a trivial column reference and
104
+ * a definite direction. Returns the attribute id and direction, or null.
105
+ */
106
+ function extractSortAttrId(sort) {
107
+ if (sort.sortKeys.length !== 1)
108
+ return null;
109
+ const key = sort.sortKeys[0];
110
+ if (key.direction !== 'desc' && key.direction !== 'asc')
111
+ return null;
112
+ if (!(key.expression instanceof ColumnReferenceNode))
113
+ return null;
114
+ return { attrId: key.expression.attributeId, direction: key.direction };
115
+ }
116
+ /** Walk an AND tree, returning each leaf conjunct. */
117
+ function flattenAnd(node) {
118
+ const result = [];
119
+ const stack = [node];
120
+ while (stack.length) {
121
+ const cur = stack.pop();
122
+ if (cur instanceof BinaryOpNode && cur.expression.operator === 'AND') {
123
+ stack.push(cur.left, cur.right);
124
+ }
125
+ else {
126
+ result.push(cur);
127
+ }
128
+ }
129
+ return result;
130
+ }
131
+ /**
132
+ * Classify each conjunct of the lateral's Filter.
133
+ *
134
+ * `rightAttrIds` is the set of attribute IDs defined within the lateral's
135
+ * inner subtree (anything below the Filter). The asof inequality must
136
+ * resolve to (right K op left K); equalities must resolve to (right P = left P).
137
+ *
138
+ * Returns null when any conjunct does not fit the asof shape.
139
+ */
140
+ function classifyPredicates(conjuncts, rightAttrIds) {
141
+ let asof;
142
+ const partition = [];
143
+ for (const c of conjuncts) {
144
+ if (!(c instanceof BinaryOpNode))
145
+ return null;
146
+ if (!(c.left instanceof ColumnReferenceNode) || !(c.right instanceof ColumnReferenceNode))
147
+ return null;
148
+ const op = c.expression.operator;
149
+ const lAttrId = c.left.attributeId;
150
+ const rAttrId = c.right.attributeId;
151
+ const lFromRight = rightAttrIds.has(lAttrId);
152
+ const rFromRight = rightAttrIds.has(rAttrId);
153
+ // Both sides referencing the same side are not valid asof shape.
154
+ if (lFromRight === rFromRight)
155
+ return null;
156
+ // Canonicalize as (right side, left side).
157
+ const rightAttrId = lFromRight ? lAttrId : rAttrId;
158
+ const leftAttrId = lFromRight ? rAttrId : lAttrId;
159
+ if (op === '=') {
160
+ partition.push({ leftAttrId, rightAttrId });
161
+ continue;
162
+ }
163
+ // Asof inequality. Two directions are supported:
164
+ // 'desc' — right ≤ left (latest right ≤ left); strict variant: right < left.
165
+ // 'asc' — right ≥ left (earliest right ≥ left); strict variant: right > left.
166
+ // All operator forms canonicalize to (right op left) before mapping:
167
+ // - q.K <= t.K → desc, strict=false - q.K >= t.K → asc, strict=false
168
+ // - q.K < t.K → desc, strict=true - q.K > t.K → asc, strict=true
169
+ // - t.K >= q.K → desc, strict=false - t.K <= q.K → asc, strict=false
170
+ // - t.K > q.K → desc, strict=true - t.K < q.K → asc, strict=true
171
+ let strict;
172
+ let direction;
173
+ if (lFromRight) {
174
+ // op is between (right.col, left.col)
175
+ if (op === '<=') {
176
+ strict = false;
177
+ direction = 'desc';
178
+ }
179
+ else if (op === '<') {
180
+ strict = true;
181
+ direction = 'desc';
182
+ }
183
+ else if (op === '>=') {
184
+ strict = false;
185
+ direction = 'asc';
186
+ }
187
+ else if (op === '>') {
188
+ strict = true;
189
+ direction = 'asc';
190
+ }
191
+ }
192
+ else {
193
+ // op is between (left.col, right.col); flip
194
+ if (op === '>=') {
195
+ strict = false;
196
+ direction = 'desc';
197
+ }
198
+ else if (op === '>') {
199
+ strict = true;
200
+ direction = 'desc';
201
+ }
202
+ else if (op === '<=') {
203
+ strict = false;
204
+ direction = 'asc';
205
+ }
206
+ else if (op === '<') {
207
+ strict = true;
208
+ direction = 'asc';
209
+ }
210
+ }
211
+ if (strict === undefined || direction === undefined)
212
+ return null;
213
+ if (asof)
214
+ return null; // multiple asof inequalities — bail
215
+ asof = { rightAttrId, leftAttrId, strict, direction };
216
+ }
217
+ if (!asof)
218
+ return null;
219
+ return { asof, partition };
220
+ }
221
+ /**
222
+ * Find the table reference at the bottom of a structural pipeline (Filter →
223
+ * AliasNode → ProjectNode → ... → Retrieve → TableReference).
224
+ *
225
+ * Returns null when the chain branches or terminates before reaching a leaf
226
+ * that's a direct TableReference.
227
+ */
228
+ function findTableReference(node) {
229
+ let cur = node;
230
+ let safety = 64;
231
+ while (safety-- > 0) {
232
+ if (cur instanceof TableReferenceNode)
233
+ return cur;
234
+ if (cur instanceof RetrieveNode) {
235
+ cur = cur.tableRef;
236
+ continue;
237
+ }
238
+ const relChildren = cur.getChildren().filter(isRelationalNode);
239
+ if (relChildren.length !== 1)
240
+ return null;
241
+ cur = relChildren[0];
242
+ }
243
+ return null;
244
+ }
245
+ /**
246
+ * Probe the table's vtab module to see if its best-access plan would advertise
247
+ * `supportsAsofRight` and `monotonicOn` on `column` for an ordered, unfiltered
248
+ * scan. If the module lacks `getBestAccessPlan`, returns false.
249
+ */
250
+ function tableAdvertisesAsof(context, tableRef, column) {
251
+ const vtabModule = tableRef.vtabModule;
252
+ if (!vtabModule.getBestAccessPlan)
253
+ return false;
254
+ const tableSchema = tableRef.tableSchema;
255
+ const columns = tableSchema.columns.map((col, index) => ({
256
+ index,
257
+ name: col.name,
258
+ type: col.logicalType,
259
+ isPrimaryKey: col.primaryKey || false,
260
+ isUnique: col.primaryKey || false,
261
+ }));
262
+ const request = {
263
+ columns,
264
+ filters: [],
265
+ requiredOrdering: [{ columnIndex: column, desc: false }],
266
+ estimatedRows: tableRef.estimatedRows ?? undefined,
267
+ };
268
+ try {
269
+ const plan = vtabModule.getBestAccessPlan(context.db, tableSchema, request);
270
+ if (!plan.supportsAsofRight)
271
+ return false;
272
+ if (!plan.monotonicOn)
273
+ return false;
274
+ if (plan.monotonicOn.columnIndex !== column)
275
+ return false;
276
+ return true;
277
+ }
278
+ catch {
279
+ return false;
280
+ }
281
+ }
282
+ function isTriviallyTrue(condition) {
283
+ if (condition instanceof LiteralNode) {
284
+ const v = condition.expression.value;
285
+ return v === true || v === 1 || v === 1n;
286
+ }
287
+ return false;
288
+ }
289
+ /**
290
+ * Build right output column indices and preserve the projection's attribute IDs
291
+ * (so the parent of the JoinNode keeps seeing the same attribute IDs after the
292
+ * rewrite).
293
+ *
294
+ * Returns null when any projection isn't a trivial column reference into the
295
+ * Filter source.
296
+ */
297
+ function resolveProjectedRightAttrs(project, joinAttrs, leftAttrCount, filterSourceAttrs) {
298
+ const filterIdToIndex = new Map(filterSourceAttrs.map((a, i) => [a.id, i]));
299
+ const rightOutputAttrs = joinAttrs.slice(leftAttrCount);
300
+ if (!project) {
301
+ // No projection — emit the filter source's columns directly. Attribute
302
+ // IDs match the filter source already (Project/Alias would have wrapped
303
+ // otherwise).
304
+ return {
305
+ columnIndices: filterSourceAttrs.map((_, i) => i),
306
+ attrs: rightOutputAttrs.slice(),
307
+ };
308
+ }
309
+ // With a projection: each projection node must be a trivial column ref into
310
+ // the filter source. Map its source attribute id → column index.
311
+ const projections = project.projections;
312
+ if (projections.length !== rightOutputAttrs.length)
313
+ return null;
314
+ const columnIndices = [];
315
+ for (let i = 0; i < projections.length; i++) {
316
+ const projNode = projections[i].node;
317
+ if (!(projNode instanceof ColumnReferenceNode))
318
+ return null;
319
+ const idx = filterIdToIndex.get(projNode.attributeId);
320
+ if (idx === undefined)
321
+ return null;
322
+ columnIndices.push(idx);
323
+ }
324
+ return {
325
+ columnIndices,
326
+ attrs: rightOutputAttrs.slice(),
327
+ };
328
+ }
329
+ export function ruleLateralTop1Asof(node, context) {
330
+ if (!(node instanceof JoinNode))
331
+ return null;
332
+ const joinType = node.joinType;
333
+ // LATERAL is meaningful for inner / left / cross with `ON TRUE` (or no condition).
334
+ if (joinType !== 'inner' && joinType !== 'left' && joinType !== 'cross')
335
+ return null;
336
+ // JoinNode condition must be absent or trivially true (LATERAL ON TRUE).
337
+ if (node.condition && !isTriviallyTrue(node.condition))
338
+ return null;
339
+ // Right subtree must be correlated against the outer (left) attributes.
340
+ if (!isCorrelatedSubquery(node.right))
341
+ return null;
342
+ const peeled = peelLateral(node.right);
343
+ if (!peeled)
344
+ return null;
345
+ if (!isLimitOne(peeled.limit))
346
+ return null;
347
+ const sortInfo = extractSortAttrId(peeled.sort);
348
+ if (sortInfo === null)
349
+ return null;
350
+ // The Filter's source defines the right attribute set we'll classify against.
351
+ const filterSourceAttrs = peeled.filter.source.getAttributes();
352
+ const rightAttrIds = new Set(filterSourceAttrs.map(a => a.id));
353
+ const conjuncts = flattenAnd(peeled.filter.predicate);
354
+ const classified = classifyPredicates(conjuncts, rightAttrIds);
355
+ if (!classified)
356
+ return null;
357
+ // Sort key must match the asof match attribute on the right.
358
+ if (classified.asof.rightAttrId !== sortInfo.attrId)
359
+ return null;
360
+ // Sort direction must match the asof direction (desc → latest-le, asc → earliest-ge).
361
+ if (sortInfo.direction !== classified.asof.direction)
362
+ return null;
363
+ // Locate the underlying table reference at the bottom of the lateral.
364
+ const tableRef = findTableReference(peeled.filter.source);
365
+ if (!tableRef) {
366
+ log('Right subtree does not bottom out in a TableReference');
367
+ return null;
368
+ }
369
+ // Translate the asof match attribute id to a table column index.
370
+ const tableAttrs = tableRef.getAttributes();
371
+ const matchColumnIdx = tableAttrs.findIndex(a => a.id === classified.asof.rightAttrId);
372
+ if (matchColumnIdx === -1) {
373
+ log('Asof match attr %d is not a column of the underlying table', classified.asof.rightAttrId);
374
+ return null;
375
+ }
376
+ // Probe the vtab module for asofRight + monotonicOn(K).
377
+ if (!tableAdvertisesAsof(context, tableRef, matchColumnIdx)) {
378
+ log('Vtab module does not advertise monotonicOn(col=%d) + asofRight', matchColumnIdx);
379
+ return null;
380
+ }
381
+ // Left must be monotonic on the match attribute (its cursor cannot regress
382
+ // per partition). Without this, the streaming scan would produce wrong rows
383
+ // for left rows whose match value decreases. We require global
384
+ // monotonicOn(left.matchAttr) — stronger than "monotonic within partition"
385
+ // but the only check we can make uniformly. The user can wrap the left in
386
+ // `ORDER BY matchAttr` to satisfy this.
387
+ const leftMonotonic = node.left.physical?.monotonicOn?.find(m => m.attrId === classified.asof.leftAttrId);
388
+ if (!leftMonotonic) {
389
+ log('Left input is not monotonicOn(left.matchAttr=%d); skipping asof rewrite', classified.asof.leftAttrId);
390
+ return null;
391
+ }
392
+ // Resolve the right output projection (preserving the original Project's
393
+ // attribute IDs so the parent of the join keeps the same IDs after rewrite).
394
+ const joinAttrs = node.getAttributes();
395
+ const leftAttrCount = node.left.getAttributes().length;
396
+ const projection = resolveProjectedRightAttrs(peeled.project, joinAttrs, leftAttrCount, filterSourceAttrs);
397
+ if (!projection) {
398
+ log('Lateral projection is non-trivial; skipping');
399
+ return null;
400
+ }
401
+ const asof = new AsofScanNode(node.scope, node.left, peeled.filter.source, { leftAttrId: classified.asof.leftAttrId, rightAttrId: classified.asof.rightAttrId }, classified.partition, classified.asof.strict, classified.asof.direction, joinType === 'left', projection.columnIndices, projection.attrs);
402
+ log('Recognized lateral-top-1 asof: outer=%s, direction=%s, strict=%s, partitions=%d', joinType === 'left', classified.asof.direction, classified.asof.strict, classified.partition.length);
403
+ return asof;
404
+ }
405
+ //# sourceMappingURL=rule-lateral-top1-asof.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule-lateral-top1-asof.js","sourceRoot":"","sources":["../../../../../src/planner/rules/join/rule-lateral-top1-asof.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Rule: Monotonic Merge Join
3
+ *
4
+ * Required Characteristics:
5
+ * - Node must be a logical JoinNode (not already physical)
6
+ * - Both inputs advertise `MonotonicOn` on (at least) one of the equi-pair
7
+ * attributes, with matching direction (ASC for v1, since the merge-join
8
+ * emitter assumes ASC ordering).
9
+ *
10
+ * Why this rule exists alongside `rule-join-physical-selection`:
11
+ * The existing rule chooses merge-join when both sources' `physical.ordering`
12
+ * matches the equi-pair attribute order positionally. That misses the canonical
13
+ * case where a merge-join's *output* declares `monotonicOn = [l.X, r.X]` but
14
+ * `ordering` reflects only the left side — a parent join on `r.X` then sees
15
+ * the right ordering implicitly through `monotonicOn` but not through
16
+ * `ordering[0]`. This rule looks the equi-pair attrs up directly in
17
+ * `monotonicOn`, recognising those (and other future MonotonicOn-propagating
18
+ * paths) without sorting.
19
+ *
20
+ * Out of scope (TODO):
21
+ * - Composite monotonic-on prefixes (multi-key streaming merge keyed on
22
+ * `(X, Y)` when both sides are jointly monotonic on the prefix).
23
+ * - Right and full outer joins — emitter doesn't support them today.
24
+ * - Recognising `monotonicOn(asc)` vs `monotonicOn(desc)` by reversing one
25
+ * side via Sort. That defeats this rule's premise (the rule's whole point is
26
+ * that no sort is needed because both sides are already monotonic).
27
+ */
28
+ import type { PlanNode } from '../../nodes/plan-node.js';
29
+ import type { OptContext } from '../../framework/context.js';
30
+ export declare function ruleMonotonicMergeJoin(node: PlanNode, _context: OptContext): PlanNode | null;
31
+ //# sourceMappingURL=rule-monotonic-merge-join.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule-monotonic-merge-join.d.ts","sourceRoot":"","sources":["../../../../../src/planner/rules/join/rule-monotonic-merge-join.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAA6B,MAAM,0BAA0B,CAAC;AACpF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAU7D,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,GAAG,QAAQ,GAAG,IAAI,CAwF5F"}
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Rule: Monotonic Merge Join
3
+ *
4
+ * Required Characteristics:
5
+ * - Node must be a logical JoinNode (not already physical)
6
+ * - Both inputs advertise `MonotonicOn` on (at least) one of the equi-pair
7
+ * attributes, with matching direction (ASC for v1, since the merge-join
8
+ * emitter assumes ASC ordering).
9
+ *
10
+ * Why this rule exists alongside `rule-join-physical-selection`:
11
+ * The existing rule chooses merge-join when both sources' `physical.ordering`
12
+ * matches the equi-pair attribute order positionally. That misses the canonical
13
+ * case where a merge-join's *output* declares `monotonicOn = [l.X, r.X]` but
14
+ * `ordering` reflects only the left side — a parent join on `r.X` then sees
15
+ * the right ordering implicitly through `monotonicOn` but not through
16
+ * `ordering[0]`. This rule looks the equi-pair attrs up directly in
17
+ * `monotonicOn`, recognising those (and other future MonotonicOn-propagating
18
+ * paths) without sorting.
19
+ *
20
+ * Out of scope (TODO):
21
+ * - Composite monotonic-on prefixes (multi-key streaming merge keyed on
22
+ * `(X, Y)` when both sides are jointly monotonic on the prefix).
23
+ * - Right and full outer joins — emitter doesn't support them today.
24
+ * - Recognising `monotonicOn(asc)` vs `monotonicOn(desc)` by reversing one
25
+ * side via Sort. That defeats this rule's premise (the rule's whole point is
26
+ * that no sort is needed because both sides are already monotonic).
27
+ */
28
+ import { createLogger } from '../../../common/logger.js';
29
+ import { JoinNode } from '../../nodes/join-node.js';
30
+ import { MergeJoinNode } from '../../nodes/merge-join-node.js';
31
+ import { nestedLoopJoinCost, hashJoinCost, mergeJoinCost } from '../../cost/index.js';
32
+ import { PlanNodeCharacteristics } from '../../framework/characteristics.js';
33
+ import { extractEquiPairs, extractEquiPairsFromUsing, combineResidual, isMergeReadyOnAllPairs } from './equi-pair-extractor.js';
34
+ const log = createLogger('optimizer:rule:monotonic-merge-join');
35
+ export function ruleMonotonicMergeJoin(node, _context) {
36
+ if (!(node instanceof JoinNode))
37
+ return null;
38
+ const joinType = node.joinType;
39
+ if (joinType !== 'inner' && joinType !== 'left' && joinType !== 'semi' && joinType !== 'anti')
40
+ return null;
41
+ const leftAttrs = node.left.getAttributes();
42
+ const rightAttrs = node.right.getAttributes();
43
+ const leftAttrIds = new Set(leftAttrs.map(a => a.id));
44
+ const rightAttrIds = new Set(rightAttrs.map(a => a.id));
45
+ const extracted = node.condition
46
+ ? extractEquiPairs(node.condition, leftAttrIds, rightAttrIds)
47
+ : extractEquiPairsFromUsing(node.usingColumns, leftAttrs, rightAttrs);
48
+ if (!extracted || extracted.equiPairs.length === 0)
49
+ return null;
50
+ // Defer to `rule-join-physical-selection` whenever both sides' physical
51
+ // ordering already covers ALL equi-pairs in merge-ready order. The
52
+ // ordering-based path produces a multi-key merge join with full
53
+ // unique-key propagation; demoting pairs to residual here would lose that
54
+ // propagation. Our rule is meant to *extend* recognition, not regress it.
55
+ if (isMergeReadyOnAllPairs(node.left, node.right, extracted.equiPairs))
56
+ return null;
57
+ const leftMon = PlanNodeCharacteristics.getMonotonicOn(node.left);
58
+ const rightMon = PlanNodeCharacteristics.getMonotonicOn(node.right);
59
+ if (leftMon.length === 0 || rightMon.length === 0)
60
+ return null;
61
+ // Find equi-pairs where BOTH sides are MonotonicOn on their respective
62
+ // attrId with matching direction. v1 requires ASC because the merge-join
63
+ // emitter assumes ASC; DESC-DESC streaming would need a reversed compareKeys.
64
+ const matchedIndices = [];
65
+ for (let i = 0; i < extracted.equiPairs.length; i++) {
66
+ const pair = extracted.equiPairs[i];
67
+ const l = leftMon.find(m => m.attrId === pair.leftAttrId && m.direction === 'asc');
68
+ if (!l)
69
+ continue;
70
+ const r = rightMon.find(m => m.attrId === pair.rightAttrId && m.direction === 'asc');
71
+ if (!r)
72
+ continue;
73
+ matchedIndices.push(i);
74
+ }
75
+ if (matchedIndices.length === 0)
76
+ return null;
77
+ // v1: pick a single driving equi-pair. Other equi-pairs (matched or not)
78
+ // must be evaluated as residual conjuncts — the merge-join emitter assumes
79
+ // the right side is sorted lexicographically across ALL listed equi-pair
80
+ // columns, which we cannot guarantee for non-driving keys.
81
+ // TODO: composite monotonic-on prefixes — recognise when both sides are
82
+ // jointly MonotonicOn on a multi-key prefix and use multiple driving keys.
83
+ const drivingIndex = matchedIndices[0];
84
+ const driving = [extracted.equiPairs[drivingIndex]];
85
+ // Residualize the rest. For pairs that originated from a real `=` BinaryOpNode
86
+ // (ON-condition path), reuse the original node. For USING-derived pairs,
87
+ // `equiPairNodes[i]` is undefined — bail out and let the existing
88
+ // ordering-based rule handle USING-with-multiple-pairs.
89
+ const extras = [];
90
+ for (let i = 0; i < extracted.equiPairs.length; i++) {
91
+ if (i === drivingIndex)
92
+ continue;
93
+ const orig = extracted.equiPairNodes[i];
94
+ if (!orig)
95
+ return null;
96
+ extras.push(orig);
97
+ }
98
+ const residual = combineResidual(extracted.residual, extras);
99
+ // Cost gate: even with the precondition met, hash or nested-loop may win
100
+ // on tiny inputs. Don't regress those.
101
+ const leftRows = node.left.estimatedRows ?? 100;
102
+ const rightRows = node.right.estimatedRows ?? 100;
103
+ const mergeC = mergeJoinCost(leftRows, rightRows, false, false);
104
+ const hashC = hashJoinCost(Math.min(leftRows, rightRows), Math.max(leftRows, rightRows));
105
+ const nlC = nestedLoopJoinCost(leftRows, rightRows);
106
+ if (Math.min(hashC, nlC) < mergeC) {
107
+ log('Skipping monotonic-merge: cheaper alternative exists (merge=%.2f, hash=%.2f, nl=%.2f)', mergeC, hashC, nlC);
108
+ return null;
109
+ }
110
+ log('Selecting monotonic merge-join on equi-pair %d=%d (merge=%.2f, hash=%.2f, nl=%.2f)', driving[0].leftAttrId, driving[0].rightAttrId, mergeC, hashC, nlC);
111
+ return new MergeJoinNode(node.scope, node.left, node.right, joinType, driving, residual, node.getAttributes().slice());
112
+ }
113
+ //# sourceMappingURL=rule-monotonic-merge-join.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule-monotonic-merge-join.js","sourceRoot":"","sources":["../../../../../src/planner/rules/join/rule-monotonic-merge-join.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Rule: Monotonic streaming window
3
+ *
4
+ * Recognizes a `WindowNode` whose source already streams in
5
+ * `[PARTITION BY..., ORDER BY[0]]` order — `physical.monotonicOn` covers the
6
+ * leading ORDER BY key and `physical.ordering` shows the partition keys as an
7
+ * emit-order prefix — and tags it with a `streaming` config so the runtime
8
+ * can switch from the buffer-and-sort path to a one-pass streaming emitter.
9
+ *
10
+ * Per-function recognition (all functions in the WindowNode must qualify; if
11
+ * any one falls through we keep the buffered path):
12
+ *
13
+ * - `ROW_NUMBER` / `RANK` / `DENSE_RANK`
14
+ * - `LAG(expr [, n [, default]])` / `LEAD(expr [, n [, default]])` with `n`
15
+ * being a non-negative integer literal
16
+ * - `FIRST_VALUE(expr)` / `LAST_VALUE(expr)` (last_value uses default frame ==
17
+ * current row, so it's the trivial expr-on-current-row evaluation)
18
+ * - `SUM` / `COUNT` / `AVG` / `MIN` / `MAX` over the default frame
19
+ * (`UNBOUNDED PRECEDING TO CURRENT ROW`, ROWS or RANGE — RANGE handles peer
20
+ * groups via delayed emit at peer boundaries)
21
+ * - `SUM` / `COUNT` / `AVG` / `MIN` / `MAX` / `FIRST_VALUE` / `LAST_VALUE`
22
+ * over a sliding frame `ROWS BETWEEN n PRECEDING AND m FOLLOWING` (literal
23
+ * non-negative integers `n`, `m`) or `RANGE BETWEEN <num> PRECEDING AND
24
+ * <num> FOLLOWING` (single numeric ORDER BY, literal non-negative offsets)
25
+ *
26
+ * Bail conditions:
27
+ *
28
+ * - leading ORDER BY key is not a trivial column reference
29
+ * - source's `monotonicOn` doesn't cover the leading key (or direction differs)
30
+ * - source's `ordering` prefix doesn't include the full ORDER BY key set
31
+ * - PARTITION BY columns aren't an emit-order prefix of the source ordering
32
+ * - any partition-by expression is non-trivial (not a column reference)
33
+ * - any function falls outside the recognized set, or is `DISTINCT`
34
+ * - frame is anything other than the default (or the explicit equivalent
35
+ * `UNBOUNDED PRECEDING TO CURRENT ROW` in `ROWS` or `RANGE`), or a
36
+ * supported sliding shape (see above)
37
+ *
38
+ * Out of scope (deferred): NTILE/PERCENT_RANK/CUME_DIST (need partition size up
39
+ * front), DISTINCT aggregates, asymmetric sliding shapes
40
+ * (`UNBOUNDED PRECEDING AND m FOLLOWING`, `n PRECEDING AND UNBOUNDED FOLLOWING`,
41
+ * `CURRENT ROW AND m FOLLOWING`), splitting a mixed WindowNode into streaming +
42
+ * buffered halves.
43
+ */
44
+ import type { PlanNode } from '../../nodes/plan-node.js';
45
+ import type { OptContext } from '../../framework/context.js';
46
+ export declare function ruleMonotonicWindow(node: PlanNode, _context: OptContext): PlanNode | null;
47
+ //# sourceMappingURL=rule-monotonic-window.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule-monotonic-window.d.ts","sourceRoot":"","sources":["../../../../../src/planner/rules/window/rule-monotonic-window.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AA8J7D,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,GAAG,QAAQ,GAAG,IAAI,CAiJzF"}