LCOV - code coverage report
Current view: top level - src/bin/pg_upgrade - function.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 64 74 86.5 %
Date: 2024-04-20 12:11:36 Functions: 3 3 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  *  function.c
       3             :  *
       4             :  *  server-side function support
       5             :  *
       6             :  *  Copyright (c) 2010-2024, PostgreSQL Global Development Group
       7             :  *  src/bin/pg_upgrade/function.c
       8             :  */
       9             : 
      10             : #include "postgres_fe.h"
      11             : 
      12             : #include "access/transam.h"
      13             : #include "catalog/pg_language_d.h"
      14             : #include "common/int.h"
      15             : #include "pg_upgrade.h"
      16             : 
      17             : /*
      18             :  * qsort comparator for pointers to library names
      19             :  *
      20             :  * We sort first by name length, then alphabetically for names of the
      21             :  * same length, then database array index.  This is to ensure that, eg,
      22             :  * "hstore_plpython" sorts after both "hstore" and "plpython"; otherwise
      23             :  * transform modules will probably fail their LOAD tests.  (The backend
      24             :  * ought to cope with that consideration, but it doesn't yet, and even
      25             :  * when it does it'll still be a good idea to have a predictable order of
      26             :  * probing here.)
      27             :  */
      28             : static int
      29          10 : library_name_compare(const void *p1, const void *p2)
      30             : {
      31          10 :     const char *str1 = ((const LibraryInfo *) p1)->name;
      32          10 :     const char *str2 = ((const LibraryInfo *) p2)->name;
      33          10 :     size_t      slen1 = strlen(str1);
      34          10 :     size_t      slen2 = strlen(str2);
      35          10 :     int         cmp = strcmp(str1, str2);
      36             : 
      37          10 :     if (slen1 != slen2)
      38           4 :         return pg_cmp_size(slen1, slen2);
      39           6 :     if (cmp != 0)
      40           4 :         return cmp;
      41           2 :     return pg_cmp_s32(((const LibraryInfo *) p1)->dbnum,
      42             :                       ((const LibraryInfo *) p2)->dbnum);
      43             : }
      44             : 
      45             : 
      46             : /*
      47             :  * get_loadable_libraries()
      48             :  *
      49             :  *  Fetch the names of all old libraries containing either C-language functions
      50             :  *  or are corresponding to logical replication output plugins.
      51             :  *
      52             :  *  We will later check that they all exist in the new installation.
      53             :  */
      54             : void
      55          16 : get_loadable_libraries(void)
      56             : {
      57             :     PGresult  **ress;
      58             :     int         totaltups;
      59             :     int         dbnum;
      60             :     int         n_libinfos;
      61             : 
      62          16 :     ress = (PGresult **) pg_malloc(old_cluster.dbarr.ndbs * sizeof(PGresult *));
      63          16 :     totaltups = 0;
      64             : 
      65             :     /* Fetch all library names, removing duplicates within each DB */
      66          64 :     for (dbnum = 0; dbnum < old_cluster.dbarr.ndbs; dbnum++)
      67             :     {
      68          48 :         DbInfo     *active_db = &old_cluster.dbarr.dbs[dbnum];
      69          48 :         PGconn     *conn = connectToServer(&old_cluster, active_db->db_name);
      70             : 
      71             :         /*
      72             :          * Fetch all libraries containing non-built-in C functions in this DB.
      73             :          */
      74          48 :         ress[dbnum] = executeQueryOrDie(conn,
      75             :                                         "SELECT DISTINCT probin "
      76             :                                         "FROM pg_catalog.pg_proc "
      77             :                                         "WHERE prolang = %u AND "
      78             :                                         "probin IS NOT NULL AND "
      79             :                                         "oid >= %u;",
      80             :                                         ClanguageId,
      81             :                                         FirstNormalObjectId);
      82          48 :         totaltups += PQntuples(ress[dbnum]);
      83             : 
      84          48 :         PQfinish(conn);
      85             :     }
      86             : 
      87             :     /*
      88             :      * Allocate memory for required libraries and logical replication output
      89             :      * plugins.
      90             :      */
      91          16 :     n_libinfos = totaltups + count_old_cluster_logical_slots();
      92          16 :     os_info.libraries = (LibraryInfo *) pg_malloc(sizeof(LibraryInfo) * n_libinfos);
      93          16 :     totaltups = 0;
      94             : 
      95          64 :     for (dbnum = 0; dbnum < old_cluster.dbarr.ndbs; dbnum++)
      96             :     {
      97          48 :         PGresult   *res = ress[dbnum];
      98             :         int         ntups;
      99             :         int         rowno;
     100          48 :         LogicalSlotInfoArr *slot_arr = &old_cluster.dbarr.dbs[dbnum].slot_arr;
     101             : 
     102          48 :         ntups = PQntuples(res);
     103          60 :         for (rowno = 0; rowno < ntups; rowno++)
     104             :         {
     105          12 :             char       *lib = PQgetvalue(res, rowno, 0);
     106             : 
     107          12 :             os_info.libraries[totaltups].name = pg_strdup(lib);
     108          12 :             os_info.libraries[totaltups].dbnum = dbnum;
     109             : 
     110          12 :             totaltups++;
     111             :         }
     112          48 :         PQclear(res);
     113             : 
     114             :         /*
     115             :          * Store the names of output plugins as well. There is a possibility
     116             :          * that duplicated plugins are set, but the consumer function
     117             :          * check_loadable_libraries() will avoid checking the same library, so
     118             :          * we do not have to consider their uniqueness here.
     119             :          */
     120          58 :         for (int slotno = 0; slotno < slot_arr->nslots; slotno++)
     121             :         {
     122          10 :             if (slot_arr->slots[slotno].invalid)
     123           0 :                 continue;
     124             : 
     125          10 :             os_info.libraries[totaltups].name = pg_strdup(slot_arr->slots[slotno].plugin);
     126          10 :             os_info.libraries[totaltups].dbnum = dbnum;
     127             : 
     128          10 :             totaltups++;
     129             :         }
     130             :     }
     131             : 
     132          16 :     pg_free(ress);
     133             : 
     134          16 :     os_info.num_libraries = totaltups;
     135          16 : }
     136             : 
     137             : 
     138             : /*
     139             :  * check_loadable_libraries()
     140             :  *
     141             :  *  Check that the new cluster contains all required libraries.
     142             :  *  We do this by actually trying to LOAD each one, thereby testing
     143             :  *  compatibility as well as presence.
     144             :  */
     145             : void
     146          12 : check_loadable_libraries(void)
     147             : {
     148          12 :     PGconn     *conn = connectToServer(&new_cluster, "template1");
     149             :     int         libnum;
     150          12 :     int         was_load_failure = false;
     151          12 :     FILE       *script = NULL;
     152             :     char        output_path[MAXPGPATH];
     153             : 
     154          12 :     prep_status("Checking for presence of required libraries");
     155             : 
     156          12 :     snprintf(output_path, sizeof(output_path), "%s/%s",
     157             :              log_opts.basedir, "loadable_libraries.txt");
     158             : 
     159             :     /*
     160             :      * Now we want to sort the library names into order.  This avoids multiple
     161             :      * probes of the same library, and ensures that libraries are probed in a
     162             :      * consistent order, which is important for reproducible behavior if one
     163             :      * library depends on another.
     164             :      */
     165          12 :     qsort(os_info.libraries, os_info.num_libraries,
     166             :           sizeof(LibraryInfo), library_name_compare);
     167             : 
     168          30 :     for (libnum = 0; libnum < os_info.num_libraries; libnum++)
     169             :     {
     170          18 :         char       *lib = os_info.libraries[libnum].name;
     171          18 :         int         llen = strlen(lib);
     172             :         char        cmd[7 + 2 * MAXPGPATH + 1];
     173             :         PGresult   *res;
     174             : 
     175             :         /* Did the library name change?  Probe it. */
     176          18 :         if (libnum == 0 || strcmp(lib, os_info.libraries[libnum - 1].name) != 0)
     177             :         {
     178          16 :             strcpy(cmd, "LOAD '");
     179          16 :             PQescapeStringConn(conn, cmd + strlen(cmd), lib, llen, NULL);
     180          16 :             strcat(cmd, "'");
     181             : 
     182          16 :             res = PQexec(conn, cmd);
     183             : 
     184          16 :             if (PQresultStatus(res) != PGRES_COMMAND_OK)
     185             :             {
     186           0 :                 was_load_failure = true;
     187             : 
     188           0 :                 if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
     189           0 :                     pg_fatal("could not open file \"%s\": %m", output_path);
     190           0 :                 fprintf(script, _("could not load library \"%s\": %s"),
     191             :                         lib,
     192             :                         PQerrorMessage(conn));
     193             :             }
     194             :             else
     195          16 :                 was_load_failure = false;
     196             : 
     197          16 :             PQclear(res);
     198             :         }
     199             : 
     200          18 :         if (was_load_failure)
     201           0 :             fprintf(script, _("In database: %s\n"),
     202           0 :                     old_cluster.dbarr.dbs[os_info.libraries[libnum].dbnum].db_name);
     203             :     }
     204             : 
     205          12 :     PQfinish(conn);
     206             : 
     207          12 :     if (script)
     208             :     {
     209           0 :         fclose(script);
     210           0 :         pg_log(PG_REPORT, "fatal");
     211           0 :         pg_fatal("Your installation references loadable libraries that are missing from the\n"
     212             :                  "new installation.  You can add these libraries to the new installation,\n"
     213             :                  "or remove the functions using them from the old installation.  A list of\n"
     214             :                  "problem libraries is in the file:\n"
     215             :                  "    %s", output_path);
     216             :     }
     217             :     else
     218          12 :         check_ok();
     219          12 : }

Generated by: LCOV version 1.14