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};
|
pub use error::{Error, ConnectError, SqlState, DbError, ErrorPosition};
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use types::{Oid, Type, ToSql, FromSql};
|
pub use types::{Oid, Type, ToSql, FromSql};
|
||||||
|
#[doc(inline)]
|
||||||
|
pub use types::Slice;
|
||||||
use io::{InternalStream, Timeout};
|
use io::{InternalStream, Timeout};
|
||||||
use message::BackendMessage::*;
|
use message::BackendMessage::*;
|
||||||
use message::FrontendMessage::*;
|
use message::FrontendMessage::*;
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
//! Traits dealing with Postgres data types
|
//! Traits dealing with Postgres data types
|
||||||
|
pub use self::slice::Slice;
|
||||||
|
|
||||||
use serialize::json;
|
use serialize::json;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::old_io::net::ip::IpAddr;
|
use std::old_io::net::ip::IpAddr;
|
||||||
@ -132,6 +134,7 @@ macro_rules! to_raw_to_impl {
|
|||||||
#[cfg(feature = "uuid")]
|
#[cfg(feature = "uuid")]
|
||||||
mod uuid;
|
mod uuid;
|
||||||
mod time;
|
mod time;
|
||||||
|
mod slice;
|
||||||
|
|
||||||
/// A Postgres OID
|
/// A Postgres OID
|
||||||
pub type Oid = u32;
|
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::num::Float;
|
||||||
use std::old_io::net::ip::IpAddr;
|
use std::old_io::net::ip::IpAddr;
|
||||||
|
|
||||||
use postgres::{Connection, SslMode};
|
use postgres::{Connection, SslMode, Slice, Error};
|
||||||
use postgres::types::{ToSql, FromSql};
|
use postgres::types::{ToSql, FromSql};
|
||||||
|
|
||||||
#[cfg(feature = "uuid")]
|
#[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::<usize, String>(0));
|
||||||
or_panic!(next.get_opt::<&str, String>("datname"));
|
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