1use crate::Direction;
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4
5#[derive(Debug, Clone, Default, Serialize)]
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 #[serde(default)]
24 pub indexes: Vec<IndexDecl>,
25}
26
27impl<'de> serde::Deserialize<'de> for TableMeta {
30 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
31 where
32 D: serde::Deserializer<'de>,
33 {
34 use serde::de::{MapAccess, SeqAccess, Visitor};
35 use std::fmt;
36
37 struct TableMetaVisitor;
38
39 impl<'de> Visitor<'de> for TableMetaVisitor {
40 type Value = TableMeta;
41
42 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43 f.write_str("a TableMeta")
44 }
45
46 fn visit_seq<A>(self, mut seq: A) -> std::result::Result<TableMeta, A::Error>
47 where
48 A: SeqAccess<'de>,
49 {
50 let columns = seq
51 .next_element::<Vec<ColumnDef>>()?
52 .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?;
53 let immutable = seq
54 .next_element::<bool>()?
55 .ok_or_else(|| serde::de::Error::invalid_length(1, &self))?;
56 let state_machine = seq
57 .next_element::<Option<StateMachineConstraint>>()?
58 .ok_or_else(|| serde::de::Error::invalid_length(2, &self))?;
59 let dag_edge_types = seq.next_element::<Vec<String>>()?.unwrap_or_default();
60 let unique_constraints =
61 seq.next_element::<Vec<Vec<String>>>()?.unwrap_or_default();
62 let natural_key_column = seq.next_element::<Option<String>>()?.unwrap_or_default();
63 let propagation_rules = seq
64 .next_element::<Vec<PropagationRule>>()?
65 .unwrap_or_default();
66 let default_ttl_seconds = seq.next_element::<Option<u64>>()?.unwrap_or_default();
67 let sync_safe = seq.next_element::<bool>()?.unwrap_or_default();
68 let expires_column = seq.next_element::<Option<String>>()?.unwrap_or_default();
74 let indexes = seq.next_element::<Vec<IndexDecl>>()?.unwrap_or_default();
75 Ok(TableMeta {
76 columns,
77 immutable,
78 state_machine,
79 dag_edge_types,
80 unique_constraints,
81 natural_key_column,
82 propagation_rules,
83 default_ttl_seconds,
84 sync_safe,
85 expires_column,
86 indexes,
87 })
88 }
89
90 fn visit_map<A>(self, mut map: A) -> std::result::Result<TableMeta, A::Error>
91 where
92 A: MapAccess<'de>,
93 {
94 let mut columns: Option<Vec<ColumnDef>> = None;
95 let mut immutable: Option<bool> = None;
96 let mut state_machine: Option<Option<StateMachineConstraint>> = None;
97 let mut dag_edge_types: Option<Vec<String>> = None;
98 let mut unique_constraints: Option<Vec<Vec<String>>> = None;
99 let mut natural_key_column: Option<Option<String>> = None;
100 let mut propagation_rules: Option<Vec<PropagationRule>> = None;
101 let mut default_ttl_seconds: Option<Option<u64>> = None;
102 let mut sync_safe: Option<bool> = None;
103 let mut expires_column: Option<Option<String>> = None;
104 let mut indexes: Option<Vec<IndexDecl>> = None;
105
106 while let Some(key) = map.next_key::<String>()? {
107 match key.as_str() {
108 "columns" => columns = Some(map.next_value()?),
109 "immutable" => immutable = Some(map.next_value()?),
110 "state_machine" => state_machine = Some(map.next_value()?),
111 "dag_edge_types" => dag_edge_types = Some(map.next_value()?),
112 "unique_constraints" => unique_constraints = Some(map.next_value()?),
113 "natural_key_column" => natural_key_column = Some(map.next_value()?),
114 "propagation_rules" => propagation_rules = Some(map.next_value()?),
115 "default_ttl_seconds" => default_ttl_seconds = Some(map.next_value()?),
116 "sync_safe" => sync_safe = Some(map.next_value()?),
117 "expires_column" => expires_column = Some(map.next_value()?),
118 "indexes" => indexes = Some(map.next_value()?),
119 _ => {
120 let _: serde::de::IgnoredAny = map.next_value()?;
121 }
122 }
123 }
124
125 Ok(TableMeta {
126 columns: columns.ok_or_else(|| serde::de::Error::missing_field("columns"))?,
127 immutable: immutable
128 .ok_or_else(|| serde::de::Error::missing_field("immutable"))?,
129 state_machine: state_machine.unwrap_or_default(),
130 dag_edge_types: dag_edge_types.unwrap_or_default(),
131 unique_constraints: unique_constraints.unwrap_or_default(),
132 natural_key_column: natural_key_column.unwrap_or_default(),
133 propagation_rules: propagation_rules.unwrap_or_default(),
134 default_ttl_seconds: default_ttl_seconds.unwrap_or_default(),
135 sync_safe: sync_safe.unwrap_or_default(),
136 expires_column: expires_column.unwrap_or_default(),
137 indexes: indexes.unwrap_or_default(),
138 })
139 }
140 }
141
142 const FIELDS: &[&str] = &[
143 "columns",
144 "immutable",
145 "state_machine",
146 "dag_edge_types",
147 "unique_constraints",
148 "natural_key_column",
149 "propagation_rules",
150 "default_ttl_seconds",
151 "sync_safe",
152 "expires_column",
153 "indexes",
154 ];
155 deserializer.deserialize_struct("TableMeta", FIELDS, TableMetaVisitor)
156 }
157}
158
159#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
163pub enum SortDirection {
164 #[default]
165 Asc,
166 Desc,
167}
168
169#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
176pub enum IndexKind {
177 Auto,
178 #[default]
179 UserDeclared,
180}
181
182#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
183pub struct IndexDecl {
184 pub name: String,
185 pub columns: Vec<(String, SortDirection)>,
186 #[serde(default)]
187 pub kind: IndexKind,
188}
189
190impl IndexDecl {
191 pub fn estimated_bytes(&self) -> usize {
192 32 + self.name.len() * 16
193 + self
194 .columns
195 .iter()
196 .fold(0usize, |acc, (c, _)| acc.saturating_add(24 + c.len() * 16))
197 }
198}
199
200#[derive(Debug, Clone, Serialize, Deserialize)]
201pub enum PropagationRule {
202 ForeignKey {
203 fk_column: String,
204 referenced_table: String,
205 referenced_column: String,
206 trigger_state: String,
207 target_state: String,
208 max_depth: u32,
209 abort_on_failure: bool,
210 },
211 Edge {
212 edge_type: String,
213 direction: Direction,
214 trigger_state: String,
215 target_state: String,
216 max_depth: u32,
217 abort_on_failure: bool,
218 },
219 VectorExclusion {
220 trigger_state: String,
221 },
222}
223
224#[derive(Debug, Clone, Serialize, Deserialize)]
225pub struct StateMachineConstraint {
226 pub column: String,
227 pub transitions: HashMap<String, Vec<String>>,
228}
229
230#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
231pub struct ColumnDef {
232 pub name: String,
233 pub column_type: ColumnType,
234 pub nullable: bool,
235 pub primary_key: bool,
236 #[serde(default)]
237 pub unique: bool,
238 #[serde(default)]
239 pub default: Option<String>,
240 #[serde(default)]
241 pub references: Option<ForeignKeyReference>,
242 #[serde(default)]
243 pub expires: bool,
244 #[serde(default)]
245 pub immutable: bool,
246}
247
248impl<'de> serde::Deserialize<'de> for ColumnDef {
255 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
256 where
257 D: serde::Deserializer<'de>,
258 {
259 use serde::de::{MapAccess, SeqAccess, Visitor};
260 use std::fmt;
261
262 struct ColumnDefVisitor;
263
264 impl<'de> Visitor<'de> for ColumnDefVisitor {
265 type Value = ColumnDef;
266
267 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
268 f.write_str("a ColumnDef")
269 }
270
271 fn visit_seq<A>(self, mut seq: A) -> std::result::Result<ColumnDef, A::Error>
272 where
273 A: SeqAccess<'de>,
274 {
275 let name = seq
276 .next_element::<String>()?
277 .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?;
278 let column_type = seq
279 .next_element::<ColumnType>()?
280 .ok_or_else(|| serde::de::Error::invalid_length(1, &self))?;
281 let nullable = seq
282 .next_element::<bool>()?
283 .ok_or_else(|| serde::de::Error::invalid_length(2, &self))?;
284 let primary_key = seq
285 .next_element::<bool>()?
286 .ok_or_else(|| serde::de::Error::invalid_length(3, &self))?;
287 let unique = seq.next_element::<bool>()?.unwrap_or_default();
288 let default = seq.next_element::<Option<String>>()?.unwrap_or_default();
289 let references = seq
290 .next_element::<Option<ForeignKeyReference>>()?
291 .unwrap_or_default();
292 let expires = seq.next_element::<bool>()?.unwrap_or_default();
293 let immutable = seq.next_element::<bool>()?.unwrap_or_default();
298 Ok(ColumnDef {
299 name,
300 column_type,
301 nullable,
302 primary_key,
303 unique,
304 default,
305 references,
306 expires,
307 immutable,
308 })
309 }
310
311 fn visit_map<A>(self, mut map: A) -> std::result::Result<ColumnDef, A::Error>
312 where
313 A: MapAccess<'de>,
314 {
315 let mut name: Option<String> = None;
316 let mut column_type: Option<ColumnType> = None;
317 let mut nullable: Option<bool> = None;
318 let mut primary_key: Option<bool> = None;
319 let mut unique: Option<bool> = None;
320 let mut default: Option<Option<String>> = None;
321 let mut references: Option<Option<ForeignKeyReference>> = None;
322 let mut expires: Option<bool> = None;
323 let mut immutable: Option<bool> = None;
324
325 while let Some(key) = map.next_key::<String>()? {
326 match key.as_str() {
327 "name" => name = Some(map.next_value()?),
328 "column_type" => column_type = Some(map.next_value()?),
329 "nullable" => nullable = Some(map.next_value()?),
330 "primary_key" => primary_key = Some(map.next_value()?),
331 "unique" => unique = Some(map.next_value()?),
332 "default" => default = Some(map.next_value()?),
333 "references" => references = Some(map.next_value()?),
334 "expires" => expires = Some(map.next_value()?),
335 "immutable" => immutable = Some(map.next_value()?),
336 _ => {
337 let _: serde::de::IgnoredAny = map.next_value()?;
338 }
339 }
340 }
341
342 Ok(ColumnDef {
343 name: name.ok_or_else(|| serde::de::Error::missing_field("name"))?,
344 column_type: column_type
345 .ok_or_else(|| serde::de::Error::missing_field("column_type"))?,
346 nullable: nullable
347 .ok_or_else(|| serde::de::Error::missing_field("nullable"))?,
348 primary_key: primary_key
349 .ok_or_else(|| serde::de::Error::missing_field("primary_key"))?,
350 unique: unique.unwrap_or_default(),
351 default: default.unwrap_or_default(),
352 references: references.unwrap_or_default(),
353 expires: expires.unwrap_or_default(),
354 immutable: immutable.unwrap_or_default(),
355 })
356 }
357 }
358
359 const FIELDS: &[&str] = &[
360 "name",
361 "column_type",
362 "nullable",
363 "primary_key",
364 "unique",
365 "default",
366 "references",
367 "expires",
368 "immutable",
369 ];
370 deserializer.deserialize_struct("ColumnDef", FIELDS, ColumnDefVisitor)
371 }
372}
373
374#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
375pub struct ForeignKeyReference {
376 pub table: String,
377 pub column: String,
378}
379
380#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
381pub enum ColumnType {
382 Integer,
383 Real,
384 Text,
385 Boolean,
386 Json,
387 Uuid,
388 Vector(usize),
389 Timestamp,
390 TxId,
391}
392
393impl TableMeta {
394 pub fn estimated_bytes(&self) -> usize {
395 let columns_bytes = self.columns.iter().fold(0usize, |acc, column| {
396 acc.saturating_add(column.estimated_bytes())
397 });
398 let state_machine_bytes = self
399 .state_machine
400 .as_ref()
401 .map(StateMachineConstraint::estimated_bytes)
402 .unwrap_or(0);
403 let dag_bytes = self.dag_edge_types.iter().fold(0usize, |acc, edge_type| {
404 acc.saturating_add(32 + edge_type.len() * 16)
405 });
406 let unique_constraint_bytes =
407 self.unique_constraints.iter().fold(0usize, |acc, columns| {
408 acc.saturating_add(
409 24 + columns
410 .iter()
411 .map(|column| 16 + column.len() * 16)
412 .sum::<usize>(),
413 )
414 });
415 let natural_key_bytes = self
416 .natural_key_column
417 .as_ref()
418 .map(|column| 32 + column.len() * 16)
419 .unwrap_or(0);
420 let propagation_bytes = self.propagation_rules.iter().fold(0usize, |acc, rule| {
421 acc.saturating_add(rule.estimated_bytes())
422 });
423 let expires_bytes = self
424 .expires_column
425 .as_ref()
426 .map(|column| 32 + column.len() * 16)
427 .unwrap_or(0);
428 let indexes_bytes = self
429 .indexes
430 .iter()
431 .fold(0usize, |acc, i| acc.saturating_add(i.estimated_bytes()));
432
433 16 + columns_bytes
434 + state_machine_bytes
435 + dag_bytes
436 + unique_constraint_bytes
437 + natural_key_bytes
438 + propagation_bytes
439 + expires_bytes
440 + indexes_bytes
441 + self.default_ttl_seconds.map(|_| 8).unwrap_or(0)
442 + 8
443 }
444}
445
446impl PropagationRule {
447 fn estimated_bytes(&self) -> usize {
448 match self {
449 PropagationRule::ForeignKey {
450 fk_column,
451 referenced_table,
452 referenced_column,
453 trigger_state,
454 target_state,
455 ..
456 } => {
457 24 + fk_column.len() * 16
458 + referenced_table.len() * 16
459 + referenced_column.len() * 16
460 + trigger_state.len() * 16
461 + target_state.len() * 16
462 }
463 PropagationRule::Edge {
464 edge_type,
465 trigger_state,
466 target_state,
467 ..
468 } => 24 + edge_type.len() * 16 + trigger_state.len() * 16 + target_state.len() * 16,
469 PropagationRule::VectorExclusion { trigger_state } => 16 + trigger_state.len() * 16,
470 }
471 }
472}
473
474impl StateMachineConstraint {
475 fn estimated_bytes(&self) -> usize {
476 let transitions_bytes = self.transitions.iter().fold(0usize, |acc, (from, tos)| {
477 acc.saturating_add(
478 32 + from.len() * 16 + tos.iter().map(|to| 16 + to.len() * 16).sum::<usize>(),
479 )
480 });
481 24 + self.column.len() * 16 + transitions_bytes
482 }
483}
484
485impl ColumnDef {
486 fn estimated_bytes(&self) -> usize {
487 let default_bytes = self
488 .default
489 .as_ref()
490 .map(|value| 32 + value.len() * 16)
491 .unwrap_or(0);
492 let reference_bytes = self
493 .references
494 .as_ref()
495 .map(|reference| 32 + reference.table.len() * 16 + reference.column.len() * 16)
496 .unwrap_or(0);
497 8 + self.name.len() * 16
498 + self.column_type.estimated_bytes()
499 + default_bytes
500 + reference_bytes
501 + 8
502 }
503}
504
505impl ColumnType {
506 fn estimated_bytes(&self) -> usize {
507 match self {
508 ColumnType::Integer => 16,
509 ColumnType::Real => 16,
510 ColumnType::Text => 16,
511 ColumnType::Boolean => 16,
512 ColumnType::Json => 24,
513 ColumnType::Uuid => 16,
514 ColumnType::Vector(_) => 24,
515 ColumnType::Timestamp => 16,
516 ColumnType::TxId => 8,
517 }
518 }
519}