I'm trying to get this Perl code to work correctly and I believe I just need to get my references right. I would appreciate any feedback on how to use references, but if this code can't be made to work without API changes I would also appreciate feedback on how to change the surrounding APIs to support the data types I need.
I'm trying to add a new test to the PostgreSQL test suite. The code in question runs a subprocess and stores the process arguments in an array literal in a hash. for example:
my %pgdump_runs = (
defaults => {
dump_cmd => [
'pg_dump', '--no-sync',
'-f', "$tempdir/defaults.sql",
'postgres',
],
},
# and others
)
That array gets passed to a helper library like so:
$node->command_ok(\@{ $pgdump_runs{$run}->{dump_cmd} },
"$run: pg_dump runs");
The helper library keeps passing the array down:
sub command_ok
{
my ($cmd, $test_name) = @_;
my $result = run_log($cmd);
and then finally calling IPC::Run:
sub run_log
{
return IPC::Run::run(@_);
}
To write my test case I have to pipe the output of a command to a file descriptor that does not support seeking. It looks like the IPC::Run module supports piping for you. Here's the example for the docs:
run \@cmd1, '|', \@cmd2;
But I tried a bunch of different ways to try and structure my array literal and none of them worked. For example, I tried this and I believe it gave me an ARRAY(0xFFFFFFFF)
error:
dump_cmd => [
['pg_dump', 'db'],
'|',
['pg_restore', 'db2'],
],
How can I get this code to set up multiple sub-processes with piping?
UPDATE: I tried out ikegami's suggestion: the command_ok
function can only have two arguments passed to it, so that doesn't work (you have to pass an array reference for the first argument).
UPDATE: turning on IPC:Run's debug functionality I can see how it's trying to interpret my command line. Instead of dereferencing the array it's trying to stringify (?) it and exec that string as an executable:
1070 IPC::Run 0002 01234567890- [#11(59856)]: parsing [ 'ARRAY(0x7ff3016f1090)', '|', 'ARRAY(0x7ff3016f1510)', '>/Users/david/src/pg_bug/postgres/foo' ]
You are effectively doing the following:[1]
my %pgdump_runs = (
foo => {
dump_cmd => [
['pg_dump', 'db'],
'|',
['pg_restore', 'db2'],
],
},
);
IPC::Run::run( $pgdump_runs{foo}{dump_cmd} )
This passes a single argument to run
, but you indicated you wanted to achieve something analogous to
run \@cmd1, '|', \@cmd2;
That would require
IPC::Run::run( @{ $pgdump_runs{foo}{dump_cmd} } )
A few ways you can fix this:
Replace
return IPC::Run::run(@_);
with
return IPC::Run::run(@{ $_[0] });
Replace
run_log($cmd);
with
run_log(@$cmd);
Replace
sub command_ok {
my ($cmd, $test_name) = @_;
my $result = run_log($cmd);
}
$node->command_ok(\@{ $pgdump_runs{$run}->{dump_cmd} }, "$run: pg_dump runs");
with
sub command_ok {
my $test_name = shift;
my $result = run_log(@_);
}
$node->command_ok("$run: pg_dump runs", @{ $pgdump_runs{$run}{dump_cmd} });
Replace
dump_cmd => [
['pg_dump', 'db'],
'|',
['pg_restore', 'db2'],
],
with
use String::ShellQuote qw( shell_quote );
dump_cmd => join(' ',
shell_quote('pg_dump', 'db'),
'|',
shell_quote('pg_restore', 'db2'),
),
Footnotes:
Unless you are trying to autovivify (which you're not),
\@{ $pgdump_runs{$run}->{dump_cmd} }
can also be written as
$pgdump_runs{$run}->{dump_cmd}
And in both cases, the ->
can be omitted because it's between two indexes.
User contributions licensed under CC BY-SA 3.0