Skip to content

Commit a476b90

Browse files
committed
Finish port
1 parent 1ab02c5 commit a476b90

File tree

4 files changed

+762
-2
lines changed

4 files changed

+762
-2
lines changed

Cargo.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,14 @@ license = "MIT"
66
description = "Array support for rust-postgres"
77
repository = "https://github.com/sfackler/rust-postgres-array"
88

9+
[features]
10+
default = ["uuid"]
11+
912
[dependencies]
1013
postgres = "0.2"
14+
rustc-serialize = "0.1"
15+
time = "0.1"
16+
17+
[dependencies.uuid]
18+
optional = true
19+
version = "0.1"

src/impls/mod.rs

Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
use std::io::ByRefReader;
2+
use std::io::util::LimitReader;
3+
use std::iter::MultiplicativeIterator;
4+
5+
use time::Timespec;
6+
use serialize::json::Json;
7+
use postgres::{mod, Error};
8+
use postgres::types::{RawFromSql, ToSql, RawToSql, Type, Oid};
9+
10+
use {Array, ArrayBase, DimensionInfo};
11+
12+
macro_rules! check_types {
13+
($($expected:pat)|+, $actual:ident) => (
14+
match $actual {
15+
$(&$expected)|+ => {}
16+
actual => return Err(::postgres::Error::WrongType(actual.clone()))
17+
}
18+
)
19+
}
20+
21+
macro_rules! from_sql_impl {
22+
($($oid:pat)|+, $t:ty) => {
23+
impl ::postgres::FromSql for Option<::ArrayBase<Option<$t>>> {
24+
fn from_sql(ty: &::postgres::Type, raw: Option<&[u8]>) -> ::postgres::Result<Self> {
25+
check_types!($($oid)|+, ty);
26+
27+
match raw {
28+
Some(mut raw) => ::postgres::types::RawFromSql::raw_from_sql(&mut raw).map(Some),
29+
None => Ok(None),
30+
}
31+
}
32+
}
33+
34+
impl ::postgres::FromSql for ::ArrayBase<Option<$t>> {
35+
fn from_sql(ty: &::postgres::Type, raw: Option<&[u8]>) -> ::postgres::Result<Self> {
36+
let v: ::postgres::Result<Option<Self>> = ::postgres::FromSql::from_sql(ty, raw);
37+
match v {
38+
Ok(None) => Err(::postgres::Error::WasNull),
39+
Ok(Some(v)) => Ok(v),
40+
Err(err) => Err(err),
41+
}
42+
}
43+
}
44+
}
45+
}
46+
47+
macro_rules! to_sql_impl {
48+
($($oid:pat)|+, $t:ty) => {
49+
impl ::postgres::ToSql for ::ArrayBase<Option<$t>> {
50+
fn to_sql(&self, ty: &::postgres::Type) -> ::postgres::Result<Option<Vec<u8>>> {
51+
check_types!($($oid)|+, ty);
52+
Ok(Some(::impls::raw_to_array(self, ty)))
53+
}
54+
}
55+
56+
impl ::postgres::ToSql for Option<::ArrayBase<Option<$t>>> {
57+
fn to_sql(&self, ty: &::postgres::Type) -> ::postgres::Result<Option<Vec<u8>>> {
58+
check_types!($($oid)|+, ty);
59+
match *self {
60+
Some(ref arr) => arr.to_sql(ty),
61+
None => Ok(None)
62+
}
63+
}
64+
}
65+
}
66+
}
67+
68+
69+
#[cfg(feature = "uuid")]
70+
mod uuid;
71+
72+
impl<T> RawFromSql for ArrayBase<Option<T>> where T: RawFromSql {
73+
fn raw_from_sql<R: Reader>(raw: &mut R) -> postgres::Result<ArrayBase<Option<T>>> {
74+
let ndim = try!(raw.read_be_u32()) as uint;
75+
let _has_null = try!(raw.read_be_i32()) == 1;
76+
let _element_type: Oid = try!(raw.read_be_u32());
77+
78+
let mut dim_info = Vec::with_capacity(ndim);
79+
for _ in range(0, ndim) {
80+
dim_info.push(DimensionInfo {
81+
len: try!(raw.read_be_u32()) as uint,
82+
lower_bound: try!(raw.read_be_i32()) as int,
83+
});
84+
}
85+
let nele = dim_info.iter().map(|info| info.len as uint).product();
86+
87+
let mut elements = Vec::with_capacity(nele);
88+
for _ in range(0, nele) {
89+
let len = try!(raw.read_be_i32());
90+
if len < 0 {
91+
elements.push(None);
92+
} else {
93+
let mut limit = LimitReader::new(raw.by_ref(), len as uint);
94+
elements.push(Some(try!(RawFromSql::raw_from_sql(&mut limit))));
95+
if limit.limit() != 0 {
96+
return Err(Error::BadData);
97+
}
98+
}
99+
}
100+
101+
Ok(ArrayBase::from_raw(elements, dim_info))
102+
}
103+
}
104+
105+
from_sql_impl!(Type::BoolArray, bool);
106+
from_sql_impl!(Type::ByteAArray, Vec<u8>);
107+
from_sql_impl!(Type::CharArray, i8);
108+
from_sql_impl!(Type::Int2Array, i16);
109+
from_sql_impl!(Type::Int4Array, i32);
110+
from_sql_impl!(Type::TextArray | Type::CharNArray | Type::VarcharArray | Type::NameArray, String);
111+
from_sql_impl!(Type::Int8Array, i64);
112+
from_sql_impl!(Type::JsonArray, Json);
113+
from_sql_impl!(Type::Float4Array, f32);
114+
from_sql_impl!(Type::Float8Array, f64);
115+
from_sql_impl!(Type::TimestampArray | Type::TimestampTZArray, Timespec);
116+
117+
fn raw_to_array<T>(array: &ArrayBase<Option<T>>, ty: &Type) -> Vec<u8> where T: RawToSql {
118+
let mut buf = vec![];
119+
120+
let _ = buf.write_be_i32(array.dimension_info().len() as i32);
121+
let _ = buf.write_be_i32(1);
122+
let _ = buf.write_be_u32(ty.member_type().to_oid());
123+
124+
for info in array.dimension_info().iter() {
125+
let _ = buf.write_be_i32(info.len as i32);
126+
let _ = buf.write_be_i32(info.lower_bound as i32);
127+
}
128+
129+
for v in array.values() {
130+
match *v {
131+
Some(ref val) => {
132+
let mut inner_buf = vec![];
133+
let _ = val.raw_to_sql(&mut inner_buf);
134+
let _ = buf.write_be_i32(inner_buf.len() as i32);
135+
let _ = buf.write(&*inner_buf);
136+
}
137+
None => {
138+
let _ = buf.write_be_i32(-1);
139+
}
140+
}
141+
}
142+
143+
buf
144+
}
145+
146+
to_sql_impl!(Type::BoolArray, bool);
147+
to_sql_impl!(Type::ByteAArray, Vec<u8>);
148+
to_sql_impl!(Type::CharArray, i8);
149+
to_sql_impl!(Type::Int2Array, i16);
150+
to_sql_impl!(Type::Int4Array, i32);
151+
to_sql_impl!(Type::Int8Array, i64);
152+
to_sql_impl!(Type::TextArray | Type::CharNArray | Type::VarcharArray | Type::NameArray, String);
153+
to_sql_impl!(Type::Float4Array, f32);
154+
to_sql_impl!(Type::Float8Array, f64);
155+
to_sql_impl!(Type::JsonArray, Json);
156+
to_sql_impl!(Type::TimestampArray | Type::TimestampTZArray, Timespec);
157+
158+
#[cfg(test)]
159+
mod test {
160+
use std::fmt;
161+
162+
use postgres::{Connection, SslMode, FromSql, ToSql};
163+
164+
fn test_type<T: PartialEq+FromSql+ToSql, S: fmt::Show>(sql_type: &str, checks: &[(T, S)]) {
165+
let conn = Connection::connect("postgres://postgres@localhost", &SslMode::None).unwrap();
166+
for &(ref val, ref repr) in checks.iter() {
167+
let stmt = conn.prepare(format!("SELECT {}::{}", *repr, sql_type)[]).unwrap();
168+
let result = stmt.query(&[]).unwrap().next().unwrap().get(0u);
169+
assert!(val == &result);
170+
171+
let stmt = conn.prepare(format!("SELECT $1::{}", sql_type)[]).unwrap();
172+
let result = stmt.query(&[val]).unwrap().next().unwrap().get(0u);
173+
assert!(val == &result);
174+
}
175+
}
176+
177+
macro_rules! test_array_params {
178+
($name:expr, $v1:expr, $s1:expr, $v2:expr, $s2:expr, $v3:expr, $s3:expr) => ({
179+
use ArrayBase;
180+
181+
let tests = &[(Some(ArrayBase::from_vec(vec!(Some($v1), Some($v2), None), 1)),
182+
format!("'{{{},{},NULL}}'", $s1, $s2).into_string()),
183+
(None, "NULL".to_string())];
184+
test_type(format!("{}[]", $name)[], tests);
185+
let mut a = ArrayBase::from_vec(vec!(Some($v1), Some($v2)), 0);
186+
a.wrap(-1);
187+
a.push_move(ArrayBase::from_vec(vec!(None, Some($v3)), 0));
188+
let tests = &[(Some(a), format!("'[-1:0][0:1]={{{{{},{}}},{{NULL,{}}}}}'",
189+
$s1, $s2, $s3).into_string())];
190+
test_type(format!("{}[][]", $name)[], tests);
191+
})
192+
}
193+
194+
#[test]
195+
fn test_boolarray_params() {
196+
test_array_params!("BOOL", false, "f", true, "t", true, "t");
197+
}
198+
199+
#[test]
200+
fn test_byteaarray_params() {
201+
test_array_params!("BYTEA", vec!(0u8, 1), r#""\\x0001""#, vec!(254u8, 255u8),
202+
r#""\\xfeff""#, vec!(10u8, 11u8), r#""\\x0a0b""#);
203+
}
204+
205+
#[test]
206+
fn test_chararray_params() {
207+
test_array_params!("\"char\"", 'a' as i8, "a", 'z' as i8, "z",
208+
'0' as i8, "0");
209+
}
210+
211+
#[test]
212+
fn test_namearray_params() {
213+
test_array_params!("NAME", "hello".to_string(), "hello", "world".to_string(),
214+
"world", "!".to_string(), "!");
215+
}
216+
217+
#[test]
218+
fn test_int2array_params() {
219+
test_array_params!("INT2", 0i16, "0", 1i16, "1", 2i16, "2");
220+
}
221+
222+
#[test]
223+
fn test_int4array_params() {
224+
test_array_params!("INT4", 0i32, "0", 1i32, "1", 2i32, "2");
225+
}
226+
227+
#[test]
228+
fn test_textarray_params() {
229+
test_array_params!("TEXT", "hello".to_string(), "hello", "world".to_string(),
230+
"world", "!".to_string(), "!");
231+
}
232+
233+
#[test]
234+
fn test_charnarray_params() {
235+
test_array_params!("CHAR(5)", "hello".to_string(), "hello",
236+
"world".to_string(), "world", "! ".to_string(), "!");
237+
}
238+
239+
#[test]
240+
fn test_varchararray_params() {
241+
test_array_params!("VARCHAR", "hello".to_string(), "hello",
242+
"world".to_string(), "world", "!".to_string(), "!");
243+
}
244+
245+
#[test]
246+
fn test_int8array_params() {
247+
test_array_params!("INT8", 0i64, "0", 1i64, "1", 2i64, "2");
248+
}
249+
250+
#[test]
251+
fn test_float4array_params() {
252+
test_array_params!("FLOAT4", 0f32, "0", 1.5f32, "1.5", 0.009f32, ".009");
253+
}
254+
255+
#[test]
256+
fn test_float8array_params() {
257+
test_array_params!("FLOAT8", 0f64, "0", 1.5f64, "1.5", 0.009f64, ".009");
258+
}
259+
}

src/impls/uuid.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
extern crate uuid;
2+
3+
use postgres::Type;
4+
use self::uuid::Uuid;
5+
6+
from_sql_impl!(Type::Uuid, Uuid);
7+
to_sql_impl!(Type::Uuid, Uuid);

0 commit comments

Comments
 (0)