From 02917eac8bdab9ca45f48f0b73516ed906d7ff0b Mon Sep 17 00:00:00 2001 From: joshua-spacetime Date: Fri, 13 Sep 2024 17:50:47 -0700 Subject: [PATCH] interpret literals and static_assert_size --- Cargo.lock | 2 +- crates/lib/src/lib.rs | 8 +- crates/planner/Cargo.toml | 5 +- crates/planner/src/logical/bind.rs | 329 +++++++++++++++------------ crates/planner/src/logical/errors.rs | 7 + crates/planner/src/logical/expr.rs | 87 +++---- crates/planner/src/logical/mod.rs | 14 ++ 7 files changed, 244 insertions(+), 208 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 53e14d3d7a3..3678f036e8c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4478,8 +4478,8 @@ name = "spacetimedb-query-planner" version = "0.12.0" dependencies = [ "derive_more", - "spacetimedb-core", "spacetimedb-lib", + "spacetimedb-primitives", "spacetimedb-sats", "spacetimedb-schema", "spacetimedb-sql-parser", diff --git a/crates/lib/src/lib.rs b/crates/lib/src/lib.rs index b895660c3c5..2ad4f9956ea 100644 --- a/crates/lib/src/lib.rs +++ b/crates/lib/src/lib.rs @@ -379,6 +379,12 @@ pub fn from_hex_pad, T: AsRef<[u8]>>( hex: T, ) -> Result { let hex = hex.as_ref(); - let hex = if hex.starts_with(b"0x") { &hex[2..] } else { hex }; + let hex = if hex.starts_with(b"0x") { + &hex[2..] + } else if hex.starts_with(b"X'") { + &hex[2..hex.len()] + } else { + hex + }; hex::FromHex::from_hex(hex) } diff --git a/crates/planner/Cargo.toml b/crates/planner/Cargo.toml index cfbfe3713fc..38328103788 100644 --- a/crates/planner/Cargo.toml +++ b/crates/planner/Cargo.toml @@ -7,10 +7,11 @@ license-file = "LICENSE" [dependencies] derive_more.workspace = true thiserror.workspace = true -spacetimedb-schema.workspace = true +spacetimedb-lib.workspace = true spacetimedb-sats.workspace = true +spacetimedb-schema.workspace = true spacetimedb-sql-parser.workspace = true [dev-dependencies] -spacetimedb-core.workspace = true spacetimedb-lib.workspace = true +spacetimedb-primitives.workspace = true diff --git a/crates/planner/src/logical/bind.rs b/crates/planner/src/logical/bind.rs index c30b8c9cf61..31b64451c83 100644 --- a/crates/planner/src/logical/bind.rs +++ b/crates/planner/src/logical/bind.rs @@ -1,5 +1,6 @@ use std::sync::Arc; +use spacetimedb_lib::{from_hex_pad, Address, AlgebraicValue, Identity}; use spacetimedb_sats::{AlgebraicType, ProductType, ProductTypeElement}; use spacetimedb_schema::schema::TableSchema; use spacetimedb_sql_parser::{ @@ -22,78 +23,83 @@ pub trait SchemaView { /// Parse and type check a subscription query pub fn parse_and_type_sub(sql: &str, tx: &impl SchemaView) -> TypingResult { - expect_table_type(bind_and_check(parse_subscription(sql)?, tx)?) + expect_table_type(type_ast(parse_subscription(sql)?, tx)?) } -/// Bind variables, check types, and lower a [SqlAst] into a [RelExpr] -pub fn bind_and_check(expr: SqlAst, tx: &impl SchemaView) -> TypingResult { +/// Type check and lower a [SqlAst] into a [RelExpr]. +/// This includes name resolution and variable binding. +pub fn type_ast(expr: SqlAst, tx: &impl SchemaView) -> TypingResult { match expr { - SqlAst::Union(a, b) => Ok(RelExpr::Union( - Box::new(bind_and_check(*a, tx)?), - Box::new(bind_and_check(*b, tx)?), - )), - SqlAst::Minus(a, b) => Ok(RelExpr::Minus( - Box::new(bind_and_check(*a, tx)?), - Box::new(bind_and_check(*b, tx)?), - )), + SqlAst::Union(a, b) => { + let a = type_ast(*a, tx)?; + let b = type_ast(*b, tx)?; + assert_eq_types(a.ty(), b.ty())?; + Ok(RelExpr::Union(Box::new(a), Box::new(b))) + } + SqlAst::Minus(a, b) => { + let a = type_ast(*a, tx)?; + let b = type_ast(*b, tx)?; + assert_eq_types(a.ty(), b.ty())?; + Ok(RelExpr::Minus(Box::new(a), Box::new(b))) + } SqlAst::Select(SqlSelect { project, from, filter: None, }) => { - let (arg, vars) = bind_and_check_from(from, tx)?; - bind_and_check_proj(project, arg, vars) + let (arg, vars) = type_from(from, tx)?; + type_proj(project, arg, vars) } SqlAst::Select(SqlSelect { project, from, filter: Some(expr), }) => { - let (from, vars) = bind_and_check_from(from, tx)?; - let arg = bind_and_check_sel(expr, from, vars.clone())?; - bind_and_check_proj(project, arg, vars.clone()) + let (from, vars) = type_from(from, tx)?; + let arg = type_select(expr, from, vars.clone())?; + type_proj(project, arg, vars.clone()) } } } -/// Bind variables and type check the relation expression in a FROM clause -pub fn bind_and_check_from(from: SqlFrom, tx: &impl SchemaView) -> TypingResult<(RelExpr, Vars)> { +/// Type check and lower a [SqlFrom] +pub fn type_from(from: SqlFrom, tx: &impl SchemaView) -> TypingResult<(RelExpr, Vars)> { match from { - SqlFrom::Expr(expr, None) => bind_and_check_rel(expr, tx), + SqlFrom::Expr(expr, None) => type_rel(expr, tx), SqlFrom::Expr(expr, Some(alias)) => { - let (expr, _) = bind_and_check_rel(expr, tx)?; + let (expr, _) = type_rel(expr, tx)?; let ty = expr.ty().clone(); Ok((expr, vec![(alias.name, ty)].into())) } SqlFrom::Join(r, alias, joins) => { - let (mut vars, mut args, mut exprs) = (Vars::new(), Vec::new(), Vec::new()); + let (mut vars, mut args, mut exprs) = (Vars::default(), Vec::new(), Vec::new()); - let (r, _) = bind_and_check_rel(r, tx)?; + let (r, _) = type_rel(r, tx)?; let ty = r.ty().clone(); args.push(r); vars.push((alias.name, ty)); for join in joins { - let (r, _) = bind_and_check_rel(join.expr, tx)?; + let (r, _) = type_rel(join.expr, tx)?; let ty = r.ty().clone(); args.push(r); vars.push((join.alias.name, ty)); if let Some(on) = join.on { - exprs.push(check_and_lower_expr(&vars, on, Some(&Type::BOOL))?); + exprs.push(type_expr(&vars, on, Some(&Type::BOOL))?); } } let types = vars.iter().map(|(_, ty)| ty.clone()).collect(); - let input = RelExpr::Join(args, Type::Tup(types)); + let input = RelExpr::Join(args.into(), Type::Tup(types)); Ok((RelExpr::select(input, vars.clone(), exprs), vars)) } } } -/// Bind variables and type check a relation expression -pub fn bind_and_check_rel(expr: ast::RelExpr, tx: &impl SchemaView) -> TypingResult<(RelExpr, Vars)> { +/// Type check and lower a [ast::RelExpr] +fn type_rel(expr: ast::RelExpr, tx: &impl SchemaView) -> TypingResult<(RelExpr, Vars)> { match expr { ast::RelExpr::Var(var) => tx .schema(&var.name, var.case_sensitive) @@ -104,18 +110,18 @@ pub fn bind_and_check_rel(expr: ast::RelExpr, tx: &impl SchemaView) -> T vec![(var.name, Type::Var(schema))].into(), ) }), - ast::RelExpr::Ast(ast) => Ok((bind_and_check(*ast, tx)?, Vars::new())), + ast::RelExpr::Ast(ast) => Ok((type_ast(*ast, tx)?, Vars::default())), } } -/// Bind variables and type check a selection -fn bind_and_check_sel(expr: SqlExpr, input: RelExpr, vars: Vars) -> TypingResult { - let exprs = vec![check_and_lower_expr(&vars, expr, Some(&Type::BOOL))?]; +/// Type check and lower a [SqlExpr] +fn type_select(expr: SqlExpr, input: RelExpr, vars: Vars) -> TypingResult { + let exprs = vec![type_expr(&vars, expr, Some(&Type::BOOL))?]; Ok(RelExpr::select(input, vars, exprs)) } -/// Bind variables and type check a projection -fn bind_and_check_proj(proj: ast::Project, input: RelExpr, vars: Vars) -> TypingResult { +/// Type check and lower a [ast::Project] +fn type_proj(proj: ast::Project, input: RelExpr, vars: Vars) -> TypingResult { match proj { ast::Project::Star(None) => Ok(input), ast::Project::Star(Some(var)) => { @@ -152,42 +158,112 @@ fn bind_and_check_proj(proj: ast::Project, input: RelExpr, vars: Vars) -> Typing } /// Type check and lower a [SqlExpr] into a logical [Expr]. -fn check_and_lower_expr(vars: &Vars, expr: SqlExpr, expected: Option<&Type>) -> TypingResult { +fn type_expr(vars: &Vars, expr: SqlExpr, expected: Option<&Type>) -> TypingResult { match (expr, expected) { (SqlExpr::Lit(SqlLiteral::Bool(v)), None | Some(Type::Alg(AlgebraicType::Bool))) => Ok(Expr::bool(v)), - (SqlExpr::Lit(SqlLiteral::Bool(_)), Some(t)) => Err(ConstraintViolation::eq(&Type::BOOL, t).into()), + (SqlExpr::Lit(SqlLiteral::Bool(_)), Some(t)) => Err(unexpected_type(&Type::BOOL, t)), (SqlExpr::Lit(SqlLiteral::Str(v)), None | Some(Type::Alg(AlgebraicType::String))) => Ok(Expr::str(v)), - (SqlExpr::Lit(SqlLiteral::Str(_)), Some(t)) => Err(ConstraintViolation::eq(&Type::STR, t).into()), - // Cannot infer the type of numeric literal - (SqlExpr::Lit(SqlLiteral::Num(_)), None) => Err(ResolutionError::UntypedLiteral.into()), - (SqlExpr::Lit(SqlLiteral::Num(v)), Some(t)) if t.is_num() => Ok(Expr::num(v, t.clone())), - (SqlExpr::Lit(SqlLiteral::Num(_)), Some(t)) => Err(ConstraintViolation::num(t).into()), - // Cannot infer the type of hex literal - (SqlExpr::Lit(SqlLiteral::Hex(_)), None) => Err(ResolutionError::UntypedLiteral.into()), - (SqlExpr::Lit(SqlLiteral::Hex(v)), Some(t)) if t.is_hex() => Ok(Expr::hex(v, t.clone())), - (SqlExpr::Lit(SqlLiteral::Hex(_)), Some(t)) => Err(ConstraintViolation::hex(t).into()), + (SqlExpr::Lit(SqlLiteral::Str(_)), Some(t)) => Err(unexpected_type(&Type::STR, t)), + (SqlExpr::Lit(SqlLiteral::Num(_) | SqlLiteral::Hex(_)), None) => Err(ResolutionError::UntypedLiteral.into()), + (SqlExpr::Lit(SqlLiteral::Num(v) | SqlLiteral::Hex(v)), Some(t)) => parse(v, t), (SqlExpr::Var(var), expected) => vars.expect_var_ref(&var.name, expected), (SqlExpr::Field(table, field), expected) => vars.expect_field_ref(&table.name, &field.name, expected), - // The operands of and/or must be boolean typed expressions - (SqlExpr::Bin(a, b, op @ BinOp::And | op @ BinOp::Or), None | Some(Type::Alg(AlgebraicType::Bool))) => { - Ok(Expr::Bin( - op, - Box::new(check_and_lower_expr(vars, *a, Some(&Type::BOOL))?), - Box::new(check_and_lower_expr(vars, *b, Some(&Type::BOOL))?), - )) - } - // The operands of binary operators must be the same type (SqlExpr::Bin(a, b, op), None | Some(Type::Alg(AlgebraicType::Bool))) => match (*a, *b) { (a, b @ SqlExpr::Lit(_)) | (b @ SqlExpr::Lit(_), a) | (a, b) => { - let a = expect_op_type(op, check_and_lower_expr(vars, a, None)?)?; - let b = expect_op_type(op, check_and_lower_expr(vars, b, Some(a.ty()))?)?; + let a = expect_op_type(op, type_expr(vars, a, None)?)?; + let b = expect_op_type(op, type_expr(vars, b, Some(a.ty()))?)?; Ok(Expr::Bin(op, Box::new(a), Box::new(b))) } }, - (SqlExpr::Bin(..), Some(t)) => Err(ConstraintViolation::eq(&Type::BOOL, t).into()), + (SqlExpr::Bin(..), Some(t)) => Err(unexpected_type(&Type::BOOL, t)), + } +} + +/// Parses a source text literal as a particular type +fn parse(v: String, ty: &Type) -> TypingResult { + let constraint_err = |v, ty| TypingError::from(ConstraintViolation::lit(v, ty)); + match ty { + Type::Alg(AlgebraicType::I8) => v + .parse::() + .map(AlgebraicValue::I8) + .map(|v| Expr::Lit(v, ty.clone())) + .map_err(|_| constraint_err(&v, ty)), + Type::Alg(AlgebraicType::U8) => v + .parse::() + .map(AlgebraicValue::U8) + .map(|v| Expr::Lit(v, ty.clone())) + .map_err(|_| constraint_err(&v, ty)), + Type::Alg(AlgebraicType::I16) => v + .parse::() + .map(AlgebraicValue::I16) + .map(|v| Expr::Lit(v, ty.clone())) + .map_err(|_| constraint_err(&v, ty)), + Type::Alg(AlgebraicType::U16) => v + .parse::() + .map(AlgebraicValue::U16) + .map(|v| Expr::Lit(v, ty.clone())) + .map_err(|_| constraint_err(&v, ty)), + Type::Alg(AlgebraicType::I32) => v + .parse::() + .map(AlgebraicValue::I32) + .map(|v| Expr::Lit(v, ty.clone())) + .map_err(|_| constraint_err(&v, ty)), + Type::Alg(AlgebraicType::U32) => v + .parse::() + .map(AlgebraicValue::U32) + .map(|v| Expr::Lit(v, ty.clone())) + .map_err(|_| constraint_err(&v, ty)), + Type::Alg(AlgebraicType::I64) => v + .parse::() + .map(AlgebraicValue::I64) + .map(|v| Expr::Lit(v, ty.clone())) + .map_err(|_| constraint_err(&v, ty)), + Type::Alg(AlgebraicType::U64) => v + .parse::() + .map(AlgebraicValue::U64) + .map(|v| Expr::Lit(v, ty.clone())) + .map_err(|_| constraint_err(&v, ty)), + Type::Alg(AlgebraicType::F32) => v + .parse::() + .map(|v| AlgebraicValue::F32(v.into())) + .map(|v| Expr::Lit(v, ty.clone())) + .map_err(|_| constraint_err(&v, ty)), + Type::Alg(AlgebraicType::F64) => v + .parse::() + .map(|v| AlgebraicValue::F64(v.into())) + .map(|v| Expr::Lit(v, ty.clone())) + .map_err(|_| constraint_err(&v, ty)), + Type::Alg(AlgebraicType::I128) => v + .parse::() + .map(|v| AlgebraicValue::I128(v.into())) + .map(|v| Expr::Lit(v, ty.clone())) + .map_err(|_| constraint_err(&v, ty)), + Type::Alg(AlgebraicType::U128) => v + .parse::() + .map(|v| AlgebraicValue::U128(v.into())) + .map(|v| Expr::Lit(v, ty.clone())) + .map_err(|_| constraint_err(&v, ty)), + Type::Alg(t) if t.is_bytes() => from_hex_pad::, _>(&v) + .map(|v| AlgebraicValue::Bytes(v.into_boxed_slice())) + .map(|v| Expr::Lit(v, ty.clone())) + .map_err(|_| constraint_err(&v, ty)), + Type::Alg(t) if t.is_identity() => Identity::from_hex(&v) + .map(AlgebraicValue::from) + .map(|v| Expr::Lit(v, ty.clone())) + .map_err(|_| constraint_err(&v, ty)), + Type::Alg(t) if t.is_address() => Address::from_hex(&v) + .map(AlgebraicValue::from) + .map(|v| Expr::Lit(v, ty.clone())) + .map_err(|_| constraint_err(&v, ty)), + _ => Err(constraint_err(&v, ty)), } } +/// Returns a type constraint violation for an unexpected type +fn unexpected_type(expected: &Type, actual: &Type) -> TypingError { + ConstraintViolation::eq(expected, actual).into() +} + /// Returns an error if the input type is not a table type [Type::Var] fn expect_table_type(expr: RelExpr) -> TypingResult { match expr.ty() { @@ -199,8 +275,11 @@ fn expect_table_type(expr: RelExpr) -> TypingResult { /// Assert that this type is compatible with this operator fn expect_op_type(op: BinOp, expr: Expr) -> TypingResult { match (op, expr.ty()) { + // Logic operators take booleans (BinOp::And | BinOp::Or, Type::Alg(AlgebraicType::Bool)) => Ok(expr), + // Comparison operators take integers or floats (BinOp::Lt | BinOp::Gt | BinOp::Lte | BinOp::Gte, Type::Alg(t)) if t.is_integer() || t.is_float() => Ok(expr), + // Equality supports numerics, strings, and bytes (BinOp::Eq | BinOp::Ne, Type::Alg(t)) if t.is_bool() || t.is_integer() @@ -216,85 +295,79 @@ fn expect_op_type(op: BinOp, expr: Expr) -> TypingResult { } } +fn assert_eq_types(a: &Type, b: &Type) -> TypingResult<()> { + if a == b { + Ok(()) + } else { + Err(unexpected_type(a, b)) + } +} + #[cfg(test)] mod tests { + use spacetimedb_lib::{db::raw_def::v9::RawModuleDefV9Builder, AlgebraicType, ProductType}; + use spacetimedb_primitives::TableId; + use spacetimedb_schema::{def::ModuleDef, schema::TableSchema}; use std::sync::Arc; - use spacetimedb::{ - address::Address, - db::{ - datastore::{ - locking_tx_datastore::{state_view::StateView, MutTxId}, - traits::IsolationLevel, - }, - relational_db::tests_utils::TestDB, - }, - execution_context::ExecutionContext, - }; - use spacetimedb_lib::{db::raw_def::RawTableDefV8, AlgebraicType, ProductType}; - use spacetimedb_schema::schema::TableSchema; - use super::{parse_and_type_sub, SchemaView}; - impl SchemaView for MutTxId { - fn schema(&self, name: &str, _: bool) -> Option> { - let table_id = self.table_id_from_name(name, Address::ZERO).ok()??; - self.schema_for_table(&ExecutionContext::default(), table_id).ok() - } - } - - #[test] - fn valid() { - let db = TestDB::in_memory().expect("database init failed"); - - let schema_t = RawTableDefV8::from_product( + fn module_def() -> ModuleDef { + let mut builder = RawModuleDefV9Builder::new(); + builder.build_table_with_new_type( "t", - ProductType::from_iter([ + ProductType::from([ ("u32", AlgebraicType::U32), ("f32", AlgebraicType::F32), ("str", AlgebraicType::String), - ( - "row", - AlgebraicType::product(ProductType::from_iter([ - ("u32", AlgebraicType::U32), - ("bool", AlgebraicType::Bool), - ])), - ), + ("arr", AlgebraicType::array(AlgebraicType::String)), ]), + true, ); - - let schema_s = RawTableDefV8::from_product( + builder.build_table_with_new_type( "s", - ProductType::from_iter([ + ProductType::from([ + ("id", AlgebraicType::identity()), ("u32", AlgebraicType::U32), + ("arr", AlgebraicType::array(AlgebraicType::String)), ("bytes", AlgebraicType::bytes()), - ( - "row", - AlgebraicType::product(ProductType::from_iter([ - ("u32", AlgebraicType::U32), - ("bool", AlgebraicType::Bool), - ])), - ), ]), + true, ); + builder.finish().try_into().expect("failed to generate module def") + } - let mut tx = db.begin_mut_tx(IsolationLevel::Serializable); + struct SchemaViewer(ModuleDef); - db.create_table(&mut tx, schema_t).expect("failed to create table"); - db.create_table(&mut tx, schema_s).expect("failed to create table"); + impl SchemaView for SchemaViewer { + fn schema(&self, name: &str, _: bool) -> Option> { + self.0.table(name).map(|def| { + Arc::new(TableSchema::from_module_def( + def, + TableId(if *def.name == *"t" { 0 } else { 1 }), + )) + }) + } + } + + #[test] + fn valid() { + let tx = SchemaViewer(module_def()); for sql in [ "select * from t", "select * from t where true", "select * from t where t.u32 = 1", "select * from t where t.u32 = 1 or t.str = ''", + "select * from s where s.bytes = 0xABCD", + "select * from s where s.bytes = X'ABCD'", "select * from s as r where r.bytes = 0xABCD", "select * from (select t.* from t join s)", "select * from (select t.* from t join s on t.u32 = s.u32 where t.f32 = 0.1)", "select * from (select t.* from t join (select s.u32 from s) s on t.u32 = s.u32)", ] { - let result = parse_and_type_sub(sql, &mut tx).inspect_err(|err| { - println!("{}", err.to_string()); + let result = parse_and_type_sub(sql, &tx).inspect_err(|_| { + // println!("sql: {}\n\n\terr: {}\n", sql, err); }); assert!(result.is_ok()); } @@ -302,43 +375,7 @@ mod tests { #[test] fn invalid() { - let db = TestDB::in_memory().expect("database init failed"); - - let schema_t = RawTableDefV8::from_product( - "t", - ProductType::from_iter([ - ("u32", AlgebraicType::U32), - ("f32", AlgebraicType::F32), - ("str", AlgebraicType::String), - ( - "row", - AlgebraicType::product(ProductType::from_iter([ - ("u32", AlgebraicType::U32), - ("bool", AlgebraicType::Bool), - ])), - ), - ]), - ); - - let schema_s = RawTableDefV8::from_product( - "s", - ProductType::from_iter([ - ("u32", AlgebraicType::U32), - ("bytes", AlgebraicType::bytes()), - ( - "row", - AlgebraicType::product(ProductType::from_iter([ - ("u32", AlgebraicType::U32), - ("bool", AlgebraicType::Bool), - ])), - ), - ]), - ); - - let mut tx = db.begin_mut_tx(IsolationLevel::Serializable); - - db.create_table(&mut tx, schema_t).expect("failed to create table"); - db.create_table(&mut tx, schema_s).expect("failed to create table"); + let tx = SchemaViewer(module_def()); for sql in [ // Table r does not exist @@ -351,6 +388,8 @@ mod tests { "select * from t as r where r.a = 1", // Field u32 is not a string "select * from t where t.u32 = 'str'", + // Field u32 is not a float + "select * from t where t.u32 = 1.3", // t is not in scope after alias "select * from t as r where t.u32 = 5", // Field u32 is not in scope @@ -360,14 +399,14 @@ mod tests { // Subscriptions must be typed to a single table "select * from t join s", // Product values are not comparable - "select * from (select t.* from t join s on t.row = s.row)", + "select * from (select t.* from t join s on t.arr = s.arr)", // Subscriptions must be typed to a single table "select * from (select s.* from t join (select s.u32 from s) s on t.u32 = s.u32)", // Field bytes is no longer in scope "select * from (select t.* from t join (select s.u32 from s) s on s.bytes = 0xABCD)", ] { - let result = parse_and_type_sub(sql, &mut tx).inspect_err(|err| { - println!("{}", err.to_string()); + let result = parse_and_type_sub(sql, &tx).inspect_err(|_| { + // println!("sql: {}\n\n\terr: {}\n", sql, err); }); assert!(result.is_err()); } diff --git a/crates/planner/src/logical/errors.rs b/crates/planner/src/logical/errors.rs index f6ee7a42108..3234b25159a 100644 --- a/crates/planner/src/logical/errors.rs +++ b/crates/planner/src/logical/errors.rs @@ -11,6 +11,8 @@ pub enum ConstraintViolation { Num(Type), #[error("{0} cannot be interpreted as a byte array")] Hex(Type), + #[error("{0} cannot be parsed as {1}")] + Lit(String, Type), #[error("{1} is not supported by the binary operator {0}")] Op(BinOp, Type), } @@ -33,6 +35,11 @@ impl ConstraintViolation { Self::Hex(t.clone()) } + // This literal expression cannot be parsed as this type + pub fn lit(v: &str, ty: &Type) -> Self { + Self::Lit(v.to_string(), ty.clone()) + } + // This type is not supported by this operator pub fn op(op: BinOp, ty: &Type) -> Self { Self::Op(op, ty.clone()) diff --git a/crates/planner/src/logical/expr.rs b/crates/planner/src/logical/expr.rs index 19bffa7b934..53f9daa34cd 100644 --- a/crates/planner/src/logical/expr.rs +++ b/crates/planner/src/logical/expr.rs @@ -1,12 +1,15 @@ use std::fmt::{Display, Formatter}; use std::sync::Arc; +use spacetimedb_lib::AlgebraicValue; use spacetimedb_sats::algebraic_type::fmt::{fmt_algebraic_type, fmt_product_type}; use spacetimedb_sats::AlgebraicType; use spacetimedb_sats::ProductType; use spacetimedb_schema::schema::{ColumnSchema, TableSchema}; use spacetimedb_sql_parser::ast::BinOp; +use crate::static_assert_size; + use super::bind::TypingResult; use super::errors::{ConstraintViolation, ResolutionError, TypingError}; @@ -18,11 +21,13 @@ pub enum Type { /// A derived relation Row(ProductType), /// A join relation - Tup(Vec), + Tup(Box<[Type]>), /// A column type Alg(AlgebraicType), } +static_assert_size!(Type, 24); + impl Display for Type { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { @@ -90,11 +95,11 @@ pub enum RelExpr { /// A base table RelVar(Arc, Type), /// A filter - Select(Select), + Select(Box