Skip to content

Commit 3f7e929

Browse files
committed
BUG32496788: Prepared statements accepts any type of parameters
For prepared statements any type or argument were possible to be used, which could produce undesired results. This patch enforces the use of list or type objects for the argument and If the parameter for the prepared statement is not of type list or tuple an error will be raised.
1 parent 380ea8b commit 3f7e929

File tree

4 files changed

+103
-10
lines changed

4 files changed

+103
-10
lines changed

CHANGES.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,14 @@ v8.0.24
1717
- WL#14027: Add support for Python 3.9
1818
- BUG#32532744: MSI destination folder page lacks details of what installs
1919
- BUG#32497631: Prepared statements fail when parameters are not given
20+
- BUG#32496788: Prepared statements accepts any type of parameters
2021
- BUG#32435181: Add support for Django 3.2
2122
- BUG#32162928: Change user command fails on pure python implementation
2223
- BUG#32120659: Prepared statements w/o parameters violates MySQL protocol
2324
- BUG#32039427: Remove python 3.4 support in MSI packaging
2425
- BUG#32029891: Add context manager support for pooled connections
2526
- BUG#31490101: Fix wrong cast of Python unicode to std::string
27+
- BUG#31315173: Added documentation for multi-host and failover
2628
- BUG#30416704: Binary columns returned as strings
2729

2830
v8.0.23

lib/mysql/connector/cursor.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1211,11 +1211,17 @@ def execute(self, operation, params=None, multi=False): # multi is unused
12111211

12121212
if self._prepared['parameters'] and not params:
12131213
return
1214-
elif params and len(self._prepared['parameters']) != len(params):
1215-
raise errors.ProgrammingError(
1216-
errno=1210,
1217-
msg="Incorrect number of arguments " \
1218-
"executing prepared statement")
1214+
elif params:
1215+
if not isinstance(params, (tuple, list)):
1216+
raise errors.ProgrammingError(
1217+
errno=1210,
1218+
msg="Incorrect type of argument, it must be of type tuple "
1219+
"or list the argument given to the prepared statement")
1220+
if len(self._prepared['parameters']) != len(params):
1221+
raise errors.ProgrammingError(
1222+
errno=1210,
1223+
msg="Incorrect number of arguments " \
1224+
"executing prepared statement")
12191225

12201226
if params is None:
12211227
params = ()

lib/mysql/connector/cursor_cext.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -958,11 +958,17 @@ def execute(self, operation, params=None, multi=False): # multi is unused
958958

959959
if self._stmt.param_count > 0 and not params:
960960
return
961-
elif params and self._stmt.param_count != len(params):
962-
raise errors.ProgrammingError(
963-
errno=1210,
964-
msg="Incorrect number of arguments executing prepared "
965-
"statement")
961+
elif params:
962+
if not isinstance(params, (tuple, list)):
963+
raise errors.ProgrammingError(
964+
errno=1210,
965+
msg="Incorrect type of argument, it must be of type tuple "
966+
"or list the argument given to the prepared statement")
967+
if self._stmt.param_count != len(params):
968+
raise errors.ProgrammingError(
969+
errno=1210,
970+
msg="Incorrect number of arguments executing prepared "
971+
"statement")
966972

967973
if params is None:
968974
params = ()

tests/test_bugs.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5945,6 +5945,85 @@ def test_without_use_unicode_cursor_prepared(self):
59455945
)
59465946

59475947

