fire_postgres/table/column/
data.rs1#[derive(Debug, Clone, PartialEq)]
2pub enum Text<'a> {
3 Owned(String),
4 Borrowed(&'a str),
5}
6
7impl Text<'_> {
8 pub fn into_string(self) -> String {
9 match self {
10 Self::Owned(s) => s,
11 Self::Borrowed(b) => b.to_string(),
12 }
13 }
14
15 pub fn as_str(&self) -> &str {
16 match self {
17 Self::Owned(s) => s,
18 Self::Borrowed(b) => b,
19 }
20 }
21
22 pub fn len(&self) -> usize {
23 match self {
24 Self::Owned(s) => s.len(),
25 Self::Borrowed(s) => s.len(),
26 }
27 }
28}
29
30impl<'a> From<&'a str> for Text<'a> {
31 fn from(s: &'a str) -> Self {
32 Self::Borrowed(s)
33 }
34}
35
36impl From<String> for Text<'_> {
37 fn from(s: String) -> Self {
38 Self::Owned(s)
39 }
40}
41
42pub enum SliceState<'a> {
45 Owned(&'a [String]),
46 Borrowed(&'a [&'a str]),
47}
48
49#[derive(Debug, Clone, PartialEq)]
50pub enum TextArray<'a> {
51 SliceOwned(&'a [String]),
52 SliceStr(&'a [&'a str]),
53 VecOwned(Vec<String>),
54 VecStr(Vec<&'a str>),
55}
56
57impl<'a> TextArray<'a> {
58 pub fn len(&self) -> usize {
59 match self {
60 Self::SliceOwned(v) => v.len(),
61 Self::SliceStr(v) => v.len(),
62 Self::VecOwned(v) => v.len(),
63 Self::VecStr(v) => v.len(),
64 }
65 }
66
67 pub fn into_vec_owned(self) -> Vec<String> {
68 match self {
69 Self::SliceOwned(s) => s.iter().map(|v| v.to_string()).collect(),
70 Self::SliceStr(s) => s.iter().map(|v| v.to_string()).collect(),
71 Self::VecOwned(v) => v,
72 Self::VecStr(v) => v.iter().map(|v| v.to_string()).collect(),
73 }
74 }
75
76 pub fn unwrap_vec_str(self) -> Vec<&'a str> {
95 match self {
96 Self::VecStr(v) => v,
97 _ => panic!("could not unwrap vec str from textarray"),
98 }
99 }
100
101 pub fn to_slice_state(&self) -> SliceState<'_> {
110 match self {
111 Self::SliceOwned(v) => SliceState::Owned(v),
112 Self::SliceStr(v) => SliceState::Borrowed(v),
113 Self::VecOwned(v) => SliceState::Owned(v.as_slice()),
114 Self::VecStr(v) => SliceState::Borrowed(v.as_slice()),
115 }
116 }
117}
118
119impl<'a> From<&'a [String]> for TextArray<'a> {
120 fn from(ar: &'a [String]) -> Self {
121 Self::SliceOwned(ar)
122 }
123}
124
125impl<'a> From<&'a [&'a str]> for TextArray<'a> {
126 fn from(ar: &'a [&'a str]) -> Self {
127 Self::SliceStr(ar)
128 }
129}
130
131impl From<Vec<String>> for TextArray<'_> {
132 fn from(ar: Vec<String>) -> Self {
133 Self::VecOwned(ar)
134 }
135}
136
137impl<'a> From<Vec<&'a str>> for TextArray<'a> {
138 fn from(ar: Vec<&'a str>) -> Self {
139 Self::VecStr(ar)
140 }
141}
142
143#[derive(Debug, Clone, PartialEq)]
151pub enum ColumnData<'a> {
152 Boolean(bool),
153 Text(Text<'a>),
154 Date(i32),
155 Timestamp(i64),
156 F64(f64),
157 F32(f32),
158 I64(i64),
159 I32(i32),
160 I16(i16),
161 Option(Option<Box<ColumnData<'a>>>),
162 TextArray(TextArray<'a>),
163 Bytea(&'a [u8]),
164}
165
166impl<'a> ColumnData<'a> {
167 #[inline(always)]
179 pub fn is_null(&self) -> bool {
180 matches!(self, Self::Option(None))
181 }
182
183 pub fn unwrap_text(self) -> Text<'a> {
184 match self {
185 Self::Text(t) => t,
186 _ => panic!("could not unwrap text"),
187 }
188 }
189}
190
191#[cfg(feature = "connect")]
192mod impl_postgres {
193
194 use super::*;
195
196 use std::error::Error;
197
198 use bytes::BytesMut;
199
200 use fallible_iterator::FallibleIterator;
201
202 use postgres_types::{accepts, to_sql_checked};
203 use postgres_types::{FromSql, IsNull, Kind as PostgresKind, ToSql, Type};
204
205 use postgres_protocol::types as ty;
206 use postgres_protocol::IsNull as ProtIsNull;
207
208 type BoxedError = Box<dyn Error + Send + Sync + 'static>;
209
210 macro_rules! accepts {
211 () => {
212 fn accepts(ty: &Type) -> bool {
213 match ty {
214 &Type::BOOL
215 | &Type::BPCHAR
216 | &Type::VARCHAR
217 | &Type::TEXT
218 | &Type::DATE
219 | &Type::TIMESTAMP
220 | &Type::FLOAT8
221 | &Type::FLOAT4
222 | &Type::INT8
223 | &Type::INT4
224 | &Type::INT2
225 | &Type::BYTEA
226 | &Type::JSON => true,
227 t => match t.kind() {
228 PostgresKind::Array(Type::TEXT) => true,
229 _ => false,
230 },
231 }
232 }
233 };
234 }
235
236 fn text_array_to_sql<'a, I, T, F>(
237 len: usize,
238 elements: I,
239 f: F,
240 buf: &mut BytesMut,
241 ) -> Result<(), BoxedError>
242 where
243 I: IntoIterator<Item = T>,
244 F: Fn(T) -> &'a str,
245 {
246 let len = i32::try_from(len).map_err(|_| "cannot convert i16 to u8")?;
247 let dimension = ty::ArrayDimension {
248 len,
249 lower_bound: 1,
250 };
251
252 let text_oid = Type::TEXT.oid();
253
254 ty::array_to_sql(
255 Some(dimension),
256 text_oid,
257 elements,
258 |e, buf| {
259 ty::text_to_sql(f(e), buf);
260 Ok(ProtIsNull::No)
261 },
262 buf,
263 )
264 }
265
266 impl<'a> ToSql for ColumnData<'a> {
267 fn to_sql(
268 &self,
269 _ty: &Type,
270 out: &mut BytesMut,
271 ) -> Result<IsNull, BoxedError> {
272 match self {
273 ColumnData::Boolean(v) => ty::bool_to_sql(*v, out),
274 ColumnData::Text(v) => ty::text_to_sql(v.as_str(), out),
275 ColumnData::Date(v) => ty::date_to_sql(*v, out),
276 ColumnData::Timestamp(v) => ty::timestamp_to_sql(*v, out),
277 ColumnData::F64(v) => ty::float8_to_sql(*v, out),
278 ColumnData::F32(v) => ty::float4_to_sql(*v, out),
279 ColumnData::I64(v) => ty::int8_to_sql(*v, out),
280 ColumnData::I32(v) => ty::int4_to_sql(*v, out),
281 ColumnData::I16(v) => ty::int2_to_sql(*v, out),
282 ColumnData::Option(o) => match o {
283 Some(v) => return v.to_sql(_ty, out),
284 None => return Ok(IsNull::Yes),
285 },
286 ColumnData::Bytea(v) => ty::bytea_to_sql(v, out),
287 ColumnData::TextArray(v) => match v.to_slice_state() {
288 SliceState::Owned(o) => text_array_to_sql(
289 o.len(),
290 o.iter(),
291 |v| v.as_str(),
292 out,
293 )?,
294 SliceState::Borrowed(b) => {
295 text_array_to_sql(b.len(), b.iter(), |v| v, out)?
296 }
297 },
298 };
301 Ok(IsNull::No)
302 }
303
304 accepts!();
305
306 to_sql_checked!();
307 }
308
309 impl<'a> FromSql<'a> for ColumnData<'a> {
310 fn from_sql(ty: &Type, raw: &'a [u8]) -> Result<Self, BoxedError> {
311 Ok(match ty {
312 &Type::BOOL => Self::Boolean(ty::bool_from_sql(raw)?),
313 &Type::BPCHAR | &Type::VARCHAR | &Type::TEXT | &Type::JSON => {
314 Self::Text(ty::text_from_sql(raw)?.into())
315 }
316 &Type::DATE => Self::Date(ty::date_from_sql(raw)?),
317 &Type::TIMESTAMP => {
318 Self::Timestamp(ty::timestamp_from_sql(raw)?)
319 }
320 &Type::FLOAT8 => Self::F64(ty::float8_from_sql(raw)?),
321 &Type::FLOAT4 => Self::F32(ty::float4_from_sql(raw)?),
322 &Type::INT8 => Self::I64(ty::int8_from_sql(raw)?),
323 &Type::INT4 => Self::I32(ty::int4_from_sql(raw)?),
324 &Type::INT2 => Self::I16(ty::int2_from_sql(raw)?),
325 &Type::BYTEA => Self::Bytea(ty::bytea_from_sql(raw)),
326 t => {
328 match t.kind() {
329 PostgresKind::Array(Type::TEXT) => {}
330 _ => return Err("type not recognized".into()),
331 };
332
333 let array = ty::array_from_sql(raw)?;
334 if array.dimensions().count()? > 1 {
335 return Err("array contains too many dimensions".into());
336 }
337
338 if array.element_type() != Type::TEXT.oid() {
339 return Err(
340 "expected array with TEXT AS Element".into()
341 );
342 }
343
344 let mut values = vec![];
345 let mut array = array.values();
346 while let Some(buf) = array.next()? {
347 match buf {
348 Some(buf) => values.push(ty::text_from_sql(buf)?),
349 None => {
350 return Err("array items cannot be null".into())
351 }
352 }
353 }
354
355 Self::TextArray(values.into())
356 }
357 })
358 }
359
360 fn from_sql_null(
361 _: &Type,
362 ) -> Result<Self, Box<dyn Error + Sync + Send>> {
363 Ok(Self::Option(None))
364 }
365
366 accepts!();
367 }
368}