查询许多列并使用预准备语句时,MySQL连接器/ python(v1.1.6)失败

时间:2014-05-06 23:57:36

标签: python mysql

我在调用unpack requires a string argument of length 8cursor.fetchone()cursor.fetchall()时收到错误:cursor.fetchmany()。下面是重现问题的调用顺序。

In [3]: cursor = connector.cursor(prepared=True)

In [4]: stmt = "SELECT * FROM my_table WHERE my_key=%s"

In [5]: cursor.execute(stmt, ('my_value',))

In [6]: cursor.fetchone()
---------------------------------------------------------------------------
error                                     Traceback (most recent call last)
<ipython-input-6-5501e92f1036> in <module>()
----> 1 cursor.fetchone()

/usr/lib/python2.7/dist-packages/mysql/connector/cursor.pyc in fetchone(self)
   1041         Returns a tuple or None.
   1042         """
-> 1043         return self._fetch_row() or None
   1044 
   1045     def fetchmany(self, size=None):

/usr/lib/python2.7/dist-packages/mysql/connector/cursor.pyc in _fetch_row(self)
    709         if self._nextrow == (None, None):
    710             (row, eof) = self._connection.get_row(
--> 711                 binary=self._binary, columns=self.description)
    712         else:
    713             (row, eof) = self._nextrow

/usr/lib/python2.7/dist-packages/mysql/connector/connection.pyc in get_row(self, binary, columns)
    599         Returns a tuple.
    600         """
--> 601         (rows, eof) = self.get_rows(count=1, binary=binary, columns=columns)
    602         if len(rows):
    603             return (rows[0], eof)

/usr/lib/python2.7/dist-packages/mysql/connector/connection.pyc in get_rows(self, count, binary, columns)
    580         if binary:
    581             rows = self._protocol.read_binary_result(
--> 582                 self._socket, columns, count)
    583         else:
    584             rows = self._protocol.read_text_result(self._socket, count)

/usr/lib/python2.7/dist-packages/mysql/connector/protocol.pyc in read_binary_result(self, sock, columns, count)
    398             elif packet[4] == '\x00':
    399                 eof = None
--> 400                 values = self._parse_binary_values(columns, packet[5:])
    401             if eof is None and values is not None:
    402                 rows.append(values)

/usr/lib/python2.7/dist-packages/mysql/connector/protocol.pyc in _parse_binary_values(self, fields, packet)
349         """Parse values from a binary result packet"""
350         null_bitmap_length = (len(fields) + 7 + 2) // 8
--> 351         null_bitmap = utils.intread(packet[0:null_bitmap_length])
352         packet = packet[null_bitmap_length:]
353 

/usr/lib/python2.7/dist-packages/mysql/connector/utils.pyc in intread(buf)
     43         else:
     44             tmp = buf + '\x00'*(8-length)
---> 45             return struct.unpack('<Q', tmp)[0]
     46     except:
     47         raise

my_table有81列。

如果我只选择几列,即将stmt更改为SELECT my_col1,my_col2,my_col3 from my_table WHERE my_key=%s,则此问题不会重现。

我怀疑这是mysql connector / python中的一个错误。

我做错了吗?

我的mysql连接器/ python版本是1.1.6,我是从http://dev.mysql.com/get/Downloads/Connector-Python/mysql-connector-python_1.1.6-1ubuntu12.04_all.deb下载的。我的操作系统是ubuntu 13.04。

1 个答案:

答案 0 :(得分:0)

我怀疑这是mysql connector / python中的一个真正的错误。该错误位于protocol.py: MySQLProtocol._parse_binary_values()utils.py: intread()

utils.py: intread()中的代码需要一个不超过8个字节的字符串,但当列数超过62(8 * 8 - 2)时,protocol.py: MySQLProtocol._parse_binary_values()会传入更长的字符串。

以下是相关的代码段:

在protocol.py中:

348:    def _parse_binary_values(self, fields, packet):
349:        """Parse values from a binary result packet"""
350:        null_bitmap_length = (len(fields) + 7 + 2) // 8
351:        null_bitmap = utils.intread(packet[0:null_bitmap_length])
352: 
353:         values = []
354:         for pos, field in enumerate(fields):
355:         if null_bitmap & 1 << (pos + 2):

在utils.py中:

32: def intread(buf):
33:     """Unpacks the given buffer to an integer"""
34:     try:
35:         if isinstance(buf, int):
36:             return buf
37:         length = len(buf)
38:         if length == 1:
39:             return int(ord(buf))
40:         if length <= 4:
41:             tmp = buf + '\x00'*(4-length)
42:             return struct.unpack('<I', tmp)[0]
43:         else:
44:             tmp = buf + '\x00'*(8-length)
45:             return struct.unpack('<Q', tmp)[0]
46:     except:
47:         raise

我可以通过进行以下更改来解决我的问题:

在protocol.py中:

348:     def _parse_binary_values(self, fields, packet):
349:         """Parse values from a binary result packet"""
350:         null_bitmap_length = (len(fields) + 7 + 2) // 8
351:         null_bitmap = utils.read_bitmap(packet[0:null_bitmap_length])
352:
353:         values = []
354:         for pos, field in enumerate(fields):
355:             if null_bitmap.test_bit(pos + 2):

在utils.py中(添加以下行):

class MySqlIntBitmap(object):

    def __init__(self, value):
        self.int_bitmap = value

    def test_bit(self, bit):
        return (self.int_bitmap & (1 << bit)) 


class MySqlByteArrayBitmap(object):

    def __init__(self, value):
        self.byte_array_bitmap = value

    def test_bit(self, bit):
        index = bit // 8
        entry_bit = bit % 8
    return (self.byte_array_bitmap[index] & (1 << entry_bit))

def read_bitmap(buf):
    """Unpacks the given buffer to a bitmap"""
    try:
        if isinstance(buf, int):
            return MySqlIntBitmap(buf)
        length = len(buf)
        if length == 1:
            return MySqlIntBitmap(int(ord(buf)))
        if length <= 4:
            tmp = buf + '\x00'*(4-length)
            return MySqlIntBitmap(struct.unpack('<I', tmp)[0])
        elif length <= 8:
            tmp = buf + '\x00'*(8-length)
            return MySqlIntBitmap(struct.unpack('<Q', tmp)[0])
        else:
            return MySqlByteArrayBitmap([int(ord(i)) for i in buf])
    except:
        raise