LCOV - code coverage report
Current view: top level - src/bin/pg_upgrade - function.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 85.0 % 80 68
Test Date: 2026-03-03 13:15:30 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           50 : process_loadable_libraries(DbInfo *dbinfo, PGresult *res, void *arg)
      61              : {
      62           50 :     struct loadable_libraries_state *state = (struct loadable_libraries_state *) arg;
      63              : 
      64           50 :     state->ress[dbinfo - old_cluster.dbarr.dbs] = res;
      65           50 :     state->totaltups += PQntuples(res);
      66           50 : }
      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           16 : get_loadable_libraries(void)
      78              : {
      79              :     int         totaltups;
      80              :     int         dbnum;
      81              :     int         n_libinfos;
      82           16 :     UpgradeTask *task = upgrade_task_create();
      83              :     struct loadable_libraries_state state;
      84              :     char       *query;
      85              : 
      86           16 :     state.ress = pg_malloc_array(PGresult *, old_cluster.dbarr.ndbs);
      87           16 :     state.totaltups = 0;
      88              : 
      89           16 :     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           16 :     upgrade_task_add_step(task, query, process_loadable_libraries,
      98              :                           false, &state);
      99              : 
     100           16 :     upgrade_task_run(task, &old_cluster);
     101           16 :     upgrade_task_free(task);
     102              : 
     103              :     /*
     104              :      * Allocate memory for required libraries and logical replication output
     105              :      * plugins.
     106              :      */
     107           16 :     n_libinfos = state.totaltups + count_old_cluster_logical_slots();
     108           16 :     os_info.libraries = pg_malloc_array(LibraryInfo, n_libinfos);
     109           16 :     totaltups = 0;
     110              : 
     111           66 :     for (dbnum = 0; dbnum < old_cluster.dbarr.ndbs; dbnum++)
     112              :     {
     113           50 :         PGresult   *res = state.ress[dbnum];
     114              :         int         ntups;
     115              :         int         rowno;
     116           50 :         LogicalSlotInfoArr *slot_arr = &old_cluster.dbarr.dbs[dbnum].slot_arr;
     117              : 
     118           50 :         ntups = PQntuples(res);
     119           57 :         for (rowno = 0; rowno < ntups; rowno++)
     120              :         {
     121            7 :             char       *lib = PQgetvalue(res, rowno, 0);
     122              : 
     123            7 :             os_info.libraries[totaltups].name = pg_strdup(lib);
     124            7 :             os_info.libraries[totaltups].dbnum = dbnum;
     125              : 
     126            7 :             totaltups++;
     127              :         }
     128           50 :         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           57 :         for (int slotno = 0; slotno < slot_arr->nslots; slotno++)
     137              :         {
     138            7 :             if (slot_arr->slots[slotno].invalid)
     139            0 :                 continue;
     140              : 
     141            7 :             os_info.libraries[totaltups].name = pg_strdup(slot_arr->slots[slotno].plugin);
     142            7 :             os_info.libraries[totaltups].dbnum = dbnum;
     143              : 
     144            7 :             totaltups++;
     145              :         }
     146              :     }
     147              : 
     148           16 :     pg_free(state.ress);
     149           16 :     pg_free(query);
     150              : 
     151           16 :     os_info.num_libraries = totaltups;
     152           16 : }
     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           14 : check_loadable_libraries(void)
     164              : {
     165           14 :     PGconn     *conn = connectToServer(&new_cluster, "template1");
     166              :     int         libnum;
     167           14 :     int         was_load_failure = false;
     168           14 :     FILE       *script = NULL;
     169              :     char        output_path[MAXPGPATH];
     170              : 
     171           14 :     prep_status("Checking for presence of required libraries");
     172              : 
     173           14 :     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           14 :     qsort(os_info.libraries, os_info.num_libraries,
     183              :           sizeof(LibraryInfo), library_name_compare);
     184              : 
     185           25 :     for (libnum = 0; libnum < os_info.num_libraries; libnum++)
     186              :     {
     187           11 :         char       *lib = os_info.libraries[libnum].name;
     188           11 :         int         llen = strlen(lib);
     189              :         char        cmd[7 + 2 * MAXPGPATH + 1];
     190              :         PGresult   *res;
     191              : 
     192              :         /* Did the library name change?  Probe it. */
     193           11 :         if (libnum == 0 || strcmp(lib, os_info.libraries[libnum - 1].name) != 0)
     194              :         {
     195            9 :             strcpy(cmd, "LOAD '");
     196            9 :             PQescapeStringConn(conn, cmd + strlen(cmd), lib, llen, NULL);
     197            9 :             strcat(cmd, "'");
     198              : 
     199            9 :             res = PQexec(conn, cmd);
     200              : 
     201            9 :             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            9 :                 was_load_failure = false;
     213              : 
     214            9 :             PQclear(res);
     215              :         }
     216              : 
     217           11 :         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           14 :     PQfinish(conn);
     223              : 
     224           14 :     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           14 :         check_ok();
     236           14 : }
        

Generated by: LCOV version 2.0-1