Support hstore

This commit is contained in:
Steven Fackler 2013-12-04 21:20:48 -08:00
parent deeba1bace
commit 6e9e9ebe3e
3 changed files with 84 additions and 5 deletions

View File

@ -265,6 +265,10 @@ types. The driver currently supports the following conversions:
<td>types::array::ArrayBase&lt;Option&lt;i64&gt;&gt;</td> <td>types::array::ArrayBase&lt;Option&lt;i64&gt;&gt;</td>
<td>INT8[], INT8[][], ...</td> <td>INT8[], INT8[][], ...</td>
</tr> </tr>
<tr>
<td>std::hashmap::HashMap&lt;~str, Option&lt;~str&gt;&gt;</td>
<td>HSTORE</td>
</tr>
</tbody> </tbody>
</table> </table>

19
test.rs
View File

@ -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);

View File

@ -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>>)