Skip to content

Commit 98637f3

Browse files
committed
FLUSH PRIVILEGES is deprecated.
This statement is deprecated as of MySQL 9.2.0, and triggers a deprecation warning when used; you should expect FLUSH PRIVILEGES to be removed in a future version of MySQL. With this patch, a deprecation warning message is printed if users utilize the "refresh grant tables" option as part of the connection API command `cmd_refresh()` alongside a MySQL server 9.2.0 or newer. Change-Id: I4d4d20216beb5af63bcf085c99ad0e16307bc967
1 parent bcbde1b commit 98637f3

File tree

10 files changed

+214
-45
lines changed

10 files changed

+214
-45
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Copyright (c) 2009, 2024, Oracle and/or its affiliates.
2+
#
3+
# This program is free software; you can redistribute it and/or modify
4+
# it under the terms of the GNU General Public License, version 2.0, as
5+
# published by the Free Software Foundation.
6+
#
7+
# This program is designed to work with certain software (including
8+
# but not limited to OpenSSL) that is licensed under separate terms,
9+
# as designated in a particular file or component or in included license
10+
# documentation. The authors of MySQL hereby grant you an
11+
# additional permission to link the program and your derivative works
12+
# with the separately licensed software that they have either included with
13+
# the program or referenced in the documentation.
14+
#
15+
# Without limiting anything contained in the foregoing, this file,
16+
# which is part of MySQL Connector/Python, is also subject to the
17+
# Universal FOSS Exception, version 1.0, a copy of which can be found at
18+
# http://oss.oracle.com/licenses/universal-foss-exception.
19+
#
20+
# This program is distributed in the hope that it will be useful, but
21+
# WITHOUT ANY WARRANTY; without even the implied warranty of
22+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
23+
# See the GNU General Public License, version 2.0, for more details.
24+
#
25+
# You should have received a copy of the GNU General Public License
26+
# along with this program; if not, write to the Free Software Foundation, Inc.,
27+
# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
28+
29+
"""Decorators Hub."""
30+
31+
import functools
32+
from typing import TYPE_CHECKING, Any, Callable
33+
import warnings
34+
from .constants import RefreshOption
35+
36+
if TYPE_CHECKING:
37+
from .abstracts import MySQLConnectionAbstract
38+
39+
40+
def cmd_refresh_verify_options() -> Callable:
41+
"""Decorator verifying which options are relevant and which aren't based on
42+
the server version the client is connecting to."""
43+
44+
def decorator(func: Callable) -> Callable:
45+
@functools.wraps(func)
46+
def wrapper(
47+
cnx: "MySQLConnectionAbstract", *args: Any, **kwargs: Any
48+
) -> Callable:
49+
options: int = args[0]
50+
if (options & RefreshOption.GRANT) and cnx.get_server_version() >= (
51+
9,
52+
2,
53+
0,
54+
):
55+
warnings.warn(
56+
"As of MySQL Server 9.2.0, refreshing grant tables is not needed "
57+
"if you use statements GRANT, REVOKE, CREATE, DROP, or ALTER. "
58+
"You should expect this option to be unsupported in a future "
59+
"version of MySQL Connector/Python when MySQL Server removes it.",
60+
category=DeprecationWarning,
61+
stacklevel=1,
62+
)
63+
64+
return func(cnx, options, **kwargs)
65+
66+
return wrapper
67+
68+
return decorator

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

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@
116116
RowItemType,
117117
RowType,
118118
StrOrBytes,
119-
WarningType,
119+
WarningType
120120
)
121121
from .utils import GenericWrapper, import_object
122122

