sqlparser-devexpress 2.1.4 → 2.2.1

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.
@@ -1,4 +1,4 @@
1
- The MIT License (MIT)
1
+ MIT License
2
2
 
3
3
  Copyright (c) 2025 Rohit Mahajan
4
4
 
@@ -9,13 +9,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
9
  copies of the Software, and to permit persons to whom the Software is
10
10
  furnished to do so, subject to the following conditions:
11
11
 
12
- The above copyright notice and this permission notice shall be included in
13
- all copies or substantial portions of the Software.
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
14
 
15
15
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
16
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
17
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
18
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- THE SOFTWARE.
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,29 +1,141 @@
1
1
  # SQLParser
2
2
 
3
- SQLParser is a JavaScript library that converts SQL-like filter strings into DevExpress format filters. It provides utilities for parsing, sanitizing, and converting SQL-like expressions into a format that can be used with DevExpress components.
3
+ SQLParser is a JavaScript library that converts SQL `WHERE` clauses into a structured **Abstract Syntax Tree (AST)** and transforms them into DevExpress filter format. It removes inline parameters while preserving them as dynamic variables for flexible query processing.
4
4
 
5
- ## Usage
5
+ ## Features
6
6
 
7
- ### Convert SQL to AST
7
+ - **AST-Based Query Processing**: Parses `WHERE` clauses and generates a structured AST.
8
+ - **Supports Dynamic Parameters**: Identifies and extracts placeholders (`{param}`) for dynamic resolution.
9
+ - **Parameter Cleanup**: Removes inline parameters while maintaining their structure.
10
+ - **DevExpress-Compatible Output**: Converts parsed SQL conditions into the DevExpress filter format.
11
+ - **Short-Circuit Optimization**: By default, eliminates `value = value` expressions for DevExpress compatibility (can be disabled for performance optimization).
12
+ - **Separation of Concerns**: Generate AST once, then use it for multiple state updates.
8
13
 
9
- To convert a SQL-like filter string to an Abstract Syntax Tree (AST):
14
+ ## Installation
15
+
16
+ ```sh
17
+ npm install sqlparser-devexpress
18
+ ```
19
+
20
+ ## Example Workflow
21
+
22
+ ### **Step 1: Input SQL**
23
+
24
+ ```sql
25
+ WHERE OrderID = {CustomerOrders.OrderID} AND Status IN (1, 3)
26
+ ```
27
+
28
+ ### **Step 2: Generate AST**
10
29
 
11
30
  ```javascript
12
- const filterString= "(ID <> {Item.ID}) AND (ItemGroupType IN ({Item.AllowedItemGroupType}))";
13
- const parsedResult = convertSQLToAst(filterString);
31
+ import { convertSQLToAst } from "sqlparser-devexpress";
32
+
33
+ const sqlQuery = "OrderID = {CustomerOrders.OrderID} AND Status IN (1, 3)";
34
+ const { ast, variables } = convertSQLToAst(sqlQuery, true); // Enable logs
14
35
  ```
15
36
 
16
- ### Convert AST to DevExpress Format
37
+ #### **AST Output**
17
38
 
18
- To convert an AST to DevExpress format:
39
+ ```json
40
+ {
41
+ "type": "logical",
42
+ "operator": "AND",
43
+ "left": {
44
+ "type": "comparison",
45
+ "field": "OrderID",
46
+ "operator": "=",
47
+ "value": {
48
+ "type": "placeholder",
49
+ "value": "CustomerOrders.OrderID"
50
+ }
51
+ },
52
+ "right": {
53
+ "type": "comparison",
54
+ "field": "Status",
55
+ "operator": "in",
56
+ "value": {
57
+ "type": "value",
58
+ "value": [1, 3]
59
+ }
60
+ }
61
+ }
62
+ ```
63
+
64
+ #### Extracted Variables
65
+
66
+ The parser identifies placeholders within the SQL query and extracts them for dynamic value resolution.
67
+
68
+ #### **Example Output:**
69
+ ```json
70
+ [
71
+ "CustomerOrders.OrderID"
72
+ ]
73
+ ```
74
+
75
+ These extracted variables can be used to fetch the corresponding state values in the application. You can store them in a `Record<string, any>`, where the key is the placeholder name, and the value is the resolved data from the application's state.
76
+
77
+ ### **Step 3: Convert AST to DevExpress Format**
19
78
 
