Skip to content

Commit a74964e

Browse files
author
Geert Vanderkelen
committed
BUG21536507: Fix raising warnings as exceptions
We fix a few issues around raising_on_warnings is set to True. When using CExtension, no errors were thrown as exceptions when the executed statement produced an error. Also, when statement produces a result, it was not possible to reuse the cursor. A test case has been added for BUG#21536507 (cherry picked from commit 6a2b67d)
1 parent e1e4425 commit a74964e

File tree

6 files changed

+65
-24
lines changed

6 files changed

+65
-24
lines changed

lib/mysql/connector/connection.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -810,8 +810,8 @@ def cursor(self, buffered=None, raw=None, prepared=None, cursor_class=None,
810810
"Cursor class needs be to subclass of cursor.CursorBase")
811811
return (cursor_class)(self)
812812

813-
buffered = buffered or self._buffered
814-
raw = raw or self._raw
813+
buffered = buffered if buffered is not None else self._buffered
814+
raw = raw if raw is not None else self._raw
815815

816816
cursor_type = 0
817817
if buffered is True:

lib/mysql/connector/cursor.py

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -396,8 +396,10 @@ def _handle_noresultset(self, res):
396396
raise errors.ProgrammingError(
397397
"Failed handling non-resultset; {0}".format(err))
398398

399-
if self._connection.get_warnings is True and self._warning_count:
400-
self._warnings = self._fetch_warnings()
399+
self._handle_warnings()
400+
if self._connection.raise_on_warnings is True and self._warnings:
401+
raise errors.get_mysql_exception(
402+
self._warnings[0][1], self._warnings[0][2])
401403

402404
def _handle_resultset(self):
403405
"""Handles result set
@@ -746,29 +748,33 @@ def _fetch_warnings(self):
746748
"""
747749
res = []
748750
try:
749-
cur = self._connection.cursor()
751+
cur = self._connection.cursor(raw=False)
750752
cur.execute("SHOW WARNINGS")
751753
res = cur.fetchall()
752754
cur.close()
753755
except Exception as err:
754756
raise errors.InterfaceError(
755757
"Failed getting warnings; %s" % err)
756758

757-
if self._connection.raise_on_warnings is True:
758-
raise errors.get_mysql_exception(res[0][1], res[0][2])
759-
else:
760-
if len(res):
761-
return res
759+
if len(res):
760+
return res
762761

763762
return None
764763

764+
def _handle_warnings(self):
765+
"""Handle possible warnings after all results are consumed"""
766+
if self._connection.get_warnings is True and self._warning_count:
767+
self._warnings = self._fetch_warnings()
768+
765769
def _handle_eof(self, eof):
766770
"""Handle EOF packet"""
767771
self._connection.unread_result = False
768772
self._nextrow = (None, None)
769773
self._warning_count = eof['warning_count']
770-
if self._connection.get_warnings is True and eof['warning_count']:
771-
self._warnings = self._fetch_warnings()
774+
self._handle_warnings()
775+
if self._connection.raise_on_warnings is True and self._warnings:
776+
raise errors.get_mysql_exception(
777+
self._warnings[0][1], self._warnings[0][2])
772778

773779
def _fetch_row(self):
774780
"""Returns the next row in the result set

lib/mysql/connector/cursor_cext.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ def _fetch_warnings(self):
130130
self._cnx.consume_results()
131131
_ = self._cnx.cmd_query("SHOW WARNINGS")
132132
warnings = self._cnx.get_rows()
133+
self._cnx.consume_results()
133134
except MySQLInterfaceError as exc:
134135
raise errors.get_mysql_exception(msg=exc.msg, errno=exc.errno,
135136
sqlstate=exc.sqlstate)
@@ -158,6 +159,9 @@ def _handle_result(self, result):
158159
self._warning_count = result['warning_count']
159160
self._affected_rows = result['affected_rows']
160161
self._rowcount = -1
162+
self._handle_warnings()
163+
if self._cnx.raise_on_warnings is True and self._warnings:
164+
raise errors.get_mysql_exception(*self._warnings[0][1:3])
161165

162166
def _handle_resultset(self):
163167
"""Handle a result set"""

tests/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import shutil
3737
import subprocess
3838
import errno
39+
import traceback
3940
from imp import load_source
4041
from functools import wraps
4142
from pkgutil import walk_packages
@@ -437,6 +438,7 @@ def wrapper(self, *args, **kwargs):
437438
try:
438439
func(self, *args, **kwargs)
439440
except Exception as exc:
441+
traceback.print_exc(file=sys.stdout)
440442
raise exc
441443
finally:
442444
try:
@@ -613,7 +615,7 @@ def connc_connect_args(self, recache=False):
613615
:return: Dictionary containing connection arguments.
614616
:rtype: dict
615617
"""
616-
self.config = get_mysql_config()
618+
self.config = get_mysql_config().copy()
617619

