PostgreSQL Source Code git master
function.c
Go to the documentation of this file.
1/*
2 * function.c
3 *
4 * server-side function support
5 *
6 * Copyright (c) 2010-2025, 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 */
28static int
29library_name_compare(const void *p1, const void *p2)
30{
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);
36
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);
43}
44
45/*
46 * Private state for get_loadable_libraries()'s UpgradeTask.
47 */
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 */
59static void
61{
63
65
67 state->totaltups += PQntuples(res);
68}
69
70/*
71 * get_loadable_libraries()
72 *
73 * Fetch the names of all old libraries containing either C-language functions
74 * or are corresponding to logical replication output plugins.
75 *
76 * We will later check that they all exist in the new installation.
77 */
78void
80{
81 int totaltups;
82 int dbnum;
83 int n_libinfos;
86 char *query;
87
88 state.ress = (PGresult **) pg_malloc(old_cluster.dbarr.ndbs * sizeof(PGresult *));
89 state.totaltups = 0;
90
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,
98
100 false, &state);
101
103 upgrade_task_free(task);
104
105 /*
106 * Allocate memory for required libraries and logical replication output
107 * plugins.
108 */
109 n_libinfos = state.totaltups + count_old_cluster_logical_slots();
110 os_info.libraries = (LibraryInfo *) pg_malloc(sizeof(LibraryInfo) * n_libinfos);
111 totaltups = 0;
112
113 for (dbnum = 0; dbnum < old_cluster.dbarr.ndbs; dbnum++)
114 {
115 PGresult *res = state.ress[dbnum];
116 int ntups;
117 int rowno;
118 LogicalSlotInfoArr *slot_arr = &old_cluster.dbarr.dbs[dbnum].slot_arr;
119
120 ntups = PQntuples(res);
121 for (rowno = 0; rowno < ntups; rowno++)
122 {
123 char *lib = PQgetvalue(res, rowno, 0);
124
127
128 totaltups++;
129 }
130 PQclear(res);
131
132 /*
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.
137 */
138 for (int slotno = 0; slotno < slot_arr->nslots; slotno++)
139 {
140 if (slot_arr->slots[slotno].invalid)
141 continue;
142
143 os_info.libraries[totaltups].name = pg_strdup(slot_arr->slots[slotno].plugin);
145
146 totaltups++;
147 }
148 }
149
150 pg_free(state.ress);
151 pg_free(query);
152
154}
155
156
157/*
158 * check_loadable_libraries()
159 *
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.
163 */
164void
166{
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];
172
173 prep_status("Checking for presence of required libraries");
174
175 snprintf(output_path, sizeof(output_path), "%s/%s",
176 log_opts.basedir, "loadable_libraries.txt");
177
178 /*
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.
183 */
186
187 for (libnum = 0; libnum < os_info.num_libraries; libnum++)
188 {
189 char *lib = os_info.libraries[libnum].name;
190 int llen = strlen(lib);
191 char cmd[7 + 2 * MAXPGPATH + 1];
192 PGresult *res;
193
194 /* Did the library name change? Probe it. */
195 if (libnum == 0 || strcmp(lib, os_info.libraries[libnum - 1].name) != 0)
196 {
197 strcpy(cmd, "LOAD '");
198 PQescapeStringConn(conn, cmd + strlen(cmd), lib, llen, NULL);
199 strcat(cmd, "'");
200
201 res = PQexec(conn, cmd);
202
204 {
205 was_load_failure = true;
206
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,
212 }
213 else
214 was_load_failure = false;
215
216 PQclear(res);
217 }
218
219 if (was_load_failure)
220 fprintf(script, _("In database: %s\n"),
222 }
223
224 PQfinish(conn);
225
226 if (script)
227 {
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);
235 }
236 else
237 check_ok();
238}
#define AssertVariableIsOfType(varname, typename)
Definition: c.h:938
#define fprintf(file, fmt, msg)
Definition: cubescan.l:21
#define _(x)
Definition: elog.c:90
void PQfinish(PGconn *conn)
Definition: fe-connect.c:5178
char * PQerrorMessage(const PGconn *conn)
Definition: fe-connect.c:7507
size_t PQescapeStringConn(PGconn *conn, char *to, const char *from, size_t length, int *error)
Definition: fe-exec.c:4183
char * PQgetvalue(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3876
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:3411
int PQntuples(const PGresult *res)
Definition: fe-exec.c:3481
PGresult * PQexec(PGconn *conn, const char *query)
Definition: fe-exec.c:2262
void * pg_malloc(size_t size)
Definition: fe_memutils.c:47
char * pg_strdup(const char *in)
Definition: fe_memutils.c:85
void pg_free(void *ptr)
Definition: fe_memutils.c:105
void check_loadable_libraries(void)
Definition: function.c:165
static void process_loadable_libraries(DbInfo *dbinfo, PGresult *res, void *arg)
Definition: function.c:60
static int library_name_compare(const void *p1, const void *p2)
Definition: function.c:29
void get_loadable_libraries(void)
Definition: function.c:79
int count_old_cluster_logical_slots(void)
Definition: info.c:742
static void check_ok(void)
Definition: initdb.c:2119
static int pg_cmp_s32(int32 a, int32 b)
Definition: int.h:646
static int pg_cmp_size(size_t a, size_t b)
Definition: int.h:670
@ PGRES_COMMAND_OK
Definition: libpq-fe.h:122
void * arg
#define pg_fatal(...)
#define MAXPGPATH
static struct LogicalRepInfo * dbinfo
OSInfo os_info
Definition: pg_upgrade.c:69
ClusterInfo new_cluster
Definition: pg_upgrade.c:68
ClusterInfo old_cluster
Definition: pg_upgrade.c:67
UpgradeTask * upgrade_task_create(void)
Definition: task.c:117
PGconn * connectToServer(ClusterInfo *cluster, const char *db_name)
Definition: server.c:28
void upgrade_task_run(const UpgradeTask *task, const ClusterInfo *cluster)
Definition: task.c:420
void void pg_log(eLogType type, const char *fmt,...) pg_attribute_printf(2
LogOpts log_opts
Definition: util.c:17
void upgrade_task_free(UpgradeTask *task)
Definition: task.c:133
#define fopen_priv(path, mode)
Definition: pg_upgrade.h:419
void(* UpgradeTaskProcessCB)(DbInfo *dbinfo, PGresult *res, void *arg)
Definition: pg_upgrade.h:500
@ PG_REPORT
Definition: pg_upgrade.h:269
void prep_status(const char *fmt,...) pg_attribute_printf(1
void upgrade_task_add_step(UpgradeTask *task, const char *query, UpgradeTaskProcessCB process_cb, bool free_result, void *arg)
Definition: task.c:151
#define snprintf
Definition: port.h:239
#define qsort(a, b, c, d)
Definition: port.h:475
char * psprintf(const char *fmt,...)
Definition: psprintf.c:43
static int cmp(const chr *x, const chr *y, size_t len)
Definition: regc_locale.c:743
PGconn * conn
Definition: streamutil.c:53
DbInfoArr dbarr
Definition: pg_upgrade.h:284
DbInfo * dbs
Definition: pg_upgrade.h:215
LogicalSlotInfoArr slot_arr
Definition: pg_upgrade.h:198
char * db_name
Definition: pg_upgrade.h:194
char * name
Definition: pg_upgrade.h:334
char * basedir
Definition: pg_upgrade.h:311
LogicalSlotInfo * slots
Definition: pg_upgrade.h:169
LibraryInfo * libraries
Definition: pg_upgrade.h:348
int num_libraries
Definition: pg_upgrade.h:349
Definition: regguts.h:323
#define FirstNormalObjectId
Definition: transam.h:197