spacetimedb_expr/
expr.rs

1use std::sync::Arc;
2
3use spacetimedb_lib::{query::Delta, AlgebraicType, AlgebraicValue};
4use spacetimedb_primitives::TableId;
5use spacetimedb_schema::schema::TableSchema;
6use spacetimedb_sql_parser::ast::{BinOp, LogOp};
7
8/// A projection is the root of any relational expression.
9/// This type represents a projection that returns relvars.
10///
11/// For example:
12///
13/// ```sql
14/// select * from t
15/// ```
16///
17/// and
18///
19/// ```sql
20/// select t.* from t join s ...
21/// ```
22#[derive(Debug, PartialEq, Eq)]
23pub enum ProjectName {
24    None(RelExpr),
25    Some(RelExpr, Box<str>),
26}
27
28impl ProjectName {
29    /// Unwrap the outer projection, returning the inner expression
30    pub fn unwrap(self) -> RelExpr {
31        match self {
32            Self::None(expr) | Self::Some(expr, _) => expr,
33        }
34    }
35
36    /// What is the name of the return table?
37    /// This is either the table name itself or its alias.
38    pub fn return_name(&self) -> Option<&str> {
39        match self {
40            Self::None(input) => input.return_name(),
41            Self::Some(_, name) => Some(name.as_ref()),
42        }
43    }
44
45    /// The [TableSchema] of the returned rows.
46    /// Note this expression returns rows from a relvar.
47    /// Hence it this method should never return [None].
48    pub fn return_table(&self) -> Option<&TableSchema> {
49        match self {
50            Self::None(input) => input.return_table(),
51            Self::Some(input, alias) => input.find_table_schema(alias),
52        }
53    }
54
55    /// The [TableId] of the returned rows.
56    /// Note this expression returns rows from a relvar.
57    /// Hence it this method should never return [None].
58    pub fn return_table_id(&self) -> Option<TableId> {
59        match self {
60            Self::None(input) => input.return_table_id(),
61            Self::Some(input, alias) => input.find_table_id(alias),
62        }
63    }
64
65    /// Iterate over the returned column names and types
66    pub fn for_each_return_field(&self, mut f: impl FnMut(&str, &AlgebraicType)) {
67        if let Some(schema) = self.return_table() {
68            for schema in schema.columns() {
69                f(&schema.col_name, &schema.col_type);
70            }
71        }
72    }
73}
74
75/// A projection is the root of any relational expression.
76/// This type represents a projection that returns fields.
77///
78/// For example:
79///
80/// ```sql
81/// select a, b from t
82/// ```
83///
84/// and
85///
86/// ```sql
87/// select t.a as x from t join s ...
88/// ```
89///
90/// Note that RLS takes a single expression and produces a list of expressions.
91/// Hence why these variants take lists rather than single expressions.
92///
93/// Why does RLS take an expression and produce a list?
94///
95/// There may be multiple RLS rules associated to a single table.
96/// Semantically these rules represent a UNION over that table,
97/// and this corresponds to a UNION in the original expression.
98///
99/// TODO: We should model the UNION explicitly in the physical plan.
100///
101/// Ex.
102///
103/// Let's say we have the following rules for the `users` table:
104/// ```rust
105/// use spacetimedb::client_visibility_filter;
106/// use spacetimedb::Filter;
107///
108/// #[client_visibility_filter]
109/// const USER_FILTER: Filter = Filter::Sql(
110///     "SELECT users.* FROM users WHERE identity = :sender"
111/// );
112///
113/// #[client_visibility_filter]
114/// const ADMIN_FILTER: Filter = Filter::Sql(
115///     "SELECT users.* FROM users JOIN admins"
116/// );
117/// ```
118///
119/// The user query
120/// ```sql
121/// SELECT * FROM users WHERE level > 5
122/// ```
123///
124/// essentially resolves to
125/// ```sql
126/// SELECT users.*
127/// FROM users
128/// WHERE identity = :sender AND level > 5
129///
130/// UNION ALL
131///
132/// SELECT users.*
133/// FROM users JOIN admins
134/// WHERE users.level > 5
135/// ```
136#[derive(Debug)]
137pub enum ProjectList {
138    Name(Vec<ProjectName>),
139    List(Vec<RelExpr>, Vec<(Box<str>, FieldProject)>),
140    Limit(Box<ProjectList>, u64),
141    Agg(Vec<RelExpr>, AggType, Box<str>, AlgebraicType),
142}
143
144#[derive(Debug)]
145pub enum AggType {
146    Count,
147}
148
149impl ProjectList {
150    /// Does this expression project a single relvar?
151    /// If so, we return it's [TableSchema].
152    /// If not, it projects a list of columns, so we return [None].
153    pub fn return_table(&self) -> Option<&TableSchema> {
154        match self {
155            Self::Name(project) => project.first().and_then(|expr| expr.return_table()),
156            Self::Limit(input, _) => input.return_table(),
157            Self::List(..) | Self::Agg(..) => None,
158        }
159    }
160
161    /// Does this expression project a single relvar?
162    /// If so, we return it's [TableId].
163    /// If not, it projects a list of columns, so we return [None].
164    pub fn return_table_id(&self) -> Option<TableId> {
165        match self {
166            Self::Name(project) => project.first().and_then(|expr| expr.return_table_id()),
167            Self::Limit(input, _) => input.return_table_id(),
168            Self::List(..) | Self::Agg(..) => None,
169        }
170    }
171
172    /// Iterate over the projected column names and types
173    pub fn for_each_return_field(&self, mut f: impl FnMut(&str, &AlgebraicType)) {
174        match self {
175            Self::Name(input) => {
176                input.first().inspect(|expr| expr.for_each_return_field(f));
177            }
178            Self::Limit(input, _) => {
179                input.for_each_return_field(f);
180            }
181            Self::List(_, fields) => {
182                for (name, FieldProject { ty, .. }) in fields {
183                    f(name, ty);
184                }
185            }
186            Self::Agg(_, _, name, ty) => f(name, ty),
187        }
188    }
189}
190
191/// A logical relational expression
192#[derive(Debug, Clone, PartialEq, Eq)]
193pub enum RelExpr {
194    /// A relvar or table reference
195    RelVar(Relvar),
196    /// A logical select for filter
197    Select(Box<RelExpr>, Expr),
198    /// A left deep binary cross product
199    LeftDeepJoin(LeftDeepJoin),
200    /// A left deep binary equi-join
201    EqJoin(LeftDeepJoin, FieldProject, FieldProject),
202}
203
204/// A table reference
205#[derive(Debug, Clone, PartialEq, Eq)]
206pub struct Relvar {
207    /// The table schema of this relvar
208    pub schema: Arc<TableSchema>,
209    /// The name of this relvar
210    pub alias: Box<str>,
211    /// Does this relvar represent a delta table?
212    pub delta: Option<Delta>,
213}
214
215impl RelExpr {
216    /// Walk the expression tree and call `f` on each node
217    pub fn visit(&self, f: &mut impl FnMut(&Self)) {
218        f(self);
219        match self {
220            Self::Select(lhs, _)
221            | Self::LeftDeepJoin(LeftDeepJoin { lhs, .. })
222            | Self::EqJoin(LeftDeepJoin { lhs, .. }, ..) => {
223                lhs.visit(f);
224            }
225            Self::RelVar(..) => {}
226        }
227    }
228
229    /// Walk the expression tree and call `f` on each node
230    pub fn visit_mut(&mut self, f: &mut impl FnMut(&mut Self)) {
231        f(self);
232        match self {
233            Self::Select(lhs, _)
234            | Self::LeftDeepJoin(LeftDeepJoin { lhs, .. })
235            | Self::EqJoin(LeftDeepJoin { lhs, .. }, ..) => {
236                lhs.visit_mut(f);
237            }
238            Self::RelVar(..) => {}
239        }
240    }
241
242    /// The number of fields this expression returns
243    pub fn nfields(&self) -> usize {
244        match self {
245            Self::RelVar(..) => 1,
246            Self::LeftDeepJoin(join) | Self::EqJoin(join, ..) => join.lhs.nfields() + 1,
247            Self::Select(input, _) => input.nfields(),
248        }
249    }
250
251    /// Does this expression return this field?
252    pub fn has_field(&self, field: &str) -> bool {
253        match self {
254            Self::RelVar(Relvar { alias, .. }) => alias.as_ref() == field,
255            Self::LeftDeepJoin(join) | Self::EqJoin(join, ..) => {
256                join.rhs.alias.as_ref() == field || join.lhs.has_field(field)
257            }
258            Self::Select(input, _) => input.has_field(field),
259        }
260    }
261
262    /// Return the [TableSchema] for a relvar in the expression
263    pub fn find_table_schema(&self, alias: &str) -> Option<&TableSchema> {
264        match self {
265            Self::RelVar(relvar) if relvar.alias.as_ref() == alias => Some(&relvar.schema),
266            Self::Select(input, _) => input.find_table_schema(alias),
267            Self::EqJoin(LeftDeepJoin { rhs, .. }, ..) if rhs.alias.as_ref() == alias => Some(&rhs.schema),
268            Self::EqJoin(LeftDeepJoin { lhs, .. }, ..) => lhs.find_table_schema(alias),
269            Self::LeftDeepJoin(LeftDeepJoin { rhs, .. }) if rhs.alias.as_ref() == alias => Some(&rhs.schema),
270            Self::LeftDeepJoin(LeftDeepJoin { lhs, .. }) => lhs.find_table_schema(alias),
271            _ => None,
272        }
273    }
274
275    /// Return the [TableId] for a relvar in the expression
276    pub fn find_table_id(&self, alias: &str) -> Option<TableId> {
277        self.find_table_schema(alias).map(|schema| schema.table_id)
278    }
279
280    /// Does this expression return a single relvar?
281    /// If so, return it's [TableSchema], otherwise return [None].
282    pub fn return_table(&self) -> Option<&TableSchema> {
283        match self {
284            Self::RelVar(Relvar { schema, .. }) => Some(schema),
285            Self::Select(input, _) => input.return_table(),
286            _ => None,
287        }
288    }
289
290    /// Does this expression return a single relvar?
291    /// If so, return it's [TableId], otherwise return [None].
292    pub fn return_table_id(&self) -> Option<TableId> {
293        self.return_table().map(|schema| schema.table_id)
294    }
295
296    /// Does this expression return a single relvar?
297    /// If so, return its name or equivalently its alias.
298    pub fn return_name(&self) -> Option<&str> {
299        match self {
300            Self::RelVar(Relvar { alias, .. }) => Some(alias.as_ref()),
301            Self::Select(input, _) => input.return_name(),
302            _ => None,
303        }
304    }
305}
306
307/// A left deep binary cross product
308#[derive(Debug, Clone, PartialEq, Eq)]
309pub struct LeftDeepJoin {
310    /// The lhs is recursive
311    pub lhs: Box<RelExpr>,
312    /// The rhs is a relvar
313    pub rhs: Relvar,
314}
315
316/// A typed scalar expression
317#[derive(Debug, Clone, PartialEq, Eq)]
318pub enum Expr {
319    /// A binary expression
320    BinOp(BinOp, Box<Expr>, Box<Expr>),
321    /// A binary logic expression
322    LogOp(LogOp, Box<Expr>, Box<Expr>),
323    /// A typed literal expression
324    Value(AlgebraicValue, AlgebraicType),
325    /// A field projection
326    Field(FieldProject),
327}
328
329impl Expr {
330    /// Walk the expression tree and call `f` on each node
331    pub fn visit(&self, f: &impl Fn(&Self)) {
332        f(self);
333        match self {
334            Self::BinOp(_, a, b) | Self::LogOp(_, a, b) => {
335                a.visit(f);
336                b.visit(f);
337            }
338            Self::Value(..) | Self::Field(..) => {}
339        }
340    }
341
342    /// Walk the expression tree and call `f` on each node
343    pub fn visit_mut(&mut self, f: &mut impl FnMut(&mut Self)) {
344        f(self);
345        match self {
346            Self::BinOp(_, a, b) | Self::LogOp(_, a, b) => {
347                a.visit_mut(f);
348                b.visit_mut(f);
349            }
350            Self::Value(..) | Self::Field(..) => {}
351        }
352    }
353
354    /// A literal boolean value
355    pub const fn bool(v: bool) -> Self {
356        Self::Value(AlgebraicValue::Bool(v), AlgebraicType::Bool)
357    }
358
359    /// A literal string value
360    pub const fn str(v: Box<str>) -> Self {
361        Self::Value(AlgebraicValue::String(v), AlgebraicType::String)
362    }
363
364    /// The [AlgebraicType] of this scalar expression
365    pub fn ty(&self) -> &AlgebraicType {
366        match self {
367            Self::BinOp(..) | Self::LogOp(..) => &AlgebraicType::Bool,
368            Self::Value(_, ty) | Self::Field(FieldProject { ty, .. }) => ty,
369        }
370    }
371}
372
373/// A typed qualified field projection
374#[derive(Debug, Clone, PartialEq, Eq)]
375pub struct FieldProject {
376    pub table: Box<str>,
377    pub field: usize,
378    pub ty: AlgebraicType,
379}