Fix memory leak (maybe).

This commit is contained in:
adustman
2000-07-11 20:56:29 +00:00
parent b7c2b7cbdd
commit 47edf13df1
4 changed files with 50 additions and 64 deletions

View File

@ -139,6 +139,13 @@ def escape_dict(d, qc):
d2[k] = qc.get(type(v), String2Literal)(v) d2[k] = qc.get(type(v), String2Literal)(v)
return d2 return d2
def _fetchall(result, *args):
rows = r = list(apply(result.fetch_row, args))
while 1:
rows = list(apply(result.fetch_row, args))
if not rows: break
r.extend(list(rows))
return r
class BaseCursor: class BaseCursor:
@ -208,10 +215,10 @@ class BaseCursor:
qc = self.connection.quote_conv qc = self.connection.quote_conv
try: try:
q = [query % escape(args[0], qc)] q = [query % escape(args[0], qc)]
except TypeError, m: except TypeError, msg:
if m.args[0] in ("not enough arguments for format string", if msg.args[0] in ("not enough arguments for format string",
"not all arguments converted"): "not all arguments converted"):
raise ProgrammingError, m.args[0] raise ProgrammingError, msg.args[0]
escape = escape_dict escape = escape_dict
q = [query % escape(args[0], qc)] q = [query % escape(args[0], qc)]
qv = query[p:] qv = query[p:]
@ -259,6 +266,10 @@ class CursorStoreResultMixIn:
def _get_result(self): return self.connection.db.store_result() def _get_result(self): return self.connection.db.store_result()
def close(self):
self.connection = None
del self._rows
def _query(self, q): def _query(self, q):
self.connection._acquire() self.connection._acquire()
try: try:
@ -345,17 +356,11 @@ class CursorUseResultMixIn:
class CursorTupleRowsMixIn: class CursorTupleRowsMixIn:
def _fetch_row(self): return self._result.fetch(1)[0] def _fetch_row(self): return self._result.fetch_row(1)[0]
def _fetch_rows(self, size): return self._result.fetch(size) def _fetch_rows(self, size): return self._result.fetch_row(size)
def _fetch_all_rows(self): def _fetch_all_rows(self): return _fetchall(self._result, self.arraysize)
r = list(self._result.fetch(self.arraysize))
while len(r) >= self.arraysize:
rows = self._result.fetch(self.arraysize)
if not rows: break
r.extend(list(rows))
return r
class CursorDictRowsMixIn: class CursorDictRowsMixIn:
@ -364,13 +369,7 @@ class CursorDictRowsMixIn:
def _fetch_rows(self, size): return self._result.fetch(size, 1) def _fetch_rows(self, size): return self._result.fetch(size, 1)
def _fetch_all_rows(self): def _fetch_all_rows(self): return _fetchall(self._result, self.arraysize, 1)
r = list(self._result.fetch(self.arraysize, 1))
while len(r) >= self.arraysize:
rows = self._result.fetch(self.arraysize, 1)
if not rows: break
r.extend(list(rows))
return r
## XXX Deprecated ## XXX Deprecated
@ -481,10 +480,10 @@ class Connection:
def get_server_info(self): return self.db.get_server_info() def get_server_info(self): return self.db.get_server_info()
def info(self): return self.db.info() def info(self): return self.db.info()
def kill(self, p): return self.db.kill(p) def kill(self, p): return self.db.kill(p)
def list_dbs(self): return self.db.list_dbs().fetch_all_rows() def list_dbs(self): return _fetchall(self.db.list_dbs())
def list_fields(self, table): return self.db.list_fields(table).fetch_all_rows() def list_fields(self, table): return _fetchall(self.db.list_fields(table))
def list_processes(self): return self.db.list_processes().fetch_all_rows() def list_processes(self): return _fetchall(self.db.list_processes())
def list_tables(self, db): return self.db.list_tables(db).fetch_all_rows() def list_tables(self, db): return _fetchall(self.db.list_tables(db))
def field_count(self): return self.db.field_count() def field_count(self): return self.db.field_count()
num_fields = field_count # used prior to MySQL-3.22.24 num_fields = field_count # used prior to MySQL-3.22.24
def ping(self): return self.db.ping() def ping(self): return self.db.ping()

