在python中将Tuple传递给MySQLdb的问题

时间:2011-04-27 00:42:16

标签: python tuples mysql-python

我有一些sql查询通过python mysqldb工作到我的mysql数据库,但我想让它们少一点sql-injection易受攻击所以很少的bobby表不会尝试添加数据..

例如:

ORIGINAL:
 (这有效,所以ListID等绝对有效)

sql="SELECT NAME FROM ListsTable WHERE ID=%s"%(ListID)  
c.execute(sql)  

与TUPLE一起尝试:

sql="SELECT NAME FROM ListsTable WHERE ID=%s"  
c.execute(sql,(ListID,))  

工作:

sql="SELECT NAME FROM ListsTable WHERE ID=%s"  
c.execute(sql, ListID)  

我不知道为什么第二次尝试不能作为元组工作但是接受它作为单个参数,但是对于另一个语句,我需要传递多个参数,所以这不适用于:

原文:

sql="SELECT * FROM ListsTable ORDER BY ID DESC LIMIT %s,%s"%(Page, (Page*20)+20)  
c.execute(sql)

哪个有效,但是如果我尝试将参数作为元组发送,则不会这样做:

sql="SELECT * FROM ListsTable ORDER BY ID DESC LIMIT %s,%s"  
var1=Page  
var2=(Page*20)+20  
params=(var1,var2)              
c.execute(sql,params)  

甚至只是

sql="SELECT * FROM ListsTable ORDER BY ID DESC LIMIT %s,%s"  
c.execute(sql,(Page,(Page*20)+20))  

我最近在我的网络服务器日志上遇到了这个错误,但是请注意这可能是一个更红的因为我以前没有错过的许多不同的东西:(错误指的是上面的尝试传递“params”变量)

File "process.py", line 98, in main  
c.execute(sql,params)  
File "/var/www/cgi-bin/MySQLdb/cursors.py", line 159, in execute  
query = query % db.literal(args)  
TypeError: not enough arguments for format string  
编辑:如果它有帮助,我使用mysqldb版本1.2.3,如果它不接受该版本的元组,但不要让我开始讨论mysqldb文档是多么垃圾..

4 个答案:

答案 0 :(得分:3)

你的mysqldb版本一定是问题。

http://mysql-python.hg.sourceforge.net/hgweb/mysql-python/MySQLdb-2.0/file/5a7c30cd9de2/MySQLdb/cursors.py#l180

更改为:

1.51 -        if args is not None:
1.52 -            query = query % self.connection.literal(args)
1.53          try:
1.54 +            if args is not None:
1.55 +                query = query % tuple(map(self.connection.literal, args))

http://mysql-python.hg.sourceforge.net/hgweb/mysql-python/MySQLdb-2.0/diff/98d968f5af11/MySQLdb/cursors.py

后来改为:

1.144 -                query = query % tuple(map(self.connection.literal, args))
1.145 +                query = query % tuple(( get_codec(a, self.encoders)(db, a) for a in args ))

http://mysql-python.hg.sourceforge.net/hgweb/mysql-python/MySQLdb-2.0/diff/d9bb912776a5/MySQLdb/cursors.py#l1.144

答案 1 :(得分:1)

在MySQLdb 1.2.3中,查询来自字符串替换,使用第一个参数作为模型字符串,第二个参数作为参数列表或单个参数,通过con.literal 传递。 con.literal获取您提供的内容并尝试将其转换为由MySQL正确解析的字符串。它为字符串添加了撇号。它输出的数字没有引号。或者,例如,如果你给它一个带撇号的字符串,它会引用撇号。

在你的特定情况下,当你把一个只有一个项目的元组(con是任何打开的连接)交给它时,MySQLdb似乎总会做错事:

>>> con.literal((10,))
('10',)
>>>

Python希望能够创建一个包含单个项目的元组,但它会导致MySQL进行barf(向右滚动以查看barfness):

mysql> select distinct sites.SiteCode, sites.SiteName, Latitude, Longitude, County, SourceID from sites, seriescatalog where sites.SiteID = seriescatalog.SiteID and sites.SiteID in (82,);
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ')' at line 1
mysql> 

