Fix pg_upgrade for invalid indexes
authorBruce Momjian <bruce@momjian.us>
Tue, 11 Dec 2012 20:09:22 +0000 (15:09 -0500)
committerBruce Momjian <bruce@momjian.us>
Tue, 11 Dec 2012 20:09:22 +0000 (15:09 -0500)
All versions of pg_upgrade upgraded invalid indexes caused by CREATE
INDEX CONCURRENTLY failures and marked them as valid.  The patch adds a
check to all pg_upgrade versions and throws an error during upgrade or
--check.

Backpatch to 9.2, 9.1, 9.0.  Patch slightly adjusted.

contrib/pg_upgrade/check.c

index eaa28f0b7ed686a9cafe9028f21cefb0977a383c..7e5fc2cfdd87907409ad9442d995352589bfb6bb 100644 (file)
@@ -17,6 +17,7 @@ static void check_locale_and_encoding(migratorContext *ctx, ControlData *oldctrl
 static void check_for_isn_and_int8_passing_mismatch(migratorContext *ctx,
                                                                                                Cluster whichCluster);
 static void check_for_reg_data_type_usage(migratorContext *ctx, Cluster whichCluster);
+static void check_for_invalid_indexes(migratorContext *ctx, Cluster whichCluster);
 
 
 /*
@@ -96,6 +97,7 @@ check_old_cluster(migratorContext *ctx, bool live_check,
 
        check_for_reg_data_type_usage(ctx, CLUSTER_OLD);
        check_for_isn_and_int8_passing_mismatch(ctx, CLUSTER_OLD);
+       check_for_invalid_indexes(ctx, CLUSTER_OLD);
 
        /* old = PG 8.3 checks? */
        if (GET_MAJOR_VERSION(ctx->old.major_version) <= 803)
@@ -676,3 +678,93 @@ check_for_reg_data_type_usage(migratorContext *ctx, Cluster whichCluster)
        else
                check_ok(ctx);
 }
+
+
+/*
+ * check_for_invalid_indexes()
+ *
+ *     CREATE INDEX CONCURRENTLY can create invalid indexes if the index build
+ *     fails.  These are dumped as valid indexes by pg_dump, but the
+ *     underlying files are still invalid indexes.  This checks to make sure
+ *     no invalid indexes exist, either failed index builds or concurrent
+ *     indexes in the process of being created.
+ */
+static void
+check_for_invalid_indexes(migratorContext *ctx, Cluster whichCluster)
+{
+       ClusterInfo *cluster = (whichCluster == CLUSTER_OLD) ?
+       &ctx->old : &ctx->new;
+       int                     dbnum;
+       FILE       *script = NULL;
+       bool            found = false;
+       char            output_path[MAXPGPATH];
+
+       prep_status(ctx, "Checking for invalid indexes from concurrent index builds");
+
+       snprintf(output_path, sizeof(output_path), "invalid_indexes.txt");
+
+       for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
+       {
+               PGresult   *res;
+               bool            db_used = false;
+               int                     ntups;
+               int                     rowno;
+               int                     i_nspname,
+                                       i_relname;
+               DbInfo     *active_db = &cluster->dbarr.dbs[dbnum];
+               PGconn     *conn = connectToServer(ctx, active_db->db_name, whichCluster);
+
+               res = executeQueryOrDie(ctx, conn,
+                                                               "SELECT n.nspname, c.relname "
+                                                               "FROM   pg_catalog.pg_class c, "
+                                                               "               pg_catalog.pg_namespace n, "
+                                                               "               pg_catalog.pg_index i "
+                                                               "WHERE  (i.indisvalid = false OR "
+                                                               "                i.indisready = false) AND "
+                                                               "               i.indexrelid = c.oid AND "
+                                                               "               c.relnamespace = n.oid AND "
+                                                               /* we do not migrate these, so skip them */
+                                                           "           n.nspname != 'pg_catalog' AND "
+                                                               "               n.nspname != 'information_schema' AND "
+                                                               /* indexes do not have toast tables */
+                                                               "               n.nspname != 'pg_toast'");
+
+               ntups = PQntuples(res);
+               i_nspname = PQfnumber(res, "nspname");
+               i_relname = PQfnumber(res, "relname");
+               for (rowno = 0; rowno < ntups; rowno++)
+               {
+                       found = true;
+                       if (script == NULL && (script = fopen(output_path, "w")) == NULL)
+                               pg_log(ctx, PG_FATAL, "Could not create necessary file:  %s\n", output_path);
+                       if (!db_used)
+                       {
+                               fprintf(script, "Database: %s\n", active_db->db_name);
+                               db_used = true;
+                       }
+                       fprintf(script, "  %s.%s\n",
+                                       PQgetvalue(res, rowno, i_nspname),
+                                       PQgetvalue(res, rowno, i_relname));
+               }
+
+               PQclear(res);
+
+               PQfinish(conn);
+       }
+
+       if (script)
+               fclose(script);
+
+       if (found)
+       {
+               pg_log(ctx, PG_REPORT, "fatal\n");
+               pg_log(ctx, PG_FATAL,
+                          "Your installation contains invalid indexes due to failed or\n"
+                          "currently running CREATE INDEX CONCURRENTLY operations.  You\n"
+                          "cannot upgrade until these indexes are valid or removed.  A\n"
+                          "list of the problem indexes is in the file:\n"
+                          "    %s\n\n", output_path);
+       }
+       else
+               check_ok(ctx);
+}