Skip to main content

basalt/
db.rs

1//! Storage core: catalog, tables, rows, and constraint enforcement.
2//!
3//! Rows live in a slot array (`Vec<Option<Row>>`) so row ids stay stable across
4//! deletes; tombstones are skipped during scans. PRIMARY KEY / UNIQUE
5//! constraints are enforced through hand-written B+trees keyed by (Value, rid).
6
7use std::collections::{HashMap, HashSet};
8
9use crate::btree::BTree;
10use crate::types::{ColumnType, Value};
11
12pub type Row = Vec<Value>;
13
14#[derive(Debug, Clone)]
15pub struct Column {
16    pub name: String,
17    pub ty: ColumnType,
18    pub not_null: bool,
19    pub unique: bool,
20    pub primary_key: bool,
21}
22
23#[derive(Debug, Clone, PartialEq, Eq)]
24pub enum DbErrorKind {
25    UnknownTable,
26    DuplicateColumn,
27    Constraint,
28    TypeMismatch,
29    Syntax(String),
30    NotNull,
31    UnknownColumn,
32    ColumnCount,
33    Io(String),
34    Busy,
35    Transaction,
36    Limit,
37    Internal(String),
38}
39
40#[derive(Debug, Clone, PartialEq, Eq)]
41pub struct DbError {
42    pub kind: DbErrorKind,
43    pub message: String,
44}
45
46impl DbError {
47    pub fn new(kind: DbErrorKind, message: impl Into<String>) -> DbError {
48        DbError {
49            kind,
50            message: message.into(),
51        }
52    }
53}
54
55impl std::fmt::Display for DbError {
56    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57        write!(f, "{}", self.message)
58    }
59}
60
61impl std::error::Error for DbError {}
62
63/// A snapshot of the whole database, shared by readers and swapped on commit.
64#[derive(Debug, Clone)]
65pub struct State {
66    pub tables: HashMap<String, Table>,
67}
68
69impl State {
70    pub fn empty() -> State {
71        State {
72            tables: HashMap::new(),
73        }
74    }
75
76    pub fn table(&self, name: &str) -> Option<&Table> {
77        self.tables
78            .iter()
79            .find(|(key, _)| key.eq_ignore_ascii_case(name))
80            .map(|(_, table)| table)
81    }
82
83    pub fn table_mut(&mut self, name: &str) -> Option<&mut Table> {
84        let key = self
85            .tables
86            .keys()
87            .find(|key| key.eq_ignore_ascii_case(name))?
88            .clone();
89        self.tables.get_mut(&key)
90    }
91
92    pub fn contains_table(&self, name: &str) -> bool {
93        self.table(name).is_some()
94    }
95
96    pub fn contains_index(&self, name: &str) -> bool {
97        self.tables.values().any(|table| table.has_index(name))
98    }
99
100    pub fn remove_table(&mut self, name: &str) -> Option<Table> {
101        let key = self
102            .tables
103            .keys()
104            .find(|key| key.eq_ignore_ascii_case(name))?
105            .clone();
106        self.tables.remove(&key)
107    }
108
109    /// Encode the catalog and row stores into a versioned, deterministic
110    /// binary representation used by the page store and WAL.
111    pub(crate) fn encode(&self) -> Vec<u8> {
112        let mut w = BinWriter::default();
113        w.bytes(b"BSS1");
114        w.u32(1);
115        let mut names: Vec<&String> = self.tables.keys().collect();
116        names.sort();
117        w.u32(names.len() as u32);
118        for name in names {
119            let table = &self.tables[name];
120            w.string(&table.name);
121            w.u32(table.columns.len() as u32);
122            for column in &table.columns {
123                w.string(&column.name);
124                w.u8(column_type_tag(&column.ty));
125                let mut flags = 0u8;
126                if column.not_null {
127                    flags |= 1;
128                }
129                if column.unique {
130                    flags |= 2;
131                }
132                if column.primary_key {
133                    flags |= 4;
134                }
135                w.u8(flags);
136            }
137            w.u32(table.indexes.len() as u32);
138            for index in &table.indexes {
139                w.string(&index.name);
140                w.u32(index.column as u32);
141                w.u8(index.unique as u8);
142            }
143            w.u64(table.row_seq);
144            w.u32(table.rows.len() as u32);
145            for row in &table.rows {
146                match row {
147                    None => w.u8(0),
148                    Some(values) => {
149                        w.u8(1);
150                        w.u32(values.len() as u32);
151                        for value in values {
152                            encode_value(&mut w, value);
153                        }
154                    }
155                }
156            }
157        }
158        w.finish()
159    }
160
161    /// Decode a snapshot and rebuild all derived indexes.  Corrupt or
162    /// inconsistent data is rejected before it becomes visible to callers.
163    pub(crate) fn decode(bytes: &[u8]) -> Result<State, DbError> {
164        let mut r = BinReader::new(bytes);
165        if r.bytes(4)? != b"BSS1" {
166            return Err(dberr(
167                DbErrorKind::Io("invalid state magic".into()),
168                "corrupt database: invalid state magic",
169            ));
170        }
171        if r.u32()? != 1 {
172            return Err(dberr(
173                DbErrorKind::Io("unsupported state version".into()),
174                "corrupt database: unsupported state version",
175            ));
176        }
177        let table_count = r.count("table")?;
178        let mut tables: HashMap<String, Table> = HashMap::new();
179        let mut index_names = HashSet::new();
180        for _ in 0..table_count {
181            let name = r.string("table name")?;
182            if tables
183                .keys()
184                .any(|existing| existing.eq_ignore_ascii_case(&name))
185            {
186                return Err(dberr(
187                    DbErrorKind::Io("duplicate table name".into()),
188                    format!("corrupt database: duplicate table '{name}'"),
189                ));
190            }
191            let column_count = r.count("column")?;
192            let mut columns = Vec::with_capacity(column_count);
193            for _ in 0..column_count {
194                let column_name = r.string("column name")?;
195                let ty = column_type_from_tag(r.u8()?)?;
196                let flags = r.u8()?;
197                if flags & !0b111 != 0 {
198                    return Err(dberr(
199                        DbErrorKind::Io("invalid column flags".into()),
200                        "corrupt database: invalid column flags",
201                    ));
202                }
203                columns.push(Column {
204                    name: column_name,
205                    ty,
206                    not_null: flags & 1 != 0,
207                    unique: flags & 2 != 0,
208                    primary_key: flags & 4 != 0,
209                });
210            }
211            let index_count = r.count("index")?;
212            let mut index_defs = Vec::with_capacity(index_count);
213            for _ in 0..index_count {
214                let index_name = r.string("index name")?;
215                let column = r.u32()? as usize;
216                let unique = match r.u8()? {
217                    0 => false,
218                    1 => true,
219                    _ => {
220                        return Err(dberr(
221                            DbErrorKind::Io("invalid index uniqueness flag".into()),
222                            "corrupt database: invalid index uniqueness flag",
223                        ));
224                    }
225                };
226                index_defs.push((index_name, column, unique));
227            }
228            let row_seq = r.u64()?;
229            let slot_count = r.count("row slot")?;
230            if row_seq != slot_count as u64 {
231                return Err(dberr(
232                    DbErrorKind::Io("row sequence does not match slot array".into()),
233                    format!("corrupt database: invalid row sequence for table '{name}'"),
234                ));
235            }
236            let mut table = Table::new(&name, columns)?;
237            let mut live = 0usize;
238            table.rows.reserve(slot_count);
239            for _ in 0..slot_count {
240                match r.u8()? {
241                    0 => table.rows.push(None),
242                    1 => {
243                        let value_count = r.count("row value")?;
244                        if value_count != table.columns.len() {
245                            return Err(dberr(
246                                DbErrorKind::Io("row width does not match table".into()),
247                                format!("corrupt database: invalid row width in table '{name}'"),
248                            ));
249                        }
250                        let mut row = Vec::with_capacity(value_count);
251                        for _ in 0..value_count {
252                            row.push(decode_value(&mut r)?);
253                        }
254                        for (idx, column) in table.columns.iter().enumerate() {
255                            if (column.not_null || column.primary_key)
256                                && matches!(row[idx], Value::Null)
257                            {
258                                return Err(dberr(
259                                    DbErrorKind::Io("stored NULL violates NOT NULL".into()),
260                                    format!("corrupt database: NULL in '{}'", column.name),
261                                ));
262                            }
263                            row[idx] = row[idx].coerce_to(&column.ty).map_err(|e| {
264                                dberr(
265                                    DbErrorKind::Io(e.clone()),
266                                    format!(
267                                        "corrupt database: invalid value in '{}': {e}",
268                                        column.name
269                                    ),
270                                )
271                            })?;
272                        }
273                        table.rows.push(Some(row));
274                        live += 1;
275                    }
276                    _ => {
277                        return Err(dberr(
278                            DbErrorKind::Io("invalid row slot marker".into()),
279                            format!("corrupt database: invalid row slot in table '{name}'"),
280                        ));
281                    }
282                }
283            }
284            table.row_seq = row_seq;
285            table.live = live;
286            table.rebuild_indexes();
287            for (index_name, column, unique) in index_defs {
288                if !index_names.insert(index_name.to_ascii_lowercase()) {
289                    return Err(dberr(
290                        DbErrorKind::Io("duplicate index name".into()),
291                        format!("corrupt database: duplicate index '{index_name}'"),
292                    ));
293                }
294                table.create_index(&index_name, column, unique)?;
295            }
296            table.validate_indexes()?;
297            tables.insert(name, table);
298        }
299        if !r.at_end() {
300            return Err(dberr(
301                DbErrorKind::Io("trailing state bytes".into()),
302                "corrupt database: trailing state bytes",
303            ));
304        }
305        Ok(State { tables })
306    }
307}
308
309/// Result payload of an executed statement.
310#[derive(Debug, Clone)]
311pub enum StatementResult {
312    Select {
313        columns: Vec<String>,
314        rows: Vec<Row>,
315    },
316    Insert {
317        rows_affected: usize,
318    },
319    Update {
320        rows_affected: usize,
321    },
322    Delete {
323        rows_affected: usize,
324    },
325    CreateTable {
326        name: String,
327    },
328    DropTable {
329        name: String,
330    },
331    CreateIndex {
332        name: String,
333        table: String,
334        column: String,
335    },
336    DropIndex {
337        name: String,
338    },
339    Explain(String),
340    Begin,
341    Commit,
342    Rollback,
343    Checkpoint,
344    Echo(String),
345}
346
347#[derive(Debug, Clone)]
348pub struct Table {
349    pub name: String,
350    pub columns: Vec<Column>,
351    rows: Vec<Option<Row>>,
352    row_seq: u64,
353    live: usize,
354    /// B-tree for the PRIMARY KEY column, if any.
355    pk: Option<BTree>,
356    /// B-trees for UNIQUE columns (column index in `columns`).
357    uniques: Vec<(usize, BTree)>,
358    /// User-created indexes used by the planner.
359    indexes: Vec<Index>,
360}
361
362#[derive(Debug, Clone)]
363pub struct Index {
364    pub name: String,
365    pub column: usize,
366    pub unique: bool,
367    pub(crate) tree: BTree,
368}
369
370impl Table {
371    pub fn new(name: &str, columns: Vec<Column>) -> Result<Table, DbError> {
372        let mut seen = HashMap::new();
373        let mut pk: Option<BTree> = None;
374        let mut uniques = Vec::new();
375        let mut primary_count = 0usize;
376        for (i, c) in columns.iter().enumerate() {
377            if seen.insert(c.name.to_ascii_lowercase(), ()).is_some() {
378                return Err(DbError::new(
379                    DbErrorKind::DuplicateColumn,
380                    format!("duplicate column name '{}'", c.name),
381                ));
382            }
383            if c.primary_key && c.ty != ColumnType::Integer {
384                return Err(DbError::new(
385                    DbErrorKind::Constraint,
386                    "PRIMARY KEY column must be INTEGER".to_string(),
387                ));
388            }
389            if c.primary_key {
390                primary_count += 1;
391                pk = Some(BTree::default());
392            }
393            if c.unique && !c.primary_key {
394                uniques.push((i, BTree::default()));
395            }
396        }
397        if primary_count > 1 {
398            return Err(DbError::new(
399                DbErrorKind::Constraint,
400                "only one PRIMARY KEY column is supported",
401            ));
402        }
403        Ok(Table {
404            name: name.to_string(),
405            columns,
406            rows: Vec::new(),
407            row_seq: 0,
408            live: 0,
409            pk,
410            uniques,
411            indexes: Vec::new(),
412        })
413    }
414
415    pub fn row_count(&self) -> usize {
416        self.live
417    }
418
419    pub fn column_index(&self, name: &str) -> Result<usize, DbError> {
420        self.columns
421            .iter()
422            .position(|c| c.name.eq_ignore_ascii_case(name))
423            .ok_or_else(|| {
424                DbError::new(
425                    DbErrorKind::UnknownColumn,
426                    format!("no such column: {name}"),
427                )
428            })
429    }
430
431    pub fn coerce_val(&self, val: &Value, col_idx: usize) -> Result<Value, DbError> {
432        let ty = &self.columns[col_idx].ty;
433        val.coerce_to(ty).map_err(|e| {
434            DbError::new(
435                DbErrorKind::TypeMismatch,
436                format!("column '{}': {e}", self.columns[col_idx].name),
437            )
438        })
439    }
440
441    /// Allocate a fresh row id, reusing a free tombstone slot when available.
442    fn alloc_slot(&mut self) -> u64 {
443        if let Some((i, slot)) = self.rows.iter_mut().enumerate().find(|(_, r)| r.is_none()) {
444            *slot = Some(Vec::new());
445            return i as u64;
446        }
447        let id = self.row_seq;
448        self.row_seq += 1;
449        self.rows.push(Some(Vec::new()));
450        id
451    }
452
453    fn pk_col_idx(&self) -> Option<usize> {
454        self.columns.iter().position(|c| c.primary_key)
455    }
456
457    /// Insert a fully-validated row value vector; maintains indexes.
458    pub fn insert_row(&mut self, values: Vec<Value>) -> Result<u64, DbError> {
459        if values.len() != self.columns.len() {
460            return Err(DbError::new(
461                DbErrorKind::ColumnCount,
462                format!(
463                    "column count mismatch: expected {}, got {}",
464                    self.columns.len(),
465                    values.len()
466                ),
467            ));
468        }
469        self.validate_row(&values, None)?;
470        let rid = self.alloc_slot();
471        self.rows[rid as usize] = Some(values.clone());
472        self.live += 1;
473        let pk_idx = self.pk_col_idx();
474        if let Some(pk) = &mut self.pk {
475            let pk_idx = pk_idx.expect("has pk");
476            pk.insert(values[pk_idx].clone(), rid);
477        }
478        for (idx, tree) in &mut self.uniques {
479            let k = values[*idx].clone();
480            if !matches!(k, Value::Null) {
481                tree.insert(k, rid);
482            }
483        }
484        for index in &mut self.indexes {
485            let key = values[index.column].clone();
486            if !index.unique || !matches!(key, Value::Null) {
487                index.tree.insert(key, rid);
488            }
489        }
490        Ok(rid)
491    }
492
493    /// Remove a row by id; also removes its index entries.
494    pub fn delete_row(&mut self, rid: u64) -> Result<(), DbError> {
495        let idx = rid as usize;
496        if idx >= self.rows.len() || self.rows[idx].is_none() {
497            return Err(dberr(
498                DbErrorKind::Internal(format!("no such row id {rid}")),
499                format!("no such row id {rid}"),
500            ));
501        }
502        let row = self.rows[idx].take().unwrap();
503        self.live -= 1;
504        if self.pk.is_some() {
505            let pk_idx = self.pk_col_idx().expect("has pk");
506            if let Some(pk) = self.pk.as_mut() {
507                pk.delete(&row[pk_idx], rid);
508            }
509        }
510        for (cidx, tree) in &mut self.uniques {
511            let k = row[*cidx].clone();
512            if !matches!(k, Value::Null) {
513                tree.delete(&k, rid);
514            }
515        }
516        for index in &mut self.indexes {
517            index.tree.delete(&row[index.column], rid);
518        }
519        Ok(())
520    }
521
522    /// Get a row by id.
523    pub fn get_row(&self, rid: u64) -> Option<&Row> {
524        let idx = rid as usize;
525        self.rows.get(idx).and_then(|r| r.as_ref())
526    }
527
528    /// Iterate (rid, row) over live slots in slot order.
529    pub fn scan(&self) -> impl Iterator<Item = (u64, &Row)> {
530        self.rows
531            .iter()
532            .enumerate()
533            .filter_map(|(i, r)| r.as_ref().map(|row| (i as u64, row)))
534    }
535    /// Replace a row's values in place, removing old index entries and
536    /// inserting new ones. Caller is responsible for constraint validation.
537    pub fn replace_row(&mut self, rid: u64, new: Vec<Value>) -> Result<(), DbError> {
538        let idx = rid as usize;
539        if idx >= self.rows.len() || self.rows[idx].is_none() {
540            return Err(dberr(
541                DbErrorKind::Internal(format!("no such row id {rid}")),
542                format!("no such row id {rid}"),
543            ));
544        }
545        if new.len() != self.columns.len() {
546            return Err(dberr(
547                DbErrorKind::ColumnCount,
548                format!(
549                    "column count mismatch: expected {}, got {}",
550                    self.columns.len(),
551                    new.len()
552                ),
553            ));
554        }
555        self.validate_row(&new, Some(rid))?;
556        let old = self.rows[idx].take().unwrap();
557        if self.pk.is_some() {
558            let pk_idx = self.pk_col_idx().expect("has pk");
559            if let Some(pk) = self.pk.as_mut() {
560                pk.delete(&old[pk_idx], rid);
561            }
562        }
563        for (cidx, tree) in &mut self.uniques {
564            let k = old[*cidx].clone();
565            if !matches!(k, Value::Null) {
566                tree.delete(&k, rid);
567            }
568        }
569        for index in &mut self.indexes {
570            index.tree.delete(&old[index.column], rid);
571        }
572        if self.pk.is_some() {
573            let pk_idx = self.pk_col_idx().expect("has pk");
574            if let Some(pk) = self.pk.as_mut() {
575                pk.insert(new[pk_idx].clone(), rid);
576            }
577        }
578        for (cidx, tree) in &mut self.uniques {
579            let k = new[*cidx].clone();
580            if !matches!(k, Value::Null) {
581                tree.insert(k, rid);
582            }
583        }
584        for index in &mut self.indexes {
585            let key = new[index.column].clone();
586            if !index.unique || !matches!(key, Value::Null) {
587                index.tree.insert(key, rid);
588            }
589        }
590        self.rows[idx] = Some(new);
591        Ok(())
592    }
593
594    /// Lazy rebuild of unique/pk indexes — used when row store is bulk-loaded
595    /// or after operations that bypass per-row index maintenance.
596    pub fn rebuild_indexes(&mut self) {
597        if self.pk.is_some() {
598            self.pk = Some(BTree::default());
599        }
600        self.uniques = self
601            .uniques
602            .drain(..)
603            .map(|(i, _)| (i, BTree::default()))
604            .collect();
605        for index in &mut self.indexes {
606            index.tree = BTree::default();
607        }
608        let live: Vec<(u64, Row)> = self.scan().map(|(r, row)| (r, row.clone())).collect();
609        for (rid, row) in live {
610            if self.pk.is_some() {
611                let pk_idx = self.pk_col_idx().expect("has pk");
612                if let Some(pk) = self.pk.as_mut() {
613                    pk.insert(row[pk_idx].clone(), rid);
614                }
615            }
616            for (cidx, tree) in &mut self.uniques {
617                let k = row[*cidx].clone();
618                if !matches!(k, Value::Null) {
619                    tree.insert(k, rid);
620                }
621            }
622            for index in &mut self.indexes {
623                let key = row[index.column].clone();
624                if !index.unique || !matches!(key, Value::Null) {
625                    index.tree.insert(key, rid);
626                }
627            }
628        }
629    }
630
631    pub fn has_pk(&self, v: &Value) -> bool {
632        self.pk
633            .as_ref()
634            .map(|t| !t.lookup_eq(v).is_empty())
635            .unwrap_or(false)
636    }
637
638    pub fn unique_tree(&self, col_idx: usize) -> Option<&BTree> {
639        self.uniques
640            .iter()
641            .find(|(i, _)| *i == col_idx)
642            .map(|(_, t)| t)
643    }
644
645    pub fn create_index(&mut self, name: &str, column: usize, unique: bool) -> Result<(), DbError> {
646        if self
647            .indexes
648            .iter()
649            .any(|index| index.name.eq_ignore_ascii_case(name))
650        {
651            return Err(dberr(
652                DbErrorKind::Constraint,
653                format!("index '{name}' already exists"),
654            ));
655        }
656        if column >= self.columns.len() {
657            return Err(dberr(
658                DbErrorKind::UnknownColumn,
659                format!("no such column index: {column}"),
660            ));
661        }
662        let mut index = Index {
663            name: name.to_string(),
664            column,
665            unique,
666            tree: BTree::default(),
667        };
668        for (rid, row) in self.scan() {
669            let key = row[column].clone();
670            if unique && !matches!(key, Value::Null) && !index.tree.lookup_eq(&key).is_empty() {
671                return Err(dberr(
672                    DbErrorKind::Constraint,
673                    format!("UNIQUE index '{name}' has duplicate value: {key}"),
674                ));
675            }
676            if !unique || !matches!(key, Value::Null) {
677                index.tree.insert(key, rid);
678            }
679        }
680        self.indexes.push(index);
681        Ok(())
682    }
683
684    pub fn drop_index(&mut self, name: &str) -> bool {
685        if let Some(position) = self
686            .indexes
687            .iter()
688            .position(|index| index.name.eq_ignore_ascii_case(name))
689        {
690            self.indexes.remove(position);
691            true
692        } else {
693            false
694        }
695    }
696
697    pub fn has_index(&self, name: &str) -> bool {
698        self.indexes
699            .iter()
700            .any(|index| index.name.eq_ignore_ascii_case(name))
701    }
702
703    pub fn index(&self, column: usize) -> Option<&Index> {
704        self.indexes.iter().find(|index| index.column == column)
705    }
706
707    /// Look up row ids through the best equality index for a column.
708    pub fn lookup_eq_index(&self, column: usize, key: &Value) -> Option<(String, Vec<u64>)> {
709        if self.pk_col_idx() == Some(column) {
710            return self
711                .pk
712                .as_ref()
713                .map(|tree| ("PRIMARY KEY".into(), tree.lookup_eq(key)));
714        }
715        if let Some((_, tree)) = self.uniques.iter().find(|(idx, _)| *idx == column) {
716            return Some((
717                format!("UNIQUE({})", self.columns[column].name),
718                tree.lookup_eq(key),
719            ));
720        }
721        self.index(column)
722            .map(|index| (index.name.clone(), index.tree.lookup_eq(key)))
723    }
724
725    /// Return row ids in an inclusive candidate range. Strict comparison
726    /// boundaries are handled by the residual WHERE predicate.
727    pub fn lookup_range_index(
728        &self,
729        column: usize,
730        low: Option<&Value>,
731        high: Option<&Value>,
732    ) -> Option<(String, Vec<u64>)> {
733        let (name, entries) = if self.pk_col_idx() == Some(column) {
734            ("PRIMARY KEY".to_string(), self.pk.as_ref()?.scan_all())
735        } else if let Some((_, tree)) = self.uniques.iter().find(|(idx, _)| *idx == column) {
736            (
737                format!("UNIQUE({})", self.columns[column].name),
738                tree.scan_all(),
739            )
740        } else {
741            let index = self.index(column)?;
742            (index.name.clone(), index.tree.scan_all())
743        };
744        let row_ids = entries
745            .into_iter()
746            .filter(|(key, _)| {
747                low.map(|bound| key.cmp_value(bound) != std::cmp::Ordering::Less)
748                    .unwrap_or(true)
749                    && high
750                        .map(|bound| key.cmp_value(bound) != std::cmp::Ordering::Greater)
751                        .unwrap_or(true)
752            })
753            .map(|(_, row_id)| row_id)
754            .collect();
755        Some((name, row_ids))
756    }
757
758    fn validate_row(&self, values: &[Value], ignore_rid: Option<u64>) -> Result<(), DbError> {
759        for (i, column) in self.columns.iter().enumerate() {
760            if (column.not_null || column.primary_key) && matches!(values[i], Value::Null) {
761                return Err(DbError::new(
762                    DbErrorKind::NotNull,
763                    format!("column '{}' violates NOT NULL", column.name),
764                ));
765            }
766            if column.ty == ColumnType::Real
767                && matches!(values[i], Value::Real(value) if !value.is_finite())
768            {
769                return Err(DbError::new(
770                    DbErrorKind::TypeMismatch,
771                    format!("column '{}' cannot store a non-finite REAL", column.name),
772                ));
773            }
774        }
775        if let Some(pk) = &self.pk {
776            let idx = self.pk_col_idx().expect("has pk");
777            if pk
778                .lookup_eq(&values[idx])
779                .into_iter()
780                .any(|rid| Some(rid) != ignore_rid)
781            {
782                return Err(DbError::new(
783                    DbErrorKind::Constraint,
784                    format!(
785                        "UNIQUE constraint failed (PRIMARY KEY): {} already exists",
786                        values[idx]
787                    ),
788                ));
789            }
790        }
791        for (idx, tree) in &self.uniques {
792            let key = &values[*idx];
793            if !matches!(key, Value::Null)
794                && tree
795                    .lookup_eq(key)
796                    .into_iter()
797                    .any(|rid| Some(rid) != ignore_rid)
798            {
799                return Err(DbError::new(
800                    DbErrorKind::Constraint,
801                    format!(
802                        "UNIQUE constraint failed on '{}': {key} already exists",
803                        self.columns[*idx].name
804                    ),
805                ));
806            }
807        }
808        for index in &self.indexes {
809            let key = &values[index.column];
810            if index.unique
811                && !matches!(key, Value::Null)
812                && index
813                    .tree
814                    .lookup_eq(key)
815                    .into_iter()
816                    .any(|rid| Some(rid) != ignore_rid)
817            {
818                return Err(DbError::new(
819                    DbErrorKind::Constraint,
820                    format!(
821                        "UNIQUE constraint failed on index '{}': {key} already exists",
822                        index.name
823                    ),
824                ));
825            }
826        }
827        Ok(())
828    }
829
830    fn validate_indexes(&self) -> Result<(), DbError> {
831        if let Some(pk) = &self.pk
832            && pk
833                .scan_all()
834                .windows(2)
835                .any(|pair| pair[0].0.cmp_value(&pair[1].0) == std::cmp::Ordering::Equal)
836        {
837            return Err(dberr(
838                DbErrorKind::Io("duplicate primary key".into()),
839                format!(
840                    "corrupt database: duplicate primary key in table '{}'",
841                    self.name
842                ),
843            ));
844        }
845        for (idx, tree) in &self.uniques {
846            if tree
847                .scan_all()
848                .windows(2)
849                .any(|pair| pair[0].0.cmp_value(&pair[1].0) == std::cmp::Ordering::Equal)
850            {
851                return Err(dberr(
852                    DbErrorKind::Io("duplicate unique value".into()),
853                    format!(
854                        "corrupt database: duplicate unique value in '{}.{}'",
855                        self.name, self.columns[*idx].name
856                    ),
857                ));
858            }
859        }
860        for index in &self.indexes {
861            if index.unique
862                && index
863                    .tree
864                    .scan_all()
865                    .windows(2)
866                    .any(|pair| pair[0].0.cmp_value(&pair[1].0) == std::cmp::Ordering::Equal)
867            {
868                return Err(dberr(
869                    DbErrorKind::Io("duplicate unique index value".into()),
870                    format!(
871                        "corrupt database: duplicate value in index '{}.{}'",
872                        self.name, index.name
873                    ),
874                ));
875            }
876        }
877        Ok(())
878    }
879}
880
881#[derive(Default)]
882struct BinWriter {
883    bytes: Vec<u8>,
884}
885
886impl BinWriter {
887    fn bytes(&mut self, value: &[u8]) {
888        self.bytes.extend_from_slice(value);
889    }
890
891    fn finish(self) -> Vec<u8> {
892        self.bytes
893    }
894
895    fn u8(&mut self, value: u8) {
896        self.bytes.push(value);
897    }
898
899    fn u32(&mut self, value: u32) {
900        self.bytes.extend_from_slice(&value.to_le_bytes());
901    }
902
903    fn u64(&mut self, value: u64) {
904        self.bytes.extend_from_slice(&value.to_le_bytes());
905    }
906
907    fn string(&mut self, value: &str) {
908        self.u32(value.len() as u32);
909        self.bytes(value.as_bytes());
910    }
911}
912
913struct BinReader<'a> {
914    bytes: &'a [u8],
915    pos: usize,
916}
917
918impl<'a> BinReader<'a> {
919    fn new(bytes: &'a [u8]) -> BinReader<'a> {
920        BinReader { bytes, pos: 0 }
921    }
922
923    fn at_end(&self) -> bool {
924        self.pos == self.bytes.len()
925    }
926
927    fn take(&mut self, len: usize, what: &str) -> Result<&'a [u8], DbError> {
928        let end = self.pos.checked_add(len).ok_or_else(|| {
929            dberr(
930                DbErrorKind::Io("state offset overflow".into()),
931                format!("corrupt database: state offset overflow while reading {what}"),
932            )
933        })?;
934        let value = self.bytes.get(self.pos..end).ok_or_else(|| {
935            dberr(
936                DbErrorKind::Io("truncated state".into()),
937                format!("corrupt database: truncated state while reading {what}"),
938            )
939        })?;
940        self.pos = end;
941        Ok(value)
942    }
943
944    fn bytes(&mut self, len: usize) -> Result<&'a [u8], DbError> {
945        self.take(len, "bytes")
946    }
947
948    fn u8(&mut self) -> Result<u8, DbError> {
949        Ok(self.take(1, "byte")?[0])
950    }
951
952    fn u32(&mut self) -> Result<u32, DbError> {
953        Ok(u32::from_le_bytes(self.take(4, "u32")?.try_into().unwrap()))
954    }
955
956    fn u64(&mut self) -> Result<u64, DbError> {
957        Ok(u64::from_le_bytes(self.take(8, "u64")?.try_into().unwrap()))
958    }
959
960    fn count(&mut self, what: &str) -> Result<usize, DbError> {
961        let count = self.u32()? as usize;
962        if count > 1_000_000 {
963            return Err(dberr(
964                DbErrorKind::Io("state collection is too large".into()),
965                format!("corrupt database: too many {what}s"),
966            ));
967        }
968        Ok(count)
969    }
970
971    fn string(&mut self, what: &str) -> Result<String, DbError> {
972        let len = self.u32()? as usize;
973        if len > 64 * 1024 * 1024 {
974            return Err(dberr(
975                DbErrorKind::Io("state string is too large".into()),
976                format!("corrupt database: {what} is too large"),
977            ));
978        }
979        let bytes = self.take(len, what)?;
980        String::from_utf8(bytes.to_vec()).map_err(|_| {
981            dberr(
982                DbErrorKind::Io("invalid UTF-8 in state".into()),
983                format!("corrupt database: invalid UTF-8 in {what}"),
984            )
985        })
986    }
987}
988
989fn column_type_tag(ty: &ColumnType) -> u8 {
990    match ty {
991        ColumnType::Integer => 0,
992        ColumnType::Real => 1,
993        ColumnType::Text => 2,
994        ColumnType::Boolean => 3,
995        ColumnType::Any => 4,
996        ColumnType::Null => 5,
997    }
998}
999
1000fn column_type_from_tag(tag: u8) -> Result<ColumnType, DbError> {
1001    match tag {
1002        0 => Ok(ColumnType::Integer),
1003        1 => Ok(ColumnType::Real),
1004        2 => Ok(ColumnType::Text),
1005        3 => Ok(ColumnType::Boolean),
1006        4 => Ok(ColumnType::Any),
1007        5 => Ok(ColumnType::Null),
1008        _ => Err(dberr(
1009            DbErrorKind::Io("invalid column type".into()),
1010            "corrupt database: invalid column type",
1011        )),
1012    }
1013}
1014
1015fn encode_value(w: &mut BinWriter, value: &Value) {
1016    match value {
1017        Value::Null => w.u8(0),
1018        Value::Integer(v) => {
1019            w.u8(1);
1020            w.u64(*v as u64);
1021        }
1022        Value::Real(v) => {
1023            w.u8(2);
1024            w.u64(v.to_bits());
1025        }
1026        Value::Text(v) => {
1027            w.u8(3);
1028            w.string(v);
1029        }
1030        Value::Boolean(v) => {
1031            w.u8(4);
1032            w.u8(*v as u8);
1033        }
1034    }
1035}
1036
1037fn decode_value(r: &mut BinReader<'_>) -> Result<Value, DbError> {
1038    match r.u8()? {
1039        0 => Ok(Value::Null),
1040        1 => Ok(Value::Integer(r.u64()? as i64)),
1041        2 => Ok(Value::Real(f64::from_bits(r.u64()?))),
1042        3 => Ok(Value::Text(r.string("text value")?)),
1043        4 => match r.u8()? {
1044            0 => Ok(Value::Boolean(false)),
1045            1 => Ok(Value::Boolean(true)),
1046            _ => Err(dberr(
1047                DbErrorKind::Io("invalid boolean".into()),
1048                "corrupt database: invalid boolean value",
1049            )),
1050        },
1051        _ => Err(dberr(
1052            DbErrorKind::Io("invalid value tag".into()),
1053            "corrupt database: invalid value tag",
1054        )),
1055    }
1056}
1057
1058pub fn dberr(kind: DbErrorKind, msg: impl Into<String>) -> DbError {
1059    DbError::new(kind, msg)
1060}