diff --git a/crates/core/src/sql/ast.rs b/crates/core/src/sql/ast.rs index 4fd4fa4ab32..e365d43f8b8 100644 --- a/crates/core/src/sql/ast.rs +++ b/crates/core/src/sql/ast.rs @@ -977,7 +977,7 @@ pub(crate) fn compile_to_ast( ) -> Result, DBError> { // NOTE: The following ensures compliance with the 1.0 sql api. // Come 1.0, it will have replaced the current compilation stack. - compile_sql_stmt(sql_text, &SchemaViewer::new(tx, auth))?; + compile_sql_stmt(sql_text, &SchemaViewer::new(tx, auth), false)?; let dialect = PostgreSqlDialect {}; let ast = Parser::parse_sql(&dialect, sql_text).map_err(|error| DBError::SqlParser { diff --git a/crates/expr/src/check.rs b/crates/expr/src/check.rs index 6c38d72c4d9..034e88ee14f 100644 --- a/crates/expr/src/check.rs +++ b/crates/expr/src/check.rs @@ -165,13 +165,17 @@ pub fn type_subscription(ast: SqlSelect, tx: &impl SchemaView) -> TypingResult

(sql: &'a str, tx: &impl SchemaView) -> TypingResult> { - let planning_time = std::time::Instant::now(); +pub fn compile_sql_sub<'a>(sql: &'a str, tx: &impl SchemaView, with_timings: bool) -> TypingResult> { + let planning_time = if with_timings { + Some(std::time::Instant::now()) + } else { + None + }; Ok(StatementCtx { statement: Statement::Select(ProjectList::Name(parse_and_type_sub(sql, tx)?)), sql, source: StatementSource::Subscription, - planning_time: planning_time.elapsed(), + planning_time: planning_time.map(|t| t.elapsed()), }) } diff --git a/crates/expr/src/lib.rs b/crates/expr/src/lib.rs index b42bc52f8df..a3752354890 100644 --- a/crates/expr/src/lib.rs +++ b/crates/expr/src/lib.rs @@ -188,5 +188,5 @@ pub struct StatementCtx<'a> { pub statement: Statement, pub sql: &'a str, pub source: StatementSource, - pub planning_time: std::time::Duration, + pub planning_time: Option, } diff --git a/crates/expr/src/statement.rs b/crates/expr/src/statement.rs index c137a7f53ed..9293a2015f6 100644 --- a/crates/expr/src/statement.rs +++ b/crates/expr/src/statement.rs @@ -278,14 +278,18 @@ fn parse_and_type_sql(sql: &str, tx: &impl SchemaView) -> TypingResult(sql: &'a str, tx: &impl SchemaView) -> TypingResult> { - let planning_time = std::time::Instant::now(); +pub fn compile_sql_stmt<'a>(sql: &'a str, tx: &impl SchemaView, with_timings: bool) -> TypingResult> { + let planning_time = if with_timings { + Some(std::time::Instant::now()) + } else { + None + }; let statement = parse_and_type_sql(sql, tx)?; Ok(StatementCtx { statement, sql, source: StatementSource::Query, - planning_time: planning_time.elapsed(), + planning_time: planning_time.map(|t| t.elapsed()), }) } diff --git a/crates/physical-plan/src/plan.rs b/crates/physical-plan/src/plan.rs index 42c7b7336f7..21977363a24 100644 --- a/crates/physical-plan/src/plan.rs +++ b/crates/physical-plan/src/plan.rs @@ -965,7 +965,7 @@ pub struct PhysicalCtx<'a> { // A map from table names to their labels pub vars: HashMap, pub source: StatementSource, - pub planning_time: std::time::Duration, + pub planning_time: Option, } impl<'a> PhysicalCtx<'a> { @@ -986,12 +986,12 @@ pub mod tests_utils { use spacetimedb_expr::statement::compile_sql_stmt; fn sub<'a>(db: &'a impl SchemaView, sql: &'a str) -> PhysicalCtx<'a> { - let plan = compile_sql_sub(sql, db).unwrap(); + let plan = compile_sql_sub(sql, db, true).unwrap(); compile(plan) } fn query<'a>(db: &'a impl SchemaView, sql: &'a str) -> PhysicalCtx<'a> { - let plan = compile_sql_stmt(sql, db).unwrap(); + let plan = compile_sql_stmt(sql, db, true).unwrap(); compile(plan) } @@ -1155,10 +1155,8 @@ Seq Scan on t /// No rewrites applied to a table scan + filter #[test] fn filter_noop() { - let t_id = TableId(1); - let t = Arc::new(schema( - t_id, + TableId(1), "t", &[("id", AlgebraicType::U64), ("x", AlgebraicType::U64)], &[&[0]], @@ -1166,7 +1164,7 @@ Seq Scan on t Some(0), )); - let db = SchemaViewer::new(vec![t.clone()]); + let db = SchemaViewer::new(vec![t]); check_sub( &db, @@ -1263,15 +1261,15 @@ Seq Scan on t expect![[r#" Index Join: Rhs on b -> Index Join: Rhs on q - -> Index Join: Rhs on p - -> Index Scan using Index id 0 on u - -> Index Cond: (u.identity = U64(5)) - -> Inner Unique: true - -> Index Cond: (u.entity_id = p.entity_id) - -> Inner Unique: false - -> Index Cond: (p.chunk = q.chunk) + -> Index Join: Rhs on p + -> Index Scan using Index id 0 on u + Index Cond: (u.identity = U64(5)) + Inner Unique: true + Join Cond: (u.entity_id = p.entity_id) + Inner Unique: false + Join Cond: (p.chunk = q.chunk) Inner Unique: true - Index Cond: (q.entity_id = b.entity_id) + Join Cond: (q.entity_id = b.entity_id) Output: b.entity_id, b.misc"#]], ); @@ -1472,21 +1470,21 @@ Index Join: Rhs on b expect![[r#" Hash Join -> Hash Join + -> Hash Join -> Hash Join - -> Hash Join - -> Seq Scan on m - -> Seq Scan on n - -> Inner Unique: false - -> Hash Cond: (m.manager = n.manager) - -> Seq Scan on u - -> Inner Unique: false - -> Hash Cond: (n.employee = u.employee) - -> Seq Scan on v - -> Inner Unique: false - -> Hash Cond: (u.project = v.project) + -> Seq Scan on m + -> Seq Scan on n + Inner Unique: false + Join Cond: (m.manager = n.manager) + -> Seq Scan on u + Inner Unique: false + Join Cond: (n.employee = u.employee) + -> Seq Scan on v + Inner Unique: false + Join Cond: (u.project = v.project) -> Seq Scan on p Inner Unique: false - Hash Cond: (p.id = v.project) + Join Cond: (p.id = v.project) Filter: (m.employee = U64(5) AND v.employee = U64(5)) Output: p.id, p.name"#]], ); @@ -1668,7 +1666,7 @@ Hash Join expect![ r#" Index Scan using Index id 2 on t - Index Cond: (t.z = U8(5), t.x = U8(3), t.y = U8(4)) + Index Cond: (t.z = U8(5), t.x = U8(3), t.y = U8(4)) Output: t.w, t.x, t.y, t.z"# ], ); @@ -1819,7 +1817,7 @@ Index Scan using Index id 2 on t Query: SELECT m.* FROM m CROSS JOIN p WHERE m.employee = 1 Nested Loop -> Index Scan using Index id 0 on m - -> Index Cond: (m.employee = U64(1)) + Index Cond: (m.employee = U64(1)) -> Seq Scan on p:2 Output: m.employee, m.manager ------- @@ -1927,7 +1925,7 @@ Label: p, TableId:3 "SELECT m.* FROM m WHERE employee = 1", expect![[r#" Index Scan using Index id 0 on m - Index Cond: (m.employee = U64(1)) + Index Cond: (m.employee = U64(1)) Output: m.employee, m.manager"#]], ); } @@ -1959,7 +1957,7 @@ Index Scan using Index id 0 on m -> Seq Scan on m -> Seq Scan on p Inner Unique: false - Hash Cond: (m.employee = p.id) + Join Cond: (m.employee = p.id) Filter: (m.employee = U64(1)) Output: p.id, p.name"#]], ); @@ -1976,7 +1974,7 @@ Index Scan using Index id 0 on m Index Join: Rhs on p -> Seq Scan on m Inner Unique: true - Index Cond: (m.employee = p.id) + Join Cond: (m.employee = p.id) Output: p.id, p.name"#]], ); } diff --git a/crates/physical-plan/src/printer.rs b/crates/physical-plan/src/printer.rs index 380c5791905..601528b16d3 100644 --- a/crates/physical-plan/src/printer.rs +++ b/crates/physical-plan/src/printer.rs @@ -198,17 +198,11 @@ impl<'a> PrintIndex<'a> { } } -pub enum JoinKind { - IxJoin, - HashJoin, - NlJoin, -} - /// A formated line of output pub enum Line<'a> { TableScan { table: &'a str, - label: Label, + label: usize, ident: u16, }, Filter { @@ -227,7 +221,6 @@ pub enum Line<'a> { }, IxJoin { semi: &'a Semi, - rhs: String, ident: u16, }, @@ -239,7 +232,6 @@ pub enum Line<'a> { ident: u16, }, JoinExpr { - kind: JoinKind, unique: bool, lhs: Field<'a>, rhs: Field<'a>, @@ -365,7 +357,7 @@ fn eval_plan<'a>(lines: &mut Lines<'a>, plan: &'a PhysicalPlan, ident: u16) { lines.add(Line::TableScan { table: schema.name, - label: *label, + label: label.0, ident, }); } @@ -385,7 +377,7 @@ fn eval_plan<'a>(lines: &mut Lines<'a>, plan: &'a PhysicalPlan, ident: u16) { lines.add(Line::FilterIxScan { idx, label: *label, - ident: ident + 2, + ident: ident + 4, }); } PhysicalPlan::IxJoin(idx, semi) => { @@ -398,7 +390,7 @@ fn eval_plan<'a>(lines: &mut Lines<'a>, plan: &'a PhysicalPlan, ident: u16) { rhs: rhs.name.to_string(), }); - eval_plan(lines, &idx.lhs, ident + 4); + eval_plan(lines, &idx.lhs, ident + 2); lines.output = output_join(lines, semi, idx.lhs_field.label, idx.rhs_label); @@ -406,7 +398,6 @@ fn eval_plan<'a>(lines: &mut Lines<'a>, plan: &'a PhysicalPlan, ident: u16) { let rhs = lines.labels.label(&idx.rhs_label, idx.rhs_field).unwrap(); lines.add(Line::JoinExpr { - kind: JoinKind::IxJoin, unique: idx.unique, lhs, rhs, @@ -416,8 +407,8 @@ fn eval_plan<'a>(lines: &mut Lines<'a>, plan: &'a PhysicalPlan, ident: u16) { PhysicalPlan::HashJoin(idx, semi) => { lines.add(Line::HashJoin { semi, ident }); - eval_plan(lines, &idx.lhs, ident + 4); - eval_plan(lines, &idx.rhs, ident + 4); + eval_plan(lines, &idx.lhs, ident + 2); + eval_plan(lines, &idx.rhs, ident + 2); lines.output = output_join(lines, semi, idx.lhs_field.label, idx.rhs_field.label); @@ -425,7 +416,6 @@ fn eval_plan<'a>(lines: &mut Lines<'a>, plan: &'a PhysicalPlan, ident: u16) { let rhs = lines.labels.field(&idx.rhs_field).unwrap(); lines.add(Line::JoinExpr { - kind: JoinKind::HashJoin, unique: idx.unique, lhs, rhs, @@ -435,8 +425,8 @@ fn eval_plan<'a>(lines: &mut Lines<'a>, plan: &'a PhysicalPlan, ident: u16) { PhysicalPlan::NLJoin(lhs, rhs) => { lines.add(Line::NlJoin { ident }); - eval_plan(lines, lhs, ident + 4); - eval_plan(lines, rhs, ident + 4); + eval_plan(lines, lhs, ident + 2); + eval_plan(lines, rhs, ident + 2); } PhysicalPlan::Filter(plan, filter) => { eval_plan(lines, plan, ident); @@ -611,14 +601,14 @@ impl<'a> fmt::Display for Explain<'a> { for line in &self.lines { let ident = line.ident(); - let (ident, arrow) = if ident > 2 { (ident - 2, "-> ") } else { (ident, "") }; - write!(f, "{:ident$}{arrow}", "")?; + let arrow = if ident > 0 { "-> " } else { "" }; + match line { Line::TableScan { table, label, ident: _ } => { if self.options.show_schema { - write!(f, "Seq Scan on {}:{}", table, label.0)?; + write!(f, "{:ident$}{arrow}Seq Scan on {table}:{label}", "")?; } else { - write!(f, "Seq Scan on {}", table)?; + write!(f, "{:ident$}{arrow}Seq Scan on {table}", "")?; } } Line::IxScan { @@ -626,22 +616,24 @@ impl<'a> fmt::Display for Explain<'a> { index, ident: _, } => { - write!(f, "Index Scan using {index} on {table_name}")?; + write!(f, "{:ident$}{arrow}Index Scan using {index} on {table_name}", "")?; } Line::Filter { expr, ident: _ } => { + write!(f, "{:ident$}Filter: ", "")?; write!( f, - "Filter: ({})", + "({})", PrintExpr { expr, labels: &self.labels, - }, + } )?; } Line::FilterIxScan { idx, label, ident: _ } => { + write!(f, "{:ident$}Index Cond: ", "")?; write!( f, - "Index Cond: ({})", + "({})", PrintSarg { expr: &idx.arg, prefix: &idx.prefix, @@ -652,33 +644,27 @@ impl<'a> fmt::Display for Explain<'a> { } Line::IxJoin { semi, rhs, ident: _ } => { - write!(f, "Index Join: {semi:?} on {rhs}")?; + write!(f, "{:ident$}{arrow}Index Join: {semi:?} on {rhs}", "")?; } Line::HashJoin { semi, ident: _ } => match semi { Semi::All => { - write!(f, "Hash Join")?; + write!(f, "{:ident$}{arrow}Hash Join", "")?; } semi => { - write!(f, "Hash Join: {semi:?}")?; + write!(f, "{:ident$}{arrow}Hash Join: {semi:?}", "")?; } }, Line::NlJoin { ident: _ } => { - write!(f, "Nested Loop")?; + write!(f, "{:ident$}{arrow}Nested Loop", "")?; } Line::JoinExpr { - kind, unique, lhs, rhs, ident: _, } => { - let kind = match kind { - JoinKind::IxJoin => "Index Cond", - JoinKind::HashJoin => "Hash Cond", - JoinKind::NlJoin => "Loop Cond", - }; - writeln!(f, "Inner Unique: {unique}")?; - write!(f, "{:ident$}{arrow}{kind}: ({} = {})", "", lhs, rhs)?; + writeln!(f, "{:ident$}Inner Unique: {unique}", "")?; + write!(f, "{:ident$}Join Cond: ({} = {})", "", lhs, rhs)?; } } writeln!(f)?;