Álvaro Herrera писал 2021-06-30 21:54:
On 2021-Jun-30, Alexander Pyhalov wrote:
I've seen the following effect on PostgreSQL 14 stable branch.
Index, created on partitioned table, disappears from pg_dump or psql
\d
output.
This seems to begin after analyze. Partitoned relation relhasindex
pg_class
field suddenly becomes false.
Yeah, that seems correct.
I didn't verify your test case, but after looking at the code I thought
there was a bit too much churn and the new conditions looked quite
messy
and unexplained. It seems simpler to be explicit at the start about
what we're doing, and keep nindexes=0 for partitioned tables; with
that,
the code works unchanged because the "for" loops do nothing without
having to check for anything. My proposal is attached.
I did run the tests and they do pass, but I didn't look very closely at
what the tests are actually doing.
I noticed that part of that comment seems to be a leftover from ... I
don't know when: "We do not analyze index columns if there was an
explicit column list in the ANALYZE command, however." I suppose this
is about some code that was removed, but I didn't dig into it.
Looks good. It seems this comment refers to line 455.
445 if (nindexes > 0)
446 {
447 indexdata = (AnlIndexData *) palloc0(nindexes *
sizeof(AnlIndexData));
448 for (ind = 0; ind < nindexes; ind++)
449 {
450 AnlIndexData *thisdata = &indexdata[ind];
451 IndexInfo *indexInfo;
452
453 thisdata->indexInfo = indexInfo =
BuildIndexInfo(Irel[ind]);
454 thisdata->tupleFract = 1.0; /* fix later if partial */
455 if (indexInfo->ii_Expressions != NIL && va_cols == NIL)
456 {
457 ListCell *indexpr_item =
list_head(indexInfo->ii_Expressions);
458
459 thisdata->vacattrstats = (VacAttrStats **)
460 palloc(indexInfo->ii_NumIndexAttrs *
sizeof(VacAttrStats *));
Also I've added non-necessary new line in test.
Restored comment and removed new line.
--
Best regards,
Alexander Pyhalov,
Postgres Professional
From 0677fca372349ab2a1f5bc2e69a91c72ae7dfaa3 Mon Sep 17 00:00:00 2001
From: Alexander Pyhalov <a.pyha...@postgrespro.ru>
Date: Wed, 30 Jun 2021 22:24:56 +0300
Subject: [PATCH] Set relhasindex for partitioned tables correctly.
The issue appeared after 0e69f705cc1a3df273b38c9883fb5765991e04fe:
in this commit we unconditionally set nindexes to 0 for partitioned
relations.
---
src/backend/commands/analyze.c | 24 +++++++++++++++++-------
src/test/regress/expected/vacuum.out | 17 +++++++++++++++++
src/test/regress/sql/vacuum.sql | 11 +++++++++++
3 files changed, 45 insertions(+), 7 deletions(-)
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 426c1e67109..c21cb7da3cf 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -422,18 +422,28 @@ do_analyze_rel(Relation onerel, VacuumParams *params,
* columns in the indexes. We do not analyze index columns if there was
* an explicit column list in the ANALYZE command, however. If we are
* doing a recursive scan, we don't want to touch the parent's indexes at
- * all.
+ * all. If we're processing a partitioned table, we need to know if there
+ * are any indexes, but we don't want to process them.
*/
- if (!inh)
+ if (!inh || onerel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+ {
vac_open_indexes(onerel, AccessShareLock, &nindexes, &Irel);
+ hasindex = nindexes > 0;
+ if (onerel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+ {
+ vac_close_indexes(nindexes, Irel, NoLock);
+ nindexes = 0;
+ Irel = NULL;
+ }
+ }
else
{
Irel = NULL;
nindexes = 0;
+ hasindex = false;
}
- hasindex = (nindexes > 0);
indexdata = NULL;
- if (hasindex)
+ if (nindexes > 0)
{
indexdata = (AnlIndexData *) palloc0(nindexes * sizeof(AnlIndexData));
for (ind = 0; ind < nindexes; ind++)
@@ -572,7 +582,7 @@ do_analyze_rel(Relation onerel, VacuumParams *params,
MemoryContextResetAndDeleteChildren(col_context);
}
- if (hasindex)
+ if (nindexes > 0)
compute_index_stats(onerel, totalrows,
indexdata, nindexes,
rows, numrows,
@@ -660,10 +670,10 @@ do_analyze_rel(Relation onerel, VacuumParams *params,
/*
* Partitioned tables don't have storage, so we don't set any fields
* in their pg_class entries except for reltuples, which is necessary
- * for auto-analyze to work properly.
+ * for auto-analyze to work properly, and relhasindex.
*/
vac_update_relstats(onerel, -1, totalrows,
- 0, false, InvalidTransactionId,
+ 0, hasindex, InvalidTransactionId,
InvalidMultiXactId,
in_outer_xact);
}
diff --git a/src/test/regress/expected/vacuum.out b/src/test/regress/expected/vacuum.out
index e5771462d57..e3d462b66fa 100644
--- a/src/test/regress/expected/vacuum.out
+++ b/src/test/regress/expected/vacuum.out
@@ -199,6 +199,23 @@ VACUUM ANALYZE vacparted(a,b,a);
ERROR: column "a" of relation "vacparted" appears more than once
ANALYZE vacparted(a,b,b);
ERROR: column "b" of relation "vacparted" appears more than once
+-- partitioned table with index
+CREATE TABLE vacparted_i (a int primary key, b varchar(100)) PARTITION BY HASH (a);
+CREATE TABLE vacparted_i1 PARTITION OF vacparted_i FOR VALUES WITH ( MODULUS 2, REMAINDER 0);
+CREATE TABLE vacparted_i2 PARTITION OF vacparted_i FOR VALUES WITH ( MODULUS 2, REMAINDER 1);
+INSERT INTO vacparted_i SELECT i, 'test_'|| i from generate_series(1,10) i;
+VACUUM (ANALYZE) vacparted_i;
+VACUUM (FULL) vacparted_i;
+VACUUM (FREEZE) vacparted_i;
+SELECT relname,relhasindex from pg_class where relname LIKE 'vacparted_i%' and relkind in ('p','r') ORDER BY relname;
+ relname | relhasindex
+--------------+-------------
+ vacparted_i | t
+ vacparted_i1 | t
+ vacparted_i2 | t
+(3 rows)
+
+DROP TABLE vacparted_i;
-- multiple tables specified
VACUUM vaccluster, vactst;
VACUUM vacparted, does_not_exist;
diff --git a/src/test/regress/sql/vacuum.sql b/src/test/regress/sql/vacuum.sql
index f220fc28a70..5c4c4159ac9 100644
--- a/src/test/regress/sql/vacuum.sql
+++ b/src/test/regress/sql/vacuum.sql
@@ -170,6 +170,17 @@ VACUUM (FREEZE) vacparted;
VACUUM ANALYZE vacparted(a,b,a);
ANALYZE vacparted(a,b,b);
+-- partitioned table with index
+CREATE TABLE vacparted_i (a int primary key, b varchar(100)) PARTITION BY HASH (a);
+CREATE TABLE vacparted_i1 PARTITION OF vacparted_i FOR VALUES WITH ( MODULUS 2, REMAINDER 0);
+CREATE TABLE vacparted_i2 PARTITION OF vacparted_i FOR VALUES WITH ( MODULUS 2, REMAINDER 1);
+INSERT INTO vacparted_i SELECT i, 'test_'|| i from generate_series(1,10) i;
+VACUUM (ANALYZE) vacparted_i;
+VACUUM (FULL) vacparted_i;
+VACUUM (FREEZE) vacparted_i;
+SELECT relname,relhasindex from pg_class where relname LIKE 'vacparted_i%' and relkind in ('p','r') ORDER BY relname;
+DROP TABLE vacparted_i;
+
-- multiple tables specified
VACUUM vaccluster, vactst;
VACUUM vacparted, does_not_exist;
--
2.25.1