0001-Fix-assertion-failure-in-WAIT-FOR-LSN-when-used-insi.patch

application/octet-stream

Filename: 0001-Fix-assertion-failure-in-WAIT-FOR-LSN-when-used-insi.patch
Type: application/octet-stream
Part: 1
Message: Bug: WAIT FOR LSN crashes with assertion failure inside PL/pgSQL DO blocks and procedures

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: format-patch
Series: patch v1-0001
Subject: Fix assertion failure in WAIT FOR LSN when used inside PL/pgSQL
File+
src/backend/commands/wait.c 11 0
From 16ace60f82062f0f13fc1814085b045aedd6f202 Mon Sep 17 00:00:00 2001
From: Satya Narlapuram <satyanarlapuram@gmail.com>
Date: Thu, 9 Apr 2026 00:50:20 +0000
Subject: [PATCH v1 1/2] Fix assertion failure in WAIT FOR LSN when used inside
 PL/pgSQL

WAIT FOR LSN ... INTO inside a DO block crashed with an assertion failure in 
EnsurePortalSnapshotExists():

  TRAP: failed Assert("portal->portalSnapshot == NULL"),
  File: "pquery.c", Line: 1776

The root cause was that ExecWaitStmt() called PopActiveSnapshot() to
release the snapshot before waiting, but did not clear the active
portal's portalSnapshot pointer.  When the next PL/pgSQL statement
requiring a snapshot called EnsurePortalSnapshotExists(), it found
a stale non-NULL portalSnapshot and hit the assertion.

PortalRunUtility() normally handles this cleanup in the top-level
execution path, but when WAIT FOR runs through SPI (PL/pgSQL),
ProcessUtility is called directly without the PortalRunUtility
wrapper, so the portalSnapshot was never cleared.

Fix by clearing ActivePortal->portalSnapshot after PopActiveSnapshot()
in ExecWaitStmt(), matching what PortalRunUtility() does.

---
 src/backend/commands/wait.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/src/backend/commands/wait.c b/src/backend/commands/wait.c
index 85fcd463..8d245dbd 100644
--- a/src/backend/commands/wait.c
+++ b/src/backend/commands/wait.c
@@ -24,6 +24,7 @@
 #include "executor/executor.h"
 #include "parser/parse_node.h"
 #include "storage/proc.h"
+#include "tcop/pquery.h"
 #include "utils/builtins.h"
 #include "utils/guc.h"
 #include "utils/pg_lsn.h"
@@ -146,10 +147,20 @@ ExecWaitStmt(ParseState *pstate, WaitStmt *stmt, DestReceiver *dest)
 	 * PlannedStmtRequiresSnapshot(), even in an atomic context, CallStmt is
 	 * processed with a snapshot.  Thankfully, we can pop this snapshot,
 	 * because PortalRunUtility() can tolerate this.
+	 *
+	 * When running inside PL/pgSQL, the active
+	 * portal may hold a pointer to this snapshot in portalSnapshot.  We must
+	 * clear it so that EnsurePortalSnapshotExists() doesn't trip an assertion
+	 * when the next PL/pgSQL statement tries to re-establish a snapshot.
 	 */
 	if (ActiveSnapshotSet())
+	{
 		PopActiveSnapshot();
 
+		if (ActivePortal && ActivePortal->portalSnapshot != NULL)
+			ActivePortal->portalSnapshot = NULL;
+	}
+
 	/*
 	 * At second, invalidate a catalog snapshot if any.  And we should be done
 	 * with the preparation.
-- 
2.43.0