1use 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 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(), Type::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() == Type::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_type::value::{identity::IdentityId, r#type::Type};
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(&[Type::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(&[Type::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(&[Type::IdentityId]);
92
93 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 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(&[Type::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 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(&[Type::IdentityId]);
132
133 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 mock.advance_millis(1);
146 }
147
148 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(&[Type::IdentityId, Type::Boolean, Type::IdentityId, Type::Int4]);
158 let mut row = shape.allocate();
159
160 let id1 = IdentityId::generate(&clock, &rng);
161 mock.advance_millis(1);
162 let id2 = IdentityId::generate(&clock, &rng);
163
164 shape.set_identity_id(&mut row, 0, id1.clone());
165 shape.set_bool(&mut row, 1, true);
166 shape.set_identity_id(&mut row, 2, id2.clone());
167 shape.set_i32(&mut row, 3, 42);
168
169 assert_eq!(shape.get_identity_id(&row, 0), id1);
170 assert_eq!(shape.get_bool(&row, 1), true);
171 assert_eq!(shape.get_identity_id(&row, 2), id2);
172 assert_eq!(shape.get_i32(&row, 3), 42);
173 }
174
175 #[test]
176 fn test_undefined_handling() {
177 let (_mock, clock, rng) = test_clock_and_rng();
178 let shape = RowShape::testing(&[Type::IdentityId, Type::IdentityId]);
179 let mut row = shape.allocate();
180
181 let id = IdentityId::generate(&clock, &rng);
182 shape.set_identity_id(&mut row, 0, id.clone());
183
184 assert_eq!(shape.try_get_identity_id(&row, 0), Some(id));
185 assert_eq!(shape.try_get_identity_id(&row, 1), None);
186
187 shape.set_none(&mut row, 0);
188 assert_eq!(shape.try_get_identity_id(&row, 0), None);
189 }
190
191 #[test]
192 fn test_persistence() {
193 let (_mock, clock, rng) = test_clock_and_rng();
194 let shape = RowShape::testing(&[Type::IdentityId]);
195 let mut row = shape.allocate();
196
197 let id = IdentityId::generate(&clock, &rng);
198 let id_string = id.to_string();
199
200 shape.set_identity_id(&mut row, 0, id.clone());
201 let retrieved = shape.get_identity_id(&row, 0);
202
203 assert_eq!(retrieved, id);
204 assert_eq!(retrieved.to_string(), id_string);
205 assert_eq!(retrieved.as_bytes(), id.as_bytes());
206 }
207
208 #[test]
209 fn test_clone_consistency() {
210 let (_mock, clock, rng) = test_clock_and_rng();
211 let shape = RowShape::testing(&[Type::IdentityId]);
212 let mut row = shape.allocate();
213
214 let original_id = IdentityId::generate(&clock, &rng);
215 shape.set_identity_id(&mut row, 0, original_id.clone());
216
217 let retrieved_id = shape.get_identity_id(&row, 0);
218 assert_eq!(retrieved_id, original_id);
219
220 assert_eq!(retrieved_id.as_bytes(), original_id.as_bytes());
223 }
224
225 #[test]
226 fn test_multiple_fields() {
227 let (mock, clock, rng) = test_clock_and_rng();
228 let shape = RowShape::testing(&[Type::IdentityId, Type::IdentityId, Type::IdentityId]);
229 let mut row = shape.allocate();
230
231 let id1 = IdentityId::generate(&clock, &rng);
232 mock.advance_millis(1);
233 let id2 = IdentityId::generate(&clock, &rng);
234 mock.advance_millis(1);
235 let id3 = IdentityId::generate(&clock, &rng);
236
237 shape.set_identity_id(&mut row, 0, id1.clone());
238 shape.set_identity_id(&mut row, 1, id2.clone());
239 shape.set_identity_id(&mut row, 2, id3.clone());
240
241 assert_eq!(shape.get_identity_id(&row, 0), id1);
242 assert_eq!(shape.get_identity_id(&row, 1), id2);
243 assert_eq!(shape.get_identity_id(&row, 2), id3);
244
245 assert_ne!(id1, id2);
247 assert_ne!(id1, id3);
248 assert_ne!(id2, id3);
249 }
250
251 #[test]
252 fn test_format_consistency() {
253 let (_mock, clock, rng) = test_clock_and_rng();
254 let shape = RowShape::testing(&[Type::IdentityId]);
255 let mut row = shape.allocate();
256
257 let id = IdentityId::generate(&clock, &rng);
258 let original_string = id.to_string();
259
260 shape.set_identity_id(&mut row, 0, id.clone());
261 let retrieved = shape.get_identity_id(&row, 0);
262 let retrieved_string = retrieved.to_string();
263
264 assert_eq!(original_string, retrieved_string);
265
266 assert_eq!(original_string.len(), 36);
269 assert_eq!(original_string.matches('-').count(), 4);
270 }
271
272 #[test]
273 fn test_byte_level_storage() {
274 let (_mock, clock, rng) = test_clock_and_rng();
275 let shape = RowShape::testing(&[Type::IdentityId]);
276 let mut row = shape.allocate();
277
278 let id = IdentityId::generate(&clock, &rng);
279 let original_bytes = *id.as_bytes();
280
281 shape.set_identity_id(&mut row, 0, id.clone());
282 let retrieved = shape.get_identity_id(&row, 0);
283 let retrieved_bytes = *retrieved.as_bytes();
284
285 assert_eq!(original_bytes, retrieved_bytes);
286
287 assert_eq!(original_bytes.len(), 16);
289 assert_eq!(retrieved_bytes.len(), 16);
290 }
291
292 #[test]
293 fn test_time_based_properties() {
294 let (mock, clock, rng) = test_clock_and_rng();
295 let shape = RowShape::testing(&[Type::IdentityId]);
296
297 let id1 = IdentityId::generate(&clock, &rng);
299 mock.advance_millis(2);
300 let id2 = IdentityId::generate(&clock, &rng);
301
302 let mut row1 = shape.allocate();
303 let mut row2 = shape.allocate();
304
305 shape.set_identity_id(&mut row1, 0, id1.clone());
306 shape.set_identity_id(&mut row2, 0, id2.clone());
307
308 let retrieved1 = shape.get_identity_id(&row1, 0);
309 let retrieved2 = shape.get_identity_id(&row2, 0);
310
311 assert!(retrieved2.as_bytes() > retrieved1.as_bytes());
314 }
315
316 #[test]
317 fn test_as_primary_key() {
318 let (_mock, clock, rng) = test_clock_and_rng();
319 let shape = RowShape::testing(&[
320 Type::IdentityId, Type::Utf8, Type::Int4, ]);
324 let mut row = shape.allocate();
325
326 let primary_key = IdentityId::generate(&clock, &rng);
328 shape.set_identity_id(&mut row, 0, primary_key.clone());
329 shape.set_utf8(&mut row, 1, "John Doe");
330 shape.set_i32(&mut row, 2, 30);
331
332 assert_eq!(shape.get_identity_id(&row, 0), primary_key);
333 assert_eq!(shape.get_utf8(&row, 1), "John Doe");
334 assert_eq!(shape.get_i32(&row, 2), 30);
335
336 assert_eq!(primary_key.get_version_num(), 7);
338 }
339
340 #[test]
341 fn test_try_get_identity_id_wrong_type() {
342 let shape = RowShape::testing(&[Type::Boolean]);
343 let mut row = shape.allocate();
344
345 shape.set_bool(&mut row, 0, true);
346
347 assert_eq!(shape.try_get_identity_id(&row, 0), None);
348 }
349}