LCOV - code coverage report
Current view: top level - libs/pageserver_api/src - reltag.rs (source / functions) Coverage Total Hit
Test: e5024a5c05016c30dec7897aca22d1040a340f63.info Lines: 35.1 % 74 26
Test Date: 2024-11-20 11:45:54 Functions: 10.8 % 37 4

            Line data    Source code
       1              : use serde::{Deserialize, Serialize};
       2              : use std::cmp::Ordering;
       3              : use std::fmt;
       4              : 
       5              : use postgres_ffi::pg_constants::GLOBALTABLESPACE_OID;
       6              : use postgres_ffi::relfile_utils::{forkname_to_number, forknumber_to_name, MAIN_FORKNUM};
       7              : use postgres_ffi::Oid;
       8              : 
       9              : ///
      10              : /// Relation data file segment id throughout the Postgres cluster.
      11              : ///
      12              : /// Every data file in Postgres is uniquely identified by 4 numbers:
      13              : /// - relation id / node (`relnode`)
      14              : /// - database id (`dbnode`)
      15              : /// - tablespace id (`spcnode`), in short this is a unique id of a separate
      16              : ///   directory to store data files.
      17              : /// - forknumber (`forknum`) is used to split different kinds of data of the same relation
      18              : ///   between some set of files (`relnode`, `relnode_fsm`, `relnode_vm`).
      19              : ///
      20              : /// In native Postgres code `RelFileNode` structure and individual `ForkNumber` value
      21              : /// are used for the same purpose.
      22              : /// [See more related comments here](https:///github.com/postgres/postgres/blob/99c5852e20a0987eca1c38ba0c09329d4076b6a0/src/include/storage/relfilenode.h#L57).
      23              : ///
      24              : // FIXME: should move 'forknum' as last field to keep this consistent with Postgres.
      25              : // Then we could replace the custom Ord and PartialOrd implementations below with
      26              : // deriving them. This will require changes in walredoproc.c.
      27            0 : #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Serialize, Deserialize)]
      28              : pub struct RelTag {
      29              :     pub forknum: u8,
      30              :     pub spcnode: Oid,
      31              :     pub dbnode: Oid,
      32              :     pub relnode: Oid,
      33              : }
      34              : 
      35              : /// Block number within a relation or SLRU. This matches PostgreSQL's BlockNumber type.
      36              : pub type BlockNumber = u32;
      37              : 
      38              : impl PartialOrd for RelTag {
      39            0 :     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
      40            0 :         Some(self.cmp(other))
      41            0 :     }
      42              : }
      43              : 
      44              : impl Ord for RelTag {
      45            0 :     fn cmp(&self, other: &Self) -> Ordering {
      46            0 :         // Custom ordering where we put forknum to the end of the list
      47            0 :         let other_tup = (other.spcnode, other.dbnode, other.relnode, other.forknum);
      48            0 :         (self.spcnode, self.dbnode, self.relnode, self.forknum).cmp(&other_tup)
      49            0 :     }
      50              : }
      51              : 
      52              : /// Display RelTag in the same format that's used in most PostgreSQL debug messages:
      53              : ///
      54              : /// ```text
      55              : /// <spcnode>/<dbnode>/<relnode>[_fsm|_vm|_init]
      56              : /// ```
      57              : impl fmt::Display for RelTag {
      58            2 :     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
      59            2 :         if let Some(forkname) = forknumber_to_name(self.forknum) {
      60            0 :             write!(
      61            0 :                 f,
      62            0 :                 "{}/{}/{}_{}",
      63            0 :                 self.spcnode, self.dbnode, self.relnode, forkname
      64            0 :             )
      65              :         } else {
      66            2 :             write!(f, "{}/{}/{}", self.spcnode, self.dbnode, self.relnode)
      67              :         }
      68            2 :     }
      69              : }
      70              : 
      71            0 : #[derive(Debug, thiserror::Error)]
      72              : pub enum ParseRelTagError {
      73              :     #[error("invalid forknum")]
      74              :     InvalidForknum(#[source] std::num::ParseIntError),
      75              :     #[error("missing triplet member {}", .0)]
      76              :     MissingTripletMember(usize),
      77              :     #[error("invalid triplet member {}", .0)]
      78              :     InvalidTripletMember(usize, #[source] std::num::ParseIntError),
      79              : }
      80              : 
      81              : impl std::str::FromStr for RelTag {
      82              :     type Err = ParseRelTagError;
      83              : 
      84            7 :     fn from_str(s: &str) -> Result<Self, Self::Err> {
      85              :         use ParseRelTagError::*;
      86              : 
      87              :         // FIXME: in postgres logs this separator is dot
      88              :         // Example:
      89              :         //     could not read block 2 in rel 1663/208101/2620.1 from page server at lsn 0/2431E6F0
      90              :         // with a regex we could get this more painlessly
      91            7 :         let (triplet, forknum) = match s.split_once('_').or_else(|| s.split_once('.')) {
      92            7 :             Some((t, f)) => {
      93            7 :                 let forknum = forkname_to_number(Some(f));
      94            7 :                 let forknum = if let Ok(f) = forknum {
      95            5 :                     f
      96              :                 } else {
      97            2 :                     f.parse::<u8>().map_err(InvalidForknum)?
      98              :                 };
      99              : 
     100            6 :                 (t, Some(forknum))
     101              :             }
     102            0 :             None => (s, None),
     103              :         };
     104              : 
     105            6 :         let mut split = triplet
     106            6 :             .splitn(3, '/')
     107            6 :             .enumerate()
     108           18 :             .map(|(i, s)| s.parse::<u32>().map_err(|e| InvalidTripletMember(i, e)));
     109            6 :         let spcnode = split.next().ok_or(MissingTripletMember(0))??;
     110            6 :         let dbnode = split.next().ok_or(MissingTripletMember(1))??;
     111            6 :         let relnode = split.next().ok_or(MissingTripletMember(2))??;
     112              : 
     113            6 :         Ok(RelTag {
     114            6 :             spcnode,
     115            6 :             forknum: forknum.unwrap_or(MAIN_FORKNUM),
     116            6 :             dbnode,
     117            6 :             relnode,
     118            6 :         })
     119            7 :     }
     120              : }
     121              : 
     122              : impl RelTag {
     123            0 :     pub fn to_segfile_name(&self, segno: u32) -> String {
     124            0 :         let mut name = if self.spcnode == GLOBALTABLESPACE_OID {
     125            0 :             "global/".to_string()
     126              :         } else {
     127            0 :             format!("base/{}/", self.dbnode)
     128              :         };
     129              : 
     130            0 :         name += &self.relnode.to_string();
     131              : 
     132            0 :         if let Some(fork_name) = forknumber_to_name(self.forknum) {
     133            0 :             name += "_";
     134            0 :             name += fork_name;
     135            0 :         }
     136              : 
     137            0 :         if segno != 0 {
     138            0 :             name += ".";
     139            0 :             name += &segno.to_string();
     140            0 :         }
     141              : 
     142            0 :         name
     143            0 :     }
     144              : 
     145            0 :     pub fn with_forknum(&self, forknum: u8) -> Self {
     146            0 :         RelTag {
     147            0 :             forknum,
     148            0 :             spcnode: self.spcnode,
     149            0 :             dbnode: self.dbnode,
     150            0 :             relnode: self.relnode,
     151            0 :         }
     152            0 :     }
     153              : }
     154              : 
     155              : ///
     156              : /// Non-relation transaction status files (clog (a.k.a. pg_xact) and
     157              : /// pg_multixact) in Postgres are handled by SLRU (Simple LRU) buffer,
     158              : /// hence the name.
     159              : ///
     160              : /// These files are global for a postgres instance.
     161              : ///
     162              : /// These files are divided into segments, which are divided into
     163              : /// pages of the same BLCKSZ as used for relation files.
     164              : ///
     165              : #[derive(
     166              :     Debug,
     167              :     Clone,
     168              :     Copy,
     169              :     Hash,
     170              :     Serialize,
     171            0 :     Deserialize,
     172              :     PartialEq,
     173              :     Eq,
     174              :     PartialOrd,
     175              :     Ord,
     176            0 :     strum_macros::EnumIter,
     177            0 :     strum_macros::FromRepr,
     178              :     enum_map::Enum,
     179              : )]
     180              : #[repr(u8)]
     181              : pub enum SlruKind {
     182              :     Clog = 0,
     183              :     MultiXactMembers,
     184              :     MultiXactOffsets,
     185              : }
     186              : 
     187              : impl SlruKind {
     188            0 :     pub fn to_str(&self) -> &'static str {
     189            0 :         match self {
     190            0 :             Self::Clog => "pg_xact",
     191            0 :             Self::MultiXactMembers => "pg_multixact/members",
     192            0 :             Self::MultiXactOffsets => "pg_multixact/offsets",
     193              :         }
     194            0 :     }
     195              : }
        

Generated by: LCOV version 2.1-beta