Add a Slice adapter type
Until impl specialization exists, we can't define this implementaion directly on &[T] because of the existing implementation for &[u8]. Closes #92
This commit is contained in:
parent
23c49c0219
commit
a0cca5fa77
@ -82,6 +82,8 @@ use url::Url;
|
||||
pub use error::{Error, ConnectError, SqlState, DbError, ErrorPosition};
|
||||
#[doc(inline)]
|
||||
pub use types::{Oid, Type, ToSql, FromSql};
|
||||
#[doc(inline)]
|
||||
pub use types::Slice;
|
||||
use io::{InternalStream, Timeout};
|
||||
use message::BackendMessage::*;
|
||||
use message::FrontendMessage::*;
|
||||
|
@ -1,4 +1,6 @@
|
||||
//! Traits dealing with Postgres data types
|
||||
pub use self::slice::Slice;
|
||||
|
||||
use serialize::json;
|
||||
use std::collections::HashMap;
|
||||
use std::old_io::net::ip::IpAddr;
|
||||
@ -132,6 +134,7 @@ macro_rules! to_raw_to_impl {
|
||||
#[cfg(feature = "uuid")]
|
||||
mod uuid;
|
||||
mod time;
|
||||
mod slice;
|
||||
|
||||
/// A Postgres OID
|
||||
pub type Oid = u32;
|
||||
|
56
src/types/slice.rs
Normal file
56
src/types/slice.rs
Normal file
@ -0,0 +1,56 @@
|
||||
use byteorder::{BigEndian, WriterBytesExt};
|
||||
|
||||
use {Type, ToSql, Result, Error};
|
||||
|
||||
/// An adapter type mapping slices to Postgres arrays.
|
||||
///
|
||||
/// `Slice`'s `ToSql` implementation maps the slice to a one-dimensional
|
||||
/// Postgres array of the relevant type. This is particularly useful with the
|
||||
/// `ANY` operator to match a column against multiple values without having
|
||||
/// to dynamically construct the query string.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # fn foo() -> postgres::Result<()> {
|
||||
/// # use postgres::{Connection, SslMode, Slice};
|
||||
/// # let conn = Connection::connect("", &SslMode::None).unwrap();
|
||||
/// let values = &[1i32, 2, 3, 4, 5, 6];
|
||||
/// let stmt = try!(conn.prepare("SELECT * FROM foo WHERE id = ANY($1)"));
|
||||
/// for row in try!(stmt.query(&[&Slice(values)])) {
|
||||
/// // ...
|
||||
/// }
|
||||
/// # Ok(()) }
|
||||
/// ```
|
||||
pub struct Slice<'a, T: 'a + ToSql>(pub &'a [T]);
|
||||
|
||||
impl<'a, T: 'a + ToSql> ToSql for Slice<'a, T> {
|
||||
fn to_sql(&self, ty: &Type) -> Result<Option<Vec<u8>>> {
|
||||
let member_type = match ty.element_type() {
|
||||
Some(member) => member,
|
||||
None => return Err(Error::WrongType(ty.clone())),
|
||||
};
|
||||
|
||||
let mut buf = vec![];
|
||||
let _ = buf.write_i32::<BigEndian>(1); // number of dimensions
|
||||
let _ = buf.write_i32::<BigEndian>(1); // has nulls
|
||||
let _ = buf.write_u32::<BigEndian>(member_type.to_oid());
|
||||
|
||||
let _ = buf.write_i32::<BigEndian>(self.0.len() as i32);
|
||||
let _ = buf.write_i32::<BigEndian>(0); // index offset
|
||||
|
||||
for e in self.0 {
|
||||
match try!(e.to_sql(&member_type)) {
|
||||
Some(inner_buf) => {
|
||||
let _ = buf.write_i32::<BigEndian>(inner_buf.len() as i32);
|
||||
let _ = buf.write_all(&inner_buf);
|
||||
}
|
||||
None => {
|
||||
let _ = buf.write_i32::<BigEndian>(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Some(buf))
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@ use std::fmt;
|
||||
use std::num::Float;
|
||||
use std::old_io::net::ip::IpAddr;
|
||||
|
||||
use postgres::{Connection, SslMode};
|
||||
use postgres::{Connection, SslMode, Slice, Error};
|
||||
use postgres::types::{ToSql, FromSql};
|
||||
|
||||
#[cfg(feature = "uuid")]
|
||||
@ -223,3 +223,28 @@ fn test_pg_database_datname() {
|
||||
or_panic!(next.get_opt::<usize, String>(0));
|
||||
or_panic!(next.get_opt::<&str, String>("datname"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_slice() {
|
||||
let conn = Connection::connect("postgres://postgres@localhost", &SslMode::None).unwrap();
|
||||
conn.batch_execute("CREATE TEMPORARY TABLE foo (id SERIAL PRIMARY KEY, f VARCHAR);
|
||||
INSERT INTO foo (f) VALUES ('a'), ('b'), ('c'), ('d');").unwrap();
|
||||
|
||||
let stmt = conn.prepare("SELECT f FROM foo WHERE id = ANY($1)").unwrap();
|
||||
let result = stmt.query(&[&Slice(&[1i32, 3, 4])]).unwrap();
|
||||
assert_eq!(&["a".to_string(), "c".to_string(), "d".to_string()][],
|
||||
result.map(|r| r.get::<_, String>(0)).collect::<Vec<_>>());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_slice_wrong_type() {
|
||||
let conn = Connection::connect("postgres://postgres@localhost", &SslMode::None).unwrap();
|
||||
conn.batch_execute("CREATE TEMPORARY TABLE foo (id SERIAL PRIMARY KEY)").unwrap();
|
||||
|
||||
let stmt = conn.prepare("SELECT * FROM foo WHERE id = ANY($1)").unwrap();
|
||||
match stmt.query(&[&Slice(&["hi"])]) {
|
||||
Ok(_) => panic!("Unexpected success"),
|
||||
Err(Error::WrongType(..)) => {}
|
||||
Err(e) => panic!("Unexpected error {}", e),
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user