#include "optimizer/bool_folding_optimizer.h"
#include <string>
#include <unordered_set>
#include "binder/expression/literal_expression.h"
#include "binder/expression/scalar_function_expression.h"
#include "common/enums/expression_type.h"
#include "common/types/value/value.h"
#include "function/boolean/vector_boolean_functions.h"
#include "function/scalar_function.h"
#include "planner/operator/logical_empty_result.h"
#include "planner/operator/logical_filter.h"
using namespace lbug::binder;
using namespace lbug::common;
using namespace lbug::planner;
using namespace lbug::function;
namespace lbug {
namespace optimizer {
static bool hasNullCheckComplements(const expression_vector& operands) {
std::unordered_set<std::string> isNull, isNotNull;
for (auto& operand : operands) {
if (operand->getNumChildren() == 0) {
continue;
}
if (operand->expressionType == ExpressionType::IS_NULL) {
auto name = operand->getChild(0)->getUniqueName();
if (isNotNull.contains(name)) {
return true;
}
isNull.insert(std::move(name));
} else if (operand->expressionType == ExpressionType::IS_NOT_NULL) {
auto name = operand->getChild(0)->getUniqueName();
if (isNull.contains(name)) {
return true;
}
isNotNull.insert(std::move(name));
}
}
return false;
}
static bool hasNotComplements(const expression_vector& operands) {
std::unordered_set<std::string> names, negated;
for (auto& operand : operands) {
auto name = operand->getUniqueName();
if (negated.contains(name)) {
return true;
}
if (operand->expressionType == ExpressionType::NOT && operand->getNumChildren() == 1) {
auto childName = operand->getChild(0)->getUniqueName();
if (names.contains(childName)) {
return true;
}
negated.insert(std::move(childName));
}
names.insert(std::move(name));
}
return false;
}
static std::shared_ptr<Expression> boolLiteral(bool v) {
return std::make_shared<LiteralExpression>(Value(v),
std::string("_lit_") + (v ? "true" : "false"));
}
static std::shared_ptr<Expression> boolOperator(ExpressionType type, expression_vector children) {
for ([[maybe_unused]] auto& child : children) {
DASSERT(child->getDataType().getLogicalTypeID() == LogicalTypeID::BOOL);
}
auto functionName = ExpressionTypeUtil::toString(type);
scalar_func_exec_t execFunc;
VectorBooleanFunction::bindExecFunction(type, children, execFunc);
scalar_func_select_t selectFunc;
VectorBooleanFunction::bindSelectFunction(type, children, selectFunc);
std::vector<LogicalTypeID> inputTypeIDs(children.size(), LogicalTypeID::BOOL);
auto uniqueName = ScalarFunctionExpression::getUniqueName(functionName, children);
auto function = std::make_unique<ScalarFunction>(functionName, std::move(inputTypeIDs),
LogicalTypeID::BOOL, execFunc, selectFunc);
return std::make_shared<ScalarFunctionExpression>(type, std::move(function),
std::make_unique<FunctionBindData>(LogicalType::BOOL()), std::move(children), uniqueName);
}
static void splitOperands(ExpressionType type, const std::shared_ptr<Expression>& expr,
expression_vector& operands) {
if (expr->expressionType != type) {
operands.push_back(expr);
return;
}
for (auto i = 0u; i < expr->getNumChildren(); ++i) {
splitOperands(type, expr->getChild(i), operands);
}
}
static std::shared_ptr<Expression> chainBoolOperator(ExpressionType type,
const expression_vector& operands) {
auto result = operands[0];
for (auto i = 1u; i < operands.size(); ++i) {
result = boolOperator(type, {result, operands[i]});
}
return result;
}
static std::shared_ptr<Expression> foldBool(const std::shared_ptr<Expression>& expr,
bool nullAsFalse) {
if (expr->expressionType == ExpressionType::NOT) {
auto child = expr->getChild(0);
auto folded = foldBool(child, false );
auto effective = folded ? folded : child;
if (effective->expressionType == ExpressionType::LITERAL) {
auto& lit = effective->constCast<LiteralExpression>();
if (lit.isNull()) {
return effective; }
return boolLiteral(!lit.getValue().getValue<bool>());
}
if (effective->expressionType == ExpressionType::NOT && effective->getNumChildren() == 1) {
auto inner = effective->getChild(0);
auto innerFolded = foldBool(inner, nullAsFalse);
return innerFolded ? innerFolded : inner;
}
return folded ? boolOperator(ExpressionType::NOT, {folded}) : nullptr;
}
if (expr->expressionType == ExpressionType::AND) {
expression_vector parts;
splitOperands(ExpressionType::AND, expr, parts);
expression_vector kept;
bool anyChanged = false;
for (auto& part : parts) {
auto folded = foldBool(part, nullAsFalse);
auto effective = folded ? folded : part;
if (folded) {
anyChanged = true;
}
if (effective->expressionType == ExpressionType::LITERAL) {
auto& lit = effective->constCast<LiteralExpression>();
if (lit.isNull() && !nullAsFalse) {
kept.push_back(effective);
continue;
}
if (lit.isNull() || !lit.getValue().getValue<bool>()) {
return boolLiteral(false); }
anyChanged = true;
continue;
}
splitOperands(ExpressionType::AND, effective, kept);
}
if (hasNullCheckComplements(kept) || (nullAsFalse && hasNotComplements(kept))) {
return boolLiteral(false);
}
if (kept.empty()) {
return boolLiteral(true);
}
if (kept.size() == 1) {
return kept[0];
}
return anyChanged ? chainBoolOperator(ExpressionType::AND, kept) : nullptr;
}
if (expr->expressionType == ExpressionType::OR) {
std::vector<std::shared_ptr<Expression>> folded;
bool anyChanged = false;
expression_vector parts;
splitOperands(ExpressionType::OR, expr, parts);
for (auto& part : parts) {
auto f = foldBool(part, nullAsFalse);
auto eff = f ? f : part;
if (f) {
anyChanged = true;
}
splitOperands(ExpressionType::OR, eff, folded);
}
if (hasNullCheckComplements(folded)) {
return boolLiteral(true);
}
expression_vector kept;
for (auto i = 0u; i < folded.size(); ++i) {
auto& eff = folded[i];
if (eff->expressionType == ExpressionType::LITERAL) {
auto& lit = eff->constCast<LiteralExpression>();
if (!lit.isNull() && lit.getValue().getValue<bool>()) {
return boolLiteral(true); }
if (lit.isNull() && !nullAsFalse) {
kept.push_back(eff);
continue;
}
anyChanged = true;
continue;
}
kept.push_back(eff);
}
if (kept.empty()) {
return boolLiteral(false);
}
if (kept.size() == 1) {
return kept[0];
}
return anyChanged ? chainBoolOperator(ExpressionType::OR, kept) : nullptr;
}
return nullptr;
}
void BoolFoldingOptimizer::rewrite(LogicalPlan* plan) {
visitOperator(plan->getLastOperator());
}
std::shared_ptr<LogicalOperator> BoolFoldingOptimizer::visitOperator(
const std::shared_ptr<LogicalOperator>& op) {
for (auto i = 0u; i < op->getNumChildren(); ++i) {
op->setChild(i, visitOperator(op->getChild(i)));
}
auto result = visitOperatorReplaceSwitch(op);
result->computeFlatSchema();
return result;
}
std::shared_ptr<LogicalOperator> BoolFoldingOptimizer::visitFilterReplace(
std::shared_ptr<LogicalOperator> op) {
auto& filter = op->constCast<LogicalFilter>();
auto predicate = filter.getPredicate();
if (predicate->expressionType == ExpressionType::LITERAL) {
auto& lit = predicate->constCast<LiteralExpression>();
if (lit.isNull() || !lit.getValue().getValue<bool>()) {
return std::make_shared<LogicalEmptyResult>(*op->getSchema());
}
return op->getChild(0); }
auto folded = foldBool(predicate, true );
if (!folded) {
return op; }
if (folded->expressionType == ExpressionType::LITERAL) {
auto& lit = folded->constCast<LiteralExpression>();
if (lit.isNull() || !lit.getValue().getValue<bool>()) {
return std::make_shared<LogicalEmptyResult>(*op->getSchema());
}
return op->getChild(0);
}
auto newFilter = std::make_shared<LogicalFilter>(std::move(folded), op->getChild(0));
newFilter->computeFlatSchema();
return newFilter;
}
} }