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