/*------------------------------------------------------------------------- * * relation.c * Generic relation related routines. * * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * src/backend/access/common/relation.c * * NOTES * This file contains relation_ routines that implement access to relations * (tables, indexes, etc). Support that's specific to subtypes of relations * should go into their respective files, not here. * *------------------------------------------------------------------------- */ #include "postgres.h" #include "access/relation.h" #include "access/xact.h" #include "catalog/namespace.h" #include "miscadmin.h" #include "pgstat.h" #include "storage/lmgr.h" #include "utils/inval.h" #include "utils/syscache.h" /* ---------------- * relation_open - open any relation by relation OID * * If lockmode is not "NoLock", the specified kind of lock is * obtained on the relation. (Generally, NoLock should only be * used if the caller knows it has some appropriate lock on the * relation already.) * * An error is raised if the relation does not exist. * * NB: a "relation" is anything with a pg_class entry. The caller is * expected to check whether the relkind is something it can handle. * ---------------- */ Relation relation_open(Oid relationId, LOCKMODE lockmode) { Relation r; Assert(lockmode >= NoLock && lockmode < MAX_LOCKMODES); /* Get the lock before trying to open the relcache entry */ if (lockmode != NoLock) LockRelationOid(relationId, lockmode); /* The relcache does all the real work... */ r = RelationIdGetRelation(relationId); if (!RelationIsValid(r)) elog(ERROR, "could not open relation with OID %u", relationId); /* * If we didn't get the lock ourselves, assert that caller holds one, * except in bootstrap mode where no locks are used. */ Assert(lockmode != NoLock || IsBootstrapProcessingMode() || CheckRelationLockedByMe(r, AccessShareLock, true)); /* Make note that we've accessed a temporary relation */ if (RelationUsesLocalBuffers(r)) MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE; pgstat_init_relation(r); return r; } /* ---------------- * try_relation_open - open any relation by relation OID * * Same as relation_open, except return NULL instead of failing * if the relation does not exist. * ---------------- */ Relation try_relation_open(Oid relationId, LOCKMODE lockmode) { Relation r; Assert(lockmode >= NoLock && lockmode < MAX_LOCKMODES); /* Get the lock first */ if (lockmode != NoLock) LockRelationOid(relationId, lockmode); /* * Now that we have the lock, probe to see if the relation really exists * or not. */ if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(relationId))) { /* Release useless lock */ if (lockmode != NoLock) UnlockRelationOid(relationId, lockmode); return NULL; } /* Should be safe to do a relcache load */ r = RelationIdGetRelation(relationId); if (!RelationIsValid(r)) elog(ERROR, "could not open relation with OID %u", relationId); /* If we didn't get the lock ourselves, assert that caller holds one */ Assert(lockmode != NoLock || CheckRelationLockedByMe(r, AccessShareLock, true)); /* Make note that we've accessed a temporary relation */ if (RelationUsesLocalBuffers(r)) MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE; pgstat_init_relation(r); return r; } /* ---------------- * relation_openrv - open any relation specified by a RangeVar * * Same as relation_open, but the relation is specified by a RangeVar. * ---------------- */ Relation relation_openrv(const RangeVar *relation, LOCKMODE lockmode) { Oid relOid; /* * Check for shared-cache-inval messages before trying to open the * relation. This is needed even if we already hold a lock on the * relation, because GRANT/REVOKE are executed without taking any lock on * the target relation, and we want to be sure we see current ACL * information. We can skip this if asked for NoLock, on the assumption * that such a call is not the first one in the current command, and so we * should be reasonably up-to-date already. (XXX this all could stand to * be redesigned, but for the moment we'll keep doing this like it's been * done historically.) */ if (lockmode != NoLock) AcceptInvalidationMessages(); /* Look up and lock the appropriate relation using namespace search */ relOid = RangeVarGetRelid(relation, lockmode, false); /* Let relation_open do the rest */ return relation_open(relOid, NoLock); } /* ---------------- * relation_openrv_extended - open any relation specified by a RangeVar * * Same as relation_openrv, but with an additional missing_ok argument * allowing a NULL return rather than an error if the relation is not * found. (Note that some other causes, such as permissions problems, * will still result in an ereport.) * ---------------- */ Relation relation_openrv_extended(const RangeVar *relation, LOCKMODE lockmode, bool missing_ok) { Oid relOid; /* * Check for shared-cache-inval messages before trying to open the * relation. See comments in relation_openrv(). */ if (lockmode != NoLock) AcceptInvalidationMessages(); /* Look up and lock the appropriate relation using namespace search */ relOid = RangeVarGetRelid(relation, lockmode, missing_ok); /* Return NULL on not-found */ if (!OidIsValid(relOid)) return NULL; /* Let relation_open do the rest */ return relation_open(relOid, NoLock); } /* ---------------- * relation_close - close any relation * * If lockmode is not "NoLock", we then release the specified lock. * * Note that it is often sensible to hold a lock beyond relation_close; * in that case, the lock is released automatically at xact end. * ---------------- */ void relation_close(Relation relation, LOCKMODE lockmode) { LockRelId relid = relation->rd_lockInfo.lockRelId; Assert(lockmode >= NoLock && lockmode < MAX_LOCKMODES); /* The relcache does the real work... */ RelationClose(relation); if (lockmode != NoLock) UnlockRelationId(&relid, lockmode); }