@quereus/quereus 0.17.1 → 1.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.
- package/README.md +80 -14
- package/dist/src/common/type-inference.d.ts +1 -0
- package/dist/src/common/type-inference.d.ts.map +1 -1
- package/dist/src/common/type-inference.js +6 -1
- package/dist/src/common/type-inference.js.map +1 -1
- package/dist/src/common/types.d.ts +10 -2
- package/dist/src/common/types.d.ts.map +1 -1
- package/dist/src/common/types.js +13 -1
- package/dist/src/common/types.js.map +1 -1
- package/dist/src/emit/ast-stringify.d.ts.map +1 -1
- package/dist/src/emit/ast-stringify.js +5 -0
- package/dist/src/emit/ast-stringify.js.map +1 -1
- package/dist/src/func/builtins/conversion.d.ts +2 -2
- package/dist/src/func/builtins/conversion.js +3 -3
- package/dist/src/func/builtins/conversion.js.map +1 -1
- package/dist/src/func/builtins/index.d.ts.map +1 -1
- package/dist/src/func/builtins/index.js +2 -1
- package/dist/src/func/builtins/index.js.map +1 -1
- package/dist/src/func/builtins/json-helpers.d.ts +10 -0
- package/dist/src/func/builtins/json-helpers.d.ts.map +1 -1
- package/dist/src/func/builtins/json-helpers.js +26 -0
- package/dist/src/func/builtins/json-helpers.js.map +1 -1
- package/dist/src/func/builtins/json-tvf.d.ts.map +1 -1
- package/dist/src/func/builtins/json-tvf.js +9 -15
- package/dist/src/func/builtins/json-tvf.js.map +1 -1
- package/dist/src/func/builtins/json.d.ts.map +1 -1
- package/dist/src/func/builtins/json.js +100 -227
- package/dist/src/func/builtins/json.js.map +1 -1
- package/dist/src/func/builtins/schema.d.ts.map +1 -1
- package/dist/src/func/builtins/schema.js +9 -0
- package/dist/src/func/builtins/schema.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/parser/lexer.d.ts +1 -0
- package/dist/src/parser/lexer.d.ts.map +1 -1
- package/dist/src/parser/lexer.js +2 -1
- package/dist/src/parser/lexer.js.map +1 -1
- package/dist/src/parser/parser.d.ts +12 -0
- package/dist/src/parser/parser.d.ts.map +1 -1
- package/dist/src/parser/parser.js +54 -1
- package/dist/src/parser/parser.js.map +1 -1
- package/dist/src/planner/analysis/const-evaluator.d.ts +5 -0
- package/dist/src/planner/analysis/const-evaluator.d.ts.map +1 -1
- package/dist/src/planner/analysis/const-evaluator.js +106 -0
- package/dist/src/planner/analysis/const-evaluator.js.map +1 -1
- package/dist/src/planner/analysis/const-pass.d.ts +4 -2
- package/dist/src/planner/analysis/const-pass.d.ts.map +1 -1
- package/dist/src/planner/analysis/const-pass.js +23 -13
- package/dist/src/planner/analysis/const-pass.js.map +1 -1
- package/dist/src/planner/analysis/constraint-extractor.d.ts +19 -1
- package/dist/src/planner/analysis/constraint-extractor.d.ts.map +1 -1
- package/dist/src/planner/analysis/constraint-extractor.js +265 -14
- package/dist/src/planner/analysis/constraint-extractor.js.map +1 -1
- package/dist/src/planner/analysis/expression-fingerprint.d.ts +15 -0
- package/dist/src/planner/analysis/expression-fingerprint.d.ts.map +1 -0
- package/dist/src/planner/analysis/expression-fingerprint.js +126 -0
- package/dist/src/planner/analysis/expression-fingerprint.js.map +1 -0
- package/dist/src/planner/cost/index.d.ts +16 -0
- package/dist/src/planner/cost/index.d.ts.map +1 -1
- package/dist/src/planner/cost/index.js +22 -0
- package/dist/src/planner/cost/index.js.map +1 -1
- package/dist/src/planner/framework/pass.d.ts.map +1 -1
- package/dist/src/planner/framework/pass.js +4 -3
- package/dist/src/planner/framework/pass.js.map +1 -1
- package/dist/src/planner/nodes/hash-aggregate.d.ts +38 -0
- package/dist/src/planner/nodes/hash-aggregate.d.ts.map +1 -0
- package/dist/src/planner/nodes/hash-aggregate.js +213 -0
- package/dist/src/planner/nodes/hash-aggregate.js.map +1 -0
- package/dist/src/planner/nodes/plan-node-type.d.ts +1 -0
- package/dist/src/planner/nodes/plan-node-type.d.ts.map +1 -1
- package/dist/src/planner/nodes/plan-node-type.js +1 -0
- package/dist/src/planner/nodes/plan-node-type.js.map +1 -1
- package/dist/src/planner/nodes/stream-aggregate.js.map +1 -1
- package/dist/src/planner/nodes/table-access-nodes.d.ts +11 -0
- package/dist/src/planner/nodes/table-access-nodes.d.ts.map +1 -1
- package/dist/src/planner/nodes/table-access-nodes.js +32 -0
- package/dist/src/planner/nodes/table-access-nodes.js.map +1 -1
- package/dist/src/planner/nodes/values-node.d.ts +3 -1
- package/dist/src/planner/nodes/values-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/values-node.js +9 -2
- package/dist/src/planner/nodes/values-node.js.map +1 -1
- package/dist/src/planner/optimizer.d.ts.map +1 -1
- package/dist/src/planner/optimizer.js +35 -7
- package/dist/src/planner/optimizer.js.map +1 -1
- package/dist/src/planner/rules/access/rule-select-access-path.js +175 -5
- package/dist/src/planner/rules/access/rule-select-access-path.js.map +1 -1
- package/dist/src/planner/rules/aggregate/rule-aggregate-streaming.d.ts +9 -11
- package/dist/src/planner/rules/aggregate/rule-aggregate-streaming.d.ts.map +1 -1
- package/dist/src/planner/rules/aggregate/rule-aggregate-streaming.js +54 -67
- package/dist/src/planner/rules/aggregate/rule-aggregate-streaming.js.map +1 -1
- package/dist/src/planner/rules/cache/rule-scalar-cse.d.ts +19 -0
- package/dist/src/planner/rules/cache/rule-scalar-cse.d.ts.map +1 -0
- package/dist/src/planner/rules/cache/rule-scalar-cse.js +245 -0
- package/dist/src/planner/rules/cache/rule-scalar-cse.js.map +1 -0
- package/dist/src/planner/rules/predicate/rule-filter-merge.d.ts +15 -0
- package/dist/src/planner/rules/predicate/rule-filter-merge.d.ts.map +1 -0
- package/dist/src/planner/rules/predicate/rule-filter-merge.js +39 -0
- package/dist/src/planner/rules/predicate/rule-filter-merge.js.map +1 -0
- package/dist/src/planner/rules/predicate/rule-predicate-pushdown.d.ts +1 -0
- package/dist/src/planner/rules/predicate/rule-predicate-pushdown.d.ts.map +1 -1
- package/dist/src/planner/rules/predicate/rule-predicate-pushdown.js +9 -0
- package/dist/src/planner/rules/predicate/rule-predicate-pushdown.js.map +1 -1
- package/dist/src/planner/rules/retrieve/rule-projection-pruning.d.ts +17 -0
- package/dist/src/planner/rules/retrieve/rule-projection-pruning.d.ts.map +1 -0
- package/dist/src/planner/rules/retrieve/rule-projection-pruning.js +75 -0
- package/dist/src/planner/rules/retrieve/rule-projection-pruning.js.map +1 -0
- package/dist/src/runtime/emit/aggregate.d.ts +9 -0
- package/dist/src/runtime/emit/aggregate.d.ts.map +1 -1
- package/dist/src/runtime/emit/aggregate.js +2 -2
- package/dist/src/runtime/emit/aggregate.js.map +1 -1
- package/dist/src/runtime/emit/empty-result.d.ts +5 -0
- package/dist/src/runtime/emit/empty-result.d.ts.map +1 -0
- package/dist/src/runtime/emit/empty-result.js +11 -0
- package/dist/src/runtime/emit/empty-result.js.map +1 -0
- package/dist/src/runtime/emit/hash-aggregate.d.ts +5 -0
- package/dist/src/runtime/emit/hash-aggregate.d.ts.map +1 -0
- package/dist/src/runtime/emit/hash-aggregate.js +319 -0
- package/dist/src/runtime/emit/hash-aggregate.js.map +1 -0
- package/dist/src/runtime/register.d.ts.map +1 -1
- package/dist/src/runtime/register.js +4 -1
- package/dist/src/runtime/register.js.map +1 -1
- package/dist/src/types/json-type.d.ts +3 -3
- package/dist/src/types/json-type.d.ts.map +1 -1
- package/dist/src/types/json-type.js +44 -38
- package/dist/src/types/json-type.js.map +1 -1
- package/dist/src/types/logical-type.d.ts +2 -1
- package/dist/src/types/logical-type.d.ts.map +1 -1
- package/dist/src/types/logical-type.js +4 -0
- package/dist/src/types/logical-type.js.map +1 -1
- package/dist/src/util/comparison.d.ts +1 -1
- package/dist/src/util/comparison.d.ts.map +1 -1
- package/dist/src/util/comparison.js +22 -4
- package/dist/src/util/comparison.js.map +1 -1
- package/dist/src/vtab/best-access-plan.d.ts +1 -1
- package/dist/src/vtab/best-access-plan.d.ts.map +1 -1
- package/dist/src/vtab/memory/layer/base-cursor.d.ts.map +1 -1
- package/dist/src/vtab/memory/layer/base-cursor.js +81 -6
- package/dist/src/vtab/memory/layer/base-cursor.js.map +1 -1
- package/dist/src/vtab/memory/layer/scan-plan.d.ts +9 -0
- package/dist/src/vtab/memory/layer/scan-plan.d.ts.map +1 -1
- package/dist/src/vtab/memory/layer/scan-plan.js +76 -9
- package/dist/src/vtab/memory/layer/scan-plan.js.map +1 -1
- package/dist/src/vtab/memory/layer/transaction-cursor.d.ts.map +1 -1
- package/dist/src/vtab/memory/layer/transaction-cursor.js +79 -11
- package/dist/src/vtab/memory/layer/transaction-cursor.js.map +1 -1
- package/dist/src/vtab/memory/module.d.ts +4 -0
- package/dist/src/vtab/memory/module.d.ts.map +1 -1
- package/dist/src/vtab/memory/module.js +79 -11
- package/dist/src/vtab/memory/module.js.map +1 -1
- package/package.json +25 -5
|
@@ -10,7 +10,9 @@ import { emitPlanNode } from '../../runtime/emitters.js';
|
|
|
10
10
|
import { EmissionContext } from '../../runtime/emission-context.js';
|
|
11
11
|
import { Scheduler } from '../../runtime/scheduler.js';
|
|
12
12
|
import { RowContextMap } from '../../runtime/context-helpers.js';
|
|
13
|
+
import { isAsyncIterable } from '../../runtime/utils.js';
|
|
13
14
|
import { createLogger } from '../../common/logger.js';
|
|
15
|
+
import { TableLiteralNode } from '../nodes/values-node.js';
|
|
14
16
|
const log = createLogger('optimizer:folding:eval');
|
|
15
17
|
/**
|
|
16
18
|
* Create an expression evaluator that uses the runtime to evaluate constant expressions
|
|
@@ -50,4 +52,108 @@ export function createRuntimeExpressionEvaluator(db) {
|
|
|
50
52
|
}
|
|
51
53
|
};
|
|
52
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* A self-materializing async iterable that caches rows on first iteration.
|
|
57
|
+
* First iteration runs the source, collects all rows, and caches them.
|
|
58
|
+
* Subsequent iterations yield from the cached array.
|
|
59
|
+
*/
|
|
60
|
+
class MaterializingAsyncIterable {
|
|
61
|
+
createSource;
|
|
62
|
+
cached = null;
|
|
63
|
+
materializing = null;
|
|
64
|
+
constructor(createSource) {
|
|
65
|
+
this.createSource = createSource;
|
|
66
|
+
}
|
|
67
|
+
[Symbol.asyncIterator]() {
|
|
68
|
+
if (this.cached) {
|
|
69
|
+
return arrayToAsyncIterator(this.cached);
|
|
70
|
+
}
|
|
71
|
+
return this.materializeAndYield();
|
|
72
|
+
}
|
|
73
|
+
materializeAndYield() {
|
|
74
|
+
// If already materializing from another iterator, wait for it
|
|
75
|
+
if (!this.materializing) {
|
|
76
|
+
this.materializing = this.doMaterialize();
|
|
77
|
+
}
|
|
78
|
+
const promise = this.materializing;
|
|
79
|
+
let index = 0;
|
|
80
|
+
return {
|
|
81
|
+
next: async () => {
|
|
82
|
+
const rows = await promise;
|
|
83
|
+
if (index < rows.length) {
|
|
84
|
+
return { value: rows[index++], done: false };
|
|
85
|
+
}
|
|
86
|
+
return { value: undefined, done: true };
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
async doMaterialize() {
|
|
91
|
+
const rows = [];
|
|
92
|
+
let source = this.createSource();
|
|
93
|
+
// Resolve if promise
|
|
94
|
+
if (source instanceof Promise) {
|
|
95
|
+
source = await source;
|
|
96
|
+
}
|
|
97
|
+
// Source should be an AsyncIterable<Row>
|
|
98
|
+
if (isAsyncIterable(source)) {
|
|
99
|
+
for await (const row of source) {
|
|
100
|
+
rows.push(row);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
throw new QuereusError('Relational evaluation did not produce an async iterable');
|
|
105
|
+
}
|
|
106
|
+
this.cached = rows;
|
|
107
|
+
return rows;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function arrayToAsyncIterator(rows) {
|
|
111
|
+
let index = 0;
|
|
112
|
+
return {
|
|
113
|
+
next: async () => {
|
|
114
|
+
if (index < rows.length) {
|
|
115
|
+
return { value: rows[index++], done: false };
|
|
116
|
+
}
|
|
117
|
+
return { value: undefined, done: true };
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Create a relational evaluator that replaces constant relational subtrees
|
|
123
|
+
* with TableLiteralNodes using deferred materialization.
|
|
124
|
+
*/
|
|
125
|
+
export function createRuntimeRelationalEvaluator(db) {
|
|
126
|
+
return function evaluateRelation(node) {
|
|
127
|
+
log('Evaluating relational constant: %s', node.nodeType);
|
|
128
|
+
try {
|
|
129
|
+
// Emit the relational subtree to an instruction tree
|
|
130
|
+
const emissionCtx = new EmissionContext(db);
|
|
131
|
+
const instruction = emitPlanNode(node, emissionCtx);
|
|
132
|
+
const scheduler = new Scheduler(instruction);
|
|
133
|
+
// Create a self-materializing async iterable
|
|
134
|
+
const iterable = new MaterializingAsyncIterable(() => {
|
|
135
|
+
const runtimeCtx = {
|
|
136
|
+
db,
|
|
137
|
+
stmt: undefined,
|
|
138
|
+
params: {},
|
|
139
|
+
context: new RowContextMap(),
|
|
140
|
+
tableContexts: new Map(),
|
|
141
|
+
enableMetrics: false
|
|
142
|
+
};
|
|
143
|
+
return scheduler.run(runtimeCtx);
|
|
144
|
+
});
|
|
145
|
+
// Preserve the original node's type and attributes
|
|
146
|
+
const relType = node.getType();
|
|
147
|
+
const relNode = node;
|
|
148
|
+
const originalAttrs = [...relNode.getAttributes()];
|
|
149
|
+
const replacement = new TableLiteralNode(node.scope, iterable, relNode.estimatedRows, relType, originalAttrs);
|
|
150
|
+
log('Replaced relational node %s with TableLiteralNode', node.id);
|
|
151
|
+
return replacement;
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
log('Failed to evaluate relational node %s: %s', node.nodeType, error);
|
|
155
|
+
throw new QuereusError('Relational evaluation failed', StatusCode.ERROR, error instanceof Error ? error : undefined);
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
}
|
|
53
159
|
//# sourceMappingURL=const-evaluator.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"const-evaluator.js","sourceRoot":"","sources":["../../../../src/planner/analysis/const-evaluator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAEnD,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACpE,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAEvD,OAAO,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"const-evaluator.js","sourceRoot":"","sources":["../../../../src/planner/analysis/const-evaluator.ts"],"names":[],"mappings":"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"}
|
|
@@ -33,16 +33,18 @@ export interface ConstFoldingContext {
|
|
|
33
33
|
borderNodes: Map<string, PlanNode>;
|
|
34
34
|
/** Evaluation function for border expressions */
|
|
35
35
|
evaluateExpression: (node: PlanNode) => MaybePromise<OutputValue>;
|
|
36
|
+
/** Evaluation function for relational border nodes */
|
|
37
|
+
evaluateRelation?: (node: PlanNode) => PlanNode;
|
|
36
38
|
}
|
|
37
39
|
/**
|
|
38
40
|
* Create a new constant folding context
|
|
39
41
|
*/
|
|
40
|
-
export declare function createConstFoldingContext(evaluateExpression: (node: PlanNode) => MaybePromise<OutputValue
|
|
42
|
+
export declare function createConstFoldingContext(evaluateExpression: (node: PlanNode) => MaybePromise<OutputValue>, evaluateRelation?: (node: PlanNode) => PlanNode): ConstFoldingContext;
|
|
41
43
|
/**
|
|
42
44
|
* Perform complete single-pass constant folding on a plan tree
|
|
43
45
|
* This is the main entry point for efficient constant folding
|
|
44
46
|
*/
|
|
45
|
-
export declare function performConstantFolding(root: PlanNode, evaluateExpression: (node: PlanNode) => MaybePromise<OutputValue
|
|
47
|
+
export declare function performConstantFolding(root: PlanNode, evaluateExpression: (node: PlanNode) => MaybePromise<OutputValue>, evaluateRelation?: (node: PlanNode) => PlanNode): PlanNode;
|
|
46
48
|
/**
|
|
47
49
|
* Perform bottom-up constant classification on a plan tree
|
|
48
50
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"const-pass.d.ts","sourceRoot":"","sources":["../../../../src/planner/analysis/const-pass.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEjD,OAAO,EAAiB,KAAK,YAAY,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAQtF;;GAEG;AACH,UAAU,cAAc;IACvB,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,QAAQ,CAAC;CACf;AAED,UAAU,YAAY;IACrB,IAAI,EAAE,KAAK,CAAC;IACZ,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAClB;AAED,UAAU,YAAY;IACrB,IAAI,EAAE,WAAW,CAAC;CAClB;AAED,KAAK,SAAS,GAAG,cAAc,GAAG,YAAY,GAAG,YAAY,CAAC;AAE9D;;GAEG;AACH,MAAM,WAAW,mBAAmB;IACnC,mDAAmD;IACnD,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAClC,yDAAyD;IACzD,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACnC,iDAAiD;IACjD,kBAAkB,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,YAAY,CAAC,WAAW,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"const-pass.d.ts","sourceRoot":"","sources":["../../../../src/planner/analysis/const-pass.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEjD,OAAO,EAAiB,KAAK,YAAY,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAQtF;;GAEG;AACH,UAAU,cAAc;IACvB,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,QAAQ,CAAC;CACf;AAED,UAAU,YAAY;IACrB,IAAI,EAAE,KAAK,CAAC;IACZ,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAClB;AAED,UAAU,YAAY;IACrB,IAAI,EAAE,WAAW,CAAC;CAClB;AAED,KAAK,SAAS,GAAG,cAAc,GAAG,YAAY,GAAG,YAAY,CAAC;AAE9D;;GAEG;AACH,MAAM,WAAW,mBAAmB;IACnC,mDAAmD;IACnD,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAClC,yDAAyD;IACzD,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACnC,iDAAiD;IACjD,kBAAkB,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,YAAY,CAAC,WAAW,CAAC,CAAC;IAClE,sDAAsD;IACtD,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,QAAQ,CAAC;CAChD;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CACxC,kBAAkB,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,YAAY,CAAC,WAAW,CAAC,EACjE,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,QAAQ,GAC7C,mBAAmB,CAOrB;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACrC,IAAI,EAAE,QAAQ,EACd,kBAAkB,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,YAAY,CAAC,WAAW,CAAC,EACjE,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,QAAQ,GAC7C,QAAQ,CAWV;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,mBAAmB,GAAG,IAAI,CAehF"}
|
|
@@ -14,19 +14,20 @@ const log = createLogger('optimizer:folding');
|
|
|
14
14
|
/**
|
|
15
15
|
* Create a new constant folding context
|
|
16
16
|
*/
|
|
17
|
-
export function createConstFoldingContext(evaluateExpression) {
|
|
17
|
+
export function createConstFoldingContext(evaluateExpression, evaluateRelation) {
|
|
18
18
|
return {
|
|
19
19
|
constInfo: new Map(),
|
|
20
20
|
borderNodes: new Map(),
|
|
21
|
-
evaluateExpression
|
|
21
|
+
evaluateExpression,
|
|
22
|
+
evaluateRelation
|
|
22
23
|
};
|
|
23
24
|
}
|
|
24
25
|
/**
|
|
25
26
|
* Perform complete single-pass constant folding on a plan tree
|
|
26
27
|
* This is the main entry point for efficient constant folding
|
|
27
28
|
*/
|
|
28
|
-
export function performConstantFolding(root, evaluateExpression) {
|
|
29
|
-
const ctx = createConstFoldingContext(evaluateExpression);
|
|
29
|
+
export function performConstantFolding(root, evaluateExpression, evaluateRelation) {
|
|
30
|
+
const ctx = createConstFoldingContext(evaluateExpression, evaluateRelation);
|
|
30
31
|
// Phase 1: Bottom-up classification
|
|
31
32
|
classifyConstants(root, ctx);
|
|
32
33
|
// Phase 2: Top-down border detection with dependency resolution
|
|
@@ -108,13 +109,19 @@ function detectBorderNodes(node, ctx, knownConstAttrs = new Set()) {
|
|
|
108
109
|
const nodeInfo = ctx.constInfo.get(node.id);
|
|
109
110
|
// Check if this node is a border node
|
|
110
111
|
if (nodeInfo?.kind === 'const') {
|
|
111
|
-
|
|
112
|
-
|
|
112
|
+
const typeClass = node.getType().typeClass;
|
|
113
|
+
if (!node.physical.constant && (typeClass === 'scalar' || typeClass === 'relation')) {
|
|
114
|
+
// Foldable const border node (scalar or relational)
|
|
113
115
|
ctx.borderNodes.set(node.id, node);
|
|
114
116
|
log('Detected const border node: %s (%s)', node.id, node.nodeType);
|
|
117
|
+
// Don't recurse into const subtrees - they'll all be replaced
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (node.physical.constant) {
|
|
121
|
+
// Already a physical constant (e.g. LiteralNode) - no folding needed
|
|
122
|
+
return;
|
|
115
123
|
}
|
|
116
|
-
//
|
|
117
|
-
return;
|
|
124
|
+
// Non-foldable const node (e.g. Block with void type) - recurse to find inner borders
|
|
118
125
|
}
|
|
119
126
|
else if (nodeInfo?.kind === 'dep') {
|
|
120
127
|
// Dep nodes become border nodes if all dependencies are resolved
|
|
@@ -171,9 +178,8 @@ function replaceBorderNodes(node, ctx) {
|
|
|
171
178
|
// If this node is a border node, replace it
|
|
172
179
|
if (ctx.borderNodes.has(node.id)) {
|
|
173
180
|
try {
|
|
174
|
-
const evaluatedValue = ctx.evaluateExpression(node);
|
|
175
|
-
// Choose replacement type based on node type
|
|
176
181
|
if (node.getType().typeClass === 'scalar') {
|
|
182
|
+
const evaluatedValue = ctx.evaluateExpression(node);
|
|
177
183
|
const literalExpr = { type: 'literal', value: evaluatedValue };
|
|
178
184
|
const replacement = new LiteralNode(node.scope, literalExpr,
|
|
179
185
|
// Preserve the original node's type metadata so that information like
|
|
@@ -182,10 +188,14 @@ function replaceBorderNodes(node, ctx) {
|
|
|
182
188
|
log('Replaced scalar border node %s with LiteralNode', node.id);
|
|
183
189
|
return replacement;
|
|
184
190
|
}
|
|
185
|
-
else {
|
|
191
|
+
else if (ctx.evaluateRelation) {
|
|
186
192
|
// Relational node - replace with TableLiteralNode
|
|
187
|
-
|
|
188
|
-
log('
|
|
193
|
+
const replacement = ctx.evaluateRelation(node);
|
|
194
|
+
log('Replaced relational border node %s with TableLiteralNode', node.id);
|
|
195
|
+
return replacement;
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
log('Relational border node %s skipped (no relational evaluator)', node.id);
|
|
189
199
|
return node;
|
|
190
200
|
}
|
|
191
201
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"const-pass.js","sourceRoot":"","sources":["../../../../src/planner/analysis/const-pass.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE1D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAItD,MAAM,GAAG,GAAG,YAAY,CAAC,mBAAmB,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"const-pass.js","sourceRoot":"","sources":["../../../../src/planner/analysis/const-pass.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,8 +3,24 @@
|
|
|
3
3
|
* Converts scalar expressions into constraints that can be pushed down to virtual tables
|
|
4
4
|
*/
|
|
5
5
|
import type { ScalarPlanNode, RelationalPlanNode, PlanNode } from '../nodes/plan-node.js';
|
|
6
|
-
import type { Row } from '../../common/types.js';
|
|
6
|
+
import type { Row, SqlValue } from '../../common/types.js';
|
|
7
7
|
import type { PredicateConstraint as VtabPredicateConstraint } from '../../vtab/best-access-plan.js';
|
|
8
|
+
/**
|
|
9
|
+
* A single range specification within an OR_RANGE constraint.
|
|
10
|
+
* Each range has optional lower and upper bounds.
|
|
11
|
+
*/
|
|
12
|
+
export interface RangeSpec {
|
|
13
|
+
lower?: {
|
|
14
|
+
op: '>=' | '>';
|
|
15
|
+
value: SqlValue;
|
|
16
|
+
valueExpr?: ScalarPlanNode;
|
|
17
|
+
};
|
|
18
|
+
upper?: {
|
|
19
|
+
op: '<=' | '<';
|
|
20
|
+
value: SqlValue;
|
|
21
|
+
valueExpr?: ScalarPlanNode;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
8
24
|
/**
|
|
9
25
|
* A constraint extracted from a predicate expression
|
|
10
26
|
* Extends the vtab PredicateConstraint with additional metadata for the planner
|
|
@@ -20,6 +36,8 @@ export interface PredicateConstraint extends VtabPredicateConstraint {
|
|
|
20
36
|
valueExpr?: ScalarPlanNode | ScalarPlanNode[];
|
|
21
37
|
/** Binding kind describing how value is supplied */
|
|
22
38
|
bindingKind?: 'literal' | 'parameter' | 'correlated' | 'expression' | 'mixed';
|
|
39
|
+
/** Range specifications for OR_RANGE constraints */
|
|
40
|
+
ranges?: RangeSpec[];
|
|
23
41
|
}
|
|
24
42
|
/**
|
|
25
43
|
* Result of constraint extraction
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constraint-extractor.d.ts","sourceRoot":"","sources":["../../../../src/planner/analysis/constraint-extractor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAM1F,OAAO,KAAK,EAAE,GAAG,
|
|
1
|
+
{"version":3,"file":"constraint-extractor.d.ts","sourceRoot":"","sources":["../../../../src/planner/analysis/constraint-extractor.ts"],"names":[],"mappings":"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"}
|
|
@@ -108,9 +108,18 @@ function extractFromExpression(expr, constraints, residual, attributeToTableMap,
|
|
|
108
108
|
extractFromExpression(binaryOp.right, constraints, residual, attributeToTableMap, perTableParts);
|
|
109
109
|
return;
|
|
110
110
|
}
|
|
111
|
-
// Handle OR expressions
|
|
111
|
+
// Handle OR expressions: try to extract as IN or OR constraint group
|
|
112
112
|
if (isOrExpression(expr)) {
|
|
113
|
-
|
|
113
|
+
const orResult = tryExtractOrBranches(expr, attributeToTableMap);
|
|
114
|
+
if (orResult) {
|
|
115
|
+
for (const c of orResult.constraints) {
|
|
116
|
+
constraints.push(c);
|
|
117
|
+
}
|
|
118
|
+
addSupportedPart(expr, attributeToTableMap, perTableParts);
|
|
119
|
+
log('OR expression extracted %d constraints', orResult.constraints.length);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
log('OR expression not extractable, treating as residual');
|
|
114
123
|
residual.push(expr);
|
|
115
124
|
return;
|
|
116
125
|
}
|
|
@@ -132,6 +141,18 @@ function extractFromExpression(expr, constraints, residual, attributeToTableMap,
|
|
|
132
141
|
return;
|
|
133
142
|
}
|
|
134
143
|
}
|
|
144
|
+
// IS NULL / IS NOT NULL → unary constraint
|
|
145
|
+
if (expr.nodeType === PlanNodeType.UnaryOp) {
|
|
146
|
+
const unaryOp = expr;
|
|
147
|
+
if (unaryOp.expression.operator === 'IS NULL' || unaryOp.expression.operator === 'IS NOT NULL') {
|
|
148
|
+
const c = extractNullConstraint(unaryOp, attributeToTableMap);
|
|
149
|
+
if (c) {
|
|
150
|
+
constraints.push(c);
|
|
151
|
+
addSupportedPart(expr, attributeToTableMap, perTableParts);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
135
156
|
// Try to extract constraint from binary comparison
|
|
136
157
|
const constraint = extractBinaryConstraint(expr, attributeToTableMap);
|
|
137
158
|
if (constraint) {
|
|
@@ -318,7 +339,7 @@ function extractBetweenConstraints(expr, attributeToTableMap) {
|
|
|
318
339
|
];
|
|
319
340
|
}
|
|
320
341
|
function extractInConstraint(expr, attributeToTableMap) {
|
|
321
|
-
// Only support column IN (
|
|
342
|
+
// Only support column IN (value-list), not subqueries
|
|
322
343
|
if (expr.source)
|
|
323
344
|
return null;
|
|
324
345
|
if (!expr.values || expr.values.length === 0)
|
|
@@ -326,9 +347,6 @@ function extractInConstraint(expr, attributeToTableMap) {
|
|
|
326
347
|
const col = expr.condition;
|
|
327
348
|
if (col.nodeType !== PlanNodeType.ColumnReference)
|
|
328
349
|
return null;
|
|
329
|
-
// Ensure all are literals
|
|
330
|
-
if (!expr.values.every(v => isLiteralConstant(v)))
|
|
331
|
-
return null;
|
|
332
350
|
const columnRef = col;
|
|
333
351
|
const tableInfo = attributeToTableMap.get(columnRef.attributeId);
|
|
334
352
|
if (!tableInfo)
|
|
@@ -336,10 +354,15 @@ function extractInConstraint(expr, attributeToTableMap) {
|
|
|
336
354
|
const columnIndex = tableInfo.columnIndexMap.get(columnRef.attributeId);
|
|
337
355
|
if (columnIndex === undefined)
|
|
338
356
|
return null;
|
|
339
|
-
//
|
|
340
|
-
|
|
341
|
-
const
|
|
342
|
-
|
|
357
|
+
// Check if all values are literals, or if some are dynamic (parameters/expressions)
|
|
358
|
+
const allLiteral = expr.values.every(v => isLiteralConstant(v));
|
|
359
|
+
const allUsable = expr.values.every(v => isLiteralConstant(v) || isDynamicValue(v));
|
|
360
|
+
if (!allUsable)
|
|
361
|
+
return null;
|
|
362
|
+
const values = allLiteral
|
|
363
|
+
? expr.values.map(v => getLiteralValue(v))
|
|
364
|
+
: expr.values.map(v => isLiteralConstant(v) ? getLiteralValue(v) : undefined);
|
|
365
|
+
const result = {
|
|
343
366
|
columnIndex,
|
|
344
367
|
attributeId: columnRef.attributeId,
|
|
345
368
|
op: 'IN',
|
|
@@ -349,6 +372,38 @@ function extractInConstraint(expr, attributeToTableMap) {
|
|
|
349
372
|
sourceExpression: expr,
|
|
350
373
|
targetRelation: tableInfo.relationKey
|
|
351
374
|
};
|
|
375
|
+
// Attach dynamic binding metadata when not all values are literals
|
|
376
|
+
if (!allLiteral) {
|
|
377
|
+
result.valueExpr = expr.values;
|
|
378
|
+
result.bindingKind = 'mixed';
|
|
379
|
+
}
|
|
380
|
+
return result;
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Extract constraint from IS NULL / IS NOT NULL unary expression
|
|
384
|
+
*/
|
|
385
|
+
function extractNullConstraint(expr, attributeToTableMap) {
|
|
386
|
+
const operand = expr.operand;
|
|
387
|
+
if (!isColumnReference(operand))
|
|
388
|
+
return null;
|
|
389
|
+
const columnRef = getColumnReference(operand);
|
|
390
|
+
const tableInfo = attributeToTableMap.get(columnRef.attributeId);
|
|
391
|
+
if (!tableInfo)
|
|
392
|
+
return null;
|
|
393
|
+
const columnIndex = tableInfo.columnIndexMap.get(columnRef.attributeId);
|
|
394
|
+
if (columnIndex === undefined)
|
|
395
|
+
return null;
|
|
396
|
+
const op = expr.expression.operator;
|
|
397
|
+
return {
|
|
398
|
+
columnIndex,
|
|
399
|
+
attributeId: columnRef.attributeId,
|
|
400
|
+
op,
|
|
401
|
+
value: undefined,
|
|
402
|
+
usable: true,
|
|
403
|
+
sourceExpression: expr,
|
|
404
|
+
targetRelation: tableInfo.relationKey,
|
|
405
|
+
bindingKind: 'literal'
|
|
406
|
+
};
|
|
352
407
|
}
|
|
353
408
|
/**
|
|
354
409
|
* Map AST operators to constraint operators
|
|
@@ -365,13 +420,209 @@ function mapOperatorToConstraint(operator, _rightValue) {
|
|
|
365
420
|
case 'MATCH': return 'MATCH';
|
|
366
421
|
case 'IN': return 'IN';
|
|
367
422
|
case 'NOT IN': return 'NOT IN';
|
|
368
|
-
// NOTE: IS NULL / IS NOT NULL are parsed as unary expressions, not binary
|
|
369
|
-
// 'IS' / 'IS NOT'. To support constraint extraction for these, add unary
|
|
370
|
-
// expression handling in extractConstraints alongside a proper
|
|
371
|
-
// empty-result physical node for the IS NULL on NOT NULL optimization.
|
|
372
423
|
default: return null;
|
|
373
424
|
}
|
|
374
425
|
}
|
|
426
|
+
/**
|
|
427
|
+
* Flatten an OR expression tree into a list of disjuncts.
|
|
428
|
+
*/
|
|
429
|
+
function flattenOrDisjuncts(expr) {
|
|
430
|
+
const result = [];
|
|
431
|
+
const stack = [expr];
|
|
432
|
+
while (stack.length > 0) {
|
|
433
|
+
const node = stack.pop();
|
|
434
|
+
if (isOrExpression(node)) {
|
|
435
|
+
const binary = node;
|
|
436
|
+
stack.push(binary.right, binary.left);
|
|
437
|
+
}
|
|
438
|
+
else {
|
|
439
|
+
result.push(node);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
return result;
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Attempt to extract index-friendly constraints from an OR expression.
|
|
446
|
+
*
|
|
447
|
+
* Handles two cases:
|
|
448
|
+
* 1. All branches are equality on the same column → collapse to IN constraint
|
|
449
|
+
* 2. All branches target the same table with extractable constraints → OR constraint group
|
|
450
|
+
*
|
|
451
|
+
* Returns null if the OR cannot be extracted (remains residual).
|
|
452
|
+
*/
|
|
453
|
+
function tryExtractOrBranches(expr, attributeToTableMap) {
|
|
454
|
+
const disjuncts = flattenOrDisjuncts(expr);
|
|
455
|
+
if (disjuncts.length < 2)
|
|
456
|
+
return null;
|
|
457
|
+
// Extract constraints from each branch independently
|
|
458
|
+
const branches = [];
|
|
459
|
+
for (const d of disjuncts) {
|
|
460
|
+
const branchConstraints = [];
|
|
461
|
+
const branchResidual = [];
|
|
462
|
+
const branchParts = new Map();
|
|
463
|
+
extractFromExpression(d, branchConstraints, branchResidual, attributeToTableMap, branchParts);
|
|
464
|
+
branches.push({
|
|
465
|
+
constraints: branchConstraints,
|
|
466
|
+
hasResidual: branchResidual.length > 0
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
// If any branch has residual (not fully extractable), the entire OR must be residual.
|
|
470
|
+
// We can't partially push down an OR — all branches must be handled.
|
|
471
|
+
if (branches.some(b => b.hasResidual || b.constraints.length === 0)) {
|
|
472
|
+
return null;
|
|
473
|
+
}
|
|
474
|
+
// Check if all branches target the same table
|
|
475
|
+
const allRelations = new Set();
|
|
476
|
+
for (const b of branches) {
|
|
477
|
+
for (const c of b.constraints) {
|
|
478
|
+
if (c.targetRelation)
|
|
479
|
+
allRelations.add(c.targetRelation);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
if (allRelations.size !== 1)
|
|
483
|
+
return null;
|
|
484
|
+
// Case 1: All branches are single equality or IN on the same column → collapse to IN
|
|
485
|
+
const allEqOrIn = branches.every(b => b.constraints.length === 1 &&
|
|
486
|
+
(b.constraints[0].op === '=' || b.constraints[0].op === 'IN'));
|
|
487
|
+
if (allEqOrIn) {
|
|
488
|
+
const firstConstraint = branches[0].constraints[0];
|
|
489
|
+
const sameColumn = branches.every(b => b.constraints[0].columnIndex === firstConstraint.columnIndex &&
|
|
490
|
+
b.constraints[0].attributeId === firstConstraint.attributeId);
|
|
491
|
+
if (sameColumn) {
|
|
492
|
+
return collapseBranchesToIn(branches, firstConstraint, expr);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
// Case 2: All branches are range (or equality) on the same column → OR_RANGE
|
|
496
|
+
const orRangeResult = tryCollapseToOrRange(branches, expr);
|
|
497
|
+
if (orRangeResult)
|
|
498
|
+
return orRangeResult;
|
|
499
|
+
return null;
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* Collapse OR branches (equality and/or IN) on the same column into a single IN constraint.
|
|
503
|
+
* Handles mixed equality + IN branches (e.g., from nested OR normalization)
|
|
504
|
+
* and both literal and non-literal (parameter, expression) values.
|
|
505
|
+
*/
|
|
506
|
+
function collapseBranchesToIn(branches, template, sourceExpr) {
|
|
507
|
+
const values = [];
|
|
508
|
+
const valueExprs = [];
|
|
509
|
+
let hasNonLiteral = false;
|
|
510
|
+
for (const b of branches) {
|
|
511
|
+
const c = b.constraints[0];
|
|
512
|
+
if (c.op === 'IN' && Array.isArray(c.value)) {
|
|
513
|
+
// IN branch: merge all its values
|
|
514
|
+
for (const v of c.value) {
|
|
515
|
+
values.push(v);
|
|
516
|
+
}
|
|
517
|
+
if (Array.isArray(c.valueExpr)) {
|
|
518
|
+
for (const ve of c.valueExpr) {
|
|
519
|
+
valueExprs.push(ve);
|
|
520
|
+
}
|
|
521
|
+
hasNonLiteral = true;
|
|
522
|
+
}
|
|
523
|
+
else {
|
|
524
|
+
// All literal IN — push placeholder source expressions
|
|
525
|
+
for (const _v of c.value) {
|
|
526
|
+
valueExprs.push(c.sourceExpression);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
else {
|
|
531
|
+
// Equality branch: single value
|
|
532
|
+
values.push(c.value);
|
|
533
|
+
if (c.valueExpr && !Array.isArray(c.valueExpr)) {
|
|
534
|
+
valueExprs.push(c.valueExpr);
|
|
535
|
+
hasNonLiteral = true;
|
|
536
|
+
}
|
|
537
|
+
else {
|
|
538
|
+
valueExprs.push(c.sourceExpression);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
const result = {
|
|
543
|
+
columnIndex: template.columnIndex,
|
|
544
|
+
attributeId: template.attributeId,
|
|
545
|
+
op: 'IN',
|
|
546
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
547
|
+
value: values,
|
|
548
|
+
usable: true,
|
|
549
|
+
sourceExpression: sourceExpr,
|
|
550
|
+
targetRelation: template.targetRelation,
|
|
551
|
+
valueExpr: hasNonLiteral ? valueExprs : undefined,
|
|
552
|
+
bindingKind: hasNonLiteral ? 'mixed' : 'literal'
|
|
553
|
+
};
|
|
554
|
+
return { constraints: [result] };
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* Collapse OR branches that are all range/equality constraints on the same column
|
|
558
|
+
* into a single OR_RANGE constraint with multiple range specs.
|
|
559
|
+
*/
|
|
560
|
+
function tryCollapseToOrRange(branches, sourceExpr) {
|
|
561
|
+
// All branches must have constraints on a single column (possibly multiple for BETWEEN-style ranges)
|
|
562
|
+
let targetColumnIndex;
|
|
563
|
+
let targetAttributeId;
|
|
564
|
+
let targetRelation;
|
|
565
|
+
const rangeSpecs = [];
|
|
566
|
+
for (const b of branches) {
|
|
567
|
+
// A branch may have 1 constraint (single bound or equality) or 2 constraints (lower + upper on same col)
|
|
568
|
+
if (b.constraints.length === 0 || b.constraints.length > 2)
|
|
569
|
+
return null;
|
|
570
|
+
// All constraints in this branch must target the same column
|
|
571
|
+
const firstCol = b.constraints[0].columnIndex;
|
|
572
|
+
const firstAttr = b.constraints[0].attributeId;
|
|
573
|
+
const firstRel = b.constraints[0].targetRelation;
|
|
574
|
+
if (!b.constraints.every(c => c.columnIndex === firstCol))
|
|
575
|
+
return null;
|
|
576
|
+
// Initialize target or verify consistency across branches
|
|
577
|
+
if (targetColumnIndex === undefined) {
|
|
578
|
+
targetColumnIndex = firstCol;
|
|
579
|
+
targetAttributeId = firstAttr;
|
|
580
|
+
targetRelation = firstRel;
|
|
581
|
+
}
|
|
582
|
+
else if (targetColumnIndex !== firstCol || targetAttributeId !== firstAttr) {
|
|
583
|
+
return null;
|
|
584
|
+
}
|
|
585
|
+
// Build range spec from branch constraints
|
|
586
|
+
const spec = {};
|
|
587
|
+
for (const c of b.constraints) {
|
|
588
|
+
const dynExpr = c.valueExpr && !Array.isArray(c.valueExpr) ? c.valueExpr : undefined;
|
|
589
|
+
if (c.op === '=') {
|
|
590
|
+
// Equality: treat as >= v AND <= v
|
|
591
|
+
spec.lower = { op: '>=', value: c.value, valueExpr: dynExpr };
|
|
592
|
+
spec.upper = { op: '<=', value: c.value, valueExpr: dynExpr };
|
|
593
|
+
}
|
|
594
|
+
else if (c.op === '>' || c.op === '>=') {
|
|
595
|
+
spec.lower = { op: c.op, value: c.value, valueExpr: dynExpr };
|
|
596
|
+
}
|
|
597
|
+
else if (c.op === '<' || c.op === '<=') {
|
|
598
|
+
spec.upper = { op: c.op, value: c.value, valueExpr: dynExpr };
|
|
599
|
+
}
|
|
600
|
+
else {
|
|
601
|
+
// Non-range, non-equality op → can't collapse
|
|
602
|
+
return null;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
// Each branch must define at least one bound
|
|
606
|
+
if (!spec.lower && !spec.upper)
|
|
607
|
+
return null;
|
|
608
|
+
rangeSpecs.push(spec);
|
|
609
|
+
}
|
|
610
|
+
if (targetColumnIndex === undefined || targetAttributeId === undefined || rangeSpecs.length < 2) {
|
|
611
|
+
return null;
|
|
612
|
+
}
|
|
613
|
+
const result = {
|
|
614
|
+
columnIndex: targetColumnIndex,
|
|
615
|
+
attributeId: targetAttributeId,
|
|
616
|
+
op: 'OR_RANGE',
|
|
617
|
+
value: undefined,
|
|
618
|
+
usable: true,
|
|
619
|
+
sourceExpression: sourceExpr,
|
|
620
|
+
targetRelation: targetRelation,
|
|
621
|
+
bindingKind: 'literal',
|
|
622
|
+
ranges: rangeSpecs,
|
|
623
|
+
};
|
|
624
|
+
return { constraints: [result] };
|
|
625
|
+
}
|
|
375
626
|
/**
|
|
376
627
|
* Check if expression is an AND operation
|
|
377
628
|
*/
|