From: Tom Lane <tgl@sss.pgh.pa.us>
To: David Rowley <david.rowley@2ndquadrant.com>
Cc: Alvaro Herrera <alvherre@2ndquadrant.com>
Cc: Amit Langote <Langote_Amit_f8@lab.ntt.co.jp>
Cc: Jesper Pedersen <jesper.pedersen@redhat.com>
Cc: Amit Langote <amitlangote09@gmail.com>
Cc: Pg Hackers <pgsql-hackers@postgresql.org>
Subject: Re: executor relation handling
Date: Sun, 30 Sep 2018 13:18:14 -0400
Message-ID: <16565.1538327894@sss.pgh.pa.us> (raw)
In-Reply-To: <6129.1538251457@sss.pgh.pa.us>
References: <5509d3e8-64e9-d584-c841-69c0d6b3c343@lab.ntt.co.jp>
<20180928154946.h5qr2quunjlh5t33@alvherre.pgsql>
<CAKJS1f8uCk937hbTN5Fud+etubv5mczhguwabgaqQ_qr_Woe7A@mail.gmail.com>
<6129.1538251457@sss.pgh.pa.us>
I wrote:
> 1. You set up transformRuleStmt to insert AccessExclusiveLock into
> the "OLD" and "NEW" RTEs for a view. This is surely wrong; we do
> not want to take exclusive lock on a view just to run a query using
> the view. It should (usually, anyway) just be AccessShareLock.
> However, because addRangeTableEntryForRelation insists that you
> hold the requested lock type *now*, just changing the parameter
> to AccessShareLock doesn't work.
> I hacked around this for the moment by passing NoLock to
> addRangeTableEntryForRelation and then changing rte->lockmode
> after it returns, but man that's ugly. It makes me wonder whether
> addRangeTableEntryForRelation should be checking the lockmode at all.
It occurred to me that it'd be reasonable to insist that the caller
holds a lock *at least as strong* as the one being recorded in the RTE,
and that there's also been discussions about verifying that some lock
is held when something like heap_open(foo, NoLock) is attempted.
So I dusted off the part of 0001 that did that, producing the
attached delta patch.
Unfortunately, I can't commit this, because it exposes at least two
pre-existing bugs :-(. So we'll need to fix those first, which seems
like it should be a separate thread. I'm just parking this here for
the moment.
I think that the call sites should ultimately look like
Assert(CheckRelationLockedByMe(...));
but for hunting down the places where the assertion currently fails,
it's more convenient if it's just an elog(WARNING).
regards, tom lane
Attachments:
[text/x-diff] check-relation-lock-held-1.patch (6.7K, ../16565.1538327894@sss.pgh.pa.us/2-check-relation-lock-held-1.patch)
download | inline diff:diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.cindex 3395445..f8a55ee 100644--- a/src/backend/access/heap/heapam.c+++ b/src/backend/access/heap/heapam.c@@ -1137,6 +1137,11 @@ relation_open(Oid relationId, LOCKMODE lockmode)
if (!RelationIsValid(r))
elog(ERROR, "could not open relation with OID %u", relationId);
+ if (lockmode == NoLock &&+ !CheckRelationLockedByMe(r, AccessShareLock, true))+ elog(WARNING, "relation_open: no lock held on %s",+ RelationGetRelationName(r));+
/* Make note that we've accessed a temporary relation */
if (RelationUsesLocalBuffers(r))
MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPREL;
@@ -1183,6 +1188,11 @@ try_relation_open(Oid relationId, LOCKMODE lockmode)
if (!RelationIsValid(r))
elog(ERROR, "could not open relation with OID %u", relationId);
+ if (lockmode == NoLock &&+ !CheckRelationLockedByMe(r, AccessShareLock, true))+ elog(WARNING, "try_relation_open: no lock held on %s",+ RelationGetRelationName(r));+
/* Make note that we've accessed a temporary relation */
if (RelationUsesLocalBuffers(r))
MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPREL;
@@ -8084,6 +8094,10 @@ ExtractReplicaIdentity(Relation relation, HeapTuple tp, bool key_changed, bool *
idx_rel = RelationIdGetRelation(replidindex);
+ if (!CheckRelationLockedByMe(idx_rel, AccessShareLock, true))+ elog(WARNING, "ExtractReplicaIdentity: no lock held on %s",+ RelationGetRelationName(idx_rel));+
/* deform tuple, so we have fast access to columns */
heap_deform_tuple(tp, desc, values, nulls);
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.cindex da600dc..aaf5544 100644--- a/src/backend/parser/parse_relation.c+++ b/src/backend/parser/parse_relation.c@@ -28,6 +28,7 @@
#include "parser/parse_enr.h"
#include "parser/parse_relation.h"
#include "parser/parse_type.h"
+#include "storage/lmgr.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
@@ -1272,7 +1273,7 @@ addRangeTableEntry(ParseState *pstate,
*
* lockmode is the lock type required for query execution; it must be one
* of AccessShareLock, RowShareLock, or RowExclusiveLock depending on the
- * RTE's role within the query. The caller should always hold that lock mode+ * RTE's role within the query. The caller must hold that lock mode
* or a stronger one.
*
* Note: properly, lockmode should be declared LOCKMODE not int, but that
@@ -1295,6 +1296,9 @@ addRangeTableEntryForRelation(ParseState *pstate,
Assert(lockmode == AccessShareLock ||
lockmode == RowShareLock ||
lockmode == RowExclusiveLock);
+ if (!CheckRelationLockedByMe(rel, lockmode, true))+ elog(WARNING, "addRangeTableEntryForRelation: no lock held on %s",+ RelationGetRelationName(rel));
rte->rtekind = RTE_RELATION;
rte->alias = alias;
diff --git a/src/backend/storage/lmgr/lmgr.c b/src/backend/storage/lmgr/lmgr.cindex dc0a439..517ff8e 100644--- a/src/backend/storage/lmgr/lmgr.c+++ b/src/backend/storage/lmgr/lmgr.c@@ -288,6 +288,51 @@ UnlockRelation(Relation relation, LOCKMODE lockmode)
}
/*
+ * CheckRelationLockedByMe+ *+ * Returns true if current transaction holds a lock on 'relation' of mode+ * 'lockmode'. If 'orstronger' is true, a stronger lockmode is also OK.+ * ("Stronger" is defined as "numerically higher", which is a bit+ * semantically dubious but is OK for the purposes we use this for.)+ */+bool+CheckRelationLockedByMe(Relation relation, LOCKMODE lockmode, bool orstronger)+{+ LOCKTAG tag;++ SET_LOCKTAG_RELATION(tag,+ relation->rd_lockInfo.lockRelId.dbId,+ relation->rd_lockInfo.lockRelId.relId);++ if (LockHeldByMe(&tag, lockmode))+ return true;++ if (orstronger)+ {+ LOCKMODE slockmode;++ for (slockmode = lockmode + 1;+ slockmode <= AccessExclusiveLock;+ slockmode++)+ {+ if (LockHeldByMe(&tag, slockmode))+ {+#ifdef NOT_USED+ /* Sometimes this might be useful for debugging purposes */+ elog(WARNING, "lock mode %s substituted for %s on relation %s",+ GetLockmodeName(tag.locktag_lockmethodid, slockmode),+ GetLockmodeName(tag.locktag_lockmethodid, lockmode),+ RelationGetRelationName(relation));+#endif+ return true;+ }+ }+ }++ return false;+}++/*
* LockHasWaitersRelation
*
* This is a function to check whether someone else is waiting for a
diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.cindex 831ae62..10f6f60 100644--- a/src/backend/storage/lmgr/lock.c+++ b/src/backend/storage/lmgr/lock.c@@ -564,6 +564,30 @@ DoLockModesConflict(LOCKMODE mode1, LOCKMODE mode2)
}
/*
+ * LockHeldByMe -- test whether lock 'locktag' is held with mode 'lockmode'+ * by the current transaction+ */+bool+LockHeldByMe(const LOCKTAG *locktag, LOCKMODE lockmode)+{+ LOCALLOCKTAG localtag;+ LOCALLOCK *locallock;++ /*+ * See if there is a LOCALLOCK entry for this lock and lockmode+ */+ MemSet(&localtag, 0, sizeof(localtag)); /* must clear padding */+ localtag.lock = *locktag;+ localtag.mode = lockmode;++ locallock = (LOCALLOCK *) hash_search(LockMethodLocalHash,+ (void *) &localtag,+ HASH_FIND, NULL);++ return (locallock && locallock->nLocks > 0);+}++/*
* LockHasWaiters -- look up 'locktag' and check if releasing this
* lock would wake up other processes waiting for it.
*/
diff --git a/src/include/storage/lmgr.h b/src/include/storage/lmgr.hindex a217de9..e5356b7 100644--- a/src/include/storage/lmgr.h+++ b/src/include/storage/lmgr.h@@ -45,6 +45,8 @@ extern void UnlockRelationOid(Oid relid, LOCKMODE lockmode);
extern void LockRelation(Relation relation, LOCKMODE lockmode);
extern bool ConditionalLockRelation(Relation relation, LOCKMODE lockmode);
extern void UnlockRelation(Relation relation, LOCKMODE lockmode);
+extern bool CheckRelationLockedByMe(Relation relation, LOCKMODE lockmode,+ bool orstronger);
extern bool LockHasWaitersRelation(Relation relation, LOCKMODE lockmode);
extern void LockRelationIdForSession(LockRelId *relid, LOCKMODE lockmode);
diff --git a/src/include/storage/lock.h b/src/include/storage/lock.hindex ff4df7f..a37fda7 100644--- a/src/include/storage/lock.h+++ b/src/include/storage/lock.h@@ -540,6 +540,7 @@ extern void LockReleaseAll(LOCKMETHODID lockmethodid, bool allLocks);
extern void LockReleaseSession(LOCKMETHODID lockmethodid);
extern void LockReleaseCurrentOwner(LOCALLOCK **locallocks, int nlocks);
extern void LockReassignCurrentOwner(LOCALLOCK **locallocks, int nlocks);
+extern bool LockHeldByMe(const LOCKTAG *locktag, LOCKMODE lockmode);
extern bool LockHasWaiters(const LOCKTAG *locktag,
LOCKMODE lockmode, bool sessionLock);
extern VirtualTransactionId *GetLockConflicts(const LOCKTAG *locktag,
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: pgsql-hackers@postgresql.org
Cc: tgl@sss.pgh.pa.us, david.rowley@2ndquadrant.com, alvherre@2ndquadrant.com, Langote_Amit_f8@lab.ntt.co.jp, jesper.pedersen@redhat.com, amitlangote09@gmail.com
Subject: Re: executor relation handling
In-Reply-To: <16565.1538327894@sss.pgh.pa.us>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox