Skip to main content

wasm_dbms/transaction/
overlay.rs

1// Rust guideline compliant 2026-02-28
2
3//! Database overlay for tracking uncommitted transaction changes.
4
5mod reader;
6mod table;
7
8use std::collections::HashMap;
9
10use wasm_dbms_api::prelude::{ColumnDef, DbmsError, DbmsResult, QueryError, TableSchema, Value};
11use wasm_dbms_memory::prelude::{MemoryAccess, TableReader};
12
13pub use self::reader::DatabaseOverlayReader;
14pub(crate) use self::table::TableOverlay;
15
16/// Manages uncommitted changes during a transaction.
17///
18/// Provides an overlay over the existing database state to track
19/// uncommitted inserts, updates, and deletes per table.
20#[derive(Debug, Default, Clone)]
21pub struct DatabaseOverlay {
22    tables: HashMap<String, TableOverlay>,
23}
24
25impl DatabaseOverlay {
26    /// Returns a reader that merges base table data with overlay changes.
27    pub fn reader<'a, T, MA>(
28        &'a mut self,
29        table_reader: TableReader<'a, T, MA>,
30    ) -> DatabaseOverlayReader<'a, T, MA>
31    where
32        T: TableSchema,
33        MA: MemoryAccess,
34    {
35        let table_name = T::table_name();
36        let table_overlay = self.tables.entry(table_name.to_string()).or_default();
37        DatabaseOverlayReader::new(table_overlay, table_reader)
38    }
39
40    /// Inserts a record into the overlay for the specified table.
41    pub fn insert<T>(&mut self, values: Vec<(ColumnDef, Value)>) -> DbmsResult<()>
42    where
43        T: TableSchema,
44    {
45        let table_name = T::table_name();
46        let pk = T::primary_key();
47        let pk = Self::primary_key(pk, &values)?;
48        let overlay = self.tables.entry(table_name.to_string()).or_default();
49        overlay.insert(pk, values);
50
51        Ok(())
52    }
53
54    /// Updates a record in the overlay for the specified table.
55    pub fn update<T>(&mut self, pk: Value, updates: Vec<(&'static str, Value)>)
56    where
57        T: TableSchema,
58    {
59        let table_name = T::table_name();
60        let overlay = self.tables.entry(table_name.to_string()).or_default();
61        overlay.update(pk, updates);
62    }
63
64    /// Deletes a record in the overlay for the specified table.
65    pub fn delete<T>(&mut self, pk: Value)
66    where
67        T: TableSchema,
68    {
69        let table_name = T::table_name();
70        let overlay = self.tables.entry(table_name.to_string()).or_default();
71        overlay.delete(pk);
72    }
73
74    fn primary_key(pk: &'static str, values: &[(ColumnDef, Value)]) -> DbmsResult<Value> {
75        for (col_def, value) in values {
76            if col_def.name == pk {
77                return Ok(value.clone());
78            }
79        }
80        Err(DbmsError::Query(QueryError::MissingNonNullableField(
81            pk.to_string(),
82        )))
83    }
84}