spacetimedb_expr/
expr.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
use std::sync::Arc;

use crate::errors::{FilterReturnType, TypingError};
use crate::static_assert_size;
use spacetimedb_lib::AlgebraicValue;
use spacetimedb_primitives::TableId;
use spacetimedb_schema::schema::TableSchema;
use spacetimedb_sql_parser::ast::BinOp;

use super::ty::{InvalidTypeId, Symbol, TyCtx, TyId, Type, TypeWithCtx};

/// A logical relational expression
#[derive(Debug)]
pub enum RelExpr {
    /// A base table
    RelVar(Arc<TableSchema>, TyId),
    /// A filter
    Select(Box<Select>),
    /// A projection
    Proj(Box<Project>),
    /// An n-ary join
    Join(Box<[RelExpr]>, TyId),
    /// Bag union
    Union(Box<RelExpr>, Box<RelExpr>),
    /// Bag difference
    Minus(Box<RelExpr>, Box<RelExpr>),
    /// Bag -> set
    Dedup(Box<RelExpr>),
}

static_assert_size!(RelExpr, 24);

impl RelExpr {
    /// Instantiate a projection [RelExpr::Proj]
    pub fn project(input: RelExpr, expr: Let) -> Self {
        Self::Proj(Box::new(Project { input, expr }))
    }

    /// Instantiate a selection [RelExpr::Select]
    pub fn select(input: RelExpr, expr: Let) -> Self {
        Self::Select(Box::new(Select { input, expr }))
    }

    /// The type id of this relation expression
    pub fn ty_id(&self) -> TyId {
        match self {
            Self::RelVar(_, id) | Self::Join(_, id) => *id,
            Self::Select(op) => op.input.ty_id(),
            Self::Proj(op) => op.expr.exprs[0].ty_id(),
            Self::Union(input, _) | Self::Minus(input, _) | Self::Dedup(input) => input.ty_id(),
        }
    }

    /// The type of this relation expression
    pub fn ty<'a>(&self, ctx: &'a TyCtx) -> Result<TypeWithCtx<'a>, InvalidTypeId> {
        ctx.try_resolve(self.ty_id())
    }

    pub fn table_id(&self, ctx: &mut TyCtx) -> Result<TableId, TypingError> {
        match &*self.ty(ctx)? {
            Type::Var(id, _) => Ok(*id),
            _ => Err(FilterReturnType.into()),
        }
    }
}

/// A relational select operation or filter
#[derive(Debug)]
pub struct Select {
    /// The input relation
    pub input: RelExpr,
    /// The predicate expression
    pub expr: Let,
}

/// A relational project operation or map
#[derive(Debug)]
pub struct Project {
    /// The input relation
    pub input: RelExpr,
    /// The projection expression
    pub expr: Let,
}

/// Let variables for selections and projections.
///
/// Relational operators take a single input paramter.
/// Let variables explicitly destructure the input row.
#[derive(Debug)]
pub struct Let {
    /// The variable definitions for this let expression
    pub vars: Vec<(Symbol, Expr)>,
    /// The expressions for which the above variables are in scope
    pub exprs: Vec<Expr>,
}

/// A typed scalar expression
#[derive(Debug)]
pub enum Expr {
    /// A binary expression
    Bin(BinOp, Box<Expr>, Box<Expr>),
    /// A variable reference
    Var(Symbol, TyId),
    /// A row or projection expression
    Row(Box<[(Symbol, Expr)]>, TyId),
    /// A typed literal expression
    Lit(AlgebraicValue, TyId),
    /// A field expression
    Field(Box<Expr>, usize, TyId),
    /// The input parameter to a relop
    Input(TyId),
}

static_assert_size!(Expr, 32);

impl Expr {
    /// Returns a boolean literal
    pub const fn bool(v: bool) -> Self {
        Self::Lit(AlgebraicValue::Bool(v), TyId::BOOL)
    }

    /// Returns a string literal
    pub fn str(v: Box<str>) -> Self {
        Self::Lit(AlgebraicValue::String(v), TyId::STR)
    }

    /// The type id of this expression
    pub fn ty_id(&self) -> TyId {
        match self {
            Self::Bin(..) => TyId::BOOL,
            Self::Lit(_, id) | Self::Var(_, id) | Self::Input(id) | Self::Field(_, _, id) | Self::Row(_, id) => *id,
        }
    }

    /// The type of this expression
    pub fn ty<'a>(&self, ctx: &'a TyCtx) -> Result<TypeWithCtx<'a>, InvalidTypeId> {
        ctx.try_resolve(self.ty_id())
    }
}