Skip to content

Commit a865ae2

Browse files
author
Geert Vanderkelen
committed
BUG21492815: Fix callproc when consuming results turned on
We fix the MySQLCursor.callproc() method when consume results is turned on by temporary disabling the automatic consumption. We also fix returning raw results after executing stored routines when a raw cursor such as MySQLCursorRaw is used. This was only a problem in the pure Python code. The string representation of call cursor have been improved. It now will state correctl which cursor class was used. A test case has been added for BUG#21492815 testing both converted and raw results. Some test cases have been updated. (cherry picked from commit c85d89c)
1 parent 172f8cc commit a865ae2

File tree

11 files changed

+113
-26
lines changed

11 files changed

+113
-26
lines changed

cpyint

lib/mysql/connector/authentication.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# MySQL Connector/Python - MySQL driver written in Python.
2-
# Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
2+
# Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
33

44
# MySQL Connector/Python is licensed under the terms of the GPLv2
55
# <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most

lib/mysql/connector/connection.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# MySQL Connector/Python - MySQL driver written in Python.
2-
# Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved.
2+
# Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
33

44
# MySQL Connector/Python is licensed under the terms of the GPLv2
55
# <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most

lib/mysql/connector/cursor.py

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ class CursorBase(MySQLCursorAbstract):
8484
It's better to inherite from MySQLCursor.
8585
"""
8686

87+
_raw = False
88+
8789
def __init__(self):
8890
self._description = None
8991
self._rowcount = -1
@@ -704,15 +706,24 @@ def callproc(self, procname, args=()):
704706

705707
call = "CALL {0}({1})".format(procname, ','.join(argnames))
706708

709+
# pylint: disable=W0212
710+
# We disable consuming results temporary to make sure we
711+
# getting all results
712+
can_consume_results = self._connection._consume_results
707713
for result in self._connection.cmd_query_iter(call):
708-
# pylint: disable=W0212
709-
tmp = MySQLCursorBuffered(self._connection._get_self())
714+
self._connection._consume_results = False
715+
if self._raw:
716+
tmp = MySQLCursorBufferedRaw(self._connection._get_self())
717+
else:
718+
tmp = MySQLCursorBuffered(self._connection._get_self())
719+
tmp._executed = "(a result of {0})".format(call)
710720
tmp._handle_result(result)
711721
if tmp._warnings is not None:
712722
self._warnings = tmp._warnings
713-
# pylint: enable=W0212
714723
if 'columns' in result:
715724
results.append(tmp)
725+
self._connection._consume_results = can_consume_results
726+
#pylint: enable=W0212
716727

717728
if argnames:
718729
select = "SELECT {0}".format(','.join(argtypes))
@@ -888,16 +899,17 @@ def with_rows(self):
888899
return True
889900

890901
def __str__(self):
891-
fmt = "MySQLCursor: %s"
902+
fmt = "{class_name}: {stmt}"
892903
if self._executed:
893-
executed = bytearray(self._executed).decode('utf-8')
894-
if len(executed) > 30:
895-
res = fmt % (executed[:30] + '..')
896-
else:
897-
res = fmt % (executed)
904+
try:
905+
executed = self._executed.decode('utf-8')
906+
except AttributeError:
907+
executed = self._executed
908+
if len(executed) > 40:
909+
executed = executed[:40] + '..'
898910
else:
899-
res = fmt % '(Nothing executed yet)'
900-
return res
911+
executed = '(Nothing executed yet)'
912+
return fmt.format(class_name=self.__class__.__name__, stmt=executed)
901913

902914

903915
class MySQLCursorBuffered(MySQLCursor):
@@ -965,6 +977,9 @@ class MySQLCursorRaw(MySQLCursor):
965977
"""
966978
Skips conversion from MySQL datatypes to Python types when fetching rows.
967979
"""
980+
981+
_raw = True
982+
968983
def fetchone(self):
969984
row = self._fetch_row()
970985
if row:
@@ -990,6 +1005,9 @@ class MySQLCursorBufferedRaw(MySQLCursorBuffered):
9901005
Cursor which skips conversion from MySQL datatypes to Python types when
9911006
fetching rows and fetches rows within execute().
9921007
"""
1008+
1009+
_raw = True
1010+
9931011
def fetchone(self):
9941012
row = self._fetch_row()
9951013
if row:

lib/mysql/connector/cursor_cext.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,7 @@ def callproc(self, procname, args=()):
434434
cur = CMySQLCursorBufferedRaw(self._cnx._get_self())
435435
else:
436436
cur = CMySQLCursorBuffered(self._cnx._get_self())
437+
cur._executed = "(a result of {0})".format(call)
437438
cur._handle_result(result)
438439
# pylint: enable=W0212
439440
results.append(cur)
@@ -610,15 +611,18 @@ def with_rows(self):
610611
return False
611612

612613
def __str__(self):
613-
fmt = "CMySQLCursor: {0}"
614+
fmt = "{class_name}: {stmt}"
614615
if self._executed:
615-
executed = self._executed.decode('utf-8')
616-
if len(executed) > 30:
617-
executed = executed[:30] + '..'
616+
try:
617+
executed = self._executed.decode('utf-8')
618+
except AttributeError:
619+
executed = self._executed
620+
if len(executed) > 40:
621+
executed = executed[:40] + '..'
618622
else:
619623
executed = '(Nothing executed yet)'
620624

