elephantry/
to_sql.rs

1use byteorder::WriteBytesExt;
2
3macro_rules! write {
4    ($fn:ident, $ty: ty) => {
5        #[inline]
6        pub fn $fn(buf: &mut Vec<u8>, data: $ty) -> crate::Result<()> {
7            buf.$fn::<byteorder::BigEndian>(data)?;
8
9            Ok(())
10        }
11    };
12}
13
14write!(write_i16, i16);
15write!(write_i32, i32);
16write!(write_i64, i64);
17write!(write_f32, f32);
18write!(write_f64, f64);
19
20#[inline]
21pub fn write_i8(buf: &mut Vec<u8>, data: i8) -> crate::Result<()> {
22    buf.write_i8(data)?;
23
24    Ok(())
25}
26
27/**
28 * Trait to allow a rust type to be translated to a SQL value.
29 */
30pub trait ToSql {
31    /** The corresponding SQL type */
32    fn ty(&self) -> crate::pq::Type;
33
34    /**
35     * Convert the value to text format
36     *
37     * See the postgresql
38     * [adt](https://github.com/postgres/postgres/tree/REL_12_0/src/backend/utils/adt)
39     * module source code, mainly `*_out` functions.
40     */
41    fn to_text(&self) -> crate::Result<Option<String>>;
42
43    /**
44     * Convert the value to binary format
45     *
46     * See the postgresql
47     * [adt](https://github.com/postgres/postgres/tree/REL_12_0/src/backend/utils/adt)
48     * module source code, mainly `*_send` functions.
49     */
50    fn to_binary(&self) -> crate::Result<Option<Vec<u8>>>;
51
52    fn error(&self, message: &str) -> crate::Error {
53        crate::Error::ToSql {
54            pg_type: self.ty(),
55            rust_type: std::any::type_name::<Self>().to_string(),
56            message: message.to_string(),
57        }
58    }
59}
60
61macro_rules! number {
62    ($sql_type:ident, $rust_type:ty, $write:ident) => {
63        impl ToSql for $rust_type {
64            fn ty(&self) -> crate::pq::Type {
65                crate::pq::types::$sql_type
66            }
67
68            fn to_binary(&self) -> crate::Result<Option<Vec<u8>>> {
69                let mut buf = Vec::new();
70                $write(&mut buf, *self)?;
71
72                Ok(Some(buf))
73            }
74
75            fn to_text(&self) -> crate::Result<Option<String>> {
76                self.to_string().to_text()
77            }
78        }
79    };
80}
81
82number!(FLOAT4, f32, write_f32);
83number!(FLOAT8, f64, write_f64);
84number!(INT2, i16, write_i16);
85number!(INT4, i32, write_i32);
86number!(INT8, i64, write_i64);
87
88impl ToSql for u16 {
89    fn ty(&self) -> crate::pq::Type {
90        crate::pq::types::INT4
91    }
92
93    fn to_binary(&self) -> crate::Result<Option<Vec<u8>>> {
94        i32::from(*self).to_binary()
95    }
96
97    fn to_text(&self) -> crate::Result<Option<String>> {
98        i32::from(*self).to_text()
99    }
100}
101
102impl ToSql for u32 {
103    fn ty(&self) -> crate::pq::Type {
104        crate::pq::types::INT8
105    }
106
107    fn to_binary(&self) -> crate::Result<Option<Vec<u8>>> {
108        i64::from(*self).to_binary()
109    }
110
111    fn to_text(&self) -> crate::Result<Option<String>> {
112        i64::from(*self).to_text()
113    }
114}
115
116impl ToSql for bool {
117    fn ty(&self) -> crate::pq::Type {
118        crate::pq::types::BOOL
119    }
120
121    /*
122     * https://github.com/postgres/postgres/blob/REL_12_0/src/backend/utils/adt/bool.c#L164
123     */
124    fn to_text(&self) -> crate::Result<Option<String>> {
125        let data = if *self { "t" } else { "f" };
126
127        data.to_text()
128    }
129
130    /*
131     * https://github.com/postgres/postgres/blob/REL_12_0/src/backend/utils/adt/bool.c#L181
132     */
133    fn to_binary(&self) -> crate::Result<Option<Vec<u8>>> {
134        Ok(Some(vec![u8::from(*self)]))
135    }
136}
137
138impl ToSql for &str {
139    fn ty(&self) -> crate::pq::Type {
140        crate::pq::types::TEXT
141    }
142
143    /*
144     * https://github.com/postgres/postgres/blob/REL_12_0/src/backend/utils/adt/varchar.c#L489
145     */
146    fn to_text(&self) -> crate::Result<Option<String>> {
147        (*self).to_string().to_text()
148    }
149
150    /*
151     * https://github.com/postgres/postgres/blob/REL_12_0/src/backend/utils/adt/varchar.c#L522
152     */
153    fn to_binary(&self) -> crate::Result<Option<Vec<u8>>> {
154        (*self).to_string().to_binary()
155    }
156}
157
158impl ToSql for char {
159    fn ty(&self) -> crate::pq::Type {
160        crate::pq::types::CHAR
161    }
162
163    /*
164     * https://github.com/postgres/postgres/blob/REL_12_0/src/backend/utils/adt/char.c#L33
165     */
166    fn to_text(&self) -> crate::Result<Option<String>> {
167        self.to_string().to_text()
168    }
169
170    /*
171     * https://github.com/postgres/postgres/blob/REL_12_0/src/backend/utils/adt/char.c#L66
172     */
173    fn to_binary(&self) -> crate::Result<Option<Vec<u8>>> {
174        Ok(Some(vec![*self as u8]))
175    }
176}
177
178impl ToSql for String {
179    fn ty(&self) -> crate::pq::Type {
180        crate::pq::types::TEXT
181    }
182
183    fn to_text(&self) -> crate::Result<Option<String>> {
184        Ok(Some(self.clone()))
185    }
186
187    fn to_binary(&self) -> crate::Result<Option<Vec<u8>>> {
188        Ok(Some(self.clone().into_bytes()))
189    }
190}
191
192impl<T: ToSql> ToSql for Option<T> {
193    fn ty(&self) -> crate::pq::Type {
194        match self {
195            Some(data) => data.ty(),
196            None => crate::pq::types::UNKNOWN,
197        }
198    }
199
200    fn to_text(&self) -> crate::Result<Option<String>> {
201        match self {
202            Some(data) => T::to_text(data),
203            None => Ok(None),
204        }
205    }
206
207    fn to_binary(&self) -> crate::Result<Option<Vec<u8>>> {
208        match self {
209            Some(data) => T::to_binary(data),
210            None => Ok(None),
211        }
212    }
213}
214
215impl ToSql for () {
216    fn ty(&self) -> crate::pq::Type {
217        crate::pq::types::UNKNOWN
218    }
219
220    fn to_text(&self) -> crate::Result<Option<String>> {
221        Ok(None)
222    }
223
224    fn to_binary(&self) -> crate::Result<Option<Vec<u8>>> {
225        Ok(None)
226    }
227}