diff --git a/crates/proof-of-sql/benches/bench_append_rows.rs b/crates/proof-of-sql/benches/bench_append_rows.rs index 8d7484d5f..150b413a9 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, tinyint, int128, owned_table, scalar, smallint, timestamptz, varchar, }, OwnedTable, @@ -76,6 +76,7 @@ pub fn generate_random_owned_table( "boolean", "int128", "scalar", + "tinyint", "varchar", "decimal75", "smallint", @@ -103,6 +104,10 @@ pub fn generate_random_owned_table( identifier.deref(), vec![generate_random_u64_array(); num_rows], )), + "tinyint" => colums.push(tinyint( + identifier.deref(), + vec![rng.gen::(); num_rows], + )), "varchar" => columns.push(varchar(identifier.deref(), gen_rnd_str(num_rows))), "decimal75" => columns.push(decimal75( identifier.deref(), 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..5c66721bb 100644 --- a/crates/proof-of-sql/src/base/commitment/column_bounds.rs +++ b/crates/proof-of-sql/src/base/commitment/column_bounds.rs @@ -207,6 +207,8 @@ pub enum ColumnBounds { NoOrder, /// The bounds of a SmallInt column. SmallInt(Bounds), + /// The bounds of a TinyInt column. + TinyInt(Bounds), /// The bounds of an Int column. Int(Bounds), /// The bounds of a BigInt column. @@ -224,6 +226,7 @@ impl ColumnBounds { pub fn from_column(column: &CommittableColumn) -> ColumnBounds { match column { CommittableColumn::SmallInt(ints) => ColumnBounds::SmallInt(Bounds::from_iter(*ints)), + CommittableColumn::TinyInt(ints) => ColumnBounds::TinyInt(Bounds::from_iter(*ints)), CommittableColumn::Int(ints) => ColumnBounds::Int(Bounds::from_iter(*ints)), CommittableColumn::BigInt(ints) => ColumnBounds::BigInt(Bounds::from_iter(*ints)), CommittableColumn::Int128(ints) => ColumnBounds::Int128(Bounds::from_iter(*ints)), @@ -247,6 +250,9 @@ impl ColumnBounds { (ColumnBounds::SmallInt(bounds_a), ColumnBounds::SmallInt(bounds_b)) => { Ok(ColumnBounds::SmallInt(bounds_a.union(bounds_b))) } + (ColumnBounds::TinyInt(bounds_a), ColumnBounds::TinyInt(bounds_b)) => { + Ok(ColumnBounds::TinyInt(bounds_a.union(bounds_b))) + } (ColumnBounds::Int(bounds_a), ColumnBounds::Int(bounds_b)) => { Ok(ColumnBounds::Int(bounds_a.union(bounds_b))) } @@ -279,6 +285,9 @@ impl ColumnBounds { (ColumnBounds::Int(bounds_a), ColumnBounds::Int(bounds_b)) => { Ok(ColumnBounds::Int(bounds_a.difference(bounds_b))) } + (ColumnBounds::TinyInt(bounds_a), ColumnBounds::TinyInt(bounds_b)) => { + Ok(ColumnBounds::TinyInt(bounds_a.difference(bounds_b))) + } (ColumnBounds::BigInt(bounds_a), ColumnBounds::BigInt(bounds_b)) => { Ok(ColumnBounds::BigInt(bounds_a.difference(bounds_b))) } @@ -298,6 +307,8 @@ impl ColumnBounds { #[cfg(test)] mod tests { + use core::time; + use super::*; use crate::base::{database::OwnedColumn, math::decimal::Precision, scalar::Curve25519Scalar}; use alloc::{string::String, vec}; @@ -522,6 +533,14 @@ mod tests { ColumnBounds::BigInt(Bounds::Sharp(BoundsInner { min: 0, max: 3 })) ); + 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 int128_column = OwnedColumn::::Int128([1, 2, 3, 1, 0].to_vec()); let committable_int128_column = CommittableColumn::from(&int128_column); let int128_column_bounds = ColumnBounds::from_column(&committable_int128_column); @@ -574,7 +593,13 @@ mod tests { int_a.try_union(int_b).unwrap(), ColumnBounds::Int(Bounds::Sharp(BoundsInner { min: 1, max: 6 })) ); - + + 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 bigint_a = ColumnBounds::BigInt(Bounds::Sharp(BoundsInner { min: 1, max: 3 })); let bigint_b = ColumnBounds::BigInt(Bounds::Sharp(BoundsInner { min: 4, max: 6 })); assert_eq!( @@ -609,6 +634,7 @@ mod tests { fn we_cannot_union_mismatched_column_bounds() { let no_order = ColumnBounds::NoOrder; let smallint = ColumnBounds::SmallInt(Bounds::Sharp(BoundsInner { min: -5, max: 5 })); + let tinyint = ColumnBounds::TinyInt(Bounds::Sharp(BoundsInner { min: -3, max: 4})); let int = ColumnBounds::Int(Bounds::Sharp(BoundsInner { min: -10, max: 10 })); let bigint = ColumnBounds::BigInt(Bounds::Sharp(BoundsInner { min: 1, max: 3 })); let int128 = ColumnBounds::Int128(Bounds::Sharp(BoundsInner { min: 4, max: 6 })); @@ -620,6 +646,7 @@ mod tests { (int, "Int"), (bigint, "BigInt"), (int128, "Int128"), + (tinyint, "TinyInt"), (timestamp, "Timestamp"), ]; @@ -648,6 +675,10 @@ mod tests { let smallint_b = ColumnBounds::SmallInt(Bounds::Empty); assert_eq!(smallint_a.try_difference(smallint_b).unwrap(), smallint_a); + let tinyint_a = ColumnBounds::TinyInt(Bounds::Sharp(BoundsInner { min: 1, max: 3 })); + let tinyint_b = ColumnBounds::TinyInt(Bounds::Empty); + assert_eq!(smallint_a.try_difference(tinyint_b).unwrap(), tinyint_a); + let int_a = ColumnBounds::Int(Bounds::Sharp(BoundsInner { min: 1, max: 3 })); let int_b = ColumnBounds::Int(Bounds::Empty); assert_eq!(int_a.try_difference(int_b).unwrap(), int_a); @@ -678,6 +709,7 @@ mod tests { let int128 = ColumnBounds::Int128(Bounds::Sharp(BoundsInner { min: 4, max: 6 })); let timestamp = ColumnBounds::TimestampTZ(Bounds::Sharp(BoundsInner { min: 4, max: 6 })); let smallint = ColumnBounds::SmallInt(Bounds::Sharp(BoundsInner { min: 1, max: 3 })); + let tinyint = ColumnBounds::TinyInt(Bounds::Sharp(BoundsInner { min: 2, max: 4})); assert!(no_order.try_difference(bigint).is_err()); assert!(bigint.try_difference(no_order).is_err()); @@ -690,5 +722,17 @@ mod tests { assert!(smallint.try_difference(timestamp).is_err()); assert!(timestamp.try_difference(smallint).is_err()); + + assert!(tinyint.try_difference(smallint).is_err()); + assert!(smallint.try_difference(tinyint).is_err()); + + assert!(tinyint.try_difference(bigint).is_err()); + assert!(bigint.try_difference(tinyint).is_err()); + + assert!(tinyint.try_difference(int128).is_err()); + assert!(int128.try_difference(tinyint).is_err()); + + assert!(tinyint.try_difference(timestamp).is_err()); + assert!(timestamp.try_difference(tinyint).is_err()); } } 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..88cbec1dc 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 @@ -47,6 +47,7 @@ impl ColumnCommitmentMetadata { (ColumnType::SmallInt, ColumnBounds::SmallInt(_)) | (ColumnType::Int, ColumnBounds::Int(_)) | (ColumnType::BigInt, ColumnBounds::BigInt(_)) + | (ColumnType::TinyInt, ColumnBounds::TinyInt(_)) | (ColumnType::Int128, ColumnBounds::Int128(_)) | (ColumnType::TimestampTZ(_, _), ColumnBounds::TimestampTZ(_)) | ( @@ -81,6 +82,10 @@ impl ColumnCommitmentMetadata { BoundsInner::try_new(i64::MIN, i64::MAX) .expect("i64::MIN and i64::MAX are valid bounds for BigInt"), )), + ColumnType::TinyInt => ColumnBounds::TinyInt(super::Bounds::Bounded( + BoundsInner::try_new(i8::MIN, i8::MAX) + .expect("i64::MIN and i64::MAX are valid bounds for TinyInt"), + )), ColumnType::TimestampTZ(_, _) => ColumnBounds::TimestampTZ(super::Bounds::Bounded( BoundsInner::try_new(i64::MIN, i64::MAX) .expect("i64::MIN and i64::MAX are valid bounds for TimeStamp"), @@ -215,6 +220,17 @@ mod tests { } ); +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::Boolean, ColumnBounds::NoOrder,).unwrap(), ColumnCommitmentMetadata { @@ -440,6 +456,18 @@ mod tests { panic!("Bounds constructed from nonempty BigInt column should be ColumnBounds::SmallInt(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.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 BigInt column should be ColumnBounds::TinyInt(Bounds::Sharp(_))"); + } + + let int128_column = OwnedColumn::::Int128([].to_vec()); let committable_int128_column = CommittableColumn::from(&int128_column); let int128_metadata = ColumnCommitmentMetadata::from_column(&committable_int128_column); @@ -509,6 +537,18 @@ mod tests { smallint_metadata_c ); + 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 + ); + let ints = [1, 2, 3, 1, 0]; let int_column_a = CommittableColumn::Int(&ints[..2]); let int_metadata_a = ColumnCommitmentMetadata::from_column(&int_column_a); @@ -647,8 +687,8 @@ mod tests { fn we_can_difference_smallint_matching_metadata() { // Ordered case let smallints = [1, 2, 3, 1, 0]; - let smallint_column_a = CommittableColumn::SmallInt(&smallints[..2]); - let smallint_metadata_a = ColumnCommitmentMetadata::from_column(&smallint_column_a); + let smallint_column_a = CommittableColumn::SmallInt(&s[..2]); + let smallint_metadata_a = ColumnCommitmentMetadata::from_column(&_column_a); let smallint_column_b = CommittableColumn::SmallInt(&smallints); let smallint_metadata_b = ColumnCommitmentMetadata::from_column(&smallint_column_b); @@ -679,6 +719,39 @@ mod tests { smallint_metadata_empty ); } + 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_int_matching_metadata() { @@ -729,6 +802,10 @@ mod tests { column_type: ColumnType::SmallInt, bounds: ColumnBounds::SmallInt(Bounds::Empty), }; + let tinyint_metadata = ColumnCommitmentMetadata { + column_type: ColumnType::TinyInt, + bounds: ColumnBounds::TinyInt(Bounds::Empty), + }; let int_metadata = ColumnCommitmentMetadata { column_type: ColumnType::Int, bounds: ColumnBounds::Int(Bounds::Empty), @@ -758,6 +835,25 @@ mod tests { assert!(smallint_metadata.try_union(boolean_metadata).is_err()); assert!(boolean_metadata.try_union(smallint_metadata).is_err()); + assert!(tinyint_metadata.try_union(scalar_metadata).is_err()); + assert!(scalar_metadata.try_union(tinyint_metadata).is_err()); + + assert!(tinyint_metadata.try_union(int_metadata).is_err()); + assert!(int_metadata.try_union(tinyint_metadata).is_err()); + + assert!(tinyint_metadata.try_union(bigint_metadata).is_err()); + assert!(bigint_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!(tinyint_metadata.try_union(boolean_metadata).is_err()); + assert!(boolean_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!(int_metadata.try_union(scalar_metadata).is_err()); assert!(scalar_metadata.try_union(int_metadata).is_err()); @@ -845,6 +941,24 @@ mod tests { assert!(boolean_metadata.try_difference(scalar_metadata).is_err()); assert!(scalar_metadata.try_difference(boolean_metadata).is_err()); + assert!(tinyint_metadata.try_difference(scalar_metadata).is_err()); + assert!(scalar_metadata.try_difference(tinyint_metadata).is_err()); + + assert!(tinyint_metadata.try_difference(int_metadata).is_err()); + assert!(int_metadata.try_difference(tinyint_metadata).is_err()); + + assert!(tinyint_metadata.try_difference(bigint_metadata).is_err()); + assert!(bigint_metadata.try_difference(tinyint_metadata).is_err()); + + assert!(tinyint_metadata.try_difference(smallint_metadata).is_err()); + assert!(smallint_metadata.try_difference(tinyint_metadata).is_err()); + + assert!(tinyint_metadata.try_difference(boolean_metadata).is_err()); + assert!(boolean_metadata.try_difference(tinyint_metadata).is_err()); + + assert!(tinyint_metadata.try_difference(varchar_metadata).is_err()); + assert!(varchar_metadata.try_difference(tinyint_metadata).is_err()); + let different_decimal75_metadata = ColumnCommitmentMetadata { column_type: ColumnType::Decimal75(Precision::new(75).unwrap(), 0), bounds: ColumnBounds::Int128(Bounds::Empty), 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..4a3df5d37 100644 --- a/crates/proof-of-sql/src/base/commitment/committable_column.rs +++ b/crates/proof-of-sql/src/base/commitment/committable_column.rs @@ -29,6 +29,8 @@ pub enum CommittableColumn<'a> { SmallInt(&'a [i16]), /// Borrowed SmallInt column, mapped to `i32`. Int(&'a [i32]), + /// Borrowed TinyInt column, mapped to `i8`. + TinyInt(&'a [i8]), /// Borrowed BigInt column, mapped to `i64`. BigInt(&'a [i64]), /// Borrowed Int128 column, mapped to `i128`. @@ -52,6 +54,7 @@ impl<'a> CommittableColumn<'a> { match self { CommittableColumn::SmallInt(col) => col.len(), CommittableColumn::Int(col) => col.len(), + CommittableColumn::TinyInt(col) => col.len(), CommittableColumn::BigInt(col) => col.len(), CommittableColumn::Int128(col) => col.len(), CommittableColumn::Decimal75(_, _, col) => col.len(), @@ -79,6 +82,7 @@ impl<'a> From<&CommittableColumn<'a>> for ColumnType { match value { CommittableColumn::SmallInt(_) => ColumnType::SmallInt, CommittableColumn::Int(_) => ColumnType::Int, + CommittableColumn::TinyInt(_) => ColumnType::TinyInt, CommittableColumn::BigInt(_) => ColumnType::BigInt, CommittableColumn::Int128(_) => ColumnType::Int128, CommittableColumn::Decimal75(precision, scale, _) => { @@ -100,6 +104,7 @@ impl<'a, S: Scalar> From<&Column<'a, S>> for CommittableColumn<'a> { match value { Column::Boolean(bools) => CommittableColumn::Boolean(bools), Column::SmallInt(ints) => CommittableColumn::SmallInt(ints), + Column::TinyInt(ints) => CommittableColumn::TinyInt(ints), Column::Int(ints) => CommittableColumn::Int(ints), Column::BigInt(ints) => CommittableColumn::BigInt(ints), Column::Int128(ints) => CommittableColumn::Int128(ints), @@ -122,6 +127,7 @@ impl<'a, S: Scalar> From<&'a OwnedColumn> for CommittableColumn<'a> { match value { OwnedColumn::Boolean(bools) => CommittableColumn::Boolean(bools), OwnedColumn::SmallInt(ints) => (ints as &[_]).into(), + OwnedColumn::TinyInt(ints) => (ints as &[_]).into(), OwnedColumn::Int(ints) => (ints as &[_]).into(), OwnedColumn::BigInt(ints) => (ints as &[_]).into(), OwnedColumn::Int128(ints) => (ints as &[_]).into(), @@ -165,6 +171,12 @@ impl<'a> From<&'a [i32]> for CommittableColumn<'a> { } } +impl<'a> From<&'a [i8]> for CommittableColumn<'a> { + fn from(value: &'a [i8]) -> Self { + CommittableColumn::TinyInt(value) + } +} + impl<'a> From<&'a [i64]> for CommittableColumn<'a> { fn from(value: &'a [i64]) -> Self { CommittableColumn::BigInt(value) @@ -191,6 +203,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), @@ -292,7 +305,19 @@ mod tests { assert!(!int_committable_column.is_empty()); assert_eq!(int_committable_column.column_type(), ColumnType::Int); } - + #[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_bigint_column() { // empty case @@ -453,6 +478,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 @@ -591,6 +631,20 @@ mod tests { CommittableColumn::SmallInt(&[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_timestamp_column() { @@ -765,6 +819,29 @@ 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_int_column_through_committable_column() { diff --git a/crates/proof-of-sql/src/base/commitment/naive_commitment.rs b/crates/proof-of-sql/src/base/commitment/naive_commitment.rs index 4891e5b60..753d03f3d 100644 --- a/crates/proof-of-sql/src/base/commitment/naive_commitment.rs +++ b/crates/proof-of-sql/src/base/commitment/naive_commitment.rs @@ -124,6 +124,9 @@ impl Commitment for NaiveCommitment { CommittableColumn::SmallInt(small_int_vec) => { small_int_vec.iter().map(|b| b.into()).collect() } + CommittableColumn::TinyInt(tiny_int_vec) => { + tiny_int_vec.iter().map(|b| b.into()).collect() + } CommittableColumn::Int(int_vec) => int_vec.iter().map(|b| b.into()).collect(), CommittableColumn::BigInt(big_int_vec) => { big_int_vec.iter().map(|b| b.into()).collect() diff --git a/crates/proof-of-sql/src/base/database/arrow_array_to_column_conversion.rs b/crates/proof-of-sql/src/base/database/arrow_array_to_column_conversion.rs index baf618a69..891c8903d 100644 --- a/crates/proof-of-sql/src/base/database/arrow_array_to_column_conversion.rs +++ b/crates/proof-of-sql/src/base/database/arrow_array_to_column_conversion.rs @@ -250,6 +250,15 @@ impl ArrayRefExt for ArrayRef { }) } } + DataType::Int8 => { + if let Some(array) = self.as_any().downcast_ref::() { + Ok(Column::TinyInt(&array.values()[range.start..range.end])) + } else { + Err(ArrowArrayToColumnConversionError::UnsupportedType { + datatype: self.data_type().clone(), + }) + } + } DataType::Int64 => { if let Some(array) = self.as_any().downcast_ref::() { Ok(Column::BigInt(&array.values()[range.start..range.end])) @@ -379,7 +388,7 @@ mod tests { use super::*; use crate::{base::scalar::Curve25519Scalar, proof_primitive::dory::DoryScalar}; use alloc::sync::Arc; - use arrow::array::Decimal256Builder; + use arrow::array::{Decimal256Builder, Int8Array}; use core::str::FromStr; #[test] @@ -727,7 +736,46 @@ mod tests { Err(ArrowArrayToColumnConversionError::ArrayContainsNulls) )); } - + #[test] + fn we_can_convert_int8_array_normal_range() { + let alloc = Bump::new(); + let array: ArrayRef = Arc::new(Int8Array::from(vec![1, -3, 42])); + let result = array.to_column::(&alloc, &(1..3), None); + assert_eq!(result.unwrap(), Column::TinyInt(&[-3, 42])); + } + + #[test] + fn we_can_convert_int8_array_empty_range() { + let alloc = Bump::new(); + let array: ArrayRef = Arc::new(Int8Array::from(vec![1, -3, 42])); + let result = array.to_column::(&alloc, &(1..1), None); + assert_eq!(result.unwrap(), Column::TinyInt(&[])); + } + + #[test] + fn we_cannot_convert_int8_array_oob_range() { + let alloc = Bump::new(); + let array: ArrayRef = Arc::new(Int8Array::from(vec![1, -3, 42])); + + let result = array.to_column::(&alloc, &(2..4), None); + + assert_eq!( + result, + Err(ArrowArrayToColumnConversionError::IndexOutOfBounds { len: 3, index: 4 }) + ); + } + + #[test] + fn we_can_convert_int8_array_with_nulls() { + let alloc = Bump::new(); + let array: ArrayRef = Arc::new(Int8Array::from(vec![Some(1), None, Some(42)])); + let result = array.to_column::(&alloc, &(0..3), None); + assert!(matches!( + result, + Err(ArrowArrayToColumnConversionError::ArrayContainsNulls) + )); + } + #[test] fn we_can_convert_int16_array_normal_range() { let alloc = Bump::new(); @@ -962,7 +1010,13 @@ mod tests { .unwrap(), Column::SmallInt(&[1, -3]) ); - + let array: ArrayRef = Arc::new(arrow::array::Int8Array::from(vec![1, -3])); + assert_eq!( + array + .to_column::(&alloc, &(0..2), None) + .unwrap(), + Column::TinyInt(&[1, -3]) + ); let array: ArrayRef = Arc::new(arrow::array::Int32Array::from(vec![1, -3])); assert_eq!( array @@ -1050,6 +1104,13 @@ mod tests { .unwrap(), Column::SmallInt(&[1, 545]) ); + let array: ArrayRef = Arc::new(arrow::array::Int8Array::from(vec![0, 1, 545])); + assert_eq!( + array + .to_column::(&alloc, &(1..3), None) + .unwrap(), + Column::TinyInt(&[1, 545]) + ); let array: ArrayRef = Arc::new(arrow::array::Int32Array::from(vec![0, 1, 545])); assert_eq!( @@ -1188,6 +1249,16 @@ mod tests { .collect::>()) ); + let data: Vec = vec![1, -3]; + let array: ArrayRef = Arc::new(Int8Array::from(data.clone())); + assert_eq!( + array.to_curve25519_scalars(), + Ok(data + .iter() + .map(|v| v.into()) + .collect::>()) + ); + let data = vec![1, -3]; let array: ArrayRef = Arc::new(Int32Array::from(data.clone())); assert_eq!( diff --git a/crates/proof-of-sql/src/base/database/column.rs b/crates/proof-of-sql/src/base/database/column.rs index 6b6c39b38..6b2fb1568 100644 --- a/crates/proof-of-sql/src/base/database/column.rs +++ b/crates/proof-of-sql/src/base/database/column.rs @@ -33,6 +33,8 @@ pub enum Column<'a, S: Scalar> { /// i16 columns SmallInt(&'a [i16]), /// i32 columns + TinyInt(&'a [i8]), + /// i8 columns Int(&'a [i32]), /// i64 columns BigInt(&'a [i64]), @@ -60,6 +62,7 @@ impl<'a, S: Scalar> Column<'a, S> { match self { Self::Boolean(_) => ColumnType::Boolean, Self::SmallInt(_) => ColumnType::SmallInt, + Self::TinyInt(_) => ColumnType::TinyInt, Self::Int(_) => ColumnType::Int, Self::BigInt(_) => ColumnType::BigInt, Self::VarChar(_) => ColumnType::VarChar, @@ -76,6 +79,7 @@ impl<'a, S: Scalar> Column<'a, S> { match self { Self::Boolean(col) => col.len(), Self::SmallInt(col) => col.len(), + Self::TinyInt(col) => col.len(), Self::Int(col) => col.len(), Self::BigInt(col) => col.len(), Self::VarChar((col, scals)) => { @@ -106,6 +110,9 @@ impl<'a, S: Scalar> Column<'a, S> { LiteralValue::SmallInt(value) => { Column::SmallInt(alloc.alloc_slice_fill_copy(length, *value)) } + LiteralValue::TinyInt(value) => { + Column::TinyInt(alloc.alloc_slice_fill_copy(length, *value)) + } LiteralValue::Int(value) => Column::Int(alloc.alloc_slice_fill_copy(length, *value)), LiteralValue::BigInt(value) => { Column::BigInt(alloc.alloc_slice_fill_copy(length, *value)) @@ -136,6 +143,7 @@ impl<'a, S: Scalar> Column<'a, S> { match owned_column { OwnedColumn::Boolean(col) => Column::Boolean(col.as_slice()), OwnedColumn::SmallInt(col) => Column::SmallInt(col.as_slice()), + OwnedColumn::TinyInt(col) => Column::TinyInt(col.as_slice()), OwnedColumn::Int(col) => Column::Int(col.as_slice()), OwnedColumn::BigInt(col) => Column::BigInt(col.as_slice()), OwnedColumn::Int128(col) => Column::Int128(col.as_slice()), @@ -173,6 +181,7 @@ impl<'a, S: Scalar> Column<'a, S> { (index < self.len()).then_some(match self { Self::Boolean(col) => S::from(col[index]), Self::SmallInt(col) => S::from(col[index]), + Self::TinyInt(col) => S::from(col[index]), Self::Int(col) => S::from(col[index]), Self::BigInt(col) => S::from(col[index]), Self::Int128(col) => S::from(col[index]), @@ -191,6 +200,7 @@ impl<'a, S: Scalar> Column<'a, S> { Self::Decimal75(_, _, col) => slice_cast_with(col, |s| *s * scale_factor), Self::VarChar((_, scals)) => slice_cast_with(scals, |s| *s * scale_factor), Self::SmallInt(col) => slice_cast_with(col, |i| S::from(i) * scale_factor), + Self::TinyInt(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), Self::Int128(col) => slice_cast_with(col, |i| S::from(i) * scale_factor), @@ -213,6 +223,9 @@ pub enum ColumnType { /// Mapped to i16 #[serde(alias = "SMALLINT", alias = "smallint")] SmallInt, + /// Mapped to i8 + #[serde(alias = "TINYINT", alias = "tinyint")] + TinyInt, /// Mapped to i32 #[serde(alias = "INT", alias = "int")] Int, @@ -243,6 +256,7 @@ impl ColumnType { self, ColumnType::SmallInt | ColumnType::Int + | ColumnType::TinyInt | ColumnType::BigInt | ColumnType::Int128 | ColumnType::Scalar @@ -254,13 +268,14 @@ impl ColumnType { pub fn is_integer(&self) -> bool { matches!( self, - ColumnType::SmallInt | ColumnType::Int | ColumnType::BigInt | ColumnType::Int128 + ColumnType::SmallInt | ColumnType::TinyInt |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 +289,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 +316,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 +333,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::SmallInt | Self::TinyInt| 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 +348,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 +365,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::SmallInt | Self::TinyInt | Self::Int | Self::BigInt | Self::Int128 | Self::TimestampTZ(_, _) => { true } Self::Decimal75(_, _) | Self::Scalar | Self::VarChar | Self::Boolean => false, @@ -361,6 +379,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 +411,7 @@ impl TryFrom for ColumnType { fn try_from(data_type: DataType) -> Result { match data_type { DataType::Boolean => Ok(ColumnType::Boolean), + DataType::Int8 => Ok(ColumnType::TinyInt), DataType::Int16 => Ok(ColumnType::SmallInt), DataType::Int32 => Ok(ColumnType::Int), DataType::Int64 => Ok(ColumnType::BigInt), @@ -422,6 +442,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 +555,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""#); @@ -640,6 +665,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 +740,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()); @@ -751,11 +787,19 @@ mod tests { smallint ); + let tinyint = ColumnType::TinyInt; + let tinyint_json = serde_json::to_string(&tinyint).unwrap(); + assert_eq!(tinyint_json, "\"Decimal\""); + assert_eq!( + serde_json::from_str::(&tinyint_json).unwrap(), + tinyint + ); + let int = ColumnType::Int; let int_json = serde_json::to_string(&int).unwrap(); assert_eq!(int_json, "\"Int\""); assert_eq!(serde_json::from_str::(&int_json).unwrap(), int); - + let bigint = ColumnType::BigInt; let bigint_json = serde_json::to_string(&bigint).unwrap(); assert_eq!(bigint_json, "\"BigInt\""); @@ -817,6 +861,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::::Int(&[1, 2, 3]); assert_eq!(column.len(), 3); assert!(!column.is_empty()); @@ -853,6 +901,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 +988,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/expression_evaluation_test.rs b/crates/proof-of-sql/src/base/database/expression_evaluation_test.rs index 9bb2ddaaf..b3868a2cd 100644 --- a/crates/proof-of-sql/src/base/database/expression_evaluation_test.rs +++ b/crates/proof-of-sql/src/base/database/expression_evaluation_test.rs @@ -143,6 +143,7 @@ fn we_can_evaluate_a_logical_expression() { #[test] fn we_can_evaluate_an_arithmetic_expression() { let table: OwnedTable = owned_table([ + tinyint("tinyints", [-128_i8, -64, 0, 64, 127]), smallint("smallints", [-2_i16, -1, 0, 1, 2]), int("ints", [-4_i32, -2, 0, 2, 4]), bigint("bigints", [-8_i64, -4, 0, 4, 8]), 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 a8afaf261..60cfb6c10 100644 --- a/crates/proof-of-sql/src/base/database/filter_util.rs +++ b/crates/proof-of-sql/src/base/database/filter_util.rs @@ -42,6 +42,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/base/database/group_by_util.rs b/crates/proof-of-sql/src/base/database/group_by_util.rs index 177e390ad..309db1c20 100644 --- a/crates/proof-of-sql/src/base/database/group_by_util.rs +++ b/crates/proof-of-sql/src/base/database/group_by_util.rs @@ -140,6 +140,7 @@ pub(crate) fn sum_aggregate_column_by_index_counts<'a, S: Scalar>( indexes: &[usize], ) -> &'a [S] { match column { + Column::TinyInt(col) => sum_aggregate_slice_by_index_counts(alloc, col, counts, indexes), Column::SmallInt(col) => sum_aggregate_slice_by_index_counts(alloc, col, counts, indexes), Column::Int(col) => sum_aggregate_slice_by_index_counts(alloc, col, counts, indexes), Column::BigInt(col) => sum_aggregate_slice_by_index_counts(alloc, col, counts, indexes), @@ -168,6 +169,7 @@ pub(crate) fn max_aggregate_column_by_index_counts<'a, S: Scalar>( ) -> &'a [Option] { match column { Column::Boolean(col) => max_aggregate_slice_by_index_counts(alloc, col, counts, indexes), + Column::TinyInt(col) => max_aggregate_slice_by_index_counts(alloc, col, counts, indexes), Column::SmallInt(col) => max_aggregate_slice_by_index_counts(alloc, col, counts, indexes), Column::Int(col) => max_aggregate_slice_by_index_counts(alloc, col, counts, indexes), Column::BigInt(col) => max_aggregate_slice_by_index_counts(alloc, col, counts, indexes), @@ -199,6 +201,7 @@ pub(crate) fn min_aggregate_column_by_index_counts<'a, S: Scalar>( ) -> &'a [Option] { match column { Column::Boolean(col) => min_aggregate_slice_by_index_counts(alloc, col, counts, indexes), + Column::TinyInt(col) => min_aggregate_slice_by_index_counts(alloc, col, counts, indexes), Column::SmallInt(col) => min_aggregate_slice_by_index_counts(alloc, col, counts, indexes), Column::Int(col) => min_aggregate_slice_by_index_counts(alloc, col, counts, indexes), Column::BigInt(col) => min_aggregate_slice_by_index_counts(alloc, col, counts, indexes), @@ -352,6 +355,7 @@ pub(crate) fn compare_indexes_by_columns( .iter() .map(|col| match col { Column::Boolean(col) => col[i].cmp(&col[j]), + Column::TinyInt(col) => col[i].cmp(&col[j]), Column::SmallInt(col) => col[i].cmp(&col[j]), Column::Int(col) => col[i].cmp(&col[j]), Column::BigInt(col) => col[i].cmp(&col[j]), @@ -378,6 +382,7 @@ pub(crate) fn compare_indexes_by_owned_columns( .iter() .map(|col| 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]), diff --git a/crates/proof-of-sql/src/base/database/literal_value.rs b/crates/proof-of-sql/src/base/database/literal_value.rs index bdabe3df2..d789ef79e 100644 --- a/crates/proof-of-sql/src/base/database/literal_value.rs +++ b/crates/proof-of-sql/src/base/database/literal_value.rs @@ -13,6 +13,8 @@ use serde::{Deserialize, Serialize}; pub enum LiteralValue { /// Boolean literals Boolean(bool), + /// i8 literals + TinyInt(i8), /// i16 literals SmallInt(i16), /// i32 literals @@ -41,6 +43,7 @@ impl LiteralValue { pub fn column_type(&self) -> ColumnType { match self { Self::Boolean(_) => ColumnType::Boolean, + Self::TinyInt(_) => ColumnType::TinyInt, Self::SmallInt(_) => ColumnType::SmallInt, Self::Int(_) => ColumnType::Int, Self::BigInt(_) => ColumnType::BigInt, @@ -56,6 +59,7 @@ impl LiteralValue { pub(crate) fn to_scalar(&self) -> S { match self { Self::Boolean(b) => b.into(), + Self::TinyInt(i) => i.into(), Self::SmallInt(i) => i.into(), Self::Int(i) => i.into(), Self::BigInt(i) => i.into(), diff --git a/crates/proof-of-sql/src/base/database/owned_and_arrow_conversions.rs b/crates/proof-of-sql/src/base/database/owned_and_arrow_conversions.rs index d4b66f2f3..720bb263b 100644 --- a/crates/proof-of-sql/src/base/database/owned_and_arrow_conversions.rs +++ b/crates/proof-of-sql/src/base/database/owned_and_arrow_conversions.rs @@ -25,9 +25,7 @@ use crate::base::{ use alloc::sync::Arc; use arrow::{ array::{ - ArrayRef, BooleanArray, Decimal128Array, Decimal256Array, Int16Array, Int32Array, - Int64Array, StringArray, TimestampMicrosecondArray, TimestampMillisecondArray, - TimestampNanosecondArray, TimestampSecondArray, + ArrayRef, BooleanArray, Decimal128Array, Decimal256Array, Int16Array, Int32Array, Int64Array, Int8Array, StringArray, TimestampMicrosecondArray, TimestampMillisecondArray, TimestampNanosecondArray, TimestampSecondArray }, datatypes::{i256, DataType, Schema, SchemaRef, TimeUnit as ArrowTimeUnit}, error::ArrowError, @@ -81,6 +79,7 @@ impl From> for ArrayRef { fn from(value: OwnedColumn) -> Self { match value { OwnedColumn::Boolean(col) => Arc::new(BooleanArray::from(col)), + OwnedColumn::TinyInt(col) => Arc::new(Int8Array::from(col)), OwnedColumn::SmallInt(col) => Arc::new(Int16Array::from(col)), OwnedColumn::Int(col) => Arc::new(Int32Array::from(col)), OwnedColumn::BigInt(col) => Arc::new(Int64Array::from(col)), @@ -147,6 +146,14 @@ impl TryFrom<&ArrayRef> for OwnedColumn { .collect::>>() .ok_or(OwnedArrowConversionError::NullNotSupportedYet)?, )), + DataType::Int8 => Ok(Self::TinyInt( + value + .as_any() + .downcast_ref::() + .unwrap() + .values() + .to_vec(), + )), DataType::Int16 => Ok(Self::SmallInt( value .as_any() 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..f9ef1819e 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::SmallInt(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::i8, 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() @@ -314,6 +330,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]), diff --git a/crates/proof-of-sql/src/base/database/owned_table_utility.rs b/crates/proof-of-sql/src/base/database/owned_table_utility.rs index 43e2c8a92..a9019e14b 100644 --- a/crates/proof-of-sql/src/base/database/owned_table_utility.rs +++ b/crates/proof-of-sql/src/base/database/owned_table_utility.rs @@ -11,6 +11,7 @@ //! scalar("d", [1, 2, 3]), //! varchar("e", ["a", "b", "c"]), //! decimal75("f", 12, 1, [1, 2, 3]), +//! tinyint("g",[1, 2, 3]), //! ]); //! ``` use super::{OwnedColumn, OwnedTable}; @@ -37,6 +38,7 @@ use proof_of_sql_parser::{ /// scalar("d", [1, 2, 3]), /// varchar("e", ["a", "b", "c"]), /// decimal75("f", 12, 1, [1, 2, 3]), +/// tinyint("g",[1, 2, 3]), /// ]); /// ``` pub fn owned_table( @@ -98,7 +100,23 @@ pub fn bigint( OwnedColumn::BigInt(data.into_iter().map(Into::into).collect()), ) } - +/// Creates a (Identifier, OwnedColumn) pair for a tinyint column. +/// This is primarily intended for use in conjunction with [owned_table]. +/// # Example +/// ``` +/// use proof_of_sql::base::{database::owned_table_utility::*, scalar::Curve25519Scalar}; +/// let result = owned_table::([ +/// tinyint("a", [1, 2, 3]), +/// ]); +pub fn tinyint( + name: impl Deref, + data: impl IntoIterator>, +) -> (Identifier, OwnedColumn) { + ( + name.parse().unwrap(), + OwnedColumn::TinyInt(data.into_iter().map(Into::into).collect()), + ) +} /// Creates a (Identifier, OwnedColumn) pair for a boolean column. /// This is primarily intended for use in conjunction with [owned_table]. /// # Example diff --git a/crates/proof-of-sql/src/base/database/test_accessor_utility.rs b/crates/proof-of-sql/src/base/database/test_accessor_utility.rs index 6e1ddceac..6ac4b3dd7 100644 --- a/crates/proof-of-sql/src/base/database/test_accessor_utility.rs +++ b/crates/proof-of-sql/src/base/database/test_accessor_utility.rs @@ -1,7 +1,7 @@ use crate::base::database::ColumnType; use arrow::{ array::{ - Array, BooleanArray, Decimal128Array, Decimal256Array, Int16Array, Int32Array, Int64Array, + Array, BooleanArray, Decimal128Array, Decimal256Array,Int8Array, Int16Array, Int32Array, Int64Array, StringArray, TimestampMicrosecondArray, TimestampMillisecondArray, TimestampNanosecondArray, TimestampSecondArray, }, @@ -69,6 +69,14 @@ pub fn make_random_test_accessor_data( .collect(); columns.push(Arc::new(Int16Array::from(values))); } + ColumnType::TinyInt => { + column_fields.push(Field::new(*col_name, DataType::Int8, false)); + let values: Vec = values + .iter() + .map(|x| ((*x >> 56) as i8)) // Shift right to align the lower 8 + .collect(); + columns.push(Arc::new(Int8Array::from(values))); + } ColumnType::Int => { column_fields.push(Field::new(*col_name, DataType::Int32, false)); let values: Vec = values @@ -170,6 +178,7 @@ mod tests { ("c", ColumnType::Int128), ("d", ColumnType::SmallInt), ("e", ColumnType::Int), + ("g", ColumnType::TinyInt), ]; let data1 = make_random_test_accessor_data(&mut rng, &cols, &descriptor); diff --git a/crates/proof-of-sql/src/base/polynomial/multilinear_extension.rs b/crates/proof-of-sql/src/base/polynomial/multilinear_extension.rs index 6d1085cf5..7af23590e 100644 --- a/crates/proof-of-sql/src/base/polynomial/multilinear_extension.rs +++ b/crates/proof-of-sql/src/base/polynomial/multilinear_extension.rs @@ -102,6 +102,7 @@ impl MultilinearExtension for Column<'_, S> { Column::Boolean(c) => c.inner_product(evaluation_vec), Column::Scalar(c) => c.inner_product(evaluation_vec), Column::SmallInt(c) => c.inner_product(evaluation_vec), + Column::TinyInt(c) => c.inner_product(evaluation_vec), Column::Int(c) => c.inner_product(evaluation_vec), Column::BigInt(c) => c.inner_product(evaluation_vec), Column::VarChar((_, c)) => c.inner_product(evaluation_vec), @@ -116,6 +117,7 @@ impl MultilinearExtension for Column<'_, S> { Column::Boolean(c) => c.mul_add(res, multiplier), Column::Scalar(c) => c.mul_add(res, multiplier), Column::SmallInt(c) => c.mul_add(res, multiplier), + Column::TinyInt(c) => c.mul_add(res, multiplier), Column::Int(c) => c.mul_add(res, multiplier), Column::BigInt(c) => c.mul_add(res, multiplier), Column::VarChar((_, c)) => c.mul_add(res, multiplier), @@ -130,6 +132,7 @@ impl MultilinearExtension for Column<'_, S> { Column::Boolean(c) => c.to_sumcheck_term(num_vars), Column::Scalar(c) => c.to_sumcheck_term(num_vars), Column::SmallInt(c) => c.to_sumcheck_term(num_vars), + Column::TinyInt(c) => c.to_sumcheck_term(num_vars), Column::Int(c) => c.to_sumcheck_term(num_vars), Column::BigInt(c) => c.to_sumcheck_term(num_vars), Column::VarChar((_, c)) => c.to_sumcheck_term(num_vars), @@ -144,6 +147,7 @@ impl MultilinearExtension for Column<'_, S> { Column::Boolean(c) => MultilinearExtension::::id(c), Column::Scalar(c) => MultilinearExtension::::id(c), Column::SmallInt(c) => MultilinearExtension::::id(c), + Column::TinyInt(c) => MultilinearExtension::::id(c), Column::Int(c) => MultilinearExtension::::id(c), Column::BigInt(c) => MultilinearExtension::::id(c), Column::VarChar((_, c)) => MultilinearExtension::::id(c), diff --git a/crates/proof-of-sql/src/proof_primitive/dory/dory_commitment_helper_cpu.rs b/crates/proof-of-sql/src/proof_primitive/dory/dory_commitment_helper_cpu.rs index 1d103421a..6efefaf58 100644 --- a/crates/proof-of-sql/src/proof_primitive/dory/dory_commitment_helper_cpu.rs +++ b/crates/proof-of-sql/src/proof_primitive/dory/dory_commitment_helper_cpu.rs @@ -53,6 +53,7 @@ fn compute_dory_commitment( ) -> DoryCommitment { match committable_column { CommittableColumn::Scalar(column) => compute_dory_commitment_impl(column, offset, setup), + CommittableColumn::TinyInt(column) => compute_dory_commitment_impl(column, offset, setup), CommittableColumn::SmallInt(column) => compute_dory_commitment_impl(column, offset, setup), CommittableColumn::Int(column) => compute_dory_commitment_impl(column, offset, setup), CommittableColumn::BigInt(column) => compute_dory_commitment_impl(column, offset, setup), diff --git a/crates/proof-of-sql/src/proof_primitive/dory/dynamic_dory_commitment_helper_cpu.rs b/crates/proof-of-sql/src/proof_primitive/dory/dynamic_dory_commitment_helper_cpu.rs index 38ad59990..55a830834 100644 --- a/crates/proof-of-sql/src/proof_primitive/dory/dynamic_dory_commitment_helper_cpu.rs +++ b/crates/proof-of-sql/src/proof_primitive/dory/dynamic_dory_commitment_helper_cpu.rs @@ -41,6 +41,7 @@ fn compute_dory_commitment( match committable_column { CommittableColumn::Scalar(column) => compute_dory_commitment_impl(column, offset, setup), CommittableColumn::SmallInt(column) => compute_dory_commitment_impl(column, offset, setup), + CommittableColumn::TinyInt(column)=> compute_dory_commitment_impl(column, offset, setup), CommittableColumn::Int(column) => compute_dory_commitment_impl(column, offset, setup), CommittableColumn::BigInt(column) => compute_dory_commitment_impl(column, offset, setup), CommittableColumn::Int128(column) => compute_dory_commitment_impl(column, offset, setup), diff --git a/crates/proof-of-sql/src/proof_primitive/dory/offset_to_bytes.rs b/crates/proof-of-sql/src/proof_primitive/dory/offset_to_bytes.rs index 82287b5c5..c2ed54b2e 100644 --- a/crates/proof-of-sql/src/proof_primitive/dory/offset_to_bytes.rs +++ b/crates/proof-of-sql/src/proof_primitive/dory/offset_to_bytes.rs @@ -7,7 +7,12 @@ impl OffsetToBytes<1> for u8 { [*self] } } - +impl OffsetToBytes<1> for i8 { + fn offset_to_bytes(&self) -> [u8; i] { + let shifted = self.wrapping_sub(i8::MIN); + shifted.to_le_bytes() + } +} impl OffsetToBytes<2> for i16 { fn offset_to_bytes(&self) -> [u8; 2] { let shifted = self.wrapping_sub(i16::MIN); diff --git a/crates/proof-of-sql/src/proof_primitive/dory/pack_scalars.rs b/crates/proof-of-sql/src/proof_primitive/dory/pack_scalars.rs index 7946e7a50..a7eab7cd7 100644 --- a/crates/proof-of-sql/src/proof_primitive/dory/pack_scalars.rs +++ b/crates/proof-of-sql/src/proof_primitive/dory/pack_scalars.rs @@ -57,6 +57,7 @@ fn output_bit_table( /// * `column_type` - The type of a committable column. const fn min_as_f(column_type: ColumnType) -> F { match column_type { + ColumnType::TinyInt => MontFp!("-128"), ColumnType::SmallInt => MontFp!("-32768"), ColumnType::Int => MontFp!("-2147483648"), ColumnType::BigInt | ColumnType::TimestampTZ(_, _) => MontFp!("-9223372036854775808"), @@ -376,6 +377,17 @@ pub fn bit_table_and_scalars_for_packed_msm( .iter() .enumerate() .for_each(|(i, column)| match column { + CommittableColumn::TinyInt(column) => { + pack_bit( + column, + &mut packed_scalars, + cumulative_bit_sum_table[i], + offset, + committable_columns[i].column_type().byte_size(), + bit_table_full_sum_in_bytes, + num_matrix_commitment_columns, + ); + } CommittableColumn::SmallInt(column) => { pack_bit( column, diff --git a/crates/proof-of-sql/src/sql/proof/provable_query_result.rs b/crates/proof-of-sql/src/sql/proof/provable_query_result.rs index daa1b19bf..304d9d735 100644 --- a/crates/proof-of-sql/src/sql/proof/provable_query_result.rs +++ b/crates/proof-of-sql/src/sql/proof/provable_query_result.rs @@ -108,9 +108,10 @@ impl ProvableQueryResult { ColumnType::SmallInt => decode_and_convert::(&self.data[offset..]), ColumnType::Int => decode_and_convert::(&self.data[offset..]), ColumnType::BigInt => decode_and_convert::(&self.data[offset..]), + ColumnType::TinyInt => decode_and_convert::(&self[offset..]), ColumnType::Int128 => decode_and_convert::(&self.data[offset..]), ColumnType::Decimal75(_, _) => decode_and_convert::(&self.data[offset..]), - + ColumnType::Scalar => decode_and_convert::(&self.data[offset..]), ColumnType::VarChar => decode_and_convert::<&str, S>(&self.data[offset..]), ColumnType::TimestampTZ(_, _) => { @@ -166,6 +167,12 @@ impl ProvableQueryResult { offset += num_read; Ok((field.name(), OwnedColumn::BigInt(col))) } + ColumnType::TinyInt => { + let (col, num_read) = decode_multiple_elements(&self.data[offset..], n)?; + offset += num_read; + Ok((field.name(), OwnedColumn::TinyInt(col))) + } + ColumnType::Int128 => { let (col, num_read) = decode_multiple_elements(&self.data[offset..], n)?; offset += num_read; diff --git a/crates/proof-of-sql/src/sql/proof_exprs/test_utility.rs b/crates/proof-of-sql/src/sql/proof_exprs/test_utility.rs index 90d283f1f..12e9f411d 100644 --- a/crates/proof-of-sql/src/sql/proof_exprs/test_utility.rs +++ b/crates/proof-of-sql/src/sql/proof_exprs/test_utility.rs @@ -61,7 +61,9 @@ pub fn multiply(left: DynProofExpr, right: DynProofExpr) -> pub fn const_bool(val: bool) -> DynProofExpr { DynProofExpr::new_literal(LiteralValue::Boolean(val)) } - +pub fn const_tinyint(val: i8) -> DynProofExpr { + DynProofExpr::new_literal(LiteralValue::TinyInt(val)) +} pub fn const_smallint(val: i16) -> DynProofExpr { DynProofExpr::new_literal(LiteralValue::SmallInt(val)) } diff --git a/crates/proof-of-sql/tests/integration_tests.rs b/crates/proof-of-sql/tests/integration_tests.rs index 0a1c9670f..0570d9690 100644 --- a/crates/proof-of-sql/tests/integration_tests.rs +++ b/crates/proof-of-sql/tests/integration_tests.rs @@ -210,6 +210,7 @@ fn we_can_prove_a_basic_query_containing_extrema_with_curve25519() { accessor.add_table( "sxt.table".parse().unwrap(), owned_table([ + tinyint("tinyint",[i8::MIN, 0, i8::MAX]), smallint("smallint", [i16::MIN, 0, i16::MAX]), int("int", [i32::MIN, 0, i32::MAX]), bigint("bigint", [i64::MIN, 0, i64::MAX]), @@ -230,6 +231,7 @@ fn we_can_prove_a_basic_query_containing_extrema_with_curve25519() { .unwrap() .table; let expected_result = owned_table([ + tinyint("tinyint",[i8::MIN, 0, i8::MAX]), smallint("smallint", [i16::MIN, 0, i16::MAX]), int("int", [i32::MIN, 0, i32::MAX]), bigint("bigint", [i64::MIN, 0, i64::MAX]), @@ -251,6 +253,7 @@ fn we_can_prove_a_basic_query_containing_extrema_with_dory() { accessor.add_table( "sxt.table".parse().unwrap(), owned_table([ + tinyint("tinyint",[i8::MIN, 0, i8::MAX]), smallint("smallint", [i16::MIN, 0, i16::MAX]), int("int", [i32::MIN, 0, i32::MAX]), bigint("bigint", [i64::MIN, 0, i64::MAX]), @@ -276,6 +279,7 @@ fn we_can_prove_a_basic_query_containing_extrema_with_dory() { .unwrap() .table; let expected_result = owned_table([ + tinyint("tinyint",[i8::MIN, 0, i8::MAX]), smallint("smallint", [i16::MIN, 0, i16::MAX]), int("int", [i32::MIN, 0, i32::MAX]), bigint("bigint", [i64::MIN, 0, i64::MAX]), diff --git a/crates/proof-of-sql/tests/timestamp_integration_tests.rs b/crates/proof-of-sql/tests/timestamp_integration_tests.rs index c4e2eb32f..fbb42aef9 100644 --- a/crates/proof-of-sql/tests/timestamp_integration_tests.rs +++ b/crates/proof-of-sql/tests/timestamp_integration_tests.rs @@ -2,7 +2,7 @@ #[cfg(feature = "blitzar")] use proof_of_sql::base::commitment::InnerProductProof; use proof_of_sql::{ - base::database::{owned_table_utility::*, OwnedTableTestAccessor, TestAccessor}, + base::database::{owned_table_utility::{tinyint, *}, OwnedTableTestAccessor, TestAccessor}, proof_primitive::dory::{ test_rng, DoryCommitment, DoryEvaluationProof, DoryProverPublicSetup, DoryVerifierPublicSetup, ProverSetup, PublicParameters, VerifierSetup, @@ -27,6 +27,7 @@ fn we_can_prove_a_basic_query_containing_rfc3339_timestamp_with_dory() { accessor.add_table( "sxt.table".parse().unwrap(), owned_table([ + tinyint("tinint", [i8::MIN, 0, i8::MAX]), smallint("smallint", [i16::MIN, 0, i16::MAX]), int("int", [i32::MIN, 0, i32::MAX]), bigint("bigint", [i64::MIN, 0, i64::MAX]), diff --git a/docs/SQLSyntaxSpecification.md b/docs/SQLSyntaxSpecification.md index 571a7bfe1..20584a0f2 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)