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

I have a quick question about a DBI/DBH. I pull in two fields from the user from a form: LastName and Birthdate...I have all the validation completed for birthday, but now I'm having problems with the lastname information. Basically if a users last name is say O'Tool my SQL statement appears to DIE because of the extra ' in the persons lastname. Is there anything I can do, to take "O'Tool" without thinking the ' is apart of the sql? Here is my statement:
my $yi = $dbh->prepare(" SELECT SSN, RTRIM(LAST_NAME,' '), TO_CHAR(BIRTH_DATE,'YYYY'), STORE FROM PYHADMIN.PY_EMP_SHADOW where TO_CHAR(BIRTH_DATE,'YYYY') like '$year' and SSN like '%$ssn' and RTRIM(LAST_NAME,' ') like upper('$name') and rownum <= 1 "); $yi->execute();

Replies are listed 'Best First'.
Re: Quick 'Quote' DBI Question
by Herkum (Parson) on Apr 06, 2007 at 13:20 UTC

    Try this

    $ssn = q{%} . $ssn; my $sql = <<"END" SELECT SSN, RTRIM(LAST_NAME,' '), TO_CHAR(BIRTH_DATE,'YYYY'), STORE FROM PYHADMIN.PY_EMP_SHADOW where TO_CHAR(BIRTH_DATE,'YYYY') like ? and SSN like ? and RTRIM(LAST_NAME,' ') like upper(?) and rownum <= 1 END ; my $yi = $dbh->prepare($sql); $yi->execute($year, $ssn, $name );
      Definitely always use placeholders; even more, as the OP mentions an ORA error - sign that Oracle is the underlying DB - I'd go with something like the following:
      my ($db_ssn, $db_lastname, $db_birth_year, $db_store); my $sql = <<'END_SQL'; SELECT ssn, RTRIM(last_name,''),TO_CHAR(birth_date,'YYYY'),store FROM pyhadmin.py_emp_shadow WHERE TO_CHAR(birth_date,'YYYY') LIKE :birth_year AND ssn LIKE :ssn AND RTRIM(last_name,' ') LIKE UPPER(:last_name) AND rownum <= 1 END_SQL eval { my $sth = $dbh->prepare($sql); $sth->bind_param( ':birth_year', $year, ORA_NUMBER ); $sth->bind_param( ':ssn', $ssn, ORA_NUMBER ); $sth->bind_param( ':last_name', $last_name, ORA_VARCHAR2); $sth->execute(); ($db_ssn, $db_lastname, $db_birth_year, $db_store) = $sth->fetchrow_array; $sth->finish(); }; if ($@) { # there was an fatal error; die() if you want, # but maybe you can do something to recover and go on , # log something, etc. }
      Have a look on DBD::Oracle for this special form of placeholders.

      These aren't portable, so avoid them if your code is going to run on other DBs, but as long as you're on Oracle, the code is much easier to read/maintain like this. Also, it is a lot harder to make (typical, sometimes hard to debug) errors like swapping values for the placeholders - think about more complex queries, where you would need to (re)use the same value in more then one place.
      Just to be clear with your example, you are "quoting" SSN? So if I dont need to both SSN rather just quote the lastname, I just change the $SSN values near the top to $name?

        All the variables in the execute() call are being 'quoted' as it were.

        I had to add the '%' to the ssn in order for your wild card search to work.

Re: Quick 'Quote' DBI Question
by derby (Abbot) on Apr 06, 2007 at 12:56 UTC

    There are several ways to handle this - placeholders, DBI's quote method or the q{} operator.

    $name = $dbh->quote( $name );

    -derby
      derby: ++!

      Update: Oops! Herkum beat me to the punch!/Update

      Seconded! You should definitely check out the placeholder syntax, as it fixes these problems for you. Something like the following should work for you:

      my $SQL = "SELECT col1, col2 FROM table WHERE lastname=?"; my $ST = $DB->prepare($SQL) or die; $ST->execute($last_name) or die;
      ...roboticus
Re: Quick 'Quote' DBI Question
by MonkE (Hermit) on Apr 06, 2007 at 13:51 UTC

    Consider what will happen to your query with $name = "; DELETE PY_EMP-SHADOW;" (← deliberately misspelled for your protection). The semicolons will end the original SELECT statement that you started and begin a new, statement that you did not intend. It will delete that important data you are keeping!! A malicious person could take other actions as well. This is called "SQL injection".

    Constructing a SQL query using user-supplied strings is a very bad habit to get into for this reason. Using placeholders (the approach roboticus and others are recommending) is a much better approach. Use it whenever possible. Placeholders also take care of all those nasty problems with quotes and other special characters. If a user enters their name as "Jim; Delete ...", using placeholders, that is precisely what his name will be set to. No wierd side-effects.

      Is the method Herkum is talking about using placeholders? I don't understand these placeholder I guess.
Re: Quick 'Quote' DBI Question
by Trihedralguy (Pilgrim) on Apr 06, 2007 at 13:11 UTC
    I added that line before my $yi and I'm getting this message:
    DBD::Oracle::db prepare failed: ORA-00907: missing right parenthesis (DBD ERROR: OCIStmtExecute/Describe
    Does $name have to be something different after I send it through the DBH quote?
Re: Quick 'Quote' DBI Question
by Trihedralguy (Pilgrim) on Apr 06, 2007 at 13:51 UTC
    Alright, I'm making progress, but I still get an error when I pass names like O'Tool through the $name variable.
    $name = q{%} . $name; my $sql = <<"END" SELECT SSN, RTRIM(LAST_NAME,' '), TO_CHAR(BIRTH_DATE,'YYYY'), STORE FROM PYHADMIN.PY_EMP_SHADOW where TO_CHAR(BIRTH_DATE,'YYYY') like '$year' and SSN like '%$ssn' and RTRIM(LAST_NAME,' ') like upper('$name') and rownum <= 1 END ; my $yi = $dbh->prepare($sql); $yi->execute(); #--------Send the values from the query into variables ($SSNc, $LAST_NAMEc, $YEARc, $storenum) = $yi->fetchrow_array;
    I understand what I did wrong here, placeholders, got it - Whoops.
      Please use Herkum's method - use placeholders.

      As a short introduction, placeholders mean you put a question mark anywhere you'd put (quoted or unquoted) input when you prepare() your sql query, and then supply the values when you execute() the query:

      my $sth = $dbh->prepare("SELECT something FROM something_else WHERE co +l1 = ? OR col2 = ?"); $sth->execute($value1,$value2); while (my ($result) = $sth->fetchrow) { # do something with $result }
      Using placeholder guarantees your values will always be "quoted" correctly, regardless of what's in them.
        What do you mean by $results, where does this value come from? I guess I don't really understand when using placeholders, how do I do a $fetchrow on the values that have already ben executed?