Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(optimizer): Implement LIKE expression rule for query optimization #96

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Prev Previous commit
Next Next commit
fix(rbo): COMMIT
  • Loading branch information
loloxwg committed Dec 5, 2023
commit 696232c23e7dd4698e7d75b63aacb7b7b843f807
71 changes: 53 additions & 18 deletions src/optimizer/rule/simplification.rs
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@ use crate::planner::operator::join::JoinCondition;
use crate::planner::operator::Operator;
use crate::types::value::{DataValue, ValueRef};
use lazy_static::lazy_static;
use crate::planner::operator::filter::FilterOperator;
use crate::types::LogicalType;
lazy_static! {
static ref LIKE_REWRITE_RULE: Pattern = {
@@ -127,26 +128,25 @@ impl Rule for LikeRewrite {

fn apply(&self, node_id: HepNodeId, graph: &mut HepGraph) -> Result<(), OptimizerError> {
if let Operator::Filter(mut filter_op) = graph.operator(node_id).clone() {
Copy link
Member

@KKould KKould Dec 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use the operator_mut method to modify directly instead of replace

// if is like expression
if let ScalarExpression::Binary {
op: BinaryOperator::Like,
left_expr,
right_expr,
ref mut left_expr,
ref mut right_expr,
ty,
} = &mut filter_op.predicate
} = filter_op.predicate.clone()
{
if let ScalarExpression::Constant(value) = right_expr.as_ref() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if let ScalarExpression::Constant(DataValue::Utf8(Some(value))) = right_expr.as_mut()

Instead of pattern matching layer by layer again

if let DataValue::Utf8(value_str) = (**value).clone() {
if let Some(value_str) = value_str.as_ref() {
value_str.map(|value_str| {
if value_str.ends_with('%') {
let x = value_str.trim_end_matches('%');
let mut new_value = increment_last_char(x);
if let Some(new_value) = new_value {
let new_expr = Self::create_new_expr(left_expr, ty, x.to_string(), new_value);
filter_op.predicate = new_expr;
}
let left_bound = value_str.trim_end_matches('%');
let mut right_bound = increment_last_char(left_bound);

right_bound.map(|rb| {
filter_op.predicate = Self::create_new_expr(&mut left_expr.clone(), ty, left_bound.to_string(), rb);
});
}
}
});
}
}
}
@@ -157,34 +157,69 @@ impl Rule for LikeRewrite {
}

impl LikeRewrite {
fn create_new_expr(left_expr: &mut Box<ScalarExpression>, ty: &mut LogicalType, mut value: String, mut new_value: String) -> ScalarExpression {
fn create_new_expr(left_expr: &mut Box<ScalarExpression>, ty: LogicalType, left_bound: String, right_bound: String) -> ScalarExpression {
let new_expr = ScalarExpression::Binary {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Meaningless variable declaration -> new_expr

op: BinaryOperator::And,
left_expr: Box::new(ScalarExpression::Binary {
op: BinaryOperator::GtEq,
left_expr: left_expr.clone(),
right_expr: Box::new(
ScalarExpression::Constant(ValueRef::from(
DataValue::Utf8(Some(value)),
DataValue::Utf8(Some(left_bound)),
)),
),
ty: ty.clone(),
ty: ty,
}),

right_expr: Box::new(ScalarExpression::Binary {
op: BinaryOperator::Lt,
left_expr: left_expr.clone(),
right_expr: Box::new(
ScalarExpression::Constant(ValueRef::from(
DataValue::Utf8(Some(new_value)),
DataValue::Utf8(Some(right_bound)),
)),
),
ty: ty.clone(),
ty,
}),
ty: ty.clone(),
ty,
};
new_expr
}

fn process_filter_operator(&self, filter_op: &mut FilterOperator) -> Result<(), OptimizerError> {
if let ScalarExpression::Binary {
op: BinaryOperator::Like,
left_expr,
right_expr,
ty,
} = &mut filter_op.predicate
{
self.process_like_expression(left_expr, right_expr, ty)?;
}
Ok(())
}

fn process_like_expression(&self, left_expr: &mut Box<ScalarExpression>, right_expr: &mut Box<ScalarExpression>, ty: &mut LogicalType) -> Result<(), OptimizerError> {
if let ScalarExpression::Constant(value) = right_expr.as_ref() {
if let DataValue::Utf8(value_str) = (**value).clone() {
if let Some(value_str) = value_str.as_ref() {
self.process_utf8_value(left_expr, ty, value_str)?;
}
}
}
Ok(())
}

fn process_utf8_value(&self, left_expr: &mut Box<ScalarExpression>, ty: &mut LogicalType, value_str: &str) -> Result<(), OptimizerError> {
if value_str.ends_with('%') {
let x = value_str.trim_end_matches('%');
if let Some(new_value) = increment_last_char(x) {
let new_expr = Self::create_new_expr(left_expr, *ty, x.to_string(), new_value);
*left_expr = Box::new(new_expr);
}
}
Ok(())
}
}

fn increment_last_char(s: &str) -> Option<String> {