Skip to main content

spacetimedb_query_planner/logical/
expr.rs

1use std::sync::Arc;
2
3use spacetimedb_lib::AlgebraicValue;
4use spacetimedb_schema::schema::TableSchema;
5use spacetimedb_sql_parser::ast::BinOp;
6
7use crate::static_assert_size;
8
9use super::ty::{InvalidTypeId, Symbol, TyCtx, TyId, TypeWithCtx};
10
11/// A logical relational expression
12#[derive(Debug)]
13pub enum RelExpr {
14    /// A base table
15    RelVar(Arc<TableSchema>, TyId),
16    /// A filter
17    Select(Box<Select>),
18    /// A projection
19    Proj(Box<Project>),
20    /// An n-ary join
21    Join(Box<[RelExpr]>, TyId),
22    /// Bag union
23    Union(Box<RelExpr>, Box<RelExpr>),
24    /// Bag difference
25    Minus(Box<RelExpr>, Box<RelExpr>),
26    /// Bag -> set
27    Dedup(Box<RelExpr>),
28}
29
30static_assert_size!(RelExpr, 24);
31
32impl RelExpr {
33    /// Instantiate a projection [RelExpr::Proj]
34    pub fn project(input: RelExpr, expr: Let) -> Self {
35        Self::Proj(Box::new(Project { input, expr }))
36    }
37
38    /// Instantiate a selection [RelExpr::Select]
39    pub fn select(input: RelExpr, expr: Let) -> Self {
40        Self::Select(Box::new(Select { input, expr }))
41    }
42
43    /// The type id of this relation expression
44    pub fn ty_id(&self) -> TyId {
45        match self {
46            Self::RelVar(_, id) | Self::Join(_, id) => *id,
47            Self::Select(op) => op.input.ty_id(),
48            Self::Proj(op) => op.expr.exprs[0].ty_id(),
49            Self::Union(input, _) | Self::Minus(input, _) | Self::Dedup(input) => input.ty_id(),
50        }
51    }
52
53    /// The type of this relation expression
54    pub fn ty<'a>(&self, ctx: &'a TyCtx) -> Result<TypeWithCtx<'a>, InvalidTypeId> {
55        ctx.try_resolve(self.ty_id())
56    }
57}
58
59/// A relational select operation or filter
60#[derive(Debug)]
61pub struct Select {
62    /// The input relation
63    pub input: RelExpr,
64    /// The predicate expression
65    pub expr: Let,
66}
67
68/// A relational project operation or map
69#[derive(Debug)]
70pub struct Project {
71    /// The input relation
72    pub input: RelExpr,
73    /// The projection expression
74    pub expr: Let,
75}
76
77/// Let variables for selections and projections.
78///
79/// Relational operators take a single input paramter.
80/// Let variables explicitly destructure the input row.
81#[derive(Debug)]
82pub struct Let {
83    /// The variable definitions for this let expression
84    pub vars: Vec<(Symbol, Expr)>,
85    /// The expressions for which the above variables are in scope
86    pub exprs: Vec<Expr>,
87}
88
89/// A typed scalar expression
90#[derive(Debug)]
91pub enum Expr {
92    /// A binary expression
93    Bin(BinOp, Box<Expr>, Box<Expr>),
94    /// A variable reference
95    Var(Symbol, TyId),
96    /// A row or projection expression
97    Row(Box<[(Symbol, Expr)]>, TyId),
98    /// A typed literal expression
99    Lit(AlgebraicValue, TyId),
100    /// A field expression
101    Field(Box<Expr>, usize, TyId),
102    /// The input parameter to a relop
103    Input(TyId),
104}
105
106static_assert_size!(Expr, 32);
107
108impl Expr {
109    /// Returns a boolean literal
110    pub const fn bool(v: bool) -> Self {
111        Self::Lit(AlgebraicValue::Bool(v), TyId::BOOL)
112    }
113
114    /// Returns a string literal
115    pub fn str(v: String) -> Self {
116        let s = v.into_boxed_str();
117        Self::Lit(AlgebraicValue::String(s), TyId::STR)
118    }
119
120    /// The type id of this expression
121    pub fn ty_id(&self) -> TyId {
122        match self {
123            Self::Bin(..) => TyId::BOOL,
124            Self::Lit(_, id) | Self::Var(_, id) | Self::Input(id) | Self::Field(_, _, id) | Self::Row(_, id) => *id,
125        }
126    }
127
128    /// The type of this expression
129    pub fn ty<'a>(&self, ctx: &'a TyCtx) -> Result<TypeWithCtx<'a>, InvalidTypeId> {
130        ctx.try_resolve(self.ty_id())
131    }
132}