use crate::{
copy_metadata::{Column, TableDataMetadata, TableDataRepresentation},
error::PgDumpError,
};
use std::str::Utf8Error;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ArchiveVersion {
major: u8,
minor: u8,
revision: u8,
}
impl ArchiveVersion {
pub const fn new(major: u8, minor: u8, revision: u8) -> Self {
Self {
major,
minor,
revision,
}
}
pub const fn major(self) -> u8 {
self.major
}
pub const fn minor(self) -> u8 {
self.minor
}
pub const fn revision(self) -> u8 {
self.revision
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
pub struct ArchiveString(Vec<u8>);
impl ArchiveString {
pub(crate) fn from_bytes(bytes: Vec<u8>) -> Self {
Self(bytes)
}
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
pub fn to_str(&self) -> Result<&str, Utf8Error> {
std::str::from_utf8(&self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Compression {
None,
Gzip,
Lz4,
Zstd,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ArchiveTimestamp {
second: i32,
minute: i32,
hour: i32,
day_of_month: i32,
month_zero_based: i32,
year_since_1900: i32,
is_dst: i32,
}
impl ArchiveTimestamp {
pub(crate) const fn new(
second: i32,
minute: i32,
hour: i32,
day_of_month: i32,
month_zero_based: i32,
year_since_1900: i32,
is_dst: i32,
) -> Self {
Self {
second,
minute,
hour,
day_of_month,
month_zero_based,
year_since_1900,
is_dst,
}
}
pub const fn second(&self) -> i32 {
self.second
}
pub const fn minute(&self) -> i32 {
self.minute
}
pub const fn hour(&self) -> i32 {
self.hour
}
pub const fn day_of_month(&self) -> i32 {
self.day_of_month
}
pub const fn month_zero_based(&self) -> i32 {
self.month_zero_based
}
pub const fn year_since_1900(&self) -> i32 {
self.year_since_1900
}
pub const fn is_dst(&self) -> i32 {
self.is_dst
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ArchiveHeader {
version: ArchiveVersion,
integer_size: u8,
offset_size: u8,
compression: Compression,
created_at: ArchiveTimestamp,
database_name: ArchiveString,
server_version: ArchiveString,
dump_version: ArchiveString,
}
impl ArchiveHeader {
#[allow(clippy::too_many_arguments)]
pub(crate) fn new(
version: ArchiveVersion,
integer_size: u8,
offset_size: u8,
compression: Compression,
created_at: ArchiveTimestamp,
database_name: ArchiveString,
server_version: ArchiveString,
dump_version: ArchiveString,
) -> Self {
Self {
version,
integer_size,
offset_size,
compression,
created_at,
database_name,
server_version,
dump_version,
}
}
pub const fn version(&self) -> ArchiveVersion {
self.version
}
pub const fn integer_size(&self) -> u8 {
self.integer_size
}
pub const fn offset_size(&self) -> u8 {
self.offset_size
}
pub const fn compression(&self) -> Compression {
self.compression
}
pub const fn created_at(&self) -> &ArchiveTimestamp {
&self.created_at
}
pub const fn database_name(&self) -> &ArchiveString {
&self.database_name
}
pub const fn server_version(&self) -> &ArchiveString {
&self.server_version
}
pub const fn dump_version(&self) -> &ArchiveString {
&self.dump_version
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DumpId(i32);
impl DumpId {
pub(crate) const fn from_valid(value: i32) -> Self {
Self(value)
}
pub const fn as_i32(self) -> i32 {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Section {
None,
PreData,
Data,
PostData,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum DataLocation {
NoData,
Unknown,
Offset(u64),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TocEntry {
id: DumpId,
has_data: bool,
catalog_table_oid: ArchiveString,
catalog_oid: ArchiveString,
name: ArchiveString,
description: ArchiveString,
section: Section,
definition: Option<ArchiveString>,
drop_statement: Option<ArchiveString>,
copy_statement: Option<ArchiveString>,
namespace: Option<ArchiveString>,
tablespace: Option<ArchiveString>,
table_access_method: Option<ArchiveString>,
relation_kind: Option<i32>,
owner: Option<ArchiveString>,
_with_oids: ArchiveString,
dependencies: Vec<DumpId>,
data_location: DataLocation,
}
impl TocEntry {
#[allow(clippy::too_many_arguments)]
pub(crate) fn new(
id: DumpId,
has_data: bool,
catalog_table_oid: ArchiveString,
catalog_oid: ArchiveString,
name: ArchiveString,
description: ArchiveString,
section: Section,
definition: Option<ArchiveString>,
drop_statement: Option<ArchiveString>,
copy_statement: Option<ArchiveString>,
namespace: Option<ArchiveString>,
tablespace: Option<ArchiveString>,
table_access_method: Option<ArchiveString>,
relation_kind: Option<i32>,
owner: Option<ArchiveString>,
with_oids: ArchiveString,
dependencies: Vec<DumpId>,
data_location: DataLocation,
) -> Self {
Self {
id,
has_data,
catalog_table_oid,
catalog_oid,
name,
description,
section,
definition,
drop_statement,
copy_statement,
namespace,
tablespace,
table_access_method,
relation_kind,
owner,
_with_oids: with_oids,
dependencies,
data_location,
}
}
pub const fn id(&self) -> DumpId {
self.id
}
pub const fn has_data(&self) -> bool {
self.has_data
}
pub const fn catalog_table_oid(&self) -> &ArchiveString {
&self.catalog_table_oid
}
pub const fn catalog_oid(&self) -> &ArchiveString {
&self.catalog_oid
}
pub const fn name(&self) -> &ArchiveString {
&self.name
}
pub fn name_bytes(&self) -> &[u8] {
self.name.as_bytes()
}
pub const fn description(&self) -> &ArchiveString {
&self.description
}
pub fn description_bytes(&self) -> &[u8] {
self.description.as_bytes()
}
pub const fn section(&self) -> Section {
self.section
}
pub const fn definition(&self) -> Option<&ArchiveString> {
self.definition.as_ref()
}
pub const fn drop_statement(&self) -> Option<&ArchiveString> {
self.drop_statement.as_ref()
}
pub const fn copy_statement(&self) -> Option<&ArchiveString> {
self.copy_statement.as_ref()
}
pub const fn namespace(&self) -> Option<&ArchiveString> {
self.namespace.as_ref()
}
pub fn namespace_bytes(&self) -> Option<&[u8]> {
self.namespace.as_ref().map(ArchiveString::as_bytes)
}
pub const fn tablespace(&self) -> Option<&ArchiveString> {
self.tablespace.as_ref()
}
pub const fn table_access_method(&self) -> Option<&ArchiveString> {
self.table_access_method.as_ref()
}
pub const fn relation_kind(&self) -> Option<i32> {
self.relation_kind
}
pub const fn owner(&self) -> Option<&ArchiveString> {
self.owner.as_ref()
}
pub fn owner_bytes(&self) -> Option<&[u8]> {
self.owner.as_ref().map(ArchiveString::as_bytes)
}
pub fn dependencies(&self) -> &[DumpId] {
&self.dependencies
}
pub const fn data_location(&self) -> DataLocation {
self.data_location
}
pub(crate) fn is_table(&self) -> bool {
self.description.as_bytes() == b"TABLE"
}
pub(crate) fn is_table_data(&self) -> bool {
self.description.as_bytes() == b"TABLE DATA"
}
pub(crate) fn copy_statement_bytes(&self) -> Option<&[u8]> {
self.copy_statement.as_ref().map(ArchiveString::as_bytes)
}
pub(crate) fn catalog_table_oid_bytes(&self) -> &[u8] {
self.catalog_table_oid.as_bytes()
}
pub(crate) fn catalog_oid_bytes(&self) -> &[u8] {
self.catalog_oid.as_bytes()
}
}
#[derive(Debug, Clone, Copy)]
pub struct TableRef<'a> {
table: &'a TocEntry,
data: Option<&'a TocEntry>,
data_metadata: Option<&'a TableDataMetadata>,
}
impl<'a> TableRef<'a> {
pub(crate) const fn new(
table: &'a TocEntry,
data: Option<&'a TocEntry>,
data_metadata: Option<&'a TableDataMetadata>,
) -> Self {
Self {
table,
data,
data_metadata,
}
}
pub fn schema(&self) -> Option<&[u8]> {
self.table.namespace_bytes()
}
pub fn name(&self) -> &[u8] {
self.table.name_bytes()
}
pub const fn table_entry_id(&self) -> DumpId {
self.table.id()
}
pub fn data_entry_id(&self) -> Option<DumpId> {
self.data.map(TocEntry::id)
}
pub fn data_representation(&self) -> Result<TableDataRepresentation, PgDumpError> {
let (data, metadata) = self.require_data_metadata()?;
metadata.representation(data.id())
}
pub fn columns(&self) -> Result<&[Column], PgDumpError> {
let (data, metadata) = self.require_data_metadata()?;
metadata.columns(data.id())
}
pub fn column_index(&self, name: &[u8]) -> Result<Option<usize>, PgDumpError> {
let (data, metadata) = self.require_data_metadata()?;
metadata.column_index(data.id(), name)
}
fn require_data_metadata(&self) -> Result<(&TocEntry, &TableDataMetadata), PgDumpError> {
let data = self.data.ok_or(PgDumpError::TableDataEntryUnavailable {
table_id: self.table.id().as_i32(),
})?;
let metadata = self
.data_metadata
.ok_or(PgDumpError::CopyColumnMetadataUnavailable {
dump_id: data.id().as_i32(),
})?;
Ok((data, metadata))
}
}