Skip to content

Commit

Permalink
feat: Minimal expression building
Browse files Browse the repository at this point in the history
  • Loading branch information
can-keklik committed Dec 1, 2024
1 parent 211a662 commit 037480d
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 38 deletions.
53 changes: 53 additions & 0 deletions lykiadb-lang/src/ast/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,59 @@ impl Display for Expr {
}

impl Expr {
pub fn walk(&self, visitor: &mut impl FnMut(&Expr) -> bool) -> bool {
if !visitor(self) {
return false;
}
match self {
Expr::Select { .. }
| Expr::Insert { .. }
| Expr::Delete { .. }
| Expr::Update { .. }
| Expr::Variable { .. }
| Expr::Literal { .. }
| Expr::Function { .. } => false,
//
Expr::Binary { left, right, .. }
| Expr::Logical { left, right, .. } => {
let rleft = left.walk(visitor);
let rright = right.walk(visitor);

rleft || rright
},
//
Expr::Grouping { expr, .. }
| Expr::Unary { expr, .. }
| Expr::Assignment { expr, .. } => expr.walk(visitor),
//
Expr::Call { callee, args, .. } => {
let rcallee = callee.walk(visitor);
let rargs = args.iter().map(|x| x.walk(visitor)).all(|x| x);

rcallee || rargs
},
Expr::Between {
lower,
upper,
subject,
..
} => {
let rlower = lower.walk(visitor);
let rupper = upper.walk(visitor);
let rsubject = subject.walk(visitor);

rlower || rupper || rsubject
},
Expr::Get { object, .. } => object.walk(visitor),
Expr::Set { object, value, .. } => {
let robject = object.walk(visitor);
let rvalue = value.walk(visitor);

robject || rvalue
},
}
}

pub fn collect(&self, predicate: &impl Fn(&Expr) -> bool, collected: &mut Vec<Expr>) {
if predicate(self) {
collected.push(self.clone());
Expand Down
64 changes: 40 additions & 24 deletions lykiadb-server/src/plan/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,6 @@ use crate::value::RV;
pub mod planner;
mod scope;

enum ExprOverride {
Subquery(usize),
Aggregate,
Field
}

enum IntermediateExpr {
Expr {
root: Expr,
overrides: HashMap<usize, ExprOverride>
},
Precalculated(RV)
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PlannerError {
ObjectNotFoundInScope(Identifier),
Expand All @@ -49,6 +35,33 @@ pub enum Aggregate {
Sum(Expr),
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum ExprOverride {
Subquery(usize),
Aggregate,
Field
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum IntermediateExpr {
Constant(RV),
Expr {
expr: Expr,
overrides: HashMap<usize, ExprOverride>
},
}

impl Display for IntermediateExpr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
IntermediateExpr::Constant(rv) => write!(f, "{:?}", rv),
IntermediateExpr::Expr { expr, .. } => {
write!(f, "{}", expr)
}
}
}
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum Plan {
Select(Node),
Expand All @@ -64,13 +77,13 @@ pub enum Node {

Aggregate {
source: Box<Node>,
group_by: Vec<Expr>,
group_by: Vec<IntermediateExpr>,
aggregates: Vec<Aggregate>,
},

Filter {
source: Box<Node>,
predicate: Expr,
predicate: IntermediateExpr,
subqueries: Vec<Node>
},

Expand All @@ -91,28 +104,28 @@ pub enum Node {

Order {
source: Box<Node>,
key: Vec<(Expr, SqlOrdering)>,
key: Vec<(IntermediateExpr, SqlOrdering)>,
},

Values {
rows: Vec<Vec<Expr>>,
rows: Vec<Vec<IntermediateExpr>>,
},

Scan {
source: SqlCollectionIdentifier,
filter: Option<Expr>,
filter: Option<IntermediateExpr>,
},

EvalScan {
source: SqlExpressionSource,
filter: Option<Expr>,
filter: Option<IntermediateExpr>,
},

Join {
left: Box<Node>,
join_type: SqlJoinType,
right: Box<Node>,
constraint: Option<Expr>,
constraint: Option<IntermediateExpr>,
},

Subquery {
Expand Down Expand Up @@ -191,9 +204,12 @@ impl Node {
predicate,
Self::NEWLINE
)?;
subqueries.iter().try_for_each(|subquery| {
subquery._fmt_recursive(f, indent + 1)
})?;
if !subqueries.is_empty() {
write!(f, "{} > subqueries{}", indent_str, Self::NEWLINE)?;
subqueries.iter().try_for_each(|subquery| {
subquery._fmt_recursive(f, indent + 2)
})?;
}
source._fmt_recursive(f, indent + 1)
}
Node::Subquery { source, alias } => {
Expand Down
73 changes: 59 additions & 14 deletions lykiadb-server/src/plan/planner.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::collections::HashMap;

use crate::{
engine::{
error::ExecutionError,
Expand All @@ -12,7 +14,8 @@ use lykiadb_lang::ast::{
visitor::VisitorMut,
};

use super::{scope::Scope, Node, Plan};
use super::{scope::Scope, IntermediateExpr, Node, Plan};

pub struct Planner<'a> {
interpreter: &'a mut Interpreter,
}
Expand Down Expand Up @@ -48,11 +51,11 @@ impl<'a> Planner<'a> {

// WHERE
if let Some(predicate) = &core.r#where {
// TODO: Traverse expression
let (expr, subqueries): (IntermediateExpr, Vec<Node>) = self.build_expr(predicate.as_ref(), true, false)?;
node = Node::Filter {
source: Box::new(node),
predicate: *predicate.clone(),
subqueries: vec![]
predicate: expr,
subqueries
}
}

Expand Down Expand Up @@ -85,16 +88,54 @@ impl<'a> Planner<'a> {
self.interpreter.visit_expr(expr)
}

fn build_expr(&mut self, expr: &Expr, allow_subqueries: bool, allow_aggregates: bool) -> Result<(IntermediateExpr, Vec<Node>), HaltReason> {
// TODO(vck): Implement this

let mut subqueries:Vec<Node> = vec![];
let mut overrides = HashMap::new();

expr.walk(&mut |expr: &Expr| {
match expr {
Expr::Get { id, object, name, .. } => {
false
}
Expr::Variable { name, id, .. } => {
false
}
Expr::Call { callee, args, id, .. } => {
false
},
Expr::Select { query, .. } => {
if !allow_subqueries {
return false; // Err(HaltReason::Error(ExecutionError::Plan("Subqueries are not allowed here".to_string())));
}
let subquery = self.build_select(query);
subqueries.push(subquery.unwrap());
false
},
_ => {
true
}
}
});
Ok((IntermediateExpr::Expr { expr: expr.clone(), overrides }, subqueries))
}

fn build_select(&mut self, query: &SqlSelect) -> Result<Node, HaltReason> {
let mut node: Node = self.build_select_core(&query.core)?;

if let Some(order_by) = &query.order_by {

let mut order_key = vec![];

for key in order_by {
let (expr, _) = self.build_expr(&key.expr, false, true)?;
order_key.push((expr, key.ordering.clone()));
}

node = Node::Order {
source: Box::new(node),
key: order_by
.iter()
.map(|x| (*x.expr.clone(), x.ordering.clone()))
.collect(),
key: order_key
};
}

Expand Down Expand Up @@ -169,12 +210,16 @@ impl<'a> Planner<'a> {
join_type,
right,
constraint,
} => Ok(Node::Join {
left: Box::new(self.build_from(left, &mut scope)?),
join_type: join_type.clone(),
right: Box::new(self.build_from(right, &mut scope)?),
constraint: constraint.clone().map(|x| *x.clone()),
}),
} => {
let constraint = constraint.as_ref().map(|x| self.build_expr(x, false, false)).transpose()?;

Ok(Node::Join {
left: Box::new(self.build_from(left, &mut scope)?),
join_type: join_type.clone(),
right: Box::new(self.build_from(right, &mut scope)?),
constraint: constraint.map(|x| x.0),
})
},
};

if let Err(err) = parent_scope.merge(scope) {
Expand Down
19 changes: 19 additions & 0 deletions lykiadb-server/tests/planner/filter
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,22 @@ SELECT * FROM books b where title like '%hello%';

- filter [(title Like Str("%hello%"))]
- scan [books as b]


#[name=with_subquery, run=plan]>

SELECT * FROM books b
where author_id in (SELECT id FROM authors where name = 'John')
or publisher_id in (SELECT id FROM publishers where name = 'Elsevier');

---

- filter [(author_id In (<SqlSelect>)) Or (publisher_id In (<SqlSelect>))]
> subqueries
- project [id as id]
- filter [(name IsEqual Str("John"))]
- scan [authors as authors]
- project [id as id]
- filter [(name IsEqual Str("Elsevier"))]
- scan [publishers as publishers]
- scan [books as b]

0 comments on commit 037480d

Please sign in to comment.