Skip to main content

oxgraph_postgres/artifact/
sections.rs

1//! Postgres-owned snapshot section assembly (inbound CSC + metadata).
2
3use oxgraph_csr::CsrSnapshotIndex;
4use oxgraph_snapshot::{Snapshot, SnapshotBuilder};
5
6use super::metadata::{
7    PostgresMetadata, SNAPSHOT_KIND_PG_INBOUND_OFFSETS_U32, SNAPSHOT_KIND_PG_INBOUND_TARGETS_U32,
8    write_postgres_metadata_section,
9};
10use crate::error::{BuildError, PostgresGraphError};
11
12/// Appends or replaces Postgres-owned sections on CSR topology bytes.
13///
14/// Forward topology sections are preserved. When `inbound_topology_bytes` is
15/// [`Some`], CSR offsets/targets are copied into inbound section kinds
16/// `0x0201` / `0x0202`.
17///
18/// # Errors
19///
20/// Returns [`PostgresGraphError::Snapshot`] when input bytes are invalid, or
21/// [`PostgresGraphError::Build`] when re-encoding fails.
22///
23/// # Performance
24///
25/// This function is `O(b + s)`.
26pub fn attach_postgres_sections(
27    forward_topology_bytes: &[u8],
28    inbound_topology_bytes: Option<&[u8]>,
29    metadata: &PostgresMetadata,
30) -> Result<Vec<u8>, PostgresGraphError> {
31    let forward = Snapshot::open(forward_topology_bytes)?;
32    let inbound = inbound_topology_bytes.map(Snapshot::open).transpose()?;
33    let mut builder = SnapshotBuilder::new();
34    for section in forward.sections() {
35        if is_postgres_owned_kind(section.kind()) {
36            continue;
37        }
38        builder.add_section(
39            section.kind(),
40            section.version(),
41            alignment_log2_from_section(&section),
42            section.bytes().to_vec(),
43        )?;
44    }
45    if let Some(inbound_snapshot) = inbound.as_ref() {
46        copy_csr_section(
47            inbound_snapshot,
48            u32::OFFSETS_KIND,
49            SNAPSHOT_KIND_PG_INBOUND_OFFSETS_U32,
50            &mut builder,
51        )?;
52        copy_csr_section(
53            inbound_snapshot,
54            u32::TARGETS_KIND,
55            SNAPSHOT_KIND_PG_INBOUND_TARGETS_U32,
56            &mut builder,
57        )?;
58    }
59    write_postgres_metadata_section(&mut builder, metadata)?;
60    builder.finish().map_err(PostgresGraphError::from)
61}
62
63/// Appends or replaces the Postgres metadata section on CSR topology bytes.
64///
65/// Prefer [`attach_postgres_sections`] when inbound CSC bytes are available.
66///
67/// # Errors
68///
69/// Returns [`PostgresGraphError::Snapshot`] when input bytes are invalid, or
70/// [`PostgresGraphError::Build`] when re-encoding fails.
71///
72/// # Performance
73///
74/// This function is `O(b + s)`.
75pub fn attach_metadata(
76    base_bytes: &[u8],
77    metadata: &PostgresMetadata,
78) -> Result<Vec<u8>, PostgresGraphError> {
79    attach_postgres_sections(base_bytes, None, metadata)
80}
81
82/// Copies one CSR topology section into a Postgres-owned section kind.
83fn copy_csr_section(
84    snapshot: &Snapshot<'_>,
85    source_kind: u32,
86    dest_kind: u32,
87    builder: &mut SnapshotBuilder,
88) -> Result<(), PostgresGraphError> {
89    let section = snapshot
90        .section(source_kind)
91        .ok_or(PostgresGraphError::Build(BuildError::MissingCsrSection {
92            kind: source_kind,
93        }))?;
94    builder.add_section(
95        dest_kind,
96        section.version(),
97        alignment_log2_from_section(&section),
98        section.bytes().to_vec(),
99    )?;
100    Ok(())
101}
102
103/// Returns whether `kind` is in the Postgres-owned reserved section range.
104fn is_postgres_owned_kind(kind: u32) -> bool {
105    (0x0200..0x0300).contains(&kind)
106}
107
108/// Derives snapshot builder alignment metadata from a borrowed section view.
109fn alignment_log2_from_section(section: &oxgraph_snapshot::Section<'_>) -> u8 {
110    let alignment = section.declared_alignment();
111    if alignment <= 1 {
112        0
113    } else {
114        u8::try_from(alignment.trailing_zeros()).unwrap_or(u8::MAX)
115    }
116}