From 843d3eedc5f25f7743c6e182769d6669d16a7dff Mon Sep 17 00:00:00 2001 From: Arash Maymandi <27716912+am357@users.noreply.github.com> Date: Fri, 16 Aug 2024 18:19:33 -0700 Subject: [PATCH] Add metas to PartiQL Common (#488) Adds an implementation for storing metadata for PartiQL objects. Currently, the implementation does not include any traits. It introduces `PartiqlMetadata` and `PartiqlMetaValue` structures: ```rust let foo_val = PartiqlMetaValue::String("foo".to_string()); let i64_val = PartiqlMetaValue::Int64(2); let expected_vec_val = vec![foo_val, i64_val]; let expected_bool_val = true; let expected_int_val = 2; let expected_float_val = 2.5; let expected_str_val = "foo"; let mut expected_map = PartiqlMetadata::new(); expected_map.insert("bool value", expected_bool_val.into()); expected_map.insert("integer value", expected_int_val.into()); let mut metas = PartiqlMetadata::new(); metas.insert("vec value", expected_vec_val.clone().into()); metas.insert("bool value", expected_bool_val.into()); metas.insert("integer value", expected_int_val.into()); metas.insert("float value", expected_float_val.into()); metas.insert("string value", expected_str_val.into()); metas.insert("map value", expected_map.clone().into()); let vec_val = metas.vec_value("vec value").expect("vec meta value"); let bool_val = metas.bool_value("bool value").expect("bool meta value"); let int_val = metas.i32_value("integer value").expect("i32 meta value"); let float_val = metas.f64_value("float value").expect("f64 meta value"); let string_val = metas.string_value("string value").expect("string meta value"); let map_val = metas.map_value("map value").expect("map meta value"); assert_eq!(vec_val, expected_vec_val.clone()); assert_eq!(bool_val, expected_bool_val.clone()); assert_eq!(int_val, expected_int_val.clone()); assert_eq!(float_val, expected_float_val.clone()); assert_eq!(string_val, expected_str_val); assert_eq!(map_val, expected_map.clone()); ``` --- CHANGELOG.md | 3 +- partiql-common/Cargo.toml | 1 + partiql-common/src/lib.rs | 1 + partiql-common/src/metadata.rs | 413 +++++++++++++++++++++++ partiql-common/src/node.rs | 1 - partiql-parser/src/parse/partiql.lalrpop | 2 +- partiql-types/src/lib.rs | 3 - 7 files changed, 418 insertions(+), 6 deletions(-) create mode 100644 partiql-common/src/metadata.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index fc612a92..f68a59b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,12 +9,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - partiql-ast: fixed pretty-printing of `PIVOT` - partiql-ast: improved pretty-printing of `CASE` and various clauses -- + ### Added - Added `partiql-common`. - Added `NodeId` to `StaticType`. - *BREAKING* Added thread-safe `PartiqlShapeBuilder` and automatic `NodeId` generation for the `StaticType`. - Added a static thread safe `shape_builder` function that provides a convenient way for using `PartiqlShapeBuilder` for creating new shapes. +- Added `partiql_common::meta::PartiqlMetadata` ### Removed - *BREAKING* Removed `partiql-source-map`. diff --git a/partiql-common/Cargo.toml b/partiql-common/Cargo.toml index 203a3b71..a20dc02d 100644 --- a/partiql-common/Cargo.toml +++ b/partiql-common/Cargo.toml @@ -23,6 +23,7 @@ bench = false indexmap = "2.2" pretty = "0.12" serde = { version = "1.*", features = ["derive"], optional = true } +rust_decimal = { version = "1.25.0", default-features = false, features = ["std"] } smallvec = { version = "1.*" } thiserror = "1.0" diff --git a/partiql-common/src/lib.rs b/partiql-common/src/lib.rs index 3f107b89..7efacd9a 100644 --- a/partiql-common/src/lib.rs +++ b/partiql-common/src/lib.rs @@ -1,4 +1,5 @@ #![deny(rust_2018_idioms)] #![deny(clippy::all)] +pub mod metadata; pub mod node; pub mod syntax; diff --git a/partiql-common/src/metadata.rs b/partiql-common/src/metadata.rs new file mode 100644 index 00000000..aad1e2a7 --- /dev/null +++ b/partiql-common/src/metadata.rs @@ -0,0 +1,413 @@ +use indexmap::map::Entry; +use indexmap::IndexMap; +use rust_decimal::Decimal; +use std::borrow::Borrow; +use std::fmt::Result; +use std::fmt::{Display, Formatter}; +use std::hash::Hash; + +/// Provides a mean to store meta-data for PartiQL objects. +/// +/// # Examples +/// ``` +/// use partiql_common::metadata::{PartiqlMetadata, PartiqlMetaValue}; +/// +/// let foo_val = PartiqlMetaValue::String("foo".to_string()); +/// let i64_val = PartiqlMetaValue::Int64(2);/// +/// let expected_vec_val = vec![foo_val, i64_val]; +/// +/// let expected_bool_val = true;/// +/// let expected_int_val = 2;/// +/// let expected_float_val = 2.5;/// +/// let expected_str_val = "foo";/// +/// +/// let mut expected_map = PartiqlMetadata::new();/// +/// expected_map.insert("bool value", expected_bool_val.into());/// +/// expected_map.insert("integer value", expected_int_val.into());/// +/// +/// let mut metas = PartiqlMetadata::new();/// +/// metas.insert("vec value", expected_vec_val.clone().into()); +/// metas.insert("bool value", expected_bool_val.into());/// +/// metas.insert("integer value", expected_int_val.into());/// +/// metas.insert("float value", expected_float_val.into());/// +/// metas.insert("string value", expected_str_val.into());/// +/// metas.insert("map value", expected_map.clone().into());/// +/// +/// let vec_val = metas.vec_value("vec value").expect("vec meta value");/// +/// let bool_val = metas.bool_value("bool value").expect("bool meta value");/// +/// let int_val = metas.i32_value("integer value").expect("i32 meta value");/// +/// let float_val = metas.f64_value("float value").expect("f64 meta value");/// +/// let string_val = metas.string_value("string value").expect("string meta value");/// +/// let map_val = metas.map_value("map value").expect("map meta value");/// +/// +/// assert_eq!(vec_val, expected_vec_val.clone());/// +/// assert_eq!(bool_val, expected_bool_val.clone());/// +/// assert_eq!(int_val, expected_int_val.clone());/// +/// assert_eq!(float_val, expected_float_val.clone());/// +/// assert_eq!(string_val, expected_str_val);/// +/// assert_eq!(map_val, expected_map.clone()); +/// ``` +#[derive(Debug, Clone, PartialEq)] +pub struct PartiqlMetadata +where + T: Eq + Clone + Hash + Borrow, +{ + inner: IndexMap>, +} + +#[allow(dead_code)] +impl PartiqlMetadata +where + T: Eq + Clone + Hash + Borrow, +{ + pub fn new() -> Self { + Self { + inner: IndexMap::new(), + } + } + + pub fn insert(&mut self, key: T, value: PartiqlMetaValue) { + self.inner.insert(key, value); + } + + pub fn get(&self, key: &T) -> Option<&PartiqlMetaValue> { + self.inner.get(key) + } + + pub fn get_mut(&mut self, key: &T) -> Option<&mut PartiqlMetaValue> { + self.inner.get_mut(key) + } + + pub fn contains_key(&self, key: &T) -> bool { + self.inner.contains_key(key) + } + + pub fn keys(&self) -> impl Iterator { + self.inner.keys() + } + + pub fn values(&self) -> impl Iterator> { + self.inner.values() + } + + pub fn values_mut(&mut self) -> impl Iterator> { + self.inner.values_mut() + } + + pub fn entry(&mut self, key: T) -> Entry<'_, T, PartiqlMetaValue> { + self.inner.entry(key) + } + + pub fn clear(&mut self) { + self.inner.clear(); + } + + pub fn remove(&mut self, key: &T) -> Option> { + self.inner.swap_remove(key) + } + + pub fn len(&self) -> usize { + self.inner.len() + } + + pub fn is_empty(&self) -> bool { + self.inner.is_empty() + } + + pub fn iter(&self) -> impl Iterator)> { + self.inner.iter() + } + + pub fn iter_mut(&mut self) -> impl Iterator)> { + self.inner.iter_mut() + } + + pub fn vec_value(&self, key: &str) -> Option>> { + let value = self.inner.get(key); + if let Some(PartiqlMetaValue::Array(v)) = value { + Some(v.clone()) + } else { + None + } + } + + pub fn bool_value(&self, key: &str) -> Option { + let value = self.inner.get(key); + if let Some(PartiqlMetaValue::Bool(v)) = value { + Some(*v) + } else { + None + } + } + + pub fn f32_value(&self, key: &str) -> Option { + let value = self.inner.get(key); + if let Some(PartiqlMetaValue::Float32(v)) = value { + Some(*v) + } else { + None + } + } + + pub fn f64_value(&self, key: &str) -> Option { + let value = self.inner.get(key); + if let Some(PartiqlMetaValue::Float64(v)) = value { + Some(*v) + } else { + None + } + } + + pub fn decimal_value(&self, key: &str) -> Option { + let value = self.inner.get(key); + if let Some(PartiqlMetaValue::Decimal(v)) = value { + Some(*v) + } else { + None + } + } + + pub fn i32_value(&self, key: &str) -> Option { + let value = self.inner.get(key); + if let Some(PartiqlMetaValue::Int32(v)) = value { + Some(*v) + } else { + None + } + } + + pub fn i64_value(&self, key: &str) -> Option { + let value = self.inner.get(key); + if let Some(PartiqlMetaValue::Int64(v)) = value { + Some(*v) + } else { + None + } + } + + pub fn map_value(&self, key: &str) -> Option> { + let value = self.inner.get(key); + if let Some(PartiqlMetaValue::Map(v)) = value { + Some(v.clone()) + } else { + None + } + } + + pub fn string_value(&self, key: &str) -> Option { + let value = self.inner.get(key); + if let Some(PartiqlMetaValue::String(v)) = value { + Some(v.clone()) + } else { + None + } + } +} + +impl Default for PartiqlMetadata +where + T: Eq + Clone + Hash + Borrow, +{ + fn default() -> Self { + Self { + inner: IndexMap::new(), + } + } +} + +impl FromIterator<(T, PartiqlMetaValue)> for PartiqlMetadata +where + T: Eq + Clone + Hash + Borrow, +{ + fn from_iter)>>(iter: I) -> Self { + let inner = iter.into_iter().collect(); + Self { inner } + } +} + +impl IntoIterator for PartiqlMetadata +where + T: Eq + Clone + Hash + Borrow, +{ + type Item = (T, PartiqlMetaValue); + type IntoIter = indexmap::map::IntoIter>; + + fn into_iter(self) -> Self::IntoIter { + self.inner.into_iter() + } +} + +#[derive(Clone, Debug, PartialEq)] +#[allow(dead_code)] +pub enum PartiqlMetaValue +where + T: Eq + Clone + Hash + Borrow, +{ + Array(Vec>), + Bool(bool), + Float32(f32), + Float64(f64), + Decimal(Decimal), + Int32(i32), + Int64(i64), + Map(PartiqlMetadata), + String(String), +} + +impl From for PartiqlMetaValue +where + T: Eq + Clone + Hash + Borrow, +{ + fn from(value: bool) -> Self { + PartiqlMetaValue::Bool(value) + } +} + +impl From for PartiqlMetaValue +where + T: Eq + Clone + Hash + Borrow, +{ + fn from(value: i32) -> Self { + PartiqlMetaValue::Int32(value) + } +} +impl From for PartiqlMetaValue +where + T: Eq + Clone + Hash + Borrow, +{ + fn from(value: i64) -> Self { + PartiqlMetaValue::Int64(value) + } +} + +impl From for PartiqlMetaValue +where + T: Eq + Clone + Hash + Borrow, +{ + fn from(value: f64) -> Self { + PartiqlMetaValue::Float64(value) + } +} + +impl From for PartiqlMetaValue +where + T: Eq + Clone + Hash + Borrow, +{ + fn from(value: String) -> Self { + PartiqlMetaValue::String(value) + } +} +impl From<&'static str> for PartiqlMetaValue +where + T: Eq + Clone + Hash + Borrow, +{ + fn from(value: &'static str) -> Self { + PartiqlMetaValue::String(value.to_owned()) + } +} + +impl From>> for PartiqlMetaValue +where + T: Eq + Clone + Hash + Borrow, +{ + fn from(value: Vec>) -> Self { + PartiqlMetaValue::Array(value) + } +} + +impl From<&[PartiqlMetaValue]> for PartiqlMetaValue +where + T: Eq + Clone + Hash + Borrow, +{ + fn from(slice: &[PartiqlMetaValue]) -> Self { + PartiqlMetaValue::Array(slice.to_vec()) + } +} + +impl From> for PartiqlMetaValue +where + T: Eq + Clone + Hash + Borrow, +{ + fn from(value: PartiqlMetadata) -> Self { + PartiqlMetaValue::Map(value) + } +} + +impl Display for PartiqlMetaValue +where + T: Eq + Hash + Display + Clone + Borrow, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self { + PartiqlMetaValue::Array(arr) => { + write!(f, "[")?; + for (idx, item) in arr.iter().enumerate() { + if idx > 0 { + write!(f, ", ")?; + } + write!(f, "{}", item)?; + } + write!(f, "]") + } + PartiqlMetaValue::Bool(v) => write!(f, "{}", v), + PartiqlMetaValue::Decimal(v) => write!(f, "{}", v), + PartiqlMetaValue::Float64(v) => write!(f, "{}", v), + PartiqlMetaValue::Float32(v) => write!(f, "{}", v), + PartiqlMetaValue::Int32(v) => write!(f, "{}", v), + PartiqlMetaValue::Int64(v) => write!(f, "{}", v), + PartiqlMetaValue::Map(map) => { + write!(f, "{{")?; + for (t, v) in map.iter() { + write!(f, "{}: {} , ", t, v)?; + } + write!(f, "}}") + } + PartiqlMetaValue::String(v) => write!(f, "{}", v), + } + } +} + +#[cfg(test)] +mod tests { + use crate::metadata::{PartiqlMetaValue, PartiqlMetadata}; + + #[test] + fn test_metadata() { + let foo_val = PartiqlMetaValue::String("foo".to_string()); + let i64_val = PartiqlMetaValue::Int64(2); + + let expected_vec_val = vec![foo_val, i64_val]; + let expected_bool_val = true; + let expected_int_val = 2; + let expected_float_val = 2.5; + let expected_str_val = "foo"; + + let mut expected_map = PartiqlMetadata::new(); + expected_map.insert("bool value", expected_bool_val.into()); + expected_map.insert("integer value", expected_int_val.into()); + + let mut metas = PartiqlMetadata::new(); + metas.insert("vec value", expected_vec_val.clone().into()); + metas.insert("bool value", expected_bool_val.into()); + metas.insert("integer value", expected_int_val.into()); + metas.insert("float value", expected_float_val.into()); + metas.insert("string value", expected_str_val.into()); + metas.insert("map value", expected_map.clone().into()); + + let vec_val = metas.vec_value("vec value").expect("vec meta value"); + let bool_val = metas.bool_value("bool value").expect("bool meta value"); + let int_val = metas.i32_value("integer value").expect("i32 meta value"); + let float_val = metas.f64_value("float value").expect("f64 meta value"); + let string_val = metas + .string_value("string value") + .expect("string meta value"); + let map_val = metas.map_value("map value").expect("map meta value"); + + assert_eq!(vec_val, expected_vec_val.clone()); + assert_eq!(bool_val, expected_bool_val.clone()); + assert_eq!(int_val, expected_int_val.clone()); + assert_eq!(float_val, expected_float_val.clone()); + assert_eq!(string_val, expected_str_val); + assert_eq!(map_val, expected_map.clone()); + } +} diff --git a/partiql-common/src/node.rs b/partiql-common/src/node.rs index 8f3b5425..9626d0f0 100644 --- a/partiql-common/src/node.rs +++ b/partiql-common/src/node.rs @@ -1,5 +1,4 @@ use indexmap::IndexMap; -use std::hash::Hash; use std::sync::{Arc, RwLock}; #[cfg(feature = "serde")] diff --git a/partiql-parser/src/parse/partiql.lalrpop b/partiql-parser/src/parse/partiql.lalrpop index 8aa04b52..d65ca1b0 100644 --- a/partiql-parser/src/parse/partiql.lalrpop +++ b/partiql-parser/src/parse/partiql.lalrpop @@ -287,7 +287,7 @@ FromClause: ast::AstNode = { ast::FromSource::Join(node) => node.id, }; - let start = state.locations.get(&start_id).unwrap_or(&total).start.0.clone(); + let start = state.locations.get(&start_id).unwrap_or(&total).start.0.clone(); let end = state.locations.get(&end_id).unwrap_or(&total).end.0.clone(); let range = start..end; let join = state.node(ast::Join { diff --git a/partiql-types/src/lib.rs b/partiql-types/src/lib.rs index 50e58ce3..8de32be0 100644 --- a/partiql-types/src/lib.rs +++ b/partiql-types/src/lib.rs @@ -6,7 +6,6 @@ use indexmap::IndexSet; use itertools::Itertools; use miette::Diagnostic; use partiql_common::node::{AutoNodeIdGenerator, NodeId, NodeIdGenerator}; -use std::collections::HashMap; use std::fmt::{Debug, Display, Formatter}; use std::hash::{Hash, Hasher}; use std::sync::OnceLock; @@ -622,8 +621,6 @@ impl Display for StaticType { } } -pub type StaticTypeMetas = HashMap; - #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub enum Static { // Scalar Types