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}