diff --git a/packages/cipherstash-proxy-integration/src/map_params.rs b/packages/cipherstash-proxy-integration/src/map_params.rs index 5fbbf11..864873b 100644 --- a/packages/cipherstash-proxy-integration/src/map_params.rs +++ b/packages/cipherstash-proxy-integration/src/map_params.rs @@ -1,7 +1,7 @@ #[cfg(test)] mod tests { use crate::common::{connect_with_tls, id, reset_schema, trace, PROXY}; - use chrono::NaiveDate; + use chrono::prelude::*; #[tokio::test] async fn map_text() { @@ -130,6 +130,32 @@ mod tests { } } + #[tokio::test] + async fn map_int8_as_biguint() { + trace(); + + let client = connect_with_tls(PROXY).await; + + let id = id(); + let encrypted_int8: i64 = 42; + + let sql = "INSERT INTO encrypted (id, encrypted_int8_as_biguint) VALUES ($1, $2)"; + client.query(sql, &[&id, &encrypted_int8]).await.unwrap(); + + let sql = "SELECT id, encrypted_int8_as_biguint FROM encrypted WHERE id = $1"; + let rows = client.query(sql, &[&id]).await.unwrap(); + + assert_eq!(rows.len(), 1); + + for row in rows { + let result_id: i64 = row.get("id"); + let result_int: i64 = row.get("encrypted_int8_as_biguint"); + + assert_eq!(id, result_id); + assert_eq!(encrypted_int8, result_int); + } + } + #[tokio::test] async fn map_float8() { trace(); @@ -182,6 +208,66 @@ mod tests { } } + #[tokio::test] + async fn map_timestamp() { + trace(); + + let client = connect_with_tls(PROXY).await; + + let id = id(); + let encrypted_timestamp = "2000-10-01T17:30:00Z".parse::>().unwrap(); + + let sql = "INSERT INTO encrypted (id, encrypted_timestamp) VALUES ($1, $2)"; + client + .query(sql, &[&id, &encrypted_timestamp]) + .await + .unwrap(); + + let sql = "SELECT id, encrypted_timestamp FROM encrypted WHERE id = $1"; + let rows = client.query(sql, &[&id]).await.unwrap(); + + assert_eq!(rows.len(), 1); + + for row in rows { + let result_id: i64 = row.get("id"); + let result: DateTime = row.get("encrypted_timestamp"); + + assert_eq!(id, result_id); + assert_eq!(encrypted_timestamp, result); + } + } + + #[tokio::test] + async fn map_timestamp_with_tz() { + trace(); + + use chrono_tz::Australia::Sydney; + + let client = connect_with_tls(PROXY).await; + + let id = id(); + let encrypted_timestamp = "2000-10-01T17:30:00Z".parse::>().unwrap(); + + let sql = "INSERT INTO encrypted (id, encrypted_timestamp) VALUES ($1, $2)"; + client + .query(sql, &[&id, &encrypted_timestamp]) + .await + .unwrap(); + + let sql = "SELECT id, encrypted_timestamp FROM encrypted WHERE id = $1"; + let rows = client.query(sql, &[&id]).await.unwrap(); + + assert_eq!(rows.len(), 1); + + for row in rows { + let result_id: i64 = row.get("id"); + let result: DateTime = row.get("encrypted_timestamp"); + + assert_eq!(id, result_id); + assert_eq!(encrypted_timestamp, result); + } + } + #[tokio::test] async fn map_jsonb() { trace(); diff --git a/packages/cipherstash-proxy/src/encrypt/config/encrypt_config.rs b/packages/cipherstash-proxy/src/encrypt/config/encrypt_config.rs index 730e678..d5bfbcc 100644 --- a/packages/cipherstash-proxy/src/encrypt/config/encrypt_config.rs +++ b/packages/cipherstash-proxy/src/encrypt/config/encrypt_config.rs @@ -56,10 +56,13 @@ pub enum CastAs { BigInt, Boolean, Date, + Timestamp, Real, Double, Int, SmallInt, + #[serde(rename = "big_uint")] + BigUInt, #[default] Text, #[serde(rename = "jsonb")] @@ -126,8 +129,10 @@ impl From for ColumnType { CastAs::Int => ColumnType::Int, CastAs::Boolean => ColumnType::Boolean, CastAs::Date => ColumnType::Date, + CastAs::Timestamp => ColumnType::Timestamp, CastAs::Real | CastAs::Double => ColumnType::Float, CastAs::Text => ColumnType::Utf8Str, + CastAs::BigUInt => ColumnType::BigUInt, CastAs::JsonB => ColumnType::JsonB, } } diff --git a/packages/cipherstash-proxy/src/postgresql/data/from_sql.rs b/packages/cipherstash-proxy/src/postgresql/data/from_sql.rs index 7850741..464c743 100644 --- a/packages/cipherstash-proxy/src/postgresql/data/from_sql.rs +++ b/packages/cipherstash-proxy/src/postgresql/data/from_sql.rs @@ -5,7 +5,7 @@ use crate::{ }; use bigdecimal::BigDecimal; use bytes::BytesMut; -use chrono::NaiveDate; +use chrono::{DateTime, NaiveDate, Utc}; use cipherstash_client::{encryption::Plaintext, schema::ColumnType}; use postgres_types::FromSql; use postgres_types::Type; @@ -153,9 +153,10 @@ fn text_from_sql( .map_err(|_| MappingError::CouldNotParseParameter) .map(Plaintext::new), - (&Type::TIMESTAMPTZ, _) => { - unimplemented!("TIMESTAMPTZ") - } + (&Type::TIMESTAMP | &Type::TIMESTAMPTZ, ColumnType::Timestamp) => val + .parse::>() + .map_err(|_| MappingError::CouldNotParseParameter) + .map(Plaintext::new), (&Type::TEXT | &Type::JSONB, ColumnType::JsonB) => { serde_json::from_str::(val) .map_err(|_| MappingError::CouldNotParseParameter) @@ -237,8 +238,9 @@ fn binary_from_sql( (&Type::TEXT, _) => parse_bytes_from_sql::(bytes, pg_type) .and_then(|val| text_from_sql(&val, pg_type, col_type)), - // TODO: timestamps - (_, ColumnType::Timestamp) => unimplemented!("TIMESTAMPTZ"), + (&Type::TIMESTAMP | &Type::TIMESTAMPTZ, ColumnType::Timestamp) => { + parse_bytes_from_sql::>(bytes, pg_type).map(Plaintext::new) + } // Unsupported (ty, _) => Err(MappingError::UnsupportedParameterType { diff --git a/packages/cipherstash-proxy/src/postgresql/data/to_sql.rs b/packages/cipherstash-proxy/src/postgresql/data/to_sql.rs index 6bc57d7..c51d16d 100644 --- a/packages/cipherstash-proxy/src/postgresql/data/to_sql.rs +++ b/packages/cipherstash-proxy/src/postgresql/data/to_sql.rs @@ -1,9 +1,11 @@ use crate::error::EncryptError; use crate::{error::Error, postgresql::format_code::FormatCode}; +use bigdecimal::FromPrimitive; use bytes::BytesMut; use cipherstash_client::encryption::Plaintext; use postgres_types::ToSql; use postgres_types::Type; +use rust_decimal::Decimal; pub fn to_sql(plaintext: &Plaintext, format_code: &FormatCode) -> Result, Error> { let bytes = match format_code { @@ -47,8 +49,10 @@ fn binary_to_sql(plaintext: &Plaintext) -> Result { Plaintext::Utf8Str(x) => x.to_sql_checked(&Type::TEXT, &mut bytes), Plaintext::JsonB(x) => x.to_sql_checked(&Type::JSONB, &mut bytes), Plaintext::Decimal(x) => x.to_sql_checked(&Type::NUMERIC, &mut bytes), - // TODO: Implement these - Plaintext::BigUInt(_x) => unimplemented!(), + Plaintext::BigUInt(x) => { + let d = x.map(|x| Decimal::from_u64(x)).flatten(); + d.to_sql_checked(&Type::NUMERIC, &mut bytes) + } }; match result { diff --git a/tests/sql/schema.sql b/tests/sql/schema.sql index 57f5e29..d9c2fe8 100644 --- a/tests/sql/schema.sql +++ b/tests/sql/schema.sql @@ -18,6 +18,7 @@ CREATE TABLE encrypted ( encrypted_int2 eql_v1_encrypted, encrypted_int4 eql_v1_encrypted, encrypted_int8 eql_v1_encrypted, + encrypted_int8_as_biguint eql_v1_encrypted, encrypted_float8 eql_v1_encrypted, encrypted_date eql_v1_encrypted, encrypted_jsonb eql_v1_encrypted, @@ -108,6 +109,12 @@ SELECT eql_v1.add_index( 'big_int' ); +SELECT eql_v1.add_index( + 'encrypted', + 'encrypted_int8_as_biguint', + 'unique', + 'big_uint' +); SELECT eql_v1.add_index( 'encrypted',