Skip to main content

reifydb_core/encoded/
uuid7.rs

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