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#[derive(Debug)]
23pub enum ProjectName {
24 None(RelExpr),
25 Some(RelExpr, Box<str>),
26}
27
28impl ProjectName {
29 pub fn return_table(&self) -> Option<&TableSchema> {
33 match self {
34 Self::None(input) => input.return_table(),
35 Self::Some(input, alias) => input.find_table_schema(alias),
36 }
37 }
38
39 pub fn return_table_id(&self) -> Option<TableId> {
43 match self {
44 Self::None(input) => input.return_table_id(),
45 Self::Some(input, alias) => input.find_table_id(alias),
46 }
47 }
48
49 pub fn for_each_return_field(&self, mut f: impl FnMut(&str, &AlgebraicType)) {
51 if let Some(schema) = self.return_table() {
52 for schema in schema.columns() {
53 f(&schema.col_name, &schema.col_type);
54 }
55 }
56 }
57}
58
59#[derive(Debug)]
74pub enum ProjectList {
75 Name(ProjectName),
76 List(RelExpr, Vec<(Box<str>, FieldProject)>),
77 Limit(Box<ProjectList>, u64),
78 Agg(RelExpr, AggType, Box<str>, AlgebraicType),
79}
80
81#[derive(Debug)]
82pub enum AggType {
83 Count,
84}
85
86impl ProjectList {
87 pub fn return_table(&self) -> Option<&TableSchema> {
91 match self {
92 Self::Name(project) => project.return_table(),
93 Self::Limit(input, _) => input.return_table(),
94 Self::List(..) | Self::Agg(..) => None,
95 }
96 }
97
98 pub fn return_table_id(&self) -> Option<TableId> {
102 match self {
103 Self::Name(project) => project.return_table_id(),
104 Self::Limit(input, _) => input.return_table_id(),
105 Self::List(..) | Self::Agg(..) => None,
106 }
107 }
108
109 pub fn for_each_return_field(&self, mut f: impl FnMut(&str, &AlgebraicType)) {
111 match self {
112 Self::Name(input) => {
113 input.for_each_return_field(f);
114 }
115 Self::Limit(input, _) => {
116 input.for_each_return_field(f);
117 }
118 Self::List(_, fields) => {
119 for (name, FieldProject { ty, .. }) in fields {
120 f(name, ty);
121 }
122 }
123 Self::Agg(_, _, name, ty) => f(name, ty),
124 }
125 }
126}
127
128#[derive(Debug)]
130pub enum RelExpr {
131 RelVar(Relvar),
133 Select(Box<RelExpr>, Expr),
135 LeftDeepJoin(LeftDeepJoin),
137 EqJoin(LeftDeepJoin, FieldProject, FieldProject),
139}
140
141#[derive(Debug)]
143pub struct Relvar {
144 pub schema: Arc<TableSchema>,
145 pub alias: Box<str>,
146 pub delta: Option<Delta>,
148}
149
150impl RelExpr {
151 pub fn nfields(&self) -> usize {
153 match self {
154 Self::RelVar(..) => 1,
155 Self::LeftDeepJoin(join) | Self::EqJoin(join, ..) => join.lhs.nfields() + 1,
156 Self::Select(input, _) => input.nfields(),
157 }
158 }
159
160 pub fn has_field(&self, field: &str) -> bool {
162 match self {
163 Self::RelVar(Relvar { alias, .. }) => alias.as_ref() == field,
164 Self::LeftDeepJoin(join) | Self::EqJoin(join, ..) => {
165 join.rhs.alias.as_ref() == field || join.lhs.has_field(field)
166 }
167 Self::Select(input, _) => input.has_field(field),
168 }
169 }
170
171 pub fn find_table_schema(&self, alias: &str) -> Option<&TableSchema> {
173 match self {
174 Self::RelVar(relvar) if relvar.alias.as_ref() == alias => Some(&relvar.schema),
175 Self::Select(input, _) => input.find_table_schema(alias),
176 Self::EqJoin(LeftDeepJoin { rhs, .. }, ..) if rhs.alias.as_ref() == alias => Some(&rhs.schema),
177 Self::EqJoin(LeftDeepJoin { lhs, .. }, ..) => lhs.find_table_schema(alias),
178 Self::LeftDeepJoin(LeftDeepJoin { rhs, .. }) if rhs.alias.as_ref() == alias => Some(&rhs.schema),
179 Self::LeftDeepJoin(LeftDeepJoin { lhs, .. }) => lhs.find_table_schema(alias),
180 _ => None,
181 }
182 }
183
184 pub fn find_table_id(&self, alias: &str) -> Option<TableId> {
186 self.find_table_schema(alias).map(|schema| schema.table_id)
187 }
188
189 pub fn return_table(&self) -> Option<&TableSchema> {
192 match self {
193 Self::RelVar(Relvar { schema, .. }) => Some(schema),
194 Self::Select(input, _) => input.return_table(),
195 _ => None,
196 }
197 }
198
199 pub fn return_table_id(&self) -> Option<TableId> {
202 self.return_table().map(|schema| schema.table_id)
203 }
204}
205
206#[derive(Debug)]
208pub struct LeftDeepJoin {
209 pub lhs: Box<RelExpr>,
211 pub rhs: Relvar,
213}
214
215#[derive(Debug)]
217pub enum Expr {
218 BinOp(BinOp, Box<Expr>, Box<Expr>),
220 LogOp(LogOp, Box<Expr>, Box<Expr>),
222 Value(AlgebraicValue, AlgebraicType),
224 Field(FieldProject),
226}
227
228impl Expr {
229 pub const fn bool(v: bool) -> Self {
231 Self::Value(AlgebraicValue::Bool(v), AlgebraicType::Bool)
232 }
233
234 pub const fn str(v: Box<str>) -> Self {
236 Self::Value(AlgebraicValue::String(v), AlgebraicType::String)
237 }
238
239 pub fn ty(&self) -> &AlgebraicType {
241 match self {
242 Self::BinOp(..) | Self::LogOp(..) => &AlgebraicType::Bool,
243 Self::Value(_, ty) | Self::Field(FieldProject { ty, .. }) => ty,
244 }
245 }
246}
247
248#[derive(Debug)]
250pub struct FieldProject {
251 pub table: Box<str>,
252 pub field: usize,
253 pub ty: AlgebraicType,
254}