Skip to content

Commit

Permalink
Add boolean function transformer in optimizer
Browse files Browse the repository at this point in the history
  • Loading branch information
zhyass committed Oct 20, 2021
1 parent ac7c810 commit 7eab895
Show file tree
Hide file tree
Showing 20 changed files with 119 additions and 30 deletions.
1 change: 1 addition & 0 deletions common/datavalues/src/data_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ impl DataValue {

pub fn as_bool(&self) -> Result<bool> {
match self {
DataValue::Null => Ok(false),
DataValue::Boolean(v) => Ok(v.map_or(false, |v| v)),
DataValue::Int8(v) => Ok(v.map_or(false, |v| v != 0)),
DataValue::Int16(v) => Ok(v.map_or(false, |v| v != 0)),
Expand Down
3 changes: 2 additions & 1 deletion common/functions/src/scalars/comparisons/comparison_eq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ impl ComparisonEqFunction {
FunctionDescription::creator(Box::new(Self::try_create_func)).features(
FunctionFeatures::default()
.deterministic()
.negative_function("<>"),
.negative_function("<>")
.bool_function(),
)
}
}
3 changes: 2 additions & 1 deletion common/functions/src/scalars/comparisons/comparison_gt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ impl ComparisonGtFunction {
FunctionDescription::creator(Box::new(Self::try_create_func)).features(
FunctionFeatures::default()
.deterministic()
.negative_function("<="),
.negative_function("<=")
.bool_function(),
)
}
}
3 changes: 2 additions & 1 deletion common/functions/src/scalars/comparisons/comparison_gt_eq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ impl ComparisonGtEqFunction {
FunctionDescription::creator(Box::new(Self::try_create_func)).features(
FunctionFeatures::default()
.deterministic()
.negative_function("<"),
.negative_function("<")
.bool_function(),
)
}
}
3 changes: 2 additions & 1 deletion common/functions/src/scalars/comparisons/comparison_like.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ impl ComparisonLikeFunction {
FunctionDescription::creator(Box::new(Self::try_create_func)).features(
FunctionFeatures::default()
.deterministic()
.negative_function("not like"),
.negative_function("not like")
.bool_function(),
)
}
}
3 changes: 2 additions & 1 deletion common/functions/src/scalars/comparisons/comparison_lt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ impl ComparisonLtFunction {
FunctionDescription::creator(Box::new(Self::try_create_func)).features(
FunctionFeatures::default()
.deterministic()
.negative_function(">="),
.negative_function(">=")
.bool_function(),
)
}
}
3 changes: 2 additions & 1 deletion common/functions/src/scalars/comparisons/comparison_lt_eq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ impl ComparisonLtEqFunction {
FunctionDescription::creator(Box::new(Self::try_create_func)).features(
FunctionFeatures::default()
.deterministic()
.negative_function(">"),
.negative_function(">")
.bool_function(),
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ impl ComparisonNotEqFunction {
FunctionDescription::creator(Box::new(Self::try_create_func)).features(
FunctionFeatures::default()
.deterministic()
.negative_function("="),
.negative_function("=")
.bool_function(),
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ impl ComparisonNotLikeFunction {
FunctionDescription::creator(Box::new(Self::try_create_func)).features(
FunctionFeatures::default()
.deterministic()
.negative_function("like"),
.negative_function("like")
.bool_function(),
)
}
}
8 changes: 6 additions & 2 deletions common/functions/src/scalars/expressions/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,16 @@ pub struct ToCastFunction;

impl ToCastFunction {
fn cast_function_creator(to_type: DataType) -> FunctionDescription {
let mut features = FunctionFeatures::default().deterministic();
if to_type == DataType::Boolean {
features = features.bool_function();
}

let function_creator: FactoryCreator = Box::new(move |display_name| {
CastFunction::create(display_name.to_string(), to_type.clone())
});

FunctionDescription::creator(function_creator)
.features(FunctionFeatures::default().deterministic())
FunctionDescription::creator(function_creator).features(features)
}

pub fn register(factory: &mut FunctionFactory) {
Expand Down
7 changes: 7 additions & 0 deletions common/functions/src/scalars/function_factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,15 @@ pub type FactoryCreator = Box<dyn Fn(&str) -> Result<Box<dyn Function>> + Send +
pub struct FunctionFeatures {
pub is_deterministic: bool,
pub negative_function_name: Option<String>,
pub is_bool_func: bool,
}

impl FunctionFeatures {
pub fn default() -> FunctionFeatures {
FunctionFeatures {
is_deterministic: false,
negative_function_name: None,
is_bool_func: false,
}
}

Expand All @@ -57,6 +59,11 @@ impl FunctionFeatures {
self.negative_function_name = Some(negative_name.to_string());
self
}

pub fn bool_function(mut self) -> FunctionFeatures {
self.is_bool_func = true;
self
}
}

pub struct FunctionDescription {
Expand Down
3 changes: 2 additions & 1 deletion common/functions/src/scalars/logics/logic_and.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ impl LogicAndFunction {
FunctionDescription::creator(Box::new(Self::try_create_func)).features(
FunctionFeatures::default()
.deterministic()
.negative_function("or"),
.negative_function("or")
.bool_function(),
)
}
}
3 changes: 2 additions & 1 deletion common/functions/src/scalars/logics/logic_not.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ impl LogicNotFunction {
// We should need remove expression if negative function is empty
FunctionFeatures::default()
.deterministic()
.negative_function(""),
.negative_function("")
.bool_function(),
)
}
}
3 changes: 2 additions & 1 deletion common/functions/src/scalars/logics/logic_or.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ impl LogicOrFunction {
FunctionDescription::creator(Box::new(Self::try_create_func)).features(
FunctionFeatures::default()
.deterministic()
.negative_function("and"),
.negative_function("and")
.bool_function(),
)
}
}
3 changes: 2 additions & 1 deletion common/functions/src/scalars/nullables/is_not_null.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ impl IsNotNullFunction {
FunctionDescription::creator(Box::new(Self::try_create_func)).features(
FunctionFeatures::default()
.deterministic()
.negative_function("isnull"),
.negative_function("isnull")
.bool_function(),
)
}
}
Expand Down
3 changes: 2 additions & 1 deletion common/functions/src/scalars/nullables/is_null.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ impl IsNullFunction {
FunctionDescription::creator(Box::new(Self::try_create_func)).features(
FunctionFeatures::default()
.deterministic()
.negative_function("isnotnull"),
.negative_function("isnotnull")
.bool_function(),
)
}
}
Expand Down
2 changes: 1 addition & 1 deletion common/functions/src/scalars/udfs/exists.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ impl ExistsFunction {

pub fn desc() -> FunctionDescription {
FunctionDescription::creator(Box::new(Self::try_create))
.features(FunctionFeatures::default())
.features(FunctionFeatures::default().bool_function())
}
}

