use std::sync::Arc;
use spacetimedb_lib::{AlgebraicType, AlgebraicValue};
use spacetimedb_primitives::TableId;
use spacetimedb_schema::schema::TableSchema;
use spacetimedb_sql_parser::ast::{BinOp, LogOp};
#[derive(Debug)]
pub enum Project {
None(RelExpr),
Relvar(RelExpr, Box<str>),
Fields(RelExpr, Vec<(Box<str>, FieldProject)>),
}
impl Project {
pub fn table_id(&self) -> Option<TableId> {
match self {
Self::Fields(..) => None,
Self::Relvar(input, var) => input.table_id(Some(var.as_ref())),
Self::None(input) => input.table_id(None),
}
}
}
#[derive(Debug)]
pub enum RelExpr {
RelVar(Arc<TableSchema>, Box<str>),
Select(Box<RelExpr>, Expr),
LeftDeepJoin(LeftDeepJoin),
EqJoin(LeftDeepJoin, FieldProject, FieldProject),
}
impl RelExpr {
pub fn nfields(&self) -> usize {
match self {
Self::RelVar(..) => 1,
Self::LeftDeepJoin(join) | Self::EqJoin(join, ..) => join.lhs.nfields() + 1,
Self::Select(input, _) => input.nfields(),
}
}
pub fn has_field(&self, field: &str) -> bool {
match self {
Self::RelVar(_, name) => name.as_ref() == field,
Self::LeftDeepJoin(join) | Self::EqJoin(join, ..) => {
join.var.as_ref() == field || join.lhs.has_field(field)
}
Self::Select(input, _) => input.has_field(field),
}
}
pub fn table_id(&self, var: Option<&str>) -> Option<TableId> {
match (self, var) {
(Self::RelVar(schema, _), None) => Some(schema.table_id),
(Self::RelVar(schema, name), Some(var)) if name.as_ref() == var => Some(schema.table_id),
(Self::RelVar(schema, _), Some(_)) => Some(schema.table_id),
(Self::Select(input, _), _) => input.table_id(var),
(Self::LeftDeepJoin(..) | Self::EqJoin(..), None) => None,
(Self::LeftDeepJoin(join) | Self::EqJoin(join, ..), Some(name)) => {
if join.var.as_ref() == name {
Some(join.rhs.table_id)
} else {
join.lhs.table_id(var)
}
}
}
}
}
#[derive(Debug)]
pub struct LeftDeepJoin {
pub lhs: Box<RelExpr>,
pub rhs: Arc<TableSchema>,
pub var: Box<str>,
}
#[derive(Debug)]
pub enum Expr {
BinOp(BinOp, Box<Expr>, Box<Expr>),
LogOp(LogOp, Box<Expr>, Box<Expr>),
Value(AlgebraicValue, AlgebraicType),
Field(FieldProject),
}
impl Expr {
pub const fn bool(v: bool) -> Self {
Self::Value(AlgebraicValue::Bool(v), AlgebraicType::Bool)
}
pub const fn str(v: Box<str>) -> Self {
Self::Value(AlgebraicValue::String(v), AlgebraicType::String)
}
pub fn ty(&self) -> &AlgebraicType {
match self {
Self::BinOp(..) | Self::LogOp(..) => &AlgebraicType::Bool,
Self::Value(_, ty) | Self::Field(FieldProject { ty, .. }) => ty,
}
}
}
#[derive(Debug)]
pub struct FieldProject {
pub table: Box<str>,
pub field: usize,
pub ty: AlgebraicType,
}