Skip to main content

wasm_dbms_api/dbms/
types.rs

1//! This module exposes the data types used in the DBMS.
2
3use serde::{Deserialize, Serialize};
4
5use crate::dbms::value::Value;
6use crate::memory::Encode;
7
8mod blob;
9mod boolean;
10mod date;
11mod datetime;
12mod decimal;
13mod integers;
14mod json;
15mod nullable;
16mod text;
17mod uuid;
18
19pub use self::blob::Blob;
20pub use self::boolean::Boolean;
21pub use self::date::Date;
22pub use self::datetime::DateTime;
23pub use self::decimal::Decimal;
24pub use self::integers::{Int8, Int16, Int32, Int64, Uint8, Uint16, Uint32, Uint64};
25pub use self::json::Json;
26pub use self::nullable::Nullable;
27pub use self::text::Text;
28pub use self::uuid::Uuid;
29
30/// A trait representing a data type that can be stored in the DBMS.
31///
32/// This is an umbrella trait that combines several other traits to ensure that
33/// any type implementing [`DataType`] can be cloned, compared, hashed, encoded,
34/// and serialized/deserialized using Serde.
35///
36/// Also it is used by the DBMS to compare and sort values of different data types.
37pub trait DataType:
38    Clone
39    + std::fmt::Debug
40    + std::fmt::Display
41    + PartialEq
42    + Eq
43    + Default
44    + PartialOrd
45    + Ord
46    + std::hash::Hash
47    + Encode
48    + Serialize
49    + Into<Value>
50    + for<'de> Deserialize<'de>
51{
52}
53
54/// A trait for user-defined custom data types.
55///
56/// Custom types are stored as type-erased [`CustomValue`](crate::dbms::custom_value::CustomValue)
57/// inside `Value::Custom`. The `TYPE_TAG` constant uniquely identifies the type
58/// and must be stable across versions.
59///
60/// # Ordering contract
61///
62/// For custom types used with range filters (`Gt`, `Lt`, `Ge`, `Le`) or `ORDER BY`,
63/// the [`Encode`] output must be order-preserving: if `a < b`,
64/// then `a.encode() < b.encode()` lexicographically.
65/// Equality filters (`Eq`, `Ne`, `In`) only require canonical encoding.
66pub trait CustomDataType: DataType {
67    /// Unique string identifier for this type (e.g., `"principal"`, `"role"`).
68    const TYPE_TAG: &'static str;
69}
70
71/// An enumeration of all supported data type kinds in the DBMS.
72#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
73pub enum DataTypeKind {
74    Blob,
75    Boolean,
76    Date,
77    DateTime,
78    Decimal,
79    Int8,
80    Int16,
81    Int32,
82    Int64,
83    Json,
84    Text,
85    Uint8,
86    Uint16,
87    Uint32,
88    Uint64,
89    Uuid,
90    /// A user-defined custom type. Carries the stable [`CustomDataType::TYPE_TAG`]
91    /// and a [`crate::dbms::table::WireSize`] descriptor so the migration codec
92    /// can slice column bytes without invoking the user's `Encode::decode`.
93    Custom {
94        tag: &'static str,
95        wire_size: crate::dbms::table::WireSize,
96    },
97}
98
99#[cfg(test)]
100mod test {
101
102    use std::collections::HashSet;
103
104    use super::*;
105
106    #[test]
107    fn test_should_implement_data_type_for_every_builtin_and_nullable_builtin() {
108        fn assert_data_type<T>()
109        where
110            T: DataType,
111        {
112        }
113
114        assert_data_type::<Blob>();
115        assert_data_type::<Boolean>();
116        assert_data_type::<Date>();
117        assert_data_type::<DateTime>();
118        assert_data_type::<Decimal>();
119        assert_data_type::<Int8>();
120        assert_data_type::<Int16>();
121        assert_data_type::<Int32>();
122        assert_data_type::<Int64>();
123        assert_data_type::<Json>();
124        assert_data_type::<Text>();
125        assert_data_type::<Uint8>();
126        assert_data_type::<Uint16>();
127        assert_data_type::<Uint32>();
128        assert_data_type::<Uint64>();
129        assert_data_type::<Uuid>();
130
131        assert_data_type::<Nullable<Blob>>();
132        assert_data_type::<Nullable<Boolean>>();
133        assert_data_type::<Nullable<Date>>();
134        assert_data_type::<Nullable<DateTime>>();
135        assert_data_type::<Nullable<Decimal>>();
136        assert_data_type::<Nullable<Int8>>();
137        assert_data_type::<Nullable<Int16>>();
138        assert_data_type::<Nullable<Int32>>();
139        assert_data_type::<Nullable<Int64>>();
140        assert_data_type::<Nullable<Json>>();
141        assert_data_type::<Nullable<Text>>();
142        assert_data_type::<Nullable<Uint8>>();
143        assert_data_type::<Nullable<Uint16>>();
144        assert_data_type::<Nullable<Uint32>>();
145        assert_data_type::<Nullable<Uint64>>();
146        assert_data_type::<Nullable<Uuid>>();
147    }
148
149    #[test]
150    fn test_should_create_all_data_type_kind_variants() {
151        let kinds = [
152            DataTypeKind::Blob,
153            DataTypeKind::Boolean,
154            DataTypeKind::Date,
155            DataTypeKind::DateTime,
156            DataTypeKind::Decimal,
157            DataTypeKind::Int8,
158            DataTypeKind::Int16,
159            DataTypeKind::Int32,
160            DataTypeKind::Int64,
161            DataTypeKind::Json,
162            DataTypeKind::Text,
163            DataTypeKind::Uint8,
164            DataTypeKind::Uint16,
165            DataTypeKind::Uint32,
166            DataTypeKind::Uint64,
167            DataTypeKind::Uuid,
168        ];
169
170        assert_eq!(kinds.len(), 16);
171    }
172
173    #[test]
174    #[allow(clippy::clone_on_copy)]
175    fn test_should_clone_data_type_kind() {
176        let kind = DataTypeKind::Text;
177        let cloned = kind.clone();
178        assert_eq!(kind, cloned);
179    }
180
181    #[test]
182    fn test_should_copy_data_type_kind() {
183        let kind = DataTypeKind::Uint32;
184        let copied = kind;
185        assert_eq!(kind, copied);
186    }
187
188    #[test]
189    fn test_should_compare_data_type_kinds() {
190        assert_eq!(DataTypeKind::Blob, DataTypeKind::Blob);
191        assert_eq!(DataTypeKind::Boolean, DataTypeKind::Boolean);
192        assert_ne!(DataTypeKind::Blob, DataTypeKind::Boolean);
193        assert_ne!(DataTypeKind::Int32, DataTypeKind::Int64);
194        assert_ne!(DataTypeKind::Uint32, DataTypeKind::Uint64);
195    }
196
197    #[test]
198    fn test_should_hash_data_type_kind() {
199        let mut set = HashSet::new();
200        set.insert(DataTypeKind::Text);
201        set.insert(DataTypeKind::Uint32);
202        set.insert(DataTypeKind::Boolean);
203
204        assert!(set.contains(&DataTypeKind::Text));
205        assert!(set.contains(&DataTypeKind::Uint32));
206        assert!(set.contains(&DataTypeKind::Boolean));
207        assert!(!set.contains(&DataTypeKind::Blob));
208    }
209
210    #[test]
211    fn test_should_debug_data_type_kind() {
212        assert_eq!(format!("{:?}", DataTypeKind::Blob), "Blob");
213        assert_eq!(format!("{:?}", DataTypeKind::Boolean), "Boolean");
214        assert_eq!(format!("{:?}", DataTypeKind::Date), "Date");
215        assert_eq!(format!("{:?}", DataTypeKind::DateTime), "DateTime");
216        assert_eq!(format!("{:?}", DataTypeKind::Decimal), "Decimal");
217        assert_eq!(format!("{:?}", DataTypeKind::Int8), "Int8");
218        assert_eq!(format!("{:?}", DataTypeKind::Int16), "Int16");
219        assert_eq!(format!("{:?}", DataTypeKind::Int32), "Int32");
220        assert_eq!(format!("{:?}", DataTypeKind::Int64), "Int64");
221        assert_eq!(format!("{:?}", DataTypeKind::Json), "Json");
222        assert_eq!(format!("{:?}", DataTypeKind::Text), "Text");
223        assert_eq!(format!("{:?}", DataTypeKind::Uint8), "Uint8");
224        assert_eq!(format!("{:?}", DataTypeKind::Uint16), "Uint16");
225        assert_eq!(format!("{:?}", DataTypeKind::Uint32), "Uint32");
226        assert_eq!(format!("{:?}", DataTypeKind::Uint64), "Uint64");
227        assert_eq!(format!("{:?}", DataTypeKind::Uuid), "Uuid");
228    }
229
230    #[test]
231    fn test_should_use_data_type_kind_as_hashmap_key() {
232        use std::collections::HashMap;
233
234        let mut map = HashMap::new();
235        map.insert(DataTypeKind::Text, "String type");
236        map.insert(DataTypeKind::Uint32, "32-bit unsigned integer");
237
238        assert_eq!(map.get(&DataTypeKind::Text), Some(&"String type"));
239        assert_eq!(
240            map.get(&DataTypeKind::Uint32),
241            Some(&"32-bit unsigned integer")
242        );
243        assert_eq!(map.get(&DataTypeKind::Blob), None);
244    }
245
246    #[test]
247    fn test_should_create_custom_data_type_kind() {
248        use crate::dbms::table::WireSize;
249        let kind = DataTypeKind::Custom {
250            tag: "role",
251            wire_size: WireSize::Fixed(1),
252        };
253        assert_eq!(
254            kind,
255            DataTypeKind::Custom {
256                tag: "role",
257                wire_size: WireSize::Fixed(1)
258            }
259        );
260        assert_ne!(
261            kind,
262            DataTypeKind::Custom {
263                tag: "status",
264                wire_size: WireSize::Fixed(1)
265            }
266        );
267        assert_ne!(kind, DataTypeKind::Text);
268    }
269
270    #[test]
271    fn test_should_copy_custom_data_type_kind() {
272        use crate::dbms::table::WireSize;
273        let kind = DataTypeKind::Custom {
274            tag: "role",
275            wire_size: WireSize::LengthPrefixed,
276        };
277        let copied = kind;
278        assert_eq!(kind, copied);
279    }
280
281    #[test]
282    fn test_should_debug_custom_data_type_kind() {
283        use crate::dbms::table::WireSize;
284        let kind = DataTypeKind::Custom {
285            tag: "role",
286            wire_size: WireSize::Fixed(1),
287        };
288        let debug = format!("{kind:?}");
289        assert!(debug.contains("Custom"));
290        assert!(debug.contains("role"));
291    }
292}