diff --git a/crates/physical-plan/src/plan.rs b/crates/physical-plan/src/plan.rs index 09afe6de0aa..26ee598edf1 100644 --- a/crates/physical-plan/src/plan.rs +++ b/crates/physical-plan/src/plan.rs @@ -1471,6 +1471,9 @@ Seq Scan on t ], ); + // TODO: The `Index Cond` should be `y, z` ie: match the index columns + // This is not to be fixed on the printer side, but on the planner side + // Select index on [y, z] check_query( &db, @@ -1715,4 +1718,75 @@ Index Join: Rhs on p Output: p.id, p.name"#]], ); } + + #[test] + fn insert() { + let db = data(); + + check_query( + &db, + "INSERT INTO p (id, name) VALUES (1, 'foo')", + expect![[r#" +Insert on p + Output: void"#]], + ); + } + + #[test] + fn update() { + let db = data().with_options(ExplainOptions::default().optimize(true)); + + check_query( + &db, + "UPDATE p SET name = 'bar'", + expect![[r#" +Update on p SET (p.name = String("bar")) + -> Seq Scan on p + Output: void"#]], + ); + + check_query( + &db, + "UPDATE p SET name = 'bar' WHERE id = 1", + expect![[r#" +Update on p SET (p.name = String("bar")) + -> Index Scan using Index id 0 on p + Index Cond: (p.id = U64(1)) + Output: void"#]], + ); + + check_query( + &db, + "UPDATE p SET id = 2 WHERE name = 'bar'", + expect![[r#" +Update on p SET (p.id = U64(2)) + -> Seq Scan on p + Filter: (p.name = String("bar")) + Output: void"#]], + ); + } + + #[test] + fn delete() { + let db = data(); + + check_query( + &db, + "DELETE FROM p", + expect![[r#" +Delete on p + -> Seq Scan on p + Output: void"#]], + ); + + check_query( + &db, + "DELETE FROM p WHERE id = 1", + expect![[r#" +Delete on p + -> Seq Scan on p + Filter: (p.id = U64(1)) + Output: void"#]], + ); + } } diff --git a/crates/physical-plan/src/printer.rs b/crates/physical-plan/src/printer.rs index 2275f424700..c201d795b1d 100644 --- a/crates/physical-plan/src/printer.rs +++ b/crates/physical-plan/src/printer.rs @@ -1,3 +1,4 @@ +use crate::dml::MutationPlan; use crate::plan::{IxScan, Label, PhysicalExpr, PhysicalPlan, ProjectListPlan, ProjectPlan, Sarg, Semi, TupleField}; use crate::{PhysicalCtx, PlanCtx}; use itertools::Itertools; @@ -235,6 +236,19 @@ pub enum Line<'a> { rhs: Field<'a>, ident: u16, }, + Insert { + table_name: &'a str, + ident: u16, + }, + Update { + table_name: &'a str, + columns: Vec<(Field<'a>, &'a AlgebraicValue)>, + ident: u16, + }, + Delete { + table_name: &'a str, + ident: u16, + }, } impl Line<'_> { @@ -248,6 +262,9 @@ impl Line<'_> { Line::HashJoin { ident, .. } => *ident, Line::NlJoin { ident, .. } => *ident, Line::JoinExpr { ident, .. } => *ident, + Line::Insert { ident, .. } => *ident, + Line::Update { ident, .. } => *ident, + Line::Delete { ident, .. } => *ident, }; ident as usize } @@ -265,6 +282,7 @@ enum Output<'a> { Unknown, Star(Vec>), Fields(Vec>), + Empty, } impl<'a> Output<'a> { @@ -283,6 +301,22 @@ impl<'a> Output<'a> { }) .collect() } + + fn fields_update( + schema: &'a TableSchema, + fields: &'a [(ColId, AlgebraicValue)], + ) -> Vec<(Field<'a>, &'a AlgebraicValue)> { + fields + .iter() + .map(|(col, value)| { + let field = Field { + table: schema.table_name.as_ref(), + field: &schema.get_column(col.idx()).unwrap().col_name, + }; + (field, value) + }) + .collect() + } } /// A list of lines to print @@ -430,6 +464,39 @@ fn eval_plan<'a>(lines: &mut Lines<'a>, plan: &'a PhysicalPlan, ident: u16) { } } +fn eval_dml_plan<'a>(lines: &mut Lines<'a>, plan: &'a MutationPlan, ident: u16) { + match plan { + MutationPlan::Insert(plan) => { + let schema = &plan.table; + + lines.add(Line::Insert { + table_name: &schema.table_name, + ident, + }); + } + + MutationPlan::Delete(plan) => { + let schema = &plan.table; + + lines.add(Line::Delete { + table_name: &schema.table_name, + ident, + }); + eval_plan(lines, &plan.filter, ident + 2); + } + MutationPlan::Update(plan) => { + let schema = &plan.table; + + lines.add(Line::Update { + table_name: &schema.table_name, + columns: Output::fields_update(schema, &plan.columns), + ident, + }); + eval_plan(lines, &plan.filter, ident + 2); + } + } + lines.output = Output::Empty; +} /// A pretty printer for physical plans /// /// The printer will format the plan in a human-readable format, suitable for the `EXPLAIN` command. @@ -483,8 +550,8 @@ impl<'a> Explain<'a> { lines.output = Output::Fields(Output::tuples(fields, &lines)); } }, - PlanCtx::DML(_plan) => { - todo!() + PlanCtx::DML(plan) => { + eval_dml_plan(&mut lines, plan, 0); } } @@ -667,6 +734,23 @@ impl fmt::Display for Explain<'_> { writeln!(f, "{:ident$}Inner Unique: {unique}", "")?; write!(f, "{:ident$}Join Cond: ({} = {})", "", lhs, rhs)?; } + Line::Insert { table_name, ident: _ } => { + write!(f, "{:ident$}{arrow}Insert on {table_name}", "")?; + } + Line::Update { + table_name, + columns, + ident: _, + } => { + let columns = columns + .iter() + .map(|(field, value)| format!("{} = {:?}", field, value)) + .join(", "); + write!(f, "{:ident$}{arrow}Update on {table_name} SET ({columns })", "")?; + } + Line::Delete { table_name, ident: _ } => { + write!(f, "{:ident$}{arrow}Delete on {table_name}", "")?; + } } writeln!(f)?; } @@ -681,6 +765,7 @@ impl fmt::Display for Explain<'_> { let columns = fields.iter().map(|x| format!("{}", x)).join(", "); Some(columns) } + Output::Empty => Some("void".to_string()), }; let end = if self.options.show_timings || self.options.show_schema { "\n"