You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
postgres/src/test/modules/libpq_pipeline/t/001_libpq_pipeline.pl

92 lines
1.8 KiB

# Copyright (c) 2021-2025, PostgreSQL Global Development Group
use strict;
use warnings FATAL => 'all';
use PostgreSQL::Test::Cluster;
use PostgreSQL::Test::Utils;
use Test::More;
# Use Test::Differences if installed, and select unified diff output.
BEGIN
{
eval {
require Test::Differences;
Test::Differences->import;
unified_diff();
};
# No dice -- fall back to 'is'
*eq_or_diff = \&is if $@;
}
my $node = PostgreSQL::Test::Cluster->new('main');
$node->init;
$node->start;
my $numrows = 700;
my ($out, $err) = run_command([ 'libpq_pipeline', 'tests' ]);
die "oops: $err" unless $err eq '';
my @tests = split(/\s+/, $out);
mkdir "$PostgreSQL::Test::Utils::tmp_check/traces";
for my $testname (@tests)
{
my @extraargs = ('-r' => $numrows);
my $cmptrace = grep(/^$testname$/,
qw(simple_pipeline nosync multi_pipelines prepared singlerow
libpq: Improve idle state handling in pipeline mode We were going into IDLE state too soon when executing queries via PQsendQuery in pipeline mode, causing several scenarios to misbehave in different ways -- most notably, as reported by Daniele Varrazzo, that a warning message is produced by libpq: message type 0x33 arrived from server while idle But it is also possible, if queries are sent and results consumed not in lockstep, for the expected mediating NULL result values from PQgetResult to be lost (a problem which has not been reported, but which is more serious). Fix this by introducing two new concepts: one is a command queue element PGQUERY_CLOSE to tell libpq to wait for the CloseComplete server response to the Close message that is sent by PQsendQuery. Because the application is not expecting any PGresult from this, the mechanism to consume it is a bit hackish. The other concept, authored by Horiguchi-san, is a PGASYNC_PIPELINE_IDLE state for libpq's state machine to differentiate "really idle" from merely "the idle state that occurs in between reading results from the server for elements in the pipeline". This makes libpq not go fully IDLE when the libpq command queue contains entries; in normal cases, we only go IDLE once at the end of the pipeline, when the server response to the final SYNC message is received. (However, there are corner cases it doesn't fix, such as terminating the query sequence by PQsendFlushRequest instead of PQpipelineSync; this sort of scenario is what requires PGQUERY_CLOSE bit above.) This last bit helps make the libpq state machine clearer; in particular we can get rid of an ugly hack in pqParseInput3 to avoid considering IDLE as such when the command queue contains entries. A new test mode is added to libpq_pipeline.c to tickle some related problematic cases. Reported-by: Daniele Varrazzo <daniele.varrazzo@gmail.com> Co-authored-by: Kyotaro Horiguchi <horikyota.ntt@gmail.com> Discussion: https://postgr.es/m/CA+mi_8bvD0_CW3sumgwPvWdNzXY32itoG_16tDYRu_1S2gV2iw@mail.gmail.com
3 years ago
pipeline_abort pipeline_idle transaction
disallowed_in_pipeline)) > 0;
# For a bunch of tests, generate a libpq trace file too.
my $traceout =
"$PostgreSQL::Test::Utils::tmp_check/traces/$testname.trace";
if ($cmptrace)
{
push @extraargs, "-t" => $traceout;
}
# Execute the test
$node->command_ok(
[
'libpq_pipeline', @extraargs,
$testname, $node->connstr('postgres')
],
"libpq_pipeline $testname");
# Compare the trace, if requested
if ($cmptrace)
{
my $expected;
my $result;
$expected = slurp_file_eval("traces/$testname.trace");
next unless $expected ne "";
$result = slurp_file_eval($traceout);
next unless $result ne "";
eq_or_diff($result, $expected, "$testname trace match");
}
}
$node->stop('fast');
done_testing();
sub slurp_file_eval
{
my $filepath = shift;
my $contents;
eval { $contents = slurp_file($filepath); };
if ($@)
{
fail "reading $filepath: $@";
return "";
}
return $contents;
}