Skip to content

Commit 8b1034a

Browse files
committed
Add a Row struct
1 parent 361c7bf commit 8b1034a

File tree

2 files changed

+110
-44
lines changed

2 files changed

+110
-44
lines changed

postgres-shared/src/lib.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@ extern crate phf;
66
extern crate postgres_protocol;
77

88
use fallible_iterator::{FallibleIterator, FromFallibleIterator};
9+
use std::ascii::AsciiExt;
910
use std::ops::Range;
1011

12+
use types::Type;
13+
1114
pub mod error;
1215
pub mod params;
1316
pub mod types;
@@ -54,3 +57,67 @@ impl RowData {
5457
}
5558
}
5659
}
60+
61+
pub struct Column {
62+
name: String,
63+
type_: Type,
64+
}
65+
66+
impl Column {
67+
#[doc(hidden)]
68+
pub fn new(name: String, type_: Type) -> Column {
69+
Column {
70+
name: name,
71+
type_: type_,
72+
}
73+
}
74+
75+
pub fn name(&self) -> &str {
76+
&self.name
77+
}
78+
79+
pub fn type_(&self) -> &Type {
80+
&self.type_
81+
}
82+
}
83+
84+
/// A trait implemented by types that can index into columns of a row.
85+
pub trait RowIndex {
86+
/// Returns the index of the appropriate column, or `None` if no such
87+
/// column exists.
88+
fn idx(&self, stmt: &[Column]) -> Option<usize>;
89+
}
90+
91+
impl RowIndex for usize {
92+
#[inline]
93+
fn idx(&self, stmt: &[Column]) -> Option<usize> {
94+
if *self >= stmt.len() {
95+
None
96+
} else {
97+
Some(*self)
98+
}
99+
}
100+
}
101+
102+
impl<'a> RowIndex for str {
103+
#[inline]
104+
fn idx(&self, stmt: &[Column]) -> Option<usize> {
105+
if let Some(idx) = stmt.iter().position(|d| d.name() == self) {
106+
return Some(idx);
107+
};
108+
109+
// FIXME ASCII-only case insensitivity isn't really the right thing to
110+
// do. Postgres itself uses a dubious wrapper around tolower and JDBC
111+
// uses the US locale.
112+
stmt.iter().position(|d| d.name().eq_ignore_ascii_case(self))
113+
}
114+
}
115+
116+
impl<'a, T> RowIndex for &'a T
117+
where T: RowIndex
118+
{
119+
#[inline]
120+
fn idx(&self, columns: &[Column]) -> Option<usize> {
121+
T::idx(*self, columns)
122+
}
123+
}

postgres-tokio/src/lib.rs

Lines changed: 43 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,21 @@ use postgres_protocol::message::{backend, frontend};
1414
use postgres_protocol::message::backend::{ErrorResponseBody, ErrorFields};
1515
use postgres_shared::RowData;
1616
use std::collections::HashMap;
17+
use std::error::Error as StdError;
1718
use std::fmt;
1819
use std::io;
1920
use std::mem;
21+
use std::sync::Arc;
2022
use std::sync::mpsc::{self, Sender, Receiver};
2123
use tokio_core::reactor::Handle;
2224

2325
#[doc(inline)]
24-
pub use postgres_shared::{params, types};
26+
pub use postgres_shared::{params, types, Column, RowIndex};
2527

2628
use error::{ConnectError, Error, DbError};
2729
use params::{ConnectParams, IntoConnectParams};
2830
use stream::PostgresStream;
29-
use types::{Oid, Type, ToSql, SessionInfo, IsNull};
31+
use types::{Oid, Type, ToSql, SessionInfo, IsNull, FromSql, WrongType};
3032

3133
pub mod error;
3234
mod stream;
@@ -293,30 +295,6 @@ impl Connection {
293295
.boxed()
294296
}
295297

