The easiest way to do this is by setting DBI's RaiseError attribute to 1; first connect to the database (and check the return), then set the RaiseError attribute:
my $dbh = DBI->connect('foo', 'bar', 'baz', 'mysql') or die "Can't connect: ", $DBI::errstr; $dbh->{RaiseError} = 1;
The other way to check for errors, of course, is to check the return of each method call, eg.:
my $sth = $dbh->prepare("select id from foo") or die "Can't prepare: ", $dbh->errstr; $sth->execute or die "Can't execute: ", $dbh->errstr;
And, for the same reasons, you should use prepare_cached instead of prepare.
bind_columns binds Perl variables to columns returned from your SELECT statement. For example, if you had the following SQL statement:
select id, name, phone from people
my($id, $name, $phone); $sth->bind_columns(undef, \$id, \$name, \$phone);
After you've called execute on your statement handle, you'll want to fetch the data. To do this, use the fetch method, which fetches the next row and returns an array reference holding the field values. But you don't need to use that array reference, because you've got the column values bound to Perl variables, and you can just use those directly:
while ($sth->fetch) { print join("\t", $id, $name, $phone), "\n"; }
# Connect to the database and set the RaiseError # attribute so that any database error will # cause a die my $dbh = DBI->connect('foo', 'bar', 'baz', 'mysql') or die "Can't connect: ", $DBI::errstr; $dbh->{RaiseError} = 1; # Setup our statement handle using a placeholder my $sth = $dbh->prepare_cached(<<SQL); select id, name, phone from people where birth_month = ? SQL my @months = qw/January February March April May June July August September October November December/; # Bind Perl variables to columns returned # from SELECT my($id, $name, $phone); $sth->bind_columns(undef, \$id, \$name, \$phone); for my $month (@months) { print "People born in $month:\n"; # Execute the statement for this $month $sth->execute($month); # Fetch each row and print out the values while ($sth->fetch) { print "\t", join("\t", $id, $name, $phone), "\n"; } } $sth->finish; $dbh->disconnect;
If you're going to use DBI with mod_perl, you'll want to make sure that your database transactions are fast, as well (because you don't want the database being the bottleneck). The easiest way to accomplish this is to use the Apache::DBI module, which gives your programs persistent database connections. It does so by overriding the DBI connect method and keeping a cache of open database handles. One of the nicest things about Apache::DBI is that you won't have to modify your existing DBI code to use it. Just add
use Apache::DBI;
use DBI;
Very tasty intro. I have been doing some ugly PostgreSQL/ Perl CGI stuff at work, but I am working on prettying it up and modularizing it. Hopefully, I will be putting some of it up on perlmonks.
My way is similar but not the same (I include just the different part):
my $sth = $dbh->prepare_cached(<<SQL); select id, name, phone from people where birth_month = ? SQL for my $month (@months) { print "People born in $month:\n"; my($id, $name, $phone); # Execute the statement for this $month $sth->execute($month); $sth->bind_columns(\$id, \$name, \$phone); # Also valid: $sth->bind_columns(\($id, $name, $phone)); # Fetch each row and print out the values while ($sth->fetch) { print "\t", join("\t", $id, $name, $phone), "\n"; } }
As for the 4 arguments to bind_columns, and the first argument being undef... I believe this is an anachronism of DBI. I just looked at the manpage, and you're right that it seems to only expect 3 arguments, now.
In the past, though, I *think* the first argument to bind_columns was supposed to be a hash ref. So I got into the habit of using undef as the first arg, because I never cared about passing anything in the hash ref. I can't find that in the DBI manpage anymore, but I did find this:
bind_columns .... For compatibility with old scripts, if the first parameter is undef or a hash reference it will be ignored.
Besides, you can always catch the die in an eval; just make sure that you handle the errors rather than ignoring them.
Michaelwho just became a monk
eval {call_function_that_may_die()}; if ($@) { # This is your error case print "Caught a die saying '$@'\n"; }
Ths is usefull when you have a SQL-statement that can fail, but where you don't want to die because of the failure. (I have used it when dropping temporary tables, that perhaps aren't there and other suchlike tings).
The code looks like this:
my $dbh=DBI->connect(....{RaiseError=>1}) or die... my $sth=$dbh->prepare(...); { local $dbh->{RaiseError} = 0; $sth->execute; if ($sth->Errstr) { # handle the error } } # $dbh->{RaiseError} is back to normal here
my $dbh = DBI->connect("dbi:mysql:foo", "bar", "baz", { AutoCommit => 1, RaiseError => 1}) or die die "Can't connect: ", $DBI::errstr;
RaiseError is set in an anonymous hash - along with other database attributes.
Note that I set AutoCommit state in the connect call. This is highly recommended, and will become mandatory in a coming DBI release. The reason is that the ODBC spec (which DBI follows as closely as possible) has AutoCommit=>1 as default, while many (most?) major database systems have AutoCommit=>0 as default. This has lead to much confusion and tearing of hairs in the past, as things were commited unintentionally.
A great article, but this line could potentially be misleading.
The Apache::DBI module is completely transparent; so you don't need to touch your scripts for any reason. Where Apache::DBI actually is defined, is in Apache's configuration file (httpd.conf by default).
... # preload these modules PerlModule Apache::Registry; PerlModule CGI; PerlModule Apache::DBI; PerlModule DBI; # startup script PerlRequire /path/to/startup.pl ...
If you have a lot of modules to preload, or to use Perl syntax as illustrated above by bttrot, you can define them in the startup script instead. The rule about Apache::DBI going before any other DBI modules still applies.
Just thought I'd clear that up for anyone who read it to mean, "Add use Apache::DBI; to your CGI scripts."
$sth->bind_columns(\my ($id, $name, $phone));
Choosing prepare or prepare_cached really depends. The caching is a slight overhead, so if I can reasonably arrange my SQL so that something is only prepared once (or if its used just once anyway), then I'll just use 'prepare'. Or if you're dynamically creating a SQL statement (lets say a million times) and the number of possible unique combinations is large, then you don't want to use prepare_cached, e.g. you have a couple of 'IN' clauses, and each one might have 1-100 elements, so you end up with something like:
select stuff from table where field1 in (?,?,?,?) and field2 in (?,?,?,?,?)
Don't use prepare_cached() with DBD::Sybase - this would open multiple connections to your server (one for each cached statement handle). If you have a need to have a few requests that you are going to call a *lot* you may want to create stored procedures for them instead.
Michael
btrott mentioned that RaiseError is the recommended way to check for errors but that you could alternatively check the error return value for each DBI method call. Here's subtle case where not using RaiseError can bite you:
On the face of it, this will repeatedly call fetch until there is nothing more to fetch. In fact, fetch will return undef either when all rows have been fetched or when there was an error. Here's a couple of cases where fetch might successfully return a number of rows then return undef for an error:
In each case, you might see some output and mistakenly believe you'd seen all matching rows. The moral - always set RaiseError and wrap DBI calls using eval
package Database; use strict; use warnings; # with or without use Apache::DBI; the result is the same # i also load Apache::DBI in the startup.pl file... use DBI; use DBD::mysql; our (@EXPORT, $dbh); use base qw/Exporter/; @EXPORT = qw($dbh); #BEGIN { print "Content-Type: text/html\n\n"; print "\nopening db connection!\n"; $dbh = DBI->connect('DBI:mysql:dbname', 'user', 'pwd'); #} END { $dbh->disconnect; print "\nshutting down db-conn!\n"; #should not happen... } 1;
#!/usr/bin/perl -w use strict; use warnings; use CGI; use Database; print "Content-type: text/html\n\n"; my $q = new CGI; my %params = $q->Vars; my $userid = delete $params{USER}; my $user = $dbh->selectrow_array("select NAME from USER where ID=?", u +ndef, $userid); print "name of the user: $user!";