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