Coverage Report

Created: 2026-11-09 07:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}