Skip to content

Commit a611aee

Browse files
committed
BUG#37275524: Exception is not interpreted properly on prepared
statements when C extension is in use This patch fixes the issue where incorrect error class is being raised by the connector while working with the prepared statements using C-Extension by ensuring that correct exception structure is maintained while forwarding the exception from C-API to the connector interface. Change-Id: Idfa0ee340dcd4f4b69cf27893f67278074ae8250
1 parent e56c4ec commit a611aee

File tree

8 files changed

+134
-72
lines changed

8 files changed

+134
-72
lines changed

CHANGES.txt

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ v9.3.0
1919
- BUG#37447394: Unable to escape a parameter marker (`%s`) used in a query that should not be treated as a parameter marker
2020
- BUG#37418436: Arbitrary File Read in MySQL Python Client library
2121
- BUG#37399636: The C-extension has a memory leak when working with prepared statements
22+
- BUG#37275524: Exception is not interpreted properly on prepared statements when C extension is in use
2223
- BUG#36098290: mysql-connector-python is distributed without setup.py or pyproject.toml
2324

2425
v9.2.0

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

+79-41
Original file line numberDiff line numberDiff line change
@@ -214,9 +214,11 @@ def autocommit(self, value: bool) -> None:
214214
self._cmysql.autocommit(value)
215215
self._autocommit = value
216216
except MySQLInterfaceError as err:
217-
raise get_mysql_exception(
218-
msg=err.msg, errno=err.errno, sqlstate=err.sqlstate
219-
) from err
217+
if hasattr(err, "errno"):
218+
raise get_mysql_exception(
219+
err.errno, msg=err.msg, sqlstate=err.sqlstate
220+
) from err
221+
raise InterfaceError(str(err)) from err
220222

221223
@property
222224
def read_timeout(self) -> Optional[int]:
@@ -255,9 +257,11 @@ def database(self, value: str) -> None:
255257
try:
256258
self._cmysql.select_db(value)
257259
except MySQLInterfaceError as err:
258-
raise get_mysql_exception(
259-
msg=err.msg, errno=err.errno, sqlstate=err.sqlstate
260-
) from err
260+
if hasattr(err, "errno"):
261+
raise get_mysql_exception(
262+
err.errno, msg=err.msg, sqlstate=err.sqlstate
263+
) from err
264+
raise InterfaceError(str(err)) from err
261265

262266
@property
263267
def in_transaction(self) -> bool:
@@ -352,9 +356,11 @@ def _open_connection(self) -> None:
352356
if self.converter:
353357
self.converter.str_fallback = self._converter_str_fallback
354358
except MySQLInterfaceError as err:
355-
raise get_mysql_exception(
356-
msg=err.msg, errno=err.errno, sqlstate=err.sqlstate
357-
) from err
359+
if hasattr(err, "errno"):
360+
raise get_mysql_exception(
361+
err.errno, msg=err.msg, sqlstate=err.sqlstate
362+
) from err
363+
raise InterfaceError(str(err)) from err
358364

359365
self._do_handshake()
360366

@@ -386,9 +392,11 @@ def close(self) -> None:
386392
except MySQLInterfaceError as err:
387393
if OTEL_ENABLED:
388394
record_exception_event(self._span, err)
389-
raise get_mysql_exception(
390-
msg=err.msg, errno=err.errno, sqlstate=err.sqlstate
391-
) from err
395+
if hasattr(err, "errno"):
396+
raise get_mysql_exception(
397+
err.errno, msg=err.msg, sqlstate=err.sqlstate
398+
) from err
399+
raise InterfaceError(str(err)) from err
392400
finally:
393401
if OTEL_ENABLED:
394402
end_span(self._span)
@@ -452,9 +460,11 @@ def info_query(self, query: StrOrBytes) -> Optional[RowType]:
452460
raise InterfaceError("Query should not return more than 1 row")
453461
self._cmysql.free_result()
454462
except MySQLInterfaceError as err:
455-
raise get_mysql_exception(
456-
msg=err.msg, errno=err.errno, sqlstate=err.sqlstate
457-
) from err
463+
if hasattr(err, "errno"):
464+
raise get_mysql_exception(
465+
err.errno, msg=err.msg, sqlstate=err.sqlstate
466+
) from err
467+
raise InterfaceError(str(err)) from err
458468

459469
return first_row
460470

