diff --git a/partiql-eval/src/eval/eval_expr_wrapper.rs b/partiql-eval/src/eval/eval_expr_wrapper.rs index 71acd48f..1fd5b3fb 100644 --- a/partiql-eval/src/eval/eval_expr_wrapper.rs +++ b/partiql-eval/src/eval/eval_expr_wrapper.rs @@ -4,7 +4,7 @@ use crate::eval::expr::{BindError, EvalExpr}; use crate::eval::EvalContext; use itertools::Itertools; -use partiql_types::{PartiqlShape, Static, TYPE_DYNAMIC}; +use partiql_types::{PartiqlShape, Static, StaticCategory, TYPE_DYNAMIC}; use partiql_value::Value::{Missing, Null}; use partiql_value::{Tuple, Value}; @@ -14,44 +14,66 @@ use std::hash::Hash; use std::marker::PhantomData; +use partiql_value::datum::{DatumCategory, DatumCategoryRef, DatumValueRef}; use std::ops::ControlFlow; -// TODO replace with type system's subsumption once it is in place -#[inline] -pub(crate) fn subsumes(typ: &PartiqlShape, value: &Value) -> bool { - match (typ, value) { - (_, Value::Null) => true, - (_, Value::Missing) => true, - (PartiqlShape::Dynamic, _) => true, - (PartiqlShape::AnyOf(anyof), val) => anyof.types().any(|typ| subsumes(typ, val)), - (PartiqlShape::Static(s), val) => match (s.ty(), val) { - ( - Static::Int | Static::Int8 | Static::Int16 | Static::Int32 | Static::Int64, - Value::Integer(_), - ) => true, - (Static::Bool, Value::Boolean(_)) => true, - (Static::Decimal | Static::DecimalP(_, _), Value::Decimal(_)) => true, - (Static::Float32 | Static::Float64, Value::Real(_)) => true, - ( - Static::String | Static::StringFixed(_) | Static::StringVarying(_), - Value::String(_), - ) => true, - (Static::Struct(_), Value::Tuple(_)) => true, - (Static::Bag(b_type), Value::Bag(b_values)) => { - let bag_element_type = b_type.element_type(); - let mut b_values = b_values.iter(); - b_values.all(|b_value| subsumes(bag_element_type, b_value)) - } - (Static::DateTime, Value::DateTime(_)) => true, +/// A Trait that represents the ability to match an expected 'type' judgement against a provided value. +trait TypeSatisfier { + /// Returns true if the provided [`Value`] satisfies this type expectation. + fn satisfies(&self, value: &Value) -> bool; +} - (Static::Array(a_type), Value::List(l_values)) => { - let array_element_type = a_type.element_type(); - let mut l_values = l_values.iter(); - l_values.all(|l_value| subsumes(array_element_type, l_value)) +/// Type subsumbtion for [`Static`] +impl TypeSatisfier for Static { + fn satisfies(&self, value: &Value) -> bool { + match (self.category(), value.category()) { + (_, DatumCategoryRef::Null) => true, + (_, DatumCategoryRef::Missing) => true, + (StaticCategory::Scalar(ty), DatumCategoryRef::Scalar(scalar)) => match scalar { + DatumValueRef::Value(scalar) => { + matches!( + (ty, scalar), + ( + Static::Int + | Static::Int8 + | Static::Int16 + | Static::Int32 + | Static::Int64, + Value::Integer(_), + ) | (Static::Bool, Value::Boolean(_)) + | (Static::Decimal | Static::DecimalP(_, _), Value::Decimal(_)) + | (Static::Float32 | Static::Float64, Value::Real(_)) + | ( + Static::String | Static::StringFixed(_) | Static::StringVarying(_), + Value::String(_), + ) + | (Static::DateTime, Value::DateTime(_)) + ) + } + }, + (StaticCategory::Sequence(shape), DatumCategoryRef::Sequence(seq)) => match shape { + PartiqlShape::Dynamic | PartiqlShape::Undefined => true, + shape => seq.into_iter().all(|v| shape.satisfies(v)), + }, + (StaticCategory::Tuple(), DatumCategoryRef::Tuple(_)) => { + true // TODO when Static typing knows how to type a tuple } _ => false, - }, - _ => false, + } + } +} + +/// Type subsumbtion for [`PartiqlShape`] +impl TypeSatisfier for PartiqlShape { + fn satisfies(&self, value: &Value) -> bool { + match (self, value) { + (_, Value::Null) => true, + (_, Value::Missing) => true, + (PartiqlShape::Dynamic, _) => true, + (PartiqlShape::AnyOf(anyof), val) => anyof.types().any(|typ| typ.satisfies(val)), + (PartiqlShape::Static(s), val) => s.ty().satisfies(val), + _ => false, + } } } @@ -187,7 +209,7 @@ impl ArgChecker Missing => ArgCheckControlFlow::Propagate(OnMissing::propagate()), Null => ArgCheckControlFlow::Propagate(Null), val => { - if subsumes(typ, val) { + if typ.satisfies(val) { ArgCheckControlFlow::Continue(arg) } else { err() @@ -322,7 +344,6 @@ where for (idx, arg) in args.iter().enumerate() { let typ = types(idx); let arg = arg.evaluate(bindings, ctx); - match ArgC::arg_check(typ, arg) { ArgCheckControlFlow::Continue(v) => { if propagate.is_none() { diff --git a/partiql-types/src/lib.rs b/partiql-types/src/lib.rs index 371c519d..16b02754 100644 --- a/partiql-types/src/lib.rs +++ b/partiql-types/src/lib.rs @@ -742,6 +742,12 @@ pub enum Static { // TODO Add BitString, ByteString, Blob, Clob, and Graph types } +pub enum StaticCategory<'a> { + Tuple(), + Sequence(&'a PartiqlShape), + Scalar(&'a Static), +} + impl Static { pub fn is_scalar(&self) -> bool { !matches!(self, Static::Struct(_) | Static::Bag(_) | Static::Array(_)) @@ -754,6 +760,15 @@ impl Static { pub fn is_struct(&self) -> bool { matches!(self, Static::Struct(_)) } + + pub fn category(&self) -> StaticCategory<'_> { + match self { + Static::Struct(_) => StaticCategory::Tuple(), + Static::Bag(b) => StaticCategory::Sequence(b.element_type()), + Static::Array(b) => StaticCategory::Sequence(b.element_type()), + _ => StaticCategory::Scalar(self), + } + } } // TODO, this should probably be via a prettyprint... diff --git a/partiql-value/src/datum.rs b/partiql-value/src/datum.rs index cd1a89d4..8464087f 100644 --- a/partiql-value/src/datum.rs +++ b/partiql-value/src/datum.rs @@ -1,4 +1,6 @@ -use crate::{Bag, BindingsName, List, Tuple, Value}; +use crate::{ + Bag, BagIntoIterator, BagIter, BindingsName, List, ListIntoIterator, ListIter, Tuple, Value, +}; use std::borrow::Cow; use std::error::Error; @@ -194,11 +196,13 @@ pub trait SequenceDatum { } } -pub trait RefSequenceView<'a, DV: DatumValue>: SequenceDatum { +pub trait RefSequenceView<'a, DV: DatumValue + 'a>: + SequenceDatum + IntoIterator +{ fn get_val(&self, k: i64) -> Option>; } -pub trait OwnedSequenceView>: SequenceDatum { +pub trait OwnedSequenceView>: SequenceDatum + IntoIterator { fn take_val(self, k: i64) -> Option; fn take_val_boxed(self: Box, k: i64) -> Option; } @@ -253,3 +257,59 @@ impl OwnedSequenceView for DatumSeqOwned { self.take_val(k) } } + +impl<'a> IntoIterator for DatumSeqRef<'a> { + type Item = &'a Value; + type IntoIter = DatumSeqRefIterator<'a>; + + fn into_iter(self) -> Self::IntoIter { + match self { + DatumSeqRef::List(l) => DatumSeqRefIterator::List(l.into_iter()), + DatumSeqRef::Bag(b) => DatumSeqRefIterator::Bag(b.into_iter()), + } + } +} + +pub enum DatumSeqRefIterator<'a> { + List(ListIter<'a>), + Bag(BagIter<'a>), +} + +impl<'a> Iterator for DatumSeqRefIterator<'a> { + type Item = &'a Value; + + fn next(&mut self) -> Option { + match self { + DatumSeqRefIterator::List(l) => l.next(), + DatumSeqRefIterator::Bag(b) => b.next(), + } + } +} + +impl IntoIterator for DatumSeqOwned { + type Item = Value; + type IntoIter = DatumSeqOwnedIterator; + + fn into_iter(self) -> Self::IntoIter { + match self { + DatumSeqOwned::List(l) => DatumSeqOwnedIterator::List(l.into_iter()), + DatumSeqOwned::Bag(b) => DatumSeqOwnedIterator::Bag(b.into_iter()), + } + } +} + +pub enum DatumSeqOwnedIterator { + List(ListIntoIterator), + Bag(BagIntoIterator), +} + +impl Iterator for DatumSeqOwnedIterator { + type Item = Value; + + fn next(&mut self) -> Option { + match self { + DatumSeqOwnedIterator::List(l) => l.next(), + DatumSeqOwnedIterator::Bag(b) => b.next(), + } + } +}