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 : #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Serialize)]
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 : }
|