| /*------------------------------------------------------------------------- |
| * relpath.c |
| * Shared frontend/backend code to compute pathnames of relation files |
| * |
| * This module also contains some logic associated with fork names. |
| * |
| * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group |
| * Portions Copyright (c) 1994, Regents of the University of California |
| * |
| * IDENTIFICATION |
| * src/common/relpath.c |
| * |
| *------------------------------------------------------------------------- |
| */ |
| #ifndef FRONTEND |
| #include "postgres.h" |
| #else |
| #include "postgres_fe.h" |
| #endif |
| |
| #include "catalog/pg_tablespace_d.h" |
| #include "common/relpath.h" |
| #include "storage/backendid.h" |
| |
| |
| /* |
| * Lookup table of fork name by fork number. |
| * |
| * If you add a new entry, remember to update the errhint in |
| * forkname_to_number() below, and update the SGML documentation for |
| * pg_relation_size(). |
| */ |
| const char *const forkNames[] = { |
| "main", /* MAIN_FORKNUM */ |
| "fsm", /* FSM_FORKNUM */ |
| "vm", /* VISIBILITYMAP_FORKNUM */ |
| "init" /* INIT_FORKNUM */ |
| }; |
| |
| StaticAssertDecl(lengthof(forkNames) == (MAX_FORKNUM + 1), |
| "array length mismatch"); |
| |
| /* |
| * forkname_to_number - look up fork number by name |
| * |
| * In backend, we throw an error for no match; in frontend, we just |
| * return InvalidForkNumber. |
| */ |
| ForkNumber |
| forkname_to_number(const char *forkName) |
| { |
| ForkNumber forkNum; |
| |
| for (forkNum = 0; forkNum <= MAX_FORKNUM; forkNum++) |
| if (strcmp(forkNames[forkNum], forkName) == 0) |
| return forkNum; |
| |
| #ifndef FRONTEND |
| ereport(ERROR, |
| (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
| errmsg("invalid fork name"), |
| errhint("Valid fork names are \"main\", \"fsm\", " |
| "\"vm\", and \"init\"."))); |
| #endif |
| |
| return InvalidForkNumber; |
| } |
| |
| /* |
| * forkname_chars |
| * We use this to figure out whether a filename could be a relation |
| * fork (as opposed to an oddly named stray file that somehow ended |
| * up in the database directory). If the passed string begins with |
| * a fork name (other than the main fork name), we return its length, |
| * and set *fork (if not NULL) to the fork number. If not, we return 0. |
| * |
| * Note that the present coding assumes that there are no fork names which |
| * are prefixes of other fork names. |
| */ |
| int |
| forkname_chars(const char *str, ForkNumber *fork) |
| { |
| ForkNumber forkNum; |
| |
| for (forkNum = 1; forkNum <= MAX_FORKNUM; forkNum++) |
| { |
| int len = strlen(forkNames[forkNum]); |
| |
| if (strncmp(forkNames[forkNum], str, len) == 0) |
| { |
| if (fork) |
| *fork = forkNum; |
| return len; |
| } |
| } |
| if (fork) |
| *fork = InvalidForkNumber; |
| return 0; |
| } |
| |
| |
| /* |
| * GetDatabasePath - construct path to a database directory |
| * |
| * Result is a palloc'd string. |
| * |
| * XXX this must agree with GetRelationPath()! |
| */ |
| char * |
| GetDatabasePath(Oid dbOid, Oid spcOid) |
| { |
| if (spcOid == GLOBALTABLESPACE_OID) |
| { |
| /* Shared system relations live in {datadir}/global */ |
| Assert(dbOid == 0); |
| return pstrdup("global"); |
| } |
| else if (spcOid == DEFAULTTABLESPACE_OID) |
| { |
| /* The default tablespace is {datadir}/base */ |
| return psprintf("base/%u", dbOid); |
| } |
| else |
| { |
| /* All other tablespaces are accessed via symlinks */ |
| return psprintf("pg_tblspc/%u/%s/%u", |
| spcOid, GP_TABLESPACE_VERSION_DIRECTORY, dbOid); |
| } |
| } |
| |
| /* |
| * GetRelationPath - construct path to a relation's file |
| * |
| * Result is a palloc'd string. |
| * |
| * Note: ideally, backendId would be declared as type BackendId, but relpath.h |
| * would have to include a backend-only header to do that; doesn't seem worth |
| * the trouble considering BackendId is just int anyway. |
| * |
| * In PostgreSQL, the 'backendid' is embedded in the filename of temporary |
| * relations. In GPDB, however, temporary relations are just prefixed with |
| * "t_*", without the backend id. For compatibility with upstream code, this |
| * function still takes 'backendid' as argument, but we only care whether |
| * it's InvalidBackendId or not. If you need to construct the path of a |
| * temporary relation, but don't know the real backend ID, pass |
| * TempRelBackendId. |
| */ |
| char * |
| GetRelationPath(Oid dbOid, Oid spcOid, RelFileNumber relNumber, |
| int backendId, ForkNumber forkNumber) |
| { |
| char *path; |
| |
| if (spcOid == GLOBALTABLESPACE_OID) |
| { |
| /* Shared system relations live in {datadir}/global */ |
| Assert(dbOid == 0); |
| Assert(backendId == InvalidBackendId); |
| if (forkNumber != MAIN_FORKNUM) |
| path = psprintf("global/%u_%s", |
| relNumber, forkNames[forkNumber]); |
| else |
| path = psprintf("global/%u", relNumber); |
| } |
| else if (spcOid == DEFAULTTABLESPACE_OID) |
| { |
| /* The default tablespace is {datadir}/base */ |
| if (backendId == InvalidBackendId) |
| { |
| if (forkNumber != MAIN_FORKNUM) |
| path = psprintf("base/%u/%u_%s", |
| dbOid, relNumber, |
| forkNames[forkNumber]); |
| else |
| path = psprintf("base/%u/%u", |
| dbOid, relNumber); |
| } |
| else |
| { |
| if (forkNumber != MAIN_FORKNUM) |
| path = psprintf("base/%u/t_%u_%s", |
| dbOid, relNumber, |
| forkNames[forkNumber]); |
| else |
| path = psprintf("base/%u/t_%u", |
| dbOid, relNumber); |
| } |
| } |
| else |
| { |
| /* All other tablespaces are accessed via symlinks */ |
| if (backendId == InvalidBackendId) |
| { |
| if (forkNumber != MAIN_FORKNUM) |
| path = psprintf("pg_tblspc/%u/%s/%u/%u_%s", |
| spcOid, GP_TABLESPACE_VERSION_DIRECTORY, |
| dbOid, relNumber, |
| forkNames[forkNumber]); |
| else |
| path = psprintf("pg_tblspc/%u/%s/%u/%u", |
| spcOid, GP_TABLESPACE_VERSION_DIRECTORY, |
| dbOid, relNumber); |
| } |
| else |
| { |
| if (forkNumber != MAIN_FORKNUM) |
| path = psprintf("pg_tblspc/%u/%s/%u/t_%u_%s", |
| spcOid, GP_TABLESPACE_VERSION_DIRECTORY, |
| dbOid, relNumber, |
| forkNames[forkNumber]); |
| else |
| path = psprintf("pg_tblspc/%u/%s/%u/t_%u", |
| spcOid, GP_TABLESPACE_VERSION_DIRECTORY, |
| dbOid, relNumber); |
| } |
| } |
| return path; |
| } |