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-2019, 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/backendid.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" , /* MAIN_FORKNUM */ |
35 | "fsm" , /* FSM_FORKNUM */ |
36 | "vm" , /* VISIBILITYMAP_FORKNUM */ |
37 | "init" /* INIT_FORKNUM */ |
38 | }; |
39 | |
40 | /* |
41 | * forkname_to_number - look up fork number by name |
42 | * |
43 | * In backend, we throw an error for no match; in frontend, we just |
44 | * return InvalidForkNumber. |
45 | */ |
46 | ForkNumber |
47 | forkname_to_number(const char *forkName) |
48 | { |
49 | ForkNumber forkNum; |
50 | |
51 | for (forkNum = 0; forkNum <= MAX_FORKNUM; forkNum++) |
52 | if (strcmp(forkNames[forkNum], forkName) == 0) |
53 | return forkNum; |
54 | |
55 | #ifndef FRONTEND |
56 | ereport(ERROR, |
57 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
58 | errmsg("invalid fork name" ), |
59 | errhint("Valid fork names are \"main\", \"fsm\", " |
60 | "\"vm\", and \"init\"." ))); |
61 | #endif |
62 | |
63 | return InvalidForkNumber; |
64 | } |
65 | |
66 | /* |
67 | * forkname_chars |
68 | * We use this to figure out whether a filename could be a relation |
69 | * fork (as opposed to an oddly named stray file that somehow ended |
70 | * up in the database directory). If the passed string begins with |
71 | * a fork name (other than the main fork name), we return its length, |
72 | * and set *fork (if not NULL) to the fork number. If not, we return 0. |
73 | * |
74 | * Note that the present coding assumes that there are no fork names which |
75 | * are prefixes of other fork names. |
76 | */ |
77 | int |
78 | forkname_chars(const char *str, ForkNumber *fork) |
79 | { |
80 | ForkNumber forkNum; |
81 | |
82 | for (forkNum = 1; forkNum <= MAX_FORKNUM; forkNum++) |
83 | { |
84 | int len = strlen(forkNames[forkNum]); |
85 | |
86 | if (strncmp(forkNames[forkNum], str, len) == 0) |
87 | { |
88 | if (fork) |
89 | *fork = forkNum; |
90 | return len; |
91 | } |
92 | } |
93 | if (fork) |
94 | *fork = InvalidForkNumber; |
95 | return 0; |
96 | } |
97 | |
98 | |
99 | /* |
100 | * GetDatabasePath - construct path to a database directory |
101 | * |
102 | * Result is a palloc'd string. |
103 | * |
104 | * XXX this must agree with GetRelationPath()! |
105 | */ |
106 | char * |
107 | GetDatabasePath(Oid dbNode, Oid spcNode) |
108 | { |
109 | if (spcNode == GLOBALTABLESPACE_OID) |
110 | { |
111 | /* Shared system relations live in {datadir}/global */ |
112 | Assert(dbNode == 0); |
113 | return pstrdup("global" ); |
114 | } |
115 | else if (spcNode == DEFAULTTABLESPACE_OID) |
116 | { |
117 | /* The default tablespace is {datadir}/base */ |
118 | return psprintf("base/%u" , dbNode); |
119 | } |
120 | else |
121 | { |
122 | /* All other tablespaces are accessed via symlinks */ |
123 | return psprintf("pg_tblspc/%u/%s/%u" , |
124 | spcNode, TABLESPACE_VERSION_DIRECTORY, dbNode); |
125 | } |
126 | } |
127 | |
128 | /* |
129 | * GetRelationPath - construct path to a relation's file |
130 | * |
131 | * Result is a palloc'd string. |
132 | * |
133 | * Note: ideally, backendId would be declared as type BackendId, but relpath.h |
134 | * would have to include a backend-only header to do that; doesn't seem worth |
135 | * the trouble considering BackendId is just int anyway. |
136 | */ |
137 | char * |
138 | GetRelationPath(Oid dbNode, Oid spcNode, Oid relNode, |
139 | int backendId, ForkNumber forkNumber) |
140 | { |
141 | char *path; |
142 | |
143 | if (spcNode == GLOBALTABLESPACE_OID) |
144 | { |
145 | /* Shared system relations live in {datadir}/global */ |
146 | Assert(dbNode == 0); |
147 | Assert(backendId == InvalidBackendId); |
148 | if (forkNumber != MAIN_FORKNUM) |
149 | path = psprintf("global/%u_%s" , |
150 | relNode, forkNames[forkNumber]); |
151 | else |
152 | path = psprintf("global/%u" , relNode); |
153 | } |
154 | else if (spcNode == DEFAULTTABLESPACE_OID) |
155 | { |
156 | /* The default tablespace is {datadir}/base */ |
157 | if (backendId == InvalidBackendId) |
158 | { |
159 | if (forkNumber != MAIN_FORKNUM) |
160 | path = psprintf("base/%u/%u_%s" , |
161 | dbNode, relNode, |
162 | forkNames[forkNumber]); |
163 | else |
164 | path = psprintf("base/%u/%u" , |
165 | dbNode, relNode); |
166 | } |
167 | else |
168 | { |
169 | if (forkNumber != MAIN_FORKNUM) |
170 | path = psprintf("base/%u/t%d_%u_%s" , |
171 | dbNode, backendId, relNode, |
172 | forkNames[forkNumber]); |
173 | else |
174 | path = psprintf("base/%u/t%d_%u" , |
175 | dbNode, backendId, relNode); |
176 | } |
177 | } |
178 | else |
179 | { |
180 | /* All other tablespaces are accessed via symlinks */ |
181 | if (backendId == InvalidBackendId) |
182 | { |
183 | if (forkNumber != MAIN_FORKNUM) |
184 | path = psprintf("pg_tblspc/%u/%s/%u/%u_%s" , |
185 | spcNode, TABLESPACE_VERSION_DIRECTORY, |
186 | dbNode, relNode, |
187 | forkNames[forkNumber]); |
188 | else |
189 | path = psprintf("pg_tblspc/%u/%s/%u/%u" , |
190 | spcNode, TABLESPACE_VERSION_DIRECTORY, |
191 | dbNode, relNode); |
192 | } |
193 | else |
194 | { |
195 | if (forkNumber != MAIN_FORKNUM) |
196 | path = psprintf("pg_tblspc/%u/%s/%u/t%d_%u_%s" , |
197 | spcNode, TABLESPACE_VERSION_DIRECTORY, |
198 | dbNode, backendId, relNode, |
199 | forkNames[forkNumber]); |
200 | else |
201 | path = psprintf("pg_tblspc/%u/%s/%u/t%d_%u" , |
202 | spcNode, TABLESPACE_VERSION_DIRECTORY, |
203 | dbNode, backendId, relNode); |
204 | } |
205 | } |
206 | return path; |
207 | } |
208 | |