How do write a dispatch table for methods? - perl

I'm trying to get rid of the if statements at the end of a subroutine I'm writing to handle SELECT queries:
sub select_query {
my ($params, $query, $return_type) = #_;
my $qh = $dbx->prepare($query);
my $param_count = 1;
foreach my $param (#$params) {
$qh->bind_param($param_count++, $param);
}
$qh->execute;
if ($return_type eq 'fetchrow_array') {
return $qh->fetchrow_array;
}
if ($return_type eq 'fetchall_arrayref') {
return $qh->fetchall_arrayref;
}
... AND SO ON ...
}
I'm familiar with the idea of a dispatch table to call different subroutines. What code could I use to efficiently call the various dbi methods on the $qh handle?

All you need is $qh->$return_type(...).
If you wish to validate, you can use a trivial lookup table.
my #valid_return_types = qw( fetchrow_array ... );
my %valid_return_types = map { $_ => 1 } #valid_return_types;
die "..." if !$valid_return_types{$return_type};

You will need to define the dispatch table after creating the statement handle.
use strict;
use warnings;
use DBI;
use Data::Dumper;
my $dbh = DBI->connect("DBI:mysql:mysql:localhost", 'user', 'password');
my $sth = $dbh->prepare("select * from user");
my $return_type = 'fetchall_arrayref';
my %dispatch = (fetchall_arrayref => sub {return $sth->fetchall_arrayref},
fetchrow_array => sub {return $sth->fetchrow_array});
$sth->execute;
print Dumper $dispatch{$return_type}->();
UPDATE:
Actually, let me make a correction. You can, if you wish, define the dispatch table prior creating the statement handle.
my %dispatch = (fetchall_arrayref => sub {return $_[0]->fetchall_arrayref},
fetchall_hashref => sub {return $_[0]->fetchall_hashref('User')},
fetchrow_array => sub {return $_[0]->fetchrow_array});
my $dbh = DBI->connect("DBI:mysql:mysql:localhost", 'root', 'password') or die;
my $sth = $dbh->prepare("select * from user");
foreach my $type (qw(fetchall_arrayref fetchall_hashref fetchrow_array)) {
$sth->execute;
print Dumper $dispatch{$type}->($sth);
}

Related

inserting expect into perl loop

