use crate::protocol::FieldDescription;
use crate::types::PostgresType;
use crate::types::{FromSqlBase, FromSqlBinary, FromSqlText, ToSql};
use std::error::Error;
use uuid::Uuid;
impl<'a> FromSqlBase<'a> for Uuid {
fn accepts_postgres_type(oid: i32) -> bool {
oid == PostgresType::UUID.oid
}
}
impl<'a> FromSqlBinary<'a> for Uuid {
fn from_sql_binary(
raw: &'a [u8],
field: &FieldDescription,
) -> Result<Self, Box<dyn Error + Sync + Send>> {
if raw.len() != 16 {
return Err(format!(
"Invalid length for UUID. Expected 16 bytes, got {} bytes instead. Error occurred when parsing field {:?}",
raw.len(), field
).into());
}
let uuid_bytes: [u8; 16] = raw.try_into().unwrap();
Ok(Uuid::from_bytes(uuid_bytes))
}
}
impl<'a> FromSqlText<'a> for Uuid {
fn from_sql_text(
raw: &'a str,
field: &FieldDescription,
) -> Result<Self, Box<dyn Error + Sync + Send>> {
Uuid::parse_str(raw)
.map_err(|e| format!(
"Failed to parse UUID from text '{raw}': {e}. Error occurred when parsing field {field:?}"
).into())
}
}
impl ToSql for Uuid {
fn to_sql_binary(
&self,
target_buffer: &mut Vec<u8>,
) -> Result<(), Box<dyn Error + Sync + Send>> {
target_buffer.extend_from_slice(self.as_bytes());
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(feature = "tokio")]
mod tokio_connection {
use super::*;
use crate::test_helpers::get_settings;
use crate::tokio_connection::new_client;
use tokio::test;
#[test]
async fn test_uuid_type() {
let mut client = new_client(get_settings()).await.unwrap();
let nil_uuid = Uuid::nil();
let value: Uuid = client
.read_single_value("select '00000000-0000-0000-0000-000000000000'::uuid;", &[])
.await;
assert_eq!(value, nil_uuid);
let test_uuid = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
let value: Uuid = client
.read_single_value_dual_mode("select '550e8400-e29b-41d4-a716-446655440000'::uuid")
.await;
assert_eq!(value, test_uuid);
client.execute_non_query_simple("drop table if exists test_uuid_table; create table test_uuid_table(value uuid);").await.unwrap();
client
.execute_non_query("insert into test_uuid_table values ($1);", &[&test_uuid])
.await
.unwrap();
let retrieved: Uuid = client
.read_single_value("select value from test_uuid_table;", &[])
.await;
assert_eq!(retrieved, test_uuid);
let null_value: Option<Uuid> = client
.read_single_value_dual_mode("select null::uuid")
.await;
assert_eq!(null_value, None);
let another_uuid = Uuid::parse_str("6ba7b810-9dad-11d1-80b4-00c04fd430c8").unwrap();
client
.execute_non_query("insert into test_uuid_table values ($1);", &[&another_uuid])
.await
.unwrap();
let retrieved_another: Uuid = client
.read_single_value(
"select value from test_uuid_table order by value desc limit 1;",
&[],
)
.await;
assert_eq!(retrieved_another, another_uuid);
}
#[test]
async fn test_uuid_arrays() {
let mut client = new_client(get_settings()).await.unwrap();
let uuids = vec![
Uuid::nil(),
Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap(),
Uuid::parse_str("6ba7b810-9dad-11d1-80b4-00c04fd430c8").unwrap(),
];
let value: Vec<Uuid> = client.read_single_value(
"select '{00000000-0000-0000-0000-000000000000,550e8400-e29b-41d4-a716-446655440000,6ba7b810-9dad-11d1-80b4-00c04fd430c8}'::uuid[];",
&[]
).await;
assert_eq!(value, uuids);
let uuids_with_nulls = vec![
Some(Uuid::nil()),
None,
Some(Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap()),
];
let value: Vec<Option<Uuid>> = client.read_single_value(
"select '{00000000-0000-0000-0000-000000000000,null,550e8400-e29b-41d4-a716-446655440000}'::uuid[];",
&[]
).await;
assert_eq!(value, uuids_with_nulls);
}
}
}