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