Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "sqlparser-devexpress",
"version": "2.3.16",
"version": "2.3.17",
"main": "src/index.js",
"type": "module",
"scripts": {
Expand Down
1 change: 1 addition & 0 deletions src/@types/core/converter.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export interface ConvertOptions {
ast: ASTNode;
resultObject?: ResultObject;
enableShortCircuit?: boolean;
isValueNullShortCircuit?: boolean;
}

/**
Expand Down
2 changes: 2 additions & 0 deletions src/@types/default.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,12 @@ export function convertSQLToAst(filterString: string, enableConsoleLogs?: boolea
* @param ast - The parsed AST from `convertSQLToAst`.
* @param state - An optional result object to resolve placeholders to actual values.
* @param enableShortCircuit - Whether to apply short-circuit evaluation.
* @param isValueNullShortCircuit - Whether to treat null values as short-circuit conditions.
* @returns DevExpressFilter - The DevExpress-compatible filter array or null.
*/
export function convertAstToDevextreme(
ast: ASTNode,
state?: ResultObject | null,
enableShortCircuit?: boolean,
isValueNullShortCircuit?: boolean
): DevExpressFilter;
19 changes: 15 additions & 4 deletions src/core/converter.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ function DevExpressConverter() {
// Global variables accessible throughout the converter
let resultObject = null;
let EnableShortCircuit = true;
let IsValueNullShortCircuit = false; // Flag to enable/disable null short-circuiting

/**
* Main conversion function that sets up the global context
Expand All @@ -16,10 +17,11 @@ function DevExpressConverter() {
* @param {boolean} enableShortCircuit - Optional enabling and disabling the shortcircuit ie evaluating value = value scenario
* @returns {Array|null} DevExpress format filter
*/
function convert(ast, ResultObject = null, enableShortCircuit = true) {
function convert(ast, ResultObject = null, enableShortCircuit = true, isValueNullShortCircuit = false) {
// Set up global context
resultObject = ResultObject;
EnableShortCircuit = enableShortCircuit;
IsValueNullShortCircuit = isValueNullShortCircuit;

// Process the AST
let result = processAstNode(ast);
Expand Down Expand Up @@ -89,6 +91,7 @@ function DevExpressConverter() {
const left = processAstNode(ast.left, operator);
const right = processAstNode(ast.right, operator);


if (EnableShortCircuit) {
// Short-circuit: always-true conditions
if (left === true || right === true) {
Expand All @@ -100,7 +103,6 @@ function DevExpressConverter() {
if (left === false || right === false) {
return left === false ? right : left;
}

}

// Detect and flatten nested logical expressions
Expand Down Expand Up @@ -165,6 +167,10 @@ function DevExpressConverter() {
}

// Apply short-circuit evaluation if enabled
if (EnableShortCircuit && IsValueNullShortCircuit && (left == null || right == null)) {
return true; // If either value is null, return true for short-circuit evaluation
}

if (EnableShortCircuit) {
if (isAlwaysTrue(comparison, leftDefault, rightDefault)) return true;
if (isAlwaysFalse(comparison, leftDefault, rightDefault)) return false;
Expand Down Expand Up @@ -242,6 +248,10 @@ function DevExpressConverter() {
}
}

if (EnableShortCircuit && IsValueNullShortCircuit && (ast.field?.type === "placeholder" || ast.value?.type === "placeholder" || ast.value === null) && resolvedValue === null) {
return true;
}

let operatorToken = operator === "IN" ? '=' : operator === "NOT IN" ? '!=' : operator;
let joinOperatorToken = operator === "IN" ? 'or' : operator === "NOT IN" ? 'and' : operator;
let field = convertValue(ast.field);
Expand Down Expand Up @@ -443,8 +453,9 @@ const devExpressConverter = DevExpressConverter();
* @param {Object} ast - The abstract syntax tree
* @param {Object} resultObject - Optional object for placeholder resolution
* @param {string} enableShortCircuit - Optional enabling and disabling the shortcircuit ie evaluating value = value scenario
* @param {boolean} isValueNullShortCircuit - Optional enabling and disabling the null shortcircuit ie evaluating value = null scenario
* @returns {Array|null} DevExpress format filter
*/
export function convertToDevExpressFormat({ ast, resultObject = null, enableShortCircuit = true }) {
return devExpressConverter.init(ast, resultObject, enableShortCircuit);
export function convertToDevExpressFormat({ ast, resultObject = null, enableShortCircuit = true, isValueNullShortCircuit = false }) {
return devExpressConverter.init(ast, resultObject, enableShortCircuit, isValueNullShortCircuit);
}
2 changes: 1 addition & 1 deletion src/debug.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
// const astTree = parsedResult.ast;
// console.log("AST Tree:", JSON.stringify(astTree, null, 2), "\n");

// return convertToDevExpressFormat({ ast: astTree, resultObject: sampleData });
// return convertToDevExpressFormat({ ast: astTree, resultObject: sampleData, isValueNullShortCircuit: true });
// }

// const devexpress = parseFilterString("(ISNULL(TicketID, 0) = ISNULL({CustomerOrders.OrderID}, 0))", sampleData);
Expand Down
4 changes: 2 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ export function convertSQLToAst(filterString, enableConsoleLogs = false) {
return parsedResult;
}

export function convertAstToDevextreme(ast, state = null, enableShortCircuit = true) {
return convertToDevExpressFormat({ ast, resultObject: state, enableShortCircuit })
export function convertAstToDevextreme(ast, state = null, enableShortCircuit = true, isValueNullShortCircuit = false) {
return convertToDevExpressFormat({ ast, resultObject: state, enableShortCircuit, isValueNullShortCircuit })
}


Expand Down
10 changes: 9 additions & 1 deletion tests/parser.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,14 @@ describe("Parser SQL to dx Filter Builder", () => {
{
input: "{LeadDocument.AllowSubDealer} != null",
expected: []
},
{
input: "ID = {SaleOrderStatusStmtGlobalRpt.RegionID}",
expected: []
},
{
input: "ID IN ({SaleOrderStatusStmtGlobalRpt.RegionID})",
expected: []
}
];

Expand All @@ -282,7 +290,7 @@ describe("Parser SQL to dx Filter Builder", () => {
const variables = astwithVariables.variables;
const ast = astwithVariables.ast;

const result = convertAstToDevextreme(ast, sampleData);
const result = convertAstToDevextreme(ast, sampleData, true, true);

if (result == null || result == true || result == false) {
expect([]).toEqual(expected);
Expand Down