plpgsql: pure parser and reentrant scanner
[pgsql.git] / src / bin / pg_upgrade / function.c
blob9ad53caeebc9d1902cb1fd1fc9d13712ac76feda
1 /*
2 * function.c
4 * server-side function support
6 * Copyright (c) 2010-2025, PostgreSQL Global Development Group
7 * src/bin/pg_upgrade/function.c
8 */
10 #include "postgres_fe.h"
12 #include "access/transam.h"
13 #include "catalog/pg_language_d.h"
14 #include "common/int.h"
15 #include "pg_upgrade.h"
18 * qsort comparator for pointers to library names
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.)
28 static int
29 library_name_compare(const void *p1, const void *p2)
31 const char *str1 = ((const LibraryInfo *) p1)->name;
32 const char *str2 = ((const LibraryInfo *) p2)->name;
33 size_t slen1 = strlen(str1);
34 size_t slen2 = strlen(str2);
35 int cmp = strcmp(str1, str2);
37 if (slen1 != slen2)
38 return pg_cmp_size(slen1, slen2);
39 if (cmp != 0)
40 return cmp;
41 return pg_cmp_s32(((const LibraryInfo *) p1)->dbnum,
42 ((const LibraryInfo *) p2)->dbnum);
46 * Private state for get_loadable_libraries()'s UpgradeTask.
48 struct loadable_libraries_state
50 PGresult **ress; /* results for each database */
51 int totaltups; /* number of tuples in all results */
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().
59 static void
60 process_loadable_libraries(DbInfo *dbinfo, PGresult *res, void *arg)
62 struct loadable_libraries_state *state = (struct loadable_libraries_state *) arg;
64 AssertVariableIsOfType(&process_loadable_libraries, UpgradeTaskProcessCB);
66 state->ress[dbinfo - old_cluster.dbarr.dbs] = res;
67 state->totaltups += PQntuples(res);
71 * get_loadable_libraries()
73 * Fetch the names of all old libraries containing either C-language functions
74 * or are corresponding to logical replication output plugins.
76 * We will later check that they all exist in the new installation.
78 void
79 get_loadable_libraries(void)
81 int totaltups;
82 int dbnum;
83 int n_libinfos;
84 UpgradeTask *task = upgrade_task_create();
85 struct loadable_libraries_state state;
86 char *query;
88 state.ress = (PGresult **) pg_malloc(old_cluster.dbarr.ndbs * sizeof(PGresult *));
89 state.totaltups = 0;
91 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);
99 upgrade_task_add_step(task, query, process_loadable_libraries,
100 false, &state);
102 upgrade_task_run(task, &old_cluster);
103 upgrade_task_free(task);
106 * Allocate memory for required libraries and logical replication output
107 * plugins.
109 n_libinfos = state.totaltups + count_old_cluster_logical_slots();
110 os_info.libraries = (LibraryInfo *) pg_malloc(sizeof(LibraryInfo) * n_libinfos);
111 totaltups = 0;
113 for (dbnum = 0; dbnum < old_cluster.dbarr.ndbs; dbnum++)
115 PGresult *res = state.ress[dbnum];
116 int ntups;
117 int rowno;
118 LogicalSlotInfoArr *slot_arr = &old_cluster.dbarr.dbs[dbnum].slot_arr;
120 ntups = PQntuples(res);
121 for (rowno = 0; rowno < ntups; rowno++)
123 char *lib = PQgetvalue(res, rowno, 0);
125 os_info.libraries[totaltups].name = pg_strdup(lib);
126 os_info.libraries[totaltups].dbnum = dbnum;
128 totaltups++;
130 PQclear(res);
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.
138 for (int slotno = 0; slotno < slot_arr->nslots; slotno++)
140 if (slot_arr->slots[slotno].invalid)
141 continue;
143 os_info.libraries[totaltups].name = pg_strdup(slot_arr->slots[slotno].plugin);
144 os_info.libraries[totaltups].dbnum = dbnum;
146 totaltups++;
150 pg_free(state.ress);
151 pg_free(query);
153 os_info.num_libraries = totaltups;
158 * check_loadable_libraries()
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.
164 void
165 check_loadable_libraries(void)
167 PGconn *conn = connectToServer(&new_cluster, "template1");
168 int libnum;
169 int was_load_failure = false;
170 FILE *script = NULL;
171 char output_path[MAXPGPATH];
173 prep_status("Checking for presence of required libraries");
175 snprintf(output_path, sizeof(output_path), "%s/%s",
176 log_opts.basedir, "loadable_libraries.txt");
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.
184 qsort(os_info.libraries, os_info.num_libraries,
185 sizeof(LibraryInfo), library_name_compare);
187 for (libnum = 0; libnum < os_info.num_libraries; libnum++)
189 char *lib = os_info.libraries[libnum].name;
190 int llen = strlen(lib);
191 char cmd[7 + 2 * MAXPGPATH + 1];
192 PGresult *res;
194 /* Did the library name change? Probe it. */
195 if (libnum == 0 || strcmp(lib, os_info.libraries[libnum - 1].name) != 0)
197 strcpy(cmd, "LOAD '");
198 PQescapeStringConn(conn, cmd + strlen(cmd), lib, llen, NULL);
199 strcat(cmd, "'");
201 res = PQexec(conn, cmd);
203 if (PQresultStatus(res) != PGRES_COMMAND_OK)
205 was_load_failure = true;
207 if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
208 pg_fatal("could not open file \"%s\": %m", output_path);
209 fprintf(script, _("could not load library \"%s\": %s"),
210 lib,
211 PQerrorMessage(conn));
213 else
214 was_load_failure = false;
216 PQclear(res);
219 if (was_load_failure)
220 fprintf(script, _("In database: %s\n"),
221 old_cluster.dbarr.dbs[os_info.libraries[libnum].dbnum].db_name);
224 PQfinish(conn);
226 if (script)
228 fclose(script);
229 pg_log(PG_REPORT, "fatal");
230 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);
236 else
237 check_ok();