sqlparser-devexpress 2.0.6 → 2.0.8
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 +2 -1
- package/src/@types/default.d.ts +35 -0
- package/src/core/converter.js +2 -2
- package/src/core/parser.js +30 -0
- package/src/core/tokenizer.js +27 -1
- package/src/index.js +5 -1
- package/tests/parser.test.js +18 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sqlparser-devexpress",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.8",
|
|
4
4
|
"main": "src/index.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"import": "./src/index.js",
|
|
11
11
|
"require": "./src/index.js"
|
|
12
12
|
},
|
|
13
|
+
"types": "src/@types/default.d.ts",
|
|
13
14
|
"keywords": [
|
|
14
15
|
"sql",
|
|
15
16
|
"parser",
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export type StateDataObject = Record<string, any>;
|
|
2
|
+
|
|
3
|
+
export interface SanitizedQuery {
|
|
4
|
+
sanitizedSQL: string;
|
|
5
|
+
extractedVariables: string[];
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface ParsedResult {
|
|
9
|
+
ast: any; // Define a more specific type if possible
|
|
10
|
+
variables: string[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface ConvertToDevExpressFormatParams {
|
|
14
|
+
ast: any; // Define a more specific type if possible
|
|
15
|
+
variables: string[];
|
|
16
|
+
resultObject: StateDataObject;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function sanitizeQuery(filterString: string): SanitizedQuery;
|
|
20
|
+
|
|
21
|
+
export function parse(query: string, variables: string[]): ParsedResult;
|
|
22
|
+
|
|
23
|
+
export function convertToDevExpressFormat(params: ConvertToDevExpressFormatParams): any;
|
|
24
|
+
|
|
25
|
+
export function convertSQLToAst(
|
|
26
|
+
filterString: string,
|
|
27
|
+
SampleData?: StateDataObject | null,
|
|
28
|
+
enableConsoleLogs?: boolean
|
|
29
|
+
): ParsedResult;
|
|
30
|
+
|
|
31
|
+
export function convertAstToDevextreme(
|
|
32
|
+
ast: any, // Define a more specific type if possible
|
|
33
|
+
variables: string[],
|
|
34
|
+
state: StateDataObject
|
|
35
|
+
): any;
|
package/src/core/converter.js
CHANGED
|
@@ -139,8 +139,8 @@ function DevExpressConverter() {
|
|
|
139
139
|
return handleInOperator(ast);
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
-
const left = convertValue(ast.field);
|
|
143
|
-
const right = convertValue(ast.value);
|
|
142
|
+
const left = ast.left !== undefined ? processAstNode(ast.left) : convertValue(ast.field);
|
|
143
|
+
const right = ast.right !== undefined ? processAstNode(ast.right) : convertValue(ast.value);
|
|
144
144
|
const comparison = [left, ast.operator.toLowerCase(), right];
|
|
145
145
|
|
|
146
146
|
// Apply short-circuit evaluation if enabled
|
package/src/core/parser.js
CHANGED
|
@@ -19,6 +19,21 @@ export function parse(input, variables = []) {
|
|
|
19
19
|
const tokenizer = new Tokenizer(input);
|
|
20
20
|
let currentToken = tokenizer.nextToken();
|
|
21
21
|
|
|
22
|
+
// // Debugging: log the tokens
|
|
23
|
+
// const tokens = [];
|
|
24
|
+
// let tempToken = currentToken;
|
|
25
|
+
// while (tempToken) {
|
|
26
|
+
// tokens.push(tempToken);
|
|
27
|
+
// tempToken = tokenizer.peekNextToken();
|
|
28
|
+
// tokenizer.nextToken();
|
|
29
|
+
// }
|
|
30
|
+
|
|
31
|
+
// console.log("Tokens:", tokens);
|
|
32
|
+
|
|
33
|
+
// // Reset the tokenizer
|
|
34
|
+
// tokenizer.reset();
|
|
35
|
+
// currentToken = tokenizer.nextToken();
|
|
36
|
+
|
|
22
37
|
// Moves to the next token in the input
|
|
23
38
|
function next() {
|
|
24
39
|
currentToken = tokenizer.nextToken();
|
|
@@ -68,6 +83,21 @@ export function parse(input, variables = []) {
|
|
|
68
83
|
}
|
|
69
84
|
|
|
70
85
|
next(); // Consume the closing parenthesis
|
|
86
|
+
|
|
87
|
+
// Check if the next token is an operator and process it
|
|
88
|
+
if (currentToken && currentToken.type === "operator") {
|
|
89
|
+
const operator = currentToken.value;
|
|
90
|
+
next(); // Move to the next token after the operator
|
|
91
|
+
const value = parseValue(); // Parse the value after the operator
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
type: "comparison",
|
|
95
|
+
left: { type: "function", name: funcName, args },
|
|
96
|
+
operator,
|
|
97
|
+
value
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
71
101
|
return { type: "function", name: funcName, args };
|
|
72
102
|
}
|
|
73
103
|
|
package/src/core/tokenizer.js
CHANGED
|
@@ -6,8 +6,8 @@ const tokenPatterns = {
|
|
|
6
6
|
number: "\\d+", // Matches numerical values
|
|
7
7
|
placeholder: "'?\\{[^}]+\\}'?", // Matches placeholders like {variable} or '{variable}'
|
|
8
8
|
string: "'(?:''|[^'])*'", // Matches strings, allowing for escaped single quotes ('')
|
|
9
|
+
operator: "=>|<=|!=|>=|=|<>|>|<|\\bAND\\b|\\bOR\\b|\\bBETWEEN\\b|\\bIN\\b|\\bLIKE\\b|\\bIS NOT\\b|\\bNOT LIKE\\b|\\bIS\\b", // Matches SQL operators and logical keywords
|
|
9
10
|
identifier: "[\\w.]+", // Matches identifiers, including table.column format
|
|
10
|
-
operator: "(?<!\w)(=>|<=|!=|>=|=|<>|>|<|AND|OR|BETWEEN|IN|LIKE|IS)(?!\w)", // Matches SQL operators and logical keywords
|
|
11
11
|
paren: "[()]", // Matches parentheses
|
|
12
12
|
comma: "," // Matches commas
|
|
13
13
|
};
|
|
@@ -47,12 +47,38 @@ class Tokenizer {
|
|
|
47
47
|
// Remove surrounding single quotes from placeholders
|
|
48
48
|
if (type === "placeholder") value = value.replace(/^['"]|['"]$/g, "");
|
|
49
49
|
|
|
50
|
+
if (type === "operator") {
|
|
51
|
+
const lowerValue = value.toLowerCase();
|
|
52
|
+
|
|
53
|
+
if (lowerValue === "is") {
|
|
54
|
+
value = "=";
|
|
55
|
+
} else if (lowerValue === "is not") {
|
|
56
|
+
value = "!=";
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
50
60
|
return { type, value };
|
|
51
61
|
}
|
|
52
62
|
|
|
53
63
|
// If no valid token is found, throw an error with the remaining input for debugging
|
|
54
64
|
throw new Error(`Unexpected token at: ${this.input.slice(this.index)}`);
|
|
55
65
|
}
|
|
66
|
+
|
|
67
|
+
peekNextToken() {
|
|
68
|
+
if (this.index >= this.input.length) return null;
|
|
69
|
+
|
|
70
|
+
const savedIndex = this.index; // Save current index
|
|
71
|
+
try {
|
|
72
|
+
return this.nextToken(); // Get next token
|
|
73
|
+
} finally {
|
|
74
|
+
this.index = savedIndex; // Restore index
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
reset() {
|
|
79
|
+
this.index = 0; // Reset index to the beginning of the input
|
|
80
|
+
}
|
|
81
|
+
|
|
56
82
|
}
|
|
57
83
|
|
|
58
84
|
export { Tokenizer };
|
package/src/index.js
CHANGED
|
@@ -38,6 +38,10 @@ export function convertAstToDevextreme(ast, variables, state) {
|
|
|
38
38
|
// Example usage
|
|
39
39
|
// const devExpressFilter = parseFilterString("((ISNULL({0}, 0) = 0 AND CompanyID = {1}) OR CompanyID IS NULL) OR BranchID = {0} | [LeadDocument.BranchID] | [LeadDocument.CompanyID]", sampleResultObject);
|
|
40
40
|
// const devExpressFilter = parseFilterString("FromDate <= '{TransferOutwardDocument.DocDate}' ", sampleResultObject, "TransferOutwardDocument", "789");
|
|
41
|
-
//const devExpressFilter =
|
|
41
|
+
// const devExpressFilter = parseFilterString("(RS2ID in ({SaleOrderStatusStmtGlobalRpt.StateID}) Or ({SaleOrderStatusStmtGlobalRpt.StateID} =0)) And (RS3ID in (0,{SaleOrderStatusStmtGlobalRpt.RegionID}) Or {SaleOrderStatusStmtGlobalRpt.RegionID} =0 )", sampleResultObject,);
|
|
42
42
|
|
|
43
|
+
// const devExpressFilter = convertSQLToAst("ISNULL(SourceID,0) = {ServiceOrderDocument.SourceID} OR ISNULL(SourceID,0) = 0");
|
|
44
|
+
// const devExpressFilterresult = convertAstToDevextreme(devExpressFilter.ast, devExpressFilter.variables,{'ServiceOrderDocument.SourceID': 2});
|
|
43
45
|
// console.log("DevExpress Filter:", JSON.stringify(devExpressFilter, null, 2));
|
|
46
|
+
// console.log("DevExpress Result:", JSON.stringify(devExpressFilterresult, null, 2));
|
|
47
|
+
|
package/tests/parser.test.js
CHANGED
|
@@ -130,6 +130,22 @@ describe("Parser SQL to dx Filter Builder", () => {
|
|
|
130
130
|
expected: [
|
|
131
131
|
"FromDate", "between", ["10-10-2021", "10-10-2022"]
|
|
132
132
|
]
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
input: "BranchID is Null OR BranchID is not 12",
|
|
136
|
+
expected: [
|
|
137
|
+
["BranchID", "=", null],
|
|
138
|
+
"or",
|
|
139
|
+
["BranchID", "!=", 12]
|
|
140
|
+
]
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
input: "ISNULL(SourceID,0) = {ServiceOrderDocument.SourceID} OR ISNULL(SourceID,0) = 0",
|
|
144
|
+
expected: [
|
|
145
|
+
["SourceID", "=", 2],
|
|
146
|
+
"or",
|
|
147
|
+
["SourceID", "=", 0]
|
|
148
|
+
]
|
|
133
149
|
}
|
|
134
150
|
];
|
|
135
151
|
|
|
@@ -193,5 +209,6 @@ const sampleData = {
|
|
|
193
209
|
"TransferOutwardDocument.RefBranchID": 42,
|
|
194
210
|
"TransferOutwardDocument.CompanyID": 7,
|
|
195
211
|
"LeadDocument.BranchID": 42,
|
|
196
|
-
"LeadDocument.CompanyID": 7
|
|
212
|
+
"LeadDocument.CompanyID": 7,
|
|
213
|
+
"ServiceOrderDocument.SourceID": 2
|
|
197
214
|
};
|