@@ -1934,32 +1934,31 @@ def cmd_query_iter(
19341934
"""
19351935

19361936
@abstractmethod
1937-
def cmd_refresh(self, options: int) -> Optional[Dict[str, Any]]:
1938-
"""Sends the Refresh command to the MySQL server.
1937+
def cmd_refresh(self, options: int) -> Dict[str, Any]:
1938+
"""Send the Refresh command to the MySQL server.
19391939
1940-
`WARNING: This MySQL Server functionality is deprecated.`
1940+
This method sends the Refresh command to the MySQL server. The options
1941+
argument should be a bitwise value using constants.RefreshOption.
19411942
1942-
This method flushes tables or caches, or resets replication server
1943-
information. The connected user must have the RELOAD privilege.
1944-
1945-
The options argument should be a bitmask value constructed using
1946-
constants from the `constants.RefreshOption` class.
1947-
1948-
The result is a dictionary with the OK packet information.
1943+
Typical usage example:
1944+
```
1945+
RefreshOption = mysql.connector.RefreshOption
1946+
refresh = RefreshOption.LOG | RefreshOption.INFO
1947+
cnx.cmd_refresh(refresh)
1948+
```
19491949
19501950
Args:
19511951
options: Bitmask value constructed using constants from
19521952
the `constants.RefreshOption` class.
19531953
19541954
Returns:
1955-
dictionary: OK packet information.
1955+
A dictionary representing the OK packet got as response when executing
1956+
the command.
19561957
1957-
Examples:
1958-
```
1959-
>>> from mysql.connector import RefreshOption
1960-
>>> refresh = RefreshOption.LOG | RefreshOption.THREADS
1961-
>>> cnx.cmd_refresh(refresh)
1962-
```
1958+
Raises:
1959+
ValueError: If an invalid command `refresh options` is provided.
1960+
DeprecationWarning: If one of the options is deprecated for the server you
1961+
are connecting to.
19631962
"""
19641963

19651964
@abstractmethod
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Copyright (c) 2009, 2024, Oracle and/or its affiliates.
2+
#
3+
# This program is free software; you can redistribute it and/or modify
4+
# it under the terms of the GNU General Public License, version 2.0, as
5+
# published by the Free Software Foundation.
6+
#
7+
# This program is designed to work with certain software (including
8+
# but not limited to OpenSSL) that is licensed under separate terms,
9+
# as designated in a particular file or component or in included license
10+
# documentation. The authors of MySQL hereby grant you an
11+
# additional permission to link the program and your derivative works
12+
# with the separately licensed software that they have either included with
13+
# the program or referenced in the documentation.
14+
#
15+
# Without limiting anything contained in the foregoing, this file,
16+
# which is part of MySQL Connector/Python, is also subject to the
17+
# Universal FOSS Exception, version 1.0, a copy of which can be found at
18+
# http://oss.oracle.com/licenses/universal-foss-exception.
19+
#
20+
# This program is distributed in the hope that it will be useful, but
21+
# WITHOUT ANY WARRANTY; without even the implied warranty of
22+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
23+
# See the GNU General Public License, version 2.0, for more details.
24+
#
25+
# You should have received a copy of the GNU General Public License
26+
# along with this program; if not, write to the Free Software Foundation, Inc.,
27+
# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
28+
29+
"""Decorators Hub."""
30+
31+
import functools
32+
from typing import TYPE_CHECKING, Any, Callable
33+
import warnings
34+
from ..constants import RefreshOption
35+
36+
if TYPE_CHECKING:
37+
from .abstracts import MySQLConnectionAbstract
38+
39+
40+
def cmd_refresh_verify_options() -> Callable:
41+
"""Decorator verifying which options are relevant and which aren't based on
42+
the server version the client is connecting to."""
43+
44+
def decorator(func: Callable) -> Callable:
45+
@functools.wraps(func)
46+
async def wrapper(
47+
cnx: "MySQLConnectionAbstract", *args: Any, **kwargs: Any
48+
) -> Callable:
49+
options: int = args[0]
50+
if (options & RefreshOption.GRANT) and cnx.get_server_version() >= (
51+
9,
52+
2,
53+
0,
54+
):
55+
warnings.warn(
56+
"As of MySQL Server 9.2.0, refreshing grant tables is not needed "
57+
"if you use statements GRANT, REVOKE, CREATE, DROP, or ALTER. "
58+
"You should expect this option to be unsupported in a future "
59+
"version of MySQL Connector/Python when MySQL Server removes it.",
60+
category=DeprecationWarning,
61+
stacklevel=1,
62+
)
63+
64+
return await func(cnx, options, **kwargs)
65+
66+
return wrapper
67+
68+
return decorator

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

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1119,7 +1119,7 @@ def get_server_version(self) -> Optional[Tuple[int, ...]]:
11191119
return `None`.
11201120
"""
11211121
if self._server_info is not None:
1122-
return self._server_info.version # type: ignore[return-value]
1122+
return self._server_info.version_tuple
11231123
return None
11241124

11251125
def get_server_info(self) -> Optional[str]:
@@ -1507,10 +1507,25 @@ async def cmd_refresh(self, options: int) -> OkPacketType:
15071507
This method sends the Refresh command to the MySQL server. The options
15081508
argument should be a bitwise value using constants.RefreshOption.
15091509
1510-
Usage example:
1510+
Typical usage example:
1511+
```
15111512
RefreshOption = mysql.connector.RefreshOption
15121513
refresh = RefreshOption.LOG | RefreshOption.INFO
1513-
cnx.cmd_refresh(refresh)
1514+
await cnx.cmd_refresh(refresh)
1515+
```
1516+
1517+
Args:
1518+
options: Bitmask value constructed using constants from
1519+
the `constants.RefreshOption` class.
1520+
1521+
Returns:
1522+
A dictionary representing the OK packet got as response when executing
1523+
the command.
1524+
1525+
Raises:
1526+
ValueError: If an invalid command `refresh options` is provided.
1527+
DeprecationWarning: If one of the options is deprecated for the server you
1528+
are connecting to.
15141529
"""
15151530

15161531
async def cmd_stmt_send_long_data(

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

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@
114114
)
115115
from .logger import logger
116116
from .network import MySQLTcpSocket, MySQLUnixSocket
117+
from ._decorating import cmd_refresh_verify_options
117118

118119

119120
class MySQLConnection(MySQLConnectionAbstract):
@@ -1164,17 +1165,8 @@ async def cmd_stmt_close(self, statement_id: int) -> None:
11641165
expect_response=False,
11651166
)
11661167

