1use crate::DataType;
7use std::collections::HashMap;
8use std::fmt;
9
10#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct TableDef {
13 pub name: String,
15 pub columns: Vec<ColumnDef>,
17 pub primary_key: Vec<String>,
19 pub indexes: Vec<IndexDef>,
21 pub constraints: Vec<Constraint>,
23 pub version: u32,
25 pub created_at: u64,
27 pub updated_at: u64,
29}
30
31impl TableDef {
32 pub fn new(name: impl Into<String>) -> Self {
34 let now = std::time::SystemTime::now()
35 .duration_since(std::time::UNIX_EPOCH)
36 .unwrap_or_default()
37 .as_secs();
38
39 Self {
40 name: name.into(),
41 columns: Vec::new(),
42 primary_key: Vec::new(),
43 indexes: Vec::new(),
44 constraints: Vec::new(),
45 version: 1,
46 created_at: now,
47 updated_at: now,
48 }
49 }
50
51 pub fn add_column(mut self, column: ColumnDef) -> Self {
53 self.columns.push(column);
54 self
55 }
56
57 pub fn primary_key(mut self, columns: Vec<String>) -> Self {
59 self.primary_key = columns;
60 self
61 }
62
63 pub fn add_index(mut self, index: IndexDef) -> Self {
65 self.indexes.push(index);
66 self
67 }
68
69 pub fn add_constraint(mut self, constraint: Constraint) -> Self {
71 self.constraints.push(constraint);
72 self
73 }
74
75 pub fn get_column(&self, name: &str) -> Option<&ColumnDef> {
77 self.columns.iter().find(|column| column.name == name)
78 }
79
80 pub fn column_index(&self, name: &str) -> Option<usize> {
82 self.columns.iter().position(|column| column.name == name)
83 }
84
85 pub fn is_primary_key_column(&self, name: &str) -> bool {
87 self.primary_key.iter().any(|column| column == name)
88 }
89
90 pub fn validate(&self) -> Result<(), TableDefError> {
92 if self.name.is_empty() {
93 return Err(TableDefError::EmptyTableName);
94 }
95
96 let mut seen = HashMap::new();
97 for column in &self.columns {
98 if seen.insert(&column.name, true).is_some() {
99 return Err(TableDefError::DuplicateColumn(column.name.clone()));
100 }
101 }
102
103 for primary_key_column in &self.primary_key {
104 if self.get_column(primary_key_column).is_none() {
105 return Err(TableDefError::InvalidPrimaryKey(primary_key_column.clone()));
106 }
107 }
108
109 for index in &self.indexes {
110 for column in &index.columns {
111 if self.get_column(column).is_none() {
112 return Err(TableDefError::InvalidIndexColumn(column.clone()));
113 }
114 }
115 }
116
117 for constraint in &self.constraints {
118 for column in &constraint.columns {
119 if self.get_column(column).is_none() {
120 return Err(TableDefError::InvalidConstraintColumn(column.clone()));
121 }
122 }
123 }
124
125 Ok(())
126 }
127}
128
129impl fmt::Display for TableDef {
130 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131 writeln!(f, "TABLE {} (version {})", self.name, self.version)?;
132 writeln!(f, " Columns:")?;
133 for column in &self.columns {
134 writeln!(f, " {column}")?;
135 }
136 if !self.primary_key.is_empty() {
137 writeln!(f, " Primary Key: ({})", self.primary_key.join(", "))?;
138 }
139 if !self.indexes.is_empty() {
140 writeln!(f, " Indexes:")?;
141 for index in &self.indexes {
142 writeln!(f, " {index}")?;
143 }
144 }
145 Ok(())
146 }
147}
148
149#[derive(Debug, Clone, PartialEq, Eq)]
151pub struct ColumnDef {
152 pub name: String,
154 pub data_type: DataType,
156 pub nullable: bool,
158 pub default: Option<Vec<u8>>,
160 pub vector_dim: Option<u32>,
162 pub compress: bool,
164 pub enum_variants: Vec<String>,
166 pub decimal_precision: u8,
168 pub element_type: Option<DataType>,
170 pub metadata: HashMap<String, String>,
172}
173
174impl ColumnDef {
175 pub fn new(name: impl Into<String>, data_type: DataType) -> Self {
177 Self {
178 name: name.into(),
179 data_type,
180 nullable: true,
181 default: None,
182 vector_dim: None,
183 compress: false,
184 enum_variants: Vec::new(),
185 decimal_precision: 4,
186 element_type: None,
187 metadata: HashMap::new(),
188 }
189 }
190
191 pub fn not_null(mut self) -> Self {
193 self.nullable = false;
194 self
195 }
196
197 pub fn with_default(mut self, default: Vec<u8>) -> Self {
199 self.default = Some(default);
200 self
201 }
202
203 pub fn with_vector_dim(mut self, dimension: u32) -> Self {
205 self.vector_dim = Some(dimension);
206 self
207 }
208
209 pub fn with_metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
211 self.metadata.insert(key.into(), value.into());
212 self
213 }
214
215 pub fn compressed(mut self) -> Self {
217 self.compress = true;
218 self
219 }
220
221 pub fn with_variants(mut self, variants: Vec<String>) -> Self {
223 self.enum_variants = variants;
224 self
225 }
226
227 pub fn with_precision(mut self, precision: u8) -> Self {
229 self.decimal_precision = precision;
230 self
231 }
232
233 pub fn with_element_type(mut self, data_type: DataType) -> Self {
235 self.element_type = Some(data_type);
236 self
237 }
238}
239
240impl fmt::Display for ColumnDef {
241 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
242 write!(f, "{} {}", self.name, self.data_type)?;
243 if let Some(dimension) = self.vector_dim {
244 write!(f, "({dimension})")?;
245 }
246 if !self.nullable {
247 write!(f, " NOT NULL")?;
248 }
249 if self.default.is_some() {
250 write!(f, " DEFAULT <value>")?;
251 }
252 Ok(())
253 }
254}
255
256#[derive(Debug, Clone, PartialEq, Eq)]
258pub struct IndexDef {
259 pub name: String,
260 pub columns: Vec<String>,
261 pub index_type: IndexType,
262 pub unique: bool,
263}
264
265impl IndexDef {
266 pub fn new(name: impl Into<String>, columns: Vec<String>) -> Self {
267 Self {
268 name: name.into(),
269 columns,
270 index_type: IndexType::BTree,
271 unique: false,
272 }
273 }
274
275 pub fn unique(mut self) -> Self {
276 self.unique = true;
277 self
278 }
279
280 pub fn with_type(mut self, index_type: IndexType) -> Self {
281 self.index_type = index_type;
282 self
283 }
284}
285
286impl fmt::Display for IndexDef {
287 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
288 if self.unique {
289 write!(f, "UNIQUE ")?;
290 }
291 write!(
292 f,
293 "INDEX {} ({}) USING {:?}",
294 self.name,
295 self.columns.join(", "),
296 self.index_type
297 )
298 }
299}
300
301#[derive(Debug, Clone, Copy, PartialEq, Eq)]
303#[repr(u8)]
304pub enum IndexType {
305 BTree = 1,
306 Hash = 2,
307 IvfFlat = 3,
308 Hnsw = 4,
309}
310
311impl IndexType {
312 pub fn from_byte(byte: u8) -> Option<Self> {
313 match byte {
314 1 => Some(Self::BTree),
315 2 => Some(Self::Hash),
316 3 => Some(Self::IvfFlat),
317 4 => Some(Self::Hnsw),
318 _ => None,
319 }
320 }
321}
322
323#[derive(Debug, Clone, PartialEq, Eq)]
325pub struct Constraint {
326 pub name: String,
327 pub constraint_type: ConstraintType,
328 pub columns: Vec<String>,
329 pub ref_table: Option<String>,
330 pub ref_columns: Option<Vec<String>>,
331}
332
333impl Constraint {
334 pub fn new(name: impl Into<String>, constraint_type: ConstraintType) -> Self {
335 Self {
336 name: name.into(),
337 constraint_type,
338 columns: Vec::new(),
339 ref_table: None,
340 ref_columns: None,
341 }
342 }
343
344 pub fn on_columns(mut self, columns: Vec<String>) -> Self {
345 self.columns = columns;
346 self
347 }
348
349 pub fn references(mut self, table: String, columns: Vec<String>) -> Self {
350 self.ref_table = Some(table);
351 self.ref_columns = Some(columns);
352 self
353 }
354}
355
356#[derive(Debug, Clone, Copy, PartialEq, Eq)]
358#[repr(u8)]
359pub enum ConstraintType {
360 PrimaryKey = 1,
361 Unique = 2,
362 ForeignKey = 3,
363 Check = 4,
364 NotNull = 5,
365}
366
367impl ConstraintType {
368 pub fn from_byte(byte: u8) -> Option<Self> {
369 match byte {
370 1 => Some(Self::PrimaryKey),
371 2 => Some(Self::Unique),
372 3 => Some(Self::ForeignKey),
373 4 => Some(Self::Check),
374 5 => Some(Self::NotNull),
375 _ => None,
376 }
377 }
378}
379
380#[derive(Debug, Clone, PartialEq, Eq)]
382pub enum TableDefError {
383 EmptyTableName,
384 DuplicateColumn(String),
385 InvalidPrimaryKey(String),
386 InvalidIndexColumn(String),
387 InvalidConstraintColumn(String),
388 TruncatedData,
389 InvalidMagic,
390 InvalidDataType,
391 InvalidIndexType,
392 InvalidConstraintType,
393 VarintOverflow,
394}
395
396impl fmt::Display for TableDefError {
397 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
398 match self {
399 Self::EmptyTableName => write!(f, "empty table name"),
400 Self::DuplicateColumn(name) => write!(f, "duplicate column: {name}"),
401 Self::InvalidPrimaryKey(name) => write!(f, "invalid primary key column: {name}"),
402 Self::InvalidIndexColumn(name) => write!(f, "invalid index column: {name}"),
403 Self::InvalidConstraintColumn(name) => {
404 write!(f, "invalid constraint column: {name}")
405 }
406 Self::TruncatedData => write!(f, "truncated data"),
407 Self::InvalidMagic => write!(f, "invalid magic bytes"),
408 Self::InvalidDataType => write!(f, "invalid data type"),
409 Self::InvalidIndexType => write!(f, "invalid index type"),
410 Self::InvalidConstraintType => write!(f, "invalid constraint type"),
411 Self::VarintOverflow => write!(f, "varint overflow"),
412 }
413 }
414}
415
416impl std::error::Error for TableDefError {}
417
418#[cfg(test)]
419mod tests {
420 use super::*;
421
422 #[test]
423 fn table_validation_accepts_valid_references() {
424 let table = TableDef::new("hosts")
425 .add_column(ColumnDef::new("id", DataType::UnsignedInteger).not_null())
426 .add_column(ColumnDef::new("ip", DataType::IpAddr))
427 .primary_key(vec!["id".into()])
428 .add_index(IndexDef::new("idx_ip", vec!["ip".into()]).unique());
429
430 assert!(table.validate().is_ok());
431 assert!(table.is_primary_key_column("id"));
432 assert_eq!(table.column_index("ip"), Some(1));
433 }
434
435 #[test]
436 fn table_validation_rejects_invalid_references() {
437 let duplicate = TableDef::new("hosts")
438 .add_column(ColumnDef::new("id", DataType::Integer))
439 .add_column(ColumnDef::new("id", DataType::Text));
440 assert_eq!(
441 duplicate.validate(),
442 Err(TableDefError::DuplicateColumn("id".into()))
443 );
444
445 let missing_key = TableDef::new("hosts").primary_key(vec!["id".into()]);
446 assert_eq!(
447 missing_key.validate(),
448 Err(TableDefError::InvalidPrimaryKey("id".into()))
449 );
450 }
451
452 #[test]
453 fn builders_preserve_column_and_constraint_details() {
454 let column = ColumnDef::new("embedding", DataType::Vector)
455 .not_null()
456 .with_default(vec![1, 2, 3])
457 .with_vector_dim(384)
458 .compressed()
459 .with_variants(vec!["a".into(), "b".into()])
460 .with_precision(6)
461 .with_element_type(DataType::Float)
462 .with_metadata("unit", "f32");
463 assert_eq!(column.vector_dim, Some(384));
464 assert_eq!(column.metadata.get("unit").map(String::as_str), Some("f32"));
465
466 let constraint = Constraint::new("fk_host", ConstraintType::ForeignKey)
467 .on_columns(vec!["host_id".into()])
468 .references("hosts".into(), vec!["id".into()]);
469 assert_eq!(constraint.ref_table.as_deref(), Some("hosts"));
470 }
471}