Allow FromSql to borrow from the buffer

This allows for in-place deserialization of text and bytea values in
particular.

Row::get_bytes is removed since it previously existed for this use case.

Closes #281
This commit is contained in:
Steven Fackler 2018-04-22 13:03:26 -07:00
parent 520aacab7e
commit fcbed9175b
21 changed files with 201 additions and 194 deletions

View File

@ -23,7 +23,7 @@ jobs:
build:
working_directory: ~/build
docker:
- image: rust:1.21.0
- image: rust:1.23.0
environment:
RUSTFLAGS: -D warnings
- image: sfackler/rust-postgres-test:3

View File

@ -3,8 +3,6 @@ extern crate marksman_escape;
extern crate phf_codegen;
extern crate regex;
#[allow(unused_imports)]
use std::ascii::AsciiExt;
use std::path::Path;
mod sqlstate;

View File

@ -1,11 +1,9 @@
use marksman_escape::Escape;
use regex::Regex;
#[allow(unused_imports)]
use std::ascii::AsciiExt;
use std::collections::BTreeMap;
use std::fs::File;
use std::io::{BufWriter, Write};
use std::path::Path;
use marksman_escape::Escape;
use snake_to_camel;
@ -178,8 +176,7 @@ fn make_impl(w: &mut BufWriter<File>, types: &BTreeMap<u32, Type>) {
w,
" {} => Some(Inner::{}),
",
oid,
type_.variant
oid, type_.variant
).unwrap();
}
@ -194,14 +191,12 @@ fn make_impl(w: &mut BufWriter<File>, types: &BTreeMap<u32, Type>) {
",
).unwrap();
for (oid, type_) in types {
write!(
w,
" Inner::{} => {},
",
type_.variant,
oid
type_.variant, oid
).unwrap();
}
@ -231,8 +226,7 @@ fn make_impl(w: &mut BufWriter<File>, types: &BTreeMap<u32, Type>) {
V
}}
",
type_.variant,
kind
type_.variant, kind
).unwrap();
}
@ -252,8 +246,7 @@ fn make_impl(w: &mut BufWriter<File>, types: &BTreeMap<u32, Type>) {
w,
r#" Inner::{} => "{}",
"#,
type_.variant,
type_.name
type_.variant, type_.name
).unwrap();
}

View File

