On Sat, Jan 18, 2025 at 7:45 PM Michael Paquier <mich...@paquier.xyz> wrote:

> On Sun, Jan 19, 2025 at 01:00:04AM +0800, jian he wrote:
> > On Fri, Jan 17, 2025 at 10:20 PM jian he <jian.universal...@gmail.com>
> wrote:
> >> dump and execute the above query generated a warning
> >> WARNING:  missing lock for relation "tenk1_hundred" (OID 18431,
> >> relkind i) @ TID (15,34)
> >
> > This seems to be an existing issue.
> > For pg_restore_relation_stats, we don't have regress tests for index
> relation.
> > I am not sure the WARNING is ok.
>
> This is not OK based on the rules introduced by Noah at aac2c9b4fde8.
> check_lock_if_inplace_updateable_rel() expects a lock to be taken.
> Note that elog() are used when reports should never be user-facing,
> for states that should not be reachable.
> --
> Michael
>

Here's a patch that


Attached is the patch, along with the regression test output prior to the
change to stat_utils.c.

Other pg_dump work has been left out of v39 to focus on this.

Attachment: regression.diffs
Description: Binary data

From a86d16c6413e624d7785c8347fe4e9fbf9a8ce66 Mon Sep 17 00:00:00 2001
From: Corey Huinker <corey.huin...@gmail.com>
Date: Mon, 20 Jan 2025 16:18:51 -0500
Subject: [PATCH v39] Lock table first when setting index relation statistics.

Jian He reported [1] a missing lock relation bug in
pg_restore_relation_stats when restoring stats to an index.

This fix follows the steps for proper locking prior to an inplace update
of a relation as specified in aac2c9b4fde8.

Confirmed that test case generates the reported error without the code
fix.

[1] https://www.postgresql.org/message-id/CACJufxGreTY7qsCV8%2BBkuv0p5SXGTScgh%3DD%2BDq6%3D%2B_%3DXTp7FWg%40mail.gmail.com
---
 src/backend/statistics/stat_utils.c        | 29 ++++++++++++++++++++--
 src/test/regress/expected/stats_import.out | 13 ++++++++++
 src/test/regress/sql/stats_import.sql      | 10 ++++++++
 3 files changed, 50 insertions(+), 2 deletions(-)

diff --git a/src/backend/statistics/stat_utils.c b/src/backend/statistics/stat_utils.c
index 0d446f55b0..381eeb38e5 100644
--- a/src/backend/statistics/stat_utils.c
+++ b/src/backend/statistics/stat_utils.c
@@ -18,13 +18,16 @@
 
 #include "access/relation.h"
 #include "catalog/pg_database.h"
+#include "catalog/index.h"
 #include "funcapi.h"
 #include "miscadmin.h"
 #include "statistics/stat_utils.h"
 #include "utils/acl.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
+#include "utils/lsyscache.h"
 #include "utils/rel.h"
+#include "utils/syscache.h"
 
 /*
  * Ensure that a given argument is not null.
@@ -126,8 +129,30 @@ stats_check_arg_pair(FunctionCallInfo fcinfo,
 void
 stats_lock_check_privileges(Oid reloid)
 {
-	Relation	rel = relation_open(reloid, ShareUpdateExclusiveLock);
-	const char	relkind = rel->rd_rel->relkind;
+	const char	relkind = get_rel_relkind(reloid);
+	Relation	rel;
+	Oid			indrelid = InvalidOid;
+
+	/*
+	 * For indexes we must lock the table first to avoid deadlocking with
+	 * analyze/vacuum.
+	 */
+	if (relkind == RELKIND_INDEX)
+	{
+		Relation	parentrel;
+
+		indrelid = IndexGetRelation(reloid, false);
+
+		parentrel = relation_open(indrelid, ShareUpdateExclusiveLock);
+
+		relation_close(parentrel, NoLock);
+	}
+
+	rel = relation_open(reloid, ShareUpdateExclusiveLock);
+
+	if (OidIsValid(indrelid) &&
+		(indrelid != IndexGetRelation(rel->rd_rel->oid, false)))
+		elog(ERROR, "index parent oid recheck failed for index %u", reloid);
 
 	/* All of the types that can be used with ANALYZE, plus indexes */
 	switch (relkind)
diff --git a/src/test/regress/expected/stats_import.out b/src/test/regress/expected/stats_import.out
index fb50da1cd8..fd9a6d2c9b 100644
--- a/src/test/regress/expected/stats_import.out
+++ b/src/test/regress/expected/stats_import.out
@@ -1414,6 +1414,19 @@ SELECT 3, 'tre', (3, 3.3, 'TRE', '2003-03-03', NULL)::stats_import.complex_type,
 UNION ALL
 SELECT 4, 'four', NULL, int4range(0,100), NULL;
 CREATE INDEX is_odd ON stats_import.test(((comp).a % 2 = 1));
+/* Test for proper locking */
+SELECT * FROM pg_catalog.pg_restore_relation_stats(
+    'relation', 'stats_import.is_odd'::regclass,
+    'version', '180000'::integer,
+    'relpages', '11'::integer,
+    'reltuples', '10000'::real,
+    'relallvisible', '0'::integer
+);
+ pg_restore_relation_stats 
+---------------------------
+ t
+(1 row)
+
 -- Generate statistics on table with data
 ANALYZE stats_import.test;
 CREATE TABLE stats_import.test_clone ( LIKE stats_import.test )
diff --git a/src/test/regress/sql/stats_import.sql b/src/test/regress/sql/stats_import.sql
index d3058bf8f6..043708f529 100644
--- a/src/test/regress/sql/stats_import.sql
+++ b/src/test/regress/sql/stats_import.sql
@@ -1062,6 +1062,16 @@ SELECT 4, 'four', NULL, int4range(0,100), NULL;
 
 CREATE INDEX is_odd ON stats_import.test(((comp).a % 2 = 1));
 
+
+/* Test for proper locking */
+SELECT * FROM pg_catalog.pg_restore_relation_stats(
+    'relation', 'stats_import.is_odd'::regclass,
+    'version', '180000'::integer,
+    'relpages', '11'::integer,
+    'reltuples', '10000'::real,
+    'relallvisible', '0'::integer
+);
+
 -- Generate statistics on table with data
 ANALYZE stats_import.test;
 
-- 
2.48.1

Reply via email to