in reply to Re: Perl Not returning SQL query result
in thread Perl Not returning SQL query result

my $dbh = DBI->connect("dbi:SQLite:dbname=$dbfile","","",{RaiseError = +> 1}) or die "Couldn't connect to database: " . DBI->errstr;

If $dbh is zero, then the connect failed!

Um, no. If $dbh ever becomes 0 in that two lines, something very unexpected has happened in perl, i.e. you have triggered a serious bug and/or corrupted memory. (Or you simply have redefined die to return 0 instead of dieing.)

Yes, I'm bean counting again. Sorry.

First, DBI->connect() is documented to return undef on error, a database handle object if the connection succeeds. It should never return 0 at all, or anything else than undef or a blessed reference (i.e. an object).

Second, 0 and undef are two very different values, and while they are treated the same on the left-hand side of or (as a boolean FALSE value), they are not generally exchangeable.

Side note: Around DBI, there is also the special zero value 0E0 that is treated by perl as a boolean TRUE value, returned by execute() and similar methods (do(), execute_array(), execute_for_fetch()) to indicate success with zero rows affected. Some of the core functions (fcntl, ioctl, msgctl, semctl, shmctl, sysseek) return the special value 0 but true for a very similar purpose. That special value is excempt from warning complaints about improper numeric conversions (documented in ioctl).

Third, should DBI->connect() return undef, 0 or any other value that evaluates to FALSE in boolean context, the right-hand side of or will be executed and die will prevent any assignment to $dbh by either exiting the interpreter or by jumping out of a surrounding eval block. So, $dbh starts unassigned, but will never be assigned 0 (unless you have redefined die to return 0).

Alexander

--
Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)

