mirror of
https://github.com/mariadb-corporation/mariadb-connector-python.git
synced 2025-07-27 13:01:19 +00:00
Fix for CONPY-108 (memory leak):
- initialize datetime API only once per object file - don't reparse same statement
This commit is contained in:
@ -17,6 +17,7 @@
|
||||
51 Franklin St., Fifth Floor, Boston, MA 02110, USA
|
||||
******************************************************************************/
|
||||
#define PY_SSIZE_T_CLEAN
|
||||
|
||||
#include "Python.h"
|
||||
#include "bytesobject.h"
|
||||
#include "structmember.h"
|
||||
@ -29,7 +30,6 @@
|
||||
#include <time.h>
|
||||
#include <docs/common.h>
|
||||
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <windows.h>
|
||||
typedef CRITICAL_SECTION pthread_mutex_t;
|
||||
@ -62,6 +62,8 @@ int clock_gettime(int dummy, struct timespec *ct);
|
||||
#define CLOCK_MONOTONIC_RAW 1
|
||||
#endif
|
||||
|
||||
int codecs_datetime_init();
|
||||
|
||||
|
||||
#define REQUIRED_CC_VERSION 30103
|
||||
|
||||
|
@ -106,6 +106,17 @@ mariadb_module= {
|
||||
Mariadb_Methods
|
||||
};
|
||||
|
||||
static int mariadb_datetime_init()
|
||||
{
|
||||
PyDateTime_IMPORT;
|
||||
|
||||
if (!PyDateTimeAPI) {
|
||||
PyErr_SetString(PyExc_ImportError, "DateTimeAPI initialization failed");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mariadb_add_exception(PyObject *module,
|
||||
PyObject **exception,
|
||||
const char *exception_name,
|
||||
@ -132,6 +143,13 @@ PyMODINIT_FUNC PyInit__mariadb(void)
|
||||
pre_release= PY_MARIADB_PRE_RELEASE_SEGMENT;
|
||||
#endif
|
||||
|
||||
/* Initialite DateTimeAPI */
|
||||
if (mariadb_datetime_init() ||
|
||||
codecs_datetime_init())
|
||||
{
|
||||
goto error;
|
||||
}
|
||||
|
||||
Py_TYPE(&MrdbConnection_Type) = &PyType_Type;
|
||||
if (PyType_Ready(&MrdbConnection_Type) == -1)
|
||||
{
|
||||
@ -304,11 +322,6 @@ Mariadb_date_from_ticks(PyObject *module, PyObject *args)
|
||||
struct tm *ts;
|
||||
time_t epoch;
|
||||
|
||||
if (!PyDateTimeAPI)
|
||||
{
|
||||
PyDateTime_IMPORT;
|
||||
}
|
||||
|
||||
if (!PyArg_ParseTuple(args, "O", &o))
|
||||
{
|
||||
return NULL;
|
||||
@ -328,11 +341,6 @@ Mariadb_time_from_ticks(PyObject *module, PyObject *args)
|
||||
time_t epoch;
|
||||
PyObject *o, *Time= NULL;
|
||||
|
||||
if (!PyDateTimeAPI)
|
||||
{
|
||||
PyDateTime_IMPORT;
|
||||
}
|
||||
|
||||
if (!PyArg_ParseTuple(args, "O", &o))
|
||||
{
|
||||
return NULL;
|
||||
@ -352,11 +360,6 @@ Mariadb_timestamp_from_ticks(PyObject *module, PyObject *args)
|
||||
struct tm *ts;
|
||||
time_t epoch;
|
||||
|
||||
if (!PyDateTimeAPI)
|
||||
{
|
||||
PyDateTime_IMPORT;
|
||||
}
|
||||
|
||||
if (!PyArg_ParseTuple(args, "O", &o))
|
||||
{
|
||||
return NULL;
|
||||
@ -377,11 +380,6 @@ Mariadb_date(PyObject *self, PyObject *args)
|
||||
PyObject *date= NULL;
|
||||
int32_t year=0, month=0, day= 0;
|
||||
|
||||
if (!PyDateTimeAPI)
|
||||
{
|
||||
PyDateTime_IMPORT;
|
||||
}
|
||||
|
||||
if (!PyArg_ParseTuple(args, "iii", &year, &month, &day))
|
||||
{
|
||||
return NULL;
|
||||
@ -398,11 +396,6 @@ Mariadb_timestamp(PyObject *self, PyObject *args)
|
||||
int32_t year=0, month=0, day= 0;
|
||||
int32_t hour=0, min=0, sec= 0;
|
||||
|
||||
if (!PyDateTimeAPI)
|
||||
{
|
||||
PyDateTime_IMPORT;
|
||||
}
|
||||
|
||||
if (!PyArg_ParseTuple(args, "iiiiii", &year, &month, &day,
|
||||
&hour, &min, &sec))
|
||||
{
|
||||
@ -419,10 +412,6 @@ Mariadb_time(PyObject *self, PyObject *args)
|
||||
{
|
||||
PyObject *time= NULL;
|
||||
int32_t hour=0, min=0, sec= 0;
|
||||
if (!PyDateTimeAPI)
|
||||
{
|
||||
PyDateTime_IMPORT;
|
||||
}
|
||||
|
||||
if (!PyArg_ParseTuple(args, "iii", &hour, &min, &sec))
|
||||
{
|
||||
|
@ -28,6 +28,18 @@
|
||||
#define IS_DECIMAL_TYPE(type) \
|
||||
((type) == MYSQL_TYPE_NEWDECIMAL || (type) == MYSQL_TYPE_DOUBLE || (type) == MYSQL_TYPE_FLOAT)
|
||||
|
||||
|
||||
int codecs_datetime_init()
|
||||
{
|
||||
PyDateTime_IMPORT;
|
||||
|
||||
if (!PyDateTimeAPI) {
|
||||
PyErr_SetString(PyExc_ImportError, "DateTimeAPI initialization failed");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
converts a Python date/time/datetime object to MYSQL_TIME
|
||||
*/
|
||||
@ -36,10 +48,6 @@ mariadb_pydate_to_tm(enum enum_field_types type,
|
||||
PyObject *obj,
|
||||
MYSQL_TIME *tm)
|
||||
{
|
||||
if (!PyDateTimeAPI)
|
||||
{
|
||||
PyDateTime_IMPORT;
|
||||
}
|
||||
memset(tm, 0, sizeof(MYSQL_TIME));
|
||||
if (type == MYSQL_TYPE_TIME ||
|
||||
type == MYSQL_TYPE_DATETIME)
|
||||
@ -387,11 +395,6 @@ field_fetch_fromtext(MrdbCursor *self, char *data, unsigned int column)
|
||||
MYSQL_TIME tm;
|
||||
unsigned long *length;
|
||||
|
||||
if (!PyDateTimeAPI)
|
||||
{
|
||||
PyDateTime_IMPORT;
|
||||
}
|
||||
|
||||
if (!data)
|
||||
{
|
||||
Py_INCREF(Py_None);
|
||||
@ -540,10 +543,6 @@ field_fetch_callback(void *data, unsigned int column, unsigned char **row)
|
||||
MrdbCursor *self= (MrdbCursor *)data;
|
||||
|
||||
MARIADB_UNBLOCK_THREADS(self);
|
||||
if (!PyDateTimeAPI)
|
||||
{
|
||||
PyDateTime_IMPORT;
|
||||
}
|
||||
|
||||
if (!row)
|
||||
{
|
||||
@ -771,11 +770,6 @@ end:
|
||||
static uint8_t
|
||||
mariadb_get_column_info(PyObject *obj, MrdbParamInfo *paraminfo)
|
||||
{
|
||||
if (!PyDateTimeAPI)
|
||||
{
|
||||
PyDateTime_IMPORT;
|
||||
}
|
||||
|
||||
if (obj == NULL)
|
||||
{
|
||||
paraminfo->type= MYSQL_TYPE_NULL;
|
||||
@ -880,14 +874,14 @@ mariadb_get_parameter(MrdbCursor *self,
|
||||
mariadb_throw_exception(self->stmt, Mariadb_DataError, 0,
|
||||
"Can't access data at row %d, column %d",
|
||||
row_nr + 1, column_nr + 1);
|
||||
return 1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (!(row= ListOrTuple_GetItem(self->data, row_nr)))
|
||||
{
|
||||
mariadb_throw_exception(self->stmt, Mariadb_DataError, 0,
|
||||
"Can't access row number %d", row_nr + 1);
|
||||
return 1;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -1191,7 +1185,7 @@ mariadb_check_execute_parameters(MrdbCursor *self,
|
||||
{
|
||||
mariadb_throw_exception(NULL, Mariadb_DataError, 0,
|
||||
"Invalid number of parameters");
|
||||
return 1;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!self->params &&
|
||||
|
@ -398,6 +398,39 @@ static PyObject *Mariadb_no_operation(MrdbCursor *self,
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ MrdbCursor_clear_result(MrdbCursor *self)
|
||||
clear pending result sets
|
||||
*/
|
||||
static void MrdbCursor_clear_result(MrdbCursor *self)
|
||||
{
|
||||
if (!self->is_text &&
|
||||
self->stmt)
|
||||
{
|
||||
/* free current result */
|
||||
mysql_stmt_free_result(self->stmt);
|
||||
|
||||
/* check if there are more pending result sets */
|
||||
while (mysql_stmt_next_result(self->stmt) == 0)
|
||||
{
|
||||
mysql_stmt_free_result(self->stmt);
|
||||
}
|
||||
} else if (self->is_text)
|
||||
{
|
||||
/* free current result */
|
||||
if (self->result)
|
||||
{
|
||||
mysql_free_result(self->result);
|
||||
}
|
||||
/* clear pending result sets */
|
||||
if (self->connection->mysql)
|
||||
{
|
||||
while (!mysql_next_result(self->connection->mysql));
|
||||
}
|
||||
}
|
||||
/* CONPY-52: Avoid possible double free */
|
||||
self->result= NULL;
|
||||
}
|
||||
|
||||
/* {{{ MrdbCursor_clear
|
||||
Resets statement attributes and frees
|
||||
associated memory
|
||||
@ -405,16 +438,10 @@ static PyObject *Mariadb_no_operation(MrdbCursor *self,
|
||||
static
|
||||
void MrdbCursor_clear(MrdbCursor *self, uint8_t new_stmt)
|
||||
{
|
||||
/* clear pending result sets */
|
||||
MrdbCursor_clear_result(self);
|
||||
|
||||
if (!self->is_text && self->stmt) {
|
||||
mysql_stmt_free_result(self->stmt);
|
||||
|
||||
/* CONPY-52: avoid possible double free */
|
||||
self->result= NULL;
|
||||
|
||||
while (!mysql_stmt_next_result(self->stmt))
|
||||
mysql_stmt_free_result(self->stmt);
|
||||
|
||||
if (new_stmt)
|
||||
{
|
||||
mysql_stmt_close(self->stmt);
|
||||
@ -423,29 +450,15 @@ void MrdbCursor_clear(MrdbCursor *self, uint8_t new_stmt)
|
||||
else {
|
||||
uint32_t val= 0;
|
||||
|
||||
mysql_stmt_attr_set(self->stmt, STMT_ATTR_CB_USER_DATA, 0);
|
||||
mysql_stmt_attr_set(self->stmt, STMT_ATTR_CB_PARAM, 0);
|
||||
mysql_stmt_attr_set(self->stmt, STMT_ATTR_CB_RESULT, 0);
|
||||
mysql_stmt_reset(self->stmt);
|
||||
|
||||
/* we need to unset array size only */
|
||||
mysql_stmt_attr_set(self->stmt, STMT_ATTR_ARRAY_SIZE, &val);
|
||||
mysql_stmt_attr_set(self->stmt, STMT_ATTR_PREBIND_PARAMS, &val);
|
||||
}
|
||||
|
||||
}
|
||||
self->fetched= 0;
|
||||
|
||||
if (self->is_text)
|
||||
{
|
||||
if (self->result)
|
||||
{
|
||||
mysql_free_result(self->result);
|
||||
self->result= 0;
|
||||
self->is_text= 0;
|
||||
}
|
||||
/* clear also pending result sets */
|
||||
if (self->connection->mysql)
|
||||
while (!mysql_next_result(self->connection->mysql));
|
||||
}
|
||||
|
||||
MARIADB_FREE_MEM(self->sequence_fields);
|
||||
self->fields= NULL;
|
||||
self->row_count= 0;
|
||||
@ -456,7 +469,6 @@ void MrdbCursor_clear(MrdbCursor *self, uint8_t new_stmt)
|
||||
MARIADB_FREE_MEM(self->statement);
|
||||
MARIADB_FREE_MEM(self->value);
|
||||
MARIADB_FREE_MEM(self->params);
|
||||
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
@ -614,6 +626,30 @@ static Py_ssize_t data_count(PyObject *data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int Mrdb_execute_direct(MrdbCursor *self)
|
||||
{
|
||||
int rc;
|
||||
|
||||
MARIADB_BEGIN_ALLOW_THREADS(self);
|
||||
/* execute_direct was implemented together with bulk operations, so we need
|
||||
to check if MARIADB_CLIENT_STMT_BULK_OPERATIONS is set in extended server
|
||||
capabilities */
|
||||
if (!(self->connection->extended_server_capabilities &
|
||||
(MARIADB_CLIENT_STMT_BULK_OPERATIONS >> 32)))
|
||||
{
|
||||
if (!(rc= mysql_stmt_prepare(self->stmt, self->parser->statement.str,
|
||||
(unsigned long)self->parser->statement.length)))
|
||||
{
|
||||
rc= mysql_stmt_execute(self->stmt);
|
||||
}
|
||||
} else {
|
||||
rc= mariadb_stmt_execute_direct(self->stmt, self->parser->statement.str,
|
||||
self->parser->statement.length);
|
||||
}
|
||||
MARIADB_END_ALLOW_THREADS(self);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* {{{ MrdbCursor_execute
|
||||
PEP-249 execute() method
|
||||
*/
|
||||
@ -671,18 +707,20 @@ PyObject *MrdbCursor_execute(MrdbCursor *self,
|
||||
{
|
||||
uint8_t do_prepare= 1;
|
||||
|
||||
if (self->is_prepared && self->statement)
|
||||
if ((self->is_prepared && self->statement))
|
||||
do_prepare= 0;
|
||||
|
||||
/* if cursor type is not prepared, we need to clear the cursor first */
|
||||
if (!self->is_prepared && self->statement)
|
||||
{
|
||||
uint8_t new_stmt= 1;
|
||||
if (!strcmp(self->statement, statement))
|
||||
new_stmt= 0;
|
||||
MrdbCursor_clear(self, new_stmt);
|
||||
MrdbParser_end(self->parser);
|
||||
self->parser= NULL;
|
||||
{
|
||||
do_prepare= 0;
|
||||
} else {
|
||||
MrdbCursor_clear(self, 1);
|
||||
MrdbParser_end(self->parser);
|
||||
self->parser= NULL;
|
||||
}
|
||||
}
|
||||
self->is_text= 0;
|
||||
|
||||
@ -729,23 +767,8 @@ PyObject *MrdbCursor_execute(MrdbCursor *self,
|
||||
mysql_stmt_attr_set(self->stmt, STMT_ATTR_CB_USER_DATA, (void *)self);
|
||||
mysql_stmt_bind_param(self->stmt, self->params);
|
||||
|
||||
MARIADB_BEGIN_ALLOW_THREADS(self);
|
||||
/* execute_direct was implemented together with bulk operations, so we need
|
||||
to check if MARIADB_CLIENT_STMT_BULK_OPERATIONS is set in extended server
|
||||
capabilities */
|
||||
if ((self->connection->extended_server_capabilities &
|
||||
(MARIADB_CLIENT_STMT_BULK_OPERATIONS >> 32)))
|
||||
{
|
||||
rc= mysql_stmt_prepare(self->stmt, self->parser->statement.str,
|
||||
(unsigned long)self->parser->statement.length);
|
||||
if (!rc)
|
||||
rc= mysql_stmt_execute(self->stmt);
|
||||
}
|
||||
else
|
||||
rc= mariadb_stmt_execute_direct(self->stmt, self->parser->statement.str,
|
||||
self->parser->statement.length);
|
||||
MARIADB_END_ALLOW_THREADS(self);
|
||||
|
||||
rc= Mrdb_execute_direct(self);
|
||||
|
||||
if (rc)
|
||||
{
|
||||
/* in case statement is not supported via binary protocol, we try
|
||||
@ -1355,11 +1378,7 @@ MrdbCursor_executemany(MrdbCursor *self,
|
||||
|
||||
mysql_stmt_bind_param(self->stmt, self->params);
|
||||
|
||||
MARIADB_BEGIN_ALLOW_THREADS(self);
|
||||
rc= mariadb_stmt_execute_direct(self->stmt, self->parser->statement.str,
|
||||
(unsigned long)self->parser->statement.length);
|
||||
MARIADB_END_ALLOW_THREADS(self);
|
||||
if (rc)
|
||||
if (Mrdb_execute_direct(self))
|
||||
{
|
||||
mariadb_throw_exception(self->stmt, NULL, 1, NULL);
|
||||
goto error;
|
||||
|
Reference in New Issue
Block a user