美文网首页
使用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