618620
if not self.hasattr('connc_kwargs') or recache is True:
619621
connect_args = [

tests/test_abstracts.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,6 @@ def test_cmd_init_db(self):
162162

163163
@foreach_cnx()
164164
def test_reset_session(self):
165-
config = tests.get_mysql_config()
166165
exp = [True, u'STRICT_ALL_TABLES', u'-09:00', 33]
167166
self.cnx.autocommit = exp[0]
168167
self.cnx.sql_mode = exp[1]
@@ -252,7 +251,6 @@ def test_cmd_process_kill(self):
252251
pid = other_cnx.connection_id
253252

254253
self.cnx.cmd_process_kill(pid)
255-
256254
self.assertFalse(other_cnx.is_connected())
257255

258256
@foreach_cnx()

tests/test_bugs.py

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1889,7 +1889,7 @@ def test_get_connection(self):
18891889
config = tests.get_mysql_config().copy()
18901890
config['connection_timeout'] = 2
18911891
cnxpool = pooling.MySQLConnectionPool(
1892-
pool_name='test', pool_size=1, **tests.get_mysql_config())
1892+
pool_name='test', pool_size=1, **config)
18931893

18941894
pcnx = cnxpool.get_connection()
18951895
self.assertTrue(isinstance(pcnx, pooling.PooledMySQLConnection))
@@ -3603,23 +3603,28 @@ class BugOra20653441(tests.MySQLConnectorTests):
36033603

36043604
def setUp(self):
36053605
self.table_name = 'Bug20653441'
3606+
self._setup()
36063607

36073608
def _setup(self):
3608-
self.cnx.cmd_query("DROP TABLE IF EXISTS {0}".format(self.table_name))
3609+
config = tests.get_mysql_config()
3610+
cnx = connection.MySQLConnection(**config)
3611+
cnx.cmd_query("DROP TABLE IF EXISTS {0}".format(self.table_name))
36093612
table = (
36103613
"CREATE TABLE {table} ("
36113614
" id INT UNSIGNED NOT NULL AUTO_INCREMENT,"
36123615
" c1 VARCHAR(255) DEFAULT '{default}',"
36133616
" PRIMARY KEY (id)"
36143617
")"
36153618
).format(table=self.table_name, default='a' * 255)
3616-
self.cnx.cmd_query(table)
3619+
cnx.cmd_query(table)
36173620

36183621
stmt = "INSERT INTO {table} (id) VALUES {values}".format(
36193622
table=self.table_name,
36203623
values=','.join(['(NULL)'] * 1024)
36213624
)
3622-
self.cnx.cmd_query(stmt)
3625+
cnx.cmd_query(stmt)
3626+
cnx.commit()
3627+
cnx.close()
36233628

36243629
def tearDown(self):
36253630
try:
@@ -3632,14 +3637,13 @@ def tearDown(self):
36323637

36333638
@foreach_cnx()
36343639
def test_kill_query(self):
3635-
self._setup()
36363640

36373641
def kill(connection_id):
36383642
"""Kill query using separate connection"""
3639-
killer = connection.MySQLConnection(**tests.get_mysql_config())
3643+
killer_cnx = connection.MySQLConnection(**tests.get_mysql_config())
36403644
time.sleep(1)
3641-
killer.cmd_query("KILL QUERY {0}".format(connection_id))
3642-
killer.close()
3645+
killer_cnx.cmd_query("KILL QUERY {0}".format(connection_id))
3646+
killer_cnx.close()
36433647

36443648
def sleepy_select(cnx):
36453649
"""Execute a SELECT statement which takes a while to complete"""
@@ -3664,10 +3668,37 @@ def sleepy_select(cnx):
36643668
worker.join()
36653669
killer.join()
36663670

3671+
self.cnx.close()
3672+
36673673
self.assertTrue(isinstance(self.cnx.test_error, errors.DatabaseError))
36683674
self.assertEqual(str(self.cnx.test_error),
36693675
"1317 (70100): Query execution was interrupted")
3670-
self.cnx.close()
3676+
3677+
3678+
class BugOra21536507(tests.MySQLConnectorTests):
3679+
"""BUG#21536507:C/PYTHON BEHAVIOR NOT PROPER WHEN RAISE_ON_WARNINGS=TRUE
3680+
"""
3681+
@cnx_config(raw=True, get_warnings=True, raise_on_warnings=True)
3682+
@foreach_cnx()
3683+
def test_with_raw(self):
3684+
cur = self.cnx.cursor()
3685+
drop_stmt = "DROP TABLE IF EXISTS unknown"
3686+
self.assertRaises(errors.DatabaseError, cur.execute, drop_stmt)
3687+
exp = [('Note', 1051, "Unknown table 'myconnpy.unknown'")]
3688+
self.assertEqual(exp, cur.fetchwarnings())
3689+
3690+
select_stmt = "SELECT 'a'+'b'"
3691+
cur.execute(select_stmt)
3692+
self.assertRaises(errors.DatabaseError, cur.fetchall)
3693+
exp = [
3694+
('Warning', 1292, "Truncated incorrect DOUBLE value: 'a'"),
3695+
('Warning', 1292, "Truncated incorrect DOUBLE value: 'b'"),
3696+
]
3697+
self.assertEqual(exp, cur.fetchwarnings())
3698+
try:
3699+
cur.close()
3700+
except errors.InternalError as exc:
3701+
self.fail("Closing cursor failed with: %s" % str(exc))
36713702

36723703

36733704
class BugOra21420633(tests.MySQLConnectorTests):

0 commit comments

Comments
 (0)