LCOV - code coverage report
Current view: top level - src/bin/pg_upgrade - function.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 85.4 % 82 70
Test Date: 2026-03-24 02:15:55 Functions: 100.0 % 4 4
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           52 : process_loadable_libraries(DbInfo *dbinfo, PGresult *res, void *arg)
      61              : {
      62           52 :     struct loadable_libraries_state *state = (struct loadable_libraries_state *) arg;
      63              : 
      64           52 :     state->ress[dbinfo - old_cluster.dbarr.dbs] = res;
      65           52 :     state->totaltups += PQntuples(res);
      66           52 : }
      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           17 : get_loadable_libraries(void)
      78              : {
      79              :     int         totaltups;
      80              :     int         dbnum;
      81              :     int         n_libinfos;
      82           17 :     UpgradeTask *task = upgrade_task_create();
      83              :     struct loadable_libraries_state state;
      84              :     char       *query;
      85              : 
      86           17 :     state.ress = pg_malloc_array(PGresult *, old_cluster.dbarr.ndbs);
      87           17 :     state.totaltups = 0;
      88              : 
      89           17 :     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           17 :     upgrade_task_add_step(task, query, process_loadable_libraries,
      98              :                           false, &state);
      99              : 
     100           17 :     upgrade_task_run(task, &old_cluster);
     101           17 :     upgrade_task_free(task);
     102              : 
     103              :     /*
     104              :      * Allocate memory for required libraries and logical replication output
     105              :      * plugins.
     106              :      */
     107           17 :     n_libinfos = state.totaltups + count_old_cluster_logical_slots();
     108           17 :     os_info.libraries = pg_malloc_array(LibraryInfo, n_libinfos);
     109           17 :     totaltups = 0;
     110              : 
     111           69 :     for (dbnum = 0; dbnum < old_cluster.dbarr.ndbs; dbnum++)
     112              :     {
     113           52 :         PGresult   *res = state.ress[dbnum];
     114              :         int         ntups;
     115              :         int         rowno;
     116           52 :         LogicalSlotInfoArr *slot_arr = &old_cluster.dbarr.dbs[dbnum].slot_arr;
     117              : 
     118           52 :         ntups = PQntuples(res);
     119           60 :         for (rowno = 0; rowno < ntups; rowno++)
     120              :         {
     121            8 :             char       *lib = PQgetvalue(res, rowno, 0);
     122              : 
     123              :             /*
     124              :              * Starting with version 19, for extensions with hardcoded
     125              :              * '$libdir/' library names, we strip the prefix to allow the
     126              :              * library search path to be used.
     127              :              */
     128            8 :             if (strncmp(lib, "$libdir/", 8) == 0)
     129            6 :                 lib += 8;
     130              : 
     131            8 :             os_info.libraries[totaltups].name = pg_strdup(lib);
     132            8 :             os_info.libraries[totaltups].dbnum = dbnum;
     133              : 
     134            8 :             totaltups++;
     135              :         }
     136           52 :         PQclear(res);
     137              : 
     138              :         /*
     139              :          * Store the names of output plugins as well. There is a possibility
     140              :          * that duplicated plugins are set, but the consumer function
     141              :          * check_loadable_libraries() will avoid checking the same library, so
     142              :          * we do not have to consider their uniqueness here.
     143              :          */
     144           59 :         for (int slotno = 0; slotno < slot_arr->nslots; slotno++)
     145              :         {
     146            7 :             if (slot_arr->slots[slotno].invalid)
     147            0 :                 continue;
     148              : 
     149            7 :             os_info.libraries[totaltups].name = pg_strdup(slot_arr->slots[slotno].plugin);
     150            7 :             os_info.libraries[totaltups].dbnum = dbnum;
     151              : 
     152            7 :             totaltups++;
     153              :         }
     154              :     }
     155              : 
     156           17 :     pg_free(state.ress);
     157           17 :     pg_free(query);
     158              : 
     159           17 :     os_info.num_libraries = totaltups;
     160           17 : }
     161              : 
     162              : 
     163              : /*
     164              :  * check_loadable_libraries()
     165              :  *
     166              :  *  Check that the new cluster contains all required libraries.
     167              :  *  We do this by actually trying to LOAD each one, thereby testing
     168              :  *  compatibility as well as presence.
     169              :  */
     170              : void
     171           15 : check_loadable_libraries(void)
     172              : {
     173           15 :     PGconn     *conn = connectToServer(&new_cluster, "template1");
     174              :     int         libnum;
     175           15 :     int         was_load_failure = false;
     176           15 :     FILE       *script = NULL;
     177              :     char        output_path[MAXPGPATH];
     178              : 
     179           15 :     prep_status("Checking for presence of required libraries");
     180              : 
     181           15 :     snprintf(output_path, sizeof(output_path), "%s/%s",
     182              :              log_opts.basedir, "loadable_libraries.txt");
     183              : 
     184              :     /*
     185              :      * Now we want to sort the library names into order.  This avoids multiple
     186              :      * probes of the same library, and ensures that libraries are probed in a
     187              :      * consistent order, which is important for reproducible behavior if one
     188              :      * library depends on another.
     189              :      */
     190           15 :     qsort(os_info.libraries, os_info.num_libraries,
     191              :           sizeof(LibraryInfo), library_name_compare);
     192              : 
     193           27 :     for (libnum = 0; libnum < os_info.num_libraries; libnum++)
     194              :     {
     195           12 :         char       *lib = os_info.libraries[libnum].name;
     196           12 :         int         llen = strlen(lib);
     197              :         char        cmd[7 + 2 * MAXPGPATH + 1];
     198              :         PGresult   *res;
     199              : 
     200              :         /* Did the library name change?  Probe it. */
     201           12 :         if (libnum == 0 || strcmp(lib, os_info.libraries[libnum - 1].name) != 0)
     202              :         {
     203           10 :             strcpy(cmd, "LOAD '");
     204           10 :             PQescapeStringConn(conn, cmd + strlen(cmd), lib, llen, NULL);
     205           10 :             strcat(cmd, "'");
     206              : 
     207           10 :             res = PQexec(conn, cmd);
     208              : 
     209           10 :             if (PQresultStatus(res) != PGRES_COMMAND_OK)
     210              :             {
     211            0 :                 was_load_failure = true;
     212              : 
     213            0 :                 if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
     214            0 :                     pg_fatal("could not open file \"%s\": %m", output_path);
     215            0 :                 fprintf(script, _("could not load library \"%s\": %s"),
     216              :                         lib,
     217              :                         PQerrorMessage(conn));
     218              :             }
     219              :             else
     220           10 :                 was_load_failure = false;
     221              : 
     222           10 :             PQclear(res);
     223              :         }
     224              : 
     225           12 :         if (was_load_failure)
     226            0 :             fprintf(script, _("In database: %s\n"),
     227            0 :                     old_cluster.dbarr.dbs[os_info.libraries[libnum].dbnum].db_name);
     228              :     }
     229              : 
     230           15 :     PQfinish(conn);
     231              : 
     232           15 :     if (script)
     233              :     {
     234            0 :         fclose(script);
     235            0 :         pg_log(PG_REPORT, "fatal");
     236            0 :         pg_fatal("Your installation references loadable libraries that are missing from the\n"
     237              :                  "new installation.  You can add these libraries to the new installation,\n"
     238              :                  "or remove the functions using them from the old installation.  A list of\n"
     239              :                  "problem libraries is in the file:\n"
     240              :                  "    %s", output_path);
     241              :     }
     242              :     else
     243           15 :         check_ok();
     244           15 : }
        

Generated by: LCOV version 2.0-1