From bfe26d0760560973f363fb16ac7a4afce66afc22 Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Fri, 27 Sep 2024 15:08:19 +0100 Subject: [PATCH 1/5] feat: add tinyint type to some files Signed-off-by: Jeffrey --- .../src/base/commitment/column_bounds.rs | 32 +++++++++ .../proof-of-sql/src/base/database/column.rs | 71 +++++++++++++++++-- .../src/base/database/owned_column.rs | 40 +++++++++++ 3 files changed, 139 insertions(+), 4 deletions(-) diff --git a/crates/proof-of-sql/src/base/commitment/column_bounds.rs b/crates/proof-of-sql/src/base/commitment/column_bounds.rs index 10272afd6..e86788540 100644 --- a/crates/proof-of-sql/src/base/commitment/column_bounds.rs +++ b/crates/proof-of-sql/src/base/commitment/column_bounds.rs @@ -205,6 +205,8 @@ pub struct ColumnBoundsMismatch { pub enum ColumnBounds { /// Column does not have order. NoOrder, + /// The bounds of a TinyInt column, + TinyInt(Bounds), /// The bounds of a SmallInt column. SmallInt(Bounds), /// The bounds of an Int column. @@ -223,6 +225,7 @@ impl ColumnBounds { /// If the column variant has order, only the minimum and maximum value will be copied. pub fn from_column(column: &CommittableColumn) -> ColumnBounds { match column { + CommittableColumn::TinyInt(ints) => ColumnBounds::TinyInt(Bounds::from_iter(*ints)), CommittableColumn::SmallInt(ints) => ColumnBounds::SmallInt(Bounds::from_iter(*ints)), CommittableColumn::Int(ints) => ColumnBounds::Int(Bounds::from_iter(*ints)), CommittableColumn::BigInt(ints) => ColumnBounds::BigInt(Bounds::from_iter(*ints)), @@ -244,6 +247,9 @@ impl ColumnBounds { pub fn try_union(self, other: Self) -> Result { match (self, other) { (ColumnBounds::NoOrder, ColumnBounds::NoOrder) => Ok(ColumnBounds::NoOrder), + (ColumnBounds::TinyInt(bounds_a), ColumnBounds::TinyInt(bounds_b)) => { + Ok(ColumnBounds::TinyInt(bounds_a.union(bounds_b))) + } (ColumnBounds::SmallInt(bounds_a), ColumnBounds::SmallInt(bounds_b)) => { Ok(ColumnBounds::SmallInt(bounds_a.union(bounds_b))) } @@ -273,6 +279,9 @@ impl ColumnBounds { pub fn try_difference(self, other: Self) -> Result { match (self, other) { (ColumnBounds::NoOrder, ColumnBounds::NoOrder) => Ok(self), + (ColumnBounds::TinyInt(bounds_a), ColumnBounds::TinyInt(bounds_b)) => { + Ok(ColumnBounds::TinyInt(bounds_a.difference(bounds_b))) + } (ColumnBounds::SmallInt(bounds_a), ColumnBounds::SmallInt(bounds_b)) => { Ok(ColumnBounds::SmallInt(bounds_a.difference(bounds_b))) } @@ -498,6 +507,14 @@ mod tests { let varchar_column_bounds = ColumnBounds::from_column(&committable_varchar_column); assert_eq!(varchar_column_bounds, ColumnBounds::NoOrder); + let tinyint_column = OwnedColumn::::TinyInt([1, 2, 3, 1, 0].to_vec()); + let committable_tinyint_column = CommittableColumn::from(&tinyint_column); + let tinyint_column_bounds = ColumnBounds::from_column(&committable_tinyint_column); + assert_eq!( + tinyint_column_bounds, + ColumnBounds::TinyInt(Bounds::Sharp(BoundsInner { min: 0, max: 3 })) + ); + let smallint_column = OwnedColumn::::SmallInt([1, 2, 3, 1, 0].to_vec()); let committable_smallint_column = CommittableColumn::from(&smallint_column); let smallint_column_bounds = ColumnBounds::from_column(&committable_smallint_column); @@ -561,6 +578,13 @@ mod tests { let no_order = ColumnBounds::NoOrder; assert_eq!(no_order.try_union(no_order).unwrap(), no_order); + let tinyint_a = ColumnBounds::TinyInt(Bounds::Sharp(BoundsInner { min: 1, max: 3 })); + let tinyint_b = ColumnBounds::TinyInt(Bounds::Sharp(BoundsInner { min: 4, max: 6 })); + assert_eq!( + tinyint_a.try_union(tinyint_b).unwrap(), + ColumnBounds::TinyInt(Bounds::Sharp(BoundsInner { min: 1, max: 6 })) + ); + let smallint_a = ColumnBounds::SmallInt(Bounds::Sharp(BoundsInner { min: 1, max: 3 })); let smallint_b = ColumnBounds::SmallInt(Bounds::Sharp(BoundsInner { min: 4, max: 6 })); assert_eq!( @@ -608,6 +632,7 @@ mod tests { #[test] fn we_cannot_union_mismatched_column_bounds() { let no_order = ColumnBounds::NoOrder; + let tinyint = ColumnBounds::TinyInt(Bounds::Sharp(BoundsInner { min: -3, max: 3 })); let smallint = ColumnBounds::SmallInt(Bounds::Sharp(BoundsInner { min: -5, max: 5 })); let int = ColumnBounds::Int(Bounds::Sharp(BoundsInner { min: -10, max: 10 })); let bigint = ColumnBounds::BigInt(Bounds::Sharp(BoundsInner { min: 1, max: 3 })); @@ -616,6 +641,7 @@ mod tests { let bounds = [ (no_order, "NoOrder"), + (tinyint, "TinyInt"), (smallint, "SmallInt"), (int, "Int"), (bigint, "BigInt"), @@ -644,6 +670,10 @@ mod tests { let no_order = ColumnBounds::NoOrder; assert_eq!(no_order.try_difference(no_order).unwrap(), no_order); + let tinyint_a = ColumnBounds::TinyInt(Bounds::Sharp(BoundsInner { min: 1, max: 3 })); + let tinyint_b = ColumnBounds::TinyInt(Bounds::Empty; + assert_eq!(tinyint_a.try_difference(tinyint_b).unwrap(), tinyint_b); + let smallint_a = ColumnBounds::SmallInt(Bounds::Sharp(BoundsInner { min: 1, max: 3 })); let smallint_b = ColumnBounds::SmallInt(Bounds::Empty); assert_eq!(smallint_a.try_difference(smallint_b).unwrap(), smallint_a); @@ -677,6 +707,7 @@ mod tests { let bigint = ColumnBounds::BigInt(Bounds::Sharp(BoundsInner { min: 1, max: 3 })); let int128 = ColumnBounds::Int128(Bounds::Sharp(BoundsInner { min: 4, max: 6 })); let timestamp = ColumnBounds::TimestampTZ(Bounds::Sharp(BoundsInner { min: 4, max: 6 })); + let tinyint = ColumnBounds::TinyInt(Bounds::Sharp(BoundsInner { min: 1, max: 3 })); let smallint = ColumnBounds::SmallInt(Bounds::Sharp(BoundsInner { min: 1, max: 3 })); assert!(no_order.try_difference(bigint).is_err()); @@ -688,6 +719,7 @@ mod tests { assert!(bigint.try_difference(int128).is_err()); assert!(int128.try_difference(bigint).is_err()); + assert!(tinyint.try_difference(timestamp).is_err()); assert!(smallint.try_difference(timestamp).is_err()); assert!(timestamp.try_difference(smallint).is_err()); } diff --git a/crates/proof-of-sql/src/base/database/column.rs b/crates/proof-of-sql/src/base/database/column.rs index 6b6c39b38..3be3b09e6 100644 --- a/crates/proof-of-sql/src/base/database/column.rs +++ b/crates/proof-of-sql/src/base/database/column.rs @@ -30,6 +30,8 @@ use serde::{Deserialize, Serialize}; pub enum Column<'a, S: Scalar> { /// Boolean columns Boolean(&'a [bool]), + /// i8 columns + TinyInt(&'a [i8]), /// i16 columns SmallInt(&'a [i16]), /// i32 columns @@ -59,6 +61,7 @@ impl<'a, S: Scalar> Column<'a, S> { pub fn column_type(&self) -> ColumnType { match self { Self::Boolean(_) => ColumnType::Boolean, + Self::TinyInt(_) => ColumnTypes::TinyInt, Self::SmallInt(_) => ColumnType::SmallInt, Self::Int(_) => ColumnType::Int, Self::BigInt(_) => ColumnType::BigInt, @@ -75,6 +78,7 @@ impl<'a, S: Scalar> Column<'a, S> { pub fn len(&self) -> usize { match self { Self::Boolean(col) => col.len(), + Self::TinyInt(col) => col.len(), Self::SmallInt(col) => col.len(), Self::Int(col) => col.len(), Self::BigInt(col) => col.len(), @@ -103,6 +107,9 @@ impl<'a, S: Scalar> Column<'a, S> { LiteralValue::Boolean(value) => { Column::Boolean(alloc.alloc_slice_fill_copy(length, *value)) } + LiteralValue::TinyInt(value) => { + Column::TinyInt(alloc.alloc_slice_fill_copy(length, *value)) + } LiteralValue::SmallInt(value) => { Column::SmallInt(alloc.alloc_slice_fill_copy(length, *value)) } @@ -135,6 +142,7 @@ impl<'a, S: Scalar> Column<'a, S> { pub fn from_owned_column(owned_column: &'a OwnedColumn, alloc: &'a Bump) -> Self { match owned_column { OwnedColumn::Boolean(col) => Column::Boolean(col.as_slice()), + OwnedColumn::TinyInt(col) => Column::TinyInt(col.as_slice()), OwnedColumn::SmallInt(col) => Column::SmallInt(col.as_slice()), OwnedColumn::Int(col) => Column::Int(col.as_slice()), OwnedColumn::BigInt(col) => Column::BigInt(col.as_slice()), @@ -172,6 +180,7 @@ impl<'a, S: Scalar> Column<'a, S> { pub(crate) fn scalar_at(&self, index: usize) -> Option { (index < self.len()).then_some(match self { Self::Boolean(col) => S::from(col[index]), + Self::TinyInt(col) => S::from(col[index]), Self::SmallInt(col) => S::from(col[index]), Self::Int(col) => S::from(col[index]), Self::BigInt(col) => S::from(col[index]), @@ -190,6 +199,7 @@ impl<'a, S: Scalar> Column<'a, S> { Self::Boolean(col) => slice_cast_with(col, |b| S::from(b) * scale_factor), Self::Decimal75(_, _, col) => slice_cast_with(col, |s| *s * scale_factor), Self::VarChar((_, scals)) => slice_cast_with(scals, |s| *s * scale_factor), + Self::TinyInt(col) => slice_cast_with(col, |i| SS::from (i) * scale_factor), Self::SmallInt(col) => slice_cast_with(col, |i| S::from(i) * scale_factor), Self::Int(col) => slice_cast_with(col, |i| S::from(i) * scale_factor), Self::BigInt(col) => slice_cast_with(col, |i| S::from(i) * scale_factor), @@ -210,6 +220,8 @@ pub enum ColumnType { /// Mapped to bool #[serde(alias = "BOOLEAN", alias = "boolean")] Boolean, + /// Mapped to i8 + #[serde(alias = "TINYINT", alias = "tinyint")] /// Mapped to i16 #[serde(alias = "SMALLINT", alias = "smallint")] SmallInt, @@ -241,7 +253,8 @@ impl ColumnType { pub fn is_numeric(&self) -> bool { matches!( self, - ColumnType::SmallInt + ColumnType::TinyInt + | ColumnType::SmallInt | ColumnType::Int | ColumnType::BigInt | ColumnType::Int128 @@ -254,13 +267,14 @@ impl ColumnType { pub fn is_integer(&self) -> bool { matches!( self, - ColumnType::SmallInt | ColumnType::Int | ColumnType::BigInt | ColumnType::Int128 + ColumnType::TinyInt | ColumnType::SmallInt | ColumnType::Int | ColumnType::BigInt | ColumnType::Int128 ) } /// Returns the number of bits in the integer type if it is an integer type. Otherwise, return None. fn to_integer_bits(self) -> Option { match self { + ColumnType::TinyInt => Some(8), ColumnType::SmallInt => Some(16), ColumnType::Int => Some(32), ColumnType::BigInt => Some(64), @@ -274,6 +288,7 @@ impl ColumnType { /// Otherwise, return None. fn from_integer_bits(bits: usize) -> Option { match bits { + 8 => Some(ColumnType::TinyInt), 16 => Some(ColumnType::SmallInt), 32 => Some(ColumnType::Int), 64 => Some(ColumnType::BigInt), @@ -300,6 +315,7 @@ impl ColumnType { /// Returns the precision of a ColumnType if it is converted to a decimal wrapped in Some(). If it can not be converted to a decimal, return None. pub fn precision_value(&self) -> Option { match self { + Self::TinyInt => Some(3_u8), Self::SmallInt => Some(5_u8), Self::Int => Some(10_u8), Self::BigInt => Some(19_u8), @@ -316,7 +332,7 @@ impl ColumnType { pub fn scale(&self) -> Option { match self { Self::Decimal75(_, scale) => Some(*scale), - Self::SmallInt | Self::Int | Self::BigInt | Self::Int128 | Self::Scalar => Some(0), + Self::TinyInt | Self::SmallInt | Self::Int | Self::BigInt | Self::Int128 | Self::Scalar => Some(0), Self::Boolean | Self::VarChar => None, Self::TimestampTZ(tu, _) => match tu { PoSQLTimeUnit::Second => Some(0), @@ -331,6 +347,7 @@ impl ColumnType { pub fn byte_size(&self) -> usize { match self { Self::Boolean => size_of::(), + Self::TinyInt => size_of::(), Self::SmallInt => size_of::(), Self::Int => size_of::(), Self::BigInt | Self::TimestampTZ(_, _) => size_of::(), @@ -347,7 +364,7 @@ impl ColumnType { /// Returns if the column type supports signed values. pub const fn is_signed(&self) -> bool { match self { - Self::SmallInt | Self::Int | Self::BigInt | Self::Int128 | Self::TimestampTZ(_, _) => { + Self::TinyInt | Self::SmallInt | Self::Int | Self::BigInt | Self::Int128 | Self::TimestampTZ(_, _) => { true } Self::Decimal75(_, _) | Self::Scalar | Self::VarChar | Self::Boolean => false, @@ -361,6 +378,7 @@ impl From<&ColumnType> for DataType { fn from(column_type: &ColumnType) -> Self { match column_type { ColumnType::Boolean => DataType::Boolean, + ColumnType::TinyInt => DataType::Int8, ColumnType::SmallInt => DataType::Int16, ColumnType::Int => DataType::Int32, ColumnType::BigInt => DataType::Int64, @@ -392,6 +410,7 @@ impl TryFrom for ColumnType { fn try_from(data_type: DataType) -> Result { match data_type { DataType::Boolean => Ok(ColumnType::Boolean), + DaaType::Int8 => OK(ColumnType::TinyInt), DataType::Int16 => Ok(ColumnType::SmallInt), DataType::Int32 => Ok(ColumnType::Int), DataType::Int64 => Ok(ColumnType::BigInt), @@ -422,6 +441,7 @@ impl Display for ColumnType { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { ColumnType::Boolean => write!(f, "BOOLEAN"), + ColumnType::TinyInt => Write!(f, "TINYINT"), ColumnType::SmallInt => write!(f, "SMALLINT"), ColumnType::Int => write!(f, "INT"), ColumnType::BigInt => write!(f, "BIGINT"), @@ -534,6 +554,10 @@ mod tests { let serialized = serde_json::to_string(&column_type).unwrap(); assert_eq!(serialized, r#""Boolean""#); + let column_type = ColumnType::TinyInt; + let serialized = serde_json::to_string(&column_type).unwrap(); + assert_eq!(serialized, r#""TinyInt""#); + let column_type = ColumnType::SmallInt; let serialized = serde_json::to_string(&column_type).unwrap(); assert_eq!(serialized, r#""SmallInt""#); @@ -575,6 +599,10 @@ mod tests { let deserialized: ColumnType = serde_json::from_str(r#""Boolean""#).unwrap(); assert_eq!(deserialized, expected_column_type); + let expected_column_type = ColumnType::TinyInt; + let deserialized: ColumnType = serde_json::from_str(r#""TinyInt""#).unwrap(); + assert_eq!(deserialized, expected_column_type); + let expected_column_type = ColumnType::SmallInt; let deserialized: ColumnType = serde_json::from_str(r#""SmallInt""#).unwrap(); assert_eq!(deserialized, expected_column_type); @@ -587,6 +615,10 @@ mod tests { let deserialized: ColumnType = serde_json::from_str(r#""BigInt""#).unwrap(); assert_eq!(deserialized, expected_column_type); + let expected_column_type = ColumnType::TinyInt; + let deserialized: ColumnType = serde_json::from_str(r#""TINYINT""#).unwrap(); + assert_eq!(deserialized, expected_column_type); + let expected_column_type = ColumnType::SmallInt; let deserialized: ColumnType = serde_json::from_str(r#""SMALLINT""#).unwrap(); assert_eq!(deserialized, expected_column_type); @@ -640,6 +672,14 @@ mod tests { serde_json::from_str::(r#""BIGINT""#).unwrap(), ColumnType::BigInt ); + assert_eq!( + serde_json::from_str::(r#""TINYINT""#).unwrap(), + ColumnType::TinyInt + ); + assert_eq!( + serde_json::from_str::(r#""TINYINT""#).unwrap(), + ColumnType::TINYINT + ); assert_eq!( serde_json::from_str::(r#""SMALLINT""#).unwrap(), ColumnType::SmallInt @@ -707,6 +747,9 @@ mod tests { let deserialized: Result = serde_json::from_str(r#""BooLean""#); assert!(deserialized.is_err()); + let deserialized: Result = serde_json::from_str(r#""TinyInt""#); + assert!(deserialized.is_err()); + let deserialized: Result = serde_json::from_str(r#""Smallint""#); assert!(deserialized.is_err()); @@ -743,6 +786,14 @@ mod tests { boolean ); + let tinyint = ColumnType::TinyInt; + let tinyint_json = serde_json::to_string(&tinyint).unwrap(); + assert_eq!(tinyint_json, "\"TinyInt\""); + assert_eq!( + serde_json::from_str::(&tinyint_json).unwrap(), + tinyint + ); + let smallint = ColumnType::SmallInt; let smallint_json = serde_json::to_string(&smallint).unwrap(); assert_eq!(smallint_json, "\"SmallInt\""); @@ -813,6 +864,10 @@ mod tests { assert_eq!(column.len(), 3); assert!(!column.is_empty()); + let column = Column::::TinyInt(&[1, 2, 3]); + assert_eq!(column.len(), 3); + assert!(!column.is_empty()); + let column = Column::::SmallInt(&[1, 2, 3]); assert_eq!(column.len(), 3); assert!(!column.is_empty()); @@ -853,6 +908,10 @@ mod tests { assert_eq!(column.len(), 0); assert!(column.is_empty()); + let column = Column::::TinyInt(&[]); + assert_eq!(column.len(), 0); + assert!(column.is_empty()); + let column = Column::::SmallInt(&[]); assert_eq!(column.len(), 0); assert!(column.is_empty()); @@ -936,6 +995,10 @@ mod tests { assert_eq!(column.column_type().byte_size(), 1); assert_eq!(column.column_type().bit_size(), 8); + let column = Column::::TinyInt(&[1, 2, 3, 4]); + assert_eq!(column.column_type().byte_size(), 1); + assert_eq!(column.column_type().bit_size(), 8); + let column = Column::::SmallInt(&[1, 2, 3, 4]); assert_eq!(column.column_type().byte_size(), 2); assert_eq!(column.column_type().bit_size(), 16); diff --git a/crates/proof-of-sql/src/base/database/owned_column.rs b/crates/proof-of-sql/src/base/database/owned_column.rs index 674bd3d6f..407852f86 100644 --- a/crates/proof-of-sql/src/base/database/owned_column.rs +++ b/crates/proof-of-sql/src/base/database/owned_column.rs @@ -26,6 +26,8 @@ use proof_of_sql_parser::{ pub enum OwnedColumn { /// Boolean columns Boolean(Vec), + /// i8 columns + TinyInt(Vec), /// i16 columns SmallInt(Vec), /// i32 columns @@ -49,6 +51,7 @@ impl OwnedColumn { pub fn len(&self) -> usize { match self { OwnedColumn::Boolean(col) => col.len(), + OwnedColumn::TinyInt(col) => col.len(), OwnedColumn::SmallInt(col) => col.len(), OwnedColumn::Int(col) => col.len(), OwnedColumn::BigInt(col) => col.len(), @@ -64,6 +67,7 @@ impl OwnedColumn { pub fn try_permute(&self, permutation: &Permutation) -> Result { Ok(match self { OwnedColumn::Boolean(col) => OwnedColumn::Boolean(permutation.try_apply(col)?), + OwnedColumn::TinyInt(col) => OwnedColumn::TinyInt(permutation.try_apply(col)?), OwnedColumn::SmallInt(col) => OwnedColumn::SmallInt(permutation.try_apply(col)?), OwnedColumn::Int(col) => OwnedColumn::Int(permutation.try_apply(col)?), OwnedColumn::BigInt(col) => OwnedColumn::BigInt(permutation.try_apply(col)?), @@ -83,6 +87,7 @@ impl OwnedColumn { pub fn slice(&self, start: usize, end: usize) -> Self { match self { OwnedColumn::Boolean(col) => OwnedColumn::Boolean(col[start..end].to_vec()), + OwnedColumn::TinyInt(col) => OwnedColumn::TinyInt(col[start..end].to_vec()), OwnedColumn::SmallInt(col) => OwnedColumn::SmallInt(col[start..end].to_vec()), OwnedColumn::Int(col) => OwnedColumn::Int(col[start..end].to_vec()), OwnedColumn::BigInt(col) => OwnedColumn::BigInt(col[start..end].to_vec()), @@ -102,6 +107,7 @@ impl OwnedColumn { pub fn is_empty(&self) -> bool { match self { OwnedColumn::Boolean(col) => col.is_empty(), + OwnedColumn::TinyInt(col) => col.is_empty(), OwnedColumn::SmallInt(col) => col.is_empty(), OwnedColumn::Int(col) => col.is_empty(), OwnedColumn::BigInt(col) => col.is_empty(), @@ -116,6 +122,7 @@ impl OwnedColumn { pub fn column_type(&self) -> ColumnType { match self { OwnedColumn::Boolean(_) => ColumnType::Boolean, + OwnedColumn::TinyInt(_) => ColumnType::TinyInt, OwnedColumn::SmallInt(_) => ColumnType::SmallInt, OwnedColumn::Int(_) => ColumnType::Int, OwnedColumn::BigInt(_) => ColumnType::BigInt, @@ -141,6 +148,15 @@ impl OwnedColumn { error: "Overflow in scalar conversions".to_string(), })?, )), + ColumnType::TinyInt => Ok(OwnedColumn::TinyInt( + scalars + .iter() + .map(|s| -> Result { TryInto::::try_into(*s) }) + .collect::, _>>() + .map_err(|_| OwnedColumnError::ScalarConversionError { + error: "Overflow in scalar conversions".to_string(), + })?, + )), ColumnType::SmallInt => Ok(OwnedColumn::SmallInt( scalars .iter() @@ -214,6 +230,15 @@ impl OwnedColumn { Self::try_from_scalars(&scalars, column_type) } + #[cfg(test)] + /// Returns an iterator over the raw data of the column + /// assuming the underlying type is [i8], panicking if it is not. + pub fn i8_iter(&self) -> impl Iterator { + match self { + OwnedColumn::TinyInt(col) => col.iter(), + _ => panic!("Expected TinyInt column"), + } + } #[cfg(test)] /// Returns an iterator over the raw data of the column /// assuming the underlying type is [i16], panicking if it is not. @@ -285,6 +310,7 @@ impl<'a, S: Scalar> From<&Column<'a, S>> for OwnedColumn { fn from(col: &Column<'a, S>) -> Self { match col { Column::Boolean(col) => OwnedColumn::Boolean(col.to_vec()), + Column::TinyInt(col) => OwnedColumn::TinyInt(col.to_vec()), Column::SmallInt(col) => OwnedColumn::SmallInt(col.to_vec()), Column::Int(col) => OwnedColumn::Int(col.to_vec()), Column::BigInt(col) => OwnedColumn::BigInt(col.to_vec()), @@ -314,6 +340,7 @@ pub(crate) fn compare_indexes_by_owned_columns_with_direction( .map(|(col, direction)| { let ordering = match col { OwnedColumn::Boolean(col) => col[i].cmp(&col[j]), + OwnedColumn::TinyInt(col) => col[i].cmp(&col[j]), OwnedColumn::SmallInt(col) => col[i].cmp(&col[j]), OwnedColumn::Int(col) => col[i].cmp(&col[j]), OwnedColumn::BigInt(col) => col[i].cmp(&col[j]), @@ -373,10 +400,12 @@ mod test { .map(|&i| Curve25519Scalar::from(i)) .collect(), ); + let col4: OwnedColumn = OwnedColumn::TinyInt(vec![1, 1, 2, 1, 1]); let order_by_pairs = vec![ (col1, OrderByDirection::Asc), (col2, OrderByDirection::Desc), (col3, OrderByDirection::Asc), + (col4,OrderByDirection::Asc), ]; // Equal on col1 and col2, less on col3 assert_eq!( @@ -398,6 +427,17 @@ mod test { compare_indexes_by_owned_columns_with_direction(&order_by_pairs, 1, 4), Ordering::Less ) + // Test TinyInt column. + assert_eq!( + compare_indexes_by_owned_columns_with_direction(&order_by_pairs, 3, 4), + Ordering::Equal + ); + // Test TinyInt column with different values + let col5: OwnedColumn = OwnedColumn::TinyInt(vec![2, 2, 1, 2, 2]); + assert_eq!( + compare_indexes_by_owned_columns_with_direction(&[(col4, OrderByDirection::Asc), (col5, OrderByDirection::Asc)], 0, 1), + Ordering::Less + ); } #[test] From 867f35a7c886ec0eb0fca7a6b8802c467f219cd2 Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Fri, 27 Sep 2024 15:26:41 +0100 Subject: [PATCH 2/5] feat: add tinyint type in committable_column file Signed-off-by: Jeffrey --- .../src/base/commitment/committable_column.rs | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/crates/proof-of-sql/src/base/commitment/committable_column.rs b/crates/proof-of-sql/src/base/commitment/committable_column.rs index 21276adbf..0f2d67958 100644 --- a/crates/proof-of-sql/src/base/commitment/committable_column.rs +++ b/crates/proof-of-sql/src/base/commitment/committable_column.rs @@ -25,6 +25,8 @@ use proof_of_sql_parser::posql_time::{PoSQLTimeUnit, PoSQLTimeZone}; pub enum CommittableColumn<'a> { /// Borrowed Bool column, mapped to `bool`. Boolean(&'a [bool]), + /// Borrowed TinyInt column, mapped to `i6`. + TinyInt(&'a [i8]), /// Borrowed SmallInt column, mapped to `i16`. SmallInt(&'a [i16]), /// Borrowed SmallInt column, mapped to `i32`. @@ -50,6 +52,7 @@ impl<'a> CommittableColumn<'a> { /// Returns the length of the column. pub fn len(&self) -> usize { match self { + CommittableColumn::TinyInt(Col) => col.len(), CommittableColumn::SmallInt(col) => col.len(), CommittableColumn::Int(col) => col.len(), CommittableColumn::BigInt(col) => col.len(), @@ -77,6 +80,7 @@ impl<'a> CommittableColumn<'a> { impl<'a> From<&CommittableColumn<'a>> for ColumnType { fn from(value: &CommittableColumn<'a>) -> Self { match value { + CommittableColumn::TinyInt(_) => ColumnType::TinyInt, CommittableColumn::SmallInt(_) => ColumnType::SmallInt, CommittableColumn::Int(_) => ColumnType::Int, CommittableColumn::BigInt(_) => ColumnType::BigInt, @@ -99,6 +103,7 @@ impl<'a, S: Scalar> From<&Column<'a, S>> for CommittableColumn<'a> { fn from(value: &Column<'a, S>) -> Self { match value { Column::Boolean(bools) => CommittableColumn::Boolean(bools), + Column::TinyInt(ints) => CommittableColumn::TinyInt(ints), Column::SmallInt(ints) => CommittableColumn::SmallInt(ints), Column::Int(ints) => CommittableColumn::Int(ints), Column::BigInt(ints) => CommittableColumn::BigInt(ints), @@ -121,6 +126,7 @@ impl<'a, S: Scalar> From<&'a OwnedColumn> for CommittableColumn<'a> { fn from(value: &'a OwnedColumn) -> Self { match value { OwnedColumn::Boolean(bools) => CommittableColumn::Boolean(bools), + OWnedColumn::TinyInt(ints) => (ints as &[_].into(), OwnedColumn::SmallInt(ints) => (ints as &[_]).into(), OwnedColumn::Int(ints) => (ints as &[_]).into(), OwnedColumn::BigInt(ints) => (ints as &[_]).into(), @@ -154,6 +160,11 @@ impl<'a> From<&'a [u8]> for CommittableColumn<'a> { CommittableColumn::RangeCheckWord(value) } } +impl<'a> From<&'a [i8]> for CommittableColumn<'a> { + fn from(value: &'a [i8]) -> Self { + CommittableColumn::TinyInt(value) + } +} impl<'a> From<&'a [i16]> for CommittableColumn<'a> { fn from(value: &'a [i16]) -> Self { CommittableColumn::SmallInt(value) @@ -191,6 +202,7 @@ impl<'a> From<&'a [bool]> for CommittableColumn<'a> { impl<'a, 'b> From<&'a CommittableColumn<'b>> for Sequence<'a> { fn from(value: &'a CommittableColumn<'b>) -> Self { match value { + CommittableColumn::TinyInt(ints) => Sequence::from(*ints), CommittableColumn::SmallInt(ints) => Sequence::from(*ints), CommittableColumn::Int(ints) => Sequence::from(*ints), CommittableColumn::BigInt(ints) => Sequence::from(*ints), @@ -259,6 +271,26 @@ mod tests { ); } + #[test] + fn we_can_get_type_and_length_of_tinyint_column() { + // empty case + let tinyint_committable_column = CommittableColumn::TinyInt(&[]); + assert_eq!(tinyint_committable_column.len(), 0); + assert!(tinyint_committable_column.is_empty()); + assert_eq!( + tinyint_committable_column.column_type(), + ColumnType::TinyInt + ); + + let tinyint_committable_column = CommittableColumn::TinyInt(&[12, 34, 56]); + assert_eq!(tinyint_committable_column.len(), 3); + assert!(!tinyint_committable_column.is_empty()); + assert_eq!( + tinyint_committable_column.column_type(), + ColumnType::TinyInt + ); + } + #[test] fn we_can_get_type_and_length_of_smallint_column() { // empty case @@ -453,6 +485,21 @@ mod tests { ); } + #[test] + fn we_can_convert_from_borrowing_tinyint_column() { + // empty case + let from_borrowed_column = + CommittableColumn::from(&Column::::TinyInt(&[])); + assert_eq!(from_borrowed_column, CommittableColumn::TinyInt(&[])); + + let from_borrowed_column = + CommittableColumn::from(&Column::::TinyInt(&[12, 34, 56])); + assert_eq!( + from_borrowed_column, + CommittableColumn::TinyInt(&[12, 34, 56]) + ); + } + #[test] fn we_can_convert_from_borrowing_smallint_column() { // empty case @@ -577,6 +624,21 @@ mod tests { assert_eq!(from_owned_column, CommittableColumn::BigInt(&[12, 34, 56])); } + #[test] + fn we_can_convert_from_owned_tinyint_column() { + // empty case + let owned_column = OwnedColumn::::TinyInt(Vec::new()); + let from_owned_column = CommittableColumn::from(&owned_column); + assert_eq!(from_owned_column, CommittableColumn::TinyInt(&[])); + + let owned_column = OwnedColumn::::TinyInt(vec![12, 34, 56]); + let from_owned_column = CommittableColumn::from(&owned_column); + assert_eq!( + from_owned_column, + CommittableColumn::TinyInt(&[12, 34, 56]) + ); + } + #[test] fn we_can_convert_from_owned_smallint_column() { // empty case @@ -742,6 +804,30 @@ mod tests { assert_eq!(commitment_buffer[0], commitment_buffer[1]); } + #[test] + fn we_can_commit_to_tinyint_column_through_committable_column() { + // empty case + let committable_column = CommittableColumn::TinyInt(&[]); + let sequence = Sequence::from(&committable_column); + let mut commitment_buffer = [CompressedRistretto::default()]; + compute_curve25519_commitments(&mut commitment_buffer, &[sequence], 0); + assert_eq!(commitment_buffer[0], CompressedRistretto::default()); + + // nonempty case + let values = [12, 34, 56]; + let committable_column = CommittableColumn::TinyInt(&values); + + let sequence_actual = Sequence::from(&committable_column); + let sequence_expected = Sequence::from(values.as_slice()); + let mut commitment_buffer = [CompressedRistretto::default(); 2]; + compute_curve25519_commitments( + &mut commitment_buffer, + &[sequence_actual, sequence_expected], + 0, + ); + assert_eq!(commitment_buffer[0], commitment_buffer[1]); + } + #[test] fn we_can_commit_to_smallint_column_through_committable_column() { // empty case From 2af949ef08a91333ed6931bb8e49a2c0fa5e585e Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Fri, 27 Sep 2024 15:29:30 +0100 Subject: [PATCH 3/5] feat: add tinyint type to sqlsyntaxspecification md file Signed-off-by: Jeffrey --- docs/SQLSyntaxSpecification.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/SQLSyntaxSpecification.md b/docs/SQLSyntaxSpecification.md index 571a7bfe1..9d919da63 100644 --- a/docs/SQLSyntaxSpecification.md +++ b/docs/SQLSyntaxSpecification.md @@ -15,6 +15,7 @@ FROM table * DataTypes - Bool / Boolean - Numeric Types + * TinyInt (8 bits) * SmallInt (16 bits) * Int / Integer (32 bits) * BigInt (64 bits) @@ -55,4 +56,4 @@ It is far more efficient for the verifier to compute the actual division, while - LIMIT clause - OFFSET clause -[^1]: Currently, we do not support any string operations beyond = and !=. \ No newline at end of file +[^1]: Currently, we do not support any string operations beyond = and !=. From e9accb3110f7317f6c3657e4de6f1543f96e78d1 Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Fri, 27 Sep 2024 15:53:28 +0100 Subject: [PATCH 4/5] feat: add tinyint type to column_commitment_metadata file Signed-off-by: Jeffrey --- .../commitment/column_commitment_metadata.rs | 99 ++++++++++++++++++- 1 file changed, 98 insertions(+), 1 deletion(-) diff --git a/crates/proof-of-sql/src/base/commitment/column_commitment_metadata.rs b/crates/proof-of-sql/src/base/commitment/column_commitment_metadata.rs index 394c4cb78..4fff8b212 100644 --- a/crates/proof-of-sql/src/base/commitment/column_commitment_metadata.rs +++ b/crates/proof-of-sql/src/base/commitment/column_commitment_metadata.rs @@ -44,7 +44,8 @@ impl ColumnCommitmentMetadata { bounds: ColumnBounds, ) -> Result { match (column_type, bounds) { - (ColumnType::SmallInt, ColumnBounds::SmallInt(_)) + (columnType::TinyInt, ColumnBounds::TinyInt(_)) + | (ColumnType::SmallInt, ColumnBounds::SmallInt(_)) | (ColumnType::Int, ColumnBounds::Int(_)) | (ColumnType::BigInt, ColumnBounds::BigInt(_)) | (ColumnType::Int128, ColumnBounds::Int128(_)) @@ -69,6 +70,10 @@ impl ColumnCommitmentMetadata { /// Construct a [`ColumnCommitmentMetadata`] with widest possible bounds for the column type. pub fn from_column_type_with_max_bounds(column_type: ColumnType) -> Self { let bounds = match column_type { + ColumnType::TinyInt => ColumnBounds::TinyInt(super:;Bounds::Bounded( + BoundsInner::try_new(i8::MIN, i8::MAX) + .expect("i8::MIN and i8::MAX are valid bounds for TinyInt"). + )), ColumnType::SmallInt => ColumnBounds::SmallInt(super::Bounds::Bounded( BoundsInner::try_new(i16::MIN, i16::MAX) .expect("i16::MIN and i16::MAX are valid bounds for SmallInt"), @@ -182,6 +187,18 @@ mod tests { #[test] fn we_can_construct_metadata() { + assert_eq!( + ColumnCommitmentMetadata::try_new( + ColumnType::TinyInt, + ColumnBounds::TinyInt(Bounds::Empty) + ) + .unwrap(), + ColumnCommitmentMetadata { + column_type: ColumnType::TinyInt, + bounds: ColumnBounds::TinyInt(Bounds::Empty) + } + ); + assert_eq!( ColumnCommitmentMetadata::try_new( ColumnType::SmallInt, @@ -429,6 +446,17 @@ mod tests { panic!("Bounds constructed from nonempty BigInt column should be ColumnBounds::Int(Bounds::Sharp(_))"); } + let tinyint_column = OwnedColumn::::TinyInt([1, 2, 3, 1, 0].to_vec()); + let committable_tinyint_column = CommittableColumn::from(&tinyint_column); + let tinyint_metadata = ColumnCommitmentMetadata::from_column(&committable_tinyint_column); + assert_eq!(tinyint_metadata.column_type(), &ColumnType::TinyInt); + if let ColumnBounds::TinyInt(Bounds::Sharp(bounds)) = tinyint_metadata.bounds() { + assert_eq!(bounds.min(), &0); + assert_eq!(bounds.max(), &3); + } else { + panic!("Bounds constructed from nonempty TinyInt column should be ColumnBounds::TinyInt(Bounds::Sharp(_))"); + } + let smallint_column = OwnedColumn::::SmallInt([1, 2, 3, 1, 0].to_vec()); let committable_smallint_column = CommittableColumn::from(&smallint_column); let smallint_metadata = ColumnCommitmentMetadata::from_column(&committable_smallint_column); @@ -496,6 +524,19 @@ mod tests { scalar_metadata ); + // Ordered case + let ints = [1, 2, 3, 1, 0]; + let tinyint_column_a = CommittableColumn::TinyInt(&ints[..2]); + let tinyint_metadata_a = ColumnCommitmentMetadata::from_column(&tinyint_column_a); + let tinyint_column_b = CommittableColumn::TinyInt(&ints[2..]); + let tinyint_metadata_b = ColumnCommitmentMetadata::from_column(&tinyint_column_b); + let tinyint_column_c = CommittableColumn::TinyInt(&ints); + let tinyint_metadata_c = ColumnCommitmentMetadata::from_column(&tinyint_column_c); + assert_eq!( + tinyint_metadata_a.try_union(tinyint_metadata_b).unwrap(), + tinyint_metadata_c + ); + // Ordered case let ints = [1, 2, 3, 1, 0]; let smallint_column_a = CommittableColumn::SmallInt(&ints[..2]); @@ -643,6 +684,43 @@ mod tests { ); } + #[test] + fn we_can_difference_tinyint_matching_metadata() { + // Ordered case + let tinyints = [1, 2, 3, 1, 0]; + let tinyint_column_a = CommittableColumn::TinyInt(&tinyints[..2]); + let tinyint_metadata_a = ColumnCommitmentMetadata::from_column(&tinyint_column_a); + let tinyint_column_b = CommittableColumn::TinyInt(&tinyints); + let tinyint_metadata_b = ColumnCommitmentMetadata::from_column(&tinyint_column_b); + + let b_difference_a = tinyint_metadata_b + .try_difference(tinyint_metadata_a) + .unwrap(); + assert_eq!(b_difference_a.column_type, ColumnType::TinyInt); + if let ColumnBounds::TinyInt(Bounds::Bounded(bounds)) = b_difference_a.bounds() { + assert_eq!(bounds.min(), &0); + assert_eq!(bounds.max(), &3); + } else { + panic!("difference of overlapping bounds should be Bounded"); + } + + let tinyint_column_empty = CommittableColumn::TinyInt(&[]); + let tinyint_metadata_empty = ColumnCommitmentMetadata::from_column(&tinyint_column_empty); + + assert_eq!( + tinyint_metadata_b + .try_difference(tinyint_metadata_empty) + .unwrap(), + tinyint_metadata_b + ); + assert_eq!( + tinyint_metadata_empty + .try_difference(tinyint_metadata_b) + .unwrap(), + tinyint_metadata_empty + ); + } + #[test] fn we_can_difference_smallint_matching_metadata() { // Ordered case @@ -725,6 +803,10 @@ mod tests { column_type: ColumnType::Scalar, bounds: ColumnBounds::NoOrder, }; + let tinyint_metadata = ColumnCommitmentMetadata { + column_type: ColumnType::TinyInt, + bounds: ColumnBounds::TinyInt(Bounds::Empty), + }; let smallint_metadata = ColumnCommitmentMetadata { column_type: ColumnType::SmallInt, bounds: ColumnBounds::SmallInt(Bounds::Empty), @@ -746,6 +828,21 @@ mod tests { bounds: ColumnBounds::Int128(Bounds::Empty), }; + assert!(tinyint_metadata.try_union(scalar_metadata).is_err()); + assert!(scalar_metadata.try_union(tinyint_metadata).is_err()); + + assert!(tinyint_metadata.try_union(decimal75_metadata).is_err()); + assert!(decimal75_metadata.try_union(tinyint_metadata).is_err()); + + assert!(tinyint_metadata.try_union(varchar_metadata).is_err()); + assert!(varchar_metadata.try_union(tinyint_metadata).is_err()); + + assert!(tinyint_metadata.try_union(boolean_metadata).is_err()); + assert!(boolean_metadata.try_union(tinyint_metadata).is_err()); + + assert!(tinyint_metadata.try_union(smallint_metadata).is_err()); + assert!(smallint_metadata.try_union(tinyint_metadata).is_err()); + assert!(smallint_metadata.try_union(scalar_metadata).is_err()); assert!(scalar_metadata.try_union(smallint_metadata).is_err()); From 5e2ee1ed24ae256ec6196c3d546507acc9b66974 Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Fri, 27 Sep 2024 16:05:09 +0100 Subject: [PATCH 5/5] feat: add tinyint type to some files Signed-off-by: Jeffrey --- crates/proof-of-sql/benches/bench_append_rows.rs | 7 ++++++- crates/proof-of-sql/src/base/database/filter_util.rs | 3 +++ .../proof-of-sql/src/sql/proof/provable_result_column.rs | 2 ++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/crates/proof-of-sql/benches/bench_append_rows.rs b/crates/proof-of-sql/benches/bench_append_rows.rs index 8d7484d5f..48b6ccde2 100644 --- a/crates/proof-of-sql/benches/bench_append_rows.rs +++ b/crates/proof-of-sql/benches/bench_append_rows.rs @@ -12,7 +12,7 @@ use proof_of_sql::{ commitment::TableCommitment, database::{ owned_table_utility::{ - bigint, boolean, decimal75, int, int128, owned_table, scalar, smallint, + bigint, boolean, decimal75, int, int128, owned_table, scalar, tinyint, smallint, timestamptz, varchar, }, OwnedTable, @@ -78,6 +78,7 @@ pub fn generate_random_owned_table( "scalar", "varchar", "decimal75", + "tinyint", "smallint", "int", "timestamptz", @@ -110,6 +111,10 @@ pub fn generate_random_owned_table( 2, vec![generate_random_u64_array(); num_rows], )), + "tinyint" => columns.push(tinyint( + identifier.deref(), + vec![rng.gen::(); num_rows], + )), "smallint" => columns.push(smallint( identifier.deref(), vec![rng.gen::(); num_rows], diff --git a/crates/proof-of-sql/src/base/database/filter_util.rs b/crates/proof-of-sql/src/base/database/filter_util.rs index 9145b614f..b9fa5da2a 100644 --- a/crates/proof-of-sql/src/base/database/filter_util.rs +++ b/crates/proof-of-sql/src/base/database/filter_util.rs @@ -41,6 +41,9 @@ pub fn filter_column_by_index<'a, S: Scalar>( Column::Boolean(col) => { Column::Boolean(alloc.alloc_slice_fill_iter(indexes.iter().map(|&i| col[i]))) } + Column::TinyInt(col) => { + Column::TinyInt(alloc.alloc_slice_fill_iter(indexes.iter().map(|&i| col[i]))) + ) Column::SmallInt(col) => { Column::SmallInt(alloc.alloc_slice_fill_iter(indexes.iter().map(|&i| col[i]))) } diff --git a/crates/proof-of-sql/src/sql/proof/provable_result_column.rs b/crates/proof-of-sql/src/sql/proof/provable_result_column.rs index c81f02c10..579e5b637 100644 --- a/crates/proof-of-sql/src/sql/proof/provable_result_column.rs +++ b/crates/proof-of-sql/src/sql/proof/provable_result_column.rs @@ -35,6 +35,7 @@ impl ProvableResultColumn for Column<'_, S> { fn num_bytes(&self, selection: &Indexes) -> usize { match self { Column::Boolean(col) => col.num_bytes(selection), + Column::TinyInt(col) => col.num_bytes(selection), Column::SmallInt(col) => col.num_bytes(selection), Column::Int(col) => col.num_bytes(selection), Column::BigInt(col) => col.num_bytes(selection), @@ -49,6 +50,7 @@ impl ProvableResultColumn for Column<'_, S> { fn write(&self, out: &mut [u8], selection: &Indexes) -> usize { match self { Column::Boolean(col) => col.write(out, selection), + Column::TinyInt(col) => col.write(out, selection), Column::SmallInt(col) => col.write(out, selection), Column::Int(col) => col.write(out, selection), Column::BigInt(col) => col.write(out, selection),