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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy