diff --git a/src/bin/scripts/reindexdb.c b/src/bin/scripts/reindexdb.c index d7fb16d3c85..d7784de3f55 100644 --- a/src/bin/scripts/reindexdb.c +++ b/src/bin/scripts/reindexdb.c @@ -434,7 +434,7 @@ reindex_one_database(ConnParams *cparams, ReindexType type, ParallelSlotSetHandler(free_slot, TableCommandResultHandler, NULL); initPQExpBuffer(&sql); - if (parallel && process_type == REINDEX_INDEX) + if (parallel && process_type == REINDEX_INDEX && !concurrently) { /* * For parallel index-level REINDEX, the indices of the same table @@ -455,14 +455,51 @@ reindex_one_database(ConnParams *cparams, ReindexType type, echo, verbose, concurrently, tablespace, &sql); } indices_tables_cell = indices_tables_cell->next; + run_reindex_command(free_slot->connection, process_type, objname, + echo, &sql); + } + else if (parallel && process_type == REINDEX_INDEX) + { + /* + * REINDEX CONCURRENTLY cannot run in a transaction block, so it + * cannot be part of a multi-statement simple query, which would + * be an implicit transaction block. The indices of the same + * table still have to be processed by the same job, to avoid + * concurrent REINDEX CONCURRENTLY commands on the same table, + * which could deadlock. So, each command is sent separately, + * waiting for it to complete before sending the next one. + */ + gen_reindex_command(free_slot->connection, process_type, objname, + echo, verbose, concurrently, tablespace, &sql); + run_reindex_command(free_slot->connection, process_type, objname, + echo, &sql); + if (!consumeQueryResult(free_slot)) + failed = true; + while (indices_tables_cell->next && + indices_tables_cell->val == indices_tables_cell->next->val) + { + indices_tables_cell = indices_tables_cell->next; + cell = cell->next; + objname = cell->val; + termPQExpBuffer(&sql); + initPQExpBuffer(&sql); + gen_reindex_command(free_slot->connection, process_type, objname, + echo, verbose, concurrently, tablespace, &sql); + run_reindex_command(free_slot->connection, process_type, objname, + echo, &sql); + if (!consumeQueryResult(free_slot)) + failed = true; + } + indices_tables_cell = indices_tables_cell->next; + ParallelSlotSetIdle(free_slot); } else { gen_reindex_command(free_slot->connection, process_type, objname, echo, verbose, concurrently, tablespace, &sql); + run_reindex_command(free_slot->connection, process_type, objname, + echo, &sql); } - run_reindex_command(free_slot->connection, process_type, objname, - echo, &sql); termPQExpBuffer(&sql); cell = cell->next; diff --git a/src/bin/scripts/t/090_reindexdb.pl b/src/bin/scripts/t/090_reindexdb.pl index ae7d3724464..1013120f312 100644 --- a/src/bin/scripts/t/090_reindexdb.pl +++ b/src/bin/scripts/t/090_reindexdb.pl @@ -168,6 +168,30 @@ $node->issues_sql_like( [ 'reindexdb', '--concurrently', '--index' => 'test1x', 'postgres' ], qr/statement: REINDEX INDEX CONCURRENTLY public\.test1x;/, 'reindex specific index concurrently'); + +# Multiple indexes of the same table with parallel jobs must not be +# batched into a single multi-statement query when using --concurrently, +# as REINDEX CONCURRENTLY cannot run inside a transaction block. +$node->safe_psql('postgres', + 'CREATE TABLE test2 (a int); CREATE INDEX test2x ON test2 (a);' + . ' CREATE INDEX test2y ON test2 (a);'); +$node->command_ok( + [ + 'reindexdb', '--jobs' => '2', '--concurrently', + '--index' => 'test2x', + '--index' => 'test2y', + 'postgres', + ], + 'reindex two indexes of the same table concurrently with two jobs'); +$node->command_ok( + [ + 'reindexdb', '--jobs' => '2', + '--index' => 'test2x', + '--index' => 'test2y', + 'postgres', + ], + 'reindex two indexes of the same table with two jobs'); + $node->issues_sql_like( [ 'reindexdb', '--concurrently', '--schema' => 'public', 'postgres' ], qr/statement: REINDEX SCHEMA CONCURRENTLY public;/, diff --git a/src/fe_utils/parallel_slot.c b/src/fe_utils/parallel_slot.c index fb9e6cc4ec1..831e0c27878 100644 --- a/src/fe_utils/parallel_slot.c +++ b/src/fe_utils/parallel_slot.c @@ -54,7 +54,7 @@ processQueryResult(ParallelSlot *slot, PGresult *result) * nothing remains. If at least one error is encountered, return false. * Note that this will block if the connection is busy. */ -static bool +bool consumeQueryResult(ParallelSlot *slot) { bool ok = true; diff --git a/src/include/fe_utils/parallel_slot.h b/src/include/fe_utils/parallel_slot.h index a6ebe273ce0..6d5972614bf 100644 --- a/src/include/fe_utils/parallel_slot.h +++ b/src/include/fe_utils/parallel_slot.h @@ -78,6 +78,15 @@ extern void ParallelSlotsTerminate(ParallelSlotArray *sa); extern bool ParallelSlotsWaitCompletion(ParallelSlotArray *sa); +/* + * Wait for the results of the query currently being processed by the given + * slot, and process them with the handler set for this slot. Returns + * false if the query failed, true otherwise. The slot is not marked as + * idle by this function. Note that this will block if the connection is + * busy. + */ +extern bool consumeQueryResult(ParallelSlot *slot); + extern bool TableCommandResultHandler(PGresult *res, PGconn *conn, void *context);