Skip to content

Commit dd7a042

Browse files
committed
BUG28133321: Fix incorrect columns names representing aggregate
functions The columns names representing aggregate functions are converted to bytearray when the C extension is used. The issue comes from the description flag that is set to NULL for these columns. This patch adds a check for NULL values so the name can be converted to a string type.
1 parent 667d542 commit dd7a042

File tree

3 files changed

+112
-5
lines changed

3 files changed

+112
-5
lines changed

src/mysql_capi_conversion.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -756,7 +756,7 @@ mytopy_string(const char *data, const unsigned long length,
756756
return NULL;
757757
}
758758

759-
if (!(flags & BINARY_FLAG) && use_unicode && strcmp(charset, "binary") != 0)
759+
if (!((flags != NULL) & flags & BINARY_FLAG) && use_unicode && strcmp(charset, "binary") != 0)
760760
{
761761
return PyUnicode_Decode(data, length, charset, NULL);
762762
}

tests/cext/test_cext_cursor.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ def test_execute(self):
125125
self.cnx.get_warnings = False
126126

127127
cur.execute("SELECT BINARY 'ham'")
128-
exp = [(b'ham',)]
128+
exp = [('ham',)]
129129
self.assertEqual(exp, cur.fetchall())
130130
cur.close()
131131

@@ -346,7 +346,7 @@ def test_fetchone(self):
346346

347347
cur = self.cnx.cursor()
348348
cur.execute("SELECT BINARY 'ham'")
349-
exp = (b'ham',)
349+
exp = ('ham',)
350350
self.assertEqual(exp, cur.fetchone())
351351
self.assertEqual(None, cur.fetchone())
352352
cur.close()
@@ -445,7 +445,7 @@ def test__str__(self):
445445
def test_column_names(self):
446446
cur = self._get_cursor(self.cnx)
447447
stmt = "SELECT NOW() as now, 'The time' as label, 123 FROM dual"
448-
exp = (b'now', 'label', b'123')
448+
exp = ('now', 'label', '123')
449449
cur.execute(stmt)
450450
cur.fetchone()
451451
self.assertEqual(exp, cur.column_names)

tests/test_bugs.py

