Skip to content

Commit

Permalink
feat: support BETWEEN on Where
Browse files Browse the repository at this point in the history
  • Loading branch information
KKould committed Feb 9, 2024
1 parent 8711818 commit 6c620a7
Show file tree
Hide file tree
Showing 8 changed files with 178 additions and 18 deletions.
12 changes: 11 additions & 1 deletion src/binder/aggregate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@ impl<'a, T: Transaction> Binder<'a, T> {
self.visit_column_agg_expr(arg)?;
}
}
ScalarExpression::Between { expr, left_expr, right_expr, .. } => {
self.visit_column_agg_expr(expr)?;
self.visit_column_agg_expr(left_expr)?;
self.visit_column_agg_expr(right_expr)?;
}
ScalarExpression::Constant(_) | ScalarExpression::ColumnRef { .. } => {}
}

Expand Down Expand Up @@ -257,7 +262,12 @@ impl<'a, T: Transaction> Binder<'a, T> {
self.validate_having_orderby(right_expr)?;
Ok(())
}

ScalarExpression::Between { expr, left_expr, right_expr, .. } => {
self.validate_having_orderby(expr)?;
self.validate_having_orderby(left_expr)?;
self.validate_having_orderby(right_expr)?;
Ok(())
}
ScalarExpression::Constant(_) => Ok(()),
}
}
Expand Down
8 changes: 8 additions & 0 deletions src/binder/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ impl<'a, T: Transaction> Binder<'a, T> {

Ok(ScalarExpression::Constant(Arc::new(value)))
}
Expr::Between { expr, negated, low, high } => {
Ok(ScalarExpression::Between {
negated: *negated,
expr: Box::new(self.bind_expr(expr)?),
left_expr: Box::new(self.bind_expr(low)?),
right_expr: Box::new(self.bind_expr(high)?),
})
}
_ => {
todo!()
}
Expand Down
27 changes: 26 additions & 1 deletion src/expression/evaluator.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::cmp::Ordering;
use crate::catalog::ColumnSummary;
use crate::errors::DatabaseError;
use crate::expression::value_compute::{binary_op, unary_op};
Expand Down Expand Up @@ -68,9 +69,17 @@ impl ScalarExpression {
negated,
} => {
let value = expr.eval(tuple)?;
if value.is_null() {
return Ok(Arc::new(DataValue::Boolean(None)));
}
let mut is_in = false;
for arg in args {
if arg.eval(tuple)? == value {
let arg_value = arg.eval(tuple)?;

if arg_value.is_null() {
return Ok(Arc::new(DataValue::Boolean(None)));
}
if arg_value == value {
is_in = true;
break;
}
Expand All @@ -92,6 +101,22 @@ impl ScalarExpression {

Ok(value)
}
ScalarExpression::Between { expr, left_expr, right_expr, negated } => {
let value = expr.eval(tuple)?;
let left = left_expr.eval(tuple)?;
let right = right_expr.eval(tuple)?;

let mut is_between = match (value.partial_cmp(&left).map(Ordering::is_ge), value.partial_cmp(&right).map(Ordering::is_le)) {
(Some(true), Some(true)) => true,
(None, _) | (_, None) => return Ok(Arc::new(DataValue::Boolean(None))),
_ => false,

};
if *negated {
is_between = !is_between;
}
Ok(Arc::new(DataValue::Boolean(Some(is_between))))
}
}
}

Expand Down
32 changes: 27 additions & 5 deletions src/expression/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ pub enum ScalarExpression {
expr: Box<ScalarExpression>,
args: Vec<ScalarExpression>,
},
Between {
negated: bool,
expr: Box<ScalarExpression>,
left_expr: Box<ScalarExpression>,
right_expr: Box<ScalarExpression>,
}
}

impl ScalarExpression {
Expand Down Expand Up @@ -99,9 +105,12 @@ impl ScalarExpression {
..
} => left_expr.nullable() && right_expr.nullable(),
ScalarExpression::In { expr, args, .. } => {
args.iter().all(ScalarExpression::nullable) && expr.nullable()
expr.nullable() && args.iter().all(ScalarExpression::nullable)
}
ScalarExpression::AggCall { args, .. } => args.iter().all(ScalarExpression::nullable),
ScalarExpression::Between { expr, left_expr, right_expr, .. } => {
expr.nullable() && left_expr.nullable() && right_expr.nullable()
}
}
}

