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 8b44e8d9f..22106c930 100644 --- a/crates/proof-of-sql/src/base/commitment/column_bounds.rs +++ b/crates/proof-of-sql/src/base/commitment/column_bounds.rs @@ -277,7 +277,9 @@ impl ColumnBounds { (ColumnBounds::Int128(bounds_a), ColumnBounds::Int128(bounds_b)) => { Ok(ColumnBounds::Int128(bounds_a.difference(bounds_b))) } - + (ColumnBounds::TimestampTZ(bounds_a), ColumnBounds::TimestampTZ(bounds_b)) => { + Ok(ColumnBounds::TimestampTZ(bounds_a.difference(bounds_b))) + } (_, _) => Err(ColumnBoundsMismatch(Box::new(self), Box::new(other))), } } @@ -286,7 +288,12 @@ impl ColumnBounds { #[cfg(test)] mod tests { use super::*; - use crate::base::{database::OwnedColumn, math::decimal::Precision, scalar::Curve25519Scalar}; + use crate::base::{ + database::OwnedColumn, + math::decimal::Precision, + scalar::Curve25519Scalar, + time::timestamp::{PoSQLTimeUnit, PoSQLTimeZone}, + }; use itertools::Itertools; #[test] @@ -526,8 +533,19 @@ mod tests { ); let committable_decimal75_column = CommittableColumn::from(&decimal75_column); let decimal75_column_bounds = ColumnBounds::from_column(&committable_decimal75_column); - assert_eq!(decimal75_column_bounds, ColumnBounds::NoOrder); + + let timestamp_column = OwnedColumn::::TimestampTZ( + PoSQLTimeUnit::Second, + PoSQLTimeZone::UTC, + vec![1_i64, 2, 3, 4], + ); + let committable_timestamp_column = CommittableColumn::from(×tamp_column); + let timestamp_column_bounds = ColumnBounds::from_column(&committable_timestamp_column); + assert_eq!( + timestamp_column_bounds, + ColumnBounds::TimestampTZ(Bounds::Sharp(BoundsInner { min: 1, max: 4 })) + ); } #[test] @@ -569,6 +587,14 @@ mod tests { int128_a.try_union(int128_b).unwrap(), ColumnBounds::Int128(Bounds::Bounded(BoundsInner { min: 1, max: 6 })) ); + + let timestamp_a = ColumnBounds::TimestampTZ(Bounds::Sharp(BoundsInner { min: 1, max: 3 })); + let timestamp_b = + ColumnBounds::TimestampTZ(Bounds::Bounded(BoundsInner { min: 4, max: 6 })); + assert_eq!( + timestamp_a.try_union(timestamp_b).unwrap(), + ColumnBounds::TimestampTZ(Bounds::Bounded(BoundsInner { min: 1, max: 6 })) + ); } #[test] @@ -578,6 +604,7 @@ mod tests { 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 })); + let timestamp = ColumnBounds::TimestampTZ(Bounds::Sharp(BoundsInner { min: 4, max: 6 })); let bounds = [ (no_order, "NoOrder"), @@ -585,6 +612,7 @@ mod tests { (int, "Int"), (bigint, "BigInt"), (int128, "Int128"), + (timestamp, "Timestamp"), ]; for ((bound_a, name_a), (bound_b, name_b)) in bounds.iter().tuple_combinations() { @@ -626,6 +654,13 @@ mod tests { int128_a.try_difference(int128_b).unwrap(), ColumnBounds::Int128(Bounds::Bounded(BoundsInner { min: 1, max: 4 })) ); + + let timestamp_a = ColumnBounds::TimestampTZ(Bounds::Sharp(BoundsInner { min: 1, max: 4 })); + let timestamp_b = ColumnBounds::TimestampTZ(Bounds::Sharp(BoundsInner { min: 3, max: 6 })); + assert_eq!( + timestamp_a.try_difference(timestamp_b).unwrap(), + ColumnBounds::TimestampTZ(Bounds::Bounded(BoundsInner { min: 1, max: 4 })) + ); } #[test] @@ -633,6 +668,8 @@ mod tests { let no_order = ColumnBounds::NoOrder; 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 smallint = ColumnBounds::SmallInt(Bounds::Sharp(BoundsInner { min: 1, max: 3 })); assert!(no_order.try_difference(bigint).is_err()); assert!(bigint.try_difference(no_order).is_err()); @@ -642,5 +679,8 @@ mod tests { assert!(bigint.try_difference(int128).is_err()); assert!(int128.try_difference(bigint).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/commitment/column_commitment_metadata.rs b/crates/proof-of-sql/src/base/commitment/column_commitment_metadata.rs index a9473ed53..9d3fce8cd 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 @@ -165,8 +165,11 @@ impl ColumnCommitmentMetadata { mod tests { use super::*; use crate::base::{ - commitment::column_bounds::Bounds, database::OwnedColumn, math::decimal::Precision, + commitment::column_bounds::Bounds, + database::OwnedColumn, + math::decimal::Precision, scalar::Curve25519Scalar, + time::timestamp::{PoSQLTimeUnit, PoSQLTimeZone}, }; #[test] @@ -224,6 +227,18 @@ mod tests { } ); + assert_eq!( + ColumnCommitmentMetadata::try_new( + ColumnType::TimestampTZ(PoSQLTimeUnit::Second, PoSQLTimeZone::UTC), + ColumnBounds::TimestampTZ(Bounds::Empty), + ) + .unwrap(), + ColumnCommitmentMetadata { + column_type: ColumnType::TimestampTZ(PoSQLTimeUnit::Second, PoSQLTimeZone::UTC), + bounds: ColumnBounds::TimestampTZ(Bounds::Empty), + } + ); + assert_eq!( ColumnCommitmentMetadata::try_new( ColumnType::Int128, @@ -354,6 +369,26 @@ mod tests { ); assert_eq!(decimal_metadata.bounds(), &ColumnBounds::NoOrder); + let timestamp_column: OwnedColumn = + OwnedColumn::::TimestampTZ( + PoSQLTimeUnit::Second, + PoSQLTimeZone::UTC, + [1i64, 2, 3, 4, 5].to_vec(), + ); + let committable_timestamp_column = CommittableColumn::from(×tamp_column); + let timestamp_metadata = + ColumnCommitmentMetadata::from_column(&committable_timestamp_column); + assert_eq!( + timestamp_metadata.column_type(), + &ColumnType::TimestampTZ(PoSQLTimeUnit::Second, PoSQLTimeZone::UTC) + ); + if let ColumnBounds::TimestampTZ(Bounds::Sharp(bounds)) = timestamp_metadata.bounds() { + assert_eq!(bounds.min(), &1); + assert_eq!(bounds.max(), &5); + } else { + panic!("Bounds constructed from nonempty TimestampTZ column should be ColumnBounds::BigInt(Bounds::Sharp(_))"); + } + let varchar_column = OwnedColumn::::VarChar( ["Lorem", "ipsum", "dolor", "sit", "amet"] .map(String::from) @@ -489,6 +524,80 @@ mod tests { bigint_metadata_a.try_union(bigint_metadata_b).unwrap(), bigint_metadata_c ); + + // Ordered case for TimestampTZ + // Example Unix epoch times + let times = [ + 1_625_072_400, + 1_625_076_000, + 1_625_079_600, + 1_625_072_400, + 1_625_065_000, + ]; + let timezone = PoSQLTimeZone::UTC; + let timeunit = PoSQLTimeUnit::Second; + let timestamp_column_a = CommittableColumn::TimestampTZ(timeunit, timezone, ×[..2]); + let timestamp_metadata_a = ColumnCommitmentMetadata::from_column(×tamp_column_a); + let timestamp_column_b = CommittableColumn::TimestampTZ(timeunit, timezone, ×[2..]); + let timestamp_metadata_b = ColumnCommitmentMetadata::from_column(×tamp_column_b); + let timestamp_column_c = CommittableColumn::TimestampTZ(timeunit, timezone, ×); + let timestamp_metadata_c = ColumnCommitmentMetadata::from_column(×tamp_column_c); + assert_eq!( + timestamp_metadata_a + .try_union(timestamp_metadata_b) + .unwrap(), + timestamp_metadata_c + ); + } + + #[test] + fn we_can_difference_timestamp_tz_matching_metadata() { + // Ordered case + let times = [ + 1_625_072_400, + 1_625_076_000, + 1_625_079_600, + 1_625_072_400, + 1_625_065_000, + ]; + let timezone = PoSQLTimeZone::UTC; + let timeunit = PoSQLTimeUnit::Second; + + let timestamp_column_a = CommittableColumn::TimestampTZ(timeunit, timezone, ×[..2]); + let timestamp_metadata_a = ColumnCommitmentMetadata::from_column(×tamp_column_a); + let timestamp_column_b = CommittableColumn::TimestampTZ(timeunit, timezone, ×); + let timestamp_metadata_b = ColumnCommitmentMetadata::from_column(×tamp_column_b); + + let b_difference_a = timestamp_metadata_b + .try_difference(timestamp_metadata_a) + .unwrap(); + assert_eq!( + b_difference_a.column_type, + ColumnType::TimestampTZ(timeunit, timezone) + ); + if let ColumnBounds::TimestampTZ(Bounds::Bounded(bounds)) = b_difference_a.bounds { + assert_eq!(bounds.min(), &1_625_065_000); + assert_eq!(bounds.max(), &1_625_079_600); + } else { + panic!("difference of overlapping bounds should be Bounded"); + } + + let timestamp_column_empty = CommittableColumn::TimestampTZ(timeunit, timezone, &[]); + let timestamp_metadata_empty = + ColumnCommitmentMetadata::from_column(×tamp_column_empty); + + assert_eq!( + timestamp_metadata_b + .try_difference(timestamp_metadata_empty) + .unwrap(), + timestamp_metadata_b + ); + assert_eq!( + timestamp_metadata_empty + .try_difference(timestamp_metadata_b) + .unwrap(), + timestamp_metadata_empty + ); } #[test] @@ -746,5 +855,43 @@ mod tests { assert!(different_decimal75_metadata .try_union(decimal75_metadata) .is_err()); + + let timestamp_tz_metadata_a = ColumnCommitmentMetadata { + column_type: ColumnType::TimestampTZ(PoSQLTimeUnit::Second, PoSQLTimeZone::UTC), + bounds: ColumnBounds::TimestampTZ(Bounds::Empty), + }; + + let timestamp_tz_metadata_b = ColumnCommitmentMetadata { + column_type: ColumnType::TimestampTZ(PoSQLTimeUnit::Millisecond, PoSQLTimeZone::UTC), + bounds: ColumnBounds::TimestampTZ(Bounds::Empty), + }; + + // Tests for union operations + assert!(timestamp_tz_metadata_a.try_union(varchar_metadata).is_err()); + assert!(varchar_metadata.try_union(timestamp_tz_metadata_a).is_err()); + + // Tests for difference operations + assert!(timestamp_tz_metadata_a + .try_difference(scalar_metadata) + .is_err()); + assert!(scalar_metadata + .try_difference(timestamp_tz_metadata_a) + .is_err()); + + // Tests for different time units within the same type + assert!(timestamp_tz_metadata_a + .try_union(timestamp_tz_metadata_b) + .is_err()); + assert!(timestamp_tz_metadata_b + .try_union(timestamp_tz_metadata_a) + .is_err()); + + // Difference with different time units + assert!(timestamp_tz_metadata_a + .try_difference(timestamp_tz_metadata_b) + .is_err()); + assert!(timestamp_tz_metadata_b + .try_difference(timestamp_tz_metadata_a) + .is_err()); } } 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 3ce212868..546f303f5 100644 --- a/crates/proof-of-sql/src/base/commitment/committable_column.rs +++ b/crates/proof-of-sql/src/base/commitment/committable_column.rs @@ -3,7 +3,7 @@ use crate::base::{ math::decimal::Precision, ref_into::RefInto, scalar::Scalar, - time::timestamp::{ProofsTimeUnit, ProofsTimeZone}, + time::timestamp::{PoSQLTimeUnit, PoSQLTimeZone}, }; #[cfg(feature = "blitzar")] use blitzar::sequence::Sequence; @@ -39,7 +39,7 @@ pub enum CommittableColumn<'a> { /// Column of limbs for committing to scalars, hashed from a VarChar column. VarChar(Vec<[u64; 4]>), /// Borrowed Timestamp column with Timezone, mapped to `i64`. - TimestampTZ(ProofsTimeUnit, ProofsTimeZone, &'a [i64]), + TimestampTZ(PoSQLTimeUnit, PoSQLTimeZone, &'a [i64]), } impl<'a> CommittableColumn<'a> { @@ -134,7 +134,9 @@ impl<'a, S: Scalar> From<&'a OwnedColumn> for CommittableColumn<'a> { .map(Into::<[u64; 4]>::into) .collect(), ), - OwnedColumn::TimestampTZ(_, _, times) => (times as &[_]).into(), + OwnedColumn::TimestampTZ(tu, tz, times) => { + CommittableColumn::TimestampTZ(*tu, *tz, times as &[_]) + } } } } @@ -150,7 +152,6 @@ impl<'a> From<&'a [i32]> for CommittableColumn<'a> { } } -// TODO: make sure this does not conflict with TimeStamp impl<'a> From<&'a [i64]> for CommittableColumn<'a> { fn from(value: &'a [i64]) -> Self { CommittableColumn::BigInt(value) @@ -219,6 +220,31 @@ mod tests { assert_eq!(res_committable_column, test_committable_column) } + #[test] + fn we_can_get_type_and_length_of_timestamp_column() { + // empty case + let smallint_committable_column = + CommittableColumn::TimestampTZ(PoSQLTimeUnit::Second, PoSQLTimeZone::UTC, &[]); + assert_eq!(smallint_committable_column.len(), 0); + assert!(smallint_committable_column.is_empty()); + assert_eq!( + smallint_committable_column.column_type(), + ColumnType::TimestampTZ(PoSQLTimeUnit::Second, PoSQLTimeZone::UTC) + ); + + let smallint_committable_column = CommittableColumn::TimestampTZ( + PoSQLTimeUnit::Second, + PoSQLTimeZone::UTC, + &[12, 34, 56], + ); + assert_eq!(smallint_committable_column.len(), 3); + assert!(!smallint_committable_column.is_empty()); + assert_eq!( + smallint_committable_column.column_type(), + ColumnType::TimestampTZ(PoSQLTimeUnit::Second, PoSQLTimeZone::UTC) + ); + } + #[test] fn we_can_get_type_and_length_of_smallint_column() { // empty case @@ -358,6 +384,34 @@ mod tests { assert_eq!(bool_committable_column.column_type(), ColumnType::Boolean); } + #[test] + fn we_can_convert_from_borrowing_timestamp_column() { + // empty case + let from_borrowed_column = + CommittableColumn::from(&Column::::TimestampTZ( + PoSQLTimeUnit::Second, + PoSQLTimeZone::UTC, + &[], + )); + assert_eq!( + from_borrowed_column, + CommittableColumn::TimestampTZ(PoSQLTimeUnit::Second, PoSQLTimeZone::UTC, &[]) + ); + + // non-empty case + let timestamps = [1625072400, 1625076000, 1625083200]; + let from_borrowed_column = + CommittableColumn::from(&Column::::TimestampTZ( + PoSQLTimeUnit::Second, + PoSQLTimeZone::UTC, + ×tamps, + )); + assert_eq!( + from_borrowed_column, + CommittableColumn::TimestampTZ(PoSQLTimeUnit::Second, PoSQLTimeZone::UTC, ×tamps) + ); + } + #[test] fn we_can_convert_from_borrowing_bigint_column() { // empty case @@ -512,6 +566,34 @@ mod tests { ); } + #[test] + fn we_can_convert_from_owned_timestamp_column() { + // empty case + let owned_column = OwnedColumn::::TimestampTZ( + PoSQLTimeUnit::Second, + PoSQLTimeZone::UTC, + Vec::new(), + ); + let from_owned_column = CommittableColumn::from(&owned_column); + assert_eq!( + from_owned_column, + CommittableColumn::TimestampTZ(PoSQLTimeUnit::Second, PoSQLTimeZone::UTC, &[]) + ); + + // non-empty case + let timestamps = vec![1625072400, 1625076000, 1625083200]; + let owned_column = OwnedColumn::::TimestampTZ( + PoSQLTimeUnit::Second, + PoSQLTimeZone::UTC, + timestamps.clone(), + ); + let from_owned_column = CommittableColumn::from(&owned_column); + assert_eq!( + from_owned_column, + CommittableColumn::TimestampTZ(PoSQLTimeUnit::Second, PoSQLTimeZone::UTC, ×tamps) + ); + } + #[test] fn we_can_convert_from_owned_int_column() { // empty case @@ -790,4 +872,30 @@ mod tests { ); assert_eq!(commitment_buffer[0], commitment_buffer[1]); } + + #[test] + fn we_can_commit_to_timestamp_column_through_committable_column() { + // Empty case + let committable_column = + CommittableColumn::TimestampTZ(PoSQLTimeUnit::Second, PoSQLTimeZone::UTC, &[]); + 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()); + + // Non-empty case + let timestamps = [1625072400, 1625076000, 1625083200]; + let committable_column = + CommittableColumn::TimestampTZ(PoSQLTimeUnit::Second, PoSQLTimeZone::UTC, ×tamps); + + let sequence_actual = Sequence::from(&committable_column); + let sequence_expected = Sequence::from(timestamps.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]); + } } 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 c24b91918..91bb4b6c1 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 @@ -4,7 +4,7 @@ use crate::{ database::Column, math::decimal::Precision, scalar::Scalar, - time::timestamp::{ProofsTimeUnit, ProofsTimeZone}, + time::timestamp::{PoSQLTimeUnit, PoSQLTimeZone}, }, sql::parse::ConversionError, }; @@ -279,9 +279,9 @@ impl ArrayRefExt for ArrayRef { ArrowTimeUnit::Second => { if let Some(array) = self.as_any().downcast_ref::() { Ok(Column::TimestampTZ( - ProofsTimeUnit::Second, - ProofsTimeZone::try_from(tz.clone())?, - array.values(), + PoSQLTimeUnit::Second, + PoSQLTimeZone::try_from(tz.clone())?, + &array.values()[range.start..range.end], )) } else { Err(ArrowArrayToColumnConversionError::UnsupportedType( @@ -292,9 +292,9 @@ impl ArrayRefExt for ArrayRef { ArrowTimeUnit::Millisecond => { if let Some(array) = self.as_any().downcast_ref::() { Ok(Column::TimestampTZ( - ProofsTimeUnit::Millisecond, - ProofsTimeZone::try_from(tz.clone())?, - array.values(), + PoSQLTimeUnit::Millisecond, + PoSQLTimeZone::try_from(tz.clone())?, + &array.values()[range.start..range.end], )) } else { Err(ArrowArrayToColumnConversionError::UnsupportedType( @@ -305,9 +305,9 @@ impl ArrayRefExt for ArrayRef { ArrowTimeUnit::Microsecond => { if let Some(array) = self.as_any().downcast_ref::() { Ok(Column::TimestampTZ( - ProofsTimeUnit::Microsecond, - ProofsTimeZone::try_from(tz.clone())?, - array.values(), + PoSQLTimeUnit::Microsecond, + PoSQLTimeZone::try_from(tz.clone())?, + &array.values()[range.start..range.end], )) } else { Err(ArrowArrayToColumnConversionError::UnsupportedType( @@ -318,9 +318,9 @@ impl ArrayRefExt for ArrayRef { ArrowTimeUnit::Nanosecond => { if let Some(array) = self.as_any().downcast_ref::() { Ok(Column::TimestampTZ( - ProofsTimeUnit::Nanosecond, - ProofsTimeZone::try_from(tz.clone())?, - array.values(), + PoSQLTimeUnit::Nanosecond, + PoSQLTimeZone::try_from(tz.clone())?, + &array.values()[range.start..range.end], )) } else { Err(ArrowArrayToColumnConversionError::UnsupportedType( @@ -365,6 +365,88 @@ mod tests { use arrow::array::Decimal256Builder; use std::{str::FromStr, sync::Arc}; + #[test] + fn we_can_convert_timestamp_array_normal_range() { + let alloc = Bump::new(); + let data = vec![1625072400, 1625076000, 1625083200]; // Example Unix timestamps + let array: ArrayRef = Arc::new(TimestampSecondArray::with_timezone_opt( + data.clone().into(), + Some("UTC"), + )); + + let result = array.to_column::(&alloc, &(1..3), None); + assert_eq!( + result.unwrap(), + Column::TimestampTZ(PoSQLTimeUnit::Second, PoSQLTimeZone::UTC, &data[1..3]) + ); + } + + #[test] + fn we_can_build_an_empty_column_from_an_empty_range_timestamp() { + let alloc = Bump::new(); + let data = vec![1625072400, 1625076000]; // Example Unix timestamps + let array: ArrayRef = Arc::new(TimestampSecondArray::with_timezone_opt( + data.into(), + Some("UTC"), + )); + + let result = array + .to_column::(&alloc, &(2..2), None) + .unwrap(); + assert_eq!( + result, + Column::TimestampTZ(PoSQLTimeUnit::Second, PoSQLTimeZone::UTC, &[]) + ); + } + + #[test] + fn we_can_convert_timestamp_array_empty_range() { + let alloc = Bump::new(); + let data = vec![1625072400, 1625076000, 1625083200]; // Example Unix timestamps + let array: ArrayRef = Arc::new(TimestampSecondArray::with_timezone_opt( + data.into(), + Some("UTC"), + )); + + let result = array.to_column::(&alloc, &(1..1), None); + assert_eq!( + result.unwrap(), + Column::TimestampTZ(PoSQLTimeUnit::Second, PoSQLTimeZone::UTC, &[]) + ); + } + + #[test] + fn we_cannot_convert_timestamp_array_oob_range() { + let alloc = Bump::new(); + let data = vec![1625072400, 1625076000, 1625083200]; + let array: ArrayRef = Arc::new(TimestampSecondArray::with_timezone_opt( + data.into(), + Some("UTC"), + )); + + let result = array.to_column::(&alloc, &(3..5), None); + assert_eq!( + result, + Err(ArrowArrayToColumnConversionError::IndexOutOfBounds(3, 5)) + ); + } + + #[test] + fn we_can_convert_timestamp_array_with_nulls() { + let alloc = Bump::new(); + let data = vec![Some(1625072400), None, Some(1625083200)]; + let array: ArrayRef = Arc::new(TimestampSecondArray::with_timezone_opt( + data.into(), + Some("UTC"), + )); + + let result = array.to_column::(&alloc, &(0..3), None); + assert!(matches!( + result, + Err(ArrowArrayToColumnConversionError::ArrayContainsNulls) + )); + } + #[test] fn we_cannot_convert_utf8_array_oob_range() { let alloc = Bump::new(); @@ -908,6 +990,24 @@ mod tests { ); } + #[test] + fn we_can_convert_valid_timestamp_array_refs_into_valid_columns() { + let alloc = Bump::new(); + let data = vec![1625072400, 1625076000]; // Example Unix timestamps + let array: ArrayRef = Arc::new(TimestampSecondArray::with_timezone_opt( + data.clone().into(), + Some("UTC"), + )); + + let result = array + .to_column::(&alloc, &(0..2), None) + .unwrap(); + assert_eq!( + result, + Column::TimestampTZ(PoSQLTimeUnit::Second, PoSQLTimeZone::UTC, &data[..]) + ); + } + #[test] fn we_can_convert_valid_boolean_array_refs_into_valid_columns_using_ranges_smaller_than_arrays() { @@ -951,6 +1051,25 @@ mod tests { ); } + #[test] + fn we_can_convert_valid_timestamp_array_refs_into_valid_columns_using_ranges_smaller_than_arrays( + ) { + let alloc = Bump::new(); + let data = vec![1625072400, 1625076000, 1625083200]; // Example Unix timestamps + let array: ArrayRef = Arc::new(TimestampSecondArray::with_timezone_opt( + data.clone().into(), + Some("UTC"), + )); + + // Test using a range smaller than the array size + assert_eq!( + array + .to_column::(&alloc, &(1..3), None) + .unwrap(), + Column::TimestampTZ(PoSQLTimeUnit::Second, PoSQLTimeZone::UTC, &data[1..3]) + ); + } + #[test] fn we_can_convert_valid_string_array_refs_into_valid_columns_using_ranges_smaller_than_arrays() { @@ -992,6 +1111,23 @@ mod tests { assert_eq!(result, Column::VarChar((&[], &[]))); } + #[test] + fn we_can_convert_valid_timestamp_array_refs_into_valid_columns_using_ranges_with_zero_size() { + let alloc = Bump::new(); + let data = vec![1625072400, 1625076000]; // Example Unix timestamps + let array: ArrayRef = Arc::new(TimestampSecondArray::with_timezone_opt( + data.clone().into(), + Some("UTC"), + )); + let result = array + .to_column::(&alloc, &(0..0), None) + .unwrap(); + assert_eq!( + result, + Column::TimestampTZ(PoSQLTimeUnit::Second, PoSQLTimeZone::UTC, &[]) + ); + } + #[test] fn we_can_convert_valid_boolean_array_refs_into_valid_vec_scalars() { let data = vec![false, true]; @@ -1005,6 +1141,23 @@ mod tests { ); } + #[test] + fn we_can_convert_valid_timestamp_array_refs_into_valid_vec_scalars() { + let data = vec![1625072400, 1625076000]; // Example Unix timestamps + let array: ArrayRef = Arc::new(TimestampSecondArray::with_timezone_opt( + data.clone().into(), + Some("UTC"), + )); + + assert_eq!( + array.to_curve25519_scalars(), + Ok(data + .iter() + .map(|&v| Curve25519Scalar::from(v)) + .collect::>()) + ); + } + #[test] fn we_can_convert_valid_integer_array_refs_into_valid_vec_scalars() { let data = vec![1, -3]; diff --git a/crates/proof-of-sql/src/base/database/column.rs b/crates/proof-of-sql/src/base/database/column.rs index 3790f8597..5bb0fa19d 100644 --- a/crates/proof-of-sql/src/base/database/column.rs +++ b/crates/proof-of-sql/src/base/database/column.rs @@ -2,7 +2,7 @@ use super::{LiteralValue, TableRef}; use crate::base::{ math::decimal::{scale_scalar, Precision}, scalar::Scalar, - time::timestamp::{ProofsTimeUnit, ProofsTimeZone}, + time::timestamp::{PoSQLTimeUnit, PoSQLTimeZone}, }; use arrow::datatypes::{DataType, Field, TimeUnit as ArrowTimeUnit}; use bumpalo::Bump; @@ -43,7 +43,7 @@ pub enum Column<'a, S: Scalar> { /// - the first element maps to the stored [`TimeUnit`] /// - the second element maps to a timezone /// - the third element maps to columns of timeunits since unix epoch - TimestampTZ(ProofsTimeUnit, ProofsTimeZone, &'a [i64]), + TimestampTZ(PoSQLTimeUnit, PoSQLTimeZone, &'a [i64]), } impl<'a, S: Scalar> Column<'a, S> { @@ -215,7 +215,7 @@ pub enum ColumnType { Decimal75(Precision, i8), /// Mapped to i64 #[serde(alias = "TIMESTAMP", alias = "timestamp")] - TimestampTZ(ProofsTimeUnit, ProofsTimeZone), + TimestampTZ(PoSQLTimeUnit, PoSQLTimeZone), } impl ColumnType { @@ -301,8 +301,8 @@ impl TryFrom for ColumnType { Ok(ColumnType::Decimal75(Precision::new(precision)?, scale)) } DataType::Timestamp(time_unit, timezone_option) => Ok(ColumnType::TimestampTZ( - ProofsTimeUnit::from(time_unit), - ProofsTimeZone::try_from(timezone_option)?, + PoSQLTimeUnit::from(time_unit), + PoSQLTimeZone::try_from(timezone_option)?, )), DataType::Utf8 => Ok(ColumnType::VarChar), _ => Err(format!("Unsupported arrow data type {:?}", data_type)), @@ -416,6 +416,10 @@ mod tests { #[test] fn column_type_serializes_to_string() { + let column_type = ColumnType::TimestampTZ(PoSQLTimeUnit::Second, PoSQLTimeZone::UTC); + let serialized = serde_json::to_string(&column_type).unwrap(); + assert_eq!(serialized, r#"{"TimestampTZ":["Second","UTC"]}"#); + let column_type = ColumnType::Boolean; let serialized = serde_json::to_string(&column_type).unwrap(); assert_eq!(serialized, r#""Boolean""#); 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 4b99c3c25..76bc41865 100644 --- a/crates/proof-of-sql/src/base/database/literal_value.rs +++ b/crates/proof-of-sql/src/base/database/literal_value.rs @@ -2,7 +2,7 @@ use crate::base::{ database::ColumnType, math::decimal::Precision, scalar::Scalar, - time::timestamp::{ProofsTimeUnit, ProofsTimeZone}, + time::timestamp::{PoSQLTimeUnit, PoSQLTimeZone}, }; use serde::{Deserialize, Serialize}; @@ -36,7 +36,7 @@ pub enum LiteralValue { Scalar(S), /// TimeStamp defined over a unit (s, ms, ns, etc) and timezone with backing store /// mapped to i64, which is time units since unix epoch - TimeStampTZ(ProofsTimeUnit, ProofsTimeZone, i64), + TimeStampTZ(PoSQLTimeUnit, PoSQLTimeZone, i64), } impl LiteralValue { 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 74e9d80b4..3e551b89e 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 @@ -20,7 +20,7 @@ use crate::base::{ }, math::decimal::Precision, scalar::Scalar, - time::timestamp::{ProofsTimeUnit, ProofsTimeZone}, + time::timestamp::{PoSQLTimeUnit, PoSQLTimeZone}, }; use arrow::{ array::{ @@ -88,10 +88,10 @@ impl From> for ArrayRef { OwnedColumn::Scalar(_) => unimplemented!("Cannot convert Scalar type to arrow type"), OwnedColumn::VarChar(col) => Arc::new(StringArray::from(col)), OwnedColumn::TimestampTZ(time_unit, _, col) => match time_unit { - ProofsTimeUnit::Second => Arc::new(TimestampSecondArray::from(col)), - ProofsTimeUnit::Millisecond => Arc::new(TimestampMillisecondArray::from(col)), - ProofsTimeUnit::Microsecond => Arc::new(TimestampMicrosecondArray::from(col)), - ProofsTimeUnit::Nanosecond => Arc::new(TimestampNanosecondArray::from(col)), + PoSQLTimeUnit::Second => Arc::new(TimestampSecondArray::from(col)), + PoSQLTimeUnit::Millisecond => Arc::new(TimestampMillisecondArray::from(col)), + PoSQLTimeUnit::Microsecond => Arc::new(TimestampMicrosecondArray::from(col)), + PoSQLTimeUnit::Nanosecond => Arc::new(TimestampNanosecondArray::from(col)), }, } } @@ -200,8 +200,8 @@ impl TryFrom<&ArrayRef> for OwnedColumn { })?; let timestamps = array.values().iter().copied().collect::>(); Ok(OwnedColumn::TimestampTZ( - ProofsTimeUnit::Second, - ProofsTimeZone::try_from(timezone.clone())?, + PoSQLTimeUnit::Second, + PoSQLTimeZone::try_from(timezone.clone())?, timestamps, )) } @@ -216,8 +216,8 @@ impl TryFrom<&ArrayRef> for OwnedColumn { })?; let timestamps = array.values().iter().copied().collect::>(); Ok(OwnedColumn::TimestampTZ( - ProofsTimeUnit::Millisecond, - ProofsTimeZone::try_from(timezone.clone())?, + PoSQLTimeUnit::Millisecond, + PoSQLTimeZone::try_from(timezone.clone())?, timestamps, )) } @@ -232,8 +232,8 @@ impl TryFrom<&ArrayRef> for OwnedColumn { })?; let timestamps = array.values().iter().copied().collect::>(); Ok(OwnedColumn::TimestampTZ( - ProofsTimeUnit::Microsecond, - ProofsTimeZone::try_from(timezone.clone())?, + PoSQLTimeUnit::Microsecond, + PoSQLTimeZone::try_from(timezone.clone())?, timestamps, )) } @@ -248,8 +248,8 @@ impl TryFrom<&ArrayRef> for OwnedColumn { })?; let timestamps = array.values().iter().copied().collect::>(); Ok(OwnedColumn::TimestampTZ( - ProofsTimeUnit::Nanosecond, - ProofsTimeZone::try_from(timezone.clone())?, + PoSQLTimeUnit::Nanosecond, + PoSQLTimeZone::try_from(timezone.clone())?, timestamps, )) } 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 9240e4702..f6b9fb6a0 100644 --- a/crates/proof-of-sql/src/base/database/owned_column.rs +++ b/crates/proof-of-sql/src/base/database/owned_column.rs @@ -6,7 +6,7 @@ use super::ColumnType; use crate::base::{ math::decimal::Precision, scalar::Scalar, - time::timestamp::{ProofsTimeUnit, ProofsTimeZone}, + time::timestamp::{PoSQLTimeUnit, PoSQLTimeZone}, }; #[derive(Debug, PartialEq, Clone, Eq)] #[non_exhaustive] @@ -29,7 +29,7 @@ pub enum OwnedColumn { /// Scalar columns Scalar(Vec), /// Timestamp columns - TimestampTZ(ProofsTimeUnit, ProofsTimeZone, Vec), + TimestampTZ(PoSQLTimeUnit, PoSQLTimeZone, Vec), } impl OwnedColumn { @@ -78,45 +78,3 @@ impl OwnedColumn { } } } - -impl FromIterator for OwnedColumn { - fn from_iter>(iter: T) -> Self { - Self::Boolean(Vec::from_iter(iter)) - } -} -impl FromIterator for OwnedColumn { - fn from_iter>(iter: T) -> Self { - Self::SmallInt(Vec::from_iter(iter)) - } -} -impl FromIterator for OwnedColumn { - fn from_iter>(iter: T) -> Self { - Self::Int(Vec::from_iter(iter)) - } -} -// TODO: does this conflict with TimeStamp? -impl FromIterator for OwnedColumn { - fn from_iter>(iter: T) -> Self { - Self::BigInt(Vec::from_iter(iter)) - } -} -impl FromIterator for OwnedColumn { - fn from_iter>(iter: T) -> Self { - Self::Int128(Vec::from_iter(iter)) - } -} -impl FromIterator for OwnedColumn { - fn from_iter>(iter: T) -> Self { - Self::VarChar(Vec::from_iter(iter)) - } -} -impl FromIterator for OwnedColumn { - fn from_iter>(iter: T) -> Self { - Self::Scalar(Vec::from_iter(iter)) - } -} -impl<'a, S: Scalar> FromIterator<&'a str> for OwnedColumn { - fn from_iter>(iter: T) -> Self { - Self::from_iter(iter.into_iter().map(|s| s.to_string())) - } -} diff --git a/crates/proof-of-sql/src/base/database/owned_table_test.rs b/crates/proof-of-sql/src/base/database/owned_table_test.rs index 3a9e3c297..617fda933 100644 --- a/crates/proof-of-sql/src/base/database/owned_table_test.rs +++ b/crates/proof-of-sql/src/base/database/owned_table_test.rs @@ -2,6 +2,7 @@ use crate::{ base::{ database::{owned_table_utility::*, OwnedColumn, OwnedTable, OwnedTableError}, scalar::Curve25519Scalar, + time::timestamp::{PoSQLTimeUnit, PoSQLTimeZone}, }, proof_primitive::dory::DoryScalar, }; @@ -56,8 +57,22 @@ fn we_can_create_an_owned_table_with_data() { "boolean", [true, false, true, false, true, false, true, false, true], ), + timestamptz( + "timestamp", + PoSQLTimeUnit::Second, + PoSQLTimeZone::UTC, + [0, 1, 2, 3, 4, 5, 6, i64::MIN, i64::MAX], + ), ]); let mut table = IndexMap::new(); + table.insert( + Identifier::try_new("timestamp").unwrap(), + OwnedColumn::TimestampTZ( + PoSQLTimeUnit::Second, + PoSQLTimeZone::UTC, + [0, 1, 2, 3, 4, 5, 6, i64::MIN, i64::MAX].into(), + ), + ); table.insert( Identifier::try_new("bigint").unwrap(), OwnedColumn::BigInt(vec![0_i64, 1, 2, 3, 4, 5, 6, i64::MIN, i64::MAX]), @@ -109,12 +124,24 @@ fn we_get_inequality_between_tables_with_differing_column_order() { int128("b", [0; 0]), varchar("c", ["0"; 0]), boolean("d", [false; 0]), + timestamptz( + "timestamp", + PoSQLTimeUnit::Second, + PoSQLTimeZone::UTC, + [0; 0], + ), ]); let owned_table_b: OwnedTable = owned_table([ boolean("d", [false; 0]), int128("b", [0; 0]), bigint("a", [0; 0]), varchar("c", ["0"; 0]), + timestamptz( + "timestamp", + PoSQLTimeUnit::Second, + PoSQLTimeZone::UTC, + [0; 0], + ), ]); assert_ne!(owned_table_a, owned_table_b); } @@ -125,12 +152,24 @@ fn we_get_inequality_between_tables_with_differing_data() { int128("b", [0]), varchar("c", ["0"]), boolean("d", [true]), + timestamptz( + "timestamp", + PoSQLTimeUnit::Second, + PoSQLTimeZone::UTC, + [1625072400], + ), ]); let owned_table_b: OwnedTable = owned_table([ bigint("a", [1]), int128("b", [0]), varchar("c", ["0"]), boolean("d", [true]), + timestamptz( + "timestamp", + PoSQLTimeUnit::Second, + PoSQLTimeZone::UTC, + [1625076000], + ), ]); assert_ne!(owned_table_a, owned_table_b); } diff --git a/crates/proof-of-sql/src/base/database/owned_table_test_accessor_test.rs b/crates/proof-of-sql/src/base/database/owned_table_test_accessor_test.rs index e3c34d906..0c70a1ade 100644 --- a/crates/proof-of-sql/src/base/database/owned_table_test_accessor_test.rs +++ b/crates/proof-of-sql/src/base/database/owned_table_test_accessor_test.rs @@ -5,6 +5,7 @@ use super::{ use crate::base::{ database::owned_table_utility::*, scalar::{compute_commitment_for_testing, Curve25519Scalar}, + time::timestamp::{PoSQLTimeUnit, PoSQLTimeZone}, }; use blitzar::proof::InnerProductProof; @@ -48,6 +49,12 @@ fn we_can_access_the_columns_of_a_table() { varchar("varchar", ["a", "bc", "d", "e"]), scalar("scalar", [1, 2, 3, 4]), boolean("boolean", [true, false, true, false]), + timestamptz( + "time", + PoSQLTimeUnit::Second, + PoSQLTimeZone::UTC, + [4, 5, 6, 5], + ), ]); accessor.add_table(table_ref_2, data2, 0_usize); @@ -99,6 +106,16 @@ fn we_can_access_the_columns_of_a_table() { Column::Boolean(col) => assert_eq!(col.to_vec(), vec![true, false, true, false]), _ => panic!("Invalid column type"), }; + + let column = ColumnRef::new( + table_ref_2, + "time".parse().unwrap(), + ColumnType::TimestampTZ(PoSQLTimeUnit::Second, PoSQLTimeZone::UTC), + ); + match accessor.get_column(column) { + Column::TimestampTZ(_, _, col) => assert_eq!(col.to_vec(), vec![4, 5, 6, 5]), + _ => panic!("Invalid column type"), + }; } #[test] 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 707cd412b..0b2131b30 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 @@ -16,7 +16,7 @@ use super::{OwnedColumn, OwnedTable}; use crate::base::{ scalar::Scalar, - time::timestamp::{ProofsTimeUnit, ProofsTimeZone}, + time::timestamp::{PoSQLTimeUnit, PoSQLTimeZone}, }; use core::ops::Deref; use proof_of_sql_parser::Identifier; @@ -212,17 +212,17 @@ pub fn decimal75( /// ``` /// use proof_of_sql::base::{database::owned_table_utility::*, /// scalar::Curve25519Scalar, -/// time::timestamp::{ProofsTimeUnit, ProofsTimeZone}}; +/// time::timestamp::{PoSQLTimeUnit, PoSQLTimeZone}}; /// use chrono_tz::Europe::London; /// /// let result = owned_table::([ -/// timestamptz("event_time", ProofsTimeUnit::Second, ProofsTimeZone::new(London), vec![1625072400, 1625076000, 1625079600]), +/// timestamptz("event_time", PoSQLTimeUnit::Second, PoSQLTimeZone::new(London), vec![1625072400, 1625076000, 1625079600]), /// ]); /// ``` pub fn timestamptz( name: impl Deref, - time_unit: ProofsTimeUnit, - timezone: ProofsTimeZone, + time_unit: PoSQLTimeUnit, + timezone: PoSQLTimeZone, data: impl IntoIterator, ) -> (Identifier, OwnedColumn) { ( 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 8538aca92..0aba06a22 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,11 +1,11 @@ -use crate::base::{database::ColumnType, time::timestamp::ProofsTimeUnit}; +use crate::base::{database::ColumnType, time::timestamp::PoSQLTimeUnit}; use arrow::{ array::{ Array, BooleanArray, Decimal128Array, Decimal256Array, Int16Array, Int32Array, Int64Array, StringArray, TimestampMicrosecondArray, TimestampMillisecondArray, TimestampNanosecondArray, TimestampSecondArray, }, - datatypes::{i256, DataType, Field, Schema, TimeUnit}, + datatypes::{i256, DataType, Field, Schema}, record_batch::RecordBatch, }; use rand::{ @@ -119,19 +119,22 @@ pub fn make_random_test_accessor_data( ColumnType::TimestampTZ(tu, tz) => { column_fields.push(Field::new( *col_name, - DataType::Timestamp(TimeUnit::from(*tu), Some(Arc::from(tz.to_string()))), + DataType::Timestamp( + (*tu).into(), + Some(Arc::from(tz.to_string())), + ), false, )); // Create the correct timestamp array based on the time unit let timestamp_array: Arc = match tu { - ProofsTimeUnit::Second => Arc::new(TimestampSecondArray::from(values.to_vec())), - ProofsTimeUnit::Millisecond => { + PoSQLTimeUnit::Second => Arc::new(TimestampSecondArray::from(values.to_vec())), + PoSQLTimeUnit::Millisecond => { Arc::new(TimestampMillisecondArray::from(values.to_vec())) } - ProofsTimeUnit::Microsecond => { + PoSQLTimeUnit::Microsecond => { Arc::new(TimestampMicrosecondArray::from(values.to_vec())) } - ProofsTimeUnit::Nanosecond => { + PoSQLTimeUnit::Nanosecond => { Arc::new(TimestampNanosecondArray::from(values.to_vec())) } }; diff --git a/crates/proof-of-sql/src/base/time/timestamp.rs b/crates/proof-of-sql/src/base/time/timestamp.rs index 4122610fc..2905c55e8 100644 --- a/crates/proof-of-sql/src/base/time/timestamp.rs +++ b/crates/proof-of-sql/src/base/time/timestamp.rs @@ -5,64 +5,71 @@ use core::fmt; use serde::{Deserialize, Serialize}; use std::{str::FromStr, sync::Arc}; -/// A postgresql-like `TimeStamp` type. It is defined over -/// a [`TimeUnit`], which is a signed count of units either -/// after or before the [Unix epoch](https://en.wikipedia.org/wiki/Unix_time). -#[derive(Debug, Clone, Deserialize, Serialize, Hash)] -pub struct TimestampTZ { - time: i64, - timeunit: ProofsTimeUnit, - timezone: Tz, -} - /// A typed TimeZone for a [`TimeStamp`]. It is optionally /// used to define a timezone other than UTC for a new TimeStamp. /// It exists as a wrapper around chrono-tz because chrono-tz does /// not implement uniform bit distribution #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)] -pub struct ProofsTimeZone(Tz); +pub struct PoSQLTimeZone(Tz); + +impl PoSQLTimeZone { + /// Convenience constant for the UTC timezone + pub const UTC: PoSQLTimeZone = PoSQLTimeZone(Tz::UTC); +} -impl ProofsTimeZone { +impl PoSQLTimeZone { /// Create a new ProofsTimeZone from a chrono TimeZone pub fn new(tz: Tz) -> Self { - ProofsTimeZone(tz) + PoSQLTimeZone(tz) } } -impl From<&ProofsTimeZone> for Arc { - fn from(timezone: &ProofsTimeZone) -> Self { +impl From<&PoSQLTimeZone> for Arc { + fn from(timezone: &PoSQLTimeZone) -> Self { Arc::from(timezone.0.name()) } } -impl From for ProofsTimeZone { +impl From for PoSQLTimeZone { fn from(tz: Tz) -> Self { - ProofsTimeZone(tz) + PoSQLTimeZone(tz) } } -impl fmt::Display for ProofsTimeZone { +impl fmt::Display for PoSQLTimeZone { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) } } -impl TryFrom>> for ProofsTimeZone { - type Error = &'static str; // Explicitly state the error type +impl TryFrom>> for PoSQLTimeZone { + type Error = &'static str; fn try_from(value: Option>) -> Result { match value { Some(arc_str) => Tz::from_str(&arc_str) - .map(ProofsTimeZone) + .map(PoSQLTimeZone) .map_err(|_| "Invalid timezone string"), - None => Ok(ProofsTimeZone(Tz::UTC)), // Default to UTC + None => Ok(PoSQLTimeZone(Tz::UTC)), // Default to UTC } } } -/// Specifies different units of time measurement relative to the Unix epoch. +impl TryFrom<&str> for PoSQLTimeZone { + type Error = &'static str; + + fn try_from(value: &str) -> Result { + Tz::from_str(value) + .map(PoSQLTimeZone) + .map_err(|_| "Invalid timezone string") + } +} + +/// Specifies different units of time measurement relative to the Unix epoch. It is essentially +/// a wrapper over [arrow::datatypes::TimeUnit] so that we can derive Copy and implement custom traits +/// such as bit distribution and Hash. #[derive(Debug, Clone, Copy, Eq, PartialEq, Deserialize, Serialize, Hash)] -pub enum ProofsTimeUnit { +pub enum PoSQLTimeUnit { /// Represents a time unit of one second. Second, /// Represents a time unit of one millisecond (1/1,000 of a second). @@ -73,35 +80,35 @@ pub enum ProofsTimeUnit { Nanosecond, } -impl From for ArrowTimeUnit { - fn from(unit: ProofsTimeUnit) -> Self { +impl From for ArrowTimeUnit { + fn from(unit: PoSQLTimeUnit) -> Self { match unit { - ProofsTimeUnit::Second => ArrowTimeUnit::Second, - ProofsTimeUnit::Millisecond => ArrowTimeUnit::Millisecond, - ProofsTimeUnit::Microsecond => ArrowTimeUnit::Microsecond, - ProofsTimeUnit::Nanosecond => ArrowTimeUnit::Nanosecond, + PoSQLTimeUnit::Second => ArrowTimeUnit::Second, + PoSQLTimeUnit::Millisecond => ArrowTimeUnit::Millisecond, + PoSQLTimeUnit::Microsecond => ArrowTimeUnit::Microsecond, + PoSQLTimeUnit::Nanosecond => ArrowTimeUnit::Nanosecond, } } } -impl fmt::Display for ProofsTimeUnit { +impl fmt::Display for PoSQLTimeUnit { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - ProofsTimeUnit::Second => write!(f, "Second"), - ProofsTimeUnit::Millisecond => write!(f, "Millisecond"), - ProofsTimeUnit::Microsecond => write!(f, "Microsecond"), - ProofsTimeUnit::Nanosecond => write!(f, "Nanosecond"), + PoSQLTimeUnit::Second => write!(f, "Second"), + PoSQLTimeUnit::Millisecond => write!(f, "Millisecond"), + PoSQLTimeUnit::Microsecond => write!(f, "Microsecond"), + PoSQLTimeUnit::Nanosecond => write!(f, "Nanosecond"), } } } -impl From for ProofsTimeUnit { +impl From for PoSQLTimeUnit { fn from(unit: ArrowTimeUnit) -> Self { match unit { - ArrowTimeUnit::Second => ProofsTimeUnit::Second, - ArrowTimeUnit::Millisecond => ProofsTimeUnit::Millisecond, - ArrowTimeUnit::Microsecond => ProofsTimeUnit::Microsecond, - ArrowTimeUnit::Nanosecond => ProofsTimeUnit::Nanosecond, + ArrowTimeUnit::Second => PoSQLTimeUnit::Second, + ArrowTimeUnit::Millisecond => PoSQLTimeUnit::Millisecond, + ArrowTimeUnit::Microsecond => PoSQLTimeUnit::Microsecond, + ArrowTimeUnit::Nanosecond => PoSQLTimeUnit::Nanosecond, } } } @@ -131,7 +138,7 @@ mod tests { let arc_tz = Arc::new(tz_str.to_string()); // Convert Arc to Arc by dereferencing to &str then creating a new Arc let arc_tz_str: Arc = Arc::from(&**arc_tz); - let timezone = ProofsTimeZone::try_from(Some(arc_tz_str)); + let timezone = PoSQLTimeZone::try_from(Some(arc_tz_str)); assert!(timezone.is_ok(), "Timezone should be valid: {}", tz_str); assert_eq!( timezone.unwrap().0, @@ -147,7 +154,7 @@ mod tests { let edge_timezones = ["Etc/GMT+12", "Etc/GMT-14", "America/Argentina/Ushuaia"]; for tz_str in &edge_timezones { let arc_tz = Arc::from(*tz_str); - let result = ProofsTimeZone::try_from(Some(arc_tz)); + let result = PoSQLTimeZone::try_from(Some(arc_tz)); assert!(result.is_ok(), "Edge timezone should be valid: {}", tz_str); assert_eq!( result.unwrap().0, @@ -161,14 +168,14 @@ mod tests { #[test] fn test_empty_timezone_string() { let empty_tz = Arc::from(""); - let result = ProofsTimeZone::try_from(Some(empty_tz)); + let result = PoSQLTimeZone::try_from(Some(empty_tz)); assert!(result.is_err(), "Empty timezone string should fail"); } #[test] fn test_unicode_timezone_strings() { let unicode_tz = Arc::from("Europe/Paris\u{00A0}"); // Non-breaking space character - let result = ProofsTimeZone::try_from(Some(unicode_tz)); + let result = PoSQLTimeZone::try_from(Some(unicode_tz)); assert!( result.is_err(), "Unicode characters should not be valid in timezone strings" @@ -177,7 +184,7 @@ mod tests { #[test] fn test_null_option() { - let result = ProofsTimeZone::try_from(None); + let result = PoSQLTimeZone::try_from(None); assert!(result.is_ok(), "None should convert without error"); assert_eq!(result.unwrap().0, Tz::UTC, "None should default to UTC"); } @@ -185,39 +192,39 @@ mod tests { #[test] fn we_can_convert_from_arrow_time_units() { assert_eq!( - ProofsTimeUnit::from(ArrowTimeUnit::Second), - ProofsTimeUnit::Second + PoSQLTimeUnit::from(ArrowTimeUnit::Second), + PoSQLTimeUnit::Second ); assert_eq!( - ProofsTimeUnit::from(ArrowTimeUnit::Millisecond), - ProofsTimeUnit::Millisecond + PoSQLTimeUnit::from(ArrowTimeUnit::Millisecond), + PoSQLTimeUnit::Millisecond ); assert_eq!( - ProofsTimeUnit::from(ArrowTimeUnit::Microsecond), - ProofsTimeUnit::Microsecond + PoSQLTimeUnit::from(ArrowTimeUnit::Microsecond), + PoSQLTimeUnit::Microsecond ); assert_eq!( - ProofsTimeUnit::from(ArrowTimeUnit::Nanosecond), - ProofsTimeUnit::Nanosecond + PoSQLTimeUnit::from(ArrowTimeUnit::Nanosecond), + PoSQLTimeUnit::Nanosecond ); } #[test] fn we_can_convert_to_arrow_time_units() { assert_eq!( - ArrowTimeUnit::from(ProofsTimeUnit::Second), + ArrowTimeUnit::from(PoSQLTimeUnit::Second), ArrowTimeUnit::Second ); assert_eq!( - ArrowTimeUnit::from(ProofsTimeUnit::Millisecond), + ArrowTimeUnit::from(PoSQLTimeUnit::Millisecond), ArrowTimeUnit::Millisecond ); assert_eq!( - ArrowTimeUnit::from(ProofsTimeUnit::Microsecond), + ArrowTimeUnit::from(PoSQLTimeUnit::Microsecond), ArrowTimeUnit::Microsecond ); assert_eq!( - ArrowTimeUnit::from(ProofsTimeUnit::Nanosecond), + ArrowTimeUnit::from(PoSQLTimeUnit::Nanosecond), ArrowTimeUnit::Nanosecond ); }