PostgreSQL Source Code git master
relpath.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 * relpath.c
3 * Shared frontend/backend code to compute pathnames of relation files
4 *
5 * This module also contains some logic associated with fork names.
6 *
7 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
9 *
10 * IDENTIFICATION
11 * src/common/relpath.c
12 *
13 *-------------------------------------------------------------------------
14 */
15#ifndef FRONTEND
16#include "postgres.h"
17#else
18#include "postgres_fe.h"
19#endif
20
21#include "catalog/pg_tablespace_d.h"
22#include "common/relpath.h"
23#include "storage/procnumber.h"
24
25
26/*
27 * Lookup table of fork name by fork number.
28 *
29 * If you add a new entry, remember to update the errhint in
30 * forkname_to_number() below, and update the SGML documentation for
31 * pg_relation_size().
32 */
33const char *const forkNames[] = {
34 [MAIN_FORKNUM] = "main",
35 [FSM_FORKNUM] = "fsm",
36 [VISIBILITYMAP_FORKNUM] = "vm",
37 [INIT_FORKNUM] = "init",
38};
39
41 "array length mismatch");
42
43/*
44 * forkname_to_number - look up fork number by name
45 *
46 * In backend, we throw an error for no match; in frontend, we just
47 * return InvalidForkNumber.
48 */
50forkname_to_number(const char *forkName)
51{
52 ForkNumber forkNum;
53
54 for (forkNum = 0; forkNum <= MAX_FORKNUM; forkNum++)
55 if (strcmp(forkNames[forkNum], forkName) == 0)
56 return forkNum;
57
58#ifndef FRONTEND
60 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
61 errmsg("invalid fork name"),
62 errhint("Valid fork names are \"main\", \"fsm\", "
63 "\"vm\", and \"init\".")));
64#endif
65
66 return InvalidForkNumber;
67}
68
69/*
70 * forkname_chars
71 * We use this to figure out whether a filename could be a relation
72 * fork (as opposed to an oddly named stray file that somehow ended
73 * up in the database directory). If the passed string begins with
74 * a fork name (other than the main fork name), we return its length,
75 * and set *fork (if not NULL) to the fork number. If not, we return 0.
76 *
77 * Note that the present coding assumes that there are no fork names which
78 * are prefixes of other fork names.
79 */
80int
81forkname_chars(const char *str, ForkNumber *fork)
82{
83 ForkNumber forkNum;
84
85 for (forkNum = 1; forkNum <= MAX_FORKNUM; forkNum++)
86 {
87 int len = strlen(forkNames[forkNum]);
88
89 if (strncmp(forkNames[forkNum], str, len) == 0)
90 {
91 if (fork)
92 *fork = forkNum;
93 return len;
94 }
95 }
96 if (fork)
97 *fork = InvalidForkNumber;
98 return 0;
99}
100
101
102/*
103 * GetDatabasePath - construct path to a database directory
104 *
105 * Result is a palloc'd string.
106 *
107 * XXX this must agree with GetRelationPath()!
108 */
109char *
110GetDatabasePath(Oid dbOid, Oid spcOid)
111{
112 if (spcOid == GLOBALTABLESPACE_OID)
113 {
114 /* Shared system relations live in {datadir}/global */
115 Assert(dbOid == 0);
116 return pstrdup("global");
117 }
118 else if (spcOid == DEFAULTTABLESPACE_OID)
119 {
120 /* The default tablespace is {datadir}/base */
121 return psprintf("base/%u", dbOid);
122 }
123 else
124 {
125 /* All other tablespaces are accessed via symlinks */
126 return psprintf("%s/%u/%s/%u",
127 PG_TBLSPC_DIR, spcOid,
129 }
130}
131
132/*
133 * GetRelationPath - construct path to a relation's file
134 *
135 * Result is a palloc'd string.
136 *
137 * Note: ideally, procNumber would be declared as type ProcNumber, but
138 * relpath.h would have to include a backend-only header to do that; doesn't
139 * seem worth the trouble considering ProcNumber is just int anyway.
140 */
141char *
142GetRelationPath(Oid dbOid, Oid spcOid, RelFileNumber relNumber,
143 int procNumber, ForkNumber forkNumber)
144{
145 char *path;
146
147 if (spcOid == GLOBALTABLESPACE_OID)
148 {
149 /* Shared system relations live in {datadir}/global */
150 Assert(dbOid == 0);
151 Assert(procNumber == INVALID_PROC_NUMBER);
152 if (forkNumber != MAIN_FORKNUM)
153 path = psprintf("global/%u_%s",
154 relNumber, forkNames[forkNumber]);
155 else
156 path = psprintf("global/%u", relNumber);
157 }
158 else if (spcOid == DEFAULTTABLESPACE_OID)
159 {
160 /* The default tablespace is {datadir}/base */
161 if (procNumber == INVALID_PROC_NUMBER)
162 {
163 if (forkNumber != MAIN_FORKNUM)
164 path = psprintf("base/%u/%u_%s",
165 dbOid, relNumber,
166 forkNames[forkNumber]);
167 else
168 path = psprintf("base/%u/%u",
169 dbOid, relNumber);
170 }
171 else
172 {
173 if (forkNumber != MAIN_FORKNUM)
174 path = psprintf("base/%u/t%d_%u_%s",
175 dbOid, procNumber, relNumber,
176 forkNames[forkNumber]);
177 else
178 path = psprintf("base/%u/t%d_%u",
179 dbOid, procNumber, relNumber);
180 }
181 }
182 else
183 {
184 /* All other tablespaces are accessed via symlinks */
185 if (procNumber == INVALID_PROC_NUMBER)
186 {
187 if (forkNumber != MAIN_FORKNUM)
188 path = psprintf("%s/%u/%s/%u/%u_%s",
189 PG_TBLSPC_DIR, spcOid,
191 dbOid, relNumber,
192 forkNames[forkNumber]);
193 else
194 path = psprintf("%s/%u/%s/%u/%u",
195 PG_TBLSPC_DIR, spcOid,
197 dbOid, relNumber);
198 }
199 else
200 {
201 if (forkNumber != MAIN_FORKNUM)
202 path = psprintf("%s/%u/%s/%u/t%d_%u_%s",
203 PG_TBLSPC_DIR, spcOid,
205 dbOid, procNumber, relNumber,
206 forkNames[forkNumber]);
207 else
208 path = psprintf("%s/%u/%s/%u/t%d_%u",
209 PG_TBLSPC_DIR, spcOid,
211 dbOid, procNumber, relNumber);
212 }
213 }
214 return path;
215}
#define Assert(condition)
Definition: c.h:815
#define lengthof(array)
Definition: c.h:745
int errhint(const char *fmt,...)
Definition: elog.c:1317
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
const char * str
char * pstrdup(const char *in)
Definition: mcxt.c:1696
const void size_t len
unsigned int Oid
Definition: postgres_ext.h:32
#define INVALID_PROC_NUMBER
Definition: procnumber.h:26
char * psprintf(const char *fmt,...)
Definition: psprintf.c:43
StaticAssertDecl(lengthof(forkNames)==(MAX_FORKNUM+1), "array length mismatch")
char * GetRelationPath(Oid dbOid, Oid spcOid, RelFileNumber relNumber, int procNumber, ForkNumber forkNumber)
Definition: relpath.c:142
int forkname_chars(const char *str, ForkNumber *fork)
Definition: relpath.c:81
ForkNumber forkname_to_number(const char *forkName)
Definition: relpath.c:50
const char *const forkNames[]
Definition: relpath.c:33
char * GetDatabasePath(Oid dbOid, Oid spcOid)
Definition: relpath.c:110
Oid RelFileNumber
Definition: relpath.h:25
ForkNumber
Definition: relpath.h:56
@ FSM_FORKNUM
Definition: relpath.h:59
@ VISIBILITYMAP_FORKNUM
Definition: relpath.h:60
@ MAIN_FORKNUM
Definition: relpath.h:58
@ InvalidForkNumber
Definition: relpath.h:57
@ INIT_FORKNUM
Definition: relpath.h:61
#define MAX_FORKNUM
Definition: relpath.h:70
#define PG_TBLSPC_DIR
Definition: relpath.h:41
#define TABLESPACE_VERSION_DIRECTORY
Definition: relpath.h:33