Skip to main content

oxgraph_postgres/artifact/
sections.rs

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