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