Hi hackers,

We encountered an issue lately, that if the database grants too many roles
`datacl` is toasted, following which, the drop database command will fail
with error "wrong tuple length".

To reproduce the issue, please follow below steps:

CREATE DATABASE test;

-- create helper function
CREATE OR REPLACE FUNCTION data_tuple() returns text as $body$
declare
          mycounter int;
begin
          for mycounter in select i from generate_series(1,2000) i loop
                    execute 'CREATE
ROLE aaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbb ' || mycounter;
                    execute 'GRANT ALL ON DATABASE test to
aaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbb ' || mycounter;
          end loop;
          return 'ok';
end;
$body$ language plpgsql volatile strict;

-- create roles and grant on the database.
SELECT data_tuple();

-- drop database command, this will result in "wrong tuple length" error.
DROP DATABASE test;

The root cause of this behaviour is that the HeapTuple in dropdb
function fetches a copy of pg_database tuple from system cache.
But the system cache flattens any toast attributes, which cause the length
check to fail in heap_inplace_update.

A patch for this issue is attached to the mail, the solution is to
change the logic to fetch the tuple by directly scanning pg_database rather
than using the catcache.

Regards,
Ayush
From 52bfa40c67815e5929f09d6b5c48310c6a4db49b Mon Sep 17 00:00:00 2001
From: Ayush Tiwari <aytiwari@microsoft.com>
Date: Tue, 13 Aug 2024 02:54:57 +0530
Subject: [PATCH] Fix-drop-database-with-pg_database-toast-attribute

---
 src/backend/commands/dbcommands.c | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index 7026352bc9..b5673217b8 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -1649,6 +1649,8 @@ dropdb(const char *dbname, bool missing_ok, bool force)
 	bool		db_istemplate;
 	Relation	pgdbrel;
 	HeapTuple	tup;
+	ScanKeyData scankey;
+	SysScanDesc scan;
 	Form_pg_database datform;
 	int			notherbackends;
 	int			npreparedxacts;
@@ -1786,7 +1788,15 @@ dropdb(const char *dbname, bool missing_ok, bool force)
 	 */
 	pgstat_drop_database(db_id);
 
-	tup = SearchSysCacheCopy1(DATABASEOID, ObjectIdGetDatum(db_id));
+	ScanKeyInit(&scankey,
+				Anum_pg_database_datname,
+				BTEqualStrategyNumber, F_NAMEEQ,
+				CStringGetDatum(dbname));
+
+	scan = systable_beginscan(pgdbrel, DatabaseNameIndexId, true,
+							  NULL, 1, &scankey);
+	tup = systable_getnext(scan);
+
 	if (!HeapTupleIsValid(tup))
 		elog(ERROR, "cache lookup failed for database %u", db_id);
 	datform = (Form_pg_database) GETSTRUCT(tup);
@@ -1812,6 +1822,8 @@ dropdb(const char *dbname, bool missing_ok, bool force)
 	 */
 	CatalogTupleDelete(pgdbrel, &tup->t_self);
 
+	systable_endscan(scan);
+
 	/*
 	 * Drop db-specific replication slots.
 	 */
-- 
2.34.1

Reply via email to