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 */
33const 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 */
46ForkNumber
47forkname_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 */
77int
78forkname_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 */
106char *
107GetDatabasePath(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 */
137char *
138GetRelationPath(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