Skip to content

Commit c8474ce

Browse files
committed
BUG#27359063: Support for dictionary, named_tuple, and raw to prepared statements cursor
This bug report is an enhancement request regarding the available prepared cursor flavors. With this patch, two more options are added to the set of available cursor types: - `prepared + named_tuple` - `prepared + raw` The option `prepared + dictionary` was not added given it was implemented while fixing BUG#23339387. The three cursor types mentioned are available for both implementations; pure python and cext. Change-Id: Idd6276c1ac021ba759be6d5c5e93f18f1f41ed05
1 parent 4f67e51 commit c8474ce

File tree

7 files changed

+468
-29
lines changed

7 files changed

+468
-29
lines changed

CHANGES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ v8.0.33
1212
=======
1313

1414
- WL#15401: Support for type hints in module mysqlx
15+
- BUG#27359063: Support for dictionary, named_tuple, and raw to prepared statements cursor
1516

1617
v8.0.32
1718
=======

lib/mysql/connector/connection.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2009, 2022, Oracle and/or its affiliates.
1+
# Copyright (c) 2009, 2023, Oracle and/or its affiliates.
22
#
33
# This program is free software; you can redistribute it and/or modify
44
# it under the terms of the GNU General Public License, version 2.0, as
@@ -79,6 +79,8 @@
7979
MySQLCursorNamedTuple,
8080
MySQLCursorPrepared,
8181
MySQLCursorPreparedDict,
82+
MySQLCursorPreparedNamedTuple,
83+
MySQLCursorPreparedRaw,
8284
MySQLCursorRaw,
8385
)
8486
from .errors import (
@@ -1436,7 +1438,9 @@ def cursor(
14361438
8: MySQLCursorNamedTuple,
14371439
9: MySQLCursorBufferedNamedTuple,
14381440
16: MySQLCursorPrepared,
1441+
18: MySQLCursorPreparedRaw,
14391442
20: MySQLCursorPreparedDict,
1443+
24: MySQLCursorPreparedNamedTuple,
14401444
}
14411445
try:
14421446
return (types[cursor_type])(self)

lib/mysql/connector/connection_cext.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2014, 2022, Oracle and/or its affiliates.
1+
# Copyright (c) 2014, 2023, Oracle and/or its affiliates.
22
#
33
# This program is free software; you can redistribute it and/or modify
44
# it under the terms of the GNU General Public License, version 2.0, as
@@ -75,6 +75,8 @@
7575
CMySQLCursorNamedTuple,
7676
CMySQLCursorPrepared,
7777
CMySQLCursorPreparedDict,
78+
CMySQLCursorPreparedNamedTuple,
79+
CMySQLCursorPreparedRaw,
7880
CMySQLCursorRaw,
7981
)
8082
except ImportError as exc:
@@ -705,7 +707,9 @@ def cursor(
705707
8: CMySQLCursorNamedTuple,
706708
9: CMySQLCursorBufferedNamedTuple,
707709
16: CMySQLCursorPrepared,
710+
18: CMySQLCursorPreparedRaw,
708711
20: CMySQLCursorPreparedDict,
712+
24: CMySQLCursorPreparedNamedTuple,
709713
}
710714
try:
711715
return (types[cursor_type])(self)

lib/mysql/connector/cursor.py

