Get a list of all Oracle data sources via DBI - perl

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";
}

Related

DBIx:Class - cannot find source for model

I am trying to use DBIx:Class. I have successfully created the Schema class using DBIx:class::Schema::Loader.
I can also connect to the database.
#!/usr/bin/perl -w
use Test::More tests => 5;
use_ok('Models::ModelRole');
use_ok('Models::User');
my $model = Models::User->new();
cmp_ok($model->{ModelName}, 'eq', 'User', 'model name');
ok($model->connect(), "connect"); #works
ok($model->{schema}->resultset('User'));
The last test returns the error message:
DBIx::Class::Schema::source(): Can't find source for User at ./tests/ModelsTests.pl line 29
This is the structure of the generated class from DBIx:Class::Schema::Loader:
This is the model user class:
package Models::User;
use DB::Glued::Schema::Result::User;
use Models::ModelRole;
use Moose;
with 'Models::ModelRole';
sub BUILD {
my $self = shift;
$self->{schema} = Glued::Schema::Result::User->new();
my #name = split('::', __PACKAGE__);
$self->{ModelName} = $name[-1];
}
1;
I hope this is enough information.
Schemata/models have to be connected to a source. The DBIC code is only describing the data and its relationships. It's entirely agnostic about the source/connection.
So you must connect DB::Glued::Schema to be able to exercise the model. The best way for tests, I think, is to connect to an in :memory: SQLite DB. The DB will be empty of course. There are a few options/approaches for populating it if you need fixtures. Search metacpan if you do.
There is a nice package to make test connections simple for you: Test::DBIx::Class.

Connecting to multiple databases in a perl script

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.

Perl, CGI::Session 4.35, "Don't know where to store the id" error

I am writing a web application that needs to use Session capability. I strictly only have access to module CGI::Session version 4.35
(backpan archive, closest version on CPAN).
When trying to use the module, specifically running this statement to create a new session (or retrieving the previous session):
use CGI::Session ('-ip_match');
…
$session = CGI::Session->new("id:incr", undef, {Directory => '/tmp'})
I'm having this error which I can't seem to find anywhere else:
Don't know where to store the id at (some_location)/x86-32.linux.2.6/5.8/lib/perl5/CGI/Session.pm line 79\n
I sense that this error is caused within the module itself, not from my code. Could you please confirm this?
You're trying to use CGI::Session::ID::incr and that module requires an IDFile argument so that it knows where to store the ID data.
use CGI::Session ( '-ip_match' );
...
$session = CGI::Session->new("id:incr",undef,
{Directory=>'/tmp',IDFile => '/tmp/id.file'});

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.