Fixed memory leak in parser

This commit is contained in:
Georg Richter
2021-08-03 11:31:31 +02:00
parent cb0c3c8f5a
commit 8cd33d55c1
6 changed files with 52 additions and 43 deletions

View File

@ -16,7 +16,7 @@ The mariadb module supports the standard defined by DB API 2.0 (PEP-249).
Establishes a connection to a database server and returns a new connection Establishes a connection to a database server and returns a new connection
object. object.
:parameters: Parameters:
.. versionadded:: 1.1.0 .. versionadded:: 1.1.0
@ -53,9 +53,11 @@ The mariadb module supports the standard defined by DB API 2.0 (PEP-249).
- **ssl_crlpath** (string): Defines a path to a PEM file that should contain one or more revoked X509 certificates to use for TLS. This option requires that you use the absolute path, not a relative path. - **ssl_crlpath** (string): Defines a path to a PEM file that should contain one or more revoked X509 certificates to use for TLS. This option requires that you use the absolute path, not a relative path.
- **ssl_verify_cert** (bool): Enables server certificate verification. - **ssl_verify_cert** (bool): Enables server certificate verification.
- **ssl** (bool): Always use a secure TLS connection - **ssl** (bool): Always use a secure TLS connection
.. versionadded:: 1.0.1 .. versionadded:: 1.0.1
- **autocommit** (bool or None): Specifies the autocommit settings: None will use the server default. True will enable autocommit, False will disable it (default). - **autocommit** (bool or None): Specifies the autocommit settings: None will use the server default. True will enable autocommit, False will disable it (default).
.. versionadded:: 1.0.3 .. versionadded:: 1.0.3
- **converter** (dict): Specifies a conversion dictionary, where keys are FIELD_TYPE values and values are conversion functions. - **converter** (dict): Specifies a conversion dictionary, where keys are FIELD_TYPE values and values are conversion functions.
:return: Returns a connection object or raises an error if the connection between client and server couldn't be established. :return: Returns a connection object or raises an error if the connection between client and server couldn't be established.

View File

@ -16,7 +16,7 @@ The mariadb module supports the standard defined by DB API 2.0 (PEP-249).
Establishes a connection to a database server and returns a new connection Establishes a connection to a database server and returns a new connection
object. object.
:parameters: Parameters:
.. versionadded:: 1.1.0 .. versionadded:: 1.1.0
@ -53,9 +53,11 @@ The mariadb module supports the standard defined by DB API 2.0 (PEP-249).
- **ssl_crlpath** (string): Defines a path to a PEM file that should contain one or more revoked X509 certificates to use for TLS. This option requires that you use the absolute path, not a relative path. - **ssl_crlpath** (string): Defines a path to a PEM file that should contain one or more revoked X509 certificates to use for TLS. This option requires that you use the absolute path, not a relative path.
- **ssl_verify_cert** (bool): Enables server certificate verification. - **ssl_verify_cert** (bool): Enables server certificate verification.
- **ssl** (bool): Always use a secure TLS connection - **ssl** (bool): Always use a secure TLS connection
.. versionadded:: 1.0.1 .. versionadded:: 1.0.1
- **autocommit** (bool or None): Specifies the autocommit settings: None will use the server default. True will enable autocommit, False will disable it (default). - **autocommit** (bool or None): Specifies the autocommit settings: None will use the server default. True will enable autocommit, False will disable it (default).
.. versionadded:: 1.0.3 .. versionadded:: 1.0.3
- **converter** (dict): Specifies a conversion dictionary, where keys are FIELD_TYPE values and values are conversion functions. - **converter** (dict): Specifies a conversion dictionary, where keys are FIELD_TYPE values and values are conversion functions.
:return: Returns a connection object or raises an error if the connection between client and server couldn't be established. :return: Returns a connection object or raises an error if the connection between client and server couldn't be established.

View File

