Perl: catch error without die

13,977

Solution 1

Okay, found the solution, apparently I needed __WARN__ instead of __DIE__ and this piece of code needed to be in the top of the file, before where the error was thrown, unlike the example I read stated :)

Solution 2

DBI connect(...) failed: ORA-12154: TNS:could not resolve the connect identifier specified (DBD ERROR: OCIServerAttach) at ...

How would I go about catching this ?

To catch and handle this level of error, use eval in block form, "eval { ... }". This will catch any die that happens in the sub code. If the code within an eval block dies, it will set $@ and the block will return false. If the code does not die, $@ will be set to ''.

Using signal handling via SIG{WARN} and SIG{DIE} is troublesome since they are global, there are also race conditions to consider (what happens if I get a signal while I'm handling a different signal? etc. The traditional issues of signal based computing). You're probably writing single-threaded code, so you're not worried about concurrency issues of multiple things calling die, but there is the user to consider (maybe he'll send a SIGKILL while you're trying to open the DBI connection)

In this specific case, you are using DBI. With DBI, you can control what happens in the case of error, if it should die, warn, or fail silently and wait for you to check the return status.

Here is a basic example of using eval { ... }.

my $dbh = eval { DBI->connect( @args) };
if ( $@ )
{
    #DBI->connect threw an error via die
    if ($@ =~ m/ORA-12154/i )
    {
        #handle this error, so I can clean up and continue
    }
    elsif ( $@ =~ m/SOME \s* other \s* ERROR \s+ string/ix )
    {
       #I can't handle this error, but I can translate it
        die "our internal error code #7";
    }
    else 
    {
      die $@; #re-throw the die
    }
}

There are some minor issues with using eval this way, having to do with the global scope of $@. The Try::Tiny cpan page has a great explanation. Try::Tiny handles a minimal Try/catch block setup and handles localizing $@ and handling the other edge cases.

Solution 3

Include this in your SIG{__DIE__} block:

### Check if exceptions being caught.
return if $^S;

This will prevent your handler from being used on exception-based code that generates a die within an eval block.

Share:
13,977

Related videos on Youtube

Pmarcoen
Author by

Pmarcoen

Updated on April 19, 2022

Comments

  • Pmarcoen
    Pmarcoen about 2 years

    I'm playing around with error handling and got a little problem. I connect with a database using the DBI module.

    I do my own error handling by using a subroutine that I call upon an error.

    I can catch my own dies and handle them just fine but when my database connection fails, the DBI module apparently prints out it's own die :

    DBI connect(...) failed: ORA-12154: TNS:could not resolve the connect identifier specified (DBD ERROR: OCIServerAttach) at ...

    How would I go about catching this ?

    I tried using $SIG{__DIE__} like so :

    local $SIG{__DIE__} = sub {
      my $e = shift;
      print "Error: " .$e;
    };
    

    This is on the bottom of my main file, in this file I also call the connect subroutine that is available in a module of my own. I also tried putting this piece of code on the bottom of my module but it still prints the error without the

    Error:

    in front of it.

  • Ether
    Ether about 14 years
    That's correct, this was a warning and not a death, and the handler must be installed first. You can install it in any module if you wrap it in a BEGIN{}.
  • Pmarcoen
    Pmarcoen about 14 years
    This was the way I had it at first, eval however does not catch warnings which I wanted to report as well. Therefor I must use SIG{WARN}.