Skip to main content

reddb_server/storage/schema/
table.rs

1//! Table Definition
2//!
3//! Defines table structure including columns, primary keys, indexes, and constraints.
4//! Tables are the primary data organization unit in RedDB.
5
6use super::types::DataType;
7use std::collections::HashMap;
8use std::fmt;
9
10/// Table definition containing all metadata
11#[derive(Debug, Clone)]
12pub struct TableDef {
13    /// Table name (unique within database)
14    pub name: String,
15    /// Column definitions in order
16    pub columns: Vec<ColumnDef>,
17    /// Primary key column names (can be composite)
18    pub primary_key: Vec<String>,
19    /// Index definitions
20    pub indexes: Vec<IndexDef>,
21    /// Table-level constraints
22    pub constraints: Vec<Constraint>,
23    /// Schema version (for migrations)
24    pub version: u32,
25    /// Creation timestamp
26    pub created_at: u64,
27    /// Last modification timestamp
28    pub updated_at: u64,
29}
30
31impl TableDef {
32    /// Create a new table definition
33    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    /// Add a column to the table
52    pub fn add_column(mut self, column: ColumnDef) -> Self {
53        self.columns.push(column);
54        self
55    }
56
57    /// Set primary key columns
58    pub fn primary_key(mut self, columns: Vec<String>) -> Self {
59        self.primary_key = columns;
60        self
61    }
62
63    /// Add an index
64    pub fn add_index(mut self, index: IndexDef) -> Self {
65        self.indexes.push(index);
66        self
67    }
68
69    /// Add a constraint
70    pub fn add_constraint(mut self, constraint: Constraint) -> Self {
71        self.constraints.push(constraint);
72        self
73    }
74
75    /// Get column by name
76    pub fn get_column(&self, name: &str) -> Option<&ColumnDef> {
77        self.columns.iter().find(|c| c.name == name)
78    }
79
80    /// Get column index by name
81    pub fn column_index(&self, name: &str) -> Option<usize> {
82        self.columns.iter().position(|c| c.name == name)
83    }
84
85    /// Check if a column is part of the primary key
86    pub fn is_primary_key_column(&self, name: &str) -> bool {
87        self.primary_key.iter().any(|pk| pk == name)
88    }
89
90    /// Validate table definition
91    pub fn validate(&self) -> Result<(), TableDefError> {
92        // Check table name
93        if self.name.is_empty() {
94            return Err(TableDefError::EmptyTableName);
95        }
96
97        // Check for duplicate column names
98        let mut seen = HashMap::new();
99        for col in &self.columns {
100            if seen.insert(&col.name, true).is_some() {
101                return Err(TableDefError::DuplicateColumn(col.name.clone()));
102            }
103        }
104
105        // Validate primary key columns exist
106        for pk_col in &self.primary_key {
107            if self.get_column(pk_col).is_none() {
108                return Err(TableDefError::InvalidPrimaryKey(pk_col.clone()));
109            }
110        }
111
112        // Validate index columns exist
113        for index in &self.indexes {
114            for col in &index.columns {
115                if self.get_column(col).is_none() {
116                    return Err(TableDefError::InvalidIndexColumn(col.clone()));
117                }
118            }
119        }
120
121        // Validate constraints reference existing columns
122        for constraint in &self.constraints {
123            for col in &constraint.columns {
124                if self.get_column(col).is_none() {
125                    return Err(TableDefError::InvalidConstraintColumn(col.clone()));
126                }
127            }
128        }
129
130        Ok(())
131    }
132
133    /// Serialize table definition to bytes
134    pub fn to_bytes(&self) -> Vec<u8> {
135        let mut buf = Vec::new();
136
137        // Magic bytes for table definition
138        buf.extend_from_slice(b"RTBL");
139
140        // Version
141        buf.extend_from_slice(&self.version.to_le_bytes());
142
143        // Table name
144        write_string(&mut buf, &self.name);
145
146        // Timestamps
147        buf.extend_from_slice(&self.created_at.to_le_bytes());
148        buf.extend_from_slice(&self.updated_at.to_le_bytes());
149
150        // Columns
151        write_varint(&mut buf, self.columns.len() as u64);
152        for col in &self.columns {
153            col.write_to(&mut buf);
154        }
155
156        // Primary key
157        write_varint(&mut buf, self.primary_key.len() as u64);
158        for pk in &self.primary_key {
159            write_string(&mut buf, pk);
160        }
161
162        // Indexes
163        write_varint(&mut buf, self.indexes.len() as u64);
164        for idx in &self.indexes {
165            idx.write_to(&mut buf);
166        }
167
168        // Constraints
169        write_varint(&mut buf, self.constraints.len() as u64);
170        for constraint in &self.constraints {
171            constraint.write_to(&mut buf);
172        }
173
174        buf
175    }
176
177    /// Deserialize table definition from bytes
178    pub fn from_bytes(data: &[u8]) -> Result<Self, TableDefError> {
179        if data.len() < 4 {
180            return Err(TableDefError::TruncatedData);
181        }
182
183        // Check magic
184        if &data[0..4] != b"RTBL" {
185            return Err(TableDefError::InvalidMagic);
186        }
187
188        let mut offset = 4;
189
190        // Version
191        if data.len() < offset + 4 {
192            return Err(TableDefError::TruncatedData);
193        }
194        let version = u32::from_le_bytes(data[offset..offset + 4].try_into().unwrap());
195        offset += 4;
196
197        // Table name
198        let (name, name_len) = read_string(&data[offset..])?;
199        offset += name_len;
200
201        // Timestamps
202        if data.len() < offset + 16 {
203            return Err(TableDefError::TruncatedData);
204        }
205        let created_at = u64::from_le_bytes(data[offset..offset + 8].try_into().unwrap());
206        offset += 8;
207        let updated_at = u64::from_le_bytes(data[offset..offset + 8].try_into().unwrap());
208        offset += 8;
209
210        // Columns
211        let (col_count, varint_len) = read_varint(&data[offset..])?;
212        offset += varint_len;
213        let mut columns = Vec::with_capacity(col_count as usize);
214        for _ in 0..col_count {
215            let (col, col_len) = ColumnDef::read_from(&data[offset..])?;
216            offset += col_len;
217            columns.push(col);
218        }
219
220        // Primary key
221        let (pk_count, varint_len) = read_varint(&data[offset..])?;
222        offset += varint_len;
223        let mut primary_key = Vec::with_capacity(pk_count as usize);
224        for _ in 0..pk_count {
225            let (pk, pk_len) = read_string(&data[offset..])?;
226            offset += pk_len;
227            primary_key.push(pk);
228        }
229
230        // Indexes
231        let (idx_count, varint_len) = read_varint(&data[offset..])?;
232        offset += varint_len;
233        let mut indexes = Vec::with_capacity(idx_count as usize);
234        for _ in 0..idx_count {
235            let (idx, idx_len) = IndexDef::read_from(&data[offset..])?;
236            offset += idx_len;
237            indexes.push(idx);
238        }
239
240        // Constraints
241        let (constraint_count, varint_len) = read_varint(&data[offset..])?;
242        offset += varint_len;
243        let mut constraints = Vec::with_capacity(constraint_count as usize);
244        for _ in 0..constraint_count {
245            let (constraint, constraint_len) = Constraint::read_from(&data[offset..])?;
246            offset += constraint_len;
247            constraints.push(constraint);
248        }
249
250        Ok(Self {
251            name,
252            columns,
253            primary_key,
254            indexes,
255            constraints,
256            version,
257            created_at,
258            updated_at,
259        })
260    }
261}
262
263impl fmt::Display for TableDef {
264    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
265        writeln!(f, "TABLE {} (version {})", self.name, self.version)?;
266        writeln!(f, "  Columns:")?;
267        for col in &self.columns {
268            writeln!(f, "    {}", col)?;
269        }
270        if !self.primary_key.is_empty() {
271            writeln!(f, "  Primary Key: ({})", self.primary_key.join(", "))?;
272        }
273        if !self.indexes.is_empty() {
274            writeln!(f, "  Indexes:")?;
275            for idx in &self.indexes {
276                writeln!(f, "    {}", idx)?;
277            }
278        }
279        Ok(())
280    }
281}
282
283/// Column definition
284#[derive(Debug, Clone)]
285pub struct ColumnDef {
286    /// Column name
287    pub name: String,
288    /// Data type
289    pub data_type: DataType,
290    /// Whether NULL values are allowed
291    pub nullable: bool,
292    /// Default value (serialized)
293    pub default: Option<Vec<u8>>,
294    /// Vector dimension (for Vector type)
295    pub vector_dim: Option<u32>,
296    /// Whether to compress this column's data (e.g., brotli for text)
297    pub compress: bool,
298    /// For Enum type: list of valid variants
299    pub enum_variants: Vec<String>,
300    /// For Decimal type: number of decimal places (default 4)
301    pub decimal_precision: u8,
302    /// For Array type: element data type
303    pub element_type: Option<DataType>,
304    /// Additional column metadata
305    pub metadata: HashMap<String, String>,
306}
307
308impl ColumnDef {
309    /// Create a new column definition
310    pub fn new(name: impl Into<String>, data_type: DataType) -> Self {
311        Self {
312            name: name.into(),
313            data_type,
314            nullable: true,
315            default: None,
316            vector_dim: None,
317            compress: false,
318            enum_variants: Vec::new(),
319            decimal_precision: 4,
320            element_type: None,
321            metadata: HashMap::new(),
322        }
323    }
324
325    /// Create a non-nullable column
326    pub fn not_null(mut self) -> Self {
327        self.nullable = false;
328        self
329    }
330
331    /// Set default value
332    pub fn with_default(mut self, default: Vec<u8>) -> Self {
333        self.default = Some(default);
334        self
335    }
336
337    /// Set vector dimension
338    pub fn with_vector_dim(mut self, dim: u32) -> Self {
339        self.vector_dim = Some(dim);
340        self
341    }
342
343    /// Add metadata
344    pub fn with_metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
345        self.metadata.insert(key.into(), value.into());
346        self
347    }
348
349    /// Enable per-column compression
350    pub fn compressed(mut self) -> Self {
351        self.compress = true;
352        self
353    }
354
355    /// Set enum variants (for Enum type columns)
356    pub fn with_variants(mut self, variants: Vec<String>) -> Self {
357        self.enum_variants = variants;
358        self
359    }
360
361    /// Set decimal precision (for Decimal type columns)
362    pub fn with_precision(mut self, precision: u8) -> Self {
363        self.decimal_precision = precision;
364        self
365    }
366
367    /// Set element type (for Array type columns)
368    pub fn with_element_type(mut self, dt: DataType) -> Self {
369        self.element_type = Some(dt);
370        self
371    }
372
373    /// Serialize column definition
374    fn write_to(&self, buf: &mut Vec<u8>) {
375        write_string(buf, &self.name);
376        buf.push(self.data_type.to_byte());
377        buf.push(if self.nullable { 1 } else { 0 });
378
379        // Default value
380        if let Some(ref default) = self.default {
381            buf.push(1);
382            write_varint(buf, default.len() as u64);
383            buf.extend_from_slice(default);
384        } else {
385            buf.push(0);
386        }
387
388        // Vector dimension
389        if let Some(dim) = self.vector_dim {
390            buf.push(1);
391            buf.extend_from_slice(&dim.to_le_bytes());
392        } else {
393            buf.push(0);
394        }
395
396        // Compress flag
397        buf.push(if self.compress { 1 } else { 0 });
398
399        // Enum variants
400        write_varint(buf, self.enum_variants.len() as u64);
401        for variant in &self.enum_variants {
402            write_string(buf, variant);
403        }
404
405        // Decimal precision
406        buf.push(self.decimal_precision);
407
408        // Element type (for Array)
409        if let Some(et) = self.element_type {
410            buf.push(1);
411            buf.push(et.to_byte());
412        } else {
413            buf.push(0);
414        }
415
416        // Metadata
417        write_varint(buf, self.metadata.len() as u64);
418        for (k, v) in &self.metadata {
419            write_string(buf, k);
420            write_string(buf, v);
421        }
422    }
423
424    /// Deserialize column definition
425    fn read_from(data: &[u8]) -> Result<(Self, usize), TableDefError> {
426        let mut offset = 0;
427
428        let (name, name_len) = read_string(&data[offset..])?;
429        offset += name_len;
430
431        if data.len() < offset + 2 {
432            return Err(TableDefError::TruncatedData);
433        }
434
435        let data_type = DataType::from_byte(data[offset]).ok_or(TableDefError::InvalidDataType)?;
436        offset += 1;
437
438        let nullable = data[offset] != 0;
439        offset += 1;
440
441        // Default value
442        if data.len() < offset + 1 {
443            return Err(TableDefError::TruncatedData);
444        }
445        let has_default = data[offset] != 0;
446        offset += 1;
447        let default = if has_default {
448            let (len, varint_len) = read_varint(&data[offset..])?;
449            offset += varint_len;
450            if data.len() < offset + len as usize {
451                return Err(TableDefError::TruncatedData);
452            }
453            let default_data = data[offset..offset + len as usize].to_vec();
454            offset += len as usize;
455            Some(default_data)
456        } else {
457            None
458        };
459
460        // Vector dimension
461        if data.len() < offset + 1 {
462            return Err(TableDefError::TruncatedData);
463        }
464        let has_vector_dim = data[offset] != 0;
465        offset += 1;
466        let vector_dim = if has_vector_dim {
467            if data.len() < offset + 4 {
468                return Err(TableDefError::TruncatedData);
469            }
470            let dim = u32::from_le_bytes(data[offset..offset + 4].try_into().unwrap());
471            offset += 4;
472            Some(dim)
473        } else {
474            None
475        };
476
477        // Compress flag
478        if data.len() < offset + 1 {
479            return Err(TableDefError::TruncatedData);
480        }
481        let compress = data[offset] != 0;
482        offset += 1;
483
484        // Enum variants
485        let (variant_count, varint_len) = read_varint(&data[offset..])?;
486        offset += varint_len;
487        let mut enum_variants = Vec::with_capacity(variant_count as usize);
488        for _ in 0..variant_count {
489            let (variant, variant_len) = read_string(&data[offset..])?;
490            offset += variant_len;
491            enum_variants.push(variant);
492        }
493
494        // Decimal precision
495        if data.len() < offset + 1 {
496            return Err(TableDefError::TruncatedData);
497        }
498        let decimal_precision = data[offset];
499        offset += 1;
500
501        // Element type
502        if data.len() < offset + 1 {
503            return Err(TableDefError::TruncatedData);
504        }
505        let has_element_type = data[offset] != 0;
506        offset += 1;
507        let element_type = if has_element_type {
508            if data.len() < offset + 1 {
509                return Err(TableDefError::TruncatedData);
510            }
511            let et = DataType::from_byte(data[offset]).ok_or(TableDefError::InvalidDataType)?;
512            offset += 1;
513            Some(et)
514        } else {
515            None
516        };
517
518        // Metadata
519        let (meta_count, varint_len) = read_varint(&data[offset..])?;
520        offset += varint_len;
521        let mut metadata = HashMap::with_capacity(meta_count as usize);
522        for _ in 0..meta_count {
523            let (k, k_len) = read_string(&data[offset..])?;
524            offset += k_len;
525            let (v, v_len) = read_string(&data[offset..])?;
526            offset += v_len;
527            metadata.insert(k, v);
528        }
529
530        Ok((
531            Self {
532                name,
533                data_type,
534                nullable,
535                default,
536                vector_dim,
537                compress,
538                enum_variants,
539                decimal_precision,
540                element_type,
541                metadata,
542            },
543            offset,
544        ))
545    }
546}
547
548impl fmt::Display for ColumnDef {
549    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
550        write!(f, "{} {}", self.name, self.data_type)?;
551        if let Some(dim) = self.vector_dim {
552            write!(f, "({})", dim)?;
553        }
554        if !self.nullable {
555            write!(f, " NOT NULL")?;
556        }
557        if self.default.is_some() {
558            write!(f, " DEFAULT <value>")?;
559        }
560        Ok(())
561    }
562}
563
564/// Index definition
565#[derive(Debug, Clone)]
566pub struct IndexDef {
567    /// Index name
568    pub name: String,
569    /// Column names in order
570    pub columns: Vec<String>,
571    /// Index type
572    pub index_type: IndexType,
573    /// Whether values must be unique
574    pub unique: bool,
575}
576
577impl IndexDef {
578    /// Create a new index
579    pub fn new(name: impl Into<String>, columns: Vec<String>) -> Self {
580        Self {
581            name: name.into(),
582            columns,
583            index_type: IndexType::BTree,
584            unique: false,
585        }
586    }
587
588    /// Create a unique index
589    pub fn unique(mut self) -> Self {
590        self.unique = true;
591        self
592    }
593
594    /// Set index type
595    pub fn with_type(mut self, index_type: IndexType) -> Self {
596        self.index_type = index_type;
597        self
598    }
599
600    /// Serialize index
601    fn write_to(&self, buf: &mut Vec<u8>) {
602        write_string(buf, &self.name);
603        buf.push(self.index_type as u8);
604        buf.push(if self.unique { 1 } else { 0 });
605        write_varint(buf, self.columns.len() as u64);
606        for col in &self.columns {
607            write_string(buf, col);
608        }
609    }
610
611    /// Deserialize index
612    fn read_from(data: &[u8]) -> Result<(Self, usize), TableDefError> {
613        let mut offset = 0;
614
615        let (name, name_len) = read_string(&data[offset..])?;
616        offset += name_len;
617
618        if data.len() < offset + 2 {
619            return Err(TableDefError::TruncatedData);
620        }
621
622        let index_type =
623            IndexType::from_byte(data[offset]).ok_or(TableDefError::InvalidIndexType)?;
624        offset += 1;
625
626        let unique = data[offset] != 0;
627        offset += 1;
628
629        let (col_count, varint_len) = read_varint(&data[offset..])?;
630        offset += varint_len;
631
632        let mut columns = Vec::with_capacity(col_count as usize);
633        for _ in 0..col_count {
634            let (col, col_len) = read_string(&data[offset..])?;
635            offset += col_len;
636            columns.push(col);
637        }
638
639        Ok((
640            Self {
641                name,
642                columns,
643                index_type,
644                unique,
645            },
646            offset,
647        ))
648    }
649}
650
651impl fmt::Display for IndexDef {
652    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
653        if self.unique {
654            write!(f, "UNIQUE ")?;
655        }
656        write!(
657            f,
658            "INDEX {} ({}) USING {:?}",
659            self.name,
660            self.columns.join(", "),
661            self.index_type
662        )
663    }
664}
665
666/// Index type
667#[derive(Debug, Clone, Copy, PartialEq, Eq)]
668#[repr(u8)]
669pub enum IndexType {
670    /// B-tree index (default, good for range queries)
671    BTree = 1,
672    /// Hash index (exact match only, faster for point queries)
673    Hash = 2,
674    /// IVF index for vector similarity search
675    IvfFlat = 3,
676    /// HNSW index for vector similarity search
677    Hnsw = 4,
678}
679
680impl IndexType {
681    fn from_byte(b: u8) -> Option<Self> {
682        match b {
683            1 => Some(IndexType::BTree),
684            2 => Some(IndexType::Hash),
685            3 => Some(IndexType::IvfFlat),
686            4 => Some(IndexType::Hnsw),
687            _ => None,
688        }
689    }
690}
691
692/// Constraint definition
693#[derive(Debug, Clone)]
694pub struct Constraint {
695    /// Constraint name
696    pub name: String,
697    /// Constraint type
698    pub constraint_type: ConstraintType,
699    /// Columns involved
700    pub columns: Vec<String>,
701    /// Reference table (for foreign keys)
702    pub ref_table: Option<String>,
703    /// Reference columns (for foreign keys)
704    pub ref_columns: Option<Vec<String>>,
705}
706
707impl Constraint {
708    /// Create a new constraint
709    pub fn new(name: impl Into<String>, constraint_type: ConstraintType) -> Self {
710        Self {
711            name: name.into(),
712            constraint_type,
713            columns: Vec::new(),
714            ref_table: None,
715            ref_columns: None,
716        }
717    }
718
719    /// Set columns
720    pub fn on_columns(mut self, columns: Vec<String>) -> Self {
721        self.columns = columns;
722        self
723    }
724
725    /// Set foreign key reference
726    pub fn references(mut self, table: String, columns: Vec<String>) -> Self {
727        self.ref_table = Some(table);
728        self.ref_columns = Some(columns);
729        self
730    }
731
732    /// Serialize constraint
733    fn write_to(&self, buf: &mut Vec<u8>) {
734        write_string(buf, &self.name);
735        buf.push(self.constraint_type as u8);
736
737        write_varint(buf, self.columns.len() as u64);
738        for col in &self.columns {
739            write_string(buf, col);
740        }
741
742        if let Some(ref table) = self.ref_table {
743            buf.push(1);
744            write_string(buf, table);
745            if let Some(ref cols) = self.ref_columns {
746                write_varint(buf, cols.len() as u64);
747                for col in cols {
748                    write_string(buf, col);
749                }
750            } else {
751                write_varint(buf, 0);
752            }
753        } else {
754            buf.push(0);
755        }
756    }
757
758    /// Deserialize constraint
759    fn read_from(data: &[u8]) -> Result<(Self, usize), TableDefError> {
760        let mut offset = 0;
761
762        let (name, name_len) = read_string(&data[offset..])?;
763        offset += name_len;
764
765        if data.len() < offset + 1 {
766            return Err(TableDefError::TruncatedData);
767        }
768
769        let constraint_type =
770            ConstraintType::from_byte(data[offset]).ok_or(TableDefError::InvalidConstraintType)?;
771        offset += 1;
772
773        let (col_count, varint_len) = read_varint(&data[offset..])?;
774        offset += varint_len;
775
776        let mut columns = Vec::with_capacity(col_count as usize);
777        for _ in 0..col_count {
778            let (col, col_len) = read_string(&data[offset..])?;
779            offset += col_len;
780            columns.push(col);
781        }
782
783        if data.len() < offset + 1 {
784            return Err(TableDefError::TruncatedData);
785        }
786
787        let has_ref = data[offset] != 0;
788        offset += 1;
789
790        let (ref_table, ref_columns) = if has_ref {
791            let (table, table_len) = read_string(&data[offset..])?;
792            offset += table_len;
793
794            let (ref_col_count, varint_len) = read_varint(&data[offset..])?;
795            offset += varint_len;
796
797            let mut ref_cols = Vec::with_capacity(ref_col_count as usize);
798            for _ in 0..ref_col_count {
799                let (col, col_len) = read_string(&data[offset..])?;
800                offset += col_len;
801                ref_cols.push(col);
802            }
803
804            (Some(table), Some(ref_cols))
805        } else {
806            (None, None)
807        };
808
809        Ok((
810            Self {
811                name,
812                constraint_type,
813                columns,
814                ref_table,
815                ref_columns,
816            },
817            offset,
818        ))
819    }
820}
821
822/// Constraint type
823#[derive(Debug, Clone, Copy, PartialEq, Eq)]
824#[repr(u8)]
825pub enum ConstraintType {
826    /// Primary key constraint
827    PrimaryKey = 1,
828    /// Unique constraint
829    Unique = 2,
830    /// Foreign key constraint
831    ForeignKey = 3,
832    /// Check constraint
833    Check = 4,
834    /// Not null constraint
835    NotNull = 5,
836}
837
838impl ConstraintType {
839    fn from_byte(b: u8) -> Option<Self> {
840        match b {
841            1 => Some(ConstraintType::PrimaryKey),
842            2 => Some(ConstraintType::Unique),
843            3 => Some(ConstraintType::ForeignKey),
844            4 => Some(ConstraintType::Check),
845            5 => Some(ConstraintType::NotNull),
846            _ => None,
847        }
848    }
849}
850
851/// Errors that can occur with table definitions
852#[derive(Debug, Clone, PartialEq)]
853pub enum TableDefError {
854    /// Empty table name
855    EmptyTableName,
856    /// Duplicate column name
857    DuplicateColumn(String),
858    /// Invalid primary key column
859    InvalidPrimaryKey(String),
860    /// Invalid index column
861    InvalidIndexColumn(String),
862    /// Invalid constraint column
863    InvalidConstraintColumn(String),
864    /// Truncated data
865    TruncatedData,
866    /// Invalid magic bytes
867    InvalidMagic,
868    /// Invalid data type
869    InvalidDataType,
870    /// Invalid index type
871    InvalidIndexType,
872    /// Invalid constraint type
873    InvalidConstraintType,
874    /// Varint overflow
875    VarintOverflow,
876}
877
878impl fmt::Display for TableDefError {
879    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
880        match self {
881            TableDefError::EmptyTableName => write!(f, "empty table name"),
882            TableDefError::DuplicateColumn(name) => write!(f, "duplicate column: {}", name),
883            TableDefError::InvalidPrimaryKey(name) => {
884                write!(f, "invalid primary key column: {}", name)
885            }
886            TableDefError::InvalidIndexColumn(name) => write!(f, "invalid index column: {}", name),
887            TableDefError::InvalidConstraintColumn(name) => {
888                write!(f, "invalid constraint column: {}", name)
889            }
890            TableDefError::TruncatedData => write!(f, "truncated data"),
891            TableDefError::InvalidMagic => write!(f, "invalid magic bytes"),
892            TableDefError::InvalidDataType => write!(f, "invalid data type"),
893            TableDefError::InvalidIndexType => write!(f, "invalid index type"),
894            TableDefError::InvalidConstraintType => write!(f, "invalid constraint type"),
895            TableDefError::VarintOverflow => write!(f, "varint overflow"),
896        }
897    }
898}
899
900impl std::error::Error for TableDefError {}
901
902/// Write a variable-length integer
903fn write_varint(buf: &mut Vec<u8>, mut value: u64) {
904    loop {
905        let mut byte = (value & 0x7F) as u8;
906        value >>= 7;
907        if value != 0 {
908            byte |= 0x80;
909        }
910        buf.push(byte);
911        if value == 0 {
912            break;
913        }
914    }
915}
916
917/// Read a variable-length integer
918fn read_varint(data: &[u8]) -> Result<(u64, usize), TableDefError> {
919    let mut result: u64 = 0;
920    let mut shift = 0;
921    let mut offset = 0;
922
923    loop {
924        if offset >= data.len() {
925            return Err(TableDefError::TruncatedData);
926        }
927        let byte = data[offset];
928        offset += 1;
929
930        if shift >= 64 {
931            return Err(TableDefError::VarintOverflow);
932        }
933
934        result |= ((byte & 0x7F) as u64) << shift;
935        shift += 7;
936
937        if byte & 0x80 == 0 {
938            break;
939        }
940    }
941
942    Ok((result, offset))
943}
944
945/// Write a length-prefixed string
946fn write_string(buf: &mut Vec<u8>, s: &str) {
947    let bytes = s.as_bytes();
948    write_varint(buf, bytes.len() as u64);
949    buf.extend_from_slice(bytes);
950}
951
952/// Read a length-prefixed string
953fn read_string(data: &[u8]) -> Result<(String, usize), TableDefError> {
954    let (len, varint_len) = read_varint(data)?;
955    let offset = varint_len;
956    if data.len() < offset + len as usize {
957        return Err(TableDefError::TruncatedData);
958    }
959    let s = String::from_utf8(data[offset..offset + len as usize].to_vec())
960        .map_err(|_| TableDefError::TruncatedData)?;
961    Ok((s, offset + len as usize))
962}
963
964#[cfg(test)]
965mod tests {
966    use super::*;
967
968    #[test]
969    fn test_table_def_basic() {
970        let table = TableDef::new("port_scans")
971            .add_column(ColumnDef::new("id", DataType::UnsignedInteger).not_null())
972            .add_column(ColumnDef::new("ip", DataType::IpAddr).not_null())
973            .add_column(ColumnDef::new("port", DataType::UnsignedInteger).not_null())
974            .add_column(ColumnDef::new("status", DataType::Text))
975            .add_column(ColumnDef::new("timestamp", DataType::Timestamp).not_null())
976            .primary_key(vec!["id".to_string()]);
977
978        assert_eq!(table.name, "port_scans");
979        assert_eq!(table.columns.len(), 5);
980        assert_eq!(table.primary_key, vec!["id"]);
981        assert!(table.validate().is_ok());
982    }
983
984    #[test]
985    fn test_table_def_with_indexes() {
986        let table = TableDef::new("subdomains")
987            .add_column(ColumnDef::new("id", DataType::UnsignedInteger).not_null())
988            .add_column(ColumnDef::new("domain", DataType::Text).not_null())
989            .add_column(ColumnDef::new("subdomain", DataType::Text).not_null())
990            .add_column(ColumnDef::new("ip", DataType::IpAddr))
991            .primary_key(vec!["id".to_string()])
992            .add_index(IndexDef::new("idx_domain", vec!["domain".to_string()]))
993            .add_index(IndexDef::new("idx_subdomain", vec!["subdomain".to_string()]).unique());
994
995        assert_eq!(table.indexes.len(), 2);
996        assert!(table.indexes[1].unique);
997        assert!(table.validate().is_ok());
998    }
999
1000    #[test]
1001    fn test_table_def_with_vector() {
1002        let table = TableDef::new("embeddings")
1003            .add_column(ColumnDef::new("id", DataType::UnsignedInteger).not_null())
1004            .add_column(
1005                ColumnDef::new("embedding", DataType::Vector)
1006                    .not_null()
1007                    .with_vector_dim(384),
1008            )
1009            .add_column(ColumnDef::new("text", DataType::Text))
1010            .primary_key(vec!["id".to_string()])
1011            .add_index(
1012                IndexDef::new("idx_embedding", vec!["embedding".to_string()])
1013                    .with_type(IndexType::IvfFlat),
1014            );
1015
1016        let col = table.get_column("embedding").unwrap();
1017        assert_eq!(col.vector_dim, Some(384));
1018        assert!(table.validate().is_ok());
1019    }
1020
1021    #[test]
1022    fn test_table_def_validation_duplicate_column() {
1023        let table = TableDef::new("test")
1024            .add_column(ColumnDef::new("id", DataType::Integer))
1025            .add_column(ColumnDef::new("id", DataType::Text)); // Duplicate
1026
1027        assert!(matches!(
1028            table.validate(),
1029            Err(TableDefError::DuplicateColumn(_))
1030        ));
1031    }
1032
1033    #[test]
1034    fn test_table_def_validation_invalid_pk() {
1035        let table = TableDef::new("test")
1036            .add_column(ColumnDef::new("id", DataType::Integer))
1037            .primary_key(vec!["nonexistent".to_string()]);
1038
1039        assert!(matches!(
1040            table.validate(),
1041            Err(TableDefError::InvalidPrimaryKey(_))
1042        ));
1043    }
1044
1045    #[test]
1046    fn test_table_def_roundtrip() {
1047        let table = TableDef::new("hosts")
1048            .add_column(ColumnDef::new("id", DataType::UnsignedInteger).not_null())
1049            .add_column(ColumnDef::new("ip", DataType::IpAddr).not_null())
1050            .add_column(ColumnDef::new("hostname", DataType::Text))
1051            .add_column(ColumnDef::new("last_seen", DataType::Timestamp))
1052            .add_column(ColumnDef::new("fingerprint", DataType::Vector).with_vector_dim(128))
1053            .primary_key(vec!["id".to_string()])
1054            .add_index(IndexDef::new("idx_ip", vec!["ip".to_string()]).unique())
1055            .add_index(
1056                IndexDef::new("idx_fingerprint", vec!["fingerprint".to_string()])
1057                    .with_type(IndexType::IvfFlat),
1058            );
1059
1060        let bytes = table.to_bytes();
1061        let recovered = TableDef::from_bytes(&bytes).unwrap();
1062
1063        assert_eq!(table.name, recovered.name);
1064        assert_eq!(table.columns.len(), recovered.columns.len());
1065        assert_eq!(table.primary_key, recovered.primary_key);
1066        assert_eq!(table.indexes.len(), recovered.indexes.len());
1067
1068        for (orig, rec) in table.columns.iter().zip(recovered.columns.iter()) {
1069            assert_eq!(orig.name, rec.name);
1070            assert_eq!(orig.data_type, rec.data_type);
1071            assert_eq!(orig.nullable, rec.nullable);
1072            assert_eq!(orig.vector_dim, rec.vector_dim);
1073        }
1074
1075        for (orig, rec) in table.indexes.iter().zip(recovered.indexes.iter()) {
1076            assert_eq!(orig.name, rec.name);
1077            assert_eq!(orig.columns, rec.columns);
1078            assert_eq!(orig.unique, rec.unique);
1079            assert_eq!(orig.index_type, rec.index_type);
1080        }
1081    }
1082
1083    #[test]
1084    fn test_column_def_metadata() {
1085        let col = ColumnDef::new("ip", DataType::IpAddr)
1086            .not_null()
1087            .with_metadata("description", "Target IP address")
1088            .with_metadata("indexed", "true");
1089
1090        assert_eq!(
1091            col.metadata.get("description"),
1092            Some(&"Target IP address".to_string())
1093        );
1094        assert_eq!(col.metadata.get("indexed"), Some(&"true".to_string()));
1095    }
1096
1097    #[test]
1098    fn test_constraint_foreign_key() {
1099        let constraint = Constraint::new("fk_host", ConstraintType::ForeignKey)
1100            .on_columns(vec!["host_id".to_string()])
1101            .references("hosts".to_string(), vec!["id".to_string()]);
1102
1103        assert_eq!(constraint.constraint_type, ConstraintType::ForeignKey);
1104        assert_eq!(constraint.columns, vec!["host_id"]);
1105        assert_eq!(constraint.ref_table, Some("hosts".to_string()));
1106        assert_eq!(constraint.ref_columns, Some(vec!["id".to_string()]));
1107    }
1108
1109    #[test]
1110    fn test_table_display() {
1111        let table = TableDef::new("test")
1112            .add_column(ColumnDef::new("id", DataType::Integer).not_null())
1113            .add_column(ColumnDef::new("name", DataType::Text))
1114            .primary_key(vec!["id".to_string()]);
1115
1116        let display = format!("{}", table);
1117        assert!(display.contains("TABLE test"));
1118        assert!(display.contains("id INTEGER NOT NULL"));
1119        assert!(display.contains("name TEXT"));
1120        assert!(display.contains("Primary Key: (id)"));
1121    }
1122}