Skip to content

Commit 76459d9

Browse files
committed
WL#15950: Support query parameters for prepared statements
With this WL, support for query attributes for prepared statements is added, in particular for the C-EXT since pure Python already supports it. Additionally, we take this implementation as an excuse to enable OpenTelemetry trace context propagation when working with prepared statements in the C-EXT. Previously it wasn't possible for the C-EXT since support for query attributes for prepared statements was missing, but now context propagation for this kind of statements is supported. Change-Id: Ic119b05765788d502f26eb2feaec492fa788edfd
1 parent 5302916 commit 76459d9

File tree

8 files changed

+498
-8
lines changed

8 files changed

+498
-8
lines changed

CHANGES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ v8.3.0
1414
- WL#16015: Remove use of removed COM_ commands
1515
- WL#15983: Stop using mysql_ssl_set api
1616
- WL#15982: Remove use of mysql_shutdown
17+
- WL#15950: Support query parameters for prepared statements
1718
- WL#15942: Improve type hints and standardize byte type handling
1819
- WL#15836: Split mysql and mysqlx into different packages
1920
- WL#15523: Support Python DB API asynchronous execution

CONTRIBUTING.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Contributing Code
2121

2222
Contributing to this project is easy. You just need to follow these steps.
2323

24-
- Make sure you have a user account at `bugs.mysql.com <https://bugs.mysql.com>`_. You will need to reference this user account when you submit your Oracle Contributor Agreement (OCA).
24+
- Make sure you have a user account at `bugs.mysql.com <https://bugs.mysql.com>`_. You will need to reference this user account when you submit your Oracle Contributor Agreement (a.k.a. OCA).
2525
- Sign the Oracle Contributor Agreement. You can find instructions for doing that at the `OCA Page <https://oca.opensource.oracle.com/>`_.
2626
- Develop your pull request. Make sure you are aware of the `requirements <https://dev.mysql.com/doc/dev/connector-python/8.0/requirements.html>`_ for the project.
2727
- Validate your pull request by including tests that sufficiently cover the functionality you are adding.

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -606,12 +606,13 @@ def cmd_stmt_prepare(self, statement: bytes) -> CMySQLPrepStmt:
606606
except MySQLInterfaceError as err:
607607
raise InterfaceError(str(err)) from err
608608

609+
@with_context_propagation
609610
def cmd_stmt_execute( # type: ignore[override]
610611
self, statement_id: CMySQLPrepStmt, *args: Any
611612
) -> Optional[Union[CextEofPacketType, CextResultType]]:
612613
"""Executes the prepared statement"""
613614
try:
614-
statement_id.stmt_execute(*args)
615+
statement_id.stmt_execute(*args, query_attrs=self.query_attrs)
615616
except MySQLInterfaceError as err:
616617
raise InterfaceError(str(err)) from err
617618

mysql-connector-python/src/include/mysql_capi.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ PyObject *
294294
MySQL_stmt_prepare(MySQL *self, PyObject *stmt);
295295

296296
PyObject *
297-
MySQLPrepStmt_execute(MySQLPrepStmt *self, PyObject *args);
297+
MySQLPrepStmt_execute(MySQLPrepStmt *self, PyObject *args, PyObject *kwds);
298298

299299
PyObject *
300300
MySQLPrepStmt_handle_result(MySQLPrepStmt *self);

mysql-connector-python/src/mysql_capi.c

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3216,19 +3216,42 @@ MySQLPrepStmt_dealloc(MySQLPrepStmt *self)
32163216
@retval PyBool_type OK
32173217
*/
32183218
PyObject *
3219-
MySQLPrepStmt_execute(MySQLPrepStmt *self, PyObject *args)
3219+
MySQLPrepStmt_execute(MySQLPrepStmt *self, PyObject *args, PyObject *kwds)
32203220
{
3221-
Py_ssize_t size = PyTuple_Size(args);
3221+
3222+
static const char* key_query_attrs = "query_attrs";
3223+
PyObject *query_attrs = PyDict_GetItemString(kwds, key_query_attrs); // returns borrowed reference
3224+
3225+
Py_ssize_t size_unnamed_params = PyTuple_Size(args);
3226+
Py_ssize_t size = size_unnamed_params + (query_attrs != NULL? PyList_Size(query_attrs): 0);
3227+
32223228
MYSQL_BIND *mbinds = calloc(size, sizeof(MYSQL_BIND));
32233229
struct MySQL_binding *bindings = calloc(size, sizeof(struct MySQL_binding));
3230+
const char **names = calloc(size, sizeof(char *));
3231+
32243232
PyObject *value;
32253233
PyObject *retval = NULL;
32263234
int i = 0, res = 0;
3235+
bool params_bind_ans;
32273236

32283237
for (i = 0; i < size; i++) {
32293238
struct MySQL_binding *pbind = &bindings[i];
32303239
MYSQL_BIND *mbind = &mbinds[i];
3231-
value = PyTuple_GetItem(args, i);
3240+
3241+
if (i < size_unnamed_params)
3242+
{
3243+
// handle unnamed parameter
3244+
names[i] = NULL; // Name being NULL indicates that the parameter is unnamed
3245+
value = PyTuple_GetItem(args, i);
3246+
}
3247+
else
3248+
{
3249+
// handle query attribute
3250+
PyObject *attr_tuple = PyList_GetItem(query_attrs, i - size_unnamed_params); // returns borrowed reference
3251+
PyObject *attr_name = PyTuple_GetItem(attr_tuple, 0); // returns borrowed reference
3252+
names[i] = PyUnicode_AsUTF8(attr_name);
3253+
value = PyTuple_GetItem(attr_tuple, 1);
3254+
}
32323255

32333256
if (value == NULL) {
32343257
goto cleanup;
@@ -3392,7 +3415,14 @@ MySQLPrepStmt_execute(MySQLPrepStmt *self, PyObject *args)
33923415
}
33933416
}
33943417

3395-
if (mysql_stmt_bind_param(self->stmt, mbinds)) {
3418+
#if MYSQL_VERSION_ID >= 80300
3419+
params_bind_ans = mysql_stmt_bind_named_param(self->stmt, mbinds, (int)size, names);
3420+
#else
3421+
params_bind_ans = mysql_stmt_bind_param(self->stmt, mbinds);
3422+
#endif
3423+
3424+
if (params_bind_ans)
3425+
{
33963426
retval = PyErr_Format(MySQLInterfaceError, (const char *)"Bind the parameters: %s",
33973427
mysql_stmt_error(self->stmt));
33983428
goto cleanup;

mysql-connector-python/src/mysql_connector.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ static PyMemberDef MySQLPrepStmt_members[] = {
244244
};
245245

246246
static PyMethodDef MySQLPrepStmt_methods[] = {
247-
{"stmt_execute", (PyCFunction)MySQLPrepStmt_execute, METH_VARARGS,
247+
{"stmt_execute", (PyCFunction)MySQLPrepStmt_execute, METH_VARARGS | METH_KEYWORDS,
248248
"Executes the prepared statement"},
249249
{"fetch_fields", (PyCFunction)MySQLPrepStmt_fetch_fields, METH_VARARGS,
250250
"Fetch information about fields in result set"},

0 commit comments

Comments
 (0)