Skip to content

Commit 4f95f14

Browse files
Osumi Takamichihackorum
authored andcommitted
disallow TRUNCATE on user_catalog_table
When TRUNCATE command is executed on user_catalog_table in synchronous_mode during logial replication, TRUNCATE waits for the subscriber's synchronization. At the same time, we don't have a restriction that output plugin cannot take a lock on it. Therefore, the walsender can hang due to the deadlock to take a new lock on a user_catalog_table, since the table is locked by TRUNCATE already. This patch addresses such a deadlock hazards by prohibiting an operation to issue TRUNCATE on user_catalog_table.
1 parent 4069df2 commit 4f95f14

4 files changed

Lines changed: 27 additions & 0 deletions

File tree

doc/src/sgml/ref/truncate.sgml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,10 @@ TRUNCATE [ TABLE ] [ ONLY ] <replaceable class="parameter">name</replaceable> [
176176
supported by the foreign data wrapper, for instance,
177177
see <xref linkend="postgres-fdw"/>.
178178
</para>
179+
180+
<para>
181+
<command>TRUNCATE</command> on user_catalog_table is not supported.
182+
</para>
179183
</refsect1>
180184

181185
<refsect1>

src/backend/commands/tablecmds.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2435,6 +2435,7 @@ static void
24352435
truncate_check_rel(Oid relid, Form_pg_class reltuple)
24362436
{
24372437
char *relname = NameStr(reltuple->relname);
2438+
Relation relation = NULL;
24382439

24392440
/*
24402441
* Only allow truncate on regular tables, foreign tables using foreign
@@ -2478,6 +2479,18 @@ truncate_check_rel(Oid relid, Form_pg_class reltuple)
24782479
errmsg("permission denied: \"%s\" is a system catalog",
24792480
relname)));
24802481

2482+
/*
2483+
* Disallow truncate on user_catalog_table, with attention to the deadlock
2484+
* scenario that output plugin takes an lock on it in synchronous mode of
2485+
* logical replication.
2486+
*/
2487+
relation = RelationIdGetRelation(relid);
2488+
if (RelationIsUsedAsCatalogTable(relation))
2489+
ereport(ERROR,
2490+
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2491+
errmsg("cannot TRUNCATE an user_catalog_table"));
2492+
RelationClose(relation);
2493+
24812494
InvokeObjectTruncateHook(relid);
24822495
}
24832496

src/test/regress/expected/truncate.out

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,3 +592,8 @@ SELECT a as "from table trunc_a" FROM trunc_a ORDER BY a;
592592
(3 rows)
593593

594594
DROP TABLE trunc_a, ref_c;
595+
-- truncate an user_catalog_table
596+
CREATE TABLE my_user_catalog_table (col integer) WITH (user_catalog_table = true);
597+
TRUNCATE my_user_catalog_table; -- should fail
598+
ERROR: cannot TRUNCATE an user_catalog_table
599+
DROP TABLE my_user_catalog_table;

src/test/regress/sql/truncate.sql

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,3 +327,8 @@ SELECT a as "from table ref_c" FROM ref_c;
327327
SELECT a as "from table trunc_a" FROM trunc_a ORDER BY a;
328328

329329
DROP TABLE trunc_a, ref_c;
330+
331+
-- truncate an user_catalog_table
332+
CREATE TABLE my_user_catalog_table (col integer) WITH (user_catalog_table = true);
333+
TRUNCATE my_user_catalog_table; -- should fail
334+
DROP TABLE my_user_catalog_table;

0 commit comments

Comments
 (0)