Skip to content

Commit c4a828b

Browse files
committed
BUG#36289767: MySQLCursorBufferedRaw do not skip conversion
Any raw-like cursor flavor using the Text Protocol is expected to return values as byte strings, therefore skipping the conversion layer. This patch fixes the behavior of MySQLCursorBufferedRaw as well as improves the code structure of all the cursors. Change-Id: I063101e782646f8d3e4c3b2b046bdba991302891
1 parent 30cdc69 commit c4a828b

15 files changed

+315
-541
lines changed

CHANGES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ v9.0.0
1313

1414
- WL#16350: Update dnspython version
1515
- BUG#36476195: Incorrect escaping in pure Python mode if sql_mode includes NO_BACKSLASH_ESCAPES
16+
- BUG#36289767: MySQLCursorBufferedRaw does not skip conversion
1617

1718
v8.4.0
1819
======

mysql-connector-python/lib/mysql/connector/abstracts.py

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,9 @@
106106
from .tls_ciphers import UNACCEPTABLE_TLS_CIPHERSUITES, UNACCEPTABLE_TLS_VERSIONS
107107
from .types import (
108108
BinaryProtocolType,
109+
CextEofPacketType,
109110
DescriptionType,
111+
EofPacketType,
110112
HandShakeType,
111113
MySQLConvertibleType,
112114
RowItemType,
@@ -1516,7 +1518,7 @@ def cursor(
15161518
but not raw.
15171519
15181520
It is possible to also give a custom cursor through the `cursor_class`
1519-
parameter, but it needs to be a subclass of `mysql.connector.cursor.CursorBase`
1521+
parameter, but it needs to be a subclass of `mysql.connector.cursor.MySQLCursor`
15201522
or `mysql.connector.cursor_cext.CMySQLCursor` according to the type of
15211523
connection that's being used.
15221524
@@ -1529,9 +1531,9 @@ def cursor(
15291531
better performance or when you want to do the conversion yourself.
15301532
prepared: If `True`, the cursor is used for executing prepared statements.
15311533
cursor_class: It can be used to pass a class to use for instantiating a
1532-
new cursor. It must be a subclass of `cursor.CursorBase` or
1533-
`cursor_cext.CMySQLCursor` according to the type of connection
1534-
that's being used.
1534+
new cursor. It must be a subclass of `cursor.MySQLCursor`
1535+
or `cursor_cext.CMySQLCursor` according to the type of
1536+
connection that's being used.
15351537
dictionary: If `True`, the cursor returns rows as dictionaries.
15361538
named_tuple: If `True`, the cursor returns rows as named tuples.
15371539
@@ -2164,8 +2166,15 @@ class MySQLCursorAbstract(ABC):
21642166
required by the Python Database API Specification v2.0.
21652167
"""
21662168

2167-
def __init__(self) -> None:
2168-
"""Initialization"""
2169+
def __init__(self, connection: Optional[MySQLConnectionAbstract] = None) -> None:
2170+
"""Defines the MySQL cursor interface."""
2171+
2172+
self._connection: Optional[MySQLConnectionAbstract] = connection
2173+
if connection is not None:
2174+
if not isinstance(connection, MySQLConnectionAbstract):
2175+
raise InterfaceError(errno=2048)
2176+
self._connection = weakref.proxy(connection)
2177+
21692178
self._description: Optional[List[DescriptionType]] = None
21702179
self._rowcount: int = -1
21712180
self._last_insert_id: Optional[int] = None
@@ -2175,6 +2184,14 @@ def __init__(self) -> None:
21752184
self._executed_list: List[StrOrBytes] = []
21762185
self._stored_results: List[MySQLCursorAbstract] = []
21772186
self.arraysize: int = 1
2187+
self._binary: bool = False
2188+
self._raw: bool = False
2189+
self._nextrow: Tuple[
2190+
Optional[RowType], Optional[Union[EofPacketType, CextEofPacketType]]
2191+
] = (
2192+
None,
2193+
None,
2194+
)
21782195

21792196
def __enter__(self) -> MySQLCursorAbstract:
21802197
return self
@@ -2431,7 +2448,6 @@ def reset(self, free: bool = True) -> None:
24312448
"""Resets the cursor to default"""
24322449

24332450
@property
2434-
@abstractmethod
24352451
def description(self) -> Optional[List[DescriptionType]]:
24362452
"""This read-only property returns a list of tuples describing the columns in a
24372453
result set.
@@ -2460,7 +2476,6 @@ def description(self) -> Optional[List[DescriptionType]]:
24602476
return self._description
24612477

24622478
@property
2463-
@abstractmethod
24642479
def rowcount(self) -> int:
24652480
"""Returns the number of rows produced or affected.
24662481
@@ -2539,8 +2554,6 @@ def get_attributes(self) -> Optional[List[Tuple[str, BinaryProtocolType]]]:
25392554
Returns:
25402555
List of existing query attributes.
25412556
"""
2542-
if hasattr(self, "_cnx"):
2543-
return self._cnx.query_attrs
25442557
if hasattr(self, "_connection"):
25452558
return self._connection.query_attrs
25462559
return None
@@ -2565,9 +2578,7 @@ def add_attribute(self, name: str, value: BinaryProtocolType) -> None:
25652578
raise ProgrammingError(
25662579
f"Object {value} cannot be converted to a MySQL type"
25672580
)
2568-
if hasattr(self, "_cnx"):
2569-
self._cnx.query_attrs_append((name, value))
2570-
elif hasattr(self, "_connection"):
2581+
if hasattr(self, "_connection"):
25712582
self._connection.query_attrs_append((name, value))
25722583

25732584
def remove_attribute(self, name: str) -> BinaryProtocolType:
@@ -2583,15 +2594,11 @@ def remove_attribute(self, name: str) -> BinaryProtocolType:
25832594
"""
25842595
if not isinstance(name, str):
25852596
raise ProgrammingError("Parameter `name` must be a string type")
2586-
if hasattr(self, "_cnx"):
2587-
return self._cnx.query_attrs_remove(name)
25882597
if hasattr(self, "_connection"):
25892598
return self._connection.query_attrs_remove(name)
25902599
return None
25912600

25922601
def clear_attributes(self) -> None:
25932602
"""Clears the list of query attributes on the connector's side."""
2594-
if hasattr(self, "_cnx"):
2595-
self._cnx.query_attrs_clear()
2596-
elif hasattr(self, "_connection"):
2603+
if hasattr(self, "_connection"):
25972604
self._connection.query_attrs_clear()

mysql-connector-python/lib/mysql/connector/aio/abstracts.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1289,7 +1289,7 @@ async def cursor(
12891289
12901290
Raises:
12911291
ProgrammingError: When cursor_class is not a subclass of
1292-
CursorBase.
1292+
MySQLCursor.
12931293
ValueError: When cursor is not available.
12941294
"""
12951295

@@ -1773,8 +1773,6 @@ def get_attributes(self) -> Optional[List[Tuple[str, BinaryProtocolType]]]:
17731773
Returns:
17741774
List of existing query attributes.
17751775
"""
1776-
if hasattr(self, "_cnx"):
1777-
return self._cnx.query_attrs
17781776
if hasattr(self, "_connection"):
17791777
return self._connection.query_attrs
17801778
return None

mysql-connector-python/lib/mysql/connector/aio/connection.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -713,7 +713,7 @@ async def cursor(
713713
714714
Raises:
715715
ProgrammingError: When cursor_class is not a subclass of
716-
CursorBase.
716+
MySQLCursor.
717717
ValueError: When cursor is not available.
718718
"""
719719
if not self._socket or not self._socket.is_connected():

mysql-connector-python/lib/mysql/connector/aio/cursor.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -847,7 +847,7 @@ async def _handle_resultset(self) -> None:
847847
column information in _handle_result(). For non-buffering cursors, this method
848848
is usually doing nothing.
849849
"""
850-
self._rows, eof = await self._connection.get_rows()
850+
self._rows, eof = await self._connection.get_rows(raw=self._raw)
851851
self._rowcount = len(self._rows)
852852
await self._handle_eof(eof)
853853
self._next_row = 0
@@ -936,7 +936,9 @@ class MySQLCursorBufferedRaw(MySQLCursorBuffered):
936936
fetching rows and fetches rows within execute().
937937
"""
938938

939-
_raw: bool = True
939+
def __init__(self, connection: MySQLConnectionAbstract) -> None:
940+
super().__init__(connection)
941+
self._raw: bool = True
940942

941943
@property
942944
def with_rows(self) -> bool:
@@ -1447,7 +1449,9 @@ class MySQLCursorPreparedRaw(MySQLCursorPrepared):
14471449
This class is a blend of features from MySQLCursorRaw and MySQLCursorPrepared
14481450
"""
14491451

1450-
_raw: bool = True
1452+
def __init__(self, connection: MySQLConnectionAbstract):
1453+
super().__init__(connection)
1454+
self._raw: bool = True
14511455

14521456
async def fetchone(self) -> Optional[RowType]:
14531457
"""Return next row of a query result set.

mysql-connector-python/lib/mysql/connector/connection.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@
6969
)
7070
from .conversion import MySQLConverter
7171
from .cursor import (
72-
CursorBase,
7372
MySQLCursor,
7473
MySQLCursorBuffered,
7574
MySQLCursorBufferedDict,
@@ -1227,10 +1226,10 @@ def cursor(
12271226
12281227
It is possible to also give a custom cursor through the
12291228
cursor_class parameter, but it needs to be a subclass of
1230-
mysql.connector.cursor.CursorBase.
1229+
mysql.connector.cursor.MySQLCursor.
12311230
12321231
Raises ProgrammingError when cursor_class is not a subclass of
1233-
CursorBase. Raises ValueError when cursor is not available.
1232+
MySQLCursor. Raises ValueError when cursor is not available.
12341233
12351234
Returns a cursor-object
12361235
"""
@@ -1239,9 +1238,9 @@ def cursor(
12391238
if not self.is_connected():
12401239
raise OperationalError("MySQL Connection not available")
12411240
if cursor_class is not None:
1242-
if not issubclass(cursor_class, CursorBase):
1241+
if not issubclass(cursor_class, MySQLCursor):
12431242
raise ProgrammingError(
1244-
"Cursor class needs be to subclass of cursor.CursorBase"
1243+
"Cursor class needs be to subclass of MySQLCursor"
12451244
)
12461245
return (cursor_class)(self)
12471246

mysql-connector-python/lib/mysql/connector/connection_cext.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -746,7 +746,7 @@ def cursor(
746746
mysql.connector.cursor_cext.CMySQLCursor.
747747
748748
Raises ProgrammingError when cursor_class is not a subclass of
749-
CursorBase. Raises ValueError when cursor is not available.
749+
CMySQLCursor. Raises ValueError when cursor is not available.
750750
751751
Returns instance of CMySQLCursor or subclass.
752752

0 commit comments

Comments
 (0)