@quereus/quereus 4.16.0 → 4.17.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 (163) hide show
  1. package/dist/src/core/database-auto-analyze.d.ts +258 -0
  2. package/dist/src/core/database-auto-analyze.d.ts.map +1 -0
  3. package/dist/src/core/database-auto-analyze.js +616 -0
  4. package/dist/src/core/database-auto-analyze.js.map +1 -0
  5. package/dist/src/core/database-materialized-views.d.ts +42 -1
  6. package/dist/src/core/database-materialized-views.d.ts.map +1 -1
  7. package/dist/src/core/database-materialized-views.js +76 -3
  8. package/dist/src/core/database-materialized-views.js.map +1 -1
  9. package/dist/src/core/database-transaction.d.ts +17 -0
  10. package/dist/src/core/database-transaction.d.ts.map +1 -1
  11. package/dist/src/core/database-transaction.js +37 -0
  12. package/dist/src/core/database-transaction.js.map +1 -1
  13. package/dist/src/core/database.d.ts +8 -1
  14. package/dist/src/core/database.d.ts.map +1 -1
  15. package/dist/src/core/database.js +135 -5
  16. package/dist/src/core/database.js.map +1 -1
  17. package/dist/src/planner/analysis/change-scope.d.ts.map +1 -1
  18. package/dist/src/planner/analysis/change-scope.js +8 -8
  19. package/dist/src/planner/analysis/change-scope.js.map +1 -1
  20. package/dist/src/planner/analysis/constraint-extractor.d.ts.map +1 -1
  21. package/dist/src/planner/analysis/constraint-extractor.js +27 -14
  22. package/dist/src/planner/analysis/constraint-extractor.js.map +1 -1
  23. package/dist/src/planner/analysis/expression-fingerprint.d.ts.map +1 -1
  24. package/dist/src/planner/analysis/expression-fingerprint.js +23 -2
  25. package/dist/src/planner/analysis/expression-fingerprint.js.map +1 -1
  26. package/dist/src/planner/analysis/mv-body-functions.d.ts +46 -0
  27. package/dist/src/planner/analysis/mv-body-functions.d.ts.map +1 -0
  28. package/dist/src/planner/analysis/mv-body-functions.js +65 -0
  29. package/dist/src/planner/analysis/mv-body-functions.js.map +1 -0
  30. package/dist/src/planner/analysis/predicate-shape.d.ts +32 -4
  31. package/dist/src/planner/analysis/predicate-shape.d.ts.map +1 -1
  32. package/dist/src/planner/analysis/predicate-shape.js +37 -5
  33. package/dist/src/planner/analysis/predicate-shape.js.map +1 -1
  34. package/dist/src/planner/analysis/query-rewrite-matcher.d.ts +2 -2
  35. package/dist/src/planner/analysis/query-rewrite-matcher.d.ts.map +1 -1
  36. package/dist/src/planner/analysis/query-rewrite-matcher.js +46 -6
  37. package/dist/src/planner/analysis/query-rewrite-matcher.js.map +1 -1
  38. package/dist/src/planner/analysis/sat-checker.js +2 -5
  39. package/dist/src/planner/analysis/sat-checker.js.map +1 -1
  40. package/dist/src/planner/building/select-aggregates.d.ts +17 -5
  41. package/dist/src/planner/building/select-aggregates.d.ts.map +1 -1
  42. package/dist/src/planner/building/select-aggregates.js +122 -79
  43. package/dist/src/planner/building/select-aggregates.js.map +1 -1
  44. package/dist/src/planner/building/select-modifiers.d.ts +1 -1
  45. package/dist/src/planner/building/select.d.ts.map +1 -1
  46. package/dist/src/planner/building/select.js +73 -55
  47. package/dist/src/planner/building/select.js.map +1 -1
  48. package/dist/src/planner/cost/index.d.ts +14 -0
  49. package/dist/src/planner/cost/index.d.ts.map +1 -1
  50. package/dist/src/planner/cost/index.js +19 -0
  51. package/dist/src/planner/cost/index.js.map +1 -1
  52. package/dist/src/planner/nodes/limit-offset.d.ts.map +1 -1
  53. package/dist/src/planner/nodes/limit-offset.js +3 -2
  54. package/dist/src/planner/nodes/limit-offset.js.map +1 -1
  55. package/dist/src/planner/optimizer-tuning.d.ts +10 -6
  56. package/dist/src/planner/optimizer-tuning.d.ts.map +1 -1
  57. package/dist/src/planner/optimizer-tuning.js.map +1 -1
  58. package/dist/src/planner/optimizer.d.ts.map +1 -1
  59. package/dist/src/planner/optimizer.js +24 -0
  60. package/dist/src/planner/optimizer.js.map +1 -1
  61. package/dist/src/planner/rules/access/rule-monotonic-range-access.d.ts.map +1 -1
  62. package/dist/src/planner/rules/access/rule-monotonic-range-access.js +8 -4
  63. package/dist/src/planner/rules/access/rule-monotonic-range-access.js.map +1 -1
  64. package/dist/src/planner/rules/access/rule-select-access-path.d.ts +14 -0
  65. package/dist/src/planner/rules/access/rule-select-access-path.d.ts.map +1 -1
  66. package/dist/src/planner/rules/access/rule-select-access-path.js +5 -1
  67. package/dist/src/planner/rules/access/rule-select-access-path.js.map +1 -1
  68. package/dist/src/planner/rules/aggregate/rule-groupby-fd-simplification.d.ts +3 -1
  69. package/dist/src/planner/rules/aggregate/rule-groupby-fd-simplification.d.ts.map +1 -1
  70. package/dist/src/planner/rules/aggregate/rule-groupby-fd-simplification.js +8 -3
  71. package/dist/src/planner/rules/aggregate/rule-groupby-fd-simplification.js.map +1 -1
  72. package/dist/src/planner/rules/aggregate/rule-minmax-index-boundary.d.ts +47 -0
  73. package/dist/src/planner/rules/aggregate/rule-minmax-index-boundary.d.ts.map +1 -0
  74. package/dist/src/planner/rules/aggregate/rule-minmax-index-boundary.js +200 -0
  75. package/dist/src/planner/rules/aggregate/rule-minmax-index-boundary.js.map +1 -0
  76. package/dist/src/planner/rules/cache/rule-nested-loop-right-cache.d.ts +29 -0
  77. package/dist/src/planner/rules/cache/rule-nested-loop-right-cache.d.ts.map +1 -1
  78. package/dist/src/planner/rules/cache/rule-nested-loop-right-cache.js +55 -16
  79. package/dist/src/planner/rules/cache/rule-nested-loop-right-cache.js.map +1 -1
  80. package/dist/src/planner/rules/join/index-nested-loop.d.ts +96 -22
  81. package/dist/src/planner/rules/join/index-nested-loop.d.ts.map +1 -1
  82. package/dist/src/planner/rules/join/index-nested-loop.js +401 -82
  83. package/dist/src/planner/rules/join/index-nested-loop.js.map +1 -1
  84. package/dist/src/planner/rules/join/rule-fanout-batched-outer.d.ts +20 -0
  85. package/dist/src/planner/rules/join/rule-fanout-batched-outer.d.ts.map +1 -1
  86. package/dist/src/planner/rules/join/rule-fanout-batched-outer.js +23 -4
  87. package/dist/src/planner/rules/join/rule-fanout-batched-outer.js.map +1 -1
  88. package/dist/src/planner/rules/join/rule-join-greedy-commute.d.ts.map +1 -1
  89. package/dist/src/planner/rules/join/rule-join-greedy-commute.js +7 -0
  90. package/dist/src/planner/rules/join/rule-join-greedy-commute.js.map +1 -1
  91. package/dist/src/planner/rules/join/rule-join-physical-selection.d.ts +19 -2
  92. package/dist/src/planner/rules/join/rule-join-physical-selection.d.ts.map +1 -1
  93. package/dist/src/planner/rules/join/rule-join-physical-selection.js +262 -40
  94. package/dist/src/planner/rules/join/rule-join-physical-selection.js.map +1 -1
  95. package/dist/src/planner/rules/predicate/rule-sargable-range-rewrite.d.ts.map +1 -1
  96. package/dist/src/planner/rules/predicate/rule-sargable-range-rewrite.js +2 -1
  97. package/dist/src/planner/rules/predicate/rule-sargable-range-rewrite.js.map +1 -1
  98. package/dist/src/planner/rules/retrieve/rule-grow-retrieve.d.ts +21 -0
  99. package/dist/src/planner/rules/retrieve/rule-grow-retrieve.d.ts.map +1 -1
  100. package/dist/src/planner/rules/retrieve/rule-grow-retrieve.js +24 -6
  101. package/dist/src/planner/rules/retrieve/rule-grow-retrieve.js.map +1 -1
  102. package/dist/src/planner/rules/shared/access-leaf.d.ts +12 -12
  103. package/dist/src/planner/rules/shared/access-leaf.d.ts.map +1 -1
  104. package/dist/src/planner/rules/shared/access-leaf.js +3 -14
  105. package/dist/src/planner/rules/shared/access-leaf.js.map +1 -1
  106. package/dist/src/planner/stats/catalog-stats.d.ts.map +1 -1
  107. package/dist/src/planner/stats/catalog-stats.js +6 -10
  108. package/dist/src/planner/stats/catalog-stats.js.map +1 -1
  109. package/dist/src/planner/type-utils.d.ts.map +1 -1
  110. package/dist/src/planner/type-utils.js +3 -5
  111. package/dist/src/planner/type-utils.js.map +1 -1
  112. package/dist/src/planner/util/fd-utils.d.ts.map +1 -1
  113. package/dist/src/planner/util/fd-utils.js +4 -7
  114. package/dist/src/planner/util/fd-utils.js.map +1 -1
  115. package/dist/src/runtime/emit/alter-table.d.ts.map +1 -1
  116. package/dist/src/runtime/emit/alter-table.js +58 -4
  117. package/dist/src/runtime/emit/alter-table.js.map +1 -1
  118. package/dist/src/runtime/emit/binary.d.ts.map +1 -1
  119. package/dist/src/runtime/emit/binary.js +5 -3
  120. package/dist/src/runtime/emit/binary.js.map +1 -1
  121. package/dist/src/runtime/emit/fanout-lookup-join.d.ts.map +1 -1
  122. package/dist/src/runtime/emit/fanout-lookup-join.js +9 -1
  123. package/dist/src/runtime/emit/fanout-lookup-join.js.map +1 -1
  124. package/dist/src/runtime/emit/limit-offset.d.ts.map +1 -1
  125. package/dist/src/runtime/emit/limit-offset.js +50 -24
  126. package/dist/src/runtime/emit/limit-offset.js.map +1 -1
  127. package/dist/src/runtime/emit/materialized-view-helpers.d.ts +33 -2
  128. package/dist/src/runtime/emit/materialized-view-helpers.d.ts.map +1 -1
  129. package/dist/src/runtime/emit/materialized-view-helpers.js +61 -5
  130. package/dist/src/runtime/emit/materialized-view-helpers.js.map +1 -1
  131. package/dist/src/runtime/emit/materialized-view.d.ts.map +1 -1
  132. package/dist/src/runtime/emit/materialized-view.js +6 -1
  133. package/dist/src/runtime/emit/materialized-view.js.map +1 -1
  134. package/dist/src/runtime/emit/ordinal-slice.d.ts.map +1 -1
  135. package/dist/src/runtime/emit/ordinal-slice.js +8 -0
  136. package/dist/src/runtime/emit/ordinal-slice.js.map +1 -1
  137. package/dist/src/schema/derivation.d.ts +15 -1
  138. package/dist/src/schema/derivation.d.ts.map +1 -1
  139. package/dist/src/schema/derivation.js.map +1 -1
  140. package/dist/src/schema/function.d.ts +9 -4
  141. package/dist/src/schema/function.d.ts.map +1 -1
  142. package/dist/src/schema/function.js +11 -9
  143. package/dist/src/schema/function.js.map +1 -1
  144. package/dist/src/schema/schema.d.ts +14 -3
  145. package/dist/src/schema/schema.d.ts.map +1 -1
  146. package/dist/src/schema/schema.js +20 -2
  147. package/dist/src/schema/schema.js.map +1 -1
  148. package/dist/src/schema/table.d.ts +27 -0
  149. package/dist/src/schema/table.d.ts.map +1 -1
  150. package/dist/src/schema/table.js +55 -0
  151. package/dist/src/schema/table.js.map +1 -1
  152. package/dist/src/schema/unique-enforcement.d.ts +6 -13
  153. package/dist/src/schema/unique-enforcement.d.ts.map +1 -1
  154. package/dist/src/schema/unique-enforcement.js +8 -17
  155. package/dist/src/schema/unique-enforcement.js.map +1 -1
  156. package/dist/src/util/comparison.d.ts +19 -0
  157. package/dist/src/util/comparison.d.ts.map +1 -1
  158. package/dist/src/util/comparison.js +22 -0
  159. package/dist/src/util/comparison.js.map +1 -1
  160. package/dist/src/vtab/memory/module.d.ts.map +1 -1
  161. package/dist/src/vtab/memory/module.js +10 -0
  162. package/dist/src/vtab/memory/module.js.map +1 -1
  163. package/package.json +3 -3
