Skip to content

Commit

Permalink
feat(planner): support logical filter
Browse files Browse the repository at this point in the history
Signed-off-by: Fedomn <fedomn.ma@gmail.com>
Fedomn committed Dec 24, 2022
1 parent ee32753 commit 3daa60d
Showing 7 changed files with 156 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use derive_new::new;
use itertools::Itertools;
use log::debug;

use super::{BoundExpression, BoundExpressionBase, ColumnBinding};
use crate::planner_v2::{BindError, ExpressionBinder};
use crate::planner_v2::{BindError, ExpressionBinder, LOGGING_TARGET};
use crate::types_v2::LogicalType;

/// A BoundColumnRef expression represents a ColumnRef expression that was bound to an actual table
@@ -64,7 +65,10 @@ impl ExpressionBinder<'_> {
result_types.push(bound_col_ref.base.return_type.clone());
Ok(BoundExpression::BoundColumnRefExpression(bound_col_ref))
} else {
println!("current binder context: {:#?}", self.binder.bind_context);
debug!(
target: LOGGING_TARGET,
"Planner binder context: {:#?}", self.binder.bind_context
);
Err(BindError::Internal(format!(
"column not found: {}",
column_name
32 changes: 32 additions & 0 deletions src/planner_v2/binder/expression_binder/column_alias_binder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use std::collections::HashMap;

use derive_new::new;
use expression_binder::ExpressionBinder;

use crate::planner_v2::{expression_binder, BindError, BoundExpression};

/// A helper binder for WhereBinder and HavingBinder which support alias as a columnref.
#[derive(new)]
pub struct ColumnAliasBinder<'a> {
pub(crate) original_select_items: &'a [sqlparser::ast::Expr],
pub(crate) alias_map: &'a HashMap<String, usize>,
}

impl<'a> ColumnAliasBinder<'a> {
pub fn bind_alias(
&self,
expression_binder: &mut ExpressionBinder,
expr: &sqlparser::ast::Expr,
) -> Result<BoundExpression, BindError> {
if let sqlparser::ast::Expr::Identifier(ident) = expr {
let alias = ident.to_string();
if let Some(alias_entry) = self.alias_map.get(&alias) {
let expr = self.original_select_items[*alias_entry].clone();
let bound_expr =
expression_binder.bind_expression(&expr, &mut vec![], &mut vec![])?;
return Ok(bound_expr);
}
}
Err(BindError::Internal(format!("column not found: {}", expr)))
}
}
4 changes: 4 additions & 0 deletions src/planner_v2/binder/expression_binder/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
mod column_alias_binder;
mod where_binder;
pub use column_alias_binder::*;
pub use where_binder::*;
49 changes: 49 additions & 0 deletions src/planner_v2/binder/expression_binder/where_binder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use derive_new::new;

use super::ColumnAliasBinder;
use crate::planner_v2::{BindError, BoundExpression, ExpressionBinder};
use crate::types_v2::LogicalType;

/// The WHERE binder is responsible for binding an expression within the WHERE clause of a SQL
/// statement
#[derive(new)]
pub struct WhereBinder<'a> {
internal_binder: ExpressionBinder<'a>,
column_alias_binder: ColumnAliasBinder<'a>,
}

impl<'a> WhereBinder<'a> {
pub fn bind_expression(
&mut self,
expr: &sqlparser::ast::Expr,
result_names: &mut Vec<String>,
result_types: &mut Vec<LogicalType>,
) -> Result<BoundExpression, BindError> {
match expr {
sqlparser::ast::Expr::Identifier(..) | sqlparser::ast::Expr::CompoundIdentifier(..) => {
self.bind_column_ref_expr(expr, result_names, result_types)
}
other => self
.internal_binder
.bind_expression(other, result_names, result_types),
}
}

fn bind_column_ref_expr(
&mut self,
expr: &sqlparser::ast::Expr,
result_names: &mut Vec<String>,
result_types: &mut Vec<LogicalType>,
) -> Result<BoundExpression, BindError> {
// bind column ref expr first
let bind_res = self
.internal_binder
.bind_expression(expr, result_names, result_types);
if bind_res.is_ok() {
return bind_res;
}
// try to bind as alias
self.column_alias_binder
.bind_alias(&mut self.internal_binder, expr)
}
}
2 changes: 2 additions & 0 deletions src/planner_v2/binder/mod.rs
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@ mod bind_context;
mod binding;
mod errors;
mod expression;
mod expression_binder;
mod query_node;
mod sqlparser_util;
mod statement;
@@ -13,6 +14,7 @@ pub use bind_context::*;
pub use binding::*;
pub use errors::*;
pub use expression::*;
pub use expression_binder::*;
pub use query_node::*;
pub use sqlparser_util::*;
pub use statement::*;
52 changes: 49 additions & 3 deletions src/planner_v2/binder/query_node/bind_select_node.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use std::collections::HashMap;

use derive_new::new;
use sqlparser::ast::{Ident, Query};

use crate::planner_v2::{
BindError, Binder, BoundExpression, BoundTableRef, ExpressionBinder, SqlparserResolver,
VALUES_LIST_ALIAS,
BindError, Binder, BoundExpression, BoundTableRef, ColumnAliasBinder, ExpressionBinder,
SqlparserResolver, WhereBinder, VALUES_LIST_ALIAS,
};
use crate::types_v2::LogicalType;

@@ -17,6 +19,13 @@ pub struct BoundSelectNode {
pub(crate) select_list: Vec<BoundExpression>,
/// The FROM clause
pub(crate) from_table: BoundTableRef,
/// The WHERE clause
#[allow(dead_code)]
pub(crate) where_clause: Option<BoundExpression>,
/// The original unparsed expressions. This is exported after binding, because the binding
/// might change the expressions (e.g. when a * clause is present)
#[allow(dead_code)]
pub(crate) original_select_items: Option<Vec<sqlparser::ast::Expr>>,
/// Index used by the LogicalProjection
#[new(default)]
pub(crate) projection_index: usize,
@@ -57,14 +66,15 @@ impl Binder {
.try_collect::<Vec<_>>()?;

let bound_table_ref = BoundTableRef::BoundExpressionListRef(bound_expression_list_ref);
let node = BoundSelectNode::new(names, types, select_list, bound_table_ref);
let node = BoundSelectNode::new(names, types, select_list, bound_table_ref, None, None);
Ok(node)
}

pub fn bind_select_body(
&mut self,
select: &sqlparser::ast::Select,
) -> Result<BoundSelectNode, BindError> {
// first bind the FROM table statement
let from_table = self.bind_table_ref(select.from.as_slice())?;

let mut result_names = vec![];
@@ -75,6 +85,40 @@ impl Binder {
return Err(BindError::Internal("empty select list".to_string()));
}

// create a mapping of (alias -> index) and a mapping of (Expression -> index) for the
// SELECT list
let mut original_select_items = vec![];
let mut alias_map = HashMap::new();
for (idx, item) in new_select_list.iter().enumerate() {
match item {
sqlparser::ast::SelectItem::UnnamedExpr(expr) => {
original_select_items.push(expr.clone());
}
sqlparser::ast::SelectItem::ExprWithAlias { expr, alias } => {
alias_map.insert(alias.to_string(), idx);
original_select_items.push(expr.clone());
}
sqlparser::ast::SelectItem::Wildcard(..)
| sqlparser::ast::SelectItem::QualifiedWildcard(..) => {
return Err(BindError::Internal(
"wildcard should be expanded before".to_string(),
))
}
}
}

// first visit the WHERE clause
// the WHERE clause happens before the GROUP BY, PROJECTION or HAVING clauses
let where_clause = if let Some(where_expr) = &select.selection {
let column_alias_binder = ColumnAliasBinder::new(&original_select_items, &alias_map);
let mut where_binder =
WhereBinder::new(ExpressionBinder::new(self), column_alias_binder);
let bound_expr = where_binder.bind_expression(where_expr, &mut vec![], &mut vec![])?;
Some(bound_expr)
} else {
None
};

let select_list = new_select_list
.iter()
.map(|item| self.bind_select_item(item, &mut result_names, &mut result_types))
@@ -85,6 +129,8 @@ impl Binder {
result_types,
select_list,
from_table,
where_clause,
Some(original_select_items),
))
}

14 changes: 14 additions & 0 deletions tests/slt/filter.slt
Original file line number Diff line number Diff line change
@@ -17,3 +17,17 @@ select id, first_name from employee where id > 3 or id = 1
----
1 Bill
4 Von


onlyif sqlrs_v2
statement ok
create table t1(v1 int, v2 int, v3 int);
insert into t1(v3, v2, v1) values (0, 4, 1), (1, 5, 2);


# onlyif sqlrs_v2
# query III
# select v1, v2 from t1 where v1 >= 1;
# ----
# 1 4
# 2 5

0 comments on commit 3daa60d

Please sign in to comment.