setup_cancel_handler(NULL);
- if (concurrentCons > 1)
- {
- /*
- * Index-level REINDEX is not supported with multiple jobs as we
- * cannot control the concurrent processing of multiple indexes
- * depending on the same relation.
- */
- if (indexes.head != NULL)
- pg_fatal("cannot use multiple jobs to reindex indexes");
-
- if (syscatalog)
- pg_fatal("cannot use multiple jobs to reindex system catalogs");
- }
+ if (concurrentCons > 1 && syscatalog)
+ pg_fatal("cannot use multiple jobs to reindex system catalogs");
if (alldb)
{
if (indexes.head != NULL)
reindex_one_database(&cparams, REINDEX_INDEX, &indexes,
progname, echo, verbose,
- concurrently, 1, tablespace);
+ concurrently, concurrentCons, tablespace);
if (tables.head != NULL)
reindex_one_database(&cparams, REINDEX_TABLE, &tables,
{
PGconn *conn;
SimpleStringListCell *cell;
+ SimpleStringListCell *indices_tables_cell;
bool parallel = concurrentCons > 1;
SimpleStringList *process_list = user_list;
+ SimpleStringList *indices_tables_list = NULL;
ReindexType process_type = type;
ParallelSlotArray *sa;
bool failed = false;
int items_count = 0;
+ char *prev_index_table_name = NULL;
+ ParallelSlot *free_slot = NULL;
conn = connectDatabase(cparams, progname, echo, false, true);
return;
break;
- case REINDEX_SYSTEM:
case REINDEX_INDEX:
+ Assert(user_list != NULL);
+
+ /*
+ * Build a list of relations from the indices. This will
+ * accordingly reorder the list of indices too.
+ */
+ indices_tables_list = get_parallel_object_list(conn, process_type,
+ user_list, echo);
+
+ if (indices_tables_list)
+ indices_tables_cell = indices_tables_list->head;
+
+ /* Bail out if nothing to process */
+ if (process_list == NULL)
+ return;
+ break;
+
+ case REINDEX_SYSTEM:
/* not supported */
Assert(false);
break;
do
{
const char *objname = cell->val;
- ParallelSlot *free_slot = NULL;
+ bool need_new_slot = true;
if (CancelRequested)
{
goto finish;
}
- free_slot = ParallelSlotsGetIdle(sa, NULL);
- if (!free_slot)
+ /*
+ * For parallel index-level REINDEX, the indices of the same table are
+ * ordered together and they are to be processed by the same job. So,
+ * we don't switch the job as soon as the index belongs to the same
+ * table as the previous one.
+ */
+ if (parallel && process_type == REINDEX_INDEX)
+ {
+ if (prev_index_table_name != NULL &&
+ strcmp(prev_index_table_name, indices_tables_cell->val) == 0)
+ need_new_slot = false;
+ prev_index_table_name = indices_tables_cell->val;
+ indices_tables_cell = indices_tables_cell->next;
+ }
+
+ if (need_new_slot)
{
- failed = true;
- goto finish;
+ free_slot = ParallelSlotsGetIdle(sa, NULL);
+ if (!free_slot)
+ {
+ failed = true;
+ goto finish;
+ }
+
+ ParallelSlotSetHandler(free_slot, TableCommandResultHandler, NULL);
}
- ParallelSlotSetHandler(free_slot, TableCommandResultHandler, NULL);
run_reindex_command(free_slot->connection, process_type, objname,
echo, verbose, concurrently, true, tablespace);
pg_free(process_list);
}
+ if (indices_tables_list)
+ {
+ simple_string_list_destroy(indices_tables_list);
+ pg_free(indices_tables_list);
+ }
+
ParallelSlotsTerminate(sa);
pfree(sa);
}
break;
- case REINDEX_SYSTEM:
case REINDEX_INDEX:
+ {
+ SimpleStringListCell *cell;
+
+ Assert(user_list != NULL);
+
+ /*
+ * Straight-forward index-level REINDEX is not supported with
+ * multiple jobs as we cannot control the concurrent
+ * processing of multiple indexes depending on the same
+ * relation. But we can extract the appropriate table name
+ * for the index and put REINDEX INDEX commands into different
+ * jobs, according to the parent tables.
+ *
+ * We will order the results to group the same tables
+ * together. We fetch index names as well to build a new list
+ * of them with matching order.
+ */
+ appendPQExpBufferStr(&catalog_query,
+ "SELECT t.relname, n.nspname, i.relname\n"
+ "FROM pg_catalog.pg_index x\n"
+ "JOIN pg_catalog.pg_class t ON t.oid = x.indrelid\n"
+ "JOIN pg_catalog.pg_class i ON i.oid = x.indexrelid\n"
+ "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.relnamespace\n"
+ "WHERE x.indexrelid OPERATOR(pg_catalog.=) ANY(ARRAY['");
+
+ for (cell = user_list->head; cell; cell = cell->next)
+ {
+ if (cell != user_list->head)
+ appendPQExpBufferStr(&catalog_query, "', '");
+
+ appendQualifiedRelation(&catalog_query, cell->val, conn, echo);
+ }
+
+ /*
+ * Order tables by the size of its greatest index. Within the
+ * table, order indexes by their sizes.
+ */
+ appendPQExpBufferStr(&catalog_query,
+ "']::pg_catalog.regclass[])\n"
+ "ORDER BY max(i.relpages) OVER \n"
+ " (PARTITION BY n.nspname, t.relname),\n"
+ " n.nspname, t.relname, i.relpages;\n");
+
+ /*
+ * We're going to re-order the user_list to match the order of
+ * tables. So, empty the user_list to fill it from the query
+ * result.
+ */
+ simple_string_list_destroy(user_list);
+ user_list->head = user_list->tail = NULL;
+ }
+ break;
+
+ case REINDEX_SYSTEM:
case REINDEX_TABLE:
Assert(false);
break;
simple_string_list_append(tables, buf.data);
resetPQExpBuffer(&buf);
+
+ if (type == REINDEX_INDEX)
+ {
+ /*
+ * For index-level REINDEX, rebuild the list of indexes to match
+ * the order of tables list.
+ */
+ appendPQExpBufferStr(&buf,
+ fmtQualifiedId(PQgetvalue(res, i, 1),
+ PQgetvalue(res, i, 2)));
+
+ simple_string_list_append(user_list, buf.data);
+ resetPQExpBuffer(&buf);
+ }
}
termPQExpBuffer(&buf);
PQclear(res);