summaryrefslogtreecommitdiff
path: root/contrib/pg_upgrade/function.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/pg_upgrade/function.c')
-rw-r--r--contrib/pg_upgrade/function.c262
1 files changed, 262 insertions, 0 deletions
diff --git a/contrib/pg_upgrade/function.c b/contrib/pg_upgrade/function.c
new file mode 100644
index 00000000000..a7a410adbec
--- /dev/null
+++ b/contrib/pg_upgrade/function.c
@@ -0,0 +1,262 @@
+/*
+ * function.c
+ *
+ * server-side function support
+ */
+
+#include "pg_upgrade.h"
+
+#include "access/transam.h"
+
+
+/*
+ * install_support_functions()
+ *
+ * pg_upgrade requires some support functions that enable it to modify
+ * backend behavior.
+ */
+void
+install_support_functions(migratorContext *ctx)
+{
+ int dbnum;
+
+ prep_status(ctx, "Adding support functions to new cluster");
+
+ for (dbnum = 0; dbnum < ctx->new.dbarr.ndbs; dbnum++)
+ {
+ DbInfo *newdb = &ctx->new.dbarr.dbs[dbnum];
+ PGconn *conn = connectToServer(ctx, newdb->db_name, CLUSTER_NEW);
+
+ /* suppress NOTICE of dropped objects */
+ PQclear(executeQueryOrDie(ctx, conn,
+ "SET client_min_messages = warning;"));
+ PQclear(executeQueryOrDie(ctx, conn,
+ "DROP SCHEMA IF EXISTS binary_upgrade CASCADE;"));
+ PQclear(executeQueryOrDie(ctx, conn,
+ "RESET client_min_messages;"));
+
+ PQclear(executeQueryOrDie(ctx, conn,
+ "CREATE SCHEMA binary_upgrade;"));
+
+ PQclear(executeQueryOrDie(ctx, conn,
+ "CREATE OR REPLACE FUNCTION "
+ " binary_upgrade.set_next_pg_type_oid(OID) "
+ "RETURNS VOID "
+ "AS '$libdir/pg_upgrade_sysoids' "
+ "LANGUAGE C STRICT;"));
+ PQclear(executeQueryOrDie(ctx, conn,
+ "CREATE OR REPLACE FUNCTION "
+ " binary_upgrade.set_next_pg_type_array_oid(OID) "
+ "RETURNS VOID "
+ "AS '$libdir/pg_upgrade_sysoids' "
+ "LANGUAGE C STRICT;"));
+ PQclear(executeQueryOrDie(ctx, conn,
+ "CREATE OR REPLACE FUNCTION "
+ " binary_upgrade.set_next_pg_type_toast_oid(OID) "
+ "RETURNS VOID "
+ "AS '$libdir/pg_upgrade_sysoids' "
+ "LANGUAGE C STRICT;"));
+ PQclear(executeQueryOrDie(ctx, conn,
+ "CREATE OR REPLACE FUNCTION "
+ " binary_upgrade.set_next_heap_relfilenode(OID) "
+ "RETURNS VOID "
+ "AS '$libdir/pg_upgrade_sysoids' "
+ "LANGUAGE C STRICT;"));
+ PQclear(executeQueryOrDie(ctx, conn,
+ "CREATE OR REPLACE FUNCTION "
+ " binary_upgrade.set_next_toast_relfilenode(OID) "
+ "RETURNS VOID "
+ "AS '$libdir/pg_upgrade_sysoids' "
+ "LANGUAGE C STRICT;"));
+ PQclear(executeQueryOrDie(ctx, conn,
+ "CREATE OR REPLACE FUNCTION "
+ " binary_upgrade.set_next_index_relfilenode(OID) "
+ "RETURNS VOID "
+ "AS '$libdir/pg_upgrade_sysoids' "
+ "LANGUAGE C STRICT;"));
+ PQclear(executeQueryOrDie(ctx, conn,
+ "CREATE OR REPLACE FUNCTION "
+ " binary_upgrade.add_pg_enum_label(OID, OID, NAME) "
+ "RETURNS VOID "
+ "AS '$libdir/pg_upgrade_sysoids' "
+ "LANGUAGE C STRICT;"));
+ PQfinish(conn);
+ }
+ check_ok(ctx);
+}
+
+
+void
+uninstall_support_functions(migratorContext *ctx)
+{
+ int dbnum;
+
+ prep_status(ctx, "Removing support functions from new cluster");
+
+ for (dbnum = 0; dbnum < ctx->new.dbarr.ndbs; dbnum++)
+ {
+ DbInfo *newdb = &ctx->new.dbarr.dbs[dbnum];
+ PGconn *conn = connectToServer(ctx, newdb->db_name, CLUSTER_NEW);
+
+ /* suppress NOTICE of dropped objects */
+ PQclear(executeQueryOrDie(ctx, conn,
+ "SET client_min_messages = warning;"));
+ PQclear(executeQueryOrDie(ctx, conn,
+ "DROP SCHEMA binary_upgrade CASCADE;"));
+ PQclear(executeQueryOrDie(ctx, conn,
+ "RESET client_min_messages;"));
+ PQfinish(conn);
+ }
+ check_ok(ctx);
+}
+
+
+/*
+ * get_loadable_libraries()
+ *
+ * Fetch the names of all old libraries containing C-language functions.
+ * We will later check that they all exist in the new installation.
+ */
+void
+get_loadable_libraries(migratorContext *ctx)
+{
+ ClusterInfo *active_cluster = &ctx->old;
+ PGresult **ress;
+ int totaltups;
+ int dbnum;
+
+ ress = (PGresult **)
+ pg_malloc(ctx, active_cluster->dbarr.ndbs * sizeof(PGresult *));
+ totaltups = 0;
+
+ /* Fetch all library names, removing duplicates within each DB */
+ for (dbnum = 0; dbnum < active_cluster->dbarr.ndbs; dbnum++)
+ {
+ DbInfo *active_db = &active_cluster->dbarr.dbs[dbnum];
+ PGconn *conn = connectToServer(ctx, active_db->db_name, CLUSTER_OLD);
+
+ /* Fetch all libraries referenced in this DB */
+ ress[dbnum] = executeQueryOrDie(ctx, conn,
+ "SELECT DISTINCT probin "
+ "FROM pg_catalog.pg_proc "
+ "WHERE prolang = 13 /* C */ AND "
+ " probin IS NOT NULL AND "
+ " oid >= %u;",
+ FirstNormalObjectId);
+ totaltups += PQntuples(ress[dbnum]);
+
+ PQfinish(conn);
+ }
+
+ /* Allocate what's certainly enough space */
+ if (totaltups > 0)
+ ctx->libraries = (char **) pg_malloc(ctx, totaltups * sizeof(char *));
+ else
+ ctx->libraries = NULL;
+
+ /*
+ * Now remove duplicates across DBs. This is pretty inefficient code, but
+ * there probably aren't enough entries to matter.
+ */
+ totaltups = 0;
+
+ for (dbnum = 0; dbnum < active_cluster->dbarr.ndbs; dbnum++)
+ {
+ PGresult *res = ress[dbnum];
+ int ntups;
+ int rowno;
+
+ ntups = PQntuples(res);
+ for (rowno = 0; rowno < ntups; rowno++)
+ {
+ char *lib = PQgetvalue(res, rowno, 0);
+ bool dup = false;
+ int n;
+
+ for (n = 0; n < totaltups; n++)
+ {
+ if (strcmp(lib, ctx->libraries[n]) == 0)
+ {
+ dup = true;
+ break;
+ }
+ }
+ if (!dup)
+ ctx->libraries[totaltups++] = pg_strdup(ctx, lib);
+ }
+
+ PQclear(res);
+ }
+
+ ctx->num_libraries = totaltups;
+
+ pg_free(ress);
+}
+
+
+/*
+ * check_loadable_libraries()
+ *
+ * Check that the new cluster contains all required libraries.
+ * We do this by actually trying to LOAD each one, thereby testing
+ * compatibility as well as presence.
+ */
+void
+check_loadable_libraries(migratorContext *ctx)
+{
+ PGconn *conn = connectToServer(ctx, "template1", CLUSTER_NEW);
+ int libnum;
+ FILE *script = NULL;
+ bool found = false;
+ char output_path[MAXPGPATH];
+
+ prep_status(ctx, "Checking for presence of required libraries");
+
+ snprintf(output_path, sizeof(output_path), "%s/loadable_libraries.txt",
+ ctx->output_dir);
+
+ for (libnum = 0; libnum < ctx->num_libraries; libnum++)
+ {
+ char *lib = ctx->libraries[libnum];
+ int llen = strlen(lib);
+ char *cmd = (char *) pg_malloc(ctx, 8 + 2 * llen + 1);
+ PGresult *res;
+
+ strcpy(cmd, "LOAD '");
+ PQescapeStringConn(conn, cmd + 6, lib, llen, NULL);
+ strcat(cmd, "'");
+
+ res = PQexec(conn, cmd);
+
+ if (PQresultStatus(res) != PGRES_COMMAND_OK)
+ {
+ found = true;
+ if (script == NULL && (script = fopen(output_path, "w")) == NULL)
+ pg_log(ctx, PG_FATAL, "Could not create necessary file: %s\n",
+ output_path);
+ fprintf(script, "Failed to load library: %s\n%s\n",
+ lib,
+ PQerrorMessage(conn));
+ }
+
+ PQclear(res);
+ pg_free(cmd);
+ }
+
+ PQfinish(conn);
+
+ if (found)
+ {
+ fclose(script);
+ pg_log(ctx, PG_REPORT, "fatal\n");
+ pg_log(ctx, PG_FATAL,
+ "| Your installation uses loadable libraries that are missing\n"
+ "| from the new installation. You can add these libraries to\n"
+ "| the new installation, or remove the functions using them\n"
+ "| from the old installation. A list of the problem libraries\n"
+ "| is in the file\n"
+ "| \"%s\".\n\n", output_path);
+ }
+ else
+ check_ok(ctx);
+}