@@ -81,19 +81,19 @@ string. For C<interactive> sessions, IO::Pty is required.
8181sub new
8282{
8383 my $class = shift ;
84- my ($interactive , $psql_params , $timeout ) = @_ ;
84+ my ($interactive , $psql_params , $timeout , $wait ) = @_ ;
8585 my $psql = {
8686 ' stdin' => ' ' ,
8787 ' stdout' => ' ' ,
8888 ' stderr' => ' ' ,
89- ' query_timer_restart' => undef
89+ ' query_timer_restart' => undef ,
90+ ' query_cnt' => 1,
9091 };
9192 my $run ;
9293
9394 # This constructor should only be called from PostgreSQL::Test::Cluster
9495 my ($package , $file , $line ) = caller ;
95- die
96- " Forbidden caller of constructor: package: $package , file: $file :$line "
96+ die " Forbidden caller of constructor: package: $package , file: $file :$line "
9797 unless $package -> isa(' PostgreSQL::Test::Cluster' );
9898
9999 $psql -> {timeout } = IPC::Run::timeout(
@@ -104,8 +104,7 @@ sub new
104104 if ($interactive )
105105 {
106106 $run = IPC::Run::start $psql_params ,
107- ' <pty<' , \$psql -> {stdin }, ' >pty>' , \$psql -> {stdout }, ' 2>' ,
108- \$psql -> {stderr },
107+ ' <pty<' , \$psql -> {stdin }, ' >pty>' , \$psql -> {stdout }, ' 2>' , \$psql -> {stderr },
109108 $psql -> {timeout };
110109 }
111110 else
@@ -119,26 +118,51 @@ sub new
119118
120119 my $self = bless $psql , $class ;
121120
122- $self -> _wait_connect();
121+ $wait = 1 unless defined ($wait );
122+ if ($wait )
123+ {
124+ $self -> wait_connect();
125+ }
123126
124127 return $self ;
125128}
126129
127- # Internal routine for awaiting psql starting up and being ready to consume
128- # input.
129- sub _wait_connect
130+ =pod
131+
132+ =item $session->wait_connect
133+
134+ Returns once psql has started up and is ready to consume input. This is called
135+ automatically for clients unless requested otherwise in the constructor.
136+
137+ =cut
138+
139+ sub wait_connect
130140{
131141 my ($self ) = @_ ;
132142
133143 # Request some output, and pump until we see it. This means that psql
134144 # connection failures are caught here, relieving callers of the need to
135145 # handle those. (Right now, we have no particularly good handling for
136146 # errors anyway, but that might be added later.)
147+ #
148+ # See query() for details about why/how the banner is used.
137149 my $banner = " background_psql: ready" ;
138- $self -> {stdin } .= " \\ echo $banner \n " ;
150+ my $banner_match = qr / (^|\n )$banner \r ?\n / ;
151+ $self -> {stdin } .= " \\ echo $banner \n\\ warn $banner \n " ;
139152 $self -> {run }-> pump()
140- until $self -> {stdout } =~ / $banner / || $self -> {timeout }-> is_expired;
141- $self -> {stdout } = ' ' ; # clear out banner
153+ until ($self -> {stdout } =~ / $banner_match /
154+ && $self -> {stderr } =~ / $banner \r ?\n / )
155+ || $self -> {timeout }-> is_expired;
156+
157+ note " connect output:\n " ,
158+ explain {
159+ stdout => $self -> {stdout },
160+ stderr => $self -> {stderr },
161+ };
162+
163+ # clear out banners
164+ $self -> {stdout } = ' ' ;
165+ $self -> {stderr } = ' ' ;
142166
143167 die " psql startup timed out" if $self -> {timeout }-> is_expired;
144168}
@@ -184,10 +208,10 @@ sub reconnect_and_clear
184208
185209 # restart
186210 $self -> {run }-> run();
187- $self -> {stdin } = ' ' ;
211+ $self -> {stdin } = ' ' ;
188212 $self -> {stdout } = ' ' ;
189213
190- $self -> _wait_connect ();
214+ $self -> wait_connect ();
191215}
192216
193217=pod
@@ -205,25 +229,57 @@ sub query
205229 my ($self , $query ) = @_ ;
206230 my $ret ;
207231 my $output ;
232+ my $query_cnt = $self -> {query_cnt }++;
233+
208234 local $Test::Builder::Level = $Test::Builder::Level + 1;
209235
210- note " issuing query via background psql: $query " ;
236+ note " issuing query $query_cnt via background psql: $query " ;
211237
212238 $self -> {timeout }-> start() if (defined ($self -> {query_timer_restart }));
213239
214240 # Feed the query to psql's stdin, followed by \n (so psql processes the
215241 # line), by a ; (so that psql issues the query, if it doesn't include a ;
216- # itself), and a separator echoed with \echo, that we can wait on.
217- my $banner = " background_psql: QUERY_SEPARATOR" ;
218- $self -> {stdin } .= " $query \n ;\n\\ echo $banner \n " ;
219-
220- pump_until($self -> {run }, $self -> {timeout }, \$self -> {stdout }, qr /$banner / );
242+ # itself), and a separator echoed both with \echo and \warn, that we can
243+ # wait on.
244+ #
245+ # To avoid somehow confusing the separator from separately issued queries,
246+ # and to make it easier to debug, we include a per-psql query counter in
247+ # the separator.
248+ #
249+ # We need both \echo (printing to stdout) and \warn (printing to stderr),
250+ # because on windows we can get data on stdout before seeing data on
251+ # stderr (or vice versa), even if psql printed them in the opposite
252+ # order. We therefore wait on both.
253+ #
254+ # We need to match for the newline, because we try to remove it below, and
255+ # it's possible to consume just the input *without* the newline. In
256+ # interactive psql we emit \r\n, so we need to allow for that. Also need
257+ # to be careful that we don't e.g. match the echoed \echo command, rather
258+ # than its output.
259+ my $banner = " background_psql: QUERY_SEPARATOR $query_cnt :" ;
260+ my $banner_match = qr / (^|\n )$banner \r ?\n / ;
261+ $self -> {stdin } .= " $query \n ;\n\\ echo $banner \n\\ warn $banner \n " ;
262+ pump_until(
263+ $self -> {run }, $self -> {timeout },
264+ \$self -> {stdout }, qr /$banner_match / );
265+ pump_until(
266+ $self -> {run }, $self -> {timeout },
267+ \$self -> {stderr }, qr /$banner_match / );
221268
222269 die " psql query timed out" if $self -> {timeout }-> is_expired;
223- $output = $self -> {stdout };
224270
225- # remove banner again, our caller doesn't care
226- $output =~ s /\n $banner\n $// s ;
271+ note " results query $query_cnt :\n " ,
272+ explain {
273+ stdout => $self -> {stdout },
274+ stderr => $self -> {stderr },
275+ };
276+
277+ # Remove banner from stdout and stderr, our caller doesn't care. The
278+ # first newline is optional, as there would not be one if consuming an
279+ # empty query result.
280+ $output = $self -> {stdout };
281+ $output =~ s / $banner_match// ;
282+ $self -> {stderr } =~ s / $banner_match// ;
227283
228284 # clear out output for the next query
229285 $self -> {stdout } = ' ' ;
0 commit comments