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

Good afternoon, I am having a problem inserting a line in to a mysql table. Actually the problem is that I can't get perl to give me an error. I am purposely trying to insert data for a non-existant column. So it should kcik back with an error - but it is not. It just not doing it and continuing with the script. It is just skipping over insert line like it didn't exist, even though I have raiseError on. I have traced the script and the only thing I can find is that the line where I am executing the insert says "<- execute= undef". Any ideas how to get this script to stop and give me an error? Here is the summarized code I am using:
use DBI; $dbh = DBI->connect("DBI:mysql:database=blah;host=127.0.0.1", "root", +"****", {'RaiseError' => 1}); DBI->trace(1); $insertline = "INSERT INTO table (column1,column2) VALUES ('value1','v +alue2');"#column2 does NOT exist in the table and should through back + an error $query = $dbh ->prepare($insertline); $query->execute();

Replies are listed 'Best First'.
Re: DBI not erroring out on Insert
by ww (Archbishop) on Sep 10, 2011 at 00:52 UTC
    You are using strict and warnings, aren't you? If not, that'll probably give you a headstart.

    Do you know for sure that the program is skipping over the "insert line?" Have you run it under the debugger?

    And you say you're offering a summary version of the code (which is the right thing to do so long as it exhibits the same problem). Does the original behave just as this does?

    And, oh yes, where are you looking for the error? Are you using a config that could be rerouting <STDERR> to a file or somewhere else unexpected?

        Given the code and the (detail-challenged) problem description, there is a (small) chance that "the summarized code I am using" is:

        1. not the actual code but instead, a summary which masks some error in OP's (presumed) actual code
          or
        2. is a fragment of the actual code.

        In case 1, the error might be revealed to OP at his/her console.

        In case 2 both cases, the fragment doesn't pass perl -c junk3.pl:

        Scalar found where operator expected at F:\_Perl_\pl_test\junk3.pl lin +e 11, near "$query" (Missing semicolon on previous line?) syntax error at F:\_Perl_\pl_test\junk3.pl line 11, near "$query " F:\_Perl_\pl_test\junk3.pl had compilation errors.

        which reveals the missing ";" just before OP's "#" at position (1 based) 82 in what I infer the OP is referring to when saying "It is just skipping over insert line like it didn't exist, "

        Sometimes, an observation or question about these pragmata is not merely boilerplate nor blind kowtowing to conventional wisdom.

        Update: s/case 2/both cases/ since the following statement is equally applicable in both cases.

