/* * Can the given OID be used as pg_class.relfilenode? * * As a side-effect, advances OID counter to the given OID and remembers * that the OID has been used as a relfilenode, so that the same value * doesn't get chosen again. */ bool CheckNewRelFileNodeIsOk(Oid newOid, Oid reltablespace, bool relisshared) { RelFileNode rnode; char *rpath; int fd; bool collides; SnapshotData SnapshotDirty; /* * Advance our current OID counter with the given value, to keep * the counter roughly in sync across all nodes. This ensures * that a GetNewRelFileNode() call after this will not choose the * same OID, and won't have to loop excessively to retry. That * still leaves a race condition, if GetNewRelFileNode() is called * just before CheckNewRelFileNodeIsOk() - UseOidForRelFileNode() * is called to plug that. * * FIXME: handle OID wraparound gracefully. */ while(GetNewObjectId() < newOid); if (!UseOidForRelFileNode(newOid)) return false; InitDirtySnapshot(SnapshotDirty); /* This should match RelationInitPhysicalAddr */ rnode.spcNode = reltablespace ? reltablespace : MyDatabaseTableSpace; rnode.dbNode = relisshared ? InvalidOid : MyDatabaseId; rnode.relNode = newOid; /* Check for existing file of same name */ rpath = relpath(rnode); fd = BasicOpenFile(rpath, O_RDONLY | PG_BINARY, 0); if (fd >= 0) { /* definite collision */ gp_retry_close(fd); collides = true; } else collides = false; pfree(rpath); elog(DEBUG1, "Called CheckNewRelFileNodeIsOk in %s mode for %u / %u / %u. " "collides = %s", (Gp_role == GP_ROLE_EXECUTE ? "execute" : Gp_role == GP_ROLE_UTILITY ? "utility" : "dispatch"), newOid, reltablespace, relisshared, collides ? "true" : "false"); return !collides; }
static void shareinput_clean_lk_ctxt(ShareInput_Lk_Context *lk_ctxt) { elog(DEBUG1, "shareinput_clean_lk_ctxt cleanup lk ctxt %p", lk_ctxt); if (!lk_ctxt) return; if (lk_ctxt->readyfd >= 0) { if (gp_retry_close(lk_ctxt->readyfd)) ereport(WARNING, (errcode(ERRCODE_IO_ERROR), errmsg("shareinput_clean_lk_ctxt cannot close readyfd: %m"))); } if (lk_ctxt->donefd >= 0) { if (gp_retry_close(lk_ctxt->donefd)) ereport(WARNING, (errcode(ERRCODE_IO_ERROR), errmsg("shareinput_clean_lk_ctxt cannot close donefd: %m"))); } if (lk_ctxt->del_ready && lk_ctxt->lkname_ready[0]) { if (unlink(lk_ctxt->lkname_ready)) ereport(WARNING, (errcode(ERRCODE_IO_ERROR), errmsg("shareinput_clean_lk_ctxt cannot unlink \"%s\": %m", lk_ctxt->lkname_ready))); } if (lk_ctxt->del_done && lk_ctxt->lkname_done[0]) { if (unlink(lk_ctxt->lkname_done)) ereport(WARNING, (errcode(ERRCODE_IO_ERROR), errmsg("shareinput_clean_lk_ctxt cannot unlink \"%s\": %m", lk_ctxt->lkname_done))); } gp_free(lk_ctxt); }
/* * GetNewRelFileNode * Generate a new relfilenode number that is unique within the given * tablespace. * * Note: we don't support using this in bootstrap mode. All relations * created by bootstrap have preassigned OIDs, so there's no need. */ Oid GetNewRelFileNode(Oid reltablespace, bool relisshared) { RelFileNode rnode; char *rpath; int fd; bool collides = true; /* This should match RelationInitPhysicalAddr */ rnode.spcNode = reltablespace ? reltablespace : MyDatabaseTableSpace; rnode.dbNode = relisshared ? InvalidOid : MyDatabaseId; do { CHECK_FOR_INTERRUPTS(); /* Generate the Relfilenode */ rnode.relNode = GetNewSegRelfilenode(); if (!IsOidAcceptable(rnode.relNode)) continue; /* Check for existing file of same name */ rpath = relpath(rnode); fd = BasicOpenFile(rpath, O_RDONLY | PG_BINARY, 0); if (fd >= 0) { /* definite collision */ gp_retry_close(fd); 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 * particular one might think this advisable for, say, EPERM. * However there really shouldn't be any unreadable files in a * tablespace directory, and if the EPERM is actually complaining * that we can't read the directory itself, we'd be in an infinite * 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); elog(DEBUG1, "Calling GetNewRelFileNode returns new relfilenode = %d", rnode.relNode); return rnode.relNode; }
static void shareinput_clean_lk_ctxt(ShareInput_Lk_Context *lk_ctxt) { int err; elog(DEBUG1, "shareinput_clean_lk_ctxt cleanup lk ctxt %p", lk_ctxt); if(lk_ctxt->readyfd >= 0) { err = gp_retry_close(lk_ctxt->readyfd); insist_log(!err, "shareinput_clean_lk_ctxt cannot close readyfd: %m"); lk_ctxt->readyfd = -1; } if(lk_ctxt->donefd >= 0) { err = gp_retry_close(lk_ctxt->donefd); insist_log(!err, "shareinput_clean_lk_ctxt cannot close donefd: %m"); lk_ctxt->donefd = -1; } if(lk_ctxt->del_ready && lk_ctxt->lkname_ready[0]) { err = unlink(lk_ctxt->lkname_ready); insist_log(!err, "shareinput_clean_lk_ctxt cannot unlink \"%s\": %m", lk_ctxt->lkname_ready); lk_ctxt->del_ready = false; } if(lk_ctxt->del_done && lk_ctxt->lkname_done[0]) { err = unlink(lk_ctxt->lkname_done); insist_log(!err, "shareinput_clean_lk_ctxt cannot unline \"%s\": %m", lk_ctxt->lkname_done); lk_ctxt->del_done = false; } gp_free2 (lk_ctxt, sizeof(ShareInput_Lk_Context)); }
/* * close a file when done with it */ void FileClose(File file) { Vfd *vfdP; Assert(FileIsValid(file)); DO_DB(elog(LOG, "FileClose: %d (%s)", file, VfdCache[file].fileName)); vfdP = &VfdCache[file]; if (!FileIsNotOpen(file)) { /* remove the file from the lru ring */ Delete(file); /* close the file */ if (gp_retry_close(vfdP->fd)) elog(ERROR, "could not close file \"%s\": %m", vfdP->fileName); --nfile; vfdP->fd = VFD_CLOSED; } /* * Delete the file if it was temporary */ if (vfdP->fdstate & FD_TEMPORARY) { /* reset flag so that die() interrupt won't cause problems */ vfdP->fdstate &= ~FD_TEMPORARY; if (unlink(vfdP->fileName)) elog(DEBUG1, "failed to unlink \"%s\": %m", vfdP->fileName); } /* * Return the Vfd slot to the free list */ FreeVfd(file); }
/* * GetNewRelFileNode * Generate a new relfilenode number that is unique within the given * tablespace. * * If the relfilenode will also be used as the relation's OID, pass the * opened pg_class catalog, and this routine will guarantee that the result * is also an unused OID within pg_class. If the result is to be used only * as a relfilenode for an existing relation, pass NULL for pg_class. * * As with GetNewOid, there is some theoretical risk of a race condition, * but it doesn't seem worth worrying about. * * Note: we don't support using this in bootstrap mode. All relations * created by bootstrap have preassigned OIDs, so there's no need. */ Oid GetNewRelFileNode(Oid reltablespace, bool relisshared, Relation pg_class) { RelFileNode rnode; char *rpath; int fd; bool collides = true; /* This should match RelationInitPhysicalAddr */ rnode.spcNode = reltablespace ? reltablespace : MyDatabaseTableSpace; rnode.dbNode = relisshared ? InvalidOid : MyDatabaseId; do { CHECK_FOR_INTERRUPTS(); /* Generate the OID */ if (pg_class) rnode.relNode = GetNewOid(pg_class); else rnode.relNode = GetNewObjectId(); if (!UseOidForRelFileNode(rnode.relNode)) continue; /* Check for existing file of same name */ rpath = relpath(rnode); fd = BasicOpenFile(rpath, O_RDONLY | PG_BINARY, 0); if (fd >= 0) { /* definite collision */ gp_retry_close(fd); 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 * particular one might think this advisable for, say, EPERM. * However there really shouldn't be any unreadable files in a * tablespace directory, and if the EPERM is actually complaining * that we can't read the directory itself, we'd be in an infinite * 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 (Gp_role == GP_ROLE_EXECUTE) Insist(!PointerIsValid(pg_class)); elog(DEBUG1, "Calling GetNewRelFileNode in %s mode %s pg_class. New relOid = %d", (Gp_role == GP_ROLE_EXECUTE ? "execute" : Gp_role == GP_ROLE_UTILITY ? "utility" : "dispatch"), pg_class ? "with" : "without", rnode.relNode); return rnode.relNode; }
bool CheckNewRelFileNodeIsOk(Oid newOid, Oid reltablespace, bool relisshared, Relation pg_class) { RelFileNode rnode; char *rpath; int fd; bool collides; if (pg_class) { Oid oidIndex; Relation indexrel; IndexScanDesc scan; ScanKeyData key; Assert(!IsBootstrapProcessingMode()); Assert(pg_class->rd_rel->relhasoids); /* The relcache will cache the identity of the OID index for us */ oidIndex = RelationGetOidIndex(pg_class); Assert(OidIsValid(oidIndex)); indexrel = index_open(oidIndex, AccessShareLock); ScanKeyInit(&key, (AttrNumber) 1, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(newOid)); scan = index_beginscan(pg_class, indexrel, SnapshotDirty, 1, &key); collides = HeapTupleIsValid(index_getnext(scan, ForwardScanDirection)); index_endscan(scan); index_close(indexrel, AccessShareLock); if (collides) elog(ERROR, "relfilenode %d already in use in \"pg_class\"", newOid); } /* This should match RelationInitPhysicalAddr */ rnode.spcNode = reltablespace ? reltablespace : MyDatabaseTableSpace; rnode.dbNode = relisshared ? InvalidOid : MyDatabaseId; rnode.relNode = newOid; /* Check for existing file of same name */ rpath = relpath(rnode); fd = BasicOpenFile(rpath, O_RDONLY | PG_BINARY, 0); if (fd >= 0) { /* definite collision */ gp_retry_close(fd); collides = true; } else collides = false; pfree(rpath); if (collides && !relisshared) elog(ERROR, "oid %d already in use", newOid); while(GetNewObjectId() < newOid); return !collides; }
/* * GetNewSequenceRelationOid * Get a sequence relation Oid and verify it is valid against * the pg_class relation by doing an index lookup. The caller * should have a suitable lock on pg_class. */ Oid GetNewSequenceRelationOid(Relation relation) { Oid newOid; Oid oidIndex; Relation indexrel; SnapshotData SnapshotDirty; IndexScanDesc scan; ScanKeyData key; bool collides; RelFileNode rnode; char *rpath; int fd; /* This should match RelationInitPhysicalAddr */ rnode.spcNode = relation->rd_rel->reltablespace ? relation->rd_rel->reltablespace : MyDatabaseTableSpace; rnode.dbNode = relation->rd_rel->relisshared ? InvalidOid : MyDatabaseId; /* We should only be using pg_class */ Assert(RelationGetRelid(relation) == RelationRelationId); /* The relcache will cache the identity of the OID index for us */ oidIndex = RelationGetOidIndex(relation); /* Otherwise, use the index to find a nonconflicting OID */ indexrel = index_open(oidIndex, AccessShareLock); InitDirtySnapshot(SnapshotDirty); /* Generate new sequence relation OIDs until we find one not in the table */ do { CHECK_FOR_INTERRUPTS(); newOid = GetNewSequenceRelationObjectId(); ScanKeyInit(&key, (AttrNumber) 1, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(newOid)); /* see notes above about using SnapshotDirty */ scan = index_beginscan(relation, indexrel, &SnapshotDirty, 1, &key); collides = HeapTupleIsValid(index_getnext(scan, ForwardScanDirection)); index_endscan(scan); if (!collides) { /* Check for existing file of same name */ rpath = relpath(rnode); fd = BasicOpenFile(rpath, O_RDONLY | PG_BINARY, 0); if (fd >= 0) { /* definite collision */ gp_retry_close(fd); 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 * particular one might think this advisable for, say, EPERM. * However there really shouldn't be any unreadable files in a * tablespace directory, and if the EPERM is actually complaining * that we can't read the directory itself, we'd be in an infinite * 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; } } /* * Also check that the OID hasn't been pre-assigned for a different * relation. * * We're a bit sloppy between OIDs and relfilenodes here; it would be * OK to use a value that's been reserved for use as a type or * relation OID here, as long as the relfilenode is free. But there's * no harm in skipping over those too, so we don't bother to * distinguish them. */ if (!collides && !IsOidAcceptable(newOid)) collides = true; } while (collides); index_close(indexrel, AccessShareLock); return newOid; }