1 /*-------------------------------------------------------------------------
2  *
3  * relation.c
4  *	  Generic relation related routines.
5  *
6  * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *	  src/backend/access/common/relation.c
12  *
13  * NOTES
14  *	  This file contains relation_ routines that implement access to relations
15  *	  (tables, indexes, etc). Support that's specific to subtypes of relations
16  *	  should go into their respective files, not here.
17  *
18  *-------------------------------------------------------------------------
19  */
20 
21 #include "postgres.h"
22 
23 #include "access/relation.h"
24 #include "access/xact.h"
25 #include "catalog/namespace.h"
26 #include "miscadmin.h"
27 #include "pgstat.h"
28 #include "storage/lmgr.h"
29 #include "utils/inval.h"
30 #include "utils/syscache.h"
31 
32 
33 /* ----------------
34  *		relation_open - open any relation by relation OID
35  *
36  *		If lockmode is not "NoLock", the specified kind of lock is
37  *		obtained on the relation.  (Generally, NoLock should only be
38  *		used if the caller knows it has some appropriate lock on the
39  *		relation already.)
40  *
41  *		An error is raised if the relation does not exist.
42  *
43  *		NB: a "relation" is anything with a pg_class entry.  The caller is
44  *		expected to check whether the relkind is something it can handle.
45  * ----------------
46  */
47 Relation
relation_open(Oid relationId,LOCKMODE lockmode)48 relation_open(Oid relationId, LOCKMODE lockmode)
49 {
50 	Relation	r;
51 
52 	Assert(lockmode >= NoLock && lockmode < MAX_LOCKMODES);
53 
54 	/* Get the lock before trying to open the relcache entry */
55 	if (lockmode != NoLock)
56 		LockRelationOid(relationId, lockmode);
57 
58 	/* The relcache does all the real work... */
59 	r = RelationIdGetRelation(relationId);
60 
61 	if (!RelationIsValid(r))
62 		elog(ERROR, "could not open relation with OID %u", relationId);
63 
64 	/*
65 	 * If we didn't get the lock ourselves, assert that caller holds one,
66 	 * except in bootstrap mode where no locks are used.
67 	 */
68 	Assert(lockmode != NoLock ||
69 		   IsBootstrapProcessingMode() ||
70 		   CheckRelationLockedByMe(r, AccessShareLock, true));
71 
72 	/* Make note that we've accessed a temporary relation */
73 	if (RelationUsesLocalBuffers(r))
74 		MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE;
75 
76 	pgstat_initstats(r);
77 
78 	return r;
79 }
80 
81 /* ----------------
82  *		try_relation_open - open any relation by relation OID
83  *
84  *		Same as relation_open, except return NULL instead of failing
85  *		if the relation does not exist.
86  * ----------------
87  */
88 Relation
try_relation_open(Oid relationId,LOCKMODE lockmode)89 try_relation_open(Oid relationId, LOCKMODE lockmode)
90 {
91 	Relation	r;
92 
93 	Assert(lockmode >= NoLock && lockmode < MAX_LOCKMODES);
94 
95 	/* Get the lock first */
96 	if (lockmode != NoLock)
97 		LockRelationOid(relationId, lockmode);
98 
99 	/*
100 	 * Now that we have the lock, probe to see if the relation really exists
101 	 * or not.
102 	 */
103 	if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(relationId)))
104 	{
105 		/* Release useless lock */
106 		if (lockmode != NoLock)
107 			UnlockRelationOid(relationId, lockmode);
108 
109 		return NULL;
110 	}
111 
112 	/* Should be safe to do a relcache load */
113 	r = RelationIdGetRelation(relationId);
114 
115 	if (!RelationIsValid(r))
116 		elog(ERROR, "could not open relation with OID %u", relationId);
117 
118 	/* If we didn't get the lock ourselves, assert that caller holds one */
119 	Assert(lockmode != NoLock ||
120 		   CheckRelationLockedByMe(r, AccessShareLock, true));
121 
122 	/* Make note that we've accessed a temporary relation */
123 	if (RelationUsesLocalBuffers(r))
124 		MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE;
125 
126 	pgstat_initstats(r);
127 
128 	return r;
129 }
130 
131 /* ----------------
132  *		relation_openrv - open any relation specified by a RangeVar
133  *
134  *		Same as relation_open, but the relation is specified by a RangeVar.
135  * ----------------
136  */
137 Relation
relation_openrv(const RangeVar * relation,LOCKMODE lockmode)138 relation_openrv(const RangeVar *relation, LOCKMODE lockmode)
139 {
140 	Oid			relOid;
141 
142 	/*
143 	 * Check for shared-cache-inval messages before trying to open the
144 	 * relation.  This is needed even if we already hold a lock on the
145 	 * relation, because GRANT/REVOKE are executed without taking any lock on
146 	 * the target relation, and we want to be sure we see current ACL
147 	 * information.  We can skip this if asked for NoLock, on the assumption
148 	 * that such a call is not the first one in the current command, and so we
149 	 * should be reasonably up-to-date already.  (XXX this all could stand to
150 	 * be redesigned, but for the moment we'll keep doing this like it's been
151 	 * done historically.)
152 	 */
153 	if (lockmode != NoLock)
154 		AcceptInvalidationMessages();
155 
156 	/* Look up and lock the appropriate relation using namespace search */
157 	relOid = RangeVarGetRelid(relation, lockmode, false);
158 
159 	/* Let relation_open do the rest */
160 	return relation_open(relOid, NoLock);
161 }
162 
163 /* ----------------
164  *		relation_openrv_extended - open any relation specified by a RangeVar
165  *
166  *		Same as relation_openrv, but with an additional missing_ok argument
167  *		allowing a NULL return rather than an error if the relation is not
168  *		found.  (Note that some other causes, such as permissions problems,
169  *		will still result in an ereport.)
170  * ----------------
171  */
172 Relation
relation_openrv_extended(const RangeVar * relation,LOCKMODE lockmode,bool missing_ok)173 relation_openrv_extended(const RangeVar *relation, LOCKMODE lockmode,
174 						 bool missing_ok)
175 {
176 	Oid			relOid;
177 
178 	/*
179 	 * Check for shared-cache-inval messages before trying to open the
180 	 * relation.  See comments in relation_openrv().
181 	 */
182 	if (lockmode != NoLock)
183 		AcceptInvalidationMessages();
184 
185 	/* Look up and lock the appropriate relation using namespace search */
186 	relOid = RangeVarGetRelid(relation, lockmode, missing_ok);
187 
188 	/* Return NULL on not-found */
189 	if (!OidIsValid(relOid))
190 		return NULL;
191 
192 	/* Let relation_open do the rest */
193 	return relation_open(relOid, NoLock);
194 }
195 
196 /* ----------------
197  *		relation_close - close any relation
198  *
199  *		If lockmode is not "NoLock", we then release the specified lock.
200  *
201  *		Note that it is often sensible to hold a lock beyond relation_close;
202  *		in that case, the lock is released automatically at xact end.
203  * ----------------
204  */
205 void
relation_close(Relation relation,LOCKMODE lockmode)206 relation_close(Relation relation, LOCKMODE lockmode)
207 {
208 	LockRelId	relid = relation->rd_lockInfo.lockRelId;
209 
210 	Assert(lockmode >= NoLock && lockmode < MAX_LOCKMODES);
211 
212 	/* The relcache does the real work... */
213 	RelationClose(relation);
214 
215 	if (lockmode != NoLock)
216 		UnlockRelationId(&relid, lockmode);
217 }
218