1168+
@cmd_refresh_verify_options()
11671169
async def cmd_refresh(self, options: int) -> OkPacketType:
1168-
"""Send the Refresh command to the MySQL server.
1169-
1170-
This method sends the Refresh command to the MySQL server. The options
1171-
argument should be a bitwise value using constants.RefreshOption.
1172-
1173-
Usage example:
1174-
RefreshOption = mysql.connector.RefreshOption
1175-
refresh = RefreshOption.LOG | RefreshOption.INFO
1176-
cnx.cmd_refresh(refresh)
1177-
"""
11781170
if not options & (
11791171
RefreshOption.GRANT
11801172
| RefreshOption.LOG

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

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@
117117
warn_ciphersuites_deprecated,
118118
warn_tls_version_deprecated,
119119
)
120+
from ._decorating import cmd_refresh_verify_options
120121

121122
if TYPE_CHECKING:
122123
from .abstracts import CMySQLPrepStmt
@@ -927,16 +928,8 @@ def cmd_query_iter(
927928
self.handle_unread_result()
928929
yield self._handle_result(self._socket.recv())
929930

931+
@cmd_refresh_verify_options()
930932
def cmd_refresh(self, options: int) -> OkPacketType:
931-
"""Send the Refresh command to the MySQL server
932-
933-
This method sends the Refresh command to the MySQL server. The options
934-
argument should be a bitwise value using constants.RefreshOption.
935-
Usage example:
936-
RefreshOption = mysql.connector.RefreshOption
937-
refresh = RefreshOption.LOG | RefreshOption.THREADS
938-
cnx.cmd_refresh(refresh)
939-
"""
940933
if not options & (
941934
RefreshOption.GRANT
942935
| RefreshOption.LOG

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181
warn_ciphersuites_deprecated,
8282
warn_tls_version_deprecated,
8383
)
84+
from ._decorating import cmd_refresh_verify_options
8485

8586
HAVE_CMYSQL = False
8687

@@ -959,8 +960,8 @@ def cmd_reset_connection(self) -> bool:
959960
self._post_connection()
960961
return res
961962

963+
@cmd_refresh_verify_options()
962964
def cmd_refresh(self, options: int) -> Optional[CextEofPacketType]:
963-
"""Send the Refresh command to the MySQL server"""
964965
try:
965966
self.handle_unread_result()
966967
self._cmysql.refresh(options)

mysql-connector-python/tests/cext/test_cext_connection.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,17 @@ def test_cmd_refresh(self):
182182
RefreshOption.STATUS,
183183
RefreshOption.REPLICA,
184184
)
185+
186+
# test individual options
185187
for option in refresh_options:
186-
self.assertEqual(OK_PACKET_RESULT, self.cnx.cmd_refresh(option))
188+
if tests.MYSQL_VERSION >= (9, 2, 0) and option == RefreshOption.GRANT:
189+
with self.assertWarns(DeprecationWarning):
190+
ok_packet = self.cnx.cmd_refresh(option)
191+
self.assertEqual(
192+
{**OK_PACKET_RESULT, **{"warning_count": 1}}, ok_packet
193+
)
194+
else:
195+
self.assertEqual(OK_PACKET_RESULT, self.cnx.cmd_refresh(option))
187196