20
79
  ```javascript
21
- const ast = { /* your AST here */ };
22
- const variables = [/* your variables here */];
23
- const state = { /* your state here */ };
80
+ import { convertAstToDevextreme } from "sqlparser-devexpress";
81
+
82
+ const sampleState = {
83
+ "CustomerOrders.OrderID": 76548
84
+ };
24
85
 
25
- const devExpressFilter = convertAstToDevextreme(ast, variables, state);
86
+ const devexpressFilter = convertAstToDevextreme(ast, sampleState, true); // Short-circuit enabled (default)
26
87
 
27
- console.log(devExpressFilter);
88
+ console.log("DevExpress Filter:", JSON.stringify(devexpressFilter, null, 2));
28
89
  ```
29
90
 
91
+ #### **DevExpress Filter Output**
92
+
93
+ ```json
94
+ [
95
+ ["OrderID", "=", 76548],
96
+ "and",
97
+ [
98
+ ["Status", "=", 1],
99
+ "or",
100
+ ["Status", "=", 3]
101
+ ]
102
+ ]
103
+ ```
104
+
105
+ ## API Reference
106
+
107
+ ### `convertSQLToAst(filterString, enableConsoleLogs = false)`
108
+
109
+ - **Input:** SQL `WHERE` clause as a string.
110
+ - **Output:** An object `{ ast }` where:
111
+ - `ast`: The parsed Abstract Syntax Tree.
112
+ - **Example:**
113
+ ```javascript
114
+ const { ast } = convertSQLToAst("OrderID = {CustomerOrders.OrderID} AND Status IN (1, 3)");
115
+ ```
116
+
117
+ ### `convertAstToDevextreme(ast, state, shortCircuit = true)`
118
+
119
+ - **Input:**
120
+ - `ast`: The AST generated from `convertSQLToAst()`.
121
+ - `state`: An object containing values for placeholders.
122
+ - `shortCircuit`: (Optional, default `true`) Enables short-circuiting of `value = value` expressions for DevExpress compatibility.
123
+ - **Output:** DevExpress filter array.
124
+ - **Example:**
125
+ ```javascript
126
+ const devexpressFilter = convertAstToDevextreme(ast, sampleState, false); // Disables short-circuiting
127
+ ```
128
+
129
+ ## Roadmap
130
+
131
+ - Support for additional SQL operators and functions.
132
+ - Improved error handling and validation.
133
+
134
+ ## Contributing
135
+
136
+ Contributions are welcome! Feel free to open issues or submit pull requests.
137
+
138
+ ## License
139
+
140
+ MIT
141
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sqlparser-devexpress",
3
- "version": "2.1.4",
3
+ "version": "2.2.1",
4
4
  "main": "src/index.js",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -32,4 +32,4 @@
32
32
  "devDependencies": {
33
33
  "vitest": "^3.0.5"
34
34
  }
