在使用torndb+mysqldb 操作SQL语句的时候,在SQL命令行中出现%的时候可能会出现问题。
具体来讲就是:
如果sql 中出现%, 会提示 :
"TypeError: not enough arguments for format string"
Traceback (most recent call last):
File "host_db.py", line 1587, in <module>
test()
File "host_db.py", line 1525, in test
res = host_db.query(sql_cmd)
File "/usr/lib/python2.7/site-packages/torndb.py", line 136, in query
self._execute(cursor, query, parameters, kwparameters)
File "/usr/lib/python2.7/site-packages/torndb.py", line 234, in _execute
return cursor.execute(query, kwparameters or parameters)
File "/usr/lib64/python2.7/site-packages/MySQLdb/cursors.py", line 187, in execute
query = query % tuple([db.literal(item) for item in args])
TypeError: not enough arguments for format string
严格来说,这是一个bug.
因为在sql 解析器中可以正常执行的sql语句,通过torndb去执行就报错。
具体修复的方法,可以从torndb去修改,也可以改torndb下层的cursors.py
查看相关源码:
def test():
sql_cmd = "select * from host_summary where valid = 1 and ip not like '10.122.53.%' order by site_id, sub_site_id, platform "
print 'sql_cmd', sql_cmd
res = host_db.query(sql_cmd)
print 'res', res
import torndb
# summary db
host = "10.99.205.127:3306"
db_name = "sacp"
user = "root"
passwd = "Lenovo!@34"
host_db = torndb.Connection(host, db_name, user=user, password=passwd)
res = host_db.query(sql_cmd)
$ vim /usr/lib/python2.7/site-packages/torndb.py
#NOTE: query 方法中,如果没有传递参数, parameters, kwparameters 分别为空的 () 和 {}
def query(self, query, *parameters, **kwparameters):
"""Returns a row list for the given query and parameters."""
cursor = self._cursor()
try:
self._execute(cursor, query, parameters, kwparameters)
column_names = [d[0] for d in cursor.description]
return [Row(itertools.izip(column_names, row)) for row in cursor]
finally:
cursor.close()
#NOTE: kwparameters 和 parameters 经过 or 运算之后,返回 ()
def _execute(self, cursor, query, parameters, kwparameters):
try:
return cursor.execute(query, kwparameters or parameters)
except OperationalError:
logging.error("Error connecting to MySQL on %s", self.host)
self.close()
raise
vim /usr/lib64/python2.7/site-packages/MySQLdb/cursors.py
#NOTE: 这里对args 做非None判断之后,判断args的类型。 如果args不是dict, 则进行匹配: query = query % tuple([db.literal(item) for item in args])
def execute(self, query, args=None):
"""Execute a query.
query -- string, query to execute on server
args -- optional sequence or mapping, parameters to use with query.
Note: If args is a sequence, then %s must be used as the
parameter placeholder in the query. If a mapping is used,
%(key)s must be used as the placeholder.
Returns long integer rows affected, if any
"""
del self.messages[:]
db = self._get_db()
if isinstance(query, unicode):
query = query.encode(db.unicode_literal.charset)
if args is not None:
if isinstance(args, dict):
query = query % dict((key, db.literal(item))
for key, item in args.iteritems())
else:
query = query % tuple([db.literal(item) for item in args])
try:
r = None
r = self._query(query)
except TypeError, m:
if m.args[0] in ("not enough arguments for format string",
"not all arguments converted"):
self.messages.append((ProgrammingError, m.args[0]))
self.errorhandler(self, ProgrammingError, m.args[0])
else:
self.messages.append((TypeError, m))
self.errorhandler(self, TypeError, m)
except (SystemExit, KeyboardInterrupt):
raise
except:
exc, value, tb = sys.exc_info()
del tb
self.messages.append((exc, value))
self.errorhandler(self, exc, value)
self._executed = query
if not self._defer_warnings: self._warning_check()
return r
>>> args = ()
>>> tuple([db.literal(item) for item in args])
()
可以看到: 如果一个包含%的字符串去 % () 的时候,会报 "TypeError: not enough arguments for format string" 这样的错误。
>>> sql_cmd
"select * from host_summary where valid = 1 and ip not like '10.122.53.%' order by site_id, sub_site_id, platform "
>>> sql_cmd % ()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: not enough arguments for format string
>>> sql_cmd2 = "select * from host_summary where valid = 1 and ip not like '10.122.53.11' order by site_id, sub_site_id, platform "
>>> sql_cmd2 % ()
"select * from host_summary where valid = 1 and ip not like '10.122.53.11' order by site_id, sub_site_id, platform "
修改:
- 从torndb端修改:
调整参数部分。如果parameters和kwparameters都是空,将args置为None.
这样到了"MySQLdb/cursors.py"中,query部分就不会再进行任何字符串匹配操作(%).
def _execute(self, cursor, query, parameters, kwparameters):
try:
if not ( kwparameters or parameters):
args = None
elif kwparameters:
args = kwparameters
else:
args = parameters
return cursor.execute(query, args)
except OperationalError:
logging.error("Error connecting to MySQL on %s", self.host)
self.close()
raise
- 从 MySQLdb 进行操作:
将"if args is not None:"改为“if args:”。
python中None, (), {} 都会被认为是False。
修改之后,可以保证args只有在存在有效值(不包括空的tuple和dict)时才进行字符串匹配。
if args:
if isinstance(args, dict):
query = query % dict((key, db.literal(item))
for key, item in args.iteritems())
else:
query = query % tuple([db.literal(item) for item in args])
print 'cursor: query', query
补充
pyhton 中 的 and,or.
and:
如果有一个0,则显示0;
如果2个非0, 显示后一个值;
如果2个0, 显示第一个值;
or:
如果只有一个0,显示非0;
如果2个0,显示后一个;
如果2个非0,显示前一个;
>>> () or {}
{}
>>> {} or ()
()
>>> 0 or 0.0
0.0
>>> 0.0 or 0
0
>>> 1 or 1.0
1
>>> 1.0 or 1
1.0
>>> 1.0 or 0
1.0
>>> 0 or 1.0
1.0
>>> 1 and 1.0
1.0
>>> 1 and 0
0
>>> 0 and 0.0
0
>>> 0.0 and 0
0.0
parameters为一个元组而kwparameters是一个字典类型。
这两个作为python的可变参数,也就是说parameters表示任何多个无名参数,然而kwags表示一个一个有着对应关系的关键字参数。
在使用的时候需要注意,*parameters要在**kwparameters之前,不然会发生语法错误。
>>> def test(*parameters, **kwparameters):
... print 'parameters', parameters
... print 'kwparameters', kwparameters
...
>>> test()
parameters ()
kwparameters {}
>>> test('a','b',name='jia', age=10)
parameters ('a', 'b')
kwparameters {'age': 10, 'name': 'jia'}
网友评论