From 4cf31775464638f8621b32d411456f8cbf6cc9a3 Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <daniel@yesql.se>
Date: Tue, 24 Jul 2018 09:23:42 +0200
Subject: [PATCH] Report which databases contain the missing libraries

When a loadable library isn't found in the new cluster during an
upgdade, report all databases in the old cluster which contains
the library in question.
---
 src/bin/pg_upgrade/function.c   | 72 ++++++++++++++++++++---------------------
 src/bin/pg_upgrade/pg_upgrade.h |  7 +++-
 2 files changed, 41 insertions(+), 38 deletions(-)

diff --git a/src/bin/pg_upgrade/function.c b/src/bin/pg_upgrade/function.c
index 03fd155dcd..c8592cb08f 100644
--- a/src/bin/pg_upgrade/function.c
+++ b/src/bin/pg_upgrade/function.c
@@ -28,8 +28,8 @@
 static int
 library_name_compare(const void *p1, const void *p2)
 {
-	const char *str1 = *(const char *const *) p1;
-	const char *str2 = *(const char *const *) p2;
+	const char *str1 = ((const LibraryInfo *) p1)->name;
+	const char *str2 = ((const LibraryInfo *) p2)->name;
 	int			slen1 = strlen(str1);
 	int			slen2 = strlen(str2);
 
@@ -137,18 +137,7 @@ get_loadable_libraries(void)
 	if (found_public_plpython_handler)
 		pg_fatal("Remove the problem functions from the old cluster to continue.\n");
 
-	/*
-	 * Now we want to remove duplicates across DBs and sort the library names
-	 * into order.  This avoids multiple probes of the same library, and
-	 * ensures that libraries are probed in a consistent order, which is
-	 * important for reproducible behavior if one library depends on another.
-	 *
-	 * First transfer all the names into one array, then sort, then remove
-	 * duplicates.  Note: we strdup each name in the first loop so that we can
-	 * safely clear the PGresults in the same loop.  This is a bit wasteful
-	 * but it's unlikely there are enough names to matter.
-	 */
-	os_info.libraries = (char **) pg_malloc(totaltups * sizeof(char *));
+	os_info.libraries = (LibraryInfo *) pg_malloc(totaltups * sizeof(LibraryInfo));
 	totaltups = 0;
 
 	for (dbnum = 0; dbnum < old_cluster.dbarr.ndbs; dbnum++)
@@ -162,36 +151,19 @@ get_loadable_libraries(void)
 		{
 			char	   *lib = PQgetvalue(res, rowno, 0);
 
-			os_info.libraries[totaltups++] = pg_strdup(lib);
+			os_info.libraries[totaltups].name = pg_strdup(lib);
+			os_info.libraries[totaltups].db = dbnum;
+
+			totaltups++;
 		}
 		PQclear(res);
 	}
 
 	pg_free(ress);
 
-	if (totaltups > 1)
-	{
-		int			i,
-					lastnondup;
-
-		qsort((void *) os_info.libraries, totaltups, sizeof(char *),
-			  library_name_compare);
-
-		for (i = 1, lastnondup = 0; i < totaltups; i++)
-		{
-			if (strcmp(os_info.libraries[i],
-					   os_info.libraries[lastnondup]) != 0)
-				os_info.libraries[++lastnondup] = os_info.libraries[i];
-			else
-				pg_free(os_info.libraries[i]);
-		}
-		totaltups = lastnondup + 1;
-	}
-
 	os_info.num_libraries = totaltups;
 }
 
-
 /*
  * check_loadable_libraries()
  *
@@ -204,6 +176,7 @@ check_loadable_libraries(void)
 {
 	PGconn	   *conn = connectToServer(&new_cluster, "template1");
 	int			libnum;
+	int			lastfail;
 	FILE	   *script = NULL;
 	bool		found = false;
 	char		output_path[MAXPGPATH];
@@ -212,13 +185,35 @@ check_loadable_libraries(void)
 
 	snprintf(output_path, sizeof(output_path), "loadable_libraries.txt");
 
-	for (libnum = 0; libnum < os_info.num_libraries; libnum++)
+	/*
+	 * Now we want to sort the library names into order.  This avoids multiple
+	 * probes of the same library, and ensures that libraries are probed in a
+	 * consistent order, which is important for reproducible behavior if one
+	 * library depends on another.
+	 */
+	qsort((void *) os_info.libraries, os_info.num_libraries,
+		  sizeof(LibraryInfo), library_name_compare);
+
+	for (libnum = 0, lastfail = -1; libnum < os_info.num_libraries; libnum++)
 	{
-		char	   *lib = os_info.libraries[libnum];
+		char	   *lib = os_info.libraries[libnum].name;
 		int			llen = strlen(lib);
 		char		cmd[7 + 2 * MAXPGPATH + 1];
 		PGresult   *res;
 
+		/*
+		 * If we have already probed for the library, skip re-probing and only
+		 * report the database name.
+		 */
+		if (found && lastfail >= 0 &&
+			strcmp(lib, os_info.libraries[lastfail].name) == 0)
+		{
+			fprintf(script, _("\t\"%s\"\n"),
+				old_cluster.dbarr.dbs[os_info.libraries[libnum].db].db_name);
+
+			continue;
+		}
+
 		/*
 		 * In Postgres 9.0, Python 3 support was added, and to do that, a
 		 * plpython2u language was created with library name plpython2.so as a
@@ -248,6 +243,7 @@ check_loadable_libraries(void)
 		if (PQresultStatus(res) != PGRES_COMMAND_OK)
 		{
 			found = true;
+			lastfail = libnum;
 
 			if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
 				pg_fatal("could not open file \"%s\": %s\n",
@@ -255,6 +251,8 @@ check_loadable_libraries(void)
 			fprintf(script, _("could not load library \"%s\": %s"),
 					lib,
 					PQerrorMessage(conn));
+			fprintf(script, _("library found in databases:\n\t\"%s\"\n"),
+					old_cluster.dbarr.dbs[os_info.libraries[libnum].db].db_name);
 		}
 
 		PQclear(res);
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 7e5e971294..f12750d930 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -300,6 +300,11 @@ typedef struct
 	int			jobs;
 } UserOpts;
 
+typedef struct
+{
+	char	   *name;
+	int			db;
+} LibraryInfo;
 
 /*
  * OSInfo
@@ -312,7 +317,7 @@ typedef struct
 	bool		user_specified; /* user specified on command-line */
 	char	  **old_tablespaces;	/* tablespaces */
 	int			num_old_tablespaces;
-	char	  **libraries;		/* loadable libraries */
+	LibraryInfo *libraries;		/* loadable libraries */
 	int			num_libraries;
 	ClusterInfo *running_cluster;
 } OSInfo;
-- 
2.14.1.145.gb3622a4ee

