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