sqlparser-devexpress 2.3.13 → 2.3.15
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 +21 -3
- package/src/core/parser.js +6 -3
- package/src/core/tokenizer.js +4 -3
- package/tests/parser.test.js +20 -9
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))) {
|
|
@@ -221,6 +229,16 @@ function DevExpressConverter() {
|
|
|
221
229
|
return list.includes(fieldVal);
|
|
222
230
|
else if (operator === "NOT IN")
|
|
223
231
|
return !list.includes(fieldVal);
|
|
232
|
+
} else if (!Array.isArray(resolvedValue)) {
|
|
233
|
+
// normalize numeric strings if LHS is number
|
|
234
|
+
const value = (typeof resolvedValue === "string" && !isNaN(resolvedValue) && typeof fieldVal === "number")
|
|
235
|
+
? Number(resolvedValue)
|
|
236
|
+
: resolvedValue;
|
|
237
|
+
|
|
238
|
+
if (operator === "IN")
|
|
239
|
+
return fieldVal == value;
|
|
240
|
+
else if (operator === "NOT IN")
|
|
241
|
+
return fieldVal != value;
|
|
224
242
|
}
|
|
225
243
|
}
|
|
226
244
|
|
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
|
{
|
|
@@ -216,11 +216,21 @@ describe("Parser SQL to dx Filter Builder", () => {
|
|
|
216
216
|
expected: []
|
|
217
217
|
},
|
|
218
218
|
{
|
|
219
|
-
input: "ID IN (
|
|
219
|
+
input: "ID IN ({WorkOrderLine.CompanyIDs}) AND 0 IN ({WorkOrderLine.CompanyIDs})",
|
|
220
220
|
expected: [
|
|
221
|
-
["ID", "=", "
|
|
221
|
+
["ID", "=", "0"],
|
|
222
222
|
"or",
|
|
223
|
-
["ID", "=", "
|
|
223
|
+
["ID", "=", "1"]
|
|
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]
|
|
224
234
|
]
|
|
225
235
|
}
|
|
226
236
|
];
|
|
@@ -285,4 +295,5 @@ const sampleData = {
|
|
|
285
295
|
"SupportResolution.TicketID": 123,
|
|
286
296
|
"SaleOrderStatusStmtGlobalRpt.StateID": null,
|
|
287
297
|
"SaleOrderStatusStmtGlobalRpt.RegionID": null,
|
|
298
|
+
"WorkOrderLine.CompanyIDs": ["0,1"],
|
|
288
299
|
};
|