View File

@ -130,6 +130,9 @@ information, see the MySQL documentation. The documentation for this
module is intentionally weak because you probably should use the module is intentionally weak because you probably should use the
higher-level <ref id="MySQLdb"> module. If you really need it, use the higher-level <ref id="MySQLdb"> module. If you really need it, use the
standard MySQL docs and transliterate as necessary. standard MySQL docs and transliterate as necessary.
<p>Compatibility note: As of 0.2.2, the various fetch_rowXXX() cursor
methods have been combined into a single fetch_row([n=1[,how=0]])
method. See the built-in module documentation for more details.
<p> <p>
The C API has been wrapped in an object-oriented way. The only MySQL The C API has been wrapped in an object-oriented way. The only MySQL
data structures which are implemented are the <tt>MYSQL</tt> (database data structures which are implemented are the <tt>MYSQL</tt> (database
@ -154,12 +157,7 @@ use is explicitly non-portable.
@ <tt>mysql_debug()</tt> | <tt>_mysql.debug()</tt> @ <tt>mysql_debug()</tt> | <tt>_mysql.debug()</tt>
@ <tt>mysql_dump_debug_info</tt> | <tt>conn.dump_debug_info()</tt> @ <tt>mysql_dump_debug_info</tt> | <tt>conn.dump_debug_info()</tt>
@ <tt>mysql_escape_string()</tt> | <tt>_mysql.escape_string()</tt> @ <tt>mysql_escape_string()</tt> | <tt>_mysql.escape_string()</tt>
@ <tt>mysql_fetch_row()</tt> | <tt>result.fetch_row()<newline> @ <tt>mysql_fetch_row()</tt> | <tt>result.fetch_row()</tt>
result_fetch_row_as_dict()<newline>
result.fetch_rows()<newline>
result.fetch_rows_as_dict()<newline>
result.fetch_all_rows()<newline>
result.fetch_all_rows_as_dict()</tt>
@ <tt>mysql_get_client_info()</tt> | <tt>_mysql.get_client_info()</tt> @ <tt>mysql_get_client_info()</tt> | <tt>_mysql.get_client_info()</tt>
@ <tt>mysql_get_host_info()</tt> | <tt>conn.get_host_info()</tt> @ <tt>mysql_get_host_info()</tt> | <tt>conn.get_host_info()</tt>
@ <tt>mysql_get_proto_info()</tt> | <tt>conn.get_proto_info()</tt> @ <tt>mysql_get_proto_info()</tt> | <tt>conn.get_proto_info()</tt>

View File

@ -3,9 +3,7 @@
# Adjust -L and -I as necessary for your local configuration. # Adjust -L and -I as necessary for your local configuration.
# If you have dynamic MySQL libraries, they will need to be on your # If you have dynamic MySQL libraries, they will need to be on your
# LD_LIBRARY_PATH at runtime. # LD_LIBRARY_PATH at runtime.
_mysql _mysqlmodule.c -L/usr/lib/mysql -I/usr/include/mysql -lmysqlclient # I find that 3.23 requires libz. It probably won't hurt earlier versions.
# I find that 3.23 requires libz.
_mysql _mysqlmodule.c -L/usr/lib/mysql -I/usr/include/mysql -lmysqlclient -lz _mysql _mysqlmodule.c -L/usr/lib/mysql -I/usr/include/mysql -lmysqlclient -lz
# Uncomment for Windows # Uncomment for Windows

View File

@ -578,19 +578,13 @@ _mysql_escape_row(self, args)
PyObject *o=NULL, *d=NULL, *r=NULL, *item, *quoted, PyObject *o=NULL, *d=NULL, *r=NULL, *item, *quoted,
*itemtype, *itemconv; *itemtype, *itemconv;
int i, n; int i, n;
if (!PyArg_ParseTuple(args, "OO:escape_row", &o, &d)) goto error; if (!PyArg_ParseTuple(args, "O!O!:escape_row", &PyTuple_Type, &o,
if (!PySequence_Check(o)) { &PyDict_Type, &d))
PyErr_SetString(PyExc_TypeError, "sequence required");
goto error; goto error;
}
if (!PyMapping_Check(d)) {
PyErr_SetString(PyExc_TypeError, "mapping required");
goto error;
}
if (!(n = PyObject_Length(o))) goto error; if (!(n = PyObject_Length(o))) goto error;
if (!(r = PyTuple_New(n))) goto error; if (!(r = PyTuple_New(n))) goto error;
for (i=0; i<n; i++) { for (i=0; i<n; i++) {
if (!(item = PySequence_GetItem(o, i))) goto error; item = PyTuple_GET_ITEM(o, i);
if (!(itemtype = PyObject_Type(item))) if (!(itemtype = PyObject_Type(item)))
goto error; goto error;
itemconv = PyObject_GetItem(d, itemtype); itemconv = PyObject_GetItem(d, itemtype);
@ -608,7 +602,6 @@ _mysql_escape_row(self, args)
quoted = PyObject_CallFunction(itemconv, "O", item); quoted = PyObject_CallFunction(itemconv, "O", item);
Py_DECREF(itemconv); Py_DECREF(itemconv);
if (!quoted) goto error; if (!quoted) goto error;
Py_DECREF(item);
PyTuple_SET_ITEM(r, i, quoted); PyTuple_SET_ITEM(r, i, quoted);
} }
return r; return r;
@ -799,12 +792,12 @@ _mysql_row_to_dict_old(self, row)
} }
static PyObject * static PyObject *
_mysql_ResultObject_fetch(self, args, kwargs) _mysql_ResultObject_fetch_row(self, args, kwargs)
_mysql_ResultObject *self; _mysql_ResultObject *self;
PyObject *args, *kwargs; PyObject *args, *kwargs;
{ {
typedef PyObject *_PYFUNC(); typedef PyObject *_PYFUNC();
static char *kwlist[] = { "maxrows", "as_dict", NULL }; static char *kwlist[] = { "maxrows", "how", NULL };
static _PYFUNC *row_converters[] = static _PYFUNC *row_converters[] =
{ {
_mysql_row_to_tuple, _mysql_row_to_tuple,
@ -812,18 +805,18 @@ _mysql_ResultObject_fetch(self, args, kwargs)
_mysql_row_to_dict_old _mysql_row_to_dict_old
}; };
_PYFUNC *convert_row; _PYFUNC *convert_row;
unsigned int maxrows=1, as_dict=0, i; unsigned int maxrows=1, how=0, i;
PyObject *r; PyObject *r;
MYSQL_ROW row; MYSQL_ROW row;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ii:fetch", kwlist, if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ii:fetch_row", kwlist,
&maxrows, &as_dict)) &maxrows, &how))
return NULL; return NULL;
if (as_dict < 0 || as_dict >= sizeof(row_converters)) { if (how < 0 || how >= sizeof(row_converters)) {
PyErr_SetString(PyExc_ValueError, "as_dict out of range"); PyErr_SetString(PyExc_ValueError, "how out of range");
return NULL; return NULL;
} }
convert_row = row_converters[as_dict]; convert_row = row_converters[how];
if (!(r = PyTuple_New(maxrows))) return NULL; if (!(r = PyTuple_New(maxrows))) return NULL;
for (i = 0; i<maxrows; i++) { for (i = 0; i<maxrows; i++) {
@ -835,8 +828,10 @@ _mysql_ResultObject_fetch(self, args, kwargs)
row = mysql_fetch_row(self->result); row = mysql_fetch_row(self->result);
Py_END_ALLOW_THREADS; Py_END_ALLOW_THREADS;
} }
if (!row && mysql_errno(self->connection)) if (!row && mysql_errno(self->connection)) {
Py_XDECREF(r);
return _mysql_Exception(self->connection); return _mysql_Exception(self->connection);
}
if (!row) { if (!row) {
if (_PyTuple_Resize(&r, i, 0) == -1) goto error; if (_PyTuple_Resize(&r, i, 0) == -1) goto error;
break; break;
@ -1312,7 +1307,7 @@ static PyMethodDef _mysql_ResultObject_methods[] = {
{"row_seek", (PyCFunction)_mysql_ResultObject_row_seek, 1}, {"row_seek", (PyCFunction)_mysql_ResultObject_row_seek, 1},
{"row_tell", (PyCFunction)_mysql_ResultObject_row_tell, 0}, {"row_tell", (PyCFunction)_mysql_ResultObject_row_tell, 0},
{"describe", (PyCFunction)_mysql_ResultObject_describe, 0}, {"describe", (PyCFunction)_mysql_ResultObject_describe, 0},
{"fetch", (PyCFunction)_mysql_ResultObject_fetch, METH_VARARGS | METH_KEYWORDS}, {"fetch_row", (PyCFunction)_mysql_ResultObject_fetch_row, METH_VARARGS | METH_KEYWORDS},
{"field_flags", (PyCFunction)_mysql_ResultObject_field_flags, 0}, {"field_flags", (PyCFunction)_mysql_ResultObject_field_flags, 0},
{"num_fields", (PyCFunction)_mysql_ResultObject_num_fields, 0}, {"num_fields", (PyCFunction)_mysql_ResultObject_num_fields, 0},
{"num_rows", (PyCFunction)_mysql_ResultObject_num_rows, 0}, {"num_rows", (PyCFunction)_mysql_ResultObject_num_rows, 0},
@ -1485,9 +1480,9 @@ FLAG_*, CLIENT_*, FIELD_TYPE_*, etc. constants are renamed to FLAG.*,\n\
CLIENT.*, FIELD_TYPE.*, etc. Deprecated functions are NOT implemented.\n\ CLIENT.*, FIELD_TYPE.*, etc. Deprecated functions are NOT implemented.\n\
\n\ \n\
type_conv is a dictionary which maps FIELD_TYPE.* to Python functions\n\ type_conv is a dictionary which maps FIELD_TYPE.* to Python functions\n\
which convert a string to some value. This is used by the various\n\ which convert a string to some value. This is used by the fetch_row method.\n\
fetch methods. Types not mapped are returned as strings. Numbers are\n\ Types not mapped are returned as strings. Numbers are all converted\n\
all converted reasonably, except DECIMAL.\n\ reasonably, except DECIMAL.\n\
\n\ \n\
result.describe() produces a DB API description of the rows.\n\ result.describe() produces a DB API description of the rows.\n\
\n\ \n\
@ -1501,14 +1496,10 @@ mysql_escape_string() on them, and returns them as a tuple.\n\
\n\ \n\
result.field_flags() returns the field flags for the result.\n\ result.field_flags() returns the field flags for the result.\n\
\n\ \n\
result.fetch_row() fetches the next row as a tuple of objects. MySQL\n\ result.fetch_row([n=0[, how=1]]) fetches up to n rows (default: n=1)\n\
returns strings, but fetch_row() does data conversion according to\n\ as a tuple of tuples (default: how=0) or dictionaries (how=1).\n\
type_conv.\n\ MySQL returns strings, but fetch_row() does data conversion\n\
\n\ according to type_conv.\n\
result.fetch_rows(n) is like fetch_row() but fetches up to n rows and\n\
returns a tuple of rows.\n\
\n\
result.fetch_all_rows() is like fetch_rows() but fetchs all rows.\n\
\n\ \n\
For everything else, check the MySQL docs." ; For everything else, check the MySQL docs." ;