Re: executor relation handling

Поиск
Список
Период
Сортировка
От Tom Lane
Тема Re: executor relation handling
Дата
Msg-id 16565.1538327894@sss.pgh.pa.us
обсуждение исходный текст
Ответ на Re: executor relation handling  (Tom Lane <tgl@sss.pgh.pa.us>)
Ответы Re: executor relation handling  (David Rowley <david.rowley@2ndquadrant.com>)
Re: executor relation handling  (Amit Langote <Langote_Amit_f8@lab.ntt.co.jp>)
Re: executor relation handling  (Julien Rouhaud <rjuju123@gmail.com>)
Список pgsql-hackers
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

diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 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.c
index 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.c
index 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.c
index 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.h
index 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.h
index 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,

В списке pgsql-hackers по дате отправления:

Предыдущее
От: Andrew Dunstan
Дата:
Сообщение: Re: Cygwin linking rules
Следующее
От: Tom Lane
Дата:
Сообщение: Relations being opened without any lock whatever