Age Owner Branch data TLA Line data Source code
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 : : */
33 : : const char *const forkNames[] = {
34 : : [MAIN_FORKNUM] = "main",
35 : : [FSM_FORKNUM] = "fsm",
36 : : [VISIBILITYMAP_FORKNUM] = "vm",
37 : : [INIT_FORKNUM] = "init",
38 : : };
39 : :
40 : : StaticAssertDecl(lengthof(forkNames) == (MAX_FORKNUM + 1),
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 : : */
49 : : ForkNumber
4147 tgl@sss.pgh.pa.us 50 :CBC 2869 : forkname_to_number(const char *forkName)
51 : : {
52 : : ForkNumber forkNum;
53 : :
54 [ + + ]: 2887 : for (forkNum = 0; forkNum <= MAX_FORKNUM; forkNum++)
55 [ + + ]: 2885 : if (strcmp(forkNames[forkNum], forkName) == 0)
56 : 2867 : return forkNum;
57 : :
58 : : #ifndef FRONTEND
59 [ + - ]: 1 : ereport(ERROR,
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 : 1 : 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 : : */
80 : : int
4580 alvherre@alvh.no-ip. 81 : 197085 : forkname_chars(const char *str, ForkNumber *fork)
82 : : {
83 : : ForkNumber forkNum;
84 : :
85 [ + - ]: 295553 : for (forkNum = 1; forkNum <= MAX_FORKNUM; forkNum++)
86 : : {
87 : 295553 : int len = strlen(forkNames[forkNum]);
88 : :
89 [ + + ]: 295553 : if (strncmp(forkNames[forkNum], str, len) == 0)
90 : : {
91 [ + + ]: 197085 : if (fork)
92 : 197065 : *fork = forkNum;
93 : 197085 : return len;
94 : : }
95 : : }
4147 tgl@sss.pgh.pa.us 96 [ # # ]:UBC 0 : if (fork)
97 : 0 : *fork = InvalidForkNumber;
4580 alvherre@alvh.no-ip. 98 : 0 : 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 : : */
109 : : char *
1158 rhaas@postgresql.org 110 :CBC 168602 : GetDatabasePath(Oid dbOid, Oid spcOid)
111 : : {
112 [ + + ]: 168602 : if (spcOid == GLOBALTABLESPACE_OID)
113 : : {
114 : : /* Shared system relations live in {datadir}/global */
115 [ - + ]: 2 : Assert(dbOid == 0);
4147 tgl@sss.pgh.pa.us 116 : 2 : return pstrdup("global");
117 : : }
1158 rhaas@postgresql.org 118 [ + + ]: 168600 : else if (spcOid == DEFAULTTABLESPACE_OID)
119 : : {
120 : : /* The default tablespace is {datadir}/base */
121 : 165559 : return psprintf("base/%u", dbOid);
122 : : }
123 : : else
124 : : {
125 : : /* All other tablespaces are accessed via symlinks */
368 michael@paquier.xyz 126 : 3041 : return psprintf("%s/%u/%s/%u",
127 : : PG_TBLSPC_DIR, spcOid,
128 : : TABLESPACE_VERSION_DIRECTORY, dbOid);
129 : : }
130 : : }
131 : :
132 : : /*
133 : : * GetRelationPath - construct path to a relation's file
134 : : *
135 : : * The result is returned in-place as a struct, to make it suitable for use in
136 : : * critical sections etc.
137 : : *
138 : : * Note: ideally, procNumber would be declared as type ProcNumber, but
139 : : * relpath.h would have to include a backend-only header to do that; doesn't
140 : : * seem worth the trouble considering ProcNumber is just int anyway.
141 : : */
142 : : RelPathStr
1158 rhaas@postgresql.org 143 : 1631274 : GetRelationPath(Oid dbOid, Oid spcOid, RelFileNumber relNumber,
144 : : int procNumber, ForkNumber forkNumber)
145 : : {
146 : : RelPathStr rp;
147 : :
148 [ + + ]: 1631274 : if (spcOid == GLOBALTABLESPACE_OID)
149 : : {
150 : : /* Shared system relations live in {datadir}/global */
151 [ - + ]: 64343 : Assert(dbOid == 0);
552 heikki.linnakangas@i 152 [ - + ]: 64343 : Assert(procNumber == INVALID_PROC_NUMBER);
4147 tgl@sss.pgh.pa.us 153 [ + + ]: 64343 : if (forkNumber != MAIN_FORKNUM)
193 andres@anarazel.de 154 : 11816 : sprintf(rp.str, "global/%u_%s",
155 : 11816 : relNumber, forkNames[forkNumber]);
156 : : else
157 : 52527 : sprintf(rp.str, "global/%u",
158 : : relNumber);
159 : : }
1158 rhaas@postgresql.org 160 [ + + ]: 1566931 : else if (spcOid == DEFAULTTABLESPACE_OID)
161 : : {
162 : : /* The default tablespace is {datadir}/base */
552 heikki.linnakangas@i 163 [ + + ]: 1552089 : if (procNumber == INVALID_PROC_NUMBER)
164 : : {
4147 tgl@sss.pgh.pa.us 165 [ + + ]: 1522218 : if (forkNumber != MAIN_FORKNUM)
166 : : {
193 andres@anarazel.de 167 : 595435 : sprintf(rp.str, "base/%u/%u_%s",
168 : : dbOid, relNumber,
169 : 595435 : forkNames[forkNumber]);
170 : : }
171 : : else
172 : 926783 : sprintf(rp.str, "base/%u/%u",
173 : : dbOid, relNumber);
174 : : }
175 : : else
176 : : {
4147 tgl@sss.pgh.pa.us 177 [ + + ]: 29871 : if (forkNumber != MAIN_FORKNUM)
193 andres@anarazel.de 178 : 15666 : sprintf(rp.str, "base/%u/t%d_%u_%s",
179 : : dbOid, procNumber, relNumber,
180 : 15666 : forkNames[forkNumber]);
181 : : else
182 : 14205 : sprintf(rp.str, "base/%u/t%d_%u",
183 : : dbOid, procNumber, relNumber);
184 : : }
185 : : }
186 : : else
187 : : {
188 : : /* All other tablespaces are accessed via symlinks */
552 heikki.linnakangas@i 189 [ + + ]: 14842 : if (procNumber == INVALID_PROC_NUMBER)
190 : : {
4147 tgl@sss.pgh.pa.us 191 [ + + ]: 14782 : if (forkNumber != MAIN_FORKNUM)
193 andres@anarazel.de 192 : 6653 : sprintf(rp.str, "%s/%u/%s/%u/%u_%s",
193 : : PG_TBLSPC_DIR, spcOid,
194 : : TABLESPACE_VERSION_DIRECTORY,
195 : : dbOid, relNumber,
196 : 6653 : forkNames[forkNumber]);
197 : : else
198 : 8129 : sprintf(rp.str, "%s/%u/%s/%u/%u",
199 : : PG_TBLSPC_DIR, spcOid,
200 : : TABLESPACE_VERSION_DIRECTORY,
201 : : dbOid, relNumber);
202 : : }
203 : : else
204 : : {
4147 tgl@sss.pgh.pa.us 205 [ + + ]: 60 : if (forkNumber != MAIN_FORKNUM)
193 andres@anarazel.de 206 : 33 : sprintf(rp.str, "%s/%u/%s/%u/t%d_%u_%s",
207 : : PG_TBLSPC_DIR, spcOid,
208 : : TABLESPACE_VERSION_DIRECTORY,
209 : : dbOid, procNumber, relNumber,
210 : 33 : forkNames[forkNumber]);
211 : : else
212 : 27 : sprintf(rp.str, "%s/%u/%s/%u/t%d_%u",
213 : : PG_TBLSPC_DIR, spcOid,
214 : : TABLESPACE_VERSION_DIRECTORY,
215 : : dbOid, procNumber, relNumber);
216 : : }
217 : : }
218 : :
219 [ - + ]: 1631274 : Assert(strnlen(rp.str, REL_PATH_STR_MAXLEN + 1) <= REL_PATH_STR_MAXLEN);
220 : :
221 : 1631274 : return rp;
222 : : }
|