@@ -542,11 +552,13 @@ def get_rows(
542552
except MySQLInterfaceError as err:
543553
if prep_stmt:
544554
prep_stmt.free_result()
545-
raise InterfaceError(str(err)) from err
546-
self.free_result()
547-
raise get_mysql_exception(
548-
msg=err.msg, errno=err.errno, sqlstate=err.sqlstate
549-
) from err
555+
else:
556+
self.free_result()
557+
if hasattr(err, "errno"):
558+
raise get_mysql_exception(
559+
err.errno, msg=err.msg, sqlstate=err.sqlstate
560+
) from err
561+
raise InterfaceError(str(err)) from err
550562

551563
return rows, _eof
552564

@@ -603,9 +615,11 @@ def cmd_init_db(self, database: str) -> None:
603615
try:
604616
self._cmysql.select_db(database)
605617
except MySQLInterfaceError as err:
606-
raise get_mysql_exception(
607-
msg=err.msg, errno=err.errno, sqlstate=err.sqlstate
608-
) from err
618+
if hasattr(err, "errno"):
619+
raise get_mysql_exception(
620+
err.errno, msg=err.msg, sqlstate=err.sqlstate
621+
) from err
622+
raise InterfaceError(str(err)) from err
609623

610624
def fetch_eof_columns(
611625
self, prep_stmt: Optional[CMySQLPrepStmt] = None
@@ -669,6 +683,10 @@ def cmd_stmt_prepare(
669683
stmt.converter_str_fallback = self._converter_str_fallback
670684
return CMySQLPrepStmt(stmt)
671685
except MySQLInterfaceError as err:
686+
if hasattr(err, "errno"):
687+
raise get_mysql_exception(
688+
err.errno, msg=err.msg, sqlstate=err.sqlstate
689+
) from err
672690
raise InterfaceError(str(err)) from err
673691

674692
@with_context_propagation
@@ -682,6 +700,10 @@ def cmd_stmt_execute(
682700
try:
683701
statement_id.stmt_execute(*args, query_attrs=self.query_attrs)
684702
except MySQLInterfaceError as err:
703+
if hasattr(err, "errno"):
704+
raise get_mysql_exception(
705+
err.errno, msg=err.msg, sqlstate=err.sqlstate
706+
) from err
685707
raise InterfaceError(str(err)) from err
686708

687709
self._columns = []
@@ -704,9 +726,11 @@ def cmd_stmt_close(
704726
try:
705727
statement_id.stmt_close()
706728
except MySQLInterfaceError as err:
707-
raise get_mysql_exception(
708-
err.errno, msg=err.msg, sqlstate=err.sqlstate
709-
) from err
729+
if hasattr(err, "errno"):
730+
raise get_mysql_exception(
731+
err.errno, msg=err.msg, sqlstate=err.sqlstate
732+
) from err
733+
raise InterfaceError(str(err)) from err
710734

711735
def cmd_stmt_reset(
712736
self,
@@ -719,6 +743,10 @@ def cmd_stmt_reset(
719743
try:
720744
statement_id.stmt_reset()
721745
except MySQLInterfaceError as err:
746+
if hasattr(err, "errno"):
747+
raise get_mysql_exception(
748+
err.errno, msg=err.msg, sqlstate=err.sqlstate
749+
) from err
722750
raise InterfaceError(str(err)) from err
723751

724752
@with_context_propagation
@@ -749,9 +777,11 @@ def cmd_query(
749777
query_attrs=self.query_attrs,
750778
)
751779
except MySQLInterfaceError as err:
752-
raise get_mysql_exception(
753-
err.errno, msg=err.msg, sqlstate=err.sqlstate
754-
) from err
780+
if hasattr(err, "errno"):
781+
raise get_mysql_exception(
782+
err.errno, msg=err.msg, sqlstate=err.sqlstate
783+
) from err
784+
raise InterfaceError(str(err)) from err
755785
except AttributeError as err:
756786
addr = (
757787
self._unix_socket if self._unix_socket else f"{self._host}:{self._port}"
@@ -966,9 +996,11 @@ def cmd_change_user(
966996
)
967997

968998
except MySQLInterfaceError as err:
969-
raise get_mysql_exception(
970-
msg=err.msg, errno=err.errno, sqlstate=err.sqlstate
971-
) from err
999+
if hasattr(err, "errno"):
1000+
raise get_mysql_exception(
1001+
err.errno, msg=err.msg, sqlstate=err.sqlstate
1002+
) from err
1003+
raise InterfaceError(str(err)) from err
9721004

9731005
# If charset isn't defined, we use the same charset ID defined previously,
9741006
# otherwise, we run a verification and update the charset ID.
@@ -1000,9 +1032,11 @@ def cmd_refresh(self, options: int) -> Optional[CextEofPacketType]:
10001032
self.handle_unread_result()
10011033
self._cmysql.refresh(options)
10021034
except MySQLInterfaceError as err:
1003-
raise get_mysql_exception(
1004-
msg=err.msg, errno=err.errno, sqlstate=err.sqlstate
1005-
) from err
1035+
if hasattr(err, "errno"):
1036+
raise get_mysql_exception(
1037+
err.errno, msg=err.msg, sqlstate=err.sqlstate
1038+
) from err
1039+
raise InterfaceError(str(err)) from err
10061040

10071041
return self.fetch_eof_status()
10081042

@@ -1029,9 +1063,11 @@ def cmd_shutdown(self, shutdown_type: Optional[int] = None) -> None:
10291063
try:
10301064
self._cmysql.shutdown(level)
10311065
except MySQLInterfaceError as err:
1032-
raise get_mysql_exception(
1033-
msg=err.msg, errno=err.errno, sqlstate=err.sqlstate
1034-
) from err
1066+
if hasattr(err, "errno"):
1067+
raise get_mysql_exception(
1068+
err.errno, msg=err.msg, sqlstate=err.sqlstate
1069+
) from err
1070+
raise InterfaceError(str(err)) from err
10351071
self.close()
10361072

10371073
def cmd_statistics(self) -> StatsPacketType:
@@ -1042,9 +1078,11 @@ def cmd_statistics(self) -> StatsPacketType:
10421078
stat = self._cmysql.stat()
10431079
return MySQLProtocol().parse_statistics(stat, with_header=False)
10441080
except (MySQLInterfaceError, InterfaceError) as err:
1045-
raise get_mysql_exception(
1046-
msg=err.msg, errno=err.errno, sqlstate=err.sqlstate
1047-
) from err
1081+
if hasattr(err, "errno"):
1082+
raise get_mysql_exception(
1083+
err.errno, msg=err.msg, sqlstate=err.sqlstate
1084+
) from err
1085+
raise InterfaceError(str(err)) from err
10481086

10491087
def cmd_process_kill(self, mysql_pid: int) -> None:
10501088
"""Kill a MySQL process"""

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

+15-9
Original file line numberDiff line numberDiff line change
@@ -238,9 +238,11 @@ def _fetch_warnings(self) -> Optional[List[WarningType]]:
238238
warns = self._connection.get_rows(raw=self._raw)[0]
239239
self._connection.consume_results()
240240
except MySQLInterfaceError as err:
241-
raise get_mysql_exception(
242-
msg=err.msg, errno=err.errno, sqlstate=err.sqlstate
243-
) from err
241+
if hasattr(err, "errno"):
242+
raise get_mysql_exception(
243+
err.errno, msg=err.msg, sqlstate=err.sqlstate
244+
) from err
245+
raise InterfaceError(str(err)) from err
244246
except Exception as err:
245247
raise InterfaceError(f"Failed getting warnings; {err}") from None
246248

@@ -359,9 +361,11 @@ def execute(
359361
)
360362
)
361363
except MySQLInterfaceError as err:
362-
raise get_mysql_exception(
363-
msg=err.msg, errno=err.errno, sqlstate=err.sqlstate
364-
) from err
364+
if hasattr(err, "errno"):
365+
raise get_mysql_exception(
366+
err.errno, msg=err.msg, sqlstate=err.sqlstate
367+
) from err
368+
raise InterfaceError(str(err)) from err
365369
return None
366370

367371
def _batch_insert(
@@ -648,9 +652,11 @@ def nextset(self) -> Optional[bool]:
648652
)
649653
)
650654
except MySQLInterfaceError as err:
651-
raise get_mysql_exception(
652-
msg=err.msg, errno=err.errno, sqlstate=err.sqlstate
653-
) from err
655+
if hasattr(err, "errno"):
656+
raise get_mysql_exception(
657+
err.errno, msg=err.msg, sqlstate=err.sqlstate
658+
) from err
659+
raise InterfaceError(str(err)) from err
654660
return True
655661

656662
self._reset_result(free=True)

0 commit comments

Comments
 (0)