-
Notifications
You must be signed in to change notification settings - Fork 54
feat(optimizer): Implement LIKE expression rule for query optimization #96
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
base: main
Are you sure you want to change the base?
Changes from 2 commits
0e95ab4
e999789
5ce7a82
696232c
5b1d4ed
74e622c
082b635
c699ce3
b3c9992
143f128
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,11 +1,19 @@ | ||
| use crate::expression::{BinaryOperator, ScalarExpression}; | ||
| use crate::optimizer::core::pattern::{Pattern, PatternChildrenPredicate}; | ||
| use crate::optimizer::core::rule::Rule; | ||
| use crate::optimizer::heuristic::graph::{HepGraph, HepNodeId}; | ||
| use crate::optimizer::OptimizerError; | ||
| use crate::planner::operator::join::JoinCondition; | ||
| use crate::planner::operator::Operator; | ||
| use crate::types::value::{DataValue, ValueRef}; | ||
| use lazy_static::lazy_static; | ||
| lazy_static! { | ||
| static ref LIKE_REWRITE_RULE: Pattern = { | ||
| Pattern { | ||
| predicate: |op| matches!(op, Operator::Filter(_)), | ||
| children: PatternChildrenPredicate::None, | ||
| } | ||
| }; | ||
| static ref CONSTANT_CALCULATION_RULE: Pattern = { | ||
| Pattern { | ||
| predicate: |_| true, | ||
|
|
@@ -109,6 +117,91 @@ impl Rule for SimplifyFilter { | |
| } | ||
| } | ||
|
|
||
| pub struct LikeRewrite; | ||
|
|
||
| impl Rule for LikeRewrite { | ||
| fn pattern(&self) -> &Pattern { | ||
| &LIKE_REWRITE_RULE | ||
| } | ||
|
|
||
| fn apply(&self, node_id: HepNodeId, graph: &mut HepGraph) -> Result<(), OptimizerError> { | ||
| if let Operator::Filter(mut filter_op) = graph.operator(node_id).clone() { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use the |
||
| // if is like expression | ||
| if let ScalarExpression::Binary { | ||
| op: BinaryOperator::Like, | ||
| left_expr, | ||
| right_expr, | ||
| ty, | ||
| } = &mut filter_op.predicate | ||
| { | ||
| // if left is column and right is constant | ||
| if let ScalarExpression::ColumnRef(_) = left_expr.as_ref() { | ||
loloxwg marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| if let ScalarExpression::Constant(value) = right_expr.as_ref() { | ||
|
||
| match value.as_ref() { | ||
| DataValue::Utf8(val_str) => { | ||
| let mut value = val_str.clone().unwrap_or_else(|| "".to_string()); | ||
|
|
||
| if value.ends_with('%') { | ||
| value.pop(); // remove '%' | ||
loloxwg marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| if let Some(last_char) = value.clone().pop() { | ||
| if let Some(next_char) = increment_char(last_char) { | ||
| let mut new_value = value.clone(); | ||
| new_value.pop(); | ||
| new_value.push(next_char); | ||
|
|
||
| let new_expr = ScalarExpression::Binary { | ||
| 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)), | ||
| )), | ||
| ), | ||
| ty: ty.clone(), | ||
| }), | ||
| 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)), | ||
| )), | ||
| ), | ||
| ty: ty.clone(), | ||
| }), | ||
| ty: ty.clone(), | ||
| }; | ||
| filter_op.predicate = new_expr; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| _ => { | ||
| graph.version += 1; | ||
loloxwg marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| return Ok(()); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| graph.replace_node(node_id, Operator::Filter(filter_op)) | ||
| } | ||
| // mark changed to skip this rule batch | ||
| graph.version += 1; | ||
| Ok(()) | ||
| } | ||
| } | ||
|
|
||
| fn increment_char(v: char) -> Option<char> { | ||
| match v { | ||
| 'z' => None, | ||
| 'Z' => None, | ||
| _ => std::char::from_u32(v as u32 + 1), | ||
| } | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod test { | ||
| use crate::binder::test::select_sql_run; | ||
|
|
@@ -118,6 +211,7 @@ mod test { | |
| use crate::expression::{BinaryOperator, ScalarExpression, UnaryOperator}; | ||
| use crate::optimizer::heuristic::batch::HepBatchStrategy; | ||
| use crate::optimizer::heuristic::optimizer::HepOptimizer; | ||
| use crate::optimizer::rule::simplification::increment_char; | ||
| use crate::optimizer::rule::RuleImpl; | ||
| use crate::planner::operator::filter::FilterOperator; | ||
| use crate::planner::operator::Operator; | ||
|
|
@@ -127,6 +221,13 @@ mod test { | |
| use std::collections::Bound; | ||
| use std::sync::Arc; | ||
|
|
||
| #[test] | ||
| fn test_increment_char() { | ||
| assert_eq!(increment_char('a'), Some('b')); | ||
| assert_eq!(increment_char('z'), None); | ||
| assert_eq!(increment_char('A'), Some('B')); | ||
| } | ||
|
|
||
| #[tokio::test] | ||
| async fn test_constant_calculation_omitted() -> Result<(), DatabaseError> { | ||
| // (2 + (-1)) < -(c1 + 1) | ||
|
|
@@ -343,7 +444,7 @@ mod test { | |
| cb_1_c1, | ||
| Some(ConstantBinary::Scope { | ||
| min: Bound::Unbounded, | ||
| max: Bound::Excluded(Arc::new(DataValue::Int32(Some(-2)))) | ||
| max: Bound::Excluded(Arc::new(DataValue::Int32(Some(-2)))), | ||
| }) | ||
| ); | ||
|
|
||
|
|
@@ -353,7 +454,7 @@ mod test { | |
| cb_1_c2, | ||
| Some(ConstantBinary::Scope { | ||
| min: Bound::Excluded(Arc::new(DataValue::Int32(Some(2)))), | ||
| max: Bound::Unbounded | ||
| max: Bound::Unbounded, | ||
| }) | ||
| ); | ||
|
|
||
|
|
@@ -363,7 +464,7 @@ mod test { | |
| cb_2_c1, | ||
| Some(ConstantBinary::Scope { | ||
| min: Bound::Excluded(Arc::new(DataValue::Int32(Some(2)))), | ||
| max: Bound::Unbounded | ||
| max: Bound::Unbounded, | ||
| }) | ||
| ); | ||
|
|
||
|
|
@@ -373,7 +474,7 @@ mod test { | |
| cb_1_c1, | ||
| Some(ConstantBinary::Scope { | ||
| min: Bound::Unbounded, | ||
| max: Bound::Excluded(Arc::new(DataValue::Int32(Some(-2)))) | ||
| max: Bound::Excluded(Arc::new(DataValue::Int32(Some(-2)))), | ||
| }) | ||
| ); | ||
|
|
||
|
|
@@ -383,7 +484,7 @@ mod test { | |
| cb_3_c1, | ||
| Some(ConstantBinary::Scope { | ||
| min: Bound::Unbounded, | ||
| max: Bound::Excluded(Arc::new(DataValue::Int32(Some(-1)))) | ||
| max: Bound::Excluded(Arc::new(DataValue::Int32(Some(-1)))), | ||
| }) | ||
| ); | ||
|
|
||
|
|
@@ -393,7 +494,7 @@ mod test { | |
| cb_3_c2, | ||
| Some(ConstantBinary::Scope { | ||
| min: Bound::Excluded(Arc::new(DataValue::Int32(Some(0)))), | ||
| max: Bound::Unbounded | ||
| max: Bound::Unbounded, | ||
| }) | ||
| ); | ||
|
|
||
|
|
@@ -403,7 +504,7 @@ mod test { | |
| cb_4_c1, | ||
| Some(ConstantBinary::Scope { | ||
| min: Bound::Excluded(Arc::new(DataValue::Int32(Some(0)))), | ||
| max: Bound::Unbounded | ||
| max: Bound::Unbounded, | ||
| }) | ||
| ); | ||
|
|
||
|
|
@@ -413,7 +514,7 @@ mod test { | |
| cb_4_c2, | ||
| Some(ConstantBinary::Scope { | ||
| min: Bound::Unbounded, | ||
| max: Bound::Excluded(Arc::new(DataValue::Int32(Some(-1)))) | ||
| max: Bound::Excluded(Arc::new(DataValue::Int32(Some(-1)))), | ||
| }) | ||
| ); | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.