From a7c872e45e04f0079a150b844b7827305de77d6c Mon Sep 17 00:00:00 2001 From: Shenhao Wang Date: Mon, 11 Apr 2022 18:12:34 +0800 Subject: [PATCH v2] Check the duplicated relfilenode when create table or truncate table. --- src/backend/catalog/catalog.c | 95 ++++++++++++++++++++++++++--------- 1 file changed, 71 insertions(+), 24 deletions(-) diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c index 520f77971b..4bf3a181e6 100644 --- a/src/backend/catalog/catalog.c +++ b/src/backend/catalog/catalog.c @@ -56,6 +56,8 @@ #define GETNEWOID_LOG_THRESHOLD 1000000 #define GETNEWOID_LOG_MAX_INTERVAL 128000000 +static bool IsCollidedRelNode(RelFileNodeBackend rnode, Relation relation); + /* * IsSystemRelation * True iff the relation is either a system catalog or a toast table. @@ -496,9 +498,8 @@ Oid GetNewRelFileNode(Oid reltablespace, Relation pg_class, char relpersistence) { RelFileNodeBackend rnode; - char *rpath; - bool collides; BackendId backend; + bool isCreateTable = (pg_class != NULL); /* * If we ever get here during pg_upgrade, there's something wrong; all @@ -532,39 +533,28 @@ GetNewRelFileNode(Oid reltablespace, Relation pg_class, char relpersistence) */ rnode.backend = backend; + /* + * We should check the new relfilenode is duplicated in pg_class, so we + * should open pg_class. + */ + if (!isCreateTable) + pg_class = table_open(RelationRelationId, AccessShareLock); + do { CHECK_FOR_INTERRUPTS(); /* Generate the OID */ - if (pg_class) + if (isCreateTable) rnode.node.relNode = GetNewOidWithIndex(pg_class, ClassOidIndexId, Anum_pg_class_oid); else rnode.node.relNode = GetNewObjectId(); - /* Check for existing file of same name */ - rpath = relpath(rnode, MAIN_FORKNUM); + } while (IsCollidedRelNode(rnode, pg_class)); - if (access(rpath, F_OK) == 0) - { - /* definite collision */ - collides = true; - } - else - { - /* - * Here we have a little bit of a dilemma: if errno is something - * other than ENOENT, should we declare a collision and loop? In - * practice it seems best to go ahead regardless of the errno. If - * there is a colliding file we will get an smgr failure when we - * attempt to create the new relation file. - */ - collides = false; - } - - pfree(rpath); - } while (collides); + if (!isCreateTable) + table_close(pg_class, AccessShareLock); return rnode.node.relNode; } @@ -672,3 +662,60 @@ pg_stop_making_pinned_objects(PG_FUNCTION_ARGS) PG_RETURN_VOID(); } + +/* + * IsCollidedRelNode + * Checking the rnode collides with existing one. + */ +static bool +IsCollidedRelNode(RelFileNodeBackend rnode, Relation pg_class) +{ + char *rpath; + bool collides; + + Assert(pg_class); + + /* Check for existing file of same name */ + rpath = relpath(rnode, MAIN_FORKNUM); + + if (access(rpath, F_OK) == 0) + { + /* definite collision */ + collides = true; + } + else + { + SysScanDesc scandesc; + ScanKeyData skey[2]; + + ScanKeyInit(&skey[0], + Anum_pg_class_reltablespace, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum( + rnode.node.spcNode == MyDatabaseTableSpace ? + InvalidOid : MyDatabaseTableSpace)); + ScanKeyInit(&skey[1], + Anum_pg_class_relfilenode, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(rnode.node.relNode)); + + /* See notes in GetNewOidWithIndex about using SnapshotAny */ + scandesc = systable_beginscan(pg_class, + ClassTblspcRelfilenodeIndexId, + true, SnapshotAny, 2, skey); + + /* + * Here we have a little bit of a dilemma: if errno is something + * other than ENOENT, should we declare a collision and loop? In + * practice it seems best to go ahead regardless of the errno. If + * there is a colliding file we will get an smgr failure when we + * attempt to create the new relation file. + */ + collides = HeapTupleIsValid(systable_getnext(scandesc)); + + systable_endscan(scandesc); + } + + pfree(rpath); + return collides; +} -- 2.32.0