use sqlparser::ast::Ident;
use super::logical_plan::{dml_roots, LogicalPlan, MergeClause};
use super::origins::output_operands;
use crate::reference::{ColumnReference, ColumnWrite, ResolutionKind, TableReference, TableWrite};
pub(super) fn collect_writes(plan: &LogicalPlan) -> Vec<ColumnWrite> {
dml_roots(plan)
.into_iter()
.flat_map(write_root_columns)
.collect()
}
fn write_root_columns(root: &LogicalPlan) -> Vec<ColumnWrite> {
match root {
LogicalPlan::Insert(i) => {
let mut w = i.columns.clone();
w.extend(i.on_conflict.iter().map(|a| a.target.clone()));
w
}
LogicalPlan::Update(u) => u.assignments.iter().map(|a| a.target.clone()).collect(),
LogicalPlan::CreateTableAs(c) => {
created_relation_writes(&c.columns, &c.input, &c.target.reference)
}
LogicalPlan::CreateView(c) => {
created_relation_writes(&c.columns, &c.input, &c.target.reference)
}
LogicalPlan::AlterTable(a) => inferred_writes(&a.columns, &a.target.reference),
LogicalPlan::Merge(m) => m.clauses.iter().flat_map(merge_clause_writes).collect(),
LogicalPlan::Scan(_)
| LogicalPlan::Filter(_)
| LogicalPlan::Join(_)
| LogicalPlan::Aggregate(_)
| LogicalPlan::Projection(_)
| LogicalPlan::Sort(_)
| LogicalPlan::SetOp(_)
| LogicalPlan::SubqueryAlias(_)
| LogicalPlan::TableFunction(_)
| LogicalPlan::With(_)
| LogicalPlan::CteRef(_)
| LogicalPlan::Values(_)
| LogicalPlan::Empty
| LogicalPlan::Delete(_)
| LogicalPlan::Drop(_) => Vec::new(),
}
}
fn merge_clause_writes(clause: &MergeClause) -> Vec<ColumnWrite> {
match clause {
MergeClause::Update { assignments } => {
assignments.iter().map(|a| a.target.clone()).collect()
}
MergeClause::Insert { columns, .. } => columns.clone(),
MergeClause::Delete => Vec::new(),
}
}
pub(super) fn collect_table_writes(plan: &LogicalPlan) -> Vec<TableWrite> {
dml_roots(plan)
.into_iter()
.flat_map(write_root_tables)
.collect()
}
pub(super) fn write_root_tables(root: &LogicalPlan) -> Vec<TableWrite> {
match root {
LogicalPlan::Insert(i) => vec![i.target.clone()],
LogicalPlan::Update(u) => {
let mut out: Vec<TableWrite> = Vec::new();
for a in &u.assignments {
if let Some(table) = &a.target.reference.table {
if out.iter().any(|w| &w.reference == table) {
continue;
}
out.push(TableWrite {
reference: table.clone(),
resolution: a.target_resolution,
});
}
}
out
}
LogicalPlan::Delete(d) => d.targets.clone(),
LogicalPlan::CreateTableAs(c) => vec![c.target.clone()],
LogicalPlan::CreateView(c) => vec![c.target.clone()],
LogicalPlan::AlterTable(a) => vec![a.target.clone()],
LogicalPlan::Merge(m) => vec![m.target.clone()],
LogicalPlan::Drop(d) => d.targets.clone(),
LogicalPlan::Scan(_)
| LogicalPlan::Filter(_)
| LogicalPlan::Join(_)
| LogicalPlan::Aggregate(_)
| LogicalPlan::Projection(_)
| LogicalPlan::Sort(_)
| LogicalPlan::SetOp(_)
| LogicalPlan::SubqueryAlias(_)
| LogicalPlan::TableFunction(_)
| LogicalPlan::With(_)
| LogicalPlan::CteRef(_)
| LogicalPlan::Values(_)
| LogicalPlan::Empty => Vec::new(),
}
}
fn inferred_writes(columns: &[Ident], target: &TableReference) -> Vec<ColumnWrite> {
columns
.iter()
.map(|name| ColumnWrite {
reference: ColumnReference {
table: Some(target.clone()),
name: name.clone(),
},
resolution: ResolutionKind::Inferred,
})
.collect()
}
fn created_relation_writes(
explicit: &[Ident],
input: &LogicalPlan,
target: &TableReference,
) -> Vec<ColumnWrite> {
if !explicit.is_empty() {
return inferred_writes(explicit, target);
}
let names: Vec<Ident> = match output_operands(input).first() {
Some(operand) => operand
.outputs
.iter()
.filter_map(|ne| ne.name.clone())
.collect(),
None => Vec::new(),
};
inferred_writes(&names, target)
}