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

While I was doing up some webforms for data entry (my most favouritist job ever), I encountered a perplexing problem.

First a bit of functional info, the form in question is a simple insert/update form. If you're adding a new record you don't pass the id (primary key) in the query string and you get a blank form; if you do pass an id the form is filled in from the database. At least that was the idea.

# flakey my $sth = $dbh->prepare_cached(<<SQL); SELECT * FROM foo WHERE id = ? SQL $sth->bind_param(1, $q->param('id'), SQL_INTEGER); $sth->execute;

So when there's no query parameter 'id' it's undefined/null so there should be an empty result set returned. Right? Wrong. Somehow my form kept filling in with the same information whenever I didn't specifiy an id. I checked typos, db records, and simplified my SQL down to a SELECT *.

After a while I concluded it somehow had to be in the SQL execution. So I tried the following statement and, lo and behold, it worked perfectly.

# functional my $sth = $dbh->prepare_cached(<<SQL); SELECT * FROM foo WHERE id = ? SQL $sth->execute( $q->param('id') );

So it had something to do with how I was binding the value. And for some reason the 'flakey' code kept returning the fourth record. I was at a complete loss to explain it, until I remebered what SQL_INTEGER mapped to. SQL_INTEGER is an imported constant from DBI and numerically equals, you guessed it, the number 4.

My argument to bind_param() then was (1, ,4) which it interpretted as (1,4)! Not the behavior I'd expect from Perl. Manually specifying (1,undef,4) in the bind_param() properly returned an empty set. So what was going on?

My question's not really asking how to solve it: I've already altered my procedure a bit and am going along happily. My question is: Is my original method exposing a flaw in my fundamental understanding of Perl's undef/null/empty string/etc. or is this something I should mention to the maintainers of DBI?

Thanks,
         Arguile

Replies are listed 'Best First'.
Re: DBI Binding Inconsistancy
by wog (Curate) on Sep 16, 2001 at 19:00 UTC
    What's happening is that the $param method is returning a null list in this case, not undef. This null list results in nothing being passed to bind_param from $q->param('id'). The reason that it's doing this is that it's being called in list context being in an argument list, and thus CGI.pm thinks it needs to return a (possibly empty) list. You should be able to fix this by explictly specifying scalar context with something like scalar $q->param('id').
      *Aguile blushes very ruddily*

      Adding the scalar immediately fixed it. I completely forgot about context in this case, even though I used $->param('id') in list context for the delete template. That'll teach me for not programming Perl for a few months.

      Thanks alot. :)
Re: DBI Binding Inconsistancy
by greywolf (Priest) on Sep 16, 2001 at 21:10 UTC
    If you do not want any SQL results (empty form) you should not perform the SQL query.
    if ($q->param('id')) { # perform sql query }
    mr greywolf

      I have to agree with greywolf. IMO, it is conceptionally clearer (and, thus, easier to maintain) to explicitly conditionalize the execution of the query on whether or not you actually want a result (ie, have an id). It also saves load on the DB, which might not be an issue now but most likely will be at some point in the future.

      bbfu
      Seasons don't fear The Reaper.
      Nor do the wind, the sun, and the rain.
      We can be like they are.

        Normally I'd agree with you both but an empty set != nothing returned. In this case I need information the statement handle returns regardless of the fact there's nothing in the set.

        As far as DB hit it's a precached query only using the primary key's index and I can quite confidently say that DB load isn't and won't be that much of an issue for this application. And I really mean it! Just like computers will never need more than 640k of RAM. Oohh... wait a sec. ;)

        Seriously though, thanks for the suggestions. The small snippet didn't provide enough information to know otherwise.