5948+
class Bug32496788(tests.MySQLConnectorTests):
5949+
"""BUG#32496788: PREPARED STATEMETS ACCEPTS ANY TYPE OF PARAMETERS."""
5950+
5951+
table_name = "Bug32496788"
5952+
test_values = (
5953+
("John", "", "Doe", 21, 77),
5954+
["Jane", "", "Doe", 19, 7]
5955+
)
5956+
exp_values = (
5957+
[("John", "", "Doe", 21, 77)],
5958+
[("Jane", "", "Doe", 19, 7)]
5959+
)
5960+
5961+
def setUp(self):
5962+
config = tests.get_mysql_config()
5963+
with mysql.connector.connection.MySQLConnection(**config) as cnx:
5964+
cnx.cmd_query(f"DROP TABLE IF EXISTS {self.table_name}")
5965+
cnx.cmd_query(
5966+
f"""
5967+
CREATE TABLE {self.table_name} (
5968+
column_first_name VARCHAR(30) DEFAULT '' NOT NULL,
5969+
column_midle_name VARCHAR(30) DEFAULT '' NOT NULL,
5970+
column_last_name VARCHAR(30) DEFAULT '' NOT NULL,
5971+
age INT DEFAULT 0 NOT NULL,
5972+
lucky_number INT DEFAULT 0 NOT NULL
5973+
) CHARACTER SET utf8 COLLATE utf8_general_ci
5974+
"""
5975+
)
5976+
cnx.commit()
5977+
5978+
def tearDown(self):
5979+
config = tests.get_mysql_config()
5980+
with mysql.connector.connection.MySQLConnection(**config) as cnx:
5981+
cnx.cmd_query(f"DROP TABLE IF EXISTS {self.table_name}")
5982+
5983+
@foreach_cnx()
5984+
def test_parameters_type(self):
5985+
stmt = f"SELECT * FROM {self.table_name} WHERE age = ? and lucky_number = ?"
5986+
with self.cnx.cursor(prepared=True) as cur:
5987+
# Test incorrect types must raise error
5988+
for param in ["12", 12, 1.3, lambda : "1"+ "2"]:
5989+
self.assertRaises(errors.ProgrammingError, cur.execute, stmt, (param,))
5990+
with self.assertRaises(errors.ProgrammingError) as contex:
5991+
cur.execute(stmt, param)
5992+
self.assertIn("Incorrect type of argument, it must be of type tuple or list",
5993+
contex.exception.msg)
5994+
5995+
with self.cnx.cursor(prepared=True) as cur:
5996+
# Correct form with tuple
5997+
cur.execute("SELECT ?, ?", ("1", "2"))
5998+
self.assertEqual(cur.fetchall(), [('1', '2')])
5999+
6000+
# Correct form with list
6001+
cur.execute("SELECT ?, ?", ["1", "2"])
6002+
self.assertEqual(cur.fetchall(), [('1', '2')])
6003+
6004+
with self.cnx.cursor(prepared=True) as cur:
6005+
insert_stmt = f"INSERT INTO {self.table_name} VALUES (?, ?, ?, ?, ?)"
6006+
cur.execute(insert_stmt)
6007+
with self.assertRaises(errors.ProgrammingError) as contex:
6008+
cur.execute(insert_stmt, "JD")
6009+
self.assertIn("Incorrect type of argument, it must be of type tuple or list",
6010+
contex.exception.msg)
6011+
self.assertRaises(errors.ProgrammingError, cur.execute, insert_stmt, ("JohnDo"))
6012+
cur.execute(insert_stmt, self.test_values[0])
6013+
cur.execute(insert_stmt, self.test_values[1])
6014+
6015+
select_stmt = f"SELECT * FROM {self.table_name} WHERE column_last_name=? and column_first_name=?"
6016+
self.assertRaises(errors.ProgrammingError, cur.execute, select_stmt, ("JD"))
6017+
with self.assertRaises(errors.ProgrammingError) as contex:
6018+
cur.execute(select_stmt, "JD")
6019+
self.assertIn("Incorrect type of argument, it must be of type tuple or list",
6020+
contex.exception.msg)
6021+
cur.execute(select_stmt, ("Doe", "John"))
6022+
self.assertEqual(cur.fetchall(), self.exp_values[0])
6023+
cur.execute(select_stmt, ("Doe", "Jane"))
6024+
self.assertEqual(cur.fetchall(), self.exp_values[1])
6025+
6026+
59486027
class Bug32162928(tests.MySQLConnectorTests):
59496028
"""BUG#32162928: change user command fails with pure python implementation.
59506029

0 commit comments

Comments
 (0)