rawsql-ts 0.29.0 → 0.30.1
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/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/index.min.js +2 -2
- package/dist/esm/index.min.js.map +4 -4
- package/dist/esm/transformers/ConditionOptimization.d.ts +16 -0
- package/dist/esm/transformers/ConditionOptimization.js +124 -2
- package/dist/esm/transformers/ConditionOptimization.js.map +1 -1
- package/dist/esm/transformers/ParameterConditionPlacementOptimizer.js +3 -71
- package/dist/esm/transformers/ParameterConditionPlacementOptimizer.js.map +1 -1
- package/dist/esm/transformers/PredicateExpressionUtils.d.ts +16 -0
- package/dist/esm/transformers/PredicateExpressionUtils.js +74 -0
- package/dist/esm/transformers/PredicateExpressionUtils.js.map +1 -0
- package/dist/esm/transformers/PredicateReachabilityAnalyzer.d.ts +92 -0
- package/dist/esm/transformers/PredicateReachabilityAnalyzer.js +516 -0
- package/dist/esm/transformers/PredicateReachabilityAnalyzer.js.map +1 -0
- package/dist/esm/transformers/StaticPredicatePlacementOptimizer.d.ts +2 -0
- package/dist/esm/transformers/StaticPredicatePlacementOptimizer.js +156 -162
- package/dist/esm/transformers/StaticPredicatePlacementOptimizer.js.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +2 -2
- package/dist/index.min.js.map +4 -4
- package/dist/src/index.d.ts +1 -0
- package/dist/src/transformers/ConditionOptimization.d.ts +16 -0
- package/dist/src/transformers/PredicateExpressionUtils.d.ts +16 -0
- package/dist/src/transformers/PredicateReachabilityAnalyzer.d.ts +92 -0
- package/dist/src/transformers/StaticPredicatePlacementOptimizer.d.ts +2 -0
- package/dist/transformers/ConditionOptimization.js +138 -2
- package/dist/transformers/ConditionOptimization.js.map +1 -1
- package/dist/transformers/ParameterConditionPlacementOptimizer.js +38 -106
- package/dist/transformers/ParameterConditionPlacementOptimizer.js.map +1 -1
- package/dist/transformers/PredicateExpressionUtils.js +88 -0
- package/dist/transformers/PredicateExpressionUtils.js.map +1 -0
- package/dist/transformers/PredicateReachabilityAnalyzer.js +537 -0
- package/dist/transformers/PredicateReachabilityAnalyzer.js.map +1 -0
- package/dist/transformers/StaticPredicatePlacementOptimizer.js +190 -196
- package/dist/transformers/StaticPredicatePlacementOptimizer.js.map +1 -1
- package/dist/tsconfig.browser.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -5,79 +5,11 @@ const Clause_1 = require("../models/Clause");
|
|
|
5
5
|
const SelectQuery_1 = require("../models/SelectQuery");
|
|
6
6
|
const ValueComponent_1 = require("../models/ValueComponent");
|
|
7
7
|
const SelectQueryParser_1 = require("../parsers/SelectQueryParser");
|
|
8
|
-
const ValueParser_1 = require("../parsers/ValueParser");
|
|
9
8
|
const SqlComponentFormatter_1 = require("./SqlComponentFormatter");
|
|
10
9
|
const SelectOutputCollector_1 = require("./SelectOutputCollector");
|
|
11
10
|
const TopLevelAndConditionDeduper_1 = require("./TopLevelAndConditionDeduper");
|
|
11
|
+
const PredicateExpressionUtils_1 = require("./PredicateExpressionUtils");
|
|
12
12
|
const VOLATILE_OR_UNSUPPORTED_FUNCTION_REASON = "Predicate contains a function call; volatile and expression predicates are not moved in the safe-only implementation.";
|
|
13
|
-
const normalizeIdentifier = (value) => value.trim().toLowerCase();
|
|
14
|
-
const isCaseSensitiveIdentifier = (value) => /[A-Z]/.test(value.trim());
|
|
15
|
-
const identifiersEqual = (left, right) => {
|
|
16
|
-
const trimmedLeft = left.trim();
|
|
17
|
-
const trimmedRight = right.trim();
|
|
18
|
-
return isCaseSensitiveIdentifier(trimmedLeft) || isCaseSensitiveIdentifier(trimmedRight)
|
|
19
|
-
? trimmedLeft === trimmedRight
|
|
20
|
-
: trimmedLeft.toLowerCase() === trimmedRight.toLowerCase();
|
|
21
|
-
};
|
|
22
|
-
const unwrapParens = (expression) => {
|
|
23
|
-
let candidate = expression;
|
|
24
|
-
while (candidate instanceof ValueComponent_1.ParenExpression) {
|
|
25
|
-
candidate = candidate.expression;
|
|
26
|
-
}
|
|
27
|
-
return candidate;
|
|
28
|
-
};
|
|
29
|
-
const isBinaryOperator = (expression, operator) => {
|
|
30
|
-
const candidate = unwrapParens(expression);
|
|
31
|
-
return candidate instanceof ValueComponent_1.BinaryExpression
|
|
32
|
-
&& candidate.operator.value.trim().toLowerCase() === operator;
|
|
33
|
-
};
|
|
34
|
-
const collectTopLevelAndTerms = (expression) => {
|
|
35
|
-
const candidate = unwrapParens(expression);
|
|
36
|
-
if (!isBinaryOperator(candidate, "and")) {
|
|
37
|
-
return [expression];
|
|
38
|
-
}
|
|
39
|
-
return [
|
|
40
|
-
...collectTopLevelAndTerms(candidate.left),
|
|
41
|
-
...collectTopLevelAndTerms(candidate.right)
|
|
42
|
-
];
|
|
43
|
-
};
|
|
44
|
-
const rebuildWhereWithoutTerms = (query, termsToRemove) => {
|
|
45
|
-
if (!query.whereClause || termsToRemove.size === 0) {
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
const remaining = collectTopLevelAndTerms(query.whereClause.condition)
|
|
49
|
-
.filter(term => !termsToRemove.has(term));
|
|
50
|
-
if (remaining.length === 0) {
|
|
51
|
-
query.whereClause = null;
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
let rebuilt = remaining[0];
|
|
55
|
-
for (let index = 1; index < remaining.length; index += 1) {
|
|
56
|
-
rebuilt = new ValueComponent_1.BinaryExpression(rebuilt, "and", remaining[index]);
|
|
57
|
-
}
|
|
58
|
-
query.whereClause = new Clause_1.WhereClause(rebuilt);
|
|
59
|
-
};
|
|
60
|
-
const cloneValueComponent = (expression, options) => {
|
|
61
|
-
return ValueParser_1.ValueParser.parse((0, SqlComponentFormatter_1.formatSqlComponent)(expression, options));
|
|
62
|
-
};
|
|
63
|
-
const cloneColumnReference = (reference) => {
|
|
64
|
-
var _a, _b;
|
|
65
|
-
const namespaces = (_b = (_a = reference.namespaces) === null || _a === void 0 ? void 0 : _a.map(namespace => namespace.name)) !== null && _b !== void 0 ? _b : null;
|
|
66
|
-
return new ValueComponent_1.ColumnReference(namespaces, reference.column.name);
|
|
67
|
-
};
|
|
68
|
-
const columnReferenceText = (reference) => {
|
|
69
|
-
const namespace = reference.getNamespace();
|
|
70
|
-
return namespace ? `${namespace}.${reference.column.name}` : reference.column.name;
|
|
71
|
-
};
|
|
72
|
-
const sameColumnReference = (left, right) => {
|
|
73
|
-
return identifiersEqual(left.column.name, right.column.name)
|
|
74
|
-
&& identifiersEqual(left.getNamespace(), right.getNamespace());
|
|
75
|
-
};
|
|
76
|
-
const appendUnique = (items, value) => {
|
|
77
|
-
if (!items.includes(value)) {
|
|
78
|
-
items.push(value);
|
|
79
|
-
}
|
|
80
|
-
};
|
|
81
13
|
class StaticPredicatePlacementOptimizer {
|
|
82
14
|
plan(input, options = {}) {
|
|
83
15
|
var _a, _b, _c;
|
|
@@ -115,77 +47,8 @@ class StaticPredicatePlacementOptimizer {
|
|
|
115
47
|
const query = parsed.query;
|
|
116
48
|
const applied = [];
|
|
117
49
|
const skipped = [];
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const candidate = this.analyzeCandidate(query, term, options);
|
|
121
|
-
if (!candidate) {
|
|
122
|
-
continue;
|
|
123
|
-
}
|
|
124
|
-
if ("code" in candidate) {
|
|
125
|
-
skipped.push(this.makeSkipped(term, candidate, options));
|
|
126
|
-
continue;
|
|
127
|
-
}
|
|
128
|
-
const target = this.resolveTarget(query, candidate);
|
|
129
|
-
if ("code" in target) {
|
|
130
|
-
skipped.push(this.makeSkipped(term, target, options));
|
|
131
|
-
continue;
|
|
132
|
-
}
|
|
133
|
-
let appliedReason = "";
|
|
134
|
-
if (target.kind === "join_on") {
|
|
135
|
-
this.appendJoinOnPredicate(target.join, candidate.expression, options);
|
|
136
|
-
appliedReason = target.reason;
|
|
137
|
-
}
|
|
138
|
-
else if (target.kind === "simple") {
|
|
139
|
-
const targetColumns = this.resolveTargetColumns(query, target.query, candidate.references);
|
|
140
|
-
if ("code" in targetColumns) {
|
|
141
|
-
skipped.push(this.makeSkipped(term, targetColumns, options));
|
|
142
|
-
continue;
|
|
143
|
-
}
|
|
144
|
-
const placement = this.resolveTargetPlacement(target.query, targetColumns);
|
|
145
|
-
if ("code" in placement) {
|
|
146
|
-
skipped.push(this.makeSkipped(term, placement, options));
|
|
147
|
-
continue;
|
|
148
|
-
}
|
|
149
|
-
const movedPredicate = this.rebasePredicate(candidate.expression, targetColumns, options);
|
|
150
|
-
target.query.appendWhere(movedPredicate);
|
|
151
|
-
(0, TopLevelAndConditionDeduper_1.dedupeWhereTopLevelAndConditions)(target.query, options);
|
|
152
|
-
appliedReason = placement.reason;
|
|
153
|
-
}
|
|
154
|
-
else {
|
|
155
|
-
const placements = [];
|
|
156
|
-
let skip = null;
|
|
157
|
-
for (const branch of target.branches) {
|
|
158
|
-
const placement = this.resolveTargetPlacement(branch.query, branch.targetColumns);
|
|
159
|
-
if ("code" in placement) {
|
|
160
|
-
skip = placement;
|
|
161
|
-
break;
|
|
162
|
-
}
|
|
163
|
-
placements.push(placement);
|
|
164
|
-
}
|
|
165
|
-
if (skip) {
|
|
166
|
-
skipped.push(this.makeSkipped(term, skip, options));
|
|
167
|
-
continue;
|
|
168
|
-
}
|
|
169
|
-
for (const branch of target.branches) {
|
|
170
|
-
const movedPredicate = this.rebasePredicate(candidate.expression, branch.targetColumns, options);
|
|
171
|
-
branch.query.appendWhere(movedPredicate);
|
|
172
|
-
(0, TopLevelAndConditionDeduper_1.dedupeWhereTopLevelAndConditions)(branch.query, options);
|
|
173
|
-
}
|
|
174
|
-
appliedReason = placements.some(item => /group by/i.test(item.reason))
|
|
175
|
-
? "Predicate is distributed to every UNION branch by output column position; grouped branches only receive GROUP BY-key predicates."
|
|
176
|
-
: "Predicate is distributed to every UNION branch by output column position before unsafe query boundaries.";
|
|
177
|
-
}
|
|
178
|
-
movedTerms.push(term);
|
|
179
|
-
applied.push({
|
|
180
|
-
kind: "move_static_predicate",
|
|
181
|
-
predicateSql: candidate.predicateSql,
|
|
182
|
-
fromScopeId: "scope:root",
|
|
183
|
-
toScopeId: target.scopeId,
|
|
184
|
-
reason: appliedReason,
|
|
185
|
-
columnReferences: candidate.references.map(columnReferenceText)
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
rebuildWhereWithoutTerms(query, new Set(movedTerms));
|
|
50
|
+
this.placePredicatesInScopes(query, options, applied, skipped);
|
|
51
|
+
// API output shape review: keep result.sql/result.query compatibility while expanding scope-aware placement.
|
|
189
52
|
const sql = applied.length > 0 || (0, SqlComponentFormatter_1.hasSqlComponentFormatOverride)(options)
|
|
190
53
|
? (0, SqlComponentFormatter_1.formatSqlComponent)(query, options)
|
|
191
54
|
: parsed.sql;
|
|
@@ -200,6 +63,121 @@ class StaticPredicatePlacementOptimizer {
|
|
|
200
63
|
formatterGeneratedSource: parsed.formatterGeneratedSource
|
|
201
64
|
});
|
|
202
65
|
}
|
|
66
|
+
placePredicatesInScopes(contextRoot, options, applied, skipped) {
|
|
67
|
+
var _a;
|
|
68
|
+
const queue = [{ query: contextRoot, scopeId: "scope:root" }];
|
|
69
|
+
let processed = 0;
|
|
70
|
+
while (queue.length > 0) {
|
|
71
|
+
processed += 1;
|
|
72
|
+
if (processed > 1000) {
|
|
73
|
+
skipped.push({
|
|
74
|
+
predicateSql: "",
|
|
75
|
+
scopeId: "scope:root",
|
|
76
|
+
code: "RECURSIVE_PLACEMENT_LIMIT",
|
|
77
|
+
reason: "Static predicate recursive placement stopped after reaching the safety iteration limit."
|
|
78
|
+
});
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const work = queue.shift();
|
|
82
|
+
const movedTerms = [];
|
|
83
|
+
const reportSkipped = work.pendingTerms === undefined;
|
|
84
|
+
const terms = (_a = work.pendingTerms) !== null && _a !== void 0 ? _a : (work.query.whereClause
|
|
85
|
+
? (0, PredicateExpressionUtils_1.collectTopLevelAndTerms)(work.query.whereClause.condition)
|
|
86
|
+
: []);
|
|
87
|
+
for (const term of terms) {
|
|
88
|
+
const candidate = this.analyzeCandidate(work.query, term, options);
|
|
89
|
+
if (!candidate) {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if ("code" in candidate) {
|
|
93
|
+
if (reportSkipped) {
|
|
94
|
+
skipped.push(this.makeSkipped(term, work.scopeId, candidate, options));
|
|
95
|
+
}
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const target = this.resolveTarget(contextRoot, work.query, candidate);
|
|
99
|
+
if ("code" in target) {
|
|
100
|
+
if (reportSkipped) {
|
|
101
|
+
skipped.push(this.makeSkipped(term, work.scopeId, target, options));
|
|
102
|
+
}
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
let appliedReason = "";
|
|
106
|
+
if (target.kind === "join_on") {
|
|
107
|
+
this.appendJoinOnPredicate(target.join, candidate.expression, options);
|
|
108
|
+
appliedReason = target.reason;
|
|
109
|
+
}
|
|
110
|
+
else if (target.kind === "simple") {
|
|
111
|
+
const targetColumns = this.resolveTargetColumns(contextRoot, target.query, candidate.references);
|
|
112
|
+
if ("code" in targetColumns) {
|
|
113
|
+
if (reportSkipped) {
|
|
114
|
+
skipped.push(this.makeSkipped(term, work.scopeId, targetColumns, options));
|
|
115
|
+
}
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
const placement = this.resolveTargetPlacement(contextRoot, target.query, targetColumns);
|
|
119
|
+
if ("code" in placement) {
|
|
120
|
+
if (reportSkipped) {
|
|
121
|
+
skipped.push(this.makeSkipped(term, work.scopeId, placement, options));
|
|
122
|
+
}
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
const movedPredicate = this.rebasePredicate(candidate.expression, targetColumns, options);
|
|
126
|
+
target.query.appendWhere(movedPredicate);
|
|
127
|
+
(0, TopLevelAndConditionDeduper_1.dedupeWhereTopLevelAndConditions)(target.query, options);
|
|
128
|
+
appliedReason = work.scopeId === "scope:root"
|
|
129
|
+
? placement.reason
|
|
130
|
+
: `${placement.reason} Predicate was safely rebased from a previously moved predicate.`;
|
|
131
|
+
const retainedTerms = target.query.whereClause
|
|
132
|
+
? (0, PredicateExpressionUtils_1.collectTopLevelAndTerms)(target.query.whereClause.condition)
|
|
133
|
+
: [];
|
|
134
|
+
if (retainedTerms.includes(movedPredicate)) {
|
|
135
|
+
queue.push({
|
|
136
|
+
query: target.query,
|
|
137
|
+
scopeId: target.scopeId,
|
|
138
|
+
pendingTerms: [movedPredicate]
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
const placements = [];
|
|
144
|
+
let skip = null;
|
|
145
|
+
for (const branch of target.branches) {
|
|
146
|
+
const placement = this.resolveTargetPlacement(contextRoot, branch.query, branch.targetColumns);
|
|
147
|
+
if ("code" in placement) {
|
|
148
|
+
skip = placement;
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
placements.push(placement);
|
|
152
|
+
}
|
|
153
|
+
if (skip) {
|
|
154
|
+
if (reportSkipped) {
|
|
155
|
+
skipped.push(this.makeSkipped(term, work.scopeId, skip, options));
|
|
156
|
+
}
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
for (const branch of target.branches) {
|
|
160
|
+
const movedPredicate = this.rebasePredicate(candidate.expression, branch.targetColumns, options);
|
|
161
|
+
branch.query.appendWhere(movedPredicate);
|
|
162
|
+
(0, TopLevelAndConditionDeduper_1.dedupeWhereTopLevelAndConditions)(branch.query, options);
|
|
163
|
+
}
|
|
164
|
+
appliedReason = placements.some(item => /group by/i.test(item.reason))
|
|
165
|
+
? "Predicate is distributed to every UNION branch by output column position; grouped branches only receive GROUP BY-key predicates."
|
|
166
|
+
: "Predicate is distributed to every UNION branch by output column position before unsafe query boundaries.";
|
|
167
|
+
}
|
|
168
|
+
movedTerms.push(term);
|
|
169
|
+
applied.push({
|
|
170
|
+
kind: "move_static_predicate",
|
|
171
|
+
predicateSql: candidate.predicateSql,
|
|
172
|
+
fromScopeId: work.scopeId,
|
|
173
|
+
toScopeId: target.scopeId,
|
|
174
|
+
reason: appliedReason,
|
|
175
|
+
columnReferences: candidate.references.map(PredicateExpressionUtils_1.columnReferenceText)
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
(0, PredicateExpressionUtils_1.rebuildWhereWithoutTerms)(work.query, new Set(movedTerms));
|
|
179
|
+
}
|
|
180
|
+
}
|
|
203
181
|
optimize(input, options = {}) {
|
|
204
182
|
var _a;
|
|
205
183
|
return this.plan(input, { ...options, dryRun: (_a = options.dryRun) !== null && _a !== void 0 ? _a : false });
|
|
@@ -272,12 +250,12 @@ class StaticPredicatePlacementOptimizer {
|
|
|
272
250
|
};
|
|
273
251
|
}
|
|
274
252
|
isExistsPredicate(expression) {
|
|
275
|
-
const candidate = unwrapParens(expression);
|
|
253
|
+
const candidate = (0, PredicateExpressionUtils_1.unwrapParens)(expression);
|
|
276
254
|
if (!(candidate instanceof ValueComponent_1.UnaryExpression)) {
|
|
277
255
|
return false;
|
|
278
256
|
}
|
|
279
257
|
const operator = candidate.operator.value.trim().toLowerCase();
|
|
280
|
-
return operator === "exists" && unwrapParens(candidate.expression) instanceof ValueComponent_1.InlineQuery;
|
|
258
|
+
return operator === "exists" && (0, PredicateExpressionUtils_1.unwrapParens)(candidate.expression) instanceof ValueComponent_1.InlineQuery;
|
|
281
259
|
}
|
|
282
260
|
findUnsupportedExpression(expression, allowExistsSubquery) {
|
|
283
261
|
let found = null;
|
|
@@ -326,7 +304,7 @@ class StaticPredicatePlacementOptimizer {
|
|
|
326
304
|
if (found) {
|
|
327
305
|
return;
|
|
328
306
|
}
|
|
329
|
-
const candidate = unwrapParens(value);
|
|
307
|
+
const candidate = (0, PredicateExpressionUtils_1.unwrapParens)(value);
|
|
330
308
|
if (candidate instanceof ValueComponent_1.BinaryExpression) {
|
|
331
309
|
visit(candidate.left);
|
|
332
310
|
visit(candidate.right);
|
|
@@ -395,13 +373,13 @@ class StaticPredicatePlacementOptimizer {
|
|
|
395
373
|
visit(expression);
|
|
396
374
|
return found;
|
|
397
375
|
}
|
|
398
|
-
resolveTarget(
|
|
376
|
+
resolveTarget(contextRoot, currentQuery, candidate) {
|
|
399
377
|
var _a;
|
|
400
|
-
const boundary = this.findRootQueryBoundary(
|
|
378
|
+
const boundary = this.findRootQueryBoundary(currentQuery);
|
|
401
379
|
if (boundary) {
|
|
402
380
|
return boundary;
|
|
403
381
|
}
|
|
404
|
-
if (!
|
|
382
|
+
if (!currentQuery.fromClause) {
|
|
405
383
|
return {
|
|
406
384
|
code: "NO_FROM_CLAUSE",
|
|
407
385
|
reason: "Predicate has no FROM source that can receive it safely."
|
|
@@ -409,7 +387,7 @@ class StaticPredicatePlacementOptimizer {
|
|
|
409
387
|
}
|
|
410
388
|
const bindings = [];
|
|
411
389
|
for (const reference of candidate.references) {
|
|
412
|
-
const binding = this.resolveSourceBinding(
|
|
390
|
+
const binding = this.resolveSourceBinding(contextRoot, currentQuery, reference);
|
|
413
391
|
if ("code" in binding) {
|
|
414
392
|
return binding;
|
|
415
393
|
}
|
|
@@ -430,14 +408,14 @@ class StaticPredicatePlacementOptimizer {
|
|
|
430
408
|
reason: "Predicate crosses a LATERAL JOIN boundary; moving it may change semantics."
|
|
431
409
|
};
|
|
432
410
|
}
|
|
433
|
-
const nullableSide = this.findNullableSideBoundary(
|
|
411
|
+
const nullableSide = this.findNullableSideBoundary(currentQuery.fromClause, binding);
|
|
434
412
|
if (nullableSide) {
|
|
435
413
|
return nullableSide;
|
|
436
414
|
}
|
|
437
|
-
const upstream = this.resolveUpstreamQuery(
|
|
415
|
+
const upstream = this.resolveUpstreamQuery(contextRoot, binding, candidate.references);
|
|
438
416
|
if ("code" in upstream) {
|
|
439
417
|
if (upstream.code === "NO_SAFE_UPSTREAM_QUERY") {
|
|
440
|
-
const joinOnTarget = this.resolveBaseTableJoinOnTarget(
|
|
418
|
+
const joinOnTarget = this.resolveBaseTableJoinOnTarget(contextRoot, currentQuery, binding);
|
|
441
419
|
if (!("code" in joinOnTarget)) {
|
|
442
420
|
return joinOnTarget;
|
|
443
421
|
}
|
|
@@ -462,7 +440,7 @@ class StaticPredicatePlacementOptimizer {
|
|
|
462
440
|
}
|
|
463
441
|
return null;
|
|
464
442
|
}
|
|
465
|
-
resolveTargetPlacement(query, targetColumns) {
|
|
443
|
+
resolveTargetPlacement(contextRoot, query, targetColumns) {
|
|
466
444
|
const hasOrdinaryDistinct = this.hasOrdinaryDistinct(query);
|
|
467
445
|
if (this.hasDistinctOnBoundary(query)) {
|
|
468
446
|
return {
|
|
@@ -482,11 +460,11 @@ class StaticPredicatePlacementOptimizer {
|
|
|
482
460
|
reason: "Predicate crosses LIMIT/OFFSET/FETCH boundary; moving it may change row selection semantics."
|
|
483
461
|
};
|
|
484
462
|
}
|
|
485
|
-
if (query.fromClause
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
}
|
|
463
|
+
if (query.fromClause) {
|
|
464
|
+
const nullableSide = this.findNullableSideBoundaryForTargetColumns(contextRoot, query, targetColumns);
|
|
465
|
+
if (nullableSide) {
|
|
466
|
+
return nullableSide;
|
|
467
|
+
}
|
|
490
468
|
}
|
|
491
469
|
if (query.groupByClause) {
|
|
492
470
|
const allReferencesAreGroupKeys = targetColumns.every(item => this.isGroupKeyColumn(query, item.targetColumn));
|
|
@@ -515,6 +493,22 @@ class StaticPredicatePlacementOptimizer {
|
|
|
515
493
|
reason: "All outer references resolve to direct upstream outputs before unsafe query boundaries."
|
|
516
494
|
};
|
|
517
495
|
}
|
|
496
|
+
findNullableSideBoundaryForTargetColumns(contextRoot, query, targetColumns) {
|
|
497
|
+
if (!query.fromClause) {
|
|
498
|
+
return null;
|
|
499
|
+
}
|
|
500
|
+
for (const item of targetColumns) {
|
|
501
|
+
const binding = this.resolveSourceBinding(contextRoot, query, item.targetColumn);
|
|
502
|
+
if ("code" in binding) {
|
|
503
|
+
return binding;
|
|
504
|
+
}
|
|
505
|
+
const nullableSide = this.findNullableSideBoundary(query.fromClause, binding);
|
|
506
|
+
if (nullableSide) {
|
|
507
|
+
return nullableSide;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
return null;
|
|
511
|
+
}
|
|
518
512
|
resolveSourceBinding(contextRoot, query, column) {
|
|
519
513
|
const fromClause = query.fromClause;
|
|
520
514
|
if (!fromClause) {
|
|
@@ -527,7 +521,7 @@ class StaticPredicatePlacementOptimizer {
|
|
|
527
521
|
const namespace = column.getNamespace();
|
|
528
522
|
const columnName = column.column.name;
|
|
529
523
|
if (namespace) {
|
|
530
|
-
const matches = bindings.filter(binding => identifiersEqual(binding.alias, namespace));
|
|
524
|
+
const matches = bindings.filter(binding => (0, PredicateExpressionUtils_1.identifiersEqual)(binding.alias, namespace));
|
|
531
525
|
if (matches.length !== 1) {
|
|
532
526
|
return {
|
|
533
527
|
code: "AMBIGUOUS_COLUMN_SOURCE",
|
|
@@ -541,13 +535,13 @@ class StaticPredicatePlacementOptimizer {
|
|
|
541
535
|
if (matchCount === 0) {
|
|
542
536
|
return {
|
|
543
537
|
code: "COLUMN_NOT_AVAILABLE_UPSTREAM",
|
|
544
|
-
reason: `Column '${columnReferenceText(column)}' is not a direct output of the referenced source.`
|
|
538
|
+
reason: `Column '${(0, PredicateExpressionUtils_1.columnReferenceText)(column)}' is not a direct output of the referenced source.`
|
|
545
539
|
};
|
|
546
540
|
}
|
|
547
541
|
if (matchCount > 1) {
|
|
548
542
|
return {
|
|
549
543
|
code: "AMBIGUOUS_COLUMN_REFERENCE",
|
|
550
|
-
reason: `Column '${columnReferenceText(column)}' resolves to multiple outputs in the referenced source.`
|
|
544
|
+
reason: `Column '${(0, PredicateExpressionUtils_1.columnReferenceText)(column)}' resolves to multiple outputs in the referenced source.`
|
|
551
545
|
};
|
|
552
546
|
}
|
|
553
547
|
return matches[0];
|
|
@@ -693,22 +687,22 @@ class StaticPredicatePlacementOptimizer {
|
|
|
693
687
|
}
|
|
694
688
|
return resolved;
|
|
695
689
|
}
|
|
696
|
-
resolveBaseTableJoinOnTarget(
|
|
690
|
+
resolveBaseTableJoinOnTarget(contextRoot, currentQuery, binding) {
|
|
697
691
|
var _a, _b, _c;
|
|
698
|
-
if (!
|
|
692
|
+
if (!currentQuery.fromClause || !this.isBaseTableBinding(contextRoot, binding)) {
|
|
699
693
|
return {
|
|
700
694
|
code: "NO_SAFE_UPSTREAM_QUERY",
|
|
701
695
|
reason: "The referenced source is a base table, so there is no upstream query block to move into."
|
|
702
696
|
};
|
|
703
697
|
}
|
|
704
|
-
if (this.hasOuterJoin(
|
|
698
|
+
if (this.hasOuterJoin(currentQuery.fromClause)) {
|
|
705
699
|
return {
|
|
706
700
|
code: "OUTER_JOIN_BOUNDARY",
|
|
707
701
|
reason: "Predicate crosses an OUTER JOIN boundary; moving it into JOIN ON may change semantics."
|
|
708
702
|
};
|
|
709
703
|
}
|
|
710
704
|
const join = binding.isPrimary
|
|
711
|
-
? (_b = (_a =
|
|
705
|
+
? (_b = (_a = currentQuery.fromClause.joins) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : null
|
|
712
706
|
: binding.join;
|
|
713
707
|
if (join === null || join === void 0 ? void 0 : join.lateral) {
|
|
714
708
|
return {
|
|
@@ -741,7 +735,7 @@ class StaticPredicatePlacementOptimizer {
|
|
|
741
735
|
if (!(join.condition instanceof Clause_1.JoinOnClause)) {
|
|
742
736
|
return;
|
|
743
737
|
}
|
|
744
|
-
join.condition.condition = new ValueComponent_1.BinaryExpression(join.condition.condition, "and", cloneValueComponent(expression, options));
|
|
738
|
+
join.condition.condition = new ValueComponent_1.BinaryExpression(join.condition.condition, "and", (0, PredicateExpressionUtils_1.cloneValueComponent)(expression, options));
|
|
745
739
|
join.condition.condition = (0, TopLevelAndConditionDeduper_1.dedupeTopLevelAndConditions)(join.condition.condition, options);
|
|
746
740
|
}
|
|
747
741
|
resolveDeepestBranchTarget(contextRoot, query, targetColumns, visited = new Set()) {
|
|
@@ -781,7 +775,7 @@ class StaticPredicatePlacementOptimizer {
|
|
|
781
775
|
if ("code" in upstreamColumns) {
|
|
782
776
|
return { query, targetColumns: [...targetColumns] };
|
|
783
777
|
}
|
|
784
|
-
const placement = this.resolveTargetPlacement(upstream.query, upstreamColumns);
|
|
778
|
+
const placement = this.resolveTargetPlacement(contextRoot, upstream.query, upstreamColumns);
|
|
785
779
|
if ("code" in placement) {
|
|
786
780
|
return { query, targetColumns: [...targetColumns] };
|
|
787
781
|
}
|
|
@@ -801,12 +795,12 @@ class StaticPredicatePlacementOptimizer {
|
|
|
801
795
|
const bindings = this.getSourceBindings(query.fromClause);
|
|
802
796
|
const namespace = column.getNamespace();
|
|
803
797
|
if (namespace) {
|
|
804
|
-
const matches = bindings.filter(binding => identifiersEqual(binding.alias, namespace));
|
|
798
|
+
const matches = bindings.filter(binding => (0, PredicateExpressionUtils_1.identifiersEqual)(binding.alias, namespace));
|
|
805
799
|
return matches.length === 1
|
|
806
800
|
? null
|
|
807
801
|
: {
|
|
808
802
|
code: "AMBIGUOUS_TARGET_COLUMN",
|
|
809
|
-
reason: `Target column '${columnReferenceText(column)}' is not uniquely resolvable in the destination query.`
|
|
803
|
+
reason: `Target column '${(0, PredicateExpressionUtils_1.columnReferenceText)(column)}' is not uniquely resolvable in the destination query.`
|
|
810
804
|
};
|
|
811
805
|
}
|
|
812
806
|
if (bindings.length === 1) {
|
|
@@ -823,16 +817,16 @@ class StaticPredicatePlacementOptimizer {
|
|
|
823
817
|
return false;
|
|
824
818
|
}
|
|
825
819
|
return groupBy.grouping.some(grouping => {
|
|
826
|
-
const candidate = unwrapParens(grouping);
|
|
820
|
+
const candidate = (0, PredicateExpressionUtils_1.unwrapParens)(grouping);
|
|
827
821
|
return candidate instanceof ValueComponent_1.ColumnReference
|
|
828
822
|
&& this.sameResolvableColumnInQuery(query, candidate, column);
|
|
829
823
|
});
|
|
830
824
|
}
|
|
831
825
|
sameResolvableColumnInQuery(query, left, right) {
|
|
832
|
-
if (sameColumnReference(left, right)) {
|
|
826
|
+
if ((0, PredicateExpressionUtils_1.sameColumnReference)(left, right)) {
|
|
833
827
|
return true;
|
|
834
828
|
}
|
|
835
|
-
if (!identifiersEqual(left.column.name, right.column.name)) {
|
|
829
|
+
if (!(0, PredicateExpressionUtils_1.identifiersEqual)(left.column.name, right.column.name)) {
|
|
836
830
|
return false;
|
|
837
831
|
}
|
|
838
832
|
const leftSource = this.resolveColumnSourceAlias(query, left);
|
|
@@ -846,13 +840,13 @@ class StaticPredicatePlacementOptimizer {
|
|
|
846
840
|
const bindings = this.getSourceBindings(query.fromClause);
|
|
847
841
|
const namespace = column.getNamespace();
|
|
848
842
|
if (namespace) {
|
|
849
|
-
const matches = bindings.filter(binding => identifiersEqual(binding.alias, namespace));
|
|
843
|
+
const matches = bindings.filter(binding => (0, PredicateExpressionUtils_1.identifiersEqual)(binding.alias, namespace));
|
|
850
844
|
return matches.length === 1 ? matches[0].alias : null;
|
|
851
845
|
}
|
|
852
846
|
return bindings.length === 1 ? bindings[0].alias : null;
|
|
853
847
|
}
|
|
854
848
|
rebasePredicate(expression, targetColumns, options) {
|
|
855
|
-
const cloned = cloneValueComponent(expression, options);
|
|
849
|
+
const cloned = (0, PredicateExpressionUtils_1.cloneValueComponent)(expression, options);
|
|
856
850
|
const visitSelect = (query, inheritedLocalAliases) => {
|
|
857
851
|
var _a, _b, _c, _d;
|
|
858
852
|
if (!(query instanceof SelectQuery_1.SimpleSelectQuery)) {
|
|
@@ -885,15 +879,15 @@ class StaticPredicatePlacementOptimizer {
|
|
|
885
879
|
}
|
|
886
880
|
};
|
|
887
881
|
const visit = (value, localAliases) => {
|
|
888
|
-
const candidate = unwrapParens(value);
|
|
882
|
+
const candidate = (0, PredicateExpressionUtils_1.unwrapParens)(value);
|
|
889
883
|
if (candidate instanceof ValueComponent_1.ColumnReference) {
|
|
890
|
-
const namespace = normalizeIdentifier(candidate.getNamespace());
|
|
884
|
+
const namespace = (0, PredicateExpressionUtils_1.normalizeIdentifier)(candidate.getNamespace());
|
|
891
885
|
if (namespace && localAliases.has(namespace)) {
|
|
892
886
|
return;
|
|
893
887
|
}
|
|
894
|
-
const target = targetColumns.find(item => sameColumnReference(candidate, item.sourceColumn));
|
|
888
|
+
const target = targetColumns.find(item => (0, PredicateExpressionUtils_1.sameColumnReference)(candidate, item.sourceColumn));
|
|
895
889
|
if (target) {
|
|
896
|
-
candidate.qualifiedName = cloneColumnReference(target.targetColumn).qualifiedName;
|
|
890
|
+
candidate.qualifiedName = (0, PredicateExpressionUtils_1.cloneColumnReference)(target.targetColumn).qualifiedName;
|
|
897
891
|
}
|
|
898
892
|
return;
|
|
899
893
|
}
|
|
@@ -1097,7 +1091,7 @@ class StaticPredicatePlacementOptimizer {
|
|
|
1097
1091
|
resolveUnionOutputIndex(root, firstBranch, outputColumnName) {
|
|
1098
1092
|
const matches = [];
|
|
1099
1093
|
this.collectSelectOutputs(root, firstBranch).forEach((item, index) => {
|
|
1100
|
-
if (identifiersEqual(item.name, outputColumnName)) {
|
|
1094
|
+
if ((0, PredicateExpressionUtils_1.identifiersEqual)(item.name, outputColumnName)) {
|
|
1101
1095
|
matches.push(index);
|
|
1102
1096
|
}
|
|
1103
1097
|
});
|
|
@@ -1119,7 +1113,7 @@ class StaticPredicatePlacementOptimizer {
|
|
|
1119
1113
|
reason: `UNION branch does not expose column '${sourceColumn.column.name}' at output position ${outputIndex + 1}.`
|
|
1120
1114
|
};
|
|
1121
1115
|
}
|
|
1122
|
-
else if (identifiersEqual(output.name, sourceColumn.column.name)) {
|
|
1116
|
+
else if ((0, PredicateExpressionUtils_1.identifiersEqual)(output.name, sourceColumn.column.name)) {
|
|
1123
1117
|
const matches = this.collectDirectOutputMatches(root, query, sourceColumn.column.name);
|
|
1124
1118
|
if (matches.length > 1) {
|
|
1125
1119
|
return {
|
|
@@ -1153,7 +1147,7 @@ class StaticPredicatePlacementOptimizer {
|
|
|
1153
1147
|
return collector.collect(query);
|
|
1154
1148
|
}
|
|
1155
1149
|
collectDirectOutputMatches(root, query, columnName) {
|
|
1156
|
-
const collectedMatches = this.collectSelectOutputs(root, query).filter(item => identifiersEqual(item.name, columnName));
|
|
1150
|
+
const collectedMatches = this.collectSelectOutputs(root, query).filter(item => (0, PredicateExpressionUtils_1.identifiersEqual)(item.name, columnName));
|
|
1157
1151
|
if (collectedMatches.length > 0) {
|
|
1158
1152
|
return this.hasExplicitOutputMatch(query, columnName)
|
|
1159
1153
|
? [...collectedMatches, ...this.inferWildcardOutputMatches(root, query, columnName, true)]
|
|
@@ -1164,11 +1158,11 @@ class StaticPredicatePlacementOptimizer {
|
|
|
1164
1158
|
hasExplicitOutputMatch(query, columnName) {
|
|
1165
1159
|
return query.selectClause.items.some(item => {
|
|
1166
1160
|
if (item.identifier) {
|
|
1167
|
-
return identifiersEqual(item.identifier.name, columnName);
|
|
1161
|
+
return (0, PredicateExpressionUtils_1.identifiersEqual)(item.identifier.name, columnName);
|
|
1168
1162
|
}
|
|
1169
1163
|
return item.value instanceof ValueComponent_1.ColumnReference
|
|
1170
1164
|
&& item.value.column.name !== "*"
|
|
1171
|
-
&& identifiersEqual(item.value.column.name, columnName);
|
|
1165
|
+
&& (0, PredicateExpressionUtils_1.identifiersEqual)(item.value.column.name, columnName);
|
|
1172
1166
|
});
|
|
1173
1167
|
}
|
|
1174
1168
|
inferWildcardOutputMatches(root, query, columnName, includeUnprovenPotential) {
|
|
@@ -1201,7 +1195,7 @@ class StaticPredicatePlacementOptimizer {
|
|
|
1201
1195
|
return this.resolveWildcardColumnFromBinding(root, query, bindings[0], columnName, includeUnprovenPotential);
|
|
1202
1196
|
}
|
|
1203
1197
|
const namespace = wildcard.getNamespace();
|
|
1204
|
-
const matches = bindings.filter(binding => identifiersEqual(binding.alias, namespace));
|
|
1198
|
+
const matches = bindings.filter(binding => (0, PredicateExpressionUtils_1.identifiersEqual)(binding.alias, namespace));
|
|
1205
1199
|
if (matches.length === 0) {
|
|
1206
1200
|
return null;
|
|
1207
1201
|
}
|
|
@@ -1227,7 +1221,7 @@ class StaticPredicatePlacementOptimizer {
|
|
|
1227
1221
|
...((_d = (_c = root.withClause) === null || _c === void 0 ? void 0 : _c.tables) !== null && _d !== void 0 ? _d : [])
|
|
1228
1222
|
];
|
|
1229
1223
|
const collector = new SelectOutputCollector_1.SelectOutputCollector(null, commonTables.length > 0 ? commonTables : null);
|
|
1230
|
-
return collector.collect(source).filter(item => identifiersEqual(item.name, columnName));
|
|
1224
|
+
return collector.collect(source).filter(item => (0, PredicateExpressionUtils_1.identifiersEqual)(item.name, columnName));
|
|
1231
1225
|
}
|
|
1232
1226
|
createColumnForBinding(binding, columnName) {
|
|
1233
1227
|
return new ValueComponent_1.ColumnReference(binding.alias ? [binding.alias] : null, columnName);
|
|
@@ -1257,13 +1251,13 @@ class StaticPredicatePlacementOptimizer {
|
|
|
1257
1251
|
}
|
|
1258
1252
|
findCte(root, name) {
|
|
1259
1253
|
var _a, _b;
|
|
1260
|
-
const normalized = normalizeIdentifier(name);
|
|
1254
|
+
const normalized = (0, PredicateExpressionUtils_1.normalizeIdentifier)(name);
|
|
1261
1255
|
const matches = ((_b = (_a = root.withClause) === null || _a === void 0 ? void 0 : _a.tables) !== null && _b !== void 0 ? _b : [])
|
|
1262
|
-
.filter(table => normalizeIdentifier(table.getSourceAliasName()) === normalized);
|
|
1256
|
+
.filter(table => (0, PredicateExpressionUtils_1.normalizeIdentifier)(table.getSourceAliasName()) === normalized);
|
|
1263
1257
|
return matches.length === 1 ? matches[0] : null;
|
|
1264
1258
|
}
|
|
1265
1259
|
countTableSourceReferences(query, tableName) {
|
|
1266
|
-
const normalized = normalizeIdentifier(tableName);
|
|
1260
|
+
const normalized = (0, PredicateExpressionUtils_1.normalizeIdentifier)(tableName);
|
|
1267
1261
|
let count = 0;
|
|
1268
1262
|
const visitSelect = (select) => {
|
|
1269
1263
|
var _a, _b;
|
|
@@ -1278,7 +1272,7 @@ class StaticPredicatePlacementOptimizer {
|
|
|
1278
1272
|
if (select.fromClause) {
|
|
1279
1273
|
for (const binding of this.getSourceBindings(select.fromClause)) {
|
|
1280
1274
|
const source = binding.source.datasource;
|
|
1281
|
-
if (source instanceof Clause_1.TableSource && normalizeIdentifier(source.table.name) === normalized) {
|
|
1275
|
+
if (source instanceof Clause_1.TableSource && (0, PredicateExpressionUtils_1.normalizeIdentifier)(source.table.name) === normalized) {
|
|
1282
1276
|
count += 1;
|
|
1283
1277
|
}
|
|
1284
1278
|
if (source instanceof Clause_1.SubQuerySource) {
|
|
@@ -1301,7 +1295,7 @@ class StaticPredicatePlacementOptimizer {
|
|
|
1301
1295
|
for (const source of (_b = (_a = query.fromClause) === null || _a === void 0 ? void 0 : _a.getSources()) !== null && _b !== void 0 ? _b : []) {
|
|
1302
1296
|
const alias = source.getAliasName();
|
|
1303
1297
|
if (alias) {
|
|
1304
|
-
aliases.add(normalizeIdentifier(alias));
|
|
1298
|
+
aliases.add((0, PredicateExpressionUtils_1.normalizeIdentifier)(alias));
|
|
1305
1299
|
}
|
|
1306
1300
|
}
|
|
1307
1301
|
return aliases;
|
|
@@ -1341,14 +1335,14 @@ class StaticPredicatePlacementOptimizer {
|
|
|
1341
1335
|
}
|
|
1342
1336
|
};
|
|
1343
1337
|
const collect = (reference) => {
|
|
1344
|
-
if (!references.some(existing => sameColumnReference(existing, reference))) {
|
|
1338
|
+
if (!references.some(existing => (0, PredicateExpressionUtils_1.sameColumnReference)(existing, reference))) {
|
|
1345
1339
|
references.push(reference);
|
|
1346
1340
|
}
|
|
1347
1341
|
};
|
|
1348
1342
|
const visit = (value, localAliases) => {
|
|
1349
|
-
const candidate = unwrapParens(value);
|
|
1343
|
+
const candidate = (0, PredicateExpressionUtils_1.unwrapParens)(value);
|
|
1350
1344
|
if (candidate instanceof ValueComponent_1.ColumnReference) {
|
|
1351
|
-
const namespace = normalizeIdentifier(candidate.getNamespace());
|
|
1345
|
+
const namespace = (0, PredicateExpressionUtils_1.normalizeIdentifier)(candidate.getNamespace());
|
|
1352
1346
|
if (namespace) {
|
|
1353
1347
|
if (rootAliases.has(namespace) && !localAliases.has(namespace)) {
|
|
1354
1348
|
collect(candidate);
|
|
@@ -1441,7 +1435,7 @@ class StaticPredicatePlacementOptimizer {
|
|
|
1441
1435
|
if (found) {
|
|
1442
1436
|
return;
|
|
1443
1437
|
}
|
|
1444
|
-
const candidate = unwrapParens(value);
|
|
1438
|
+
const candidate = (0, PredicateExpressionUtils_1.unwrapParens)(value);
|
|
1445
1439
|
if (candidate instanceof ValueComponent_1.FunctionCall) {
|
|
1446
1440
|
if (candidate.over) {
|
|
1447
1441
|
found = true;
|
|
@@ -1523,9 +1517,9 @@ class StaticPredicatePlacementOptimizer {
|
|
|
1523
1517
|
}
|
|
1524
1518
|
};
|
|
1525
1519
|
const visit = (value) => {
|
|
1526
|
-
const candidate = unwrapParens(value);
|
|
1520
|
+
const candidate = (0, PredicateExpressionUtils_1.unwrapParens)(value);
|
|
1527
1521
|
if (candidate instanceof ValueComponent_1.ParameterExpression) {
|
|
1528
|
-
appendUnique(names, candidate.name.value);
|
|
1522
|
+
(0, PredicateExpressionUtils_1.appendUnique)(names, candidate.name.value);
|
|
1529
1523
|
return;
|
|
1530
1524
|
}
|
|
1531
1525
|
if (candidate instanceof ValueComponent_1.BinaryExpression) {
|
|
@@ -1600,10 +1594,10 @@ class StaticPredicatePlacementOptimizer {
|
|
|
1600
1594
|
visit(expression);
|
|
1601
1595
|
return names;
|
|
1602
1596
|
}
|
|
1603
|
-
makeSkipped(expression, draft, options) {
|
|
1597
|
+
makeSkipped(expression, scopeId, draft, options) {
|
|
1604
1598
|
return {
|
|
1605
1599
|
predicateSql: (0, SqlComponentFormatter_1.formatSqlComponent)(expression, options),
|
|
1606
|
-
scopeId
|
|
1600
|
+
scopeId,
|
|
1607
1601
|
...draft
|
|
1608
1602
|
};
|
|
1609
1603
|
}
|