Give a better error for duplicate entries in VACUUM/ANALYZE column list.
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 21 Sep 2017 22:13:11 +0000 (18:13 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 21 Sep 2017 22:13:11 +0000 (18:13 -0400)
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
src/test/regress/expected/vacuum.out
src/test/regress/sql/vacuum.sql

index 2b638271b3dbd85f7c95c1ec28d8f00227bdd427..28b679d030700649bfda9df6782cdc1aadbae795 100644 (file)
@@ -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++;
index 6f686630875bc687a0cc87fd6973d02c4f41b306..a16f26da7e7d4d1848fbd5cab075486da302d8ef 100644 (file)
@@ -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;
index 7c5fb049176bbd12305099e36b96c1a25ed74dd4..96a848ca954b0a0a11f1b323ca75b6aed79e1a77 100644 (file)
@@ -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;