| /*------------------------------------------------------------------------- |
| * |
| * relation.c |
| * Generic relation related routines. |
| * |
| * Portions Copyright (c) 1996-2021, 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)) |
| ereport(ERROR, |
| (errcode(ERRCODE_UNDEFINED_TABLE), |
| errmsg("could not open relation with OID %u", relationId), |
| errdetail("This can be validly caused by a concurrent delete operation on this object."))); |
| |
| /* |
| * 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)); |
| |
| #if 0 /* Upstream code not applicable to GPDB */ |
| /* Make note that we've accessed a temporary relation */ |
| if (RelationUsesLocalBuffers(r)) |
| MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE; |
| #endif |
| |
| pgstat_initstats(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, bool noWait) |
| { |
| Relation r; |
| |
| Assert(lockmode >= NoLock && lockmode < MAX_LOCKMODES); |
| |
| /* Get the lock first */ |
| if (lockmode != NoLock) |
| { |
| if (!noWait) |
| LockRelationOid(relationId, lockmode); |
| else |
| { |
| /* |
| * noWait is a Cloudberry addition to the open_relation code |
| * basically to support INSERT ... FOR UPDATE NOWAIT. Our NoWait |
| * handling needs to be more tolerant of failed locks than standard |
| * postgres largely due to the fact that we have to promote certain |
| * update locks in order to handle distributed updates. |
| */ |
| if (!ConditionalLockRelationOid(relationId, lockmode)) |
| return NULL; |
| } |
| } |
| |
| /* |
| * 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)) |
| { |
| ereport(ERROR, |
| (errcode(ERRCODE_UNDEFINED_TABLE), |
| errmsg("could not open relation with OID %u", relationId), |
| errdetail("This can be validly caused by a concurrent delete operation on this object."))); |
| } |
| |
| /* If we didn't get the lock ourselves, assert that caller holds one */ |
| Assert(lockmode != NoLock || |
| CheckRelationLockedByMe(r, AccessShareLock, true)); |
| |
| #if 0 /* Upstream code not applicable to GPDB */ |
| /* Make note that we've accessed a temporary relation */ |
| if (RelationUsesLocalBuffers(r)) |
| MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE; |
| #endif |
| |
| pgstat_initstats(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 try_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); |
| else |
| { |
| LOCKTAG tag; |
| |
| SET_LOCKTAG_RELATION(tag, relid.dbId, relid.relId); |
| |
| /* |
| * Closing with NoLock is a sufficient condition for a relation lock |
| * to be transaction-level(means the lock can only be released after |
| * the holding transaction is over). |
| * This is because the difference betwwen the ref counts in the |
| * relation and the lock tag can not be removed. |
| * So this is a good time to set the holdTillEndXact flag for the lock. |
| */ |
| LockSetHoldTillEndXact(&tag); |
| } |
| } |