@ -52,12 +52,9 @@
<span class="sig-prename descclassname"><span class="pre">mariadb.</span></span><span class="sig-name descname"><span class="pre">connect</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">cursorclass</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">mariadb.connections.Connection</span> <span class="pre">**</span> <span class="pre">kwargs</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#mariadb.connect" title="Permalink to this definition"></a></dt> <span class="sig-prename descclassname"><span class="pre">mariadb.</span></span><span class="sig-name descname"><span class="pre">connect</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">cursorclass</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">mariadb.connections.Connection</span> <span class="pre">**</span> <span class="pre">kwargs</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#mariadb.connect" title="Permalink to this definition"></a></dt>
<dd><p>Establishes a connection to a database server and returns a new connection <dd><p>Establishes a connection to a database server and returns a new connection
object.</p> object.</p>
<p>Parameters:</p>
</dd></dl> </dd></dl>
<dl class="field-list simple">
<dt class="field-odd">parameters</dt>
<dd class="field-odd"><p></p></dd>
</dl>
<div class="versionadded"> <div class="versionadded">
<p><span class="versionmodified added">New in version 1.1.0: </span></p> <p><span class="versionmodified added">New in version 1.1.0: </span></p>
<ul class="simple"> <ul class="simple">
@ -102,10 +99,16 @@ be used</p>
</ul> </ul>
</div> </div>
<div class="versionadded"> <div class="versionadded">
<p><span class="versionmodified added">New in version 1.0.1: </span>- <strong>autocommit</strong> (bool or None): Specifies the autocommit settings: None will use the server default. True will enable autocommit, False will disable it (default).</p> <p><span class="versionmodified added">New in version 1.0.1: </span></p>
<ul class="simple">
<li><p><strong>autocommit</strong> (bool or None): Specifies the autocommit settings: None will use the server default. True will enable autocommit, False will disable it (default).</p></li>
</ul>
</div> </div>
<div class="versionadded"> <div class="versionadded">
<p><span class="versionmodified added">New in version 1.0.3: </span>- <strong>converter</strong> (dict): Specifies a conversion dictionary, where keys are FIELD_TYPE values and values are conversion functions.</p> <p><span class="versionmodified added">New in version 1.0.3: </span></p>
<ul class="simple">
<li><p><strong>converter</strong> (dict): Specifies a conversion dictionary, where keys are FIELD_TYPE values and values are conversion functions.</p></li>
</ul>
<dl class="field-list simple"> <dl class="field-list simple">
<dt class="field-odd">return</dt> <dt class="field-odd">return</dt>
<dd class="field-odd"><p>Returns a connection object or raises an error if the connection between client and server couldnt be established.</p> <dd class="field-odd"><p>Returns a connection object or raises an error if the connection between client and server couldnt be established.</p>

View File

@ -99,30 +99,31 @@ class Cursor(mariadb._mariadb.cursor):
new_stmt= self.statement new_stmt= self.statement
replace_diff= 0 replace_diff= 0
for i in range (0,len(self._paramlist)): if self._paramlist:
if self._paramstyle == PARAMSTYLE_PYFORMAT: for i in range (0,len(self._paramlist)):
val= self._data[self._keys[i]] if self._paramstyle == PARAMSTYLE_PYFORMAT:
else: val= self._data[self._keys[i]]
val= self._data[i]
if val is None:
replace= "NULL";
else:
if isinstance(val, INDICATOR.MrdbIndicator):
if val == INDICATOR.NULL:
replace= "NULL"
if val == INDICATOR.DEFAULT:
replace= "DEFAULT"
elif isinstance(val, Number):
replace= val.__str__()
else: else:
if isinstance(val, (bytes, bytearray)): val= self._data[i]
replace= "\"%s\"" % self.connection.escape_string(val.decode(encoding='latin1')) if val is None:
replace= "NULL";
else:
if isinstance(val, INDICATOR.MrdbIndicator):
if val == INDICATOR.NULL:
replace= "NULL"
if val == INDICATOR.DEFAULT:
replace= "DEFAULT"
elif isinstance(val, Number):
replace= val.__str__()
else: else:
replace= "\"%s\"" % self.connection.escape_string(val.__str__()) if isinstance(val, (bytes, bytearray)):
ofs= self._paramlist[i] + replace_diff replace= "\"%s\"" % self.connection.escape_string(val.decode(encoding='latin1'))
else:
new_stmt= new_stmt[:ofs] + replace.__str__() + new_stmt[ofs+1:] replace= "\"%s\"" % self.connection.escape_string(val.__str__())
replace_diff+= len(replace) - 1 ofs= self._paramlist[i] + replace_diff
new_stmt= new_stmt[:ofs] + replace.__str__() + new_stmt[ofs+1:]
replace_diff+= len(replace) - 1
return new_stmt return new_stmt
def _check_execute_params(self): def _check_execute_params(self):
@ -138,8 +139,8 @@ class Cursor(mariadb._mariadb.cursor):
# check if number of place holders matches the number of # check if number of place holders matches the number of
# supplied elements in data tuple # supplied elements in data tuple
if (not self._data and len(self._paramlist) > 0) or \ if self._paramlist and ((not self._data and len(self._paramlist) > 0) or \
(len(self._data) != len(self._paramlist)): (len(self._data) != len(self._paramlist))):
raise mariadb.DataError("Number of parameters in statement (%s)"\ raise mariadb.DataError("Number of parameters in statement (%s)"\
" doesn't match the number of data elements (%s)."\ " doesn't match the number of data elements (%s)."\
% (len(self._paramlist), len(self._data))) % (len(self._paramlist), len(self._data)))
@ -342,7 +343,6 @@ class Cursor(mariadb._mariadb.cursor):
The cursor will be unusable from this point forward; an Error (or subclass) The cursor will be unusable from this point forward; an Error (or subclass)
exception will be raised if any operation is attempted with the cursor." exception will be raised if any operation is attempted with the cursor."
""" """
super().close() super().close()
def fetchone(self): def fetchone(self):
@ -462,7 +462,6 @@ class Cursor(mariadb._mariadb.cursor):
def __exit__(self, exc_type, exc_val, exc_tb): def __exit__(self, exc_type, exc_val, exc_tb):
"""Closes cursor.""" """Closes cursor."""
self.close() self.close()
def __del__(self): def __del__(self):