Re: DBI not erroring out on Insert
by Anonymous Monk on Sep 10, 2011 at 06:02 UTC
Re: DBI not erroring out on Insert
by Marshall (Canon) on Sep 11, 2011 at 20:44 UTC
    I did some test code of my own. This is with SQLite instead of MySQL, but this is so simple that results should be the same.

    The code should fail at the prepare statement (see my test code below). If you have a valid $dbh handle, the prepare simply won't work if in your case column "column2" does not exist, in my case column "abc".

    I would include an "or die" clause on the connect() like I did below because you are not guaranteed to have the 'RaiseError' in effect unless the connect succeeds. Think about it, if you omitted say the user name field, the connect() is going to fail and things won't work so well interpreting the attribute hash as a password string! In other words, don't rely on 'RaiseError' working until you are sure that the statement that set that attribute itself worked! Your code may be going wrong right at the connect!

    I did some tests with and without "use strict" and "use warnings". This made no difference in the error that I got in this simple test. However there are run time aspects to those and the DBI does work with standard $SIG(__WARN__} and $SIG{__DIE__} signal handlers, so I would leave this extra warning stuff on unless there is an unusual performance requirement and even then don't turn it off until after testing is complete.

    #!/usr/bin/perl -w use strict; #result the same without strict or warnings use DBI; my $dbfile = "./SomeDB.sqlite"; my %attr = ( RaiseError => 1); #auto die with error printout my $dbh = DBI->connect("dbi:SQLite:dbname=$dbfile","","",\%attr) or die "Couldn't connect to database: " . DBI->errstr; my $s2 = $dbh->prepare("INSERT INTO logs ('abc') VALUES ('xyzzy')"); $s2->execute(); __END__ DBD::SQLite::db prepare failed: table logs has no column named abc process exits with error code 2 in my environment...
    At the end of the day, a single connect statement and a single prepare statement is enough to demonstrate what happens when an SQL INSERT into a non-existent column (in an existing table, in my case the "logs" table) is attempted.
      I would include an "or die" clause on the connect() like I did below because you are not guaranteed to have the 'RaiseError' in effect unless the connect succeeds.

      Read the source code of DBI's connect method. It will use Carp to croak when RaiseError is in effect if the connection failed.


      Improve your skills with Modern Perl: the free book.

        We are way off the original topic now... I did look at the DBI source code as chromatic suggested... I guess this is the sort of thing where everybody can claim "victory" depending upon their point of view!

        The code in DBI's connect() related to RaiseError:

        unless ( $attr->{HandleError} && $attr->{HandleError}->($msg, $drh, $dbh)) { Carp::croak($msg) if $attr->{RaiseError}; Carp::carp ($msg) if $attr->{PrintError}; }
        Ok, fair enough. If RaiseError is true, then the DBI croaks on an error.

        I was pondering a different question. I was wondering what happens if connect() fails because the arguments supplied are are of wrong number or type - in a more general case, what happens if the arg list is such that the DBI connect cannot recognize that RaiseError is intended to be 'on'? That case is handled by this DBI code:

        Carp::croak('Usage: $class->connect([$dsn [,$user [,$passwd [,\%attr]] +]])') if (ref $old_driver or ($attr and not ref $attr) or ref $pass);
        So I guess a more accurate description of what the DBI does: The DBI will croak no matter what the state of RaiseError if it detects an invalid argument list to connect(). I also found out through testing that if you misspell "Raiseerror", the DBI also croaks - it will croak for any syntax error that is detected (the spec doesn't say this, but the code does it).

        I'm testing right now with SQLite which doesn't have users or passwords so there are scenarios with MySQL that I am unable to test.

        At the end of the day, if I omit the "or die" clause to a connect (which also specifies the RaiseError attribute), then I would have to be 100% sure that the "or die" code can never be executed under any circumstance. I can't prove that and that is certainly not part of the public specification.

        Now I know that the "hey Mom, Jimmy's mom let's him to X" (the other guys do it defense) is pretty weak, but I learned my "bad" habit about this from Programming the Perl DBI" by Alligator Descartes and Tim Bunce.

        I think that I am on very solid ground with my current practice and recommendations.

        Back to original question in the original post!!!
        I still have not found any way at all for a prepare of the INSERT statement to succeed where one of the columns does not exist in the table.

      However there are run time aspects to those and the DBI does work with standard $SIG(__WARN__} and $SIG{__DIE__} signal handlers

      Both strict and warnings are lexical pragmas. If enabling them in this test program causes DBI to behave differently, either DBI or the Perl 5 core has a strange bug.

      Improve your skills with Modern Perl: the free book.

        I think we are getting off of the issue of the OP (Original Post). I was a bit sloppy in terminology. Ok.

        I could not find any difference in behavior in the 2 lines of code that are relevant to OP's post as regards to: "-w" vs "use warnings;".

        If we want to get into exactly how the "-w command line option" and "use warnings;" differ, then I would suggest that a new thread get started. For folks who want to know more, here are a couple of links: -w vs use warnings and use warnings;.

        I think that we are getting "off topic".
        My main point is that the attribute "RaiseError" does work and I suspect that it didn't because the statement that set that did not work. I have been unable to find any other scenario in my testing.

        Now of course there could be a situation that I haven't found, but I'm reporting 2 lines of code and asking the OP to run my code. The "-w" or "use warnings;" issue is minor and does not affect my test results; having said that, I test all my code with the command line -w version.