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