Expand Down
2 changes: 1 addition & 1 deletion common/functions/src/scalars/udfs/udf_example.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ impl UdfExampleFunction {

pub fn desc() -> FunctionDescription {
FunctionDescription::creator(Box::new(Self::try_create))
.features(FunctionFeatures::default().deterministic())
.features(FunctionFeatures::default().deterministic().bool_function())
}
}

Expand Down
74 changes: 61 additions & 13 deletions query/src/optimizers/optimizer_expression_transform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,7 @@ use common_datavalues::prelude::*;
use common_exception::ErrorCode;
use common_exception::Result;
use common_functions::scalars::FunctionFactory;
use common_planners::AggregatorFinalPlan;
use common_planners::AggregatorPartialPlan;
use common_planners::Expression;
use common_planners::Expressions;
use common_planners::PlanBuilder;
use common_planners::PlanNode;
use common_planners::PlanRewriter;
use common_planners::*;

use crate::optimizers::Optimizer;
use crate::sessions::DatabendQueryContextRef;
Expand Down Expand Up @@ -51,14 +45,14 @@ impl ExprTransformImpl {
let factory = FunctionFactory::instance();
let function_features = factory.get_features(op)?;

match function_features.negative_function_name.as_ref() {
Some(v) => Ok(f(v, args)),
None => Ok(Expression::create_unary_expression("NOT", vec![
origin.clone()
])),
}
let expr = function_features.negative_function_name.as_ref().map_or(
Expression::create_unary_expression("NOT", vec![origin.clone()]),
|v| f(v, args),
);
Ok(expr)
}

