diff options
-rw-r--r-- | src/bin/pg_basebackup/pg_createsubscriber.c | 53 | ||||
-rw-r--r-- | src/bin/pg_basebackup/t/040_pg_createsubscriber.pl | 111 |
2 files changed, 96 insertions, 68 deletions
diff --git a/src/bin/pg_basebackup/pg_createsubscriber.c b/src/bin/pg_basebackup/pg_createsubscriber.c index 1138c20e560..7c943317860 100644 --- a/src/bin/pg_basebackup/pg_createsubscriber.c +++ b/src/bin/pg_basebackup/pg_createsubscriber.c @@ -26,6 +26,7 @@ #include "common/restricted_token.h" #include "fe_utils/recovery_gen.h" #include "fe_utils/simple_list.h" +#include "fe_utils/string_utils.h" #include "getopt_long.h" #define DEFAULT_SUB_PORT "50432" @@ -237,14 +238,27 @@ usage(void) } /* + * Subroutine to append "keyword=value" to a connection string, + * with proper quoting of the value. (We assume keywords don't need that.) + */ +static void +appendConnStrItem(PQExpBuffer buf, const char *keyword, const char *val) +{ + if (buf->len > 0) + appendPQExpBufferChar(buf, ' '); + appendPQExpBufferStr(buf, keyword); + appendPQExpBufferChar(buf, '='); + appendConnStrVal(buf, val); +} + +/* * Validate a connection string. Returns a base connection string that is a * connection string without a database name. * * Since we might process multiple databases, each database name will be * appended to this base connection string to provide a final connection * string. If the second argument (dbname) is not null, returns dbname if the - * provided connection string contains it. If option --database is not - * provided, uses dbname as the only database to setup the logical replica. + * provided connection string contains it. * * It is the caller's responsibility to free the returned connection string and * dbname. @@ -257,7 +271,6 @@ get_base_conninfo(const char *conninfo, char **dbname) PQconninfoOption *conn_opt; char *errmsg = NULL; char *ret; - int i; conn_opts = PQconninfoParse(conninfo, &errmsg); if (conn_opts == NULL) @@ -268,22 +281,17 @@ get_base_conninfo(const char *conninfo, char **dbname) } buf = createPQExpBuffer(); - i = 0; for (conn_opt = conn_opts; conn_opt->keyword != NULL; conn_opt++) { - if (strcmp(conn_opt->keyword, "dbname") == 0 && conn_opt->val != NULL) - { - if (dbname) - *dbname = pg_strdup(conn_opt->val); - continue; - } - if (conn_opt->val != NULL && conn_opt->val[0] != '\0') { - if (i > 0) - appendPQExpBufferChar(buf, ' '); - appendPQExpBuffer(buf, "%s=%s", conn_opt->keyword, conn_opt->val); - i++; + if (strcmp(conn_opt->keyword, "dbname") == 0) + { + if (dbname) + *dbname = pg_strdup(conn_opt->val); + continue; + } + appendConnStrItem(buf, conn_opt->keyword, conn_opt->val); } } @@ -305,13 +313,13 @@ get_sub_conninfo(const struct CreateSubscriberOptions *opt) PQExpBuffer buf = createPQExpBuffer(); char *ret; - appendPQExpBuffer(buf, "port=%s", opt->sub_port); + appendConnStrItem(buf, "port", opt->sub_port); #if !defined(WIN32) - appendPQExpBuffer(buf, " host=%s", opt->socket_dir); + appendConnStrItem(buf, "host", opt->socket_dir); #endif if (opt->sub_username != NULL) - appendPQExpBuffer(buf, " user=%s", opt->sub_username); - appendPQExpBuffer(buf, " fallback_application_name=%s", progname); + appendConnStrItem(buf, "user", opt->sub_username); + appendConnStrItem(buf, "fallback_application_name", progname); ret = pg_strdup(buf->data); @@ -402,7 +410,7 @@ concat_conninfo_dbname(const char *conninfo, const char *dbname) Assert(conninfo != NULL); appendPQExpBufferStr(buf, conninfo); - appendPQExpBuffer(buf, " dbname=%s", dbname); + appendConnStrItem(buf, "dbname", dbname); ret = pg_strdup(buf->data); destroyPQExpBuffer(buf); @@ -1312,8 +1320,9 @@ start_standby_server(const struct CreateSubscriberOptions *opt, bool restricted_ PQExpBuffer pg_ctl_cmd = createPQExpBuffer(); int rc; - appendPQExpBuffer(pg_ctl_cmd, "\"%s\" start -D \"%s\" -s -o \"-c sync_replication_slots=off\"", - pg_ctl_path, subscriber_dir); + appendPQExpBuffer(pg_ctl_cmd, "\"%s\" start -D ", pg_ctl_path); + appendShellString(pg_ctl_cmd, subscriber_dir); + appendPQExpBuffer(pg_ctl_cmd, " -s -o \"-c sync_replication_slots=off\""); if (restricted_access) { appendPQExpBuffer(pg_ctl_cmd, " -o \"-p %s\"", opt->sub_port); diff --git a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl index 0516d4e17ed..68b798333d1 100644 --- a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl +++ b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl @@ -15,6 +15,27 @@ program_options_handling_ok('pg_createsubscriber'); my $datadir = PostgreSQL::Test::Utils::tempdir; +# Generate a database with a name made of a range of ASCII characters. +# Extracted from 002_pg_upgrade.pl. +sub generate_db +{ + my ($node, $prefix, $from_char, $to_char, $suffix) = @_; + + my $dbname = $prefix; + for my $i ($from_char .. $to_char) + { + next if $i == 7 || $i == 10 || $i == 13; # skip BEL, LF, and CR + $dbname = $dbname . sprintf('%c', $i); + } + + $dbname .= $suffix; + $node->command_ok( + [ 'createdb', $dbname ], + "created database with ASCII characters from $from_char to $to_char"); + + return $dbname; +} + # # Test mandatory options command_fails(['pg_createsubscriber'], @@ -104,16 +125,14 @@ $node_f->init(force_initdb => 1, allows_streaming => 'logical'); # - create test tables # - insert a row # - create a physical replication slot -$node_p->safe_psql( - 'postgres', q( - CREATE DATABASE pg1; - CREATE DATABASE pg2; -)); -$node_p->safe_psql('pg1', 'CREATE TABLE tbl1 (a text)'); -$node_p->safe_psql('pg1', "INSERT INTO tbl1 VALUES('first row')"); -$node_p->safe_psql('pg2', 'CREATE TABLE tbl2 (a text)'); +my $db1 = generate_db($node_p, 'regression\\"\\', 1, 45, '\\\\"\\\\\\'); +my $db2 = generate_db($node_p, 'regression', 46, 90, ''); + +$node_p->safe_psql($db1, 'CREATE TABLE tbl1 (a text)'); +$node_p->safe_psql($db1, "INSERT INTO tbl1 VALUES('first row')"); +$node_p->safe_psql($db2, 'CREATE TABLE tbl2 (a text)'); my $slotname = 'physical_slot'; -$node_p->safe_psql('pg2', +$node_p->safe_psql($db2, "SELECT pg_create_physical_replication_slot('$slotname')"); # Set up node S as standby linking to node P @@ -143,11 +162,11 @@ command_fails( 'pg_createsubscriber', '--verbose', '--dry-run', '--pgdata', $node_t->data_dir, '--publisher-server', - $node_p->connstr('pg1'), '--socket-directory', + $node_p->connstr($db1), '--socket-directory', $node_t->host, '--subscriber-port', $node_t->port, '--database', - 'pg1', '--database', - 'pg2' + $db1, '--database', + $db2 ], 'target server is not in recovery'); @@ -157,11 +176,11 @@ command_fails( 'pg_createsubscriber', '--verbose', '--dry-run', '--pgdata', $node_s->data_dir, '--publisher-server', - $node_p->connstr('pg1'), '--socket-directory', + $node_p->connstr($db1), '--socket-directory', $node_s->host, '--subscriber-port', $node_s->port, '--database', - 'pg1', '--database', - 'pg2' + $db1, '--database', + $db2 ], 'standby is up and running'); @@ -170,11 +189,11 @@ command_fails( [ 'pg_createsubscriber', '--verbose', '--pgdata', $node_f->data_dir, - '--publisher-server', $node_p->connstr('pg1'), + '--publisher-server', $node_p->connstr($db1), '--socket-directory', $node_f->host, '--subscriber-port', $node_f->port, - '--database', 'pg1', - '--database', 'pg2' + '--database', $db1, + '--database', $db2 ], 'subscriber data directory is not a copy of the source database cluster'); @@ -191,16 +210,16 @@ command_fails( 'pg_createsubscriber', '--verbose', '--dry-run', '--pgdata', $node_c->data_dir, '--publisher-server', - $node_s->connstr('pg1'), '--socket-directory', + $node_s->connstr($db1), '--socket-directory', $node_c->host, '--subscriber-port', $node_c->port, '--database', - 'pg1', '--database', - 'pg2' + $db1, '--database', + $db2 ], 'primary server is in recovery'); # Insert another row on node P and wait node S to catch up -$node_p->safe_psql('pg1', "INSERT INTO tbl1 VALUES('second row')"); +$node_p->safe_psql($db1, "INSERT INTO tbl1 VALUES('second row')"); $node_p->wait_for_replay_catchup($node_s); # Check some unmet conditions on node P @@ -218,11 +237,11 @@ command_fails( 'pg_createsubscriber', '--verbose', '--dry-run', '--pgdata', $node_s->data_dir, '--publisher-server', - $node_p->connstr('pg1'), '--socket-directory', + $node_p->connstr($db1), '--socket-directory', $node_s->host, '--subscriber-port', $node_s->port, '--database', - 'pg1', '--database', - 'pg2' + $db1, '--database', + $db2 ], 'primary contains unmet conditions on node P'); # Restore default settings here but only apply it after testing standby. Some @@ -247,11 +266,11 @@ command_fails( 'pg_createsubscriber', '--verbose', '--dry-run', '--pgdata', $node_s->data_dir, '--publisher-server', - $node_p->connstr('pg1'), '--socket-directory', + $node_p->connstr($db1), '--socket-directory', $node_s->host, '--subscriber-port', $node_s->port, '--database', - 'pg1', '--database', - 'pg2' + $db1, '--database', + $db2 ], 'standby contains unmet conditions on node S'); $node_s->append_conf( @@ -265,7 +284,7 @@ $node_p->restart; # Create failover slot to test its removal my $fslotname = 'failover_slot'; -$node_p->safe_psql('pg1', +$node_p->safe_psql($db1, "SELECT pg_create_logical_replication_slot('$fslotname', 'pgoutput', false, false, true)"); $node_s->start; $node_s->safe_psql('postgres', "SELECT pg_sync_replication_slots()"); @@ -280,15 +299,15 @@ command_ok( '--recovery-timeout', "$PostgreSQL::Test::Utils::timeout_default", '--dry-run', '--pgdata', $node_s->data_dir, '--publisher-server', - $node_p->connstr('pg1'), '--socket-directory', + $node_p->connstr($db1), '--socket-directory', $node_s->host, '--subscriber-port', $node_s->port, '--publication', 'pub1', '--publication', 'pub2', '--subscription', 'sub1', '--subscription', 'sub2', '--database', - 'pg1', '--database', - 'pg2' + $db1, '--database', + $db2 ], 'run pg_createsubscriber --dry-run on node S'); @@ -304,7 +323,7 @@ command_ok( 'pg_createsubscriber', '--verbose', '--dry-run', '--pgdata', $node_s->data_dir, '--publisher-server', - $node_p->connstr('pg1'), '--socket-directory', + $node_p->connstr($db1), '--socket-directory', $node_s->host, '--subscriber-port', $node_s->port, '--replication-slot', 'replslot1' @@ -318,20 +337,20 @@ command_ok( '--recovery-timeout', "$PostgreSQL::Test::Utils::timeout_default", '--verbose', '--pgdata', $node_s->data_dir, '--publisher-server', - $node_p->connstr('pg1'), '--socket-directory', + $node_p->connstr($db1), '--socket-directory', $node_s->host, '--subscriber-port', $node_s->port, '--publication', 'pub1', '--publication', 'Pub2', '--replication-slot', 'replslot1', '--replication-slot', 'replslot2', '--database', - 'pg1', '--database', - 'pg2' + $db1, '--database', + $db2 ], 'run pg_createsubscriber on node S'); # Confirm the physical replication slot has been removed -$result = $node_p->safe_psql('pg1', +$result = $node_p->safe_psql($db1, "SELECT count(*) FROM pg_replication_slots WHERE slot_name = '$slotname'" ); is($result, qq(0), @@ -339,8 +358,8 @@ is($result, qq(0), ); # Insert rows on P -$node_p->safe_psql('pg1', "INSERT INTO tbl1 VALUES('third row')"); -$node_p->safe_psql('pg2', "INSERT INTO tbl2 VALUES('row 1')"); +$node_p->safe_psql($db1, "INSERT INTO tbl1 VALUES('third row')"); +$node_p->safe_psql($db2, "INSERT INTO tbl2 VALUES('row 1')"); # Start subscriber $node_s->start; @@ -357,20 +376,20 @@ $node_s->wait_for_subscription_sync($node_p, $subnames[0]); $node_s->wait_for_subscription_sync($node_p, $subnames[1]); # Confirm the failover slot has been removed -$result = $node_s->safe_psql('pg1', +$result = $node_s->safe_psql($db1, "SELECT count(*) FROM pg_replication_slots WHERE slot_name = '$fslotname'"); is($result, qq(0), 'failover slot was removed'); -# Check result on database pg1 -$result = $node_s->safe_psql('pg1', 'SELECT * FROM tbl1'); +# Check result on database $db1 +$result = $node_s->safe_psql($db1, 'SELECT * FROM tbl1'); is( $result, qq(first row second row third row), - 'logical replication works on database pg1'); + "logical replication works on database $db1"); -# Check result on database pg2 -$result = $node_s->safe_psql('pg2', 'SELECT * FROM tbl2'); -is($result, qq(row 1), 'logical replication works on database pg2'); +# Check result on database $db2 +$result = $node_s->safe_psql($db2, 'SELECT * FROM tbl2'); +is($result, qq(row 1), "logical replication works on database $db2"); # Different system identifier? my $sysid_p = $node_p->safe_psql('postgres', |