mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-02-05 19:09:58 +08:00
Add POD docs to PostgresNode
Also, the dump_info method got split into another method that returns the stuff as a string instead of just printing it to stdout. Add a new README in src/test/perl too. Author: Craig Ringer Reviewed by: Michaël Paquier
This commit is contained in:
parent
bda0b08198
commit
e64009303d
@ -1,8 +1,58 @@
|
||||
# PostgresNode, class representing a data directory and postmaster.
|
||||
#
|
||||
# This contains a basic set of routines able to work on a PostgreSQL node,
|
||||
# allowing to start, stop, backup and initialize it with various options.
|
||||
# The set of nodes managed by a given test is also managed by this module.
|
||||
=pod
|
||||
|
||||
=head1 NAME
|
||||
|
||||
PostgresNode - class representing PostgreSQL server instance
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use PostgresNode;
|
||||
|
||||
my $node = get_new_node('mynode');
|
||||
|
||||
# Create a data directory with initdb
|
||||
$node->init();
|
||||
|
||||
# Start the PostgreSQL server
|
||||
$node->start();
|
||||
|
||||
# Change a setting and restart
|
||||
$node->append_conf('postgresql.conf', 'hot_standby = on');
|
||||
$node->restart('fast');
|
||||
|
||||
# run a query with psql
|
||||
# like: psql -qAXt postgres -c 'SELECT 1;'
|
||||
$psql_stdout = $node->psql('postgres', 'SELECT 1');
|
||||
|
||||
# run query every second until it returns 't'
|
||||
# or times out
|
||||
$node->poll_query_until('postgres', q|SELECT random() < 0.1;|')
|
||||
or print "timed out";
|
||||
|
||||
# Do an online pg_basebackup
|
||||
my $ret = $node->backup('testbackup');
|
||||
|
||||
# Restore it to create a new independent node (not a replica)
|
||||
my $replica = get_new_node('replica');
|
||||
$replica->init_from_backup($node, 'testbackup');
|
||||
$replica->start;
|
||||
|
||||
# Stop the server
|
||||
$node->stop('fast');
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
PostgresNode contains a set of routines able to work on a PostgreSQL node,
|
||||
allowing to start, stop, backup and initialize it with various options.
|
||||
The set of nodes managed by a given test is also managed by this module.
|
||||
|
||||
In addition to node management, PostgresNode instances have some wrappers
|
||||
around Test::More functions to run commands with an envronment set up to
|
||||
point to the instance.
|
||||
|
||||
The IPC::Run module is required.
|
||||
|
||||
=cut
|
||||
|
||||
package PostgresNode;
|
||||
|
||||
@ -40,6 +90,21 @@ INIT
|
||||
$last_port_assigned = int(rand() * 16384) + 49152;
|
||||
}
|
||||
|
||||
=pod
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=over
|
||||
|
||||
=item PostgresNode::new($class, $name, $pghost, $pgport)
|
||||
|
||||
Create a new PostgresNode instance. Does not initdb or start it.
|
||||
|
||||
You should generally prefer to use get_new_node() instead since it takes care
|
||||
of finding port numbers, registering instances for cleanup, etc.
|
||||
|
||||
=cut
|
||||
|
||||
sub new
|
||||
{
|
||||
my $class = shift;
|
||||
@ -61,36 +126,91 @@ sub new
|
||||
return $self;
|
||||
}
|
||||
|
||||
=pod
|
||||
|
||||
=item $node->port()
|
||||
|
||||
Get the port number assigned to the host. This won't necessarily be a TCP port
|
||||
open on the local host since we prefer to use unix sockets if possible.
|
||||
|
||||
Use $node->connstr() if you want a connection string.
|
||||
|
||||
=cut
|
||||
|
||||
sub port
|
||||
{
|
||||
my ($self) = @_;
|
||||
return $self->{_port};
|
||||
}
|
||||
|
||||
=pod
|
||||
|
||||
=item $node->host()
|
||||
|
||||
Return the host (like PGHOST) for this instance. May be a UNIX socket path.
|
||||
|
||||
Use $node->connstr() if you want a connection string.
|
||||
|
||||
=cut
|
||||
|
||||
sub host
|
||||
{
|
||||
my ($self) = @_;
|
||||
return $self->{_host};
|
||||
}
|
||||
|
||||
=pod
|
||||
|
||||
=item $node->basedir()
|
||||
|
||||
The directory all the node's files will be within - datadir, archive directory,
|
||||
backups, etc.
|
||||
|
||||
=cut
|
||||
|
||||
sub basedir
|
||||
{
|
||||
my ($self) = @_;
|
||||
return $self->{_basedir};
|
||||
}
|
||||
|
||||
=pod
|
||||
|
||||
=item $node->name()
|
||||
|
||||
The name assigned to the node at creation time.
|
||||
|
||||
=cut
|
||||
|
||||
sub name
|
||||
{
|
||||
my ($self) = @_;
|
||||
return $self->{_name};
|
||||
}
|
||||
|
||||
=pod
|
||||
|
||||
=item $node->logfile()
|
||||
|
||||
Path to the PostgreSQL log file for this instance.
|
||||
|
||||
=cut
|
||||
|
||||
sub logfile
|
||||
{
|
||||
my ($self) = @_;
|
||||
return $self->{_logfile};
|
||||
}
|
||||
|
||||
=pod
|
||||
|
||||
=item $node->connstr()
|
||||
|
||||
Get a libpq connection string that will establish a connection to
|
||||
this node. Suitable for passing to psql, DBD::Pg, etc.
|
||||
|
||||
=cut
|
||||
|
||||
sub connstr
|
||||
{
|
||||
my ($self, $dbname) = @_;
|
||||
@ -103,6 +223,15 @@ sub connstr
|
||||
return "port=$pgport host=$pghost dbname=$dbname";
|
||||
}
|
||||
|
||||
=pod
|
||||
|
||||
=item $node->data_dir()
|
||||
|
||||
Returns the path to the data directory. postgresql.conf and pg_hba.conf are
|
||||
always here.
|
||||
|
||||
=cut
|
||||
|
||||
sub data_dir
|
||||
{
|
||||
my ($self) = @_;
|
||||
@ -110,6 +239,14 @@ sub data_dir
|
||||
return "$res/pgdata";
|
||||
}
|
||||
|
||||
=pod
|
||||
|
||||
=item $node->archive_dir()
|
||||
|
||||
If archiving is enabled, WAL files go here.
|
||||
|
||||
=cut
|
||||
|
||||
sub archive_dir
|
||||
{
|
||||
my ($self) = @_;
|
||||
@ -117,6 +254,14 @@ sub archive_dir
|
||||
return "$basedir/archives";
|
||||
}
|
||||
|
||||
=pod
|
||||
|
||||
=item $node->backup_dir()
|
||||
|
||||
The output path for backups taken with $node->backup()
|
||||
|
||||
=cut
|
||||
|
||||
sub backup_dir
|
||||
{
|
||||
my ($self) = @_;
|
||||
@ -124,23 +269,55 @@ sub backup_dir
|
||||
return "$basedir/backup";
|
||||
}
|
||||
|
||||
# Dump node information
|
||||
=pod
|
||||
|
||||
=item $node->info()
|
||||
|
||||
Return a string containing human-readable diagnostic information (paths, etc)
|
||||
about this node.
|
||||
|
||||
=cut
|
||||
|
||||
sub info
|
||||
{
|
||||
my ($self) = @_;
|
||||
my $_info = '';
|
||||
open my $fh, '>', \$_info or die;
|
||||
print $fh "Name: " . $self->name . "\n";
|
||||
print $fh "Data directory: " . $self->data_dir . "\n";
|
||||
print $fh "Backup directory: " . $self->backup_dir . "\n";
|
||||
print $fh "Archive directory: " . $self->archive_dir . "\n";
|
||||
print $fh "Connection string: " . $self->connstr . "\n";
|
||||
print $fh "Log file: " . $self->logfile . "\n";
|
||||
close $fh or die;
|
||||
return $_info;
|
||||
}
|
||||
|
||||
=pod
|
||||
|
||||
=item $node->dump_info()
|
||||
|
||||
Print $node->info()
|
||||
|
||||
=cut
|
||||
|
||||
sub dump_info
|
||||
{
|
||||
my ($self) = @_;
|
||||
print "Name: " . $self->name . "\n";
|
||||
print "Data directory: " . $self->data_dir . "\n";
|
||||
print "Backup directory: " . $self->backup_dir . "\n";
|
||||
print "Archive directory: " . $self->archive_dir . "\n";
|
||||
print "Connection string: " . $self->connstr . "\n";
|
||||
print "Log file: " . $self->logfile . "\n";
|
||||
print $self->info;
|
||||
}
|
||||
|
||||
|
||||
# Internal method to set up trusted pg_hba.conf for replication. Not
|
||||
# documented because you shouldn't use it, it's called automatically if needed.
|
||||
sub set_replication_conf
|
||||
{
|
||||
my ($self) = @_;
|
||||
my $pgdata = $self->data_dir;
|
||||
|
||||
$self->host eq $test_pghost
|
||||
or die "set_replication_conf only works with the default host";
|
||||
|
||||
open my $hba, ">>$pgdata/pg_hba.conf";
|
||||
print $hba "\n# Allow replication (set up by PostgresNode.pm)\n";
|
||||
if (!$TestLib::windows_os)
|
||||
@ -155,13 +332,26 @@ sub set_replication_conf
|
||||
close $hba;
|
||||
}
|
||||
|
||||
# Initialize a new cluster for testing.
|
||||
#
|
||||
# Authentication is set up so that only the current OS user can access the
|
||||
# cluster. On Unix, we use Unix domain socket connections, with the socket in
|
||||
# a directory that's only accessible to the current user to ensure that.
|
||||
# On Windows, we use SSPI authentication to ensure the same (by pg_regress
|
||||
# --config-auth).
|
||||
=pod
|
||||
|
||||
=item $node->init(...)
|
||||
|
||||
Initialize a new cluster for testing.
|
||||
|
||||
Authentication is set up so that only the current OS user can access the
|
||||
cluster. On Unix, we use Unix domain socket connections, with the socket in
|
||||
a directory that's only accessible to the current user to ensure that.
|
||||
On Windows, we use SSPI authentication to ensure the same (by pg_regress
|
||||
--config-auth).
|
||||
|
||||
pg_hba.conf is configured to allow replication connections. Pass the keyword
|
||||
parameter hba_permit_replication => 0 to disable this.
|
||||
|
||||
The new node is set up in a fast but unsafe configuration where fsync is
|
||||
disabled.
|
||||
|
||||
=cut
|
||||
|
||||
sub init
|
||||
{
|
||||
my ($self, %params) = @_;
|
||||
@ -197,6 +387,19 @@ sub init
|
||||
$self->set_replication_conf if ($params{hba_permit_replication});
|
||||
}
|
||||
|
||||
=pod
|
||||
|
||||
=item $node->append_conf(filename, str)
|
||||
|
||||
A shortcut method to append to files like pg_hba.conf and postgresql.conf.
|
||||
|
||||
Does no validation or sanity checking. Does not reload the configuration
|
||||
after writing.
|
||||
|
||||
A newline is NOT automatically appended to the string.
|
||||
|
||||
=cut
|
||||
|
||||
sub append_conf
|
||||
{
|
||||
my ($self, $filename, $str) = @_;
|
||||
@ -206,6 +409,19 @@ sub append_conf
|
||||
TestLib::append_to_file($conffile, $str);
|
||||
}
|
||||
|
||||
=pod
|
||||
|
||||
=item $node->backup(backup_name)
|
||||
|
||||
Create a hot backup with pg_basebackup in $node->backup_dir,
|
||||
including the transaction logs. xlogs are fetched at the
|
||||
end of the backup, not streamed.
|
||||
|
||||
You'll have to configure a suitable max_wal_senders on the
|
||||
target server since it isn't done by default.
|
||||
|
||||
=cut
|
||||
|
||||
sub backup
|
||||
{
|
||||
my ($self, $backup_name) = @_;
|
||||
@ -218,6 +434,23 @@ sub backup
|
||||
print "# Backup finished\n";
|
||||
}
|
||||
|
||||
=pod
|
||||
|
||||
=item $node->init_from_backup(root_node, backup_name)
|
||||
|
||||
Initialize a node from a backup, which may come from this node or a different
|
||||
node. root_node must be a PostgresNode reference, backup_name the string name
|
||||
of a backup previously created on that node with $node->backup.
|
||||
|
||||
Does not start the node after init.
|
||||
|
||||
A recovery.conf is not created.
|
||||
|
||||
The backup is copied, leaving the original unmodified. pg_hba.conf is
|
||||
unconditionally set to enable replication connections.
|
||||
|
||||
=cut
|
||||
|
||||
sub init_from_backup
|
||||
{
|
||||
my ($self, $root_node, $backup_name) = @_;
|
||||
@ -248,6 +481,16 @@ port = $port
|
||||
$self->set_replication_conf;
|
||||
}
|
||||
|
||||
=pod
|
||||
|
||||
=item $node->start()
|
||||
|
||||
Wrapper for pg_ctl -w start
|
||||
|
||||
Start the node and wait until it is ready to accept connections.
|
||||
|
||||
=cut
|
||||
|
||||
sub start
|
||||
{
|
||||
my ($self) = @_;
|
||||
@ -268,6 +511,14 @@ sub start
|
||||
$self->_update_pid;
|
||||
}
|
||||
|
||||
=pod
|
||||
|
||||
=item $node->stop(mode)
|
||||
|
||||
Stop the node using pg_ctl -m $mode and wait for it to stop.
|
||||
|
||||
=cut
|
||||
|
||||
sub stop
|
||||
{
|
||||
my ($self, $mode) = @_;
|
||||
@ -281,6 +532,14 @@ sub stop
|
||||
$self->_update_pid;
|
||||
}
|
||||
|
||||
=pod
|
||||
|
||||
=item $node->restart()
|
||||
|
||||
wrapper for pg_ctl -w restart
|
||||
|
||||
=cut
|
||||
|
||||
sub restart
|
||||
{
|
||||
my ($self) = @_;
|
||||
@ -294,6 +553,8 @@ sub restart
|
||||
$self->_update_pid;
|
||||
}
|
||||
|
||||
|
||||
# Internal method
|
||||
sub _update_pid
|
||||
{
|
||||
my $self = shift;
|
||||
@ -314,14 +575,20 @@ sub _update_pid
|
||||
print "# No postmaster PID\n";
|
||||
}
|
||||
|
||||
#
|
||||
# Cluster management functions
|
||||
#
|
||||
=pod
|
||||
|
||||
=item get_new_node(node_name)
|
||||
|
||||
Build a new PostgresNode object, assigning a free port number. Standalone
|
||||
function that's automatically imported.
|
||||
|
||||
We also register the node, to avoid the port number from being reused
|
||||
for another node even when this one is not active.
|
||||
|
||||
You should generally use this instead of PostgresNode::new(...).
|
||||
|
||||
=cut
|
||||
|
||||
# Build a new PostgresNode object, assigning a free port number.
|
||||
#
|
||||
# We also register the node, to avoid the port number from being reused
|
||||
# for another node even when this one is not active.
|
||||
sub get_new_node
|
||||
{
|
||||
my $name = shift;
|
||||
@ -360,6 +627,7 @@ sub get_new_node
|
||||
return $node;
|
||||
}
|
||||
|
||||
# Attempt automatic cleanup
|
||||
sub DESTROY
|
||||
{
|
||||
my $self = shift;
|
||||
@ -369,6 +637,14 @@ sub DESTROY
|
||||
TestLib::system_log('pg_ctl', 'kill', 'QUIT', $self->{_pid});
|
||||
}
|
||||
|
||||
=pod
|
||||
|
||||
=item $node->teardown_node()
|
||||
|
||||
Do an immediate stop of the node
|
||||
|
||||
=cut
|
||||
|
||||
sub teardown_node
|
||||
{
|
||||
my $self = shift;
|
||||
@ -376,6 +652,18 @@ sub teardown_node
|
||||
$self->stop('immediate');
|
||||
}
|
||||
|
||||
=pod
|
||||
|
||||
=item $node->psql(dbname, sql)
|
||||
|
||||
Run a query with psql and return stdout, or on error print stderr.
|
||||
|
||||
Executes a query/script with psql and returns psql's standard output. psql is
|
||||
run in unaligned tuples-only quiet mode with psqlrc disabled so simple queries
|
||||
will just return the result row(s) with fields separated by commas.
|
||||
|
||||
=cut
|
||||
|
||||
sub psql
|
||||
{
|
||||
my ($self, $dbname, $sql) = @_;
|
||||
@ -399,7 +687,15 @@ sub psql
|
||||
return $stdout;
|
||||
}
|
||||
|
||||
# Run a query once a second, until it returns 't' (i.e. SQL boolean true).
|
||||
=pod
|
||||
|
||||
=item $node->poll_query_until(dbname, query)
|
||||
|
||||
Run a query once a second, until it returns 't' (i.e. SQL boolean true).
|
||||
Continues polling if psql returns an error result. Times out after 90 seconds.
|
||||
|
||||
=cut
|
||||
|
||||
sub poll_query_until
|
||||
{
|
||||
my ($self, $dbname, $query) = @_;
|
||||
@ -432,6 +728,16 @@ sub poll_query_until
|
||||
return 0;
|
||||
}
|
||||
|
||||
=pod
|
||||
|
||||
=item $node->command_ok(...)
|
||||
|
||||
Runs a shell command like TestLib::command_ok, but with PGPORT
|
||||
set so that the command will default to connecting to this
|
||||
PostgresNode.
|
||||
|
||||
=cut
|
||||
|
||||
sub command_ok
|
||||
{
|
||||
my $self = shift;
|
||||
@ -441,6 +747,14 @@ sub command_ok
|
||||
TestLib::command_ok(@_);
|
||||
}
|
||||
|
||||
=pod
|
||||
|
||||
=item $node->command_fails(...) - TestLib::command_fails with our PGPORT
|
||||
|
||||
See command_ok(...)
|
||||
|
||||
=cut
|
||||
|
||||
sub command_fails
|
||||
{
|
||||
my $self = shift;
|
||||
@ -450,6 +764,14 @@ sub command_fails
|
||||
TestLib::command_fails(@_);
|
||||
}
|
||||
|
||||
=pod
|
||||
|
||||
=item $node->command_like(...)
|
||||
|
||||
TestLib::command_like with our PGPORT. See command_ok(...)
|
||||
|
||||
=cut
|
||||
|
||||
sub command_like
|
||||
{
|
||||
my $self = shift;
|
||||
@ -459,8 +781,17 @@ sub command_like
|
||||
TestLib::command_like(@_);
|
||||
}
|
||||
|
||||
# Run a command on the node, then verify that $expected_sql appears in the
|
||||
# server log file.
|
||||
=pod
|
||||
|
||||
=item $node->issues_sql_like(cmd, expected_sql, test_name)
|
||||
|
||||
Run a command on the node, then verify that $expected_sql appears in the
|
||||
server log file.
|
||||
|
||||
Reads the whole log file so be careful when working with large log outputs.
|
||||
|
||||
=cut
|
||||
|
||||
sub issues_sql_like
|
||||
{
|
||||
my ($self, $cmd, $expected_sql, $test_name) = @_;
|
||||
@ -474,4 +805,10 @@ sub issues_sql_like
|
||||
like($log, $expected_sql, "$test_name: SQL found in server log");
|
||||
}
|
||||
|
||||
=pod
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
1;
|
||||
|
53
src/test/perl/README
Normal file
53
src/test/perl/README
Normal file
@ -0,0 +1,53 @@
|
||||
Perl-based TAP tests
|
||||
====================
|
||||
|
||||
src/test/perl/ contains shared infrastructure that's used by Perl-based tests
|
||||
across the source tree, particularly tests in src/bin and src/test. It's used
|
||||
to drive tests for backup and restore, replication, etc - anything that can't
|
||||
really be expressed using pg_regress or the isolation test framework.
|
||||
|
||||
You should prefer to write tests using pg_regress in src/test/regress, or
|
||||
isolation tester specs in src/test/isolation, if possible. If not, check to
|
||||
see if your new tests make sense under an existing tree in src/test, like
|
||||
src/test/ssl, or should be added to one of the suites for an existing utility.
|
||||
|
||||
Writing tests
|
||||
-------------
|
||||
|
||||
Tests are written using Perl's Test::More with some PostgreSQL-specific
|
||||
infrastructure from src/test/perl providing node management, support for
|
||||
invoking 'psql' to run queries and get results, etc. You should read the
|
||||
documentation for Test::More before trying to write tests.
|
||||
|
||||
Test scripts in the t/ subdirectory of a suite are executed in alphabetical
|
||||
order.
|
||||
|
||||
Each test script should begin with:
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use PostgresNode;
|
||||
use TestLib;
|
||||
# Replace with the number of tests to execute:
|
||||
use Test::More tests => 1;
|
||||
|
||||
then it will generally need to set up one or more nodes, run commands
|
||||
against them and evaluate the results. For example:
|
||||
|
||||
my $node = get_new_node('master');
|
||||
$node->init;
|
||||
$node->start;
|
||||
|
||||
my $ret = $node->psql('postgres', 'SELECT 1');
|
||||
is($ret, '1', 'SELECT 1 returns 1');
|
||||
|
||||
$node->stop('fast');
|
||||
|
||||
Read the Test::More documentation for more on how to write tests:
|
||||
|
||||
perldoc Test::More
|
||||
|
||||
For available PostgreSQL-specific test methods and some example tests read the
|
||||
perldoc for the test modules, e.g.:
|
||||
|
||||
perldoc src/test/perl/PostgresNode.pm
|
Loading…
Reference in New Issue
Block a user