Store element type for unknown types

This commit is contained in:
Steven Fackler 2015-01-21 22:11:43 -08:00
parent 08460b889e
commit e0758ed5b8
4 changed files with 57 additions and 50 deletions

View File

@ -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 {

View File

@ -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);
})

View File

@ -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) {

View File

@ -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
}
}