rawsql-ts 0.26.0 → 0.27.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 +2 -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 +11 -1
- package/dist/esm/transformers/ConditionOptimization.js +122 -66
- package/dist/esm/transformers/ConditionOptimization.js.map +1 -1
- package/dist/esm/transformers/ParameterConditionPlacementOptimizer.d.ts +26 -7
- package/dist/esm/transformers/ParameterConditionPlacementOptimizer.js +408 -141
- 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 +11 -0
- package/dist/esm/transformers/SqlComponentFormatter.js +16 -0
- package/dist/esm/transformers/SqlComponentFormatter.js.map +1 -0
- package/dist/esm/transformers/StaticPredicatePlacementOptimizer.d.ts +103 -0
- package/dist/esm/transformers/StaticPredicatePlacementOptimizer.js +1435 -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 +2 -0
- package/dist/src/transformers/ConditionOptimization.d.ts +11 -1
- package/dist/src/transformers/ParameterConditionPlacementOptimizer.d.ts +26 -7
- package/dist/src/transformers/SSSQLFilterBuilder.d.ts +6 -0
- package/dist/src/transformers/SqlComponentFormatter.d.ts +11 -0
- package/dist/src/transformers/StaticPredicatePlacementOptimizer.d.ts +103 -0
- package/dist/transformers/ConditionOptimization.js +146 -65
- package/dist/transformers/ConditionOptimization.js.map +1 -1
- package/dist/transformers/ParameterConditionPlacementOptimizer.js +407 -140
- 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 +21 -0
- package/dist/transformers/SqlComponentFormatter.js.map +1 -0
- package/dist/transformers/StaticPredicatePlacementOptimizer.js +1445 -0
- package/dist/transformers/StaticPredicatePlacementOptimizer.js.map +1 -0
- package/dist/tsconfig.browser.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -1,15 +1,13 @@
|
|
|
1
|
-
import { SubQuerySource, TableSource, WhereClause } from "../models/Clause";
|
|
1
|
+
import { DistinctOn, SubQuerySource, TableSource, WhereClause } from "../models/Clause";
|
|
2
2
|
import { BinarySelectQuery, SimpleSelectQuery } from "../models/SelectQuery";
|
|
3
3
|
import { ArrayExpression, ArrayQueryExpression, BetweenExpression, BinaryExpression, CaseExpression, CastExpression, ColumnReference, FunctionCall, InlineQuery, JsonPredicateExpression, ParameterExpression, ParenExpression, TupleExpression, TypeValue, UnaryExpression, ValueList } from "../models/ValueComponent";
|
|
4
4
|
import { SelectQueryParser } from "../parsers/SelectQueryParser";
|
|
5
5
|
import { ValueParser } from "../parsers/ValueParser";
|
|
6
|
-
import {
|
|
6
|
+
import { formatSqlComponent, hasSqlComponentFormatOverride } from "./SqlComponentFormatter";
|
|
7
|
+
import { SelectOutputCollector } from "./SelectOutputCollector";
|
|
7
8
|
const SUPPORTED_OPERATORS = new Set(["=", "<>", "!=", "<", "<=", ">", ">=", "like", "ilike", "in"]);
|
|
8
9
|
const VOLATILE_OR_UNSUPPORTED_FUNCTION_REASON = "Condition contains a function call; volatile and expression predicates are not moved in the safe-only implementation.";
|
|
9
10
|
const normalizeIdentifier = (value) => value.trim().toLowerCase();
|
|
10
|
-
const formatSqlComponent = (component) => {
|
|
11
|
-
return new SqlFormatter().format(component).formattedSql;
|
|
12
|
-
};
|
|
13
11
|
const unwrapParens = (expression) => {
|
|
14
12
|
let candidate = expression;
|
|
15
13
|
while (candidate instanceof ParenExpression) {
|
|
@@ -48,8 +46,8 @@ const rebuildWhereWithoutTerms = (query, termsToRemove) => {
|
|
|
48
46
|
}
|
|
49
47
|
query.whereClause = new WhereClause(rebuilt);
|
|
50
48
|
};
|
|
51
|
-
const cloneValueComponent = (expression) => {
|
|
52
|
-
return ValueParser.parse(formatSqlComponent(expression));
|
|
49
|
+
const cloneValueComponent = (expression, options) => {
|
|
50
|
+
return ValueParser.parse(formatSqlComponent(expression, options));
|
|
53
51
|
};
|
|
54
52
|
const cloneColumnReference = (reference) => {
|
|
55
53
|
var _a, _b;
|
|
@@ -72,11 +70,12 @@ const appendUnique = (items, value) => {
|
|
|
72
70
|
export class ParameterConditionPlacementOptimizer {
|
|
73
71
|
plan(input, options = {}) {
|
|
74
72
|
var _a, _b, _c;
|
|
75
|
-
const parsed = this.parseInput(input);
|
|
73
|
+
const parsed = this.parseInput(input, options);
|
|
76
74
|
const warnings = [...parsed.warnings];
|
|
77
75
|
const errors = [...parsed.errors];
|
|
78
76
|
if (!parsed.query) {
|
|
79
77
|
return this.buildResult({
|
|
78
|
+
query: null,
|
|
80
79
|
sql: parsed.sql,
|
|
81
80
|
applied: [],
|
|
82
81
|
skipped: [],
|
|
@@ -92,6 +91,7 @@ export class ParameterConditionPlacementOptimizer {
|
|
|
92
91
|
message: "Parameter condition placement currently supports only SimpleSelectQuery roots."
|
|
93
92
|
});
|
|
94
93
|
return this.buildResult({
|
|
94
|
+
query: parsed.query,
|
|
95
95
|
sql: parsed.sql,
|
|
96
96
|
applied: [],
|
|
97
97
|
skipped: [],
|
|
@@ -106,40 +106,75 @@ export class ParameterConditionPlacementOptimizer {
|
|
|
106
106
|
const skipped = [];
|
|
107
107
|
const movedTerms = [];
|
|
108
108
|
for (const term of query.whereClause ? collectTopLevelAndTerms(query.whereClause.condition) : []) {
|
|
109
|
-
const candidate = this.analyzeCandidate(term);
|
|
109
|
+
const candidate = this.analyzeCandidate(term, options);
|
|
110
110
|
if (!candidate) {
|
|
111
111
|
continue;
|
|
112
112
|
}
|
|
113
113
|
if ("code" in candidate) {
|
|
114
|
-
skipped.push(this.makeSkipped(term, candidate));
|
|
114
|
+
skipped.push(this.makeSkipped(term, candidate, options));
|
|
115
115
|
continue;
|
|
116
116
|
}
|
|
117
117
|
const target = this.resolveTarget(query, candidate);
|
|
118
118
|
if ("code" in target) {
|
|
119
|
-
skipped.push(this.makeSkipped(term, target));
|
|
119
|
+
skipped.push(this.makeSkipped(term, target, options));
|
|
120
120
|
continue;
|
|
121
121
|
}
|
|
122
|
-
|
|
123
|
-
if ("
|
|
124
|
-
|
|
125
|
-
|
|
122
|
+
let appliedReason = "";
|
|
123
|
+
if (target.kind === "simple") {
|
|
124
|
+
const targetColumns = this.resolveTargetColumns(query, target.query, candidate.references);
|
|
125
|
+
if ("code" in targetColumns) {
|
|
126
|
+
skipped.push(this.makeSkipped(term, targetColumns, options));
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
const placement = this.resolveTargetPlacement(target.query, targetColumns);
|
|
130
|
+
if ("code" in placement) {
|
|
131
|
+
skipped.push(this.makeSkipped(term, placement, options));
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
const movedCondition = this.rebaseCondition(candidate.expression, targetColumns, options);
|
|
135
|
+
target.query.appendWhere(movedCondition);
|
|
136
|
+
appliedReason = placement.reason;
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
const placements = [];
|
|
140
|
+
let skip = null;
|
|
141
|
+
for (const branch of target.branches) {
|
|
142
|
+
const placement = this.resolveTargetPlacement(branch.query, branch.targetColumns);
|
|
143
|
+
if ("code" in placement) {
|
|
144
|
+
skip = placement;
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
placements.push(placement);
|
|
148
|
+
}
|
|
149
|
+
if (skip) {
|
|
150
|
+
skipped.push(this.makeSkipped(term, skip, options));
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
for (const branch of target.branches) {
|
|
154
|
+
const movedCondition = this.rebaseCondition(candidate.expression, branch.targetColumns, options);
|
|
155
|
+
branch.query.appendWhere(movedCondition);
|
|
156
|
+
}
|
|
157
|
+
appliedReason = placements.some(item => /group by/i.test(item.reason))
|
|
158
|
+
? "Condition is distributed to every UNION branch by output column position; grouped branches only receive GROUP BY-key predicates."
|
|
159
|
+
: "Condition is distributed to every UNION branch by output column position before unsafe query boundaries.";
|
|
126
160
|
}
|
|
127
|
-
const movedCondition = this.rebaseCondition(candidate.expression, candidate.column, targetColumn.column);
|
|
128
|
-
target.query.appendWhere(movedCondition);
|
|
129
161
|
movedTerms.push(term);
|
|
130
162
|
applied.push({
|
|
131
163
|
kind: "move_condition",
|
|
132
164
|
conditionSql: candidate.conditionSql,
|
|
133
165
|
fromScopeId: "scope:root",
|
|
134
166
|
toScopeId: target.scopeId,
|
|
135
|
-
reason:
|
|
167
|
+
reason: appliedReason,
|
|
136
168
|
parameterNames: candidate.parameterNames,
|
|
137
|
-
columnReferences:
|
|
169
|
+
columnReferences: candidate.references.map(columnReferenceText)
|
|
138
170
|
});
|
|
139
171
|
}
|
|
140
172
|
rebuildWhereWithoutTerms(query, new Set(movedTerms));
|
|
141
|
-
const sql = applied.length > 0
|
|
173
|
+
const sql = applied.length > 0 || hasSqlComponentFormatOverride(options)
|
|
174
|
+
? formatSqlComponent(query, options)
|
|
175
|
+
: parsed.sql;
|
|
142
176
|
return this.buildResult({
|
|
177
|
+
query,
|
|
143
178
|
sql,
|
|
144
179
|
applied,
|
|
145
180
|
skipped,
|
|
@@ -153,13 +188,22 @@ export class ParameterConditionPlacementOptimizer {
|
|
|
153
188
|
var _a;
|
|
154
189
|
return this.plan(input, Object.assign(Object.assign({}, options), { dryRun: (_a = options.dryRun) !== null && _a !== void 0 ? _a : false }));
|
|
155
190
|
}
|
|
156
|
-
parseInput(input) {
|
|
191
|
+
parseInput(input, options) {
|
|
157
192
|
const warnings = [];
|
|
158
193
|
const errors = [];
|
|
159
194
|
try {
|
|
160
195
|
const sourceSql = typeof input === "string"
|
|
161
196
|
? input
|
|
162
|
-
: formatSqlComponent(input);
|
|
197
|
+
: formatSqlComponent(input, options);
|
|
198
|
+
if (typeof input !== "string" && options.cloneInput === false) {
|
|
199
|
+
return {
|
|
200
|
+
query: input,
|
|
201
|
+
sql: sourceSql,
|
|
202
|
+
formatterGeneratedSource: false,
|
|
203
|
+
warnings,
|
|
204
|
+
errors
|
|
205
|
+
};
|
|
206
|
+
}
|
|
163
207
|
if (typeof input !== "string") {
|
|
164
208
|
warnings.push({
|
|
165
209
|
code: "AST_INPUT_FORMATTED",
|
|
@@ -190,7 +234,7 @@ export class ParameterConditionPlacementOptimizer {
|
|
|
190
234
|
};
|
|
191
235
|
}
|
|
192
236
|
}
|
|
193
|
-
analyzeCandidate(expression) {
|
|
237
|
+
analyzeCandidate(expression, options) {
|
|
194
238
|
const parameterNames = this.collectParameterNames(expression);
|
|
195
239
|
if (parameterNames.length === 0) {
|
|
196
240
|
return null;
|
|
@@ -199,40 +243,22 @@ export class ParameterConditionPlacementOptimizer {
|
|
|
199
243
|
if (unsupported) {
|
|
200
244
|
return unsupported;
|
|
201
245
|
}
|
|
202
|
-
const candidate = unwrapParens(expression);
|
|
203
|
-
if (!(candidate instanceof BinaryExpression)) {
|
|
204
|
-
return {
|
|
205
|
-
code: "UNSUPPORTED_PARAMETER_CONDITION",
|
|
206
|
-
reason: "Only simple binary parameter predicates are moved in the safe-only implementation."
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
const operator = candidate.operator.value.trim().toLowerCase();
|
|
210
|
-
if (!SUPPORTED_OPERATORS.has(operator)) {
|
|
211
|
-
return {
|
|
212
|
-
code: "UNSUPPORTED_OPERATOR",
|
|
213
|
-
reason: `Operator '${candidate.operator.value}' is not supported for safe-only parameter condition placement.`
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
246
|
const columnReferences = this.collectColumnReferences(expression);
|
|
217
|
-
if (columnReferences.length
|
|
247
|
+
if (columnReferences.length === 0) {
|
|
218
248
|
return {
|
|
219
249
|
code: "AMBIGUOUS_COLUMN_REFERENCE",
|
|
220
|
-
reason:
|
|
221
|
-
? "Parameter condition has no column reference to anchor the move."
|
|
222
|
-
: "Parameter condition references multiple columns; moving it may change semantics."
|
|
250
|
+
reason: "Parameter condition has no column reference to anchor the move."
|
|
223
251
|
};
|
|
224
252
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
reason: "Only simple column IN (:parameter) predicates are moved in the safe-only implementation."
|
|
229
|
-
};
|
|
253
|
+
const unsupportedShape = this.findUnsupportedParameterPredicateShape(expression);
|
|
254
|
+
if (unsupportedShape) {
|
|
255
|
+
return unsupportedShape;
|
|
230
256
|
}
|
|
231
257
|
return {
|
|
232
258
|
expression,
|
|
233
|
-
conditionSql: formatSqlComponent(expression),
|
|
259
|
+
conditionSql: formatSqlComponent(expression, options),
|
|
234
260
|
parameterNames,
|
|
235
|
-
|
|
261
|
+
references: columnReferences
|
|
236
262
|
};
|
|
237
263
|
}
|
|
238
264
|
findUnsupportedExpression(expression) {
|
|
@@ -243,13 +269,6 @@ export class ParameterConditionPlacementOptimizer {
|
|
|
243
269
|
}
|
|
244
270
|
const candidate = unwrapParens(value);
|
|
245
271
|
if (candidate instanceof BinaryExpression) {
|
|
246
|
-
if (candidate.operator.value.trim().toLowerCase() === "or") {
|
|
247
|
-
found = {
|
|
248
|
-
code: "OR_PREDICATE_UNSUPPORTED",
|
|
249
|
-
reason: "Condition contains OR predicates, which are not moved in the first safe-only implementation."
|
|
250
|
-
};
|
|
251
|
-
return;
|
|
252
|
-
}
|
|
253
272
|
visit(candidate.left);
|
|
254
273
|
visit(candidate.right);
|
|
255
274
|
return;
|
|
@@ -275,13 +294,6 @@ export class ParameterConditionPlacementOptimizer {
|
|
|
275
294
|
};
|
|
276
295
|
return;
|
|
277
296
|
}
|
|
278
|
-
if (candidate instanceof BetweenExpression) {
|
|
279
|
-
found = {
|
|
280
|
-
code: "BETWEEN_PREDICATE_UNSUPPORTED",
|
|
281
|
-
reason: "BETWEEN predicates are not moved in the first safe-only implementation."
|
|
282
|
-
};
|
|
283
|
-
return;
|
|
284
|
-
}
|
|
285
297
|
if (candidate instanceof UnaryExpression) {
|
|
286
298
|
visit(candidate.expression);
|
|
287
299
|
return;
|
|
@@ -313,6 +325,39 @@ export class ParameterConditionPlacementOptimizer {
|
|
|
313
325
|
visit(expression);
|
|
314
326
|
return found;
|
|
315
327
|
}
|
|
328
|
+
findUnsupportedParameterPredicateShape(expression) {
|
|
329
|
+
const visit = (value) => {
|
|
330
|
+
var _a;
|
|
331
|
+
const candidate = unwrapParens(value);
|
|
332
|
+
if (candidate instanceof BetweenExpression) {
|
|
333
|
+
return null;
|
|
334
|
+
}
|
|
335
|
+
if (candidate instanceof BinaryExpression) {
|
|
336
|
+
const operator = candidate.operator.value.trim().toLowerCase();
|
|
337
|
+
if (operator === "and" || operator === "or") {
|
|
338
|
+
return (_a = visit(candidate.left)) !== null && _a !== void 0 ? _a : visit(candidate.right);
|
|
339
|
+
}
|
|
340
|
+
if (!SUPPORTED_OPERATORS.has(operator)) {
|
|
341
|
+
return {
|
|
342
|
+
code: "UNSUPPORTED_OPERATOR",
|
|
343
|
+
reason: `Operator '${candidate.operator.value}' is not supported for safe-only parameter condition placement.`
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
if (operator === "in" && !this.isSupportedInPredicate(candidate)) {
|
|
347
|
+
return {
|
|
348
|
+
code: "UNSUPPORTED_IN_PREDICATE",
|
|
349
|
+
reason: "Only simple column IN (:parameter) predicates are moved in the safe-only implementation."
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
return null;
|
|
353
|
+
}
|
|
354
|
+
return {
|
|
355
|
+
code: "UNSUPPORTED_PARAMETER_CONDITION",
|
|
356
|
+
reason: "Only simple binary, BETWEEN, and whole OR/AND parameter predicates are moved in the safe-only implementation."
|
|
357
|
+
};
|
|
358
|
+
};
|
|
359
|
+
return visit(expression);
|
|
360
|
+
}
|
|
316
361
|
isSupportedInPredicate(expression) {
|
|
317
362
|
const left = unwrapParens(expression.left);
|
|
318
363
|
const right = unwrapParens(expression.right);
|
|
@@ -329,7 +374,7 @@ export class ParameterConditionPlacementOptimizer {
|
|
|
329
374
|
&& right.values.every(value => unwrapParens(value) instanceof ParameterExpression);
|
|
330
375
|
}
|
|
331
376
|
resolveTarget(root, candidate) {
|
|
332
|
-
const boundary = this.
|
|
377
|
+
const boundary = this.findRootQueryBoundary(root);
|
|
333
378
|
if (boundary) {
|
|
334
379
|
return boundary;
|
|
335
380
|
}
|
|
@@ -339,35 +384,38 @@ export class ParameterConditionPlacementOptimizer {
|
|
|
339
384
|
reason: "Condition has no FROM source that can receive the predicate safely."
|
|
340
385
|
};
|
|
341
386
|
}
|
|
342
|
-
const
|
|
343
|
-
|
|
344
|
-
|
|
387
|
+
const bindings = [];
|
|
388
|
+
for (const reference of candidate.references) {
|
|
389
|
+
const binding = this.resolveSourceBinding(root, root, reference);
|
|
390
|
+
if ("code" in binding) {
|
|
391
|
+
return binding;
|
|
392
|
+
}
|
|
393
|
+
if (!bindings.some(item => item.source === binding.source)) {
|
|
394
|
+
bindings.push(binding);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
if (bindings.length !== 1) {
|
|
398
|
+
return {
|
|
399
|
+
code: "MULTIPLE_SOURCE_REFERENCES",
|
|
400
|
+
reason: "Parameter condition references multiple source query blocks; moving it may change semantics."
|
|
401
|
+
};
|
|
345
402
|
}
|
|
346
|
-
const
|
|
403
|
+
const binding = bindings[0];
|
|
404
|
+
const nullableSide = this.findNullableSideBoundary(root.fromClause, binding);
|
|
347
405
|
if (nullableSide) {
|
|
348
406
|
return nullableSide;
|
|
349
407
|
}
|
|
350
|
-
const upstream = this.resolveUpstreamQuery(root,
|
|
408
|
+
const upstream = this.resolveUpstreamQuery(root, binding, candidate.references);
|
|
351
409
|
if ("code" in upstream) {
|
|
352
410
|
return upstream;
|
|
353
411
|
}
|
|
354
|
-
const targetBoundary = this.findTargetQueryBoundary(upstream.query);
|
|
355
|
-
if (targetBoundary) {
|
|
356
|
-
return targetBoundary;
|
|
357
|
-
}
|
|
358
412
|
return upstream;
|
|
359
413
|
}
|
|
360
|
-
|
|
361
|
-
if (query
|
|
414
|
+
findRootQueryBoundary(query) {
|
|
415
|
+
if (this.hasDistinctOnBoundary(query)) {
|
|
362
416
|
return {
|
|
363
417
|
code: "DISTINCT_BOUNDARY",
|
|
364
|
-
reason: "Condition crosses DISTINCT boundary; moving it may change semantics."
|
|
365
|
-
};
|
|
366
|
-
}
|
|
367
|
-
if (query.groupByClause || query.havingClause) {
|
|
368
|
-
return {
|
|
369
|
-
code: "GROUP_BY_BOUNDARY",
|
|
370
|
-
reason: "Condition crosses GROUP BY boundary; moving it may change semantics."
|
|
418
|
+
reason: "Condition crosses DISTINCT ON boundary; moving it may change semantics."
|
|
371
419
|
};
|
|
372
420
|
}
|
|
373
421
|
if (this.hasWindowUsage(query)) {
|
|
@@ -378,10 +426,19 @@ export class ParameterConditionPlacementOptimizer {
|
|
|
378
426
|
}
|
|
379
427
|
return null;
|
|
380
428
|
}
|
|
381
|
-
|
|
382
|
-
const
|
|
383
|
-
if (
|
|
384
|
-
return
|
|
429
|
+
resolveTargetPlacement(query, targetColumns) {
|
|
430
|
+
const hasOrdinaryDistinct = this.hasOrdinaryDistinct(query);
|
|
431
|
+
if (this.hasDistinctOnBoundary(query)) {
|
|
432
|
+
return {
|
|
433
|
+
code: "DISTINCT_BOUNDARY",
|
|
434
|
+
reason: "Condition crosses DISTINCT ON boundary; moving it may change semantics."
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
if (this.hasWindowUsage(query)) {
|
|
438
|
+
return {
|
|
439
|
+
code: "WINDOW_BOUNDARY",
|
|
440
|
+
reason: "Condition crosses WINDOW boundary; moving it may change semantics."
|
|
441
|
+
};
|
|
385
442
|
}
|
|
386
443
|
if (query.limitClause || query.offsetClause || query.fetchClause) {
|
|
387
444
|
return {
|
|
@@ -395,10 +452,35 @@ export class ParameterConditionPlacementOptimizer {
|
|
|
395
452
|
reason: "Target query contains an OUTER JOIN boundary that is not moved across in the safe-only implementation."
|
|
396
453
|
};
|
|
397
454
|
}
|
|
398
|
-
|
|
455
|
+
if (query.groupByClause) {
|
|
456
|
+
const allReferencesAreGroupKeys = targetColumns.every(item => this.isGroupKeyColumn(query, item.targetColumn));
|
|
457
|
+
if (!allReferencesAreGroupKeys) {
|
|
458
|
+
return {
|
|
459
|
+
code: "GROUP_BY_BOUNDARY",
|
|
460
|
+
reason: "Condition references a target column that is not proven to be a GROUP BY key."
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
return {
|
|
464
|
+
reason: "Condition references only GROUP BY keys; it is moved into pre-aggregation WHERE."
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
if (query.havingClause) {
|
|
468
|
+
return {
|
|
469
|
+
code: "GROUP_BY_BOUNDARY",
|
|
470
|
+
reason: "Condition crosses HAVING aggregation boundary; moving it may change semantics."
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
if (hasOrdinaryDistinct) {
|
|
474
|
+
return {
|
|
475
|
+
reason: "Condition references a direct ordinary DISTINCT output column; it is moved into the DISTINCT input WHERE."
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
return {
|
|
479
|
+
reason: "All referenced columns resolve to a single direct upstream output before unsafe query boundaries."
|
|
480
|
+
};
|
|
399
481
|
}
|
|
400
|
-
resolveSourceBinding(
|
|
401
|
-
const fromClause =
|
|
482
|
+
resolveSourceBinding(contextRoot, query, column) {
|
|
483
|
+
const fromClause = query.fromClause;
|
|
402
484
|
if (!fromClause) {
|
|
403
485
|
return {
|
|
404
486
|
code: "NO_FROM_CLAUSE",
|
|
@@ -416,10 +498,7 @@ export class ParameterConditionPlacementOptimizer {
|
|
|
416
498
|
reason: `Column source alias '${column.getNamespace()}' is not uniquely resolvable.`
|
|
417
499
|
};
|
|
418
500
|
}
|
|
419
|
-
|
|
420
|
-
return matches[0];
|
|
421
|
-
}
|
|
422
|
-
const matchCount = this.getOutputColumnMatchCount(root, matches[0], columnName);
|
|
501
|
+
const matchCount = this.getOutputColumnMatchCount(contextRoot, matches[0], columnName);
|
|
423
502
|
if (matchCount === 0) {
|
|
424
503
|
return {
|
|
425
504
|
code: "COLUMN_NOT_AVAILABLE_UPSTREAM",
|
|
@@ -435,13 +514,8 @@ export class ParameterConditionPlacementOptimizer {
|
|
|
435
514
|
return matches[0];
|
|
436
515
|
}
|
|
437
516
|
const matches = [];
|
|
438
|
-
let unionSource = null;
|
|
439
517
|
for (const binding of bindings) {
|
|
440
|
-
|
|
441
|
-
unionSource !== null && unionSource !== void 0 ? unionSource : (unionSource = binding);
|
|
442
|
-
continue;
|
|
443
|
-
}
|
|
444
|
-
const matchCount = this.getOutputColumnMatchCount(root, binding, columnName);
|
|
518
|
+
const matchCount = this.getOutputColumnMatchCount(contextRoot, binding, columnName);
|
|
445
519
|
if (matchCount > 1) {
|
|
446
520
|
return {
|
|
447
521
|
code: "AMBIGUOUS_COLUMN_REFERENCE",
|
|
@@ -452,12 +526,6 @@ export class ParameterConditionPlacementOptimizer {
|
|
|
452
526
|
matches.push(binding);
|
|
453
527
|
}
|
|
454
528
|
}
|
|
455
|
-
if (matches.length === 0 && unionSource) {
|
|
456
|
-
return {
|
|
457
|
-
code: "UNION_BOUNDARY",
|
|
458
|
-
reason: "Condition would need distribution into a UNION branch, which is unsupported."
|
|
459
|
-
};
|
|
460
|
-
}
|
|
461
529
|
if (matches.length !== 1) {
|
|
462
530
|
return {
|
|
463
531
|
code: "AMBIGUOUS_COLUMN_REFERENCE",
|
|
@@ -468,17 +536,20 @@ export class ParameterConditionPlacementOptimizer {
|
|
|
468
536
|
}
|
|
469
537
|
return matches[0];
|
|
470
538
|
}
|
|
471
|
-
resolveUpstreamQuery(root, binding,
|
|
539
|
+
resolveUpstreamQuery(root, binding, references) {
|
|
472
540
|
const source = binding.source.datasource;
|
|
473
541
|
if (source instanceof SubQuerySource) {
|
|
474
542
|
if (source.query instanceof SimpleSelectQuery) {
|
|
475
543
|
return {
|
|
544
|
+
kind: "simple",
|
|
476
545
|
query: source.query,
|
|
477
546
|
scopeId: `subquery:${binding.alias}`,
|
|
478
|
-
outputColumnName,
|
|
479
547
|
sourceBinding: binding
|
|
480
548
|
};
|
|
481
549
|
}
|
|
550
|
+
if (source.query instanceof BinarySelectQuery) {
|
|
551
|
+
return this.resolveUnionTarget(root, source.query, `subquery:${binding.alias}`, references);
|
|
552
|
+
}
|
|
482
553
|
return {
|
|
483
554
|
code: "UNION_BOUNDARY",
|
|
484
555
|
reason: "Condition would need distribution into a UNION or non-simple subquery, which is unsupported."
|
|
@@ -506,10 +577,7 @@ export class ParameterConditionPlacementOptimizer {
|
|
|
506
577
|
};
|
|
507
578
|
}
|
|
508
579
|
if (commonTable.query instanceof BinarySelectQuery) {
|
|
509
|
-
return {
|
|
510
|
-
code: "UNION_BOUNDARY",
|
|
511
|
-
reason: "Condition would need distribution into a UNION branch, which is unsupported."
|
|
512
|
-
};
|
|
580
|
+
return this.resolveUnionTarget(root, commonTable.query, `cte:${commonTable.getSourceAliasName()}`, references);
|
|
513
581
|
}
|
|
514
582
|
if (!(commonTable.query instanceof SimpleSelectQuery)) {
|
|
515
583
|
return {
|
|
@@ -518,35 +586,140 @@ export class ParameterConditionPlacementOptimizer {
|
|
|
518
586
|
};
|
|
519
587
|
}
|
|
520
588
|
return {
|
|
589
|
+
kind: "simple",
|
|
521
590
|
query: commonTable.query,
|
|
522
591
|
scopeId: `cte:${commonTable.getSourceAliasName()}`,
|
|
523
|
-
outputColumnName,
|
|
524
592
|
sourceBinding: binding,
|
|
525
593
|
cteName: commonTable.getSourceAliasName()
|
|
526
594
|
};
|
|
527
595
|
}
|
|
528
|
-
|
|
529
|
-
const
|
|
530
|
-
if (
|
|
596
|
+
resolveUnionTarget(root, query, scopeId, references) {
|
|
597
|
+
const branches = this.collectUnionBranches(query);
|
|
598
|
+
if ("code" in branches) {
|
|
599
|
+
return branches;
|
|
600
|
+
}
|
|
601
|
+
const outputIndexes = new Map();
|
|
602
|
+
for (const reference of references) {
|
|
603
|
+
const outputIndex = this.resolveUnionOutputIndex(root, branches[0], reference.column.name);
|
|
604
|
+
if ("code" in outputIndex) {
|
|
605
|
+
return outputIndex;
|
|
606
|
+
}
|
|
607
|
+
outputIndexes.set(reference, outputIndex.index);
|
|
608
|
+
}
|
|
609
|
+
const branchTargets = [];
|
|
610
|
+
for (const branch of branches) {
|
|
611
|
+
const targetColumns = [];
|
|
612
|
+
for (const [reference, outputIndex] of outputIndexes.entries()) {
|
|
613
|
+
const targetColumn = this.resolveTargetColumnByOutputIndex(root, branch, outputIndex, reference);
|
|
614
|
+
if ("code" in targetColumn) {
|
|
615
|
+
return targetColumn;
|
|
616
|
+
}
|
|
617
|
+
targetColumns.push(targetColumn);
|
|
618
|
+
}
|
|
619
|
+
branchTargets.push(this.resolveDeepestBranchTarget(root, branch, targetColumns));
|
|
620
|
+
}
|
|
621
|
+
return {
|
|
622
|
+
kind: "union",
|
|
623
|
+
scopeId,
|
|
624
|
+
branches: branchTargets
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
resolveTargetColumns(root, query, references) {
|
|
628
|
+
const resolved = [];
|
|
629
|
+
for (const reference of references) {
|
|
630
|
+
const matches = this.collectSelectOutputs(root, query).filter(item => normalizeIdentifier(item.name) === normalizeIdentifier(reference.column.name));
|
|
631
|
+
if (matches.length !== 1) {
|
|
632
|
+
return {
|
|
633
|
+
code: "AMBIGUOUS_TARGET_COLUMN",
|
|
634
|
+
reason: matches.length === 0
|
|
635
|
+
? `Target query does not expose '${reference.column.name}' as a direct output column.`
|
|
636
|
+
: `Target query exposes multiple '${reference.column.name}' columns.`
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
const value = matches[0].value;
|
|
640
|
+
if (!(value instanceof ColumnReference)) {
|
|
641
|
+
return {
|
|
642
|
+
code: "EXPRESSION_OUTPUT_UNSUPPORTED",
|
|
643
|
+
reason: `Target output '${reference.column.name}' is an expression, not a direct column reference.`
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
const sourceResolution = this.verifyColumnResolvableInQuery(query, value);
|
|
647
|
+
if (sourceResolution) {
|
|
648
|
+
return sourceResolution;
|
|
649
|
+
}
|
|
650
|
+
resolved.push({
|
|
651
|
+
sourceColumn: reference,
|
|
652
|
+
targetColumn: value
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
return resolved;
|
|
656
|
+
}
|
|
657
|
+
resolveTargetColumnByOutputIndex(root, query, outputIndex, sourceColumn) {
|
|
658
|
+
const output = this.collectSelectOutputs(root, query)[outputIndex];
|
|
659
|
+
if (!output) {
|
|
531
660
|
return {
|
|
532
|
-
code: "
|
|
533
|
-
reason:
|
|
534
|
-
? `Target query does not expose '${outputColumnName}' as a direct output column.`
|
|
535
|
-
: `Target query exposes multiple '${outputColumnName}' columns.`
|
|
661
|
+
code: "UNION_COLUMN_MISMATCH",
|
|
662
|
+
reason: `UNION branch does not expose column '${sourceColumn.column.name}' at output position ${outputIndex + 1}.`
|
|
536
663
|
};
|
|
537
664
|
}
|
|
538
|
-
|
|
539
|
-
if (!(value instanceof ColumnReference)) {
|
|
665
|
+
if (!(output.value instanceof ColumnReference)) {
|
|
540
666
|
return {
|
|
541
667
|
code: "EXPRESSION_OUTPUT_UNSUPPORTED",
|
|
542
|
-
reason: `
|
|
668
|
+
reason: `UNION branch output at position ${outputIndex + 1} for '${sourceColumn.column.name}' is an expression, not a direct column reference.`
|
|
543
669
|
};
|
|
544
670
|
}
|
|
545
|
-
const sourceResolution = this.verifyColumnResolvableInQuery(query, value);
|
|
671
|
+
const sourceResolution = this.verifyColumnResolvableInQuery(query, output.value);
|
|
546
672
|
if (sourceResolution) {
|
|
547
673
|
return sourceResolution;
|
|
548
674
|
}
|
|
549
|
-
return {
|
|
675
|
+
return {
|
|
676
|
+
sourceColumn,
|
|
677
|
+
targetColumn: output.value
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
resolveDeepestBranchTarget(contextRoot, query, targetColumns, visited = new Set()) {
|
|
681
|
+
if (visited.has(query) || targetColumns.length === 0) {
|
|
682
|
+
return { query, targetColumns: [...targetColumns] };
|
|
683
|
+
}
|
|
684
|
+
const nextVisited = new Set(visited);
|
|
685
|
+
nextVisited.add(query);
|
|
686
|
+
const bindings = [];
|
|
687
|
+
for (const item of targetColumns) {
|
|
688
|
+
const binding = this.resolveSourceBinding(contextRoot, query, item.targetColumn);
|
|
689
|
+
if ("code" in binding) {
|
|
690
|
+
return { query, targetColumns: [...targetColumns] };
|
|
691
|
+
}
|
|
692
|
+
if (!bindings.some(existing => existing.source === binding.source)) {
|
|
693
|
+
bindings.push(binding);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
if (bindings.length !== 1) {
|
|
697
|
+
return { query, targetColumns: [...targetColumns] };
|
|
698
|
+
}
|
|
699
|
+
const binding = bindings[0];
|
|
700
|
+
const nullableSide = query.fromClause
|
|
701
|
+
? this.findNullableSideBoundary(query.fromClause, binding)
|
|
702
|
+
: null;
|
|
703
|
+
if (nullableSide) {
|
|
704
|
+
return { query, targetColumns: [...targetColumns] };
|
|
705
|
+
}
|
|
706
|
+
const upstream = this.resolveUpstreamQuery(contextRoot, binding, targetColumns.map(item => item.targetColumn));
|
|
707
|
+
if ("code" in upstream || upstream.kind === "union") {
|
|
708
|
+
return { query, targetColumns: [...targetColumns] };
|
|
709
|
+
}
|
|
710
|
+
const upstreamColumns = this.resolveTargetColumns(contextRoot, upstream.query, targetColumns.map(item => item.targetColumn));
|
|
711
|
+
if ("code" in upstreamColumns) {
|
|
712
|
+
return { query, targetColumns: [...targetColumns] };
|
|
713
|
+
}
|
|
714
|
+
const placement = this.resolveTargetPlacement(upstream.query, upstreamColumns);
|
|
715
|
+
if ("code" in placement) {
|
|
716
|
+
return { query, targetColumns: [...targetColumns] };
|
|
717
|
+
}
|
|
718
|
+
const rebasedColumns = upstreamColumns.map((item, index) => ({
|
|
719
|
+
sourceColumn: targetColumns[index].sourceColumn,
|
|
720
|
+
targetColumn: item.targetColumn
|
|
721
|
+
}));
|
|
722
|
+
return this.resolveDeepestBranchTarget(contextRoot, upstream.query, rebasedColumns, nextVisited);
|
|
550
723
|
}
|
|
551
724
|
verifyColumnResolvableInQuery(query, column) {
|
|
552
725
|
if (!query.fromClause) {
|
|
@@ -574,13 +747,47 @@ export class ParameterConditionPlacementOptimizer {
|
|
|
574
747
|
reason: `Unqualified target column '${column.column.name}' is ambiguous in a multi-source destination query.`
|
|
575
748
|
};
|
|
576
749
|
}
|
|
577
|
-
|
|
578
|
-
const
|
|
750
|
+
isGroupKeyColumn(query, column) {
|
|
751
|
+
const groupBy = query.groupByClause;
|
|
752
|
+
if (!groupBy || groupBy.mode || groupBy.grouping.length === 0) {
|
|
753
|
+
return false;
|
|
754
|
+
}
|
|
755
|
+
return groupBy.grouping.some(grouping => {
|
|
756
|
+
const candidate = unwrapParens(grouping);
|
|
757
|
+
return candidate instanceof ColumnReference
|
|
758
|
+
&& this.sameResolvableColumnInQuery(query, candidate, column);
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
sameResolvableColumnInQuery(query, left, right) {
|
|
762
|
+
if (sameColumnReference(left, right)) {
|
|
763
|
+
return true;
|
|
764
|
+
}
|
|
765
|
+
if (normalizeIdentifier(left.column.name) !== normalizeIdentifier(right.column.name)) {
|
|
766
|
+
return false;
|
|
767
|
+
}
|
|
768
|
+
const leftSource = this.resolveColumnSourceAlias(query, left);
|
|
769
|
+
const rightSource = this.resolveColumnSourceAlias(query, right);
|
|
770
|
+
return leftSource !== null && leftSource === rightSource;
|
|
771
|
+
}
|
|
772
|
+
resolveColumnSourceAlias(query, column) {
|
|
773
|
+
if (!query.fromClause) {
|
|
774
|
+
return null;
|
|
775
|
+
}
|
|
776
|
+
const bindings = this.getSourceBindings(query.fromClause);
|
|
777
|
+
const namespace = normalizeIdentifier(column.getNamespace());
|
|
778
|
+
if (namespace) {
|
|
779
|
+
const matches = bindings.filter(binding => normalizeIdentifier(binding.alias) === namespace);
|
|
780
|
+
return matches.length === 1 ? normalizeIdentifier(matches[0].alias) : null;
|
|
781
|
+
}
|
|
782
|
+
return bindings.length === 1 ? normalizeIdentifier(bindings[0].alias) : null;
|
|
783
|
+
}
|
|
784
|
+
rebaseCondition(expression, targetColumns, options) {
|
|
785
|
+
const cloned = cloneValueComponent(expression, options);
|
|
579
786
|
for (const reference of this.collectColumnReferences(cloned)) {
|
|
580
|
-
|
|
581
|
-
|
|
787
|
+
const target = targetColumns.find(item => sameColumnReference(reference, item.sourceColumn));
|
|
788
|
+
if (target) {
|
|
789
|
+
reference.qualifiedName = cloneColumnReference(target.targetColumn).qualifiedName;
|
|
582
790
|
}
|
|
583
|
-
reference.qualifiedName = cloneColumnReference(targetColumn).qualifiedName;
|
|
584
791
|
}
|
|
585
792
|
return cloned;
|
|
586
793
|
}
|
|
@@ -638,12 +845,80 @@ export class ParameterConditionPlacementOptimizer {
|
|
|
638
845
|
return joinType.includes("left") || joinType.includes("right") || joinType.includes("full") || joinType.includes("outer");
|
|
639
846
|
});
|
|
640
847
|
}
|
|
848
|
+
hasDistinctOnBoundary(query) {
|
|
849
|
+
return query.selectClause.distinct instanceof DistinctOn;
|
|
850
|
+
}
|
|
851
|
+
hasOrdinaryDistinct(query) {
|
|
852
|
+
return query.selectClause.distinct !== null && !this.hasDistinctOnBoundary(query);
|
|
853
|
+
}
|
|
641
854
|
getOutputColumnMatchCount(root, binding, columnName) {
|
|
642
855
|
const target = this.resolveSourceQueryForColumns(root, binding.source);
|
|
643
|
-
if (!target
|
|
856
|
+
if (!target) {
|
|
644
857
|
return 0;
|
|
645
858
|
}
|
|
646
|
-
|
|
859
|
+
if (target instanceof BinarySelectQuery) {
|
|
860
|
+
const branches = this.collectUnionBranches(target);
|
|
861
|
+
if ("code" in branches) {
|
|
862
|
+
return 0;
|
|
863
|
+
}
|
|
864
|
+
return this.collectSelectOutputs(root, branches[0]).filter(item => normalizeIdentifier(item.name) === normalizeIdentifier(columnName)).length;
|
|
865
|
+
}
|
|
866
|
+
if (!(target instanceof SimpleSelectQuery)) {
|
|
867
|
+
return 0;
|
|
868
|
+
}
|
|
869
|
+
return this.collectSelectOutputs(root, target).filter(item => normalizeIdentifier(item.name) === normalizeIdentifier(columnName)).length;
|
|
870
|
+
}
|
|
871
|
+
collectUnionBranches(query) {
|
|
872
|
+
const branches = [];
|
|
873
|
+
const visit = (select) => {
|
|
874
|
+
var _a;
|
|
875
|
+
if (select instanceof SimpleSelectQuery) {
|
|
876
|
+
branches.push(select);
|
|
877
|
+
return null;
|
|
878
|
+
}
|
|
879
|
+
if (!(select instanceof BinarySelectQuery)) {
|
|
880
|
+
return {
|
|
881
|
+
code: "UNION_BOUNDARY",
|
|
882
|
+
reason: "Condition would need distribution into a non-simple UNION branch, which is unsupported."
|
|
883
|
+
};
|
|
884
|
+
}
|
|
885
|
+
const operator = select.operator.value.trim().toLowerCase();
|
|
886
|
+
if (operator !== "union" && operator !== "union all") {
|
|
887
|
+
return {
|
|
888
|
+
code: "UNION_BOUNDARY",
|
|
889
|
+
reason: `Condition would need distribution through '${select.operator.value}', which is unsupported.`
|
|
890
|
+
};
|
|
891
|
+
}
|
|
892
|
+
return (_a = visit(select.left)) !== null && _a !== void 0 ? _a : visit(select.right);
|
|
893
|
+
};
|
|
894
|
+
const skip = visit(query);
|
|
895
|
+
return skip !== null && skip !== void 0 ? skip : branches;
|
|
896
|
+
}
|
|
897
|
+
resolveUnionOutputIndex(root, firstBranch, outputColumnName) {
|
|
898
|
+
const matches = [];
|
|
899
|
+
this.collectSelectOutputs(root, firstBranch).forEach((item, index) => {
|
|
900
|
+
if (normalizeIdentifier(item.name) === normalizeIdentifier(outputColumnName)) {
|
|
901
|
+
matches.push(index);
|
|
902
|
+
}
|
|
903
|
+
});
|
|
904
|
+
if (matches.length !== 1) {
|
|
905
|
+
return {
|
|
906
|
+
code: "AMBIGUOUS_TARGET_COLUMN",
|
|
907
|
+
reason: matches.length === 0
|
|
908
|
+
? `UNION output does not expose '${outputColumnName}' from its first branch.`
|
|
909
|
+
: `UNION first branch exposes multiple '${outputColumnName}' columns.`
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
return { index: matches[0] };
|
|
913
|
+
}
|
|
914
|
+
collectSelectOutputs(root, query) {
|
|
915
|
+
var _a, _b, _c, _d;
|
|
916
|
+
const commonTables = [
|
|
917
|
+
...((_b = (_a = query.withClause) === null || _a === void 0 ? void 0 : _a.tables) !== null && _b !== void 0 ? _b : []),
|
|
918
|
+
...((_d = (_c = root.withClause) === null || _c === void 0 ? void 0 : _c.tables) !== null && _d !== void 0 ? _d : [])
|
|
919
|
+
];
|
|
920
|
+
const collector = new SelectOutputCollector(null, commonTables.length > 0 ? commonTables : null);
|
|
921
|
+
return collector.collect(query);
|
|
647
922
|
}
|
|
648
923
|
resolveSourceQueryForColumns(root, source) {
|
|
649
924
|
var _a;
|
|
@@ -658,15 +933,6 @@ export class ParameterConditionPlacementOptimizer {
|
|
|
658
933
|
}
|
|
659
934
|
return null;
|
|
660
935
|
}
|
|
661
|
-
getSelectItemOutputName(item) {
|
|
662
|
-
if (item.identifier) {
|
|
663
|
-
return item.identifier.name;
|
|
664
|
-
}
|
|
665
|
-
if (item.value instanceof ColumnReference) {
|
|
666
|
-
return item.value.column.name;
|
|
667
|
-
}
|
|
668
|
-
return null;
|
|
669
|
-
}
|
|
670
936
|
findCte(root, name) {
|
|
671
937
|
var _a, _b;
|
|
672
938
|
const normalized = normalizeIdentifier(name);
|
|
@@ -912,13 +1178,14 @@ export class ParameterConditionPlacementOptimizer {
|
|
|
912
1178
|
visit(expression);
|
|
913
1179
|
return names;
|
|
914
1180
|
}
|
|
915
|
-
makeSkipped(expression, draft) {
|
|
916
|
-
return Object.assign({ conditionSql: formatSqlComponent(expression), scopeId: "scope:root" }, draft);
|
|
1181
|
+
makeSkipped(expression, draft, options) {
|
|
1182
|
+
return Object.assign({ conditionSql: formatSqlComponent(expression, options), scopeId: "scope:root" }, draft);
|
|
917
1183
|
}
|
|
918
1184
|
buildResult(params) {
|
|
919
1185
|
return {
|
|
920
1186
|
ok: params.errors.length === 0,
|
|
921
1187
|
sql: params.sql,
|
|
1188
|
+
query: params.query,
|
|
922
1189
|
applied: params.applied,
|
|
923
1190
|
skipped: params.skipped,
|
|
924
1191
|
warnings: params.warnings,
|