use crate::protocol::parts::type_id::TypeId;
use crate::protocol::util;
use byteorder::{LittleEndian, ReadBytesExt};
use std::{ops::Deref, rc::Rc};
use vec_map::VecMap;
#[derive(Debug)]
pub struct ResultSetMetadata(Vec<FieldMetadata>);
impl Deref for ResultSetMetadata {
type Target = Vec<FieldMetadata>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl std::fmt::Display for ResultSetMetadata {
#[allow(clippy::significant_drop_in_scrutinee)]
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
writeln!(fmt)?;
for field_metadata in &self.0 {
write!(fmt, "{}, ", field_metadata.displayname())?;
}
writeln!(fmt)?;
Ok(())
}
}
impl ResultSetMetadata {
pub(crate) fn parse(
count: usize,
rdr: &mut dyn std::io::Read,
) -> std::io::Result<ResultSetMetadata> {
fn add_to_names(names: &mut VecMap<String>, offset: u32) {
if offset != u32::max_value() {
let offset = offset as usize;
if !names.contains_key(offset) {
names.insert(offset, String::new());
};
}
}
let mut inner_fms = Vec::<InnerFieldMetadata>::new();
let mut names = VecMap::<String>::new();
trace!("Got count {}", count);
for _ in 0..count {
let column_options = rdr.read_u8()?; let type_code = rdr.read_u8()?; let scale = rdr.read_i16::<LittleEndian>()?; let precision = rdr.read_i16::<LittleEndian>()?; rdr.read_i16::<LittleEndian>()?; let tablename_idx = rdr.read_u32::<LittleEndian>()?; add_to_names(&mut names, tablename_idx);
let schemaname_idx = rdr.read_u32::<LittleEndian>()?; add_to_names(&mut names, schemaname_idx);
let columnname_idx = rdr.read_u32::<LittleEndian>()?; add_to_names(&mut names, columnname_idx);
let displayname_idx = rdr.read_u32::<LittleEndian>()?; add_to_names(&mut names, displayname_idx);
let type_id = TypeId::try_new(type_code)?;
let fm = InnerFieldMetadata {
schemaname_idx,
tablename_idx,
columnname_idx,
displayname_idx,
column_options,
type_id,
scale,
precision,
};
inner_fms.push(fm);
}
let mut offset = 0;
for _ in 0..names.len() {
let nl = rdr.read_u8()?; let name = util::string_from_cesu8(util::parse_bytes(nl as usize, rdr)?)
.map_err(util::io_error)?; trace!("offset = {}, name = {}", offset, name);
names.insert(offset as usize, name.to_string());
offset += u32::from(nl) + 1;
}
let names = Rc::new(names);
Ok(ResultSetMetadata(
inner_fms
.into_iter()
.map(|inner| FieldMetadata {
inner,
names: Rc::clone(&names),
})
.collect(),
))
}
}
#[derive(Clone, Debug)]
pub struct FieldMetadata {
inner: InnerFieldMetadata,
names: Rc<VecMap<String>>,
}
#[derive(Clone, Copy, Debug)]
struct InnerFieldMetadata {
schemaname_idx: u32,
tablename_idx: u32,
columnname_idx: u32,
displayname_idx: u32,
column_options: u8,
type_id: TypeId,
scale: i16,
precision: i16,
}
impl FieldMetadata {
pub fn schemaname(&self) -> &str {
self.names
.get(self.inner.schemaname_idx as usize)
.map_or("", String::as_str)
}
pub fn tablename(&self) -> &str {
self.names
.get(self.inner.tablename_idx as usize)
.map_or("", String::as_str)
}
pub fn columnname(&self) -> &str {
self.names
.get(self.inner.columnname_idx as usize)
.map_or("", String::as_str)
}
pub fn displayname(&self) -> &str {
self.names
.get(self.inner.displayname_idx as usize)
.map_or("", String::as_str)
}
pub fn type_id(&self) -> TypeId {
self.inner.type_id
}
pub fn is_nullable(&self) -> bool {
(self.inner.column_options & 0b_0000_0010_u8) != 0
}
pub fn precision(&self) -> i16 {
self.inner.precision
}
pub fn scale(&self) -> i16 {
self.inner.scale
}
pub fn has_default(&self) -> bool {
(self.inner.column_options & 0b_0000_0100_u8) != 0
}
pub fn is_read_only(&self) -> bool {
(self.inner.column_options & 0b_0100_0000_u8) != 0
}
pub fn is_auto_incremented(&self) -> bool {
(self.inner.column_options & 0b_0010_0000_u8) != 0
}
pub fn is_array_type(&self) -> bool {
(self.inner.column_options & 0b_0100_0000_u8) != 0
}
}