summaryrefslogtreecommitdiff
path: root/src/bin/psql
diff options
context:
space:
mode:
Diffstat (limited to 'src/bin/psql')
-rw-r--r--src/bin/psql/meson.build1
-rw-r--r--src/bin/psql/t/030_pager.pl106
2 files changed, 107 insertions, 0 deletions
diff --git a/src/bin/psql/meson.build b/src/bin/psql/meson.build
index f795ff28271..d344053c23b 100644
--- a/src/bin/psql/meson.build
+++ b/src/bin/psql/meson.build
@@ -77,6 +77,7 @@ tests += {
't/001_basic.pl',
't/010_tab_completion.pl',
't/020_cancel.pl',
+ 't/030_pager.pl',
],
},
}
diff --git a/src/bin/psql/t/030_pager.pl b/src/bin/psql/t/030_pager.pl
new file mode 100644
index 00000000000..afe97355c44
--- /dev/null
+++ b/src/bin/psql/t/030_pager.pl
@@ -0,0 +1,106 @@
+
+# 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 Data::Dumper;
+
+# If we don't have IO::Pty, forget it, because IPC::Run depends on that
+# to support pty connections
+eval { require IO::Pty; };
+if ($@)
+{
+ plan skip_all => 'IO::Pty is needed to run this test';
+}
+
+# Check that "wc -l" does what we expect, else forget it
+my $wcstdin = "foo bar\nbaz\n";
+my ($wcstdout, $wcstderr);
+my $result = IPC::Run::run [ 'wc', '-l' ],
+ '<' => \$wcstdin,
+ '>' => \$wcstdout,
+ '2>' => \$wcstderr;
+chomp $wcstdout;
+if ($wcstdout !~ /^ *2$/ || $wcstderr ne '')
+{
+ note "wc stdout = '$wcstdout'\n";
+ note "wc stderr = '$wcstderr'\n";
+ plan skip_all => '"wc -l" is needed to run this test';
+}
+
+# We set up "wc -l" as the pager so we can tell whether psql used the pager
+$ENV{PSQL_PAGER} = "wc -l";
+
+# start a new server
+my $node = PostgreSQL::Test::Cluster->new('main');
+$node->init;
+$node->start;
+
+# fire up an interactive psql session
+my $h = $node->interactive_psql('postgres');
+
+# set the pty's window size to known values
+# (requires undesirable chumminess with the innards of IPC::Run)
+for my $pty (values %{ $h->{run}->{PTYS} })
+{
+ $pty->set_winsize(24, 80);
+}
+
+# Simple test case: type something and see if psql responds as expected
+sub do_command
+{
+ my ($send, $pattern, $annotation) = @_;
+
+ # report test failures from caller location
+ local $Test::Builder::Level = $Test::Builder::Level + 1;
+
+ # restart per-command timer
+ $h->{timeout}->start($PostgreSQL::Test::Utils::timeout_default);
+
+ # send the data to be sent and wait for its result
+ my $out = $h->query_until($pattern, $send);
+ my $okay = ($out =~ $pattern && !$h->{timeout}->is_expired);
+ ok($okay, $annotation);
+ # for debugging, log actual output if it didn't match
+ local $Data::Dumper::Terse = 1;
+ local $Data::Dumper::Useqq = 1;
+ diag 'Actual output was ' . Dumper($out) . "Did not match \"$pattern\"\n"
+ if !$okay;
+ return;
+}
+
+# Test invocation of the pager
+#
+# Note that interactive_psql starts psql with --no-align --tuples-only,
+# and that the output string will include psql's prompts and command echo.
+
+do_command(
+ "SELECT 'test' AS t FROM generate_series(1,23);\n",
+ qr/^test\r?$/m,
+ "execute SELECT query that needs no pagination");
+
+do_command(
+ "SELECT 'test' AS t FROM generate_series(1,24);\n",
+ qr/^ *24\r?$/m,
+ "execute SELECT query that needs pagination");
+
+do_command(
+ "\\pset expanded\nSELECT generate_series(1,20) as g;\n",
+ qr/^ *39\r?$/m,
+ "execute SELECT query that needs pagination in expanded mode");
+
+do_command(
+ "\\pset tuples_only off\n\\d+ information_schema.referential_constraints\n",
+ qr/^ *\d+\r?$/m,
+ "execute command with footer that needs pagination");
+
+# send psql an explicit \q to shut it down, else pty won't close properly
+$h->quit or die "psql returned $?";
+
+# done
+$node->stop;
+done_testing();