Skip to content

Commit fd1a70f

Browse files
committed
Binary return value support
The set of types the driver asks for in binary format is currently hard coded. cc #7
1 parent 1a6d681 commit fd1a70f

File tree

3 files changed

+107
-102
lines changed

3 files changed

+107
-102
lines changed

src/lib.rs

+16-9
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,9 @@ impl<'self> PostgresStatement<'self> {
443443
values.push(value);
444444
};
445445
446-
let result_formats = [];
446+
let result_formats: ~[i16] = self.result_desc.iter().map(|desc| {
447+
types::result_format(desc.type_oid) as i16
448+
}).collect();
447449
448450
self.conn.write_messages([
449451
&Bind {
@@ -593,14 +595,17 @@ impl<'self> Drop for PostgresResult<'self> {
593595
}
594596
}
595597
596-
impl<'self> Iterator<PostgresRow> for PostgresResult<'self> {
597-
fn next(&mut self) -> Option<PostgresRow> {
598+
impl<'self> Iterator<PostgresRow<'self>> for PostgresResult<'self> {
599+
fn next(&mut self) -> Option<PostgresRow<'self>> {
598600
if self.data.is_empty() && self.more_rows {
599601
self.execute();
600602
}
601603
602604
do self.data.pop_front().map_move |row| {
603-
PostgresRow { data: row }
605+
PostgresRow {
606+
stmt: self.stmt,
607+
data: row
608+
}
604609
}
605610
}
606611
}
@@ -633,24 +638,26 @@ impl<'self> PostgresResult<'self> {
633638
}
634639
}
635640

636-
pub struct PostgresRow {
641+
pub struct PostgresRow<'self> {
642+
priv stmt: &'self PostgresStatement<'self>,
637643
priv data: ~[Option<~[u8]>]
638644
}
639645

640-
impl<'self> Container for PostgresRow {
646+
impl<'self> Container for PostgresRow<'self> {
641647
fn len(&self) -> uint {
642648
self.data.len()
643649
}
644650
}
645651

646-
impl<T: FromSql> Index<uint, T> for PostgresRow {
652+
impl<'self, T: FromSql> Index<uint, T> for PostgresRow<'self> {
647653
fn index(&self, idx: &uint) -> T {
648654
self.get(*idx)
649655
}
650656
}
651657

652-
impl PostgresRow {
658+
impl<'self> PostgresRow<'self> {
653659
pub fn get<T: FromSql>(&self, idx: uint) -> T {
654-
FromSql::from_sql(&self.data[idx])
660+
FromSql::from_sql(self.stmt.result_desc[idx].type_oid,
661+
&self.data[idx])
655662
}
656663
}

src/test.rs

+18-7
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ fn test_query() {
4848
let stmt = trans.prepare("SELECT * from foo ORDER BY id");
4949
let result = stmt.query([]);
5050

51-
assert_eq!(~[1, 2], result.map(|row| { row[0] }).collect());
51+
assert_eq!(~[1i64, 2], result.map(|row| { row[0] }).collect());
5252
}
5353
}
5454

@@ -112,39 +112,50 @@ fn test_param_type<T: Eq+ToSql+FromSql>(sql_type: &str, values: &[T]) {
112112
}
113113
114114
#[test]
115-
fn test_binary_bool_params() {
115+
fn test_bool_params() {
116116
test_param_type("BOOL", [Some(true), Some(false), None]);
117117
}
118118
119119
#[test]
120-
fn test_binary_i16_params() {
120+
fn test_i16_params() {
121121
test_param_type("SMALLINT", [Some(0x0011i16), Some(-0x0011i16), None]);
122122
}
123123
124124
#[test]
125-
fn test_binary_i32_params() {
125+
fn test_i32_params() {
126126
test_param_type("INT", [Some(0x00112233i32), Some(-0x00112233i32), None]);
127127
}
128128
129129
#[test]
130-
fn test_binary_i64_params() {
130+
fn test_i64_params() {
131131
test_param_type("BIGINT", [Some(0x0011223344556677i64),
132132
Some(-0x0011223344556677i64), None]);
133133
}
134134
135135
#[test]
136-
fn test_binary_f32_params() {
136+
fn test_f32_params() {
137137
test_param_type("REAL", [Some(f32::infinity), Some(f32::neg_infinity),
138138
Some(1000.55), None]);
139139
}
140140
141141
#[test]
142-
fn test_binary_f64_params() {
142+
fn test_f64_params() {
143143
test_param_type("DOUBLE PRECISION", [Some(f64::infinity),
144144
Some(f64::neg_infinity),
145145
Some(10000.55), None]);
146146
}
147147
148+
#[test]
149+
fn test_varchar_params() {
150+
test_param_type("VARCHAR", [Some(~"hello world"),
151+
Some(~"イロハニホヘト チリヌルヲ"), None]);
152+
}
153+
154+
#[test]
155+
fn test_bytea_params() {
156+
test_param_type("BYTEA", [Some(~[0u8, 1, 2, 3, 254, 255]), None]);
157+
}
158+
148159
fn test_nan_param<T: Float+ToSql+FromSql>(sql_type: &str) {
149160
do test_in_transaction |trans| {
150161
trans.update("CREATE TABLE foo (

src/types.rs

+73-86
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
use std::rt::io::Decorator;
2-
use std::rt::io::extensions::WriterByteConversions;
3-
use std::rt::io::mem::MemWriter;
2+
use std::rt::io::extensions::{WriterByteConversions, ReaderByteConversions};
3+
use std::rt::io::mem::{MemWriter, MemReader};
44
use std::str;
5-
use std::f32;
6-
use std::f64;
75

86
pub type Oid = i32;
97

108
// Values from pg_type.h
119
static BOOLOID: Oid = 16;
10+
static BYTEAOID: Oid = 17;
1211
static INT8OID: Oid = 20;
1312
static INT2OID: Oid = 21;
1413
static INT4OID: Oid = 23;
@@ -21,20 +20,40 @@ pub enum Format {
2120
Binary = 1
2221
}
2322

23+
pub fn result_format(ty: Oid) -> Format {
24+
match ty {
25+
BOOLOID |
26+
BYTEAOID |
27+
INT8OID |
28+
INT2OID |
29+
INT4OID |
30+
FLOAT4OID |
31+
FLOAT8OID => Binary,
32+
_ => Text
33+
}
34+
}
35+
36+
macro_rules! check_oid(
37+
($expected:ident, $actual:ident) => (
38+
if $expected != $actual {
39+
fail!("Expected Oid %? but got Oid %?", $expected, $actual);
40+
}
41+
)
42+
)
43+
2444
pub trait FromSql {
25-
fn from_sql(raw: &Option<~[u8]>) -> Self;
45+
fn from_sql(ty: Oid, raw: &Option<~[u8]>) -> Self;
2646
}
2747

28-
macro_rules! from_str_impl(
29-
($t:ty) => (
48+
macro_rules! from_conversions_impl(
49+
($oid:ident, $t:ty, $f:ident) => (
3050
impl FromSql for Option<$t> {
31-
fn from_sql(raw: &Option<~[u8]>) -> Option<$t> {
32-
match *raw {
33-
None => None,
34-
Some(ref buf) => {
35-
let s = str::from_bytes_slice(buf.as_slice());
36-
Some(FromStr::from_str(s).unwrap())
37-
}
51+
fn from_sql(ty: Oid, raw: &Option<~[u8]>) -> Option<$t> {
52+
check_oid!($oid, ty)
53+
do raw.map |buf| {
54+
// TODO change to BufReader when implemented
55+
let mut reader = MemReader::new(buf.to_owned());
56+
reader.$f()
3857
}
3958
}
4059
}
@@ -44,109 +63,58 @@ macro_rules! from_str_impl(
4463
macro_rules! from_option_impl(
4564
($t:ty) => (
4665
impl FromSql for $t {
47-
fn from_sql(raw: &Option<~[u8]>) -> $t {
66+
fn from_sql(ty: Oid, raw: &Option<~[u8]>) -> $t {
4867
// FIXME when you can specify Self types properly
49-
let ret: Option<$t> = FromSql::from_sql(raw);
68+
let ret: Option<$t> = FromSql::from_sql(ty, raw);
5069
ret.unwrap()
5170
}
5271
}
5372
)
5473
)
5574

5675
impl FromSql for Option<bool> {
57-
fn from_sql(raw: &Option<~[u8]>) -> Option<bool> {
58-
match *raw {
59-
None => None,
60-
Some(ref buf) => {
61-
assert_eq!(1, buf.len());
62-
match buf[0] as char {
63-
't' => Some(true),
64-
'f' => Some(false),
65-
byte => fail!("Invalid byte: %?", byte)
66-
}
67-
}
76+
fn from_sql(ty: Oid, raw: &Option<~[u8]>) -> Option<bool> {
77+
check_oid!(BOOLOID, ty)
78+
do raw.map |buf| {
79+
buf[0] != 0
6880
}
6981
}
7082
}
7183
from_option_impl!(bool)
7284

73-
from_str_impl!(int)
74-
from_option_impl!(int)
75-
from_str_impl!(i8)
76-
from_option_impl!(i8)
77-
from_str_impl!(i16)
85+
from_conversions_impl!(INT2OID, i16, read_be_i16_)
7886
from_option_impl!(i16)
79-
from_str_impl!(i32)
87+
from_conversions_impl!(INT4OID, i32, read_be_i32_)
8088
from_option_impl!(i32)
81-
from_str_impl!(i64)
89+
from_conversions_impl!(INT8OID, i64, read_be_i64_)
8290
from_option_impl!(i64)
83-
from_str_impl!(uint)
84-
from_option_impl!(uint)
85-
from_str_impl!(u8)
86-
from_option_impl!(u8)
87-
from_str_impl!(u16)
88-
from_option_impl!(u16)
89-
from_str_impl!(u32)
90-
from_option_impl!(u32)
91-
from_str_impl!(u64)
92-
from_option_impl!(u64)
93-
94-
impl FromSql for Option<f32> {
95-
fn from_sql(raw: &Option<~[u8]>) -> Option<f32> {
96-
match *raw {
97-
None => None,
98-
Some(ref buf) => {
99-
Some(match str::from_bytes_slice(buf.as_slice()) {
100-
"NaN" => f32::NaN,
101-
"Infinity" => f32::infinity,
102-
"-Infinity" => f32::neg_infinity,
103-
str => FromStr::from_str(str).unwrap()
104-
})
105-
}
106-
}
107-
}
108-
}
91+
from_conversions_impl!(FLOAT4OID, f32, read_be_f32_)
10992
from_option_impl!(f32)
110-
111-
impl FromSql for Option<f64> {
112-
fn from_sql(raw: &Option<~[u8]>) -> Option<f64> {
113-
match *raw {
114-
None => None,
115-
Some(ref buf) => {
116-
Some(match str::from_bytes_slice(buf.as_slice()) {
117-
"NaN" => f64::NaN,
118-
"Infinity" => f64::infinity,
119-
"-Infinity" => f64::neg_infinity,
120-
str => FromStr::from_str(str).unwrap()
121-
})
122-
}
123-
}
124-
}
125-
}
93+
from_conversions_impl!(FLOAT8OID, f64, read_be_f64_)
12694
from_option_impl!(f64)
12795

12896
impl FromSql for Option<~str> {
129-
fn from_sql(raw: &Option<~[u8]>) -> Option<~str> {
97+
fn from_sql(ty:Oid, raw: &Option<~[u8]>) -> Option<~str> {
98+
check_oid!(VARCHAROID, ty)
13099
do raw.chain_ref |buf| {
131100
Some(str::from_bytes(buf.as_slice()))
132101
}
133102
}
134103
}
135104
from_option_impl!(~str)
136105

106+
impl FromSql for Option<~[u8]> {
107+
fn from_sql(ty: Oid, raw: &Option<~[u8]>) -> Option<~[u8]> {
108+
check_oid!(BYTEAOID, ty)
109+
raw.clone()
110+
}
111+
}
112+
from_option_impl!(~[u8])
113+
137114
pub trait ToSql {
138115
fn to_sql(&self, ty: Oid) -> (Format, Option<~[u8]>);
139116
}
140117

141-
macro_rules! check_oid(
142-
($expected:ident, $actual:ident) => (
143-
if $expected != $actual {
144-
fail!("Attempted to bind an invalid type. Expected Oid %? but got \
145-
Oid %?", $expected, $actual);
146-
}
147-
)
148-
)
149-
150118
macro_rules! to_option_impl(
151119
($oid:ident, $t:ty) => (
152120
impl ToSql for Option<$t> {
@@ -213,3 +181,22 @@ impl<'self> ToSql for Option<&'self str> {
213181
}
214182
}
215183
}
184+
185+
impl<'self> ToSql for &'self [u8] {
186+
fn to_sql(&self, ty: Oid) -> (Format, Option<~[u8]>) {
187+
check_oid!(BYTEAOID, ty)
188+
(Binary, Some(self.to_owned()))
189+
}
190+
}
191+
192+
to_option_impl!(BYTEAOID, ~[u8])
193+
194+
impl<'self> ToSql for Option<&'self [u8]> {
195+
fn to_sql(&self, ty: Oid) -> (Format, Option<~[u8]>) {
196+
check_oid!(BYTEAOID, ty)
197+
match *self {
198+
None => (Text, None),
199+
Some(val) => val.to_sql(ty)
200+
}
201+
}
202+
}

0 commit comments

Comments
 (0)