Skip to content

Commit

Permalink
Add lowering of Variants
Browse files Browse the repository at this point in the history
  • Loading branch information
jpschorr committed Feb 3, 2025
1 parent c4aba69 commit 6535718
Show file tree
Hide file tree
Showing 13 changed files with 325 additions and 86 deletions.
1 change: 1 addition & 0 deletions extension/partiql-extension-ion/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ bench = false
[dependencies]
partiql-value = { path = "../../partiql-value", version = "0.11.*" }
partiql-common = { path = "../../partiql-common", version = "0.11.*" }
partiql-types = { path = "../../partiql-types", version = "0.11.*" }

serde = { version = "1", features = ["derive"], optional = true }
typetag = { version = "0.2", optional = true }
Expand Down
80 changes: 60 additions & 20 deletions extension/partiql-extension-ion/src/boxed_ion.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
use crate::util::{PartiqlValueTarget, ToPartiqlValue};
use ion_rs::{
AnyEncoding, Element, ElementReader, IonResult, IonType, OwnedSequenceIterator, Reader,
Sequence,
};
use ion_rs_old::IonReader;
use partiql_value::boxed_variant::{
BoxedVariant, BoxedVariantResult, BoxedVariantType, BoxedVariantValueIntoIterator,
};
use partiql_value::datum::{
Datum, DatumCategoryOwned, DatumCategoryRef, DatumSeqOwned, DatumSeqRef, DatumTupleOwned,
DatumTupleRef, DatumValueOwned, DatumValueRef, OwnedSequenceView, OwnedTupleView,
RefSequenceView, RefTupleView, SequenceDatum, TupleDatum,
Datum, DatumCategoryOwned, DatumCategoryRef, DatumLower, DatumLowerResult, DatumSeqOwned,
DatumSeqRef, DatumTupleOwned, DatumTupleRef, DatumValueOwned, DatumValueRef, OwnedSequenceView,
OwnedTupleView, RefSequenceView, RefTupleView, SequenceDatum, TupleDatum,
};
use partiql_value::{BindingsName, Value, Variant};
use partiql_value::{Bag, BindingsName, List, Tuple, Value, Variant};
use peekmore::{PeekMore, PeekMoreIterator};
#[cfg(feature = "serde")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};
Expand Down Expand Up @@ -109,7 +109,7 @@ impl<'de> Deserialize<'de> for BoxedIon {
}

impl Hash for BoxedIon {
fn hash<H: Hasher>(&self, state: &mut H) {
fn hash<H: Hasher>(&self, _: &mut H) {
todo!("BoxedIon.hash")
}
}
Expand All @@ -126,23 +126,21 @@ impl BoxedVariant for BoxedIon {
fn category(&self) -> DatumCategoryRef<'_> {
match &self.doc {
BoxedIonValue::Stream() => DatumCategoryRef::Sequence(DatumSeqRef::Dynamic(self)),
BoxedIonValue::Sequence(seq) => DatumCategoryRef::Sequence(DatumSeqRef::Dynamic(self)),
BoxedIonValue::Sequence(_) => DatumCategoryRef::Sequence(DatumSeqRef::Dynamic(self)),
BoxedIonValue::Value(elt) => match elt.ion_type() {
IonType::List => DatumCategoryRef::Sequence(DatumSeqRef::Dynamic(self)),
IonType::SExp => DatumCategoryRef::Sequence(DatumSeqRef::Dynamic(self)),
IonType::Null => DatumCategoryRef::Null,
IonType::Struct => DatumCategoryRef::Tuple(DatumTupleRef::Dynamic(self)),
_ => DatumCategoryRef::Scalar(DatumValueRef::Value(todo!(
"Boxed Ion DatumCategoryRef::Scalar"
))),
_ => DatumCategoryRef::Scalar(DatumValueRef::Lower(self)),
},
}
}

fn into_category(self: Box<Self>) -> DatumCategoryOwned {
match &self.doc {
BoxedIonValue::Stream() => DatumCategoryOwned::Sequence(DatumSeqOwned::Dynamic(self)),
BoxedIonValue::Sequence(seq) => {
BoxedIonValue::Sequence(_) => {
DatumCategoryOwned::Sequence(DatumSeqOwned::Dynamic(self))
}
BoxedIonValue::Value(elt) => match elt.ion_type() {
Expand All @@ -156,6 +154,44 @@ impl BoxedVariant for BoxedIon {
}
}

impl DatumLower<Value> for BoxedIon {
fn into_lower(self) -> DatumLowerResult<Value> {
let Self { ctx, doc } = self;
match doc {
BoxedIonValue::Stream() => todo!("into_lower stream"),
BoxedIonValue::Sequence(_) => todo!("into_lower seq"),
BoxedIonValue::Value(elt) => {
let pval = elt.into_partiql_value()?;
Ok(match pval {
PartiqlValueTarget::Atom(val) => val,
PartiqlValueTarget::List(l) => {
let vals = l.into_iter().map(|elt| Self::new_value(elt, ctx.clone()));
List::from_iter(vals).into()
}
PartiqlValueTarget::Bag(b) => {
let vals = b.into_iter().map(|elt| Self::new_value(elt, ctx.clone()));
Bag::from_iter(vals).into()
}
PartiqlValueTarget::Struct(s) => {
let vals = s
.into_iter()
.map(|(key, elt)| (key, Self::new_value(elt, ctx.clone())));
Tuple::from_iter(vals).into()
}
})
}
}
}

fn into_lower_boxed(self: Box<Self>) -> DatumLowerResult<Value> {
self.into_lower()
}

fn lower(&self) -> DatumLowerResult<Cow<'_, Value>> {
self.clone().into_lower().map(Cow::Owned)
}
}

impl SequenceDatum for BoxedIon {
fn is_ordered(&self) -> bool {
true
Expand All @@ -169,7 +205,7 @@ impl SequenceDatum for BoxedIon {
BoxedIonValue::Sequence(seq) => seq.len(),
BoxedIonValue::Value(elt) => match elt.expect_sequence() {
Ok(seq) => seq.len(), // TODO
Err(e) => todo!(),
Err(_) => todo!(),
},
}
}
Expand All @@ -189,7 +225,7 @@ impl<'a> RefSequenceView<'a, Value> for BoxedIon {
.iter()
.nth(k as usize)
.map(|elt| Cow::Owned(self.child_value(elt.clone()))), // TODO remove clone
Err(e) => todo!(),
Err(_) => todo!(),
},
}
}
Expand All @@ -211,14 +247,18 @@ impl OwnedSequenceView<Value> for BoxedIon {
.into_iter()
.nth(k as usize)
.map(|elt| Self::new_value(elt, ctx)),
Err(e) => todo!(),
Err(_) => todo!(),
},
}
}

fn take_val_boxed(self: Box<Self>, k: i64) -> Option<Value> {
OwnedSequenceView::take_val(*self, k)
}

fn into_iter_boxed(self: Box<Self>) -> Box<dyn Iterator<Item = Value>> {
todo!()
}
}

impl TupleDatum for BoxedIon {
Expand All @@ -227,12 +267,12 @@ impl TupleDatum for BoxedIon {
BoxedIonValue::Stream() => {
todo!()
}
BoxedIonValue::Sequence(seq) => {
BoxedIonValue::Sequence(_) => {
todo!()
}
BoxedIonValue::Value(elt) => match elt.expect_struct() {
Ok(strct) => strct.len(),
Err(e) => todo!(),
Err(_) => todo!(),
},
}
}
Expand All @@ -246,7 +286,7 @@ impl<'a> RefTupleView<'a, Value> for BoxedIon {
BoxedIonValue::Stream() => {
todo!()
}
BoxedIonValue::Sequence(seq) => {
BoxedIonValue::Sequence(_) => {
todo!()
}
BoxedIonValue::Value(elt) => match elt.expect_struct() {
Expand All @@ -260,7 +300,7 @@ impl<'a> RefTupleView<'a, Value> for BoxedIon {
}
None
}
Err(e) => todo!(),
Err(_) => todo!(),
},
}
}
Expand All @@ -278,7 +318,7 @@ impl OwnedTupleView<Value> for BoxedIon {
BoxedIonValue::Stream() => {
todo!()
}
BoxedIonValue::Sequence(seq) => {
BoxedIonValue::Sequence(_) => {
todo!()
}
BoxedIonValue::Value(elt) => match elt.try_into_struct() {
Expand All @@ -292,7 +332,7 @@ impl OwnedTupleView<Value> for BoxedIon {
}
None
}
Err(e) => todo!(),
Err(_) => todo!(),
},
}
}
Expand Down
1 change: 1 addition & 0 deletions extension/partiql-extension-ion/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub mod boxed_ion;
mod common;
pub mod decode;
pub mod encode;
mod util;

