Support hstore
This commit is contained in:
parent
deeba1bace
commit
6e9e9ebe3e
@ -265,6 +265,10 @@ types. The driver currently supports the following conversions:
|
|||||||
<td>types::array::ArrayBase<Option<i64>></td>
|
<td>types::array::ArrayBase<Option<i64>></td>
|
||||||
<td>INT8[], INT8[][], ...</td>
|
<td>INT8[], INT8[][], ...</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>std::hashmap::HashMap<~str, Option<~str>></td>
|
||||||
|
<td>HSTORE</td>
|
||||||
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
19
test.rs
19
test.rs
@ -12,6 +12,7 @@ use extra::uuid::Uuid;
|
|||||||
use ssl::{SslContext, Sslv3};
|
use ssl::{SslContext, Sslv3};
|
||||||
use std::f32;
|
use std::f32;
|
||||||
use std::f64;
|
use std::f64;
|
||||||
|
use std::hashmap::HashMap;
|
||||||
use std::io::timer;
|
use std::io::timer;
|
||||||
|
|
||||||
use lib::{PostgresNoticeHandler,
|
use lib::{PostgresNoticeHandler,
|
||||||
@ -447,6 +448,24 @@ fn test_int8array_params() {
|
|||||||
[(Some(a), "'[-1:0][0:1]={{0,1},{NULL,3}}'")]);
|
[(Some(a), "'[-1:0][0:1]={{0,1},{NULL,3}}'")]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hstore_params() {
|
||||||
|
macro_rules! make_map(
|
||||||
|
($($k:expr => $v:expr),+) => ({
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
$(map.insert($k, $v);)+
|
||||||
|
map
|
||||||
|
})
|
||||||
|
)
|
||||||
|
test_type("hstore",
|
||||||
|
[(Some(make_map!(~"a" => Some(~"1"))), "'a=>1'"),
|
||||||
|
(Some(make_map!(~"hello" => Some(~"world!"),
|
||||||
|
~"hola" => Some(~"mundo!"),
|
||||||
|
~"what" => None)),
|
||||||
|
"'hello=>world!,hola=>mundo!,what=>NULL'"),
|
||||||
|
(None, "NULL")]);
|
||||||
|
}
|
||||||
|
|
||||||
fn test_nan_param<T: Float+ToSql+FromSql>(sql_type: &str) {
|
fn test_nan_param<T: Float+ToSql+FromSql>(sql_type: &str) {
|
||||||
let conn = PostgresConnection::connect("postgres://postgres@localhost", &NoSsl);
|
let conn = PostgresConnection::connect("postgres://postgres@localhost", &NoSsl);
|
||||||
let stmt = conn.prepare("SELECT 'NaN'::" + sql_type);
|
let stmt = conn.prepare("SELECT 'NaN'::" + sql_type);
|
||||||
|
66
types/mod.rs
66
types/mod.rs
@ -6,6 +6,7 @@ use extra::time::Timespec;
|
|||||||
use extra::json;
|
use extra::json;
|
||||||
use extra::json::Json;
|
use extra::json::Json;
|
||||||
use extra::uuid::Uuid;
|
use extra::uuid::Uuid;
|
||||||
|
use std::hashmap::HashMap;
|
||||||
use std::io::Decorator;
|
use std::io::Decorator;
|
||||||
use std::io::mem::{MemWriter, BufReader};
|
use std::io::mem::{MemWriter, BufReader};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
@ -143,6 +144,7 @@ impl PostgresType {
|
|||||||
/// Returns the wire format needed for the value of `self`.
|
/// Returns the wire format needed for the value of `self`.
|
||||||
pub fn result_format(&self) -> Format {
|
pub fn result_format(&self) -> Format {
|
||||||
match *self {
|
match *self {
|
||||||
|
PgUnknownType { name: ~"hstore", .. } => Binary,
|
||||||
PgUnknownType { .. } => Text,
|
PgUnknownType { .. } => Text,
|
||||||
_ => Binary
|
_ => Binary
|
||||||
}
|
}
|
||||||
@ -379,6 +381,31 @@ from_option_impl!(ArrayBase<Option<i32>>)
|
|||||||
from_array_impl!(PgInt8Array, i64)
|
from_array_impl!(PgInt8Array, i64)
|
||||||
from_option_impl!(ArrayBase<Option<i64>>)
|
from_option_impl!(ArrayBase<Option<i64>>)
|
||||||
|
|
||||||
|
from_map_impl!(PgUnknownType { name: ~"hstore", .. },
|
||||||
|
HashMap<~str, Option<~str>>, |buf| {
|
||||||
|
let mut rdr = BufReader::new(buf.as_slice());
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
|
||||||
|
let count = rdr.read_be_i32();
|
||||||
|
|
||||||
|
for _ in range(0, count) {
|
||||||
|
let key_len = rdr.read_be_i32();
|
||||||
|
let key = str::from_utf8_owned(rdr.read_bytes(key_len as uint));
|
||||||
|
|
||||||
|
let val_len = rdr.read_be_i32();
|
||||||
|
let val = if val_len < 0 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(str::from_utf8_owned(rdr.read_bytes(val_len as uint)))
|
||||||
|
};
|
||||||
|
|
||||||
|
map.insert(key, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
map
|
||||||
|
})
|
||||||
|
from_option_impl!(HashMap<~str, Option<~str>>)
|
||||||
|
|
||||||
/// A trait for types that can be converted into Postgres values
|
/// A trait for types that can be converted into Postgres values
|
||||||
pub trait ToSql {
|
pub trait ToSql {
|
||||||
/// Converts the value of `self` into a format appropriate for the Postgres
|
/// Converts the value of `self` into a format appropriate for the Postgres
|
||||||
@ -427,7 +454,7 @@ impl RawToSql for Timespec {
|
|||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! to_option_impl(
|
macro_rules! to_option_impl(
|
||||||
($($oid:ident)|+, $t:ty) => (
|
($($oid:pat)|+, $t:ty) => (
|
||||||
impl ToSql for Option<$t> {
|
impl ToSql for Option<$t> {
|
||||||
fn to_sql(&self, ty: &PostgresType) -> (Format, Option<~[u8]>) {
|
fn to_sql(&self, ty: &PostgresType) -> (Format, Option<~[u8]>) {
|
||||||
check_types!($($oid)|+, ty)
|
check_types!($($oid)|+, ty)
|
||||||
@ -438,8 +465,11 @@ macro_rules! to_option_impl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
(self, $($oid:ident)|+, $t:ty) => (
|
)
|
||||||
|
|
||||||
|
macro_rules! to_option_impl_self(
|
||||||
|
($($oid:pat)|+, $t:ty) => (
|
||||||
impl<'self> ToSql for Option<$t> {
|
impl<'self> ToSql for Option<$t> {
|
||||||
fn to_sql(&self, ty: &PostgresType) -> (Format, Option<~[u8]>) {
|
fn to_sql(&self, ty: &PostgresType) -> (Format, Option<~[u8]>) {
|
||||||
check_types!($($oid)|+, ty)
|
check_types!($($oid)|+, ty)
|
||||||
@ -518,7 +548,7 @@ impl<'self> ToSql for &'self str {
|
|||||||
}
|
}
|
||||||
|
|
||||||
to_option_impl!(PgVarchar | PgText | PgCharN, ~str)
|
to_option_impl!(PgVarchar | PgText | PgCharN, ~str)
|
||||||
to_option_impl!(self, PgVarchar | PgText | PgCharN, &'self str)
|
to_option_impl_self!(PgVarchar | PgText | PgCharN, &'self str)
|
||||||
|
|
||||||
impl ToSql for ~[u8] {
|
impl ToSql for ~[u8] {
|
||||||
fn to_sql(&self, ty: &PostgresType) -> (Format, Option<~[u8]>) {
|
fn to_sql(&self, ty: &PostgresType) -> (Format, Option<~[u8]>) {
|
||||||
@ -535,7 +565,7 @@ impl<'self> ToSql for &'self [u8] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
to_option_impl!(PgByteA, ~[u8])
|
to_option_impl!(PgByteA, ~[u8])
|
||||||
to_option_impl!(self, PgByteA, &'self [u8])
|
to_option_impl_self!(PgByteA, &'self [u8])
|
||||||
|
|
||||||
impl ToSql for Json {
|
impl ToSql for Json {
|
||||||
fn to_sql(&self, ty: &PostgresType) -> (Format, Option<~[u8]>) {
|
fn to_sql(&self, ty: &PostgresType) -> (Format, Option<~[u8]>) {
|
||||||
@ -652,3 +682,29 @@ to_option_impl!(PgInt4Array, ArrayBase<Option<i32>>)
|
|||||||
|
|
||||||
to_array_impl!(PgInt8Array, INT8OID, i64)
|
to_array_impl!(PgInt8Array, INT8OID, i64)
|
||||||
to_option_impl!(PgInt8Array, ArrayBase<Option<i64>>)
|
to_option_impl!(PgInt8Array, ArrayBase<Option<i64>>)
|
||||||
|
|
||||||
|
impl<'self> ToSql for HashMap<~str, Option<~str>> {
|
||||||
|
fn to_sql(&self, ty: &PostgresType) -> (Format, Option<~[u8]>) {
|
||||||
|
check_types!(PgUnknownType { name: ~"hstore", .. }, ty)
|
||||||
|
let mut buf = MemWriter::new();
|
||||||
|
|
||||||
|
buf.write_be_i32(self.len() as i32);
|
||||||
|
|
||||||
|
for (key, val) in self.iter() {
|
||||||
|
buf.write_be_i32(key.len() as i32);
|
||||||
|
buf.write(key.as_bytes());
|
||||||
|
|
||||||
|
match *val {
|
||||||
|
Some(ref val) => {
|
||||||
|
buf.write_be_i32(val.len() as i32);
|
||||||
|
buf.write(val.as_bytes());
|
||||||
|
}
|
||||||
|
None => buf.write_be_i32(-1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(Binary, Some(buf.inner()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
to_option_impl!(PgUnknownType { name: ~"hstore", .. },
|
||||||
|
HashMap<~str, Option<~str>>)
|
||||||
|
Loading…
Reference in New Issue
Block a user