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-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
28 library_name_compare(const void *p1, const void *p2)
29 {
30  const char *str1 = ((const LibraryInfo *) p1)->name;
31  const char *str2 = ((const LibraryInfo *) p2)->name;
32  int slen1 = strlen(str1);
33  int slen2 = strlen(str2);
34  int cmp = strcmp(str1, str2);
35 
36  if (slen1 != slen2)
37  return slen1 - slen2;
38  if (cmp != 0)
39  return cmp;
40  else
41  return ((const LibraryInfo *) p1)->dbnum -
42  ((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
54 {
55  PGresult **ress;
56  int totaltups;
57  int dbnum;
58 
59  ress = (PGresult **) pg_malloc(old_cluster.dbarr.ndbs * sizeof(PGresult *));
60  totaltups = 0;
61 
62  /* Fetch all library names, removing duplicates within each DB */
63  for (dbnum = 0; dbnum < old_cluster.dbarr.ndbs; dbnum++)
64  {
65  DbInfo *active_db = &old_cluster.dbarr.dbs[dbnum];
67 
68  /*
69  * Fetch all libraries containing non-built-in C functions in this DB.
70  */
71  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,
79  totaltups += PQntuples(ress[dbnum]);
80 
81  PQfinish(conn);
82  }
83 
84  os_info.libraries = (LibraryInfo *) pg_malloc(totaltups * sizeof(LibraryInfo));
85  totaltups = 0;
86 
87  for (dbnum = 0; dbnum < old_cluster.dbarr.ndbs; dbnum++)
88  {
89  PGresult *res = ress[dbnum];
90  int ntups;
91  int rowno;
92 
93  ntups = PQntuples(res);
94  for (rowno = 0; rowno < ntups; rowno++)
95  {
96  char *lib = PQgetvalue(res, rowno, 0);
97 
98  os_info.libraries[totaltups].name = pg_strdup(lib);
99  os_info.libraries[totaltups].dbnum = dbnum;
100 
101  totaltups++;
102  }
103  PQclear(res);
104  }
105 
106  pg_free(ress);
107 
108  os_info.num_libraries = totaltups;
109 }
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
121 {
122  PGconn *conn = connectToServer(&new_cluster, "template1");
123  int libnum;
124  int was_load_failure = false;
125  FILE *script = NULL;
126  char output_path[MAXPGPATH];
127 
128  prep_status("Checking for presence of required libraries");
129 
130  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.
138  */
141 
142  for (libnum = 0; libnum < os_info.num_libraries; libnum++)
143  {
144  char *lib = os_info.libraries[libnum].name;
145  int llen = strlen(lib);
146  char cmd[7 + 2 * MAXPGPATH + 1];
147  PGresult *res;
148 
149  /* Did the library name change? Probe it. */
150  if (libnum == 0 || strcmp(lib, os_info.libraries[libnum - 1].name) != 0)
151  {
152  strcpy(cmd, "LOAD '");
153  PQescapeStringConn(conn, cmd + strlen(cmd), lib, llen, NULL);
154  strcat(cmd, "'");
155 
156  res = PQexec(conn, cmd);
157 
159  {
160  was_load_failure = true;
161 
162  if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
163  pg_fatal("could not open file \"%s\": %s",
164  output_path, strerror(errno));
165  fprintf(script, _("could not load library \"%s\": %s"),
166  lib,
168  }
169  else
170  was_load_failure = false;
171 
172  PQclear(res);
173  }
174 
175  if (was_load_failure)
176  fprintf(script, _("In database: %s\n"),
178  }
179 
180  PQfinish(conn);
181 
182  if (script)
183  {
184  fclose(script);
185  pg_log(PG_REPORT, "fatal");
186  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);
191  }
192  else
193  check_ok();
194 }
#define _(x)
Definition: elog.c:91
char * PQerrorMessage(const PGconn *conn)
Definition: fe-connect.c:7018
void PQfinish(PGconn *conn)
Definition: fe-connect.c:4405
size_t PQescapeStringConn(PGconn *conn, char *to, const char *from, size_t length, int *error)
Definition: fe-exec.c:3978
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:3240
int PQntuples(const PGresult *res)
Definition: fe-exec.c:3310
PGresult * PQexec(PGconn *conn, const char *query)
Definition: fe-exec.c:2225
char * PQgetvalue(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3705
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:120
static int library_name_compare(const void *p1, const void *p2)
Definition: function.c:28
void get_loadable_libraries(void)
Definition: function.c:53
static void check_ok(void)
Definition: initdb.c:2041
@ PGRES_COMMAND_OK
Definition: libpq-fe.h:97
#define pg_fatal(...)
#define MAXPGPATH
OSInfo os_info
Definition: pg_upgrade.c:65
ClusterInfo new_cluster
Definition: pg_upgrade.c:64
ClusterInfo old_cluster
Definition: pg_upgrade.c:63
void void pg_log(eLogType type, const char *fmt,...) pg_attribute_printf(2
LogOpts log_opts
Definition: util.c:17
#define fopen_priv(path, mode)
Definition: pg_upgrade.h:390
@ PG_REPORT
Definition: pg_upgrade.h:248
PGconn * connectToServer(ClusterInfo *cluster, const char *db_name)
Definition: server.c:28
void prep_status(const char *fmt,...) pg_attribute_printf(1
PGresult * executeQueryOrDie(PGconn *conn, const char *fmt,...) pg_attribute_printf(2
#define strerror
Definition: port.h:251
#define snprintf
Definition: port.h:238
#define fprintf
Definition: port.h:242
#define qsort(a, b, c, d)
Definition: port.h:445
static int cmp(const chr *x, const chr *y, size_t len)
Definition: regc_locale.c:747
PGconn * conn
Definition: streamutil.c:54
DbInfoArr dbarr
Definition: pg_upgrade.h:263
DbInfo * dbs
Definition: pg_upgrade.h:195
char * db_name
Definition: pg_upgrade.h:175
char * name
Definition: pg_upgrade.h:311
char * basedir
Definition: pg_upgrade.h:289
LibraryInfo * libraries
Definition: pg_upgrade.h:325
int num_libraries
Definition: pg_upgrade.h:326
#define FirstNormalObjectId
Definition: transam.h:197