diff --git a/src/bin/pg_dump/t/010_dump_connstr.pl b/src/bin/pg_dump/t/010_dump_connstr.pl new file mode 100644 index 0000000000000000000000000000000000000000..2d0d1e4298e0e1b486bb46fe4c5753b3e05b1983 --- /dev/null +++ b/src/bin/pg_dump/t/010_dump_connstr.pl @@ -0,0 +1,142 @@ +use strict; +use warnings; + +use PostgresNode; +use TestLib; +use Test::More tests => 14; + +# In a SQL_ASCII database, pgwin32_message_to_UTF16() needs to +# interpret everything as UTF8. We're going to use byte sequences +# that aren't valid UTF-8 strings, so that would fail. Use LATIN1, +# which accepts any byte and has a conversion from each byte to UTF-8. +$ENV{LC_ALL} = 'C'; +$ENV{PGCLIENTENCODING} = 'LATIN1'; + +# Create database and user names covering the range of LATIN1 +# characters, for use in a connection string by pg_dumpall. Skip ',' +# because of pg_regress --create-role, skip [\n\r] because pg_dumpall +# does not allow them. +my $dbname1 = generate_ascii_string(1, 9) . + generate_ascii_string(11, 12) . + generate_ascii_string(14, 33) . + ($TestLib::windows_os ? '' : '"x"') . # IPC::Run mishandles '"' on Windows + generate_ascii_string(35, 43) . + generate_ascii_string(45, 63); # contains '=' +my $dbname2 = generate_ascii_string(67, 129); # skip 64-66 to keep length to 62 +my $dbname3 = generate_ascii_string(130, 192); +my $dbname4 = generate_ascii_string(193, 255); + +my $node = get_new_node('main'); +$node->init(extra => ['--locale=C', '--encoding=LATIN1']); +# prep pg_hba.conf and pg_ident.conf +$node->run_log([$ENV{PG_REGRESS}, '--config-auth', $node->data_dir, + '--create-role', "$dbname1,$dbname2,$dbname3,$dbname4"]); +$node->start; + +my $backupdir = $node->backup_dir; +my $discard = "$backupdir/discard.sql"; +my $plain = "$backupdir/plain.sql"; +my $dirfmt = "$backupdir/dirfmt"; + +foreach my $dbname ($dbname1, $dbname2, $dbname3, $dbname4, 'CamelCase') +{ + $node->run_log(['createdb', $dbname]); + $node->run_log(['createuser', '-s', $dbname]); +} + + +# For these tests, pg_dumpall -r is used because it produces a short +# dump. +$node->command_ok(['pg_dumpall', '-r', '-f', $discard, '--dbname', + $node->connstr($dbname1), '-U', $dbname4], + 'pg_dumpall with long ASCII name 1'); +$node->command_ok(['pg_dumpall', '-r', '-f', $discard, '--dbname', + $node->connstr($dbname2), '-U', $dbname3], + 'pg_dumpall with long ASCII name 2'); +$node->command_ok(['pg_dumpall', '-r', '-f', $discard, '--dbname', + $node->connstr($dbname3), '-U', $dbname2], + 'pg_dumpall with long ASCII name 3'); +$node->command_ok(['pg_dumpall', '-r', '-f', $discard, '--dbname', + $node->connstr($dbname4), '-U', $dbname1], + 'pg_dumpall with long ASCII name 4'); +$node->command_ok(['pg_dumpall', '-r', '-l', 'dbname=template1'], + 'pg_dumpall -l accepts connection string'); + +$node->run_log(['createdb', "foo\n\rbar"]); +# not sufficient to use -r here +$node->command_fails(['pg_dumpall', '-f', $discard], + 'pg_dumpall with \n\r in database name'); +$node->run_log(['dropdb', "foo\n\rbar"]); + + +# make a table, so the parallel worker has something to dump +$node->safe_psql($dbname1, 'CREATE TABLE t0()'); +# XXX no printed message when this fails, just SIGPIPE termination +$node->command_ok(['pg_dump', '-Fd', '-j2', '-f', $dirfmt, + '-U', $dbname1, $node->connstr($dbname1)], + 'parallel dump'); + +# recreate $dbname1 for restore test +$node->run_log(['dropdb', $dbname1]); +$node->run_log(['createdb', $dbname1]); + +$node->command_ok(['pg_restore', '-v', '-d', 'template1', '-j2', + '-U', $dbname1, $dirfmt], + 'parallel restore'); + +$node->run_log(['dropdb', $dbname1]); + +$node->command_ok(['pg_restore', '-C', '-v', '-d', 'template1', '-j2', + '-U', $dbname1, $dirfmt], + 'parallel restore with create'); + + +$node->command_ok(['pg_dumpall', '-f', $plain, '-U', $dbname1], + 'take full dump'); +system_log('cat', $plain); +my($stderr, $result); +my $bootstrap_super = 'boot'; +my $restore_super = qq{a'b\\c=d\\ne"f}; + + +# Restore full dump through psql using environment variables for +# dbname/user connection parameters + +my $envar_node = get_new_node('destination_envar'); +$envar_node->init(extra => ['-U', $bootstrap_super, + '--locale=C', '--encoding=LATIN1']); +$envar_node->run_log([$ENV{PG_REGRESS}, + '--config-auth', $envar_node->data_dir, + '--create-role', "$bootstrap_super,$restore_super"]); +$envar_node->start; + +# make superuser for restore +$envar_node->run_log(['createuser', '-U', $bootstrap_super, '-s', $restore_super]); + +{ + local $ENV{PGPORT} = $envar_node->port; + local $ENV{PGUSER} = $restore_super; + $result = run_log(['psql', '-X', '-f', $plain], '2>', \$stderr); +} +ok($result, 'restore full dump using environment variables for connection parameters'); +is($stderr, '', 'no dump errors'); + + +# Restore full dump through psql using command-line options for +# dbname/user connection parameters. "\connect dbname=" forgets +# user/port from command line. + +$restore_super =~ s/"//g if $TestLib::windows_os; # IPC::Run mishandles '"' on Windows +my $cmdline_node = get_new_node('destination_cmdline'); +$cmdline_node->init(extra => ['-U', $bootstrap_super, + '--locale=C', '--encoding=LATIN1']); +$cmdline_node->run_log([$ENV{PG_REGRESS}, + '--config-auth', $cmdline_node->data_dir, + '--create-role', "$bootstrap_super,$restore_super"]); +$cmdline_node->start; +$cmdline_node->run_log(['createuser', '-U', $bootstrap_super, '-s', $restore_super]); +{ + $result = run_log(['psql', '-p', $cmdline_node->port, '-U', $restore_super, '-X', '-f', $plain], '2>', \$stderr); +} +ok($result, 'restore full dump with command-line options for connection parameters'); +is($stderr, '', 'no dump errors'); diff --git a/src/bin/pg_rewind/RewindTest.pm b/src/bin/pg_rewind/RewindTest.pm index 135d8f0449f596cf86e8a43809356b78b0b2d4ea..c7c3a8f45c6abe8b05006d899fcf885ecfd6934c 100644 --- a/src/bin/pg_rewind/RewindTest.pm +++ b/src/bin/pg_rewind/RewindTest.pm @@ -133,7 +133,7 @@ sub create_standby $node_standby = get_new_node('standby'); $node_master->backup('my_backup'); $node_standby->init_from_backup($node_master, 'my_backup'); - my $connstr_master = $node_master->connstr('postgres'); + my $connstr_master = $node_master->connstr(); $node_standby->append_conf( "recovery.conf", qq( diff --git a/src/bin/scripts/t/010_clusterdb.pl b/src/bin/scripts/t/010_clusterdb.pl index 0e677cacf18cc51cfd9e74365467eb08e2ccf117..f3de9c016cee7176593f0777476f2a232eef5645 100644 --- a/src/bin/scripts/t/010_clusterdb.pl +++ b/src/bin/scripts/t/010_clusterdb.pl @@ -3,7 +3,7 @@ use warnings; use PostgresNode; use TestLib; -use Test::More tests => 13; +use Test::More tests => 14; program_help_ok('clusterdb'); program_version_ok('clusterdb'); @@ -28,3 +28,6 @@ $node->issues_sql_like( [ 'clusterdb', '-t', 'test1' ], qr/statement: CLUSTER test1;/, 'cluster specific table'); + +$node->command_ok([qw(clusterdb --echo --verbose dbname=template1)], + 'clusterdb with connection string'); diff --git a/src/bin/scripts/t/090_reindexdb.pl b/src/bin/scripts/t/090_reindexdb.pl index d92896f34f6ae3f5eedc3e54758266654671f590..42d6fb4eb456c3c4353446d129a2a89ec552f483 100644 --- a/src/bin/scripts/t/090_reindexdb.pl +++ b/src/bin/scripts/t/090_reindexdb.pl @@ -3,7 +3,7 @@ use warnings; use PostgresNode; use TestLib; -use Test::More tests => 20; +use Test::More tests => 23; program_help_ok('reindexdb'); program_version_ok('reindexdb'); @@ -42,3 +42,10 @@ $node->issues_sql_like( [ 'reindexdb', '-v', '-t', 'test1', 'postgres' ], qr/statement: REINDEX \(VERBOSE\) TABLE test1;/, 'reindex with verbose output'); + +$node->command_ok([qw(reindexdb --echo --table=pg_am dbname=template1)], + 'reindexdb table with connection string'); +$node->command_ok([qw(reindexdb --echo dbname=template1)], + 'reindexdb database with connection string'); +$node->command_ok([qw(reindexdb --echo --system dbname=template1)], + 'reindexdb system with connection string'); diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl index c183ccb6a1903ae3e5fea77c657b46b334cd4179..07c6e9e7ce136a61dd1ccf0430b081d93aae4973 100644 --- a/src/bin/scripts/t/100_vacuumdb.pl +++ b/src/bin/scripts/t/100_vacuumdb.pl @@ -3,7 +3,7 @@ use warnings; use PostgresNode; use TestLib; -use Test::More tests => 18; +use Test::More tests => 19; program_help_ok('vacuumdb'); program_version_ok('vacuumdb'); @@ -33,3 +33,5 @@ $node->issues_sql_like( [ 'vacuumdb', '-Z', 'postgres' ], qr/statement: ANALYZE;/, 'vacuumdb -Z'); +$node->command_ok([qw(vacuumdb -Z --table=pg_am dbname=template1)], + 'vacuumdb with connection string'); diff --git a/src/bin/scripts/t/200_connstr.pl b/src/bin/scripts/t/200_connstr.pl new file mode 100644 index 0000000000000000000000000000000000000000..89945712d29ac4089fb66b3ae570cced3bef0ced --- /dev/null +++ b/src/bin/scripts/t/200_connstr.pl @@ -0,0 +1,38 @@ +use strict; +use warnings; + +use PostgresNode; +use TestLib; +use Test::More tests => 3; + +# Tests to check connection string handling in utilities + +# In a SQL_ASCII database, pgwin32_message_to_UTF16() needs to +# interpret everything as UTF8. We're going to use byte sequences +# that aren't valid UTF-8 strings, so that would fail. Use LATIN1, +# which accepts any byte and has a conversion from each byte to UTF-8. +$ENV{LC_ALL} = 'C'; +$ENV{PGCLIENTENCODING} = 'LATIN1'; + +# Create database names covering the range of LATIN1 characters and +# run the utilities' --all options over them. +my $dbname1 = generate_ascii_string(1, 63); # contains '=' +my $dbname2 = generate_ascii_string(67, 129); # skip 64-66 to keep length to 62 +my $dbname3 = generate_ascii_string(130, 192); +my $dbname4 = generate_ascii_string(193, 255); + +my $node = get_new_node('main'); +$node->init(extra => ['--locale=C', '--encoding=LATIN1']); +$node->start; + +foreach my $dbname ($dbname1, $dbname2, $dbname3, $dbname4, 'CamelCase') +{ + $node->run_log(['createdb', $dbname]); +} + +$node->command_ok([qw(vacuumdb --all --echo --analyze-only)], + 'vacuumdb --all with unusual database names'); +$node->command_ok([qw(reindexdb --all --echo)], + 'reindexdb --all with unusual database names'); +$node->command_ok([qw(clusterdb --all --echo --verbose)], + 'clusterdb --all with unusual database names'); diff --git a/src/test/perl/PostgresNode.pm b/src/test/perl/PostgresNode.pm index fede1e601b96d122a3594e01195c6744ed4f51ef..afbdb6332bd8a1a949f2c906f4efeb6aebb849bc 100644 --- a/src/test/perl/PostgresNode.pm +++ b/src/test/perl/PostgresNode.pm @@ -243,7 +243,13 @@ sub connstr { return "port=$pgport host=$pghost"; } - return "port=$pgport host=$pghost dbname=$dbname"; + + # Escape properly the database string before using it, only + # single quotes and backslashes need to be treated this way. + $dbname =~ s#\\#\\\\#g; + $dbname =~ s#\'#\\\'#g; + + return "port=$pgport host=$pghost dbname='$dbname'"; } =pod @@ -396,7 +402,8 @@ sub init mkdir $self->backup_dir; mkdir $self->archive_dir; - TestLib::system_or_bail('initdb', '-D', $pgdata, '-A', 'trust', '-N'); + TestLib::system_or_bail('initdb', '-D', $pgdata, '-A', 'trust', '-N', + @{ $params{extra} }); TestLib::system_or_bail($ENV{PG_REGRESS}, '--config-auth', $pgdata); open my $conf, ">>$pgdata/postgresql.conf"; @@ -1300,6 +1307,24 @@ sub issues_sql_like =pod +=item $node->run_log(...) + +Runs a shell command like TestLib::run_log, but with PGPORT set so +that the command will default to connecting to this PostgresNode. + +=cut + +sub run_log +{ + my $self = shift; + + local $ENV{PGPORT} = $self->port; + + TestLib::run_log(@_); +} + +=pod + =back =cut diff --git a/src/test/perl/TestLib.pm b/src/test/perl/TestLib.pm index 51b533e08cd51fe30eb3454af8cf97554ef71695..31e7acd4daea8db486fc0b4d27c6a1821b96f88e 100644 --- a/src/test/perl/TestLib.pm +++ b/src/test/perl/TestLib.pm @@ -20,6 +20,7 @@ use SimpleTee; use Test::More; our @EXPORT = qw( + generate_ascii_string slurp_dir slurp_file append_to_file @@ -166,6 +167,19 @@ sub run_log return IPC::Run::run(@_); } +# Generate a string made of the given range of ASCII characters +sub generate_ascii_string +{ + my ($from_char, $to_char) = @_; + my $res; + + for my $i ($from_char .. $to_char) + { + $res .= sprintf("%c", $i); + } + return $res; +} + sub slurp_dir { my ($dir) = @_; diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c index 14c87c91abdb37b8a68de7621a8bde035dd5a732..1154d4c300e292536c319c53c314c8c978d61749 100644 --- a/src/test/regress/pg_regress.c +++ b/src/test/regress/pg_regress.c @@ -876,6 +876,29 @@ initialize_environment(void) load_resultmap(); } +pg_attribute_unused() +static const char * +fmtHba(const char *raw) +{ + static char *ret; + const char *rp; + char *wp; + + wp = ret = realloc(ret, 3 + strlen(raw) * 2); + + *wp++ = '"'; + for (rp = raw; *rp; rp++) + { + if (*rp == '"') + *wp++ = '"'; + *wp++ = *rp; + } + *wp++ = '"'; + *wp++ = '\0'; + + return ret; +} + #ifdef ENABLE_SSPI /* * Get account and domain/realm names for the current user. This is based on @@ -1037,11 +1060,11 @@ config_sspi_auth(const char *pgdata) * '#'. Windows forbids the double-quote character itself, so don't * bother escaping embedded double-quote characters. */ - CW(fprintf(ident, "regress \"%s@%s\" \"%s\"\n", - accountname, domainname, username) >= 0); + CW(fprintf(ident, "regress \"%s@%s\" %s\n", + accountname, domainname, fmtHba(username)) >= 0); for (sl = extraroles; sl; sl = sl->next) - CW(fprintf(ident, "regress \"%s@%s\" \"%s\"\n", - accountname, domainname, sl->str) >= 0); + CW(fprintf(ident, "regress \"%s@%s\" %s\n", + accountname, domainname, fmtHba(sl->str)) >= 0); CW(fclose(ident) == 0); } #endif @@ -2064,7 +2087,7 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc * before we add the specified one. */ free_stringlist(&dblist); - split_to_stringlist(optarg, ", ", &dblist); + split_to_stringlist(optarg, ",", &dblist); break; case 2: debug = true; @@ -2114,7 +2137,7 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc dlpath = pg_strdup(optarg); break; case 18: - split_to_stringlist(optarg, ", ", &extraroles); + split_to_stringlist(optarg, ",", &extraroles); break; case 19: add_stringlist_item(&temp_configs, optarg);