Skip to content

Commit 815e25b

Browse files
authored
Merge pull request #107 from aio-libs/fix-json
Fix json error for field that contains unicode
2 parents 17ca9bf + 5a1dbeb commit 815e25b

File tree

2 files changed

+63
-5
lines changed

2 files changed

+63
-5
lines changed

aiomysql/connection.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from pymysql.constants import SERVER_STATUS
1717
from pymysql.constants import CLIENT
1818
from pymysql.constants import COMMAND
19+
from pymysql.constants import FIELD_TYPE
1920
from pymysql.util import byte2int, int2byte
2021
from pymysql.converters import (escape_item, encoders, decoders,
2122
escape_string, through)
@@ -973,6 +974,7 @@ def _get_descriptions(self):
973974
self.fields = []
974975
self.converters = []
975976
use_unicode = self.connection.use_unicode
977+
conn_encoding = self.connection.encoding
976978
description = []
977979
for i in range(self.field_count):
978980
field = yield from self.connection._read_packet(
@@ -981,14 +983,24 @@ def _get_descriptions(self):
981983
description.append(field.description())
982984
field_type = field.type_code
983985
if use_unicode:
984-
if field_type in TEXT_TYPES:
985-
charset = charset_by_id(field.charsetnr)
986-
if charset.is_binary:
986+
if field_type == FIELD_TYPE.JSON:
987+
# When SELECT from JSON column: charset = binary
988+
# When SELECT CAST(... AS JSON): charset = connection
989+
# encoding
990+
# This behavior is different from TEXT / BLOB.
991+
# We should decode result by connection encoding
992+
# regardless charsetnr.
993+
# See https://github.com/PyMySQL/PyMySQL/issues/488
994+
encoding = conn_encoding # SELECT CAST(... AS JSON)
995+
elif field_type in TEXT_TYPES:
996+
if field.charsetnr == 63: # binary
987997
# TEXTs with charset=binary means BINARY types.
988998
encoding = None
989999
else:
990-
encoding = charset.encoding
1000+
encoding = conn_encoding
9911001
else:
1002+
# Integers, Dates and Times, and other basic data
1003+
# is encoded in ascii
9921004
encoding = 'ascii'
9931005
else:
9941006
encoding = None

tests/test_basic.py

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import asyncio
2-
import time
32
import datetime
3+
import json
4+
import re
5+
import time
46

57
import pytest
68
from pymysql import util
@@ -215,3 +217,47 @@ def test_rollback(connection, cursor):
215217

216218
# should not return any rows since no inserts was commited
217219
assert len(data) == 0
220+
221+
222+
def mysql_server_is(server_version, version_tuple):
223+
"""Return True if the given connection is on the version given or
224+
greater.
225+
e.g.::
226+
if self.mysql_server_is(conn, (5, 6, 4)):
227+
# do something for MySQL 5.6.4 and above
228+
"""
229+
server_version_tuple = tuple(
230+
(int(dig) if dig is not None else 0)
231+
for dig in
232+
re.match(r'(\d+)\.(\d+)\.(\d+)', server_version).group(1, 2, 3)
233+
)
234+
return server_version_tuple >= version_tuple
235+
236+
237+
@pytest.mark.run_loop
238+
def test_json(connection_creator, table_cleanup):
239+
connection = yield from connection_creator(
240+
charset="utf8mb4", autocommit=True)
241+
server_info = connection.get_server_info()
242+
if not mysql_server_is(server_info, (5, 7, 0)):
243+
raise pytest.skip("JSON type is not supported on MySQL <= 5.6")
244+
245+
cursor = yield from connection.cursor()
246+
yield from cursor.execute("""\
247+
CREATE TABLE test_json (
248+
id INT NOT NULL,
249+
json JSON NOT NULL,
250+
PRIMARY KEY (id)
251+
);""")
252+
table_cleanup("test_json")
253+
json_str = '{"hello": "こんにちは"}'
254+
yield from cursor.execute(
255+
"INSERT INTO test_json (id, `json`) values (42, %s)", (json_str,))
256+
yield from cursor.execute("SELECT `json` from `test_json` WHERE `id`=42")
257+
258+
r = yield from cursor.fetchone()
259+
assert json.loads(r[0]) == json.loads(json_str)
260+
261+
yield from cursor.execute("SELECT CAST(%s AS JSON) AS x", (json_str,))
262+
r = yield from cursor.fetchone()
263+
assert json.loads(r[0]) == json.loads(json_str)

0 commit comments

Comments
 (0)