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(planner_v2): add type checker #717

Merged
merged 9 commits into from
Oct 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/array/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,7 @@ macro_rules! impl_array {
}

/// Return a string describing the type of this array.
pub fn type_string(&self) -> &str {
pub fn type_string(&self) -> &'static str {
match self {
$(
Self::$Abc(_) => stringify!($Abc),
Expand Down
26 changes: 13 additions & 13 deletions src/binder/expression/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,12 @@ impl BoundExpr {
pub fn return_type(&self) -> Option<DataType> {
match self {
Self::Constant(v) => v.data_type(),
Self::ColumnRef(expr) => Some(expr.desc.datatype().clone()),
Self::BinaryOp(expr) => expr.return_type.clone(),
Self::UnaryOp(expr) => expr.return_type.clone(),
Self::TypeCast(expr) => Some(expr.ty.clone().nullable()),
Self::AggCall(expr) => Some(expr.return_type.clone()),
Self::InputRef(expr) => Some(expr.return_type.clone()),
Self::ColumnRef(expr) => Some(*expr.desc.datatype()),
Self::BinaryOp(expr) => expr.return_type,
Self::UnaryOp(expr) => expr.return_type,
Self::TypeCast(expr) => Some(expr.ty.nullable()),
Self::AggCall(expr) => Some(expr.return_type),
Self::InputRef(expr) => Some(expr.return_type),
Self::IsNull(_) => Some(DataTypeKind::Bool.not_null()),
Self::ExprWithAlias(expr) => expr.expr.return_type(),
Self::Alias(expr) => expr.expr.return_type(),
Expand Down Expand Up @@ -334,9 +334,9 @@ mod tests {
let data_type = DataType::new(DataTypeKind::Int32, true);
let expr = BoundExpr::InputRef(BoundInputRef {
index: 0,
return_type: data_type.clone(),
return_type: data_type,
});
let child_schema = vec![ColumnDesc::new(data_type.clone(), "a".to_string(), false)];
let child_schema = vec![ColumnDesc::new(data_type, "a".to_string(), false)];
let expr = BoundExpr::UnaryOp(BoundUnaryOp {
op: UnaryOperator::Minus,
expr: Box::new(expr),
Expand All @@ -353,7 +353,7 @@ mod tests {
let left_data_type = DataType::new(DataTypeKind::Int32, true);
let left_expr = BoundExpr::InputRef(BoundInputRef {
index: 0,
return_type: left_data_type.clone(),
return_type: left_data_type,
});
let right_expr = BoundExpr::Constant(DataValue::Int64(1));
let child_schema = vec![ColumnDesc::new(left_data_type, "a".to_string(), false)];
Expand All @@ -370,15 +370,15 @@ mod tests {
let data_type = DataType::new(DataTypeKind::Int32, true);
let left_expr = BoundExpr::InputRef(BoundInputRef {
index: 0,
return_type: data_type.clone(),
return_type: data_type,
});
let right_expr = BoundExpr::InputRef(BoundInputRef {
index: 1,
return_type: data_type.clone(),
return_type: data_type,
});
let child_schema = vec![
ColumnDesc::new(data_type.clone(), "a".to_string(), false),
ColumnDesc::new(data_type.clone(), "b".to_string(), false),
ColumnDesc::new(data_type, "a".to_string(), false),
ColumnDesc::new(data_type, "b".to_string(), false),
];

let expr = BoundExpr::BinaryOp(BoundBinaryOp {
Expand Down
11 changes: 9 additions & 2 deletions src/binder_v2/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::types::{DataTypeKind, DataValue, Interval, F64};
impl Binder {
/// Bind an expression.
pub fn bind_expr(&mut self, expr: Expr) -> Result {
match expr {
let id = match expr {
Expr::Value(v) => Ok(self.egraph.add(Node::Constant(v.into()))),
Expr::Identifier(ident) => self.bind_ident([ident]),
Expr::CompoundIdentifier(idents) => self.bind_ident(idents),
Expand All @@ -33,7 +33,11 @@ impl Binder {
high,
} => self.bind_between(*expr, negated, *low, *high),
_ => todo!("bind expression: {:?}", expr),
}?;
if let Err(e) = &self.egraph[id].data.type_ {
return Err(e.clone().into());
}
Ok(id)
}

fn bind_ident(&mut self, idents: impl IntoIterator<Item = Ident>) -> Result {
Expand Down Expand Up @@ -73,7 +77,10 @@ impl Binder {
if column_ids.next().is_some() {
return Err(BindError::AmbiguousColumn(column_name.into()));
}
return Ok(self.egraph.add(Node::Column(column_ref_id)));
let id = self.egraph.add(Node::Column(column_ref_id));
self.egraph[id].data.type_ =
Ok(self.catalog.get_column(&column_ref_id).unwrap().datatype());
return Ok(id);
}
if let Some(id) = self.current_ctx().aliases.get(column_name) {
return Ok(*id);
Expand Down
4 changes: 3 additions & 1 deletion src/binder_v2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use itertools::Itertools;

use crate::catalog::{RootCatalog, TableRefId, DEFAULT_DATABASE_NAME, DEFAULT_SCHEMA_NAME};
use crate::parser::*;
use crate::planner::{Expr as Node, ExprAnalysis, RecExpr};
use crate::planner::{Expr as Node, ExprAnalysis, RecExpr, TypeError};
use crate::types::{DataTypeKind, DataValue};

mod drop;
Expand Down Expand Up @@ -59,6 +59,8 @@ pub enum BindError {
CastError(DataValue, DataTypeKind),
#[error("{0}")]
BindFunctionError(String),
#[error("type error: {0}")]
TypeError(#[from] TypeError),
}

/// The binder resolves all expressions referring to schema objects such as
Expand Down
2 changes: 1 addition & 1 deletion src/catalog/column.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ impl ColumnCatalog {
}

pub fn datatype(&self) -> DataType {
self.desc.datatype.clone()
self.desc.datatype
}

pub fn set_primary(&mut self, is_primary: bool) {
Expand Down
8 changes: 8 additions & 0 deletions src/catalog/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,14 @@ impl ColumnRefId {
column_id,
}
}

pub const fn table(&self) -> TableRefId {
TableRefId {
database_id: self.database_id,
schema_id: self.schema_id,
table_id: self.table_id,
}
}
}

impl std::fmt::Display for ColumnRefId {
Expand Down
5 changes: 5 additions & 0 deletions src/catalog/root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,11 @@ impl RootCatalog {
schema.get_table_by_id(table_ref_id.table_id)
}

pub fn get_column(&self, column_ref_id: &ColumnRefId) -> Option<ColumnCatalog> {
self.get_table(&column_ref_id.table())?
.get_column_by_id(column_ref_id.column_id)
}

pub fn add_table(
&self,
table_ref_id: TableRefId,
Expand Down
76 changes: 42 additions & 34 deletions src/executor/evaluator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ impl BoundExpr {
BoundExpr::BinaryOp(binary_op) => {
let left = binary_op.left_expr.eval(chunk)?;
let right = binary_op.right_expr.eval(chunk)?;
Ok(left.binary_op(&binary_op.op, &right))
left.binary_op(&binary_op.op, &right)
}
BoundExpr::UnaryOp(op) => {
let array = op.expr.eval(chunk)?;
Ok(array.unary_op(&op.op))
array.unary_op(&op.op)
}
BoundExpr::Constant(v) => {
let mut builder = ArrayBuilderImpl::with_capacity(
Expand All @@ -41,7 +41,7 @@ impl BoundExpr {
if self.return_type() == cast.expr.return_type() {
return Ok(array);
}
array.try_cast(cast.ty.clone())
array.try_cast(cast.ty)
}
BoundExpr::IsNull(expr) => {
let array = expr.expr.eval(chunk)?;
Expand Down Expand Up @@ -71,11 +71,11 @@ impl BoundExpr {
let right = binary_op
.right_expr
.eval_array_in_storage(chunk, cardinality)?;
Ok(left.binary_op(&binary_op.op, &right))
left.binary_op(&binary_op.op, &right)
}
BoundExpr::UnaryOp(op) => {
let array = op.expr.eval_array_in_storage(chunk, cardinality)?;
Ok(array.unary_op(&op.op))
array.unary_op(&op.op)
}
BoundExpr::Constant(v) => {
let mut builder =
Expand All @@ -91,7 +91,7 @@ impl BoundExpr {
if self.return_type() == cast.expr.return_type() {
return Ok(array);
}
array.try_cast(cast.ty.clone())
array.try_cast(cast.ty)
}
BoundExpr::IsNull(expr) => {
let array = expr.expr.eval_array_in_storage(chunk, cardinality)?;
Expand All @@ -108,34 +108,41 @@ impl BoundExpr {

impl ArrayImpl {
/// Perform unary operation.
pub fn unary_op(&self, op: &UnaryOperator) -> ArrayImpl {
pub fn unary_op(&self, op: &UnaryOperator) -> Result<ArrayImpl, ConvertError> {
type A = ArrayImpl;
match op {
Ok(match op {
UnaryOperator::Plus => match self {
A::Int32(_) => self.clone(),
A::Int64(_) => self.clone(),
A::Float64(_) => self.clone(),
A::Decimal(_) => self.clone(),
_ => panic!("+ can only be applied to Int, Float or Decimal array"),
_ => return Err(ConvertError::NoUnaryOp("+".into(), self.type_string())),
},
UnaryOperator::Minus => match self {
A::Int32(a) => A::new_int32(unary_op(a.as_ref(), |v| -v)),
A::Int64(a) => A::new_int64(unary_op(a.as_ref(), |v| -v)),
A::Float64(a) => A::new_float64(unary_op(a.as_ref(), |v| -v)),
A::Decimal(a) => A::new_decimal(unary_op(a.as_ref(), |v| -v)),
_ => panic!("- can only be applied to Int, Float or Decimal array"),
_ => return Err(ConvertError::NoUnaryOp("-".into(), self.type_string())),
},
UnaryOperator::Not => match self {
A::Bool(a) => A::new_bool(unary_op(a.as_ref(), |b| !b)),
_ => panic!("Not can only be applied to BOOL array"),
_ => return Err(ConvertError::NoUnaryOp("not".into(), self.type_string())),
},
_ => todo!("evaluate operator: {:?}", op),
}
_ => return Err(ConvertError::NoUnaryOp(op.to_string(), self.type_string())),
})
}

/// Perform binary operation.
pub fn binary_op(&self, op: &BinaryOperator, right: &ArrayImpl) -> ArrayImpl {
pub fn binary_op(
&self,
op: &BinaryOperator,
right: &ArrayImpl,
) -> Result<ArrayImpl, ConvertError> {
use BinaryOperator::*;
type A = ArrayImpl;
let no_op =
|| ConvertError::NoBinaryOp(op.to_string(), self.type_string(), right.type_string());
macro_rules! arith {
($op:tt) => {
match (self, right) {
Expand All @@ -155,7 +162,8 @@ impl ArrayImpl {

(A::Decimal(a), A::Decimal(b)) => A::new_decimal(binary_op(a.as_ref(), b.as_ref(), |a, b| a $op b)),
(A::Date(a), A::Interval(b)) => A::new_date(binary_op(a.as_ref(), b.as_ref(), |a, b| *a $op *b)),
_ => todo!("Support {} {} {}", self.type_string(), stringify!($op), right.type_string()),

_ => return Err(no_op()),
}
}
}
Expand All @@ -169,23 +177,23 @@ impl ArrayImpl {
(A::Utf8(a), A::Utf8(b)) => A::new_bool(binary_op(a.as_ref(), b.as_ref(), |a, b| a $op b)),
(A::Date(a), A::Date(b)) => A::new_bool(binary_op(a.as_ref(), b.as_ref(), |a, b| a $op b)),
(A::Decimal(a), A::Decimal(b)) => A::new_bool(binary_op(a.as_ref(), b.as_ref(), |a, b| a $op b)),
_ => todo!("Support {} {} {}", self.type_string(), stringify!($op), right.type_string()),
_ => return Err(no_op()),
}
}
}
match op {
BinaryOperator::Plus => arith!(+),
BinaryOperator::Minus => arith!(-),
BinaryOperator::Multiply => arith!(*),
BinaryOperator::Divide => arith!(/),
BinaryOperator::Modulo => arith!(%),
BinaryOperator::Eq => cmp!(==),
BinaryOperator::NotEq => cmp!(!=),
BinaryOperator::Gt => cmp!(>),
BinaryOperator::Lt => cmp!(<),
BinaryOperator::GtEq => cmp!(>=),
BinaryOperator::LtEq => cmp!(<=),
BinaryOperator::And => match (self, right) {
Ok(match op {
Plus => arith!(+),
Minus => arith!(-),
Multiply => arith!(*),
Divide => arith!(/),
Modulo => arith!(%),
Eq => cmp!(==),
NotEq => cmp!(!=),
Gt => cmp!(>),
Lt => cmp!(<),
GtEq => cmp!(>=),
LtEq => cmp!(<=),
And => match (self, right) {
(A::Bool(a), A::Bool(b)) => {
A::new_bool(binary_op_with_null(a.as_ref(), b.as_ref(), |a, b| {
match (a, b) {
Expand All @@ -195,9 +203,9 @@ impl ArrayImpl {
}
}))
}
_ => panic!("And can only be applied to BOOL arrays"),
_ => return Err(no_op()),
},
BinaryOperator::Or => match (self, right) {
Or => match (self, right) {
(A::Bool(a), A::Bool(b)) => {
A::new_bool(binary_op_with_null(a.as_ref(), b.as_ref(), |a, b| {
match (a, b) {
Expand All @@ -207,10 +215,10 @@ impl ArrayImpl {
}
}))
}
_ => panic!("Or can only be applied to BOOL arrays"),
_ => return Err(no_op()),
},
_ => todo!("evaluate operator: {:?}", op),
}
_ => return Err(no_op()),
})
}

/// Cast the array to another type.
Expand Down
4 changes: 2 additions & 2 deletions src/function/abs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ impl AbsFunction {
}

Ok(Box::new(AbsFunction {
return_type: input_types[0].clone(),
return_type: input_types[0],
}))
}
}
Expand All @@ -32,7 +32,7 @@ impl Function for AbsFunction {
// TODO: When unsigned types are supported,
// we can convert signed type to unsigned type
// this makes abs(i32::MIN) can represent by u32
self.return_type.clone()
self.return_type
}

fn execute(&self, input: &[&ArrayImpl]) -> Result<ArrayImpl, FunctionError> {
Expand Down
4 changes: 2 additions & 2 deletions src/function/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ impl AddFunction {
}

Ok(Box::new(AddFunction {
return_type: input_types[0].clone(),
return_type: input_types[0],
}))
}
}
Expand All @@ -38,7 +38,7 @@ impl Function for AddFunction {
}

fn return_types(&self) -> DataType {
self.return_type.clone()
self.return_type
}

fn execute(&self, input: &[&ArrayImpl]) -> Result<ArrayImpl, FunctionError> {
Expand Down
4 changes: 2 additions & 2 deletions src/function/repeat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ impl RepeatFunction {
}

Ok(Box::new(RepeatFunction {
return_type: input_types[0].clone(),
return_type: input_types[0],
}))
}
}
Expand All @@ -48,7 +48,7 @@ impl Function for RepeatFunction {
}

fn return_types(&self) -> DataType {
self.return_type.clone()
self.return_type
}

fn execute(&self, input: &[&ArrayImpl]) -> Result<ArrayImpl, FunctionError> {
Expand Down
Loading