Skip to main content

contextdb_core/
table_meta.rs

1use crate::Direction;
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4
5#[derive(Debug, Clone, Default, Serialize, Deserialize)]
6pub struct TableMeta {
7    pub columns: Vec<ColumnDef>,
8    pub immutable: bool,
9    pub state_machine: Option<StateMachineConstraint>,
10    #[serde(default)]
11    pub dag_edge_types: Vec<String>,
12    #[serde(default)]
13    pub unique_constraints: Vec<Vec<String>>,
14    pub natural_key_column: Option<String>,
15    #[serde(default)]
16    pub propagation_rules: Vec<PropagationRule>,
17    #[serde(default)]
18    pub default_ttl_seconds: Option<u64>,
19    #[serde(default)]
20    pub sync_safe: bool,
21    #[serde(default)]
22    pub expires_column: Option<String>,
23}
24
25#[derive(Debug, Clone, Serialize, Deserialize)]
26pub enum PropagationRule {
27    ForeignKey {
28        fk_column: String,
29        referenced_table: String,
30        referenced_column: String,
31        trigger_state: String,
32        target_state: String,
33        max_depth: u32,
34        abort_on_failure: bool,
35    },
36    Edge {
37        edge_type: String,
38        direction: Direction,
39        trigger_state: String,
40        target_state: String,
41        max_depth: u32,
42        abort_on_failure: bool,
43    },
44    VectorExclusion {
45        trigger_state: String,
46    },
47}
48
49#[derive(Debug, Clone, Serialize, Deserialize)]
50pub struct StateMachineConstraint {
51    pub column: String,
52    pub transitions: HashMap<String, Vec<String>>,
53}
54
55#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
56pub struct ColumnDef {
57    pub name: String,
58    pub column_type: ColumnType,
59    pub nullable: bool,
60    pub primary_key: bool,
61    #[serde(default)]
62    pub unique: bool,
63    #[serde(default)]
64    pub default: Option<String>,
65    #[serde(default)]
66    pub references: Option<ForeignKeyReference>,
67    #[serde(default)]
68    pub expires: bool,
69}
70
71#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
72pub struct ForeignKeyReference {
73    pub table: String,
74    pub column: String,
75}
76
77#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
78pub enum ColumnType {
79    Integer,
80    Real,
81    Text,
82    Boolean,
83    Json,
84    Uuid,
85    Vector(usize),
86    Timestamp,
87}
88
89impl TableMeta {
90    pub fn estimated_bytes(&self) -> usize {
91        let columns_bytes = self.columns.iter().fold(0usize, |acc, column| {
92            acc.saturating_add(column.estimated_bytes())
93        });
94        let state_machine_bytes = self
95            .state_machine
96            .as_ref()
97            .map(StateMachineConstraint::estimated_bytes)
98            .unwrap_or(0);
99        let dag_bytes = self.dag_edge_types.iter().fold(0usize, |acc, edge_type| {
100            acc.saturating_add(32 + edge_type.len() * 16)
101        });
102        let unique_constraint_bytes =
103            self.unique_constraints.iter().fold(0usize, |acc, columns| {
104                acc.saturating_add(
105                    24 + columns
106                        .iter()
107                        .map(|column| 16 + column.len() * 16)
108                        .sum::<usize>(),
109                )
110            });
111        let natural_key_bytes = self
112            .natural_key_column
113            .as_ref()
114            .map(|column| 32 + column.len() * 16)
115            .unwrap_or(0);
116        let propagation_bytes = self.propagation_rules.iter().fold(0usize, |acc, rule| {
117            acc.saturating_add(rule.estimated_bytes())
118        });
119        let expires_bytes = self
120            .expires_column
121            .as_ref()
122            .map(|column| 32 + column.len() * 16)
123            .unwrap_or(0);
124
125        16 + columns_bytes
126            + state_machine_bytes
127            + dag_bytes
128            + unique_constraint_bytes
129            + natural_key_bytes
130            + propagation_bytes
131            + expires_bytes
132            + self.default_ttl_seconds.map(|_| 8).unwrap_or(0)
133            + 8
134    }
135}
136
137impl PropagationRule {
138    fn estimated_bytes(&self) -> usize {
139        match self {
140            PropagationRule::ForeignKey {
141                fk_column,
142                referenced_table,
143                referenced_column,
144                trigger_state,
145                target_state,
146                ..
147            } => {
148                24 + fk_column.len() * 16
149                    + referenced_table.len() * 16
150                    + referenced_column.len() * 16
151                    + trigger_state.len() * 16
152                    + target_state.len() * 16
153            }
154            PropagationRule::Edge {
155                edge_type,
156                trigger_state,
157                target_state,
158                ..
159            } => 24 + edge_type.len() * 16 + trigger_state.len() * 16 + target_state.len() * 16,
160            PropagationRule::VectorExclusion { trigger_state } => 16 + trigger_state.len() * 16,
161        }
162    }
163}
164
165impl StateMachineConstraint {
166    fn estimated_bytes(&self) -> usize {
167        let transitions_bytes = self.transitions.iter().fold(0usize, |acc, (from, tos)| {
168            acc.saturating_add(
169                32 + from.len() * 16 + tos.iter().map(|to| 16 + to.len() * 16).sum::<usize>(),
170            )
171        });
172        24 + self.column.len() * 16 + transitions_bytes
173    }
174}
175
176impl ColumnDef {
177    fn estimated_bytes(&self) -> usize {
178        let default_bytes = self
179            .default
180            .as_ref()
181            .map(|value| 32 + value.len() * 16)
182            .unwrap_or(0);
183        let reference_bytes = self
184            .references
185            .as_ref()
186            .map(|reference| 32 + reference.table.len() * 16 + reference.column.len() * 16)
187            .unwrap_or(0);
188        8 + self.name.len() * 16
189            + self.column_type.estimated_bytes()
190            + default_bytes
191            + reference_bytes
192            + 8
193    }
194}
195
196impl ColumnType {
197    fn estimated_bytes(&self) -> usize {
198        match self {
199            ColumnType::Integer => 16,
200            ColumnType::Real => 16,
201            ColumnType::Text => 16,
202            ColumnType::Boolean => 16,
203            ColumnType::Json => 24,
204            ColumnType::Uuid => 16,
205            ColumnType::Vector(_) => 24,
206            ColumnType::Timestamp => 16,
207        }
208    }
209}