【问题标题】:Problem passing Tuple to MySQLdb in python在 python 中将元组传递给 MySQLdb 的问题
【发布时间】:2011-04-27 00:42:16
【问题描述】:

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

例如:

原文:
(这行得通,所以 ListID 等肯定是有效的)

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

尝试使用元组:

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 文档有多垃圾..

【问题讨论】:

  • 如果您在前几个案例中提供的信息多于“不起作用”的信息,并在收到记录的错误时显示您正在执行的操作,这将非常有帮助。请注意,您可以并且应该在 Python 交互式提示 (>>>) 和/或可以在 shell 命令行上运行的非常简单的脚本中尝试解决您的问题——避免像您的 web 服务器这样使调试变得简单的无关紧要问题更难。显示您的数据,例如print repr(ListID).
  • 在上面的代码中一切正常,当文档说它只接受元组时,我不明白为什么将它从字符串替换移动到元组参数化是一个问题。我会认为对于不是我的任何人来说,这都是一个容易发现的错误。它是读取 JSON 结构并将其返回到 Android 手机的 CGI 脚本的一部分,因此安装本地 apache 网络服务器并调整输入只是为了调试可能非常明显的内容可能会适得其反,尤其是作为实际脚本但为此完成。此外,当第一批不工作时,没有错误。
  • 正如我所说,所有这些 CGI/JSON/Android/apache 内容都与您所谓的 MySQLdb 问题无关。我当然不是鼓励您安装本地 apache 网络服务器,而是像 @jsw 所做的那样做一些与您的问题相关的实验。 """当第一批不工作时,没有错误""" 那么“不工作”到底是什么意思?如果您无法以类似于@jsw 的方式重现您的问题,您需要记录 (1) 您的 sql 语句 (2) 参数元组 (3) fetchall() 结果(在每种情况下使用 @ 987654330@ 以避免歧义)并向我们展示结果。
  • 嗨,是的,我明白你的意思(对不起,我没有粗鲁,我很感谢你的时间!),我只是想我必须安装一个本地 MySQL 服务器或其他东西正确测试它?我绝对可以这样做,我只是做了一个(也许是错误的)假设,即问题是我所做的简单的事情,不需要大量调查。参数、数据库、sql 查询等都可以正常工作,唯一不同的是作为元组的参数(即使作为单个参数也可以,但不能作为单元素元组)

标签: python tuples mysql-python


【解决方案1】:

一定是你的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

【讨论】:

  • 可能是,它是连接文字触发了我所知道的错误..
  • @user:naw,您的来源是 1.2.3,您可以在原始问题发布中看到引发异常的行。我认为这个差异来自较新的来源。你的是query % connection.literal(args)
  • 它在文字行失败但在字符串替换时成功,但我选择了 1.2.3 mysqldb,因为它说它与 oyhtin 2.3.6 兼容.. 你是说代码看起来不是来自1.2.3?
【解决方案2】:

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

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

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

这就是 Python 期望能够创建一个包含单个项目的元组,但它会导致 MySQL 出错(向右滚动以查看出错情况):

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')"
>>> 

我没有查看这是否是更高版本中修复的错误、当前问题、我的错误或他们不知道的问题。

【讨论】:

  • 人力资源部。我似乎已将 my 问题视为与您的问题相同。你只是想传入一个或两个简单的变量。抱歉,我无法重现您使用 MySQLdb 1.2.3 的问题,这正是您正在使用的。
【解决方案3】:

我无法用 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 final,这是我的成绩单:

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 源的 sn-p:

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'

【讨论】:

  • 您好,感谢您尝试使用类似版本进行复制!不幸的是,结果相同,只是失败了,没有 python 错误,也没有结果输出
  • (我还输入了一些东西来输出异常:除了 MySQLdb.Error,e: dumpout.write("\n"+e.args[0]+" "+e.args[ 1])但这也没有被输出..)尽管我的网络服务器刚刚再次登录:对于您提供的代码,与上面相同的“TypeError:格式字符串的参数不足”
  • 尝试:c=db.cursor() c.execute('SELECT %s + %s', (1,2)) sqlresults=c.fetchone() dumpout.write(str(sqlresults [0])) 给出相同的参数不足错误,但它应该知道元组包含两个,而不是元组本身是唯一的一个..
  • 这里可能没关系,但是在获取调试信息时养成使用repr()而不是str()的习惯。
  • @user...:我在上面更新了 1.2.3 最终版本(请参阅编辑),但仍然无法复制问题。当然,我们可以找到保护您免受 Bobby 伤害的方法;删除表 ListsTable;!
【解决方案4】:

工作与非工作示例:

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

mysql.connect 的 conv 参数对我来说是个问题。没有它,我的查询参数将按预期解释。有了它,我把头发扯掉了。

【讨论】:

    猜你喜欢
    • 2016-04-07
    • 2015-10-14
    • 2012-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-02
    • 2016-03-17
    • 2015-09-02
    相关资源
    最近更新 更多