sqlparser-devexpress 2.4.2 → 2.5.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sqlparser-devexpress",
3
- "version": "2.4.2",
3
+ "version": "2.5.0",
4
4
  "main": "src/index.js",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -164,9 +164,13 @@ function DevExpressConverter() {
164
164
  if (includeExtradata)
165
165
  comparison = [left, operatorToken, right, { type: originalOperator }, right];
166
166
 
167
- // Last null because of special case when using dropdown it https://github.com/DevExpress/DevExtreme/blob/25_1/packages/devextreme/js/__internal/data/m_utils.ts#L18 it takes last value as null
168
- if ((ast.left && isFunctionNullCheck(ast.left, true)) || (ast.value && isFunctionNullCheck(ast.value, false))) {
167
+ let isLeftNullCheck = isFunctionNullCheck(ast.left, true);
168
+ let isRightNullCheck = isFunctionNullCheck(ast.right, false) || (ast.value && isFunctionNullCheck(ast.value, false));
169
+ let isBothNullChecks = isLeftNullCheck && isRightNullCheck;
170
+
171
+ if (!isBothNullChecks && isLeftNullCheck) {
169
172
  const nullCheckArg = (ast.left ?? ast.value).args[1]?.value;
173
+
170
174
  let baseComparison = comparison;
171
175
 
172
176
  if (Array.isArray(right) && (right.includes("or") || right.includes("and"))) {
@@ -174,18 +178,56 @@ function DevExpressConverter() {
174
178
  baseComparison = [[left, operatorToken, valueRight], ...right];
175
179
  }
176
180
 
177
- comparison = [baseComparison, 'or', [left, operatorToken, null, { type: "ISNULL", defaultValue: nullCheckArg }, null]];
178
- } else if (ast.right && isFunctionNullCheck(ast.right, true)) {
179
- const nullCheckArg = ast.right.args[1]?.value;
181
+ comparison = [[...baseComparison], 'or', [left, operatorToken, null, { type: "ISNULL", position: "column", defaultValue: nullCheckArg }, null]];
182
+
183
+ } else if (!isBothNullChecks && isRightNullCheck) {
184
+ const nullCheckArg = (ast.right ?? ast.value).args[1]?.value;
185
+
180
186
  let baseComparison = comparison;
181
187
 
182
- if (Array.isArray(right) && (right.includes("or") || right.includes("and"))) {
183
- const valueRight = right.shift();
184
- baseComparison = [[left, operatorToken, valueRight], ...right];
188
+ if (Array.isArray(left) && (left.includes("or") || left.includes("and"))) {
189
+ const valueLeft = left.shift();
190
+ baseComparison = [[valueLeft, operatorToken, right], ...left];
185
191
  }
186
192
 
187
- comparison = [baseComparison, 'or', [right, operatorToken, null, { type: "ISNULL", defaultValue: nullCheckArg }, null]];
193
+ comparison = [[...baseComparison], 'or', [left, operatorToken, null, { type: "ISNULL", position: "value", defaultValue: nullCheckArg }, null]];
194
+
195
+ } else if (isBothNullChecks) {
196
+ const nullCheckArgleft = (ast.left ?? ast.value).args[1]?.value;
197
+ const nullCheckArgright = (ast.right ?? ast.value).args[1]?.value;
198
+
199
+ let baseComparison = comparison;
200
+
201
+ if (Array.isArray(left) && (left.includes("or") || left.includes("and"))) {
202
+ const valueLeft = left.shift();
203
+ baseComparison = [[valueLeft, operatorToken, right], ...left];
204
+ }
205
+
206
+ comparison = [[...baseComparison, { type: "ISNULL", position: "both", defaultValue: nullCheckArgleft, defaultValueRight: nullCheckArgright }, baseComparison[2]], 'or', [left, operatorToken, null]];
207
+
188
208
  }
209
+ // Last null because of special case when using dropdown it https://github.com/DevExpress/DevExtreme/blob/25_1/packages/devextreme/js/__internal/data/m_utils.ts#L18 it takes last value as null
210
+ // if ((ast.left && isFunctionNullCheck(ast.left, true)) || (ast.value && isFunctionNullCheck(ast.value, false))) {
211
+ // const nullCheckArg = (ast.left ?? ast.value).args[1]?.value;
212
+ // let baseComparison = comparison;
213
+
214
+ // if (Array.isArray(right) && (right.includes("or") || right.includes("and"))) {
215
+ // const valueRight = right.shift();
216
+ // baseComparison = [[left, operatorToken, valueRight], ...right];
217
+ // }
218
+
219
+ // comparison = [baseComparison, 'or', [left, operatorToken, null, { type: "ISNULL", defaultValue: nullCheckArg }, null]];
220
+ // } else if (ast.right && isFunctionNullCheck(ast.right, true)) {
221
+ // const nullCheckArg = ast.right.args[1]?.value;
222
+ // let baseComparison = comparison;
223
+
224
+ // if (Array.isArray(right) && (right.includes("or") || right.includes("and"))) {
225
+ // const valueRight = right.shift();
226
+ // baseComparison = [[left, operatorToken, valueRight], ...right];
227
+ // }
228
+
229
+ // comparison = [baseComparison, 'or', [right, operatorToken, null, { type: "ISNULL", defaultValue: nullCheckArg }, null]];
230
+ // }
189
231
 
190
232
  // Apply short-circuit evaluation if enabled
191
233
  if (EnableShortCircuit && IsValueNullShortCircuit && (left == null || right == null)) {
@@ -106,6 +106,21 @@ export function parse(input, variables = []) {
106
106
  return { type: "logical", operator, left: { type: "function", name: functionName, args: functionArgs }, right: rightOperand };
107
107
  }
108
108
 
109
+ if (rightOperand.type === "logical" && nodeType === "comparison") {
110
+ return {
111
+ type: "logical",
112
+ left: {
113
+ type: "comparison",
114
+ left: { type: "function", name: functionName, args: functionArgs },
115
+ operator: operator,
116
+ right: rightOperand.left
117
+ },
118
+ operator: rightOperand.operator,
119
+ right: rightOperand.right
120
+
121
+ }
122
+ }
123
+
109
124
  return {
110
125
  type: "comparison",
111
126
  left: { type: "function", name: functionName, args: functionArgs },
@@ -134,11 +149,10 @@ export function parse(input, variables = []) {
134
149
  operator: operator,
135
150
  value: rightList
136
151
  };
137
- }
138
-
139
- if (LOGICAL_OPERATORS.includes(operator.toLowerCase())) {
152
+ } else if (LOGICAL_OPERATORS.includes(operator.toLowerCase())) {
140
153
  // Recursively parse the right-hand expression with adjusted precedence
141
- const right = parseExpression(OPERATOR_PRECEDENCE[operator]);
154
+ // Add 1 to ensure left-associativity for operators of the same precedence
155
+ const right = parseExpression(OPERATOR_PRECEDENCE[operator] + 1);
142
156
  left = { type: "logical", operator, left, right };
143
157
  } else if (currentToken?.type == "identifier") {
144
158
  const right = parseValue(operator);
@@ -173,7 +187,32 @@ export function parse(input, variables = []) {
173
187
  }
174
188
 
175
189
  // Handle function calls like ISNULL(field)
176
- if (currentToken.type === "function") return parseFunction();
190
+ if (currentToken.type === "function") {
191
+ const functionNode = parseFunction();
192
+
193
+ // Check if the function is followed by a comparison operator
194
+ if (currentToken && currentToken.type === "operator" && !LOGICAL_OPERATORS.includes(currentToken.value.toLowerCase())) {
195
+ const operator = currentToken.value.toLowerCase();
196
+ const originalOperator = currentToken.originalValue;
197
+ next();
198
+
199
+ if (operator === "between") {
200
+ return parseBetweenComparison(functionNode, operator);
201
+ }
202
+
203
+ const value = parseValue(operator);
204
+
205
+ return {
206
+ type: "comparison",
207
+ field: functionNode,
208
+ operator,
209
+ value,
210
+ originalOperator
211
+ };
212
+ }
213
+
214
+ return functionNode;
215
+ }
177
216
 
178
217
  // Handle literal values (numbers, strings, null)
179
218
  if (LITERALS.includes(currentToken.type)) {
@@ -186,7 +225,7 @@ export function parse(input, variables = []) {
186
225
  const field = parseValue();
187
226
 
188
227
  // Check if it's part of a comparison expression
189
- if (currentToken && currentToken.type === "operator") {
228
+ if (currentToken && currentToken.type === "operator" && !LOGICAL_OPERATORS.includes(currentToken.value.toLowerCase())) {
190
229
  const operator = currentToken.value.toLowerCase();
191
230
  const originalOperator = currentToken.originalValue;
192
231
  next();
package/src/debug.js CHANGED
@@ -11,7 +11,8 @@
11
11
  // 'LeadStatementGlobalRpt.StateID': null,
12
12
  // 'LeadStatementGlobalRpt.RegionID': null,
13
13
  // 'ServiceOrderDocument.SourceID': 2,
14
- // 'CustomerOrders.OrderID': 76548
14
+ // 'CustomerOrders.OrderID': 76548,
15
+ // "TransferOutwardDocument.CompanyID": 7,
15
16
  // }
16
17
 
17
18
  // export function parseFilterString(filterString, sampleData = null) {
@@ -28,7 +29,7 @@
28
29
  // return convertToDevExpressFormat({ ast: astTree, resultObject: sampleData, isValueNullShortCircuit: true });
29
30
  // }
30
31
 
31
- // const devexpress = parseFilterString("ISNULL(CompanyID,0) = ISNULL({CustomerOrders.OrderID},0) OR (ISNULL(CompanyID,0) = 0)", sampleData);
32
+ // const devexpress = parseFilterString("ISNULL(CompanyID,0) = ISNULL({TransferOutwardDocument.CompanyID},0) OR ISNULL(CompanyID,0) = 0", sampleData);
32
33
  // console.log("DevExpress Filter:", JSON.stringify(devexpress, null, 2));
33
34
  // // const devexpress = parseFilterString("(RS2ID in ({LeadStatementGlobalRpt.StateID}) Or ({LeadStatementGlobalRpt.StateID} =0)) And (RS3ID in (0,{LeadStatementGlobalRpt.RegionID}) Or {LeadStatementGlobalRpt.RegionID} =0 )", sampleData);
34
35
 
@@ -71,18 +71,20 @@ describe("Parser SQL to dx Filter Builder", () => {
71
71
  expected: [
72
72
  [
73
73
  [
74
- ["FromDate", "<=", "2022-01-01"],
75
- 'and',
76
- ["ToDate", ">=", "2022-01-01"]
74
+ [
75
+ ["FromDate", "<=", "2022-01-01"],
76
+ 'and',
77
+ ["ToDate", ">=", "2022-01-01"]
78
+ ],
79
+ 'or',
80
+ ["ToDate", "=", null, { "type": "IS" }, null]
77
81
  ],
78
- 'or',
79
- ["ToDate", "=", null, { "type": "IS" }, null]
80
- ],
81
- "and",
82
- [
83
- ["BranchID", "=", 42],
84
- "or",
85
- ["RefBranchID", "=", null, { "type": "IS" }, null]
82
+ "and",
83
+ [
84
+ ["BranchID", "=", 42],
85
+ "or",
86
+ ["RefBranchID", "=", null, { "type": "IS" }, null]
87
+ ]
86
88
  ],
87
89
  "and",
88
90
  [
@@ -137,11 +139,11 @@ describe("Parser SQL to dx Filter Builder", () => {
137
139
  expected: [
138
140
  ["SourceID", "=", 2],
139
141
  "or",
140
- ["SourceID", "=", null, { "defaultValue": 0, "type": "ISNULL" }, null],
142
+ ["SourceID", "=", null, { "defaultValue": 0, "position": "column", "type": "ISNULL" }, null],
141
143
  "or",
142
144
  ["SourceID", "=", 0],
143
145
  "or",
144
- ["SourceID", "=", null, { "defaultValue": 0, "type": "ISNULL" }, null],
146
+ ["SourceID", "=", null, { "defaultValue": 0, "position": "column", "type": "ISNULL" }, null],
145
147
  "or",
146
148
  ["SourceID", "=", false]
147
149
  ]
@@ -155,7 +157,7 @@ describe("Parser SQL to dx Filter Builder", () => {
155
157
  [
156
158
  ["CompanyID", "=", 0],
157
159
  "or",
158
- ["CompanyID", "=", null, { "defaultValue": 0, "type": "ISNULL" }, null],
160
+ ["CompanyID", "=", null, { "defaultValue": 0, "position": "column", "type": "ISNULL" }, null],
159
161
  "or",
160
162
  ["CompanyID", "=", false]
161
163
  ]
@@ -164,7 +166,7 @@ describe("Parser SQL to dx Filter Builder", () => {
164
166
  [
165
167
  ["IsSubdealer", "=", true],
166
168
  "or",
167
- ["IsSubdealer", "=", null, { "defaultValue": 0, "type": "ISNULL" }, null]
169
+ ["IsSubdealer", "=", null, { "defaultValue": 0, "position": "column", "type": "ISNULL" }, null]
168
170
  ]
169
171
  ]
170
172
  },
@@ -189,9 +191,9 @@ describe("Parser SQL to dx Filter Builder", () => {
189
191
  {
190
192
  input: "(ISNULL(TicketID, 0) = ISNULL({SupportResolution.TicketID}, 0))",
191
193
  expected: [
192
- ["TicketID", "=", 123],
194
+ ["TicketID", "=", 123, { "type": "ISNULL", "position": "both", "defaultValue": 0, "defaultValueRight": 0 }, 123],
193
195
  "or",
194
- ["TicketID", "=", null, { "defaultValue": 0, "type": "ISNULL" }, null]
196
+ ["TicketID", "=", null]
195
197
  ]
196
198
  },
197
199
  {
@@ -199,14 +201,13 @@ describe("Parser SQL to dx Filter Builder", () => {
199
201
  expected: [
200
202
  ["CompanyID", "=", 7],
201
203
  "or",
202
- ["CompanyID", "=", null, { "defaultValue": 0, "type": "ISNULL" }, null],
204
+ ["CompanyID", "=", null, { "type": "ISNULL", "position": "value", "defaultValue": 0 }, null],
203
205
  "or",
204
206
  ["CompanyID", "=", 0],
205
207
  "or",
206
- ["CompanyID", "=", null, { "defaultValue": 0, "type": "ISNULL" }, null],
208
+ ["CompanyID", "=", null, { "type": "ISNULL", "position": "column", "defaultValue": 0 }, null],
207
209
  "or",
208
210
  ["CompanyID", "=", false]
209
-
210
211
  ]
211
212
  },
212
213
  {
@@ -306,31 +307,15 @@ describe("Parser SQL to dx Filter Builder", () => {
306
307
  {
307
308
  input: "ISNULL(CompanyID,0) = ISNULL({TransferOutwardDocument.CompanyID},0) OR (ISNULL(CompanyID,0) = 0)",
308
309
  expected: [
309
- [
310
-
311
- ["CompanyID", "=", 7],
312
- "or",
313
- [
314
- ["CompanyID", "=", 0],
315
- "or",
316
- ["CompanyID", "=", null,
317
- {
318
- "type": "ISNULL",
319
- "defaultValue": 0
320
- }, null
321
- ],
322
- "or",
323
- ["CompanyID", "=", false]
324
- ]
325
- ],
310
+ ["CompanyID", "=", 7, { "type": "ISNULL", "position": "both", "defaultValue": 0, "defaultValueRight": 0 }, 7],
326
311
  "or",
327
- [
328
- "CompanyID", "=", null,
329
- {
330
- "type": "ISNULL",
331
- "defaultValue": 0
332
- }, null
333
- ]
312
+ ["CompanyID", "=", null],
313
+ "or",
314
+ ["CompanyID", "=", 0],
315
+ "or",
316
+ ["CompanyID", "=", null, { "type": "ISNULL", "position": "column", "defaultValue": 0 }, null],
317
+ "or",
318
+ ["CompanyID", "=", false]
334
319
  ]
335
320
  }
336
321
  ];