@@ -5,7 +5,9 @@
5
5
  * GROUP BY columns under the aggregate-output FDs and equivalence classes.
6
6
  * Each dropped column is re-emitted as a `MIN(<original-column>)` picker
7
7
  * aggregate so the output attribute IDs (and therefore downstream binding)
8
- * are preserved.
8
+ * are preserved. The picker relies on the BUILT-IN `min`'s semantics (any
9
+ * value from the group, since the group is a single source row); the rule
10
+ * declines when `min/1` has been shadowed by a user-registered function.
9
11
  *
10
12
  * The aggregate's own `physical.fds` and `physical.equivClasses` are already
11
13
  * projected onto its output column indices by `propagateAggregateFds`, so
@@ -1 +1 @@
1
- {"version":3,"file":"rule-groupby-fd-simplification.d.ts","sourceRoot":"","sources":["../../../../../src/planner/rules/aggregate/rule-groupby-fd-simplification.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAmD,MAAM,0BAA0B,CAAC;AAC1G,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAY7D,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,GAAG,QAAQ,GAAG,IAAI,CAuJhG"}
1
+ {"version":3,"file":"rule-groupby-fd-simplification.d.ts","sourceRoot":"","sources":["../../../../../src/planner/rules/aggregate/rule-groupby-fd-simplification.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAmD,MAAM,0BAA0B,CAAC;AAC1G,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAY7D,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,GAAG,QAAQ,GAAG,IAAI,CA0JhG"}
@@ -5,7 +5,9 @@
5
5
  * GROUP BY columns under the aggregate-output FDs and equivalence classes.
6
6
  * Each dropped column is re-emitted as a `MIN(<original-column>)` picker
7
7
  * aggregate so the output attribute IDs (and therefore downstream binding)
8
- * are preserved.
8
+ * are preserved. The picker relies on the BUILT-IN `min`'s semantics (any
9
+ * value from the group, since the group is a single source row); the rule
10
+ * declines when `min/1` has been shadowed by a user-registered function.
9
11
  *
10
12
  * The aggregate's own `physical.fds` and `physical.equivClasses` are already
11
13
  * projected onto its output column indices by `propagateAggregateFds`, so
@@ -133,9 +135,12 @@ export function ruleGroupByFdSimplification(node, context) {
133
135
  if (keptGroupBy.length === 0)
134
136
  return null;
135
137
  // Synthesize picker MIN aggregates for each dropped column, in original order.
136
- const minSchema = context.db._findFunction('min', 1);
138
+ // Gate on schema identity, not name: `addFunction` overwrites by name/numArgs, so
139
+ // a user `min/1` and `_findFunction('min', 1)` would otherwise agree with each
140
+ // other while disagreeing with the built-in semantics this rewrite relies on.
141
+ const minSchema = context.db._findBuiltinFunction('min', 1);
137
142
  if (!minSchema || !isAggregateFunctionSchema(minSchema)) {
138
- log('min/1 not registered as aggregate; skipping');
143
+ log('min/1 is not the built-in aggregate; skipping');
139
144
  return null;
140
145
  }
141
146
  const pickerAggregates = [];
@@ -1 +1 @@
1
- {"version":3,"file":"rule-groupby-fd-simplification.js","sourceRoot":"","sources":["../../../../../src/planner/rules/aggregate/rule-groupby-fd-simplification.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"rule-groupby-fd-simplification.js","sourceRoot":"","sources":["../../../../../src/planner/rules/aggregate/rule-groupby-fd-simplification.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Rule: answer an ungrouped MIN / MAX from the index boundary.
3
+ *
4
+ * `min(c)` over any relation `S` equals `min(c)` over
5
+ * `(S where c is not null order by c asc limit 1)`; `max(c)` is the same with
6
+ * `desc`. The identity holds for every `S`, so this is a pure plan-shape change —
7
+ * never a different answer, only a cheaper one.
8
+ *
9
+ * The rewrite builds exactly that shape and hands it to the planner's existing
10
+ * sort-absorption probe (`trySortAbsorbViaIndexOrdering`). The `AggregateNode`
11
+ * itself is kept, unchanged, on top:
12
+ *
13
+ * Aggregate [max(c)] <- unchanged
14
+ * └─ LimitOffset(limit=1) <- new
15
+ * └─ Filter(c is not null) <- new, only when `c` is nullable
16
+ * └─ Retrieve(t) <- equipped with an ordering access plan
17
+ *
18
+ * Keeping the aggregate is what makes the two hard cases free:
19
+ *
20
+ * - Empty relation. An ungrouped aggregate over zero rows still emits exactly one
21
+ * row, and min/max finalize an empty accumulator to NULL — so
22
+ * `select max(c) from empty_table` keeps returning one NULL row with no special
23
+ * casing here.
24
+ * - The comparator. This rule never compares values itself. It delegates "does
25
+ * index order agree with the argument's semantic order" to the same access-plan
26
+ * ordering claim plain `ORDER BY` relies on, and min/max already rank under the
27
+ * same semantic comparator ORDER BY uses, so the two agree by construction.
28
+ *
29
+ * The probe runs BEFORE anything is committed: if it returns null the rule returns
30
+ * null and the plan is left byte-identical. No Sort, Filter or Limit is ever
31
+ * introduced into a plan that cannot serve the ordering.
32
+ *
33
+ * Neither shipped backend walks an index backwards (both require the requested
34
+ * direction to equal the index column's declared direction), so in practice:
35
+ *
36
+ * min(c) + ascending index on c (incl. the primary key) -> boundary read
37
+ * max(c) + descending index on c -> boundary read
38
+ * max(c) + only an ascending index on c -> unchanged, full scan
39
+ * either + no index -> unchanged, full scan
40
+ *
41
+ * Making `max(c)` fast off an ascending index needs backwards index walks, tracked
42
+ * separately as `feat-reverse-index-walk-for-desc-ordering`.
43
+ */
44
+ import type { PlanNode } from '../../nodes/plan-node.js';
45
+ import type { OptContext } from '../../framework/context.js';
46
+ export declare function ruleMinMaxIndexBoundary(node: PlanNode, context: OptContext): PlanNode | null;
47
+ //# sourceMappingURL=rule-minmax-index-boundary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule-minmax-index-boundary.d.ts","sourceRoot":"","sources":["../../../../../src/planner/rules/aggregate/rule-minmax-index-boundary.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAsC,MAAM,0BAA0B,CAAC;AAC7F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAkB7D,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,GAAG,QAAQ,GAAG,IAAI,CA4D5F"}
@@ -0,0 +1,200 @@
1
+ /**
2
+ * Rule: answer an ungrouped MIN / MAX from the index boundary.
3
+ *
4
+ * `min(c)` over any relation `S` equals `min(c)` over
5
+ * `(S where c is not null order by c asc limit 1)`; `max(c)` is the same with
6
+ * `desc`. The identity holds for every `S`, so this is a pure plan-shape change —
7
+ * never a different answer, only a cheaper one.
8
+ *
9
+ * The rewrite builds exactly that shape and hands it to the planner's existing
10
+ * sort-absorption probe (`trySortAbsorbViaIndexOrdering`). The `AggregateNode`
11
+ * itself is kept, unchanged, on top:
12
+ *
13
+ * Aggregate [max(c)] <- unchanged
14
+ * └─ LimitOffset(limit=1) <- new
15
+ * └─ Filter(c is not null) <- new, only when `c` is nullable
16
+ * └─ Retrieve(t) <- equipped with an ordering access plan
17
+ *
18
+ * Keeping the aggregate is what makes the two hard cases free:
19
+ *
20
+ * - Empty relation. An ungrouped aggregate over zero rows still emits exactly one
21
+ * row, and min/max finalize an empty accumulator to NULL — so
22
+ * `select max(c) from empty_table` keeps returning one NULL row with no special
23
+ * casing here.
24
+ * - The comparator. This rule never compares values itself. It delegates "does
25
+ * index order agree with the argument's semantic order" to the same access-plan
26
+ * ordering claim plain `ORDER BY` relies on, and min/max already rank under the
27
+ * same semantic comparator ORDER BY uses, so the two agree by construction.
28
+ *
29
+ * The probe runs BEFORE anything is committed: if it returns null the rule returns
30
+ * null and the plan is left byte-identical. No Sort, Filter or Limit is ever
31
+ * introduced into a plan that cannot serve the ordering.
32
+ *
33
+ * Neither shipped backend walks an index backwards (both require the requested
34
+ * direction to equal the index column's declared direction), so in practice:
35
+ *
36
+ * min(c) + ascending index on c (incl. the primary key) -> boundary read
37
+ * max(c) + descending index on c -> boundary read
38
+ * max(c) + only an ascending index on c -> unchanged, full scan
39
+ * either + no index -> unchanged, full scan
40
+ *
41
+ * Making `max(c)` fast off an ascending index needs backwards index walks, tracked
42
+ * separately as `feat-reverse-index-walk-for-desc-ordering`.
43
+ */
44
+ import { createLogger } from '../../../common/logger.js';
45
+ import { AggregateNode } from '../../nodes/aggregate-node.js';
46
+ import { AggregateFunctionCallNode } from '../../nodes/aggregate-function.js';
47
+ import { ColumnReferenceNode } from '../../nodes/reference.js';
48
+ import { FilterNode } from '../../nodes/filter.js';
49
+ import { SortNode } from '../../nodes/sort.js';
50
+ import { LimitOffsetNode } from '../../nodes/limit-offset.js';
51
+ import { LiteralNode, UnaryOpNode } from '../../nodes/scalar.js';
52
+ import { PlanNodeCharacteristics } from '../../framework/characteristics.js';
53
+ import { trySortAbsorbViaIndexOrdering } from '../retrieve/rule-grow-retrieve.js';
54
+ const log = createLogger('optimizer:rule:minmax-index-boundary');
55
+ export function ruleMinMaxIndexBoundary(node, context) {
56
+ if (!(node instanceof AggregateNode))
57
+ return null;
58
+ const call = soleUngroupedAggregateCall(node);
59
+ if (!call)
60
+ return null;
61
+ const extremum = classifyExtremum(call, context);
62
+ if (!extremum)
63
+ return null;
64
+ const colRef = boundaryColumn(call);
65
+ if (!colRef)
66
+ return null;
67
+ if (!sourceAcceptsTruncation(node))
68
+ return null;
69
+ const scope = node.scope;
70
+ // NOTE: two different sources of truth decide "can a NULL reach this column" — this
71
+ // line reads the column reference's own type, while `nullSafeOrderingPrefixLength`
72
+ // (vtab/best-access-plan.ts) reads `tableSchema.columns[i].notNull`. They agree today
73
+ // because a bare table column's `columnType` is built from that same schema column,
74
+ // and the DESC direction fails safe either way (a missing filter makes the ordering
75
+ // claim be refused, so the rule declines). The ASC direction does NOT fail safe: an
76
+ // ordering claim over a nullable ASC column is granted unconditionally, so a type that
77
+ // under-reports nullability would skip this filter and `min(c)` would return NULL.
78
+ // Revisit if a rewrite ever narrows a ColumnReferenceNode's nullability, or if a
79
+ // non-table relation becomes reachable through the absorb probe's chain walk.
80
+ const inner = colRef.getType().nullable
81
+ ? new FilterNode(scope, node.source, buildIsNotNull(scope, colRef))
82
+ : node.source;
83
+ // Probe-then-commit: build a throwaway Sort purely to ask whether the access
84
+ // path can serve this ordering. `trySortAbsorbViaIndexOrdering` is
85
+ // side-effect-free, so a null answer costs nothing and leaves the plan alone.
86
+ const probe = new SortNode(scope, inner, [
87
+ { expression: colRef, direction: extremum.direction, nulls: undefined },
88
+ ]);
89
+ const absorbed = trySortAbsorbViaIndexOrdering(probe, context);
90
+ if (!absorbed) {
91
+ log('access path cannot serve %s ordering for %s; leaving the aggregate alone', extremum.direction, colRef.toString());
92
+ return null;
93
+ }
94
+ // NOTE: the `Literal(null)` OFFSET is load-bearing, not cosmetic. `ruleGrowRetrieve`'s
95
+ // LimitOffset arm (`buildRequest`, rule-grow-retrieve.ts) would otherwise swallow this
96
+ // node into `Retrieve.source`, where the index-style branch of `ruleSelectAccessPath`
97
+ // never executes it — the early stop would silently vanish and the scan would run to
98
+ // completion. That arm refuses every LIMIT whose OFFSET is a non-numeric literal, which
99
+ // is exactly this shape. Its own comment invites reading a null/absent OFFSET as 0
100
+ // instead of refusing; if that ever changes, this rewrite needs its own guard, and
101
+ // test/optimizer/minmax-index-boundary.spec.ts pins the LIMITOFFSET still being present
102
+ // above the access leaf.
103
+ const limited = new LimitOffsetNode(scope, absorbed, literal(scope, 1), literal(scope, null));
104
+ log('rewrote %s(%s) into an index-boundary read', extremum.name, colRef.toString());
105
+ return node.withChildren([limited, ...node.getChildren().slice(1)]);
106
+ }
107
+ /**
108
+ * The node's single aggregate call, when it is an ungrouped aggregate carrying
109
+ * exactly one of them.
110
+ *
111
+ * Two aggregates decline: `select min(c), max(c) from t` needs both ends of the
112
+ * index (out of scope for this rewrite), and `select max(c), count(*) from t` would
113
+ * be answered WRONG by it — `count(*)` genuinely needs every row, so truncating the
114
+ * source to one row would return 1.
115
+ */
116
+ function soleUngroupedAggregateCall(node) {
117
+ if (node.groupBy.length !== 0)
118
+ return null;
119
+ if (node.aggregates.length !== 1)
120
+ return null;
121
+ const expr = node.aggregates[0].expression;
122
+ return expr instanceof AggregateFunctionCallNode ? expr : null;
123
+ }
124
+ /**
125
+ * Identify the call as the BUILTIN `min`/`max` over one argument.
126
+ *
127
+ * The gate is `Database._isBuiltinFunction` — schema identity against the built-in
128
+ * registration — not a name match and not a comparison against what the name resolves
129
+ * to now. `addFunction` overwrites by `name/numArgs`, so after
130
+ * `db.createAggregateFunction('min', …)` the call and `_findFunction('min', 1)` return
131
+ * the SAME shadow and comparing them proves nothing; the rewrite would then truncate a
132
+ * user aggregate's source to one row and change its answer. `FILTER (where …)` and an
133
+ * aggregate-level `ORDER BY` both decline; `DISTINCT` is accepted in either state
134
+ * because `min(distinct c) = min(c)`.
135
+ */
136
+ function classifyExtremum(call, context) {
137
+ if (call.args.length !== 1)
138
+ return null;
139
+ if (call.filter)
140
+ return null;
141
+ if (call.orderBy && call.orderBy.length > 0)
142
+ return null;
143
+ if (!context.db._isBuiltinFunction(call.functionSchema))
144
+ return null;
145
+ for (const candidate of [{ name: 'min', direction: 'asc' }, { name: 'max', direction: 'desc' }]) {
146
+ if (call.functionSchema.name === candidate.name) {
147
+ return { name: candidate.name, direction: candidate.direction };
148
+ }
149
+ }
150
+ return null;
151
+ }
152
+ /** The aggregated column, when the argument is a bare column reference. */
153
+ function boundaryColumn(call) {
154
+ const arg = call.args[0];
155
+ return arg instanceof ColumnReferenceNode ? arg : null;
156
+ }
157
+ /**
158
+ * Whether it is sound to read only a prefix of the aggregate's source.
159
+ *
160
+ * The rewrite truncates how much of the source is read, so a source that writes
161
+ * must decline. The idempotence guard is belt-and-braces: `applyPassRules` never
162
+ * re-offers a rule its own output (the applied-rule set is inherited across the
163
+ * re-mint), so the rule cannot stack LimitOffsets on itself.
164
+ */
165
+ function sourceAcceptsTruncation(node) {
166
+ if (node.source instanceof LimitOffsetNode)
167
+ return false;
168
+ return !PlanNodeCharacteristics.subtreeHasSideEffects(node.source);
169
+ }
170
+ /**
171
+ * `<col> IS NOT NULL`, the predicate that does two jobs.
172
+ *
173
+ * Correctness first: ORDER BY in this engine places NULLs FIRST for BOTH directions
174
+ * (`orderByNullResult`, util/comparison.ts), so without this filter `limit 1` would
175
+ * hand the aggregate a NULL and `max(d)` would return NULL whenever the column has
176
+ * any NULL at all.
177
+ *
178
+ * It also unlocks the descending case: `nullSafeOrderingPrefixLength`
179
+ * (vtab/best-access-plan.ts) refuses a DESC ordering claim over a column NULLs could
180
+ * reach unless a NULL-excluding filter is present in the request, and `IS NOT NULL`
181
+ * is one of its recognized ops.
182
+ *
183
+ * NOTE: when the module does NOT consume this as a seek bound, the ordered walk steps
184
+ * over the whole NULL run before reaching the first real value — bounded by the NULL
185
+ * count, so at worst an index entry plus a row per NULL (roughly twice a plain scan on
186
+ * an all-NULL column, free on a column with few NULLs). Worth acting on only if a
187
+ * mostly-NULL indexed column shows up slower than before this rewrite existed.
188
+ */
189
+ function buildIsNotNull(scope, colRef) {
190
+ const ast = {
191
+ type: 'unary',
192
+ operator: 'IS NOT NULL',
193
+ expr: colRef.expression,
194
+ };
195
+ return new UnaryOpNode(scope, ast, colRef);
196
+ }
197
+ function literal(scope, value) {
198
+ return new LiteralNode(scope, { type: 'literal', value });
199
+ }
200
+ //# sourceMappingURL=rule-minmax-index-boundary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule-minmax-index-boundary.js","sourceRoot":"","sources":["../../../../../src/planner/rules/aggregate/rule-minmax-index-boundary.ts"],"names":[],"mappings":"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"}
@@ -21,5 +21,34 @@
21
21
  */
22
22
  import { PlanNode } from '../../nodes/plan-node.js';
23
23
  import type { OptContext } from '../../framework/context.js';
24
+ import { JoinNode } from '../../nodes/join-node.js';
25
+ /**
26
+ * Would this join's right side be wrapped in a `CacheNode` by
27
+ * {@link ruleNestedLoopRightCache}? The single source of truth for that
28
+ * question: the rule below calls it and then builds the node, and
29
+ * `rule-join-physical-selection` calls it to decide whether a plain nested loop
30
+ * pays its inner side's first-row latency ONCE (cached: one open plus N buffer
31
+ * replays) or once per outer row (uncached: N re-opens). Neither caller may
32
+ * restate any of the gates.
33
+ *
34
+ * Every gate is a decline; the comments on each explain what it protects.
35
+ */
36
+ export declare function canCacheNestedLoopRight(node: JoinNode, context: OptContext): boolean;
37
+ /**
38
+ * Is this join's right side opened ONCE per join rather than once per outer row?
39
+ * True two ways: it is already materialized (a `CacheNode` from this rule on an
40
+ * earlier visit, or from the cte / in-subquery / mutating-subquery caches — all
41
+ * of which scan once and replay a buffer), or {@link canCacheNestedLoopRight}
42
+ * says this rule is about to make it so.
43
+ *
44
+ * `rule-join-physical-selection` asks THIS question, not the cacheability one:
45
+ * the two differ exactly on an already-cached right side, which
46
+ * `canCacheNestedLoopRight` declines (nothing left to do) but which pays its
47
+ * first-row latency once, not per outer row. Pricing that side per outer row
48
+ * made the selection rule re-visit a join this rule had just improved, re-price
49
+ * its nested loop as if the cache were not there, and convert it to a hash join
50
+ * — discarding the cache and landing on the costlier plan.
51
+ */
52
+ export declare function nestedLoopRightOpensOnce(node: JoinNode, context: OptContext): boolean;
24
53
  export declare function ruleNestedLoopRightCache(node: PlanNode, context: OptContext): PlanNode | null;
25
54
  //# sourceMappingURL=rule-nested-loop-right-cache.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"rule-nested-loop-right-cache.d.ts","sourceRoot":"","sources":["../../../../../src/planner/rules/cache/rule-nested-loop-right-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAuF7D,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,GAAG,QAAQ,GAAG,IAAI,CAqF7F"}
1
+ {"version":3,"file":"rule-nested-loop-right-cache.d.ts","sourceRoot":"","sources":["../../../../../src/planner/rules/cache/rule-nested-loop-right-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAE7D,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAqFpD;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CA+DpF;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAIrF;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,GAAG,QAAQ,GAAG,IAAI,CA8B7F"}
@@ -103,14 +103,18 @@ function estimateRightRows(node) {
103
103
  }
104
104
  return max;
105
105
  }
106
- export function ruleNestedLoopRightCache(node, context) {
107
- // Only logical JoinNodes reach here uncached. By PostOptimization,
108
- // join-physical-selection has already converted every equi-join it wanted to
109
- // hash/merge (those materialize their build side), so any surviving logical
110
- // JoinNode IS a nested loop — the exact structural signal we need.
111
- if (!(node instanceof JoinNode)) {
112
- return null;
113
- }
106
+ /**
107
+ * Would this join's right side be wrapped in a `CacheNode` by
108
+ * {@link ruleNestedLoopRightCache}? The single source of truth for that
109
+ * question: the rule below calls it and then builds the node, and
110
+ * `rule-join-physical-selection` calls it to decide whether a plain nested loop
111
+ * pays its inner side's first-row latency ONCE (cached: one open plus N buffer
112
+ * replays) or once per outer row (uncached: N re-opens). Neither caller may
113
+ * restate any of the gates.
114
+ *
115
+ * Every gate is a decline; the comments on each explain what it protects.
116
+ */
117
+ export function canCacheNestedLoopRight(node, context) {
114
118
  // Driver gate: only the left-driven join types re-scan the right side.
115
119
  // `right` / `full` buffer the left side once and scan the right side once
116
120
  // (`emitLoopJoin.driveFromRight`), so caching their right side is pure waste.
@@ -124,23 +128,23 @@ export function ruleNestedLoopRightCache(node, context) {
124
128
  // (same reopen count until the buffer fills, a win after), just data-dependent
125
129
  // rather than the guaranteed replay inner/cross/left get.
126
130
  if (node.joinType === 'right' || node.joinType === 'full') {
127
- return null;
131
+ return false;
128
132
  }
129
133
  const right = node.right;
130
134
  // Already cached (e.g. by cte / in-subquery / mutating-subquery cache).
131
135
  if (CapabilityDetectors.isCached(right) && right.isCached()) {
132
- return null;
136
+ return false;
133
137
  }
134
138
  // Purity gate: side effects are the mutating-subquery-cache rule's job.
135
139
  // Double-wrapping a write would change nothing and only burn memory.
136
140
  if (PlanNodeCharacteristics.subtreeHasSideEffects(right)) {
137
- return null;
141
+ return false;
138
142
  }
139
143
  // Determinism gate: a non-deterministic right side (e.g. `random()`) must be
140
144
  // re-evaluated per left row to preserve today's observable behavior; caching
141
145
  // would freeze the first scan's values across every subsequent left row.
142
146
  if (!PlanNodeCharacteristics.isDeterministic(right)) {
143
- return null;
147
+ return false;
144
148
  }
145
149
  // Correlation gate: a right subtree that references left attributes (a
146
150
  // parameterized/lateral seek produced by predicate pushdown) is
@@ -149,24 +153,59 @@ export function ruleNestedLoopRightCache(node, context) {
149
153
  // separate JoinNode.condition child, not inside the right subtree — so the
150
154
  // bare right access has no external refs and is cacheable.
151
155
  if (isCorrelatedSubquery(right)) {
152
- return null;
156
+ return false;
153
157
  }
154
158
  // CTE-safety gate: eagerly materializing a CTE-backed right side breaks the
155
159
  // join's row-context at runtime (see subtreeTouchesCte above).
156
160
  if (subtreeTouchesCte(right)) {
157
- return null;
161
+ return false;
158
162
  }
159
163
  // Size gate (memory safety): materializing an unbounded right side trades I/O
160
164
  // for memory. Respect the existing nested-loop caching threshold.
161
165
  const estimatedRows = estimateRightRows(right) ?? context.tuning.defaultRowEstimate;
162
166
  if (estimatedRows > context.tuning.join.maxRightRowsForCaching) {
163
167
  log('Right side too large to cache (%d rows > %d), skipping', estimatedRows, context.tuning.join.maxRightRowsForCaching);
168
+ return false;
169
+ }
170
+ return true;
171
+ }
172
+ /**
173
+ * Is this join's right side opened ONCE per join rather than once per outer row?
174
+ * True two ways: it is already materialized (a `CacheNode` from this rule on an
175
+ * earlier visit, or from the cte / in-subquery / mutating-subquery caches — all
176
+ * of which scan once and replay a buffer), or {@link canCacheNestedLoopRight}
177
+ * says this rule is about to make it so.
178
+ *
179
+ * `rule-join-physical-selection` asks THIS question, not the cacheability one:
180
+ * the two differ exactly on an already-cached right side, which
181
+ * `canCacheNestedLoopRight` declines (nothing left to do) but which pays its
182
+ * first-row latency once, not per outer row. Pricing that side per outer row
183
+ * made the selection rule re-visit a join this rule had just improved, re-price
184
+ * its nested loop as if the cache were not there, and convert it to a hash join
185
+ * — discarding the cache and landing on the costlier plan.
186
+ */
187
+ export function nestedLoopRightOpensOnce(node, context) {
188
+ const right = node.right;
189
+ if (CapabilityDetectors.isCached(right) && right.isCached())
190
+ return true;
191
+ return canCacheNestedLoopRight(node, context);
192
+ }
193
+ export function ruleNestedLoopRightCache(node, context) {
194
+ // Only logical JoinNodes reach here uncached. By PostOptimization,
195
+ // join-physical-selection has already converted every equi-join it wanted to
196
+ // hash/merge (those materialize their build side), so any surviving logical
197
+ // JoinNode IS a nested loop — the exact structural signal we need.
198
+ if (!(node instanceof JoinNode)) {
199
+ return null;
200
+ }
201
+ if (!canCacheNestedLoopRight(node, context)) {
164
202
  return null;
165
203
  }
166
- log('Caching pure nested-loop right side (%s, %d rows)', node.joinType, estimatedRows);
204
+ const right = node.right;
205
+ log('Caching pure nested-loop right side (%s)', node.joinType);
167
206
  // Memory strategy + a CacheNode threshold (which degrades to pass-through
168
207
  // past its limit) mirrors the sibling mutating-subquery-cache rule. Spill is
169
- // unreachable under the size gate above (maxRightRowsForCaching <
208
+ // unreachable under `canCacheNestedLoopRight`'s size gate (maxRightRowsForCaching <
170
209
  // cache.spillThreshold), so 'memory' is the only live choice here.
171
210
  const threshold = CachingAnalysis.getCacheThreshold(right);
172
211
  const cached = new CacheNode(right.scope, right, 'memory', threshold);
@@ -1 +1 @@
1
- {"version":3,"file":"rule-nested-loop-right-cache.js","sourceRoot":"","sources":["../../../../../src/planner/rules/cache/rule-nested-loop-right-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAEzD,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAE7D,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACpE,OAAO,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AACnH,OAAO,EAAE,oBAAoB,EAAE,MAAM,qCAAqC,CAAC;AAE3E,MAAM,GAAG,GAAG,YAAY,CAAC,wCAAwC,CAAC,CAAC;AAEnE;;;;;;;;;;;;;GAaG;AACH,MAAM,cAAc,GAA8B,IAAI,GAAG,CAAC;IACzD,YAAY,CAAC,YAAY;IACzB,YAAY,CAAC,GAAG;IAChB,YAAY,CAAC,YAAY;IACzB,YAAY,CAAC,uBAAuB;CACpC,CAAC,CAAC;AAEH,6EAA6E;AAC7E,SAAS,iBAAiB,CAAC,IAAc;IACxC,MAAM,KAAK,GAAe,CAAC,IAAI,CAAC,CAAC;IACjC,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,EAAG,CAAC;QAC7B,IAAI,cAAc,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC;YAAE,OAAO,IAAI,CAAC;QACtD,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC;YAC3C,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACnB,CAAC;IACF,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,SAAS,iBAAiB,CAAC,IAAc;IACxC,IAAI,GAAuB,CAAC;IAC5B,MAAM,QAAQ,GAAG,CAAC,IAAwB,EAAQ,EAAE;QACnD,IAAI,IAAI,KAAK,SAAS,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YACjD,GAAG,GAAG,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACtD,CAAC;IACF,CAAC,CAAC;IACF,MAAM,KAAK,GAAe,CAAC,IAAI,CAAC,CAAC;IACjC,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,EAAG,CAAC;QAC7B,QAAQ,CAAC,OAAO,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;QAC1C,IAAI,OAAO,YAAY,eAAe,EAAE,CAAC;YACxC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,aAAa,CAAC,CAAC,CAAC;QACpE,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC;YAC3C,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACnB,CAAC;IACF,CAAC;IACD,OAAO,GAAG,CAAC;AACZ,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,IAAc,EAAE,OAAmB;IAC3E,mEAAmE;IACnE,6EAA6E;IAC7E,4EAA4E;IAC5E,mEAAmE;IACnE,IAAI,CAAC,CAAC,IAAI,YAAY,QAAQ,CAAC,EAAE,CAAC;QACjC,OAAO,IAAI,CAAC;IACb,CAAC;IAED,uEAAuE;IACvE,0EAA0E;IAC1E,8EAA8E;IAC9E,6EAA6E;IAC7E,4EAA4E;IAC5E,8EAA8E;IAC9E,yEAAyE;IACzE,4EAA4E;IAC5E,8EAA8E;IAC9E,yEAAyE;IACzE,+EAA+E;IAC/E,0DAA0D;IAC1D,IAAI,IAAI,CAAC,QAAQ,KAAK,OAAO,IAAI,IAAI,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;QAC3D,OAAO,IAAI,CAAC;IACb,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;IAEzB,wEAAwE;IACxE,IAAI,mBAAmB,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,QAAQ,EAAE,EAAE,CAAC;QAC7D,OAAO,IAAI,CAAC;IACb,CAAC;IAED,wEAAwE;IACxE,qEAAqE;IACrE,IAAI,uBAAuB,CAAC,qBAAqB,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1D,OAAO,IAAI,CAAC;IACb,CAAC;IAED,6EAA6E;IAC7E,6EAA6E;IAC7E,yEAAyE;IACzE,IAAI,CAAC,uBAAuB,CAAC,eAAe,CAAC,KAAK,CAAC,EAAE,CAAC;QACrD,OAAO,IAAI,CAAC;IACb,CAAC;IAED,uEAAuE;IACvE,gEAAgE;IAChE,uEAAuE;IACvE,wEAAwE;IACxE,2EAA2E;IAC3E,2DAA2D;IAC3D,IAAI,oBAAoB,CAAC,KAAK,CAAC,EAAE,CAAC;QACjC,OAAO,IAAI,CAAC;IACb,CAAC;IAED,4EAA4E;IAC5E,+DAA+D;IAC/D,IAAI,iBAAiB,CAAC,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAO,IAAI,CAAC;IACb,CAAC;IAED,8EAA8E;IAC9E,kEAAkE;IAClE,MAAM,aAAa,GAAG,iBAAiB,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,kBAAkB,CAAC;IACpF,IAAI,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,sBAAsB,EAAE,CAAC;QAChE,GAAG,CAAC,wDAAwD,EAC3D,aAAa,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;QAC5D,OAAO,IAAI,CAAC;IACb,CAAC;IAED,GAAG,CAAC,mDAAmD,EAAE,IAAI,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;IAEvF,0EAA0E;IAC1E,6EAA6E;IAC7E,kEAAkE;IAClE,mEAAmE;IACnE,MAAM,SAAS,GAAG,eAAe,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC;IAC3D,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;IAEtE,+EAA+E;IAC/E,+EAA+E;IAC/E,0EAA0E;IAC1E,OAAO,IAAI,CAAC,YAAY,CACvB,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAC1E,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"rule-nested-loop-right-cache.js","sourceRoot":"","sources":["../../../../../src/planner/rules/cache/rule-nested-loop-right-cache.ts"],"names":[],"mappings":"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"}