Connecting to multiple databases in a perl script - perl

How can I connect to multiple databases in perl without it bailing out if it can't connect to one? If I have the database names in an array and go trough them in a loop, I want to try and connect and perform a query and disconnect. I am going to report it back to the big brother server monitoring page we have. I don't want the script to terminate if it can't connect to one, since it obviously needs to check every one in the array. Right now i use the DBI modules state method but I don't know that it works correctly. Thanks for everyone's time!

Can we see the code? I don't think a call to DBI->connect() will die unless you explicitly tell it to, as in:
DBI->connect($dsn, $user, $pass) or die "Can't connect: $DBI::errstr\n";
I'm pretty sure even using {RaiseError => 1} won't make it die automatically. So, if you are calling or die... just don't do it!
EDIT:
Given the code that #squiguy posted, I would do it like this:
foreach my $host (#hosts) {
$dbh = DBI->connect("dbi:Oracle:; host=$host; port=$port", $username, $password);
next unless $dbh;
if ($dbh->state()) {
# Do stuff with state information
}
}

You should look up exception handling in Perl. I don't use Perl so I do not know the syntax for exception handling, should be easy enough to find online though.

I have the database names in an array and go trough them in a loop, I want to try and connect and perform a query and disconnect.
If you're looping, there's no reason to open multiple databases. In your loop, open a database, do your stuff, and close.
How can I connect to multiple databases in perl without it bailing out if it can't connect to one?
I'm assuming that this is in your loop. You can always use eval whenever you are doing any Perl command that might error out and stop your program from executing.
You do something like this:
for my $database (#database_list) {
my $dbh;
eval {
$dbh = DBI->connect($database, $user, $password);
};
if (not $#) {
yadda, yadda, yadda
}
}
The eval will catch any sort of ordinary deadly error. If $# has a value, the call failed, and eval returned an error description. If $# is empty, no error, and you can simply continue.
HOWEVER, BY default, DBI doesn't automatically die if it can't connect. Instead, it merely returns an undefined value. You should be able to use that to determine whether you've succeeded, or need to go to the next database:
for my $database (#database_list) {
my $dbh = DBI->connect($database, $user, $password);
if ($dbh) {
yadda, yadda, yadda
}
}
If I remember correctly, there's an attribute called RaiseError that if set will cause your program to die in a failed DBI call. However, the default should be not set, so you shouldn't be having any issues.

Related

Can't figure out why Sereal encoder/decoder round-trip is not returning proper object

