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(
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 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 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 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 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 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 mock.advance_millis(1);
148 }
149
150 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 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 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 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 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 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 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, Type::Utf8, Type::Int4, ]);
326 let mut row = shape.allocate();
327
328 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 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}