Red_Dragon has asked for the wisdom of the Perl Monks concerning the following question:

Learned Monks,

In Perl, when doing SQL inserts, I set a value upon error. I would like to change the execute portion(line 4 after the OR in the code sample) to something that actually captures the error text issued by an insert failure. How do I code that, within the framework of the code sample below:

$sqlwi="INSERT INTO $ICT (ict_category,ict_type,ict_subtype,ict_instance,ict_assign2_grp,ict_severity_lvl,ict_date)
VALUES('$a_catg','$a_type','$a_styp','$a_inst','$a_agrp','$a_sevl','$dat3')";

my $sthwi=$dbh->prepare($sqlwi) or $emsg = "prepareWI failed, inserting $ICT";
if (!$emsg) {$sthwi->execute() or $emsg = "executeWI failed, inserting $ICT"}
$sthwi->finish;
if ($emsg) {&exit_now} # call exit routine

Thank you for any guidance you can provide.

Replies are listed 'Best First'.
Re: Capturing SQL errors in Perl
by pc88mxer (Vicar) on Apr 17, 2008 at 14:53 UTC
    $dbh->errstr should give you what you want.

    As moritz suggested, using exceptions can simplify your logic:

    $dbh->{RaiseError} = 1; # only need to do this once ... eval { my $sth = $dbh->prepare(...); $sth->execute(...); }; if ($@) { # unable to insert, error message is in $@ ... }
    Also, please use placeholders!
Re: Capturing SQL errors in Perl
by moritz (Cardinal) on Apr 17, 2008 at 14:51 UTC
    Why don't you simply use the RaiseError => 1 option in DBI::connect?
Re: Capturing SQL errors in Perl
by Akoya (Scribe) on Apr 17, 2008 at 15:32 UTC

    There are a couple of things that I would recommend:

    1. First, when posting code in your question, please wrap it in <code> ... </code> tags, so that it is easier to read.
    2. Second, it is generally a best practice to use placeholders in your query, and pass the values as arguments.

    See What are placeholders in DBI, and why would I want to use them? for more information about placeholders.


    There are 10 types of people in this world...those who grok binary, and those who don't.
Re: Capturing SQL errors in Perl
by Narveson (Chaplain) on Apr 17, 2008 at 15:54 UTC

    Concurring with the RaiseError, placeholder, and code tag suggestions, let me also note that there's no need to call finish on DML statements (inserts and updates).

    Here's what your code will look like if you follow the advice above:

    my @fields = qw( ict_category ict_type ict_subtype ict_instance ict_assign2_grp ict_severity_lvl ict_date ); my @placeholders = map {'?'} @fields; { local $" = ','; $sqlwi = "INSERT INTO $ICT (@fields) VALUES(@placeholders)"; } $dbh->{RaiseError} = 1; eval { my $sth = $dbh->prepare($sqlwi); $sth->execute( $a_catg, $a_type, $a_styp, $a_inst, $a_agrp, $a_sevl, $dat3, ); }; if ($@) { # unable to insert, error message is in $@ ... }
      Thank you for investing in me, this example is most helpful, I appreciate it.
Re: Capturing SQL errors in Perl
by olus (Curate) on Apr 17, 2008 at 16:21 UTC

    Aside from the best practices that have already been suggested, and that you should follow, there is one thing that at first glance indicate a point of failure. You are trying to build your query based on the values that those $a_* variables have. But you are passing them inside single quotes, and what goes inside single quotes is not interpolated. So, what you are really trying to insert is what you actually can read on that line of code, not the values of the variables.

    Also, instead of the custom error message, use the one DBI gives you with DBI->errtsr

    update: Thanks Crackers2 and Narveson, you are absolutely right. Please ignore this post.

      You are trying to build your query based on the values that those $a_* variables have. But you are passing them inside single quotes, and what goes inside single quotes is not interpolated.

      No, only the outermost quotes determine whether interpolation will happen or not. Since those are double quotes it will work fine. Minimal example:

      my $var = test'; my $result = "This is a '$var' again"; print "$result\n";

      $var will be interpolated and This is a 'test' again will be printed.

      But you are passing them inside single quotes, and what goes inside single quotes is not interpolated.

      Wrong. The single quotes are inside a double-quoted string.

      The original version is not best practice, but it won't fail until one of the interpolated variables contains an embedded quote.

      my $name = 'Larry'; my $sql = "Insert into Users (name) values ('$name')"; print $sql; # Insert into Users (name) values ('Larry')