rawsql-ts 0.26.0 → 0.26.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 +10 -1
- package/dist/esm/transformers/ConditionOptimization.js +29 -13
- package/dist/esm/transformers/ConditionOptimization.js.map +1 -1
- package/dist/esm/transformers/ParameterConditionPlacementOptimizer.js +1 -4
- package/dist/esm/transformers/ParameterConditionPlacementOptimizer.js.map +1 -1
- package/dist/esm/transformers/SSSQLFilterBuilder.d.ts +6 -0
- package/dist/esm/transformers/SSSQLFilterBuilder.js +84 -8
- package/dist/esm/transformers/SSSQLFilterBuilder.js.map +1 -1
- package/dist/esm/transformers/SqlComponentFormatter.d.ts +3 -0
- package/dist/esm/transformers/SqlComponentFormatter.js +7 -0
- package/dist/esm/transformers/SqlComponentFormatter.js.map +1 -0
- package/dist/esm/transformers/StaticPredicatePlacementOptimizer.d.ts +85 -0
- package/dist/esm/transformers/StaticPredicatePlacementOptimizer.js +1198 -0
- package/dist/esm/transformers/StaticPredicatePlacementOptimizer.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/ConditionOptimization.d.ts +10 -1
- package/dist/src/transformers/SSSQLFilterBuilder.d.ts +6 -0
- package/dist/src/transformers/SqlComponentFormatter.d.ts +3 -0
- package/dist/src/transformers/StaticPredicatePlacementOptimizer.d.ts +85 -0
- package/dist/transformers/ConditionOptimization.js +50 -22
- package/dist/transformers/ConditionOptimization.js.map +1 -1
- package/dist/transformers/ParameterConditionPlacementOptimizer.js +6 -9
- package/dist/transformers/ParameterConditionPlacementOptimizer.js.map +1 -1
- package/dist/transformers/SSSQLFilterBuilder.js +92 -16
- package/dist/transformers/SSSQLFilterBuilder.js.map +1 -1
- package/dist/transformers/SqlComponentFormatter.js +11 -0
- package/dist/transformers/SqlComponentFormatter.js.map +1 -0
- package/dist/transformers/StaticPredicatePlacementOptimizer.js +1208 -0
- package/dist/transformers/StaticPredicatePlacementOptimizer.js.map +1 -0
- package/dist/tsconfig.browser.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,1208 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.optimizeStaticPredicatePlacement = exports.planStaticPredicatePlacement = exports.StaticPredicatePlacementOptimizer = void 0;
|
|
4
|
+
const Clause_1 = require("../models/Clause");
|
|
5
|
+
const SelectQuery_1 = require("../models/SelectQuery");
|
|
6
|
+
const ValueComponent_1 = require("../models/ValueComponent");
|
|
7
|
+
const SelectQueryParser_1 = require("../parsers/SelectQueryParser");
|
|
8
|
+
const ValueParser_1 = require("../parsers/ValueParser");
|
|
9
|
+
const SqlComponentFormatter_1 = require("./SqlComponentFormatter");
|
|
10
|
+
const VOLATILE_OR_UNSUPPORTED_FUNCTION_REASON = "Predicate contains a function call; volatile and expression predicates are not moved in the safe-only implementation.";
|
|
11
|
+
const normalizeIdentifier = (value) => value.trim().toLowerCase();
|
|
12
|
+
const unwrapParens = (expression) => {
|
|
13
|
+
let candidate = expression;
|
|
14
|
+
while (candidate instanceof ValueComponent_1.ParenExpression) {
|
|
15
|
+
candidate = candidate.expression;
|
|
16
|
+
}
|
|
17
|
+
return candidate;
|
|
18
|
+
};
|
|
19
|
+
const isBinaryOperator = (expression, operator) => {
|
|
20
|
+
const candidate = unwrapParens(expression);
|
|
21
|
+
return candidate instanceof ValueComponent_1.BinaryExpression
|
|
22
|
+
&& candidate.operator.value.trim().toLowerCase() === operator;
|
|
23
|
+
};
|
|
24
|
+
const collectTopLevelAndTerms = (expression) => {
|
|
25
|
+
const candidate = unwrapParens(expression);
|
|
26
|
+
if (!isBinaryOperator(candidate, "and")) {
|
|
27
|
+
return [expression];
|
|
28
|
+
}
|
|
29
|
+
return [
|
|
30
|
+
...collectTopLevelAndTerms(candidate.left),
|
|
31
|
+
...collectTopLevelAndTerms(candidate.right)
|
|
32
|
+
];
|
|
33
|
+
};
|
|
34
|
+
const rebuildWhereWithoutTerms = (query, termsToRemove) => {
|
|
35
|
+
if (!query.whereClause || termsToRemove.size === 0) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const remaining = collectTopLevelAndTerms(query.whereClause.condition)
|
|
39
|
+
.filter(term => !termsToRemove.has(term));
|
|
40
|
+
if (remaining.length === 0) {
|
|
41
|
+
query.whereClause = null;
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
let rebuilt = remaining[0];
|
|
45
|
+
for (let index = 1; index < remaining.length; index += 1) {
|
|
46
|
+
rebuilt = new ValueComponent_1.BinaryExpression(rebuilt, "and", remaining[index]);
|
|
47
|
+
}
|
|
48
|
+
query.whereClause = new Clause_1.WhereClause(rebuilt);
|
|
49
|
+
};
|
|
50
|
+
const cloneValueComponent = (expression) => {
|
|
51
|
+
return ValueParser_1.ValueParser.parse((0, SqlComponentFormatter_1.formatSqlComponent)(expression));
|
|
52
|
+
};
|
|
53
|
+
const cloneColumnReference = (reference) => {
|
|
54
|
+
var _a, _b;
|
|
55
|
+
const namespaces = (_b = (_a = reference.namespaces) === null || _a === void 0 ? void 0 : _a.map(namespace => namespace.name)) !== null && _b !== void 0 ? _b : null;
|
|
56
|
+
return new ValueComponent_1.ColumnReference(namespaces, reference.column.name);
|
|
57
|
+
};
|
|
58
|
+
const columnReferenceText = (reference) => {
|
|
59
|
+
const namespace = reference.getNamespace();
|
|
60
|
+
return namespace ? `${namespace}.${reference.column.name}` : reference.column.name;
|
|
61
|
+
};
|
|
62
|
+
const sameColumnReference = (left, right) => {
|
|
63
|
+
return normalizeIdentifier(left.column.name) === normalizeIdentifier(right.column.name)
|
|
64
|
+
&& normalizeIdentifier(left.getNamespace()) === normalizeIdentifier(right.getNamespace());
|
|
65
|
+
};
|
|
66
|
+
const appendUnique = (items, value) => {
|
|
67
|
+
if (!items.includes(value)) {
|
|
68
|
+
items.push(value);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
class StaticPredicatePlacementOptimizer {
|
|
72
|
+
plan(input, options = {}) {
|
|
73
|
+
var _a, _b, _c;
|
|
74
|
+
const parsed = this.parseInput(input);
|
|
75
|
+
const warnings = [...parsed.warnings];
|
|
76
|
+
const errors = [...parsed.errors];
|
|
77
|
+
if (!parsed.query) {
|
|
78
|
+
return this.buildResult({
|
|
79
|
+
sql: parsed.sql,
|
|
80
|
+
applied: [],
|
|
81
|
+
skipped: [],
|
|
82
|
+
warnings,
|
|
83
|
+
errors,
|
|
84
|
+
dryRun: (_a = options.dryRun) !== null && _a !== void 0 ? _a : true,
|
|
85
|
+
formatterGeneratedSource: parsed.formatterGeneratedSource
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
if (!(parsed.query instanceof SelectQuery_1.SimpleSelectQuery)) {
|
|
89
|
+
warnings.push({
|
|
90
|
+
code: "UNSUPPORTED_ROOT_QUERY",
|
|
91
|
+
message: "Static predicate placement currently supports only SimpleSelectQuery roots."
|
|
92
|
+
});
|
|
93
|
+
return this.buildResult({
|
|
94
|
+
sql: parsed.sql,
|
|
95
|
+
applied: [],
|
|
96
|
+
skipped: [],
|
|
97
|
+
warnings,
|
|
98
|
+
errors,
|
|
99
|
+
dryRun: (_b = options.dryRun) !== null && _b !== void 0 ? _b : true,
|
|
100
|
+
formatterGeneratedSource: parsed.formatterGeneratedSource
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
const query = parsed.query;
|
|
104
|
+
const applied = [];
|
|
105
|
+
const skipped = [];
|
|
106
|
+
const movedTerms = [];
|
|
107
|
+
for (const term of query.whereClause ? collectTopLevelAndTerms(query.whereClause.condition) : []) {
|
|
108
|
+
const candidate = this.analyzeCandidate(query, term);
|
|
109
|
+
if (!candidate) {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if ("code" in candidate) {
|
|
113
|
+
skipped.push(this.makeSkipped(term, candidate));
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
const target = this.resolveTarget(query, candidate);
|
|
117
|
+
if ("code" in target) {
|
|
118
|
+
skipped.push(this.makeSkipped(term, target));
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
const targetColumns = this.resolveTargetColumns(target.query, candidate.references);
|
|
122
|
+
if ("code" in targetColumns) {
|
|
123
|
+
skipped.push(this.makeSkipped(term, targetColumns));
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
const movedPredicate = this.rebasePredicate(candidate.expression, targetColumns);
|
|
127
|
+
target.query.appendWhere(movedPredicate);
|
|
128
|
+
movedTerms.push(term);
|
|
129
|
+
applied.push({
|
|
130
|
+
kind: "move_static_predicate",
|
|
131
|
+
predicateSql: candidate.predicateSql,
|
|
132
|
+
fromScopeId: "scope:root",
|
|
133
|
+
toScopeId: target.scopeId,
|
|
134
|
+
reason: "All outer references resolve to direct upstream outputs before unsafe query boundaries.",
|
|
135
|
+
columnReferences: candidate.references.map(columnReferenceText)
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
rebuildWhereWithoutTerms(query, new Set(movedTerms));
|
|
139
|
+
const sql = applied.length > 0 ? (0, SqlComponentFormatter_1.formatSqlComponent)(query) : parsed.sql;
|
|
140
|
+
return this.buildResult({
|
|
141
|
+
sql,
|
|
142
|
+
applied,
|
|
143
|
+
skipped,
|
|
144
|
+
warnings,
|
|
145
|
+
errors,
|
|
146
|
+
dryRun: (_c = options.dryRun) !== null && _c !== void 0 ? _c : true,
|
|
147
|
+
formatterGeneratedSource: parsed.formatterGeneratedSource
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
optimize(input, options = {}) {
|
|
151
|
+
var _a;
|
|
152
|
+
return this.plan(input, { ...options, dryRun: (_a = options.dryRun) !== null && _a !== void 0 ? _a : false });
|
|
153
|
+
}
|
|
154
|
+
parseInput(input) {
|
|
155
|
+
const warnings = [];
|
|
156
|
+
const errors = [];
|
|
157
|
+
try {
|
|
158
|
+
const sourceSql = typeof input === "string"
|
|
159
|
+
? input
|
|
160
|
+
: (0, SqlComponentFormatter_1.formatSqlComponent)(input);
|
|
161
|
+
if (typeof input !== "string") {
|
|
162
|
+
warnings.push({
|
|
163
|
+
code: "AST_INPUT_FORMATTED",
|
|
164
|
+
message: "AST input is cloned through formatter output so the caller-owned query is not mutated."
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
query: SelectQueryParser_1.SelectQueryParser.parse(sourceSql),
|
|
169
|
+
sql: sourceSql,
|
|
170
|
+
formatterGeneratedSource: typeof input !== "string",
|
|
171
|
+
warnings,
|
|
172
|
+
errors
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
177
|
+
errors.push({
|
|
178
|
+
code: "PARSE_FAILED",
|
|
179
|
+
message: "Static predicate placement could not parse the input SQL.",
|
|
180
|
+
detail
|
|
181
|
+
});
|
|
182
|
+
return {
|
|
183
|
+
query: null,
|
|
184
|
+
sql: typeof input === "string" ? input : "",
|
|
185
|
+
formatterGeneratedSource: typeof input !== "string",
|
|
186
|
+
warnings,
|
|
187
|
+
errors
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
analyzeCandidate(root, expression) {
|
|
192
|
+
if (this.collectParameterNames(expression).length > 0) {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
const unsupported = this.findUnsupportedExpression(expression, this.isExistsPredicate(expression));
|
|
196
|
+
if (unsupported) {
|
|
197
|
+
return unsupported;
|
|
198
|
+
}
|
|
199
|
+
const references = this.collectRootColumnReferences(root, expression);
|
|
200
|
+
if (references.length === 0) {
|
|
201
|
+
return {
|
|
202
|
+
code: "NO_COLUMN_REFERENCE",
|
|
203
|
+
reason: "Static predicate has no outer column reference to anchor the move."
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
return {
|
|
207
|
+
expression,
|
|
208
|
+
predicateSql: (0, SqlComponentFormatter_1.formatSqlComponent)(expression),
|
|
209
|
+
references
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
isExistsPredicate(expression) {
|
|
213
|
+
const candidate = unwrapParens(expression);
|
|
214
|
+
if (!(candidate instanceof ValueComponent_1.UnaryExpression)) {
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
const operator = candidate.operator.value.trim().toLowerCase();
|
|
218
|
+
return operator === "exists" && unwrapParens(candidate.expression) instanceof ValueComponent_1.InlineQuery;
|
|
219
|
+
}
|
|
220
|
+
findUnsupportedExpression(expression, allowExistsSubquery) {
|
|
221
|
+
let found = null;
|
|
222
|
+
const visitSelect = (query) => {
|
|
223
|
+
var _a, _b, _c, _d;
|
|
224
|
+
if (found) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
if (query instanceof SelectQuery_1.BinarySelectQuery) {
|
|
228
|
+
found = {
|
|
229
|
+
code: "UNION_BOUNDARY",
|
|
230
|
+
reason: "Static predicate would need distribution into a UNION branch, which is unsupported."
|
|
231
|
+
};
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
if (!(query instanceof SelectQuery_1.SimpleSelectQuery)) {
|
|
235
|
+
found = {
|
|
236
|
+
code: "UNSUPPORTED_SUBQUERY",
|
|
237
|
+
reason: "Static predicate contains a non-simple subquery, which is unsupported."
|
|
238
|
+
};
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
query.selectClause.items.forEach(item => visit(item.value));
|
|
242
|
+
for (const join of (_b = (_a = query.fromClause) === null || _a === void 0 ? void 0 : _a.joins) !== null && _b !== void 0 ? _b : []) {
|
|
243
|
+
if (join.condition) {
|
|
244
|
+
visit(join.condition.condition);
|
|
245
|
+
}
|
|
246
|
+
const source = join.source.datasource;
|
|
247
|
+
if (source instanceof Clause_1.SubQuerySource) {
|
|
248
|
+
visitSelect(source.query);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
if (query.whereClause) {
|
|
252
|
+
visit(query.whereClause.condition);
|
|
253
|
+
}
|
|
254
|
+
if (query.havingClause) {
|
|
255
|
+
visit(query.havingClause.condition);
|
|
256
|
+
}
|
|
257
|
+
for (const cte of (_d = (_c = query.withClause) === null || _c === void 0 ? void 0 : _c.tables) !== null && _d !== void 0 ? _d : []) {
|
|
258
|
+
if (cte.query instanceof SelectQuery_1.SimpleSelectQuery || cte.query instanceof SelectQuery_1.BinarySelectQuery) {
|
|
259
|
+
visitSelect(cte.query);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
const visit = (value) => {
|
|
264
|
+
if (found) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
const candidate = unwrapParens(value);
|
|
268
|
+
if (candidate instanceof ValueComponent_1.BinaryExpression) {
|
|
269
|
+
if (candidate.operator.value.trim().toLowerCase() === "or") {
|
|
270
|
+
found = {
|
|
271
|
+
code: "OR_PREDICATE_UNSUPPORTED",
|
|
272
|
+
reason: "Predicate contains OR predicates, which are not moved in the first safe-only implementation."
|
|
273
|
+
};
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
visit(candidate.left);
|
|
277
|
+
visit(candidate.right);
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
if (candidate instanceof ValueComponent_1.FunctionCall) {
|
|
281
|
+
found = {
|
|
282
|
+
code: "FUNCTION_PREDICATE_UNSUPPORTED",
|
|
283
|
+
reason: VOLATILE_OR_UNSUPPORTED_FUNCTION_REASON
|
|
284
|
+
};
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
if (candidate instanceof ValueComponent_1.CaseExpression) {
|
|
288
|
+
found = {
|
|
289
|
+
code: "CASE_PREDICATE_UNSUPPORTED",
|
|
290
|
+
reason: "CASE predicates are not moved in the first safe-only implementation."
|
|
291
|
+
};
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
if (candidate instanceof ValueComponent_1.InlineQuery) {
|
|
295
|
+
if (!allowExistsSubquery) {
|
|
296
|
+
found = {
|
|
297
|
+
code: "SUBQUERY_PREDICATE_UNSUPPORTED",
|
|
298
|
+
reason: "Subquery predicates are only moved when they are simple EXISTS predicates."
|
|
299
|
+
};
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
visitSelect(candidate.selectQuery);
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
if (candidate instanceof ValueComponent_1.ArrayQueryExpression) {
|
|
306
|
+
found = {
|
|
307
|
+
code: "SUBQUERY_PREDICATE_UNSUPPORTED",
|
|
308
|
+
reason: "Array subquery predicates are not moved in the first safe-only implementation."
|
|
309
|
+
};
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
if (candidate instanceof ValueComponent_1.BetweenExpression) {
|
|
313
|
+
found = {
|
|
314
|
+
code: "BETWEEN_PREDICATE_UNSUPPORTED",
|
|
315
|
+
reason: "BETWEEN predicates are not moved in the first safe-only implementation."
|
|
316
|
+
};
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
if (candidate instanceof ValueComponent_1.UnaryExpression) {
|
|
320
|
+
visit(candidate.expression);
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
if (candidate instanceof ValueComponent_1.CastExpression) {
|
|
324
|
+
visit(candidate.input);
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
if (candidate instanceof ValueComponent_1.JsonPredicateExpression) {
|
|
328
|
+
visit(candidate.expression);
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
if (candidate instanceof ValueComponent_1.ArrayExpression) {
|
|
332
|
+
visit(candidate.expression);
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
if (candidate instanceof ValueComponent_1.ValueList) {
|
|
336
|
+
candidate.values.forEach(visit);
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
if (candidate instanceof ValueComponent_1.TupleExpression) {
|
|
340
|
+
candidate.values.forEach(visit);
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
if (candidate instanceof ValueComponent_1.TypeValue && candidate.argument) {
|
|
344
|
+
visit(candidate.argument);
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
visit(expression);
|
|
348
|
+
return found;
|
|
349
|
+
}
|
|
350
|
+
resolveTarget(root, candidate) {
|
|
351
|
+
const boundary = this.findCurrentQueryBoundary(root);
|
|
352
|
+
if (boundary) {
|
|
353
|
+
return boundary;
|
|
354
|
+
}
|
|
355
|
+
if (!root.fromClause) {
|
|
356
|
+
return {
|
|
357
|
+
code: "NO_FROM_CLAUSE",
|
|
358
|
+
reason: "Predicate has no FROM source that can receive it safely."
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
const bindings = [];
|
|
362
|
+
for (const reference of candidate.references) {
|
|
363
|
+
const binding = this.resolveSourceBinding(root, reference);
|
|
364
|
+
if ("code" in binding) {
|
|
365
|
+
return binding;
|
|
366
|
+
}
|
|
367
|
+
if (!bindings.some(item => item.source === binding.source)) {
|
|
368
|
+
bindings.push(binding);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
if (bindings.length !== 1) {
|
|
372
|
+
return {
|
|
373
|
+
code: "MULTIPLE_SOURCE_REFERENCES",
|
|
374
|
+
reason: "Static predicate references multiple source query blocks; moving it may change semantics."
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
const binding = bindings[0];
|
|
378
|
+
const nullableSide = this.findNullableSideBoundary(root.fromClause, binding);
|
|
379
|
+
if (nullableSide) {
|
|
380
|
+
return nullableSide;
|
|
381
|
+
}
|
|
382
|
+
const upstream = this.resolveUpstreamQuery(root, binding);
|
|
383
|
+
if ("code" in upstream) {
|
|
384
|
+
return upstream;
|
|
385
|
+
}
|
|
386
|
+
const targetBoundary = this.findTargetQueryBoundary(upstream.query);
|
|
387
|
+
if (targetBoundary) {
|
|
388
|
+
return targetBoundary;
|
|
389
|
+
}
|
|
390
|
+
return upstream;
|
|
391
|
+
}
|
|
392
|
+
findCurrentQueryBoundary(query) {
|
|
393
|
+
if (query.selectClause.distinct) {
|
|
394
|
+
return {
|
|
395
|
+
code: "DISTINCT_BOUNDARY",
|
|
396
|
+
reason: "Predicate crosses DISTINCT boundary; moving it may change semantics."
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
if (query.groupByClause || query.havingClause) {
|
|
400
|
+
return {
|
|
401
|
+
code: "GROUP_BY_BOUNDARY",
|
|
402
|
+
reason: "Predicate crosses GROUP BY boundary; moving it may change semantics."
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
if (this.hasWindowUsage(query)) {
|
|
406
|
+
return {
|
|
407
|
+
code: "WINDOW_BOUNDARY",
|
|
408
|
+
reason: "Predicate crosses WINDOW boundary; moving it may change semantics."
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
return null;
|
|
412
|
+
}
|
|
413
|
+
findTargetQueryBoundary(query) {
|
|
414
|
+
const commonBoundary = this.findCurrentQueryBoundary(query);
|
|
415
|
+
if (commonBoundary) {
|
|
416
|
+
return commonBoundary;
|
|
417
|
+
}
|
|
418
|
+
if (query.limitClause || query.offsetClause || query.fetchClause) {
|
|
419
|
+
return {
|
|
420
|
+
code: "ROW_LIMIT_BOUNDARY",
|
|
421
|
+
reason: "Predicate crosses LIMIT/OFFSET/FETCH boundary; moving it may change row selection semantics."
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
if (query.fromClause && this.hasOuterJoin(query.fromClause)) {
|
|
425
|
+
return {
|
|
426
|
+
code: "OUTER_JOIN_BOUNDARY",
|
|
427
|
+
reason: "Target query contains an OUTER JOIN boundary that is not moved across in the safe-only implementation."
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
return null;
|
|
431
|
+
}
|
|
432
|
+
resolveSourceBinding(root, column) {
|
|
433
|
+
const fromClause = root.fromClause;
|
|
434
|
+
if (!fromClause) {
|
|
435
|
+
return {
|
|
436
|
+
code: "NO_FROM_CLAUSE",
|
|
437
|
+
reason: "Predicate has no FROM source that can receive it safely."
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
const bindings = this.getSourceBindings(fromClause);
|
|
441
|
+
const namespace = normalizeIdentifier(column.getNamespace());
|
|
442
|
+
const columnName = column.column.name;
|
|
443
|
+
if (namespace) {
|
|
444
|
+
const matches = bindings.filter(binding => normalizeIdentifier(binding.alias) === namespace);
|
|
445
|
+
if (matches.length !== 1) {
|
|
446
|
+
return {
|
|
447
|
+
code: "AMBIGUOUS_COLUMN_SOURCE",
|
|
448
|
+
reason: `Column source alias '${column.getNamespace()}' is not uniquely resolvable.`
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
if (this.resolveSourceQueryForColumns(root, matches[0].source) instanceof SelectQuery_1.BinarySelectQuery) {
|
|
452
|
+
return matches[0];
|
|
453
|
+
}
|
|
454
|
+
const matchCount = this.getOutputColumnMatchCount(root, matches[0], columnName);
|
|
455
|
+
if (matchCount === 0) {
|
|
456
|
+
return {
|
|
457
|
+
code: "COLUMN_NOT_AVAILABLE_UPSTREAM",
|
|
458
|
+
reason: `Column '${columnReferenceText(column)}' is not a direct output of the referenced source.`
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
if (matchCount > 1) {
|
|
462
|
+
return {
|
|
463
|
+
code: "AMBIGUOUS_COLUMN_REFERENCE",
|
|
464
|
+
reason: `Column '${columnReferenceText(column)}' resolves to multiple outputs in the referenced source.`
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
return matches[0];
|
|
468
|
+
}
|
|
469
|
+
const matches = [];
|
|
470
|
+
let unionSource = null;
|
|
471
|
+
for (const binding of bindings) {
|
|
472
|
+
if (this.resolveSourceQueryForColumns(root, binding.source) instanceof SelectQuery_1.BinarySelectQuery) {
|
|
473
|
+
unionSource !== null && unionSource !== void 0 ? unionSource : (unionSource = binding);
|
|
474
|
+
continue;
|
|
475
|
+
}
|
|
476
|
+
const matchCount = this.getOutputColumnMatchCount(root, binding, columnName);
|
|
477
|
+
if (matchCount > 1) {
|
|
478
|
+
return {
|
|
479
|
+
code: "AMBIGUOUS_COLUMN_REFERENCE",
|
|
480
|
+
reason: `Column '${columnName}' resolves to multiple outputs in source '${binding.alias}'.`
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
if (matchCount === 1) {
|
|
484
|
+
matches.push(binding);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
if (matches.length === 0 && unionSource) {
|
|
488
|
+
return {
|
|
489
|
+
code: "UNION_BOUNDARY",
|
|
490
|
+
reason: "Predicate would need distribution into a UNION branch, which is unsupported."
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
if (matches.length !== 1) {
|
|
494
|
+
return {
|
|
495
|
+
code: "AMBIGUOUS_COLUMN_REFERENCE",
|
|
496
|
+
reason: matches.length === 0
|
|
497
|
+
? `Column '${columnName}' is not uniquely resolvable to a safe upstream source.`
|
|
498
|
+
: `Column '${columnName}' is ambiguous across source query blocks.`
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
return matches[0];
|
|
502
|
+
}
|
|
503
|
+
resolveUpstreamQuery(root, binding) {
|
|
504
|
+
const source = binding.source.datasource;
|
|
505
|
+
if (source instanceof Clause_1.SubQuerySource) {
|
|
506
|
+
if (source.query instanceof SelectQuery_1.SimpleSelectQuery) {
|
|
507
|
+
return {
|
|
508
|
+
query: source.query,
|
|
509
|
+
scopeId: `subquery:${binding.alias}`,
|
|
510
|
+
sourceBinding: binding
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
return {
|
|
514
|
+
code: "UNION_BOUNDARY",
|
|
515
|
+
reason: "Predicate would need distribution into a UNION or non-simple subquery, which is unsupported."
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
if (!(source instanceof Clause_1.TableSource)) {
|
|
519
|
+
return {
|
|
520
|
+
code: "UNSUPPORTED_SOURCE",
|
|
521
|
+
reason: "Only CTE and simple derived-table sources can receive moved static predicates."
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
const cteName = source.table.name;
|
|
525
|
+
const commonTable = this.findCte(root, cteName);
|
|
526
|
+
if (!commonTable) {
|
|
527
|
+
return {
|
|
528
|
+
code: "NO_SAFE_UPSTREAM_QUERY",
|
|
529
|
+
reason: "The referenced source is a base table, so there is no upstream query block to move into."
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
const referenceCount = this.countTableSourceReferences(root, cteName);
|
|
533
|
+
if (referenceCount !== 1) {
|
|
534
|
+
return {
|
|
535
|
+
code: "CTE_REUSE_UNSUPPORTED",
|
|
536
|
+
reason: `CTE '${cteName}' is referenced ${referenceCount} times; moving a predicate into it may affect other consumers.`
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
if (commonTable.query instanceof SelectQuery_1.BinarySelectQuery) {
|
|
540
|
+
return {
|
|
541
|
+
code: "UNION_BOUNDARY",
|
|
542
|
+
reason: "Predicate would need distribution into a UNION branch, which is unsupported."
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
if (!(commonTable.query instanceof SelectQuery_1.SimpleSelectQuery)) {
|
|
546
|
+
return {
|
|
547
|
+
code: "UNSUPPORTED_CTE_QUERY",
|
|
548
|
+
reason: "Writable or non-select CTE bodies are not moved into by static predicate placement."
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
return {
|
|
552
|
+
query: commonTable.query,
|
|
553
|
+
scopeId: `cte:${commonTable.getSourceAliasName()}`,
|
|
554
|
+
sourceBinding: binding,
|
|
555
|
+
cteName: commonTable.getSourceAliasName()
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
resolveTargetColumns(query, references) {
|
|
559
|
+
const resolved = [];
|
|
560
|
+
for (const reference of references) {
|
|
561
|
+
const matches = query.selectClause.items.filter(item => { var _a; return normalizeIdentifier((_a = this.getSelectItemOutputName(item)) !== null && _a !== void 0 ? _a : "") === normalizeIdentifier(reference.column.name); });
|
|
562
|
+
if (matches.length !== 1) {
|
|
563
|
+
return {
|
|
564
|
+
code: "AMBIGUOUS_TARGET_COLUMN",
|
|
565
|
+
reason: matches.length === 0
|
|
566
|
+
? `Target query does not expose '${reference.column.name}' as a direct output column.`
|
|
567
|
+
: `Target query exposes multiple '${reference.column.name}' columns.`
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
const value = matches[0].value;
|
|
571
|
+
if (!(value instanceof ValueComponent_1.ColumnReference)) {
|
|
572
|
+
return {
|
|
573
|
+
code: "EXPRESSION_OUTPUT_UNSUPPORTED",
|
|
574
|
+
reason: `Target output '${reference.column.name}' is an expression, not a direct column reference.`
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
const sourceResolution = this.verifyColumnResolvableInQuery(query, value);
|
|
578
|
+
if (sourceResolution) {
|
|
579
|
+
return sourceResolution;
|
|
580
|
+
}
|
|
581
|
+
resolved.push({
|
|
582
|
+
sourceColumn: reference,
|
|
583
|
+
targetColumn: value
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
return resolved;
|
|
587
|
+
}
|
|
588
|
+
verifyColumnResolvableInQuery(query, column) {
|
|
589
|
+
if (!query.fromClause) {
|
|
590
|
+
return {
|
|
591
|
+
code: "NO_TARGET_FROM_CLAUSE",
|
|
592
|
+
reason: "Target query has no FROM clause where the moved column can be resolved."
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
const bindings = this.getSourceBindings(query.fromClause);
|
|
596
|
+
const namespace = normalizeIdentifier(column.getNamespace());
|
|
597
|
+
if (namespace) {
|
|
598
|
+
const matches = bindings.filter(binding => normalizeIdentifier(binding.alias) === namespace);
|
|
599
|
+
return matches.length === 1
|
|
600
|
+
? null
|
|
601
|
+
: {
|
|
602
|
+
code: "AMBIGUOUS_TARGET_COLUMN",
|
|
603
|
+
reason: `Target column '${columnReferenceText(column)}' is not uniquely resolvable in the destination query.`
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
if (bindings.length === 1) {
|
|
607
|
+
return null;
|
|
608
|
+
}
|
|
609
|
+
return {
|
|
610
|
+
code: "AMBIGUOUS_TARGET_COLUMN",
|
|
611
|
+
reason: `Unqualified target column '${column.column.name}' is ambiguous in a multi-source destination query.`
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
rebasePredicate(expression, targetColumns) {
|
|
615
|
+
const cloned = cloneValueComponent(expression);
|
|
616
|
+
const visitSelect = (query, inheritedLocalAliases) => {
|
|
617
|
+
var _a, _b, _c, _d;
|
|
618
|
+
if (!(query instanceof SelectQuery_1.SimpleSelectQuery)) {
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
const localAliases = new Set(inheritedLocalAliases);
|
|
622
|
+
for (const alias of this.collectSourceAliases(query)) {
|
|
623
|
+
localAliases.add(alias);
|
|
624
|
+
}
|
|
625
|
+
query.selectClause.items.forEach(item => visit(item.value, localAliases));
|
|
626
|
+
for (const join of (_b = (_a = query.fromClause) === null || _a === void 0 ? void 0 : _a.joins) !== null && _b !== void 0 ? _b : []) {
|
|
627
|
+
if (join.condition) {
|
|
628
|
+
visit(join.condition.condition, localAliases);
|
|
629
|
+
}
|
|
630
|
+
const source = join.source.datasource;
|
|
631
|
+
if (source instanceof Clause_1.SubQuerySource) {
|
|
632
|
+
visitSelect(source.query, localAliases);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
if (query.whereClause) {
|
|
636
|
+
visit(query.whereClause.condition, localAliases);
|
|
637
|
+
}
|
|
638
|
+
if (query.havingClause) {
|
|
639
|
+
visit(query.havingClause.condition, localAliases);
|
|
640
|
+
}
|
|
641
|
+
for (const cte of (_d = (_c = query.withClause) === null || _c === void 0 ? void 0 : _c.tables) !== null && _d !== void 0 ? _d : []) {
|
|
642
|
+
if (cte.query instanceof SelectQuery_1.SimpleSelectQuery || cte.query instanceof SelectQuery_1.BinarySelectQuery) {
|
|
643
|
+
visitSelect(cte.query, localAliases);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
};
|
|
647
|
+
const visit = (value, localAliases) => {
|
|
648
|
+
const candidate = unwrapParens(value);
|
|
649
|
+
if (candidate instanceof ValueComponent_1.ColumnReference) {
|
|
650
|
+
const namespace = normalizeIdentifier(candidate.getNamespace());
|
|
651
|
+
if (namespace && localAliases.has(namespace)) {
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
const target = targetColumns.find(item => sameColumnReference(candidate, item.sourceColumn));
|
|
655
|
+
if (target) {
|
|
656
|
+
candidate.qualifiedName = cloneColumnReference(target.targetColumn).qualifiedName;
|
|
657
|
+
}
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
if (candidate instanceof ValueComponent_1.BinaryExpression) {
|
|
661
|
+
visit(candidate.left, localAliases);
|
|
662
|
+
visit(candidate.right, localAliases);
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
if (candidate instanceof ValueComponent_1.UnaryExpression) {
|
|
666
|
+
visit(candidate.expression, localAliases);
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
if (candidate instanceof ValueComponent_1.InlineQuery) {
|
|
670
|
+
visitSelect(candidate.selectQuery, localAliases);
|
|
671
|
+
return;
|
|
672
|
+
}
|
|
673
|
+
if (candidate instanceof ValueComponent_1.FunctionCall) {
|
|
674
|
+
if (candidate.argument) {
|
|
675
|
+
visit(candidate.argument, localAliases);
|
|
676
|
+
}
|
|
677
|
+
if (candidate.filterCondition) {
|
|
678
|
+
visit(candidate.filterCondition, localAliases);
|
|
679
|
+
}
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
if (candidate instanceof ValueComponent_1.CastExpression) {
|
|
683
|
+
visit(candidate.input, localAliases);
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
686
|
+
if (candidate instanceof ValueComponent_1.CaseExpression) {
|
|
687
|
+
if (candidate.condition) {
|
|
688
|
+
visit(candidate.condition, localAliases);
|
|
689
|
+
}
|
|
690
|
+
for (const pair of candidate.switchCase.cases) {
|
|
691
|
+
visit(pair.key, localAliases);
|
|
692
|
+
visit(pair.value, localAliases);
|
|
693
|
+
}
|
|
694
|
+
if (candidate.switchCase.elseValue) {
|
|
695
|
+
visit(candidate.switchCase.elseValue, localAliases);
|
|
696
|
+
}
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
if (candidate instanceof ValueComponent_1.BetweenExpression) {
|
|
700
|
+
visit(candidate.expression, localAliases);
|
|
701
|
+
visit(candidate.lower, localAliases);
|
|
702
|
+
visit(candidate.upper, localAliases);
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
705
|
+
if (candidate instanceof ValueComponent_1.JsonPredicateExpression) {
|
|
706
|
+
visit(candidate.expression, localAliases);
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
if (candidate instanceof ValueComponent_1.ArrayExpression) {
|
|
710
|
+
visit(candidate.expression, localAliases);
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
if (candidate instanceof ValueComponent_1.ValueList) {
|
|
714
|
+
candidate.values.forEach(item => visit(item, localAliases));
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
if (candidate instanceof ValueComponent_1.TupleExpression) {
|
|
718
|
+
candidate.values.forEach(item => visit(item, localAliases));
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
721
|
+
if (candidate instanceof ValueComponent_1.TypeValue && candidate.argument) {
|
|
722
|
+
visit(candidate.argument, localAliases);
|
|
723
|
+
}
|
|
724
|
+
};
|
|
725
|
+
visit(cloned, new Set());
|
|
726
|
+
return cloned;
|
|
727
|
+
}
|
|
728
|
+
getSourceBindings(fromClause) {
|
|
729
|
+
var _a, _b, _c;
|
|
730
|
+
const bindings = [{
|
|
731
|
+
source: fromClause.source,
|
|
732
|
+
alias: (_a = fromClause.source.getAliasName()) !== null && _a !== void 0 ? _a : "",
|
|
733
|
+
join: null,
|
|
734
|
+
joinIndex: -1,
|
|
735
|
+
isPrimary: true
|
|
736
|
+
}];
|
|
737
|
+
for (let index = 0; index < ((_b = fromClause.joins) !== null && _b !== void 0 ? _b : []).length; index += 1) {
|
|
738
|
+
const join = fromClause.joins[index];
|
|
739
|
+
bindings.push({
|
|
740
|
+
source: join.source,
|
|
741
|
+
alias: (_c = join.source.getAliasName()) !== null && _c !== void 0 ? _c : "",
|
|
742
|
+
join,
|
|
743
|
+
joinIndex: index,
|
|
744
|
+
isPrimary: false
|
|
745
|
+
});
|
|
746
|
+
}
|
|
747
|
+
return bindings;
|
|
748
|
+
}
|
|
749
|
+
findNullableSideBoundary(fromClause, binding) {
|
|
750
|
+
var _a;
|
|
751
|
+
const joins = (_a = fromClause.joins) !== null && _a !== void 0 ? _a : [];
|
|
752
|
+
if (binding.isPrimary) {
|
|
753
|
+
return this.hasLaterJoinThatNullsPriorSources(joins, -1)
|
|
754
|
+
? {
|
|
755
|
+
code: "OUTER_JOIN_NULLABLE_SIDE",
|
|
756
|
+
reason: "Predicate crosses OUTER JOIN nullable side; moving it may change semantics."
|
|
757
|
+
}
|
|
758
|
+
: null;
|
|
759
|
+
}
|
|
760
|
+
const join = binding.join;
|
|
761
|
+
if (!join) {
|
|
762
|
+
return null;
|
|
763
|
+
}
|
|
764
|
+
const joinType = join.joinType.value.toLowerCase();
|
|
765
|
+
if (joinType.includes("left")) {
|
|
766
|
+
return {
|
|
767
|
+
code: "OUTER_JOIN_NULLABLE_SIDE",
|
|
768
|
+
reason: "Predicate crosses LEFT JOIN nullable side; moving it may change semantics."
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
if (joinType.includes("full")) {
|
|
772
|
+
return {
|
|
773
|
+
code: "OUTER_JOIN_NULLABLE_SIDE",
|
|
774
|
+
reason: "Predicate crosses FULL JOIN nullable side; moving it may change semantics."
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
if (this.hasLaterJoinThatNullsPriorSources(joins, binding.joinIndex)) {
|
|
778
|
+
return {
|
|
779
|
+
code: "OUTER_JOIN_NULLABLE_SIDE",
|
|
780
|
+
reason: "Predicate crosses later RIGHT/FULL JOIN nullable side; moving it may change semantics."
|
|
781
|
+
};
|
|
782
|
+
}
|
|
783
|
+
return null;
|
|
784
|
+
}
|
|
785
|
+
hasLaterJoinThatNullsPriorSources(joins, sourceJoinIndex) {
|
|
786
|
+
return joins
|
|
787
|
+
.slice(sourceJoinIndex + 1)
|
|
788
|
+
.some(join => {
|
|
789
|
+
const joinType = join.joinType.value.toLowerCase();
|
|
790
|
+
return joinType.includes("right") || joinType.includes("full");
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
hasOuterJoin(fromClause) {
|
|
794
|
+
var _a;
|
|
795
|
+
return ((_a = fromClause.joins) !== null && _a !== void 0 ? _a : []).some(join => {
|
|
796
|
+
const joinType = join.joinType.value.toLowerCase();
|
|
797
|
+
return joinType.includes("left") || joinType.includes("right") || joinType.includes("full") || joinType.includes("outer");
|
|
798
|
+
});
|
|
799
|
+
}
|
|
800
|
+
getOutputColumnMatchCount(root, binding, columnName) {
|
|
801
|
+
const target = this.resolveSourceQueryForColumns(root, binding.source);
|
|
802
|
+
if (!target || !(target instanceof SelectQuery_1.SimpleSelectQuery)) {
|
|
803
|
+
return 0;
|
|
804
|
+
}
|
|
805
|
+
return target.selectClause.items.filter(item => { var _a; return normalizeIdentifier((_a = this.getSelectItemOutputName(item)) !== null && _a !== void 0 ? _a : "") === normalizeIdentifier(columnName); }).length;
|
|
806
|
+
}
|
|
807
|
+
resolveSourceQueryForColumns(root, source) {
|
|
808
|
+
var _a;
|
|
809
|
+
if (source.datasource instanceof Clause_1.SubQuerySource) {
|
|
810
|
+
return source.datasource.query;
|
|
811
|
+
}
|
|
812
|
+
if (source.datasource instanceof Clause_1.TableSource) {
|
|
813
|
+
const cteQuery = (_a = this.findCte(root, source.datasource.table.name)) === null || _a === void 0 ? void 0 : _a.query;
|
|
814
|
+
return cteQuery instanceof SelectQuery_1.SimpleSelectQuery || cteQuery instanceof SelectQuery_1.BinarySelectQuery
|
|
815
|
+
? cteQuery
|
|
816
|
+
: null;
|
|
817
|
+
}
|
|
818
|
+
return null;
|
|
819
|
+
}
|
|
820
|
+
getSelectItemOutputName(item) {
|
|
821
|
+
if (item.identifier) {
|
|
822
|
+
return item.identifier.name;
|
|
823
|
+
}
|
|
824
|
+
if (item.value instanceof ValueComponent_1.ColumnReference) {
|
|
825
|
+
return item.value.column.name;
|
|
826
|
+
}
|
|
827
|
+
return null;
|
|
828
|
+
}
|
|
829
|
+
findCte(root, name) {
|
|
830
|
+
var _a, _b;
|
|
831
|
+
const normalized = normalizeIdentifier(name);
|
|
832
|
+
const matches = ((_b = (_a = root.withClause) === null || _a === void 0 ? void 0 : _a.tables) !== null && _b !== void 0 ? _b : [])
|
|
833
|
+
.filter(table => normalizeIdentifier(table.getSourceAliasName()) === normalized);
|
|
834
|
+
return matches.length === 1 ? matches[0] : null;
|
|
835
|
+
}
|
|
836
|
+
countTableSourceReferences(query, tableName) {
|
|
837
|
+
const normalized = normalizeIdentifier(tableName);
|
|
838
|
+
let count = 0;
|
|
839
|
+
const visitSelect = (select) => {
|
|
840
|
+
var _a, _b;
|
|
841
|
+
if (select instanceof SelectQuery_1.BinarySelectQuery) {
|
|
842
|
+
visitSelect(select.left);
|
|
843
|
+
visitSelect(select.right);
|
|
844
|
+
return;
|
|
845
|
+
}
|
|
846
|
+
if (!(select instanceof SelectQuery_1.SimpleSelectQuery)) {
|
|
847
|
+
return;
|
|
848
|
+
}
|
|
849
|
+
if (select.fromClause) {
|
|
850
|
+
for (const binding of this.getSourceBindings(select.fromClause)) {
|
|
851
|
+
const source = binding.source.datasource;
|
|
852
|
+
if (source instanceof Clause_1.TableSource && normalizeIdentifier(source.table.name) === normalized) {
|
|
853
|
+
count += 1;
|
|
854
|
+
}
|
|
855
|
+
if (source instanceof Clause_1.SubQuerySource) {
|
|
856
|
+
visitSelect(source.query);
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
for (const cte of (_b = (_a = select.withClause) === null || _a === void 0 ? void 0 : _a.tables) !== null && _b !== void 0 ? _b : []) {
|
|
861
|
+
if (cte.query instanceof SelectQuery_1.SimpleSelectQuery || cte.query instanceof SelectQuery_1.BinarySelectQuery) {
|
|
862
|
+
visitSelect(cte.query);
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
};
|
|
866
|
+
visitSelect(query);
|
|
867
|
+
return count;
|
|
868
|
+
}
|
|
869
|
+
collectSourceAliases(query) {
|
|
870
|
+
var _a, _b;
|
|
871
|
+
const aliases = new Set();
|
|
872
|
+
for (const source of (_b = (_a = query.fromClause) === null || _a === void 0 ? void 0 : _a.getSources()) !== null && _b !== void 0 ? _b : []) {
|
|
873
|
+
const alias = source.getAliasName();
|
|
874
|
+
if (alias) {
|
|
875
|
+
aliases.add(normalizeIdentifier(alias));
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
return aliases;
|
|
879
|
+
}
|
|
880
|
+
collectRootColumnReferences(root, expression) {
|
|
881
|
+
const references = [];
|
|
882
|
+
const rootAliases = this.collectSourceAliases(root);
|
|
883
|
+
const visitSelect = (query, inheritedLocalAliases) => {
|
|
884
|
+
var _a, _b, _c, _d;
|
|
885
|
+
if (!(query instanceof SelectQuery_1.SimpleSelectQuery)) {
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
888
|
+
const localAliases = new Set(inheritedLocalAliases);
|
|
889
|
+
for (const alias of this.collectSourceAliases(query)) {
|
|
890
|
+
localAliases.add(alias);
|
|
891
|
+
}
|
|
892
|
+
query.selectClause.items.forEach(item => visit(item.value, localAliases));
|
|
893
|
+
for (const join of (_b = (_a = query.fromClause) === null || _a === void 0 ? void 0 : _a.joins) !== null && _b !== void 0 ? _b : []) {
|
|
894
|
+
if (join.condition) {
|
|
895
|
+
visit(join.condition.condition, localAliases);
|
|
896
|
+
}
|
|
897
|
+
const source = join.source.datasource;
|
|
898
|
+
if (source instanceof Clause_1.SubQuerySource) {
|
|
899
|
+
visitSelect(source.query, localAliases);
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
if (query.whereClause) {
|
|
903
|
+
visit(query.whereClause.condition, localAliases);
|
|
904
|
+
}
|
|
905
|
+
if (query.havingClause) {
|
|
906
|
+
visit(query.havingClause.condition, localAliases);
|
|
907
|
+
}
|
|
908
|
+
for (const cte of (_d = (_c = query.withClause) === null || _c === void 0 ? void 0 : _c.tables) !== null && _d !== void 0 ? _d : []) {
|
|
909
|
+
if (cte.query instanceof SelectQuery_1.SimpleSelectQuery || cte.query instanceof SelectQuery_1.BinarySelectQuery) {
|
|
910
|
+
visitSelect(cte.query, localAliases);
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
};
|
|
914
|
+
const collect = (reference) => {
|
|
915
|
+
if (!references.some(existing => sameColumnReference(existing, reference))) {
|
|
916
|
+
references.push(reference);
|
|
917
|
+
}
|
|
918
|
+
};
|
|
919
|
+
const visit = (value, localAliases) => {
|
|
920
|
+
const candidate = unwrapParens(value);
|
|
921
|
+
if (candidate instanceof ValueComponent_1.ColumnReference) {
|
|
922
|
+
const namespace = normalizeIdentifier(candidate.getNamespace());
|
|
923
|
+
if (namespace) {
|
|
924
|
+
if (rootAliases.has(namespace) && !localAliases.has(namespace)) {
|
|
925
|
+
collect(candidate);
|
|
926
|
+
}
|
|
927
|
+
return;
|
|
928
|
+
}
|
|
929
|
+
if (localAliases.size === 0) {
|
|
930
|
+
collect(candidate);
|
|
931
|
+
}
|
|
932
|
+
return;
|
|
933
|
+
}
|
|
934
|
+
if (candidate instanceof ValueComponent_1.BinaryExpression) {
|
|
935
|
+
visit(candidate.left, localAliases);
|
|
936
|
+
visit(candidate.right, localAliases);
|
|
937
|
+
return;
|
|
938
|
+
}
|
|
939
|
+
if (candidate instanceof ValueComponent_1.UnaryExpression) {
|
|
940
|
+
visit(candidate.expression, localAliases);
|
|
941
|
+
return;
|
|
942
|
+
}
|
|
943
|
+
if (candidate instanceof ValueComponent_1.InlineQuery) {
|
|
944
|
+
visitSelect(candidate.selectQuery, localAliases);
|
|
945
|
+
return;
|
|
946
|
+
}
|
|
947
|
+
if (candidate instanceof ValueComponent_1.FunctionCall) {
|
|
948
|
+
if (candidate.argument) {
|
|
949
|
+
visit(candidate.argument, localAliases);
|
|
950
|
+
}
|
|
951
|
+
if (candidate.filterCondition) {
|
|
952
|
+
visit(candidate.filterCondition, localAliases);
|
|
953
|
+
}
|
|
954
|
+
return;
|
|
955
|
+
}
|
|
956
|
+
if (candidate instanceof ValueComponent_1.CastExpression) {
|
|
957
|
+
visit(candidate.input, localAliases);
|
|
958
|
+
return;
|
|
959
|
+
}
|
|
960
|
+
if (candidate instanceof ValueComponent_1.CaseExpression) {
|
|
961
|
+
if (candidate.condition) {
|
|
962
|
+
visit(candidate.condition, localAliases);
|
|
963
|
+
}
|
|
964
|
+
for (const pair of candidate.switchCase.cases) {
|
|
965
|
+
visit(pair.key, localAliases);
|
|
966
|
+
visit(pair.value, localAliases);
|
|
967
|
+
}
|
|
968
|
+
if (candidate.switchCase.elseValue) {
|
|
969
|
+
visit(candidate.switchCase.elseValue, localAliases);
|
|
970
|
+
}
|
|
971
|
+
return;
|
|
972
|
+
}
|
|
973
|
+
if (candidate instanceof ValueComponent_1.BetweenExpression) {
|
|
974
|
+
visit(candidate.expression, localAliases);
|
|
975
|
+
visit(candidate.lower, localAliases);
|
|
976
|
+
visit(candidate.upper, localAliases);
|
|
977
|
+
return;
|
|
978
|
+
}
|
|
979
|
+
if (candidate instanceof ValueComponent_1.JsonPredicateExpression) {
|
|
980
|
+
visit(candidate.expression, localAliases);
|
|
981
|
+
return;
|
|
982
|
+
}
|
|
983
|
+
if (candidate instanceof ValueComponent_1.ArrayExpression) {
|
|
984
|
+
visit(candidate.expression, localAliases);
|
|
985
|
+
return;
|
|
986
|
+
}
|
|
987
|
+
if (candidate instanceof ValueComponent_1.ArrayQueryExpression) {
|
|
988
|
+
visitSelect(candidate.query, localAliases);
|
|
989
|
+
return;
|
|
990
|
+
}
|
|
991
|
+
if (candidate instanceof ValueComponent_1.ValueList) {
|
|
992
|
+
candidate.values.forEach(item => visit(item, localAliases));
|
|
993
|
+
return;
|
|
994
|
+
}
|
|
995
|
+
if (candidate instanceof ValueComponent_1.TupleExpression) {
|
|
996
|
+
candidate.values.forEach(item => visit(item, localAliases));
|
|
997
|
+
return;
|
|
998
|
+
}
|
|
999
|
+
if (candidate instanceof ValueComponent_1.TypeValue && candidate.argument) {
|
|
1000
|
+
visit(candidate.argument, localAliases);
|
|
1001
|
+
}
|
|
1002
|
+
};
|
|
1003
|
+
visit(expression, new Set());
|
|
1004
|
+
return references;
|
|
1005
|
+
}
|
|
1006
|
+
hasWindowUsage(query) {
|
|
1007
|
+
if (query.windowClause) {
|
|
1008
|
+
return true;
|
|
1009
|
+
}
|
|
1010
|
+
let found = false;
|
|
1011
|
+
const visit = (value) => {
|
|
1012
|
+
if (found) {
|
|
1013
|
+
return;
|
|
1014
|
+
}
|
|
1015
|
+
const candidate = unwrapParens(value);
|
|
1016
|
+
if (candidate instanceof ValueComponent_1.FunctionCall) {
|
|
1017
|
+
if (candidate.over) {
|
|
1018
|
+
found = true;
|
|
1019
|
+
return;
|
|
1020
|
+
}
|
|
1021
|
+
if (candidate.argument) {
|
|
1022
|
+
visit(candidate.argument);
|
|
1023
|
+
}
|
|
1024
|
+
if (candidate.filterCondition) {
|
|
1025
|
+
visit(candidate.filterCondition);
|
|
1026
|
+
}
|
|
1027
|
+
return;
|
|
1028
|
+
}
|
|
1029
|
+
if (candidate instanceof ValueComponent_1.BinaryExpression) {
|
|
1030
|
+
visit(candidate.left);
|
|
1031
|
+
visit(candidate.right);
|
|
1032
|
+
return;
|
|
1033
|
+
}
|
|
1034
|
+
if (candidate instanceof ValueComponent_1.UnaryExpression) {
|
|
1035
|
+
visit(candidate.expression);
|
|
1036
|
+
return;
|
|
1037
|
+
}
|
|
1038
|
+
if (candidate instanceof ValueComponent_1.CastExpression) {
|
|
1039
|
+
visit(candidate.input);
|
|
1040
|
+
return;
|
|
1041
|
+
}
|
|
1042
|
+
if (candidate instanceof ValueComponent_1.CaseExpression) {
|
|
1043
|
+
if (candidate.condition) {
|
|
1044
|
+
visit(candidate.condition);
|
|
1045
|
+
}
|
|
1046
|
+
for (const pair of candidate.switchCase.cases) {
|
|
1047
|
+
visit(pair.key);
|
|
1048
|
+
visit(pair.value);
|
|
1049
|
+
}
|
|
1050
|
+
if (candidate.switchCase.elseValue) {
|
|
1051
|
+
visit(candidate.switchCase.elseValue);
|
|
1052
|
+
}
|
|
1053
|
+
return;
|
|
1054
|
+
}
|
|
1055
|
+
if (candidate instanceof ValueComponent_1.ValueList) {
|
|
1056
|
+
candidate.values.forEach(visit);
|
|
1057
|
+
}
|
|
1058
|
+
};
|
|
1059
|
+
query.selectClause.items.forEach(item => visit(item.value));
|
|
1060
|
+
return found;
|
|
1061
|
+
}
|
|
1062
|
+
collectParameterNames(expression) {
|
|
1063
|
+
const names = [];
|
|
1064
|
+
const visitSelect = (query) => {
|
|
1065
|
+
var _a, _b, _c, _d;
|
|
1066
|
+
if (query instanceof SelectQuery_1.BinarySelectQuery) {
|
|
1067
|
+
visitSelect(query.left);
|
|
1068
|
+
visitSelect(query.right);
|
|
1069
|
+
return;
|
|
1070
|
+
}
|
|
1071
|
+
if (!(query instanceof SelectQuery_1.SimpleSelectQuery)) {
|
|
1072
|
+
return;
|
|
1073
|
+
}
|
|
1074
|
+
query.selectClause.items.forEach(item => visit(item.value));
|
|
1075
|
+
for (const join of (_b = (_a = query.fromClause) === null || _a === void 0 ? void 0 : _a.joins) !== null && _b !== void 0 ? _b : []) {
|
|
1076
|
+
if (join.condition) {
|
|
1077
|
+
visit(join.condition.condition);
|
|
1078
|
+
}
|
|
1079
|
+
const source = join.source.datasource;
|
|
1080
|
+
if (source instanceof Clause_1.SubQuerySource) {
|
|
1081
|
+
visitSelect(source.query);
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
if (query.whereClause) {
|
|
1085
|
+
visit(query.whereClause.condition);
|
|
1086
|
+
}
|
|
1087
|
+
if (query.havingClause) {
|
|
1088
|
+
visit(query.havingClause.condition);
|
|
1089
|
+
}
|
|
1090
|
+
for (const cte of (_d = (_c = query.withClause) === null || _c === void 0 ? void 0 : _c.tables) !== null && _d !== void 0 ? _d : []) {
|
|
1091
|
+
if (cte.query instanceof SelectQuery_1.SimpleSelectQuery || cte.query instanceof SelectQuery_1.BinarySelectQuery) {
|
|
1092
|
+
visitSelect(cte.query);
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
};
|
|
1096
|
+
const visit = (value) => {
|
|
1097
|
+
const candidate = unwrapParens(value);
|
|
1098
|
+
if (candidate instanceof ValueComponent_1.ParameterExpression) {
|
|
1099
|
+
appendUnique(names, candidate.name.value);
|
|
1100
|
+
return;
|
|
1101
|
+
}
|
|
1102
|
+
if (candidate instanceof ValueComponent_1.BinaryExpression) {
|
|
1103
|
+
visit(candidate.left);
|
|
1104
|
+
visit(candidate.right);
|
|
1105
|
+
return;
|
|
1106
|
+
}
|
|
1107
|
+
if (candidate instanceof ValueComponent_1.UnaryExpression) {
|
|
1108
|
+
visit(candidate.expression);
|
|
1109
|
+
return;
|
|
1110
|
+
}
|
|
1111
|
+
if (candidate instanceof ValueComponent_1.InlineQuery) {
|
|
1112
|
+
visitSelect(candidate.selectQuery);
|
|
1113
|
+
return;
|
|
1114
|
+
}
|
|
1115
|
+
if (candidate instanceof ValueComponent_1.ArrayQueryExpression) {
|
|
1116
|
+
visitSelect(candidate.query);
|
|
1117
|
+
return;
|
|
1118
|
+
}
|
|
1119
|
+
if (candidate instanceof ValueComponent_1.FunctionCall) {
|
|
1120
|
+
if (candidate.argument) {
|
|
1121
|
+
visit(candidate.argument);
|
|
1122
|
+
}
|
|
1123
|
+
if (candidate.filterCondition) {
|
|
1124
|
+
visit(candidate.filterCondition);
|
|
1125
|
+
}
|
|
1126
|
+
return;
|
|
1127
|
+
}
|
|
1128
|
+
if (candidate instanceof ValueComponent_1.CastExpression) {
|
|
1129
|
+
visit(candidate.input);
|
|
1130
|
+
return;
|
|
1131
|
+
}
|
|
1132
|
+
if (candidate instanceof ValueComponent_1.CaseExpression) {
|
|
1133
|
+
if (candidate.condition) {
|
|
1134
|
+
visit(candidate.condition);
|
|
1135
|
+
}
|
|
1136
|
+
for (const pair of candidate.switchCase.cases) {
|
|
1137
|
+
visit(pair.key);
|
|
1138
|
+
visit(pair.value);
|
|
1139
|
+
}
|
|
1140
|
+
if (candidate.switchCase.elseValue) {
|
|
1141
|
+
visit(candidate.switchCase.elseValue);
|
|
1142
|
+
}
|
|
1143
|
+
return;
|
|
1144
|
+
}
|
|
1145
|
+
if (candidate instanceof ValueComponent_1.BetweenExpression) {
|
|
1146
|
+
visit(candidate.expression);
|
|
1147
|
+
visit(candidate.lower);
|
|
1148
|
+
visit(candidate.upper);
|
|
1149
|
+
return;
|
|
1150
|
+
}
|
|
1151
|
+
if (candidate instanceof ValueComponent_1.JsonPredicateExpression) {
|
|
1152
|
+
visit(candidate.expression);
|
|
1153
|
+
return;
|
|
1154
|
+
}
|
|
1155
|
+
if (candidate instanceof ValueComponent_1.ArrayExpression) {
|
|
1156
|
+
visit(candidate.expression);
|
|
1157
|
+
return;
|
|
1158
|
+
}
|
|
1159
|
+
if (candidate instanceof ValueComponent_1.ValueList) {
|
|
1160
|
+
candidate.values.forEach(visit);
|
|
1161
|
+
return;
|
|
1162
|
+
}
|
|
1163
|
+
if (candidate instanceof ValueComponent_1.TupleExpression) {
|
|
1164
|
+
candidate.values.forEach(visit);
|
|
1165
|
+
return;
|
|
1166
|
+
}
|
|
1167
|
+
if (candidate instanceof ValueComponent_1.TypeValue && candidate.argument) {
|
|
1168
|
+
visit(candidate.argument);
|
|
1169
|
+
}
|
|
1170
|
+
};
|
|
1171
|
+
visit(expression);
|
|
1172
|
+
return names;
|
|
1173
|
+
}
|
|
1174
|
+
makeSkipped(expression, draft) {
|
|
1175
|
+
return {
|
|
1176
|
+
predicateSql: (0, SqlComponentFormatter_1.formatSqlComponent)(expression),
|
|
1177
|
+
scopeId: "scope:root",
|
|
1178
|
+
...draft
|
|
1179
|
+
};
|
|
1180
|
+
}
|
|
1181
|
+
buildResult(params) {
|
|
1182
|
+
return {
|
|
1183
|
+
ok: params.errors.length === 0,
|
|
1184
|
+
sql: params.sql,
|
|
1185
|
+
applied: params.applied,
|
|
1186
|
+
skipped: params.skipped,
|
|
1187
|
+
warnings: params.warnings,
|
|
1188
|
+
errors: params.errors,
|
|
1189
|
+
safety: {
|
|
1190
|
+
mode: "safe_only",
|
|
1191
|
+
unsafeRewriteApplied: false,
|
|
1192
|
+
dryRun: params.dryRun,
|
|
1193
|
+
formatterGeneratedSource: params.formatterGeneratedSource
|
|
1194
|
+
},
|
|
1195
|
+
staticPredicateMoves: params.applied
|
|
1196
|
+
};
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
exports.StaticPredicatePlacementOptimizer = StaticPredicatePlacementOptimizer;
|
|
1200
|
+
const planStaticPredicatePlacement = (input, options = {}) => {
|
|
1201
|
+
return new StaticPredicatePlacementOptimizer().plan(input, options);
|
|
1202
|
+
};
|
|
1203
|
+
exports.planStaticPredicatePlacement = planStaticPredicatePlacement;
|
|
1204
|
+
const optimizeStaticPredicatePlacement = (input, options = {}) => {
|
|
1205
|
+
return new StaticPredicatePlacementOptimizer().optimize(input, options);
|
|
1206
|
+
};
|
|
1207
|
+
exports.optimizeStaticPredicatePlacement = optimizeStaticPredicatePlacement;
|
|
1208
|
+
//# sourceMappingURL=StaticPredicatePlacementOptimizer.js.map
|