/src/postgres/src/common/relpath.c
Line | Count | Source |
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-2026, 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 |
50 | | forkname_to_number(const char *forkName) |
51 | 0 | { |
52 | 0 | ForkNumber forkNum; |
53 | |
|
54 | 0 | for (forkNum = 0; forkNum <= MAX_FORKNUM; forkNum++) |
55 | 0 | if (strcmp(forkNames[forkNum], forkName) == 0) |
56 | 0 | return forkNum; |
57 | | |
58 | 0 | #ifndef FRONTEND |
59 | 0 | ereport(ERROR, |
60 | 0 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
61 | 0 | errmsg("invalid fork name"), |
62 | 0 | errhint("Valid fork names are \"main\", \"fsm\", " |
63 | 0 | "\"vm\", and \"init\"."))); |
64 | 0 | #endif |
65 | | |
66 | 0 | return InvalidForkNumber; |
67 | 0 | } |
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 |
81 | | forkname_chars(const char *str, ForkNumber *fork) |
82 | 0 | { |
83 | 0 | ForkNumber forkNum; |
84 | |
|
85 | 0 | for (forkNum = 1; forkNum <= MAX_FORKNUM; forkNum++) |
86 | 0 | { |
87 | 0 | int len = strlen(forkNames[forkNum]); |
88 | |
|
89 | 0 | if (strncmp(forkNames[forkNum], str, len) == 0) |
90 | 0 | { |
91 | 0 | if (fork) |
92 | 0 | *fork = forkNum; |
93 | 0 | return len; |
94 | 0 | } |
95 | 0 | } |
96 | 0 | if (fork) |
97 | 0 | *fork = InvalidForkNumber; |
98 | 0 | return 0; |
99 | 0 | } |
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 * |
110 | | GetDatabasePath(Oid dbOid, Oid spcOid) |
111 | 0 | { |
112 | 0 | if (spcOid == GLOBALTABLESPACE_OID) |
113 | 0 | { |
114 | | /* Shared system relations live in {datadir}/global */ |
115 | 0 | Assert(dbOid == 0); |
116 | 0 | return pstrdup("global"); |
117 | 0 | } |
118 | 0 | else if (spcOid == DEFAULTTABLESPACE_OID) |
119 | 0 | { |
120 | | /* The default tablespace is {datadir}/base */ |
121 | 0 | return psprintf("base/%u", dbOid); |
122 | 0 | } |
123 | 0 | else |
124 | 0 | { |
125 | | /* All other tablespaces are accessed via symlinks */ |
126 | 0 | return psprintf("%s/%u/%s/%u", |
127 | 0 | PG_TBLSPC_DIR, spcOid, |
128 | 0 | TABLESPACE_VERSION_DIRECTORY, dbOid); |
129 | 0 | } |
130 | 0 | } |
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 |
143 | | GetRelationPath(Oid dbOid, Oid spcOid, RelFileNumber relNumber, |
144 | | int procNumber, ForkNumber forkNumber) |
145 | 0 | { |
146 | 0 | RelPathStr rp; |
147 | |
|
148 | 0 | if (spcOid == GLOBALTABLESPACE_OID) |
149 | 0 | { |
150 | | /* Shared system relations live in {datadir}/global */ |
151 | 0 | Assert(dbOid == 0); |
152 | 0 | Assert(procNumber == INVALID_PROC_NUMBER); |
153 | 0 | if (forkNumber != MAIN_FORKNUM) |
154 | 0 | sprintf(rp.str, "global/%u_%s", |
155 | 0 | relNumber, forkNames[forkNumber]); |
156 | 0 | else |
157 | 0 | sprintf(rp.str, "global/%u", |
158 | 0 | relNumber); |
159 | 0 | } |
160 | 0 | else if (spcOid == DEFAULTTABLESPACE_OID) |
161 | 0 | { |
162 | | /* The default tablespace is {datadir}/base */ |
163 | 0 | if (procNumber == INVALID_PROC_NUMBER) |
164 | 0 | { |
165 | 0 | if (forkNumber != MAIN_FORKNUM) |
166 | 0 | { |
167 | 0 | sprintf(rp.str, "base/%u/%u_%s", |
168 | 0 | dbOid, relNumber, |
169 | 0 | forkNames[forkNumber]); |
170 | 0 | } |
171 | 0 | else |
172 | 0 | sprintf(rp.str, "base/%u/%u", |
173 | 0 | dbOid, relNumber); |
174 | 0 | } |
175 | 0 | else |
176 | 0 | { |
177 | 0 | if (forkNumber != MAIN_FORKNUM) |
178 | 0 | sprintf(rp.str, "base/%u/t%d_%u_%s", |
179 | 0 | dbOid, procNumber, relNumber, |
180 | 0 | forkNames[forkNumber]); |
181 | 0 | else |
182 | 0 | sprintf(rp.str, "base/%u/t%d_%u", |
183 | 0 | dbOid, procNumber, relNumber); |
184 | 0 | } |
185 | 0 | } |
186 | 0 | else |
187 | 0 | { |
188 | | /* All other tablespaces are accessed via symlinks */ |
189 | 0 | if (procNumber == INVALID_PROC_NUMBER) |
190 | 0 | { |
191 | 0 | if (forkNumber != MAIN_FORKNUM) |
192 | 0 | sprintf(rp.str, "%s/%u/%s/%u/%u_%s", |
193 | 0 | PG_TBLSPC_DIR, spcOid, |
194 | 0 | TABLESPACE_VERSION_DIRECTORY, |
195 | 0 | dbOid, relNumber, |
196 | 0 | forkNames[forkNumber]); |
197 | 0 | else |
198 | 0 | sprintf(rp.str, "%s/%u/%s/%u/%u", |
199 | 0 | PG_TBLSPC_DIR, spcOid, |
200 | 0 | TABLESPACE_VERSION_DIRECTORY, |
201 | 0 | dbOid, relNumber); |
202 | 0 | } |
203 | 0 | else |
204 | 0 | { |
205 | 0 | if (forkNumber != MAIN_FORKNUM) |
206 | 0 | sprintf(rp.str, "%s/%u/%s/%u/t%d_%u_%s", |
207 | 0 | PG_TBLSPC_DIR, spcOid, |
208 | 0 | TABLESPACE_VERSION_DIRECTORY, |
209 | 0 | dbOid, procNumber, relNumber, |
210 | 0 | forkNames[forkNumber]); |
211 | 0 | else |
212 | 0 | sprintf(rp.str, "%s/%u/%s/%u/t%d_%u", |
213 | 0 | PG_TBLSPC_DIR, spcOid, |
214 | 0 | TABLESPACE_VERSION_DIRECTORY, |
215 | 0 | dbOid, procNumber, relNumber); |
216 | 0 | } |
217 | 0 | } |
218 | |
|
219 | 0 | Assert(strnlen(rp.str, REL_PATH_STR_MAXLEN + 1) <= REL_PATH_STR_MAXLEN); |
220 | |
|
221 | 0 | return rp; |
222 | 0 | } |