views:

272

answers:

3

I am trying to assemble the following SQL statement using python's db-api:

SELECT x FROM myTable WHERE x LIKE 'BEGINNING_OF_STRING%';

where BEGINNING_OF_STRING should be a python var to be safely filled in through the DB-API. I tried

beginningOfString = 'abc'

cursor.execute('SELECT x FROM myTable WHERE x LIKE '%s%', beginningOfString) 
cursor.execute('SELECT x FROM myTable WHERE x LIKE '%s%%', beginningOfString)

I am out of ideas; what is the correct way to do this?

A: 

EDIT:

As Brian and Thomas noted, the far better way to do this would be to use:

beginningOfString += '%'
cursor.execute("SELECT x FROM myTable WHERE x LIKE ?", (beginningOfString,) )

since the first method leaves you open to SQL injection attacks.


Left in for history:

Try:

cursor.execute("SELECT x FROM myTable WHERE x LIKE '%s%%'" % beginningOfString)
Sean Vieira
-1 `"SELECT x FROM myTable WHERE x LIKE '%s%%'" % "doom' ; drop table x; select '"` **Oops?**
Brian
@Brian; very valid point! I was focusing on the syntax error; I should not have assumed that `beginning` was clean data. Thanks for the catch!
Sean Vieira
-1 removed. You still have a syntax error, though. Use double quotes for your string and you won't need to escape your single quotes.
Brian
Edited to fix the syntax error and the bug that Thomas pointed out.
Sean Vieira
+5  A: 

It's best to separate the parameters from the sql if you can. Then you can let the db module take care of proper quoting of the parameters.

sql='SELECT x FROM myTable WHERE x LIKE %s'
args=[beginningOfString+'%']
cursor.execute(sql,args)
unutbu
@~unutbu: thanks, that nails it. Hadn't crossed my mind to simply append the % to the string itself *before* passing it as an arg through the db-api.
laramichaels
A: 

Take note of Sqlite3 documentation:

Usually your SQL operations will need to use values from Python variables. You shouldn’t assemble your query using Python’s string operations because doing so is insecure; it makes your program vulnerable to an SQL injection attack.

Instead, use the DB-API’s parameter substitution. Put ? as a placeholder wherever you want to use a value, and then provide a tuple of values as the second argument to the cursor’s execute() method. (Other database modules may use a different placeholder, such as %s or :1.) For example:

# Never do this -- insecure!
symbol = 'IBM'
c.execute("... where symbol = '%s'" % symbol)

# Do this instead
t = (symbol,)
c.execute('select * from stocks where symbol=?', t)

# Larger example
for t in [('2006-03-28', 'BUY', 'IBM', 1000, 45.00),
          ('2006-04-05', 'BUY', 'MSOFT', 1000, 72.00),
          ('2006-04-06', 'SELL', 'IBM', 500, 53.00),
         ]:
    c.execute('insert into stocks values (?,?,?,?,?)', t)

I think you want this:

cursor.execute('SELECT x FROM myTable WHERE x LIKE '%?%', (beginningOfString,) )
Brian
Nope, that will do the wrong thing.
Thomas Wouters
@Thomas: Why? Other than the fact that I used `'%?%'` instead of `'?%'` (He was not fully consistent in his query which he wanted), I don't see a problem.
Brian
The problem is that the ? gets replaced by the *quoted* version of the actual argument you pass. So given, for instance "';DROP TABLE x;SELECT '" (notice the single *and* double quotes there), you end up with this obviously bad query: SELECT x FROM myTable WHERE x LIKE '%';DROP TABLE x; SELECT '%'
Thomas Wouters
I don't follow. Doesn't DB-API's parameter substitution fix that?
Brian