Skip to content

Commit

Permalink
Printer for the plan, ie: EXPLAIN
Browse files Browse the repository at this point in the history
Improve output of plan

Fix print of alias and the output of joins

Allow the test utils of the plan to be used elsewhere

Addressing some PR comments

Use the arrow only for nodes, make timing optional
Apply new clippy hints and remove old test logic

merge
  • Loading branch information
mamcx committed Feb 5, 2025
1 parent 2d90657 commit d954c44
Show file tree
Hide file tree
Showing 13 changed files with 1,230 additions and 422 deletions.
19 changes: 19 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ enum-as-inner = "0.6"
enum-map = "2.6.3"
env_logger = "0.10"
ethnum = { version = "1.5.0", features = ["serde"] }
expect-test = "1.5.0"
flate2 = "1.0.24"
fs-err = "2.9.0"
fs2 = "0.4.3"
Expand Down
1 change: 1 addition & 0 deletions crates/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ derive_more.workspace = true
dirs.workspace = true
enum-as-inner.workspace = true
enum-map.workspace = true
expect-test.workspace = true
flate2.workspace = true
fs2.workspace = true
futures.workspace = true
Expand Down
2 changes: 1 addition & 1 deletion crates/core/src/sql/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -977,7 +977,7 @@ pub(crate) fn compile_to_ast<T: TableSchemaView + StateView>(
) -> Result<Vec<SqlAst>, 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 {
Expand Down
8 changes: 7 additions & 1 deletion crates/expr/src/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,11 +165,17 @@ pub fn type_subscription(ast: SqlSelect, tx: &impl SchemaView) -> TypingResult<P
}

/// Parse and type check a *subscription* query into a `StatementCtx`
pub fn compile_sql_sub<'a>(sql: &'a str, tx: &impl SchemaView) -> TypingResult<StatementCtx<'a>> {
pub fn compile_sql_sub<'a>(sql: &'a str, tx: &impl SchemaView, with_timings: bool) -> TypingResult<StatementCtx<'a>> {
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.map(|t| t.elapsed()),
})
}

Expand Down
2 changes: 2 additions & 0 deletions crates/expr/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,7 @@ pub(crate) fn parse(value: &str, ty: &AlgebraicType) -> anyhow::Result<Algebraic
}

/// The source of a statement
#[derive(Debug, Clone, Copy)]
pub enum StatementSource {
Subscription,
Query,
Expand All @@ -317,4 +318,5 @@ pub struct StatementCtx<'a> {
pub statement: Statement,
pub sql: &'a str,
pub source: StatementSource,
pub planning_time: Option<std::time::Duration>,
}
8 changes: 7 additions & 1 deletion crates/expr/src/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -419,12 +419,18 @@ pub fn parse_and_type_sql(sql: &str, tx: &impl SchemaView) -> TypingResult<State
}

/// Parse and type check a *general* query into a [StatementCtx].
pub fn compile_sql_stmt<'a>(sql: &'a str, tx: &impl SchemaView) -> TypingResult<StatementCtx<'a>> {
pub fn compile_sql_stmt<'a>(sql: &'a str, tx: &impl SchemaView, with_timings: bool) -> TypingResult<StatementCtx<'a>> {
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.map(|t| t.elapsed()),
})
}

Expand Down
2 changes: 2 additions & 0 deletions crates/physical-plan/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ description = "The physical query plan for the SpacetimeDB query engine"
[dependencies]
anyhow.workspace = true
derive_more.workspace = true
expect-test.workspace = true
itertools.workspace = true
spacetimedb-lib.workspace = true
spacetimedb-primitives.workspace = true
spacetimedb-schema.workspace = true
Expand Down
32 changes: 30 additions & 2 deletions crates/physical-plan/src/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ use std::collections::HashMap;
use crate::dml::{DeletePlan, MutationPlan, UpdatePlan};
use crate::plan::{HashJoin, Label, PhysicalExpr, PhysicalPlan, ProjectListPlan, ProjectPlan, Semi, TupleField};

use crate::{PhysicalCtx, PlanCtx};
use spacetimedb_expr::expr::{Expr, FieldProject, LeftDeepJoin, ProjectList, ProjectName, RelExpr, Relvar};
use spacetimedb_expr::statement::DML;
use spacetimedb_expr::statement::{Statement, DML};
use spacetimedb_expr::StatementCtx;

