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-2020, 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 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
forkname_to_number(const char * forkName)50 forkname_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
59 	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 	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
forkname_chars(const char * str,ForkNumber * fork)81 forkname_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  */
109 char *
GetDatabasePath(Oid dbNode,Oid spcNode)110 GetDatabasePath(Oid dbNode, Oid spcNode)
111 {
112 	if (spcNode == GLOBALTABLESPACE_OID)
113 	{
114 		/* Shared system relations live in {datadir}/global */
115 		Assert(dbNode == 0);
116 		return pstrdup("global");
117 	}
118 	else if (spcNode == DEFAULTTABLESPACE_OID)
119 	{
120 		/* The default tablespace is {datadir}/base */
121 		return psprintf("base/%u", dbNode);
122 	}
123 	else
124 	{
125 		/* All other tablespaces are accessed via symlinks */
126 		return psprintf("pg_tblspc/%u/%s/%u",
127 						spcNode, TABLESPACE_VERSION_DIRECTORY, dbNode);
128 	}
129 }
130 
131 /*
132  * GetRelationPath - construct path to a relation's file
133  *
134  * Result is a palloc'd string.
135  *
136  * Note: ideally, backendId would be declared as type BackendId, but relpath.h
137  * would have to include a backend-only header to do that; doesn't seem worth
138  * the trouble considering BackendId is just int anyway.
139  */
140 char *
GetRelationPath(Oid dbNode,Oid spcNode,Oid relNode,int backendId,ForkNumber forkNumber)141 GetRelationPath(Oid dbNode, Oid spcNode, Oid relNode,
142 				int backendId, ForkNumber forkNumber)
143 {
144 	char	   *path;
145 
146 	if (spcNode == GLOBALTABLESPACE_OID)
147 	{
148 		/* Shared system relations live in {datadir}/global */
149 		Assert(dbNode == 0);
150 		Assert(backendId == InvalidBackendId);
151 		if (forkNumber != MAIN_FORKNUM)
152 			path = psprintf("global/%u_%s",
153 							relNode, forkNames[forkNumber]);
154 		else
155 			path = psprintf("global/%u", relNode);
156 	}
157 	else if (spcNode == DEFAULTTABLESPACE_OID)
158 	{
159 		/* The default tablespace is {datadir}/base */
160 		if (backendId == InvalidBackendId)
161 		{
162 			if (forkNumber != MAIN_FORKNUM)
163 				path = psprintf("base/%u/%u_%s",
164 								dbNode, relNode,
165 								forkNames[forkNumber]);
166 			else
167 				path = psprintf("base/%u/%u",
168 								dbNode, relNode);
169 		}
170 		else
171 		{
172 			if (forkNumber != MAIN_FORKNUM)
173 				path = psprintf("base/%u/t%d_%u_%s",
174 								dbNode, backendId, relNode,
175 								forkNames[forkNumber]);
176 			else
177 				path = psprintf("base/%u/t%d_%u",
178 								dbNode, backendId, relNode);
179 		}
180 	}
181 	else
182 	{
183 		/* All other tablespaces are accessed via symlinks */
184 		if (backendId == InvalidBackendId)
185 		{
186 			if (forkNumber != MAIN_FORKNUM)
187 				path = psprintf("pg_tblspc/%u/%s/%u/%u_%s",
188 								spcNode, TABLESPACE_VERSION_DIRECTORY,
189 								dbNode, relNode,
190 								forkNames[forkNumber]);
191 			else
192 				path = psprintf("pg_tblspc/%u/%s/%u/%u",
193 								spcNode, TABLESPACE_VERSION_DIRECTORY,
194 								dbNode, relNode);
195 		}
196 		else
197 		{
198 			if (forkNumber != MAIN_FORKNUM)
199 				path = psprintf("pg_tblspc/%u/%s/%u/t%d_%u_%s",
200 								spcNode, TABLESPACE_VERSION_DIRECTORY,
201 								dbNode, backendId, relNode,
202 								forkNames[forkNumber]);
203 			else
204 				path = psprintf("pg_tblspc/%u/%s/%u/t%d_%u",
205 								spcNode, TABLESPACE_VERSION_DIRECTORY,
206 								dbNode, backendId, relNode);
207 		}
208 	}
209 	return path;
210 }
211