Come posso evitare che il programma si chiuda quando il DBI di Perl rileva un errore durante la preparazione di un'istruzione?
Domanda
Sto realizzando uno script che passa attraverso una tabella che contiene tutti gli altri nomi di tabella nel database. Mentre analizza ogni riga, controlla se la tabella è vuota di
select count(*) cnt from $table_name
Alcune tabelle non esistono più nello schema e se lo faccio
select count(*)
direttamente nel prompt dei comandi, restituisce l'errore:
206: La tabella specificata (adm_rpt_rec) non è nel database.
Quando lo eseguo dall'interno di Perl, lo aggiunge all'inizio:
Preparazione DBD :: Informix :: db non riuscita: SQL: -
Come posso evitare che il programma si chiuda quando tenta di preparare questa istruzione SQL?
Soluzione
Codice di lavoro - supponendo che tu abbia un database 'store'.
#!/bin/perl -w
use strict;
use DBI;
my $dbh = DBI->connect('dbi:Informix:stores','','',
{RaiseError=>0,PrintError=>1}) or die;
$dbh->do("create temp table tlist(tname varchar(128) not null) with no log");
$dbh->do("insert into tlist values('systables')");
$dbh->do("insert into tlist values('syzygy')");
my $sth = $dbh->prepare("select tname from tlist");
$sth->execute;
while (my($tabname) = $sth->fetchrow_array)
{
my $sql = "select count(*) cnt from $tabname";
my $st2 = $dbh->prepare($sql);
if ($st2)
{
$st2->execute;
if (my($num) = $st2->fetchrow_array)
{
print "$tabname: $num\n";
}
else
{
print "$tabname: error - missing?\n";
}
}
}
$sth->finish;
$dbh->disconnect;
print "Done - finished under control.\n";
Output dall'esecuzione del codice sopra.
systables: 72
DBD::Informix::db prepare failed: SQL: -206: The specified table (syzygy) is not in the database.
ISAM: -111: ISAM error: no record found. at xx.pl line 14.
Done - finished under control.
Questo ha stampato l'errore ( PrintError = > 1
), ma è continuato. Modificare 1 su 0 e non viene visualizzato alcun errore. Le parentesi nelle dichiarazioni di $ tabname
e $ num
sono cruciali: contesto di array vs contesto scalare.
Altri suggerimenti
Un'opzione è di non usare RaiseError = > 1 durante la costruzione di $ dbh. L'altro è avvolgere la preparazione in un blocco di valutazione.
Basta inserire le chiamate che potrebbero fallire in un blocco eval come questo:
for my $table (@tables) {
my $count;
eval {
($count) = $dbi->selectrow_array("select count(*) from $table");
1; #this is here so the block returns true if it succeeds
} or do {
warn $@;
next;
}
print "$table has $count rows\n";
}
Anche se, in questo caso, poiché stai utilizzando Informix, hai un'opzione molto migliore: le tabelle del catalogo di sistema. Informix mantiene i metadati in questo modo in una serie di tabelle del catalogo di sistema. In questo caso vuoi systables:
my $sth = $dbh->prepare("select nrows from systables where tabname = ?");
for my $table (@tables) {
$sth->execute($table);
my ($count) = $sth->fetchrow_array;
$sth->finish;
unless (defined $count) {
print "$table does not exist\n";
next;
}
print "$table has $count rows\n";
}
Questo è più veloce e sicuro del count (*)
rispetto alla tabella. La documentazione completa delle tabelle del catalogo di sistema è disponibile in IBM Informix Guide to SQL (attenzione questo è un PDF).