rawsql-ts 0.10.1-beta → 0.10.3-beta
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.min.js +16 -0
- package/dist/esm/index.min.js.map +7 -0
- package/dist/esm/models/ValueComponent.js +7 -0
- package/dist/esm/models/ValueComponent.js.map +1 -1
- package/dist/esm/parsers/CommandExpressionParser.js +1 -16
- package/dist/esm/parsers/CommandExpressionParser.js.map +1 -1
- package/dist/esm/parsers/FunctionExpressionParser.js +51 -6
- package/dist/esm/parsers/FunctionExpressionParser.js.map +1 -1
- package/dist/esm/parsers/SqlPrintTokenParser.js +11 -1
- package/dist/esm/parsers/SqlPrintTokenParser.js.map +1 -1
- package/dist/esm/parsers/ValueParser.js +56 -11
- package/dist/esm/parsers/ValueParser.js.map +1 -1
- package/dist/esm/tokenReaders/CommandTokenReader.js +0 -1
- package/dist/esm/tokenReaders/CommandTokenReader.js.map +1 -1
- package/dist/esm/tokenReaders/FunctionTokenReader.js +2 -0
- package/dist/esm/tokenReaders/FunctionTokenReader.js.map +1 -1
- package/dist/esm/transformers/CTECollector.js +18 -2
- package/dist/esm/transformers/CTECollector.js.map +1 -1
- package/dist/esm/transformers/CTEDisabler.js +6 -1
- package/dist/esm/transformers/CTEDisabler.js.map +1 -1
- package/dist/esm/transformers/SelectableColumnCollector.js +5 -1
- package/dist/esm/transformers/SelectableColumnCollector.js.map +1 -1
- package/dist/esm/transformers/TableSourceCollector.js +37 -2
- package/dist/esm/transformers/TableSourceCollector.js.map +1 -1
- package/dist/esm/types/models/ValueComponent.d.ts +6 -1
- package/dist/esm/types/parsers/CommandExpressionParser.d.ts +0 -1
- package/dist/esm/types/parsers/FunctionExpressionParser.d.ts +13 -1
- package/dist/esm/types/parsers/SqlPrintTokenParser.d.ts +1 -0
- package/dist/esm/types/parsers/ValueParser.d.ts +9 -1
- package/dist/esm/types/transformers/CTECollector.d.ts +3 -0
- package/dist/esm/types/transformers/CTEDisabler.d.ts +2 -1
- package/dist/esm/types/transformers/SelectableColumnCollector.d.ts +1 -0
- package/dist/esm/types/transformers/TableSourceCollector.d.ts +9 -0
- package/dist/esm/types/utils/OperatorPrecedence.d.ts +29 -0
- package/dist/esm/types/utils/ParameterRemover.d.ts +225 -0
- package/dist/esm/utils/OperatorPrecedence.js +79 -0
- package/dist/esm/utils/OperatorPrecedence.js.map +1 -0
- package/dist/esm/utils/ParameterRemover.js +777 -0
- package/dist/esm/utils/ParameterRemover.js.map +1 -0
- package/dist/index.min.js +16 -0
- package/dist/index.min.js.map +7 -0
- package/dist/models/ValueComponent.d.ts +6 -1
- package/dist/models/ValueComponent.js +9 -1
- package/dist/models/ValueComponent.js.map +1 -1
- package/dist/parsers/CommandExpressionParser.d.ts +0 -1
- package/dist/parsers/CommandExpressionParser.js +0 -15
- package/dist/parsers/CommandExpressionParser.js.map +1 -1
- package/dist/parsers/FunctionExpressionParser.d.ts +13 -1
- package/dist/parsers/FunctionExpressionParser.js +50 -5
- package/dist/parsers/FunctionExpressionParser.js.map +1 -1
- package/dist/parsers/SqlPrintTokenParser.d.ts +1 -0
- package/dist/parsers/SqlPrintTokenParser.js +10 -0
- package/dist/parsers/SqlPrintTokenParser.js.map +1 -1
- package/dist/parsers/ValueParser.d.ts +9 -1
- package/dist/parsers/ValueParser.js +55 -10
- package/dist/parsers/ValueParser.js.map +1 -1
- package/dist/tokenReaders/CommandTokenReader.js +0 -1
- package/dist/tokenReaders/CommandTokenReader.js.map +1 -1
- package/dist/tokenReaders/FunctionTokenReader.js +2 -0
- package/dist/tokenReaders/FunctionTokenReader.js.map +1 -1
- package/dist/transformers/CTECollector.d.ts +3 -0
- package/dist/transformers/CTECollector.js +16 -0
- package/dist/transformers/CTECollector.js.map +1 -1
- package/dist/transformers/CTEDisabler.d.ts +2 -1
- package/dist/transformers/CTEDisabler.js +5 -0
- package/dist/transformers/CTEDisabler.js.map +1 -1
- package/dist/transformers/SelectableColumnCollector.d.ts +1 -0
- package/dist/transformers/SelectableColumnCollector.js +4 -0
- package/dist/transformers/SelectableColumnCollector.js.map +1 -1
- package/dist/transformers/TableSourceCollector.d.ts +9 -0
- package/dist/transformers/TableSourceCollector.js +35 -0
- package/dist/transformers/TableSourceCollector.js.map +1 -1
- package/dist/utils/OperatorPrecedence.d.ts +29 -0
- package/dist/utils/OperatorPrecedence.js +83 -0
- package/dist/utils/OperatorPrecedence.js.map +1 -0
- package/dist/utils/ParameterRemover.d.ts +225 -0
- package/dist/utils/ParameterRemover.js +781 -0
- package/dist/utils/ParameterRemover.js.map +1 -0
- package/package.json +4 -2
@@ -0,0 +1,781 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.ParameterRemover = 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 OperatorPrecedence_1 = require("./OperatorPrecedence");
|
8
|
+
/**
|
9
|
+
* Helper visitor to detect if a component tree contains any ParameterExpression
|
10
|
+
*/
|
11
|
+
class ParameterDetector {
|
12
|
+
constructor() {
|
13
|
+
this.handlers = new Map();
|
14
|
+
// ParameterExpression always returns true
|
15
|
+
this.handlers.set(ValueComponent_1.ParameterExpression.kind, () => true);
|
16
|
+
// Binary expressions check both sides
|
17
|
+
this.handlers.set(ValueComponent_1.BinaryExpression.kind, (expr) => this.visit(expr.left) || this.visit(expr.right));
|
18
|
+
// Parenthesized expressions check inner expression
|
19
|
+
this.handlers.set(ValueComponent_1.ParenExpression.kind, (expr) => this.visit(expr.expression));
|
20
|
+
// Unary expressions check inner expression
|
21
|
+
this.handlers.set(ValueComponent_1.UnaryExpression.kind, (expr) => this.visit(expr.expression));
|
22
|
+
// Function calls check argument if present
|
23
|
+
this.handlers.set(ValueComponent_1.FunctionCall.kind, (expr) => expr.argument ? this.visit(expr.argument) : false);
|
24
|
+
// Case expressions check condition and switch cases
|
25
|
+
this.handlers.set(ValueComponent_1.CaseExpression.kind, (expr) => {
|
26
|
+
const conditionHasParam = expr.condition ? this.visit(expr.condition) : false;
|
27
|
+
const switchCaseHasParam = this.visit(expr.switchCase);
|
28
|
+
return conditionHasParam || switchCaseHasParam;
|
29
|
+
});
|
30
|
+
// Between expressions check all three parts
|
31
|
+
this.handlers.set(ValueComponent_1.BetweenExpression.kind, (expr) => this.visit(expr.expression) || this.visit(expr.lower) || this.visit(expr.upper));
|
32
|
+
// Default case: no parameters for simple types
|
33
|
+
// (ColumnReference, LiteralValue, IdentifierString, etc.)
|
34
|
+
}
|
35
|
+
visit(component) {
|
36
|
+
const handler = this.handlers.get(component.getKind());
|
37
|
+
if (handler) {
|
38
|
+
return handler(component);
|
39
|
+
}
|
40
|
+
return false; // Default: no parameters
|
41
|
+
}
|
42
|
+
static detect(component) {
|
43
|
+
const detector = new ParameterDetector();
|
44
|
+
return detector.visit(component);
|
45
|
+
}
|
46
|
+
}
|
47
|
+
/**
|
48
|
+
* Helper to analyze SQL expression structure safely
|
49
|
+
*/
|
50
|
+
class ExpressionAnalyzer {
|
51
|
+
/**
|
52
|
+
* Check if a component is a binary expression with logical operator (AND/OR)
|
53
|
+
*/
|
54
|
+
static isLogicalBinaryExpression(component) {
|
55
|
+
if (component.getKind() !== ValueComponent_1.BinaryExpression.kind) {
|
56
|
+
return false;
|
57
|
+
}
|
58
|
+
const expr = component;
|
59
|
+
return OperatorPrecedence_1.OperatorPrecedence.isLogicalOperator(expr.operator.value);
|
60
|
+
}
|
61
|
+
/**
|
62
|
+
* Check if a component is a comparison operator
|
63
|
+
*/
|
64
|
+
static isComparisonBinaryExpression(component) {
|
65
|
+
if (component.getKind() !== ValueComponent_1.BinaryExpression.kind) {
|
66
|
+
return false;
|
67
|
+
}
|
68
|
+
const expr = component;
|
69
|
+
return OperatorPrecedence_1.OperatorPrecedence.isComparisonOperator(expr.operator.value);
|
70
|
+
}
|
71
|
+
}
|
72
|
+
/**
|
73
|
+
* Utility class to traverse and remove ParameterExpression nodes from the SQL AST.
|
74
|
+
* This removes any binary expression containing a ParameterExpression as a whole.
|
75
|
+
* For compound logical expressions (AND/OR), only the parameterized parts are removed.
|
76
|
+
* If all conditions are removed from a logical expression, the parent node is removed as well.
|
77
|
+
*/
|
78
|
+
class ParameterRemover {
|
79
|
+
constructor() {
|
80
|
+
this.visitedNodes = new Set();
|
81
|
+
this.isRootVisit = true;
|
82
|
+
this.handlers = new Map();
|
83
|
+
// Setup handlers for all component types
|
84
|
+
// SelectQuery types
|
85
|
+
this.handlers.set(SelectQuery_1.SimpleSelectQuery.kind, (expr) => this.visitSimpleSelectQuery(expr));
|
86
|
+
this.handlers.set(SelectQuery_1.BinarySelectQuery.kind, (expr) => this.visitBinarySelectQuery(expr));
|
87
|
+
this.handlers.set(SelectQuery_1.ValuesQuery.kind, (expr) => this.visitValuesQuery(expr));
|
88
|
+
// WithClause and CommonTable
|
89
|
+
this.handlers.set(Clause_1.WithClause.kind, (expr) => this.visitWithClause(expr));
|
90
|
+
this.handlers.set(Clause_1.CommonTable.kind, (expr) => this.visitCommonTable(expr));
|
91
|
+
// SelectClause and SelectItem
|
92
|
+
this.handlers.set(Clause_1.SelectClause.kind, (expr) => this.visitSelectClause(expr));
|
93
|
+
this.handlers.set(Clause_1.SelectItem.kind, (expr) => this.visitSelectItem(expr));
|
94
|
+
this.handlers.set(Clause_1.Distinct.kind, (expr) => this.visitDistinctComponent(expr));
|
95
|
+
this.handlers.set(Clause_1.DistinctOn.kind, (expr) => this.visitDistinctComponent(expr));
|
96
|
+
// Identifiers and raw strings
|
97
|
+
this.handlers.set(ValueComponent_1.IdentifierString.kind, (expr) => this.visitIdentifierString(expr));
|
98
|
+
this.handlers.set(ValueComponent_1.RawString.kind, (expr) => this.visitRawString(expr));
|
99
|
+
this.handlers.set(ValueComponent_1.ColumnReference.kind, (expr) => this.visitColumnReference(expr));
|
100
|
+
this.handlers.set(ValueComponent_1.ParameterExpression.kind, (expr) => this.visitParameterExpression(expr));
|
101
|
+
this.handlers.set(ValueComponent_1.LiteralValue.kind, (expr) => this.visitLiteralValue(expr));
|
102
|
+
// Source components
|
103
|
+
this.handlers.set(Clause_1.SourceExpression.kind, (expr) => this.visitSourceExpression(expr));
|
104
|
+
this.handlers.set(Clause_1.TableSource.kind, (expr) => this.visitTableSource(expr));
|
105
|
+
this.handlers.set(Clause_1.ParenSource.kind, (expr) => this.visitParenSource(expr));
|
106
|
+
this.handlers.set(Clause_1.SourceAliasExpression.kind, (expr) => this.visitSourceAliasExpression(expr));
|
107
|
+
// Subqueries and inline queries
|
108
|
+
this.handlers.set(Clause_1.SubQuerySource.kind, (expr) => this.visitSubQuerySource(expr));
|
109
|
+
this.handlers.set(ValueComponent_1.InlineQuery.kind, (expr) => this.visitInlineQuery(expr));
|
110
|
+
// FROM and JOIN clauses
|
111
|
+
this.handlers.set(Clause_1.FromClause.kind, (expr) => this.visitFromClause(expr));
|
112
|
+
this.handlers.set(Clause_1.JoinClause.kind, (expr) => this.visitJoinClause(expr));
|
113
|
+
this.handlers.set(Clause_1.JoinOnClause.kind, (expr) => this.visitJoinOnClause(expr));
|
114
|
+
this.handlers.set(Clause_1.JoinUsingClause.kind, (expr) => this.visitJoinUsingClause(expr));
|
115
|
+
// WHERE clause
|
116
|
+
this.handlers.set(Clause_1.WhereClause.kind, (expr) => this.visitWhereClause(expr));
|
117
|
+
// Value components that might contain subqueries or parameters
|
118
|
+
this.handlers.set(ValueComponent_1.ParenExpression.kind, (expr) => this.visitParenExpression(expr));
|
119
|
+
this.handlers.set(ValueComponent_1.BinaryExpression.kind, (expr) => this.visitBinaryExpression(expr));
|
120
|
+
this.handlers.set(ValueComponent_1.UnaryExpression.kind, (expr) => this.visitUnaryExpression(expr));
|
121
|
+
this.handlers.set(ValueComponent_1.CaseExpression.kind, (expr) => this.visitCaseExpression(expr));
|
122
|
+
this.handlers.set(ValueComponent_1.CaseKeyValuePair.kind, (expr) => this.visitCaseKeyValuePair(expr));
|
123
|
+
this.handlers.set(ValueComponent_1.SwitchCaseArgument.kind, (expr) => this.visitSwitchCaseArgument(expr));
|
124
|
+
this.handlers.set(ValueComponent_1.BetweenExpression.kind, (expr) => this.visitBetweenExpression(expr));
|
125
|
+
this.handlers.set(ValueComponent_1.FunctionCall.kind, (expr) => this.visitFunctionCall(expr));
|
126
|
+
this.handlers.set(ValueComponent_1.ArrayExpression.kind, (expr) => this.visitArrayExpression(expr));
|
127
|
+
this.handlers.set(ValueComponent_1.TupleExpression.kind, (expr) => this.visitTupleExpression(expr));
|
128
|
+
this.handlers.set(ValueComponent_1.CastExpression.kind, (expr) => this.visitCastExpression(expr));
|
129
|
+
this.handlers.set(ValueComponent_1.WindowFrameExpression.kind, (expr) => this.visitWindowFrameExpression(expr));
|
130
|
+
this.handlers.set(ValueComponent_1.WindowFrameSpec.kind, (expr) => this.visitWindowFrameSpec(expr));
|
131
|
+
this.handlers.set(ValueComponent_1.TypeValue.kind, (expr) => this.visitTypeValue(expr));
|
132
|
+
// Other clauses
|
133
|
+
this.handlers.set(Clause_1.GroupByClause.kind, (expr) => this.visitGroupByClause(expr));
|
134
|
+
this.handlers.set(Clause_1.HavingClause.kind, (expr) => this.visitHavingClause(expr));
|
135
|
+
this.handlers.set(Clause_1.OrderByClause.kind, (expr) => this.visitOrderByClause(expr));
|
136
|
+
this.handlers.set(Clause_1.OrderByItem.kind, (expr) => this.visitOrderByItem(expr));
|
137
|
+
this.handlers.set(Clause_1.WindowFrameClause.kind, (expr) => this.visitWindowFrameClause(expr));
|
138
|
+
this.handlers.set(Clause_1.WindowsClause.kind, (expr) => this.visitWindowsClause(expr));
|
139
|
+
this.handlers.set(Clause_1.LimitClause.kind, (expr) => this.visitLimitClause(expr));
|
140
|
+
this.handlers.set(Clause_1.ForClause.kind, (expr) => this.visitForClause(expr));
|
141
|
+
this.handlers.set(Clause_1.OffsetClause.kind, (expr) => this.visitOffsetClause(expr));
|
142
|
+
this.handlers.set(Clause_1.FetchClause.kind, (expr) => this.visitFetchClause(expr));
|
143
|
+
this.handlers.set(Clause_1.FetchExpression.kind, (expr) => this.visitFetchExpression(expr));
|
144
|
+
}
|
145
|
+
/**
|
146
|
+
* Reset the visited nodes tracking
|
147
|
+
*/
|
148
|
+
reset() {
|
149
|
+
this.visitedNodes.clear();
|
150
|
+
}
|
151
|
+
/**
|
152
|
+
* Main entry point for the visitor pattern.
|
153
|
+
* @param arg The SQL component to visit
|
154
|
+
* @returns The component with parameter expressions removed, or null if the entire component should be removed
|
155
|
+
*/
|
156
|
+
visit(arg) {
|
157
|
+
// If not a root visit, just visit the node and return
|
158
|
+
if (!this.isRootVisit) {
|
159
|
+
return this.visitNode(arg);
|
160
|
+
}
|
161
|
+
// If this is a root visit, we need to reset the state
|
162
|
+
this.reset();
|
163
|
+
this.isRootVisit = false;
|
164
|
+
try {
|
165
|
+
return this.visitNode(arg);
|
166
|
+
}
|
167
|
+
finally {
|
168
|
+
// Regardless of success or failure, reset the root visit flag
|
169
|
+
this.isRootVisit = true;
|
170
|
+
}
|
171
|
+
}
|
172
|
+
/**
|
173
|
+
* Internal visit method used for all nodes.
|
174
|
+
* This separates the visit flag management from the actual node visitation logic.
|
175
|
+
*/
|
176
|
+
visitNode(arg) {
|
177
|
+
var _a, _b;
|
178
|
+
// Check for circular references - if node already visited, return as is
|
179
|
+
if (this.visitedNodes.has(arg)) {
|
180
|
+
return arg;
|
181
|
+
}
|
182
|
+
// Mark as visited node
|
183
|
+
this.visitedNodes.add(arg);
|
184
|
+
// Handle null values
|
185
|
+
if (!arg) {
|
186
|
+
return null;
|
187
|
+
}
|
188
|
+
const handler = this.handlers.get(arg.getKind());
|
189
|
+
if (handler) {
|
190
|
+
return handler(arg);
|
191
|
+
}
|
192
|
+
// Provide more detailed error message
|
193
|
+
const kindSymbol = ((_a = arg.getKind()) === null || _a === void 0 ? void 0 : _a.toString()) || 'unknown';
|
194
|
+
const constructor = ((_b = arg.constructor) === null || _b === void 0 ? void 0 : _b.name) || 'unknown';
|
195
|
+
throw new Error(`[ParameterRemover] No handler for ${constructor} with kind ${kindSymbol}.`);
|
196
|
+
}
|
197
|
+
/**
|
198
|
+
* Visit SimpleSelectQuery node
|
199
|
+
*/
|
200
|
+
visitSimpleSelectQuery(query) {
|
201
|
+
const withClause = query.withClause ? this.visit(query.withClause) : null;
|
202
|
+
// SelectClause is required
|
203
|
+
if (!query.selectClause) {
|
204
|
+
throw new Error("[ParameterRemover] SimpleSelectQuery missing required selectClause");
|
205
|
+
}
|
206
|
+
const selectClause = this.visit(query.selectClause);
|
207
|
+
const fromClause = query.fromClause ? this.visit(query.fromClause) : null;
|
208
|
+
const whereClause = query.whereClause ? this.visit(query.whereClause) : null;
|
209
|
+
const groupByClause = query.groupByClause ? this.visit(query.groupByClause) : null;
|
210
|
+
const havingClause = query.havingClause ? this.visit(query.havingClause) : null;
|
211
|
+
const orderByClause = query.orderByClause ? this.visit(query.orderByClause) : null;
|
212
|
+
const windowClause = query.windowClause ? this.visit(query.windowClause) : null;
|
213
|
+
const limitClause = query.limitClause ? this.visit(query.limitClause) : null;
|
214
|
+
const offsetClause = query.offsetClause ? this.visit(query.offsetClause) : null;
|
215
|
+
const fetchClause = query.fetchClause ? this.visit(query.fetchClause) : null;
|
216
|
+
const forClause = query.forClause ? this.visit(query.forClause) : null;
|
217
|
+
return new SelectQuery_1.SimpleSelectQuery({
|
218
|
+
withClause,
|
219
|
+
selectClause,
|
220
|
+
fromClause,
|
221
|
+
whereClause,
|
222
|
+
groupByClause,
|
223
|
+
havingClause,
|
224
|
+
orderByClause,
|
225
|
+
windowClause,
|
226
|
+
limitClause,
|
227
|
+
offsetClause,
|
228
|
+
fetchClause,
|
229
|
+
forClause
|
230
|
+
});
|
231
|
+
}
|
232
|
+
/**
|
233
|
+
* Visit BinarySelectQuery node
|
234
|
+
*/
|
235
|
+
visitBinarySelectQuery(query) {
|
236
|
+
if (!query.left || !query.right) {
|
237
|
+
return null;
|
238
|
+
}
|
239
|
+
const left = this.visit(query.left);
|
240
|
+
if (!left) {
|
241
|
+
return null;
|
242
|
+
}
|
243
|
+
const right = this.visit(query.right);
|
244
|
+
if (!right) {
|
245
|
+
return null;
|
246
|
+
}
|
247
|
+
const operation = query.operator;
|
248
|
+
return new SelectQuery_1.BinarySelectQuery(left, operation.value, right);
|
249
|
+
}
|
250
|
+
/**
|
251
|
+
* Visit ValuesQuery node
|
252
|
+
*/
|
253
|
+
visitValuesQuery(query) {
|
254
|
+
// Since ValuesQuery doesn't typically contain parameters in WHERE conditions,
|
255
|
+
// we'll just return it as is for now
|
256
|
+
return query;
|
257
|
+
}
|
258
|
+
/**
|
259
|
+
* Visit WithClause node
|
260
|
+
*/
|
261
|
+
visitWithClause(clause) {
|
262
|
+
if (!clause.tables) {
|
263
|
+
return null;
|
264
|
+
}
|
265
|
+
const tables = clause.tables
|
266
|
+
.map(table => this.visit(table))
|
267
|
+
.filter(table => table !== null);
|
268
|
+
if (tables.length === 0) {
|
269
|
+
return null;
|
270
|
+
}
|
271
|
+
return new Clause_1.WithClause(clause.recursive, tables);
|
272
|
+
}
|
273
|
+
/**
|
274
|
+
* Visit CommonTable node
|
275
|
+
*/
|
276
|
+
visitCommonTable(table) {
|
277
|
+
if (!table.aliasExpression || !table.query) {
|
278
|
+
return null;
|
279
|
+
}
|
280
|
+
const aliasExpression = this.visit(table.aliasExpression);
|
281
|
+
if (!aliasExpression) {
|
282
|
+
return null;
|
283
|
+
}
|
284
|
+
const selectQuery = this.visit(table.query);
|
285
|
+
if (!selectQuery) {
|
286
|
+
return null;
|
287
|
+
}
|
288
|
+
return new Clause_1.CommonTable(selectQuery, aliasExpression, table.materialized);
|
289
|
+
}
|
290
|
+
/**
|
291
|
+
* Visit SelectClause node
|
292
|
+
*/
|
293
|
+
visitSelectClause(clause) {
|
294
|
+
if (!clause.items) {
|
295
|
+
throw new Error("[ParameterRemover] SelectClause missing required items");
|
296
|
+
}
|
297
|
+
const items = clause.items
|
298
|
+
.map(item => this.visit(item))
|
299
|
+
.filter(item => item !== null);
|
300
|
+
const distinct = clause.distinct ? this.visit(clause.distinct) : null;
|
301
|
+
if (items.length === 0) {
|
302
|
+
throw new Error("[ParameterRemover] SelectClause must have at least one item");
|
303
|
+
}
|
304
|
+
return new Clause_1.SelectClause(items, distinct);
|
305
|
+
}
|
306
|
+
/**
|
307
|
+
* Visit SelectItem node
|
308
|
+
*/
|
309
|
+
visitSelectItem(item) {
|
310
|
+
var _a;
|
311
|
+
if (!item.value) {
|
312
|
+
return null;
|
313
|
+
}
|
314
|
+
const value = this.visit(item.value);
|
315
|
+
if (!value) {
|
316
|
+
return null;
|
317
|
+
}
|
318
|
+
return new Clause_1.SelectItem(value, ((_a = item.identifier) === null || _a === void 0 ? void 0 : _a.name) || null);
|
319
|
+
}
|
320
|
+
// Simple visitor methods that return the node unchanged
|
321
|
+
visitIdentifierString(identifier) { return identifier; }
|
322
|
+
visitRawString(str) { return str; }
|
323
|
+
visitColumnReference(ref) { return ref; }
|
324
|
+
visitParameterExpression(param) { return null; }
|
325
|
+
visitLiteralValue(literal) { return literal; }
|
326
|
+
visitTableSource(source) { return source; }
|
327
|
+
visitForClause(clause) { return clause; }
|
328
|
+
visitDistinctComponent(component) { return component; }
|
329
|
+
/**
|
330
|
+
* Visit SourceExpression node
|
331
|
+
*/
|
332
|
+
visitSourceExpression(source) {
|
333
|
+
if (!source.datasource) {
|
334
|
+
return source; // Return as is instead of null
|
335
|
+
}
|
336
|
+
const sourceComponent = this.visit(source.datasource);
|
337
|
+
if (!sourceComponent) {
|
338
|
+
return source; // Return as is instead of null
|
339
|
+
}
|
340
|
+
return new Clause_1.SourceExpression(sourceComponent, source.aliasExpression);
|
341
|
+
}
|
342
|
+
/**
|
343
|
+
* Visit ParenSource node
|
344
|
+
*/
|
345
|
+
visitParenSource(source) {
|
346
|
+
return new Clause_1.ParenSource(this.visit(source.source));
|
347
|
+
}
|
348
|
+
/**
|
349
|
+
* Visit SourceAliasExpression node
|
350
|
+
*/
|
351
|
+
visitSourceAliasExpression(expr) {
|
352
|
+
const table = expr.table;
|
353
|
+
const columns = expr.columns;
|
354
|
+
const columnNames = columns ? columns.map(col => col.name) : null;
|
355
|
+
return new Clause_1.SourceAliasExpression(table.name, columnNames);
|
356
|
+
}
|
357
|
+
/**
|
358
|
+
* Visit SubQuerySource node
|
359
|
+
*/
|
360
|
+
visitSubQuerySource(source) {
|
361
|
+
if (!source.query) {
|
362
|
+
return null;
|
363
|
+
}
|
364
|
+
const query = this.visit(source.query);
|
365
|
+
if (!query) {
|
366
|
+
return null;
|
367
|
+
}
|
368
|
+
return new Clause_1.SubQuerySource(query);
|
369
|
+
}
|
370
|
+
/**
|
371
|
+
* Visit InlineQuery node
|
372
|
+
*/
|
373
|
+
visitInlineQuery(query) {
|
374
|
+
if (!query.selectQuery) {
|
375
|
+
return null;
|
376
|
+
}
|
377
|
+
const selectQuery = this.visit(query.selectQuery);
|
378
|
+
if (!selectQuery) {
|
379
|
+
return null;
|
380
|
+
}
|
381
|
+
return new ValueComponent_1.InlineQuery(selectQuery);
|
382
|
+
}
|
383
|
+
/**
|
384
|
+
* Visit FromClause node
|
385
|
+
*/
|
386
|
+
visitFromClause(clause) {
|
387
|
+
if (!clause.source) {
|
388
|
+
return clause; // Return as is instead of null
|
389
|
+
}
|
390
|
+
// Always keep the source, even if something inside might change
|
391
|
+
const source = this.visit(clause.source);
|
392
|
+
let joins = null;
|
393
|
+
if (clause.joins) {
|
394
|
+
const processedJoins = clause.joins
|
395
|
+
.map(join => this.visit(join))
|
396
|
+
.filter(join => join !== null);
|
397
|
+
if (processedJoins.length > 0) {
|
398
|
+
joins = processedJoins;
|
399
|
+
}
|
400
|
+
}
|
401
|
+
return new Clause_1.FromClause(source || clause.source, joins);
|
402
|
+
}
|
403
|
+
/**
|
404
|
+
* Visit JoinClause node
|
405
|
+
*/
|
406
|
+
visitJoinClause(clause) {
|
407
|
+
if (!clause.source) {
|
408
|
+
return null;
|
409
|
+
}
|
410
|
+
const source = this.visit(clause.source);
|
411
|
+
if (!source) {
|
412
|
+
return null;
|
413
|
+
}
|
414
|
+
const condition = clause.condition ? this.visit(clause.condition) : null;
|
415
|
+
return new Clause_1.JoinClause(clause.joinType.value, source, condition, clause.lateral);
|
416
|
+
}
|
417
|
+
/**
|
418
|
+
* Visit JoinOnClause node
|
419
|
+
*/
|
420
|
+
visitJoinOnClause(clause) {
|
421
|
+
const condition = this.visit(clause.condition);
|
422
|
+
// If condition has been removed (contains only parameters), return null
|
423
|
+
if (!condition) {
|
424
|
+
return null;
|
425
|
+
}
|
426
|
+
return new Clause_1.JoinOnClause(condition);
|
427
|
+
}
|
428
|
+
/**
|
429
|
+
* Visit JoinUsingClause node
|
430
|
+
*/
|
431
|
+
visitJoinUsingClause(clause) {
|
432
|
+
return clause;
|
433
|
+
}
|
434
|
+
/**
|
435
|
+
* Visit WhereClause node - key method for parameter removal
|
436
|
+
*/
|
437
|
+
visitWhereClause(clause) {
|
438
|
+
const condition = this.visit(clause.condition);
|
439
|
+
// If the entire condition has been removed (contains only parameters), return null
|
440
|
+
if (!condition) {
|
441
|
+
return null;
|
442
|
+
}
|
443
|
+
return new Clause_1.WhereClause(condition);
|
444
|
+
}
|
445
|
+
/**
|
446
|
+
* Visit ParenExpression node
|
447
|
+
*/
|
448
|
+
visitParenExpression(expr) {
|
449
|
+
const innerExpression = this.visit(expr.expression);
|
450
|
+
// If the inner expression has been removed (contains only parameters), return null
|
451
|
+
if (!innerExpression) {
|
452
|
+
return null;
|
453
|
+
}
|
454
|
+
return new ValueComponent_1.ParenExpression(innerExpression);
|
455
|
+
}
|
456
|
+
/**
|
457
|
+
* Visit BinaryExpression node - improved logic for right-associative parser structure
|
458
|
+
*/
|
459
|
+
visitBinaryExpression(expr) {
|
460
|
+
const operator = expr.operator.value.toLowerCase();
|
461
|
+
if (OperatorPrecedence_1.OperatorPrecedence.isLogicalOperator(operator)) {
|
462
|
+
// Handle logical operators normally
|
463
|
+
const left = this.visit(expr.left);
|
464
|
+
const right = this.visit(expr.right);
|
465
|
+
if (!left && !right) {
|
466
|
+
return null;
|
467
|
+
}
|
468
|
+
if (!left && right) {
|
469
|
+
return right;
|
470
|
+
}
|
471
|
+
if (left && !right) {
|
472
|
+
return left;
|
473
|
+
}
|
474
|
+
return new ValueComponent_1.BinaryExpression(left, expr.operator.value, right);
|
475
|
+
}
|
476
|
+
else {
|
477
|
+
// For comparison operators, handle right-associative parser structure
|
478
|
+
return this.handleComparisonExpression(expr);
|
479
|
+
}
|
480
|
+
}
|
481
|
+
/**
|
482
|
+
* Handle comparison expressions, accounting for right-associative parser structure
|
483
|
+
*/
|
484
|
+
handleComparisonExpression(expr) {
|
485
|
+
const left = this.visit(expr.left);
|
486
|
+
// Check if the right side is a logical expression (AND/OR) using type-safe analysis
|
487
|
+
if (ExpressionAnalyzer.isLogicalBinaryExpression(expr.right)) {
|
488
|
+
// This is the problematic case: comparison = (logical expression)
|
489
|
+
// We need to restructure this as: (comparison) logical (other parts)
|
490
|
+
return this.restructureComparisonWithLogical(expr, left);
|
491
|
+
}
|
492
|
+
// Normal comparison processing
|
493
|
+
const right = this.visit(expr.right);
|
494
|
+
if (!left || !right) {
|
495
|
+
return null;
|
496
|
+
}
|
497
|
+
return new ValueComponent_1.BinaryExpression(left, expr.operator.value, right);
|
498
|
+
}
|
499
|
+
/**
|
500
|
+
* Restructure expressions like "id = (1 AND ...)" to "(id = 1) AND ..."
|
501
|
+
*/
|
502
|
+
restructureComparisonWithLogical(expr, processedLeft) {
|
503
|
+
if (!processedLeft) {
|
504
|
+
return null;
|
505
|
+
}
|
506
|
+
const rightBinary = expr.right;
|
507
|
+
const logicalOperator = rightBinary.operator.value;
|
508
|
+
// Process the logical expression's left side as the right side of our comparison
|
509
|
+
const comparisonRight = this.visit(rightBinary.left);
|
510
|
+
if (!comparisonRight) {
|
511
|
+
// If the comparison right side contains only parameters,
|
512
|
+
// try to process the logical expression's right side
|
513
|
+
return this.visit(rightBinary.right);
|
514
|
+
}
|
515
|
+
// Create the restructured comparison: "id = 1"
|
516
|
+
const restructuredComparison = new ValueComponent_1.BinaryExpression(processedLeft, expr.operator.value, comparisonRight);
|
517
|
+
// Process the remaining logical expression's right side
|
518
|
+
const logicalRight = this.visit(rightBinary.right);
|
519
|
+
if (!logicalRight) {
|
520
|
+
// Only the left comparison is valid
|
521
|
+
return restructuredComparison;
|
522
|
+
}
|
523
|
+
// Combine: "(id = 1) AND (remaining expression)"
|
524
|
+
return new ValueComponent_1.BinaryExpression(restructuredComparison, logicalOperator, logicalRight);
|
525
|
+
}
|
526
|
+
/**
|
527
|
+
* Check if an operator is a logical operator */
|
528
|
+
/**
|
529
|
+
* Check if the resulting expression would be nonsensical
|
530
|
+
* This is a heuristic to detect cases like "name = age > 18"
|
531
|
+
*/
|
532
|
+
wouldCreateNonsensicalExpression(left, operator, right) {
|
533
|
+
// Only apply this check for simple cases where we have a direct comparison operator
|
534
|
+
// followed by another comparison operator in the right side
|
535
|
+
if (OperatorPrecedence_1.OperatorPrecedence.isComparisonOperator(operator) && ExpressionAnalyzer.isComparisonBinaryExpression(right)) {
|
536
|
+
const rightBinary = right;
|
537
|
+
if (rightBinary.operator && OperatorPrecedence_1.OperatorPrecedence.isComparisonOperator(rightBinary.operator.value)) {
|
538
|
+
// Additional check: make sure this isn't a legitimate nested case
|
539
|
+
// If the left side is a simple column and the right side is a comparison,
|
540
|
+
// this is likely nonsensical (like "name = age > 18")
|
541
|
+
if (left.getKind().toString().includes('ColumnReference')) {
|
542
|
+
return true;
|
543
|
+
}
|
544
|
+
}
|
545
|
+
}
|
546
|
+
return false;
|
547
|
+
}
|
548
|
+
/**
|
549
|
+
* Check if a ValueComponent contains a ParameterExpression anywhere in its tree
|
550
|
+
*/
|
551
|
+
containsParameter(component) {
|
552
|
+
return ParameterDetector.detect(component);
|
553
|
+
}
|
554
|
+
/**
|
555
|
+
* Visit UnaryExpression node
|
556
|
+
*/
|
557
|
+
visitUnaryExpression(expr) {
|
558
|
+
const expression = this.visit(expr.expression);
|
559
|
+
// If the expression has been removed (contains only parameters), return null
|
560
|
+
if (!expression) {
|
561
|
+
return null;
|
562
|
+
}
|
563
|
+
return new ValueComponent_1.UnaryExpression(expr.operator.value, expression);
|
564
|
+
}
|
565
|
+
/**
|
566
|
+
* Visit CaseExpression node
|
567
|
+
*/
|
568
|
+
visitCaseExpression(expr) {
|
569
|
+
const condition = expr.condition ? this.visit(expr.condition) : null;
|
570
|
+
const switchCase = this.visit(expr.switchCase);
|
571
|
+
// If switchCase has been removed (contains only parameters), return null
|
572
|
+
if (!switchCase) {
|
573
|
+
return null;
|
574
|
+
}
|
575
|
+
return new ValueComponent_1.CaseExpression(condition, switchCase);
|
576
|
+
}
|
577
|
+
/**
|
578
|
+
* Visit CaseKeyValuePair node
|
579
|
+
*/
|
580
|
+
visitCaseKeyValuePair(pair) {
|
581
|
+
// If either key or value contains parameters, remove the entire pair
|
582
|
+
if (this.containsParameter(pair.key) || this.containsParameter(pair.value)) {
|
583
|
+
return null;
|
584
|
+
}
|
585
|
+
const key = this.visit(pair.key);
|
586
|
+
const value = this.visit(pair.value);
|
587
|
+
return new ValueComponent_1.CaseKeyValuePair(key, value);
|
588
|
+
}
|
589
|
+
/**
|
590
|
+
* Visit SwitchCaseArgument node
|
591
|
+
*/
|
592
|
+
visitSwitchCaseArgument(arg) {
|
593
|
+
// Process all case pairs, filter out null results
|
594
|
+
const cases = arg.cases
|
595
|
+
.map(caseItem => this.visit(caseItem))
|
596
|
+
.filter(caseItem => caseItem !== null);
|
597
|
+
// Process the else value if it exists
|
598
|
+
const elseValue = arg.elseValue ? this.visit(arg.elseValue) : null;
|
599
|
+
// If no cases remain and no else value, remove the entire switch case
|
600
|
+
if (cases.length === 0 && !elseValue) {
|
601
|
+
return null;
|
602
|
+
}
|
603
|
+
return new ValueComponent_1.SwitchCaseArgument(cases, elseValue);
|
604
|
+
}
|
605
|
+
/**
|
606
|
+
* Visit BetweenExpression node
|
607
|
+
*/
|
608
|
+
visitBetweenExpression(expr) {
|
609
|
+
// If any part of the expression contains a parameter, remove the entire expression
|
610
|
+
if (this.containsParameter(expr.expression) ||
|
611
|
+
this.containsParameter(expr.lower) ||
|
612
|
+
this.containsParameter(expr.upper)) {
|
613
|
+
return null;
|
614
|
+
}
|
615
|
+
const expression = this.visit(expr.expression);
|
616
|
+
const lower = this.visit(expr.lower);
|
617
|
+
const upper = this.visit(expr.upper);
|
618
|
+
return new ValueComponent_1.BetweenExpression(expression, lower, upper, expr.negated);
|
619
|
+
}
|
620
|
+
/**
|
621
|
+
* Visit FunctionCall node
|
622
|
+
*/
|
623
|
+
visitFunctionCall(call) {
|
624
|
+
const argument = call.argument ? this.visit(call.argument) : null;
|
625
|
+
const over = call.over ? this.visit(call.over) : null;
|
626
|
+
return new ValueComponent_1.FunctionCall(call.qualifiedName.namespaces, call.qualifiedName.name, argument, over);
|
627
|
+
}
|
628
|
+
/**
|
629
|
+
* Visit ArrayExpression node
|
630
|
+
*/
|
631
|
+
visitArrayExpression(expr) {
|
632
|
+
const expression = this.visit(expr.expression);
|
633
|
+
// If the expression has been removed (contains only parameters), return null
|
634
|
+
if (!expression) {
|
635
|
+
return null;
|
636
|
+
}
|
637
|
+
return new ValueComponent_1.ArrayExpression(expression);
|
638
|
+
}
|
639
|
+
/**
|
640
|
+
* Visit TupleExpression node
|
641
|
+
*/
|
642
|
+
visitTupleExpression(expr) {
|
643
|
+
const values = expr.values
|
644
|
+
.map(value => this.visit(value))
|
645
|
+
.filter(value => value !== null);
|
646
|
+
return new ValueComponent_1.TupleExpression(values);
|
647
|
+
}
|
648
|
+
/**
|
649
|
+
* Visit CastExpression node
|
650
|
+
*/
|
651
|
+
visitCastExpression(expr) {
|
652
|
+
// If the input contains a parameter, remove the entire expression
|
653
|
+
if (this.containsParameter(expr.input)) {
|
654
|
+
return null;
|
655
|
+
}
|
656
|
+
const input = this.visit(expr.input);
|
657
|
+
const castType = this.visit(expr.castType);
|
658
|
+
// If the input has been removed, return null
|
659
|
+
if (!input) {
|
660
|
+
return null;
|
661
|
+
}
|
662
|
+
return new ValueComponent_1.CastExpression(input, castType);
|
663
|
+
}
|
664
|
+
/**
|
665
|
+
* Visit WindowFrameExpression node
|
666
|
+
*/
|
667
|
+
visitWindowFrameExpression(expr) {
|
668
|
+
const partition = expr.partition ? this.visit(expr.partition) : null;
|
669
|
+
const order = expr.order ? this.visit(expr.order) : null;
|
670
|
+
const frameSpec = expr.frameSpec ? this.visit(expr.frameSpec) : null;
|
671
|
+
return new ValueComponent_1.WindowFrameExpression(partition, order, frameSpec);
|
672
|
+
}
|
673
|
+
/**
|
674
|
+
* Visit WindowFrameSpec node
|
675
|
+
*/
|
676
|
+
visitWindowFrameSpec(spec) {
|
677
|
+
return spec;
|
678
|
+
}
|
679
|
+
/**
|
680
|
+
* Visit TypeValue node
|
681
|
+
*/
|
682
|
+
visitTypeValue(type) {
|
683
|
+
const argument = type.argument ? this.visit(type.argument) : null;
|
684
|
+
return new ValueComponent_1.TypeValue(type.qualifiedName.namespaces, type.qualifiedName.name, argument);
|
685
|
+
}
|
686
|
+
/**
|
687
|
+
* Visit GroupByClause node
|
688
|
+
*/
|
689
|
+
visitGroupByClause(clause) {
|
690
|
+
if (!clause.grouping || clause.grouping.length === 0) {
|
691
|
+
return null;
|
692
|
+
}
|
693
|
+
const grouping = clause.grouping
|
694
|
+
.map(expr => this.visit(expr))
|
695
|
+
.filter(expr => expr !== null);
|
696
|
+
if (grouping.length === 0) {
|
697
|
+
return null;
|
698
|
+
}
|
699
|
+
return new Clause_1.GroupByClause(grouping);
|
700
|
+
}
|
701
|
+
/**
|
702
|
+
* Visit HavingClause node
|
703
|
+
*/
|
704
|
+
visitHavingClause(clause) {
|
705
|
+
const condition = this.visit(clause.condition);
|
706
|
+
// If the condition has been removed (contains only parameters), return null
|
707
|
+
if (!condition) {
|
708
|
+
return null;
|
709
|
+
}
|
710
|
+
return new Clause_1.HavingClause(condition);
|
711
|
+
}
|
712
|
+
/**
|
713
|
+
* Visit OrderByClause node
|
714
|
+
*/
|
715
|
+
visitOrderByClause(clause) {
|
716
|
+
const items = clause.order
|
717
|
+
.map((item) => this.visit(item))
|
718
|
+
.filter((item) => item !== null);
|
719
|
+
return new Clause_1.OrderByClause(items);
|
720
|
+
}
|
721
|
+
/**
|
722
|
+
* Visit OrderByItem node
|
723
|
+
*/
|
724
|
+
visitOrderByItem(item) {
|
725
|
+
const value = this.visit(item.value);
|
726
|
+
return new Clause_1.OrderByItem(value, item.sortDirection, item.nullsPosition);
|
727
|
+
}
|
728
|
+
/**
|
729
|
+
* Visit WindowFrameClause node
|
730
|
+
*/
|
731
|
+
visitWindowFrameClause(clause) {
|
732
|
+
const expression = this.visit(clause.expression);
|
733
|
+
return new Clause_1.WindowFrameClause(clause.name.name, expression);
|
734
|
+
}
|
735
|
+
/**
|
736
|
+
* Visit WindowsClause node
|
737
|
+
*/
|
738
|
+
visitWindowsClause(clause) {
|
739
|
+
const windows = clause.windows.map(window => this.visit(window));
|
740
|
+
return new Clause_1.WindowsClause(windows);
|
741
|
+
}
|
742
|
+
/**
|
743
|
+
* Visit LimitClause node
|
744
|
+
*/
|
745
|
+
visitLimitClause(clause) {
|
746
|
+
const value = this.visit(clause.value);
|
747
|
+
return new Clause_1.LimitClause(value);
|
748
|
+
}
|
749
|
+
/**
|
750
|
+
* Visit OffsetClause node
|
751
|
+
*/
|
752
|
+
visitOffsetClause(clause) {
|
753
|
+
const value = this.visit(clause.value);
|
754
|
+
return new Clause_1.OffsetClause(value);
|
755
|
+
}
|
756
|
+
/**
|
757
|
+
* Visit FetchClause node
|
758
|
+
*/
|
759
|
+
visitFetchClause(clause) {
|
760
|
+
const expression = this.visit(clause.expression);
|
761
|
+
return new Clause_1.FetchClause(expression);
|
762
|
+
}
|
763
|
+
/**
|
764
|
+
* Visit FetchExpression node
|
765
|
+
*/
|
766
|
+
visitFetchExpression(expression) {
|
767
|
+
const count = this.visit(expression.count);
|
768
|
+
return new Clause_1.FetchExpression(expression.type, count, expression.unit);
|
769
|
+
}
|
770
|
+
/**
|
771
|
+
* Static method to apply parameter removal transformation on an SQL AST
|
772
|
+
* @param node The SQL AST node to transform
|
773
|
+
* @returns The transformed SQL AST with parameter expressions removed
|
774
|
+
*/
|
775
|
+
static remove(node) {
|
776
|
+
const remover = new ParameterRemover();
|
777
|
+
return remover.visit(node);
|
778
|
+
}
|
779
|
+
}
|
780
|
+
exports.ParameterRemover = ParameterRemover;
|
781
|
+
//# sourceMappingURL=ParameterRemover.js.map
|