Skip to content

Commit 5de2df5

Browse files
committed
BUG#34223015: Invalidate the usage of non-compatible cursor types
Connector/Python c-ext and pure Python cursors types are not interchangeable when using connection objects that aren't of the same type. Therefore, c-ext connection objects can't deal with pure Python cursor objects and vice-versa. This patch fixes this issue by checking if the provided cursor class uses CMySQLCursor as the base class. Change-Id: I8941360bc816961c5d5cd5540a5398dfea7bb3d8
1 parent 4fb79de commit 5de2df5

File tree

4 files changed

+68
-2
lines changed

4 files changed

+68
-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.30
1818
- WL#14822: Refactor the authentication plugin mechanism
1919
- WL#14815: Support OpenSSL 3.0
2020
- BUG#34228442: Fix NO_BACKSLASH_ESCAPES SQL mode support in c-ext
21+
- BUG#34223015: Invalidate the usage of non-compatible cursor types
2122
- BUG#34127959: Add isolation level support in Django backend
2223
- BUG#33923516: Allow tuple of dictionaries as "failover" argument
2324
- BUG#28821983: Fix rounding errors for decimal values

lib/mysql/connector/connection_cext.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -621,7 +621,7 @@ def cursor(
621621
if not self.is_connected():
622622
raise OperationalError("MySQL Connection not available.")
623623
if cursor_class is not None:
624-
if not issubclass(cursor_class, MySQLCursorAbstract):
624+
if not issubclass(cursor_class, CMySQLCursor):
625625
raise ProgrammingError(
626626
"Cursor class needs be to subclass of cursor_cext.CMySQLCursor"
627627
)

tests/cext/test_cext_connection.py

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535

3636
import tests
3737

38-
from mysql.connector import errors
38+
from mysql.connector import cursor, cursor_cext, errors
3939
from mysql.connector.connection import MySQLConnection
4040
from mysql.connector.connection_cext import CMySQLConnection
4141
from mysql.connector.constants import ClientFlag, flag_is_set
@@ -158,3 +158,56 @@ def test_connection_id(self):
158158
self.cnx.close()
159159
self.assertIsNone(self.cnx.connection_id)
160160
self.cnx.connect()
161+
162+
def test_cursor(self):
163+
"""Test CEXT cursors."""
164+
165+
class FalseCursor:
166+
...
167+
168+
class TrueCursor(cursor_cext.CMySQLCursor):
169+
...
170+
171+
self.assertRaises(
172+
errors.ProgrammingError, self.cnx.cursor, cursor_class=FalseCursor
173+
)
174+
self.assertTrue(
175+
isinstance(self.cnx.cursor(cursor_class=TrueCursor), TrueCursor)
176+
)
177+
178+
self.assertRaises(
179+
errors.ProgrammingError, self.cnx.cursor, cursor_class=cursor.MySQLCursor
180+
)
181+
self.assertRaises(
182+
errors.ProgrammingError,
183+
self.cnx.cursor,
184+
cursor_class=cursor.MySQLCursorBuffered,
185+
)
186+
187+
cases = [
188+
({}, cursor_cext.CMySQLCursor),
189+
({"buffered": True}, cursor_cext.CMySQLCursorBuffered),
190+
({"raw": True}, cursor_cext.CMySQLCursorRaw),
191+
({"buffered": True, "raw": True}, cursor_cext.CMySQLCursorBufferedRaw),
192+
({"prepared": True}, cursor_cext.CMySQLCursorPrepared),
193+
({"dictionary": True}, cursor_cext.CMySQLCursorDict),
194+
({"named_tuple": True}, cursor_cext.CMySQLCursorNamedTuple),
195+
(
196+
{"dictionary": True, "buffered": True},
197+
cursor_cext.CMySQLCursorBufferedDict,
198+
),
199+
(
200+
{"named_tuple": True, "buffered": True},
201+
cursor_cext.CMySQLCursorBufferedNamedTuple,
202+
),
203+
]
204+
for kwargs, exp in cases:
205+
self.assertTrue(isinstance(self.cnx.cursor(**kwargs), exp))
206+
207+
self.assertRaises(ValueError, self.cnx.cursor, prepared=True, buffered=True)
208+
self.assertRaises(ValueError, self.cnx.cursor, dictionary=True, raw=True)
209+
self.assertRaises(ValueError, self.cnx.cursor, named_tuple=True, raw=True)
210+
211+
# Test when connection is closed
212+
self.cnx.close()
213+
self.assertRaises(errors.OperationalError, self.cnx.cursor)

tests/test_connection.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1694,6 +1694,18 @@ def __init__(self, cnx=None):
16941694
isinstance(self.cnx.cursor(cursor_class=TrueCursor), TrueCursor)
16951695
)
16961696

1697+
if HAVE_CMYSQL:
1698+
self.assertRaises(
1699+
errors.ProgrammingError,
1700+
self.cnx.cursor,
1701+
cursor_class=cursor_cext.CMySQLCursor,
1702+
)
1703+
self.assertRaises(
1704+
errors.ProgrammingError,
1705+
self.cnx.cursor,
1706+
cursor_class=cursor_cext.CMySQLCursorBufferedRaw,
1707+
)
1708+
16971709
cases = [
16981710
({}, cursor.MySQLCursor),
16991711
({"buffered": True}, cursor.MySQLCursorBuffered),

0 commit comments

Comments
 (0)