Expand All @@ -121,7 +130,7 @@ impl ScalarExpression {
Self::AggCall {
ty: return_type, ..
} => *return_type,
Self::IsNull { .. } | Self::In { .. } => LogicalType::Boolean,
Self::IsNull { .. } | Self::In { .. } | ScalarExpression::Between { .. } => LogicalType::Boolean,
Self::Alias { expr, .. } => expr.return_type(),
}
}
Expand Down Expand Up @@ -194,6 +203,9 @@ impl ScalarExpression {
ScalarExpression::In { expr, args, .. } => {
expr.has_agg_call() || args.iter().any(|arg| arg.has_agg_call())
}
ScalarExpression::Between { expr, left_expr, right_expr, .. } => {
expr.has_agg_call() || left_expr.has_agg_call() || right_expr.has_agg_call()
}
}
}

Expand Down Expand Up @@ -250,16 +262,26 @@ impl ScalarExpression {
} => {
let args_string = args
.iter()
.map(|arg| arg.output_column().name().to_string())
.map(|arg| arg.output_name())
.join(", ");
let op_string = if *negated { "not in" } else { "in" };
format!(
"{} {} ({})",
expr.output_column().name(),
expr.output_name(),
op_string,
args_string
)
}
ScalarExpression::Between { expr, left_expr, right_expr, negated } => {
let op_string = if *negated { "not between" } else { "between" };
format!(
"{} {} [{}, {}]",
expr.output_name(),
op_string,
left_expr.output_name(),
right_expr.output_name()
)
}
}
}

Expand Down Expand Up @@ -320,7 +342,7 @@ pub enum BinaryOperator {

impl fmt::Display for ScalarExpression {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", self.output_column().name())
write!(f, "{}", self.output_name())
}
}

Expand Down
31 changes: 29 additions & 2 deletions src/expression/simplify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,31 @@ impl ScalarExpression {

let _ = mem::replace(self, new_expr);
}
ScalarExpression::Between { expr, left_expr, right_expr, negated } => {
let (op, left_op, right_op) = if *negated {
(BinaryOperator::Or, BinaryOperator::Lt, BinaryOperator::Gt)
} else {
(BinaryOperator::And, BinaryOperator::GtEq, BinaryOperator::LtEq)
};
let new_expr = ScalarExpression::Binary {
op,
left_expr: Box::new(ScalarExpression::Binary {
op: left_op,
left_expr: expr.clone(),
right_expr: left_expr.clone(),
ty: LogicalType::Boolean,
}),
right_expr: Box::new(ScalarExpression::Binary {
op: right_op,
left_expr: expr.clone(),
right_expr: right_expr.clone(),
ty: LogicalType::Boolean,
}),
ty: LogicalType::Boolean,
};

let _ = mem::replace(self, new_expr);
}
_ => (),
}

