summaryrefslogtreecommitdiffstats
path: root/src/backend/access/common/relation.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/access/common/relation.c')
-rw-r--r--src/backend/access/common/relation.c217
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);
+}