summaryrefslogtreecommitdiff
path: root/contrib/pg_upgrade/server.c
diff options
context:
space:
mode:
authorBruce Momjian <bruce@momjian.us>2010-05-12 02:19:11 +0000
committerBruce Momjian <bruce@momjian.us>2010-05-12 02:19:11 +0000
commitc2e9b2f288185a8569f6391ea250c7eeafa6c14b (patch)
tree408e8eb0c0aacaf177602789c02d7a416bbd59e1 /contrib/pg_upgrade/server.c
parent28e1742217716076da0700094a369eae5766974c (diff)
Add pg_upgrade to /contrib; will be in 9.0 beta2.
Add documentation. Supports migration from PG 8.3 and 8.4.
Diffstat (limited to 'contrib/pg_upgrade/server.c')
-rw-r--r--contrib/pg_upgrade/server.c316
1 files changed, 316 insertions, 0 deletions
diff --git a/contrib/pg_upgrade/server.c b/contrib/pg_upgrade/server.c
new file mode 100644
index 00000000000..15f4c5f07f9
--- /dev/null
+++ b/contrib/pg_upgrade/server.c
@@ -0,0 +1,316 @@
+/*
+ * server.c
+ *
+ * database server functions
+ */
+
+#include "pg_upgrade.h"
+
+#define POSTMASTER_UPTIME 20
+
+#define STARTUP_WARNING_TRIES 2
+
+
+static pgpid_t get_postmaster_pid(migratorContext *ctx, const char *datadir);
+static bool test_server_conn(migratorContext *ctx, int timeout,
+ Cluster whichCluster);
+
+
+/*
+ * connectToServer()
+ *
+ * Connects to the desired database on the designated server.
+ * If the connection attempt fails, this function logs an error
+ * message and calls exit_nicely() to kill the program.
+ */
+PGconn *
+connectToServer(migratorContext *ctx, const char *db_name,
+ Cluster whichCluster)
+{
+ char connectString[MAXPGPATH];
+ unsigned short port = (whichCluster == CLUSTER_OLD) ?
+ ctx->old.port : ctx->new.port;
+ PGconn *conn;
+
+ snprintf(connectString, sizeof(connectString),
+ "dbname = '%s' user = '%s' port = %d", db_name, ctx->user, port);
+
+ conn = PQconnectdb(connectString);
+
+ if (conn == NULL || PQstatus(conn) != CONNECTION_OK)
+ {
+ pg_log(ctx, PG_REPORT, "Connection to database failed: %s\n",
+ PQerrorMessage(conn));
+
+ if (conn)
+ PQfinish(conn);
+
+ exit_nicely(ctx, true);
+ }
+
+ return conn;
+}
+
+
+/*
+ * executeQueryOrDie()
+ *
+ * Formats a query string from the given arguments and executes the
+ * resulting query. If the query fails, this function logs an error
+ * message and calls exit_nicely() to kill the program.
+ */
+PGresult *
+executeQueryOrDie(migratorContext *ctx, PGconn *conn, const char *fmt,...)
+{
+ static char command[8192];
+ va_list args;
+ PGresult *result;
+ ExecStatusType status;
+
+ va_start(args, fmt);
+ vsnprintf(command, sizeof(command), fmt, args);
+ va_end(args);
+
+ pg_log(ctx, PG_DEBUG, "executing: %s\n", command);
+ result = PQexec(conn, command);
+ status = PQresultStatus(result);
+
+ if ((status != PGRES_TUPLES_OK) && (status != PGRES_COMMAND_OK))
+ {
+ pg_log(ctx, PG_REPORT, "DB command failed\n%s\n%s\n", command,
+ PQerrorMessage(conn));
+ PQclear(result);
+ PQfinish(conn);
+ exit_nicely(ctx, true);
+ return NULL; /* Never get here, but keeps compiler happy */
+ }
+ else
+ return result;
+}
+
+
+/*
+ * get_postmaster_pid()
+ *
+ * Returns the pid of the postmaster running on datadir. pid is retrieved
+ * from the postmaster.pid file
+ */
+static pgpid_t
+get_postmaster_pid(migratorContext *ctx, const char *datadir)
+{
+ FILE *pidf;
+ long pid;
+ char pid_file[MAXPGPATH];
+
+ snprintf(pid_file, sizeof(pid_file), "%s/postmaster.pid", datadir);
+ pidf = fopen(pid_file, "r");
+
+ if (pidf == NULL)
+ return (pgpid_t) 0;
+
+ if (fscanf(pidf, "%ld", &pid) != 1)
+ {
+ fclose(pidf);
+ pg_log(ctx, PG_FATAL, "%s: invalid data in PID file \"%s\"\n",
+ ctx->progname, pid_file);
+ }
+
+ fclose(pidf);
+
+ return (pgpid_t) pid;
+}
+
+
+/*
+ * get_major_server_version()
+ *
+ * gets the version (in unsigned int form) for the given "datadir". Assumes
+ * that datadir is an absolute path to a valid pgdata directory. The version
+ * is retrieved by reading the PG_VERSION file.
+ */
+uint32
+get_major_server_version(migratorContext *ctx, char **verstr, Cluster whichCluster)
+{
+ const char *datadir = whichCluster == CLUSTER_OLD ?
+ ctx->old.pgdata : ctx->new.pgdata;
+ FILE *version_fd;
+ char ver_file[MAXPGPATH];
+ int integer_version = 0;
+ int fractional_version = 0;
+
+ *verstr = pg_malloc(ctx, 64);
+
+ snprintf(ver_file, sizeof(ver_file), "%s/PG_VERSION", datadir);
+ if ((version_fd = fopen(ver_file, "r")) == NULL)
+ return 0;
+
+ if (fscanf(version_fd, "%63s", *verstr) == 0 ||
+ sscanf(*verstr, "%d.%d", &integer_version, &fractional_version) != 2)
+ {
+ pg_log(ctx, PG_FATAL, "could not get version from %s\n", datadir);
+ fclose(version_fd);
+ return 0;
+ }
+
+ return (100 * integer_version + fractional_version) * 100;
+}
+
+
+void
+start_postmaster(migratorContext *ctx, Cluster whichCluster, bool quiet)
+{
+ char cmd[MAXPGPATH];
+ const char *bindir;
+ const char *datadir;
+ unsigned short port;
+
+ if (whichCluster == CLUSTER_OLD)
+ {
+ bindir = ctx->old.bindir;
+ datadir = ctx->old.pgdata;
+ port = ctx->old.port;
+ }
+ else
+ {
+ bindir = ctx->new.bindir;
+ datadir = ctx->new.pgdata;
+ port = ctx->new.port;
+ }
+
+ /* use -l for Win32 */
+ sprintf(cmd, SYSTEMQUOTE "\"%s/pg_ctl\" -l \"%s\" -D \"%s\" "
+ "-o \"-p %d -c autovacuum=off -c autovacuum_freeze_max_age=2000000000\" "
+ "start >> \"%s\" 2>&1" SYSTEMQUOTE,
+ bindir, ctx->logfile, datadir, port, ctx->logfile);
+ exec_prog(ctx, true, "%s", cmd);
+
+ /* wait for the server to start properly */
+
+ if (test_server_conn(ctx, POSTMASTER_UPTIME, whichCluster) == false)
+ pg_log(ctx, PG_FATAL, " Unable to start %s postmaster with the command: %s\nPerhaps pg_hba.conf was not set to \"trust\".",
+ CLUSTERNAME(whichCluster), cmd);
+
+ if ((ctx->postmasterPID = get_postmaster_pid(ctx, datadir)) == 0)
+ pg_log(ctx, PG_FATAL, " Unable to get postmaster pid\n");
+ ctx->running_cluster = whichCluster;
+}
+
+
+void
+stop_postmaster(migratorContext *ctx, bool fast, bool quiet)
+{
+ const char *bindir;
+ const char *datadir;
+
+ if (ctx->running_cluster == CLUSTER_OLD)
+ {
+ bindir = ctx->old.bindir;
+ datadir = ctx->old.pgdata;
+ }
+ else if (ctx->running_cluster == CLUSTER_NEW)
+ {
+ bindir = ctx->new.bindir;
+ datadir = ctx->new.pgdata;
+ }
+ else
+ return; /* no cluster running */
+
+ /* use -l for Win32 */
+ exec_prog(ctx, fast ? false : true,
+ SYSTEMQUOTE "\"%s/pg_ctl\" -l \"%s\" -D \"%s\" %s stop >> \"%s\" 2>&1" SYSTEMQUOTE,
+ bindir, ctx->logfile, datadir, fast ? "-m fast" : "", ctx->logfile);
+
+ ctx->postmasterPID = 0;
+ ctx->running_cluster = NONE;
+}
+
+
+/*
+ * test_server_conn()
+ *
+ * tests whether postmaster is running or not by trying to connect
+ * to it. If connection is unsuccessfull we do a sleep of 1 sec and then
+ * try the connection again. This process continues "timeout" times.
+ *
+ * Returns true if the connection attempt was successfull, false otherwise.
+ */
+static bool
+test_server_conn(migratorContext *ctx, int timeout, Cluster whichCluster)
+{
+ PGconn *conn = NULL;
+ char con_opts[MAX_STRING];
+ int tries;
+ unsigned short port = (whichCluster == CLUSTER_OLD) ?
+ ctx->old.port : ctx->new.port;
+ bool ret = false;
+
+ snprintf(con_opts, sizeof(con_opts),
+ "dbname = 'template1' user = '%s' port = %d ", ctx->user, port);
+
+ for (tries = 0; tries < timeout; tries++)
+ {
+ sleep(1);
+ if ((conn = PQconnectdb(con_opts)) != NULL &&
+ PQstatus(conn) == CONNECTION_OK)
+ {
+ PQfinish(conn);
+ ret = true;
+ break;
+ }
+
+ if (tries == STARTUP_WARNING_TRIES)
+ prep_status(ctx, "Trying to start %s server ",
+ CLUSTERNAME(whichCluster));
+ else if (tries > STARTUP_WARNING_TRIES)
+ pg_log(ctx, PG_REPORT, ".");
+ }
+
+ if (tries > STARTUP_WARNING_TRIES)
+ check_ok(ctx);
+
+ return ret;
+}
+
+
+/*
+ * check_for_libpq_envvars()
+ *
+ * tests whether any libpq environment variables are set.
+ * Since pg_upgrade connects to both the old and the new server,
+ * it is potentially dangerous to have any of these set.
+ *
+ * If any are found, will log them and cancel.
+ */
+void
+check_for_libpq_envvars(migratorContext *ctx)
+{
+ PQconninfoOption *option;
+ PQconninfoOption *start;
+ bool found = false;
+
+ /* Get valid libpq env vars from the PQconndefaults function */
+
+ start = option = PQconndefaults();
+
+ while (option->keyword != NULL)
+ {
+ const char *value;
+
+ if (option->envvar && (value = getenv(option->envvar)) && strlen(value) > 0)
+ {
+ found = true;
+
+ pg_log(ctx, PG_WARNING,
+ "libpq env var %-20s is currently set to: %s\n", option->envvar, value);
+ }
+
+ option++;
+ }
+
+ /* Free the memory that libpq allocated on our behalf */
+ PQconninfoFree(start);
+
+ if (found)
+ pg_log(ctx, PG_FATAL,
+ "libpq env vars have been found and listed above, please unset them for pg_upgrade\n");
+}