From 71480501057fee9fa3649b072173ff10e2b842d0 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Thu, 21 Sep 2017 18:13:11 -0400 Subject: [PATCH] Give a better error for duplicate entries in VACUUM/ANALYZE column list. Previously, the code didn't think about this case and would just try to analyze such a column twice. That would fail at the point of inserting the second version of the pg_statistic row, with obscure error messsages like "duplicate key value violates unique constraint" or "tuple already updated by self", depending on context and PG version. We could allow the case by ignoring duplicate column specifications, but it seems better to reject it explicitly. The bogus error messages seem like arguably a bug, so back-patch to all supported versions. Nathan Bossart, per a report from Michael Paquier, and whacked around a bit by me. Discussion: https://postgr.es/m/E061A8E3-5E3D-494D-94F0-E8A9B312BBFC@amazon.com --- src/backend/commands/analyze.c | 25 ++++++++++++++++++------- src/test/regress/expected/vacuum.out | 5 +++++ src/test/regress/sql/vacuum.sql | 5 +++++ 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index 08fc18e96b..1248b2ee5c 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -370,10 +370,14 @@ do_analyze_rel(Relation onerel, int options, VacuumParams *params, /* * Determine which columns to analyze * - * Note that system attributes are never analyzed. + * Note that system attributes are never analyzed, so we just reject them + * at the lookup stage. We also reject duplicate column mentions. (We + * could alternatively ignore duplicates, but analyzing a column twice + * won't work; we'd end up making a conflicting update in pg_statistic.) */ if (va_cols != NIL) { + Bitmapset *unique_cols = NULL; ListCell *le; vacattrstats = (VacAttrStats **) palloc(list_length(va_cols) * @@ -389,6 +393,13 @@ do_analyze_rel(Relation onerel, int options, VacuumParams *params, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("column \"%s\" of relation \"%s\" does not exist", col, RelationGetRelationName(onerel)))); + if (bms_is_member(i, unique_cols)) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_COLUMN), + errmsg("column \"%s\" of relation \"%s\" is specified twice", + col, RelationGetRelationName(onerel)))); + unique_cols = bms_add_member(unique_cols, i); + vacattrstats[tcnt] = examine_attribute(onerel, i, NULL); if (vacattrstats[tcnt] != NULL) tcnt++; @@ -527,9 +538,9 @@ do_analyze_rel(Relation onerel, int options, VacuumParams *params, stats->rows = rows; stats->tupDesc = onerel->rd_att; stats->compute_stats(stats, - std_fetch_func, - numrows, - totalrows); + std_fetch_func, + numrows, + totalrows); /* * If the appropriate flavor of the n_distinct option is @@ -831,9 +842,9 @@ compute_index_stats(Relation onerel, double totalrows, stats->exprnulls = exprnulls + i; stats->rowstride = attr_cnt; stats->compute_stats(stats, - ind_fetch_func, - numindexrows, - totalindexrows); + ind_fetch_func, + numindexrows, + totalindexrows); /* * If the n_distinct option is specified, it overrides the diff --git a/src/test/regress/expected/vacuum.out b/src/test/regress/expected/vacuum.out index 6f68663087..a16f26da7e 100644 --- a/src/test/regress/expected/vacuum.out +++ b/src/test/regress/expected/vacuum.out @@ -90,4 +90,9 @@ UPDATE vacparted SET b = 'b'; VACUUM (ANALYZE) vacparted; VACUUM (FULL) vacparted; VACUUM (FREEZE) vacparted; +-- check behavior with duplicate column mentions +VACUUM ANALYZE vacparted(a,b,a); +ERROR: column "a" of relation "vacparted" is specified twice +ANALYZE vacparted(a,b,b); +ERROR: column "b" of relation "vacparted" is specified twice DROP TABLE vacparted; diff --git a/src/test/regress/sql/vacuum.sql b/src/test/regress/sql/vacuum.sql index 7c5fb04917..96a848ca95 100644 --- a/src/test/regress/sql/vacuum.sql +++ b/src/test/regress/sql/vacuum.sql @@ -73,4 +73,9 @@ UPDATE vacparted SET b = 'b'; VACUUM (ANALYZE) vacparted; VACUUM (FULL) vacparted; VACUUM (FREEZE) vacparted; + +-- check behavior with duplicate column mentions +VACUUM ANALYZE vacparted(a,b,a); +ANALYZE vacparted(a,b,b); + DROP TABLE vacparted;