Hi,

On Tue, 29 Sept 2026 at 02:24, Michael Paquier <[email protected]> wrote:
>
> On Mon, Sep 28, 2026 at 12:54:18PM -0400, Andrew Dunstan wrote:
> > I think the right way is to have a wrapper function as in the attached, so
> > instead of calling IPC::Run::run you would call ipc_run ...
> >
> > Note that this patch only touches the sites that will actually be affected
> > by this. If we wanted to be consistent we would replace calls in a further
> > 23 files.
>
> That seems much better in the long-run, thanks.  The
> ipc_run_text_mode() being published in Utils.pm does not seem
> necessary, though.  This is a part that I'm sure could become
> confusing if we allow code outside of Utils.pm to use it.

I tested Andrew Dunstan's patch in CI after removing the IPC::Run
version pin from the Windows tasks. The 035_standby_logical_decoding
test failed on both the Windows VS and MinGW tasks [1]:

```
stderr:
# die: IPC::Run: timeout on timer #17 at
C:/Strawberry/perl/site/lib/IPC/Run.pm line 3361.
```

Using ipc_start() in the 035_standby_logical_decoding test fixes the
failure, and CI now passes [2].

Here are three patches:
- 0001 is Andrew Dunstan's patch, unchanged.
- 0002 is a fixup for the 035_standby_logical_decoding test.
- 0003 removes the IPC::Run version pin from the Windows tasks.

[1] https://github.com/nbyavuz/postgres/actions/runs/36537214540
[2] https://github.com/nbyavuz/postgres/actions/runs/36537155885

-- 
Regards,
Nazir Bilal Yavuz
Microsoft
From 25a60f1e72d13bb2137b328f0b4ba2d6e66f0e66 Mon Sep 17 00:00:00 2001
From: Andrew Dunstan <[email protected]>
Date: Mon, 28 Sep 2026 12:07:58 -0400
Subject: [PATCH v2 1/3] Fix TAP tests with recent IPC::Run on Windows

Recent IPC::Run releases default to binary mode on Windows, so
captured command output retains CRLF that comparisons against expected
text don't expect.  Some tests also rely on implicit standard streams,
which recent IPC::Run fails outright on Windows.  Andrew A. Bille
diagnosed both problems and posted a fix that requests text mode at
each affected redirection individually.

Instead, add ipc_run()/ipc_start() wrappers in PostgreSQL::Test::Utils
that default every non-PTY stream redirection to text mode automatically,
per a suggestion from Michael Paquier.

Discussion: https://postgr.es/m/cajnzarxyuspsecyg4inqbqsvmj7b0mgz3v4_eehskahgtgx...@mail.gmail.com
---
 src/bin/pg_dump/t/010_dump_connstr.pl         |   2 +
 src/bin/pg_rewind/t/RewindTest.pm             |   4 +-
 src/interfaces/libpq/t/001_uri.pl             |   3 +-
 .../modules/test_escape/t/001_test_escape.pl  |   4 +-
 .../perl/PostgreSQL/Test/BackgroundPsql.pm    |   8 +-
 src/test/perl/PostgreSQL/Test/Cluster.pm      |  41 ++++--
 src/test/perl/PostgreSQL/Test/Utils.pm        | 123 ++++++++++++++----
 src/test/recovery/t/021_row_visibility.pl     |   4 +-
 src/test/recovery/t/032_relfilenode_reuse.pl  |   4 +-
 9 files changed, 148 insertions(+), 45 deletions(-)