View File

@ -391,6 +391,8 @@ void MrdbCursor_clearparseinfo(MrdbParseInfo *parseinfo)
if (parseinfo->statement) if (parseinfo->statement)
MARIADB_FREE_MEM(parseinfo->statement); MARIADB_FREE_MEM(parseinfo->statement);
Py_XDECREF(parseinfo->keys); Py_XDECREF(parseinfo->keys);
if (parseinfo->paramlist)
Py_XDECREF(parseinfo->paramlist);
memset(parseinfo, 0, sizeof(MrdbParseInfo)); memset(parseinfo, 0, sizeof(MrdbParseInfo));
} }
@ -467,8 +469,7 @@ void MrdbCursor_clear(MrdbCursor *self, uint8_t new_stmt)
self->fields= NULL; self->fields= NULL;
self->row_count= 0; self->row_count= 0;
self->affected_rows= 0; self->affected_rows= 0;
MARIADB_FREE_MEM(self->parseinfo.statement); MrdbCursor_clearparseinfo(&self->parseinfo);
memset(&self->parseinfo, 0, sizeof(MrdbParseInfo));
MARIADB_FREE_MEM(self->values); MARIADB_FREE_MEM(self->values);
MARIADB_FREE_MEM(self->bind); MARIADB_FREE_MEM(self->bind);
MARIADB_FREE_MEM(self->statement); MARIADB_FREE_MEM(self->statement);
@ -520,8 +521,7 @@ void ma_cursor_close(MrdbCursor *self)
self->stmt= NULL; self->stmt= NULL;
} }
MARIADB_FREE_MEM(self->parseinfo.statement); MrdbCursor_clearparseinfo(&self->parseinfo);
self->is_closed= 1; self->is_closed= 1;
} }
} }
@ -822,10 +822,12 @@ static PyObject *MrdbCursor_seek(MrdbCursor *self, PyObject *args)
{ {
return NULL; return NULL;
} }
Py_BEGIN_ALLOW_THREADS;
if (self->parseinfo.is_text) if (self->parseinfo.is_text)
mysql_data_seek(self->result, new_position); mysql_data_seek(self->result, new_position);
else else
mysql_stmt_data_seek(self->stmt, new_position); mysql_stmt_data_seek(self->stmt, new_position);
Py_END_ALLOW_THREADS;
Py_RETURN_NONE; Py_RETURN_NONE;
} }
@ -899,8 +901,7 @@ Mariadb_row_count(MrdbCursor *self)
static PyObject * static PyObject *
Mariadb_row_number(MrdbCursor *self) Mariadb_row_number(MrdbCursor *self)
{ {
unsigned int field_count= self->field_count; if (!self->field_count) {
if (!field_count) {
Py_RETURN_NONE; Py_RETURN_NONE;
} }
return PyLong_FromLongLong(self->row_number); return PyLong_FromLongLong(self->row_number);
@ -989,6 +990,7 @@ MrdbCursor_parse(MrdbCursor *self, PyObject *args)
memcpy(self->parseinfo.statement, parser->statement.str, parser->statement.length); memcpy(self->parseinfo.statement, parser->statement.str, parser->statement.length);
self->parseinfo.statement_len= parser->statement.length; self->parseinfo.statement_len= parser->statement.length;
self->parseinfo.paramlist= parser->param_list; self->parseinfo.paramlist= parser->param_list;
parser->param_list= NULL;
self->parseinfo.is_text= (parser->command == SQL_NONE || parser->command == SQL_OTHER); self->parseinfo.is_text= (parser->command == SQL_NONE || parser->command == SQL_OTHER);
self->parseinfo.command= parser->command; self->parseinfo.command= parser->command;
@ -1000,6 +1002,7 @@ MrdbCursor_parse(MrdbCursor *self, PyObject *args)
PyObject *key; PyObject *key;
key= PyUnicode_FromString(parser->keys[i].str); key= PyUnicode_FromString(parser->keys[i].str);
PyTuple_SetItem(tmp, i, key); PyTuple_SetItem(tmp, i, key);
Py_DECREF(key);
} }
self->parseinfo.keys= tmp; self->parseinfo.keys= tmp;
} }

View File

@ -106,9 +106,9 @@ MrdbParser_init(MYSQL *mysql, const char *statement, size_t length)
memcpy(p->statement.str, statement, length); memcpy(p->statement.str, statement, length);
p->statement.length= length; p->statement.length= length;
p->mysql= mysql; p->mysql= mysql;
p->param_list= PyList_New(0);
p->param_count= 0; p->param_count= 0;
} }
p->param_list= PyList_New(0);
return p; return p;
} }