proof_of_sql/base/database/
literal_value.rs

1use crate::base::{
2    database::ColumnType,
3    math::{decimal::Precision, i256::I256},
4    posql_time::{PoSQLTimeUnit, PoSQLTimeZone},
5    scalar::{Scalar, ScalarExt},
6    standard_serializations::limbs::{deserialize_to_limbs, serialize_limbs},
7};
8use alloc::{string::String, vec::Vec};
9use serde::{Deserialize, Serialize};
10
11/// Represents a literal value.
12///
13/// Note: The types here should correspond to native SQL database types.
14/// See `<https://ignite.apache.org/docs/latest/sql-reference/data-types>` for
15/// a description of the native types used by Apache Ignite.
16#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
17#[non_exhaustive]
18pub enum LiteralValue {
19    /// Boolean literals
20    Boolean(bool),
21    /// u8 literals
22    Uint8(u8),
23    /// i8 literals
24    TinyInt(i8),
25    /// i16 literals
26    SmallInt(i16),
27    /// i32 literals
28    Int(i32),
29    /// i64 literals
30    BigInt(i64),
31    /// i128 literals
32    Int128(i128),
33
34    /// String literals
35    ///  - the first element maps to the str value.
36    ///  - the second element maps to the str hash (see [`crate::base::scalar::Scalar`]).
37    VarChar(String),
38    /// Decimal literals with a max width of 252 bits
39    ///  - the backing store maps to the type [`crate::base::scalar::Curve25519Scalar`]
40    Decimal75(Precision, i8, I256),
41    /// `TimeStamp` defined over a unit (s, ms, ns, etc) and timezone with backing store
42    /// mapped to i64, which is time units since unix epoch
43    TimeStampTZ(PoSQLTimeUnit, PoSQLTimeZone, i64),
44    /// Scalar literals. The underlying `[u64; 4]` is the limbs of the canonical form of the literal
45    #[serde(
46        serialize_with = "serialize_limbs",
47        deserialize_with = "deserialize_to_limbs"
48    )]
49    Scalar([u64; 4]),
50    /// Binary data literals
51    ///  - the backing store is a Vec<u8> for variable length binary data
52    VarBinary(Vec<u8>),
53}
54
55impl LiteralValue {
56    /// Provides the column type associated with the column
57    #[must_use]
58    pub fn column_type(&self) -> ColumnType {
59        match self {
60            Self::Boolean(_) => ColumnType::Boolean,
61            Self::Uint8(_) => ColumnType::Uint8,
62            Self::TinyInt(_) => ColumnType::TinyInt,
63            Self::SmallInt(_) => ColumnType::SmallInt,
64            Self::Int(_) => ColumnType::Int,
65            Self::BigInt(_) => ColumnType::BigInt,
66            Self::VarChar(_) => ColumnType::VarChar,
67            Self::VarBinary(_) => ColumnType::VarBinary,
68            Self::Int128(_) => ColumnType::Int128,
69            Self::Scalar(_) => ColumnType::Scalar,
70            Self::Decimal75(precision, scale, _) => ColumnType::Decimal75(*precision, *scale),
71            Self::TimeStampTZ(tu, tz, _) => ColumnType::TimestampTZ(*tu, *tz),
72        }
73    }
74
75    /// Converts the literal to a scalar
76    pub(crate) fn to_scalar<S: Scalar>(&self) -> S {
77        match self {
78            Self::Boolean(b) => b.into(),
79            Self::Uint8(i) => i.into(),
80            Self::TinyInt(i) => i.into(),
81            Self::SmallInt(i) => i.into(),
82            Self::Int(i) => i.into(),
83            Self::BigInt(i) => i.into(),
84            Self::VarChar(str) => str.into(),
85            Self::VarBinary(bytes) => S::from_byte_slice_via_hash(bytes),
86            Self::Decimal75(_, _, i) => i.into_scalar(),
87            Self::Int128(i) => i.into(),
88            Self::Scalar(limbs) => (*limbs).into(),
89            Self::TimeStampTZ(_, _, time) => time.into(),
90        }
91    }
92}
93
94#[cfg(test)]
95mod tests {
96    use crate::base::{
97        database::LiteralValue,
98        math::decimal::Precision,
99        posql_time::{PoSQLTimeUnit, PoSQLTimeZone},
100        try_standard_binary_serialization,
101    };
102
103    /// This allows us to reuse code within solidity more safely
104    #[test]
105    fn literal_value_and_column_type_varaints_should_have_same_data_type_serialization() {
106        let literal_values = vec![
107            LiteralValue::Boolean(true),
108            LiteralValue::Uint8(2),
109            LiteralValue::TinyInt(3),
110            LiteralValue::SmallInt(4),
111            LiteralValue::Int(5),
112            LiteralValue::BigInt(6),
113            LiteralValue::Int128(7),
114            LiteralValue::VarChar("test".to_string()),
115            LiteralValue::Decimal75(Precision::new(9).unwrap(), 2, 7010.into()),
116            LiteralValue::TimeStampTZ(PoSQLTimeUnit::Millisecond, PoSQLTimeZone::utc(), 10),
117            LiteralValue::Scalar([1; 4]),
118            LiteralValue::VarBinary(vec![1]),
119        ];
120        for literal_value in literal_values {
121            let column_type = literal_value.column_type();
122            let serialized_column_type =
123                hex::encode(try_standard_binary_serialization(column_type).unwrap());
124            let serialized_literal_value =
125                hex::encode(try_standard_binary_serialization(literal_value).unwrap());
126            assert!(serialized_literal_value.starts_with(&serialized_column_type));
127        }
128    }
129}