Skip to main content

reifydb_core/encoded/
identity.rs

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