I have the following script that runs a command and puts the data in a DB. I need to account for the possibility of being asked for a password "password:" some of the time. How do I wrap an expect call into this?
#!/usr/software/bin/perl
use strict;
use DatabaseLib;
use Data::Dumper;
use Expect;
#Connect to database
my $dbh = DBI->connect($DB_CONNECT_STRING, $DB_USER, $DB_PASSWORD, { RaiseError => 1, AutoCommit => 1 })
or die "failed to connect to database: $DB_CONNECT_STRING";
my $expect = Expect->new;
my %burtHash;
my #cols = qw/date_create sub_by impact date-lastmod lastmod-by bug_rel case_score state s p type subtype subteam found_by target_release/;
my #burtInfo = `mycommand`;
my $timeout = 20;
my $password = "password";
while(my $ele = shift(#burtInfo)){
my ($index, #data) = split(/\s+/, $ele);
for my $i(0 .. $#cols){
$burtHash{$index}->{$cols[$i]} = shift(#data);
}
for my $id (keys %burtHash){
my %burt_details;
for my $col (keys %{$burtHash{$id}} ) {
$burt_details{$col} = $burtHash{$id}->{$col};
}
if ( $id =~ /\d+/) {
burt_update(
$dbh,
$id ,
\%burt_details,
);
}
}
}
I think I just need to put in something like this and call it, but i'm not sure where/how:
$expect->expect($timeout,
[ qr/password:/i, #/
sub {
my $self = shift;
$self->send("$password\n");
exp_continue;
}
]);
You're not using $expect anywhere there. You have to run your command via $expect->spawn so that your Expect object can handle things. And then you'll need some way of gathering its output (I'm thinking using $expect->log_file(...) to set the log to a string filehandle or something).
Once you're using $expect->spawn, then you can insert your password check. But there's no way you can do this with qx (the backticks).

Return multiple variables perl

I have this
sub test
{
my ($arg1, $arg2) = #_; # Argument list
code
return ($variable1, $variable2);
}
So, when i call this by
test('text1','text2');
concatenates the two return values in one. How can i call only one at a time?
my $output_choice_1 = ( test('text1','text2') )[0];
my $output_choice_2 = ( test('text1','text2') )[1];
or both at once:
my ( $output_choice_1, $output_choice_2 ) = test('text1','text2');
Though sometimes it makes for clearer code to return a hashref:
sub test {
...
return { 'choice1' => $variable1, 'choice2' => $variable2 };
}
...
my $output_choice_1 = test('text1','text2')->{'choice1'};
Are you asking how to assign the two values returned by a sub to two different scalars?
my ($var1, $var2) = test('text1', 'text2');
I wasn't really happy with what I found in google so posting my solution here.
Returning an array from a sub.
Especially the syntax with the backslash caused me headaches.
#!/usr/bin/perl
use warnings;
use strict;
use Data::Dumper;
sub returnArrayWithHash {
(my $value, my %testHash) = #_;
return ( $value, \%testHash );
}
my %testHash = ( one => 'foo' , two => 'bar' );
my #result = returnArrayWithHash('someValue', %testHash);
print Dumper(\#result) . "\n";
Returns me
$VAR1 = [
'someValue',
{
'one' => 'foo',
'two' => 'bar'
}
];

Should I put the "eval" in the subroutine or the subroutine in the "eval"?

Does one of these two ways have to be preferred or is it only a matter of taste?
#!/usr/bin/env perl
use warnings;
use strict;
use DBI;
my $db = 'sqlite_db';
#################### A ####################
sub get_database_handle {
my ( $db ) = #_;
my $dbh;
eval {
$dbh = DBI->connect( "DBI:SQLite:$db", '', '', {...} )
or die DBI->errstr;
};
if ( $# ) {
print $#;
return;
}
return $dbh;
}
DATABASES: while ( 1 ) {
# choose a database from a list of databases
# ...
my $dbh = get_database_handle( $db );
next DATABASES if not defined $dbh;
# ...
# do something with the database
}
#################### B ####################
sub get_database_handle {
my ( $db ) = #_;
my $dbh = DBI->connect( "DBI:SQLite:$db", '', '', {...} )
or die DBI->errstr;
return $dbh;
}
DATABASES: while ( 1 ) {
# choose a database from a list of databases
# ...
my $dbh;
eval { $dbh = get_database_handle( $db ); };
if ( $# ) {
print $#;
next DATABASES;
}
# ...
# do something with the database
}
Why eval at all? What are you going to do when you can't get a database handle?
At the very least, the subroutine should either return a database handle or die, so no eval in there.
If you really have something productive to do when there is a database error, then eval outside of the subroutine. Not necessary around just the subroutine, could be even wider scope, as appropriate for your error handling logic.
But if all you want is terminate the program and print an error, just let the exception bubble up.
It really makes no sense to use RaiseError => 1 at all if your code is going to look like that. If you want to keep that code layout, use RaiseError => 0 instead. (You can always turn it on later using $dbh->{RaiseError} = 1;`.)
sub get_database_handle {
my ( $db ) = #_;
return DBI->connect("DBI:SQLite:$db", '', '', {
...
RaiseError => 0,
PrintError => 1,
});
}
for my $db ( ... ) {
my $dbh = get_database_handle( $db )
or next;
...
}
That said, I suggest that you continue using RaiseError => 1, but that you change your code layout instead. Specifically, you should widen the scope of the eval.
sub get_database_handle {
my ( $db ) = #_;
return DBI->connect("DBI:SQLite:$db", '', '', {
...
RaiseError => 1,
PrintError => 0,
});
}
for my $db ( ... ) {
if (!eval {
my $dbh = get_database_handle( $db );
...
1 # No exception
}) {
warn("Error processing database $db: $#");
}
}
This will catch any errors, not just database connection errors.
It depends on the preferred way of handling errors in the rest of your project.
If you plan to use exceptions, let your function throw them.
If you are going to handle errors manually via conditionals, don't throw (eval inside).
I myself prefer exceptions. They are loud (you know something is broken!), plus stack traces via Carp::confess/Carp::longmess and $SIG{__DIE__} are a nice bonus in case of a large codebase.
Here's an (un)success story. A month or two ago we rolled out broken code that silently corrupted data due to unchecked return value from DB-handling function. It was in production for a day. Resurrecting the data was a world of pain.

Perl Classes :: Can not write output

I am new to Object oriented programming in perl. So, I have a silly question.
What --
I am writing a script which will do something and write result to stream ( STDOUT or NETWORK ).
How --
[main.pl]
#!/usr/bin/perl
use strict;
require const::StreamTypes;
require output;
my $out = output->new("output");
$out->writeLine("Sample output");
[output.pm]
#!/usr/bin/perl
use strict;
require const::StreamTypes;
package output;
my $OUTSTR;
sub new{
my $class = shift();
my $stream = shift();
if($stream eq const::StreamTypes->STDNET){
}elsif($stream eq const::StreamTypes->STDWEB){
}else{
*OUTSTR = *STDOUT;
}
my $self = {
"_outStream" => $stream,
"_outStreamPtr" => $OUTSTR
};
bless($self, $class);
}
sub writeLine{
my $msg = shift();
print(OUTSTR "$msg\n");
}
return 1;
So, can anyone help me understand what is going wrong here? 'cas program runs without error but with no output.
Thanks!
I changed a couple of things here:
the first parameter of a methd is the invocant (instance or class) itself
indirect file handles are globals!
the autodie module comes in handy, if using open
consider using strict in your modules, too
I would not recommend the use of package global variable ( my $OUTSTR; ), because that's going to be messy with multiple instances, which want to have different streams.
And I definitely got into the habit of using accessors for all attributes. You can use a lightweight system like Class::Accessor or perhaps you are even lucky enough to use Moose our Mouse. Of course there are a couple of other modules also providing accessors in different ways.
package output;
use strict;
use warnings;
use autodie;
use Class::Accessor "moose-like";
has "outStream" => ( is => 'rw' );
sub new{
my ( $class, $stream ) = #_;
my $self = bless( {}, $class );
if ( 0 ) {
# ...
} else {
open( my $outStream, '>&', \*STDOUT );
$self->outStream( $outStream );
}
return $self;
}
sub writeLine{
my ( $self, $msg ) = #_;
print { $self->outStream } "$msg\n";
}
return 1;
Moose would create a constructor for you, but you can insert your parameter processing as easy as follows:
use Moose;
has "outStream" => ( is => 'rw' );
sub BUILDARGS {
my ( $class, $stream ) = #_;
open( my $outStream, '>&', \*STDOUT );
return {
outStream => $outStream,
};
}
$OUTSTR and *OUTSTR are very different things -- you should clear up your misunderstanding about this before you worry about object oriented programming.
That said, you can probably fix this script by getting everything to refer to $OUTSTR:
...
}else{
$OUTSTR = *STDOUT;
}
...
print $OUTSTR "$msg\n";
How about just passing a file handle directly into the object's constructor?
package output;
sub new {
my ($class, $fh) = #_;
bless { file_handle => $fh }, $class;
}
sub writeLine {
my $self = shift;
my $line = shift;
print {$self->{file_handle}} $line;
}
1;
Example usage:
my $output = output->new(\*STDOUT); # write to stdout
my $socket = IO::Socket::INET->new('www.perl.org', PeerPort => 'http(80)', Proto => 'tcp');
my $output = output->new($socket); # write to a socket
Please don't use barenames for file handles. Use lexical file handles.
The following lines assume that there is a hash %type_handlers somewhere that looks something like this:
{ const::StreamTypes->STDNET => \&constructor_for_stdnet_handles
, const::StreamTypes->STDWEB => \&constructor_for_stdweb_handles
}
Then you can replace the bottom of your constructor with:
my $handler = $type_handlers{ $stream };
my $outstr
= $handler ? $handler->()
: do { my $h; open( $h, '>&', \*::STDOUT ) and $h; }
;
return bless( {
_outStream => $stream
, _outStreamPtr => $outstr
}
, $class
);
Then writeLine becomes:
sub writeLine {
my ( $self, $msg ) = #_;
( $self->{_outStreamPtr} || *::STDOUT{IO} )->say( $msg );
}
The method is a little more robust in cases where somebody just blessed themselves into your class.
my $q_and_d = bless {}, 'output';
If you don't want to allow "quick & dirty" instances, and want more precise messages from possible failures, you could do this:
Carp::croak( 'No outstream!' )
unless my $h = Params::Util::_HANDLE( $self->{_outStreamPtr} )
;

Perl Parallel::ForkManager with DBI database handlers

im interested in performing multiple database actions in parallel. I have played with Perl Parallel::ForkManager but not used it with any databases yet. I have read that database connectivity is not supported very well with this. Does anyone have experience with this?
As an example i would probably be spawning a system call(which does the DBI work) NOT raw code, i.e.
#!/opt/local/bin/perl -w
use strict;
use Parallel::ForkManager;
$| = 1;
my $max_procs = 10;
my $pm = new Parallel::ForkManager($max_procs);
for (my $var = 0; $var <= 10; $var++) {
my $pid = $pm->start('proc'.$var) and next;
sleep ( 2 );
system( "./DBworker.pl $var" );
$pm->finish(0);
}
print "Waiting for child procs\n";
$pm->wait_all_children;
print "complete!\n";
If the work is being done by other programs, there is no danger to forking. The danger comes when you open a connection to the database and then fork. The child can't reuse the parents connection; however, take a look at DBIx::Connector, it handles the things you need to do after forking for you and running multiple programs is generally not the right answer.
#!/usr/bin/perl
use strict;
use warnings;
use DBIx::Connector;
use Parallel::ForkManager;
my $dsn = "dbi:SQLite:dbname=foo.db";
my $user = "";
my $pass = "";
my $conn = DBIx::Connector->new($dsn, $user, $pass,
{
AutoCommit => 0,
PrintError => 0,
RaiseError => 1,
ChopBlanks => 1,
FetchHashKeyName => 'NAME_lc',
}
);
END { unlink "foo.db" }
#setup table
$conn->run(fixup => sub {
my $dbh = $_;
$dbh->do("create table foo ( id integer, name char(35) )");
my $sth = $dbh->prepare("insert into foo (id, name) values (?, ?)");
while (<DATA>) {
chomp;
$sth->execute(split /,/);
}
});
my $pm = Parallel::ForkManager->new(3);
my $sth = $conn->dbh->prepare("select * from foo where id = ?");
for my $id (1 .. 3) {
next if $pm->start;
$sth->execute($id);
while (my $row = $sth->fetchrow_hashref) {
print "$id saw $row->{id} => $row->{name}\n";
}
$pm->finish;
}
$pm->wait_all_children;
print "done\n";
__DATA__
1,foo
2,bar
3,baz