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-2024, 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 library_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  */
59 static void
61 {
63 
65 
66  state->ress[dbinfo - old_cluster.dbarr.dbs] = res;
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  */
78 void
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 
126  os_info.libraries[totaltups].dbnum = dbnum;
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);
144  os_info.libraries[totaltups].dbnum = dbnum;
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  */
164 void
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:972
#define _(x)
Definition: elog.c:90
char * PQerrorMessage(const PGconn *conn)
Definition: fe-connect.c:7212
void PQfinish(PGconn *conn)
Definition: fe-connect.c:4893
size_t PQescapeStringConn(PGconn *conn, char *to, const char *from, size_t length, int *error)
Definition: fe-exec.c:4145
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
char * PQgetvalue(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3876
char * pg_strdup(const char *in)
Definition: fe_memutils.c:85
void pg_free(void *ptr)
Definition: fe_memutils.c:105
void * pg_malloc(size_t size)
Definition: fe_memutils.c:47
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:2091
static int pg_cmp_s32(int32 a, int32 b)
Definition: int.h:598
static int pg_cmp_size(size_t a, size_t b)
Definition: int.h:622
@ PGRES_COMMAND_OK
Definition: libpq-fe.h:120
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
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
PGconn * connectToServer(ClusterInfo *cluster, const char *db_name)
Definition: server.c:28
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:238
#define fprintf
Definition: port.h:242
#define qsort(a, b, c, d)
Definition: port.h:447
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