Replies are listed 'Best First'.
Re^3: Perl Not returning SQL query result
by Marshall (Canon) on May 12, 2021 at 22:09 UTC
    Yes, I'm bean counting again. Sorry. No worries!...Let's get the details right! These threads have a way of relentlessly getting the exact details right.

    Ok, the code that I showed will work. 0 and undef are treated the same in this logic, but I did misspeak. I accept your point! My post updated. I was correct in saying, "I may have made a mistake which the other Monks will quickly point out."

    Yes, the string "0 but true" is hard coded into Perl from ancient times and will evaluate to 0 when used in a numeric context (and no warning will be issued, even if use warnings; is in effect).

    The DBI doesn't use that nowadays. The DBI will return the string "0E0" which evaluates to "True" in a logical context (it is a non-null string), but zero in a numeric context. Yes, this is the DBI's version of "true, but zero rows affected." This value cannot be returned by a SELECT SQL statement, but can be returned by say an ALTER.

    In the code shown, if the SELECT does not return any data, the array_ref will be a reference to an array with zero elements. There was another thread recently where this was discussed at length and the specific point that the returned scalar value from execute() won't work for a SELECT.

    I am not quite sure about your Third point. If the DB connect fails, this code will DIE. The idea of an eval{} block was not under consideration here. The OP was asking some very basic questions and I tried (perhaps not successfully) to give a simple "roadmap".

    But yes, in the context of an Oracle DB with username and password, there should be some way to recover and get better credentials. In the SQLite world, there is no username or password. Connect will fail if 1) wrong file name, 2)wrong path to that file name, or 3)wrong permissions or perhaps 4) corrupt DB file. I copied my connect code from a program that is in its 6th year of production. In this code, the user doesn't have anything to say about things. If the code that this is in bombs, there is a serious problem that my Perl code cannot fix.

    Update: SQLite is an amazing critter. It generates some journal files as it works. It is possible for that thing to recover itself to a known ACID state even after a serious event like power fail. I have never personally seen (4) corrupt DB file because SQLite is much better at recovery than you might think. But I have communicated with others who have seen this.

    https://en.wikipedia.org/wiki/ACID

      I am not quite sure about your Third point. If the DB connect fails, this code will DIE. The idea of an eval{} block was not under consideration here. The OP was asking some very basic questions and I tried (perhaps not successfully) to give a simple "roadmap".

      Well, once I start bean-counting, it's hard to stop. ;-)

      C:\Users\alex>perl -Mstrict -w -E "sub false { 0 } my $x=false or die +'Died'; say 'not reached';" Died at -e line 1.

      $x will never be assigned, because perl dies before it could assign the result of the or operation. That's what happens in your code, and I could have stopped here.

      BUT, in beancounting mode, there may be an eval around the assignment, not shown in the posting. Yes, this is beyond basic use of perl.

      C:\Users\alex>perl -MWin32 -E "sub false { 0 } eval { my $x=false or d +ie 'Died'; say 'not reached'; 1 } or Win32::MsgBox(qq[$@]);"

      That example aborts, but the last thing it does before is to display a message box contaning "Died at -e line 1.". That does not make much sense when hacking a one-liner in a console window. But it becomes really useful when perl is started by something like a self-extracting archive or simply a non-console application. Without that messagebox, you would just see a console window flashing into existance and disappear again, and you had no chance to see a useful error message. I used that trick almost two decades ago, see Re^3: How do you distribute your Perl application to your customers?.

      C:\Users\alex>perl -Mstrict -w -E "BEGIN { *CORE::GLOBAL::die=sub { 'I + am evil' } } sub false { 0 } my $x=false or die 'Died'; say 'not rea +ched';" not reached

      And that silly example demonstrates that one can redefine die so it does not die. Just to make people really, really angry.


      A little extra:

      Quick, don't think, just answer for yourself: What does that example print out?

      C:\Users\alex>perl -Mstrict -w -E "BEGIN { *CORE::GLOBAL::die=sub { 'I + am evil' } } sub false { 0 } my $x=false or die 'Died'; say 'not rea +ched, $x=',$x;"

      Alexander

      --
      Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
        EDIT: after writing all that up and posting, I re-read your final paragraphs, and realized you said basically the same thing.

        So ignore all the original detail in the spoiler

        $x will never be assigned, because perl dies before it could assign the result of the or operation.

        Could you explain or expand on that statement, please? I still cannot figure out why that would be true. If it were the high precedence ||, then I would 100% agree, because perl would evaluate the 0 then then die before evaluating the assignment operator. But or has lower precedence than =, so the assignment should be processed first.

        And I do remember seeing many posts that the compile-time vs run-time effects of my make it more confusing, as is obvious from it not passing strict if I try to use the $x in the die without separating the my out. But the conditional my considerations are separate from the assignment considerations, I would think. If I am wrong, then how have I misunderstood the precedence table as published, which seems to me to very clearly say that assignment has higher precedence than or.

        Digging into examples, trying to prove myself wrong, and better explain myself if I am right:

        When I simplify your example farther, but with the my separated from the conditional, I see:

        C:\Users\Peter>perl -Mstrict -le "my $x; $x = 0 or die $x" 0 at -e line 1.

        And this makes sense to me if I look at the B::Deparsed version, too:

        C:\Users\Peter>perl -MO=Deparse -Mstrict -le "my $x; $x = 0 or die $x" BEGIN { $/ = "\n"; $\ = "\n"; } use strict; my $x; die $x unless $x = 0; -e syntax OK
        ... where $x=0 presumably has to be executed before the unless can be decided, which then decides whether or not die gets to run. But die has access to the $x variable with value 0.

        Adding B::Deparse to your example

        C:\Users\Peter>perl -MO=Deparse -Mstrict -w -E "sub false { 0 } my $x= +false or die 'Died'; say 'not reached';" BEGIN { $^W = 1; } use strict; use feature 'current_sub', 'bitwise', 'evalbytes', 'fc', 'postderef_qq +', 'say', 'state', 'switch', 'unicode_strings', 'unicode_eval'; sub false { 0; } die 'Died' unless my $x = false(); say 'not reached'; -e syntax OK
        ... which shows a similar structure to my simplified example, so I don't think I'm completely out of line in my interpretation

        Because of strict, I cannot just do die 'Died',$x in your example. But if I move the my outside the conditional:

        C:\Users\Peter>perl -Mstrict -w -E "sub false { 0 } my $x; $x=false or + die 'Died',$x; say 'not reached';" Died0 at -e line 1.

        The assignment happens; if it didn't, the results would have not had the 0 in the printout, and would have warned about unintialized value, because $x would have been undefined.

        Now, if I change the or to || in your the oneliner with $x in the die, it deparses differently:

        C:\Users\Peter>perl -MO=Deparse -Mstrict -w -E "sub false { 0 } my $x= +false || die 'Died',$x; say 'not reached';" Global symbol "$x" requires explicit package name (did you forget to d +eclare "my $x"?) at -e line 1. -e had compilation errors. BEGIN { $^W = 1; } use strict; use feature 'current_sub', 'bitwise', 'evalbytes', 'fc', 'postderef_qq +', 'say', 'state', 'switch', 'unicode_strings', 'unicode_eval'; sub false { 0; } my $x = false() || die('Died', ${'x'}); say 'not reached'; C:\Users\Peter>perl -MO=Deparse -Mstrict -w -E "sub false { 0 } my $x; + $x=false || die 'Died',$x; say 'not reached';" BEGIN { $^W = 1; } use strict; use feature 'current_sub', 'bitwise', 'evalbytes', 'fc', 'postderef_qq +', 'say', 'state', 'switch', 'unicode_strings', 'unicode_eval'; sub false { 0; } my $x; $x = false() || die('Died', $x); say 'not reached'; -e syntax OK
        ... and there, I would agree that the die occurs before the assignment for ||.

        If I compare the B::Concise's terse output from the or vs || with your example but with the two precendences on the logic (so the my is still attached to the conditional), the sassign happens after the LOGOP with ||, but before the LOGOP with or, which I again interpret to mean that the assignment should happen with or:

        C:\Users\Peter>perl -MO=Concise,-terse,-exec -Mstrict -w -E "sub false + { 0 } my $x=false || die 'Died'; say 'not reached';" OP (0x2a1c6b8) enter COP (0x2a85b00) nextstate OP (0xfdfb00) pushmark PADOP (0xfdfac0) gv IV (0x105fb40) \&main::false UNOP (0x2a85cf0) entersub LOGOP (0x2a85be8) or OP (0x2a85c78) pushmark SVOP (0x2a85cb0) const [4] PV (0x105f618) "Died" LISTOP (0x2a85c30) die [3] OP (0xfdfb40) padsv [1] BINOP (0x2a85b60) sassign COP (0x2a85998) nextstate OP (0x2a85a40) pushmark SVOP (0x2a85a78) const [5] PV (0x105f5b8) "not reached" LISTOP (0x2a859f8) say LISTOP (0x2a85ab8) leave [1] -e syntax OK C:\Users\Peter>perl -MO=Concise,-terse,-exec -Mstrict -w -E "sub false + { 0 } my $x=false or die 'Died'; say 'not reached';" OP (0x2a249d8) enter COP (0x2a76030) nextstate OP (0xfc08e0) pushmark PADOP (0xfc08a0) gv IV (0x103e770) \&main::false UNOP (0x2a76220) entersub OP (0xfc0920) padsv [1] BINOP (0x2a761d8) sassign LOGOP (0x2a760d0) or OP (0x2a76160) pushmark SVOP (0x2a76198) const [4] PV (0x103e1e8) "Died" LISTOP (0x2a76118) die [3] COP (0x2a75ec8) nextstate OP (0x2a75f70) pushmark SVOP (0x2a75fa8) const [5] PV (0x103e248) "not reached" LISTOP (0x2a75f28) say LISTOP (0x2a75fe8) leave [1] -e syntax OK

        I have tried every combination I can think of, and everything I see would seem to indicate that, my notwithstanding, the assignment happens. If your claim was really about the my in the original post, rather than the assignment, I would agree with you; but if it's about the assignment as you phrased it, then so far, I haven't convinced myself.

        But I understand that I am not an expert on the B:: modules' outputs, so maybe I'm misinterpreting things. But I thought I understood the precedence. If I am mistaken, I would appreciate learning where I've misinterpreted things.

        I don't see any big issue here.

        I will mention something that is very useful about die messages. If you put a trailing \n, like "some message\n", that will suppress the details like line number, etc. I write different code for a sysop versus Windows user. For the inexperienced, usually a simple "what can I do about it?" message works best and extra detail just confuses them.