Lines changed: 99 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2009, 2022, Oracle and/or its affiliates.
1+
# Copyright (c) 2009, 2023, Oracle and/or its affiliates.
22
#
33
# This program is free software; you can redistribute it and/or modify
44
# it under the terms of the GNU General Public License, version 2.0, as
@@ -1549,35 +1549,26 @@ def fetchone(self) -> Optional[RowType]:
15491549
Returns:
15501550
tuple or None: A row from query result set.
15511551
"""
1552-
self._check_executed()
1553-
row = self._fetch_row()
1554-
if row:
1555-
if hasattr(self._connection, "converter"):
1556-
return self._row_to_python(row, self.description)
1557-
return row
1558-
return None
1552+
row = super().fetchone()
1553+
if not row:
1554+
return None
1555+
return (
1556+
self._row_to_python(row, self.description)
1557+
if hasattr(self._connection, "converter")
1558+
else row
1559+
)
15591560

15601561
def fetchall(self) -> List[Optional[RowType]]:
15611562
"""Return all rows of a query result set.
15621563
15631564
Returns:
15641565
list: A list of tuples with all rows of a query result set.
15651566
"""
1566-
self._check_executed()
1567-
if not self._have_unread_result():
1568-
return []
1569-
1570-
(rows, eof) = self._connection.get_rows()
1571-
if self._nextrow[0]:
1572-
rows.insert(0, self._nextrow[0])
1573-
res = [self._row_to_python(row, self.description) for row in rows]
1574-
1575-
self._handle_eof(eof)
1576-
rowcount = len(rows)
1577-
if rowcount >= 0 and self._rowcount == -1:
1578-
self._rowcount = 0
1579-
self._rowcount += rowcount
1580-
return res
1567+
return [
1568+
self._row_to_python(row, self.description)
1569+
for row in super().fetchall()
1570+
if row
1571+
]
15811572

15821573

15831574
class MySQLCursorBufferedDict(MySQLCursorDict, MySQLCursorBuffered):
@@ -1680,3 +1671,88 @@ def fetchmany(
16801671
for row in super().fetchmany(size=size)
16811672
if row
16821673
]
1674+
1675+
1676+
class MySQLCursorPreparedNamedTuple(MySQLCursorNamedTuple, MySQLCursorPrepared):
1677+
"""
1678+
This class is a blend of features from MySQLCursorNamedTuple and MySQLCursorPrepared
1679+
"""
1680+
1681+
def fetchmany(self, size: Optional[int] = None) -> List[RowType]:
1682+
"""Return the next set of rows of a query result set.
1683+
1684+
When no more rows are available, it returns an empty list.
1685+
The number of rows returned can be specified using the size argument,
1686+
which defaults to one.
1687+
1688+
Returns:
1689+
list: The next set of rows of a query result set represented
1690+
as a list of named tuples where column names are used as names.
1691+
"""
1692+
return [
1693+
self._row_to_python(row, self.description)
1694+
for row in super().fetchmany(size=size)
1695+
if row
1696+
]
1697+
1698+
1699+
class MySQLCursorPreparedRaw(MySQLCursorPrepared):
1700+
"""
1701+
This class is a blend of features from MySQLCursorRaw and MySQLCursorPrepared
1702+
"""
1703+
1704+
_raw: bool = True
1705+
1706+
def fetchone(self) -> Optional[RowType]:
1707+
"""Return next row of a query result set.
1708+
1709+
Returns:
1710+
tuple or None: A row from query result set.
1711+
"""
1712+
self._check_executed()
1713+
if self._cursor_exists:
1714+
self._connection.cmd_stmt_fetch(self._prepared["statement_id"])
1715+
return self._fetch_row(raw=self._raw) or None
1716+
1717+
def fetchmany(self, size: Optional[int] = None) -> List[RowType]:
1718+
"""Return the next set of rows of a query result set.
1719+
1720+
When no more rows are available, it returns an empty list.
1721+
The number of rows returned can be specified using the size argument,
1722+
which defaults to one.
1723+
1724+
Returns:
1725+
list: The next set of rows of a query result set.
1726+
"""
1727+
self._check_executed()
1728+
res = []
1729+
cnt = size or self.arraysize
1730+
while cnt > 0 and self._have_unread_result():
1731+
cnt -= 1
1732+
row = self._fetch_row(raw=self._raw)
1733+
if row:
1734+
res.append(row)
1735+
return res
1736+
1737+
def fetchall(self) -> List[RowType]:
1738+
"""Return all rows of a query result set.
1739+
1740+
Returns:
1741+
list: A list of tuples with all rows of a query result set.
1742+
"""
1743+
self._check_executed()
1744+
rows = []
1745+
if self._nextrow[0]:
1746+
rows.append(self._nextrow[0])
1747+
while self._have_unread_result():
1748+
if self._cursor_exists:
1749+
self._connection.cmd_stmt_fetch(
1750+
self._prepared["statement_id"], MAX_RESULTS
1751+
)
1752+
(tmp, eof) = self._connection.get_rows(
1753+
raw=self._raw, binary=self._binary, columns=self.description
1754+
)
1755+
rows.extend(tmp)
1756+
self._handle_eof(eof)
1757+
self._rowcount = len(rows)
1758+
return rows

lib/mysql/connector/cursor_cext.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2014, 2022, Oracle and/or its affiliates.
1+
# Copyright (c) 2014, 2023, Oracle and/or its affiliates.
22
#
33
# This program is free software; you can redistribute it and/or modify
44
# it under the terms of the GNU General Public License, version 2.0, as
@@ -976,7 +976,7 @@ def fetchmany(self, size: int = 1) -> List[RowType]:
976976
res = super().fetchmany(size=size)
977977
if not res:
978978
return []
979-
return [self.named_tuple(*res[0])]
979+
return [self.named_tuple(*row) for row in res]
980980

981981
def fetchall(self) -> List[RowType]:
982982
"""Return all rows of a query result set.
@@ -1278,3 +1278,13 @@ class is searched once.
12781278
3. CMySQLCursorPrepared (right parent class)
12791279
4. CMySQLCursor (base class)
12801280
"""
1281+
1282+
1283+
class CMySQLCursorPreparedNamedTuple(CMySQLCursorNamedTuple, CMySQLCursorPrepared):
1284+
"""This class is a blend of features from CMySQLCursorNamedTuple and CMySQLCursorPrepared"""
1285+
1286+
1287+
class CMySQLCursorPreparedRaw(CMySQLCursorPrepared):
1288+
"""This class is a blend of features from CMySQLCursorRaw and CMySQLCursorPrepared"""
1289+
1290+
_raw: bool = True

0 commit comments

Comments
 (0)