Skip to content

Commit d5f1bdb

Browse files
committed
Composite type support
1 parent 7599c4e commit d5f1bdb

File tree

3 files changed

+105
-6
lines changed

3 files changed

+105
-6
lines changed

src/lib.rs

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ use message::{WriteMessage, ReadMessage};
7979
use notification::{Notifications, Notification};
8080
use rows::{Rows, LazyRows};
8181
use stmt::{Statement, Column};
82-
use types::{IsNull, Kind, Type, SessionInfo, Oid, Other, WrongType, ToSql, FromSql};
82+
use types::{IsNull, Kind, Type, SessionInfo, Oid, Other, WrongType, ToSql, FromSql, Field};
8383
use url::Url;
8484

8585
#[macro_use]
@@ -97,7 +97,8 @@ pub mod stmt;
9797
pub mod types;
9898
pub mod notification;
9999

100-
const TYPEINFO_QUERY: &'static str = "t";
100+
const TYPEINFO_QUERY: &'static str = "__typeinfo";
101+
const TYPEINFO_ARRAY_QUERY: &'static str = "__typeinfo_array";
101102

102103
/// A type alias of the result returned by many methods.
103104
pub type Result<T> = result::Result<T, Error>;
@@ -463,9 +464,22 @@ impl InnerConnection {
463464

464465
#[cfg_attr(rustfmt, rustfmt_skip)]
465466
fn setup_typeinfo_query(&mut self) -> result::Result<(), ConnectError> {
467+
match self.raw_prepare(TYPEINFO_ARRAY_QUERY,
468+
"SELECT attname, atttypid \
469+
FROM pg_catalog.pg_attribute \
470+
WHERE attrelid = $1 \
471+
AND NOT attisdropped \
472+
AND attnum > 0 \
473+
ORDER BY attnum") {
474+
Ok(..) => {}
475+
Err(Error::Io(e)) => return Err(ConnectError::Io(e)),
476+
Err(Error::Db(e)) => return Err(ConnectError::Db(e)),
477+
Err(Error::Conversion(_)) => unreachable!(),
478+
}
479+
466480
match self.raw_prepare(TYPEINFO_QUERY,
467481
"SELECT t.typname, t.typtype, t.typelem, r.rngsubtype, \
468-
t.typbasetype, n.nspname \
482+
t.typbasetype, n.nspname, t.typrelid \
469483
FROM pg_catalog.pg_type t \
470484
LEFT OUTER JOIN pg_catalog.pg_range r ON \
471485
r.rngtypid = t.oid \
@@ -823,7 +837,7 @@ impl InnerConnection {
823837
try!(self.read_rows(&mut rows));
824838
let row = rows.pop_front().unwrap();
825839

826-
let (name, type_, elem_oid, rngsubtype, basetype, schema) = {
840+
let (name, type_, elem_oid, rngsubtype, basetype, schema, relid) = {
827841
let ctx = SessionInfo::new(self);
828842
let name = try!(String::from_sql(&Type::Name,
829843
&mut &**row[0].as_ref().unwrap(),
@@ -844,7 +858,10 @@ impl InnerConnection {
844858
let schema = try!(String::from_sql(&Type::Name,
845859
&mut &**row[5].as_ref().unwrap(),
846860
&ctx));
847-
(name, type_, elem_oid, rngsubtype, basetype, schema)
861+
let relid = try!(Oid::from_sql(&Type::Oid,
862+
&mut &**row[6].as_ref().unwrap(),
863+
&ctx));
864+
(name, type_, elem_oid, rngsubtype, basetype, schema, relid)
848865
};
849866

850867
let kind = if type_ == b'e' as i8 {
@@ -855,6 +872,28 @@ impl InnerConnection {
855872
Kind::Domain(try!(self.get_type(basetype)))
856873
} else if elem_oid != 0 {
857874
Kind::Array(try!(self.get_type(elem_oid)))
875+
} else if relid != 0 {
876+
try!(self.raw_execute(TYPEINFO_ARRAY_QUERY, "", 0, &[Type::Oid], &[&relid]));
877+
let mut rows = VecDeque::new();
878+
try!(self.read_rows(&mut rows));
879+
880+
let mut fields = vec![];
881+
for row in rows {
882+
let (name, type_) = {
883+
let ctx = SessionInfo::new(self);
884+
let name = try!(String::from_sql(&Type::Name,
885+
&mut &**row[0].as_ref().unwrap(),
886+
&ctx));
887+
let type_ = try!(Oid::from_sql(&Type::Oid,
888+
&mut &**row[1].as_ref().unwrap(),
889+
&ctx));
890+
(name, type_)
891+
};
892+
let type_ = try!(self.get_type(type_));
893+
fields.push(Field::new(name, type_));
894+
}
895+
896+
Kind::Composite(fields)
858897
} else {
859898
match rngsubtype {
860899
Some(oid) => Kind::Range(try!(self.get_type(oid))),
@@ -1549,3 +1588,7 @@ trait NotificationsNew<'conn> {
15491588
trait WrongTypeNew {
15501589
fn new(ty: Type) -> WrongType;
15511590
}
1591+
1592+
trait FieldNew {
1593+
fn new(name: String, type_: Type) -> Field;
1594+
}

src/types/mod.rs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use std::sync::Arc;
88
use byteorder::{ReadBytesExt, WriteBytesExt, BigEndian};
99

1010
pub use self::slice::Slice;
11-
use {Result, SessionInfoNew, InnerConnection, OtherNew, WrongTypeNew};
11+
use {Result, SessionInfoNew, InnerConnection, OtherNew, WrongTypeNew, FieldNew};
1212
use error::Error;
1313
use util;
1414

@@ -115,10 +115,40 @@ pub enum Kind {
115115
Range(Type),
116116
/// A domain type along with its underlying type.
117117
Domain(Type),
118+
/// A composite type along with information about its fields.
119+
Composite(Vec<Field>),
118120
#[doc(hidden)]
119121
__PseudoPrivateForExtensibility,
120122
}
121123

124+
/// Information about a field of a composite type.
125+
#[derive(Debug, Clone, PartialEq, Eq)]
126+
pub struct Field {
127+
name: String,
128+
type_: Type,
129+
}
130+
131+
impl Field {
132+
/// Returns the name of the field.
133+
pub fn name(&self) -> &str {
134+
&self.name
135+
}
136+
137+
/// Returns the type of the field.
138+
pub fn type_(&self) -> &Type {
139+
&self.type_
140+
}
141+
}
142+
143+
impl FieldNew for Field {
144+
fn new(name: String, type_: Type) -> Field {
145+
Field {
146+
name: name,
147+
type_: type_,
148+
}
149+
}
150+
}
151+
122152
macro_rules! as_pat {
123153
($p:pat) => ($p)
124154
}

tests/types/mod.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,3 +281,29 @@ fn domain() {
281281
let rows = conn.query("SELECT id FROM pg_temp.foo", &[]).unwrap();
282282
assert_eq!(id, rows.get(0).get(0));
283283
}
284+
285+
#[test]
286+
fn composite() {
287+
let conn = Connection::connect("postgres://postgres@localhost", SslMode::None).unwrap();
288+
conn.batch_execute("CREATE TYPE pg_temp.inventory_item AS (
289+
name TEXT,
290+
supplier INTEGER,
291+
price NUMERIC
292+
)")
293+
.unwrap();
294+
295+
let stmt = conn.prepare("SELECT $1::inventory_item").unwrap();
296+
let type_ = &stmt.param_types()[0];
297+
assert_eq!(type_.name(), "inventory_item");
298+
match type_.kind() {
299+
&Kind::Composite(ref fields) => {
300+
assert_eq!(fields[0].name(), "name");
301+
assert_eq!(fields[0].type_(), &Type::Text);
302+
assert_eq!(fields[1].name(), "supplier");
303+
assert_eq!(fields[1].type_(), &Type::Int4);
304+
assert_eq!(fields[2].name(), "price");
305+
assert_eq!(fields[2].type_(), &Type::Numeric);
306+
}
307+
t => panic!("bad type {:?}", t),
308+
}
309+
}

0 commit comments

Comments
 (0)