188197
# Test combined options
189198
options = RefreshOption.LOG | RefreshOption.STATUS

mysql-connector-python/tests/test_aio_connection.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -550,8 +550,17 @@ async def test_cmd_refresh(self):
550550
RefreshOption.STATUS,
551551
RefreshOption.REPLICA,
552552
)
553+
554+
# test individual options
553555
for option in refresh_options:
554-
self.assertEqual(OK_PACKET_RESULT, await self.cnx.cmd_refresh(option))
556+
if tests.MYSQL_VERSION >= (9, 2, 0) and option == RefreshOption.GRANT:
557+
with self.assertWarns(DeprecationWarning):
558+
ok_packet = await self.cnx.cmd_refresh(option)
559+
self.assertEqual(
560+
{**OK_PACKET_RESULT, **{"warning_count": 1}}, ok_packet
561+
)
562+
else:
563+
self.assertEqual(OK_PACKET_RESULT, await self.cnx.cmd_refresh(option))
555564

556565
# Test combined options
557566
options = RefreshOption.LOG | RefreshOption.STATUS
@@ -1097,7 +1106,10 @@ class TestConverter(MySQLConverterBase): ...
10971106
@foreach_cnx_aio()
10981107
async def test_get_server_version(self):
10991108
"""Get the MySQL version"""
1100-
self.assertEqual(self.cnx._server_info.version, self.cnx.get_server_version())
1109+
self.assertIn(
1110+
".".join([str(x) for x in self.cnx.get_server_version()]),
1111+
self.cnx._server_info.version,
1112+
)
11011113

11021114
@foreach_cnx_aio()
11031115
async def test_get_server_info(self):

mysql-connector-python/tests/test_connection.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -583,8 +583,20 @@ def test_cmd_refresh(self):
583583
constants.RefreshOption.STATUS,
584584
constants.RefreshOption.REPLICA,
585585
)
586+
587+
# test individual options
586588
for option in refresh_options:
587-
self.assertEqual(OK_PACKET_RESULT, self.cnx.cmd_refresh(option))
589+
if (
590+
tests.MYSQL_VERSION >= (9, 2, 0)
591+
and option == constants.RefreshOption.GRANT
592+
):
593+
with self.assertWarns(DeprecationWarning):
594+
ok_packet = self.cnx.cmd_refresh(option)
595+
self.assertEqual(
596+
{**OK_PACKET_RESULT, **{"warning_count": 1}}, ok_packet
597+
)
598+
else:
599+
self.assertEqual(OK_PACKET_RESULT, self.cnx.cmd_refresh(option))
588600

589601
# Test combined options
590602
options = constants.RefreshOption.LOG | constants.RefreshOption.STATUS

0 commit comments

Comments
 (0)