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 +1 -1
- package/src/core/converter.js +51 -9
- package/src/core/parser.js +45 -6
- package/src/debug.js +3 -2
- package/tests/parser.test.js +29 -44
package/package.json
CHANGED
package/src/core/converter.js
CHANGED
|
@@ -164,9 +164,13 @@ function DevExpressConverter() {
|
|
|
164
164
|
if (includeExtradata)
|
|
165
165
|
comparison = [left, operatorToken, right, { type: originalOperator }, right];
|
|
166
166
|
|
|
167
|
-
|
|
168
|
-
|
|
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
|
-
|
|
179
|
-
|
|
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(
|
|
183
|
-
const
|
|
184
|
-
baseComparison = [[
|
|
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', [
|
|
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)) {
|
package/src/core/parser.js
CHANGED
|
@@ -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
|
-
|
|
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")
|
|
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({
|
|
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
|
|
package/tests/parser.test.js
CHANGED
|
@@ -71,18 +71,20 @@ describe("Parser SQL to dx Filter Builder", () => {
|
|
|
71
71
|
expected: [
|
|
72
72
|
[
|
|
73
73
|
[
|
|
74
|
-
[
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
79
|
-
[
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
|
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, { "
|
|
204
|
+
["CompanyID", "=", null, { "type": "ISNULL", "position": "value", "defaultValue": 0 }, null],
|
|
203
205
|
"or",
|
|
204
206
|
["CompanyID", "=", 0],
|
|
205
207
|
"or",
|
|
206
|
-
["CompanyID", "=", 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
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
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
|
];
|