reifydb_core/value/encoded/
identity_id.rs

1// Copyright (c) reifydb.com 2025
2// This file is licensed under the AGPL-3.0-or-later, see license.md file
3
4use std::ptr;
5
6use reifydb_type::{IdentityId, Type, Uuid7};
7use uuid::Uuid;
8
9use crate::value::encoded::{EncodedValues, EncodedValuesLayout};
10
11impl EncodedValuesLayout {
12	pub fn set_identity_id(&self, row: &mut EncodedValues, index: usize, value: IdentityId) {
13		let field = &self.fields[index];
14		debug_assert!(row.len() >= self.total_static_size());
15		debug_assert_eq!(field.r#type, Type::IdentityId);
16		row.set_valid(index, true);
17		unsafe {
18			// IdentityId wraps Uuid7 which is 16 bytes
19			ptr::write_unaligned(
20				row.make_mut().as_mut_ptr().add(field.offset) as *mut [u8; 16],
21				*value.as_bytes(),
22			);
23		}
24	}
25
26	pub fn get_identity_id(&self, row: &EncodedValues, index: usize) -> IdentityId {
27		let field = &self.fields[index];
28		debug_assert!(row.len() >= self.total_static_size());
29		debug_assert_eq!(field.r#type, Type::IdentityId);
30		unsafe {
31			// IdentityId wraps Uuid7 which is 16 bytes
32			let bytes: [u8; 16] = ptr::read_unaligned(row.as_ptr().add(field.offset) as *const [u8; 16]);
33			let uuid = Uuid::from_bytes(bytes);
34			let uuid7 = Uuid7::from(uuid);
35			IdentityId::from(uuid7)
36		}
37	}
38
39	pub fn try_get_identity_id(&self, row: &EncodedValues, index: usize) -> Option<IdentityId> {
40		if row.is_defined(index) {
41			Some(self.get_identity_id(row, index))
42		} else {
43			None
44		}
45	}
46}
47
48#[cfg(test)]
49mod tests {
50	use reifydb_type::{IdentityId, Type};
51
52	use crate::value::encoded::EncodedValuesLayout;
53
54	#[test]
55	fn test_set_get_identity_id() {
56		let layout = EncodedValuesLayout::new(&[Type::IdentityId]);
57		let mut row = layout.allocate();
58
59		let id = IdentityId::generate();
60		layout.set_identity_id(&mut row, 0, id.clone());
61		assert_eq!(layout.get_identity_id(&row, 0), id);
62	}
63
64	#[test]
65	fn test_try_get_identity_id() {
66		let layout = EncodedValuesLayout::new(&[Type::IdentityId]);
67		let mut row = layout.allocate();
68
69		assert_eq!(layout.try_get_identity_id(&row, 0), None);
70
71		let id = IdentityId::generate();
72		layout.set_identity_id(&mut row, 0, id.clone());
73		assert_eq!(layout.try_get_identity_id(&row, 0), Some(id));
74	}
75
76	#[test]
77	fn test_multiple_generations() {
78		let layout = EncodedValuesLayout::new(&[Type::IdentityId]);
79
80		// Generate multiple Identity IDs and ensure they're different
81		let mut ids = Vec::new();
82		for _ in 0..10 {
83			let mut row = layout.allocate();
84			let id = IdentityId::generate();
85			layout.set_identity_id(&mut row, 0, id.clone());
86			let retrieved = layout.get_identity_id(&row, 0);
87			assert_eq!(retrieved, id);
88			ids.push(id);
89		}
90
91		// Ensure all generated Identity IDs are unique
92		for i in 0..ids.len() {
93			for j in (i + 1)..ids.len() {
94				assert_ne!(ids[i], ids[j], "Identity IDs should be unique");
95			}
96		}
97	}
98
99	#[test]
100	fn test_uuid7_properties() {
101		let layout = EncodedValuesLayout::new(&[Type::IdentityId]);
102		let mut row = layout.allocate();
103
104		let id = IdentityId::generate();
105		layout.set_identity_id(&mut row, 0, id.clone());
106		let retrieved = layout.get_identity_id(&row, 0);
107
108		// Verify it's backed by a version 7 UUID
109		assert_eq!(retrieved.get_version_num(), 7);
110		assert_eq!(id.get_version_num(), 7);
111	}
112
113	#[test]
114	fn test_timestamp_ordering() {
115		let layout = EncodedValuesLayout::new(&[Type::IdentityId]);
116
117		// Generate Identity IDs in sequence - they should be ordered by
118		// timestamp
119		let mut ids = Vec::new();
120		for _ in 0..5 {
121			let mut row = layout.allocate();
122			let id = IdentityId::generate();
123			layout.set_identity_id(&mut row, 0, id.clone());
124			let retrieved = layout.get_identity_id(&row, 0);
125			assert_eq!(retrieved, id);
126			ids.push(id);
127
128			// Small delay to ensure different timestamps
129			std::thread::sleep(std::time::Duration::from_millis(1));
130		}
131
132		// Verify that Identity IDs are ordered (timestamp-based)
133		for i in 1..ids.len() {
134			assert!(ids[i].as_bytes() >= ids[i - 1].as_bytes(), "Identity IDs should be timestamp-ordered");
135		}
136	}
137
138	#[test]
139	fn test_mixed_with_other_types() {
140		let layout = EncodedValuesLayout::new(&[Type::IdentityId, Type::Boolean, Type::IdentityId, Type::Int4]);
141		let mut row = layout.allocate();
142
143		let id1 = IdentityId::generate();
144		let id2 = IdentityId::generate();
145
146		layout.set_identity_id(&mut row, 0, id1.clone());
147		layout.set_bool(&mut row, 1, true);
148		layout.set_identity_id(&mut row, 2, id2.clone());
149		layout.set_i32(&mut row, 3, 42);
150
151		assert_eq!(layout.get_identity_id(&row, 0), id1);
152		assert_eq!(layout.get_bool(&row, 1), true);
153		assert_eq!(layout.get_identity_id(&row, 2), id2);
154		assert_eq!(layout.get_i32(&row, 3), 42);
155	}
156
157	#[test]
158	fn test_undefined_handling() {
159		let layout = EncodedValuesLayout::new(&[Type::IdentityId, Type::IdentityId]);
160		let mut row = layout.allocate();
161
162		let id = IdentityId::generate();
163		layout.set_identity_id(&mut row, 0, id.clone());
164
165		assert_eq!(layout.try_get_identity_id(&row, 0), Some(id));
166		assert_eq!(layout.try_get_identity_id(&row, 1), None);
167
168		layout.set_undefined(&mut row, 0);
169		assert_eq!(layout.try_get_identity_id(&row, 0), None);
170	}
171
172	#[test]
173	fn test_persistence() {
174		let layout = EncodedValuesLayout::new(&[Type::IdentityId]);
175		let mut row = layout.allocate();
176
177		let id = IdentityId::generate();
178		let id_string = id.to_string();
179
180		layout.set_identity_id(&mut row, 0, id.clone());
181		let retrieved = layout.get_identity_id(&row, 0);
182
183		assert_eq!(retrieved, id);
184		assert_eq!(retrieved.to_string(), id_string);
185		assert_eq!(retrieved.as_bytes(), id.as_bytes());
186	}
187
188	#[test]
189	fn test_clone_consistency() {
190		let layout = EncodedValuesLayout::new(&[Type::IdentityId]);
191		let mut row = layout.allocate();
192
193		let original_id = IdentityId::generate();
194		layout.set_identity_id(&mut row, 0, original_id.clone());
195
196		let retrieved_id = layout.get_identity_id(&row, 0);
197		assert_eq!(retrieved_id, original_id);
198
199		// Verify that the underlying UUID7 byte representation is
200		// identical
201		assert_eq!(retrieved_id.as_bytes(), original_id.as_bytes());
202	}
203
204	#[test]
205	fn test_multiple_fields() {
206		let layout = EncodedValuesLayout::new(&[Type::IdentityId, Type::IdentityId, Type::IdentityId]);
207		let mut row = layout.allocate();
208
209		let id1 = IdentityId::generate();
210		let id2 = IdentityId::generate();
211		let id3 = IdentityId::generate();
212
213		layout.set_identity_id(&mut row, 0, id1.clone());
214		layout.set_identity_id(&mut row, 1, id2.clone());
215		layout.set_identity_id(&mut row, 2, id3.clone());
216
217		assert_eq!(layout.get_identity_id(&row, 0), id1);
218		assert_eq!(layout.get_identity_id(&row, 1), id2);
219		assert_eq!(layout.get_identity_id(&row, 2), id3);
220
221		// Ensure all Identity IDs are different
222		assert_ne!(id1, id2);
223		assert_ne!(id1, id3);
224		assert_ne!(id2, id3);
225	}
226
227	#[test]
228	fn test_format_consistency() {
229		let layout = EncodedValuesLayout::new(&[Type::IdentityId]);
230		let mut row = layout.allocate();
231
232		let id = IdentityId::generate();
233		let original_string = id.to_string();
234
235		layout.set_identity_id(&mut row, 0, id.clone());
236		let retrieved = layout.get_identity_id(&row, 0);
237		let retrieved_string = retrieved.to_string();
238
239		assert_eq!(original_string, retrieved_string);
240
241		// Verify UUID string format (8-4-4-4-12) since IdentityId is
242		// based on UUID7
243		assert_eq!(original_string.len(), 36);
244		assert_eq!(original_string.matches('-').count(), 4);
245	}
246
247	#[test]
248	fn test_byte_level_storage() {
249		let layout = EncodedValuesLayout::new(&[Type::IdentityId]);
250		let mut row = layout.allocate();
251
252		let id = IdentityId::generate();
253		let original_bytes = *id.as_bytes();
254
255		layout.set_identity_id(&mut row, 0, id.clone());
256		let retrieved = layout.get_identity_id(&row, 0);
257		let retrieved_bytes = *retrieved.as_bytes();
258
259		assert_eq!(original_bytes, retrieved_bytes);
260
261		// Verify that it's exactly 16 bytes
262		assert_eq!(original_bytes.len(), 16);
263		assert_eq!(retrieved_bytes.len(), 16);
264	}
265
266	#[test]
267	fn test_time_based_properties() {
268		let layout = EncodedValuesLayout::new(&[Type::IdentityId]);
269
270		// Generate Identity IDs at different times
271		let id1 = IdentityId::generate();
272		std::thread::sleep(std::time::Duration::from_millis(2));
273		let id2 = IdentityId::generate();
274
275		let mut row1 = layout.allocate();
276		let mut row2 = layout.allocate();
277
278		layout.set_identity_id(&mut row1, 0, id1.clone());
279		layout.set_identity_id(&mut row2, 0, id2.clone());
280
281		let retrieved1 = layout.get_identity_id(&row1, 0);
282		let retrieved2 = layout.get_identity_id(&row2, 0);
283
284		// The second Identity ID should be "greater" due to timestamp
285		// ordering
286		assert!(retrieved2.as_bytes() > retrieved1.as_bytes());
287	}
288
289	#[test]
290	fn test_as_primary_key() {
291		let layout = EncodedValuesLayout::new(&[
292			Type::IdentityId, // Primary key
293			Type::Utf8,       // Name field
294			Type::Int4,       // Age field
295		]);
296		let mut row = layout.allocate();
297
298		// Simulate a database record with Identity ID as primary key
299		let primary_key = IdentityId::generate();
300		layout.set_identity_id(&mut row, 0, primary_key.clone());
301		layout.set_utf8(&mut row, 1, "John Doe");
302		layout.set_i32(&mut row, 2, 30);
303
304		assert_eq!(layout.get_identity_id(&row, 0), primary_key);
305		assert_eq!(layout.get_utf8(&row, 1), "John Doe");
306		assert_eq!(layout.get_i32(&row, 2), 30);
307
308		// Verify that the primary key is suitable for ordering/indexing
309		assert_eq!(primary_key.get_version_num(), 7);
310	}
311}