sqlparser-devexpress 2.3.2 → 2.3.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sqlparser-devexpress",
3
- "version": "2.3.2",
3
+ "version": "2.3.3",
4
4
  "main": "src/index.js",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -140,9 +140,9 @@ function DevExpressConverter() {
140
140
 
141
141
  let comparison = [left, operatorToken, right];
142
142
 
143
- if (isFunctionNullCheck(ast.left, true)) {
143
+ if ((ast.left && isFunctionNullCheck(ast.left, true)) || (ast.value && isFunctionNullCheck(ast.value, false))) {
144
144
  comparison = [[left, operatorToken, right], 'or', [left, operatorToken, null]];
145
- } else if (isFunctionNullCheck(ast.right, true)) {
145
+ } else if (ast.right && isFunctionNullCheck(ast.right, true)) {
146
146
  comparison = [[left, operatorToken, right], 'or', [right, operatorToken, null]];
147
147
  }
148
148
 
@@ -1,4 +1,4 @@
1
- import { LITERALS, OPERATOR_PRECEDENCE, UNSUPPORTED_PATTERN } from "../constants.js";
1
+ import { LITERALS, LOGICAL_OPERATORS, OPERATOR_PRECEDENCE, UNSUPPORTED_PATTERN } from "../constants.js";
2
2
  import { Tokenizer } from "./tokenizer.js";
3
3
 
4
4
 
@@ -78,20 +78,20 @@ export function parse(input, variables = []) {
78
78
  }
79
79
 
80
80
  function parseFunction() {
81
- const funcName = currentToken.value.toUpperCase();
81
+ const functionName = currentToken.value.toUpperCase();
82
82
  next();
83
83
 
84
- expectedToken(currentToken, "(", `Expected ( after ${funcName}`);
84
+ expectedToken(currentToken, "(", `Expected ( after ${functionName}`);
85
85
 
86
86
  next();
87
87
 
88
- const args = [];
88
+ const functionArgs = [];
89
89
  while (currentToken && currentToken.value !== ")") {
90
- args.push(parseExpression());
90
+ functionArgs.push(parseExpression());
91
91
  if (currentToken && currentToken.value === ",") next();
92
92
  }
93
93
 
94
- expectedToken(currentToken, ")", `Expected ) after ${funcName}`);
94
+ expectedToken(currentToken, ")", `Expected ) after ${functionName}`);
95
95
 
96
96
  next(); // Consume the closing parenthesis
97
97
 
@@ -99,17 +99,22 @@ export function parse(input, variables = []) {
99
99
  if (currentToken && currentToken.type === "operator") {
100
100
  const operator = currentToken.value;
101
101
  next(); // Move to the next token after the operator
102
- const value = parseValue(); // Parse the value after the operator
102
+ const rightOperand = parseValue(); // Parse the value after the operator
103
+ const nodeType = LOGICAL_OPERATORS.includes(operator.toLowerCase()) ? "logical" : "comparison";
104
+
105
+ if(nodeType === "logical") {
106
+ return { type: "logical", operator, left: { type: "function", name: functionName, args: functionArgs }, right: rightOperand };
107
+ }
103
108
 
104
109
  return {
105
110
  type: "comparison",
106
- left: { type: "function", name: funcName, args },
111
+ left: { type: "function", name: functionName, args: functionArgs },
107
112
  operator,
108
- value
113
+ value: rightOperand
109
114
  };
110
115
  }
111
116
 
112
- return { type: "function", name: funcName, args };
117
+ return { type: "function", name: functionName, args: functionArgs };
113
118
  }
114
119
 
115
120
  // Parses logical expressions using operator precedence
@@ -165,6 +170,21 @@ export function parse(input, variables = []) {
165
170
 
166
171
  if (operator === "between") return parseBetweenComparison(field, operator);
167
172
 
173
+ if (currentToken.type === "function") {
174
+ const functionNode = parseFunction();
175
+
176
+ // Wrap the function inside a comparison if it's directly after an operator
177
+ const leftComparison = {
178
+ type: "comparison",
179
+ field,
180
+ operator,
181
+ value: functionNode.left
182
+ };
183
+
184
+ functionNode.left = leftComparison;
185
+ return functionNode;
186
+ }
187
+
168
188
  // For other comparison operators, parse a single right-hand value
169
189
  const valueType = currentToken.type;
170
190
  const value = parseValue(operator);
@@ -184,39 +204,59 @@ export function parse(input, variables = []) {
184
204
  function parseValue(operatorToken) {
185
205
  if (!currentToken) throw new Error("Unexpected end of input");
186
206
 
187
- if(currentToken.type === "function") return parseFunction();
207
+ // Handle function without consuming the token
208
+ if (currentToken.type === "function") {
209
+ return parseFunction();
210
+ }
188
211
 
189
212
  const token = currentToken;
190
213
  next(); // Move to the next token
191
214
 
192
- if (token.type === "number") return Number(token.value);
193
- if (token.type === "string") return token.value.slice(1, -1).replace(/''/g, "");
194
- if (token.type === "identifier") return token.value;
195
- if (token.type === "null") return null;
215
+ switch (token.type) {
216
+ case "number":
217
+ return Number(token.value);
196
218
 
197
- // Handle placeholders like `{VariableName}`
198
- if (token.type === "placeholder") {
199
- const val = token.value.slice(1, -1);
200
- if (!variables.includes(val)) variables.push(val);
201
- return { type: "placeholder", value: val };
202
- }
219
+ case "string":
220
+ return token.value.slice(1, -1).replace(/''/g, "");
203
221
 
204
- operatorToken = operatorToken?.toUpperCase();
222
+ case "identifier":
223
+ return token.value;
205
224
 
206
- // Handle IN operator which requires a list of values
207
- if (operatorToken && (operatorToken === "IN" || operatorToken === "NOT IN")) return parseInList(token);
225
+ case "null":
226
+ return null;
208
227
 
209
- // Handle ({Placeholder}) syntax for placeholders inside parentheses
210
- const nextToken = tokenizer.peekNextToken();
211
- if(token.type === "paren" && currentToken && currentToken.type === "placeholder" && nextToken && nextToken.type === "paren") {
212
- const val = parseValue();
213
- return { type: "placeholder", value: val };
228
+ case "placeholder": {
229
+ const val = token.value.slice(1, -1);
230
+ if (!variables.includes(val)) variables.push(val);
231
+ return { type: "placeholder", value: val };
232
+ }
233
+
234
+ case "paren": {
235
+ if (currentToken.type === "function") {
236
+ return parseFunction();
237
+ }
238
+ // Handle ({Placeholder}) syntax for placeholders inside parentheses
239
+ const nextToken = tokenizer.peekNextToken();
240
+ if (currentToken && currentToken.type === "placeholder" &&
241
+ nextToken && nextToken.type === "paren") {
242
+ const val = parseValue();
243
+ return { type: "placeholder", value: val };
244
+ }
245
+ break;
246
+ }
247
+ }
248
+
249
+ // Handle IN or NOT IN operator (outside switch as intended)
250
+ operatorToken = operatorToken?.toUpperCase();
251
+ if (operatorToken === "IN" || operatorToken === "NOT IN") {
252
+ return parseInList(token);
214
253
  }
215
254
 
216
255
  throw new Error(`Unexpected value: ${token.value}`);
217
256
  }
218
257
 
219
258
 
259
+
220
260
  // Start parsing and return the AST with extracted variables
221
261
  return { ast: parseExpression(), variables };
222
262
  }
@@ -189,6 +189,19 @@ describe("Parser SQL to dx Filter Builder", () => {
189
189
  "or",
190
190
  ["TicketID", "=", null]
191
191
  ]
192
+ },
193
+ {
194
+ input: "CompanyID = ISNULL({LeadDocument.CompanyID},0) OR (ISNULL(CompanyID,0) = 0))",
195
+ expected: [
196
+ ["CompanyID", "=", 7],
197
+ "or",
198
+ ["CompanyID", "=", null],
199
+ "or",
200
+ ["CompanyID", "=", 0],
201
+ "or",
202
+ ["CompanyID", "=", null]
203
+
204
+ ]
192
205
  }
193
206
  ];
194
207