621-
return fmt.format(executed)
625+
return fmt.format(class_name=self.__class__.__name__, stmt=executed)
622626

623627

624628
class CMySQLCursorBuffered(CMySQLCursor):

tests/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# MySQL Connector/Python - MySQL driver written in Python.
2-
# Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
2+
# Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
33

44
# MySQL Connector/Python is licensed under the terms of the GPLv2
55
# <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most

tests/cext/test_cext_cursor.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# -*- coding: utf-8 -*-
22
# MySQL Connector/Python - MySQL driver written in Python.
3-
# Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
3+
# Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
44

55
# MySQL Connector/Python is licensed under the terms of the GPLv2
66
# <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most
@@ -431,7 +431,7 @@ def test__str__(self):
431431
stmt = "SELECT VERSION(),USER(),CURRENT_TIME(),NOW(),SHA1('myconnpy')"
432432
cur.execute(stmt)
433433
cur.fetchone()
434-
self.assertEqual("CMySQLCursor: {0}..".format(stmt[:30]),
434+
self.assertEqual("CMySQLCursor: {0}..".format(stmt[:40]),
435435
cur.__str__())
436436
cur.close()
437437

tests/test_abstracts.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# MySQL Connector/Python - MySQL driver written in Python.
2-
# Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
2+
# Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
33

44
# MySQL Connector/Python is licensed under the terms of the GPLv2
55
# <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most

tests/test_bugs.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3856,3 +3856,68 @@ def test_password_with_spaces(self):
38563856
self.fail('Failed using password with spaces')
38573857
else:
38583858
cnx.close()
3859+
3860+
3861+
class BugOra21492815(tests.MySQLConnectorTests):
3862+
"""BUG#21492815: CALLPROC() HANGS WHEN CONSUME_RESULTS=TRUE
3863+
"""
3864+
def setUp(self):
3865+
config = tests.get_mysql_config()
3866+
cnx = connection.MySQLConnection(**config)
3867+
cur = cnx.cursor()
3868+
3869+
self.proc1 = 'Bug20834643'
3870+
self.proc2 = 'Bug20834643_1'
3871+
cur.execute("DROP PROCEDURE IF EXISTS {0}".format(self.proc1))
3872+
3873+
create = ("CREATE PROCEDURE {0}() BEGIN SELECT 1234; "
3874+
"END".format(self.proc1))
3875+
cur.execute(create)
3876+
cur.execute("DROP PROCEDURE IF EXISTS {0}".format(self.proc2))
3877+
3878+
create = ("CREATE PROCEDURE {0}() BEGIN SELECT 9876; "
3879+
"SELECT CONCAT('','abcd'); END".format(self.proc2))
3880+
cur.execute(create)
3881+
cur.close()
3882+
cnx.close()
3883+
3884+
def tearDown(self):
3885+
config = tests.get_mysql_config()
3886+
cnx = connection.MySQLConnection(**config)
3887+
cur = cnx.cursor()
3888+
cur.execute("DROP PROCEDURE IF EXISTS {0}".format(self.proc1))
3889+
cur.execute("DROP PROCEDURE IF EXISTS {0}".format(self.proc2))
3890+
cur.close()
3891+
cnx.close()
3892+
3893+
@cnx_config(consume_results=True, raw=True)
3894+
@foreach_cnx()
3895+
def test_set(self):
3896+
cur = self.cnx.cursor()
3897+
cur.callproc(self.proc1)
3898+
self.assertEqual((bytearray(b'1234'),),
3899+
next(cur.stored_results()).fetchone())
3900+
3901+
cur.callproc(self.proc2)
3902+
exp = [[(bytearray(b'9876'),)], [(bytearray(b'abcd'),)]]
3903+
results = []
3904+
for result in cur.stored_results():
3905+
results.append(result.fetchall())
3906+
self.assertEqual(exp, results)
3907+
cur.close()
3908+
3909+
@cnx_config(consume_results=True, raw=False)
3910+
@foreach_cnx()
3911+
def test_set(self):
3912+
cur = self.cnx.cursor()
3913+
cur.callproc(self.proc1)
3914+
self.assertEqual((1234,),
3915+
next(cur.stored_results()).fetchone())
3916+
3917+
cur.callproc(self.proc2)
3918+
exp = [[(9876,)], [('abcd',)]]
3919+
results = []
3920+
for result in cur.stored_results():
3921+
results.append(result.fetchall())
3922+
self.assertEqual(exp, results)
3923+
cur.close()

tests/test_connection.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# MySQL Connector/Python - MySQL driver written in Python.
2-
# Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved.
2+
# Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
33

44
# MySQL Connector/Python is licensed under the terms of the GPLv2
55
# <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most

tests/test_cursor.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# -*- coding: utf-8 -*-
2-
# Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved.
2+
# Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
33

44
# MySQL Connector/Python is licensed under the terms of the GPLv2
55
# <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most
@@ -916,7 +916,7 @@ def test__str__(self):
916916
stmt = "SELECT VERSION(),USER(),CURRENT_TIME(),NOW(),SHA1('myconnpy')"
917917
self.cur.execute(stmt)
918918
self.cur.fetchone()
919-
self.assertEqual("MySQLCursor: {0}..".format(stmt[:30]),
919+
self.assertEqual("MySQLCursor: {0}..".format(stmt[:40]),
920920
self.cur.__str__())
921921
self.cur.close()
922922

0 commit comments

Comments
 (0)