[pypy-issue] Issue #3014: JIT Issue inlining `struct.unpack('<HH', bytearray(...))` produces incorrect results (pypy/pypy)
Jason at bitbucket.org
Jason at bitbucket.org
Tue May 14 18:32:47 EDT 2019
New issue 3014: JIT Issue inlining `struct.unpack('<HH', bytearray(...))` produces incorrect results
https://bitbucket.org/pypy/pypy/issues/3014/jit-issue-inlining-structunpack-hh
Jason Madden:
Versions:
* Python 2.7.13 \(8cdda8b8cdb8, Apr 14 2019, 14:06:58\)
\[PyPy 7.1.1 with GCC 4.2.1 Compatible Apple LLVM 10.0.0 \(clang-1000.11.45.5\)\] \(macOS 10.14\)
* Python 3.6.1 \(784b254d6699, Apr 14 2019, 10:22:55\)
\[PyPy 7.1.1-beta0 with GCC 4.2.1 Compatible Apple LLVM 10.0.0 \(clang-1000.11.45.5\)\]
* Python 2.7.13 \(8cdda8b8cdb8, Apr 14 2019, 14:06:58\)
\[PyPy 7.1.1 with GCC 4.2.1 Compatible Apple LLVM 10.0.0 \(clang-1000.11.45.5\)\]
I’ve been working on getting RelStorage to run on PyPy with \(pure-Python\) mysql-connector-python at [https://github.com/zodb/relstorage/issues/228](https://github.com/zodb/relstorage/issues/228#issuecomment-492423284), but was running into a very strange, non-sensical error only on PyPy partway through the test sequence.
In a nutshell, the value of the `status_flag` in the OK result packet from the MySQL server is coming back with the `ServerFlag.MORE_RESULTS_EXIST` bit set, leading to the error. But that’s not actually the packet that the server is sending.
`protocol.py` has `parse_ok` that parses the `status_flag` from a packet \(a `bytearray`\) like this:
```python
def parse_ok(self, packet):
"""Parse a MySQL OK-packet"""
if not packet[4] == 0:
raise errors.InterfaceError("Failed parsing OK packet (invalid).")
ok_packet = {}
ok_packet['orig_data'] = bytes(packet)
try:
ok_packet['field_count'] = struct_unpack('<xxxxB', packet[0:5])[0]
(packet, ok_packet['affected_rows']) = utils.read_lc_int(packet[5:])
(packet, ok_packet['insert_id']) = utils.read_lc_int(packet)
(ok_packet['status_flag'], # This is the bad value
ok_packet['warning_count']) = struct_unpack('<HH', packet[0:4])
packet = packet[4:]
if packet:
(packet, ok_packet['info_msg']) = utils.read_lc_string(packet)
ok_packet['info_msg'] = ok_packet['info_msg'].decode('utf-8')
except ValueError:
raise errors.InterfaceError("Failed parsing OK packet.")
return ok_packet
```
\( `struct_unpack` is a small helper function for Python 2 that wraps a `bytearray` into a `buffer` and uses `struct.unpack_from`; I get the same results if I modify the code to use `struct.unpack` directly. Python 3 uses struct.unpack directly already.\)
I tweaked the error message to report the packet value, and to also unpack the data at that time. \(This is a cold branch of code that’s never been taken before\)
```python
if self._have_next_result: # The status_flag was not what we wanted.
from .catch23 import PY2, struct_unpack
raise errors.InterfaceError(
'Use cmd_query_iter for statements with multiple queries.'
'\n%s\n%r\n%r\nUnpacks to:%r' % (
query, result, raw_result,
struct_unpack('<HH', raw_result[0:4])))
```
```
InterfaceError: Use cmd_query_iter for statements with multiple queries.
INSERT INTO temp_pack_visit (zoid, keep_tid)
SELECT zoid, 0
FROM current_object
WHERE tid > 274639514178723874
{'field_count': 0, 'affected_rows': 0, 'insert_id': 0,
'status_flag': 3480, 'warning_count': 0, 'info_msg': u'Records: 0 Duplicates: 0 Warnings: 0'}
bytearray(b'.\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00&Records: 0 Duplicates: 0 Warnings: 0')
Unpacks to: (46, 256)
```
You can see that protocol.py's `parse_ok` method gets a `status_flag` of `3480`, while performing the same unpacking on the same data in this function gets a value of `46` \(which is what CPython gets too\).
\*\*Running with `--jit off` solves the problem and lets the tests complete successfully. Also, just running with `--jit inlining=0` solves the problem too.\*\*
How can I help with this? I imagine you’ll want a JIT log of some sort?
More information about the pypy-issue
mailing list