diff --git a/src/bin/scripts/reindexdb.c b/src/bin/scripts/reindexdb.c index 665864fd22..68ce43afc9 100644 --- a/src/bin/scripts/reindexdb.c +++ b/src/bin/scripts/reindexdb.c @@ -33,10 +33,14 @@ typedef enum ReindexType } ReindexType; -static SimpleStringList *get_parallel_object_list(PGconn *conn, +static SimpleStringList *get_parallel_tables_list(PGconn *conn, ReindexType type, SimpleStringList *user_list, bool echo); +static void get_parallel_tabidx_list(PGconn *conn, + SimpleStringList *index_list, + SimpleStringList *table_list, + bool echo); static void reindex_one_database(ConnParams *cparams, ReindexType type, SimpleStringList *user_list, const char *progname, @@ -333,26 +337,32 @@ reindex_one_database(ConnParams *cparams, ReindexType type, case REINDEX_DATABASE: /* Build a list of relations from the database */ - process_list = get_parallel_object_list(conn, process_type, + process_list = get_parallel_tables_list(conn, process_type, user_list, echo); process_type = REINDEX_TABLE; /* Bail out if nothing to process */ if (process_list == NULL) + { + PQfinish(conn); return; + } break; case REINDEX_SCHEMA: Assert(user_list != NULL); /* Build a list of relations from all the schemas */ - process_list = get_parallel_object_list(conn, process_type, + process_list = get_parallel_tables_list(conn, process_type, user_list, echo); process_type = REINDEX_TABLE; /* Bail out if nothing to process */ if (process_list == NULL) + { + PQfinish(conn); return; + } break; case REINDEX_INDEX: @@ -362,21 +372,16 @@ reindex_one_database(ConnParams *cparams, ReindexType type, * 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); + get_parallel_tabidx_list(conn, user_list, + indices_tables_list, echo); - /* - * Bail out if nothing to process. 'user_list' was modified - * in-place, so check if it has at least one cell. - */ - if (user_list->head == NULL) + /* Bail out if nothing to process */ + if (indices_tables_list == NULL) + { + PQfinish(conn); return; + } - /* - * Assuming 'user_list' is not empty, 'indices_tables_list' - * shouldn't be empty as well. - */ - Assert(indices_tables_list != NULL); indices_tables_cell = indices_tables_list->head; break; @@ -480,6 +485,7 @@ finish: ParallelSlotsTerminate(sa); pfree(sa); + PQfinish(conn); if (failed) exit(1); @@ -613,7 +619,7 @@ run_reindex_command(PGconn *conn, ReindexType type, const char *name, } /* - * Prepare the list of objects to process by querying the catalogs. + * Prepare the list of tables to process by querying the catalogs. * * This function will return a SimpleStringList object containing the entire * list of tables in the given database that should be processed by a parallel @@ -621,15 +627,13 @@ run_reindex_command(PGconn *conn, ReindexType type, const char *name, * table. */ static SimpleStringList * -get_parallel_object_list(PGconn *conn, ReindexType type, +get_parallel_tables_list(PGconn *conn, ReindexType type, SimpleStringList *user_list, bool echo) { PQExpBufferData catalog_query; - PQExpBufferData buf; PGresult *res; SimpleStringList *tables; - int ntups, - i; + int ntups; initPQExpBuffer(&catalog_query); @@ -658,7 +662,6 @@ get_parallel_object_list(PGconn *conn, ReindexType type, case REINDEX_SCHEMA: { SimpleStringListCell *cell; - bool nsp_listed = false; Assert(user_list != NULL); @@ -680,14 +683,10 @@ get_parallel_object_list(PGconn *conn, ReindexType type, for (cell = user_list->head; cell; cell = cell->next) { - const char *nspname = cell->val; - - if (nsp_listed) - appendPQExpBufferStr(&catalog_query, ", "); - else - nsp_listed = true; + if (cell != user_list->head) + appendPQExpBufferChar(&catalog_query, ','); - appendStringLiteralConn(&catalog_query, nspname, conn); + appendStringLiteralConn(&catalog_query, cell->val, conn); } appendPQExpBufferStr(&catalog_query, ")\n" @@ -696,59 +695,6 @@ get_parallel_object_list(PGconn *conn, ReindexType type, break; 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); @@ -765,45 +711,134 @@ get_parallel_object_list(PGconn *conn, ReindexType type, if (ntups == 0) { PQclear(res); - PQfinish(conn); return NULL; } tables = pg_malloc0(sizeof(SimpleStringList)); /* Build qualified identifiers for each table */ - initPQExpBuffer(&buf); - for (i = 0; i < ntups; i++) + for (int i = 0; i < ntups; i++) { - appendPQExpBufferStr(&buf, + simple_string_list_append(tables, fmtQualifiedIdEnc(PQgetvalue(res, i, 1), PQgetvalue(res, i, 0), PQclientEncoding(conn))); - - 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, - fmtQualifiedIdEnc(PQgetvalue(res, i, 1), - PQgetvalue(res, i, 2), - PQclientEncoding(conn))); - - simple_string_list_append(user_list, buf.data); - resetPQExpBuffer(&buf); - } } - termPQExpBuffer(&buf); PQclear(res); return tables; } +/* + * Prepare the list of tables to process by querying the catalogs. + * + * This function will return a SimpleStringList object containing the entire + * list of tables in the given database that should be processed by a parallel + * database-wide reindex (excluding system tables), or NULL if there's no such + * table. + */ +static void +get_parallel_tabidx_list(PGconn *conn, + SimpleStringList *index_list, + SimpleStringList *table_list, + bool echo) +{ + PQExpBufferData catalog_query; + PGresult *res; + SimpleStringListCell *cell; + int ntups; + + Assert(index_list != NULL); + Assert(table_list == NULL); + + initPQExpBuffer(&catalog_query); + + /* + * The queries here are using a safe search_path, so there's no need to + * fully qualify everything. + */ + + /* + * 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 = index_list->head; cell; cell = cell->next) + { + if (cell != index_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 index_list to match the order of + * tables. So, empty the index_list to fill it from the query + * result. + */ + simple_string_list_destroy(index_list); + index_list->head = index_list->tail = NULL; + + res = executeQuery(conn, catalog_query.data, echo); + termPQExpBuffer(&catalog_query); + + /* + * If no rows are returned, there are no matching tables, so we are done. + */ + ntups = PQntuples(res); + if (ntups == 0) + { + PQclear(res); + return; + } + + table_list = pg_malloc0(sizeof(SimpleStringList)); + + /* Build qualified identifiers for each index */ + for (int i = 0; i < ntups; i++) + { + simple_string_list_append(table_list, + fmtQualifiedIdEnc(PQgetvalue(res, i, 1), + PQgetvalue(res, i, 0), + PQclientEncoding(conn))); + + /* + * For index-level REINDEX, rebuild the list of indices to match + * the order of tables list. + */ + simple_string_list_append(index_list, + fmtQualifiedIdEnc(PQgetvalue(res, i, 1), + PQgetvalue(res, i, 2), + PQclientEncoding(conn))); + } + PQclear(res); +} + static void reindex_all_databases(ConnParams *cparams, const char *progname, bool echo, bool quiet, bool verbose,