rawsql-ts 0.28.0 → 0.30.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/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/ConditionDeduplicationOptimizer.d.ts +22 -0
- package/dist/esm/transformers/ConditionDeduplicationOptimizer.js +198 -0
- package/dist/esm/transformers/ConditionDeduplicationOptimizer.js.map +1 -0
- package/dist/esm/transformers/ConditionOptimization.d.ts +31 -1
- package/dist/esm/transformers/ConditionOptimization.js +321 -4
- package/dist/esm/transformers/ConditionOptimization.js.map +1 -1
- package/dist/esm/transformers/ParameterConditionPlacementOptimizer.d.ts +13 -0
- package/dist/esm/transformers/ParameterConditionPlacementOptimizer.js +238 -87
- 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 +88 -0
- package/dist/esm/transformers/PredicateReachabilityAnalyzer.js +495 -0
- package/dist/esm/transformers/PredicateReachabilityAnalyzer.js.map +1 -0
- package/dist/esm/transformers/StaticPredicatePlacementOptimizer.d.ts +14 -0
- package/dist/esm/transformers/StaticPredicatePlacementOptimizer.js +344 -157
- package/dist/esm/transformers/StaticPredicatePlacementOptimizer.js.map +1 -1
- package/dist/esm/transformers/TopLevelAndConditionDeduper.d.ts +10 -0
- package/dist/esm/transformers/TopLevelAndConditionDeduper.js +66 -0
- package/dist/esm/transformers/TopLevelAndConditionDeduper.js.map +1 -0
- 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/ConditionDeduplicationOptimizer.d.ts +22 -0
- package/dist/src/transformers/ConditionOptimization.d.ts +31 -1
- package/dist/src/transformers/ParameterConditionPlacementOptimizer.d.ts +13 -0
- package/dist/src/transformers/PredicateExpressionUtils.d.ts +16 -0
- package/dist/src/transformers/PredicateReachabilityAnalyzer.d.ts +88 -0
- package/dist/src/transformers/StaticPredicatePlacementOptimizer.d.ts +14 -0
- package/dist/src/transformers/TopLevelAndConditionDeduper.d.ts +10 -0
- package/dist/transformers/ConditionDeduplicationOptimizer.js +202 -0
- package/dist/transformers/ConditionDeduplicationOptimizer.js.map +1 -0
- package/dist/transformers/ConditionOptimization.js +324 -4
- package/dist/transformers/ConditionOptimization.js.map +1 -1
- package/dist/transformers/ParameterConditionPlacementOptimizer.js +261 -110
- 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 +508 -0
- package/dist/transformers/PredicateReachabilityAnalyzer.js.map +1 -0
- package/dist/transformers/StaticPredicatePlacementOptimizer.js +366 -179
- package/dist/transformers/StaticPredicatePlacementOptimizer.js.map +1 -1
- package/dist/transformers/TopLevelAndConditionDeduper.js +72 -0
- package/dist/transformers/TopLevelAndConditionDeduper.js.map +1 -0
- package/dist/tsconfig.browser.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -5,70 +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");
|
|
10
|
+
const TopLevelAndConditionDeduper_1 = require("./TopLevelAndConditionDeduper");
|
|
11
|
+
const PredicateExpressionUtils_1 = require("./PredicateExpressionUtils");
|
|
11
12
|
const VOLATILE_OR_UNSUPPORTED_FUNCTION_REASON = "Predicate contains a function call; volatile and expression predicates are not moved in the safe-only implementation.";
|
|
12
|
-
const normalizeIdentifier = (value) => value.trim().toLowerCase();
|
|
13
|
-
const unwrapParens = (expression) => {
|
|
14
|
-
let candidate = expression;
|
|
15
|
-
while (candidate instanceof ValueComponent_1.ParenExpression) {
|
|
16
|
-
candidate = candidate.expression;
|
|
17
|
-
}
|
|
18
|
-
return candidate;
|
|
19
|
-
};
|
|
20
|
-
const isBinaryOperator = (expression, operator) => {
|
|
21
|
-
const candidate = unwrapParens(expression);
|
|
22
|
-
return candidate instanceof ValueComponent_1.BinaryExpression
|
|
23
|
-
&& candidate.operator.value.trim().toLowerCase() === operator;
|
|
24
|
-
};
|
|
25
|
-
const collectTopLevelAndTerms = (expression) => {
|
|
26
|
-
const candidate = unwrapParens(expression);
|
|
27
|
-
if (!isBinaryOperator(candidate, "and")) {
|
|
28
|
-
return [expression];
|
|
29
|
-
}
|
|
30
|
-
return [
|
|
31
|
-
...collectTopLevelAndTerms(candidate.left),
|
|
32
|
-
...collectTopLevelAndTerms(candidate.right)
|
|
33
|
-
];
|
|
34
|
-
};
|
|
35
|
-
const rebuildWhereWithoutTerms = (query, termsToRemove) => {
|
|
36
|
-
if (!query.whereClause || termsToRemove.size === 0) {
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
const remaining = collectTopLevelAndTerms(query.whereClause.condition)
|
|
40
|
-
.filter(term => !termsToRemove.has(term));
|
|
41
|
-
if (remaining.length === 0) {
|
|
42
|
-
query.whereClause = null;
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
let rebuilt = remaining[0];
|
|
46
|
-
for (let index = 1; index < remaining.length; index += 1) {
|
|
47
|
-
rebuilt = new ValueComponent_1.BinaryExpression(rebuilt, "and", remaining[index]);
|
|
48
|
-
}
|
|
49
|
-
query.whereClause = new Clause_1.WhereClause(rebuilt);
|
|
50
|
-
};
|
|
51
|
-
const cloneValueComponent = (expression, options) => {
|
|
52
|
-
return ValueParser_1.ValueParser.parse((0, SqlComponentFormatter_1.formatSqlComponent)(expression, options));
|
|
53
|
-
};
|
|
54
|
-
const cloneColumnReference = (reference) => {
|
|
55
|
-
var _a, _b;
|
|
56
|
-
const namespaces = (_b = (_a = reference.namespaces) === null || _a === void 0 ? void 0 : _a.map(namespace => namespace.name)) !== null && _b !== void 0 ? _b : null;
|
|
57
|
-
return new ValueComponent_1.ColumnReference(namespaces, reference.column.name);
|
|
58
|
-
};
|
|
59
|
-
const columnReferenceText = (reference) => {
|
|
60
|
-
const namespace = reference.getNamespace();
|
|
61
|
-
return namespace ? `${namespace}.${reference.column.name}` : reference.column.name;
|
|
62
|
-
};
|
|
63
|
-
const sameColumnReference = (left, right) => {
|
|
64
|
-
return normalizeIdentifier(left.column.name) === normalizeIdentifier(right.column.name)
|
|
65
|
-
&& normalizeIdentifier(left.getNamespace()) === normalizeIdentifier(right.getNamespace());
|
|
66
|
-
};
|
|
67
|
-
const appendUnique = (items, value) => {
|
|
68
|
-
if (!items.includes(value)) {
|
|
69
|
-
items.push(value);
|
|
70
|
-
}
|
|
71
|
-
};
|
|
72
13
|
class StaticPredicatePlacementOptimizer {
|
|
73
14
|
plan(input, options = {}) {
|
|
74
15
|
var _a, _b, _c;
|
|
@@ -106,71 +47,8 @@ class StaticPredicatePlacementOptimizer {
|
|
|
106
47
|
const query = parsed.query;
|
|
107
48
|
const applied = [];
|
|
108
49
|
const skipped = [];
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
const candidate = this.analyzeCandidate(query, term, options);
|
|
112
|
-
if (!candidate) {
|
|
113
|
-
continue;
|
|
114
|
-
}
|
|
115
|
-
if ("code" in candidate) {
|
|
116
|
-
skipped.push(this.makeSkipped(term, candidate, options));
|
|
117
|
-
continue;
|
|
118
|
-
}
|
|
119
|
-
const target = this.resolveTarget(query, candidate);
|
|
120
|
-
if ("code" in target) {
|
|
121
|
-
skipped.push(this.makeSkipped(term, target, options));
|
|
122
|
-
continue;
|
|
123
|
-
}
|
|
124
|
-
let appliedReason = "";
|
|
125
|
-
if (target.kind === "simple") {
|
|
126
|
-
const targetColumns = this.resolveTargetColumns(query, target.query, candidate.references);
|
|
127
|
-
if ("code" in targetColumns) {
|
|
128
|
-
skipped.push(this.makeSkipped(term, targetColumns, options));
|
|
129
|
-
continue;
|
|
130
|
-
}
|
|
131
|
-
const placement = this.resolveTargetPlacement(target.query, targetColumns);
|
|
132
|
-
if ("code" in placement) {
|
|
133
|
-
skipped.push(this.makeSkipped(term, placement, options));
|
|
134
|
-
continue;
|
|
135
|
-
}
|
|
136
|
-
const movedPredicate = this.rebasePredicate(candidate.expression, targetColumns, options);
|
|
137
|
-
target.query.appendWhere(movedPredicate);
|
|
138
|
-
appliedReason = placement.reason;
|
|
139
|
-
}
|
|
140
|
-
else {
|
|
141
|
-
const placements = [];
|
|
142
|
-
let skip = null;
|
|
143
|
-
for (const branch of target.branches) {
|
|
144
|
-
const placement = this.resolveTargetPlacement(branch.query, branch.targetColumns);
|
|
145
|
-
if ("code" in placement) {
|
|
146
|
-
skip = placement;
|
|
147
|
-
break;
|
|
148
|
-
}
|
|
149
|
-
placements.push(placement);
|
|
150
|
-
}
|
|
151
|
-
if (skip) {
|
|
152
|
-
skipped.push(this.makeSkipped(term, skip, options));
|
|
153
|
-
continue;
|
|
154
|
-
}
|
|
155
|
-
for (const branch of target.branches) {
|
|
156
|
-
const movedPredicate = this.rebasePredicate(candidate.expression, branch.targetColumns, options);
|
|
157
|
-
branch.query.appendWhere(movedPredicate);
|
|
158
|
-
}
|
|
159
|
-
appliedReason = placements.some(item => /group by/i.test(item.reason))
|
|
160
|
-
? "Predicate is distributed to every UNION branch by output column position; grouped branches only receive GROUP BY-key predicates."
|
|
161
|
-
: "Predicate is distributed to every UNION branch by output column position before unsafe query boundaries.";
|
|
162
|
-
}
|
|
163
|
-
movedTerms.push(term);
|
|
164
|
-
applied.push({
|
|
165
|
-
kind: "move_static_predicate",
|
|
166
|
-
predicateSql: candidate.predicateSql,
|
|
167
|
-
fromScopeId: "scope:root",
|
|
168
|
-
toScopeId: target.scopeId,
|
|
169
|
-
reason: appliedReason,
|
|
170
|
-
columnReferences: candidate.references.map(columnReferenceText)
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
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.
|
|
174
52
|
const sql = applied.length > 0 || (0, SqlComponentFormatter_1.hasSqlComponentFormatOverride)(options)
|
|
175
53
|
? (0, SqlComponentFormatter_1.formatSqlComponent)(query, options)
|
|
176
54
|
: parsed.sql;
|
|
@@ -185,6 +63,121 @@ class StaticPredicatePlacementOptimizer {
|
|
|
185
63
|
formatterGeneratedSource: parsed.formatterGeneratedSource
|
|
186
64
|
});
|
|
187
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
|
+
}
|
|
188
181
|
optimize(input, options = {}) {
|
|
189
182
|
var _a;
|
|
190
183
|
return this.plan(input, { ...options, dryRun: (_a = options.dryRun) !== null && _a !== void 0 ? _a : false });
|
|
@@ -257,12 +250,12 @@ class StaticPredicatePlacementOptimizer {
|
|
|
257
250
|
};
|
|
258
251
|
}
|
|
259
252
|
isExistsPredicate(expression) {
|
|
260
|
-
const candidate = unwrapParens(expression);
|
|
253
|
+
const candidate = (0, PredicateExpressionUtils_1.unwrapParens)(expression);
|
|
261
254
|
if (!(candidate instanceof ValueComponent_1.UnaryExpression)) {
|
|
262
255
|
return false;
|
|
263
256
|
}
|
|
264
257
|
const operator = candidate.operator.value.trim().toLowerCase();
|
|
265
|
-
return operator === "exists" && unwrapParens(candidate.expression) instanceof ValueComponent_1.InlineQuery;
|
|
258
|
+
return operator === "exists" && (0, PredicateExpressionUtils_1.unwrapParens)(candidate.expression) instanceof ValueComponent_1.InlineQuery;
|
|
266
259
|
}
|
|
267
260
|
findUnsupportedExpression(expression, allowExistsSubquery) {
|
|
268
261
|
let found = null;
|
|
@@ -311,7 +304,7 @@ class StaticPredicatePlacementOptimizer {
|
|
|
311
304
|
if (found) {
|
|
312
305
|
return;
|
|
313
306
|
}
|
|
314
|
-
const candidate = unwrapParens(value);
|
|
307
|
+
const candidate = (0, PredicateExpressionUtils_1.unwrapParens)(value);
|
|
315
308
|
if (candidate instanceof ValueComponent_1.BinaryExpression) {
|
|
316
309
|
visit(candidate.left);
|
|
317
310
|
visit(candidate.right);
|
|
@@ -380,12 +373,13 @@ class StaticPredicatePlacementOptimizer {
|
|
|
380
373
|
visit(expression);
|
|
381
374
|
return found;
|
|
382
375
|
}
|
|
383
|
-
resolveTarget(
|
|
384
|
-
|
|
376
|
+
resolveTarget(contextRoot, currentQuery, candidate) {
|
|
377
|
+
var _a;
|
|
378
|
+
const boundary = this.findRootQueryBoundary(currentQuery);
|
|
385
379
|
if (boundary) {
|
|
386
380
|
return boundary;
|
|
387
381
|
}
|
|
388
|
-
if (!
|
|
382
|
+
if (!currentQuery.fromClause) {
|
|
389
383
|
return {
|
|
390
384
|
code: "NO_FROM_CLAUSE",
|
|
391
385
|
reason: "Predicate has no FROM source that can receive it safely."
|
|
@@ -393,7 +387,7 @@ class StaticPredicatePlacementOptimizer {
|
|
|
393
387
|
}
|
|
394
388
|
const bindings = [];
|
|
395
389
|
for (const reference of candidate.references) {
|
|
396
|
-
const binding = this.resolveSourceBinding(
|
|
390
|
+
const binding = this.resolveSourceBinding(contextRoot, currentQuery, reference);
|
|
397
391
|
if ("code" in binding) {
|
|
398
392
|
return binding;
|
|
399
393
|
}
|
|
@@ -408,12 +402,25 @@ class StaticPredicatePlacementOptimizer {
|
|
|
408
402
|
};
|
|
409
403
|
}
|
|
410
404
|
const binding = bindings[0];
|
|
411
|
-
|
|
405
|
+
if ((_a = binding.join) === null || _a === void 0 ? void 0 : _a.lateral) {
|
|
406
|
+
return {
|
|
407
|
+
code: "LATERAL_JOIN_BOUNDARY",
|
|
408
|
+
reason: "Predicate crosses a LATERAL JOIN boundary; moving it may change semantics."
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
const nullableSide = this.findNullableSideBoundary(currentQuery.fromClause, binding);
|
|
412
412
|
if (nullableSide) {
|
|
413
413
|
return nullableSide;
|
|
414
414
|
}
|
|
415
|
-
const upstream = this.resolveUpstreamQuery(
|
|
415
|
+
const upstream = this.resolveUpstreamQuery(contextRoot, binding, candidate.references);
|
|
416
416
|
if ("code" in upstream) {
|
|
417
|
+
if (upstream.code === "NO_SAFE_UPSTREAM_QUERY") {
|
|
418
|
+
const joinOnTarget = this.resolveBaseTableJoinOnTarget(contextRoot, currentQuery, binding);
|
|
419
|
+
if (!("code" in joinOnTarget)) {
|
|
420
|
+
return joinOnTarget;
|
|
421
|
+
}
|
|
422
|
+
return joinOnTarget;
|
|
423
|
+
}
|
|
417
424
|
return upstream;
|
|
418
425
|
}
|
|
419
426
|
return upstream;
|
|
@@ -433,7 +440,7 @@ class StaticPredicatePlacementOptimizer {
|
|
|
433
440
|
}
|
|
434
441
|
return null;
|
|
435
442
|
}
|
|
436
|
-
resolveTargetPlacement(query, targetColumns) {
|
|
443
|
+
resolveTargetPlacement(contextRoot, query, targetColumns) {
|
|
437
444
|
const hasOrdinaryDistinct = this.hasOrdinaryDistinct(query);
|
|
438
445
|
if (this.hasDistinctOnBoundary(query)) {
|
|
439
446
|
return {
|
|
@@ -453,11 +460,11 @@ class StaticPredicatePlacementOptimizer {
|
|
|
453
460
|
reason: "Predicate crosses LIMIT/OFFSET/FETCH boundary; moving it may change row selection semantics."
|
|
454
461
|
};
|
|
455
462
|
}
|
|
456
|
-
if (query.fromClause
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
}
|
|
463
|
+
if (query.fromClause) {
|
|
464
|
+
const nullableSide = this.findNullableSideBoundaryForTargetColumns(contextRoot, query, targetColumns);
|
|
465
|
+
if (nullableSide) {
|
|
466
|
+
return nullableSide;
|
|
467
|
+
}
|
|
461
468
|
}
|
|
462
469
|
if (query.groupByClause) {
|
|
463
470
|
const allReferencesAreGroupKeys = targetColumns.every(item => this.isGroupKeyColumn(query, item.targetColumn));
|
|
@@ -486,6 +493,22 @@ class StaticPredicatePlacementOptimizer {
|
|
|
486
493
|
reason: "All outer references resolve to direct upstream outputs before unsafe query boundaries."
|
|
487
494
|
};
|
|
488
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
|
+
}
|
|
489
512
|
resolveSourceBinding(contextRoot, query, column) {
|
|
490
513
|
const fromClause = query.fromClause;
|
|
491
514
|
if (!fromClause) {
|
|
@@ -495,10 +518,10 @@ class StaticPredicatePlacementOptimizer {
|
|
|
495
518
|
};
|
|
496
519
|
}
|
|
497
520
|
const bindings = this.getSourceBindings(fromClause);
|
|
498
|
-
const namespace =
|
|
521
|
+
const namespace = column.getNamespace();
|
|
499
522
|
const columnName = column.column.name;
|
|
500
523
|
if (namespace) {
|
|
501
|
-
const matches = bindings.filter(binding =>
|
|
524
|
+
const matches = bindings.filter(binding => (0, PredicateExpressionUtils_1.identifiersEqual)(binding.alias, namespace));
|
|
502
525
|
if (matches.length !== 1) {
|
|
503
526
|
return {
|
|
504
527
|
code: "AMBIGUOUS_COLUMN_SOURCE",
|
|
@@ -506,16 +529,19 @@ class StaticPredicatePlacementOptimizer {
|
|
|
506
529
|
};
|
|
507
530
|
}
|
|
508
531
|
const matchCount = this.getOutputColumnMatchCount(contextRoot, matches[0], columnName);
|
|
532
|
+
if (matchCount === 0 && this.isBaseTableBinding(contextRoot, matches[0])) {
|
|
533
|
+
return matches[0];
|
|
534
|
+
}
|
|
509
535
|
if (matchCount === 0) {
|
|
510
536
|
return {
|
|
511
537
|
code: "COLUMN_NOT_AVAILABLE_UPSTREAM",
|
|
512
|
-
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.`
|
|
513
539
|
};
|
|
514
540
|
}
|
|
515
541
|
if (matchCount > 1) {
|
|
516
542
|
return {
|
|
517
543
|
code: "AMBIGUOUS_COLUMN_REFERENCE",
|
|
518
|
-
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.`
|
|
519
545
|
};
|
|
520
546
|
}
|
|
521
547
|
return matches[0];
|
|
@@ -634,7 +660,7 @@ class StaticPredicatePlacementOptimizer {
|
|
|
634
660
|
resolveTargetColumns(root, query, references) {
|
|
635
661
|
const resolved = [];
|
|
636
662
|
for (const reference of references) {
|
|
637
|
-
const matches = this.
|
|
663
|
+
const matches = this.collectDirectOutputMatches(root, query, reference.column.name);
|
|
638
664
|
if (matches.length !== 1) {
|
|
639
665
|
return {
|
|
640
666
|
code: "AMBIGUOUS_TARGET_COLUMN",
|
|
@@ -661,6 +687,57 @@ class StaticPredicatePlacementOptimizer {
|
|
|
661
687
|
}
|
|
662
688
|
return resolved;
|
|
663
689
|
}
|
|
690
|
+
resolveBaseTableJoinOnTarget(contextRoot, currentQuery, binding) {
|
|
691
|
+
var _a, _b, _c;
|
|
692
|
+
if (!currentQuery.fromClause || !this.isBaseTableBinding(contextRoot, binding)) {
|
|
693
|
+
return {
|
|
694
|
+
code: "NO_SAFE_UPSTREAM_QUERY",
|
|
695
|
+
reason: "The referenced source is a base table, so there is no upstream query block to move into."
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
if (this.hasOuterJoin(currentQuery.fromClause)) {
|
|
699
|
+
return {
|
|
700
|
+
code: "OUTER_JOIN_BOUNDARY",
|
|
701
|
+
reason: "Predicate crosses an OUTER JOIN boundary; moving it into JOIN ON may change semantics."
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
const join = binding.isPrimary
|
|
705
|
+
? (_b = (_a = currentQuery.fromClause.joins) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : null
|
|
706
|
+
: binding.join;
|
|
707
|
+
if (join === null || join === void 0 ? void 0 : join.lateral) {
|
|
708
|
+
return {
|
|
709
|
+
code: "LATERAL_JOIN_BOUNDARY",
|
|
710
|
+
reason: "Predicate crosses a LATERAL JOIN boundary; moving it into JOIN ON may change semantics."
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
if (!join || !this.isInnerJoin(join)) {
|
|
714
|
+
return {
|
|
715
|
+
code: "NO_SAFE_JOIN_ON_TARGET",
|
|
716
|
+
reason: "Base-table predicates are moved only into INNER JOIN ON clauses in the safe-only implementation."
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
if (!(join.condition instanceof Clause_1.JoinOnClause)) {
|
|
720
|
+
return {
|
|
721
|
+
code: "NO_SAFE_JOIN_ON_TARGET",
|
|
722
|
+
reason: "Base-table predicates are moved only into existing JOIN ON clauses, not USING or conditionless joins."
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
return {
|
|
726
|
+
kind: "join_on",
|
|
727
|
+
join,
|
|
728
|
+
scopeId: `join_on:${(_c = join.source.getAliasName()) !== null && _c !== void 0 ? _c : "unknown"}`,
|
|
729
|
+
reason: binding.isPrimary
|
|
730
|
+
? "Predicate references the primary source of an INNER JOIN; it is moved into the first JOIN ON clause."
|
|
731
|
+
: "Predicate references the joined source of an INNER JOIN; it is moved into that JOIN ON clause."
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
appendJoinOnPredicate(join, expression, options) {
|
|
735
|
+
if (!(join.condition instanceof Clause_1.JoinOnClause)) {
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
join.condition.condition = new ValueComponent_1.BinaryExpression(join.condition.condition, "and", (0, PredicateExpressionUtils_1.cloneValueComponent)(expression, options));
|
|
739
|
+
join.condition.condition = (0, TopLevelAndConditionDeduper_1.dedupeTopLevelAndConditions)(join.condition.condition, options);
|
|
740
|
+
}
|
|
664
741
|
resolveDeepestBranchTarget(contextRoot, query, targetColumns, visited = new Set()) {
|
|
665
742
|
if (visited.has(query) || targetColumns.length === 0) {
|
|
666
743
|
return { query, targetColumns: [...targetColumns] };
|
|
@@ -688,14 +765,17 @@ class StaticPredicatePlacementOptimizer {
|
|
|
688
765
|
return { query, targetColumns: [...targetColumns] };
|
|
689
766
|
}
|
|
690
767
|
const upstream = this.resolveUpstreamQuery(contextRoot, binding, targetColumns.map(item => item.targetColumn));
|
|
691
|
-
if ("code" in upstream
|
|
768
|
+
if ("code" in upstream) {
|
|
769
|
+
return { query, targetColumns: [...targetColumns] };
|
|
770
|
+
}
|
|
771
|
+
if (upstream.kind !== "simple") {
|
|
692
772
|
return { query, targetColumns: [...targetColumns] };
|
|
693
773
|
}
|
|
694
774
|
const upstreamColumns = this.resolveTargetColumns(contextRoot, upstream.query, targetColumns.map(item => item.targetColumn));
|
|
695
775
|
if ("code" in upstreamColumns) {
|
|
696
776
|
return { query, targetColumns: [...targetColumns] };
|
|
697
777
|
}
|
|
698
|
-
const placement = this.resolveTargetPlacement(upstream.query, upstreamColumns);
|
|
778
|
+
const placement = this.resolveTargetPlacement(contextRoot, upstream.query, upstreamColumns);
|
|
699
779
|
if ("code" in placement) {
|
|
700
780
|
return { query, targetColumns: [...targetColumns] };
|
|
701
781
|
}
|
|
@@ -713,14 +793,14 @@ class StaticPredicatePlacementOptimizer {
|
|
|
713
793
|
};
|
|
714
794
|
}
|
|
715
795
|
const bindings = this.getSourceBindings(query.fromClause);
|
|
716
|
-
const namespace =
|
|
796
|
+
const namespace = column.getNamespace();
|
|
717
797
|
if (namespace) {
|
|
718
|
-
const matches = bindings.filter(binding =>
|
|
798
|
+
const matches = bindings.filter(binding => (0, PredicateExpressionUtils_1.identifiersEqual)(binding.alias, namespace));
|
|
719
799
|
return matches.length === 1
|
|
720
800
|
? null
|
|
721
801
|
: {
|
|
722
802
|
code: "AMBIGUOUS_TARGET_COLUMN",
|
|
723
|
-
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.`
|
|
724
804
|
};
|
|
725
805
|
}
|
|
726
806
|
if (bindings.length === 1) {
|
|
@@ -737,16 +817,16 @@ class StaticPredicatePlacementOptimizer {
|
|
|
737
817
|
return false;
|
|
738
818
|
}
|
|
739
819
|
return groupBy.grouping.some(grouping => {
|
|
740
|
-
const candidate = unwrapParens(grouping);
|
|
820
|
+
const candidate = (0, PredicateExpressionUtils_1.unwrapParens)(grouping);
|
|
741
821
|
return candidate instanceof ValueComponent_1.ColumnReference
|
|
742
822
|
&& this.sameResolvableColumnInQuery(query, candidate, column);
|
|
743
823
|
});
|
|
744
824
|
}
|
|
745
825
|
sameResolvableColumnInQuery(query, left, right) {
|
|
746
|
-
if (sameColumnReference(left, right)) {
|
|
826
|
+
if ((0, PredicateExpressionUtils_1.sameColumnReference)(left, right)) {
|
|
747
827
|
return true;
|
|
748
828
|
}
|
|
749
|
-
if (
|
|
829
|
+
if (!(0, PredicateExpressionUtils_1.identifiersEqual)(left.column.name, right.column.name)) {
|
|
750
830
|
return false;
|
|
751
831
|
}
|
|
752
832
|
const leftSource = this.resolveColumnSourceAlias(query, left);
|
|
@@ -758,15 +838,15 @@ class StaticPredicatePlacementOptimizer {
|
|
|
758
838
|
return null;
|
|
759
839
|
}
|
|
760
840
|
const bindings = this.getSourceBindings(query.fromClause);
|
|
761
|
-
const namespace =
|
|
841
|
+
const namespace = column.getNamespace();
|
|
762
842
|
if (namespace) {
|
|
763
|
-
const matches = bindings.filter(binding =>
|
|
764
|
-
return matches.length === 1 ?
|
|
843
|
+
const matches = bindings.filter(binding => (0, PredicateExpressionUtils_1.identifiersEqual)(binding.alias, namespace));
|
|
844
|
+
return matches.length === 1 ? matches[0].alias : null;
|
|
765
845
|
}
|
|
766
|
-
return bindings.length === 1 ?
|
|
846
|
+
return bindings.length === 1 ? bindings[0].alias : null;
|
|
767
847
|
}
|
|
768
848
|
rebasePredicate(expression, targetColumns, options) {
|
|
769
|
-
const cloned = cloneValueComponent(expression, options);
|
|
849
|
+
const cloned = (0, PredicateExpressionUtils_1.cloneValueComponent)(expression, options);
|
|
770
850
|
const visitSelect = (query, inheritedLocalAliases) => {
|
|
771
851
|
var _a, _b, _c, _d;
|
|
772
852
|
if (!(query instanceof SelectQuery_1.SimpleSelectQuery)) {
|
|
@@ -799,15 +879,15 @@ class StaticPredicatePlacementOptimizer {
|
|
|
799
879
|
}
|
|
800
880
|
};
|
|
801
881
|
const visit = (value, localAliases) => {
|
|
802
|
-
const candidate = unwrapParens(value);
|
|
882
|
+
const candidate = (0, PredicateExpressionUtils_1.unwrapParens)(value);
|
|
803
883
|
if (candidate instanceof ValueComponent_1.ColumnReference) {
|
|
804
|
-
const namespace = normalizeIdentifier(candidate.getNamespace());
|
|
884
|
+
const namespace = (0, PredicateExpressionUtils_1.normalizeIdentifier)(candidate.getNamespace());
|
|
805
885
|
if (namespace && localAliases.has(namespace)) {
|
|
806
886
|
return;
|
|
807
887
|
}
|
|
808
|
-
const target = targetColumns.find(item => sameColumnReference(candidate, item.sourceColumn));
|
|
888
|
+
const target = targetColumns.find(item => (0, PredicateExpressionUtils_1.sameColumnReference)(candidate, item.sourceColumn));
|
|
809
889
|
if (target) {
|
|
810
|
-
candidate.qualifiedName = cloneColumnReference(target.targetColumn).qualifiedName;
|
|
890
|
+
candidate.qualifiedName = (0, PredicateExpressionUtils_1.cloneColumnReference)(target.targetColumn).qualifiedName;
|
|
811
891
|
}
|
|
812
892
|
return;
|
|
813
893
|
}
|
|
@@ -900,6 +980,14 @@ class StaticPredicatePlacementOptimizer {
|
|
|
900
980
|
}
|
|
901
981
|
return bindings;
|
|
902
982
|
}
|
|
983
|
+
isBaseTableBinding(root, binding) {
|
|
984
|
+
const source = binding.source.datasource;
|
|
985
|
+
return source instanceof Clause_1.TableSource && !this.findCte(root, source.table.name);
|
|
986
|
+
}
|
|
987
|
+
isInnerJoin(join) {
|
|
988
|
+
const joinType = join.joinType.value.trim().toLowerCase();
|
|
989
|
+
return joinType === "join" || joinType === "inner join";
|
|
990
|
+
}
|
|
903
991
|
findNullableSideBoundary(fromClause, binding) {
|
|
904
992
|
var _a;
|
|
905
993
|
const joins = (_a = fromClause.joins) !== null && _a !== void 0 ? _a : [];
|
|
@@ -967,12 +1055,12 @@ class StaticPredicatePlacementOptimizer {
|
|
|
967
1055
|
if ("code" in branches) {
|
|
968
1056
|
return 0;
|
|
969
1057
|
}
|
|
970
|
-
return this.
|
|
1058
|
+
return this.collectDirectOutputMatches(root, branches[0], columnName).length;
|
|
971
1059
|
}
|
|
972
1060
|
if (!(target instanceof SelectQuery_1.SimpleSelectQuery)) {
|
|
973
1061
|
return 0;
|
|
974
1062
|
}
|
|
975
|
-
return this.
|
|
1063
|
+
return this.collectDirectOutputMatches(root, target, columnName).length;
|
|
976
1064
|
}
|
|
977
1065
|
collectUnionBranches(query) {
|
|
978
1066
|
const branches = [];
|
|
@@ -1003,7 +1091,7 @@ class StaticPredicatePlacementOptimizer {
|
|
|
1003
1091
|
resolveUnionOutputIndex(root, firstBranch, outputColumnName) {
|
|
1004
1092
|
const matches = [];
|
|
1005
1093
|
this.collectSelectOutputs(root, firstBranch).forEach((item, index) => {
|
|
1006
|
-
if (
|
|
1094
|
+
if ((0, PredicateExpressionUtils_1.identifiersEqual)(item.name, outputColumnName)) {
|
|
1007
1095
|
matches.push(index);
|
|
1008
1096
|
}
|
|
1009
1097
|
});
|
|
@@ -1025,6 +1113,15 @@ class StaticPredicatePlacementOptimizer {
|
|
|
1025
1113
|
reason: `UNION branch does not expose column '${sourceColumn.column.name}' at output position ${outputIndex + 1}.`
|
|
1026
1114
|
};
|
|
1027
1115
|
}
|
|
1116
|
+
else if ((0, PredicateExpressionUtils_1.identifiersEqual)(output.name, sourceColumn.column.name)) {
|
|
1117
|
+
const matches = this.collectDirectOutputMatches(root, query, sourceColumn.column.name);
|
|
1118
|
+
if (matches.length > 1) {
|
|
1119
|
+
return {
|
|
1120
|
+
code: "AMBIGUOUS_TARGET_COLUMN",
|
|
1121
|
+
reason: `UNION branch exposes multiple '${sourceColumn.column.name}' columns.`
|
|
1122
|
+
};
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1028
1125
|
if (!(output.value instanceof ValueComponent_1.ColumnReference)) {
|
|
1029
1126
|
return {
|
|
1030
1127
|
code: "EXPRESSION_OUTPUT_UNSUPPORTED",
|
|
@@ -1049,6 +1146,96 @@ class StaticPredicatePlacementOptimizer {
|
|
|
1049
1146
|
const collector = new SelectOutputCollector_1.SelectOutputCollector(null, commonTables.length > 0 ? commonTables : null);
|
|
1050
1147
|
return collector.collect(query);
|
|
1051
1148
|
}
|
|
1149
|
+
collectDirectOutputMatches(root, query, columnName) {
|
|
1150
|
+
const collectedMatches = this.collectSelectOutputs(root, query).filter(item => (0, PredicateExpressionUtils_1.identifiersEqual)(item.name, columnName));
|
|
1151
|
+
if (collectedMatches.length > 0) {
|
|
1152
|
+
return this.hasExplicitOutputMatch(query, columnName)
|
|
1153
|
+
? [...collectedMatches, ...this.inferWildcardOutputMatches(root, query, columnName, true)]
|
|
1154
|
+
: collectedMatches;
|
|
1155
|
+
}
|
|
1156
|
+
return this.inferWildcardOutputMatches(root, query, columnName, false);
|
|
1157
|
+
}
|
|
1158
|
+
hasExplicitOutputMatch(query, columnName) {
|
|
1159
|
+
return query.selectClause.items.some(item => {
|
|
1160
|
+
if (item.identifier) {
|
|
1161
|
+
return (0, PredicateExpressionUtils_1.identifiersEqual)(item.identifier.name, columnName);
|
|
1162
|
+
}
|
|
1163
|
+
return item.value instanceof ValueComponent_1.ColumnReference
|
|
1164
|
+
&& item.value.column.name !== "*"
|
|
1165
|
+
&& (0, PredicateExpressionUtils_1.identifiersEqual)(item.value.column.name, columnName);
|
|
1166
|
+
});
|
|
1167
|
+
}
|
|
1168
|
+
inferWildcardOutputMatches(root, query, columnName, includeUnprovenPotential) {
|
|
1169
|
+
const matches = [];
|
|
1170
|
+
query.selectClause.items.forEach((item, outputIndex) => {
|
|
1171
|
+
if (item.identifier || !(item.value instanceof ValueComponent_1.ColumnReference) || item.value.column.name !== "*") {
|
|
1172
|
+
return;
|
|
1173
|
+
}
|
|
1174
|
+
const targetColumn = this.inferWildcardTargetColumn(root, query, item.value, columnName, includeUnprovenPotential);
|
|
1175
|
+
if (!targetColumn) {
|
|
1176
|
+
return;
|
|
1177
|
+
}
|
|
1178
|
+
if (targetColumn === "ambiguous") {
|
|
1179
|
+
matches.push(this.createInferredOutputColumn(columnName, new ValueComponent_1.ColumnReference(null, columnName), outputIndex), this.createInferredOutputColumn(columnName, new ValueComponent_1.ColumnReference(null, columnName), outputIndex));
|
|
1180
|
+
return;
|
|
1181
|
+
}
|
|
1182
|
+
matches.push(this.createInferredOutputColumn(columnName, targetColumn, outputIndex));
|
|
1183
|
+
});
|
|
1184
|
+
return matches;
|
|
1185
|
+
}
|
|
1186
|
+
inferWildcardTargetColumn(root, query, wildcard, columnName, includeUnprovenPotential) {
|
|
1187
|
+
if (!query.fromClause) {
|
|
1188
|
+
return null;
|
|
1189
|
+
}
|
|
1190
|
+
const bindings = this.getSourceBindings(query.fromClause);
|
|
1191
|
+
if (wildcard.namespaces === null) {
|
|
1192
|
+
if (bindings.length !== 1) {
|
|
1193
|
+
return "ambiguous";
|
|
1194
|
+
}
|
|
1195
|
+
return this.resolveWildcardColumnFromBinding(root, query, bindings[0], columnName, includeUnprovenPotential);
|
|
1196
|
+
}
|
|
1197
|
+
const namespace = wildcard.getNamespace();
|
|
1198
|
+
const matches = bindings.filter(binding => (0, PredicateExpressionUtils_1.identifiersEqual)(binding.alias, namespace));
|
|
1199
|
+
if (matches.length === 0) {
|
|
1200
|
+
return null;
|
|
1201
|
+
}
|
|
1202
|
+
return matches.length === 1
|
|
1203
|
+
? this.resolveWildcardColumnFromBinding(root, query, matches[0], columnName, includeUnprovenPotential)
|
|
1204
|
+
: "ambiguous";
|
|
1205
|
+
}
|
|
1206
|
+
resolveWildcardColumnFromBinding(root, query, binding, columnName, includeUnprovenPotential) {
|
|
1207
|
+
const matches = this.collectSourceOutputMatches(root, query, binding.source, columnName);
|
|
1208
|
+
if (matches.length > 1) {
|
|
1209
|
+
return "ambiguous";
|
|
1210
|
+
}
|
|
1211
|
+
if (matches.length === 1) {
|
|
1212
|
+
const value = matches[0].value;
|
|
1213
|
+
return value instanceof ValueComponent_1.ColumnReference ? value : null;
|
|
1214
|
+
}
|
|
1215
|
+
return includeUnprovenPotential ? this.createColumnForBinding(binding, columnName) : null;
|
|
1216
|
+
}
|
|
1217
|
+
collectSourceOutputMatches(root, query, source, columnName) {
|
|
1218
|
+
var _a, _b, _c, _d;
|
|
1219
|
+
const commonTables = [
|
|
1220
|
+
...((_b = (_a = query.withClause) === null || _a === void 0 ? void 0 : _a.tables) !== null && _b !== void 0 ? _b : []),
|
|
1221
|
+
...((_d = (_c = root.withClause) === null || _c === void 0 ? void 0 : _c.tables) !== null && _d !== void 0 ? _d : [])
|
|
1222
|
+
];
|
|
1223
|
+
const collector = new SelectOutputCollector_1.SelectOutputCollector(null, commonTables.length > 0 ? commonTables : null);
|
|
1224
|
+
return collector.collect(source).filter(item => (0, PredicateExpressionUtils_1.identifiersEqual)(item.name, columnName));
|
|
1225
|
+
}
|
|
1226
|
+
createColumnForBinding(binding, columnName) {
|
|
1227
|
+
return new ValueComponent_1.ColumnReference(binding.alias ? [binding.alias] : null, columnName);
|
|
1228
|
+
}
|
|
1229
|
+
createInferredOutputColumn(name, value, outputIndex) {
|
|
1230
|
+
return {
|
|
1231
|
+
name,
|
|
1232
|
+
value,
|
|
1233
|
+
outputIndex,
|
|
1234
|
+
sourceAlias: value.getNamespace() || null,
|
|
1235
|
+
sourceName: null,
|
|
1236
|
+
sourceColumnName: name
|
|
1237
|
+
};
|
|
1238
|
+
}
|
|
1052
1239
|
resolveSourceQueryForColumns(root, source) {
|
|
1053
1240
|
var _a;
|
|
1054
1241
|
if (source.datasource instanceof Clause_1.SubQuerySource) {
|
|
@@ -1064,13 +1251,13 @@ class StaticPredicatePlacementOptimizer {
|
|
|
1064
1251
|
}
|
|
1065
1252
|
findCte(root, name) {
|
|
1066
1253
|
var _a, _b;
|
|
1067
|
-
const normalized = normalizeIdentifier(name);
|
|
1254
|
+
const normalized = (0, PredicateExpressionUtils_1.normalizeIdentifier)(name);
|
|
1068
1255
|
const matches = ((_b = (_a = root.withClause) === null || _a === void 0 ? void 0 : _a.tables) !== null && _b !== void 0 ? _b : [])
|
|
1069
|
-
.filter(table => normalizeIdentifier(table.getSourceAliasName()) === normalized);
|
|
1256
|
+
.filter(table => (0, PredicateExpressionUtils_1.normalizeIdentifier)(table.getSourceAliasName()) === normalized);
|
|
1070
1257
|
return matches.length === 1 ? matches[0] : null;
|
|
1071
1258
|
}
|
|
1072
1259
|
countTableSourceReferences(query, tableName) {
|
|
1073
|
-
const normalized = normalizeIdentifier(tableName);
|
|
1260
|
+
const normalized = (0, PredicateExpressionUtils_1.normalizeIdentifier)(tableName);
|
|
1074
1261
|
let count = 0;
|
|
1075
1262
|
const visitSelect = (select) => {
|
|
1076
1263
|
var _a, _b;
|
|
@@ -1085,7 +1272,7 @@ class StaticPredicatePlacementOptimizer {
|
|
|
1085
1272
|
if (select.fromClause) {
|
|
1086
1273
|
for (const binding of this.getSourceBindings(select.fromClause)) {
|
|
1087
1274
|
const source = binding.source.datasource;
|
|
1088
|
-
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) {
|
|
1089
1276
|
count += 1;
|
|
1090
1277
|
}
|
|
1091
1278
|
if (source instanceof Clause_1.SubQuerySource) {
|
|
@@ -1108,7 +1295,7 @@ class StaticPredicatePlacementOptimizer {
|
|
|
1108
1295
|
for (const source of (_b = (_a = query.fromClause) === null || _a === void 0 ? void 0 : _a.getSources()) !== null && _b !== void 0 ? _b : []) {
|
|
1109
1296
|
const alias = source.getAliasName();
|
|
1110
1297
|
if (alias) {
|
|
1111
|
-
aliases.add(normalizeIdentifier(alias));
|
|
1298
|
+
aliases.add((0, PredicateExpressionUtils_1.normalizeIdentifier)(alias));
|
|
1112
1299
|
}
|
|
1113
1300
|
}
|
|
1114
1301
|
return aliases;
|
|
@@ -1148,14 +1335,14 @@ class StaticPredicatePlacementOptimizer {
|
|
|
1148
1335
|
}
|
|
1149
1336
|
};
|
|
1150
1337
|
const collect = (reference) => {
|
|
1151
|
-
if (!references.some(existing => sameColumnReference(existing, reference))) {
|
|
1338
|
+
if (!references.some(existing => (0, PredicateExpressionUtils_1.sameColumnReference)(existing, reference))) {
|
|
1152
1339
|
references.push(reference);
|
|
1153
1340
|
}
|
|
1154
1341
|
};
|
|
1155
1342
|
const visit = (value, localAliases) => {
|
|
1156
|
-
const candidate = unwrapParens(value);
|
|
1343
|
+
const candidate = (0, PredicateExpressionUtils_1.unwrapParens)(value);
|
|
1157
1344
|
if (candidate instanceof ValueComponent_1.ColumnReference) {
|
|
1158
|
-
const namespace = normalizeIdentifier(candidate.getNamespace());
|
|
1345
|
+
const namespace = (0, PredicateExpressionUtils_1.normalizeIdentifier)(candidate.getNamespace());
|
|
1159
1346
|
if (namespace) {
|
|
1160
1347
|
if (rootAliases.has(namespace) && !localAliases.has(namespace)) {
|
|
1161
1348
|
collect(candidate);
|
|
@@ -1248,7 +1435,7 @@ class StaticPredicatePlacementOptimizer {
|
|
|
1248
1435
|
if (found) {
|
|
1249
1436
|
return;
|
|
1250
1437
|
}
|
|
1251
|
-
const candidate = unwrapParens(value);
|
|
1438
|
+
const candidate = (0, PredicateExpressionUtils_1.unwrapParens)(value);
|
|
1252
1439
|
if (candidate instanceof ValueComponent_1.FunctionCall) {
|
|
1253
1440
|
if (candidate.over) {
|
|
1254
1441
|
found = true;
|
|
@@ -1330,9 +1517,9 @@ class StaticPredicatePlacementOptimizer {
|
|
|
1330
1517
|
}
|
|
1331
1518
|
};
|
|
1332
1519
|
const visit = (value) => {
|
|
1333
|
-
const candidate = unwrapParens(value);
|
|
1520
|
+
const candidate = (0, PredicateExpressionUtils_1.unwrapParens)(value);
|
|
1334
1521
|
if (candidate instanceof ValueComponent_1.ParameterExpression) {
|
|
1335
|
-
appendUnique(names, candidate.name.value);
|
|
1522
|
+
(0, PredicateExpressionUtils_1.appendUnique)(names, candidate.name.value);
|
|
1336
1523
|
return;
|
|
1337
1524
|
}
|
|
1338
1525
|
if (candidate instanceof ValueComponent_1.BinaryExpression) {
|
|
@@ -1407,10 +1594,10 @@ class StaticPredicatePlacementOptimizer {
|
|
|
1407
1594
|
visit(expression);
|
|
1408
1595
|
return names;
|
|
1409
1596
|
}
|
|
1410
|
-
makeSkipped(expression, draft, options) {
|
|
1597
|
+
makeSkipped(expression, scopeId, draft, options) {
|
|
1411
1598
|
return {
|
|
1412
1599
|
predicateSql: (0, SqlComponentFormatter_1.formatSqlComponent)(expression, options),
|
|
1413
|
-
scopeId
|
|
1600
|
+
scopeId,
|
|
1414
1601
|
...draft
|
|
1415
1602
|
};
|
|
1416
1603
|
}
|