diff --git a/src/bin/pg_dump/t/010_dump_connstr.pl b/src/bin/pg_dump/t/010_dump_connstr.pl
index bf2c3b6d00b..3cf54a4502f 100644
--- a/src/bin/pg_dump/t/010_dump_connstr.pl
+++ b/src/bin/pg_dump/t/010_dump_connstr.pl
@@ -247,6 +247,7 @@ $envar_node->run_log(
 	local $ENV{PGPORT} = $envar_node->port;
 	local $ENV{PGUSER} = $restore_super;
 	$result = run_log([ 'psql', '--no-psqlrc', '--file' => $plain ],
+		'>' => sub { print STDOUT $_[0]; },
 		'2>' => \$stderr);
 }
 ok($result,
@@ -286,6 +287,7 @@ $cmdline_node->run_log(
 			'--no-psqlrc',
 			'--file' => $plain,
 		],
+		'>' => sub { print STDOUT $_[0]; },
 		'2>' => \$stderr);
 }
 ok($result,
diff --git a/src/bin/pg_rewind/t/RewindTest.pm b/src/bin/pg_rewind/t/RewindTest.pm
index 32aeca80f13..5b109414649 100644
--- a/src/bin/pg_rewind/t/RewindTest.pm
+++ b/src/bin/pg_rewind/t/RewindTest.pm
@@ -38,7 +38,6 @@ use Carp;
 use Exporter 'import';
 use File::Copy;
 use File::Path qw(rmtree);
-use IPC::Run   qw(run);
 use PostgreSQL::Test::Cluster;
 use PostgreSQL::Test::RecursiveCopy;
 use PostgreSQL::Test::Utils;
@@ -96,14 +95,13 @@ sub check_query
 	my ($stdout, $stderr);
 
 	# we want just the output, no formatting
-	my $result = run [
+	my $result = ipc_run [
 		'psql', '--quiet', '--no-align', '--tuples-only', '--no-psqlrc',
 		'--dbname' => $node_primary->connstr('postgres'),
 		'--command' => $query
 	  ],
 	  '>' => \$stdout,
 	  '2>' => \$stderr;
-
 	is($result, 1, "$test_name: psql exit code");
 	is($stderr, '', "$test_name: psql no stderr");
 	is($stdout, $expected_stdout, "$test_name: query result matches");
diff --git a/src/interfaces/libpq/t/001_uri.pl b/src/interfaces/libpq/t/001_uri.pl
index 64f257ae046..ae63b3c283a 100644
--- a/src/interfaces/libpq/t/001_uri.pl
+++ b/src/interfaces/libpq/t/001_uri.pl
@@ -4,7 +4,6 @@ use warnings FATAL => 'all';
 
 use PostgreSQL::Test::Utils;
 use Test::More;
-use IPC::Run;
 
 
 # List of URIs tests. For each test the first element is the input string, the
@@ -268,7 +267,7 @@ sub test_uri
 	%ENV = (%ENV, %envvars);
 
 	my $cmd = [ 'libpq_uri_regress', $uri ];
-	$result{exit} = IPC::Run::run $cmd,
+	$result{exit} = ipc_run $cmd,
 	  '>' => \$result{stdout},
 	  '2>' => \$result{stderr};
 
diff --git a/src/test/modules/test_escape/t/001_test_escape.pl b/src/test/modules/test_escape/t/001_test_escape.pl
index 3c6c968c07b..99c15331bef 100644
--- a/src/test/modules/test_escape/t/001_test_escape.pl
+++ b/src/test/modules/test_escape/t/001_test_escape.pl
@@ -20,7 +20,9 @@ my $cmd =
 # There currently is no good other way to transport test results from a C
 # program that requires just the node being set-up...
 my ($stderr, $stdout);
-my $result = IPC::Run::run $cmd, '>', \$stdout, '2>', \$stderr;
+my $result = ipc_run $cmd,
+  '>' => \$stdout,
+  '2>' => \$stderr;
 
 is($result, 1, "test_escape returns 0");
 is($stderr, '', "test_escape stderr is empty");
diff --git a/src/test/perl/PostgreSQL/Test/BackgroundPsql.pm b/src/test/perl/PostgreSQL/Test/BackgroundPsql.pm
index d7797225451..9b8a1b0132f 100644
--- a/src/test/perl/PostgreSQL/Test/BackgroundPsql.pm
+++ b/src/test/perl/PostgreSQL/Test/BackgroundPsql.pm
@@ -59,7 +59,7 @@ use warnings FATAL => 'all';
 use Carp;
 use Config;
 use IPC::Run;
-use PostgreSQL::Test::Utils qw(pump_until);
+use PostgreSQL::Test::Utils qw(ipc_start pump_until);
 use Test::More;
 
 =pod
@@ -107,7 +107,9 @@ sub new
 
 	if ($interactive)
 	{
-		$run = IPC::Run::start $psql_params,
+		# The pty streams are left alone; the stderr pipe is a plain
+		# (non-pty) stream, so ipc_start() still defaults it to text mode.
+		$run = ipc_start $psql_params,
 		  '<pty<' => \$psql->{stdin},
 		  '>pty>' => \$psql->{stdout},
 		  '2>' => \$psql->{stderr},
@@ -115,7 +117,7 @@ sub new
 	}
 	else
 	{
-		$run = IPC::Run::start $psql_params,
+		$run = ipc_start $psql_params,
 		  '<' => \$psql->{stdin},
 		  '>' => \$psql->{stdout},
 		  '2>' => \$psql->{stderr},
diff --git a/src/test/perl/PostgreSQL/Test/Cluster.pm b/src/test/perl/PostgreSQL/Test/Cluster.pm
index 920d831be9e..c5ec73b8ffd 100644
--- a/src/test/perl/PostgreSQL/Test/Cluster.pm
+++ b/src/test/perl/PostgreSQL/Test/Cluster.pm
@@ -505,11 +505,10 @@ sub config_data
 
 	my ($stdout, $stderr);
 	my $result =
-	  IPC::Run::run [ $self->installed_command('pg_config'), @options ],
+	  PostgreSQL::Test::Utils::ipc_run
+	  [ $self->installed_command('pg_config'), @options ],
 	  '>', \$stdout, '2>', \$stderr
 	  or croak "could not execute pg_config";
-	# standardize line endings
-	$stdout =~ s/\r(?=\n)//g;
 	# no options, scalar context: just hand back the output
 	return $stdout unless (wantarray || @options);
 	chomp($stdout);
@@ -2271,11 +2270,31 @@ sub psql
 		local $@;
 		eval {
 			my @ipcrun_opts = (\@psql_params, '<' => \$sql);
-			push @ipcrun_opts, '>' => $stdout if defined $stdout;
-			push @ipcrun_opts, '2>' => $stderr if defined $stderr;
+
+			# ipc_run() defaults these to text mode, so tests aren't
+			# tripped up by platform-specific line endings.
+			if (defined $stdout)
+			{
+				push @ipcrun_opts, '>' => $stdout;
+			}
+			elsif ($PostgreSQL::Test::Utils::windows_os)
+			{
+				# Preserve pass-through behavior explicitly on Windows.
+				push @ipcrun_opts, '>' => sub { print STDOUT $_[0]; };
+			}
+
+			if (defined $stderr)
+			{
+				push @ipcrun_opts, '2>' => $stderr;
+			}
+			elsif ($PostgreSQL::Test::Utils::windows_os)
+			{
+				push @ipcrun_opts, '2>' => sub { print STDERR $_[0]; };
+			}
+
 			push @ipcrun_opts, $timeout if defined $timeout;
 
-			IPC::Run::run @ipcrun_opts;
+			PostgreSQL::Test::Utils::ipc_run @ipcrun_opts;
 			$ret = $?;
 		};
 		my $exc_save = $@;
@@ -2787,7 +2806,7 @@ sub poll_query_until
 
 	while ($attempts < $max_attempts)
 	{
-		my $result = IPC::Run::run $cmd,
+		my $result = PostgreSQL::Test::Utils::ipc_run $cmd,
 		  '<' => \$query,
 		  '>' => \$stdout,
 		  '2>' => \$stderr;
@@ -3811,7 +3830,11 @@ sub pg_recvlogical_upto
 	{
 		local $@;
 		eval {
-			IPC::Run::run(\@cmd, '>' => \$stdout, '2>' => \$stderr, $timeout);
+			PostgreSQL::Test::Utils::ipc_run(
+				\@cmd,
+				'>' => \$stdout,
+				'2>' => \$stderr,
+				$timeout);
 			$ret = $?;
 		};
 		my $exc_save = $@;
@@ -3923,7 +3946,7 @@ sub create_logical_slot_on_standby
 
 	my $handle;
 
-	$handle = IPC::Run::start(
+	$handle = PostgreSQL::Test::Utils::ipc_start(
 		[
 			'pg_recvlogical',
 			'--dbname' => $self->connstr($dbname),
diff --git a/src/test/perl/PostgreSQL/Test/Utils.pm b/src/test/perl/PostgreSQL/Test/Utils.pm
index d3e6abf7a68..cedf4acb7f3 100644
--- a/src/test/perl/PostgreSQL/Test/Utils.pm
+++ b/src/test/perl/PostgreSQL/Test/Utils.pm
@@ -83,6 +83,9 @@ our @EXPORT = qw(
   scan_server_header
   system_or_bail
   system_log
+  ipc_run_text_mode
+  ipc_run
+  ipc_start
   run_log
   run_command
   pump_until
@@ -421,9 +424,80 @@ sub system_or_bail
 
 =pod
 
+=item ipc_run_text_mode()
+
+Return a filter suitable for C<IPC::Run> redirections that requests text mode.
+Most callers should use C<ipc_run()>/C<ipc_start()> instead, which apply this
+automatically; this is exposed for the rare filter chain that needs it
+explicitly (e.g. a non-terminal stage of a filter chain).
+
+=cut
+
+sub ipc_run_text_mode
+{
+	return IPC::Run::binary(0);
+}
+
+=pod
+
+=item ipc_run(@args)
+
+Wrapper for C<IPC::Run::run()> that defaults every non-PTY stream
+redirection ('<', '>', '>>', '2>', '2>>') to text mode, so that command
+output TAP tests treat as text doesn't retain platform-specific line
+endings (notably CRLF on Windows).  Callers do not need to request this
+themselves; pass an explicit C<IPC::Run::binary(1)> filter to opt out for a
+particular redirection.  PTY redirections ('<pty<', '>pty>') are left
+untouched, since IPC::Run handles ptys differently and text mode does not
+apply to them.
+
+=cut
+
+sub ipc_run
+{
+	return IPC::Run::run(_ipc_run_default_text_mode(@_));
+}
+
+=pod
+
+=item ipc_start(@args)
+
+As C<ipc_run()> above, but wraps C<IPC::Run::start()>.
+
+=cut
+
+sub ipc_start
+{
+	return IPC::Run::start(_ipc_run_default_text_mode(@_));
+}
+
+# Insert an explicit ipc_run_text_mode() filter after every non-PTY stream
+# redirection token in @args that doesn't already have one, so ipc_run()
+# and ipc_start() apply it by default without every call site needing to
+# remember to ask for it.
+sub _ipc_run_default_text_mode
+{
+	my @args = @_;
+	my @out;
+	for my $i (0 .. $#args)
+	{
+		push @out, $args[$i];
+		next if ref $args[$i];
+		next unless $args[$i] =~ /^\d*>>?$/;
+		my $next_arg = $args[$i + 1];
+		next
+		  if ref $next_arg
+		  && UNIVERSAL::isa($next_arg, 'IPC::Run::binmode_pseudo_filter');
+		push @out, ipc_run_text_mode();
+	}
+	return @out;
+}
+
+=pod
+
 =item run_log(@cmd)
 
-Run the given command via C<IPC::Run::run()>, noting it in the log.
+Run the given command via C<ipc_run()>, noting it in the log.
 The return value from the command is passed through.
 
 =cut
@@ -431,14 +505,14 @@ The return value from the command is passed through.
 sub run_log
 {
 	print("# Running: " . join(" ", @{ $_[0] }) . "\n");
-	return IPC::Run::run(@_);
+	return ipc_run(@_);
 }
 
 =pod
 
 =item run_command(cmd)
 
-Run (via C<IPC::Run::run()>) the command passed as argument.
+Run (via C<ipc_run()>) the command passed as argument.
 The return value from the command is ignored.
 The return value is C<($stdout, $stderr)>.
 
@@ -448,7 +522,7 @@ sub run_command
 {
 	my ($cmd) = @_;
 	my ($stdout, $stderr);
-	my $result = IPC::Run::run $cmd, '>' => \$stdout, '2>' => \$stderr;
+	my $result = ipc_run $cmd, '>' => \$stdout, '2>' => \$stderr;
 	chomp($stdout);
 	chomp($stderr);
 	return ($stdout, $stderr);
@@ -801,12 +875,11 @@ sub scan_server_header
 	my ($header_path, $regexp) = @_;
 
 	my ($stdout, $stderr);
-	my $result = IPC::Run::run [ 'pg_config', '--includedir-server' ],
+	my $result = ipc_run [ 'pg_config', '--includedir-server' ],
 	  '>' => \$stdout,
 	  '2>' => \$stderr
 	  or croak "could not execute pg_config";
 	chomp($stdout);
-	$stdout =~ s/\r$//;
 
 	open my $header_h, '<', "$stdout/$header_path" or croak "$!";
 
@@ -840,12 +913,11 @@ sub check_pg_config
 {
 	my ($regexp) = @_;
 	my ($stdout, $stderr);
-	my $result = IPC::Run::run [ 'pg_config', '--includedir' ],
+	my $result = ipc_run [ 'pg_config', '--includedir' ],
 	  '>' => \$stdout,
 	  '2>' => \$stderr
 	  or croak "could not execute pg_config";
 	chomp($stdout);
-	$stdout =~ s/\r$//;
 
 	open my $pg_config_h, '<', "$stdout/pg_config.h" or croak "$!";
 	my $match = (grep { /^$regexp/ } <$pg_config_h>);
@@ -1009,8 +1081,12 @@ sub command_ok
 	local $Test::Builder::Level = $Test::Builder::Level + 1;
 	my ($cmd, $test_name) = @_;
 	my ($stdout, $stderr);
+	my $stdin = '';
 	print("# Running: " . join(" ", @{$cmd}) . "\n");
-	my $result = IPC::Run::run $cmd, '>' => \$stdout, '2>' => \$stderr;
+	my $result = ipc_run $cmd,
+	  '<' => \$stdin,
+	  '>' => \$stdout,
+	  '2>' => \$stderr;
 	ok($result, $test_name) or do
 	{
 		diag("---------- command failed ----------");
@@ -1033,7 +1109,7 @@ sub command_fails
 	my ($cmd, $test_name) = @_;
 	my ($stdout, $stderr);
 	print("# Running: " . join(" ", @{$cmd}) . "\n");
-	my $result = IPC::Run::run $cmd, '>' => \$stdout, '2>' => \$stderr;
+	my $result = ipc_run $cmd, '>' => \$stdout, '2>' => \$stderr;
 	ok(!$result, $test_name) or do
 	{
 		diag("-- command succeeded unexpectedly --");
@@ -1055,7 +1131,7 @@ sub command_exit_is
 	local $Test::Builder::Level = $Test::Builder::Level + 1;
 	my ($cmd, $expected, $test_name) = @_;
 	print("# Running: " . join(" ", @{$cmd}) . "\n");
-	my $h = IPC::Run::start $cmd;
+	my $h = ipc_start $cmd;
 	$h->finish();
 
 	# Normally, if the child called exit(N), IPC::Run::result() returns N.  On
@@ -1083,9 +1159,7 @@ sub program_help_ok
 	my ($cmd) = @_;
 	my ($stdout, $stderr);
 	print("# Running: $cmd --help\n");
-	my $result = IPC::Run::run [ $cmd, '--help' ],
-	  '>' => \$stdout,
-	  '2>' => \$stderr;
+	my $result = ipc_run [ $cmd, '--help' ], '>' => \$stdout, '2>' => \$stderr;
 	ok($result, "$cmd --help exit code 0");
 	isnt($stdout, '', "$cmd --help goes to stdout");
 	is($stderr, '', "$cmd --help nothing to stderr");
@@ -1115,9 +1189,8 @@ sub program_version_ok
 	my ($cmd) = @_;
 	my ($stdout, $stderr);
 	print("# Running: $cmd --version\n");
-	my $result = IPC::Run::run [ $cmd, '--version' ],
-	  '>' => \$stdout,
-	  '2>' => \$stderr;
+	my $result =
+	  ipc_run [ $cmd, '--version' ], '>' => \$stdout, '2>' => \$stderr;
 	ok($result, "$cmd --version exit code 0");
 	isnt($stdout, '', "$cmd --version goes to stdout");
 	is($stderr, '', "$cmd --version nothing to stderr");
@@ -1139,7 +1212,7 @@ sub program_options_handling_ok
 	my ($cmd) = @_;
 	my ($stdout, $stderr);
 	print("# Running: $cmd --not-a-valid-option\n");
-	my $result = IPC::Run::run [ $cmd, '--not-a-valid-option' ],
+	my $result = ipc_run [ $cmd, '--not-a-valid-option' ],
 	  '>' => \$stdout,
 	  '2>' => \$stderr;
 	ok(!$result, "$cmd with invalid option nonzero exit code");
@@ -1162,7 +1235,7 @@ sub command_like
 	my ($cmd, $expected_stdout, $test_name) = @_;
 	my ($stdout, $stderr);
 	print("# Running: " . join(" ", @{$cmd}) . "\n");
-	my $result = IPC::Run::run $cmd, '>' => \$stdout, '2>' => \$stderr;
+	my $result = ipc_run $cmd, '>' => \$stdout, '2>' => \$stderr;
 	ok($result, "$test_name: exit code 0");
 	is($stderr, '', "$test_name: no stderr");
 	like($stdout, $expected_stdout, "$test_name: matches");
@@ -1191,7 +1264,7 @@ sub command_like_safe
 	my $stdoutfile = File::Temp->new();
 	my $stderrfile = File::Temp->new();
 	print("# Running: " . join(" ", @{$cmd}) . "\n");
-	my $result = IPC::Run::run $cmd, '>' => $stdoutfile, '2>' => $stderrfile;
+	my $result = ipc_run $cmd, '>' => $stdoutfile, '2>' => $stderrfile;
 	$stdout = slurp_file($stdoutfile);
 	$stderr = slurp_file($stderrfile);
 	ok($result, "$test_name: exit code 0");
@@ -1215,7 +1288,7 @@ sub command_fails_like
 	my ($cmd, $expected_stderr, $test_name) = @_;
 	my ($stdout, $stderr);
 	print("# Running: " . join(" ", @{$cmd}) . "\n");
-	my $result = IPC::Run::run $cmd, '>' => \$stdout, '2>' => \$stderr;
+	my $result = ipc_run $cmd, '>' => \$stdout, '2>' => \$stderr;
 	ok(!$result, "$test_name: exit code not 0");
 	like($stderr, $expected_stderr, "$test_name: matches");
 	return;
@@ -1235,8 +1308,12 @@ sub command_ok_or_fails_like
 	local $Test::Builder::Level = $Test::Builder::Level + 1;
 	my ($cmd, $expected_stdout, $expected_stderr, $test_name) = @_;
 	my ($stdout, $stderr);
+	my $stdin = '';
 	print("# Running: " . join(" ", @{$cmd}) . "\n");
-	my $result = IPC::Run::run $cmd, '>' => \$stdout, '2>' => \$stderr;
+	my $result = ipc_run $cmd,
+	  '<' => \$stdin,
+	  '>' => \$stdout,
+	  '2>' => \$stderr;
 	if (!$result)
 	{
 		like($stdout, $expected_stdout, "$test_name: stdout matches");
@@ -1277,7 +1354,7 @@ sub command_checks_all
 	# run command
 	my ($stdout, $stderr);
 	print("# Running: " . join(" ", @{$cmd}) . "\n");
-	IPC::Run::run($cmd, '>' => \$stdout, '2>' => \$stderr);
+	ipc_run($cmd, '>' => \$stdout, '2>' => \$stderr);
 
 	# See http://perldoc.perl.org/perlvar.html#%24CHILD_ERROR
 	my $ret = $?;
diff --git a/src/test/recovery/t/021_row_visibility.pl b/src/test/recovery/t/021_row_visibility.pl
index 0a4d22b3698..44ecc940927 100644
--- a/src/test/recovery/t/021_row_visibility.pl
+++ b/src/test/recovery/t/021_row_visibility.pl
@@ -37,7 +37,7 @@ my $psql_timeout =
 # One psql to primary and standby each, for all queries. That allows
 # to check uncommitted changes being replicated and such.
 my %psql_primary = (stdin => '', stdout => '', stderr => '');
-$psql_primary{run} = IPC::Run::start(
+$psql_primary{run} = ipc_start(
 	[
 		'psql', '--no-psqlrc', '--no-align',
 		'--file' => '-',
@@ -49,7 +49,7 @@ $psql_primary{run} = IPC::Run::start(
 	$psql_timeout);
 
 my %psql_standby = ('stdin' => '', 'stdout' => '', 'stderr' => '');
-$psql_standby{run} = IPC::Run::start(
+$psql_standby{run} = ipc_start(
 	[
 		'psql', '--no-psqlrc', '--no-align',
 		'--file' => '-',
diff --git a/src/test/recovery/t/032_relfilenode_reuse.pl b/src/test/recovery/t/032_relfilenode_reuse.pl
index d9e22e9bcaa..88438a78cb5 100644
--- a/src/test/recovery/t/032_relfilenode_reuse.pl
+++ b/src/test/recovery/t/032_relfilenode_reuse.pl
@@ -35,7 +35,7 @@ $node_standby->start;
 my $psql_timeout = IPC::Run::timer($PostgreSQL::Test::Utils::timeout_default);
 
 my %psql_primary = (stdin => '', stdout => '', stderr => '');
-$psql_primary{run} = IPC::Run::start(
+$psql_primary{run} = ipc_start(
 	[
 		'psql', '--no-psqlrc', '--no-align',
 		'--file' => '-',
@@ -47,7 +47,7 @@ $psql_primary{run} = IPC::Run::start(
 	$psql_timeout);
 
 my %psql_standby = ('stdin' => '', 'stdout' => '', 'stderr' => '');
-$psql_standby{run} = IPC::Run::start(
+$psql_standby{run} = ipc_start(
 	[
 		'psql', '--no-psqlrc', '--no-align',
 		'--file' => '-',
-- 
2.47.3

From 3c57c10f429e9ee6608699bd6b474e391226bc68 Mon Sep 17 00:00:00 2001
From: Nazir Bilal Yavuz <[email protected]>
Date: Tue, 29 Sep 2026 10:28:54 +0300
Subject: [PATCH v2 2/3] Fix TAP tests with recent IPC::Run on Windows - 2

Follow up on prior commit. Use ipc_start() in the standby logical
decoding test too. Otherwise, test timeouts.
---
 src/test/recovery/t/035_standby_logical_decoding.pl | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 88893f71350..20c824b6e5b 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -79,7 +79,7 @@ sub make_slot_active
 	my $slot_user_handle;
 
 	my $active_slot = $slot_prefix . 'activeslot';
-	$slot_user_handle = IPC::Run::start(
+	$slot_user_handle = ipc_start(
 		[
 			'pg_recvlogical',
 			'--dbname' => $node->connstr('testdb'),
@@ -354,7 +354,7 @@ my %psql_subscriber = (
 	'subscriber_stdin' => '',
 	'subscriber_stdout' => '',
 	'subscriber_stderr' => '');
-$psql_subscriber{run} = IPC::Run::start(
+$psql_subscriber{run} = ipc_start(
 	[
 		'psql', '--no-psqlrc', '--no-align',
 		'--file' => '-',
@@ -1106,7 +1106,7 @@ $node_cascading_standby->wait_for_event('startup',
 
 # Start pg_recvlogical.
 my ($stdout2, $stderr2);
-my $handle2 = IPC::Run::start(
+my $handle2 = ipc_start(
 	[
 		'pg_recvlogical',
 		'--dbname' => $node_cascading_standby->connstr('testdb'),
-- 
2.47.3

From 3521696229433c851ccf2cfd68db5f506d65f472 Mon Sep 17 00:00:00 2001
From: Nazir Bilal Yavuz <[email protected]>
Date: Tue, 29 Sep 2026 10:06:47 +0300
Subject: [PATCH v2 3/3] ci: Install latest IPC:Run on Windows tasks

Earlier commit removed dependency to NJM/IPC-Run-20250809.0.tar.gz, we
can install latest version now.

Discussion: https://postgr.es/m/cajnzarxyuspsecyg4inqbqsvmj7b0mgz3v4_eehskahgtgx...@mail.gmail.com
---
 .github/workflows/pg-ci.yml | 16 +++-------------
 1 file changed, 3 insertions(+), 13 deletions(-)

diff --git a/.github/workflows/pg-ci.yml b/.github/workflows/pg-ci.yml
index a2629c8335a..1f25c36ff27 100644
--- a/.github/workflows/pg-ci.yml
+++ b/.github/workflows/pg-ci.yml
@@ -963,16 +963,8 @@ jobs:
           echo ::endgroup::
 
           # Install IPC::Run.
-          # - recommends_policy=0 keeps cpan from pulling in IO::Tty / IO::Pty,
-          #   which don't build on Windows ("This module requires a POSIX
-          #   compliant system to work").
-          # - Pin to NJM/IPC-Run-20250809.0 because TODDR/IPC-Run-20260322.0
-          #   broke postgres tap tests on Windows (changed pipe stdio
-          #   handling). See upstream pg-vm-images commit ff5238afa3 and
-          #   the thread at
-          #   https://postgr.es/m/CAN55FZ06xanSbJdHe-CurjX_qNuBWZDEvS1kAk36L38YCtZXnw%40mail.gmail.com
           echo ::group::cpan_ipc_run
-          "o conf recommends_policy 0`no conf commit`nnotest install NJM/IPC-Run-20250809.0.tar.gz" | cpan
+          "o conf recommends_policy 0`nnotest install IPC::Run" | cpan
           if (!$?) { throw 'cmdfail' }
           perl -mIPC::Run -e 1
           if (!$?) { throw 'cmdfail' }
@@ -1106,11 +1098,9 @@ jobs:
 
       - name: Install additional dependencies
         run: |
-          # Pin IPC::Run to NJM/IPC-Run-20250809.0; TODDR/IPC-Run-20260322.0
-          # broke postgres tap tests on Windows (pipe stdio handling).
-          # See pg-vm-images commit ff5238afa3.
+          # Install IPC::Run.
           echo ::group::cpan_ipc_run
-          (echo; echo o conf recommends_policy 0; echo notest install NJM/IPC-Run-20250809.0.tar.gz) | cpan
+          (echo; echo o conf recommends_policy 0; echo notest install IPC::Run) | cpan
           perl -mIPC::Run -e 1
           echo ::endgroup::
 
-- 
2.47.3

Reply via email to