Lines changed: 108 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4216,7 +4216,7 @@ class BugOra21656282(tests.MySQLConnectorTests):
42164216
"""
42174217
def setUp(self):
42184218
config = tests.get_mysql_config()
4219-
self.cnx = connection.MySQLConnection(**config)
4219+
self.cnx = CMySQLConnection(**config)
42204220
self.host = '127.0.0.1' if config['unix_socket'] and os.name != 'nt' \
42214221
else config['host']
42224222
self.user = 'unicode_user'
@@ -4340,6 +4340,113 @@ def test_cext_verify_server_certifcate(self):
43404340
self._verify_cert(config)
43414341

43424342

4343+
@unittest.skipIf(tests.MYSQL_VERSION < (5, 6, 39), "skip in older server")
4344+
@unittest.skipIf(not CMySQLConnection, ERR_NO_CEXT)
4345+
class Bug28133321(tests.MySQLConnectorTests):
4346+
"""BUG#28133321: FIX INCORRECT COLUMNS NAMES REPRESENTING AGGREGATE
4347+
FUNCTIONS
4348+
"""
4349+
tbl = "BUG28133321"
4350+
4351+
def setUp(self):
4352+
create_table = ("CREATE TABLE {} ("
4353+
" dish_id INT(11) UNSIGNED AUTO_INCREMENT UNIQUE KEY,"
4354+
" category TEXT,"
4355+
" dish_name TEXT,"
4356+
" price FLOAT,"
4357+
" servings INT,"
4358+
" order_time TIME) CHARACTER SET utf8"
4359+
" COLLATE utf8_general_ci")
4360+
config = tests.get_mysql_config()
4361+
cnx = CMySQLConnection(**config)
4362+
4363+
try:
4364+
cnx.cmd_query("DROP TABLE IF EXISTS {0}".format(self.tbl))
4365+
except:
4366+
pass
4367+
cnx.cmd_query(create_table.format(self.tbl))
4368+
4369+
cur = cnx.cursor(dictionary=True)
4370+
insert_stmt = ('INSERT INTO {} ('
4371+
' category, dish_name, price, servings, order_time'
4372+
') VALUES ("{{}}", "{{}}", {{}}, {{}}, "{{}}")'
4373+
).format(self.tbl)
4374+
values = [("dinner", "lassanya", 10.53, "2", "00:10"),
4375+
("dinner", "hamburger", 9.35, "1", "00:15"),
4376+
("dinner", "hamburger whit fries", 10.99, "2", "00:20"),
4377+
("dinner", "Pizza", 9.99, "4", "00:30"),
4378+
("dessert", "cheescake", 4.95, "1", "00:05"),
4379+
("dessert", "cheescake special", 5.95, "2", "00:05")]
4380+
4381+
for value in values:
4382+
cur.execute(insert_stmt.format(*value))
4383+
cnx.close()
4384+
4385+
def tearDown(self):
4386+
config = tests.get_mysql_config()
4387+
cnx = CMySQLConnection(**config)
4388+
try:
4389+
cnx.cmd_query("DROP TABLE IF EXISTS {0}".format(self.tbl))
4390+
except:
4391+
pass
4392+
cnx.close()
4393+
4394+
def test_columns_name_are_not_bytearray(self):
4395+
sql_statement = ["SELECT",
4396+
" dish_id,",
4397+
" category,",
4398+
" JSON_OBJECTAGG(category, dish_name) as special,",
4399+
" JSON_ARRAYAGG(dish_name) as dishes,",
4400+
" GROUP_CONCAT(dish_name) as dishes2,",
4401+
" price,",
4402+
" servings,",
4403+
" ROUND(AVG(price)) AS round_avg_price,",
4404+
" AVG(price) AS avg_price,",
4405+
" MIN(price) AS min_price,",
4406+
" MAX(price) AS max_price,",
4407+
" MAX(order_time) AS preparation_time,",
4408+
" STD(servings) as deviation,",
4409+
" SUM(price) AS sum,",
4410+
" VARIANCE(price) AS var,",
4411+
" COUNT(DISTINCT servings) AS cd_servings,",
4412+
" COUNT(servings) AS c_servings ",
4413+
"FROM {} ",
4414+
"GROUP BY category"]
4415+
# Remove JSON functions when testing againsts server version < 5.7.22
4416+
# JSON_OBJECTAGG JSON_ARRAYAGG were introduced on 5.7.22
4417+
if tests.MYSQL_VERSION < (5, 7, 22):
4418+
sql_statement.pop(3)
4419+
sql_statement.pop(3)
4420+
sql_statement = "".join(sql_statement)
4421+
config = tests.get_mysql_config()
4422+
cnx = CMySQLConnection(**config)
4423+
4424+
cur = cnx.cursor(dictionary=True)
4425+
cur.execute(sql_statement.format(self.tbl))
4426+
rows = cur.fetchall()
4427+
col_names = [x[0] for x in cur.description]
4428+
4429+
for row in rows:
4430+
for col, val in row.items():
4431+
self.assertTrue(isinstance(col, STRING_TYPES),
4432+
"The columns name {} is not a string type"
4433+
"".format(col))
4434+
self.assertFalse(isinstance(col, (bytearray)),
4435+
"The columns name {} is a bytearray type"
4436+
"".format(col))
4437+
self.assertFalse(isinstance(val, (bytearray)),
4438+
"The value {} of column {} is a bytearray type"
4439+
"".format(val, col))
4440+
4441+
for col_name in col_names:
4442+
self.assertTrue(isinstance(col_name, STRING_TYPES),
4443+
"The columns name {} is not a string type"
4444+
"".format(col_name))
4445+
self.assertFalse(isinstance(col_name, (bytearray)),
4446+
"The columns name {} is a bytearray type"
4447+
"".format(col_name))
4448+
4449+
43434450
class BugOra21947091(tests.MySQLConnectorTests):
43444451
"""BUG#21947091: """
43454452
def setUp(self):

0 commit comments

Comments
 (0)