From ff27db5dd2fc096d89d3f995d3f650ec6d3bc147 Mon Sep 17 00:00:00 2001 From: Andres Freund Date: Sun, 12 Jul 2015 22:06:27 +0200 Subject: [PATCH] Optionally don't error out due to preexisting slots in commandline utilities. pg_receivexlog and pg_recvlogical error out when --create-slot is specified and a slot with the same name already exists. In some cases, especially with pg_receivexlog, that's rather annoying and requires additional scripting. Backpatch to 9.5 as slot control functions have newly been added to pg_receivexlog, and there doesn't seem much point leaving it in a less useful state. Discussion: 20150619144755.GG29350@alap3.anarazel.de --- doc/src/sgml/ref/pg_receivexlog.sgml | 10 ++++++ doc/src/sgml/ref/pg_recvlogical.sgml | 10 ++++++ src/bin/pg_basebackup/pg_receivexlog.c | 11 +++++-- src/bin/pg_basebackup/pg_recvlogical.c | 9 +++++- src/bin/pg_basebackup/streamutil.c | 44 +++++++++++--------------- src/bin/pg_basebackup/streamutil.h | 4 +-- 6 files changed, 58 insertions(+), 30 deletions(-) diff --git a/doc/src/sgml/ref/pg_receivexlog.sgml b/doc/src/sgml/ref/pg_receivexlog.sgml index fd787649e4..a4c98921cb 100644 --- a/doc/src/sgml/ref/pg_receivexlog.sgml +++ b/doc/src/sgml/ref/pg_receivexlog.sgml @@ -92,6 +92,16 @@ PostgreSQL documentation + + + + + Do not not error out when is specified + and a slot with the specified name already exists. + + + + diff --git a/doc/src/sgml/ref/pg_recvlogical.sgml b/doc/src/sgml/ref/pg_recvlogical.sgml index a28dbc3f18..4eda9ebdd1 100644 --- a/doc/src/sgml/ref/pg_recvlogical.sgml +++ b/doc/src/sgml/ref/pg_recvlogical.sgml @@ -154,6 +154,16 @@ PostgreSQL documentation + + + + + Do not not error out when is specified + and a slot with the specified name already exists. + + + + diff --git a/src/bin/pg_basebackup/pg_receivexlog.c b/src/bin/pg_basebackup/pg_receivexlog.c index 5d964e4ee6..00536bd097 100644 --- a/src/bin/pg_basebackup/pg_receivexlog.c +++ b/src/bin/pg_basebackup/pg_receivexlog.c @@ -38,6 +38,7 @@ static int noloop = 0; static int standby_message_timeout = 10 * 1000; /* 10 sec = default */ static volatile bool time_to_abort = false; static bool do_create_slot = false; +static bool slot_exists_ok = false; static bool do_drop_slot = false; static bool synchronous = false; @@ -66,6 +67,7 @@ usage(void) printf(_(" %s [OPTION]...\n"), progname); printf(_("\nOptions:\n")); printf(_(" -D, --directory=DIR receive transaction log files into this directory\n")); + printf(_(" --if-not-exists do not treat naming conflicts as an error when creating a slot\n")); printf(_(" -n, --no-loop do not loop on connection lost\n")); printf(_(" -s, --status-interval=SECS\n" " time between status packets sent to server (default: %d)\n"), (standby_message_timeout / 1000)); @@ -371,7 +373,8 @@ main(int argc, char **argv) /* action */ {"create-slot", no_argument, NULL, 1}, {"drop-slot", no_argument, NULL, 2}, - {"synchronous", no_argument, NULL, 3}, + {"if-not-exists", no_argument, NULL, 3}, + {"synchronous", no_argument, NULL, 4}, {NULL, 0, NULL, 0} }; @@ -455,6 +458,9 @@ main(int argc, char **argv) do_drop_slot = true; break; case 3: + slot_exists_ok = true; + break; + case 4: synchronous = true; break; default: @@ -575,7 +581,8 @@ main(int argc, char **argv) _("%s: creating replication slot \"%s\"\n"), progname, replication_slot); - if (!CreateReplicationSlot(conn, replication_slot, NULL, NULL, true)) + if (!CreateReplicationSlot(conn, replication_slot, NULL, true, + slot_exists_ok)) disconnect_and_exit(1); } diff --git a/src/bin/pg_basebackup/pg_recvlogical.c b/src/bin/pg_basebackup/pg_recvlogical.c index 50844e700d..f189f71eff 100644 --- a/src/bin/pg_basebackup/pg_recvlogical.c +++ b/src/bin/pg_basebackup/pg_recvlogical.c @@ -38,6 +38,7 @@ static int standby_message_timeout = 10 * 1000; /* 10 sec = default */ static int fsync_interval = 10 * 1000; /* 10 sec = default */ static XLogRecPtr startpos = InvalidXLogRecPtr; static bool do_create_slot = false; +static bool slot_exists_ok = false; static bool do_start_slot = false; static bool do_drop_slot = false; @@ -75,6 +76,7 @@ usage(void) printf(_(" -f, --file=FILE receive log into this file, - for stdout\n")); printf(_(" -F --fsync-interval=SECS\n" " time between fsyncs to the output file (default: %d)\n"), (fsync_interval / 1000)); + printf(_(" --if-not-exists do not treat naming conflicts as an error when creating a slot\n")); printf(_(" -I, --startpos=LSN where in an existing slot should the streaming start\n")); printf(_(" -n, --no-loop do not loop on connection lost\n")); printf(_(" -o, --option=NAME[=VALUE]\n" @@ -633,6 +635,7 @@ main(int argc, char **argv) {"create-slot", no_argument, NULL, 1}, {"start", no_argument, NULL, 2}, {"drop-slot", no_argument, NULL, 3}, + {"if-not-exists", no_argument, NULL, 4}, {NULL, 0, NULL, 0} }; int c; @@ -764,6 +767,9 @@ main(int argc, char **argv) case 3: do_drop_slot = true; break; + case 4: + slot_exists_ok = true; + break; default: @@ -891,8 +897,9 @@ main(int argc, char **argv) progname, replication_slot); if (!CreateReplicationSlot(conn, replication_slot, plugin, - &startpos, false)) + false, slot_exists_ok)) disconnect_and_exit(1); + startpos = InvalidXLogRecPtr; } if (!do_start_slot) diff --git a/src/bin/pg_basebackup/streamutil.c b/src/bin/pg_basebackup/streamutil.c index 0ed61440b0..a5cad350f8 100644 --- a/src/bin/pg_basebackup/streamutil.c +++ b/src/bin/pg_basebackup/streamutil.c @@ -31,6 +31,8 @@ #include "common/fe_memutils.h" #include "datatype/timestamp.h" +#define ERRCODE_DUPLICATE_OBJECT "42710" + const char *progname; char *connection_string = NULL; char *dbhost = NULL; @@ -314,7 +316,7 @@ RunIdentifySystem(PGconn *conn, char **sysid, TimeLineID *starttli, */ bool CreateReplicationSlot(PGconn *conn, const char *slot_name, const char *plugin, - XLogRecPtr *startpos, bool is_physical) + bool is_physical, bool slot_exists_ok) { PQExpBuffer query; PGresult *res; @@ -336,12 +338,23 @@ CreateReplicationSlot(PGconn *conn, const char *slot_name, const char *plugin, res = PQexec(conn, query->data); if (PQresultStatus(res) != PGRES_TUPLES_OK) { - fprintf(stderr, _("%s: could not send replication command \"%s\": %s"), - progname, query->data, PQerrorMessage(conn)); + const char *sqlstate = PQresultErrorField(res, PG_DIAG_SQLSTATE); - destroyPQExpBuffer(query); - PQclear(res); - return false; + if (slot_exists_ok && strcmp(sqlstate, ERRCODE_DUPLICATE_OBJECT) == 0) + { + destroyPQExpBuffer(query); + PQclear(res); + return true; + } + else + { + fprintf(stderr, _("%s: could not send replication command \"%s\": %s"), + progname, query->data, PQerrorMessage(conn)); + + destroyPQExpBuffer(query); + PQclear(res); + return false; + } } if (PQntuples(res) != 1 || PQnfields(res) != 4) @@ -356,25 +369,6 @@ CreateReplicationSlot(PGconn *conn, const char *slot_name, const char *plugin, return false; } - /* Get LSN start position if necessary */ - if (startpos != NULL) - { - uint32 hi, - lo; - - if (sscanf(PQgetvalue(res, 0, 1), "%X/%X", &hi, &lo) != 2) - { - fprintf(stderr, - _("%s: could not parse transaction log location \"%s\"\n"), - progname, PQgetvalue(res, 0, 1)); - - destroyPQExpBuffer(query); - PQclear(res); - return false; - } - *startpos = ((uint64) hi) << 32 | lo; - } - destroyPQExpBuffer(query); PQclear(res); return true; diff --git a/src/bin/pg_basebackup/streamutil.h b/src/bin/pg_basebackup/streamutil.h index 01ab5660a1..b95f83f87e 100644 --- a/src/bin/pg_basebackup/streamutil.h +++ b/src/bin/pg_basebackup/streamutil.h @@ -32,8 +32,8 @@ extern PGconn *GetConnection(void); /* Replication commands */ extern bool CreateReplicationSlot(PGconn *conn, const char *slot_name, - const char *plugin, XLogRecPtr *startpos, - bool is_physical); + const char *plugin, bool is_physical, + bool slot_exists_ok); extern bool DropReplicationSlot(PGconn *conn, const char *slot_name); extern bool RunIdentifySystem(PGconn *conn, char **sysid, TimeLineID *starttli,