use crate::archive::{K_VERS_1_10, K_VERS_1_11, K_VERS_1_14, K_VERS_1_16};
use crate::io::ReadConfig;
use crate::types::{ArchiveError, Offset, Oid, Section};
use crate::Version;
use std::io::prelude::*;
pub type ID = i64;
#[derive(Debug, PartialEq, Clone)]
pub struct TocEntry {
pub id: ID,
pub had_dumper: bool,
pub table_oid: u64,
pub oid: Oid,
pub tag: String,
pub desc: String,
pub section: Section,
pub defn: String,
pub drop_stmt: String,
pub copy_stmt: String,
pub namespace: String,
pub tablespace: String,
pub table_access_method: String,
pub owner: String,
pub dependencies: Vec<ID>,
pub offset: Offset,
}
impl TocEntry {
pub fn parse(
f: &mut (impl Read + ?Sized),
cfg: &ReadConfig,
version: Version,
) -> Result<TocEntry, ArchiveError> {
let id: ID = cfg.read_int(f)?;
if id < 0 {
return Err(ArchiveError::InvalidEntryData(id, "negative TOC id".into()));
}
let had_dumper = cfg.read_int_bool(f)?;
let table_oid = cfg.read_oid(f)?;
let oid = cfg.read_oid(f)?;
let tag = cfg.read_string(f)?;
let desc = cfg.read_string(f)?;
let section: Section = if version >= K_VERS_1_11 {
cfg.read_int(f)?
.try_into()
.or(Err(ArchiveError::InvalidEntryData(
id,
"invalid section type".into(),
)))?
} else {
Section::None
};
let defn = cfg.read_string(f)?;
let drop_stmt = cfg.read_string(f)?;
let copy_stmt = cfg.read_string(f)?;
let namespace = cfg.read_string(f)?;
let tablespace = if version >= K_VERS_1_10 {
cfg.read_string(f)?
} else {
String::new()
};
let table_access_method = if version >= K_VERS_1_14 {
cfg.read_string(f)?
} else {
String::new()
};
let _relkind = if version >= K_VERS_1_16 {
cfg.read_int(f)?
} else {
0
};
let owner = cfg.read_string(f)?;
if cfg.read_string_bool(f)? {
return Err(ArchiveError::InvalidEntryData(
id,
"mysterious value must be false".into(),
));
}
let mut dependencies = Vec::new();
loop {
let dep_id = cfg.read_string(f)?;
if dep_id.is_empty() {
break;
}
dependencies.push(ID::from_str_radix(dep_id.as_str(), 10).or(Err(
ArchiveError::InvalidEntryData(id, "invalid dependency id".into()),
))?);
}
let offset = cfg.read_offset(f)?;
Ok(TocEntry {
id,
had_dumper,
table_oid,
oid,
tag,
desc,
section,
defn,
drop_stmt,
copy_stmt,
namespace,
tablespace,
table_access_method,
owner,
dependencies,
offset,
})
}
}
pub fn read_toc(
f: &mut (impl Read + ?Sized),
cfg: &ReadConfig,
version: Version,
) -> Result<Vec<TocEntry>, ArchiveError> {
let num_entries = cfg.read_int(f)?;
let mut entries = Vec::with_capacity(num_entries as usize);
for _ in 0..num_entries {
entries.push(TocEntry::parse(f, cfg, version)?);
}
Ok(entries)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::archive::K_VERS_1_15;
use hex_literal::hex;
#[test]
fn encoding_toc_entry() -> Result<(), ArchiveError> {
let mut input = &hex!(
"00 8e 11 00 00" "00 00 00 00 00" "00 01 00 00 00 30" "00 01 00 00 00 30" "00 08 00 00 00 45 4e 43 4f 44 49 4e 47" "00 08 00 00 00 45 4e 43 4f 44 49 4e 47" "00 02 00 00 00" "00 1e 00 00 00 53 45 54 20 63 6c 69 65 6e 74 5f 65 6e 63 6f 64 69 6e 67 20 3d 20 27 55 54 46 38 27 3b 0a" "01 01 00 00 00" "01 01 00 00 00" "01 01 00 00 00" "01 01 00 00 00" "01 01 00 00 00" "01 01 00 00 00" "00 05 00 00 00 66 61 6c 73 65" "01 01 00 00 00" "03" "00 00 00 00 00 00 00 00" )[..];
let cfg = ReadConfig {
int_size: 4,
offset_size: 8,
};
let entry = TocEntry::parse(&mut input, &cfg, K_VERS_1_15)?;
assert_eq!(
entry,
TocEntry {
id: 0x118e,
had_dumper: false,
table_oid: 0,
oid: 0,
tag: String::from("ENCODING"),
desc: String::from("ENCODING"),
section: Section::PreData,
defn: String::from("SET client_encoding = 'UTF8';\x0a"),
drop_stmt: String::from(""),
copy_stmt: String::from(""),
namespace: String::from(""),
tablespace: String::from(""),
table_access_method: String::from(""),
owner: String::from(""),
dependencies: vec![],
offset: Offset::NoData,
}
);
Ok(())
}
#[test]
fn extension_toc_entry() -> Result<(), ArchiveError> {
let mut input = &hex!(
"00 02 00 00 00" "00 00 00 00 00" "00 04 00 00 00 33 30 37 39" "00 05 00 00 00 33 33 37 30 38" "00 07 00 00 00 70 6f 73 74 67 69 73" "00 09 00 00 00 45 58 54 45 4e 53 49 4f 4e" "00 02 00 00 00" "00 3b 00 00 00 43 52 45 41 54 45 20 45 58 54 45 4e 53 49 4f 4e 20 49 46 20 4e 4f 54 20 45 58 49 53 54 53 20 70 6f 73 74 67 69 73 20 57 49 54 48 20 53 43 48 45 4d 41 20 70 75 62 6c 69 63 3b 0a" "00 18 00 00 00 44 52 4f 50 20 45 58 54 45 4e 53 49 4f 4e 20 70 6f 73 74 67 69 73 3b 0a" "01 01 00 00 00" "01 01 00 00 00" "01 01 00 00 00" "01 01 00 00 00" "01 01 00 00 00" "00 05 00 00 00 66 61 6c 73 65" "01 01 00 00 00" "03" "00 00 00 00 00 00 00 00" )[..];
let cfg = ReadConfig {
int_size: 4,
offset_size: 8,
};
let entry = TocEntry::parse(&mut input, &cfg, K_VERS_1_15)?;
assert_eq!(
entry,
TocEntry {
id: 2,
had_dumper: false,
table_oid: 3079,
oid: 33708,
tag: String::from("postgis"),
desc: String::from("EXTENSION"),
section: Section::PreData,
defn: String::from(
"CREATE EXTENSION IF NOT EXISTS postgis WITH SCHEMA public;\x0a"
),
drop_stmt: String::from("DROP EXTENSION postgis;\x0a"),
copy_stmt: String::from(""),
namespace: String::from(""),
tablespace: String::from(""),
table_access_method: String::from(""),
owner: String::from(""),
dependencies: vec![],
offset: Offset::NoData,
}
);
Ok(())
}
#[test]
fn table_data_toc_entry() -> Result<(), ArchiveError> {
let mut input = &hex!(
"00 8a 11 00 00" "00 01 00 00 00" "00 01 00 00 00 31" "00 05 00 00 00 33 33 36 38 36" "00 05 00 00 00 70 69 7a 7a 61" "00 0a 00 00 00 54 41 42 4c 45 20 44 41 54 41" "00 03 00 00 00" "01 01 00 00 00" "01 01 00 00 00" "00 2f 00 00 00 43 4f 50 59 20 70 75 62 6c 69 63 2e 70 69 7a 7a 61 20 28 70 69 7a 7a 61 5f 69 64 2c 20 6e 61 6d 65 29 20 46 52 4f 4d 20 73 74 64 69 6e 3b 0a" "00 06 00 00 00 70 75 62 6c 69 63" "01 01 00 00 00" "01 01 00 00 00" "00 07 00 00 00 77 69 63 68 65 72 74" "00 05 00 00 00 66 61 6c 73 65" "00 03 00 00 00 32 31 33" "01 01 00 00 00" "02" "d7 16 00 00 00 00 00 00" )[..];
let cfg = ReadConfig {
int_size: 4,
offset_size: 8,
};
let entry = TocEntry::parse(&mut input, &cfg, K_VERS_1_15)?;
assert_eq!(
entry,
TocEntry {
id: 0x118a,
had_dumper: true,
table_oid: 1,
oid: 33686,
tag: String::from("pizza"),
desc: String::from("TABLE DATA"),
section: Section::Data,
defn: String::from(""),
drop_stmt: String::from(""),
copy_stmt: String::from("COPY public.pizza (pizza_id, name) FROM stdin;\x0a"),
namespace: String::from("public"),
tablespace: String::from(""),
table_access_method: String::from(""),
owner: String::from("wichert"),
dependencies: vec![213],
offset: Offset::PosSet(0x16d7),
}
);
Ok(())
}
#[test]
fn empty_toc() -> Result<(), ArchiveError> {
let mut input = &hex!("00 00 00 00 00")[..];
let cfg = ReadConfig {
int_size: 4,
offset_size: 8,
};
let toc = read_toc(&mut input, &cfg, K_VERS_1_15)?;
assert!(toc.is_empty());
Ok(())
}
#[test]
fn single_entry_toc() -> Result<(), ArchiveError> {
let mut input = &hex!(
"00 01 00 00 00"
"00 8e 11 00 00" "00 00 00 00 00" "00 01 00 00 00 30" "00 01 00 00 00 30" "00 08 00 00 00 45 4e 43 4f 44 49 4e 47" "00 08 00 00 00 45 4e 43 4f 44 49 4e 47" "00 02 00 00 00" "00 1e 00 00 00 53 45 54 20 63 6c 69 65 6e 74 5f 65 6e 63 6f 64 69 6e 67 20 3d 20 27 55 54 46 38 27 3b 0a" "01 01 00 00 00" "01 01 00 00 00" "01 01 00 00 00" "01 01 00 00 00" "01 01 00 00 00" "01 01 00 00 00" "00 05 00 00 00 66 61 6c 73 65" "01 01 00 00 00" "03" "00 00 00 00 00 00 00 00" )[..];
let cfg = ReadConfig {
int_size: 4,
offset_size: 8,
};
let toc = read_toc(&mut input, &cfg, K_VERS_1_15)?;
assert_eq!(toc.len(), 1);
Ok(())
}
}