Expand Down Expand Up @@ -882,7 +907,8 @@ impl ScalarExpression {
ScalarExpression::Alias { expr, .. }
| ScalarExpression::TypeCast { expr, .. }
| ScalarExpression::Unary { expr, .. }
| ScalarExpression::In { expr, .. } => expr.convert_binary(col_id),
| ScalarExpression::In { expr, .. }
| ScalarExpression::Between { expr , ..} => expr.convert_binary(col_id),
ScalarExpression::IsNull { expr, negated, .. } => match expr.as_ref() {
ScalarExpression::ColumnRef(column) => {
Ok(column.id().is_some_and(|id| col_id == &id).then(|| {
Expand All @@ -900,7 +926,8 @@ impl ScalarExpression {
| ScalarExpression::Unary { .. }
| ScalarExpression::Binary { .. }
| ScalarExpression::AggCall { .. }
| ScalarExpression::In { .. } => expr.convert_binary(col_id),
| ScalarExpression::In { .. }
|ScalarExpression::Between { .. } => expr.convert_binary(col_id),
},
ScalarExpression::Constant(_)
| ScalarExpression::ColumnRef(_)
Expand Down
2 changes: 1 addition & 1 deletion src/expression/value_compute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -531,7 +531,7 @@ pub fn binary_op(
_ => todo!("unsupported operator"),
}
}
LogicalType::SqlNull => return Err(DatabaseError::NotNull),
LogicalType::SqlNull => return Ok(DataValue::Null),
LogicalType::Invalid => return Err(DatabaseError::InvalidType),
};

Expand Down
60 changes: 60 additions & 0 deletions tests/slt/filter.slt
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,66 @@ select * from t1 where id not in (1, 2)
0 KipSQL
3 Cool!

query II
select * from t1 where id in (1, null)
----

query II
select * from t1 where null in (1, 2)
----

query II
select * from t1 where null in (1, null)
----

query II
select * from t1 where id not in (1, null)
----

query II
select * from t1 where null not in (1, 2)
----

query II
select * from t1 where null not in (1, null)
----

query II
select * from t1 where id between 1 and 2
----
1 KipDB
2 KipBlog

query II
select * from t1 where id not between 1 and 2
----
0 KipSQL
3 Cool!

query II
select * from t1 where id between 1 and null
----

query II
select * from t1 where null between 1 and 2
----

query II
select * from t1 where null between 1 and null
----

query II
select * from t1 where id not between 1 and null
----

query II
select * from t1 where null not between 1 and 2
----

query II
select * from t1 where null not between 1 and null
----

statement ok
drop table t

Expand Down
24 changes: 16 additions & 8 deletions tests/slt/sql_2016/E061_02.slt
Original file line number Diff line number Diff line change
@@ -1,33 +1,41 @@
# E061-02: BETWEEN predicate

# TODO: Support `BETWEEN` on `Where`
statement ok
CREATE TABLE TABLE_E061_02_01_01 ( ID INT PRIMARY KEY, A INT );

# statement ok
# CREATE TABLE TABLE_E061_02_01_01 ( ID INT PRIMARY KEY, A INT );

# SELECT A FROM TABLE_E061_02_01_01 WHERE A BETWEEN 1 AND 1
query I
SELECT A FROM TABLE_E061_02_01_01 WHERE A BETWEEN 1 AND 1

# statement ok
# CREATE TABLE TABLE_E061_02_01_02 ( ID INT PRIMARY KEY, A INT );

# sqlparser-rs unsupported
# query I
# SELECT A FROM TABLE_E061_02_01_02 WHERE A BETWEEN ASYMMETRIC 1 AND 1

# statement ok
# CREATE TABLE TABLE_E061_02_01_03 ( ID INT PRIMARY KEY, A INT );

# sqlparser-rs unsupported
# query I
# SELECT A FROM TABLE_E061_02_01_03 WHERE A BETWEEN SYMMETRIC 1 AND 1

# statement ok
# CREATE TABLE TABLE_E061_02_01_04 ( ID INT PRIMARY KEY, A INT );
statement ok
CREATE TABLE TABLE_E061_02_01_04 ( ID INT PRIMARY KEY, A INT );

# SELECT A FROM TABLE_E061_02_01_04 WHERE A NOT BETWEEN 1 AND 1
query I
SELECT A FROM TABLE_E061_02_01_04 WHERE A NOT BETWEEN 1 AND 1

# statement ok
# CREATE TABLE TABLE_E061_02_01_05 ( ID INT PRIMARY KEY, A INT );

# sqlparser-rs unsupported
# query I
# SELECT A FROM TABLE_E061_02_01_05 WHERE A NOT BETWEEN ASYMMETRIC 1 AND 1

# statement ok
# CREATE TABLE TABLE_E061_02_01_06 ( ID INT PRIMARY KEY, A INT );

# sqlparser-rs unsupported
# query I
# SELECT A FROM TABLE_E061_02_01_06 WHERE A NOT BETWEEN SYMMETRIC 1 AND 1

0 comments on commit 6c620a7

Please sign in to comment.