// Apply NOT transformation to the expression and return a new one.
fn truth_transformer(origin: &Expression, is_negated: bool) -> Result<Expression> {
match origin {
// TODO: support in and not in.
Expand Down Expand Up @@ -110,6 +104,46 @@ impl ExprTransformImpl {
}
}
}

fn make_condition(op: &str, origin: &Expression) -> Result<Expression> {
let factory = FunctionFactory::instance();
let function_features = factory.get_features(op)?;
if function_features.is_bool_func {
Ok(origin.clone())
} else {
Ok(origin.not_eq(lit(0)))
}
}

// Ensure that all expressions involved in conditions are boolean functions.
// Specifically, change <non-bool-expr> to (0 <> <non-bool-expr>).
fn boolean_transformer(origin: &Expression) -> Result<Expression> {
match origin {
Expression::Literal { .. } => Ok(origin.clone()),
Expression::BinaryExpression { op, left, right } => match op.to_lowercase().as_str() {
"and" => {
let new_left = Self::boolean_transformer(left)?;
let new_right = Self::boolean_transformer(right)?;
Ok(new_left.and(new_right))
}
"or" => {
let new_left = Self::boolean_transformer(left)?;
let new_right = Self::boolean_transformer(right)?;
Ok(new_left.or(new_right))
}
other => Self::make_condition(other, origin),
},
Expression::UnaryExpression { op, expr } => match op.to_lowercase().as_str() {
"not" => {
let new_expr = Self::boolean_transformer(expr)?;
Ok(not(new_expr))
}
other => Self::make_condition(other, origin),
},
Expression::ScalarFunction { op, .. } => Self::make_condition(op.as_str(), origin),
_ => Ok(origin.not_eq(lit(0))),
}
}
}

impl PlanRewriter for ExprTransformImpl {
Expand Down Expand Up @@ -150,6 +184,20 @@ impl PlanRewriter for ExprTransformImpl {
}
}
}

fn rewrite_filter(&mut self, plan: &FilterPlan) -> Result<PlanNode> {
let new_input = self.rewrite_plan_node(plan.input.as_ref())?;
let new_predicate = Self::boolean_transformer(&plan.predicate)?;
let new_predicate = Self::truth_transformer(&new_predicate, false)?;
PlanBuilder::from(&new_input).filter(new_predicate)?.build()
}

fn rewrite_having(&mut self, plan: &HavingPlan) -> Result<PlanNode> {
let new_input = self.rewrite_plan_node(plan.input.as_ref())?;
let new_predicate = Self::boolean_transformer(&plan.predicate)?;
let new_predicate = Self::truth_transformer(&new_predicate, false)?;
PlanBuilder::from(&new_input).having(new_predicate)?.build()
}
}

impl ExprTransformImpl {
Expand Down
16 changes: 16 additions & 0 deletions query/src/optimizers/optimizer_expression_transform_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,22 @@ mod tests {
\n Filter: toBoolean(number)\
\n ReadDataSource: scan partitions: [8], scan schema: [number:UInt64], statistics: [read_rows: 10, read_bytes: 80]",
},
Test {
name: "Boolean transform",
query: "select number from numbers_mt(10) where number",
expect: "\
Projection: number:UInt64\
\n Filter: (number != 0)\
\n ReadDataSource: scan partitions: [8], scan schema: [number:UInt64], statistics: [read_rows: 10, read_bytes: 80]",
},
Test {
name: "Boolean and truth transform",
query: "select number from numbers_mt(10) where not number",
expect: "\
Projection: number:UInt64\
\n Filter: (number = 0)\
\n ReadDataSource: scan partitions: [8], scan schema: [number:UInt64], statistics: [read_rows: 10, read_bytes: 80]",
},
];

for test in tests {
Expand Down

0 comments on commit 7eab895

Please sign in to comment.