296-
#[allow(dead_code)]
297-
fn read_rows(self, mut rows: Vec<RowData>) -> BoxFuture<(Vec<RowData>, Connection), Error> {
298-
self.0.read()
299-
.map_err(Error::Io)
300-
.and_then(|(m, s)| {
301-
match m {
302-
backend::Message::EmptyQueryResponse |
303-
backend::Message::CommandComplete(_) => Connection(s).ready(rows).boxed(),
304-
backend::Message::DataRow(body) => {
305-
match body.values().collect() {
306-
Ok(row) => {
307-
rows.push(row);
308-
Connection(s).read_rows(rows)
309-
}
310-
Err(e) => Err(Error::Io(e)).into_future().boxed(),
311-
}
312-
}
313-
backend::Message::ErrorResponse(body) => Connection(s).ready_err(body),
314-
_ => Err(bad_message()).into_future().boxed(),
315-
}
316-
})
317-
.boxed()
318-
}
319-
320298
fn ready<T>(self, t: T) -> BoxFuture<(T, Connection), Error>
321299
where T: 'static + Send
322300
{
@@ -452,7 +430,7 @@ impl Connection {
452430
s.get_types(r.into_iter(),
453431
vec![],
454432
|f| f.1,
455-
|f, t| Column { name: f.0, type_: t })
433+
|f, t| Column::new(f.0, t))
456434
.map(|(r, s)| (p, r, s))
457435
})
458436
.boxed()
@@ -594,7 +572,7 @@ impl Connection {
594572
close_sender: conn.0.close_sender.clone(),
595573
name: name,
596574
params: params,
597-
columns: columns,
575+
columns: Arc::new(columns),
598576
};
599577
(stmt, conn)
600578
})
@@ -615,26 +593,11 @@ impl Connection {
615593
}
616594
}
617595

618-
pub struct Column {
619-
name: String,
620-
type_: Type,
621-
}
622-
623-
impl Column {
624-
pub fn name(&self) -> &str {
625-
&self.name
626-
}
627-
628-
pub fn type_(&self) -> &Type {
629-
&self.type_
630-
}
631-
}
632-
633596
pub struct Statement {
634597
close_sender: Sender<(u8, String)>,
635598
name: String,
636599
params: Vec<Type>,
637-
columns: Vec<Column>,
600+
columns: Arc<Vec<Column>>,
638601
}
639602

640603
impl Drop for Statement {
@@ -664,6 +627,42 @@ impl Statement {
664627
}
665628
}
666629

630+
pub struct Row {
631+
columns: Arc<Vec<Column>>,
632+
data: RowData,
633+
}
634+
635+
impl Row {
636+
pub fn get<T, I>(&self, idx: I) -> T
637+
where T: FromSql,
638+
I: RowIndex + fmt::Debug
639+
{
640+
match self.try_get(&idx) {
641+
Ok(Some(v)) => v,
642+
Ok(None) => panic!("no such column {:?}", idx),
643+
Err(e) => panic!("error retrieving row {:?}: {}", idx, e),
644+
}
645+
}
646+
647+
pub fn try_get<T, I>(&self, idx: I) -> Result<Option<T>, Box<StdError + Sync + Send>>
648+
where T: FromSql,
649+
I: RowIndex
650+
{
651+
let idx = match idx.idx(&self.columns) {
652+
Some(idx) => idx,
653+
None => return Ok(None),
654+
};
655+
656+
let ty = self.columns[idx].type_();
657+
if !T::accepts(ty) {
658+
return Err(Box::new(WrongType::new(ty.clone())));
659+
}
660+
661+
// FIXME
662+
T::from_sql_nullable(ty, self.data.get(idx), &SessionInfo::new(&HashMap::new())).map(Some)
663+
}
664+
}
665+
667666
fn connect_err(fields: &mut ErrorFields) -> ConnectError {
668667
match DbError::new(fields) {
669668
Ok(err) => ConnectError::Db(Box::new(err)),

0 commit comments

Comments
 (0)