pub use common::Encoding;

Expand Down
129 changes: 129 additions & 0 deletions extension/partiql-extension-ion/src/util.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
use crate::decode::IonDecodeError;
use ion_rs::{Decimal, Element, IonResult};
use partiql_value::datum::DatumLowerResult;
use partiql_value::{DateTime, Value};
use std::num::NonZeroU8;
use std::str::FromStr;

pub enum PartiqlValueTarget<T> {
Atom(Value),
List(Vec<T>),
Bag(Vec<T>),

Check warning on line 11 in extension/partiql-extension-ion/src/util.rs

View workflow job for this annotation

GitHub Actions / clippy

variant `Bag` is never constructed

warning: variant `Bag` is never constructed --> extension/partiql-extension-ion/src/util.rs:11:5 | 8 | pub enum PartiqlValueTarget<T> { | ------------------ variant in this enum ... 11 | Bag(Vec<T>), | ^^^ | = note: `#[warn(dead_code)]` on by default
Struct(Vec<(String, T)>),
}

impl<T, V> From<V> for PartiqlValueTarget<T>
where
V: Into<Value>,
{
fn from(value: V) -> Self {
PartiqlValueTarget::Atom(value.into())
}
}

pub trait ToPartiqlValue<T> {
fn into_partiql_value(self) -> DatumLowerResult<PartiqlValueTarget<T>>;
}

impl ToPartiqlValue<Element> for Element {
fn into_partiql_value(self) -> DatumLowerResult<PartiqlValueTarget<Element>> {
let value = self.into_value();
match value {
ion_rs::Value::Null(_) => Ok(Value::Null.into()),
ion_rs::Value::Bool(inner) => Ok(inner.into()),
ion_rs::Value::Int(inner) => inner.into_partiql_value(),
ion_rs::Value::Float(inner) => Ok(inner.into()),
ion_rs::Value::Decimal(inner) => inner.into_partiql_value(),
ion_rs::Value::Timestamp(inner) => inner.into_partiql_value(),
ion_rs::Value::Symbol(inner) => inner.into_partiql_value(),
ion_rs::Value::String(inner) => inner.into_partiql_value(),
ion_rs::Value::Clob(inner) => inner.into_partiql_value(),
ion_rs::Value::Blob(inner) => inner.into_partiql_value(),
ion_rs::Value::List(inner) => inner.into_partiql_value(),
ion_rs::Value::SExp(inner) => inner.into_partiql_value(),
ion_rs::Value::Struct(inner) => inner.into_partiql_value(),
}
}
}

impl ToPartiqlValue<Element> for ion_rs::Int {
fn into_partiql_value(self) -> DatumLowerResult<PartiqlValueTarget<Element>> {
if let Some(n) = self.as_i64() {
Ok(Value::from(n).into())
} else {
let large = self.as_i128().expect("ion int i128");
Ok(Value::from(large).into())
}
}
}

impl ToPartiqlValue<Element> for ion_rs::Decimal {
fn into_partiql_value(self) -> DatumLowerResult<PartiqlValueTarget<Element>> {
let dec = ion_decimal_to_decimal(&self);
Ok(dec.expect("ion decimal").into())
}
}

impl ToPartiqlValue<Element> for ion_rs::Timestamp {
fn into_partiql_value(self) -> DatumLowerResult<PartiqlValueTarget<Element>> {
let ts = self;
let offset = ts.offset();
let datetime = DateTime::from_ymdhms_nano_offset_minutes(
ts.year() as i32,
NonZeroU8::new(ts.month() as u8).ok_or(IonDecodeError::ConversionError(
"month outside of range".into(),
))?,
ts.day() as u8,
ts.hour() as u8,
ts.minute() as u8,
ts.second() as u8,
ts.nanoseconds(),
offset,
);
Ok(datetime.into())
}
}

impl ToPartiqlValue<Element> for ion_rs::Symbol {
fn into_partiql_value(self) -> DatumLowerResult<PartiqlValueTarget<Element>> {
Ok(self.expect_text()?.into())
}
}

impl ToPartiqlValue<Element> for ion_rs::Str {
fn into_partiql_value(self) -> DatumLowerResult<PartiqlValueTarget<Element>> {
Ok(self.text().into())
}
}

impl ToPartiqlValue<Element> for ion_rs::Bytes {
fn into_partiql_value(self) -> DatumLowerResult<PartiqlValueTarget<Element>> {
Ok(Value::Blob(Box::new(self.into())).into())
}
}

impl ToPartiqlValue<Element> for ion_rs::Sequence {
fn into_partiql_value(self) -> DatumLowerResult<PartiqlValueTarget<Element>> {
Ok(PartiqlValueTarget::List(
self.into_iter().collect::<Vec<_>>(),
))
}
}

impl ToPartiqlValue<Element> for ion_rs::Struct {
fn into_partiql_value(self) -> DatumLowerResult<PartiqlValueTarget<Element>> {
let data: IonResult<Vec<_>> = self
.into_iter()
.map(|(sym, elt)| sym.expect_text().map(String::from).map(|s| (s, elt)))
.collect();
Ok(PartiqlValueTarget::Struct(data?))
}
}

fn ion_decimal_to_decimal(ion_dec: &Decimal) -> Result<rust_decimal::Decimal, rust_decimal::Error> {
// TODO ion Decimal doesn't give a lot of functionality to get at the data currently
// TODO and it's not clear whether we'll continue with rust decimal or switch to big decimal
let ion_dec_str = format!("{ion_dec}").replace('d', "e");
rust_decimal::Decimal::from_str(&ion_dec_str)
.or_else(|_| rust_decimal::Decimal::from_scientific(&ion_dec_str))
}
10 changes: 9 additions & 1 deletion partiql-eval/src/eval/eval_expr_wrapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use std::hash::Hash;

use std::marker::PhantomData;

use partiql_value::datum::{DatumCategory, DatumCategoryRef, DatumValueRef};
use partiql_value::datum::{DatumCategory, DatumCategoryRef, DatumLower, DatumValueRef};
use std::ops::ControlFlow;

/// A Trait that represents the ability to match an expected 'type' judgement against a provided value.
Expand Down Expand Up @@ -50,6 +50,9 @@ impl TypeSatisfier for Static {
| (Static::DateTime, Value::DateTime(_))
)
}
DatumValueRef::Lower(lower) => {

Check warning on line 53 in partiql-eval/src/eval/eval_expr_wrapper.rs

View workflow job for this annotation

GitHub Actions / clippy

unused variable: `lower`

warning: unused variable: `lower` --> partiql-eval/src/eval/eval_expr_wrapper.rs:53:38 | 53 | DatumValueRef::Lower(lower) => { | ^^^^^ help: if this is intentional, prefix it with an underscore: `_lower` | = note: `#[warn(unused_variables)]` on by default
unreachable!("Value must be 'lower'ed before trying to satisfy")
}
},
(StaticCategory::Sequence(shape), DatumCategoryRef::Sequence(seq)) => match shape {
PartiqlShape::Dynamic | PartiqlShape::Undefined => true,
Expand Down Expand Up @@ -344,6 +347,11 @@ where
for (idx, arg) in args.iter().enumerate() {
let typ = types(idx);
let arg = arg.evaluate(bindings, ctx);
let arg = match arg {
Cow::Borrowed(arg) => arg.lower(),
Cow::Owned(arg) => arg.into_lower().map(Cow::Owned),
}
.expect("lowering failed"); // TODO proper error messaging for lowering
match ArgC::arg_check(typ, arg) {
ArgCheckControlFlow::Continue(v) => {
if propagate.is_none() {
Expand Down
Loading

0 comments on commit 6535718

Please sign in to comment.