Skip to main content

wasm_dbms_api/dbms/table/
record.rs

1use crate::dbms::table::{ColumnDef, TableSchema};
2use crate::dbms::value::Value;
3use crate::error::DbmsResult;
4use crate::prelude::Filter;
5
6pub type TableColumns = Vec<(ValuesSource, Vec<(ColumnDef, Value)>)>;
7
8/// Flattens [`TableColumns`] rows into flat column-value pairs.
9///
10/// Only includes columns whose source is [`ValuesSource::This`],
11/// discarding any foreign or eager-loaded columns.
12pub fn flatten_table_columns(rows: Vec<TableColumns>) -> Vec<Vec<(ColumnDef, Value)>> {
13    rows.into_iter()
14        .map(|row| {
15            row.into_iter()
16                .filter(|(source, _)| *source == ValuesSource::This)
17                .flat_map(|(_, cols)| cols)
18                .collect()
19        })
20        .collect()
21}
22
23/// Indicates the source of the column values.
24#[derive(Debug, Clone, PartialEq, Eq, Hash)]
25pub enum ValuesSource {
26    /// Column values belong to the current table.
27    This,
28    /// Column values belong to a foreign table.
29    Foreign { table: String, column: String },
30}
31
32/// This trait represents a record returned by a [`crate::dbms::query::Query`] for a table.
33pub trait TableRecord: Clone {
34    /// The table schema associated with this record.
35    type Schema: TableSchema<Record = Self>;
36
37    /// Constructs [`TableRecord`] from a list of column values grouped by table.
38    fn from_values(values: TableColumns) -> Self;
39
40    /// Converts the record into a list of column [`Value`]s.
41    fn to_values(&self) -> Vec<(ColumnDef, Value)>;
42}
43
44/// This trait represents a record for inserting into a table.
45pub trait InsertRecord: Sized + Clone {
46    /// The [`TableRecord`] type associated with this table schema.
47    type Record: TableRecord;
48    /// The table schema associated with this record.
49    type Schema: TableSchema<Record = Self::Record>;
50
51    /// Creates an insert record from a list of column [`Value`]s.
52    fn from_values(values: &[(ColumnDef, Value)]) -> DbmsResult<Self>;
53
54    /// Converts the record into a list of column [`Value`]s for insertion.
55    fn into_values(self) -> Vec<(ColumnDef, Value)>;
56
57    /// Converts the insert record into the corresponding table record.
58    fn into_record(self) -> Self::Schema;
59}
60
61/// This trait represents a record for updating a table.
62pub trait UpdateRecord: Sized + Clone {
63    /// The [`TableRecord`] type associated with this table schema.
64    type Record: TableRecord;
65    /// The table schema associated with this record.
66    type Schema: TableSchema<Record = Self::Record>;
67
68    /// Creates an update record from a list of column [`Value`]s and an optional [`Filter`] for the where clause.
69    ///
70    /// Values for columns that are not part of the table are ignored.
71    ///
72    /// # Errors
73    ///
74    /// Returns an error if a value does not match the type of its column: a value of another
75    /// type, [`Value::Null`] for a non-nullable column, or a custom value with another type tag
76    /// or an undecodable payload.
77    fn from_values(values: &[(ColumnDef, Value)], where_clause: Option<Filter>)
78    -> DbmsResult<Self>;
79
80    /// Get the list of column [`Value`]s to be updated.
81    fn update_values(&self) -> Vec<(ColumnDef, Value)>;
82
83    /// Get the [`Filter`] condition for the update operation.
84    fn where_clause(&self) -> Option<Filter>;
85}
86
87#[cfg(test)]
88mod test {
89
90    use super::*;
91
92    #[test]
93    fn test_should_create_values_source_this() {
94        let source = ValuesSource::This;
95        assert_eq!(source, ValuesSource::This);
96    }
97
98    #[test]
99    fn test_should_create_values_source_foreign() {
100        let source = ValuesSource::Foreign {
101            table: "users".to_string(),
102            column: "id".to_string(),
103        };
104
105        if let ValuesSource::Foreign { table, column } = source {
106            assert_eq!(table, "users");
107            assert_eq!(column, "id");
108        } else {
109            panic!("expected ValuesSource::Foreign");
110        }
111    }
112
113    #[test]
114    fn test_should_clone_values_source() {
115        let source = ValuesSource::Foreign {
116            table: "posts".to_string(),
117            column: "author_id".to_string(),
118        };
119
120        let cloned = source.clone();
121        assert_eq!(source, cloned);
122    }
123
124    #[test]
125    fn test_should_compare_values_sources() {
126        let source1 = ValuesSource::This;
127        let source2 = ValuesSource::This;
128        let source3 = ValuesSource::Foreign {
129            table: "users".to_string(),
130            column: "id".to_string(),
131        };
132        let source4 = ValuesSource::Foreign {
133            table: "users".to_string(),
134            column: "id".to_string(),
135        };
136        let source5 = ValuesSource::Foreign {
137            table: "posts".to_string(),
138            column: "id".to_string(),
139        };
140
141        assert_eq!(source1, source2);
142        assert_eq!(source3, source4);
143        assert_ne!(source1, source3);
144        assert_ne!(source3, source5);
145    }
146
147    #[test]
148    fn test_should_hash_values_source() {
149        use std::collections::HashSet;
150
151        let mut set = HashSet::new();
152        set.insert(ValuesSource::This);
153        set.insert(ValuesSource::Foreign {
154            table: "users".to_string(),
155            column: "id".to_string(),
156        });
157
158        assert!(set.contains(&ValuesSource::This));
159        assert!(set.contains(&ValuesSource::Foreign {
160            table: "users".to_string(),
161            column: "id".to_string(),
162        }));
163        assert!(!set.contains(&ValuesSource::Foreign {
164            table: "posts".to_string(),
165            column: "id".to_string(),
166        }));
167    }
168
169    #[test]
170    fn test_should_debug_values_source() {
171        let source = ValuesSource::This;
172        let debug_str = format!("{:?}", source);
173        assert_eq!(debug_str, "This");
174
175        let foreign = ValuesSource::Foreign {
176            table: "users".to_string(),
177            column: "id".to_string(),
178        };
179        let foreign_debug = format!("{:?}", foreign);
180        assert!(foreign_debug.contains("Foreign"));
181        assert!(foreign_debug.contains("users"));
182        assert!(foreign_debug.contains("id"));
183    }
184}