Assert-lwlocks-not-held-during-qual-evaluation.patch.WIP
application/octet-stream
Filename: Assert-lwlocks-not-held-during-qual-evaluation.patch.WIP
Type: application/octet-stream
Part: 0
Message:
Re: Qual push down to table AM
From 63bead18fbfb71e46b4193ac4464740f63fa4295 Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Tue, 7 Oct 2025 16:49:22 -0700
Subject: [PATCH v2] Assert lwlocks not held during qual evaluation
Add assertions in the new push-down quals code that no lwlocks are
held, including lwlocks on buffers, during the evaluation of the
quals.
---
src/backend/access/heap/heapam_valid.c | 2 ++
src/backend/executor/nodeSeqscan.c | 16 ++++++++++++++++
src/backend/storage/lmgr/lwlock.c | 9 +++++++++
src/include/storage/lwlock.h | 6 ++++++
4 files changed, 33 insertions(+)
diff --git a/src/backend/access/heap/heapam_valid.c b/src/backend/access/heap/heapam_valid.c
index a05738a9144..00936331109 100644
--- a/src/backend/access/heap/heapam_valid.c
+++ b/src/backend/access/heap/heapam_valid.c
@@ -123,6 +123,8 @@ HeapKeyTest(HeapTuple tuple, TupleDesc tupdesc, int nkeys, ScanKey keys)
int cur_nkeys = nkeys;
ScanKey cur_key = keys;
+ AssertNoLWLockHeldByMe("HeapKeyTest");
+
for (; cur_nkeys--; cur_key++)
{
Datum atp;
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 210d4cb84e0..ea80208b4ee 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -65,6 +65,8 @@ ExecSeqBuildScanKeys(PlanState *planstate, List *quals, int *numScanKeys,
int n_runtime_keys;
int max_runtime_keys;
+ AssertNoLWLockHeldByMe("ExecSeqBuildScanKeys");
+
n_quals = list_length(quals);
/*
@@ -396,6 +398,8 @@ SeqNext(SeqScanState *node)
ScanDirection direction;
TupleTableSlot *slot;
+ AssertNoLWLockHeldByMe("SeqNext");
+
/*
* get information from the estate and scan state
*/
@@ -778,6 +782,8 @@ ExecReScanSeqScan(SeqScanState *node)
{
TableScanDesc scan;
+ AssertNoLWLockHeldByMe("ExecReScanSeqScan");
+
/*
* If we are doing runtime key calculations (ie, any of the scan key
* values weren't simple Consts), compute the new key values. But first,
@@ -821,6 +827,8 @@ ExecSeqScanEvalRuntimeKeys(ExprContext *econtext,
int j;
MemoryContext oldContext;
+ AssertNoLWLockHeldByMe("ExecSeqScanEvalRuntimeKeys");
+
/* We want to keep the key values in per-tuple memory */
oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
@@ -881,6 +889,8 @@ ExecSeqScanEstimate(SeqScanState *node,
{
EState *estate = node->ss.ps.state;
+ AssertNoLWLockHeldByMe("ExecSeqScanEstimate");
+
node->pscan_len = table_parallelscan_estimate(node->ss.ss_currentRelation,
estate->es_snapshot);
shm_toc_estimate_chunk(&pcxt->estimator, node->pscan_len);
@@ -900,6 +910,8 @@ ExecSeqScanInitializeDSM(SeqScanState *node,
EState *estate = node->ss.ps.state;
ParallelTableScanDesc pscan;
+ AssertNoLWLockHeldByMe("ExecSeqScanInitializeDSM");
+
pscan = shm_toc_allocate(pcxt->toc, node->pscan_len);
table_parallelscan_initialize(node->ss.ss_currentRelation,
pscan,
@@ -931,6 +943,8 @@ ExecSeqScanReInitializeDSM(SeqScanState *node,
{
ParallelTableScanDesc pscan;
+ AssertNoLWLockHeldByMe("ExecSeqScanReInitializeDSM");
+
pscan = node->ss.ss_currentScanDesc->rs_parallel;
table_parallelscan_reinitialize(node->ss.ss_currentRelation, pscan);
}
@@ -947,6 +961,8 @@ ExecSeqScanInitializeWorker(SeqScanState *node,
{
ParallelTableScanDesc pscan;
+ AssertNoLWLockHeldByMe("ExecSeqScanInitializeWorker");
+
pscan = shm_toc_lookup(pwcxt->toc, node->ss.ps.plan->plan_node_id, false);
node->ss.ss_currentScanDesc =
table_beginscan_parallel(node->ss.ss_currentRelation, pscan,
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index ec9c345ffdf..3aed1e3de4c 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -743,6 +743,15 @@ GetLWLockIdentifier(uint32 classId, uint16 eventId)
return GetLWTrancheName(eventId);
}
+#ifdef USE_ASSERT_CHECKING
+void
+AssertNoLWLockHeldByMe(const char *context)
+{
+ if (num_held_lwlocks > 0)
+ elog(ERROR, "In %s: holding %d lightweight locks", context, num_held_lwlocks);
+}
+#endif
+
/*
* Internal function that tries to atomically acquire the lwlock in the passed
* in mode.
diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h
index 5e717765764..f786b84ba91 100644
--- a/src/include/storage/lwlock.h
+++ b/src/include/storage/lwlock.h
@@ -146,6 +146,12 @@ extern void InitLWLockAccess(void);
extern const char *GetLWLockIdentifier(uint32 classId, uint16 eventId);
+#ifdef USE_ASSERT_CHECKING
+extern void AssertNoLWLockHeldByMe(const char *context);
+#else
+#define AssertNoLWLockHeldByMe(context)
+#endif
+
/*
* Extensions (or core code) can obtain an LWLocks by calling
* RequestNamedLWLockTranche() during postmaster startup. Subsequently,
--
2.39.5 (Apple Git-154)