Skip to content

Commit 8f17fbf

Browse files
committed
BUG#34655520: Wrong cursor.statement values in the results of cursor.execute(..., multi=True)
Wrong MySQLCursor.statement values are returned in the results of cursor.execute(query_text, multi=True) when the following conditions stand: a) query_text is a string containing two queries or more, separated by semicolons (;). For example, query_text = "SELECT 1; SELECT 2". b) The second query (or third, fourth, ...) uses a literal or an identifier containing an odd number of backticks (`), an odd number of quotes (') or an odd number of double quotes ("). For example, query_text = 'SELECT 1; SELECT "`"' The expected answer is that each cursor from the result returns one of the queries embedded in query_text when the corresponding statement property is consulted. However, when the above conditions stand, the before mentioned property may return more than one query. With this patch, the behaviour is corrected by updating the regex used to split out the executed operation (query_text). Change-Id: I894509a0545947559818e0dc3ad588841b88eae1
1 parent e2fdcfb commit 8f17fbf

File tree

4 files changed

+39
-2
lines changed

4 files changed

+39
-2
lines changed

CHANGES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ v8.0.32
1818
- BUG#34695103: Remove debug messages that shows authentication data
1919
- BUG#34689812: Fix datetime conversion when using prepared cursors
2020
- BUG#34675508: Character set 'utf8' unsupported in python mysql connector when using MariaDB
21+
- BUG#34655520: Wrong MySQLCursor.statement values in the results of cursor.execute(..., multi=True)
2122
- BUG#34556157: Kerberos authorization fails when using SSPI as security interface
2223
- BUG#34499578: MySQLCursor.executemany() fails to correctly identify BULK data loading ops
2324
- BUG#34467201: Add init_command connection option

lib/mysql/connector/cursor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@
9999
""",
100100
re.X,
101101
)
102-
RE_SQL_SPLIT_STMTS = re.compile(b""";(?=(?:[^"'`]*["'`][^"'`]*["'`])*[^"'`]*$)""")
102+
RE_SQL_SPLIT_STMTS = re.compile(b""";(?=(?:[^"'`]*["'`].*["'`])*[^"'`]*$)""")
103103
RE_SQL_FIND_PARAM = re.compile(b"""%s(?=(?:[^"'`]*["'`][^"'`]*["'`])*[^"'`]*$)""")
104104

105105
ERR_NO_RESULT_TO_FETCH = "No result set to fetch from"

tests/cext/test_cext_cursor.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,7 @@ def tests_nextset(self):
578578
cur.close()
579579
self.cnx.rollback()
580580

581-
def tests_execute_multi(self):
581+
def test_execute_multi(self):
582582
tbl = "myconnpy_execute_multi"
583583
stmt = (
584584
"SELECT 'result', 1; INSERT INTO {0} () VALUES (); "
@@ -623,6 +623,23 @@ def tests_execute_multi(self):
623623

624624
cur.close()
625625

626+
# test statement property
627+
operations = [
628+
'select 1; SELECT "`";',
629+
"SELECT '\"'; SELECT 2; select '```';",
630+
"select 1; select '`'; select 3; select \"'''''\";",
631+
]
632+
control = [
633+
["select 1", 'SELECT "`"'],
634+
["SELECT '\"'", "SELECT 2", "select '```'"],
635+
["select 1", "select '`'", "select 3", "select \"'''''\""],
636+
]
637+
with self._get_cursor(self.cnx) as cur:
638+
for operation, exps in zip(operations, control):
639+
for res_cur, exp in zip(cur.execute(operation, multi=True), exps):
640+
self.assertEqual(exp, res_cur.statement)
641+
_ = res_cur.fetchall()
642+
626643

627644
class CExtMySQLCursorBufferedTests(tests.CMySQLCursorTests):
628645
def _get_cursor(self, cnx=None):

tests/test_cursor.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,25 @@ def test_execute(self):
628628
self.assertEqual(exp_result, results)
629629
self.cur.execute("DROP PROCEDURE multi_results")
630630

631+
def test_execute_multi(self):
632+
# Tests when execute(..., multi=True)
633+
operations = [
634+
'select 1; SELECT "`";',
635+
"SELECT '\"'; SELECT 2; select '```';",
636+
"select 1; select '`'; select 3; select \"'''''\";",
637+
]
638+
control = [
639+
["select 1", 'SELECT "`"'],
640+
["SELECT '\"'", "SELECT 2", "select '```'"],
641+
["select 1", "select '`'", "select 3", "select \"'''''\""],
642+
]
643+
self.cnx = connection.MySQLConnection(**tests.get_mysql_config())
644+
with self.cnx.cursor() as cur:
645+
for operation, exps in zip(operations, control):
646+
for res_cur, exp in zip(cur.execute(operation, multi=True), exps):
647+
self.assertEqual(exp, res_cur.statement)
648+
_ = res_cur.fetchall()
649+
631650
def test_executemany(self):
632651
"""MySQLCursor object executemany()-method"""
633652
self.check_method(self.cur, "executemany")

0 commit comments

Comments
 (0)