1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
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();
|