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