in reply to Backslashes with DBI quote( ) and MySQL

Escaping is not done to put lovely backslashes in the database. Escaping is just to make sure the data gets in the database correctly. SQL uses quotes to delimit text, so they need to be escaped when the data contains them, and you of course need to escape the character you use to escape other things with.

With INSERT INTO foo VALUES ('bar\'baz') the string bar'baz is stored, without the backslash. It's like saying print "bar\"baz", which prints only bar"baz, again without the backslash itself.

Many, many PHP programmers are confused about escaping and quoting, because PHP quotes strings for you by default. That is, if you have the user input bar'baz, the actual string content will be bar\'baz, which is terribly wrong and confusing. (Blame PHP.) Some then read mysql can't handle single quotes, because they're used as delimiter and start escaping the quotes and backslashes, turning the string into bar\\\'baz, and with 'bar\\\'baz' in an SQL query, bar\'baz is stored in the database. This is where a lot of web programmers using PHP go wrong. They have escaped data in their database, which is very confusing, and a waste of space.

DBI has a nice escaping mechanism. Instead of escaping everything that could be dangerous, only that what is dangerous is escaped.

If you have the user input bar'baz in Perl, your string will have those 7 characters, and no disgusting automatic escaping. That means you will have to arrange for the backslash to be there yourself. You could do it the hard and potentially dangerous way, by using quotemeta or some s///-construct, but DBI has a database-specific method called quote, which is handled internally if you use placeholders. If you use that, the data is turned into bar\'baz for usage in an SQL query. In the query, there will be 'bar\'baz', so the data stored in the database is bar'baz. That also means you do not have to un-escape what you get out of the database. (Again PHP confuses people by having a function that un-escapes automatically. If with SQL everthing is done the way it's supposed to be done, you never have to unescape, because there are no escapes in the database!)

The following script will create a test2 table in the test database, and insert and immediately select the data that follows __DATA__ (through the magic DATA filehandle). It will then report whether the data has changed or not:

#!/usr/bin/perl -w use strict; use DBI; my $dbh = DBI->connect('DBI:mysql:database=test', 'root', ''); $dbh->do('drop table if exists test2'); $dbh->do('create table test2 (xyzzy blob)'); undef $/; # Slurp mode my $before = <DATA>; $dbh -> prepare('insert into test2 values( ? )') -> execute($before); my $sth = $dbh -> prepare('select * from test2'); $sth -> execute; my ($after) = $sth -> fetchrow_array; print $before eq $after ? "Data has not changed\n" : "Data has changed!!\n"; __DATA__ \ = backslash \\ = two backslashes \\\ = three backslashes ' = a single quote '' = two single quotes \' = a backslash and a single quote " = a double quote \" = a backslash and a double quote '"' = a double quote within 2 single quotes


(I experimented a little with style in here. When you read everything that is indented by one level, you read only the program flow. I haven't used this style before, but I kind of like it. Please let me know what you think about having the methods indented like this.)

HTH

++ vs lbh qrpbqrq guvf hfvat n ge va Crey :)
Nabgure bar vs lbh qvq fb jvgubhg ernqvat n znahny svefg.
-- vs lbh hfrq OFQ pnrfne ;)
    - Whreq

Replies are listed 'Best First'.
Re: Re: Backslashes with DBI quote( ) and MySQL
by gmax (Abbot) on Mar 07, 2002 at 07:53 UTC
    Just a stylistic comment, since you asked.
    Personally, I don't like the visual impact of methods indented as you did, although I have to admit that it is easier to identify them. Maybe it's a matter of getting used to it.

    I have three more stylistic remarks about your script, though.
    1. Your connect statement is not something a sensible administrator would recommend. Leaving the 'root' user without a password is calling for trouble. That's why I always write my examples referring to a configuration script or using a bogus password. The readers can't use your script as it is, unless they have a configuration file. So, since they have to change it anyway, I think it's better to show them the best practice.
    2. You should either set RaiseError in the connect or deal with the errors after each call to a DBI method.
    3. You should call $dbh->disconnect at the end of the script.
    Cheers
    _ _ _ _ (_|| | |(_|>< _|