elefant_client/types/
nullable.rs1use crate::protocol::FieldDescription;
2use crate::types::{EnumTypeRegistry, FromSqlBase, FromSqlBinary, FromSqlText, ToSql};
3use crate::ElefantClientError;
4use std::error::Error;
5
6impl<'a, T> FromSqlBase<'a> for Option<T>
7where
8 T: FromSqlBase<'a>,
9{
10 fn accepts_postgres_type(oid: i32) -> bool {
11 T::accepts_postgres_type(oid)
12 }
13
14 fn accepts_with_registry(field: &FieldDescription, registry: &EnumTypeRegistry) -> bool {
15 T::accepts_with_registry(field, registry)
16 }
17
18 fn from_null(_field: &FieldDescription) -> Result<Self, ElefantClientError> {
19 Ok(None)
20 }
21}
22
23impl<'a, T> FromSqlBinary<'a> for Option<T>
24where
25 T: FromSqlBinary<'a>,
26{
27 fn from_sql_binary(
28 raw: &'a [u8],
29 field: &FieldDescription,
30 ) -> Result<Self, Box<dyn Error + Sync + Send>> {
31 T::from_sql_binary(raw, field).map(Some)
32 }
33}
34
35impl<'a, T> FromSqlText<'a> for Option<T>
36where
37 T: FromSqlText<'a>,
38{
39 fn from_sql_text(
40 raw: &'a str,
41 field: &FieldDescription,
42 ) -> Result<Self, Box<dyn Error + Sync + Send>> {
43 T::from_sql_text(raw, field).map(Some)
44 }
45}
46
47impl<T> ToSql for Option<T>
48where
49 T: ToSql,
50{
51 fn to_sql_binary(
52 &self,
53 target_buffer: &mut Vec<u8>,
54 ) -> Result<(), Box<dyn Error + Sync + Send>> {
55 match self {
56 Some(value) => value.to_sql_binary(target_buffer),
57 None => Err("Cannot convert None to binary representation. This case should never happens and should be considered a bug in the ElefantClient library. Please create an issue on GitHub.".into())
58 }
59 }
60
61 fn is_null(&self) -> bool {
62 self.is_none()
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 #[cfg(feature = "tokio")]
69 mod tokio_connection {
70 use crate::test_helpers::get_settings;
71 use crate::tokio_connection::new_client;
72 use crate::ElefantClientError;
73 use tokio::test;
74
75 #[test]
76 async fn test_nullable_types() {
77 let mut client = new_client(get_settings()).await.unwrap();
78
79 client
80 .execute_non_query_simple(
81 r#"
82 drop table if exists test_table;
83 create table test_table(value int2);
84 insert into test_table values (42);
85 "#,
86 )
87 .await
88 .unwrap();
89
90 let value: Option<i16> = client
91 .read_single_value("select value from test_table;", &[])
92 .await;
93 assert_eq!(value, Some(42));
94
95 client
96 .execute_non_query_simple(
97 "delete from test_table; insert into test_table values (null);",
98 )
99 .await
100 .unwrap();
101
102 let value: Option<i16> = client
103 .read_single_value("select value from test_table;", &[])
104 .await;
105 assert_eq!(value, None);
106
107 let result = client
108 .try_read_single_value::<i16>("select value from test_table;", &[])
109 .await;
110
111 if let Err(ElefantClientError::UnexpectedNullValue { postgres_field }) = result {
112 assert_eq!(postgres_field.column_attribute_number, 1);
113 } else {
114 panic!("Expected UnexpectedNullValue error, got {result:?}");
115 }
116
117 client
118 .execute_non_query("delete from test_table;", &[])
119 .await
120 .unwrap();
121
122 client
123 .execute_non_query("insert into test_table values ($1);", &[&None::<i16>])
124 .await
125 .unwrap();
126 let value: Option<i16> = client
127 .read_single_value("select value from test_table;", &[])
128 .await;
129 assert_eq!(value, None);
130
131 client
132 .execute_non_query("delete from test_table;", &[])
133 .await
134 .unwrap();
135
136 client
137 .execute_non_query("insert into test_table values ($1);", &[&Some(42i16)])
138 .await
139 .unwrap();
140 let value: Option<i16> = client
141 .read_single_value("select value from test_table;", &[])
142 .await;
143 assert_eq!(value, Some(42));
144 }
145 }
146}