@ -1,12 +1,10 @@
use fallible_iterator::FallibleIterator;
use postgres_protocol::message::backend::DataRowBody;
#[allow(unused_imports)]
use std::ascii::AsciiExt;
use std::io;
use std::ops::Range;
use stmt::Column;
use rows::sealed::Sealed;
use stmt::Column;
mod sealed {
use stmt::Column;

View File

@ -1,12 +1,12 @@
extern crate bit_vec;
use postgres_protocol::types;
use self::bit_vec::BitVec;
use postgres_protocol::types;
use std::error::Error;
use types::{FromSql, IsNull, ToSql, Type, BIT, VARBIT};
impl FromSql for BitVec {
impl<'a> FromSql<'a> for BitVec {
fn from_sql(_: &Type, raw: &[u8]) -> Result<BitVec, Box<Error + Sync + Send>> {
let varbit = types::varbit_from_sql(raw)?;
let mut bitvec = BitVec::from_bytes(varbit.bytes());

View File

@ -1,8 +1,8 @@
extern crate chrono;
use postgres_protocol::types;
use self::chrono::{DateTime, Duration, FixedOffset, Local, NaiveDate, NaiveDateTime, NaiveTime,
Utc};
use postgres_protocol::types;
use std::error::Error;
use types::{FromSql, IsNull, ToSql, Type, DATE, TIME, TIMESTAMP, TIMESTAMPTZ};
@ -11,7 +11,7 @@ fn base() -> NaiveDateTime {
NaiveDate::from_ymd(2000, 1, 1).and_hms(0, 0, 0)
}
impl FromSql for NaiveDateTime {
impl<'a> FromSql<'a> for NaiveDateTime {
fn from_sql(_: &Type, raw: &[u8]) -> Result<NaiveDateTime, Box<Error + Sync + Send>> {
let t = types::timestamp_from_sql(raw)?;
Ok(base() + Duration::microseconds(t))
@ -34,7 +34,7 @@ impl ToSql for NaiveDateTime {
to_sql_checked!();
}
impl FromSql for DateTime<Utc> {
impl<'a> FromSql<'a> for DateTime<Utc> {
fn from_sql(type_: &Type, raw: &[u8]) -> Result<DateTime<Utc>, Box<Error + Sync + Send>> {
let naive = NaiveDateTime::from_sql(type_, raw)?;
Ok(DateTime::from_utc(naive, Utc))
@ -52,7 +52,7 @@ impl ToSql for DateTime<Utc> {
to_sql_checked!();
}
impl FromSql for DateTime<Local> {
impl<'a> FromSql<'a> for DateTime<Local> {
fn from_sql(type_: &Type, raw: &[u8]) -> Result<DateTime<Local>, Box<Error + Sync + Send>> {
let utc = DateTime::<Utc>::from_sql(type_, raw)?;
Ok(utc.with_timezone(&Local))
@ -70,7 +70,7 @@ impl ToSql for DateTime<Local> {
to_sql_checked!();
}
impl FromSql for DateTime<FixedOffset> {
impl<'a> FromSql<'a> for DateTime<FixedOffset> {
fn from_sql(
type_: &Type,
raw: &[u8],
@ -91,7 +91,7 @@ impl ToSql for DateTime<FixedOffset> {
to_sql_checked!();
}
impl FromSql for NaiveDate {
impl<'a> FromSql<'a> for NaiveDate {
fn from_sql(_: &Type, raw: &[u8]) -> Result<NaiveDate, Box<Error + Sync + Send>> {
let jd = types::date_from_sql(raw)?;
Ok(base().date() + Duration::days(jd as i64))
@ -115,7 +115,7 @@ impl ToSql for NaiveDate {
to_sql_checked!();
}
impl FromSql for NaiveTime {
impl<'a> FromSql<'a> for NaiveTime {
fn from_sql(_: &Type, raw: &[u8]) -> Result<NaiveTime, Box<Error + Sync + Send>> {
let usec = types::time_from_sql(raw)?;
Ok(NaiveTime::from_hms(0, 0, 0) + Duration::microseconds(usec))

View File

@ -1,12 +1,12 @@
extern crate eui48;
use self::eui48::MacAddress;
use std::error::Error;
use postgres_protocol::types;
use std::error::Error;
use types::{FromSql, ToSql, Type, IsNull, MACADDR};
use types::{FromSql, IsNull, ToSql, Type, MACADDR};
impl FromSql for MacAddress {
impl<'a> FromSql<'a> for MacAddress {
fn from_sql(_: &Type, raw: &[u8]) -> Result<MacAddress, Box<Error + Sync + Send>> {
let bytes = types::macaddr_from_sql(raw)?;
Ok(MacAddress::new(bytes))

View File

@ -1,13 +1,13 @@
extern crate geo;
use postgres_protocol::types;
use self::geo::{Bbox, LineString, Point};
use std::error::Error;
use fallible_iterator::FallibleIterator;
use postgres_protocol::types;
use std::error::Error;
use types::{FromSql, ToSql, IsNull, Type, POINT, BOX, PATH};
use types::{FromSql, IsNull, ToSql, Type, BOX, PATH, POINT};
impl FromSql for Point<f64> {
impl<'a> FromSql<'a> for Point<f64> {
fn from_sql(_: &Type, raw: &[u8]) -> Result<Self, Box<Error + Sync + Send>> {
let point = types::point_from_sql(raw)?;
Ok(Point::new(point.x(), point.y()))
@ -26,7 +26,7 @@ impl ToSql for Point<f64> {
to_sql_checked!();
}
impl FromSql for Bbox<f64> {
impl<'a> FromSql<'a> for Bbox<f64> {
fn from_sql(_: &Type, raw: &[u8]) -> Result<Self, Box<Error + Sync + Send>> {
let bbox = types::box_from_sql(raw)?;
Ok(Bbox {
@ -50,7 +50,7 @@ impl ToSql for Bbox<f64> {
to_sql_checked!();
}
impl FromSql for LineString<f64> {
impl<'a> FromSql<'a> for LineString<f64> {
fn from_sql(_: &Type, raw: &[u8]) -> Result<Self, Box<Error + Sync + Send>> {
let path = types::path_from_sql(raw)?;
let points = path.points().map(|p| Point::new(p.x(), p.y())).collect()?;

View File

@ -14,8 +14,8 @@ use types::type_gen::{Inner, Other};
#[doc(inline)]
pub use postgres_protocol::Oid;
pub use types::type_gen::consts::*;
pub use types::special::{Date, Timestamp};
pub use types::type_gen::consts::*;
/// Generates a simple implementation of `ToSql::accepts` which accepts the
/// types passed to it.
@ -68,20 +68,20 @@ where
#[cfg(feature = "with-bit-vec")]
mod bit_vec;
#[cfg(feature = "with-uuid")]
mod uuid;
#[cfg(feature = "with-time")]
mod time;
#[cfg(feature = "with-rustc-serialize")]
mod rustc_serialize;
#[cfg(feature = "with-serde_json")]
mod serde_json;
#[cfg(feature = "with-chrono")]
mod chrono;
#[cfg(feature = "with-eui48")]
mod eui48;
#[cfg(feature = "with-geo")]
mod geo;
#[cfg(feature = "with-rustc-serialize")]
mod rustc_serialize;
#[cfg(feature = "with-serde_json")]
mod serde_json;
#[cfg(feature = "with-time")]
mod time;
#[cfg(feature = "with-uuid")]
mod uuid;
mod special;
mod type_gen;
@ -254,8 +254,8 @@ impl WrongType {
/// | `i64` | BIGINT, BIGSERIAL |
/// | `f32` | REAL |
/// | `f64` | DOUBLE PRECISION |
/// | `String` | VARCHAR, CHAR(n), TEXT, CITEXT, NAME, UNKNOWN |
/// | `Vec<u8>` | BYTEA |
/// | `&str`/`String` | VARCHAR, CHAR(n), TEXT, CITEXT, NAME, UNKNOWN |
/// | `&[u8]`/`Vec<u8>` | BYTEA |
/// | `HashMap<String, Option<String>>` | HSTORE |
///
/// In addition, some implementations are provided for types in third party
@ -290,13 +290,13 @@ impl WrongType {
///
/// `FromSql` is implemented for `Vec<T>` where `T` implements `FromSql`, and
/// corresponds to one-dimensional Postgres arrays.
pub trait FromSql: Sized {
pub trait FromSql<'a>: Sized {
/// Creates a new value of this type from a buffer of data of the specified
/// Postgres `Type` in its binary format.
///
/// The caller of this method is responsible for ensuring that this type
/// is compatible with the Postgres `Type`.
fn from_sql(ty: &Type, raw: &[u8]) -> Result<Self, Box<Error + Sync + Send>>;
fn from_sql(ty: &Type, raw: &'a [u8]) -> Result<Self, Box<Error + Sync + Send>>;
/// Creates a new value of this type from a `NULL` SQL value.
///
@ -312,7 +312,10 @@ pub trait FromSql: Sized {
/// A convenience function that delegates to `from_sql` and `from_sql_null` depending on the
/// value of `raw`.
fn from_sql_nullable(ty: &Type, raw: Option<&[u8]>) -> Result<Self, Box<Error + Sync + Send>> {
fn from_sql_nullable(
ty: &Type,
raw: Option<&'a [u8]>,
) -> Result<Self, Box<Error + Sync + Send>> {
match raw {
Some(raw) => Self::from_sql(ty, raw),
None => Self::from_sql_null(ty),
@ -324,8 +327,19 @@ pub trait FromSql: Sized {
fn accepts(ty: &Type) -> bool;
}
impl<T: FromSql> FromSql for Option<T> {
fn from_sql(ty: &Type, raw: &[u8]) -> Result<Option<T>, Box<Error + Sync + Send>> {
/// A trait for types which can be created from a Postgres value without borrowing any data.
///
/// This is primarily useful for trait bounds on functions.
pub trait FromSqlOwned: for<'a> FromSql<'a> {}
impl<T> FromSqlOwned for T
where
T: for<'a> FromSql<'a>,
{
}
impl<'a, T: FromSql<'a>> FromSql<'a> for Option<T> {
fn from_sql(ty: &Type, raw: &'a [u8]) -> Result<Option<T>, Box<Error + Sync + Send>> {
<T as FromSql>::from_sql(ty, raw).map(Some)
}
@ -338,8 +352,8 @@ impl<T: FromSql> FromSql for Option<T> {
}
}
impl<T: FromSql> FromSql for Vec<T> {
fn from_sql(ty: &Type, raw: &[u8]) -> Result<Vec<T>, Box<Error + Sync + Send>> {
impl<'a, T: FromSql<'a>> FromSql<'a> for Vec<T> {
fn from_sql(ty: &Type, raw: &'a [u8]) -> Result<Vec<T>, Box<Error + Sync + Send>> {
let member_type = match *ty.kind() {
Kind::Array(ref member) => member,
_ => panic!("expected array type"),
@ -364,19 +378,37 @@ impl<T: FromSql> FromSql for Vec<T> {
}
}
impl FromSql for Vec<u8> {
fn from_sql(_: &Type, raw: &[u8]) -> Result<Vec<u8>, Box<Error + Sync + Send>> {
impl<'a> FromSql<'a> for Vec<u8> {
fn from_sql(_: &Type, raw: &'a [u8]) -> Result<Vec<u8>, Box<Error + Sync + Send>> {
Ok(types::bytea_from_sql(raw).to_owned())
}
accepts!(BYTEA);
}
impl FromSql for String {
fn from_sql(_: &Type, raw: &[u8]) -> Result<String, Box<Error + Sync + Send>> {
impl<'a> FromSql<'a> for &'a [u8] {
fn from_sql(_: &Type, raw: &'a [u8]) -> Result<&'a [u8], Box<Error + Sync + Send>> {
Ok(types::bytea_from_sql(raw))
}
accepts!(BYTEA);
}
impl<'a> FromSql<'a> for String {
fn from_sql(_: &Type, raw: &'a [u8]) -> Result<String, Box<Error + Sync + Send>> {
types::text_from_sql(raw).map(|b| b.to_owned())
}
fn accepts(ty: &Type) -> bool {
<&str as FromSql>::accepts(ty)
}
}
impl<'a> FromSql<'a> for &'a str {
fn from_sql(_: &Type, raw: &'a [u8]) -> Result<&'a str, Box<Error + Sync + Send>> {
types::text_from_sql(raw)
}
fn accepts(ty: &Type) -> bool {
match *ty {
VARCHAR | TEXT | BPCHAR | NAME | UNKNOWN => true,
@ -388,10 +420,8 @@ impl FromSql for String {
macro_rules! simple_from {
($t:ty, $f:ident, $($expected:pat),+) => {
impl FromSql for $t {
fn from_sql(_: &Type,
raw: &[u8])
-> Result<$t, Box<Error + Sync + Send>> {
impl<'a> FromSql<'a> for $t {
fn from_sql(_: &Type, raw: &'a [u8]) -> Result<$t, Box<Error + Sync + Send>> {
types::$f(raw)
}
@ -409,10 +439,10 @@ simple_from!(i64, int8_from_sql, INT8);
simple_from!(f32, float4_from_sql, FLOAT4);
simple_from!(f64, float8_from_sql, FLOAT8);
impl FromSql for HashMap<String, Option<String>> {
impl<'a> FromSql<'a> for HashMap<String, Option<String>> {
fn from_sql(
_: &Type,
raw: &[u8],
raw: &'a [u8],
) -> Result<HashMap<String, Option<String>>, Box<Error + Sync + Send>> {
types::hstore_from_sql(raw)?
.map(|(k, v)| (k.to_owned(), v.map(str::to_owned)))
@ -449,10 +479,8 @@ pub enum IsNull {
/// | `i64` | BIGINT, BIGSERIAL |
/// | `f32` | REAL |
/// | `f64` | DOUBLE PRECISION |
/// | `String` | VARCHAR, CHAR(n), TEXT, CITEXT, NAME |
/// | `&str` | VARCHAR, CHAR(n), TEXT, CITEXT, NAME |
/// | `Vec<u8>` | BYTEA |
/// | `&[u8]` | BYTEA |
/// | `&str`/`String` | VARCHAR, CHAR(n), TEXT, CITEXT, NAME |
/// | `&[u8]`/Vec<u8>` | BYTEA |
/// | `HashMap<String, Option<String>>` | HSTORE |
///
/// In addition, some implementations are provided for types in third party

View File

@ -1,12 +1,12 @@
extern crate rustc_serialize;
use self::rustc_serialize::json;
use std::io::{Read, Write};
use std::error::Error;
use std::io::{Read, Write};
use types::{FromSql, IsNull, ToSql, Type, JSON, JSONB};
impl FromSql for json::Json {
impl<'a> FromSql<'a> for json::Json {
fn from_sql(ty: &Type, mut raw: &[u8]) -> Result<json::Json, Box<Error + Sync + Send>> {
if *ty == JSONB {
let mut b = [0; 1];

View File

@ -6,7 +6,7 @@ use std::io::{Read, Write};
use types::{FromSql, IsNull, ToSql, Type, JSON, JSONB};
impl FromSql for Value {
impl<'a> FromSql<'a> for Value {
fn from_sql(ty: &Type, mut raw: &[u8]) -> Result<Value, Box<Error + Sync + Send>> {
if *ty == JSONB {
let mut b = [0; 1];

View File

@ -1,8 +1,8 @@
use postgres_protocol::types;
use std::{i32, i64};
use std::error::Error;
use std::{i32, i64};
use types::{Type, FromSql, ToSql, IsNull, DATE, TIMESTAMP, TIMESTAMPTZ};
use types::{FromSql, IsNull, ToSql, Type, DATE, TIMESTAMP, TIMESTAMPTZ};
/// A wrapper that can be used to represent infinity with `Type::Date` types.
#[derive(Debug, Clone, Copy, PartialEq)]
@ -15,8 +15,8 @@ pub enum Date<T> {
Value(T),
}
impl<T: FromSql> FromSql for Date<T> {
fn from_sql(ty: &Type, raw: &[u8]) -> Result<Self, Box<Error + Sync + Send>> {
impl<'a, T: FromSql<'a>> FromSql<'a> for Date<T> {
fn from_sql(ty: &Type, raw: &'a [u8]) -> Result<Self, Box<Error + Sync + Send>> {
match types::date_from_sql(raw)? {
i32::MAX => Ok(Date::PosInfinity),
i32::MIN => Ok(Date::NegInfinity),
@ -28,6 +28,7 @@ impl<T: FromSql> FromSql for Date<T> {
*ty == DATE && T::accepts(ty)
}
}
impl<T: ToSql> ToSql for Date<T> {
fn to_sql(&self, ty: &Type, out: &mut Vec<u8>) -> Result<IsNull, Box<Error + Sync + Send>> {
let value = match *self {
@ -59,8 +60,8 @@ pub enum Timestamp<T> {
Value(T),
}
impl<T: FromSql> FromSql for Timestamp<T> {
fn from_sql(ty: &Type, raw: &[u8]) -> Result<Self, Box<Error + Sync + Send>> {
impl<'a, T: FromSql<'a>> FromSql<'a> for Timestamp<T> {
fn from_sql(ty: &Type, raw: &'a [u8]) -> Result<Self, Box<Error + Sync + Send>> {
match types::timestamp_from_sql(raw)? {
i64::MAX => Ok(Timestamp::PosInfinity),
i64::MIN => Ok(Timestamp::NegInfinity),
@ -71,7 +72,7 @@ impl<T: FromSql> FromSql for Timestamp<T> {
fn accepts(ty: &Type) -> bool {
match *ty {
TIMESTAMP | TIMESTAMPTZ if T::accepts(ty) => true,
_ => false
_ => false,
}
}
}
@ -91,7 +92,7 @@ impl<T: ToSql> ToSql for Timestamp<T> {
fn accepts(ty: &Type) -> bool {
match *ty {
TIMESTAMP | TIMESTAMPTZ if T::accepts(ty) => true,
_ => false
_ => false,
}
}

View File

@ -1,10 +1,10 @@
extern crate time;
use self::time::Timespec;
use std::error::Error;
use postgres_protocol::types;
use std::error::Error;
use types::{Type, FromSql, ToSql, IsNull, TIMESTAMP, TIMESTAMPTZ};
use types::{FromSql, IsNull, ToSql, Type, TIMESTAMP, TIMESTAMPTZ};
const USEC_PER_SEC: i64 = 1_000_000;
const NSEC_PER_USEC: i64 = 1_000;
@ -12,7 +12,7 @@ const NSEC_PER_USEC: i64 = 1_000;
// Number of seconds from 1970-01-01 to 2000-01-01
const TIME_SEC_CONVERSION: i64 = 946684800;
impl FromSql for Timespec {
impl<'a> FromSql<'a> for Timespec {
fn from_sql(_: &Type, raw: &[u8]) -> Result<Timespec, Box<Error + Sync + Send>> {
let t = types::timestamp_from_sql(raw)?;
let mut sec = t / USEC_PER_SEC + TIME_SEC_CONVERSION;

View File

@ -1,12 +1,12 @@
extern crate uuid;
use postgres_protocol::types;
use self::uuid::Uuid;
use postgres_protocol::types;
use std::error::Error;
use types::{FromSql, ToSql, Type, IsNull, UUID};
use types::{FromSql, IsNull, ToSql, Type, UUID};
impl FromSql for Uuid {
impl<'a> FromSql<'a> for Uuid {
fn from_sql(_: &Type, raw: &[u8]) -> Result<Uuid, Box<Error + Sync + Send>> {
let bytes = types::uuid_from_sql(raw)?;
Ok(Uuid::from_bytes(&bytes).unwrap())

View File

@ -80,6 +80,11 @@ extern crate postgres_shared;
extern crate socket2;
use fallible_iterator::FallibleIterator;
use postgres_protocol::authentication;
use postgres_protocol::authentication::sasl::{self, ScramSha256};
use postgres_protocol::message::backend::{self, ErrorFields};
use postgres_protocol::message::frontend;
use postgres_shared::rows::RowData;
use std::cell::{Cell, RefCell};
use std::collections::{HashMap, VecDeque};
use std::fmt;
@ -88,38 +93,33 @@ use std::mem;
use std::result;
use std::sync::Arc;
use std::time::Duration;
use postgres_protocol::authentication;
use postgres_protocol::authentication::sasl::{self, ScramSha256};
use postgres_protocol::message::backend::{self, ErrorFields};
use postgres_protocol::message::frontend;
use postgres_shared::rows::RowData;
use error::{DbError, UNDEFINED_COLUMN, UNDEFINED_TABLE};
use tls::TlsHandshake;
use notification::{Notification, Notifications};
use params::{IntoConnectParams, User};
use priv_io::MessageStream;
use rows::Rows;
use stmt::{Column, Statement};
use tls::TlsHandshake;
use transaction::{IsolationLevel, Transaction};
use types::{Field, FromSql, IsNull, Kind, Oid, ToSql, Type, CHAR, NAME, OID};
#[doc(inline)]
pub use error::Error;
#[doc(inline)]
pub use postgres_shared::CancelData;
#[doc(inline)]
pub use postgres_shared::{error, types};
#[doc(inline)]
pub use error::Error;
#[macro_use]
mod macros;
mod priv_io;
pub mod tls;
pub mod notification;
pub mod params;
mod priv_io;
pub mod rows;
pub mod stmt;
pub mod tls;
pub mod transaction;
const TYPEINFO_QUERY: &'static str = "__typeinfo";

View File

@ -13,11 +13,11 @@ use std::sync::Arc;
#[doc(inline)]
pub use postgres_shared::rows::RowIndex;
use {Error, Result, StatementInfo};
use error;
use stmt::{Column, Statement};
use transaction::Transaction;
use types::{FromSql, WrongType};
use stmt::{Statement, Column};
use {Error, Result, StatementInfo};
enum MaybeOwned<'a, T: 'a> {
Borrowed(&'a T),
@ -113,11 +113,9 @@ impl<'a> Iterator for Iter<'a> {
type Item = Row<'a>;
fn next(&mut self) -> Option<Row<'a>> {
self.iter.next().map(|row| {
Row {
stmt_info: self.stmt_info,
data: MaybeOwned::Borrowed(row),
}
self.iter.next().map(|row| Row {
stmt_info: self.stmt_info,
data: MaybeOwned::Borrowed(row),
})
}
@ -128,11 +126,9 @@ impl<'a> Iterator for Iter<'a> {
impl<'a> DoubleEndedIterator for Iter<'a> {
fn next_back(&mut self) -> Option<Row<'a>> {
self.iter.next_back().map(|row| {
Row {
stmt_info: self.stmt_info,
data: MaybeOwned::Borrowed(row),
}
self.iter.next_back().map(|row| Row {
stmt_info: self.stmt_info,
data: MaybeOwned::Borrowed(row),
})
}
}
@ -191,10 +187,10 @@ impl<'a> Row<'a> {
/// println!("{}: {}", foo, bar);
/// }
/// ```
pub fn get<I, T>(&self, idx: I) -> T
pub fn get<'b, I, T>(&'b self, idx: I) -> T
where
I: RowIndex + fmt::Debug,
T: FromSql,
T: FromSql<'b>,
{
match self.get_inner(&idx) {
Some(Ok(ok)) => ok,
@ -211,18 +207,18 @@ impl<'a> Row<'a> {
/// Returns `None` if the index does not reference a column, `Some(Err(..))`
/// if there was an error converting the result value, and `Some(Ok(..))`
/// on success.
pub fn get_opt<I, T>(&self, idx: I) -> Option<Result<T>>
pub fn get_opt<'b, I, T>(&'b self, idx: I) -> Option<Result<T>>
where
I: RowIndex,
T: FromSql,
T: FromSql<'b>,
{
self.get_inner(&idx)
}
fn get_inner<I, T>(&self, idx: &I) -> Option<Result<T>>
fn get_inner<'b, I, T>(&'b self, idx: &I) -> Option<Result<T>>
where
I: RowIndex,
T: FromSql,
T: FromSql<'b>,
{
let idx = match idx.__idx(&self.stmt_info.columns) {
Some(idx) => idx,
@ -236,21 +232,6 @@ impl<'a> Row<'a> {
let value = FromSql::from_sql_nullable(ty, self.data.get(idx));
Some(value.map_err(error::conversion))
}
/// 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<I>(&self, idx: I) -> Option<&[u8]>
where
I: RowIndex + fmt::Debug,
{
match idx.__idx(&self.stmt_info.columns) {
Some(idx) => self.data.get(idx),
None => panic!("invalid index {:?}", idx),
}
}
}
/// A lazily-loaded iterator over the resulting rows of a query.
@ -313,18 +294,13 @@ impl<'trans, 'stmt> LazyRows<'trans, 'stmt> {
fn execute(&mut self) -> Result<()> {
let mut conn = self.stmt.conn().0.borrow_mut();
conn.stream.write_message(|buf| {
frontend::execute(&self.name, self.row_limit, buf)
})?;
conn.stream.write_message(
|buf| Ok::<(), io::Error>(frontend::sync(buf)),
)?;
conn.stream
.write_message(|buf| frontend::execute(&self.name, self.row_limit, buf))?;
conn.stream
.write_message(|buf| Ok::<(), io::Error>(frontend::sync(buf)))?;
conn.stream.flush()?;
conn.read_rows(|row| self.data.push_back(row)).map(
|more_rows| {
self.more_rows = more_rows
},
)
conn.read_rows(|row| self.data.push_back(row))
.map(|more_rows| self.more_rows = more_rows)
}
/// Returns a slice describing the columns of the `LazyRows`.
@ -350,11 +326,9 @@ impl<'trans, 'stmt> FallibleIterator for LazyRows<'trans, 'stmt> {
self.execute()?;
}
let row = self.data.pop_front().map(|r| {
Row {
stmt_info: &**self.stmt.info(),
data: MaybeOwned::Owned(r),
}
let row = self.data.pop_front().map(|r| Row {
stmt_info: &**self.stmt.info(),
data: MaybeOwned::Owned(r),
});
Ok(row)

View File

@ -2,13 +2,11 @@
use std::cell::Cell;
use std::fmt;
#[allow(unused_imports)]
use std::ascii::AsciiExt;
use {bad_response, Connection, Result};
use rows::Rows;
use stmt::Statement;
use types::ToSql;
use {bad_response, Connection, Result};
/// An enumeration of transaction isolation levels.
///

View File

@ -1320,20 +1320,6 @@ fn test_parameter() {
assert_eq!(None, conn.parameter("asdf"));
}
#[test]
fn test_get_bytes() {
let conn = or_panic!(Connection::connect(
"postgres://postgres@localhost:5433",
TlsMode::None,
));
let stmt = or_panic!(conn.prepare("SELECT '\\x00010203'::BYTEA"));
let result = or_panic!(stmt.query(&[]));
assert_eq!(
b"\x00\x01\x02\x03",
result.iter().next().unwrap().get_bytes(0).unwrap()
);
}
#[test]
fn url_unencoded_password() {
assert!(

View File

@ -5,38 +5,47 @@ use std::f64;
use std::fmt;
use std::result;
use postgres::types::{FromSql, FromSqlOwned, INT4, IsNull, Kind, ToSql, Type, WrongType, NUMERIC,
TEXT};
use postgres::{Connection, TlsMode};
use postgres::types::{ToSql, FromSql, WrongType, Type, IsNull, Kind, TEXT, INT4, NUMERIC};
#[cfg(feature = "with-bit-vec")]
mod bit_vec;
#[cfg(feature = "with-chrono")]
mod chrono;
#[cfg(feature = "with-eui48")]
mod eui48;
#[cfg(feature = "with-uuid")]
mod uuid;
#[cfg(feature = "with-time")]
mod time;
#[cfg(feature = "with-geo")]
mod geo;
#[cfg(feature = "with-rustc-serialize")]
mod rustc_serialize;
#[cfg(feature = "with-serde_json")]
mod serde_json;
#[cfg(feature = "with-chrono")]
mod chrono;
#[cfg(feature = "with-geo")]
mod geo;
#[cfg(feature = "with-time")]
mod time;
#[cfg(feature = "with-uuid")]
mod uuid;
fn test_type<T: PartialEq + FromSql + ToSql, S: fmt::Display>(sql_type: &str, checks: &[(T, S)]) {
fn test_type<T, S>(sql_type: &str, checks: &[(T, S)])
where
T: PartialEq + for<'a> FromSqlOwned + ToSql,
S: fmt::Display,
{
let conn = or_panic!(Connection::connect(
"postgres://postgres@localhost:5433",
TlsMode::None,
));
for &(ref val, ref repr) in checks.iter() {
let stmt = or_panic!(conn.prepare(&*format!("SELECT {}::{}", *repr, sql_type)));
let result = or_panic!(stmt.query(&[])).iter().next().unwrap().get(0);
let rows = or_panic!(stmt.query(&[]));
let row = rows.iter().next().unwrap();
let result = row.get(0);
assert_eq!(val, &result);
let stmt = or_panic!(conn.prepare(&*format!("SELECT $1::{}", sql_type)));
let result = or_panic!(stmt.query(&[val])).iter().next().unwrap().get(0);
let rows = or_panic!(stmt.query(&[val]));
let row = rows.iter().next().unwrap();
let result = row.get(0);
assert_eq!(val, &result);
}
}
@ -184,6 +193,19 @@ fn test_text_params() {
);
}
#[test]
fn test_borrowed_text() {
let conn = or_panic!(Connection::connect(
"postgres://postgres@localhost:5433",
TlsMode::None,
));
let rows = or_panic!(conn.query("SELECT 'foo'", &[]));
let row = rows.get(0);
let s: &str = row.get(0);
assert_eq!(s, "foo");
}
#[test]
fn test_bpchar_params() {
let conn = or_panic!(Connection::connect(
@ -225,15 +247,9 @@ fn test_citext_params() {
));
or_panic!(conn.execute(
"INSERT INTO foo (b) VALUES ($1), ($2), ($3)",
&[
&Some("foobar"),
&Some("FooBar"),
&None::<&'static str>,
],
));
let stmt = or_panic!(conn.prepare(
"SELECT id FROM foo WHERE b = 'FOOBAR' ORDER BY id",
&[&Some("foobar"), &Some("FooBar"), &None::<&'static str>],
));
let stmt = or_panic!(conn.prepare("SELECT id FROM foo WHERE b = 'FOOBAR' ORDER BY id",));
let res = or_panic!(stmt.query(&[]));
assert_eq!(
@ -253,6 +269,19 @@ fn test_bytea_params() {
);
}
#[test]
fn test_borrowed_bytea() {
let conn = or_panic!(Connection::connect(
"postgres://postgres@localhost:5433",
TlsMode::None,
));
let rows = or_panic!(conn.query("SELECT 'foo'::BYTEA", &[]));
let row = rows.get(0);
let s: &[u8] = row.get(0);
assert_eq!(s, b"foo");
}
#[test]
fn test_hstore_params() {
macro_rules! make_map {
@ -293,7 +322,10 @@ fn test_array_params() {
);
}
fn test_nan_param<T: PartialEq + ToSql + FromSql>(sql_type: &str) {
fn test_nan_param<T>(sql_type: &str)
where
T: PartialEq + ToSql + FromSqlOwned,
{
let conn = or_panic!(Connection::connect(
"postgres://postgres@localhost:5433",
TlsMode::None,
@ -394,17 +426,16 @@ fn domain() {
}
fn accepts(ty: &Type) -> bool {
ty.name() == "session_id" &&
match *ty.kind() {
Kind::Domain(_) => true,
_ => false,
}
ty.name() == "session_id" && match *ty.kind() {
Kind::Domain(_) => true,
_ => false,
}
}
to_sql_checked!();
}
impl FromSql for SessionId {
impl<'a> FromSql<'a> for SessionId {
fn from_sql(
ty: &Type,
raw: &[u8],

View File

@ -9,7 +9,7 @@ use std::sync::Arc;
#[doc(inline)]
pub use postgres_shared::rows::RowIndex;
use types::{WrongType, FromSql};
use types::{FromSql, WrongType};
/// A row from Postgres.
pub struct Row {
@ -44,9 +44,9 @@ impl Row {
///
/// Panics if the index does not reference a column or the return type is
/// not compatible with the Postgres type.
pub fn get<T, I>(&self, idx: I) -> T
pub fn get<'a, T, I>(&'a self, idx: I) -> T
where
T: FromSql,
T: FromSql<'a>,
I: RowIndex + fmt::Debug,
{
match self.try_get(&idx) {
@ -64,9 +64,9 @@ impl Row {
/// Returns `None` if the index does not reference a column, `Some(Err(..))`
/// if there was an error converting the result value, and `Some(Ok(..))`
/// on success.
pub fn try_get<T, I>(&self, idx: I) -> Result<Option<T>, Box<Error + Sync + Send>>
pub fn try_get<'a, T, I>(&'a self, idx: I) -> Result<Option<T>, Box<Error + Sync + Send>>
where
T: FromSql,
T: FromSql<'a>,
I: RowIndex,
{
let idx = match idx.__idx(&self.columns) {

View File

@ -313,7 +313,7 @@ fn domain() {
to_sql_checked!();
}
impl FromSql for SessionId {
impl<'a> FromSql<'a> for SessionId {
fn from_sql(ty: &Type, raw: &[u8]) -> Result<Self, Box<StdError + Sync + Send>> {
Vec::<u8>::from_sql(ty, raw).map(SessionId)
}