Use heap_inplace_update() to unset pg_database.dathasloginevt
authorAlexander Korotkov <akorotkov@postgresql.org>
Sun, 11 Feb 2024 22:33:44 +0000 (00:33 +0200)
committerAlexander Korotkov <akorotkov@postgresql.org>
Sun, 11 Feb 2024 22:52:25 +0000 (00:52 +0200)
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.

It's known that changes made by heap_inplace_update() may be lost due to
concurrent normal updates.  However, we are OK with that.  The subsequent
connections will still have a chance to set "dathasloginevt" to false.

Reported-by: Alexander Lakhin
Discussion: https://postgr.es/m/e2a0248e-5f32-af0c-9832-a90d303c2c61%40gmail.com

src/backend/commands/event_trigger.c

index f193c7ddf60ea7b6988bd6e16b5e2823f2fd9054..c8b662131c3a82f0257fcfbc7ea5e450775271ae 100644 (file)
@@ -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,45 @@ 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.
+                *
+                * It's known that changes made by heap_inplace_update() may
+                * be lost due to concurrent normal updates.  However, we are
+                * OK with that.  The subsequent connections will still have a
+                * chance to set "dathasloginevt" to false.
+                */
+               heap_inplace_update(pg_db, tuple);
            }
            table_close(pg_db, RowExclusiveLock);
            heap_freetuple(tuple);