这是我的工作,以防止Bobby Tabling:

siteinfoquery = """select distinct sites.SiteCode, sites.SiteName, Latitude, Longitude, County, SourceID from sites, seriescatalog where sites.SiteID = seriescatalog.SiteID and sites.SiteID in (%s);"""

cur.execute(siteinfoquery % ",".join([str(int(i)) for i in SiteID]))
顺便说一句,下面的内容并不起作用,因为con.literal(在MySQLdb内部调用execute()函数)引用结果字符串,将其从数字列表转换为字符串,然后变为单个值与任何SiteID都不匹配:

cur.execute(siteinfoquery , ",".join([str(int(i)) for i in SiteID]))

>>> "(%s)" % con.literal(",".join([str(float(i)) for i in SiteID]))
"('10.0,20.0')"
>>> 

我还没想过看看这是在以后的版本中修复的错误,当前的问题,我的错误,还是他们不知道的问题。

答案 2 :(得分:0)

我不能用MySQLdb复制这个。我正在使用1.2.2决赛。 也许尝试一个简单的调试,以确保问题出在你指出的地方:

In [13]: cur.execute('select %s + %s', (1,2))
Out[13]: 1L

In [14]: cur.fetchall()
Out[14]: ((3L,),)

更新1:所以我抓住并安装了1.2.3决赛,这是我的成绩单:

In [1]: import MySQLdb
In [2]: MySQLdb.version_info
Out[2]: (1, 2, 3, 'final', 0)
In [3]: con = MySQLdb.connect(user='root', db='inventory')
In [4]: cur = con.cursor()
In [5]: cur.execute('select %s + %s', (1,2))
Out[5]: 1L
In [6]: cur.fetchall()
Out[6]: ((3L,),)

如果我能复制你的问题,那么也许我可以提供解决方案!? 那么我们的两个环境还有什么不同呢?

$ mysql --version
mysql  Ver 14.14 Distrib 5.1.41, for debian-linux-gnu (x86_64) using readline 6.1

更新2: 你可以将事情分解得比上面多一点。请注意MySQLdb-1.2.3源代码中的以下代码段:

139     def execute(self, query, args=None):
...
158         if args is not None:
159             query = query % db.literal(args)
160         try:
161             r = self._query(query)
162         except TypeError, m:
163             if m.args[0] in ("not enough arguments for format string",
164                              "not all arguments converted"):
165                 self.messages.append((ProgrammingError, m.args[0]))

第159行是转换/转义args然后插入查询字符串的位置。 所以也许你可以这样做:

In [24]: con.literal((1,2,))
Out[24]: ('1', '2')

在与查询字符串合并之前,查看驱动程序如何转换参数。

In [26]: "select %s + %s" % con.literal((1,2,))
Out[26]: 'select 1 + 2'

答案 3 :(得分:0)

工作与非工作的例子:

import MySQLdb as mysql
from MySQLdb.constants import FIELD_TYPE
In [9]: conn = mysql.connect(host=hostname, user=user_name, passwd=password, port=port_number, db=schema_name)
In [10]: cursor = conn.cursor()
In [11]: cursor.execute('select %s + %s', (1, 3))
Out[11]: 1L

In [16]: conn = mysql.connect(host=hostname, user=user_name, passwd=password, port=port_number, db=schema_name, conv={FIELD_TYPE.LONG: long})
In [17]: cursor = conn.cursor()
In [18]: cursor.execute('select %s + %s', (1, 3))---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-18-08423fe5373f> in <module>()
----> 1 cursor.execute('select %s + %s', (1, 3))
/usr/lib64/python2.6/site-packages/MySQLdb/cursors.pyc in execute(self, query, args)
    156             query = query.encode(charset)
    157         if args is not None:
--> 158             query = query % db.literal(args)
    159         try:
    160             r = self._query(query)
TypeError: not enough arguments for format string

conv的{​​{1}}参数对我来说是个问题。没有它,我的查询参数将被解释为预期。有了它,我就把头发撕掉了。