pub trait VarLabel {
fn label(&mut self, name: &str) -> Label;
Expand Down Expand Up @@ -121,6 +123,12 @@ struct NamesToIds {
map: HashMap<String, usize>,
}

impl NamesToIds {
fn into_map(self) -> HashMap<String, usize> {
self.map
}
}

impl VarLabel for NamesToIds {
fn label(&mut self, name: &str) -> Label {
if let Some(id) = self.map.get(name) {
Expand All @@ -143,7 +151,11 @@ pub fn compile_select(project: ProjectName) -> ProjectPlan {
/// Note, this utility is applicable to a generic selections.
/// In particular, it supports explicit column projections.
pub fn compile_select_list(project: ProjectList) -> ProjectListPlan {
compile_project_list(&mut NamesToIds::default(), project)
compile_select_list_raw(&mut NamesToIds::default(), project)
}

pub fn compile_select_list_raw(var: &mut impl VarLabel, project: ProjectList) -> ProjectListPlan {
compile_project_list(var, project)
}

/// Converts a logical DML statement into a physical plan,
Expand All @@ -155,3 +167,19 @@ pub fn compile_dml_plan(stmt: DML) -> MutationPlan {
DML::Update(update) => MutationPlan::Update(UpdatePlan::compile(update)),
}
}

pub fn compile(ast: StatementCtx<'_>) -> PhysicalCtx<'_> {
let mut vars = NamesToIds::default();
let plan = match ast.statement {
Statement::Select(project) => PlanCtx::ProjectList(compile_select_list_raw(&mut vars, project)),
Statement::DML(stmt) => PlanCtx::DML(compile_dml_plan(stmt)),
};

PhysicalCtx {
plan,
sql: ast.sql,
vars: vars.into_map(),
source: ast.source,
planning_time: None,
}
}
4 changes: 4 additions & 0 deletions crates/physical-plan/src/dml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use spacetimedb_schema::schema::TableSchema;
use crate::{compile::compile_select, plan::ProjectPlan};

/// A plan for mutating a table in the database
#[derive(Debug)]
pub enum MutationPlan {
Insert(InsertPlan),
Delete(DeletePlan),
Expand All @@ -30,6 +31,7 @@ impl MutationPlan {
}

/// A plan for inserting rows into a table
#[derive(Debug)]
pub struct InsertPlan {
pub table: Arc<TableSchema>,
pub rows: Vec<ProductValue>,
Expand All @@ -44,6 +46,7 @@ impl From<TableInsert> for InsertPlan {
}

/// A plan for deleting rows from a table
#[derive(Debug)]
pub struct DeletePlan {
pub table: Arc<TableSchema>,
pub filter: ProjectPlan,
Expand Down Expand Up @@ -77,6 +80,7 @@ impl DeletePlan {
}

/// A plan for updating rows in a table
#[derive(Debug)]
pub struct UpdatePlan {
pub table: Arc<TableSchema>,
pub columns: Vec<(ColId, AlgebraicValue)>,
Expand Down
42 changes: 42 additions & 0 deletions crates/physical-plan/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,46 @@
use crate::dml::MutationPlan;
use crate::plan::ProjectListPlan;
use anyhow::Result;
use spacetimedb_expr::StatementSource;
use std::collections::HashMap;

pub mod compile;
pub mod dml;
pub mod plan;
pub mod printer;
pub mod rules;

#[derive(Debug)]
pub enum PlanCtx {
ProjectList(ProjectListPlan),
DML(MutationPlan),
}

impl PlanCtx {
pub(crate) fn optimize(self) -> Result<PlanCtx> {
Ok(match self {
Self::ProjectList(plan) => Self::ProjectList(plan.optimize()?),
Self::DML(plan) => Self::DML(plan.optimize()?),
})
}
}

/// A physical context for the result of a query compilation.
#[derive(Debug)]
pub struct PhysicalCtx<'a> {
pub plan: PlanCtx,
pub sql: &'a str,
// A map from table names to their labels
pub vars: HashMap<String, usize>,
pub source: StatementSource,
pub planning_time: Option<std::time::Duration>,
}

impl PhysicalCtx<'_> {
pub fn optimize(self) -> Result<Self> {
Ok(Self {
plan: self.plan.optimize()?,
..self
})
}
}
Loading

0 comments on commit d954c44

Please sign in to comment.