From 650d1054f61854967d1658f8f7f294a8c1e7deea Mon Sep 17 00:00:00 2001 From: Jacob Champion Date: Fri, 4 Sep 2026 13:11:53 -0700 Subject: [PATCH v2 1/3] Move relfile name parsing to src/common This will allow pg_checksums to use the same relfile parsing logic as the backend in a future commit. Discussion: https://postgr.es/m/CAOYmi%2B%3DE0zz%2B_MgQXAoOyn5C0vW53740izzRiTv6excqtCJfRg%40mail.gmail.com --- src/include/common/relpath.h | 5 ++ src/include/storage/reinit.h | 4 -- src/backend/storage/file/reinit.c | 86 ------------------------------- src/common/relpath.c | 86 +++++++++++++++++++++++++++++++ 4 files changed, 91 insertions(+), 90 deletions(-) diff --git a/src/include/common/relpath.h b/src/include/common/relpath.h index 9772125be73..8187cc8ad6b 100644 --- a/src/include/common/relpath.h +++ b/src/include/common/relpath.h @@ -133,6 +133,11 @@ extern char *GetDatabasePath(Oid dbOid, Oid spcOid); extern RelPathStr GetRelationPath(Oid dbOid, Oid spcOid, RelFileNumber relNumber, int procNumber, ForkNumber forkNumber); +extern bool parse_filename_for_nontemp_relation(const char *name, + RelFileNumber *relnumber, + ForkNumber *fork, + unsigned *segno); + /* * Wrapper macros for GetRelationPath. Beware of multiple * evaluation of the RelFileLocator or RelFileLocatorBackend argument! diff --git a/src/include/storage/reinit.h b/src/include/storage/reinit.h index a053742f695..efdb58ca77e 100644 --- a/src/include/storage/reinit.h +++ b/src/include/storage/reinit.h @@ -19,10 +19,6 @@ extern void ResetUnloggedRelations(int op); -extern bool parse_filename_for_nontemp_relation(const char *name, - RelFileNumber *relnumber, - ForkNumber *fork, - unsigned *segno); #define UNLOGGED_RELATION_CLEANUP 0x0001 #define UNLOGGED_RELATION_INIT 0x0002 diff --git a/src/backend/storage/file/reinit.c b/src/backend/storage/file/reinit.c index 25fa2151309..496b2974a78 100644 --- a/src/backend/storage/file/reinit.c +++ b/src/backend/storage/file/reinit.c @@ -365,89 +365,3 @@ ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname, int op) fsync_fname(dbspacedirname, true); } } - -/* - * Basic parsing of putative relation filenames. - * - * This function returns true if the file appears to be in the correct format - * for a non-temporary relation and false otherwise. - * - * If it returns true, it sets *relnumber, *fork, and *segno to the values - * extracted from the filename. If it returns false, these values are set to - * InvalidRelFileNumber, InvalidForkNumber, and 0, respectively. - */ -bool -parse_filename_for_nontemp_relation(const char *name, RelFileNumber *relnumber, - ForkNumber *fork, unsigned *segno) -{ - unsigned long n, - s; - ForkNumber f; - char *endp; - - *relnumber = InvalidRelFileNumber; - *fork = InvalidForkNumber; - *segno = 0; - - /* - * Relation filenames should begin with a digit that is not a zero. By - * rejecting cases involving leading zeroes, the caller can assume that - * there's only one possible string of characters that could have produced - * any given value for *relnumber. - * - * (To be clear, we don't expect files with names like 0017.3 to exist at - * all -- but if 0017.3 does exist, it's a non-relation file, not part of - * the main fork for relfilenode 17.) - */ - if (name[0] < '1' || name[0] > '9') - return false; - - /* - * Parse the leading digit string. If the value is out of range, we - * conclude that this isn't a relation file at all. - */ - errno = 0; - n = strtoul(name, &endp, 10); - if (errno || name == endp || n <= 0 || n > PG_UINT32_MAX) - return false; - name = endp; - - /* Check for a fork name. */ - if (*name != '_') - f = MAIN_FORKNUM; - else - { - int forkchar; - - forkchar = forkname_chars(name + 1, &f); - if (forkchar <= 0) - return false; - name += forkchar + 1; - } - - /* Check for a segment number. */ - if (*name != '.') - s = 0; - else - { - /* Reject leading zeroes, just like we do for RelFileNumber. */ - if (name[1] < '1' || name[1] > '9') - return false; - - errno = 0; - s = strtoul(name + 1, &endp, 10); - if (errno || name + 1 == endp || s <= 0 || s > PG_UINT32_MAX) - return false; - name = endp; - } - - /* Now we should be at the end. */ - if (*name != '\0') - return false; - - /* Set out parameters and return. */ - *relnumber = (RelFileNumber) n; - *fork = f; - *segno = (unsigned) s; - return true; -} diff --git a/src/common/relpath.c b/src/common/relpath.c index 8fb3bed7873..eb0c4aaf5e1 100644 --- a/src/common/relpath.c +++ b/src/common/relpath.c @@ -220,3 +220,89 @@ GetRelationPath(Oid dbOid, Oid spcOid, RelFileNumber relNumber, return rp; } + +/* + * Basic parsing of putative relation filenames. + * + * This function returns true if the file appears to be in the correct format + * for a non-temporary relation and false otherwise. + * + * If it returns true, it sets *relnumber, *fork, and *segno to the values + * extracted from the filename. If it returns false, these values are set to + * InvalidRelFileNumber, InvalidForkNumber, and 0, respectively. + */ +bool +parse_filename_for_nontemp_relation(const char *name, RelFileNumber *relnumber, + ForkNumber *fork, unsigned *segno) +{ + unsigned long n, + s; + ForkNumber f; + char *endp; + + *relnumber = InvalidRelFileNumber; + *fork = InvalidForkNumber; + *segno = 0; + + /* + * Relation filenames should begin with a digit that is not a zero. By + * rejecting cases involving leading zeroes, the caller can assume that + * there's only one possible string of characters that could have produced + * any given value for *relnumber. + * + * (To be clear, we don't expect files with names like 0017.3 to exist at + * all -- but if 0017.3 does exist, it's a non-relation file, not part of + * the main fork for relfilenode 17.) + */ + if (name[0] < '1' || name[0] > '9') + return false; + + /* + * Parse the leading digit string. If the value is out of range, we + * conclude that this isn't a relation file at all. + */ + errno = 0; + n = strtoul(name, &endp, 10); + if (errno || name == endp || n <= 0 || n > PG_UINT32_MAX) + return false; + name = endp; + + /* Check for a fork name. */ + if (*name != '_') + f = MAIN_FORKNUM; + else + { + int forkchar; + + forkchar = forkname_chars(name + 1, &f); + if (forkchar <= 0) + return false; + name += forkchar + 1; + } + + /* Check for a segment number. */ + if (*name != '.') + s = 0; + else + { + /* Reject leading zeroes, just like we do for RelFileNumber. */ + if (name[1] < '1' || name[1] > '9') + return false; + + errno = 0; + s = strtoul(name + 1, &endp, 10); + if (errno || name + 1 == endp || s <= 0 || s > PG_UINT32_MAX) + return false; + name = endp; + } + + /* Now we should be at the end. */ + if (*name != '\0') + return false; + + /* Set out parameters and return. */ + *relnumber = (RelFileNumber) n; + *fork = f; + *segno = (unsigned) s; + return true; +} -- 2.34.1