Skip to main content

oxgraph_postgres/artifact/
metadata.rs

1//! Postgres-owned snapshot metadata section (`0x0203`).
2
3use oxgraph_snapshot::{PlanError, Snapshot, SnapshotBuilder, SnapshotError};
4use zerocopy::{
5    FromBytes, Immutable, IntoBytes, KnownLayout,
6    byteorder::{LE, U32, U64},
7};
8
9/// Section kind for serialized catalog blobs owned by Postgres.
10pub const SNAPSHOT_KIND_PG_CATALOG: u32 = 0x0200;
11
12/// Section kind for Postgres engine metadata (`0x0200` reserved range).
13pub const SNAPSHOT_KIND_PG_METADATA: u32 = 0x0203;
14
15/// Fixed-layout Postgres metadata stored in snapshot section payloads.
16#[derive(Clone, Copy, Debug, PartialEq, Eq, FromBytes, Immutable, IntoBytes, KnownLayout)]
17#[repr(C)]
18pub struct PostgresMetadata {
19    /// Metadata schema version.
20    pub version: U32<LE>,
21    /// Bit flags (`READ_ONLY`, `HAS_REVERSE_INDEX`, …).
22    pub flags: U32<LE>,
23    /// Build timestamp in Unix seconds (semantic-free).
24    pub built_at_unix: U64<LE>,
25    /// Node count at build time.
26    pub node_count: U32<LE>,
27    /// Edge count at build time.
28    pub edge_count: U32<LE>,
29}
30
31impl PostgresMetadata {
32    /// Metadata schema version written by this library.
33    pub const VERSION: u32 = 1;
34
35    /// Flag indicating the artifact was published read-only.
36    pub const FLAG_READ_ONLY: u32 = 1;
37
38    /// Flag indicating inbound CSC sections (`0x0201` / `0x0202`) are present.
39    pub const FLAG_HAS_REVERSE_INDEX: u32 = 2;
40
41    /// Creates metadata for a freshly built artifact.
42    #[must_use]
43    pub const fn new(
44        node_count: u32,
45        edge_count: u32,
46        built_at_unix: u64,
47        read_only: bool,
48    ) -> Self {
49        let mut flags = 0_u32;
50        if read_only {
51            flags |= Self::FLAG_READ_ONLY;
52        }
53        Self {
54            version: U32::new(Self::VERSION),
55            flags: U32::new(flags),
56            built_at_unix: U64::new(built_at_unix),
57            node_count: U32::new(node_count),
58            edge_count: U32::new(edge_count),
59        }
60    }
61
62    /// Returns metadata with [`Self::FLAG_HAS_REVERSE_INDEX`] set.
63    #[must_use]
64    pub const fn with_reverse_index(mut self) -> Self {
65        self.flags = U32::new(self.flags.get() | Self::FLAG_HAS_REVERSE_INDEX);
66        self
67    }
68
69    /// Returns whether the read-only flag is set.
70    #[must_use]
71    pub const fn is_read_only(self) -> bool {
72        self.flags.get() & Self::FLAG_READ_ONLY != 0
73    }
74
75    /// Returns whether inbound CSC sections are required at engine open.
76    #[must_use]
77    pub const fn has_reverse_index(self) -> bool {
78        self.flags.get() & Self::FLAG_HAS_REVERSE_INDEX != 0
79    }
80}
81
82/// Errors while reading Postgres-owned snapshot sections.
83#[derive(Debug, Clone, PartialEq, Eq)]
84pub enum PostgresSectionError {
85    /// Underlying snapshot validation failed.
86    Snapshot(SnapshotError),
87    /// Expected Postgres section was absent.
88    MissingSection,
89    /// Section payload could not be interpreted.
90    Malformed(alloc::string::String),
91}
92
93/// Reads [`PostgresMetadata`] from a snapshot's Postgres section.
94///
95/// # Errors
96///
97/// Returns [`PostgresSectionError`] when the section is missing or malformed.
98///
99/// # Performance
100///
101/// This function is `O(s)`.
102pub(super) fn read_postgres_metadata(
103    snapshot: &Snapshot<'_>,
104) -> Result<PostgresMetadata, PostgresSectionError> {
105    let section = snapshot
106        .section(SNAPSHOT_KIND_PG_METADATA)
107        .ok_or(PostgresSectionError::MissingSection)?;
108    PostgresMetadata::ref_from_bytes(section.bytes())
109        .map_err(|error| {
110            PostgresSectionError::Malformed(alloc::format!("postgres metadata layout: {error}"))
111        })
112        .copied()
113}
114
115/// Writes the Postgres metadata section into a snapshot builder.
116///
117/// # Errors
118///
119/// Returns [`SnapshotError`] when planning or encoding fails.
120///
121/// # Performance
122///
123/// This function is `O(1)`.
124pub(super) fn write_postgres_metadata_section(
125    builder: &mut SnapshotBuilder,
126    metadata: &PostgresMetadata,
127) -> Result<(), PlanError> {
128    builder.add_section_typed(
129        SNAPSHOT_KIND_PG_METADATA,
130        PostgresMetadata::VERSION,
131        core::slice::from_ref(metadata),
132    )?;
133    Ok(())
134}