diff --git a/CHANGELOG.md b/CHANGELOG.md index 13f0d8a3..032f9b27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Changed +- *BREAKING:* Removed `NULL` and `MISSING` types from `partiql_types::PartiQLType` +- *BREAKING:* Removed `partiql_ast_passes::partiql_type` ### Added diff --git a/partiql-ast-passes/src/lib.rs b/partiql-ast-passes/src/lib.rs index 9b57cd02..535f4cb4 100644 --- a/partiql-ast-passes/src/lib.rs +++ b/partiql-ast-passes/src/lib.rs @@ -9,4 +9,3 @@ pub mod error; pub mod name_resolver; -pub mod partiql_typer; diff --git a/partiql-ast-passes/src/partiql_typer.rs b/partiql-ast-passes/src/partiql_typer.rs deleted file mode 100644 index 40ee31f5..00000000 --- a/partiql-ast-passes/src/partiql_typer.rs +++ /dev/null @@ -1,267 +0,0 @@ -use crate::error::{AstTransformError, AstTransformationError}; -use partiql_ast::ast::{ - AstNode, AstTypeMap, Bag, Expr, List, Lit, NodeId, Query, QuerySet, Struct, TopLevelQuery, -}; -use partiql_ast::visit::{Traverse, Visit, Visitor}; -use partiql_catalog::Catalog; -use partiql_types::{ArrayType, BagType, PartiqlType, StructType, TypeKind}; - -#[derive(Debug, Clone)] -#[allow(dead_code)] -pub struct AstPartiqlTyper<'c> { - id_stack: Vec, - container_stack: Vec>, - errors: Vec, - type_map: AstTypeMap, - catalog: &'c dyn Catalog, -} - -impl<'c> AstPartiqlTyper<'c> { - pub fn new(catalog: &'c dyn Catalog) -> Self { - AstPartiqlTyper { - id_stack: Default::default(), - container_stack: Default::default(), - errors: Default::default(), - type_map: Default::default(), - catalog, - } - } - - pub fn type_nodes( - mut self, - query: &AstNode, - ) -> Result, AstTransformationError> { - query.visit(&mut self); - if self.errors.is_empty() { - Ok(self.type_map) - } else { - Err(AstTransformationError { - errors: self.errors, - }) - } - } - - #[inline] - fn current_node(&self) -> &NodeId { - self.id_stack.last().unwrap() - } -} - -impl<'c, 'ast> Visitor<'ast> for AstPartiqlTyper<'c> { - fn enter_ast_node(&mut self, id: NodeId) -> Traverse { - self.id_stack.push(id); - Traverse::Continue - } - - fn exit_ast_node(&mut self, id: NodeId) -> Traverse { - assert_eq!(self.id_stack.pop(), Some(id)); - Traverse::Continue - } - - fn enter_query(&mut self, _query: &'ast Query) -> Traverse { - Traverse::Continue - } - - fn exit_query(&mut self, _query: &'ast Query) -> Traverse { - Traverse::Continue - } - - fn enter_query_set(&mut self, _query_set: &'ast QuerySet) -> Traverse { - match _query_set { - QuerySet::BagOp(_) => { - todo!() - } - QuerySet::Select(_) => {} - QuerySet::Expr(_) => {} - QuerySet::Values(_) => { - todo!() - } - QuerySet::Table(_) => { - todo!() - } - } - Traverse::Continue - } - - fn exit_query_set(&mut self, _query_set: &'ast QuerySet) -> Traverse { - Traverse::Continue - } - - fn enter_expr(&mut self, _expr: &'ast Expr) -> Traverse { - Traverse::Continue - } - - fn exit_expr(&mut self, _expr: &'ast Expr) -> Traverse { - Traverse::Continue - } - - fn enter_lit(&mut self, _lit: &'ast Lit) -> Traverse { - // Currently we're assuming no-schema, hence typing to arbitrary sized scalars. - // TODO type to the corresponding scalar with the introduction of schema - let kind = match _lit { - Lit::Null => TypeKind::Null, - Lit::Missing => TypeKind::Missing, - Lit::Int8Lit(_) => TypeKind::Int, - Lit::Int16Lit(_) => TypeKind::Int, - Lit::Int32Lit(_) => TypeKind::Int, - Lit::Int64Lit(_) => TypeKind::Int, - Lit::DecimalLit(_) => TypeKind::Decimal, - Lit::NumericLit(_) => TypeKind::Decimal, - Lit::RealLit(_) => TypeKind::Float64, - Lit::FloatLit(_) => TypeKind::Float64, - Lit::DoubleLit(_) => TypeKind::Float64, - Lit::BoolLit(_) => TypeKind::Bool, - Lit::IonStringLit(_) => todo!(), - Lit::CharStringLit(_) => TypeKind::String, - Lit::NationalCharStringLit(_) => TypeKind::String, - Lit::BitStringLit(_) => todo!(), - Lit::HexStringLit(_) => todo!(), - Lit::StructLit(_) => TypeKind::Struct(StructType::new_any()), - Lit::ListLit(_) => TypeKind::Array(ArrayType::new_any()), - Lit::BagLit(_) => TypeKind::Bag(BagType::new_any()), - Lit::TypedLit(_, _) => todo!(), - }; - - let ty = PartiqlType::new(kind); - let id = *self.current_node(); - if let Some(c) = self.container_stack.last_mut() { - c.push(ty.clone()); - } - self.type_map.insert(id, ty); - Traverse::Continue - } - - fn enter_struct(&mut self, _struct: &'ast Struct) -> Traverse { - self.container_stack.push(vec![]); - Traverse::Continue - } - - fn exit_struct(&mut self, _struct: &'ast Struct) -> Traverse { - let id = *self.current_node(); - let fields = self.container_stack.pop(); - - // Such type checking will very likely move to a common module - // TODO move to a more appropriate place for re-use. - if let Some(f) = fields { - // We already fail during parsing if the struct has wrong number of key-value pairs, e.g.: - // {'a', 1, 'b'} - // However, adding this check here. - let is_malformed = f.len() % 2 > 0; - if is_malformed { - self.errors.push(AstTransformError::IllegalState( - "Struct key-value pairs are malformed".to_string(), - )); - } - - let has_invalid_keys = f.chunks(2).map(|t| &t[0]).any(|t| !t.is_string()); - if has_invalid_keys || is_malformed { - self.errors.push(AstTransformError::IllegalState( - "Struct keys can only resolve to `String` type".to_string(), - )); - } - } - - let ty = PartiqlType::new_struct(StructType::new_any()); - self.type_map.insert(id, ty.clone()); - - if let Some(c) = self.container_stack.last_mut() { - c.push(ty); - } - - Traverse::Continue - } - - fn enter_bag(&mut self, _bag: &'ast Bag) -> Traverse { - self.container_stack.push(vec![]); - Traverse::Continue - } - - fn exit_bag(&mut self, _bag: &'ast Bag) -> Traverse { - // TODO add schema validation of BAG elements, e.g. for Schema Bag if there is at least - // one element that isn't INT there is a type checking error. - - // TODO clarify if we need to record the internal types of bag literal or stick w/Schema? - self.container_stack.pop(); - - let id = *self.current_node(); - let ty = PartiqlType::new_bag(BagType::new_any()); - - self.type_map.insert(id, ty.clone()); - if let Some(s) = self.container_stack.last_mut() { - s.push(ty); - } - Traverse::Continue - } - - fn enter_list(&mut self, _list: &'ast List) -> Traverse { - self.container_stack.push(vec![]); - Traverse::Continue - } - - fn exit_list(&mut self, _list: &'ast List) -> Traverse { - // TODO clarify if we need to record the internal types of array literal or stick w/Schema? - // one element that isn't INT there is a type checking error. - - // TODO clarify if we need to record the internal types of array literal or stick w/Schema? - self.container_stack.pop(); - - let id = *self.current_node(); - let ty = PartiqlType::new_array(ArrayType::new_any()); - - self.type_map.insert(id, ty.clone()); - if let Some(s) = self.container_stack.last_mut() { - s.push(ty); - } - Traverse::Continue - } -} - -#[cfg(test)] -mod tests { - use super::*; - use assert_matches::assert_matches; - use partiql_catalog::PartiqlCatalog; - use partiql_types::{PartiqlType, TypeKind}; - - #[test] - fn simple_test() { - assert_matches!(run_literal_test("NULL"), TypeKind::Null); - assert_matches!(run_literal_test("MISSING"), TypeKind::Missing); - assert_matches!(run_literal_test("Missing"), TypeKind::Missing); - assert_matches!(run_literal_test("true"), TypeKind::Bool); - assert_matches!(run_literal_test("false"), TypeKind::Bool); - assert_matches!(run_literal_test("1"), TypeKind::Int); - assert_matches!(run_literal_test("1.5"), TypeKind::Decimal); - assert_matches!(run_literal_test("'hello world!'"), TypeKind::String); - assert_matches!(run_literal_test("[1, 2 , {'a': 2}]"), TypeKind::Array(_)); - assert_matches!(run_literal_test("<<'1', {'a': 11}>>"), TypeKind::Bag(_)); - assert_matches!( - run_literal_test("{'a': 1, 'b': 3, 'c': [1, 2]}"), - TypeKind::Struct(_) - ); - } - - #[test] - fn simple_err_test() { - assert!(type_statement("{'a': 1, a.b: 3}", &PartiqlCatalog::default()).is_err()); - } - - fn run_literal_test(q: &str) -> TypeKind { - let out = type_statement(q, &PartiqlCatalog::default()).expect("type map"); - let values: Vec<&PartiqlType> = out.values().collect(); - values.last().unwrap().kind().clone() - } - - fn type_statement( - q: &str, - catalog: &dyn Catalog, - ) -> Result, AstTransformationError> { - let parsed = partiql_parser::Parser::default() - .parse(q) - .expect("Expect successful parse"); - - let typer = AstPartiqlTyper::new(catalog); - let q = &parsed.ast; - typer.type_nodes(q) - } -} diff --git a/partiql-eval/src/eval/eval_expr_wrapper.rs b/partiql-eval/src/eval/eval_expr_wrapper.rs index 95f8db16..542936aa 100644 --- a/partiql-eval/src/eval/eval_expr_wrapper.rs +++ b/partiql-eval/src/eval/eval_expr_wrapper.rs @@ -407,25 +407,25 @@ where impl UnaryValueExpr { #[allow(dead_code)] #[inline] - pub(crate) fn create_with_any( + pub(crate) fn create_with_any( args: Vec>, f: F, ) -> Result, BindError> where - F: Fn(&Value) -> Value, + F: 'static + Fn(&Value) -> Value, { Self::create_typed::([TYPE_ANY; 1], args, f) } #[allow(dead_code)] #[inline] - pub(crate) fn create_typed( + pub(crate) fn create_typed( types: [PartiqlType; 1], args: Vec>, f: F, ) -> Result, BindError> where - F: Fn(&Value) -> Value, + F: 'static + Fn(&Value) -> Value, { type Check = DefaultArgChecker>; Self::create_checked::<{ STRICT }, Check, F>(types, args, f) @@ -433,13 +433,13 @@ impl UnaryValueExpr { #[allow(dead_code)] #[inline] - pub(crate) fn create_checked( + pub(crate) fn create_checked( types: [PartiqlType; 1], args: Vec>, f: F, ) -> Result, BindError> where - F: Fn(&Value) -> Value, + F: 'static + Fn(&Value) -> Value, ArgC: 'static + ArgChecker, { EvalExprWrapper::create_checked::<{ STRICT }, 1, ArgC>(Self::default(), types, args, f) @@ -471,25 +471,25 @@ where impl BinaryValueExpr { #[allow(dead_code)] #[inline] - pub(crate) fn create_with_any( + pub(crate) fn create_with_any( args: Vec>, f: F, ) -> Result, BindError> where - F: Fn(&Value, &Value) -> Value, + F: 'static + Fn(&Value, &Value) -> Value, { Self::create_typed::([TYPE_ANY; 2], args, f) } #[allow(dead_code)] #[inline] - pub(crate) fn create_typed( + pub(crate) fn create_typed( types: [PartiqlType; 2], args: Vec>, f: F, ) -> Result, BindError> where - F: Fn(&Value, &Value) -> Value, + F: 'static + Fn(&Value, &Value) -> Value, { type Check = DefaultArgChecker>; Self::create_checked::<{ STRICT }, Check, F>(types, args, f) @@ -497,13 +497,13 @@ impl BinaryValueExpr { #[allow(dead_code)] #[inline] - pub(crate) fn create_checked( + pub(crate) fn create_checked( types: [PartiqlType; 2], args: Vec>, f: F, ) -> Result, BindError> where - F: Fn(&Value, &Value) -> Value, + F: 'static + Fn(&Value, &Value) -> Value, ArgC: 'static + ArgChecker, { EvalExprWrapper::create_checked::<{ STRICT }, 2, ArgC>(Self::default(), types, args, f) @@ -535,25 +535,25 @@ where impl TernaryValueExpr { #[allow(dead_code)] #[inline] - pub(crate) fn create_with_any( + pub(crate) fn create_with_any( args: Vec>, f: F, ) -> Result, BindError> where - F: Fn(&Value, &Value, &Value) -> Value, + F: 'static + Fn(&Value, &Value, &Value) -> Value, { Self::create_typed::([TYPE_ANY; 3], args, f) } #[allow(dead_code)] #[inline] - pub(crate) fn create_typed( + pub(crate) fn create_typed( types: [PartiqlType; 3], args: Vec>, f: F, ) -> Result, BindError> where - F: Fn(&Value, &Value, &Value) -> Value, + F: 'static + Fn(&Value, &Value, &Value) -> Value, { type Check = DefaultArgChecker>; Self::create_checked::<{ STRICT }, Check, F>(types, args, f) @@ -561,13 +561,13 @@ impl TernaryValueExpr { #[allow(dead_code)] #[inline] - pub(crate) fn create_checked( + pub(crate) fn create_checked( types: [PartiqlType; 3], args: Vec>, f: F, ) -> Result, BindError> where - F: Fn(&Value, &Value, &Value) -> Value, + F: 'static + Fn(&Value, &Value, &Value) -> Value, ArgC: 'static + ArgChecker, { EvalExprWrapper::create_checked::<{ STRICT }, 3, ArgC>(Self::default(), types, args, f) @@ -604,25 +604,25 @@ where impl QuaternaryValueExpr { #[allow(dead_code)] #[inline] - pub(crate) fn create_with_any( + pub(crate) fn create_with_any( args: Vec>, f: F, ) -> Result, BindError> where - F: Fn(&Value, &Value, &Value, &Value) -> Value, + F: 'static + Fn(&Value, &Value, &Value, &Value) -> Value, { Self::create_typed::([TYPE_ANY; 4], args, f) } #[allow(dead_code)] #[inline] - pub(crate) fn create_typed( + pub(crate) fn create_typed( types: [PartiqlType; 4], args: Vec>, f: F, ) -> Result, BindError> where - F: Fn(&Value, &Value, &Value, &Value) -> Value, + F: 'static + Fn(&Value, &Value, &Value, &Value) -> Value, { type Check = DefaultArgChecker>; Self::create_checked::<{ STRICT }, Check, F>(types, args, f) @@ -630,13 +630,13 @@ impl QuaternaryValueExpr { #[allow(dead_code)] #[inline] - pub(crate) fn create_checked( + pub(crate) fn create_checked( types: [PartiqlType; 4], args: Vec>, f: F, ) -> Result, BindError> where - F: Fn(&Value, &Value, &Value, &Value) -> Value, + F: 'static + Fn(&Value, &Value, &Value, &Value) -> Value, ArgC: 'static + ArgChecker, { EvalExprWrapper::create_checked::<{ STRICT }, 4, ArgC>(Self::default(), types, args, f) diff --git a/partiql-logical-planner/src/typer.rs b/partiql-logical-planner/src/typer.rs index b1617d57..d1f8d16f 100644 --- a/partiql-logical-planner/src/typer.rs +++ b/partiql-logical-planner/src/typer.rs @@ -4,8 +4,8 @@ use partiql_ast::ast::{CaseSensitivity, SymbolPrimitive}; use partiql_catalog::Catalog; use partiql_logical::{BindingsOp, LogicalPlan, OpId, PathComponent, ValueExpr, VarRefType}; use partiql_types::{ - any, missing, null, undefined, ArrayType, BagType, PartiqlType, StructConstraint, StructField, - StructType, TypeKind, + any, undefined, ArrayType, BagType, PartiqlType, StructConstraint, StructField, StructType, + TypeKind, }; use partiql_value::{BindingsName, Value}; use petgraph::algo::toposort; @@ -318,8 +318,8 @@ impl<'c> PlanTyper<'c> { } ValueExpr::Lit(v) => { let kind = match **v { - Value::Null => TypeKind::Null, - Value::Missing => TypeKind::Missing, + Value::Null => TypeKind::Undefined, + Value::Missing => TypeKind::Undefined, Value::Integer(_) => TypeKind::Int, Value::Decimal(_) => TypeKind::Decimal, Value::Boolean(_) => TypeKind::Bool, @@ -419,7 +419,7 @@ impl<'c> PlanTyper<'c> { Some(any!()) } else { match &self.typing_mode { - TypingMode::Permissive => Some(missing!()), + TypingMode::Permissive => Some(undefined!()), TypingMode::Strict => { self.errors.push(TypingError::TypeCheck(format!( "No Typing Information for {:?} in closed Schema {:?}", @@ -512,7 +512,7 @@ impl<'c> PlanTyper<'c> { if let TypingMode::Permissive = &self.typing_mode { // TODO Revise this once the following discussion is conclusive and spec. is // in place: https://github.com/partiql/partiql-spec/discussions/64 - let type_ctx = ty_ctx![(&ty_env![(key.clone(), null!())], &undefined!())]; + let type_ctx = ty_ctx![(&ty_env![(key.clone(), undefined!())], &undefined!())]; self.type_env_stack.push(type_ctx); } @@ -662,7 +662,7 @@ mod tests { vec![ StructField::new("id", int!()), StructField::new("name", str!()), - StructField::new("age", null!()), + StructField::new("age", undefined!()), ], ) .expect("Type"); diff --git a/partiql-types/src/lib.rs b/partiql-types/src/lib.rs index 701c1f1e..56e989c5 100644 --- a/partiql-types/src/lib.rs +++ b/partiql-types/src/lib.rs @@ -17,20 +17,6 @@ macro_rules! any { }; } -#[macro_export] -macro_rules! null { - () => { - $crate::PartiqlType::new($crate::TypeKind::Null) - }; -} - -#[macro_export] -macro_rules! missing { - () => { - $crate::PartiqlType::new($crate::TypeKind::Missing) - }; -} - #[macro_export] macro_rules! int { () => { @@ -149,10 +135,6 @@ pub enum TypeKind { Any, AnyOf(AnyOf), - // Absent Types - Null, - Missing, - // Scalar Types Int, Int8, @@ -191,8 +173,6 @@ impl Display for TypeKind { anyof.types.iter().map(PartiqlType::kind).join(",") ) } - TypeKind::Null => "Null".to_string(), - TypeKind::Missing => "Missing".to_string(), TypeKind::Int => "Int".to_string(), TypeKind::Int8 => "Int8".to_string(), TypeKind::Int16 => "Int16".to_string(), @@ -223,8 +203,6 @@ impl Display for TypeKind { } pub const TYPE_ANY: PartiqlType = PartiqlType::new(TypeKind::Any); -pub const TYPE_NULL: PartiqlType = PartiqlType::new(TypeKind::Null); -pub const TYPE_MISSING: PartiqlType = PartiqlType::new(TypeKind::Missing); pub const TYPE_BOOL: PartiqlType = PartiqlType::new(TypeKind::Bool); pub const TYPE_INT: PartiqlType = PartiqlType::new(TypeKind::Int); pub const TYPE_INT8: PartiqlType = PartiqlType::new(TypeKind::Int8);