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-2022, 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  bool found = false;
127  char output_path[MAXPGPATH];
128 
129  prep_status("Checking for presence of required libraries");
130 
131  snprintf(output_path, sizeof(output_path), "%s/%s",
132  log_opts.basedir, "loadable_libraries.txt");
133 
134  /*
135  * Now we want to sort the library names into order. This avoids multiple
136  * probes of the same library, and ensures that libraries are probed in a
137  * consistent order, which is important for reproducible behavior if one
138  * library depends on another.
139  */
142 
143  for (libnum = 0; libnum < os_info.num_libraries; libnum++)
144  {
145  char *lib = os_info.libraries[libnum].name;
146  int llen = strlen(lib);
147  char cmd[7 + 2 * MAXPGPATH + 1];
148  PGresult *res;
149 
150  /* Did the library name change? Probe it. */
151  if (libnum == 0 || strcmp(lib, os_info.libraries[libnum - 1].name) != 0)
152  {
153  strcpy(cmd, "LOAD '");
154  PQescapeStringConn(conn, cmd + strlen(cmd), lib, llen, NULL);
155  strcat(cmd, "'");
156 
157  res = PQexec(conn, cmd);
158 
160  {
161  found = true;
162  was_load_failure = true;
163 
164  if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
165  pg_fatal("could not open file \"%s\": %s\n",
166  output_path, strerror(errno));
167  fprintf(script, _("could not load library \"%s\": %s"),
168  lib,
170  }
171  else
172  was_load_failure = false;
173 
174  PQclear(res);
175  }
176 
177  if (was_load_failure)
178  fprintf(script, _("In database: %s\n"),
180  }
181 
182  PQfinish(conn);
183 
184  if (found)
185  {
186  fclose(script);
187  pg_log(PG_REPORT, "fatal\n");
188  pg_fatal("Your installation references loadable libraries that are missing from the\n"
189  "new installation. You can add these libraries to the new installation,\n"
190  "or remove the functions using them from the old installation. A list of\n"
191  "problem libraries is in the file:\n"
192  " %s\n\n", output_path);
193  }
194  else
195  check_ok();
196 }
#define _(x)
Definition: elog.c:89
char * PQerrorMessage(const PGconn *conn)
Definition: fe-connect.c:6908
void PQfinish(PGconn *conn)
Definition: fe-connect.c:4261
size_t PQescapeStringConn(PGconn *conn, char *to, const char *from, size_t length, int *error)
Definition: fe-exec.c:4005
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:3270
void PQclear(PGresult *res)
Definition: fe-exec.c:718
int PQntuples(const PGresult *res)
Definition: fe-exec.c:3340
PGresult * PQexec(PGconn *conn, const char *query)
Definition: fe-exec.c:2273
char * PQgetvalue(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3735
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:1908
@ 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:380
@ PG_REPORT
Definition: pg_upgrade.h:237
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:238
#define snprintf
Definition: port.h:225
#define fprintf
Definition: port.h:229
#define qsort(a, b, c, d)
Definition: port.h:495
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:254
DbInfo * dbs
Definition: pg_upgrade.h:185
char * db_name
Definition: pg_upgrade.h:172
char * name
Definition: pg_upgrade.h:301
char * basedir
Definition: pg_upgrade.h:279
LibraryInfo * libraries
Definition: pg_upgrade.h:315
int num_libraries
Definition: pg_upgrade.h:316
#define FirstNormalObjectId
Definition: transam.h:197