use crate::ast::{BinaryOp, Expr};
use crate::catalog::Catalog;
use crate::error::DbError;
use crate::storage::Storage;
use crate::types::Value;
use super::clock::Clock;
use super::context::ExecutionContext;
use super::evaluator::eval_expr;
use super::model::ContextTable;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum JoinSide {
Left,
Right,
Both,
None,
}
fn combine_sides(s1: JoinSide, s2: JoinSide) -> JoinSide {
match (s1, s2) {
(JoinSide::None, other) | (other, JoinSide::None) => other,
(JoinSide::Left, JoinSide::Left) => JoinSide::Left,
(JoinSide::Right, JoinSide::Right) => JoinSide::Right,
_ => JoinSide::Both,
}
}
fn get_expr_side(
expr: &Expr,
left_shape: &[ContextTable],
right_shape: &[ContextTable],
) -> JoinSide {
match expr {
Expr::Identifier(name) => {
let in_left = left_shape.iter().any(|b| {
b.table
.columns
.iter()
.any(|c| c.name.eq_ignore_ascii_case(name))
});
let in_right = right_shape.iter().any(|b| {
b.table
.columns
.iter()
.any(|c| c.name.eq_ignore_ascii_case(name))
});
match (in_left, in_right) {
(true, false) => JoinSide::Left,
(false, true) => JoinSide::Right,
(true, true) => JoinSide::Both,
(false, false) => JoinSide::None,
}
}
Expr::QualifiedIdentifier(parts) => {
if parts.len() != 2 {
return JoinSide::None;
}
let table_name = &parts[0];
let in_left = left_shape.iter().any(|b| {
b.alias.eq_ignore_ascii_case(table_name)
|| b.table.name.eq_ignore_ascii_case(table_name)
});
let in_right = right_shape.iter().any(|b| {
b.alias.eq_ignore_ascii_case(table_name)
|| b.table.name.eq_ignore_ascii_case(table_name)
});
match (in_left, in_right) {
(true, false) => JoinSide::Left,
(false, true) => JoinSide::Right,
(true, true) => JoinSide::Both,
(false, false) => JoinSide::None,
}
}
Expr::Binary { left, right, .. } => {
let l_side = get_expr_side(left, left_shape, right_shape);
let r_side = get_expr_side(right, left_shape, right_shape);
combine_sides(l_side, r_side)
}
Expr::Unary { expr, .. } => get_expr_side(expr, left_shape, right_shape),
Expr::Cast { expr, .. }
| Expr::TryCast { expr, .. }
| Expr::Convert { expr, .. }
| Expr::TryConvert { expr, .. } => get_expr_side(expr, left_shape, right_shape),
Expr::FunctionCall { args, .. } => {
let mut side = JoinSide::None;
for arg in args {
side = combine_sides(side, get_expr_side(arg, left_shape, right_shape));
}
side
}
_ => JoinSide::None,
}
}
pub(crate) fn find_equi_join_conditions(
on: &Expr,
left_shape: &[ContextTable],
right_shape: &[ContextTable],
) -> Option<(Vec<Expr>, Vec<Expr>)> {
match on {
Expr::Binary {
left,
op: BinaryOp::And,
right,
} => {
let (mut l1, mut r1) = find_equi_join_conditions(left, left_shape, right_shape)?;
let (l2, r2) = find_equi_join_conditions(right, left_shape, right_shape)?;
l1.extend(l2);
r1.extend(r2);
Some((l1, r1))
}
Expr::Binary {
left,
op: BinaryOp::Eq,
right,
} => {
let l_side = get_expr_side(left, left_shape, right_shape);
let r_side = get_expr_side(right, left_shape, right_shape);
match (l_side, r_side) {
(JoinSide::Left, JoinSide::Right) => {
Some((vec![(**left).clone()], vec![(**right).clone()]))
}
(JoinSide::Right, JoinSide::Left) => {
Some((vec![(**right).clone()], vec![(**left).clone()]))
}
_ => None,
}
}
_ => None,
}
}
pub(crate) fn eval_key(
exprs: &[Expr],
row: &[ContextTable],
ctx: &mut ExecutionContext,
catalog: &dyn Catalog,
storage: &dyn Storage,
clock: &dyn Clock,
) -> Result<Vec<Value>, DbError> {
let mut key = Vec::with_capacity(exprs.len());
for expr in exprs {
key.push(eval_expr(expr, row, ctx, catalog, storage, clock)?);
}
Ok(key)
}