pg_upgrade: report database names with missing extension libs
authorBruce Momjian <bruce@momjian.us>
Sat, 28 Jul 2018 16:33:54 +0000 (12:33 -0400)
committerBruce Momjian <bruce@momjian.us>
Sat, 28 Jul 2018 16:33:54 +0000 (12:33 -0400)
Previously only the missing library name was reported, forcing users to
look in all databases to find the library entries.

Discussion: https://postgr.es/m/20180713162815.GA3835@momjian.us

Author: Daniel Gustafsson, me

src/bin/pg_upgrade/function.c
src/bin/pg_upgrade/pg_upgrade.h

index 03fd155dcdae06567219b631ccae5b7229df89fa..627838bdbcdc3f9277033f23c5d26b2ed67a3a17 100644 (file)
 /*
  * qsort comparator for pointers to library names
  *
- * We sort first by name length, then alphabetically for names of the same
- * length.  This is to ensure that, eg, "hstore_plpython" sorts after both
- * "hstore" and "plpython"; otherwise transform modules will probably fail
- * their LOAD tests.  (The backend ought to cope with that consideration,
- * but it doesn't yet, and even when it does it'll still be a good idea
- * to have a predictable order of probing here.)
+ * We sort first by name length, then alphabetically for names of the
+ * same length, then database array index.  This is to ensure that, eg,
+ * "hstore_plpython" sorts after both "hstore" and "plpython"; otherwise
+ * transform modules will probably fail their LOAD tests.  (The backend
+ * ought to cope with that consideration, but it doesn't yet, and even
+ * when it does it'll still be a good idea to have a predictable order of
+ * probing here.)
  */
 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);
-
+   int         cmp = strcmp(str1, str2);
+   
    if (slen1 != slen2)
        return slen1 - slen2;
-   return strcmp(str1, str2);
+   if (cmp != 0)
+       return cmp;
+   else
+       return ((const LibraryInfo *) p1)->dbnum -
+              ((const LibraryInfo *) p2)->dbnum;
 }
 
 
@@ -137,18 +143,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,32 +157,16 @@ 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].dbnum = 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;
 }
 
@@ -204,6 +183,7 @@ check_loadable_libraries(void)
 {
    PGconn     *conn = connectToServer(&new_cluster, "template1");
    int         libnum;
+   int         was_load_failure = false;
    FILE       *script = NULL;
    bool        found = false;
    char        output_path[MAXPGPATH];
@@ -212,52 +192,72 @@ check_loadable_libraries(void)
 
    snprintf(output_path, sizeof(output_path), "loadable_libraries.txt");
 
+   /*
+    * 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; 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;
 
-       /*
-        * 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
-        * symbolic link to plpython.so.  In Postgres 9.1, only the
-        * plpython2.so library was created, and both plpythonu and plpython2u
-        * pointing to it.  For this reason, any reference to library name
-        * "plpython" in an old PG <= 9.1 cluster must look for "plpython2" in
-        * the new cluster.
-        *
-        * For this case, we could check pg_pltemplate, but that only works
-        * for languages, and does not help with function shared objects, so
-        * we just do a general fix.
-        */
-       if (GET_MAJOR_VERSION(old_cluster.major_version) < 901 &&
-           strcmp(lib, "$libdir/plpython") == 0)
-       {
-           lib = "$libdir/plpython2";
-           llen = strlen(lib);
-       }
-
-       strcpy(cmd, "LOAD '");
-       PQescapeStringConn(conn, cmd + strlen(cmd), lib, llen, NULL);
-       strcat(cmd, "'");
-
-       res = PQexec(conn, cmd);
-
-       if (PQresultStatus(res) != PGRES_COMMAND_OK)
+       /* Did the library name change?  Probe it. */
+       if (libnum == 0 || strcmp(lib, os_info.libraries[libnum - 1].name) != 0)
        {
-           found = true;
-
-           if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-               pg_fatal("could not open file \"%s\": %s\n",
-                        output_path, strerror(errno));
-           fprintf(script, _("could not load library \"%s\": %s"),
-                   lib,
-                   PQerrorMessage(conn));
+           /*
+            * 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
+            * symbolic link to plpython.so.  In Postgres 9.1, only the
+            * plpython2.so library was created, and both plpythonu and plpython2u
+            * pointing to it.  For this reason, any reference to library name
+            * "plpython" in an old PG <= 9.1 cluster must look for "plpython2" in
+            * the new cluster.
+            *
+            * For this case, we could check pg_pltemplate, but that only works
+            * for languages, and does not help with function shared objects, so
+            * we just do a general fix.
+            */
+           if (GET_MAJOR_VERSION(old_cluster.major_version) < 901 &&
+               strcmp(lib, "$libdir/plpython") == 0)
+           {
+               lib = "$libdir/plpython2";
+               llen = strlen(lib);
+           }
+   
+           strcpy(cmd, "LOAD '");
+           PQescapeStringConn(conn, cmd + strlen(cmd), lib, llen, NULL);
+           strcat(cmd, "'");
+   
+           res = PQexec(conn, cmd);
+   
+           if (PQresultStatus(res) != PGRES_COMMAND_OK)
+           {
+               found = true;
+               was_load_failure = true;
+   
+               if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
+                   pg_fatal("could not open file \"%s\": %s\n",
+                            output_path, strerror(errno));
+               fprintf(script, _("could not load library \"%s\": %s"),
+                       lib,
+                       PQerrorMessage(conn));
+           }
+           else
+               was_load_failure = false;
+   
+           PQclear(res);
        }
 
-       PQclear(res);
+       if (was_load_failure)
+           fprintf(script, _("Database: %s\n"),
+               old_cluster.dbarr.dbs[os_info.libraries[libnum].dbnum].db_name);
    }
 
    PQfinish(conn);
index 7e5e97129471cddd6922b6e5f76b9dc0c4be5cc7..f83a3eeb6748002f51b40829c420faf18dd64dcb 100644 (file)
@@ -300,6 +300,11 @@ typedef struct
    int         jobs;
 } UserOpts;
 
+typedef struct
+{
+   char       *name;
+   int         dbnum;
+} 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;