美文网首页
使用torndb时当查询语句中出现%时的字符匹配问题

使用torndb时当查询语句中出现%时的字符匹配问题

作者: jiaxiaolei | 来源:发表于2020-05-27 15:11 被阅读0次

在使用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 "

修改:

  1. 从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

  1. 从 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'}



相关文章

网友评论

      本文标题:使用torndb时当查询语句中出现%时的字符匹配问题

      本文链接:https://www.haomeiwen.com/subject/bivrvhtx.html