With all the hating on Storable -- I decided to check out Sereal for serialization needs. Plus I was having some issues with 32bit/64bit cross platform issues with Storable, so I figured this would be a good time.
After having some issues, I boiled the problem down to the following code. (i'm persisting an HTTP::Request object, hence the example code).
This is my encode test, i'm storing to a file:
use Sereal::Encoder;
use HTTP::Request;
use HTTP::Headers;
use URI;
my $encoder = Sereal::Encoder->new();
open(my $fh, ">", 'myfile.data') or die $!;
binmode($fh);
my $uri = URI->new('http://www.example.com');
my $headers = HTTP::Headers->new(
Content_Type => 'application/json',
);
my $http_request = HTTP::Request->new(POST => $uri, $headers, 'bleh');
print $fh $encoder->encode( $http_request );
close($fh);
And on the same machine(same perl etc. on 5.18), I run the following:
use Sereal::Decoder;
use File::Slurp qw(read_file);
use URI;
my $data = read_file('myfile.data') or die $!;
my $dec = Sereal::Decoder->new();
my $decoded = $dec->decode($data);
print $decoded->{_uri}->scheme,"\n";
And the output of running the encoding program, and then the decoding program is:
Can't locate object method "scheme" via package "URI::http" at testd.pl line 8.
Anyhow, was really nagging me as to what the problem was. I ended up reverting back to Storable and using nfreeze to solve my arch issues with Storable but was wondering why my attempt to transition to Sereal crashed and burned.
Thanks!
Sereal, unlike Storable, won't automatically load a module when it encounters a serialized object. This is a security issue with Storable, so Sereal is working as intended.[1]
At the point where scheme is called in the second test program, URI::http hasn't been loaded yet, so the method call results in an error. It seems that URI will load its subclasses when its constructor is used on a string that "looks like" one of them, e.g.
URI->new('http://www.stackoverflow.com');
loads the URI::http module. So one solution would be to add a dummy invocation of that constructor to ensure URI::http is loaded, or manually use URI::http instead. Either option causes the print $decoded->{_uri}->scheme line of the second script to work as expected, but I think the second is the lesser of two evils (importing an undocumented submodule from URI versus an arbitrary method call done specifically for its not-immediately-obvious side effect).

Get a list of all Oracle data sources via DBI

I'm creating a perl program where the user has to enter the name of a database, in addition to a username and password, and the program connects to that database using DBI->connect. What I'd like to do is to have the program present a list of names of databases that it can connect to, and all the user has to do is input their username and password, for ease of use. I took a look at the DBI documentation for data sources, thinking it was what I need but when I call DBI->data_sources('Oracle') it doesn't return anything (but no errors are encountered either). I don't see how this can happen, as the program is able to connect without any hitches if the user supplies the database name. Is there something that I'm doing wrong, or something that I'm not getting?
And yes, I realize I can just manually open the tnsnames.ora file and parse that. In fact, that's what I did first but I'd like to use DBI preferably.
Try this:
#!/usr/bin/perl -w
use DBI;
my #drivers = DBI->available_drivers();
die "No drivers found!\n" unless #drivers; # should never happen
foreach my $driver ( #drivers ) {
print "Driver: $driver\n";
my #dataSources = DBI->data_sources( $driver );
foreach my $dataSource ( #dataSources ) {
print "\tData Source is $dataSource\n";
}
print "\n";
}

How to properly use the try catch in perl that error.pm provides?

I have found that there is the module Error that provides try and catch functionality like in java. But I am confused at how you can print the exception that returns.
I would like to understand how to do the following
try {
// do something that will fail!
} catch (Error e) {
// Print out the exception that occurred
System.out.println(e.getMessage());
}
How do I get the print of the error with the stack trace?
You're probably better off using Try::Tiny which will help you avoid a number of pitfalls with older perls.
use Try::Tiny;
try {
die "foo";
} catch {
warn "caught error: $_";
};
Last I checked, Error was deprecated. But here's how you would do it without that module:
eval {
die "Oops!";
1;
} or do {
my $e = $#;
print("Something went wrong: $e\n");
};
Basically, use eval instead of try, die instead of throw, and look for the exception in $#. The true value at the end of the eval block is part of an idiom to prevent $# from unintentionally changing before it is used again in Perl versions older than 5.14, see P::C::P::ErrorHandling::RequireCheckingReturnValueOfEval for details. For example, this code suffers from this flaw.
# BAD, DO NOT USE WITH PERLS OLDER THAN 5.14
eval {
die "Oops!";
};
if (my $e = $#) {
print("Something went wrong: $e\n");
}
# BAD, DO NOT USE WITH PERLS OLDER THAN 5.14
But note that many Perl operations do not raise exceptions when they fail; they simply return an error code. This behavior can be altered via autodie for builtins and standard modules. If you're using autodie, then the standard way of doing try/catch is this (straight out of the autodie perldoc):
use feature qw(switch);
eval {
use autodie;
open(my $fh, '<', $some_file);
my #records = <$fh>;
# Do things with #records...
close($fh);
};
given ($#) {
when (undef) { say "No error"; }
when ('open') { say "Error from open"; }
when (':io') { say "Non-open, IO error."; }
when (':all') { say "All other autodie errors." }
default { say "Not an autodie error at all." }
}
For getting a stacktrace, look at Carp.
If you want something a bit more powerful than Try::Tiny, you might want to try looking at the TryCatch module in CPAN.
Native try/catch/finally.
Perl now has native support for try/catch/finally. You can use it like this. As of Perl v5.36, It's currently experimental.
use experimental "try";
try { die 42 }
catch ($err) {
print "got $err"
}
Unfortunately TryCatch has been broken with the new version 0.006020 of Devel::Declare and there seems to be no intention of fixing it. The perl core developers team also complained about the funky stuff TryCatch was relying on to make it work.
Instead there is a new implementation called Nice::Try, which is a perfect replacement.
There is no need to have semi colon on the last brace like Try::Tiny.
You can also do exception variable assignment like
try
{
# something
}
catch( $e )
{
# catch this in $e
}
It also works using class exception like
try
{
# something
}
catch( Exception $e )
{
# catch this in $e
}
And it also supports finally. Its features set make it quite unique.
Full disclosure: I have developed this module when TryCatch got broken.
IMHO Syntax::Keyword::Try is a better option than Try::Tiny for most cases.
Try::Tiny is an order of magnitude slower than either eval or Syntax::Keyword::Try. It depends on your application if this is a problem or not. For many applications its not important.
Also if you are a visitor from another language, Try::Tiny has syntax quirks which make it not quite the try/catch you are used to.
As has been said, the old Error module is deprecated, but it has continued to work for me long after. It's been a while since I last checked if its still working. I tried some of the "better" replacements and found them all lacking, so I still use Error.
Here's a sample from some code that's pretty straight forward and prints the error that was caught. This is from an app I wrote that uses Storable to store and retrieve serialized perl objects to an MLDBM backend file.
use Error(':try');
...
try {
$obj = retrieve( $objfile );
} catch Error::Simple with {
my $E = shift;
warn "Caught error $E";
};
This returns a message something like:
Caught error Not a HASH reference at ./json2text.pl line 12.
If you want the full stack trace, you can either run the code under the debugger, or you can read about the stacktrace method on the perldoc for the Error class itself. I never found it necessary to have apps not running under the debugger to print full stack traces, so I don't have any examples of that in my collection to paste a sample from.
I hope this helps. Sometimes you just have to wait for a dinosaur to show up.

Catching errors with both mod_cgi & mod_perl

Thanks to everyone in advance.
I've been doing some research on error handling and I don't feel like I'm getting a solid understanding of what I should do.
Preamble: My code is living in Apache and executed in the browser, my goals don't include command line execution.
I'd like to have the behavior of CGI::Carp (fatalsToBrowser) with the ability to capture the output and be able to throw it in my own templated page, email it etc... I did notice that fatalsToBrowser doesn't work with mod_perl. Does anyone know why? How is Apache/mod_perl getting in the way?
First Goal: I'd like to put something together that works if the code is being executed with mod_perl or mod_cgi.
Second Goal: I'd like to have a high-level method(s) that catches all the errors similar to .NET's Application_Error (in global.asax) and PHP's set_exception_handler() and set_error_handler() methods. These allow you to take control when an error is raised, without wrapping code in messy/gross try-catch statements.
Things I've read/reviewed:
1.) OO Exception Handling in Perl, but wasn't what I was looking for. Most of the stuff I want to catch is die()ing. The next link also says that this article is out of date and deprecated.
2.) Perl: $SIG{__DIE__}, eval { } and stack trace, but I didn't get much from this related to my goals.
3.) Practical Mode Perl (O'Reilly), Chapter 21 "Error Handling and Debugging". Thankfully all my perl code uses strict and warnings are enabled, and most important things mentioned in Chapter 6 "Coding with mod_perl in Mind" are already done.
4.) I've dug through the tables of contents in "Learning Perl", "Perl Cookbook", "Programming Perl" and "Higher Order Perl" and didn't see anything that stuck out at me. If you think I missed something there please let me know. :)
I don't remember where (maybe in "Practical mod_perl", but I've read that you should not mess with $SIG{__DIE__}.
Have you read the mod_perl website's bit on Alternative Exception Handling Techniques? It discusses about how you can catch uncaught exceptions though the use of overriding the global die() function instead of using $SIG{__DIE__}. A much cleaner method but not perfect.
What type of errors are you trying to catch? Are custom error pages not sufficient for your purposes?
My CGI scripts are short (OK, this is really bare bones — and untested):
#!/usr/bin/perl
use strict;
use warnings;
use My::App;
use My::ErrorReporter qw( error_to_html );
run();
sub run {
my $app = eval {
My::App->new(
'some_param',
'another_param',
)
};
unless ( $app ) {
print error_to_html( $# );
return;
}
eval {
$app->handle_request;
} and return;
print error_to_html( $# );
return;
}
__END__
Now, fatalsToBrowser is not for your users. That is a development aid for you. The error messages users see should not convey information about the program. So, for example, in a routine that opens and reads a configuration file, you should do something like:
sub read_my_config {
my $self = shift;
open my $config_h, '<', $self->config_file;
unless ( $config_h ) {
# This goes to the Apache error log where you can read it
warn sprintf(
"Cannot open '%s': %s",
$self->config_file, $!
);
# This is for web site visitors to see
die "Cannot open configuration file";
}
# rest of the code
}

How can I fix the "Couldn't create file parser context for file ..." bug with Perl libxml on Debian?

When I try to read an XML file with XML::Simple, sometimes I get this error message:
Couldn't create file parser context for file ...
After some googling, it seems to be a problem with libxml-libxml-perl and is supposed to be fixed in the version I use (1.59-2).
Any ideas?
Edit: (code)
sub Read
{
my ($file, $no_option) = #_;
my %XML_INPUT_OPTIONS = ( KeyAttr => [], ForceArray => 1 );
if ((defined $file) && (-f $file))
{
my #stats = stat($file);
if ((defined $XML_CACHE{$file})
&& ($stats[9] == $XML_CACHE{$file}{modif_time}))
{
return ($XML_CACHE{$file}{xml});
}
else
{
my $xml = eval { XMLin($file,
(defined $no_option ? () : %XML_INPUT_OPTIONS)) };
AAT::Syslog("AAT::XML", "XML_READ_ERROR", $#) if ($#);
$XML_CACHE{$file}{modif_time} = $stats[9];
$XML_CACHE{$file}{xml} = $xml;
return ($xml);
}
}
return (undef);
}
And yes, I should & will use XML::Simple cache feature...
Does the error continue "No such file or directory at..."? If so, then I think that the problem is that (for whatever reason) when you get to that point in the script, whatever you are passing to XML::Simple has no xml file in it. Long story short, the script you are using may be passing a bad variable (blank? empty?) to XML::Simple at which point the module chokes. To debug, add a check on whatever you hand to XML::Simple before you pass it along. (See the next paragraph for a concrete example explaining why I think this may be your problem.)
A few months ago, I had a similar problem with Weather::Google. In a nutshell, the weather module was trying to get data from Google via LWP::Simple without a user agent. Google began (apparently) to reject requests without a user agent. I had to backtrack through the modules because the error appeared to come from XML::Simple. In fact, it was caused by what was done in LWP::Simple and Weather::Google. Or rather, the error was a result of Weather::Google not checking the data that was in an object created via LWP::Simple. In a case like this, it can be hard at first to see what's going wrong and where.