Skip to main content

rudb_exec/
build.rs

1//! Turning a bound plan into a tree of operators.
2//!
3//! One match, one arm per logical operator, and nothing else. There is no physical plan and no cost
4//! based choice between two ways of running the same node, which is the honest description of tier
5//! 0: there is one implementation of each operator so there is nothing to choose between. The
6//! physical planner that section 9.6 describes goes here, and the reason this is a separate module
7//! from the operators is so that it can grow into one without any of them moving.
8//!
9//! The tree borrows the plan and the catalog for as long as it exists. A scan reads its rows out of
10//! the catalog's table rather than copying them, and an expression reads its constants, its function
11//! names and its types out of the plan's arena, so a plan that outlives the query it built is the
12//! whole of the lifetime story here.
13
14use rudb_catalog::{Catalog, QualifiedName};
15use rudb_common::Result;
16use rudb_functions::TableFunction;
17use rudb_plan::{Node, NodeRef, Plan};
18
19use crate::group::{Aggregate, Distinct};
20use crate::join::{CrossProduct, Join};
21use crate::operator::Operator;
22use crate::setop::SetOp;
23use crate::sort::Sort;
24use crate::source::{Dummy, FileScan, Scan, Series, Values};
25use crate::stream::{Filter, Limit, Project};
26
27/// Builds the operator tree for a plan's root.
28///
29/// # Errors
30///
31/// If the plan names a table or a column the catalog does not have, if an expression is malformed
32/// in a way [`Plan::validate`] would have caught, or anything an operator's construction reports.
33pub fn build<'a>(plan: &'a Plan, catalog: &'a Catalog) -> Result<Box<dyn Operator + 'a>> {
34    node(plan, catalog, plan.root())
35}
36
37fn node<'a>(
38    plan: &'a Plan,
39    catalog: &'a Catalog,
40    reference: NodeRef,
41) -> Result<Box<dyn Operator + 'a>> {
42    Ok(match *plan.node(reference) {
43        Node::Get { catalog: database, schema, table, index, columns, .. } => {
44            let name =
45                QualifiedName::new(plan.string(database), plan.string(schema), plan.string(table));
46            Box::new(Scan::new(plan, catalog.table(&name)?, index, columns)?)
47        }
48        Node::Dummy => Box::new(Dummy::new()),
49        Node::Values { index, columns, rows } => Box::new(Values::new(plan, index, columns, rows)?),
50        Node::TableFunction { index, function, args, columns } => {
51            match TableFunction::lookup(plan.string(function)) {
52                Some(function @ (TableFunction::ReadParquet | TableFunction::ReadCsv)) => {
53                    Box::new(FileScan::new(plan, index, function, args, columns)?)
54                }
55                _ => Box::new(Series::new(plan, index, plan.string(function), args)?),
56            }
57        }
58        Node::Filter { input, predicate } => {
59            Box::new(Filter::new(plan, node(plan, catalog, input)?, predicate)?)
60        }
61        Node::Project { input, index, exprs, names } => {
62            Box::new(Project::new(plan, node(plan, catalog, input)?, index, exprs, names)?)
63        }
64        Node::Aggregate { input, index, groups, aggregates } => {
65            Box::new(Aggregate::new(plan, node(plan, catalog, input)?, index, groups, aggregates)?)
66        }
67        Node::Sort { input, keys } => Box::new(Sort::new(plan, node(plan, catalog, input)?, keys)),
68        Node::Limit { input, count, offset } => {
69            Box::new(Limit::new(node(plan, catalog, input)?, count, offset))
70        }
71        Node::Distinct { input, on } => {
72            Box::new(Distinct::new(plan, node(plan, catalog, input)?, on))
73        }
74        Node::Join { left, right, kind, conditions } => Box::new(Join::new(
75            plan,
76            node(plan, catalog, left)?,
77            node(plan, catalog, right)?,
78            kind,
79            conditions,
80        )),
81        Node::CrossProduct { left, right } => {
82            Box::new(CrossProduct::new(node(plan, catalog, left)?, node(plan, catalog, right)?))
83        }
84        Node::SetOp { left, right, kind, all, index } => Box::new(SetOp::new(
85            node(plan, catalog, left)?,
86            node(plan, catalog, right)?,
87            kind,
88            all,
89            index,
90        )),
91    })
92}