diff --git a/src/backend/storage/file/reinit.c b/src/backend/storage/file/reinit.c index 92363ae6ad..73a25fa3e7 100644 --- a/src/backend/storage/file/reinit.c +++ b/src/backend/storage/file/reinit.c @@ -21,7 +21,6 @@ #include "storage/copydir.h" #include "storage/fd.h" #include "storage/reinit.h" -#include "utils/hsearch.h" #include "utils/memutils.h" static void ResetUnloggedRelationsInTablespaceDir(const char *tsdirname, @@ -145,6 +144,100 @@ ResetUnloggedRelationsInTablespaceDir(const char *tsdirname, int op) FreeDir(ts_dir); } +/* + * Find all unlogged relations in the specified directory and return their OIDs + * as a hash table. + * + * It's possible that someone could create a ton of unlogged relations in the + * same database & tablespace, so we'd better use a hash table rather than an + * array or linked list to keep track of which files need to be reset. + * Otherwise, search operations would be O(n^2). + */ +HTAB * +ResetUnloggedRelationsHash(const char *dbspacedirname) +{ + DIR *dbspace_dir; + struct dirent *de; + HTAB *hash = NULL; + + /* Scan the directory. */ + dbspace_dir = AllocateDir(dbspacedirname); + while ((de = ReadDir(dbspace_dir, dbspacedirname)) != NULL) + { + ForkNumber forkNum; + int oidchars; + unlogged_relation_entry ent; + + /* Skip anything that doesn't look like a relation data file. */ + if (!parse_filename_for_nontemp_relation(de->d_name, &oidchars, + &forkNum)) + continue; + + /* Also skip it unless this is the init fork. */ + if (forkNum != INIT_FORKNUM) + continue; + + /* Create the hash table if it has not been created already. */ + if (!hash) + { + HASHCTL ctl; + + memset(&ctl, 0, sizeof(ctl)); + ctl.keysize = sizeof(unlogged_relation_entry); + ctl.entrysize = sizeof(unlogged_relation_entry); + hash = hash_create("unlogged hash", 32, &ctl, HASH_ELEM); + } + + /* + * Put the OID portion of the name into the hash table, if it + * isn't already. + */ + memset(ent.oid, 0, sizeof(ent.oid)); + memcpy(ent.oid, de->d_name, oidchars); + hash_search(hash, &ent, HASH_ENTER, NULL); + } + + /* Done with the first pass. */ + FreeDir(dbspace_dir); + + return hash; +} + +/* + * Determine whether the specified file is an unlogged relation fork. + * + * If not an unlogged relation then return notUnlogged, otherwise return + * unloggedInit if an unlogged init fork and unloggedOther if any other unlogged + * fork. + */ +UnloggedRelationFork +ResetUnloggedRelationsMatch(HTAB *hash, const char *file) +{ + ForkNumber forkNum; + int oidchars; + bool found; + unlogged_relation_entry ent; + + /* If it's not a relation then it's not unlogged. */ + if (!parse_filename_for_nontemp_relation(file, &oidchars, &forkNum)) + return notUnlogged; + + /* An unlogged init fork. */ + if (forkNum == INIT_FORKNUM) + return unloggedInit; + + /* See whether the OID portion of the name shows up in the hash table. */ + memset(ent.oid, 0, sizeof(ent.oid)); + memcpy(ent.oid, file, oidchars); + hash_search(hash, &ent, HASH_FIND, &found); + + /* If found this is another fork of an unlogged table (but not init). */ + if (found) + return unloggedOther; + + return notUnlogged; +} + /* * Process one per-dbspace directory for ResetUnloggedRelations */ @@ -166,58 +259,13 @@ ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname, int op) if ((op & UNLOGGED_RELATION_CLEANUP) != 0) { HTAB *hash; - HASHCTL ctl; - - /* - * It's possible that someone could create a ton of unlogged relations - * in the same database & tablespace, so we'd better use a hash table - * rather than an array or linked list to keep track of which files - * need to be reset. Otherwise, this cleanup operation would be - * O(n^2). - */ - memset(&ctl, 0, sizeof(ctl)); - ctl.keysize = sizeof(unlogged_relation_entry); - ctl.entrysize = sizeof(unlogged_relation_entry); - hash = hash_create("unlogged hash", 32, &ctl, HASH_ELEM); - - /* Scan the directory. */ - dbspace_dir = AllocateDir(dbspacedirname); - while ((de = ReadDir(dbspace_dir, dbspacedirname)) != NULL) - { - ForkNumber forkNum; - int oidchars; - unlogged_relation_entry ent; - - /* Skip anything that doesn't look like a relation data file. */ - if (!parse_filename_for_nontemp_relation(de->d_name, &oidchars, - &forkNum)) - continue; - - /* Also skip it unless this is the init fork. */ - if (forkNum != INIT_FORKNUM) - continue; - /* - * Put the OID portion of the name into the hash table, if it - * isn't already. - */ - memset(ent.oid, 0, sizeof(ent.oid)); - memcpy(ent.oid, de->d_name, oidchars); - hash_search(hash, &ent, HASH_ENTER, NULL); - } - - /* Done with the first pass. */ - FreeDir(dbspace_dir); + /* Build a hash table of all unlogged relations. */ + hash = ResetUnloggedRelationsHash(dbspacedirname); - /* - * If we didn't find any init forks, there's no point in continuing; - * we can bail out now. - */ - if (hash_get_num_entries(hash) == 0) - { - hash_destroy(hash); + /* No need to continue if there are no unlogged tables. */ + if (!hash) return; - } /* * Now, make a second pass and remove anything that matches. @@ -225,30 +273,9 @@ ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname, int op) dbspace_dir = AllocateDir(dbspacedirname); while ((de = ReadDir(dbspace_dir, dbspacedirname)) != NULL) { - ForkNumber forkNum; - int oidchars; - bool found; - unlogged_relation_entry ent; - - /* Skip anything that doesn't look like a relation data file. */ - if (!parse_filename_for_nontemp_relation(de->d_name, &oidchars, - &forkNum)) - continue; - - /* We never remove the init fork. */ - if (forkNum == INIT_FORKNUM) - continue; - - /* - * See whether the OID portion of the name shows up in the hash - * table. - */ - memset(ent.oid, 0, sizeof(ent.oid)); - memcpy(ent.oid, de->d_name, oidchars); - hash_search(hash, &ent, HASH_FIND, &found); - - /* If so, nuke it! */ - if (found) + /* If this is an unlogged relation fork other than init, nuke it! */ + if (ResetUnloggedRelationsMatch( + hash, de->d_name) == unloggedOther) { snprintf(rm_path, sizeof(rm_path), "%s/%s", dbspacedirname, de->d_name); diff --git a/src/include/storage/reinit.h b/src/include/storage/reinit.h index addda2c0ea..cd422e519a 100644 --- a/src/include/storage/reinit.h +++ b/src/include/storage/reinit.h @@ -15,9 +15,24 @@ #ifndef REINIT_H #define REINIT_H +#include "utils/hsearch.h" + extern void ResetUnloggedRelations(int op); #define UNLOGGED_RELATION_CLEANUP 0x0001 #define UNLOGGED_RELATION_INIT 0x0002 +/* Return values for ResetUnloggedRelationsMatch(). */ +typedef enum +{ + notUnlogged, /* Not a relation or not an unlogged relation. */ + unloggedInit, /* An unlogged relation init fork. */ + unloggedOther /* An unlogged relation fork other than init. */ +} UnloggedRelationFork; + +/* Utility functions for identifying unlogged table forks. */ +extern HTAB *ResetUnloggedRelationsHash(const char *dbspacedirname); +extern UnloggedRelationFork ResetUnloggedRelationsMatch( + HTAB *unloggedHash, const char *fileName); + #endif /* REINIT_H */