Store element type for unknown types
This commit is contained in:
parent
08460b889e
commit
e0758ed5b8
78
src/lib.rs
78
src/lib.rs
@ -389,7 +389,7 @@ struct InnerConnection {
|
||||
notice_handler: Box<NoticeHandler>,
|
||||
notifications: RingBuf<Notification>,
|
||||
cancel_data: CancelData,
|
||||
unknown_types: HashMap<Oid, String>,
|
||||
unknown_types: HashMap<Oid, Type>,
|
||||
desynchronized: bool,
|
||||
finished: bool,
|
||||
trans_depth: u32,
|
||||
@ -457,7 +457,7 @@ impl InnerConnection {
|
||||
}
|
||||
|
||||
match conn.raw_prepare(TYPENAME_QUERY,
|
||||
"SELECT typname FROM pg_catalog.pg_type WHERE oid = $1") {
|
||||
"SELECT typname, typelem FROM pg_catalog.pg_type WHERE oid = $1") {
|
||||
Ok(..) => {}
|
||||
Err(Error::IoError(e)) => return Err(ConnectError::IoError(e)),
|
||||
Err(Error::DbError(e)) => return Err(ConnectError::DbError(e)),
|
||||
@ -580,34 +580,30 @@ impl InnerConnection {
|
||||
_ => bad_response!(self),
|
||||
}
|
||||
|
||||
let mut param_types: Vec<_> = match try!(self.read_message()) {
|
||||
ParameterDescription { types } => {
|
||||
types.into_iter().map(Type::from_oid).collect()
|
||||
}
|
||||
let raw_param_types = match try!(self.read_message()) {
|
||||
ParameterDescription { types } => types,
|
||||
_ => bad_response!(self),
|
||||
};
|
||||
|
||||
let mut result_desc: Vec<_> = match try!(self.read_message()) {
|
||||
RowDescription { descriptions } => {
|
||||
descriptions.into_iter().map(|RowDescriptionEntry { name, type_oid, .. }| {
|
||||
ResultDescription {
|
||||
name: name,
|
||||
ty: Type::from_oid(type_oid)
|
||||
}
|
||||
}).collect()
|
||||
}
|
||||
let raw_result_desc = match try!(self.read_message()) {
|
||||
RowDescription { descriptions } => descriptions,
|
||||
NoData => vec![],
|
||||
_ => bad_response!(self)
|
||||
};
|
||||
|
||||
try!(self.wait_for_ready());
|
||||
|
||||
// now that the connection is ready again, get unknown type names,
|
||||
try!(self.set_type_names(param_types.iter_mut()));
|
||||
// An empty statement name means we're calling execute, so we don't
|
||||
// care about result types
|
||||
if stmt_name != "" {
|
||||
try!(self.set_type_names(result_desc.iter_mut().map(|d| &mut d.ty)));
|
||||
let mut param_types = vec![];
|
||||
for oid in raw_param_types.into_iter() {
|
||||
param_types.push(try!(self.get_type(oid)));
|
||||
}
|
||||
|
||||
let mut result_desc = vec![];
|
||||
for RowDescriptionEntry { name, type_oid, .. } in raw_result_desc.into_iter() {
|
||||
result_desc.push(ResultDescription {
|
||||
name: name,
|
||||
ty: try!(self.get_type(type_oid)),
|
||||
});
|
||||
}
|
||||
|
||||
Ok((param_types, result_desc))
|
||||
@ -674,21 +670,15 @@ impl InnerConnection {
|
||||
resp
|
||||
}
|
||||
|
||||
fn set_type_names<'a, I>(&mut self, mut it: I) -> Result<()>
|
||||
where I: Iterator<Item=&'a mut Type> {
|
||||
for ty in it {
|
||||
if let &mut Type::Unknown { oid, ref mut name } = ty {
|
||||
*name = try!(self.get_type_name(oid));
|
||||
}
|
||||
fn get_type(&mut self, oid: Oid) -> Result<Type> {
|
||||
if let Some(ty) = Type::from_oid(oid) {
|
||||
return Ok(ty);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_type_name(&mut self, oid: Oid) -> Result<String> {
|
||||
if let Some(name) = self.unknown_types.get(&oid) {
|
||||
return Ok(name.clone());
|
||||
if let Some(ty) = self.unknown_types.get(&oid) {
|
||||
return Ok(ty.clone());
|
||||
}
|
||||
|
||||
// Ew @ doing this manually :(
|
||||
try!(self.write_messages(&[
|
||||
Bind {
|
||||
@ -711,8 +701,11 @@ impl InnerConnection {
|
||||
}
|
||||
_ => bad_response!(self)
|
||||
}
|
||||
let name: String = match try!(self.read_message()) {
|
||||
DataRow { row } => try!(FromSql::from_sql(&Type::Name, row[0].as_ref().map(|r| &**r))),
|
||||
let (name, elem_oid): (String, Oid) = match try!(self.read_message()) {
|
||||
DataRow { row } => {
|
||||
(try!(FromSql::from_sql(&Type::Name, row[0].as_ref().map(|r| &**r))),
|
||||
try!(FromSql::from_sql(&Type::Oid, row[1].as_ref().map(|r| &**r))))
|
||||
}
|
||||
ErrorResponse { fields } => {
|
||||
try!(self.wait_for_ready());
|
||||
return DbError::new(fields);
|
||||
@ -728,8 +721,19 @@ impl InnerConnection {
|
||||
_ => bad_response!(self)
|
||||
}
|
||||
try!(self.wait_for_ready());
|
||||
self.unknown_types.insert(oid, name.clone());
|
||||
Ok(name)
|
||||
|
||||
let element_type = if elem_oid != 0 {
|
||||
Some(Box::new(try!(self.get_type(oid))))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let type_ = Type::Unknown {
|
||||
oid: oid,
|
||||
name: name,
|
||||
element_type: element_type,
|
||||
};
|
||||
self.unknown_types.insert(oid, type_.clone());
|
||||
Ok(type_)
|
||||
}
|
||||
|
||||
fn is_desynchronized(&self) -> bool {
|
||||
|
@ -23,7 +23,7 @@ macro_rules! check_desync {
|
||||
|
||||
macro_rules! bad_response {
|
||||
($s:expr) => ({
|
||||
debug!("Unexpected response");
|
||||
debug!("Bad response at {}:{}", file!(), line!());
|
||||
$s.desynchronized = true;
|
||||
return Err(::Error::BadResponse);
|
||||
})
|
||||
|
@ -360,7 +360,7 @@ fn read_fields<R: Buffer>(buf: &mut R) -> IoResult<Vec<(u8, String)>> {
|
||||
}
|
||||
|
||||
fn read_data_row<R: Buffer>(buf: &mut R) -> IoResult<BackendMessage> {
|
||||
let len = try!(buf.read_be_i16()) as usize;
|
||||
let len = try!(buf.read_be_u16()) as usize;
|
||||
let mut values = Vec::with_capacity(len);
|
||||
|
||||
for _ in range(0, len) {
|
||||
|
@ -195,7 +195,9 @@ macro_rules! make_postgres_type {
|
||||
/// The name of the type
|
||||
name: String,
|
||||
/// The OID of the type
|
||||
oid: Oid
|
||||
oid: Oid,
|
||||
/// If this type is an array, the type of its members
|
||||
element_type: Option<Box<Type>>,
|
||||
}
|
||||
}
|
||||
|
||||
@ -203,8 +205,10 @@ macro_rules! make_postgres_type {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
let s = match *self {
|
||||
$(Type::$variant => stringify!($variant),)+
|
||||
Type::Unknown { ref name, ref oid } => {
|
||||
return write!(fmt, "Unknown {{ name: {:?}, oid: {:?} }}", name, oid);
|
||||
Type::Unknown { ref name, ref oid, ref element_type } => {
|
||||
return write!(fmt, "Unknown {{ name: {:?}, oid: {:?}, \
|
||||
element_type: {:?} }}",
|
||||
name, oid, element_type);
|
||||
}
|
||||
};
|
||||
fmt.write_str(s)
|
||||
@ -226,13 +230,11 @@ macro_rules! make_postgres_type {
|
||||
impl Type {
|
||||
/// Creates a `Type` from an OID.
|
||||
///
|
||||
/// If the OID is unknown, the `name` field is initialized to an
|
||||
/// empty string.
|
||||
pub fn from_oid(oid: Oid) -> Type {
|
||||
/// If the OID is unknown, `None` is returned.
|
||||
pub fn from_oid(oid: Oid) -> Option<Type> {
|
||||
match oid {
|
||||
$($oid => Type::$variant,)+
|
||||
// We have to load an empty string now, it'll get filled in later
|
||||
oid => Type::Unknown { name: String::new(), oid: oid }
|
||||
$($oid => Some(Type::$variant),)+
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
@ -244,12 +246,13 @@ macro_rules! make_postgres_type {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the member `Type` if this `Type` is an array.
|
||||
pub fn member_type(&self) -> Option<Type> {
|
||||
/// If this `Type` is an array, returns the type of its elements
|
||||
pub fn element_type(&self) -> Option<Type> {
|
||||
match *self {
|
||||
$(
|
||||
$(Type::$variant => Some(Type::$member),)*
|
||||
)+
|
||||
Type::Unknown { element_type: Some(ref t), .. } => Some((**t).clone()),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user