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

Generated by: LCOV version 1.16