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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
|
# Copyright (c) 2021-2022, PostgreSQL Global Development Group
use strict;
use warnings;
use PostgreSQL::Test::Cluster;
use PostgreSQL::Test::Utils;
use Test::More;
program_help_ok('psql');
program_version_ok('psql');
program_options_handling_ok('psql');
# Execute a psql command and check its output.
sub psql_like
{
local $Test::Builder::Level = $Test::Builder::Level + 1;
my ($node, $sql, $expected_stdout, $test_name) = @_;
my ($ret, $stdout, $stderr) = $node->psql('postgres', $sql);
is($ret, 0, "$test_name: exit code 0");
is($stderr, '', "$test_name: no stderr");
like($stdout, $expected_stdout, "$test_name: matches");
return;
}
# Execute a psql command and check that it fails and check the stderr.
sub psql_fails_like
{
local $Test::Builder::Level = $Test::Builder::Level + 1;
my ($node, $sql, $expected_stderr, $test_name) = @_;
# Use the context of a WAL sender, some of the tests rely on that.
my ($ret, $stdout, $stderr) = $node->psql(
'postgres', $sql,
replication => 'database');
isnt($ret, 0, "$test_name: exit code not 0");
like($stderr, $expected_stderr, "$test_name: matches");
return;
}
# test --help=foo, analogous to program_help_ok()
foreach my $arg (qw(commands variables))
{
my ($stdout, $stderr);
my $result;
$result = IPC::Run::run [ 'psql', "--help=$arg" ], '>', \$stdout, '2>',
\$stderr;
ok($result, "psql --help=$arg exit code 0");
isnt($stdout, '', "psql --help=$arg goes to stdout");
is($stderr, '', "psql --help=$arg nothing to stderr");
}
my $node = PostgreSQL::Test::Cluster->new('main');
$node->init(extra => [ '--locale=C', '--encoding=UTF8' ]);
$node->append_conf(
'postgresql.conf', q{
wal_level = 'logical'
max_replication_slots = 4
max_wal_senders = 4
});
$node->start;
psql_like($node, '\copyright', qr/Copyright/, '\copyright');
psql_like($node, '\help', qr/ALTER/, '\help without arguments');
psql_like($node, '\help SELECT', qr/SELECT/, '\help with argument');
# Test clean handling of unsupported replication command responses
psql_fails_like(
$node,
'START_REPLICATION 0/0',
qr/unexpected PQresultStatus: 8$/,
'handling of unexpected PQresultStatus');
# test \timing
psql_like(
$node,
'\timing on
SELECT 1',
qr/^1$
^Time: \d+.\d\d\d ms/m,
'\timing');
# test that ENCODING variable is set and that it is updated when
# client encoding is changed
psql_like(
$node,
'\echo :ENCODING
set client_encoding = LATIN1;
\echo :ENCODING',
qr/^UTF8$
^LATIN1$/m,
'ENCODING variable is set and updated');
# test LISTEN/NOTIFY
psql_like(
$node,
'LISTEN foo;
NOTIFY foo;',
qr/^Asynchronous notification "foo" received from server process with PID \d+\.$/,
'notification');
psql_like(
$node,
"LISTEN foo;
NOTIFY foo, 'bar';",
qr/^Asynchronous notification "foo" with payload "bar" received from server process with PID \d+\.$/,
'notification with payload');
# test behavior and output on server crash
my ($ret, $out, $err) = $node->psql(
'postgres',
"SELECT 'before' AS running;\n" .
"SELECT pg_terminate_backend(pg_backend_pid());\n" .
"SELECT 'AFTER' AS not_running;\n");
is($ret, 2, 'server crash: psql exit code');
like($out, qr/before/, 'server crash: output before crash');
ok($out !~ qr/AFTER/, 'server crash: no output after crash');
is($err, 'psql:<stdin>:2: FATAL: terminating connection due to administrator command
psql:<stdin>:2: server closed the connection unexpectedly
This probably means the server terminated abnormally
before or while processing the request.
psql:<stdin>:2: fatal: connection to server was lost',
'server crash: error message');
# test \errverbose
#
# (This is not in the regular regression tests because the output
# contains the source code location and we don't want to have to
# update that every time it changes.)
psql_like(
$node,
'SELECT 1;
\errverbose',
qr/^1\nThere is no previous error\.$/,
'\errverbose with no previous error');
# There are three main ways to run a query that might affect
# \errverbose: The normal way, using a cursor by setting FETCH_COUNT,
# and using \gdesc. Test them all.
like(($node->psql('postgres', "SELECT error;\n\\errverbose", on_error_stop => 0))[2],
qr/\A^psql:<stdin>:1: ERROR: .*$
^LINE 1: SELECT error;$
^ *^.*$
^psql:<stdin>:2: error: ERROR: [0-9A-Z]{5}: .*$
^LINE 1: SELECT error;$
^ *^.*$
^LOCATION: .*$/m,
'\errverbose after normal query with error');
like(($node->psql('postgres', "\\set FETCH_COUNT 1\nSELECT error;\n\\errverbose", on_error_stop => 0))[2],
qr/\A^psql:<stdin>:2: ERROR: .*$
^LINE 2: SELECT error;$
^ *^.*$
^psql:<stdin>:3: error: ERROR: [0-9A-Z]{5}: .*$
^LINE 2: SELECT error;$
^ *^.*$
^LOCATION: .*$/m,
'\errverbose after FETCH_COUNT query with error');
like(($node->psql('postgres', "SELECT error\\gdesc\n\\errverbose", on_error_stop => 0))[2],
qr/\A^psql:<stdin>:1: ERROR: .*$
^LINE 1: SELECT error$
^ *^.*$
^psql:<stdin>:2: error: ERROR: [0-9A-Z]{5}: .*$
^LINE 1: SELECT error$
^ *^.*$
^LOCATION: .*$/m,
'\errverbose after \gdesc with error');
done_testing();
|