use sqlparser::ast::{self, Expr};
use crate::error::Result;
use crate::functions::registry::FunctionRegistry;
use crate::resolver::columns::TableScope;
use crate::types::*;
pub struct SubqueryExtraction {
pub joins: Vec<SubqueryJoin>,
pub remaining_where: Option<Expr>,
}
pub struct SubqueryJoin {
pub on: Vec<(String, String)>,
pub inner_plan: SqlPlan,
pub join_type: JoinType,
}
pub fn extract_subqueries(
expr: &Expr,
outer: &TableScope,
catalog: &dyn SqlCatalog,
functions: &FunctionRegistry,
temporal: crate::TemporalScope,
) -> Result<SubqueryExtraction> {
let mut joins = Vec::new();
let remaining = extract_recursive(expr, &mut joins, outer, catalog, functions, temporal)?;
Ok(SubqueryExtraction {
joins,
remaining_where: remaining,
})
}
fn extract_recursive(
expr: &Expr,
joins: &mut Vec<SubqueryJoin>,
outer: &TableScope,
catalog: &dyn SqlCatalog,
functions: &FunctionRegistry,
temporal: crate::TemporalScope,
) -> Result<Option<Expr>> {
match expr {
Expr::BinaryOp {
left,
op: ast::BinaryOperator::And,
right,
} => {
let left_remaining =
extract_recursive(left, joins, outer, catalog, functions, temporal)?;
let right_remaining =
extract_recursive(right, joins, outer, catalog, functions, temporal)?;
match (left_remaining, right_remaining) {
(None, None) => Ok(None),
(Some(l), None) => Ok(Some(l)),
(None, Some(r)) => Ok(Some(r)),
(Some(l), Some(r)) => Ok(Some(Expr::BinaryOp {
left: Box::new(l),
op: ast::BinaryOperator::And,
right: Box::new(r),
})),
}
}
Expr::InSubquery {
expr: outer_expr,
subquery,
negated,
} => {
if let Some(join) = super::in_list::try_plan_in_subquery(
outer_expr, subquery, *negated, outer, catalog, functions, temporal,
)? {
joins.push(join);
Ok(None) } else {
Ok(Some(expr.clone()))
}
}
Expr::BinaryOp { left, op, right } if is_comparison_op(op) => {
if let Expr::Subquery(subquery) = right.as_ref() {
if let Some(scalar) =
super::scalar::try_plan_scalar_subquery(subquery, catalog, functions, temporal)?
{
joins.push(scalar.join);
Ok(Some(Expr::BinaryOp {
left: left.clone(),
op: op.clone(),
right: Box::new(scalar.replacement_expr),
}))
} else {
Ok(Some(expr.clone()))
}
} else {
Ok(Some(expr.clone()))
}
}
Expr::Exists { subquery, negated } => {
joins.push(super::exists::plan_exists_subquery(
subquery, *negated, outer, catalog, functions, temporal,
)?);
Ok(None)
}
Expr::Nested(inner) => extract_recursive(inner, joins, outer, catalog, functions, temporal),
_ => Ok(Some(expr.clone())),
}
}
fn is_comparison_op(op: &ast::BinaryOperator) -> bool {
matches!(
op,
ast::BinaryOperator::Gt
| ast::BinaryOperator::GtEq
| ast::BinaryOperator::Lt
| ast::BinaryOperator::LtEq
| ast::BinaryOperator::Eq
| ast::BinaryOperator::NotEq
)
}