Skip to main content

reifydb_core/encoded/
identity.rs

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