rawsql-ts 0.28.0 → 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/index.min.js +2 -2
- package/dist/esm/index.min.js.map +4 -4
- package/dist/esm/transformers/ConditionDeduplicationOptimizer.d.ts +22 -0
- package/dist/esm/transformers/ConditionDeduplicationOptimizer.js +198 -0
- package/dist/esm/transformers/ConditionDeduplicationOptimizer.js.map +1 -0
- package/dist/esm/transformers/ConditionOptimization.d.ts +31 -1
- package/dist/esm/transformers/ConditionOptimization.js +321 -4
- package/dist/esm/transformers/ConditionOptimization.js.map +1 -1
- package/dist/esm/transformers/ParameterConditionPlacementOptimizer.d.ts +13 -0
- package/dist/esm/transformers/ParameterConditionPlacementOptimizer.js +238 -87
- package/dist/esm/transformers/ParameterConditionPlacementOptimizer.js.map +1 -1
- package/dist/esm/transformers/PredicateExpressionUtils.d.ts +16 -0
- package/dist/esm/transformers/PredicateExpressionUtils.js +74 -0
- package/dist/esm/transformers/PredicateExpressionUtils.js.map +1 -0
- package/dist/esm/transformers/PredicateReachabilityAnalyzer.d.ts +88 -0
- package/dist/esm/transformers/PredicateReachabilityAnalyzer.js +495 -0
- package/dist/esm/transformers/PredicateReachabilityAnalyzer.js.map +1 -0
- package/dist/esm/transformers/StaticPredicatePlacementOptimizer.d.ts +14 -0
- package/dist/esm/transformers/StaticPredicatePlacementOptimizer.js +344 -157
- package/dist/esm/transformers/StaticPredicatePlacementOptimizer.js.map +1 -1
- package/dist/esm/transformers/TopLevelAndConditionDeduper.d.ts +10 -0
- package/dist/esm/transformers/TopLevelAndConditionDeduper.js +66 -0
- package/dist/esm/transformers/TopLevelAndConditionDeduper.js.map +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +2 -2
- package/dist/index.min.js.map +4 -4
- package/dist/src/index.d.ts +1 -0
- package/dist/src/transformers/ConditionDeduplicationOptimizer.d.ts +22 -0
- package/dist/src/transformers/ConditionOptimization.d.ts +31 -1
- package/dist/src/transformers/ParameterConditionPlacementOptimizer.d.ts +13 -0
- package/dist/src/transformers/PredicateExpressionUtils.d.ts +16 -0
- package/dist/src/transformers/PredicateReachabilityAnalyzer.d.ts +88 -0
- package/dist/src/transformers/StaticPredicatePlacementOptimizer.d.ts +14 -0
- package/dist/src/transformers/TopLevelAndConditionDeduper.d.ts +10 -0
- package/dist/transformers/ConditionDeduplicationOptimizer.js +202 -0
- package/dist/transformers/ConditionDeduplicationOptimizer.js.map +1 -0
- package/dist/transformers/ConditionOptimization.js +324 -4
- package/dist/transformers/ConditionOptimization.js.map +1 -1
- package/dist/transformers/ParameterConditionPlacementOptimizer.js +261 -110
- package/dist/transformers/ParameterConditionPlacementOptimizer.js.map +1 -1
- package/dist/transformers/PredicateExpressionUtils.js +88 -0
- package/dist/transformers/PredicateExpressionUtils.js.map +1 -0
- package/dist/transformers/PredicateReachabilityAnalyzer.js +508 -0
- package/dist/transformers/PredicateReachabilityAnalyzer.js.map +1 -0
- package/dist/transformers/StaticPredicatePlacementOptimizer.js +366 -179
- package/dist/transformers/StaticPredicatePlacementOptimizer.js.map +1 -1
- package/dist/transformers/TopLevelAndConditionDeduper.js +72 -0
- package/dist/transformers/TopLevelAndConditionDeduper.js.map +1 -0
- package/dist/tsconfig.browser.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { SelectQuery } from "../models/SelectQuery";
|
|
2
|
+
import { SqlComponentFormatOptions } from "./SqlComponentFormatter";
|
|
3
|
+
export interface ConditionDeduplicationApplied {
|
|
4
|
+
kind: "dedupe_condition";
|
|
5
|
+
conditionSql: string;
|
|
6
|
+
scopeId: string;
|
|
7
|
+
reason: string;
|
|
8
|
+
}
|
|
9
|
+
export interface ConditionDeduplicationResult {
|
|
10
|
+
/** API output shape review: this phase mutates and returns the model only; ConditionOptimization remains responsible for compatibility sql output. */
|
|
11
|
+
query: SelectQuery | null;
|
|
12
|
+
applied: readonly ConditionDeduplicationApplied[];
|
|
13
|
+
}
|
|
14
|
+
export declare class ConditionDeduplicationOptimizer {
|
|
15
|
+
private visited;
|
|
16
|
+
private applied;
|
|
17
|
+
optimize(query: SelectQuery | null, options: SqlComponentFormatOptions): ConditionDeduplicationResult;
|
|
18
|
+
private visitSelectQuery;
|
|
19
|
+
private visitSourceExpression;
|
|
20
|
+
private visitValueComponent;
|
|
21
|
+
private dedupeCondition;
|
|
22
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { FunctionSource, HavingClause, JoinOnClause, OrderByItem, ParenSource, SubQuerySource, WhereClause } from "../models/Clause";
|
|
2
|
+
import { BinarySelectQuery, SimpleSelectQuery } from "../models/SelectQuery";
|
|
3
|
+
import { ArrayExpression, ArrayIndexExpression, ArrayQueryExpression, ArraySliceExpression, BetweenExpression, BinaryExpression, CaseExpression, CastExpression, FunctionCall, InlineQuery, JsonPredicateExpression, ParenExpression, TupleExpression, TypeValue, UnaryExpression, ValueList } from "../models/ValueComponent";
|
|
4
|
+
import { dedupeTopLevelAndConditionsWithMetadata } from "./TopLevelAndConditionDeduper";
|
|
5
|
+
export class ConditionDeduplicationOptimizer {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.visited = new WeakSet();
|
|
8
|
+
this.applied = [];
|
|
9
|
+
}
|
|
10
|
+
optimize(query, options) {
|
|
11
|
+
this.visited = new WeakSet();
|
|
12
|
+
this.applied = [];
|
|
13
|
+
if (!query) {
|
|
14
|
+
return { query: null, applied: [] };
|
|
15
|
+
}
|
|
16
|
+
this.visitSelectQuery(query, "root", options);
|
|
17
|
+
return {
|
|
18
|
+
query,
|
|
19
|
+
applied: [...this.applied]
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
visitSelectQuery(query, scopeId, options) {
|
|
23
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
24
|
+
if (this.visited.has(query)) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
this.visited.add(query);
|
|
28
|
+
if (query instanceof BinarySelectQuery) {
|
|
29
|
+
this.visitSelectQuery(query.left, `${scopeId}.left`, options);
|
|
30
|
+
this.visitSelectQuery(query.right, `${scopeId}.right`, options);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (!(query instanceof SimpleSelectQuery)) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
for (const cte of (_b = (_a = query.withClause) === null || _a === void 0 ? void 0 : _a.tables) !== null && _b !== void 0 ? _b : []) {
|
|
37
|
+
const cteQuery = cte.query;
|
|
38
|
+
if (cteQuery instanceof SimpleSelectQuery || cteQuery instanceof BinarySelectQuery) {
|
|
39
|
+
this.visitSelectQuery(cteQuery, `${scopeId}.cte:${cte.getSourceAliasName()}`, options);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
for (const item of query.selectClause.items) {
|
|
43
|
+
this.visitValueComponent(item.value, `${scopeId}.select`, options);
|
|
44
|
+
}
|
|
45
|
+
if (query.fromClause) {
|
|
46
|
+
this.visitSourceExpression(query.fromClause.source, `${scopeId}.from`, options);
|
|
47
|
+
for (const join of (_c = query.fromClause.joins) !== null && _c !== void 0 ? _c : []) {
|
|
48
|
+
const joinScope = `${scopeId}.join:${(_d = join.source.getAliasName()) !== null && _d !== void 0 ? _d : "unknown"}`;
|
|
49
|
+
this.visitSourceExpression(join.source, joinScope, options);
|
|
50
|
+
if (join.condition instanceof JoinOnClause) {
|
|
51
|
+
join.condition.condition = this.dedupeCondition(join.condition.condition, `${joinScope}.on`, "Removed a duplicate top-level AND condition from a JOIN ON clause.", options);
|
|
52
|
+
this.visitValueComponent(join.condition.condition, `${joinScope}.on`, options);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (query.whereClause) {
|
|
57
|
+
query.whereClause = new WhereClause(this.dedupeCondition(query.whereClause.condition, `${scopeId}.where`, "Removed a duplicate top-level AND condition from a WHERE clause.", options));
|
|
58
|
+
this.visitValueComponent(query.whereClause.condition, `${scopeId}.where`, options);
|
|
59
|
+
}
|
|
60
|
+
if (query.havingClause) {
|
|
61
|
+
query.havingClause = new HavingClause(this.dedupeCondition(query.havingClause.condition, `${scopeId}.having`, "Removed a duplicate top-level AND condition from a HAVING clause.", options));
|
|
62
|
+
this.visitValueComponent(query.havingClause.condition, `${scopeId}.having`, options);
|
|
63
|
+
}
|
|
64
|
+
for (const item of (_f = (_e = query.groupByClause) === null || _e === void 0 ? void 0 : _e.grouping) !== null && _f !== void 0 ? _f : []) {
|
|
65
|
+
this.visitValueComponent(item, `${scopeId}.group_by`, options);
|
|
66
|
+
}
|
|
67
|
+
for (const item of (_h = (_g = query.orderByClause) === null || _g === void 0 ? void 0 : _g.order) !== null && _h !== void 0 ? _h : []) {
|
|
68
|
+
if (item instanceof OrderByItem) {
|
|
69
|
+
this.visitValueComponent(item.value, `${scopeId}.order_by`, options);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
this.visitValueComponent(item, `${scopeId}.order_by`, options);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
visitSourceExpression(source, scopeId, options) {
|
|
77
|
+
const datasource = source.datasource;
|
|
78
|
+
if (datasource instanceof SubQuerySource) {
|
|
79
|
+
this.visitSelectQuery(datasource.query, `${scopeId}.subquery`, options);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (datasource instanceof ParenSource) {
|
|
83
|
+
const nested = datasource.source;
|
|
84
|
+
if (nested instanceof SubQuerySource) {
|
|
85
|
+
this.visitSelectQuery(nested.query, `${scopeId}.subquery`, options);
|
|
86
|
+
}
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (datasource instanceof FunctionSource && datasource.argument) {
|
|
90
|
+
this.visitValueComponent(datasource.argument, `${scopeId}.function`, options);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
visitValueComponent(value, scopeId, options) {
|
|
94
|
+
if (this.visited.has(value)) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
this.visited.add(value);
|
|
98
|
+
if (value instanceof InlineQuery) {
|
|
99
|
+
this.visitSelectQuery(value.selectQuery, `${scopeId}.inline_query`, options);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (value instanceof ArrayQueryExpression) {
|
|
103
|
+
this.visitSelectQuery(value.query, `${scopeId}.array_query`, options);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (value instanceof ParenExpression) {
|
|
107
|
+
this.visitValueComponent(value.expression, scopeId, options);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (value instanceof BinaryExpression) {
|
|
111
|
+
this.visitValueComponent(value.left, scopeId, options);
|
|
112
|
+
this.visitValueComponent(value.right, scopeId, options);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
if (value instanceof UnaryExpression) {
|
|
116
|
+
this.visitValueComponent(value.expression, scopeId, options);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
if (value instanceof FunctionCall) {
|
|
120
|
+
if (value.argument) {
|
|
121
|
+
this.visitValueComponent(value.argument, scopeId, options);
|
|
122
|
+
}
|
|
123
|
+
if (value.filterCondition) {
|
|
124
|
+
this.visitValueComponent(value.filterCondition, `${scopeId}.filter`, options);
|
|
125
|
+
}
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (value instanceof CastExpression) {
|
|
129
|
+
this.visitValueComponent(value.input, scopeId, options);
|
|
130
|
+
if (value.castType.argument) {
|
|
131
|
+
this.visitValueComponent(value.castType.argument, scopeId, options);
|
|
132
|
+
}
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
if (value instanceof CaseExpression) {
|
|
136
|
+
if (value.condition) {
|
|
137
|
+
this.visitValueComponent(value.condition, scopeId, options);
|
|
138
|
+
}
|
|
139
|
+
for (const item of value.switchCase.cases) {
|
|
140
|
+
this.visitValueComponent(item.key, scopeId, options);
|
|
141
|
+
this.visitValueComponent(item.value, scopeId, options);
|
|
142
|
+
}
|
|
143
|
+
if (value.switchCase.elseValue) {
|
|
144
|
+
this.visitValueComponent(value.switchCase.elseValue, scopeId, options);
|
|
145
|
+
}
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (value instanceof BetweenExpression) {
|
|
149
|
+
this.visitValueComponent(value.expression, scopeId, options);
|
|
150
|
+
this.visitValueComponent(value.lower, scopeId, options);
|
|
151
|
+
this.visitValueComponent(value.upper, scopeId, options);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
if (value instanceof JsonPredicateExpression) {
|
|
155
|
+
this.visitValueComponent(value.expression, scopeId, options);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
if (value instanceof ArrayExpression) {
|
|
159
|
+
this.visitValueComponent(value.expression, scopeId, options);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
if (value instanceof ArrayIndexExpression) {
|
|
163
|
+
this.visitValueComponent(value.array, scopeId, options);
|
|
164
|
+
this.visitValueComponent(value.index, scopeId, options);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (value instanceof ArraySliceExpression) {
|
|
168
|
+
this.visitValueComponent(value.array, scopeId, options);
|
|
169
|
+
if (value.startIndex) {
|
|
170
|
+
this.visitValueComponent(value.startIndex, scopeId, options);
|
|
171
|
+
}
|
|
172
|
+
if (value.endIndex) {
|
|
173
|
+
this.visitValueComponent(value.endIndex, scopeId, options);
|
|
174
|
+
}
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
if (value instanceof ValueList || value instanceof TupleExpression) {
|
|
178
|
+
value.values.forEach(item => this.visitValueComponent(item, scopeId, options));
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
if (value instanceof TypeValue && value.argument) {
|
|
182
|
+
this.visitValueComponent(value.argument, scopeId, options);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
dedupeCondition(condition, scopeId, reason, options) {
|
|
186
|
+
const result = dedupeTopLevelAndConditionsWithMetadata(condition, options);
|
|
187
|
+
for (const conditionSql of result.removedConditionSql) {
|
|
188
|
+
this.applied.push({
|
|
189
|
+
kind: "dedupe_condition",
|
|
190
|
+
conditionSql,
|
|
191
|
+
scopeId,
|
|
192
|
+
reason
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
return result.expression;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
//# sourceMappingURL=ConditionDeduplicationOptimizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConditionDeduplicationOptimizer.js","sourceRoot":"","sources":["../../../src/transformers/ConditionDeduplicationOptimizer.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { SelectQuery, SimpleSelectQuery } from "../models/SelectQuery";
|
|
2
2
|
import { ParameterConditionMove, ParameterConditionOptimizationError, ParameterConditionOptimizationOptions, ParameterConditionOptimizationWarning, ParameterConditionSkipped } from "./ParameterConditionPlacementOptimizer";
|
|
3
3
|
import { StaticPredicateMove, StaticPredicatePlacementError, StaticPredicatePlacementWarning, StaticPredicateSkipped } from "./StaticPredicatePlacementOptimizer";
|
|
4
|
+
import { ConditionDeduplicationApplied } from "./ConditionDeduplicationOptimizer";
|
|
4
5
|
import { OptionalConditionPruningParameters } from "./PruneOptionalConditionBranches";
|
|
5
6
|
export type ConditionOptimizationInput = string | SelectQuery | SimpleSelectQuery;
|
|
6
|
-
export type ConditionOptimizationPhaseKind = "sssql_optional_condition" | "parameter_condition_placement" | "static_predicate_placement";
|
|
7
|
+
export type ConditionOptimizationPhaseKind = "sssql_optional_condition" | "parameter_condition_placement" | "static_predicate_placement" | "condition_deduplication";
|
|
7
8
|
export interface ConditionOptimizationOptions extends ParameterConditionOptimizationOptions {
|
|
8
9
|
/**
|
|
9
10
|
* Explicit opt-in values for SSSQL optional branch pruning.
|
|
@@ -41,6 +42,8 @@ export type ConditionOptimizationApplied = SssqlOptionalConditionApplied | (Para
|
|
|
41
42
|
phaseKind: "parameter_condition_placement";
|
|
42
43
|
}) | (StaticPredicateMove & {
|
|
43
44
|
phaseKind: "static_predicate_placement";
|
|
45
|
+
}) | (ConditionDeduplicationApplied & {
|
|
46
|
+
phaseKind: "condition_deduplication";
|
|
44
47
|
});
|
|
45
48
|
export type ConditionOptimizationSkipped = SssqlOptionalConditionSkipped | (ParameterConditionSkipped & {
|
|
46
49
|
phaseKind: "parameter_condition_placement";
|
|
@@ -65,6 +68,31 @@ export interface ConditionOptimizationSafety {
|
|
|
65
68
|
dryRun: boolean;
|
|
66
69
|
formatterGeneratedSource: boolean;
|
|
67
70
|
}
|
|
71
|
+
export interface ConditionOptimizationSourceFilterProbe {
|
|
72
|
+
kind: "source_filter_probe";
|
|
73
|
+
source: string;
|
|
74
|
+
sourceAlias: string;
|
|
75
|
+
predicate: string;
|
|
76
|
+
suggestedSql: string;
|
|
77
|
+
reason: string;
|
|
78
|
+
}
|
|
79
|
+
export interface ConditionOptimizationSkippedProbe {
|
|
80
|
+
kind: "source_filter_probe_skipped";
|
|
81
|
+
source?: string;
|
|
82
|
+
sourceAlias?: string;
|
|
83
|
+
predicate: string;
|
|
84
|
+
code: string;
|
|
85
|
+
reason: string;
|
|
86
|
+
}
|
|
87
|
+
export interface ConditionOptimizationDiagnostics {
|
|
88
|
+
/**
|
|
89
|
+
* Probe diagnostics are collected after SSSQL optional pruning/refresh and
|
|
90
|
+
* before parameter/static placement phases, so they describe the pre-placement
|
|
91
|
+
* source-filter debugging snapshot rather than the final rewritten query.
|
|
92
|
+
*/
|
|
93
|
+
probes: readonly ConditionOptimizationSourceFilterProbe[];
|
|
94
|
+
skippedProbes: readonly ConditionOptimizationSkippedProbe[];
|
|
95
|
+
}
|
|
68
96
|
export interface ConditionOptimizationResult {
|
|
69
97
|
ok: boolean;
|
|
70
98
|
sql: string;
|
|
@@ -75,6 +103,8 @@ export interface ConditionOptimizationResult {
|
|
|
75
103
|
warnings: readonly ConditionOptimizationWarning[];
|
|
76
104
|
errors: readonly ConditionOptimizationError[];
|
|
77
105
|
safety: ConditionOptimizationSafety;
|
|
106
|
+
/** API output shape review: diagnostics is additive; sql/query keep their existing output shape for callers. */
|
|
107
|
+
diagnostics?: ConditionOptimizationDiagnostics;
|
|
78
108
|
}
|
|
79
109
|
export declare const planConditionOptimization: (input: ConditionOptimizationInput, options?: ConditionOptimizationOptions) => ConditionOptimizationResult;
|
|
80
110
|
export declare const optimizeConditions: (input: ConditionOptimizationInput, options?: ConditionOptimizationOptions) => ConditionOptimizationResult;
|
|
@@ -1,10 +1,311 @@
|
|
|
1
|
+
import { SimpleSelectQuery } from "../models/SelectQuery";
|
|
2
|
+
import { ArrayExpression, ArrayIndexExpression, ArrayQueryExpression, ArraySliceExpression, BetweenExpression, BinaryExpression, CaseExpression, CastExpression, ColumnReference, FunctionCall, InlineQuery, JsonPredicateExpression, ParenExpression, TupleExpression, TypeValue, UnaryExpression, ValueList } from "../models/ValueComponent";
|
|
3
|
+
import { SubQuerySource, TableSource } from "../models/Clause";
|
|
1
4
|
import { SelectQueryParser } from "../parsers/SelectQueryParser";
|
|
5
|
+
import { rewriteValueComponentWithColumnResolver } from "../utils/ValueComponentRewriter";
|
|
2
6
|
import { ParameterConditionPlacementOptimizer } from "./ParameterConditionPlacementOptimizer";
|
|
3
7
|
import { StaticPredicatePlacementOptimizer } from "./StaticPredicatePlacementOptimizer";
|
|
8
|
+
import { ConditionDeduplicationOptimizer } from "./ConditionDeduplicationOptimizer";
|
|
4
9
|
import { collectSupportedOptionalConditionBranches, pruneOptionalConditionBranches } from "./PruneOptionalConditionBranches";
|
|
5
10
|
import { SSSQLFilterBuilder } from "./SSSQLFilterBuilder";
|
|
6
11
|
import { formatSqlComponent, hasSqlComponentFormatOverride } from "./SqlComponentFormatter";
|
|
12
|
+
import { SqlFormatter } from "./SqlFormatter";
|
|
7
13
|
const hasOwnParameter = (parameters, parameterName) => Object.prototype.hasOwnProperty.call(parameters, parameterName);
|
|
14
|
+
const normalizeIdentifier = (value) => value.trim().toLowerCase();
|
|
15
|
+
const unwrapParens = (expression) => {
|
|
16
|
+
let candidate = expression;
|
|
17
|
+
while (candidate instanceof ParenExpression) {
|
|
18
|
+
candidate = candidate.expression;
|
|
19
|
+
}
|
|
20
|
+
return candidate;
|
|
21
|
+
};
|
|
22
|
+
const isBinaryOperator = (expression, operator) => {
|
|
23
|
+
const candidate = unwrapParens(expression);
|
|
24
|
+
return candidate instanceof BinaryExpression
|
|
25
|
+
&& candidate.operator.value.trim().toLowerCase() === operator;
|
|
26
|
+
};
|
|
27
|
+
const collectTopLevelAndTerms = (expression) => {
|
|
28
|
+
const candidate = unwrapParens(expression);
|
|
29
|
+
if (!isBinaryOperator(candidate, "and")) {
|
|
30
|
+
return [expression];
|
|
31
|
+
}
|
|
32
|
+
return [
|
|
33
|
+
...collectTopLevelAndTerms(candidate.left),
|
|
34
|
+
...collectTopLevelAndTerms(candidate.right)
|
|
35
|
+
];
|
|
36
|
+
};
|
|
37
|
+
const uniqueMatchNames = (names) => {
|
|
38
|
+
const unique = [];
|
|
39
|
+
for (const name of names) {
|
|
40
|
+
if (name && !unique.includes(name)) {
|
|
41
|
+
unique.push(name);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return unique;
|
|
45
|
+
};
|
|
46
|
+
const getTableSourceTableName = (source) => source.table.name;
|
|
47
|
+
const getProbeSourceAlias = (source) => {
|
|
48
|
+
var _a;
|
|
49
|
+
if (source.aliasExpression) {
|
|
50
|
+
return source.aliasExpression.table.name;
|
|
51
|
+
}
|
|
52
|
+
if (source.datasource instanceof TableSource) {
|
|
53
|
+
return getTableSourceTableName(source.datasource);
|
|
54
|
+
}
|
|
55
|
+
return (_a = source.getAliasName()) !== null && _a !== void 0 ? _a : "";
|
|
56
|
+
};
|
|
57
|
+
const getProbeSourceMatchNames = (source) => {
|
|
58
|
+
var _a;
|
|
59
|
+
if (source.aliasExpression) {
|
|
60
|
+
return uniqueMatchNames([source.aliasExpression.table.name]);
|
|
61
|
+
}
|
|
62
|
+
if (source.datasource instanceof TableSource) {
|
|
63
|
+
return uniqueMatchNames([
|
|
64
|
+
getTableSourceTableName(source.datasource),
|
|
65
|
+
source.datasource.getSourceName()
|
|
66
|
+
]);
|
|
67
|
+
}
|
|
68
|
+
return uniqueMatchNames([(_a = source.getAliasName()) !== null && _a !== void 0 ? _a : ""]);
|
|
69
|
+
};
|
|
70
|
+
const collectJoinSourceBindings = (query) => {
|
|
71
|
+
var _a;
|
|
72
|
+
if (!query.fromClause) {
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
const bindings = [{
|
|
76
|
+
source: query.fromClause.source,
|
|
77
|
+
alias: getProbeSourceAlias(query.fromClause.source),
|
|
78
|
+
matchNames: getProbeSourceMatchNames(query.fromClause.source),
|
|
79
|
+
join: null,
|
|
80
|
+
isPrimary: true
|
|
81
|
+
}];
|
|
82
|
+
for (const join of (_a = query.fromClause.joins) !== null && _a !== void 0 ? _a : []) {
|
|
83
|
+
bindings.push({
|
|
84
|
+
source: join.source,
|
|
85
|
+
alias: getProbeSourceAlias(join.source),
|
|
86
|
+
matchNames: getProbeSourceMatchNames(join.source),
|
|
87
|
+
join,
|
|
88
|
+
isPrimary: false
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
return bindings;
|
|
92
|
+
};
|
|
93
|
+
const isProbeSafeJoin = (join) => {
|
|
94
|
+
const joinType = join.joinType.value.trim().toLowerCase();
|
|
95
|
+
return joinType === "join" || joinType === "inner join" || joinType === "cross join";
|
|
96
|
+
};
|
|
97
|
+
const collectPredicateReferences = (expression) => {
|
|
98
|
+
const references = [];
|
|
99
|
+
let hasNestedQuery = false;
|
|
100
|
+
const visitSelect = () => {
|
|
101
|
+
hasNestedQuery = true;
|
|
102
|
+
};
|
|
103
|
+
const visit = (value) => {
|
|
104
|
+
const candidate = unwrapParens(value);
|
|
105
|
+
if (candidate instanceof ColumnReference) {
|
|
106
|
+
references.push(candidate);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (candidate instanceof BinaryExpression) {
|
|
110
|
+
visit(candidate.left);
|
|
111
|
+
visit(candidate.right);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
if (candidate instanceof UnaryExpression) {
|
|
115
|
+
visit(candidate.expression);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (candidate instanceof InlineQuery) {
|
|
119
|
+
visitSelect();
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (candidate instanceof ArrayQueryExpression) {
|
|
123
|
+
visitSelect();
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
if (candidate instanceof FunctionCall) {
|
|
127
|
+
if (candidate.argument) {
|
|
128
|
+
visit(candidate.argument);
|
|
129
|
+
}
|
|
130
|
+
if (candidate.filterCondition) {
|
|
131
|
+
visit(candidate.filterCondition);
|
|
132
|
+
}
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
if (candidate instanceof CastExpression) {
|
|
136
|
+
visit(candidate.input);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
if (candidate instanceof CaseExpression) {
|
|
140
|
+
if (candidate.condition) {
|
|
141
|
+
visit(candidate.condition);
|
|
142
|
+
}
|
|
143
|
+
for (const pair of candidate.switchCase.cases) {
|
|
144
|
+
visit(pair.key);
|
|
145
|
+
visit(pair.value);
|
|
146
|
+
}
|
|
147
|
+
if (candidate.switchCase.elseValue) {
|
|
148
|
+
visit(candidate.switchCase.elseValue);
|
|
149
|
+
}
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if (candidate instanceof BetweenExpression) {
|
|
153
|
+
visit(candidate.expression);
|
|
154
|
+
visit(candidate.lower);
|
|
155
|
+
visit(candidate.upper);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
if (candidate instanceof JsonPredicateExpression) {
|
|
159
|
+
visit(candidate.expression);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
if (candidate instanceof ArrayExpression) {
|
|
163
|
+
visit(candidate.expression);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (candidate instanceof ArrayIndexExpression) {
|
|
167
|
+
visit(candidate.array);
|
|
168
|
+
visit(candidate.index);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
if (candidate instanceof ArraySliceExpression) {
|
|
172
|
+
visit(candidate.array);
|
|
173
|
+
if (candidate.startIndex) {
|
|
174
|
+
visit(candidate.startIndex);
|
|
175
|
+
}
|
|
176
|
+
if (candidate.endIndex) {
|
|
177
|
+
visit(candidate.endIndex);
|
|
178
|
+
}
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
if (candidate instanceof ValueList) {
|
|
182
|
+
candidate.values.forEach(visit);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
if (candidate instanceof TupleExpression) {
|
|
186
|
+
candidate.values.forEach(visit);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
if (candidate instanceof TypeValue && candidate.argument) {
|
|
190
|
+
visit(candidate.argument);
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
visit(expression);
|
|
194
|
+
return { references, hasNestedQuery };
|
|
195
|
+
};
|
|
196
|
+
const stripAliasFromPredicate = (expression, sourceMatchNames, options) => {
|
|
197
|
+
const sourceNames = uniqueMatchNames(sourceMatchNames);
|
|
198
|
+
return rewriteValueComponentWithColumnResolver(expression, reference => sourceNames.includes(reference.getNamespace())
|
|
199
|
+
? new ColumnReference(null, reference.column.name)
|
|
200
|
+
: reference);
|
|
201
|
+
};
|
|
202
|
+
const addLowercaseBindingFallback = (lowerBindings, name, binding) => {
|
|
203
|
+
const normalized = normalizeIdentifier(name);
|
|
204
|
+
const existing = lowerBindings.get(normalized);
|
|
205
|
+
if (existing === undefined) {
|
|
206
|
+
lowerBindings.set(normalized, binding);
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
if (existing !== binding) {
|
|
210
|
+
lowerBindings.set(normalized, null);
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
const resolveProbeBinding = (exactBindings, lowerBindings, namespace) => {
|
|
214
|
+
var _a;
|
|
215
|
+
const exact = exactBindings.get(namespace);
|
|
216
|
+
if (exact) {
|
|
217
|
+
return exact;
|
|
218
|
+
}
|
|
219
|
+
return (_a = lowerBindings.get(normalizeIdentifier(namespace))) !== null && _a !== void 0 ? _a : null;
|
|
220
|
+
};
|
|
221
|
+
const formatTableSourceForProbe = (source, options) => {
|
|
222
|
+
return new SqlFormatter(Object.assign({ exportComment: true }, options.formatOptions)).format(source).formattedSql;
|
|
223
|
+
};
|
|
224
|
+
const makeSkippedProbe = (expression, code, reason, options, binding) => {
|
|
225
|
+
const source = (binding === null || binding === void 0 ? void 0 : binding.source.datasource) instanceof TableSource
|
|
226
|
+
? binding.source.datasource.getSourceName()
|
|
227
|
+
: undefined;
|
|
228
|
+
return {
|
|
229
|
+
kind: "source_filter_probe_skipped",
|
|
230
|
+
source,
|
|
231
|
+
sourceAlias: (binding === null || binding === void 0 ? void 0 : binding.alias) || undefined,
|
|
232
|
+
predicate: formatSqlComponent(expression, options),
|
|
233
|
+
code,
|
|
234
|
+
reason
|
|
235
|
+
};
|
|
236
|
+
};
|
|
237
|
+
const collectConditionOptimizationDiagnostics = (query, options) => {
|
|
238
|
+
var _a;
|
|
239
|
+
const probes = [];
|
|
240
|
+
const skippedProbes = [];
|
|
241
|
+
if (!(query instanceof SimpleSelectQuery) || !((_a = query.fromClause) === null || _a === void 0 ? void 0 : _a.joins) || !query.whereClause) {
|
|
242
|
+
return { probes, skippedProbes };
|
|
243
|
+
}
|
|
244
|
+
const bindings = collectJoinSourceBindings(query);
|
|
245
|
+
const bindingsByExactName = new Map();
|
|
246
|
+
const bindingsByLowerName = new Map();
|
|
247
|
+
for (const binding of bindings) {
|
|
248
|
+
for (const matchName of binding.matchNames) {
|
|
249
|
+
bindingsByExactName.set(matchName, binding);
|
|
250
|
+
addLowercaseBindingFallback(bindingsByLowerName, matchName, binding);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
for (const term of collectTopLevelAndTerms(query.whereClause.condition)) {
|
|
254
|
+
const analysis = collectPredicateReferences(term);
|
|
255
|
+
if (analysis.hasNestedQuery) {
|
|
256
|
+
skippedProbes.push(makeSkippedProbe(term, "NESTED_QUERY_UNSUPPORTED", "Probe metadata skips predicates containing nested queries because source-local execution cannot be represented safely.", options));
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
if (analysis.references.length === 0) {
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
const namespaces = Array.from(new Set(analysis.references.map(reference => reference.getNamespace())));
|
|
263
|
+
if (namespaces.some(namespace => namespace === "")) {
|
|
264
|
+
skippedProbes.push(makeSkippedProbe(term, "UNQUALIFIED_COLUMN_REFERENCE", "Probe metadata requires qualified column references so the source table is unambiguous.", options));
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
if (namespaces.length !== 1) {
|
|
268
|
+
skippedProbes.push(makeSkippedProbe(term, "MULTI_SOURCE_PREDICATE", "Probe metadata only suggests source filters when the predicate references one joined table.", options));
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
const binding = resolveProbeBinding(bindingsByExactName, bindingsByLowerName, namespaces[0]);
|
|
272
|
+
if (!binding) {
|
|
273
|
+
skippedProbes.push(makeSkippedProbe(term, "UNKNOWN_SOURCE_ALIAS", `Probe metadata could not match '${namespaces[0]}' to a source in the root FROM clause.`, options));
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
if (binding.isPrimary || !binding.join) {
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
if (binding.join.lateral) {
|
|
280
|
+
skippedProbes.push(makeSkippedProbe(term, "LATERAL_SOURCE_UNSUPPORTED", "Probe metadata skips lateral join sources because their input can depend on earlier sources.", options, binding));
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
if (!isProbeSafeJoin(binding.join)) {
|
|
284
|
+
skippedProbes.push(makeSkippedProbe(term, "JOIN_TYPE_UNSUPPORTED", `Probe metadata skips '${binding.join.joinType.value}' sources because probing the nullable side can mislead debugging.`, options, binding));
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
if (binding.source.datasource instanceof SubQuerySource) {
|
|
288
|
+
skippedProbes.push(makeSkippedProbe(term, "DERIVED_SOURCE_UNSUPPORTED", "Probe metadata skips derived table sources until their input query can be represented without implying a rewrite.", options, binding));
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
if (!(binding.source.datasource instanceof TableSource)) {
|
|
292
|
+
skippedProbes.push(makeSkippedProbe(term, "SOURCE_KIND_UNSUPPORTED", "Probe metadata currently supports only base table join sources.", options, binding));
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
const source = binding.source.datasource.getSourceName();
|
|
296
|
+
const predicate = formatSqlComponent(term, options);
|
|
297
|
+
const sourcePredicate = stripAliasFromPredicate(term, binding.matchNames, options);
|
|
298
|
+
probes.push({
|
|
299
|
+
kind: "source_filter_probe",
|
|
300
|
+
source,
|
|
301
|
+
sourceAlias: binding.alias,
|
|
302
|
+
predicate,
|
|
303
|
+
suggestedSql: `select * from ${formatTableSourceForProbe(binding.source.datasource, options)} where ${formatSqlComponent(sourcePredicate, options)}`,
|
|
304
|
+
reason: "predicate references only this joined table"
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
return { probes, skippedProbes };
|
|
308
|
+
};
|
|
8
309
|
const isAbsentOptionalValue = (parameters, parameterName) => parameters[parameterName] === null || parameters[parameterName] === undefined;
|
|
9
310
|
const parseConditionOptimizationInput = (input, options) => {
|
|
10
311
|
const warnings = [];
|
|
@@ -281,6 +582,7 @@ const mapStaticWarnings = (warnings) => warnings.map(warning => (Object.assign({
|
|
|
281
582
|
const mapStaticErrors = (errors) => errors.map(error => (Object.assign({ phaseKind: "static_predicate_placement" }, error)));
|
|
282
583
|
const mapStaticApplied = (applied) => applied.map(item => (Object.assign({ phaseKind: "static_predicate_placement" }, item)));
|
|
283
584
|
const mapStaticSkipped = (skipped) => skipped.map(item => (Object.assign({ phaseKind: "static_predicate_placement", skipDisposition: "blocked" }, item)));
|
|
585
|
+
const mapConditionDeduplicationApplied = (applied) => applied.map(item => (Object.assign({ phaseKind: "condition_deduplication" }, item)));
|
|
284
586
|
const makePhaseSummary = (kind, counts) => ({
|
|
285
587
|
kind,
|
|
286
588
|
appliedCount: counts.appliedCount,
|
|
@@ -295,6 +597,7 @@ const runConditionOptimization = (input, options, defaultDryRun) => {
|
|
|
295
597
|
// Run semantic SSSQL handling before generic placement phases so optional
|
|
296
598
|
// branches remain owned by SSSQL even if later phases grow broader support.
|
|
297
599
|
const sssqlPhase = runSssqlOptionalConditionPhase(input, Object.assign(Object.assign({}, options), { dryRun }));
|
|
600
|
+
const diagnostics = collectConditionOptimizationDiagnostics(sssqlPhase.query, options);
|
|
298
601
|
const parameterOptimizer = new ParameterConditionPlacementOptimizer();
|
|
299
602
|
const parameterInput = reuseOwnedModel
|
|
300
603
|
? (_b = sssqlPhase.query) !== null && _b !== void 0 ? _b : sssqlPhase.sql
|
|
@@ -321,10 +624,17 @@ const runConditionOptimization = (input, options, defaultDryRun) => {
|
|
|
321
624
|
const staticSkipped = mapStaticSkipped(staticPhase.skipped);
|
|
322
625
|
const warnings = [...sssqlPhase.warnings, ...parameterWarnings, ...staticWarnings];
|
|
323
626
|
const errors = [...sssqlPhase.errors, ...parameterErrors, ...staticErrors];
|
|
627
|
+
const dedupePhase = errors.length === 0
|
|
628
|
+
? new ConditionDeduplicationOptimizer().optimize(staticPhase.query, options)
|
|
629
|
+
: { query: staticPhase.query, applied: [] };
|
|
630
|
+
const dedupeApplied = mapConditionDeduplicationApplied(dedupePhase.applied);
|
|
631
|
+
const finalSql = dedupePhase.applied.length > 0 && dedupePhase.query
|
|
632
|
+
? formatSqlComponent(dedupePhase.query, options)
|
|
633
|
+
: staticPhase.sql;
|
|
324
634
|
return {
|
|
325
635
|
ok: errors.length === 0,
|
|
326
|
-
sql:
|
|
327
|
-
query:
|
|
636
|
+
sql: finalSql,
|
|
637
|
+
query: dedupePhase.query,
|
|
328
638
|
phases: [
|
|
329
639
|
makePhaseSummary("sssql_optional_condition", {
|
|
330
640
|
appliedCount: sssqlPhase.applied.length,
|
|
@@ -343,9 +653,15 @@ const runConditionOptimization = (input, options, defaultDryRun) => {
|
|
|
343
653
|
skippedCount: staticSkipped.length,
|
|
344
654
|
warningCount: staticWarnings.length,
|
|
345
655
|
errorCount: staticErrors.length
|
|
656
|
+
}),
|
|
657
|
+
makePhaseSummary("condition_deduplication", {
|
|
658
|
+
appliedCount: dedupeApplied.length,
|
|
659
|
+
skippedCount: 0,
|
|
660
|
+
warningCount: 0,
|
|
661
|
+
errorCount: 0
|
|
346
662
|
})
|
|
347
663
|
],
|
|
348
|
-
applied: [...sssqlPhase.applied, ...parameterApplied, ...staticApplied],
|
|
664
|
+
applied: [...sssqlPhase.applied, ...parameterApplied, ...staticApplied, ...dedupeApplied],
|
|
349
665
|
skipped: [...sssqlPhase.skipped, ...parameterSkipped, ...staticSkipped],
|
|
350
666
|
warnings,
|
|
351
667
|
errors,
|
|
@@ -356,7 +672,8 @@ const runConditionOptimization = (input, options, defaultDryRun) => {
|
|
|
356
672
|
formatterGeneratedSource: sssqlPhase.formatterGeneratedSource
|
|
357
673
|
|| parameterPhase.safety.formatterGeneratedSource
|
|
358
674
|
|| staticPhase.safety.formatterGeneratedSource
|
|
359
|
-
}
|
|
675
|
+
},
|
|
676
|
+
diagnostics
|
|
360
677
|
};
|
|
361
678
|
};
|
|
362
679
|
export const planConditionOptimization = (input, options = {}) => {
|