LCOV - code coverage report
Current view: top level - src/bin/pg_upgrade - function.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 69 79 87.3 %
Date: 2024-11-21 08:14:44 Functions: 4 4 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             :  * Private state for get_loadable_libraries()'s UpgradeTask.
      47             :  */
      48             : struct loadable_libraries_state
      49             : {
      50             :     PGresult  **ress;           /* results for each database */
      51             :     int         totaltups;      /* number of tuples in all results */
      52             : };
      53             : 
      54             : /*
      55             :  * Callback function for processing results of query for
      56             :  * get_loadable_libraries()'s UpgradeTask.  This function stores the results
      57             :  * for later use within get_loadable_libraries().
      58             :  */
      59             : static void
      60          48 : process_loadable_libraries(DbInfo *dbinfo, PGresult *res, void *arg)
      61             : {
      62          48 :     struct loadable_libraries_state *state = (struct loadable_libraries_state *) arg;
      63             : 
      64             :     AssertVariableIsOfType(&process_loadable_libraries, UpgradeTaskProcessCB);
      65             : 
      66          48 :     state->ress[dbinfo - old_cluster.dbarr.dbs] = res;
      67          48 :     state->totaltups += PQntuples(res);
      68          48 : }
      69             : 
      70             : /*
      71             :  * get_loadable_libraries()
      72             :  *
      73             :  *  Fetch the names of all old libraries containing either C-language functions
      74             :  *  or are corresponding to logical replication output plugins.
      75             :  *
      76             :  *  We will later check that they all exist in the new installation.
      77             :  */
      78             : void
      79          16 : get_loadable_libraries(void)
      80             : {
      81             :     int         totaltups;
      82             :     int         dbnum;
      83             :     int         n_libinfos;
      84          16 :     UpgradeTask *task = upgrade_task_create();
      85             :     struct loadable_libraries_state state;
      86             :     char       *query;
      87             : 
      88          16 :     state.ress = (PGresult **) pg_malloc(old_cluster.dbarr.ndbs * sizeof(PGresult *));
      89          16 :     state.totaltups = 0;
      90             : 
      91          16 :     query = psprintf("SELECT DISTINCT probin "
      92             :                      "FROM pg_catalog.pg_proc "
      93             :                      "WHERE prolang = %u AND "
      94             :                      "probin IS NOT NULL AND "
      95             :                      "oid >= %u",
      96             :                      ClanguageId,
      97             :                      FirstNormalObjectId);
      98             : 
      99          16 :     upgrade_task_add_step(task, query, process_loadable_libraries,
     100             :                           false, &state);
     101             : 
     102          16 :     upgrade_task_run(task, &old_cluster);
     103          16 :     upgrade_task_free(task);
     104             : 
     105             :     /*
     106             :      * Allocate memory for required libraries and logical replication output
     107             :      * plugins.
     108             :      */
     109          16 :     n_libinfos = state.totaltups + count_old_cluster_logical_slots();
     110          16 :     os_info.libraries = (LibraryInfo *) pg_malloc(sizeof(LibraryInfo) * n_libinfos);
     111          16 :     totaltups = 0;
     112             : 
     113          64 :     for (dbnum = 0; dbnum < old_cluster.dbarr.ndbs; dbnum++)
     114             :     {
     115          48 :         PGresult   *res = state.ress[dbnum];
     116             :         int         ntups;
     117             :         int         rowno;
     118          48 :         LogicalSlotInfoArr *slot_arr = &old_cluster.dbarr.dbs[dbnum].slot_arr;
     119             : 
     120          48 :         ntups = PQntuples(res);
     121          60 :         for (rowno = 0; rowno < ntups; rowno++)
     122             :         {
     123          12 :             char       *lib = PQgetvalue(res, rowno, 0);
     124             : 
     125          12 :             os_info.libraries[totaltups].name = pg_strdup(lib);
     126          12 :             os_info.libraries[totaltups].dbnum = dbnum;
     127             : 
     128          12 :             totaltups++;
     129             :         }
     130          48 :         PQclear(res);
     131             : 
     132             :         /*
     133             :          * Store the names of output plugins as well. There is a possibility
     134             :          * that duplicated plugins are set, but the consumer function
     135             :          * check_loadable_libraries() will avoid checking the same library, so
     136             :          * we do not have to consider their uniqueness here.
     137             :          */
     138          58 :         for (int slotno = 0; slotno < slot_arr->nslots; slotno++)
     139             :         {
     140          10 :             if (slot_arr->slots[slotno].invalid)
     141           0 :                 continue;
     142             : 
     143          10 :             os_info.libraries[totaltups].name = pg_strdup(slot_arr->slots[slotno].plugin);
     144          10 :             os_info.libraries[totaltups].dbnum = dbnum;
     145             : 
     146          10 :             totaltups++;
     147             :         }
     148             :     }
     149             : 
     150          16 :     pg_free(state.ress);
     151          16 :     pg_free(query);
     152             : 
     153          16 :     os_info.num_libraries = totaltups;
     154          16 : }
     155             : 
     156             : 
     157             : /*
     158             :  * check_loadable_libraries()
     159             :  *
     160             :  *  Check that the new cluster contains all required libraries.
     161             :  *  We do this by actually trying to LOAD each one, thereby testing
     162             :  *  compatibility as well as presence.
     163             :  */
     164             : void
     165          12 : check_loadable_libraries(void)
     166             : {
     167          12 :     PGconn     *conn = connectToServer(&new_cluster, "template1");
     168             :     int         libnum;
     169          12 :     int         was_load_failure = false;
     170          12 :     FILE       *script = NULL;
     171             :     char        output_path[MAXPGPATH];
     172             : 
     173          12 :     prep_status("Checking for presence of required libraries");
     174             : 
     175          12 :     snprintf(output_path, sizeof(output_path), "%s/%s",
     176             :              log_opts.basedir, "loadable_libraries.txt");
     177             : 
     178             :     /*
     179             :      * Now we want to sort the library names into order.  This avoids multiple
     180             :      * probes of the same library, and ensures that libraries are probed in a
     181             :      * consistent order, which is important for reproducible behavior if one
     182             :      * library depends on another.
     183             :      */
     184          12 :     qsort(os_info.libraries, os_info.num_libraries,
     185             :           sizeof(LibraryInfo), library_name_compare);
     186             : 
     187          30 :     for (libnum = 0; libnum < os_info.num_libraries; libnum++)
     188             :     {
     189          18 :         char       *lib = os_info.libraries[libnum].name;
     190          18 :         int         llen = strlen(lib);
     191             :         char        cmd[7 + 2 * MAXPGPATH + 1];
     192             :         PGresult   *res;
     193             : 
     194             :         /* Did the library name change?  Probe it. */
     195          18 :         if (libnum == 0 || strcmp(lib, os_info.libraries[libnum - 1].name) != 0)
     196             :         {
     197          16 :             strcpy(cmd, "LOAD '");
     198          16 :             PQescapeStringConn(conn, cmd + strlen(cmd), lib, llen, NULL);
     199          16 :             strcat(cmd, "'");
     200             : 
     201          16 :             res = PQexec(conn, cmd);
     202             : 
     203          16 :             if (PQresultStatus(res) != PGRES_COMMAND_OK)
     204             :             {
     205           0 :                 was_load_failure = true;
     206             : 
     207           0 :                 if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
     208           0 :                     pg_fatal("could not open file \"%s\": %m", output_path);
     209           0 :                 fprintf(script, _("could not load library \"%s\": %s"),
     210             :                         lib,
     211             :                         PQerrorMessage(conn));
     212             :             }
     213             :             else
     214          16 :                 was_load_failure = false;
     215             : 
     216          16 :             PQclear(res);
     217             :         }
     218             : 
     219          18 :         if (was_load_failure)
     220           0 :             fprintf(script, _("In database: %s\n"),
     221           0 :                     old_cluster.dbarr.dbs[os_info.libraries[libnum].dbnum].db_name);
     222             :     }
     223             : 
     224          12 :     PQfinish(conn);
     225             : 
     226          12 :     if (script)
     227             :     {
     228           0 :         fclose(script);
     229           0 :         pg_log(PG_REPORT, "fatal");
     230           0 :         pg_fatal("Your installation references loadable libraries that are missing from the\n"
     231             :                  "new installation.  You can add these libraries to the new installation,\n"
     232             :                  "or remove the functions using them from the old installation.  A list of\n"
     233             :                  "problem libraries is in the file:\n"
     234             :                  "    %s", output_path);
     235             :     }
     236             :     else
     237          12 :         check_ok();
     238          12 : }

Generated by: LCOV version 1.14