use sqlparser::ast::Ident;
use crate::reference::{ColumnWrite, ResolutionKind, TableRead, TableReference, TableWrite};
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) enum LogicalPlan {
Scan(Scan),
Filter(Filter),
Join(Join),
Aggregate(Aggregate),
Projection(Projection),
Sort(Sort),
SetOp(SetOp),
SubqueryAlias(SubqueryAlias),
TableFunction(TableFunction),
With(With),
CteRef(CteRef),
Values(Values),
Empty,
Insert(Insert),
Update(Update),
Delete(Delete),
Merge(Merge),
CreateTableAs(CreateTableAs),
CreateView(CreateView),
AlterTable(AlterTable),
Drop(Drop),
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct Scan {
pub(crate) table: TableReference,
pub(crate) resolution: ResolutionKind,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) enum Columns {
Cataloged(Vec<Ident>),
Unknown,
}
impl Columns {
pub(crate) fn from_catalog(columns: Vec<Ident>) -> Columns {
if columns.is_empty() {
Columns::Unknown
} else {
Columns::Cataloged(columns)
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct Filter {
pub(crate) input: Box<LogicalPlan>,
pub(crate) predicate: Vec<Expr>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct Join {
pub(crate) left: Box<LogicalPlan>,
pub(crate) right: Box<LogicalPlan>,
pub(crate) on: Vec<Expr>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct Aggregate {
pub(crate) input: Box<LogicalPlan>,
pub(crate) group_by: Vec<Expr>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct Projection {
pub(crate) input: Box<LogicalPlan>,
pub(crate) exprs: Vec<NamedExpr>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct Sort {
pub(crate) input: Box<LogicalPlan>,
pub(crate) keys: Vec<Expr>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct SetOp {
pub(crate) left: Box<LogicalPlan>,
pub(crate) right: Box<LogicalPlan>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct SubqueryAlias {
pub(crate) alias: Ident,
pub(crate) input: Box<LogicalPlan>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct TableFunction {
pub(crate) alias: Option<Ident>,
pub(crate) input: Box<LogicalPlan>,
pub(crate) args: Vec<Expr>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct With {
pub(crate) ctes: Vec<Cte>,
pub(crate) body: Box<LogicalPlan>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct Cte {
pub(crate) name: Ident,
pub(crate) body: LogicalPlan,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct CteRef {
pub(crate) name: Ident,
pub(crate) alias: Option<Ident>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct Values {
pub(crate) rows: Vec<Vec<Expr>>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct Insert {
pub(crate) target: TableWrite,
pub(crate) columns: Vec<ColumnWrite>,
pub(crate) input: Box<LogicalPlan>,
pub(crate) returning: Vec<NamedExpr>,
pub(crate) on_conflict: Vec<Assignment>,
pub(crate) conflict_predicate: Vec<Expr>,
pub(crate) target_predicate: Vec<Expr>,
pub(crate) target_context: Box<LogicalPlan>,
pub(crate) source_wildcard: bool,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct Update {
pub(crate) target: TableReference,
pub(crate) assignments: Vec<Assignment>,
pub(crate) input: Box<LogicalPlan>,
pub(crate) returning: Vec<NamedExpr>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct Delete {
pub(crate) targets: Vec<TableWrite>,
pub(crate) input: Box<LogicalPlan>,
pub(crate) returning: Vec<NamedExpr>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct Merge {
pub(crate) target: TableWrite,
pub(crate) source: Box<LogicalPlan>,
pub(crate) on: Vec<Expr>,
pub(crate) clauses: Vec<MergeClause>,
pub(crate) returning: Vec<NamedExpr>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) enum MergeClause {
Update { assignments: Vec<Assignment> },
Insert {
columns: Vec<ColumnWrite>,
values: Vec<Expr>,
},
Delete,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct CreateTableAs {
pub(crate) target: TableWrite,
pub(crate) columns: Vec<Ident>,
pub(crate) input: Box<LogicalPlan>,
pub(crate) schema_source: Option<SchemaSource>,
pub(crate) source_wildcard: bool,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct SchemaSource {
pub(crate) source: TableRead,
pub(crate) copies_data: bool,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct CreateView {
pub(crate) target: TableWrite,
pub(crate) columns: Vec<Ident>,
pub(crate) input: Box<LogicalPlan>,
pub(crate) source_wildcard: bool,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct AlterTable {
pub(crate) target: TableWrite,
pub(crate) columns: Vec<Ident>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct Drop {
pub(crate) targets: Vec<TableWrite>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) enum Expr {
Column(Box<BoundColumn>),
Call { args: Vec<Expr> },
Case {
when: Vec<Expr>,
then: Vec<Expr>,
else_result: Option<Box<Expr>>,
},
Window {
arg: Box<Expr>,
partition: Vec<Expr>,
order: Vec<Expr>,
},
Subquery {
plan: Box<LogicalPlan>,
output: usize,
},
Exists(Box<LogicalPlan>),
InSubquery {
expr: Box<Expr>,
subquery: Box<LogicalPlan>,
},
Filter(Vec<Expr>),
Fanin(Vec<BoundColumn>),
}
impl Expr {
pub(super) fn value_operands(&self) -> Vec<&Expr> {
match self {
Expr::Call { args } => args.iter().collect(),
Expr::Case {
then, else_result, ..
} => {
let mut v: Vec<&Expr> = then.iter().collect();
if let Some(e) = else_result {
v.push(e);
}
v
}
Expr::Window { arg, .. } => vec![arg],
Expr::InSubquery { expr, .. } => vec![expr],
Expr::Column(_)
| Expr::Subquery { .. }
| Expr::Exists(_)
| Expr::Filter(_)
| Expr::Fanin(_) => Vec::new(),
}
}
pub(super) fn filter_operands(&self) -> Vec<&Expr> {
match self {
Expr::Case { when, .. } => when.iter().collect(),
Expr::Window {
partition, order, ..
} => partition.iter().chain(order).collect(),
Expr::Filter(exprs) => exprs.iter().collect(),
Expr::Column(_)
| Expr::Call { .. }
| Expr::Subquery { .. }
| Expr::Exists(_)
| Expr::InSubquery { .. }
| Expr::Fanin(_) => Vec::new(),
}
}
pub(super) fn value_subplans(&self) -> Vec<&LogicalPlan> {
match self {
Expr::Subquery { plan, output: 0 } => vec![plan],
Expr::Subquery { .. }
| Expr::Column(_)
| Expr::Call { .. }
| Expr::Case { .. }
| Expr::Window { .. }
| Expr::Exists(_)
| Expr::InSubquery { .. }
| Expr::Filter(_)
| Expr::Fanin(_) => Vec::new(),
}
}
pub(super) fn filter_subplans(&self) -> Vec<&LogicalPlan> {
match self {
Expr::Exists(plan) => vec![plan],
Expr::InSubquery { subquery, .. } => vec![subquery],
Expr::Column(_)
| Expr::Call { .. }
| Expr::Case { .. }
| Expr::Window { .. }
| Expr::Subquery { .. }
| Expr::Filter(_)
| Expr::Fanin(_) => Vec::new(),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct NamedExpr {
pub(crate) names: OutputNames,
pub(crate) expr: Expr,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) enum OutputNames {
Single(Option<Ident>),
Fan(Vec<Ident>),
}
impl OutputNames {
pub(crate) fn count(&self) -> usize {
match self {
OutputNames::Single(_) => 1,
OutputNames::Fan(names) => names.len(),
}
}
pub(crate) fn get(&self, j: usize) -> Option<Option<&Ident>> {
match self {
OutputNames::Single(name) => (j == 0).then_some(name.as_ref()),
OutputNames::Fan(names) => names.get(j).map(Some),
}
}
}
pub(super) fn slot_count(outputs: &[NamedExpr]) -> usize {
outputs.iter().map(|ne| ne.names.count()).sum()
}
pub(super) fn output_slots(outputs: &[NamedExpr]) -> impl Iterator<Item = (Option<&Ident>, &Expr)> {
outputs
.iter()
.flat_map(|ne| (0..ne.names.count()).map(|j| (ne.names.get(j).flatten(), &ne.expr)))
}
pub(super) fn resolved_output_expr(outputs: &[NamedExpr], i: usize) -> Option<&Expr> {
output_slots(outputs).nth(i).map(|(_, e)| e)
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct Assignment {
pub(crate) target: ColumnWrite,
pub(crate) target_resolution: ResolutionKind,
pub(crate) value: Expr,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct BoundColumn {
pub(crate) qualifier: Option<Ident>,
pub(crate) name: Ident,
pub(crate) binding: Binding,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) enum Binding {
Base {
table: TableReference,
resolution: ResolutionKind,
},
Derived,
Unresolved,
Ambiguous,
Local,
}
pub(super) fn own_exprs(op: &LogicalPlan) -> Vec<&Expr> {
match op {
LogicalPlan::Filter(f) => f.predicate.iter().collect(),
LogicalPlan::Join(j) => j.on.iter().collect(),
LogicalPlan::Projection(p) => p.exprs.iter().map(|ne| &ne.expr).collect(),
LogicalPlan::Aggregate(a) => a.group_by.iter().collect(),
LogicalPlan::Sort(s) => s.keys.iter().collect(),
LogicalPlan::TableFunction(tf) => tf.args.iter().collect(),
LogicalPlan::Values(v) => v.rows.iter().flatten().collect(),
LogicalPlan::Insert(i) => i
.returning
.iter()
.map(|ne| &ne.expr)
.chain(i.on_conflict.iter().map(|a| &a.value))
.chain(i.conflict_predicate.iter())
.chain(i.target_predicate.iter())
.collect(),
LogicalPlan::Update(u) => u
.assignments
.iter()
.map(|a| &a.value)
.chain(u.returning.iter().map(|ne| &ne.expr))
.collect(),
LogicalPlan::Delete(d) => d.returning.iter().map(|ne| &ne.expr).collect(),
LogicalPlan::Merge(m) => {
let mut exprs: Vec<&Expr> = m.on.iter().collect();
for clause in &m.clauses {
match clause {
MergeClause::Update { assignments } => {
exprs.extend(assignments.iter().map(|a| &a.value));
}
MergeClause::Insert { values, .. } => exprs.extend(values.iter()),
MergeClause::Delete => {}
}
}
exprs.extend(m.returning.iter().map(|ne| &ne.expr));
exprs
}
LogicalPlan::Scan(_)
| LogicalPlan::SubqueryAlias(_)
| LogicalPlan::SetOp(_)
| LogicalPlan::With(_)
| LogicalPlan::CteRef(_)
| LogicalPlan::Empty
| LogicalPlan::CreateTableAs(_)
| LogicalPlan::CreateView(_)
| LogicalPlan::AlterTable(_)
| LogicalPlan::Drop(_) => Vec::new(),
}
}
pub(super) fn children(op: &LogicalPlan) -> Vec<&LogicalPlan> {
match op {
LogicalPlan::Filter(f) => vec![&f.input],
LogicalPlan::Join(j) => vec![&j.left, &j.right],
LogicalPlan::Aggregate(a) => vec![&a.input],
LogicalPlan::Projection(p) => vec![&p.input],
LogicalPlan::Sort(s) => vec![&s.input],
LogicalPlan::SetOp(so) => vec![&so.left, &so.right],
LogicalPlan::SubqueryAlias(sa) => vec![&sa.input],
LogicalPlan::TableFunction(tf) => vec![&tf.input],
LogicalPlan::With(w) => vec![&w.body],
LogicalPlan::Insert(i) => vec![&i.input, &i.target_context],
LogicalPlan::Update(u) => vec![&u.input],
LogicalPlan::Delete(d) => vec![&d.input],
LogicalPlan::Merge(m) => vec![&m.source],
LogicalPlan::CreateTableAs(c) => vec![&c.input],
LogicalPlan::CreateView(c) => vec![&c.input],
LogicalPlan::Scan(_)
| LogicalPlan::CteRef(_)
| LogicalPlan::Values(_)
| LogicalPlan::Empty
| LogicalPlan::AlterTable(_)
| LogicalPlan::Drop(_) => Vec::new(),
}
}
pub(super) fn own_expr_subplans(op: &LogicalPlan) -> Vec<&LogicalPlan> {
let mut out = Vec::new();
for expr in own_exprs(op) {
collect_subplans(expr, &mut out);
}
out
}
fn collect_subplans<'a>(expr: &'a Expr, out: &mut Vec<&'a LogicalPlan>) {
out.extend(expr.value_subplans());
out.extend(expr.filter_subplans());
for child in expr
.value_operands()
.into_iter()
.chain(expr.filter_operands())
{
collect_subplans(child, out);
}
}
pub(super) fn peel_with(op: &LogicalPlan) -> &LogicalPlan {
let mut node = op;
while let LogicalPlan::With(w) = node {
node = &w.body;
}
node
}
pub(super) fn dml_roots(op: &LogicalPlan) -> Vec<&LogicalPlan> {
let mut out = Vec::new();
collect_dml_roots(op, &mut out);
out
}
pub(super) fn is_dml_root(op: &LogicalPlan) -> bool {
matches!(
op,
LogicalPlan::Insert(_)
| LogicalPlan::Update(_)
| LogicalPlan::Delete(_)
| LogicalPlan::Merge(_)
| LogicalPlan::CreateTableAs(_)
| LogicalPlan::CreateView(_)
| LogicalPlan::AlterTable(_)
| LogicalPlan::Drop(_)
)
}
fn collect_dml_roots<'a>(op: &'a LogicalPlan, out: &mut Vec<&'a LogicalPlan>) {
match op {
LogicalPlan::With(w) => {
for cte in &w.ctes {
collect_dml_roots(&cte.body, out);
}
collect_dml_roots(&w.body, out);
}
LogicalPlan::Insert(_)
| LogicalPlan::Update(_)
| LogicalPlan::Delete(_)
| LogicalPlan::Merge(_)
| LogicalPlan::CreateTableAs(_)
| LogicalPlan::CreateView(_)
| LogicalPlan::AlterTable(_)
| LogicalPlan::Drop(_) => out.push(op),
LogicalPlan::Scan(_)
| LogicalPlan::Filter(_)
| LogicalPlan::Join(_)
| LogicalPlan::Aggregate(_)
| LogicalPlan::Projection(_)
| LogicalPlan::Sort(_)
| LogicalPlan::SetOp(_)
| LogicalPlan::SubqueryAlias(_)
| LogicalPlan::TableFunction(_)
| LogicalPlan::CteRef(_)
| LogicalPlan::Values(_)
| LogicalPlan::Empty => {}
}
}
pub(super) fn walk_plan(op: &LogicalPlan, f: &mut impl FnMut(&LogicalPlan)) {
f(op);
for child in children(op) {
walk_plan(child, f);
}
for sub in own_expr_subplans(op) {
walk_plan(sub, f);
}
if let LogicalPlan::With(w) = op {
for cte in &w.ctes {
walk_plan(&cte.body, f);
}
}
}