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

