LCOV - code coverage report
Current view: top level - src/common - relpath.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 40 43 93.0 %
Date: 2025-04-01 14:15:22 Functions: 4 4 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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-2025, 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        5766 : forkname_to_number(const char *forkName)
      51             : {
      52             :     ForkNumber  forkNum;
      53             : 
      54        5802 :     for (forkNum = 0; forkNum <= MAX_FORKNUM; forkNum++)
      55        5798 :         if (strcmp(forkNames[forkNum], forkName) == 0)
      56        5762 :             return forkNum;
      57             : 
      58             : #ifndef FRONTEND
      59           2 :     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           2 :     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
      81      394866 : forkname_chars(const char *str, ForkNumber *fork)
      82             : {
      83             :     ForkNumber  forkNum;
      84             : 
      85      592154 :     for (forkNum = 1; forkNum <= MAX_FORKNUM; forkNum++)
      86             :     {
      87      592154 :         int         len = strlen(forkNames[forkNum]);
      88             : 
      89      592154 :         if (strncmp(forkNames[forkNum], str, len) == 0)
      90             :         {
      91      394866 :             if (fork)
      92      394826 :                 *fork = forkNum;
      93      394866 :             return len;
      94             :         }
      95             :     }
      96           0 :     if (fork)
      97           0 :         *fork = InvalidForkNumber;
      98           0 :     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 *
     110      333752 : GetDatabasePath(Oid dbOid, Oid spcOid)
     111             : {
     112      333752 :     if (spcOid == GLOBALTABLESPACE_OID)
     113             :     {
     114             :         /* Shared system relations live in {datadir}/global */
     115             :         Assert(dbOid == 0);
     116           4 :         return pstrdup("global");
     117             :     }
     118      333748 :     else if (spcOid == DEFAULTTABLESPACE_OID)
     119             :     {
     120             :         /* The default tablespace is {datadir}/base */
     121      330918 :         return psprintf("base/%u", dbOid);
     122             :     }
     123             :     else
     124             :     {
     125             :         /* All other tablespaces are accessed via symlinks */
     126        2830 :         return psprintf("%s/%u/%s/%u",
     127             :                         PG_TBLSPC_DIR, spcOid,
     128             :                         TABLESPACE_VERSION_DIRECTORY, dbOid);
     129             :     }
     130             : }
     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     3377126 : GetRelationPath(Oid dbOid, Oid spcOid, RelFileNumber relNumber,
     144             :                 int procNumber, ForkNumber forkNumber)
     145             : {
     146             :     RelPathStr  rp;
     147             : 
     148     3377126 :     if (spcOid == GLOBALTABLESPACE_OID)
     149             :     {
     150             :         /* Shared system relations live in {datadir}/global */
     151             :         Assert(dbOid == 0);
     152             :         Assert(procNumber == INVALID_PROC_NUMBER);
     153      164742 :         if (forkNumber != MAIN_FORKNUM)
     154       42918 :             sprintf(rp.str, "global/%u_%s",
     155             :                     relNumber, forkNames[forkNumber]);
     156             :         else
     157      121824 :             sprintf(rp.str, "global/%u",
     158             :                     relNumber);
     159             :     }
     160     3212384 :     else if (spcOid == DEFAULTTABLESPACE_OID)
     161             :     {
     162             :         /* The default tablespace is {datadir}/base */
     163     3202098 :         if (procNumber == INVALID_PROC_NUMBER)
     164             :         {
     165     3145218 :             if (forkNumber != MAIN_FORKNUM)
     166             :             {
     167     1265990 :                 sprintf(rp.str, "base/%u/%u_%s",
     168             :                         dbOid, relNumber,
     169             :                         forkNames[forkNumber]);
     170             :             }
     171             :             else
     172     1879228 :                 sprintf(rp.str, "base/%u/%u",
     173             :                         dbOid, relNumber);
     174             :         }
     175             :         else
     176             :         {
     177       56880 :             if (forkNumber != MAIN_FORKNUM)
     178       29708 :                 sprintf(rp.str, "base/%u/t%d_%u_%s",
     179             :                         dbOid, procNumber, relNumber,
     180             :                         forkNames[forkNumber]);
     181             :             else
     182       27172 :                 sprintf(rp.str, "base/%u/t%d_%u",
     183             :                         dbOid, procNumber, relNumber);
     184             :         }
     185             :     }
     186             :     else
     187             :     {
     188             :         /* All other tablespaces are accessed via symlinks */
     189       10286 :         if (procNumber == INVALID_PROC_NUMBER)
     190             :         {
     191       10166 :             if (forkNumber != MAIN_FORKNUM)
     192        4480 :                 sprintf(rp.str, "%s/%u/%s/%u/%u_%s",
     193             :                         PG_TBLSPC_DIR, spcOid,
     194             :                         TABLESPACE_VERSION_DIRECTORY,
     195             :                         dbOid, relNumber,
     196             :                         forkNames[forkNumber]);
     197             :             else
     198        5686 :                 sprintf(rp.str, "%s/%u/%s/%u/%u",
     199             :                         PG_TBLSPC_DIR, spcOid,
     200             :                         TABLESPACE_VERSION_DIRECTORY,
     201             :                         dbOid, relNumber);
     202             :         }
     203             :         else
     204             :         {
     205         120 :             if (forkNumber != MAIN_FORKNUM)
     206          66 :                 sprintf(rp.str, "%s/%u/%s/%u/t%d_%u_%s",
     207             :                         PG_TBLSPC_DIR, spcOid,
     208             :                         TABLESPACE_VERSION_DIRECTORY,
     209             :                         dbOid, procNumber, relNumber,
     210             :                         forkNames[forkNumber]);
     211             :             else
     212          54 :                 sprintf(rp.str, "%s/%u/%s/%u/t%d_%u",
     213             :                         PG_TBLSPC_DIR, spcOid,
     214             :                         TABLESPACE_VERSION_DIRECTORY,
     215             :                         dbOid, procNumber, relNumber);
     216             :         }
     217             :     }
     218             : 
     219             :     Assert(strnlen(rp.str, REL_PATH_STR_MAXLEN + 1) <= REL_PATH_STR_MAXLEN);
     220             : 
     221     3377126 :     return rp;
     222             : }

Generated by: LCOV version 1.14