35
- }
35
+ }
package/src/constants.js CHANGED
@@ -9,3 +9,5 @@ export const OPERATOR_PRECEDENCE = {
9
9
 
10
10
  // Regular expression to check for unsupported SQL patterns (like SELECT-FROM or JOIN statements)
11
11
  export const UNSUPPORTED_PATTERN = /\bSELECT\b.*\bFROM\b|\bINNER\s+JOIN\b/i;
12
+
13
+ export const LOGICAL_OPERATORS = ['and', 'or'];
@@ -1,31 +1,25 @@
1
- const logicalOperators = ['and', 'or'];
1
+ import { LOGICAL_OPERATORS } from "../constants.js";
2
2
 
3
3
  /**
4
4
  * Main conversion function that sets up the global context
5
- * @param {Object} ast - The abstract syntax tree
6
- * @param {Array} vars - Array of variable names
7
- * @param {Object} results - Optional object for placeholder resolution
8
5
  * @returns {Array|null} DevExpress format filter
9
6
  */
10
7
  function DevExpressConverter() {
11
8
  // Global variables accessible throughout the converter
12
9
  let resultObject = null;
13
- let primaryEntity = null;
14
- let primaryKey = null;
15
- let variables = [];
16
10
  const EnableShortCircuit = true;
17
11
 
18
12
  /**
19
13
  * Main conversion function that sets up the global context
20
14
  * @param {Object} ast - The abstract syntax tree
21
- * @param {Array} vars - Array of variable names
22
15
  * @param {Object} ResultObject - Optional object for placeholder resolution
16
+ * @param {boolean} enableShortCircuit - Optional enabling and disabling the shortcircuit ie evaluating value = value scenario
23
17
  * @returns {Array|null} DevExpress format filter
24
18
  */
25
- function convert(ast, vars, ResultObject = null) {
19
+ function convert(ast, ResultObject = null, enableShortCircuit = true) {
26
20
  // Set up global context
27
- variables = vars;
28
21
  resultObject = ResultObject;
22
+ EnableShortCircuit = enableShortCircuit;
29
23
 
30
24
  // Process the AST
31
25
  let result = processAstNode(ast);
@@ -111,8 +105,8 @@ function DevExpressConverter() {
111
105
 
112
106
  // Detect and flatten nested logical expressions
113
107
  if (parentOperator === null) {
114
- if (left.length === 3 && logicalOperators.includes(left[1])) parentOperator = left[1];
115
- if (right.length === 3 && logicalOperators.includes(right[1])) parentOperator = right[1];
108
+ if (left.length === 3 && LOGICAL_OPERATORS.includes(left[1])) parentOperator = left[1];
109
+ if (right.length === 3 && LOGICAL_OPERATORS.includes(right[1])) parentOperator = right[1];
116
110
  }
117
111
 
118
112
  // Flatten nested logical expressions if applicable
@@ -377,12 +371,10 @@ const devExpressConverter = DevExpressConverter();
377
371
  /**
378
372
  * Converts an abstract syntax tree to DevExpress format
379
373
  * @param {Object} ast - The abstract syntax tree
380
- * @param {Array} variables - Array of variable names
381
374
  * @param {Object} resultObject - Optional object for placeholder resolution
382
- * @param {string} primaryEntity - Optional primary entity name
383
- * @param {string} primaryKey - Optional primary key value
375
+ * @param {string} enableShortCircuit - Optional enabling and disabling the shortcircuit ie evaluating value = value scenario
384
376
  * @returns {Array|null} DevExpress format filter
385
377
  */
386
- export function convertToDevExpressFormat({ ast, variables, resultObject = null }) {
387
- return devExpressConverter.init(ast, variables, resultObject);
378
+ export function convertToDevExpressFormat({ ast, resultObject = null, enableShortCircuit = true }) {
379
+ return devExpressConverter.init(ast, resultObject,enableShortCircuit);
388
380
  }
@@ -1,4 +1,4 @@
1
- import { LITERALS } from "../constants";
1
+ import { LITERALS } from "../constants.js";
2
2
 
3
3
  // Define regex patterns for different token types
4
4
  const tokenPatterns = {
@@ -9,7 +9,7 @@ const tokenPatterns = {
9
9
  placeholder: "'?\\{[^}]+\\}'?", // Matches placeholders like {variable} or '{variable}'
10
10
  string: "\\('\\w+\\'\\)|'(?:''|[^'])*'", // Matches strings, allowing for escaped single quotes ('')
11
11
  operator: "=>|<=|!=|>=|=|<>|>|<|\\bAND\\b|\\bOR\\b|\\bBETWEEN\\b|\\bIN\\b|\\bNOT IN\\b|\\bLIKE\\b|\\bIS NOT\\b|\\bNOT LIKE\\b|\\bIS\\b", // Matches SQL operators and logical keywords
12
- identifier: "[\\w.]+", // Matches identifiers, including table.column format
12
+ identifier: "[\\w.]+|\"[^\"]+\"|\\[[^\\]]+\\]", // Matches regular identifiers, quoted identifiers ("identifier"), and bracketed identifiers [identifier]
13
13
  paren: "[()]", // Matches standalone parentheses
14
14
  comma: "," // Matches commas
15
15
  };
@@ -59,8 +59,15 @@ class Tokenizer {
59
59
  }
60
60
  }
61
61
 
62
- if (LITERALS.includes(type)) value = value.replace(/^[(]|[)]$/g, "")
62
+ if (LITERALS.includes(type)){
63
+ value = value.replace(/^[(]|[)]$/g, "");
64
+ }
63
65
 
66
+ if (type === "identifier") {
67
+ value = value.replace(/^["\[]|["\]]$/g, "");
68
+ }
69
+
70
+
64
71
  return { type, value };
65
72
  }
66
73
 
package/src/debug.js CHANGED
@@ -6,17 +6,15 @@
6
6
  // import { convertToDevExpressFormat } from "./core/converter.js";
7
7
  // import { parse } from "./core/parser.js";
8
8
  // import { sanitizeQuery } from "./core/sanitizer.js";
9
- // import { convertAstToDevextreme, convertSQLToAst } from "./index.js";
10
9
 
11
10
  // const sampleData = {
12
11
  // 'LeadStatementGlobalRpt.StateID': null,
13
12
  // 'LeadStatementGlobalRpt.RegionID': null,
14
- // 'ServiceOrderDocument.SourceID': 2
13
+ // 'ServiceOrderDocument.SourceID': 2,
14
+ // 'CustomerOrders.OrderID': 76548
15
15
  // }
16
16
 
17
17
  // export function parseFilterString(filterString, sampleData = null) {
18
- // if (filterString.toUpperCase().startsWith("SELECT")) return null; // Skip full SQL queries
19
-
20
18
  // let { sanitizedSQL, extractedVariables } = sanitizeQuery(filterString);
21
19
  // console.log("Sanitized SQL:", sanitizedSQL, "\n");
22
20
 
@@ -30,9 +28,9 @@
30
28
  // return convertToDevExpressFormat({ ast: astTree, variables: extractedVariables, resultObject: sampleData });
31
29
  // }
32
30
 
33
- // // const devexpress = parseFilterString("(RS2ID in ({LeadStatementGlobalRpt.StateID}) Or ({LeadStatementGlobalRpt.StateID} =0)) And (RS3ID in (0,{LeadStatementGlobalRpt.RegionID}) Or {LeadStatementGlobalRpt.RegionID} =0 )", sampleData);
34
- // const devexpress = parseFilterString("{LeadStatementGlobalRpt.RegionID} =0", sampleData);
31
+ // const devexpress = parseFilterString("OrderID = {CustomerOrders.OrderID} AND Status IN (1, 3)", sampleData);
35
32
  // console.log("DevExpress Filter:", JSON.stringify(devexpress, null, 2));
33
+ // // const devexpress = parseFilterString("(RS2ID in ({LeadStatementGlobalRpt.StateID}) Or ({LeadStatementGlobalRpt.StateID} =0)) And (RS3ID in (0,{LeadStatementGlobalRpt.RegionID}) Or {LeadStatementGlobalRpt.RegionID} =0 )", sampleData);
36
34
 
37
35
  // // const devExpressFilter = convertSQLToAst("(RS2ID in ({LeadStatementGlobalRpt.StateID}) Or ({LeadStatementGlobalRpt.StateID} =0)) And (RS3ID in (0,{LeadStatementGlobalRpt.RegionID}) Or {LeadStatementGlobalRpt.RegionID} =0 ) ");
38
36
  // // const devExpressFilterresult = convertAstToDevextreme(devExpressFilter.ast, devExpressFilter.variables, sampleData);