Line data Source code
1 : use anyhow::{bail, Result};
2 : use byteorder::{ByteOrder, BE};
3 : use bytes::BufMut;
4 : use postgres_ffi::relfile_utils::{FSM_FORKNUM, VISIBILITYMAP_FORKNUM};
5 : use postgres_ffi::{Oid, TransactionId};
6 : use serde::{Deserialize, Serialize};
7 : use std::{fmt, ops::Range};
8 :
9 : use crate::reltag::{BlockNumber, RelTag, SlruKind};
10 :
11 : /// Key used in the Repository kv-store.
12 : ///
13 : /// The Repository treats this as an opaque struct, but see the code in pgdatadir_mapping.rs
14 : /// for what we actually store in these fields.
15 1688 : #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Ord, PartialOrd, Serialize, Deserialize)]
16 : pub struct Key {
17 : pub field1: u8,
18 : pub field2: u32,
19 : pub field3: u32,
20 : pub field4: u32,
21 : pub field5: u8,
22 : pub field6: u32,
23 : }
24 :
25 : /// The storage key size.
26 : pub const KEY_SIZE: usize = 18;
27 :
28 : /// The metadata key size. 2B fewer than the storage key size because field2 is not fully utilized.
29 : /// See [`Key::to_i128`] for more information on the encoding.
30 : pub const METADATA_KEY_SIZE: usize = 16;
31 :
32 : /// The key prefix start range for the metadata keys. All keys with the first byte >= 0x40 is a metadata key.
33 : pub const METADATA_KEY_BEGIN_PREFIX: u8 = 0x60;
34 : pub const METADATA_KEY_END_PREFIX: u8 = 0x7F;
35 :
36 : /// The (reserved) key prefix of relation sizes.
37 : pub const RELATION_SIZE_PREFIX: u8 = 0x61;
38 :
39 : /// The key prefix of AUX file keys.
40 : pub const AUX_KEY_PREFIX: u8 = 0x62;
41 :
42 : /// Check if the key falls in the range of metadata keys.
43 16 : pub const fn is_metadata_key_slice(key: &[u8]) -> bool {
44 16 : key[0] >= METADATA_KEY_BEGIN_PREFIX && key[0] < METADATA_KEY_END_PREFIX
45 16 : }
46 :
47 : impl Key {
48 : /// Check if the key falls in the range of metadata keys.
49 2 : pub const fn is_metadata_key(&self) -> bool {
50 2 : self.field1 >= METADATA_KEY_BEGIN_PREFIX && self.field1 < METADATA_KEY_END_PREFIX
51 2 : }
52 :
53 : /// Encode a metadata key to a storage key.
54 14 : pub fn from_metadata_key_fixed_size(key: &[u8; METADATA_KEY_SIZE]) -> Self {
55 14 : assert!(is_metadata_key_slice(key), "key not in metadata key range");
56 14 : Key {
57 14 : field1: key[0],
58 14 : field2: u16::from_be_bytes(key[1..3].try_into().unwrap()) as u32,
59 14 : field3: u32::from_be_bytes(key[3..7].try_into().unwrap()),
60 14 : field4: u32::from_be_bytes(key[7..11].try_into().unwrap()),
61 14 : field5: key[11],
62 14 : field6: u32::from_be_bytes(key[12..16].try_into().unwrap()),
63 14 : }
64 14 : }
65 :
66 : /// Encode a metadata key to a storage key.
67 2 : pub fn from_metadata_key(key: &[u8]) -> Self {
68 2 : Self::from_metadata_key_fixed_size(key.try_into().expect("expect 16 byte metadata key"))
69 2 : }
70 :
71 : /// Extract a metadata key to a writer. The result should always be 16 bytes.
72 2 : pub fn extract_metadata_key_to_writer(&self, mut writer: impl BufMut) {
73 2 : writer.put_u8(self.field1);
74 2 : assert!(self.field2 <= 0xFFFF);
75 2 : writer.put_u16(self.field2 as u16);
76 2 : writer.put_u32(self.field3);
77 2 : writer.put_u32(self.field4);
78 2 : writer.put_u8(self.field5);
79 2 : writer.put_u32(self.field6);
80 2 : }
81 :
82 : /// Get the range of metadata keys.
83 800 : pub const fn metadata_key_range() -> Range<Self> {
84 800 : Key {
85 800 : field1: METADATA_KEY_BEGIN_PREFIX,
86 800 : field2: 0,
87 800 : field3: 0,
88 800 : field4: 0,
89 800 : field5: 0,
90 800 : field6: 0,
91 800 : }..Key {
92 800 : field1: METADATA_KEY_END_PREFIX,
93 800 : field2: 0,
94 800 : field3: 0,
95 800 : field4: 0,
96 800 : field5: 0,
97 800 : field6: 0,
98 800 : }
99 800 : }
100 :
101 : /// Get the range of aux keys.
102 204 : pub fn metadata_aux_key_range() -> Range<Self> {
103 204 : Key {
104 204 : field1: AUX_KEY_PREFIX,
105 204 : field2: 0,
106 204 : field3: 0,
107 204 : field4: 0,
108 204 : field5: 0,
109 204 : field6: 0,
110 204 : }..Key {
111 204 : field1: AUX_KEY_PREFIX + 1,
112 204 : field2: 0,
113 204 : field3: 0,
114 204 : field4: 0,
115 204 : field5: 0,
116 204 : field6: 0,
117 204 : }
118 204 : }
119 :
120 : /// 'field2' is used to store tablespaceid for relations and small enum numbers for other relish.
121 : /// As long as Neon does not support tablespace (because of lack of access to local file system),
122 : /// we can assume that only some predefined namespace OIDs are used which can fit in u16
123 9087290 : pub fn to_i128(&self) -> i128 {
124 9087290 : assert!(self.field2 < 0xFFFF || self.field2 == 0xFFFFFFFF || self.field2 == 0x22222222);
125 9087290 : (((self.field1 & 0x7F) as i128) << 120)
126 9087290 : | (((self.field2 & 0xFFFF) as i128) << 104)
127 9087290 : | ((self.field3 as i128) << 72)
128 9087290 : | ((self.field4 as i128) << 40)
129 9087290 : | ((self.field5 as i128) << 32)
130 9087290 : | self.field6 as i128
131 9087290 : }
132 :
133 150114 : pub const fn from_i128(x: i128) -> Self {
134 150114 : Key {
135 150114 : field1: ((x >> 120) & 0x7F) as u8,
136 150114 : field2: ((x >> 104) & 0xFFFF) as u32,
137 150114 : field3: (x >> 72) as u32,
138 150114 : field4: (x >> 40) as u32,
139 150114 : field5: (x >> 32) as u8,
140 150114 : field6: x as u32,
141 150114 : }
142 150114 : }
143 :
144 6418015 : pub const fn next(&self) -> Key {
145 6418015 : self.add(1)
146 6418015 : }
147 :
148 6434983 : pub const fn add(&self, x: u32) -> Key {
149 6434983 : let mut key = *self;
150 6434983 :
151 6434983 : let r = key.field6.overflowing_add(x);
152 6434983 : key.field6 = r.0;
153 6434983 : if r.1 {
154 18 : let r = key.field5.overflowing_add(1);
155 18 : key.field5 = r.0;
156 18 : if r.1 {
157 0 : let r = key.field4.overflowing_add(1);
158 0 : key.field4 = r.0;
159 0 : if r.1 {
160 0 : let r = key.field3.overflowing_add(1);
161 0 : key.field3 = r.0;
162 0 : if r.1 {
163 0 : let r = key.field2.overflowing_add(1);
164 0 : key.field2 = r.0;
165 0 : if r.1 {
166 0 : let r = key.field1.overflowing_add(1);
167 0 : key.field1 = r.0;
168 0 : assert!(!r.1);
169 0 : }
170 0 : }
171 0 : }
172 18 : }
173 6434965 : }
174 6434983 : key
175 6434983 : }
176 :
177 : /// Convert a 18B slice to a key. This function should not be used for metadata keys because field2 is handled differently.
178 : /// Use [`Key::from_metadata_key`] instead.
179 2193455 : pub fn from_slice(b: &[u8]) -> Self {
180 2193455 : Key {
181 2193455 : field1: b[0],
182 2193455 : field2: u32::from_be_bytes(b[1..5].try_into().unwrap()),
183 2193455 : field3: u32::from_be_bytes(b[5..9].try_into().unwrap()),
184 2193455 : field4: u32::from_be_bytes(b[9..13].try_into().unwrap()),
185 2193455 : field5: b[13],
186 2193455 : field6: u32::from_be_bytes(b[14..18].try_into().unwrap()),
187 2193455 : }
188 2193455 : }
189 :
190 : /// Convert a key to a 18B slice. This function should not be used for metadata keys because field2 is handled differently.
191 : /// Use [`Key::extract_metadata_key_to_writer`] instead.
192 6678706 : pub fn write_to_byte_slice(&self, buf: &mut [u8]) {
193 6678706 : buf[0] = self.field1;
194 6678706 : BE::write_u32(&mut buf[1..5], self.field2);
195 6678706 : BE::write_u32(&mut buf[5..9], self.field3);
196 6678706 : BE::write_u32(&mut buf[9..13], self.field4);
197 6678706 : buf[13] = self.field5;
198 6678706 : BE::write_u32(&mut buf[14..18], self.field6);
199 6678706 : }
200 : }
201 :
202 : impl fmt::Display for Key {
203 66550 : fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
204 66550 : write!(
205 66550 : f,
206 66550 : "{:02X}{:08X}{:08X}{:08X}{:02X}{:08X}",
207 66550 : self.field1, self.field2, self.field3, self.field4, self.field5, self.field6
208 66550 : )
209 66550 : }
210 : }
211 :
212 : impl Key {
213 : pub const MIN: Key = Key {
214 : field1: u8::MIN,
215 : field2: u32::MIN,
216 : field3: u32::MIN,
217 : field4: u32::MIN,
218 : field5: u8::MIN,
219 : field6: u32::MIN,
220 : };
221 : pub const MAX: Key = Key {
222 : field1: u8::MAX,
223 : field2: u32::MAX,
224 : field3: u32::MAX,
225 : field4: u32::MAX,
226 : field5: u8::MAX,
227 : field6: u32::MAX,
228 : };
229 :
230 2300 : pub fn from_hex(s: &str) -> Result<Self> {
231 2300 : if s.len() != 36 {
232 0 : bail!("parse error");
233 2300 : }
234 2300 : Ok(Key {
235 2300 : field1: u8::from_str_radix(&s[0..2], 16)?,
236 2300 : field2: u32::from_str_radix(&s[2..10], 16)?,
237 2300 : field3: u32::from_str_radix(&s[10..18], 16)?,
238 2300 : field4: u32::from_str_radix(&s[18..26], 16)?,
239 2300 : field5: u8::from_str_radix(&s[26..28], 16)?,
240 2300 : field6: u32::from_str_radix(&s[28..36], 16)?,
241 : })
242 2300 : }
243 : }
244 :
245 : // Layout of the Key address space
246 : //
247 : // The Key struct, used to address the underlying key-value store, consists of
248 : // 18 bytes, split into six fields. See 'Key' in repository.rs. We need to map
249 : // all the data and metadata keys into those 18 bytes.
250 : //
251 : // Principles for the mapping:
252 : //
253 : // - Things that are often accessed or modified together, should be close to
254 : // each other in the key space. For example, if a relation is extended by one
255 : // block, we create a new key-value pair for the block data, and update the
256 : // relation size entry. Because of that, the RelSize key comes after all the
257 : // RelBlocks of a relation: the RelSize and the last RelBlock are always next
258 : // to each other.
259 : //
260 : // The key space is divided into four major sections, identified by the first
261 : // byte, and the form a hierarchy:
262 : //
263 : // 00 Relation data and metadata
264 : //
265 : // DbDir () -> (dbnode, spcnode)
266 : // Filenodemap
267 : // RelDir -> relnode forknum
268 : // RelBlocks
269 : // RelSize
270 : //
271 : // 01 SLRUs
272 : //
273 : // SlruDir kind
274 : // SlruSegBlocks segno
275 : // SlruSegSize
276 : //
277 : // 02 pg_twophase
278 : //
279 : // 03 misc
280 : // Controlfile
281 : // checkpoint
282 : // pg_version
283 : //
284 : // 04 aux files
285 : //
286 : // Below is a full list of the keyspace allocation:
287 : //
288 : // DbDir:
289 : // 00 00000000 00000000 00000000 00 00000000
290 : //
291 : // Filenodemap:
292 : // 00 SPCNODE DBNODE 00000000 00 00000000
293 : //
294 : // RelDir:
295 : // 00 SPCNODE DBNODE 00000000 00 00000001 (Postgres never uses relfilenode 0)
296 : //
297 : // RelBlock:
298 : // 00 SPCNODE DBNODE RELNODE FORK BLKNUM
299 : //
300 : // RelSize:
301 : // 00 SPCNODE DBNODE RELNODE FORK FFFFFFFF
302 : //
303 : // SlruDir:
304 : // 01 kind 00000000 00000000 00 00000000
305 : //
306 : // SlruSegBlock:
307 : // 01 kind 00000001 SEGNO 00 BLKNUM
308 : //
309 : // SlruSegSize:
310 : // 01 kind 00000001 SEGNO 00 FFFFFFFF
311 : //
312 : // TwoPhaseDir:
313 : // 02 00000000 00000000 00000000 00 00000000
314 : //
315 : // TwoPhaseFile:
316 : // 02 00000000 00000000 00000000 00 XID
317 : //
318 : // ControlFile:
319 : // 03 00000000 00000000 00000000 00 00000000
320 : //
321 : // Checkpoint:
322 : // 03 00000000 00000000 00000000 00 00000001
323 : //
324 : // AuxFiles:
325 : // 03 00000000 00000000 00000000 00 00000002
326 : //
327 :
328 : //-- Section 01: relation data and metadata
329 :
330 : pub const DBDIR_KEY: Key = Key {
331 : field1: 0x00,
332 : field2: 0,
333 : field3: 0,
334 : field4: 0,
335 : field5: 0,
336 : field6: 0,
337 : };
338 :
339 : #[inline(always)]
340 0 : pub fn dbdir_key_range(spcnode: Oid, dbnode: Oid) -> Range<Key> {
341 0 : Key {
342 0 : field1: 0x00,
343 0 : field2: spcnode,
344 0 : field3: dbnode,
345 0 : field4: 0,
346 0 : field5: 0,
347 0 : field6: 0,
348 0 : }..Key {
349 0 : field1: 0x00,
350 0 : field2: spcnode,
351 0 : field3: dbnode,
352 0 : field4: 0xffffffff,
353 0 : field5: 0xff,
354 0 : field6: 0xffffffff,
355 0 : }
356 0 : }
357 :
358 : #[inline(always)]
359 16 : pub fn relmap_file_key(spcnode: Oid, dbnode: Oid) -> Key {
360 16 : Key {
361 16 : field1: 0x00,
362 16 : field2: spcnode,
363 16 : field3: dbnode,
364 16 : field4: 0,
365 16 : field5: 0,
366 16 : field6: 0,
367 16 : }
368 16 : }
369 :
370 : #[inline(always)]
371 1948 : pub fn rel_dir_to_key(spcnode: Oid, dbnode: Oid) -> Key {
372 1948 : Key {
373 1948 : field1: 0x00,
374 1948 : field2: spcnode,
375 1948 : field3: dbnode,
376 1948 : field4: 0,
377 1948 : field5: 0,
378 1948 : field6: 1,
379 1948 : }
380 1948 : }
381 :
382 : #[inline(always)]
383 870748 : pub fn rel_block_to_key(rel: RelTag, blknum: BlockNumber) -> Key {
384 870748 : Key {
385 870748 : field1: 0x00,
386 870748 : field2: rel.spcnode,
387 870748 : field3: rel.dbnode,
388 870748 : field4: rel.relnode,
389 870748 : field5: rel.forknum,
390 870748 : field6: blknum,
391 870748 : }
392 870748 : }
393 :
394 : #[inline(always)]
395 289754 : pub fn rel_size_to_key(rel: RelTag) -> Key {
396 289754 : Key {
397 289754 : field1: 0x00,
398 289754 : field2: rel.spcnode,
399 289754 : field3: rel.dbnode,
400 289754 : field4: rel.relnode,
401 289754 : field5: rel.forknum,
402 289754 : field6: 0xffffffff,
403 289754 : }
404 289754 : }
405 :
406 : #[inline(always)]
407 2 : pub fn rel_key_range(rel: RelTag) -> Range<Key> {
408 2 : Key {
409 2 : field1: 0x00,
410 2 : field2: rel.spcnode,
411 2 : field3: rel.dbnode,
412 2 : field4: rel.relnode,
413 2 : field5: rel.forknum,
414 2 : field6: 0,
415 2 : }..Key {
416 2 : field1: 0x00,
417 2 : field2: rel.spcnode,
418 2 : field3: rel.dbnode,
419 2 : field4: rel.relnode,
420 2 : field5: rel.forknum + 1,
421 2 : field6: 0,
422 2 : }
423 2 : }
424 :
425 : //-- Section 02: SLRUs
426 :
427 : #[inline(always)]
428 930 : pub fn slru_dir_to_key(kind: SlruKind) -> Key {
429 930 : Key {
430 930 : field1: 0x01,
431 930 : field2: match kind {
432 310 : SlruKind::Clog => 0x00,
433 310 : SlruKind::MultiXactMembers => 0x01,
434 310 : SlruKind::MultiXactOffsets => 0x02,
435 : },
436 : field3: 0,
437 : field4: 0,
438 : field5: 0,
439 : field6: 0,
440 : }
441 930 : }
442 :
443 : #[inline(always)]
444 14 : pub fn slru_block_to_key(kind: SlruKind, segno: u32, blknum: BlockNumber) -> Key {
445 14 : Key {
446 14 : field1: 0x01,
447 14 : field2: match kind {
448 10 : SlruKind::Clog => 0x00,
449 2 : SlruKind::MultiXactMembers => 0x01,
450 2 : SlruKind::MultiXactOffsets => 0x02,
451 : },
452 : field3: 1,
453 14 : field4: segno,
454 14 : field5: 0,
455 14 : field6: blknum,
456 14 : }
457 14 : }
458 :
459 : #[inline(always)]
460 6 : pub fn slru_segment_size_to_key(kind: SlruKind, segno: u32) -> Key {
461 6 : Key {
462 6 : field1: 0x01,
463 6 : field2: match kind {
464 2 : SlruKind::Clog => 0x00,
465 2 : SlruKind::MultiXactMembers => 0x01,
466 2 : SlruKind::MultiXactOffsets => 0x02,
467 : },
468 : field3: 1,
469 6 : field4: segno,
470 6 : field5: 0,
471 6 : field6: 0xffffffff,
472 6 : }
473 6 : }
474 :
475 : #[inline(always)]
476 0 : pub fn slru_segment_key_range(kind: SlruKind, segno: u32) -> Range<Key> {
477 0 : let field2 = match kind {
478 0 : SlruKind::Clog => 0x00,
479 0 : SlruKind::MultiXactMembers => 0x01,
480 0 : SlruKind::MultiXactOffsets => 0x02,
481 : };
482 :
483 0 : Key {
484 0 : field1: 0x01,
485 0 : field2,
486 0 : field3: 1,
487 0 : field4: segno,
488 0 : field5: 0,
489 0 : field6: 0,
490 0 : }..Key {
491 0 : field1: 0x01,
492 0 : field2,
493 0 : field3: 1,
494 0 : field4: segno,
495 0 : field5: 1,
496 0 : field6: 0,
497 0 : }
498 0 : }
499 :
500 : //-- Section 03: pg_twophase
501 :
502 : pub const TWOPHASEDIR_KEY: Key = Key {
503 : field1: 0x02,
504 : field2: 0,
505 : field3: 0,
506 : field4: 0,
507 : field5: 0,
508 : field6: 0,
509 : };
510 :
511 : #[inline(always)]
512 0 : pub fn twophase_file_key(xid: TransactionId) -> Key {
513 0 : Key {
514 0 : field1: 0x02,
515 0 : field2: 0,
516 0 : field3: 0,
517 0 : field4: 0,
518 0 : field5: 0,
519 0 : field6: xid,
520 0 : }
521 0 : }
522 :
523 : #[inline(always)]
524 0 : pub fn twophase_key_range(xid: TransactionId) -> Range<Key> {
525 0 : let (next_xid, overflowed) = xid.overflowing_add(1);
526 0 :
527 0 : Key {
528 0 : field1: 0x02,
529 0 : field2: 0,
530 0 : field3: 0,
531 0 : field4: 0,
532 0 : field5: 0,
533 0 : field6: xid,
534 0 : }..Key {
535 0 : field1: 0x02,
536 0 : field2: 0,
537 0 : field3: 0,
538 0 : field4: 0,
539 0 : field5: u8::from(overflowed),
540 0 : field6: next_xid,
541 0 : }
542 0 : }
543 :
544 : //-- Section 03: Control file
545 : pub const CONTROLFILE_KEY: Key = Key {
546 : field1: 0x03,
547 : field2: 0,
548 : field3: 0,
549 : field4: 0,
550 : field5: 0,
551 : field6: 0,
552 : };
553 :
554 : pub const CHECKPOINT_KEY: Key = Key {
555 : field1: 0x03,
556 : field2: 0,
557 : field3: 0,
558 : field4: 0,
559 : field5: 0,
560 : field6: 1,
561 : };
562 :
563 : pub const AUX_FILES_KEY: Key = Key {
564 : field1: 0x03,
565 : field2: 0,
566 : field3: 0,
567 : field4: 0,
568 : field5: 0,
569 : field6: 2,
570 : };
571 :
572 : // Reverse mappings for a few Keys.
573 : // These are needed by WAL redo manager.
574 :
575 : /// Non inherited range for vectored get.
576 : pub const NON_INHERITED_RANGE: Range<Key> = AUX_FILES_KEY..AUX_FILES_KEY.next();
577 : /// Sparse keyspace range for vectored get. Missing key error will be ignored for this range.
578 : pub const NON_INHERITED_SPARSE_RANGE: Range<Key> = Key::metadata_key_range();
579 :
580 : // AUX_FILES currently stores only data for logical replication (slots etc), and
581 : // we don't preserve these on a branch because safekeepers can't follow timeline
582 : // switch (and generally it likely should be optional), so ignore these.
583 : #[inline(always)]
584 1039248 : pub fn is_inherited_key(key: Key) -> bool {
585 1039248 : !NON_INHERITED_RANGE.contains(&key) && !NON_INHERITED_SPARSE_RANGE.contains(&key)
586 1039248 : }
587 :
588 : #[inline(always)]
589 0 : pub fn is_rel_fsm_block_key(key: Key) -> bool {
590 0 : key.field1 == 0x00 && key.field4 != 0 && key.field5 == FSM_FORKNUM && key.field6 != 0xffffffff
591 0 : }
592 :
593 : #[inline(always)]
594 0 : pub fn is_rel_vm_block_key(key: Key) -> bool {
595 0 : key.field1 == 0x00
596 0 : && key.field4 != 0
597 0 : && key.field5 == VISIBILITYMAP_FORKNUM
598 0 : && key.field6 != 0xffffffff
599 0 : }
600 :
601 : #[inline(always)]
602 0 : pub fn key_to_slru_block(key: Key) -> anyhow::Result<(SlruKind, u32, BlockNumber)> {
603 0 : Ok(match key.field1 {
604 : 0x01 => {
605 0 : let kind = match key.field2 {
606 0 : 0x00 => SlruKind::Clog,
607 0 : 0x01 => SlruKind::MultiXactMembers,
608 0 : 0x02 => SlruKind::MultiXactOffsets,
609 0 : _ => anyhow::bail!("unrecognized slru kind 0x{:02x}", key.field2),
610 : };
611 0 : let segno = key.field4;
612 0 : let blknum = key.field6;
613 0 :
614 0 : (kind, segno, blknum)
615 : }
616 0 : _ => anyhow::bail!("unexpected value kind 0x{:02x}", key.field1),
617 : })
618 0 : }
619 :
620 : #[inline(always)]
621 0 : pub fn is_slru_block_key(key: Key) -> bool {
622 0 : key.field1 == 0x01 // SLRU-related
623 0 : && key.field3 == 0x00000001 // but not SlruDir
624 0 : && key.field6 != 0xffffffff // and not SlruSegSize
625 0 : }
626 :
627 : #[inline(always)]
628 2677843 : pub fn is_rel_block_key(key: &Key) -> bool {
629 2677843 : key.field1 == 0x00 && key.field4 != 0 && key.field6 != 0xffffffff
630 2677843 : }
631 :
632 : /// Guaranteed to return `Ok()` if [[is_rel_block_key]] returns `true` for `key`.
633 : #[inline(always)]
634 6 : pub fn key_to_rel_block(key: Key) -> anyhow::Result<(RelTag, BlockNumber)> {
635 6 : Ok(match key.field1 {
636 6 : 0x00 => (
637 6 : RelTag {
638 6 : spcnode: key.field2,
639 6 : dbnode: key.field3,
640 6 : relnode: key.field4,
641 6 : forknum: key.field5,
642 6 : },
643 6 : key.field6,
644 6 : ),
645 0 : _ => anyhow::bail!("unexpected value kind 0x{:02x}", key.field1),
646 : })
647 6 : }
648 :
649 : impl std::str::FromStr for Key {
650 : type Err = anyhow::Error;
651 :
652 18 : fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
653 18 : Self::from_hex(s)
654 18 : }
655 : }
656 :
657 : #[cfg(test)]
658 : mod tests {
659 : use std::str::FromStr;
660 :
661 : use crate::key::is_metadata_key_slice;
662 : use crate::key::Key;
663 :
664 : use rand::Rng;
665 : use rand::SeedableRng;
666 :
667 : use super::AUX_KEY_PREFIX;
668 :
669 : #[test]
670 2 : fn display_fromstr_bijection() {
671 2 : let mut rng = rand::rngs::StdRng::seed_from_u64(42);
672 2 :
673 2 : let key = Key {
674 2 : field1: rng.gen(),
675 2 : field2: rng.gen(),
676 2 : field3: rng.gen(),
677 2 : field4: rng.gen(),
678 2 : field5: rng.gen(),
679 2 : field6: rng.gen(),
680 2 : };
681 2 :
682 2 : assert_eq!(key, Key::from_str(&format!("{key}")).unwrap());
683 2 : }
684 :
685 : #[test]
686 2 : fn test_metadata_keys() {
687 2 : let mut metadata_key = vec![AUX_KEY_PREFIX];
688 2 : metadata_key.extend_from_slice(&[0xFF; 15]);
689 2 : let encoded_key = Key::from_metadata_key(&metadata_key);
690 2 : let mut output_key = Vec::new();
691 2 : encoded_key.extract_metadata_key_to_writer(&mut output_key);
692 2 : assert_eq!(metadata_key, output_key);
693 2 : assert!(encoded_key.is_metadata_key());
694 2 : assert!(is_metadata_key_slice(&metadata_key));
695 2 : }
696 : }
|