From bb04f891ecaaa9d97939092252df9fe25320b254 Mon Sep 17 00:00:00 2001 From: ShengHuang Date: Wed, 8 Jul 2026 21:14:44 +0800 Subject: [PATCH] optimize: prune unreachable CASE WHEN branches --- src/binder/expression_binder.cpp | 115 +++++++++++++++++++++++++ src/include/binder/expression_binder.h | 7 ++ test/test_files/function/case.test | 30 +++++++ 3 files changed, 152 insertions(+) diff --git a/src/binder/expression_binder.cpp b/src/binder/expression_binder.cpp index 9480fdea4f..53f54ee4a0 100644 --- a/src/binder/expression_binder.cpp +++ b/src/binder/expression_binder.cpp @@ -1,7 +1,9 @@ #include "binder/expression_binder.h" #include "binder/binder.h" +#include "binder/expression/case_expression.h" #include "binder/expression/expression_util.h" +#include "binder/expression/literal_expression.h" #include "binder/expression/parameter_expression.h" #include "binder/expression_visitor.h" #include "common/exception/binder.h" @@ -19,6 +21,15 @@ using namespace lbug::parser; namespace lbug { namespace binder { +static bool isBoolLiteral(const Expression& expression, bool value) { + if (expression.expressionType != ExpressionType::LITERAL || + expression.dataType != LogicalType::BOOL()) { + return false; + } + auto literalValue = expression.constCast().getValue(); + return !literalValue.isNull() && literalValue.getValue() == value; +} + std::shared_ptr ExpressionBinder::bindExpression( const ParsedExpression& parsedExpression) { // Normally u can only reference an existing expression through alias which is a parsed @@ -77,12 +88,116 @@ std::shared_ptr ExpressionBinder::bindExpression( throw NotImplementedException( "bindExpression(" + ExpressionTypeUtil::toString(expressionType) + ")."); } + expression = simplifyExpression(expression); if (ConstantExpressionVisitor::needFold(*expression)) { return foldExpression(expression); } return expression; } +std::shared_ptr ExpressionBinder::simplifyExpression( + const std::shared_ptr& expression) { + switch (expression->expressionType) { + case ExpressionType::AND: + case ExpressionType::OR: + case ExpressionType::NOT: + return simplifyBooleanExpression(expression); + case ExpressionType::CASE_ELSE: + return simplifyCaseExpression(expression); + default: + return expression; + } +} + +std::shared_ptr ExpressionBinder::simplifyBooleanExpression( + const std::shared_ptr& expression) { + auto expressionType = expression->expressionType; + if (expressionType == ExpressionType::NOT) { + auto child = simplifyExpression(expression->getChild(0)); + if (isBoolLiteral(*child, true)) { + return createLiteralExpression(Value(false)); + } + if (isBoolLiteral(*child, false)) { + return createLiteralExpression(Value(true)); + } + if (child->expressionType == ExpressionType::NOT) { + return child->getChild(0); + } + if (child != expression->getChild(0)) { + return bindBooleanExpression(expressionType, expression_vector{child}); + } + return expression; + } + DASSERT(expressionType == ExpressionType::AND || expressionType == ExpressionType::OR); + auto left = simplifyExpression(expression->getChild(0)); + auto right = simplifyExpression(expression->getChild(1)); + if (expressionType == ExpressionType::AND) { + if (isBoolLiteral(*left, false)) { + return left; + } + if (isBoolLiteral(*right, false)) { + return right; + } + if (isBoolLiteral(*left, true)) { + return right; + } + if (isBoolLiteral(*right, true)) { + return left; + } + } else { + if (isBoolLiteral(*left, true)) { + return left; + } + if (isBoolLiteral(*right, true)) { + return right; + } + if (isBoolLiteral(*left, false)) { + return right; + } + if (isBoolLiteral(*right, false)) { + return left; + } + } + if (left != expression->getChild(0) || right != expression->getChild(1)) { + return bindBooleanExpression(expressionType, expression_vector{left, right}); + } + return expression; +} + +std::shared_ptr ExpressionBinder::simplifyCaseExpression( + const std::shared_ptr& expression) { + auto& caseExpression = expression->constCast(); + auto elseExpression = simplifyExpression(caseExpression.getElseExpression()); + auto simplifiedCaseExpression = std::make_shared(expression->dataType.copy(), + elseExpression, expression->getUniqueName()); + bool changed = elseExpression != caseExpression.getElseExpression(); + for (auto i = 0u; i < caseExpression.getNumCaseAlternatives(); ++i) { + auto alternative = caseExpression.getCaseAlternative(i); + auto whenExpression = simplifyExpression(alternative->whenExpression); + auto thenExpression = simplifyExpression(alternative->thenExpression); + if (ExpressionUtil::isNullLiteral(*whenExpression) || + isBoolLiteral(*whenExpression, false)) { + changed = true; + continue; + } + if (isBoolLiteral(*whenExpression, true)) { + changed = true; + if (simplifiedCaseExpression->getNumCaseAlternatives() == 0) { + return thenExpression; + } + simplifiedCaseExpression->addCaseAlternative(whenExpression, thenExpression); + break; + } + changed = changed || whenExpression != alternative->whenExpression || + thenExpression != alternative->thenExpression; + simplifiedCaseExpression->addCaseAlternative(whenExpression, thenExpression); + } + if (simplifiedCaseExpression->getNumCaseAlternatives() == 0) { + return elseExpression; + } + return changed ? simplifiedCaseExpression : expression; +} + std::shared_ptr ExpressionBinder::foldExpression( const std::shared_ptr& expression) const { auto value = diff --git a/src/include/binder/expression_binder.h b/src/include/binder/expression_binder.h index cb04e5dd6c..e644ec3bb0 100644 --- a/src/include/binder/expression_binder.h +++ b/src/include/binder/expression_binder.h @@ -143,6 +143,13 @@ class ExpressionBinder { const ExpressionBinderConfig& getConfig() { return config; } +private: + std::shared_ptr simplifyExpression(const std::shared_ptr& expression); + std::shared_ptr simplifyBooleanExpression( + const std::shared_ptr& expression); + std::shared_ptr simplifyCaseExpression( + const std::shared_ptr& expression); + private: Binder* binder; main::ClientContext* context; diff --git a/test/test_files/function/case.test b/test/test_files/function/case.test index e8935378dc..1cbcdc32d7 100644 --- a/test/test_files/function/case.test +++ b/test/test_files/function/case.test @@ -91,3 +91,33 @@ abcdefghijklmnopq 0 3 5 + +-LOG CaseConditionalSimplification + +-STATEMENT MATCH (a:person) WHERE a.ID = 0 RETURN CASE WHEN false AND a.ID > 0 THEN 1 ELSE 2 END +---- 1 +2 +-STATEMENT MATCH (a:person) WHERE a.ID = 0 RETURN CASE WHEN true OR a.ID > 0 THEN 1 ELSE 2 END +---- 1 +1 +-STATEMENT RETURN CASE WHEN NULL THEN 1 ELSE 2 END +---- 1 +2 +-STATEMENT MATCH (a:person) WHERE a.ID = 0 RETURN CASE WHEN true THEN 1 WHEN a.ID > 0 THEN 2 ELSE 3 END +---- 1 +1 +-STATEMENT MATCH (a:person) WHERE CASE WHEN false AND coalesce(a.ID = a.ID, true) THEN false WHEN true OR coalesce(a.ID = a.ID, true) THEN a.ID >= 0 ELSE false END RETURN COUNT(*) +---- 1 +8 +-STATEMENT RETURN CASE WHEN NULL OR false THEN 1 ELSE 2 END +---- 1 +2 +-STATEMENT RETURN CASE WHEN NULL AND true THEN 1 ELSE 2 END +---- 1 +2 +-STATEMENT RETURN CASE WHEN NULL AND false THEN 1 ELSE 2 END +---- 1 +2 +-STATEMENT RETURN CASE WHEN NULL OR true THEN 1 ELSE 2 END +---- 1 +1