From 0da5aaaedf20085cb078ac2ec5ede0f0cabcb86a Mon Sep 17 00:00:00 2001
From: Alexander Korotkov <akorotkov@postgresql.org>
Date: Mon, 5 Feb 2024 01:46:41 +0200
Subject: [PATCH] Use heap_inplace_update() to unset pg_database.dathasloginevt

Reported-by: Alexander Lakhin
Discussion: https://postgr.es/m/e2a0248e-5f32-af0c-9832-a90d303c2c61%40gmail.com
---
 src/backend/commands/event_trigger.c | 31 ++++++++++++++++++++++++----
 1 file changed, 27 insertions(+), 4 deletions(-)

diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index f193c7ddf60..da01b458dc3 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -13,6 +13,7 @@
  */
 #include "postgres.h"
 
+#include "access/heapam.h"
 #include "access/htup_details.h"
 #include "access/table.h"
 #include "access/xact.h"
@@ -943,18 +944,40 @@ EventTriggerOnLogin(void)
 			Relation	pg_db = table_open(DatabaseRelationId, RowExclusiveLock);
 			HeapTuple	tuple;
 			Form_pg_database db;
+			ScanKeyData key[1];
+			SysScanDesc scan;
 
-			tuple = SearchSysCacheCopy1(DATABASEOID,
-										ObjectIdGetDatum(MyDatabaseId));
+			/*
+			 * Get the pg_database tuple to scribble on.  Note that this does
+			 * not directly rely on the syscache to avoid issues with
+			 * flattened toast values for the in-place update.
+			 */
+			ScanKeyInit(&key[0],
+						Anum_pg_database_oid,
+						BTEqualStrategyNumber, F_OIDEQ,
+						ObjectIdGetDatum(MyDatabaseId));
+
+			scan = systable_beginscan(pg_db, DatabaseOidIndexId, true,
+									  NULL, 1, key);
+			tuple = systable_getnext(scan);
+			tuple = heap_copytuple(tuple);
+			systable_endscan(scan);
 
 			if (!HeapTupleIsValid(tuple))
-				elog(ERROR, "cache lookup failed for database %u", MyDatabaseId);
+				elog(ERROR, "could not find tuple for database %u", MyDatabaseId);
 
 			db = (Form_pg_database) GETSTRUCT(tuple);
 			if (db->dathasloginevt)
 			{
 				db->dathasloginevt = false;
-				CatalogTupleUpdate(pg_db, &tuple->t_self, tuple);
+
+				/*
+				 * Do an "in place" update of the pg_database tuple.  Doing
+				 * this instead of regular updates serves two purposes.
+				 * First, that avoids possible waiting on the row-level lock.
+				 * Second, that avoids dealing with TOAST.
+				 */
+				heap_inplace_update(pg_db, tuple);
 			}
 			table_close(pg_db, RowExclusiveLock);
 			heap_freetuple(tuple);
-- 
2.39.3 (Apple Git-145)

