Skip to main content

tokio_postgres/
row.rs

1//! Rows.
2
3use crate::row::sealed::{AsName, Sealed};
4use crate::simple_query::SimpleColumn;
5use crate::statement::Column;
6use crate::types::{FromSql, Type, WrongType};
7use crate::{Error, Statement};
8use fallible_iterator::FallibleIterator;
9use postgres_protocol::message::backend::DataRowBody;
10use std::fmt;
11use std::io;
12use std::ops::Range;
13use std::str;
14use std::sync::Arc;
15
16mod sealed {
17    pub trait Sealed {}
18
19    pub trait AsName {
20        fn as_name(&self) -> &str;
21    }
22}
23
24impl AsName for Column {
25    fn as_name(&self) -> &str {
26        self.name()
27    }
28}
29
30impl AsName for String {
31    fn as_name(&self) -> &str {
32        self
33    }
34}
35
36/// A trait implemented by types that can index into columns of a row.
37///
38/// This cannot be implemented outside of this crate.
39pub trait RowIndex: Sealed {
40    #[doc(hidden)]
41    fn __idx<T>(&self, columns: &[T]) -> Option<usize>
42    where
43        T: AsName;
44}
45
46impl Sealed for usize {}
47
48impl RowIndex for usize {
49    #[inline]
50    fn __idx<T>(&self, columns: &[T]) -> Option<usize>
51    where
52        T: AsName,
53    {
54        if *self >= columns.len() {
55            None
56        } else {
57            Some(*self)
58        }
59    }
60}
61
62impl Sealed for str {}
63
64impl RowIndex for str {
65    #[inline]
66    fn __idx<T>(&self, columns: &[T]) -> Option<usize>
67    where
68        T: AsName,
69    {
70        if let Some(idx) = columns.iter().position(|d| d.as_name() == self) {
71            return Some(idx);
72        };
73
74        // FIXME ASCII-only case insensitivity isn't really the right thing to
75        // do. Postgres itself uses a dubious wrapper around tolower and JDBC
76        // uses the US locale.
77        columns
78            .iter()
79            .position(|d| d.as_name().eq_ignore_ascii_case(self))
80    }
81}
82
83impl<T> Sealed for &T where T: ?Sized + Sealed {}
84
85impl<T> RowIndex for &T
86where
87    T: ?Sized + RowIndex,
88{
89    #[inline]
90    fn __idx<U>(&self, columns: &[U]) -> Option<usize>
91    where
92        U: AsName,
93    {
94        T::__idx(*self, columns)
95    }
96}
97
98/// A row of data returned from the database by a query.
99#[derive(Clone)]
100pub struct Row {
101    statement: Statement,
102    body: DataRowBody,
103    ranges: Vec<Option<Range<usize>>>,
104}
105
106impl fmt::Debug for Row {
107    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108        f.debug_struct("Row")
109            .field("columns", &self.columns())
110            .finish()
111    }
112}
113
114impl Row {
115    pub(crate) fn new(statement: Statement, body: DataRowBody) -> Result<Row, Error> {
116        let ranges = body.ranges().collect().map_err(Error::parse)?;
117        let row = Row {
118            statement,
119            body,
120            ranges,
121        };
122        // The DataRow field count is sent by the server independently of the
123        // RowDescription column count; a mismatch would make column accessors
124        // index `ranges` out of bounds and panic, so reject it up front.
125        if row.ranges.len() != row.statement.columns().len() {
126            return Err(Error::parse(io::Error::new(
127                io::ErrorKind::InvalidData,
128                "DataRow field count does not match the number of columns",
129            )));
130        }
131        Ok(row)
132    }
133
134    /// Returns information about the columns of data in the row.
135    pub fn columns(&self) -> &[Column] {
136        self.statement.columns()
137    }
138
139    /// Determines if the row contains no values.
140    pub fn is_empty(&self) -> bool {
141        self.len() == 0
142    }
143
144    /// Returns the number of values in the row.
145    pub fn len(&self) -> usize {
146        self.columns().len()
147    }
148
149    /// Deserializes a value from the row.
150    ///
151    /// The value can be specified either by its numeric index in the row, or by its column name.
152    ///
153    /// # Panics
154    ///
155    /// Panics if the index is out of bounds or if the value cannot be converted to the specified type.
156    #[track_caller]
157    pub fn get<'a, I, T>(&'a self, idx: I) -> T
158    where
159        I: RowIndex + fmt::Display,
160        T: FromSql<'a>,
161    {
162        match self.get_inner(&idx) {
163            Ok(ok) => ok,
164            Err(err) => panic!("error retrieving column {}: {}", idx, err),
165        }
166    }
167
168    /// Like `Row::get`, but returns a `Result` rather than panicking.
169    pub fn try_get<'a, I, T>(&'a self, idx: I) -> Result<T, Error>
170    where
171        I: RowIndex + fmt::Display,
172        T: FromSql<'a>,
173    {
174        self.get_inner(&idx)
175    }
176
177    fn get_inner<'a, I, T>(&'a self, idx: &I) -> Result<T, Error>
178    where
179        I: RowIndex + fmt::Display,
180        T: FromSql<'a>,
181    {
182        let idx = match idx.__idx(self.columns()) {
183            Some(idx) => idx,
184            None => return Err(Error::column(idx.to_string())),
185        };
186
187        let ty = self.columns()[idx].type_();
188        if !T::accepts(ty) {
189            return Err(Error::from_sql(
190                Box::new(WrongType::new::<T>(ty.clone())),
191                idx,
192            ));
193        }
194
195        FromSql::from_sql_nullable(ty, self.col_buffer(idx)).map_err(|e| Error::from_sql(e, idx))
196    }
197
198    /// Returns the raw size of the row in bytes.
199    pub fn raw_size_bytes(&self) -> usize {
200        self.body.buffer_bytes().len()
201    }
202
203    /// Get the raw bytes for the column at the given index.
204    fn col_buffer(&self, idx: usize) -> Option<&[u8]> {
205        let range = self.ranges[idx].to_owned()?;
206        Some(&self.body.buffer()[range])
207    }
208}
209
210impl AsName for SimpleColumn {
211    fn as_name(&self) -> &str {
212        self.name()
213    }
214}
215
216/// A row of data returned from the database by a simple query.
217#[derive(Debug)]
218pub struct SimpleQueryRow {
219    columns: Arc<[SimpleColumn]>,
220    body: DataRowBody,
221    ranges: Vec<Option<Range<usize>>>,
222}
223
224impl SimpleQueryRow {
225    #[allow(clippy::new_ret_no_self)]
226    pub(crate) fn new(
227        columns: Arc<[SimpleColumn]>,
228        body: DataRowBody,
229    ) -> Result<SimpleQueryRow, Error> {
230        let ranges = body.ranges().collect().map_err(Error::parse)?;
231        let row = SimpleQueryRow {
232            columns,
233            body,
234            ranges,
235        };
236        // The DataRow field count is sent by the server independently of the
237        // RowDescription column count; a mismatch would make column accessors
238        // index `ranges` out of bounds and panic, so reject it up front.
239        if row.ranges.len() != row.columns.len() {
240            return Err(Error::parse(io::Error::new(
241                io::ErrorKind::InvalidData,
242                "DataRow field count does not match the number of columns",
243            )));
244        }
245        Ok(row)
246    }
247
248    /// Returns information about the columns of data in the row.
249    pub fn columns(&self) -> &[SimpleColumn] {
250        &self.columns
251    }
252
253    /// Determines if the row contains no values.
254    pub fn is_empty(&self) -> bool {
255        self.len() == 0
256    }
257
258    /// Returns the number of values in the row.
259    pub fn len(&self) -> usize {
260        self.columns.len()
261    }
262
263    /// Returns a value from the row.
264    ///
265    /// The value can be specified either by its numeric index in the row, or by its column name.
266    ///
267    /// # Panics
268    ///
269    /// Panics if the index is out of bounds or if the value cannot be converted to the specified type.
270    #[track_caller]
271    pub fn get<I>(&self, idx: I) -> Option<&str>
272    where
273        I: RowIndex + fmt::Display,
274    {
275        match self.get_inner(&idx) {
276            Ok(ok) => ok,
277            Err(err) => panic!("error retrieving column {}: {}", idx, err),
278        }
279    }
280
281    /// Like `SimpleQueryRow::get`, but returns a `Result` rather than panicking.
282    pub fn try_get<I>(&self, idx: I) -> Result<Option<&str>, Error>
283    where
284        I: RowIndex + fmt::Display,
285    {
286        self.get_inner(&idx)
287    }
288
289    fn get_inner<I>(&self, idx: &I) -> Result<Option<&str>, Error>
290    where
291        I: RowIndex + fmt::Display,
292    {
293        let idx = match idx.__idx(&self.columns) {
294            Some(idx) => idx,
295            None => return Err(Error::column(idx.to_string())),
296        };
297
298        let buf = self.ranges[idx].clone().map(|r| &self.body.buffer()[r]);
299        FromSql::from_sql_nullable(&Type::TEXT, buf).map_err(|e| Error::from_sql(e, idx))
300    }
301}
302
303#[cfg(test)]
304mod test {
305    use bytes::BytesMut;
306    use postgres_protocol::message::backend::{DataRowBody, Message};
307
308    use super::*;
309
310    fn data_row(field_count: u16, fields: &[&[u8]]) -> DataRowBody {
311        let mut body = BytesMut::new();
312        body.extend_from_slice(&field_count.to_be_bytes());
313        for field in fields {
314            body.extend_from_slice(&(field.len() as i32).to_be_bytes());
315            body.extend_from_slice(field);
316        }
317
318        let mut buf = BytesMut::new();
319        buf.extend_from_slice(b"D");
320        buf.extend_from_slice(&(body.len() as i32 + 4).to_be_bytes());
321        buf.extend_from_slice(&body);
322
323        match Message::parse(&mut buf).unwrap().unwrap() {
324            Message::DataRow(body) => body,
325            _ => unreachable!("expected DataRow"),
326        }
327    }
328
329    fn column(name: &str) -> Column {
330        Column {
331            name: name.to_string(),
332            table_oid: None,
333            column_id: None,
334            type_modifier: 0,
335            r#type: Type::TEXT,
336        }
337    }
338
339    #[test]
340    fn fewer_data_row_fields_than_columns_is_rejected() {
341        // a server advertising two columns but sending a DataRow with a single
342        // field would make column accessors index out of bounds and panic.
343        let body = data_row(1, &[b""]);
344        let statement = Statement::unnamed(vec![], vec![column("a"), column("b")]);
345        assert!(Row::new(statement, body).is_err());
346    }
347
348    #[test]
349    fn matching_data_row_field_count_is_accepted() {
350        let body = data_row(2, &[b"x", b"y"]);
351        let statement = Statement::unnamed(vec![], vec![column("a"), column("b")]);
352        assert!(Row::new(statement, body).is_ok());
353    }
354}