zero_postgres/conversion/
row.rs

1//! Row decoding traits and implementations.
2
3use crate::conversion::FromWireValue;
4use crate::error::{Error, Result};
5use crate::protocol::backend::query::{DataRow, FieldDescription};
6
7/// Trait for decoding a PostgreSQL row into a Rust type.
8pub trait FromRow<'a>: Sized {
9    /// Decode a row from text format (simple protocol).
10    fn from_row_text(cols: &[FieldDescription], row: DataRow<'a>) -> Result<Self>;
11
12    /// Decode a row from binary format (extended protocol).
13    fn from_row_binary(cols: &[FieldDescription], row: DataRow<'a>) -> Result<Self>;
14}
15
16/// Decode a single column value as text.
17fn decode_column_text<'a, T: FromWireValue<'a>>(
18    field: &FieldDescription,
19    value: Option<&'a [u8]>,
20) -> Result<T> {
21    match value {
22        None => T::from_null(),
23        Some(bytes) => T::from_text(field.type_oid(), bytes),
24    }
25}
26
27/// Decode a single column value as binary.
28fn decode_column_binary<'a, T: FromWireValue<'a>>(
29    field: &FieldDescription,
30    value: Option<&'a [u8]>,
31) -> Result<T> {
32    match value {
33        None => T::from_null(),
34        Some(bytes) => T::from_binary(field.type_oid(), bytes),
35    }
36}
37
38// === Tuple implementations ===
39
40/// Implementation for empty tuple - used for statements that don't return rows
41impl FromRow<'_> for () {
42    fn from_row_text(_cols: &[FieldDescription], _row: DataRow<'_>) -> Result<Self> {
43        Ok(())
44    }
45
46    fn from_row_binary(_cols: &[FieldDescription], _row: DataRow<'_>) -> Result<Self> {
47        Ok(())
48    }
49}
50
51macro_rules! impl_from_row_tuple {
52    ($count:literal: $($idx:tt => $T:ident),+) => {
53        impl<'a, $($T: FromWireValue<'a>),+> FromRow<'a> for ($($T,)+) {
54            fn from_row_text(cols: &[FieldDescription], row: DataRow<'a>) -> Result<Self> {
55                if cols.len() < $count {
56                    return Err(Error::Decode("not enough columns for tuple".into()));
57                }
58                let mut iter = row.iter();
59                Ok(($(
60                    decode_column_text(&cols[$idx], iter.next().flatten())?,
61                )+))
62            }
63
64            fn from_row_binary(cols: &[FieldDescription], row: DataRow<'a>) -> Result<Self> {
65                if cols.len() < $count {
66                    return Err(Error::Decode("not enough columns for tuple".into()));
67                }
68                let mut iter = row.iter();
69                Ok(($(
70                    decode_column_binary(&cols[$idx], iter.next().flatten())?,
71                )+))
72            }
73        }
74    };
75}
76
77impl_from_row_tuple!(1: 0 => T1);
78impl_from_row_tuple!(2: 0 => T1, 1 => T2);
79impl_from_row_tuple!(3: 0 => T1, 1 => T2, 2 => T3);
80impl_from_row_tuple!(4: 0 => T1, 1 => T2, 2 => T3, 3 => T4);
81impl_from_row_tuple!(5: 0 => T1, 1 => T2, 2 => T3, 3 => T4, 4 => T5);
82impl_from_row_tuple!(6: 0 => T1, 1 => T2, 2 => T3, 3 => T4, 4 => T5, 5 => T6);
83impl_from_row_tuple!(7: 0 => T1, 1 => T2, 2 => T3, 3 => T4, 4 => T5, 5 => T6, 6 => T7);
84impl_from_row_tuple!(8: 0 => T1, 1 => T2, 2 => T3, 3 => T4, 4 => T5, 5 => T6, 6 => T7, 7 => T8);
85impl_from_row_tuple!(9: 0 => T1, 1 => T2, 2 => T3, 3 => T4, 4 => T5, 5 => T6, 6 => T7, 7 => T8, 8 => T9);
86impl_from_row_tuple!(10: 0 => T1, 1 => T2, 2 => T3, 3 => T4, 4 => T5, 5 => T6, 6 => T7, 7 => T8, 8 => T9, 9 => T10);
87impl_from_row_tuple!(11: 0 => T1, 1 => T2, 2 => T3, 3 => T4, 4 => T5, 5 => T6, 6 => T7, 7 => T8, 8 => T9, 9 => T10, 10 => T11);
88impl_from_row_tuple!(12: 0 => T1, 1 => T2, 2 => T3, 3 => T4, 4 => T5, 5 => T6, 6 => T7, 7 => T8, 8 => T9, 9 => T10, 10 => T11, 11 => T12);