sqlparser-devexpress 2.3.14 → 2.3.16
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 +1 -1
- package/src/core/converter.js +15 -3
- package/src/core/parser.js +6 -3
- package/src/core/tokenizer.js +4 -3
- package/tests/parser.test.js +44 -6
package/package.json
CHANGED
package/src/core/converter.js
CHANGED
|
@@ -124,10 +124,11 @@ function DevExpressConverter() {
|
|
|
124
124
|
*/
|
|
125
125
|
function handleComparisonOperator(ast) {
|
|
126
126
|
const operator = ast.operator.toUpperCase();
|
|
127
|
+
const originalOperator = ast.originalOperator?.toUpperCase();
|
|
127
128
|
|
|
128
129
|
// Handle "IS NULL" condition
|
|
129
|
-
if (operator === "IS" && ast.value === null) {
|
|
130
|
-
return [ast.field, "=", null];
|
|
130
|
+
if ((operator === "IS" || originalOperator === "IS") && ast.value === null) {
|
|
131
|
+
return [ast.field, "=", null, { type: originalOperator }, null];
|
|
131
132
|
}
|
|
132
133
|
|
|
133
134
|
// Handle "IN" condition, including comma-separated values
|
|
@@ -140,14 +141,21 @@ function DevExpressConverter() {
|
|
|
140
141
|
const right = ast.right !== undefined ? processAstNode(ast.right) : convertValue(ast.value);
|
|
141
142
|
const rightDefault = ast.right?.args[1]?.value;
|
|
142
143
|
let operatorToken = ast.operator.toLowerCase();
|
|
144
|
+
let includeExtradata = false;
|
|
143
145
|
|
|
144
146
|
if (operatorToken === "like") {
|
|
145
147
|
operatorToken = "contains";
|
|
146
148
|
} else if (operatorToken === "not like") {
|
|
147
149
|
operatorToken = "notcontains";
|
|
150
|
+
} else if (operatorToken === "=" && originalOperator === "IS") {
|
|
151
|
+
includeExtradata = true
|
|
152
|
+
} else if (operatorToken == "!=" && originalOperator === "IS NOT") {
|
|
153
|
+
operatorToken = "!=";
|
|
154
|
+
includeExtradata = true;
|
|
148
155
|
}
|
|
149
|
-
|
|
150
156
|
let comparison = [left, operatorToken, right];
|
|
157
|
+
if (includeExtradata)
|
|
158
|
+
comparison = [left, operatorToken, right, { type: originalOperator }, right];
|
|
151
159
|
|
|
152
160
|
// 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
|
|
153
161
|
if ((ast.left && isFunctionNullCheck(ast.left, true)) || (ast.value && isFunctionNullCheck(ast.value, false))) {
|
|
@@ -402,6 +410,10 @@ function DevExpressConverter() {
|
|
|
402
410
|
|
|
403
411
|
if ((left !== null && isNaN(left)) || (right !== null && isNaN(right))) return null;
|
|
404
412
|
|
|
413
|
+
// Handle NULL == 0 OR NULL == "" cases
|
|
414
|
+
if (left === null && (right == 0 || right == "")) return true;
|
|
415
|
+
if (right === null && (left == 0 || left == "")) return true;
|
|
416
|
+
|
|
405
417
|
if (left === null || right === null) {
|
|
406
418
|
if (operator === '=' || operator === '==') return left === right;
|
|
407
419
|
if (operator === '<>' || operator === '!=') return left !== right;
|
package/src/core/parser.js
CHANGED
|
@@ -178,6 +178,7 @@ export function parse(input, variables = []) {
|
|
|
178
178
|
// Check if it's part of a comparison expression
|
|
179
179
|
if (currentToken && currentToken.type === "operator") {
|
|
180
180
|
const operator = currentToken.value.toLowerCase();
|
|
181
|
+
const originalOperator = currentToken.originalValue;
|
|
181
182
|
next();
|
|
182
183
|
|
|
183
184
|
if (operator === "between") return parseBetweenComparison(field, operator);
|
|
@@ -190,7 +191,8 @@ export function parse(input, variables = []) {
|
|
|
190
191
|
type: "comparison",
|
|
191
192
|
field,
|
|
192
193
|
operator,
|
|
193
|
-
value: functionNode
|
|
194
|
+
value: functionNode,
|
|
195
|
+
originalOperator
|
|
194
196
|
}
|
|
195
197
|
}
|
|
196
198
|
|
|
@@ -199,7 +201,8 @@ export function parse(input, variables = []) {
|
|
|
199
201
|
type: "comparison",
|
|
200
202
|
field,
|
|
201
203
|
operator,
|
|
202
|
-
value: functionNode.left
|
|
204
|
+
value: functionNode.left,
|
|
205
|
+
originalOperator
|
|
203
206
|
};
|
|
204
207
|
|
|
205
208
|
functionNode.left = leftComparison;
|
|
@@ -215,7 +218,7 @@ export function parse(input, variables = []) {
|
|
|
215
218
|
throw new Error(`Invalid comparison: ${field} ${operator} ${value}`);
|
|
216
219
|
}
|
|
217
220
|
|
|
218
|
-
return { type: "comparison", field, operator, value };
|
|
221
|
+
return { type: "comparison", field, operator, value, originalOperator };
|
|
219
222
|
}
|
|
220
223
|
|
|
221
224
|
return { type: "field", value: field };
|
package/src/core/tokenizer.js
CHANGED
|
@@ -45,12 +45,13 @@ class Tokenizer {
|
|
|
45
45
|
if (!type || type === "whitespace") return this.nextToken();
|
|
46
46
|
|
|
47
47
|
let value = match.groups[type];
|
|
48
|
+
const originalValue = value; // Store the original value for debugging
|
|
48
49
|
let dataType = null;
|
|
49
50
|
|
|
50
51
|
// Remove surrounding single quotes from placeholders
|
|
51
|
-
if(type === "placeholder"){
|
|
52
|
+
if (type === "placeholder") {
|
|
52
53
|
|
|
53
|
-
if(value.startsWith("'") && value.endsWith("'")){
|
|
54
|
+
if (value.startsWith("'") && value.endsWith("'")) {
|
|
54
55
|
dataType = "string";
|
|
55
56
|
}
|
|
56
57
|
|
|
@@ -77,7 +78,7 @@ class Tokenizer {
|
|
|
77
78
|
}
|
|
78
79
|
|
|
79
80
|
|
|
80
|
-
return { type, value, ...(dataType !== null && { dataType }) };
|
|
81
|
+
return { type, value, originalValue, ...(dataType !== null && { dataType }) };
|
|
81
82
|
}
|
|
82
83
|
|
|
83
84
|
// If no valid token is found, throw an error with the remaining input for debugging
|
package/tests/parser.test.js
CHANGED
|
@@ -76,13 +76,13 @@ describe("Parser SQL to dx Filter Builder", () => {
|
|
|
76
76
|
["ToDate", ">=", "2022-01-01"]
|
|
77
77
|
],
|
|
78
78
|
'or',
|
|
79
|
-
["ToDate", "=", null]
|
|
79
|
+
["ToDate", "=", null, { "type": "IS" }, null]
|
|
80
80
|
],
|
|
81
81
|
"and",
|
|
82
82
|
[
|
|
83
83
|
["BranchID", "=", 42],
|
|
84
84
|
"or",
|
|
85
|
-
["RefBranchID", "=", null]
|
|
85
|
+
["RefBranchID", "=", null, { "type": "IS" }, null]
|
|
86
86
|
],
|
|
87
87
|
"and",
|
|
88
88
|
[
|
|
@@ -90,7 +90,7 @@ describe("Parser SQL to dx Filter Builder", () => {
|
|
|
90
90
|
// "or",
|
|
91
91
|
// [7,"=",0],
|
|
92
92
|
"or",
|
|
93
|
-
["CompanyID", "=", null]
|
|
93
|
+
["CompanyID", "=", null, { "type": "IS" }, null]
|
|
94
94
|
]
|
|
95
95
|
]
|
|
96
96
|
},
|
|
@@ -112,7 +112,7 @@ describe("Parser SQL to dx Filter Builder", () => {
|
|
|
112
112
|
["CompanyID", "=", 7],
|
|
113
113
|
// ],
|
|
114
114
|
"or",
|
|
115
|
-
["CompanyID", "=", null],
|
|
115
|
+
["CompanyID", "=", null, { "type": "IS" }, null],
|
|
116
116
|
// ],
|
|
117
117
|
'or',
|
|
118
118
|
["BranchID", "=", 42]
|
|
@@ -127,9 +127,9 @@ describe("Parser SQL to dx Filter Builder", () => {
|
|
|
127
127
|
{
|
|
128
128
|
input: "BranchID is Null OR BranchID is not 12",
|
|
129
129
|
expected: [
|
|
130
|
-
["BranchID", "=", null],
|
|
130
|
+
["BranchID", "=", null, { "type": "IS" }, null],
|
|
131
131
|
"or",
|
|
132
|
-
["BranchID", "!=", 12]
|
|
132
|
+
["BranchID", "!=", 12, { "type": "IS NOT" }, 12]
|
|
133
133
|
]
|
|
134
134
|
},
|
|
135
135
|
{
|
|
@@ -222,6 +222,44 @@ describe("Parser SQL to dx Filter Builder", () => {
|
|
|
222
222
|
"or",
|
|
223
223
|
["ID", "=", "1"]
|
|
224
224
|
]
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
input: "CompanyID is null OR CompanyID is 7 OR CompanyID is not 8",
|
|
228
|
+
expected: [
|
|
229
|
+
["CompanyID", "=", null, { "type": "IS" }, null],
|
|
230
|
+
"or",
|
|
231
|
+
["CompanyID", "=", 7, { "type": "IS" }, 7],
|
|
232
|
+
"or",
|
|
233
|
+
["CompanyID", "!=", 8, { "type": "IS NOT" }, 8]
|
|
234
|
+
]
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
input: "null = 0",
|
|
238
|
+
expected: []
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
input: "null = null",
|
|
242
|
+
expected: []
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
input: "null = {SaleOrderStatusStmtGlobalRpt.StateID}",
|
|
246
|
+
expected: []
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
input: "null = {SaleOrderStatusStmtGlobalRpt.RegionID}",
|
|
250
|
+
expected: []
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
input: "null = {LeadDocument.CompanyID}",
|
|
254
|
+
expected: []
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
input: "{LeadDocument.BranchID} = null",
|
|
258
|
+
expected: []
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
input: "{LeadDocument.AllowSubDealer} != null",
|
|
262
|
+
expected: []
|
|
225
263
|
}
|
|
226
264
|
];
|
|
227
265
|
|