From 47edf13df137fea66f12ca636de5f3fc396f8007 Mon Sep 17 00:00:00 2001 From: adustman Date: Tue, 11 Jul 2000 20:56:29 +0000 Subject: [PATCH] Fix memory leak (maybe). --- mysql/MySQLdb.py | 47 +++++++++++++++++++-------------------- mysql/MySQLdb.sgml | 10 ++++----- mysql/Setup.in | 4 +--- mysql/_mysqlmodule.c | 53 ++++++++++++++++++-------------------------- 4 files changed, 50 insertions(+), 64 deletions(-) diff --git a/mysql/MySQLdb.py b/mysql/MySQLdb.py index 64b5ef4..e64375b 100644 --- a/mysql/MySQLdb.py +++ b/mysql/MySQLdb.py @@ -139,7 +139,14 @@ def escape_dict(d, qc): d2[k] = qc.get(type(v), String2Literal)(v) 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: """A base for Cursor classes. Useful attributes: @@ -208,10 +215,10 @@ class BaseCursor: qc = self.connection.quote_conv try: q = [query % escape(args[0], qc)] - except TypeError, m: - if m.args[0] in ("not enough arguments for format string", + except TypeError, msg: + if msg.args[0] in ("not enough arguments for format string", "not all arguments converted"): - raise ProgrammingError, m.args[0] + raise ProgrammingError, msg.args[0] escape = escape_dict q = [query % escape(args[0], qc)] qv = query[p:] @@ -259,6 +266,10 @@ class CursorStoreResultMixIn: def _get_result(self): return self.connection.db.store_result() + def close(self): + self.connection = None + del self._rows + def _query(self, q): self.connection._acquire() try: @@ -345,17 +356,11 @@ class CursorUseResultMixIn: 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): - 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 + def _fetch_all_rows(self): return _fetchall(self._result, self.arraysize) class CursorDictRowsMixIn: @@ -364,13 +369,7 @@ class CursorDictRowsMixIn: def _fetch_rows(self, size): return self._result.fetch(size, 1) - def _fetch_all_rows(self): - 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 + def _fetch_all_rows(self): return _fetchall(self._result, self.arraysize, 1) ## XXX Deprecated @@ -481,10 +480,10 @@ class Connection: def get_server_info(self): return self.db.get_server_info() def info(self): return self.db.info() def kill(self, p): return self.db.kill(p) - def list_dbs(self): return self.db.list_dbs().fetch_all_rows() - def list_fields(self, table): return self.db.list_fields(table).fetch_all_rows() - def list_processes(self): return self.db.list_processes().fetch_all_rows() - def list_tables(self, db): return self.db.list_tables(db).fetch_all_rows() + def list_dbs(self): return _fetchall(self.db.list_dbs()) + def list_fields(self, table): return _fetchall(self.db.list_fields(table)) + def list_processes(self): return _fetchall(self.db.list_processes()) + def list_tables(self, db): return _fetchall(self.db.list_tables(db)) def field_count(self): return self.db.field_count() num_fields = field_count # used prior to MySQL-3.22.24 def ping(self): return self.db.ping() diff --git a/mysql/MySQLdb.sgml b/mysql/MySQLdb.sgml index f5db15c..4d0731f 100644 --- a/mysql/MySQLdb.sgml +++ b/mysql/MySQLdb.sgml @@ -130,6 +130,9 @@ information, see the MySQL documentation. The documentation for this module is intentionally weak because you probably should use the higher-level module. If you really need it, use the standard MySQL docs and transliterate as necessary. +

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.

The C API has been wrapped in an object-oriented way. The only MySQL data structures which are implemented are the MYSQL (database @@ -154,12 +157,7 @@ use is explicitly non-portable. @ mysql_debug() | _mysql.debug() @ mysql_dump_debug_info | conn.dump_debug_info() @ mysql_escape_string() | _mysql.escape_string() -@ mysql_fetch_row() | result.fetch_row() -result_fetch_row_as_dict() -result.fetch_rows() -result.fetch_rows_as_dict() -result.fetch_all_rows() -result.fetch_all_rows_as_dict() +@ mysql_fetch_row() | result.fetch_row() @ mysql_get_client_info() | _mysql.get_client_info() @ mysql_get_host_info() | conn.get_host_info() @ mysql_get_proto_info() | conn.get_proto_info() diff --git a/mysql/Setup.in b/mysql/Setup.in index ba0612a..853500a 100644 --- a/mysql/Setup.in +++ b/mysql/Setup.in @@ -3,9 +3,7 @@ # Adjust -L and -I as necessary for your local configuration. # If you have dynamic MySQL libraries, they will need to be on your # LD_LIBRARY_PATH at runtime. -_mysql _mysqlmodule.c -L/usr/lib/mysql -I/usr/include/mysql -lmysqlclient - -# I find that 3.23 requires libz. +# I find that 3.23 requires libz. It probably won't hurt earlier versions. _mysql _mysqlmodule.c -L/usr/lib/mysql -I/usr/include/mysql -lmysqlclient -lz # Uncomment for Windows diff --git a/mysql/_mysqlmodule.c b/mysql/_mysqlmodule.c index 56625f3..8e4885a 100644 --- a/mysql/_mysqlmodule.c +++ b/mysql/_mysqlmodule.c @@ -578,19 +578,13 @@ _mysql_escape_row(self, args) PyObject *o=NULL, *d=NULL, *r=NULL, *item, *quoted, *itemtype, *itemconv; int i, n; - if (!PyArg_ParseTuple(args, "OO:escape_row", &o, &d)) goto error; - if (!PySequence_Check(o)) { - PyErr_SetString(PyExc_TypeError, "sequence required"); + if (!PyArg_ParseTuple(args, "O!O!:escape_row", &PyTuple_Type, &o, + &PyDict_Type, &d)) goto error; - } - if (!PyMapping_Check(d)) { - PyErr_SetString(PyExc_TypeError, "mapping required"); - goto error; - } if (!(n = PyObject_Length(o))) goto error; if (!(r = PyTuple_New(n))) goto error; for (i=0; i= sizeof(row_converters)) { - PyErr_SetString(PyExc_ValueError, "as_dict out of range"); + if (how < 0 || how >= sizeof(row_converters)) { + PyErr_SetString(PyExc_ValueError, "how out of range"); return NULL; } - convert_row = row_converters[as_dict]; + convert_row = row_converters[how]; if (!(r = PyTuple_New(maxrows))) return NULL; for (i = 0; iresult); Py_END_ALLOW_THREADS; } - if (!row && mysql_errno(self->connection)) + if (!row && mysql_errno(self->connection)) { + Py_XDECREF(r); return _mysql_Exception(self->connection); + } if (!row) { if (_PyTuple_Resize(&r, i, 0) == -1) goto error; break; @@ -1312,7 +1307,7 @@ static PyMethodDef _mysql_ResultObject_methods[] = { {"row_seek", (PyCFunction)_mysql_ResultObject_row_seek, 1}, {"row_tell", (PyCFunction)_mysql_ResultObject_row_tell, 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}, {"num_fields", (PyCFunction)_mysql_ResultObject_num_fields, 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\ \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\ -fetch methods. Types not mapped are returned as strings. Numbers are\n\ -all converted reasonably, except DECIMAL.\n\ +which convert a string to some value. This is used by the fetch_row method.\n\ +Types not mapped are returned as strings. Numbers are all converted\n\ +reasonably, except DECIMAL.\n\ \n\ result.describe() produces a DB API description of the rows.\n\ \n\ @@ -1501,14 +1496,10 @@ mysql_escape_string() on them, and returns them as a tuple.\n\ \n\ result.field_flags() returns the field flags for the result.\n\ \n\ -result.fetch_row() fetches the next row as a tuple of objects. MySQL\n\ -returns strings, but fetch_row() does data conversion according to\n\ -type_conv.\n\ -\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\ +result.fetch_row([n=0[, how=1]]) fetches up to n rows (default: n=1)\n\ +as a tuple of tuples (default: how=0) or dictionaries (how=1).\n\ +MySQL returns strings, but fetch_row() does data conversion\n\ +according to type_conv.\n\ \n\ For everything else, check the MySQL docs." ;