Skip to main content

sqlite_knowledge_graph/version/
mod.rs

1//! QuaQue-inspired versioning for the knowledge graph.
2//!
3//! Based on arXiv:2603.18654 — condensed relational model using bitstring validity.
4//! Each entity/relation row carries a `validity` bitstring; bit position is determined
5//! by a version's `bit_slot` (0–63), NOT by its `id`.  Slots are reclaimable: when a
6//! version is deleted its slot is freed and the next `create_version` call reuses the
7//! lowest available slot.  A row belongs to version V when
8//! `(validity & (1 << V.bit_slot)) != 0`.  Version filtering uses bitwise operations.
9
10pub mod diff;
11pub mod merge;
12pub mod query;
13pub mod snapshot;
14pub mod store;
15
16use serde::{Deserialize, Serialize};
17
18/// Metadata for a named version/snapshot of the knowledge graph.
19#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct Version {
21    pub id: i64,
22    pub name: String,
23    pub branch: String,
24    pub parent_id: Option<i64>,
25    pub description: Option<String>,
26    pub created_at: Option<i64>,
27    pub is_merged: bool,
28}
29
30/// Result of comparing two versions.
31#[derive(Debug, Clone, Serialize, Deserialize)]
32pub struct VersionDiff {
33    pub added_entities: Vec<crate::graph::Entity>,
34    pub removed_entities: Vec<crate::graph::Entity>,
35    pub common_entities: Vec<crate::graph::Entity>,
36    pub added_relations: Vec<crate::graph::Relation>,
37    pub removed_relations: Vec<crate::graph::Relation>,
38    pub common_relations: Vec<crate::graph::Relation>,
39}
40
41/// Strategy for merging versions.
42#[derive(Debug, Clone, Copy, PartialEq, Eq)]
43pub enum MergeStrategy {
44    /// Entity/relation exists in merged version if it exists in ANY source version.
45    Union,
46    /// Entity/relation exists in merged version only if it exists in ALL source versions.
47    Intersection,
48}
49
50/// Maximum number of concurrent versions, bounded by the 64 usable bits of a
51/// signed 64-bit `validity` column.
52pub(crate) const MAX_VERSIONS: i64 = 64;
53
54/// Compute the validity bitmask for a `bit_slot`, or `None` if `slot` is outside
55/// `[0, 63]`.
56///
57/// Slots — not version ids — drive the bit position so the limit is exactly 64
58/// *concurrent* versions (slots are reclaimed on delete) instead of 64 version
59/// *creations* over the database lifetime.  Returning `None` for an out-of-range
60/// slot keeps the helper panic-free even in release builds: a corrupted/manual
61/// DB row is surfaced as a regular error by the caller (see
62/// [`store::version_bit_for`]) instead of crashing on `1 << slot`.
63#[inline]
64pub(crate) fn bit_from_slot(slot: i64) -> Option<i64> {
65    if (0..MAX_VERSIONS).contains(&slot) {
66        Some(1 << slot)
67    } else {
68        None
69    }
70}