sqlitegraph 3.9.0

Embedded graph database with explicit SQLite, native-v3, and combined backend modes; HNSW vector search; and graph algorithms
Documentation
use super::V3Backend;
use crate::SqliteGraphError;
use crate::backend::native::v3::NodeRecordV3;
use crate::snapshot::SnapshotId;
use std::fs::OpenOptions;
use std::io::{Read, Seek, SeekFrom};

impl V3Backend {
    pub(super) fn validate_node_snapshot_visibility(
        &self,
        snapshot_id: SnapshotId,
        node_id: i64,
    ) -> Result<Option<u64>, SqliteGraphError> {
        let mut deleted_before_present = None;

        if snapshot_id.0 == 0 {
            return Ok(deleted_before_present);
        }

        let conn = self.sqlite_conn.lock();
        let mut stmt = conn
            .prepare(
                "SELECT created_version, updated_version, deleted_version FROM node_properties WHERE node_id = ?1",
            )
            .map_err(|e| SqliteGraphError::connection(format!("Failed to prepare query: {}", e)))?;

        let mut rows = stmt
            .query([node_id])
            .map_err(|e| SqliteGraphError::connection(format!("Failed to query: {}", e)))?;

        if let Some(row) = rows
            .next()
            .map_err(|e| SqliteGraphError::connection(format!("Failed to fetch row: {}", e)))?
        {
            let created_version: u64 = row.get(0).map_err(|e| {
                SqliteGraphError::connection(format!("Failed to get created_version: {}", e))
            })?;
            let updated_version: Option<u64> = row.get(1).map_err(|e| {
                SqliteGraphError::connection(format!("Failed to get updated_version: {}", e))
            })?;
            let deleted_version: Option<u64> = row.get(2).map_err(|e| {
                SqliteGraphError::connection(format!("Failed to get deleted_version: {}", e))
            })?;

            if created_version > snapshot_id.0 {
                return Err(SqliteGraphError::query(format!(
                    "Node {} not found in snapshot",
                    node_id
                )));
            }

            if let Some(version) = updated_version
                && snapshot_id.0 < version
            {
                return Err(SqliteGraphError::query(format!(
                    "Historical version for node {} is unavailable after update",
                    node_id
                )));
            }

            if let Some(version) = deleted_version {
                if snapshot_id.0 >= version {
                    return Err(SqliteGraphError::query(format!(
                        "Node {} not found in snapshot",
                        node_id
                    )));
                }
                deleted_before_present = Some(version);
            }
        } else {
            return Err(SqliteGraphError::query(format!(
                "Node {} not found",
                node_id
            )));
        }

        Ok(deleted_before_present)
    }

    pub(super) fn load_node_record_bytes(
        &self,
        record: &NodeRecordV3,
    ) -> Result<Vec<u8>, SqliteGraphError> {
        if let Some(inline) = record.data_inline.clone() {
            return Ok(inline);
        }

        if let Some(offset) = record.data_external_offset {
            let actual_data_len =
                record.data_len & crate::backend::native::v3::node::record::constants::MAX_DATA_LEN;
            let mut file = OpenOptions::new()
                .read(true)
                .open(&self.db_path)
                .map_err(|e| SqliteGraphError::connection(format!("Failed to open file: {}", e)))?;

            let mut buffer = vec![0u8; actual_data_len as usize];
            file.seek(SeekFrom::Start(offset))
                .map_err(|e| SqliteGraphError::connection(format!("Failed to seek: {}", e)))?;
            file.read_exact(&mut buffer)
                .map_err(|e| SqliteGraphError::connection(format!("Failed to read: {}", e)))?;
            return Ok(buffer);
        }

        Ok(Vec::new())
    }
}