diff options
Diffstat (limited to 'src/backend/access/common/relation.c')
-rw-r--r-- | src/backend/access/common/relation.c | 217 |
1 files changed, 217 insertions, 0 deletions
diff --git a/src/backend/access/common/relation.c b/src/backend/access/common/relation.c new file mode 100644 index 0000000..382a42f --- /dev/null +++ b/src/backend/access/common/relation.c @@ -0,0 +1,217 @@ +/*------------------------------------------------------------------------- + * + * relation.c + * Generic relation related routines. + * + * Portions Copyright (c) 1996-2022, 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); +} |