I've found several similar questions but none of them seem to explain what's happening here.
Code:
print type(price)
print type(sale_price)
print type(url)
price=0.0
sale_price=0.0
url="asdf"
dbc.execute("UPDATE logTable SET price=%f, salePrice=%f, modified=NOW() WHERE url=%s", [price, sale_price, url])
Output:
<type 'float'>
<type 'float'>
<type 'str'>
Traceback (most recent call last):
File "./scrape.py", line 102, in <module>
dbc.execute("UPDATE logTable SET price=%f, salePrice=%f, modified=NOW() WHERE url=%s", [price, sale_price, url])
File "/usr/lib/python2.7/dist-packages/MySQLdb/cursors.py", line 210, in execute
query = query % args
TypeError: float argument required, not str
There aren't stray % characters in the interpolated string. The number of arguments matches the number of placeholders. The types sure seem to be correct.
I tried using ? placeholders instead of printf-style ones, even though docs suggest both are supported. That gives a different exception:
Traceback (most recent call last):
File "./scrape.py", line 102, in <module>
dbc.execute("UPDATE logTable SET price=?, salePrice=?, modified=NOW() WHERE url=?", [price, sale_price, url])
File "/usr/lib/python2.7/dist-packages/MySQLdb/cursors.py", line 210, in execute
query = query % args
TypeError: not all arguments converted during string formatting
That exception also yields plenty of StackOverflow answers but none really shed light on what's going wrong here. I did try using a tuple instead of a list as the second argument to execute with both style placeholders but still get the same exceptions.
1 Answer 1
Your question seems to indicate that there is anything wrong here. This is not the case.
As per the Python database specification, a concrete database library is free to use one of several placeholders for the parameters of a (real or pseudo) prepared statement. Normally, the choice is made with respect to the most simple way to solve the task.
Libraries which use real prepared statements can use the ? placeholders. This allows them to pass the statement directly to the prepared statement engine of the interface, because these usually use ? for the data to be placed into the query.
Other libraries, such as MySQLdb, don't use prepared statements, but another (more or less) safe method to create the queries resp. to embed the data into the statements. In this case, %s (and only %s and no other patterns) can be used. In these cases, the library transforms the given data into an appropriate replacement: strings are enclosed in ' characters and the content is treated appropriately, as would the C function mysql_real_escape_string() would do. Other data are transformed differently, such as numbers which are transformed into their respective string representation.
In all cases, strings are created out of these data and then inserted into the queries. That's why %s is used and nothing else (like %f, %d etc.).
3 Comments
%s was used. It always seemed awkward to me considering SQLite - a run-of-the-mill library, I guess - went to lengths to support multiple ways.? and :XYZ are some which are used by some prepared statement libraries, %s is used for libraries which assemble the query on their own.
[price, sale_price, url]? I was too hasty in my initial comment, this seems a bit strange.%sis not the same as regular string interpolation in Python, it's a placeholder. That said, I don't know exactly how it differs to give you a definitive answer. However, there is a PEP that details the acceptable parameter styles that can be used -%sis one of them.