//! Rows. use crate::row::sealed::{AsName, Sealed}; use crate::statement::Column; use crate::types::{FromSql, Type, WrongType}; use crate::{Error, Statement}; use fallible_iterator::FallibleIterator; use postgres_protocol::message::backend::DataRowBody; use std::fmt; use std::ops::Range; use std::str; use std::sync::Arc; mod sealed { pub trait Sealed {} pub trait AsName { fn as_name(&self) -> &str; } } impl AsName for Column { fn as_name(&self) -> &str { self.name() } } impl AsName for String { fn as_name(&self) -> &str { self } } /// A trait implemented by types that can index into columns of a row. /// /// This cannot be implemented outside of this crate. pub trait RowIndex: Sealed { #[doc(hidden)] fn __idx(&self, columns: &[T]) -> Option where T: AsName; } impl Sealed for usize {} impl RowIndex for usize { #[inline] fn __idx(&self, columns: &[T]) -> Option where T: AsName, { if *self >= columns.len() { None } else { Some(*self) } } } impl Sealed for str {} impl RowIndex for str { #[inline] fn __idx(&self, columns: &[T]) -> Option where T: AsName, { if let Some(idx) = columns.iter().position(|d| d.as_name() == self) { return Some(idx); }; // FIXME ASCII-only case insensitivity isn't really the right thing to // do. Postgres itself uses a dubious wrapper around tolower and JDBC // uses the US locale. columns .iter() .position(|d| d.as_name().eq_ignore_ascii_case(self)) } } impl<'a, T> Sealed for &'a T where T: ?Sized + Sealed {} impl<'a, T> RowIndex for &'a T where T: ?Sized + RowIndex, { #[inline] fn __idx(&self, columns: &[U]) -> Option where U: AsName, { T::__idx(*self, columns) } } /// A row of data returned from the database by a query. pub struct Row { statement: Statement, body: DataRowBody, ranges: Vec>>, } impl fmt::Debug for Row { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Row") .field("columns", &self.columns()) .finish() } } impl Row { pub(crate) fn new(statement: Statement, body: DataRowBody) -> Result { let ranges = body.ranges().collect().map_err(Error::parse)?; Ok(Row { statement, body, ranges, }) } /// Returns information about the columns of data in the row. pub fn columns(&self) -> &[Column] { self.statement.columns() } /// Determines if the row contains no values. pub fn is_empty(&self) -> bool { self.len() == 0 } /// Returns the number of values in the row. pub fn len(&self) -> usize { self.columns().len() } /// Deserializes a value from the row. /// /// The value can be specified either by its numeric index in the row, or by its column name. /// /// # Panics /// /// Panics if the index is out of bounds or if the value cannot be converted to the specified type. pub fn get<'a, I, T>(&'a self, idx: I) -> T where I: RowIndex + fmt::Display, T: FromSql<'a>, { match self.get_inner(&idx) { Ok(ok) => ok, Err(err) => panic!("error retrieving column {}: {}", idx, err), } } /// Like `Row::get`, but returns a `Result` rather than panicking. pub fn try_get<'a, I, T>(&'a self, idx: I) -> Result where I: RowIndex + fmt::Display, T: FromSql<'a>, { self.get_inner(&idx) } fn get_inner<'a, I, T>(&'a self, idx: &I) -> Result where I: RowIndex + fmt::Display, T: FromSql<'a>, { let idx = match idx.__idx(self.columns()) { Some(idx) => idx, None => return Err(Error::column(idx.to_string())), }; let ty = self.columns()[idx].type_(); if !T::accepts(ty) { return Err(Error::from_sql( Box::new(WrongType::new::(ty.clone())), idx, )); } FromSql::from_sql_nullable(ty, self.col_buffer(idx)).map_err(|e| Error::from_sql(e, idx)) } /// Get the raw bytes for the column at the given index. fn col_buffer(&self, idx: usize) -> Option<&[u8]> { let range = self.ranges[idx].to_owned()?; Some(&self.body.buffer()[range]) } } /// A row of data returned from the database by a simple query. pub struct SimpleQueryRow { columns: Arc<[String]>, body: DataRowBody, ranges: Vec>>, } impl SimpleQueryRow { #[allow(clippy::new_ret_no_self)] pub(crate) fn new(columns: Arc<[String]>, body: DataRowBody) -> Result { let ranges = body.ranges().collect().map_err(Error::parse)?; Ok(SimpleQueryRow { columns, body, ranges, }) } /// Determines if the row contains no values. pub fn is_empty(&self) -> bool { self.len() == 0 } /// Returns the number of values in the row. pub fn len(&self) -> usize { self.columns.len() } /// Returns a value from the row. /// /// The value can be specified either by its numeric index in the row, or by its column name. /// /// # Panics /// /// Panics if the index is out of bounds or if the value cannot be converted to the specified type. pub fn get(&self, idx: I) -> Option<&str> where I: RowIndex + fmt::Display, { match self.get_inner(&idx) { Ok(ok) => ok, Err(err) => panic!("error retrieving column {}: {}", idx, err), } } /// Like `SimpleQueryRow::get`, but returns a `Result` rather than panicking. pub fn try_get(&self, idx: I) -> Result, Error> where I: RowIndex + fmt::Display, { self.get_inner(&idx) } fn get_inner(&self, idx: &I) -> Result, Error> where I: RowIndex + fmt::Display, { let idx = match idx.__idx(&self.columns) { Some(idx) => idx, None => return Err(Error::column(idx.to_string())), }; let buf = self.ranges[idx].clone().map(|r| &self.body.buffer()[r]); FromSql::from_sql_nullable(&Type::TEXT, buf).map_err(|e| Error::from_sql(e, idx)) } }