Skip to content

Commit

Permalink
Introduce access/{table.h, relation.h}, for generic functions from he…
Browse files Browse the repository at this point in the history
…apam.h.

access/heapam contains functions that are very storage specific (say
heap_insert() and a lot of lower level functions), and fairly generic
infrastructure like relation_open(), heap_open() etc.  In the upcoming
pluggable storage work we're introducing a layer between table
accesses in general and heapam, to allow for different storage
methods. For a bit cleaner separation it thus seems advantageous to
move generic functions like the aforementioned to their own headers.

access/relation.h will contain relation_open() etc, and access/table.h
will contain table_open() (formerly known as heap_open()). I've decided
for table.h not to include relation.h, but we might change that at a
later stage.

relation.h already exists in another directory, but the other
plausible name (rel.h) also conflicts. It'd be nice if there were a
non-conflicting name, but nobody came up with a suggestion. It's
possible that the appropriate way to address the naming conflict would
be to rename nodes/relation.h, which isn't particularly well named.

To avoid breaking a lot of extensions that just use heap_open() etc,
table.h has macros mapping the old names to the new ones, and heapam.h
includes relation, table.h.  That also allows to keep the
bulk renaming of existing callers in a separate commit.

Author: Andres Freund
Discussion: https://postgr.es/m/20190111000539.xbv7s6w7ilcvm7dp@alap3.anarazel.de
  • Loading branch information
anarazel committed Jan 21, 2019
1 parent f1ad067 commit 4b21acf
Show file tree
Hide file tree
Showing 9 changed files with 441 additions and 305 deletions.
2 changes: 1 addition & 1 deletion src/backend/access/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ top_builddir = ../../..
include $(top_builddir)/src/Makefile.global

SUBDIRS = brin common gin gist hash heap index nbtree rmgrdesc spgist \
tablesample transam
table tablesample transam

include $(top_srcdir)/src/backend/common.mk
2 changes: 1 addition & 1 deletion src/backend/access/common/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global

OBJS = bufmask.o heaptuple.o indextuple.o printsimple.o printtup.o \
reloptions.o scankey.o session.o tupconvert.o tupdesc.o
relation.o reloptions.o scankey.o session.o tupconvert.o tupdesc.o

include $(top_srcdir)/src/backend/common.mk
217 changes: 217 additions & 0 deletions src/backend/access/common/relation.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
/*-------------------------------------------------------------------------
*
* relation.c
* Generic relation related routines.
*
* Portions Copyright (c) 1996-2019, 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_ACCESSEDTEMPREL;

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)
{
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_ACCESSEDTEMPREL;

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 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);
}
Loading

0 comments on commit 4b21acf

Please sign in to comment.