sqlx_core/
row.rs

1use crate::column::{Column, ColumnIndex};
2use crate::database::Database;
3use crate::decode::Decode;
4use crate::error::{mismatched_types, Error};
5
6use crate::type_checking::TypeChecking;
7use crate::type_info::TypeInfo;
8use crate::types::Type;
9use crate::value::ValueRef;
10
11/// Represents a single row from the database.
12///
13/// [`FromRow`]: crate::row::FromRow
14/// [`Query::fetch`]: crate::query::Query::fetch
15pub trait Row: Unpin + Send + Sync + 'static {
16    type Database: Database<Row = Self>;
17
18    /// Returns `true` if this row has no columns.
19    #[inline]
20    fn is_empty(&self) -> bool {
21        self.len() == 0
22    }
23
24    /// Returns the number of columns in this row.
25    #[inline]
26    fn len(&self) -> usize {
27        self.columns().len()
28    }
29
30    /// Gets the column information at `index`.
31    ///
32    /// A string index can be used to access a column by name and a `usize` index
33    /// can be used to access a column by position.
34    ///
35    /// # Panics
36    ///
37    /// Panics if `index` is out of bounds.
38    /// See [`try_column`](Self::try_column) for a non-panicking version.
39    fn column<I>(&self, index: I) -> &<Self::Database as Database>::Column
40    where
41        I: ColumnIndex<Self>,
42    {
43        self.try_column(index).unwrap()
44    }
45
46    /// Gets the column information at `index` or a `ColumnIndexOutOfBounds` error if out of bounds.
47    fn try_column<I>(&self, index: I) -> Result<&<Self::Database as Database>::Column, Error>
48    where
49        I: ColumnIndex<Self>,
50    {
51        Ok(&self.columns()[index.index(self)?])
52    }
53
54    /// Gets all columns in this statement.
55    fn columns(&self) -> &[<Self::Database as Database>::Column];
56
57    /// Index into the database row and decode a single value.
58    ///
59    /// A string index can be used to access a column by name and a `usize` index
60    /// can be used to access a column by position.
61    ///
62    /// # Panics
63    ///
64    /// Panics if the column does not exist or its value cannot be decoded into the requested type.
65    /// See [`try_get`](Self::try_get) for a non-panicking version.
66    ///
67    #[inline]
68    #[track_caller]
69    fn get<'r, T, I>(&'r self, index: I) -> T
70    where
71        I: ColumnIndex<Self>,
72        T: Decode<'r, Self::Database> + Type<Self::Database>,
73    {
74        self.try_get::<T, I>(index).unwrap()
75    }
76
77    /// Index into the database row and decode a single value.
78    ///
79    /// Unlike [`get`](Self::get), this method does not check that the type
80    /// being returned from the database is compatible with the Rust type and blindly tries
81    /// to decode the value.
82    ///
83    /// # Panics
84    ///
85    /// Panics if the column does not exist or its value cannot be decoded into the requested type.
86    /// See [`try_get_unchecked`](Self::try_get_unchecked) for a non-panicking version.
87    ///
88    #[inline]
89    fn get_unchecked<'r, T, I>(&'r self, index: I) -> T
90    where
91        I: ColumnIndex<Self>,
92        T: Decode<'r, Self::Database>,
93    {
94        self.try_get_unchecked::<T, I>(index).unwrap()
95    }
96
97    /// Index into the database row and decode a single value.
98    ///
99    /// A string index can be used to access a column by name and a `usize` index
100    /// can be used to access a column by position.
101    ///
102    /// # Errors
103    ///
104    ///  * [`ColumnNotFound`] if the column by the given name was not found.
105    ///  * [`ColumnIndexOutOfBounds`] if the `usize` index was greater than the number of columns in the row.
106    ///  * [`ColumnDecode`] if the value could not be decoded into the requested type.
107    ///
108    /// [`ColumnDecode`]: Error::ColumnDecode
109    /// [`ColumnNotFound`]: Error::ColumnNotFound
110    /// [`ColumnIndexOutOfBounds`]: Error::ColumnIndexOutOfBounds
111    ///
112    fn try_get<'r, T, I>(&'r self, index: I) -> Result<T, Error>
113    where
114        I: ColumnIndex<Self>,
115        T: Decode<'r, Self::Database> + Type<Self::Database>,
116    {
117        let value = self.try_get_raw(&index)?;
118
119        if !value.is_null() {
120            let ty = value.type_info();
121
122            if !ty.is_null() && !T::compatible(&ty) {
123                return Err(Error::ColumnDecode {
124                    index: format!("{index:?}"),
125                    source: mismatched_types::<Self::Database, T>(&ty),
126                });
127            }
128        }
129
130        T::decode(value).map_err(|source| Error::ColumnDecode {
131            index: format!("{index:?}"),
132            source,
133        })
134    }
135
136    /// Index into the database row and decode a single value.
137    ///
138    /// Unlike [`try_get`](Self::try_get), this method does not check that the type
139    /// being returned from the database is compatible with the Rust type and blindly tries
140    /// to decode the value.
141    ///
142    /// # Errors
143    ///
144    ///  * [`ColumnNotFound`] if the column by the given name was not found.
145    ///  * [`ColumnIndexOutOfBounds`] if the `usize` index was greater than the number of columns in the row.
146    ///  * [`ColumnDecode`] if the value could not be decoded into the requested type.
147    ///
148    /// [`ColumnDecode`]: Error::ColumnDecode
149    /// [`ColumnNotFound`]: Error::ColumnNotFound
150    /// [`ColumnIndexOutOfBounds`]: Error::ColumnIndexOutOfBounds
151    ///
152    #[inline]
153    fn try_get_unchecked<'r, T, I>(&'r self, index: I) -> Result<T, Error>
154    where
155        I: ColumnIndex<Self>,
156        T: Decode<'r, Self::Database>,
157    {
158        let value = self.try_get_raw(&index)?;
159
160        T::decode(value).map_err(|source| Error::ColumnDecode {
161            index: format!("{index:?}"),
162            source,
163        })
164    }
165
166    /// Index into the database row and decode a single value.
167    ///
168    /// # Errors
169    ///
170    ///  * [`ColumnNotFound`] if the column by the given name was not found.
171    ///  * [`ColumnIndexOutOfBounds`] if the `usize` index was greater than the number of columns in the row.
172    ///
173    /// [`ColumnNotFound`]: Error::ColumnNotFound
174    /// [`ColumnIndexOutOfBounds`]: Error::ColumnIndexOutOfBounds
175    ///
176    fn try_get_raw<I>(&self, index: I) -> Result<<Self::Database as Database>::ValueRef<'_>, Error>
177    where
178        I: ColumnIndex<Self>;
179}
180
181pub fn debug_row<R>(row: &R, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
182where
183    R: Row,
184    usize: ColumnIndex<R>,
185    <R as Row>::Database: TypeChecking,
186{
187    write!(f, "{} ", std::any::type_name::<R>())?;
188
189    let mut debug_map = f.debug_map();
190
191    for column in row.columns().iter() {
192        match row.try_get_raw(column.ordinal()) {
193            Ok(value) => {
194                debug_map.entry(
195                    &column.name(),
196                    &<R as Row>::Database::fmt_value_debug(
197                        &<<R as Row>::Database as Database>::ValueRef::to_owned(&value),
198                    ),
199                );
200            }
201            Err(error) => {
202                debug_map.entry(&column.name(), &format!("decode error: {error:?}"));
203            }
204        }
205    }
206
207    debug_map.finish()
208}