0004-Add-a-glibc-dependent-option.patch
application/octet-stream
Filename: 0004-Add-a-glibc-dependent-option.patch
Type: application/octet-stream
Part: 2
Patch
Format: format-patch
Series: patch 0004
Subject: Add a --glibc-dependent option.
| File | + | − |
|---|---|---|
| src/bin/scripts/reindexdb.c | 306 | 81 |
| src/bin/scripts/t/092_reindexdb_glibc.pl | 106 | 0 |
From 25c668725ad0619351d32920ae882e9f9aec1e56 Mon Sep 17 00:00:00 2001
From: Julien Rouhaud <julien.rouhaud@free.fr>
Date: Fri, 28 Jun 2019 14:21:17 +0200
Subject: [PATCH 4/4] Add a --glibc-dependent option.
When this flag is passed, all indexes that depend on a glibc collation will be
reindexed. This is especially important given the upcoming glibc 2.28 version
which break the ordering of most collations, including en_US. This option is
compatible with the --job, --dbname and the --all options (among others) but
not the --schema, --table and --index options.
---
src/bin/scripts/reindexdb.c | 387 ++++++++++++++++++-----
src/bin/scripts/t/092_reindexdb_glibc.pl | 106 +++++++
2 files changed, 412 insertions(+), 81 deletions(-)
create mode 100644 src/bin/scripts/t/092_reindexdb_glibc.pl
diff --git a/src/bin/scripts/reindexdb.c b/src/bin/scripts/reindexdb.c
index f8a78a1b4a..3742cebef0 100644
--- a/src/bin/scripts/reindexdb.c
+++ b/src/bin/scripts/reindexdb.c
@@ -10,6 +10,9 @@
*/
#include "postgres_fe.h"
+
+#include "catalog/pg_class_d.h"
+
#include "common.h"
#include "common/logging.h"
#include "fe_utils/connect.h"
@@ -30,25 +33,29 @@ typedef enum ReindexType
static ReindexType get_parallel_object_list(PGconn *conn, ReindexType type,
SimpleStringList *user_list,
SimplePtrList * process_list,
- const char *progname, bool echo);
+ const char *progname, bool echo,
+ bool glibc_dependent);
static void reindex_one_database(const char *dbname, ReindexType type,
SimpleStringList *user_list, const char *host,
const char *port, const char *username,
enum trivalue prompt_password, const char *progname,
bool echo, bool verbose, bool concurrently,
- int concurrentCons);
+ int concurrentCons, bool glibc_dependent);
static void reindex_all_databases(const char *maintenance_db,
const char *host, const char *port,
const char *username, enum trivalue prompt_password,
const char *progname, bool echo,
bool quiet, bool verbose, bool concurrently,
- int concurrentCons);
+ int concurrentCons, bool glibc_dependent);
static void run_reindex_command(PGconn *conn, ReindexType type,
const char *name, const char *progname, bool echo,
bool verbose, bool concurrently, bool async);
static void slot_process_item(ParallelSlot *slot, int *pending_conn,
ReindexType process_type, const char *progname, bool echo,
bool verbose, bool concurrently, bool parallel);
+static char *get_glibc_query(PGconn *conn);
+static void append_indexes_per_table(PGresult *res,
+ SimplePtrList *process_list);
static void help(const char *progname);
@@ -73,6 +80,7 @@ main(int argc, char *argv[])
{"verbose", no_argument, NULL, 'v'},
{"concurrently", no_argument, NULL, 1},
{"maintenance-db", required_argument, NULL, 2},
+ {"glibc-dependent", no_argument, NULL, 3},
{NULL, 0, NULL, 0}
};
@@ -92,6 +100,7 @@ main(int argc, char *argv[])
bool quiet = false;
bool verbose = false;
bool concurrently = false;
+ bool glibc_dependent = false;
SimpleStringList indexes = {NULL, NULL};
SimpleStringList tables = {NULL, NULL};
SimpleStringList schemas = {NULL, NULL};
@@ -174,6 +183,9 @@ main(int argc, char *argv[])
case 2:
maintenance_db = pg_strdup(optarg);
break;
+ case 3:
+ glibc_dependent = true;
+ break;
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
exit(1);
@@ -230,7 +242,7 @@ main(int argc, char *argv[])
reindex_all_databases(maintenance_db, host, port, username,
prompt_password, progname, echo, quiet, verbose,
- concurrently, 1);
+ concurrently, 1, glibc_dependent);
}
else if (syscatalog)
{
@@ -256,6 +268,12 @@ main(int argc, char *argv[])
exit(1);
}
+ if (glibc_dependent)
+ {
+ pg_log_error("cannot reindex glibc dependent objects and system catalogs at the same time");
+ exit(1);
+ }
+
if (dbname == NULL)
{
if (getenv("PGDATABASE"))
@@ -268,10 +286,17 @@ main(int argc, char *argv[])
reindex_one_database(dbname, REINDEX_SYSTEM, NULL, host,
port, username, prompt_password, progname,
- echo, verbose, concurrently, 1);
+ echo, verbose, concurrently, 1, false);
}
else
{
+ if (glibc_dependent && (schemas.head != NULL || indexes.head != NULL
+ || tables.head != NULL))
+ {
+ pg_log_error("cannot reindex glibc dependent objects and a subset of objects");
+ exit(1);
+ }
+
if (dbname == NULL)
{
if (getenv("PGDATABASE"))
@@ -286,7 +311,7 @@ main(int argc, char *argv[])
reindex_one_database(dbname, REINDEX_SCHEMA, &schemas, host,
port, username, prompt_password, progname,
echo, verbose, concurrently,
- Min(concurrentCons, nsp_count));
+ Min(concurrentCons, nsp_count), false);
if (indexes.head != NULL)
@@ -297,13 +322,13 @@ main(int argc, char *argv[])
reindex_one_database(dbname, REINDEX_INDEX, &indexes, host,
port, username, prompt_password, progname,
echo, verbose, concurrently,
- concurrentCons);
+ concurrentCons, false);
if (tables.head != NULL)
reindex_one_database(dbname, REINDEX_TABLE, &tables, host,
port, username, prompt_password, progname,
echo, verbose, concurrently,
- Min(concurrentCons, tbl_count));
+ Min(concurrentCons, tbl_count), false);
/*
* reindex database only if neither index nor table nor schema is
@@ -312,7 +337,8 @@ main(int argc, char *argv[])
if (indexes.head == NULL && tables.head == NULL && schemas.head == NULL)
reindex_one_database(dbname, REINDEX_DATABASE, NULL, host,
port, username, prompt_password, progname,
- echo, verbose, concurrently, concurrentCons);
+ echo, verbose, concurrently, concurrentCons,
+ glibc_dependent);
}
exit(0);
@@ -323,7 +349,8 @@ reindex_one_database(const char *dbname, ReindexType type,
SimpleStringList *user_list, const char *host,
const char *port, const char *username,
enum trivalue prompt_password, const char *progname, bool echo,
- bool verbose, bool concurrently, int concurrentCons)
+ bool verbose, bool concurrently, int concurrentCons,
+ bool glibc_dependent)
{
PGconn *conn;
bool parallel = concurrentCons > 1;
@@ -350,12 +377,58 @@ reindex_one_database(const char *dbname, ReindexType type,
{
if (user_list)
{
+ Assert(!glibc_dependent);
+
/*
* In non parallel mode, if the user provided a list, just use it
* as-is
*/
simple_ptr_list_append(&process_list, user_list);
}
+ else if (glibc_dependent)
+ {
+ SimpleStringList *dummy = palloc0(sizeof(SimpleStringList));
+ PGresult *res;
+ PQExpBufferData buf;
+ int ntups;
+ int i;
+
+ process_type = REINDEX_INDEX;
+
+ run_reindex_command(conn, REINDEX_SYSTEM, NULL, progname, echo,
+ verbose, concurrently, glibc_dependent);
+
+ res = executeQuery(conn, get_glibc_query(conn), progname, echo);
+
+ /*
+ * If no rows are returned, there are no matching tables, so we
+ * are done.
+ */
+ ntups = PQntuples(res);
+ if (ntups == 0)
+ {
+ PQclear(res);
+ PQfinish(conn);
+ return;
+ }
+
+ /* Build qualified identifiers for each table */
+ initPQExpBuffer(&buf);
+ for (i = 0; i < ntups; i++)
+ {
+ appendPQExpBufferStr(&buf,
+ fmtQualifiedId(PQgetvalue(res, i, 1),
+ PQgetvalue(res, i, 0)));
+
+ simple_string_list_append(dummy, buf.data);
+
+ resetPQExpBuffer(&buf);
+ }
+ simple_ptr_list_append(&process_list, dummy);
+
+ termPQExpBuffer(&buf);
+ PQclear(res);
+ }
else
{
/*
@@ -383,7 +456,8 @@ reindex_one_database(const char *dbname, ReindexType type,
/* Get the list of objects to process */
process_type = get_parallel_object_list(conn, type, user_list,
- &process_list, progname, echo);
+ &process_list, progname, echo,
+ glibc_dependent);
}
/* Lower down the number of required connections if needed. */
@@ -622,7 +696,7 @@ static ReindexType
get_parallel_object_list(PGconn *conn, ReindexType type,
SimpleStringList *user_list,
SimplePtrList * process_list, const char *progname,
- bool echo)
+ bool echo, bool glibc_dependent)
{
ReindexType process_type = type;
PQExpBufferData buf;
@@ -639,66 +713,88 @@ get_parallel_object_list(PGconn *conn, ReindexType type,
Assert(user_list == NULL);
- process_type = REINDEX_TABLE;
+ if (glibc_dependent)
+ {
+ process_type = REINDEX_INDEX;
- initPQExpBuffer(&catalog_query);
+ res = executeQuery(conn, get_glibc_query(conn), progname, echo);
- /*
- * This query is run using a safe search_path, so there's no need to
- * fully qualify everything.
- */
- appendPQExpBuffer(&catalog_query,
- "SELECT c.relname, ns.nspname\n"
- " FROM pg_catalog.pg_class c\n"
- " JOIN pg_catalog.pg_namespace ns"
- " ON c.relnamespace = ns.oid\n"
- " WHERE ns.nspname != 'pg_catalog'\n"
- " AND c.relkind IN ("
- CppAsString2(RELKIND_RELATION) ", "
- CppAsString2(RELKIND_MATVIEW) ")\n"
- " ORDER BY c.relpages DESC;");
-
- res = executeQuery(conn, catalog_query.data, progname, 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);
+ PQfinish(conn);
+ return process_type;
+ }
- /*
- * If no rows are returned, there are no matching tables, so we are
- * done.
- */
- ntups = PQntuples(res);
- if (ntups == 0)
- {
- PQclear(res);
- PQfinish(conn);
- return process_type;
+ append_indexes_per_table(res, process_list);
}
-
- /* Build qualified identifiers for each table */
- initPQExpBuffer(&buf);
- for (i = 0; i < ntups; i++)
+ else
{
- tables = pg_malloc0(sizeof(SimpleStringList));
+ process_type = REINDEX_TABLE;
+
+ initPQExpBuffer(&catalog_query);
+
+ /*
+ * This query is run using a safe search_path, so there's no need
+ * to fully qualify everything.
+ */
+ appendPQExpBuffer(&catalog_query,
+ "SELECT c.relname, ns.nspname\n"
+ " FROM pg_catalog.pg_class c\n"
+ " JOIN pg_catalog.pg_namespace ns"
+ " ON c.relnamespace = ns.oid\n"
+ " WHERE ns.nspname != 'pg_catalog'\n"
+ " AND c.relkind IN ("
+ CppAsString2(RELKIND_RELATION) ", "
+ CppAsString2(RELKIND_MATVIEW) ")\n"
+ " ORDER BY c.relpages DESC;");
+
+ res = executeQuery(conn, catalog_query.data, progname, 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);
+ PQfinish(conn);
+ return process_type;
+ }
+
+ /* Build qualified identifiers for each table */
+ initPQExpBuffer(&buf);
+ for (i = 0; i < ntups; i++)
+ {
+ tables = pg_malloc0(sizeof(SimpleStringList));
- appendPQExpBufferStr(&buf,
- fmtQualifiedId(PQgetvalue(res, i, 1),
- PQgetvalue(res, i, 0)));
+ appendPQExpBufferStr(&buf,
+ fmtQualifiedId(PQgetvalue(res, i, 1),
+ PQgetvalue(res, i, 0)));
- simple_string_list_append(tables, buf.data);
- simple_ptr_list_append(process_list, tables);
- resetPQExpBuffer(&buf);
+ simple_string_list_append(tables, buf.data);
+ simple_ptr_list_append(process_list, tables);
+ resetPQExpBuffer(&buf);
+ }
+ termPQExpBuffer(&buf);
+ PQclear(res);
}
- termPQExpBuffer(&buf);
- PQclear(res);
}
else if (type == REINDEX_INDEX)
{
- SimpleStringList *indexes = NULL;
SimpleStringListCell *cell;
PGresult *res;
int ntups;
- char *prev_rel = NULL;
Assert(user_list != NULL);
+ Assert(!glibc_dependent);
initPQExpBuffer(&catalog_query);
@@ -710,7 +806,8 @@ get_parallel_object_list(PGconn *conn, ReindexType type,
* locking issues that would be implied.
*/
appendPQExpBuffer(&catalog_query,
- "SELECT ic.relname, ns.nspname, tc.oid\n"
+ "SELECT ic.relname AS index_relname,"
+ " ns.nspname AS index_nspname, tc.oid AS table_oid\n"
" FROM pg_catalog.pg_index i\n"
" JOIN pg_catalog.pg_class ic"
" ON i.indexrelid OPERATOR(pg_catalog.=) ic.oid\n"
@@ -759,28 +856,7 @@ get_parallel_object_list(PGconn *conn, ReindexType type,
return process_type;
}
- indexes = pg_malloc0(sizeof(SimpleStringList));
-
- /* Build a list of qualified index name, aggregated per table */
- for (i = 0; i < ntups; i++)
- {
- if (!prev_rel)
- prev_rel = pg_strdup(PQgetvalue(res, i, 2));
-
- if (strcmp(PQgetvalue(res, i, 2), prev_rel) != 0)
- {
- simple_ptr_list_append(process_list, indexes);
-
- indexes = pg_malloc0(sizeof(SimpleStringList));
- pg_free(prev_rel);
- prev_rel = pg_strdup(PQgetvalue(res, i, 2));
- }
-
- simple_string_list_append(indexes,
- fmtQualifiedId(PQgetvalue(res, i, 1),
- PQgetvalue(res, i, 0)));
- }
- simple_ptr_list_append(process_list, indexes);
+ append_indexes_per_table(res, process_list);
PQclear(res);
}
@@ -796,6 +872,7 @@ get_parallel_object_list(PGconn *conn, ReindexType type,
SimpleStringListCell *cell;
Assert(user_list != NULL);
+ Assert(!glibc_dependent);
for (cell = user_list->head; cell; cell = cell->next)
{
@@ -809,6 +886,152 @@ get_parallel_object_list(PGconn *conn, ReindexType type,
return process_type;
}
+/*
+ * This function returns a query to retrieve all indexes that depends on a
+ * glibc stable sort ordering, and thus could get corrupted if that order
+ * changed. The returned query will be executed using a safe search_path, so
+ * there's no need to fully qualify everything.
+ *
+ * Indexes on the system catalogs are excluded, as those would get reindexed
+ * explicitly if a reindex on glibc-dependent indexes is asked.
+ *
+ * The list of impacted indexes is retrieved using the indclass field in
+ * pg_index and not the indkey field, as indexes must be retrieved too. The
+ * underlying type is then joined, and only indexes containing at least one
+ * column of type category String are kept. We however ignore special opclass
+ * varchar_pattern_ops and text_pattern_ops, as those do not depend on the
+ * underlying collation order.
+ *
+ * Note that there's no knowledge about specific glibc version or collation
+ * name.
+ */
+static char *
+get_glibc_query(PGconn *conn)
+{
+ /* pg 10 added pg_collation */
+ if (PQserverVersion(conn) >= 100000)
+ return "SELECT DISTINCT ic.relname AS index_relname,"
+ " n.nspname AS index_nspname, tc.oid AS table_oid,"
+ " pg_catalog.pg_indexes_size(tc.oid)"
+ " FROM pg_catalog.pg_class AS tc\n"
+ " JOIN pg_catalog.pg_index AS ix ON ix.indrelid = tc.oid\n"
+ " JOIN pg_catalog.pg_namespace AS n ON n.oid = tc.relnamespace\n"
+ " JOIN pg_catalog.pg_class AS ic ON ic.oid = ix.indexrelid\n"
+ " JOIN pg_catalog.pg_opclass AS opc ON opc.oid = ANY(ix.indclass)\n"
+ " JOIN pg_catalog.pg_type AS t ON t.oid = opc.opcintype\n"
+ " JOIN pg_catalog.pg_collation AS coll ON coll.oid = t.typcollation\n"
+ " WHERE t.typcategory = 'S'\n"
+ " AND n.nspname != 'pg_catalog'\n"
+ " AND tc.relkind IN ('m', 'r')\n"
+ " AND opc.opcname NOT IN ('text_pattern_ops', 'varchar_pattern_ops')\n"
+ " AND coll.collprovider IN ('d', 'c')\n"
+ " ORDER BY pg_catalog.pg_indexes_size(tc.oid) DESC;";
+ /* pg 9.3 added materialized views */
+ else if (PQserverVersion(conn) >= 90300)
+ return "SELECT DISTINCT ic.relname AS index_relname,"
+ " n.nspname AS index_nspname, tc.oid AS table_oid,"
+ " pg_catalog.pg_indexes_size(tc.oid)"
+ " FROM pg_catalog.pg_class AS tc\n"
+ " JOIN pg_catalog.pg_index AS ix ON ix.indrelid = tc.oid\n"
+ " JOIN pg_catalog.pg_namespace AS n ON n.oid = tc.relnamespace\n"
+ " JOIN pg_catalog.pg_class AS ic ON ic.oid = ix.indexrelid\n"
+ " JOIN pg_catalog.pg_opclass AS opc ON opc.oid = ANY(ix.indclass)\n"
+ " JOIN pg_catalog.pg_type AS t ON t.oid = opc.opcintype\n"
+ " WHERE t.typcategory = 'S'\n"
+ " AND n.nspname != 'pg_catalog'\n"
+ " AND tc.relkind IN ('m', 'r')\n"
+ " AND opc.opcname NOT IN ('text_pattern_ops', 'varchar_pattern_ops')\n"
+ " ORDER BY pg_catalog.pg_indexes_size(tc.oid) DESC;";
+ /* pg 9.0 added pg_indexes_size() */
+ else if (PQserverVersion(conn) >= 90000)
+ return "SELECT DISTINCT ic.relname AS index_relname,"
+ " n.nspname AS index_nspname, tc.oid AS table_oid,"
+ " pg_catalog.pg_indexes_size(tc.oid)"
+ " FROM pg_catalog.pg_class AS tc\n"
+ " JOIN pg_catalog.pg_index AS ix ON ix.indrelid = tc.oid\n"
+ " JOIN pg_catalog.pg_namespace AS n ON n.oid = tc.relnamespace\n"
+ " JOIN pg_catalog.pg_class AS ic ON ic.oid = ix.indexrelid\n"
+ " JOIN pg_catalog.pg_opclass AS opc ON opc.oid = ANY(ix.indclass)\n"
+ " JOIN pg_catalog.pg_type AS t ON t.oid = opc.opcintype\n"
+ " WHERE t.typcategory = 'S'\n"
+ " AND n.nspname != 'pg_catalog'\n"
+ " AND tc.relkind = 'r'\n"
+ " AND opc.opcname NOT IN ('text_pattern_ops', 'varchar_pattern_ops')\n"
+ " ORDER BY pg_catalog.pg_indexes_size(tc.oid) DESC;";
+ /* pg 8.1 added pg_relation_size() */
+ else if (PQserverVersion(conn) >= 80100)
+ return "SELECT DISTINCT ic.relname AS index_relname,"
+ " n.nspname AS index_nspname, tc.oid AS table_oid,"
+ " pg_catalog.pg_relation_size(tc.oid)"
+ " FROM pg_catalog.pg_class AS tc\n"
+ " JOIN pg_catalog.pg_index AS ix ON ix.indrelid = tc.oid\n"
+ " JOIN pg_catalog.pg_namespace AS n ON n.oid = tc.relnamespace\n"
+ " JOIN pg_catalog.pg_class AS ic ON ic.oid = ix.indexrelid\n"
+ " JOIN pg_catalog.pg_opclass AS opc ON opc.oid = ANY(ix.indclass)\n"
+ " JOIN pg_catalog.pg_type AS t ON t.oid = opc.opcintype\n"
+ " WHERE t.typcategory = 'S'\n"
+ " AND n.nspname != 'pg_catalog'\n"
+ " AND tc.relkind = 'r'\n"
+ " AND opc.opcname NOT IN ('text_pattern_ops', 'varchar_pattern_ops')\n"
+ " ORDER BY pg_catalog.pg_relation_size(tc.oid) DESC;";
+ else
+ return "SELECT DISTINCT ic.relname AS index_relname,"
+ " n.nspname AS index_nspname, tc.oid AS table_oid,"
+ " FROM pg_catalog.pg_class AS tc\n"
+ " JOIN pg_catalog.pg_index AS ix ON ix.indrelid = tc.oid\n"
+ " JOIN pg_catalog.pg_namespace AS n ON n.oid = tc.relnamespace\n"
+ " JOIN pg_catalog.pg_class AS ic ON ic.oid = ix.indexrelid\n"
+ " JOIN pg_catalog.pg_opclass AS opc ON opc.oid = ANY(ix.indclass)\n"
+ " JOIN pg_catalog.pg_type AS t ON t.oid = opc.opcintype\n"
+ " WHERE t.typcategory = 'S'\n"
+ " AND n.nspname != 'pg_catalog'\n"
+ " AND tc.relkind = 'r'\n"
+ " AND opc.opcname NOT IN ('text_pattern_ops', 'varchar_pattern_ops');";
+}
+
+/*
+ * Build a list of qualified index name, per table.
+ *
+ * The result set must be:
+ * (index_relname, index_nspname, table_oid)
+ */
+static void
+append_indexes_per_table(PGresult *res, SimplePtrList *process_list)
+{
+ SimpleStringList *indexes = NULL;
+ char *prev_rel = NULL;
+ int ntups = PQntuples(res);
+ int i;
+
+ indexes = pg_malloc0(sizeof(SimpleStringList));
+
+ Assert(strcmp(PQfname(res, 0), "index_relname") == 0);
+ Assert(strcmp(PQfname(res, 1), "index_nspname") == 0);
+ Assert(strcmp(PQfname(res, 2), "table_oid") == 0);
+
+ /* Build a list of qualified index name, per table */
+ for (i = 0; i < ntups; i++)
+ {
+ if (!prev_rel)
+ prev_rel = pg_strdup(PQgetvalue(res, i, 2));
+
+ if (strcmp(PQgetvalue(res, i, 2), prev_rel) != 0)
+ {
+ simple_ptr_list_append(process_list, indexes);
+
+ indexes = pg_malloc0(sizeof(SimpleStringList));
+ pg_free(prev_rel);
+ prev_rel = pg_strdup(PQgetvalue(res, i, 2));
+ }
+
+ simple_string_list_append(indexes,
+ fmtQualifiedId(PQgetvalue(res, i, 1),
+ PQgetvalue(res, i, 0)));
+ }
+
+ simple_ptr_list_append(process_list, indexes);
+}
+
static void
slot_process_item(ParallelSlot *slot, int *pending_conn,
ReindexType process_type, const char *progname, bool echo,
@@ -840,7 +1063,8 @@ reindex_all_databases(const char *maintenance_db,
const char *host, const char *port,
const char *username, enum trivalue prompt_password,
const char *progname, bool echo, bool quiet, bool verbose,
- bool concurrently, int concurrentCons)
+ bool concurrently, int concurrentCons,
+ bool glibc_dependent)
{
PGconn *conn;
PGresult *result;
@@ -870,7 +1094,7 @@ reindex_all_databases(const char *maintenance_db,
reindex_one_database(connstr.data, REINDEX_DATABASE, NULL, host,
port, username, prompt_password,
progname, echo, verbose, concurrently,
- concurrentCons);
+ concurrentCons, glibc_dependent);
}
termPQExpBuffer(&connstr);
@@ -888,6 +1112,7 @@ help(const char *progname)
printf(_(" --concurrently reindex concurrently\n"));
printf(_(" -d, --dbname=DBNAME database to reindex\n"));
printf(_(" -e, --echo show the commands being sent to the server\n"));
+ printf(_(" --glibc-dependent only reindex indexes depending on GLIBC\n"));
printf(_(" -i, --index=INDEX recreate specific index(es) only\n"));
printf(_(" -j, --jobs=NUM use this many concurrent connections to reindex\n"));
printf(_(" -q, --quiet don't write any messages\n"));
diff --git a/src/bin/scripts/t/092_reindexdb_glibc.pl b/src/bin/scripts/t/092_reindexdb_glibc.pl
new file mode 100644
index 0000000000..22b07d2acb
--- /dev/null
+++ b/src/bin/scripts/t/092_reindexdb_glibc.pl
@@ -0,0 +1,106 @@
+use strict;
+use warnings;
+
+use PostgresNode;
+use TestLib;
+use Test::More tests => 60;
+
+if (exists $ENV{MY_PG_REGRESS})
+{
+ $ENV{PG_REGRESS} = $ENV{MY_PG_REGRESS};
+}
+
+my $node = get_new_node('main');
+$node->init;
+$node->start;
+
+$ENV{PGOPTIONS} = '--client-min-messages=WARNING';
+
+# glibc-dependent processing
+my %idxs;
+my %newidxs;
+
+$node->psql('postgres', 'CREATE TABLE "TEST"(id integer, val text)');
+$node->psql('postgres', 'CREATE INDEX idx1 ON "TEST"(id)');
+$node->psql('postgres', 'CREATE INDEX "IDX2" ON "TEST"(val)');
+$node->psql('postgres', 'CREATE INDEX idx3 ON "TEST"(val text_pattern_ops)');
+$node->psql('postgres', 'CREATE INDEX idx4 ON "TEST"(val text_pattern_ops, val)');
+$node->psql('postgres', 'CREATE INDEX idx5 ON "TEST"((lower(val)))');
+$node->psql('postgres', 'CREATE INDEX idx6 ON "TEST"((lower(val)) text_pattern_ops)');
+$node->psql('postgres', 'CREATE INDEX idx7 ON "TEST"((lower(val)), (lower(val)) text_pattern_ops)');
+
+# get original indexes' relfilenode
+foreach my $idx ('idx1', 'IDX2', 'idx3', 'idx4', 'idx5', 'idx6', 'idx7')
+{
+ my ($cmdret, $stdout, $stderr) = $node->psql('postgres', "SELECT relfilenode"
+ . " FROM pg_catalog.pg_class c"
+ . " WHERE c.relname = '$idx'");
+
+ isnt($stdout, '', 'A relfinode should be returned');
+ isnt($stdout, 0, 'Relfinode should not be 0');
+ $idxs{$idx} = $stdout;
+}
+
+$node->issues_sql_like(
+ [ 'vreindexdb', '--glibc-dependent', 'postgres' ],
+ qr/statement: REINDEX SYSTEM postgres;/,
+ 'reindex system tables');
+
+# get possibly new indexes' relfilenode
+foreach my $idx ('idx1', 'IDX2', 'idx3', 'idx4', 'idx5', 'idx6', 'idx7')
+{
+ my ($cmdret, $stdout, $stderr) = $node->psql('postgres', "SELECT relfilenode"
+ . " FROM pg_catalog.pg_class c"
+ . " WHERE c.relname = '$idx'");
+
+ isnt($stdout, '', 'A relfinode should be returned');
+ isnt($stdout, 0, "Relfinode shouldn't be 0");
+ $newidxs{$idx} = $stdout;
+}
+
+is($idxs{'idx1'}, $newidxs{'idx1'},
+ 'Non string index should not have been reindexed');
+isnt($idxs{'IDX2'}, $newidxs{'IDX2'},
+ 'String index with non specific opclass should have been reindexed');
+is($idxs{'idx3'}, $newidxs{'idx3'},
+ 'String index with specific opclass should not have been reindexed');
+isnt($idxs{'idx4'}, $newidxs{'idx4'},
+ 'String index with mix of opclass should have been reindexed');
+isnt($idxs{'idx5'}, $newidxs{'idx5'},
+ 'String functional index with non specific opclass should have been reindexed');
+is($idxs{'idx6'}, $newidxs{'idx6'},
+ 'String functional index with specific opclass should not have been reindexed');
+isnt($idxs{'idx7'}, $newidxs{'idx7'},
+ 'String functional index with mix of opclass should have been reindexed');
+
+$node->issues_sql_like(
+ [ 'vreindexdb', '--glibc-dependent', '-j2', 'postgres' ],
+ qr/statement: REINDEX SYSTEM postgres;/,
+ 'reindex system tables');
+
+# get possibly new indexes' relfilenode
+foreach my $idx ('idx1', 'IDX2', 'idx3', 'idx4', 'idx5', 'idx6', 'idx7')
+{
+ my ($cmdret, $stdout, $stderr) = $node->psql('postgres', "SELECT relfilenode"
+ . " FROM pg_catalog.pg_class c"
+ . " WHERE c.relname = '$idx'");
+
+ isnt($stdout, '', 'A relfinode should be returned');
+ isnt($stdout, 0, "Relfinode shouldn't be 0");
+ $idxs{$idx} = $stdout;
+}
+
+is($idxs{'idx1'}, $newidxs{'idx1'},
+ 'Non string index should not have been reindexed');
+isnt($idxs{'IDX2'}, $newidxs{'IDX2'},
+ 'String index with non specific opclass should have been reindexed');
+is($idxs{'idx3'}, $newidxs{'idx3'},
+ 'String index with specific opclass should not have been reindexed');
+isnt($idxs{'idx4'}, $newidxs{'idx4'},
+ 'String index with mix of opclass should have been reindexed');
+isnt($idxs{'idx5'}, $newidxs{'idx5'},
+ 'String functional index with non specific opclass should have been reindexed');
+is($idxs{'idx6'}, $newidxs{'idx6'},
+ 'String functional index with specific opclass should not have been reindexed');
+isnt($idxs{'idx7'}, $newidxs{'idx7'},
+ 'String functional index with mix of opclass should have been reindexed');
--
2.20.1