From 092f03828893434e5f7a27b1272663b8d4555716 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Fri, 29 May 2015 22:54:10 -0700 Subject: [PATCH] Pull rows to their own module --- src/lib.rs | 362 +++----------------------------------------------- src/rows.rs | 374 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 394 insertions(+), 342 deletions(-) create mode 100644 src/rows.rs diff --git a/src/lib.rs b/src/lib.rs index 4bc8d3f5..41be68f7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -61,7 +61,7 @@ use crypto::digest::Digest; use crypto::md5::Md5; use debug_builders::DebugStruct; use std::ascii::AsciiExt; -use std::borrow::{ToOwned, Cow}; +use std::borrow::ToOwned; use std::cell::{Cell, RefCell}; use std::collections::{VecDeque, HashMap}; use std::fmt; @@ -69,15 +69,12 @@ use std::iter::IntoIterator; use std::io as std_io; use std::io::prelude::*; use std::mem; -use std::slice; use std::result; -use std::vec; #[cfg(feature = "unix_socket")] use std::path::PathBuf; use error::{Error, ConnectError, SqlState, DbError}; -#[doc(inline)] -pub use types::{ToSql, FromSql}; +use types::{ToSql, FromSql}; use io::{StreamWrapper, NegotiateSsl}; use types::{IsNull, Kind, Type, SessionInfo, Oid, Other}; use message::BackendMessage::*; @@ -85,6 +82,7 @@ use message::FrontendMessage::*; use message::{FrontendMessage, BackendMessage, RowDescriptionEntry}; use message::{WriteMessage, ReadMessage}; use url::Url; +use rows::{Rows, LazyRows}; #[macro_use] mod macros; @@ -96,6 +94,7 @@ mod priv_io; mod url; mod util; pub mod types; +pub mod rows; const TYPEINFO_QUERY: &'static str = "t"; @@ -1544,10 +1543,7 @@ impl<'conn> Statement<'conn> { pub fn query<'a>(&'a self, params: &[&ToSql]) -> Result> { check_desync!(self.conn); self.inner_query("", 0, params).map(|(buf, _)| { - Rows { - stmt: self, - data: buf.into_iter().collect() - } + Rows::new(self, buf.into_iter().collect()) }) } @@ -1588,15 +1584,7 @@ impl<'conn> Statement<'conn> { let portal_name = format!("{}p{}", self.name, id); self.inner_query(&portal_name, row_limit, params).map(move |(data, more_rows)| { - LazyRows { - _trans: trans, - stmt: self, - data: data, - name: portal_name, - row_limit: row_limit, - more_rows: more_rows, - finished: false, - } + LazyRows::new(self, data, portal_name, row_limit, more_rows, false, trans) }) } @@ -1745,330 +1733,6 @@ fn read_rows(conn: &mut InnerConnection, buf: &mut VecDeque>> Ok(more_rows) } -/// The resulting rows of a query. -pub struct Rows<'stmt> { - stmt: &'stmt Statement<'stmt>, - data: Vec>>>, -} - -impl<'a> fmt::Debug for Rows<'a> { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - DebugStruct::new(fmt, "Rows") - .field("columns", &self.columns()) - .field("rows", &self.data.len()) - .finish() - } -} - -impl<'stmt> Rows<'stmt> { - /// Returns a slice describing the columns of the `Rows`. - pub fn columns(&self) -> &'stmt [Column] { - self.stmt.columns() - } - - /// Returns an iterator over the `Row`s. - pub fn iter<'a>(&'a self) -> RowsIter<'a> { - RowsIter { - stmt: self.stmt, - iter: self.data.iter() - } - } -} - -impl<'a> IntoIterator for &'a Rows<'a> { - type Item = Row<'a>; - type IntoIter = RowsIter<'a>; - - fn into_iter(self) -> RowsIter<'a> { - self.iter() - } -} - -impl<'stmt> IntoIterator for Rows<'stmt> { - type Item = Row<'stmt>; - type IntoIter = RowsIntoIter<'stmt>; - - fn into_iter(self) -> RowsIntoIter<'stmt> { - RowsIntoIter { - stmt: self.stmt, - iter: self.data.into_iter() - } - } -} - -/// An iterator over `Row`s. -pub struct RowsIter<'a> { - stmt: &'a Statement<'a>, - iter: slice::Iter<'a, Vec>>>, -} - -impl<'a> Iterator for RowsIter<'a> { - type Item = Row<'a>; - - fn next(&mut self) -> Option> { - self.iter.next().map(|row| { - Row { - stmt: self.stmt, - data: Cow::Borrowed(row), - } - }) - } - - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -impl<'a> DoubleEndedIterator for RowsIter<'a> { - fn next_back(&mut self) -> Option> { - self.iter.next_back().map(|row| { - Row { - stmt: self.stmt, - data: Cow::Borrowed(row), - } - }) - } -} - -impl<'a> ExactSizeIterator for RowsIter<'a> {} - -/// An owning iterator over `Row`s. -pub struct RowsIntoIter<'stmt> { - stmt: &'stmt Statement<'stmt>, - iter: vec::IntoIter>>>, -} - -impl<'stmt> Iterator for RowsIntoIter<'stmt> { - type Item = Row<'stmt>; - - fn next(&mut self) -> Option> { - self.iter.next().map(|row| { - Row { - stmt: self.stmt, - data: Cow::Owned(row), - } - }) - } - - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -impl<'stmt> DoubleEndedIterator for RowsIntoIter<'stmt> { - fn next_back(&mut self) -> Option> { - self.iter.next_back().map(|row| { - Row { - stmt: self.stmt, - data: Cow::Owned(row), - } - }) - } -} - -impl<'stmt> ExactSizeIterator for RowsIntoIter<'stmt> {} - -/// A single result row of a query. -pub struct Row<'a> { - stmt: &'a Statement<'a>, - data: Cow<'a, [Option>]> -} - -impl<'a> fmt::Debug for Row<'a> { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - DebugStruct::new(fmt, "Row") - .field("statement", self.stmt) - .finish() - } -} - -impl<'a> Row<'a> { - /// Returns the number of values in the row. - pub fn len(&self) -> usize { - self.data.len() - } - - /// Returns a slice describing the columns of the `Row`. - pub fn columns(&self) -> &'a [Column] { - self.stmt.columns() - } - - /// Retrieves the contents of a field of the row. - /// - /// A field can be accessed by the name or index of its column, though - /// access by index is more efficient. Rows are 0-indexed. - /// - /// Returns an `Error` value if the index does not reference a column or - /// the return type is not compatible with the Postgres type. - pub fn get_opt(&self, idx: I) -> Result where I: RowIndex, T: FromSql { - let idx = try!(idx.idx(self.stmt).ok_or(Error::InvalidColumn)); - let ty = &self.stmt.columns[idx].type_; - if !::accepts(ty) { - return Err(Error::WrongType(ty.clone())); - } - let conn = self.stmt.conn.conn.borrow(); - FromSql::from_sql_nullable(ty, self.data[idx].as_ref().map(|e| &**e).as_mut(), - &SessionInfo::new(&*conn)) - } - - /// Retrieves the contents of a field of the row. - /// - /// A field can be accessed by the name or index of its column, though - /// access by index is more efficient. Rows are 0-indexed. - /// - /// ## Panics - /// - /// Panics if the index does not reference a column or the return type is - /// not compatible with the Postgres type. - /// - /// ## Example - /// - /// ```rust,no_run - /// # use postgres::{Connection, SslMode}; - /// # let conn = Connection::connect("", &SslMode::None).unwrap(); - /// # let stmt = conn.prepare("").unwrap(); - /// # let mut result = stmt.query(&[]).unwrap(); - /// # let row = result.iter().next().unwrap(); - /// let foo: i32 = row.get(0); - /// let bar: String = row.get("bar"); - /// ``` - pub fn get(&self, idx: I) -> T where I: RowIndex + fmt::Debug + Clone, T: FromSql { - match self.get_opt(idx.clone()) { - Ok(ok) => ok, - Err(err) => panic!("error retrieving column {:?}: {:?}", idx, err) - } - } - - /// Retrieves the specified field as a raw buffer of Postgres data. - /// - /// ## Panics - /// - /// Panics if the index does not reference a column. - pub fn get_bytes(&self, idx: I) -> Option<&[u8]> where I: RowIndex + fmt::Debug { - match idx.idx(self.stmt) { - Some(idx) => self.data[idx].as_ref().map(|e| &**e), - None => panic!("invalid index {:?}", idx), - } - } -} - -/// A trait implemented by types that can index into columns of a row. -pub trait RowIndex { - /// Returns the index of the appropriate column, or `None` if no such - /// column exists. - fn idx(&self, stmt: &Statement) -> Option; -} - -impl RowIndex for usize { - #[inline] - fn idx(&self, stmt: &Statement) -> Option { - if *self >= stmt.columns.len() { - None - } else { - Some(*self) - } - } -} - -impl<'a> RowIndex for &'a str { - #[inline] - fn idx(&self, stmt: &Statement) -> Option { - stmt.columns().iter().position(|d| d.name == *self) - } -} - -/// A lazily-loaded iterator over the resulting rows of a query. -pub struct LazyRows<'trans, 'stmt> { - stmt: &'stmt Statement<'stmt>, - data: VecDeque>>>, - name: String, - row_limit: i32, - more_rows: bool, - finished: bool, - _trans: &'trans Transaction<'trans>, -} - -impl<'a, 'b> Drop for LazyRows<'a, 'b> { - fn drop(&mut self) { - if !self.finished { - let _ = self.finish_inner(); - } - } -} - -impl<'a, 'b> fmt::Debug for LazyRows<'a, 'b> { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - DebugStruct::new(fmt, "LazyRows") - .field("name", &self.name) - .field("row_limit", &self.row_limit) - .field("remaining_rows", &self.data.len()) - .field("more_rows", &self.more_rows) - .finish() - } -} - -impl<'trans, 'stmt> LazyRows<'trans, 'stmt> { - fn finish_inner(&mut self) -> Result<()> { - let mut conn = self.stmt.conn.conn.borrow_mut(); - check_desync!(conn); - conn.close_statement(&self.name, b'P') - } - - fn execute(&mut self) -> Result<()> { - let mut conn = self.stmt.conn.conn.borrow_mut(); - - try!(conn.write_messages(&[ - Execute { - portal: &self.name, - max_rows: self.row_limit - }, - Sync])); - read_rows(&mut conn, &mut self.data).map(|more_rows| self.more_rows = more_rows) - } - - /// Returns a slice describing the columns of the `LazyRows`. - pub fn columns(&self) -> &'stmt [Column] { - self.stmt.columns() - } - - /// Consumes the `LazyRows`, cleaning up associated state. - /// - /// Functionally identical to the `Drop` implementation on `LazyRows` - /// except that it returns any error to the caller. - pub fn finish(mut self) -> Result<()> { - self.finish_inner() - } -} - -impl<'trans, 'stmt> Iterator for LazyRows<'trans, 'stmt> { - type Item = Result>; - - fn next(&mut self) -> Option>> { - if self.data.is_empty() && self.more_rows { - if let Err(err) = self.execute() { - return Some(Err(err)); - } - } - - self.data.pop_front().map(|r| { - Ok(Row { - stmt: self.stmt, - data: Cow::Owned(r), - }) - }) - } - - fn size_hint(&self) -> (usize, Option) { - let lower = self.data.len(); - let upper = if self.more_rows { - None - } else { - Some(lower) - }; - (lower, upper) - } -} - /// A trait allowing abstraction over connections and transactions pub trait GenericConnection { /// Like `Connection::prepare`. @@ -2155,3 +1819,17 @@ trait DbErrorNew { trait TypeNew { fn new(oid: Oid) -> Option; } + +trait RowsNew<'a> { + fn new(stmt: &'a Statement<'a>, data: Vec>>>) -> Rows<'a>; +} + +trait LazyRowsNew<'trans, 'stmt> { + fn new(stmt: &'stmt Statement<'stmt>, + data: VecDeque>>>, + name: String, + row_limit: i32, + more_rows: bool, + finished: bool, + trans: &'trans Transaction<'trans>) -> LazyRows<'trans, 'stmt>; +} diff --git a/src/rows.rs b/src/rows.rs new file mode 100644 index 00000000..36148db5 --- /dev/null +++ b/src/rows.rs @@ -0,0 +1,374 @@ +//! Query result rows. + +use std::fmt; +use std::collections::VecDeque; +use debug_builders::DebugStruct; +use std::borrow::Cow; +use std::slice; +use std::vec; + +use {Statement, + Column, + Result, + Transaction, + read_rows, + DbErrorNew, + SessionInfoNew, + RowsNew, + LazyRowsNew}; +use types::{FromSql, SessionInfo}; +use error::Error; +use message::FrontendMessage::*; + +/// The resulting rows of a query. +pub struct Rows<'stmt> { + stmt: &'stmt Statement<'stmt>, + data: Vec>>>, +} + +impl<'a> RowsNew<'a> for Rows<'a> { + fn new(stmt: &'a Statement<'a>, data: Vec>>>) -> Rows<'a> { + Rows { + stmt: stmt, + data: data, + } + } +} + +impl<'a> fmt::Debug for Rows<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + DebugStruct::new(fmt, "Rows") + .field("columns", &self.columns()) + .field("rows", &self.data.len()) + .finish() + } +} + +impl<'stmt> Rows<'stmt> { + /// Returns a slice describing the columns of the `Rows`. + pub fn columns(&self) -> &'stmt [Column] { + self.stmt.columns() + } + + /// Returns an iterator over the `Row`s. + pub fn iter<'a>(&'a self) -> Iter<'a> { + Iter { + stmt: self.stmt, + iter: self.data.iter() + } + } +} + +impl<'a> IntoIterator for &'a Rows<'a> { + type Item = Row<'a>; + type IntoIter = Iter<'a>; + + fn into_iter(self) -> Iter<'a> { + self.iter() + } +} + +impl<'stmt> IntoIterator for Rows<'stmt> { + type Item = Row<'stmt>; + type IntoIter = IntoIter<'stmt>; + + fn into_iter(self) -> IntoIter<'stmt> { + IntoIter { + stmt: self.stmt, + iter: self.data.into_iter() + } + } +} + +/// An iterator over `Row`s. +pub struct Iter<'a> { + stmt: &'a Statement<'a>, + iter: slice::Iter<'a, Vec>>>, +} + +impl<'a> Iterator for Iter<'a> { + type Item = Row<'a>; + + fn next(&mut self) -> Option> { + self.iter.next().map(|row| { + Row { + stmt: self.stmt, + data: Cow::Borrowed(row), + } + }) + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +impl<'a> DoubleEndedIterator for Iter<'a> { + fn next_back(&mut self) -> Option> { + self.iter.next_back().map(|row| { + Row { + stmt: self.stmt, + data: Cow::Borrowed(row), + } + }) + } +} + +impl<'a> ExactSizeIterator for Iter<'a> {} + +/// An owning iterator over `Row`s. +pub struct IntoIter<'stmt> { + stmt: &'stmt Statement<'stmt>, + iter: vec::IntoIter>>>, +} + +impl<'stmt> Iterator for IntoIter<'stmt> { + type Item = Row<'stmt>; + + fn next(&mut self) -> Option> { + self.iter.next().map(|row| { + Row { + stmt: self.stmt, + data: Cow::Owned(row), + } + }) + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +impl<'stmt> DoubleEndedIterator for IntoIter<'stmt> { + fn next_back(&mut self) -> Option> { + self.iter.next_back().map(|row| { + Row { + stmt: self.stmt, + data: Cow::Owned(row), + } + }) + } +} + +impl<'stmt> ExactSizeIterator for IntoIter<'stmt> {} + +/// A single result row of a query. +pub struct Row<'a> { + stmt: &'a Statement<'a>, + data: Cow<'a, [Option>]> +} + +impl<'a> fmt::Debug for Row<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + DebugStruct::new(fmt, "Row") + .field("statement", self.stmt) + .finish() + } +} + +impl<'a> Row<'a> { + /// Returns the number of values in the row. + pub fn len(&self) -> usize { + self.data.len() + } + + /// Returns a slice describing the columns of the `Row`. + pub fn columns(&self) -> &'a [Column] { + self.stmt.columns() + } + + /// Retrieves the contents of a field of the row. + /// + /// A field can be accessed by the name or index of its column, though + /// access by index is more efficient. Rows are 0-indexed. + /// + /// Returns an `Error` value if the index does not reference a column or + /// the return type is not compatible with the Postgres type. + pub fn get_opt(&self, idx: I) -> Result where I: RowIndex, T: FromSql { + let idx = try!(idx.idx(self.stmt).ok_or(Error::InvalidColumn)); + let ty = &self.stmt.columns[idx].type_; + if !::accepts(ty) { + return Err(Error::WrongType(ty.clone())); + } + let conn = self.stmt.conn.conn.borrow(); + FromSql::from_sql_nullable(ty, self.data[idx].as_ref().map(|e| &**e).as_mut(), + &SessionInfo::new(&*conn)) + } + + /// Retrieves the contents of a field of the row. + /// + /// A field can be accessed by the name or index of its column, though + /// access by index is more efficient. Rows are 0-indexed. + /// + /// ## Panics + /// + /// Panics if the index does not reference a column or the return type is + /// not compatible with the Postgres type. + /// + /// ## Example + /// + /// ```rust,no_run + /// # use postgres::{Connection, SslMode}; + /// # let conn = Connection::connect("", &SslMode::None).unwrap(); + /// # let stmt = conn.prepare("").unwrap(); + /// # let mut result = stmt.query(&[]).unwrap(); + /// # let row = result.iter().next().unwrap(); + /// let foo: i32 = row.get(0); + /// let bar: String = row.get("bar"); + /// ``` + pub fn get(&self, idx: I) -> T where I: RowIndex + fmt::Debug + Clone, T: FromSql { + match self.get_opt(idx.clone()) { + Ok(ok) => ok, + Err(err) => panic!("error retrieving column {:?}: {:?}", idx, err) + } + } + + /// Retrieves the specified field as a raw buffer of Postgres data. + /// + /// ## Panics + /// + /// Panics if the index does not reference a column. + pub fn get_bytes(&self, idx: I) -> Option<&[u8]> where I: RowIndex + fmt::Debug { + match idx.idx(self.stmt) { + Some(idx) => self.data[idx].as_ref().map(|e| &**e), + None => panic!("invalid index {:?}", idx), + } + } +} + +/// A trait implemented by types that can index into columns of a row. +pub trait RowIndex { + /// Returns the index of the appropriate column, or `None` if no such + /// column exists. + fn idx(&self, stmt: &Statement) -> Option; +} + +impl RowIndex for usize { + #[inline] + fn idx(&self, stmt: &Statement) -> Option { + if *self >= stmt.columns.len() { + None + } else { + Some(*self) + } + } +} + +impl<'a> RowIndex for &'a str { + #[inline] + fn idx(&self, stmt: &Statement) -> Option { + stmt.columns().iter().position(|d| d.name == *self) + } +} + +/// A lazily-loaded iterator over the resulting rows of a query. +pub struct LazyRows<'trans, 'stmt> { + stmt: &'stmt Statement<'stmt>, + data: VecDeque>>>, + name: String, + row_limit: i32, + more_rows: bool, + finished: bool, + _trans: &'trans Transaction<'trans>, +} + +impl<'trans, 'stmt> LazyRowsNew<'trans, 'stmt> for LazyRows<'trans, 'stmt> { + fn new(stmt: &'stmt Statement<'stmt>, + data: VecDeque>>>, + name: String, + row_limit: i32, + more_rows: bool, + finished: bool, + trans: &'trans Transaction<'trans>) -> LazyRows<'trans, 'stmt> { + LazyRows { + stmt: stmt, + data: data, + name: name, + row_limit: row_limit, + more_rows: more_rows, + finished: finished, + _trans: trans + } + } +} + +impl<'a, 'b> Drop for LazyRows<'a, 'b> { + fn drop(&mut self) { + if !self.finished { + let _ = self.finish_inner(); + } + } +} + +impl<'a, 'b> fmt::Debug for LazyRows<'a, 'b> { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + DebugStruct::new(fmt, "LazyRows") + .field("name", &self.name) + .field("row_limit", &self.row_limit) + .field("remaining_rows", &self.data.len()) + .field("more_rows", &self.more_rows) + .finish() + } +} + +impl<'trans, 'stmt> LazyRows<'trans, 'stmt> { + fn finish_inner(&mut self) -> Result<()> { + let mut conn = self.stmt.conn.conn.borrow_mut(); + check_desync!(conn); + conn.close_statement(&self.name, b'P') + } + + fn execute(&mut self) -> Result<()> { + let mut conn = self.stmt.conn.conn.borrow_mut(); + + try!(conn.write_messages(&[ + Execute { + portal: &self.name, + max_rows: self.row_limit + }, + Sync])); + read_rows(&mut conn, &mut self.data).map(|more_rows| self.more_rows = more_rows) + } + + /// Returns a slice describing the columns of the `LazyRows`. + pub fn columns(&self) -> &'stmt [Column] { + self.stmt.columns() + } + + /// Consumes the `LazyRows`, cleaning up associated state. + /// + /// Functionally identical to the `Drop` implementation on `LazyRows` + /// except that it returns any error to the caller. + pub fn finish(mut self) -> Result<()> { + self.finish_inner() + } +} + +impl<'trans, 'stmt> Iterator for LazyRows<'trans, 'stmt> { + type Item = Result>; + + fn next(&mut self) -> Option>> { + if self.data.is_empty() && self.more_rows { + if let Err(err) = self.execute() { + return Some(Err(err)); + } + } + + self.data.pop_front().map(|r| { + Ok(Row { + stmt: self.stmt, + data: Cow::Owned(r), + }) + }) + } + + fn size_hint(&self) -> (usize, Option) { + let lower = self.data.len(); + let upper = if self.more_rows { + None + } else { + Some(lower) + }; + (lower, upper) + } +}