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