# HG changeset patch # User adustman # Date 1266811004 0 # Node ID 3b03cb566032ef5305e4e3f40911e3b47ce5d1a8 # Parent 80164eb2f09000e6a0e8911d55a0d2e637925ab1 More serious restructuring and cleaning, especially in the handling of result sets. All tests pass. diff -r 80164eb2f090 -r 3b03cb566032 MySQLdb/connections.py --- a/MySQLdb/connections.py Sat Feb 20 04:27:21 2010 +0000 +++ b/MySQLdb/connections.py Mon Feb 22 03:56:44 2010 +0000 @@ -126,8 +126,7 @@ """ from MySQLdb.constants import CLIENT, FIELD_TYPE - from MySQLdb.converters import default_decoders, default_encoders - from MySQLdb.converters import simple_type_encoders as conversions + from MySQLdb.converters import default_decoders, default_encoders, default_row_formatter from MySQLdb.cursors import Cursor import _mysql @@ -135,10 +134,10 @@ self.cursorclass = Cursor charset = kwargs2.pop('charset', '') - if 'decoder_stack' not in kwargs2: - kwargs2['decoder_stack'] = default_decoders; + self.encoders = kwargs2.pop('encoders', default_encoders) self.decoders = kwargs2.pop('decoders', default_decoders) + self.row_formatter = kwargs2.pop('row_formatter', default_row_formatter) client_flag = kwargs.get('client_flag', 0) client_version = tuple( @@ -187,14 +186,14 @@ def close(self): return self._db.close() - + def escape_string(self, s): return self._db.escape_string(s) def string_literal(self, s): - return self._db.string_literal(s) - - def cursor(self, encoders=None, decoders=None): + return self._db.string_literal(s) + + def cursor(self, encoders=None, decoders=None, row_formatter=None): """ Create a cursor on which queries may be performed. The optional cursorclass parameter is used to create the Cursor. By default, @@ -208,8 +207,11 @@ if not decoders: decoders = self.decoders[:] + + if not row_formatter: + row_formatter = self.row_formatter - self._active_cursor = self.cursorclass(self, encoders, decoders) + self._active_cursor = self.cursorclass(self, encoders, decoders, row_formatter) return self._active_cursor def __enter__(self): @@ -220,7 +222,7 @@ self.rollback() else: self.commit() - + def literal(self, obj): """ Given an object obj, returns an SQL literal as a string. @@ -234,17 +236,6 @@ raise self.NotSupportedError("could not encode as SQL", obj) - def _warning_count(self): - """Return the number of warnings generated from the last query.""" - if hasattr(self._db, "warning_count"): - return self._db.warning_count() - else: - info = self._db.info() - if info: - return int(info.split()[-1]) - else: - return 0 - def character_set_name(self): return self._db.character_set_name() @@ -263,9 +254,7 @@ if self._server_version < (4, 1): raise self.NotSupportedError("server is too old to set charset") self._db.query('SET NAMES %s' % charset) - self._db.store_result() - self.string_decoder.charset = charset - self.unicode_literal.charset = charset + self._db.get_result() def set_sql_mode(self, sql_mode): """Set the connection sql_mode. See MySQL documentation for legal @@ -276,8 +265,19 @@ if self._server_version < (4, 1): raise self.NotSupportedError("server is too old to set sql_mode") self._db.query("SET SESSION sql_mode='%s'" % sql_mode) - self._db.store_result() + self._db.get_result() + def _warning_count(self): + """Return the number of warnings generated from the last query.""" + if hasattr(self._db, "warning_count"): + return self._db.warning_count() + else: + info = self._db.info() + if info: + return int(info.split()[-1]) + else: + return 0 + def _show_warnings(self): """Return detailed information about warnings as a sequence of tuples of (Level, Code, Message). This is only supported in MySQL-4.1 and up. @@ -287,7 +287,6 @@ so you should not usually call it yourself.""" if self._server_version < (4, 1): return () self._db.query("SHOW WARNINGS") - result = self._db.store_result() - warnings = result.fetch_row(0) - return warnings + return tuple(self._db.get_result()) + diff -r 80164eb2f090 -r 3b03cb566032 MySQLdb/converters.py --- a/MySQLdb/converters.py Sat Feb 20 04:27:21 2010 +0000 +++ b/MySQLdb/converters.py Mon Feb 22 03:56:44 2010 +0000 @@ -110,7 +110,7 @@ } # Decoder protocol -# Each decoder is passed a cursor object and a field object. +# Each decoder is passed a field object. # The decoder returns a single value: # * A callable that given an SQL value, returns a Python object. # This can be as simple as int or str, etc. If the decoder @@ -178,8 +178,5 @@ return None return tuple(iter_row_decoder(decoders, row)) - - +default_row_formatter = tuple_row_decoder - - diff -r 80164eb2f090 -r 3b03cb566032 MySQLdb/cursors.py --- a/MySQLdb/cursors.py Sat Feb 20 04:27:21 2010 +0000 +++ b/MySQLdb/cursors.py Mon Feb 22 03:56:44 2010 +0000 @@ -13,7 +13,8 @@ import re import sys import weakref -from MySQLdb.converters import get_codec, tuple_row_decoder +from MySQLdb.converters import get_codec +from warnings import warn INSERT_VALUES = re.compile(r"(?P.+values\s*)" r"(?P\(((?= len(self.rows): + row = self.result.fetch_row() + if row is None: + return row + self.rows.append(self.row_formatter(self.row_decoders, row)) + if self.row_index >= len(self.rows): + return None + row = self.rows[self.row_index] + self.row_index += 1 + return row + + def __iter__(self): return self + + def next(self): + row = self.fetchone() + if row is None: + raise StopIteration + return row + + def fetchmany(self, size): + """Fetch up to size rows from the cursor. Result set may be smaller + than size. If size is not defined, cursor.arraysize is used.""" + row_end = self.row_index + size + if self.result: + while self.row_index >= len(self.rows): + row = self.result.fetch_row() + if row is None: + break + self.rows.append(self.row_formatter(self.row_decoders, row)) + if self.row_index >= len(self.rows): + return [] + if row_end >= len(self.rows): + row_end = len(self.rows) + rows = self.rows[self.row_index:row_end] + self.row_index = row_end + return rows + + def fetchall(self): + if self.result: + self.flush() + rows = self.rows[self.row_index:] + self.row_index = len(self.rows) + return rows + + def warning_check(self): + """Check for warnings, and report via the warnings module.""" + if self.warning_count: + cursor = self.cursor + warnings = cursor._get_db()._show_warnings() + if warnings: + # This is done in two loops in case + # Warnings are set to raise exceptions. + for warning in warnings: + cursor.warnings.append((self.Warning, warning)) + for warning in warnings: + warn(warning[-1], self.Warning, 3) + elif self._info: + cursor.messages.append((self.Warning, self._info)) + warn(self._info, self.Warning, 3) + + diff -r 80164eb2f090 -r 3b03cb566032 src/connections.c --- a/src/connections.c Sat Feb 20 04:27:21 2010 +0000 +++ b/src/connections.c Mon Feb 22 03:56:44 2010 +0000 @@ -9,7 +9,6 @@ PyObject *kwargs) { MYSQL *conn = NULL; - PyObject *decoder_stack = NULL; PyObject *ssl = NULL; #if HAVE_OPENSSL char *key = NULL, *cert = NULL, *ca = NULL, @@ -20,7 +19,7 @@ unsigned int port = 0; unsigned int client_flag = 0; static char *kwlist[] = { "host", "user", "passwd", "db", "port", - "unix_socket", "decoder_stack", + "unix_socket", "connect_timeout", "compress", "named_pipe", "init_command", "read_default_file", "read_default_group", @@ -33,13 +32,12 @@ *read_default_file=NULL, *read_default_group=NULL; - self->decoder_stack = NULL; self->open = 0; check_server_init(-1); - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ssssisOiiisssiOi:connect", + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ssssisiiisssiOi:connect", kwlist, &host, &user, &passwd, &db, - &port, &unix_socket, &decoder_stack, + &port, &unix_socket, &connect_timeout, &compress, &named_pipe, &init_command, &read_default_file, @@ -107,12 +105,6 @@ return -1; } - if (!decoder_stack) - decoder_stack = PyList_New(0); - else - Py_INCREF(decoder_stack); - self->decoder_stack = decoder_stack; - /* PyType_GenericAlloc() automatically sets up GC allocation and tracking for GC objects, at least in 2.2.1, so it does not need to @@ -195,16 +187,12 @@ visitproc visit, void *arg) { - if (self->decoder_stack) - return visit(self->decoder_stack, arg); return 0; } static int _mysql_ConnectionObject_clear( _mysql_ConnectionObject *self) { - Py_XDECREF(self->decoder_stack); - self->decoder_stack = NULL; return 0; } @@ -399,7 +387,7 @@ now call store_result(), warning_count(), affected_rows()\n\ , and so forth. \n\ \n\ -Returns 0 if there are more results; -1 if there are no more results\n\ +Returns True if there are more results.\n\ \n\ Non-standard.\n\ "; @@ -418,7 +406,7 @@ #endif Py_END_ALLOW_THREADS if (err > 0) return _mysql_Exception(self); - return PyInt_FromLong(err); + return PyInt_FromLong(err == 0); } #if MYSQL_VERSION_ID >= 40100 @@ -917,22 +905,25 @@ return PyString_FromString(s); } -static char _mysql_ConnectionObject_store_result__doc__[] = -"Returns a result object acquired by mysql_store_result\n\ -(results stored in the client). If no results are available,\n\ -None is returned. Non-standard.\n\ +static char _mysql_ConnectionObject_get_result__doc__[] = +"Returns a result object. If use is True, mysql_use_result()\n\ +is used; otherwise mysql_store_result() is used (the default).\n\ "; static PyObject * -_mysql_ConnectionObject_store_result( +_mysql_ConnectionObject_get_result( _mysql_ConnectionObject *self, - PyObject *unused) + PyObject *args, + PyObject *kwargs) { PyObject *arglist=NULL, *kwarglist=NULL, *result=NULL; + static char *kwlist[] = {"use", NULL}; _mysql_ResultObject *r=NULL; - + int use = 0; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i:get_result", kwlist, &use)) return NULL; check_connection(self); - arglist = Py_BuildValue("(OiO)", self, 0, self->decoder_stack); + arglist = Py_BuildValue("(Oi)", self, use); if (!arglist) goto error; kwarglist = PyDict_New(); if (!kwarglist) goto error; @@ -977,41 +968,6 @@ return PyInt_FromLong((long)pid); } -static char _mysql_ConnectionObject_use_result__doc__[] = -"Returns a result object acquired by mysql_use_result\n\ -(results stored in the server). If no results are available,\n\ -None is returned. Non-standard.\n\ -"; - -static PyObject * -_mysql_ConnectionObject_use_result( - _mysql_ConnectionObject *self, - PyObject *unused) -{ - PyObject *arglist=NULL, *kwarglist=NULL, *result=NULL; - _mysql_ResultObject *r=NULL; - - check_connection(self); - arglist = Py_BuildValue("(OiO)", self, 1, self->decoder_stack); - if (!arglist) return NULL; - kwarglist = PyDict_New(); - if (!kwarglist) goto error; - r = MyAlloc(_mysql_ResultObject, _mysql_ResultObject_Type); - if (!r) goto error; - result = (PyObject *) r; - if (_mysql_ResultObject_Initialize(r, arglist, kwarglist)) - goto error; - if (!(r->result)) { - Py_DECREF(result); - Py_INCREF(Py_None); - result = Py_None; - } - error: - Py_DECREF(arglist); - Py_XDECREF(kwarglist); - return result; -} - static void _mysql_ConnectionObject_dealloc( _mysql_ConnectionObject *self) @@ -1171,6 +1127,12 @@ _mysql_ConnectionObject_get_proto_info__doc__ }, { + "get_result", + (PyCFunction)_mysql_ConnectionObject_get_result, + METH_VARARGS | METH_KEYWORDS, + _mysql_ConnectionObject_get_result__doc__ + }, + { "get_server_info", (PyCFunction)_mysql_ConnectionObject_get_server_info, METH_NOARGS, @@ -1225,12 +1187,6 @@ _mysql_ConnectionObject_stat__doc__ }, { - "store_result", - (PyCFunction)_mysql_ConnectionObject_store_result, - METH_NOARGS, - _mysql_ConnectionObject_store_result__doc__ - }, - { "string_literal", (PyCFunction)_mysql_string_literal, METH_VARARGS, @@ -1241,12 +1197,6 @@ METH_NOARGS, _mysql_ConnectionObject_thread_id__doc__ }, - { - "use_result", - (PyCFunction)_mysql_ConnectionObject_use_result, - METH_NOARGS, - _mysql_ConnectionObject_use_result__doc__ - }, {NULL, NULL} /* sentinel */ }; @@ -1259,13 +1209,6 @@ "True if connection is open" }, { - "decoder_stack", - T_OBJECT, - offsetof(_mysql_ConnectionObject, decoder_stack), - 0, - "Type decoder stack" - }, - { "server_capabilities", T_UINT, offsetof(_mysql_ConnectionObject, connection.server_capabilities), diff -r 80164eb2f090 -r 3b03cb566032 src/mysqlmod.c --- a/src/mysqlmod.c Sat Feb 20 04:27:21 2010 +0000 +++ b/src/mysqlmod.c Mon Feb 22 03:56:44 2010 +0000 @@ -255,70 +255,6 @@ PyObject *self, PyObject *args); -static char _mysql_escape_sequence__doc__[] = -"escape_sequence(seq, dict) -- escape any special characters in sequence\n\ -seq using mapping dict to provide quoting functions for each type.\n\ -Returns a tuple of escaped items."; -static PyObject * -_mysql_escape_sequence( - PyObject *self, - PyObject *args) -{ - PyObject *o=NULL, *d=NULL, *r=NULL, *item, *quoted; - int i, n; - if (!PyArg_ParseTuple(args, "OO:escape_sequence", &o, &d)) - goto error; - if (!PyMapping_Check(d)) { - PyErr_SetString(PyExc_TypeError, - "argument 2 must be a mapping"); - return NULL; - } - if ((n = PyObject_Length(o)) == -1) goto error; - if (!(r = PyTuple_New(n))) goto error; - for (i=0; iconn = (PyObject *) conn; Py_INCREF(conn); self->use = use; @@ -74,32 +72,13 @@ self->result = result; Py_END_ALLOW_THREADS ; if (!result) { - self->decoders = PyTuple_New(0); return 0; } n = mysql_num_fields(result); - ns = PySequence_Length(decoder_stack); self->nfields = n; - if (!(self->decoders = PyTuple_New(n))) return -1; self->fields = _mysql_ResultObject_get_fields(self, NULL); - for (i=0; ifields, i); - for (j=0; jdecoders, i, f); - break; - } - Py_DECREF(f); - } - } + return 0; - error: - return -1; } static int @@ -109,25 +88,14 @@ void *arg) { int r; - if (self->decoders) { - if (!(r = visit(self->decoders, arg))) return r; + if (self->fields) { + if (!(r = visit(self->fields, arg))) return r; } if (self->conn) return visit(self->conn, arg); return 0; } -static int -_mysql_ResultObject_clear( - _mysql_ResultObject *self) -{ - Py_XDECREF(self->decoders); - self->decoders = NULL; - Py_XDECREF(self->conn); - self->conn = NULL; - return 0; -} - static char _mysql_ResultObject_describe__doc__[] = "Returns the sequence of 7-tuples required by the DB-API for\n\ the Cursor.description attribute.\n\ @@ -165,6 +133,61 @@ return NULL; } +static char _mysql_ResultObject_fetch_row__doc__[] = +"fetchrow()\n\ + Fetches one row as a tuple of strings.\n\ + NULL is returned as None.\n\ + A single None indicates the end of the result set.\n\ +"; + +static PyObject * +_mysql_ResultObject_fetch_row( + _mysql_ResultObject *self, + PyObject *unused) + { + unsigned int n, i; + unsigned long *length; + PyObject *r=NULL; + MYSQL_ROW row; + + check_result_connection(self); + + if (!self->use) + row = mysql_fetch_row(self->result); + else { + Py_BEGIN_ALLOW_THREADS; + row = mysql_fetch_row(self->result); + Py_END_ALLOW_THREADS; + } + if (!row && mysql_errno(&(((_mysql_ConnectionObject *)(self->conn))->connection))) { + _mysql_Exception((_mysql_ConnectionObject *)self->conn); + goto error; + } + if (!row) { + Py_INCREF(Py_None); + return Py_None; + } + + n = mysql_num_fields(self->result); + if (!(r = PyTuple_New(n))) return NULL; + length = mysql_fetch_lengths(self->result); + for (i=0; iresult); - if (!(r = PyTuple_New(n))) return NULL; - length = mysql_fetch_lengths(self->result); - for (i=0; idecoders, i); - v = _mysql_field_to_python(c, row[i], length[i]); - if (!v) goto error; - PyTuple_SET_ITEM(r, i, v); - } - return r; - error: - Py_XDECREF(r); - return NULL; -} - -static PyObject * -_mysql_row_to_dict( - _mysql_ResultObject *self, - MYSQL_ROW row) -{ - unsigned int n, i; - unsigned long *length; - PyObject *r, *c; - MYSQL_FIELD *fields; - - n = mysql_num_fields(self->result); - if (!(r = PyDict_New())) return NULL; - length = mysql_fetch_lengths(self->result); - fields = mysql_fetch_fields(self->result); - for (i=0; idecoders, i); - v = _mysql_field_to_python(c, row[i], length[i]); - if (!v) goto error; - if (!PyMapping_HasKeyString(r, fields[i].name)) { - PyMapping_SetItemString(r, fields[i].name, v); - } else { - int len; - char buf[256]; - strncpy(buf, fields[i].table, 256); - len = strlen(buf); - strncat(buf, ".", 256-len); - len = strlen(buf); - strncat(buf, fields[i].name, 256-len); - PyMapping_SetItemString(r, buf, v); - } - Py_DECREF(v); - } - return r; - error: - Py_XDECREF(r); - return NULL; -} - -static PyObject * -_mysql_row_to_dict_old( - _mysql_ResultObject *self, - MYSQL_ROW row) -{ - unsigned int n, i; - unsigned long *length; - PyObject *r, *c; - MYSQL_FIELD *fields; - - n = mysql_num_fields(self->result); - if (!(r = PyDict_New())) return NULL; - length = mysql_fetch_lengths(self->result); - fields = mysql_fetch_fields(self->result); - for (i=0; idecoders, i); - v = _mysql_field_to_python(c, row[i], length[i]); - if (!v) goto error; - { - int len=0; - char buf[256]=""; - if (strlen(fields[i].table)) { - strncpy(buf, fields[i].table, 256); - len = strlen(buf); - strncat(buf, ".", 256-len); - len = strlen(buf); - } - strncat(buf, fields[i].name, 256-len); - PyMapping_SetItemString(r, buf, v); - } - Py_DECREF(v); - } - return r; - error: - Py_XDECREF(r); - return NULL; -} - typedef PyObject *_PYFUNC(_mysql_ResultObject *, MYSQL_ROW); -int -_mysql__fetch_row( - _mysql_ResultObject *self, - PyObject **r, - int skiprows, - int maxrows, - _PYFUNC *convert_row) -{ - unsigned int i; - MYSQL_ROW row; - - for (i = skiprows; i<(skiprows+maxrows); i++) { - PyObject *v; - if (!self->use) - row = mysql_fetch_row(self->result); - else { - Py_BEGIN_ALLOW_THREADS; - row = mysql_fetch_row(self->result); - Py_END_ALLOW_THREADS; - } - if (!row && mysql_errno(&(((_mysql_ConnectionObject *)(self->conn))->connection))) { - _mysql_Exception((_mysql_ConnectionObject *)self->conn); - goto error; - } - if (!row) { - if (_PyTuple_Resize(r, i) == -1) goto error; - break; - } - v = convert_row(self, row); - if (!v) goto error; - PyTuple_SET_ITEM(*r, i, v); - } - return i-skiprows; - error: - return -1; -} - -static char _mysql_ResultObject_fetch_row__doc__[] = -"fetch_row([maxrows, how]) -- Fetches up to maxrows as a tuple.\n\ -The rows are formatted according to how:\n\ -\n\ - 0 -- tuples (default)\n\ - 1 -- dictionaries, key=column or table.column if duplicated\n\ - 2 -- dictionaries, key=table.column\n\ +static char _mysql_ResultObject_clear__doc__[] = +"clear()\n\ + Reads to the end of the result set, discarding all the rows.\n\ "; static PyObject * -_mysql_ResultObject_fetch_row( - _mysql_ResultObject *self, - PyObject *args, - PyObject *kwargs) -{ - typedef PyObject *_PYFUNC(_mysql_ResultObject *, MYSQL_ROW); - static char *kwlist[] = { "maxrows", "how", NULL }; - static _PYFUNC *row_converters[] = - { - _mysql_row_to_tuple, - _mysql_row_to_dict, - _mysql_row_to_dict_old - }; - _PYFUNC *convert_row; - unsigned int maxrows=1, how=0, skiprows=0, rowsadded; - PyObject *r=NULL; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ii:fetch_row", kwlist, - &maxrows, &how)) - return NULL; - check_result_connection(self); - if (how < 0 || how >= sizeof(row_converters)) { - PyErr_SetString(PyExc_ValueError, "how out of range"); - return NULL; - } - convert_row = row_converters[how]; - if (maxrows) { - if (!(r = PyTuple_New(maxrows))) goto error; - rowsadded = _mysql__fetch_row(self, &r, skiprows, maxrows, - convert_row); - if (rowsadded == -1) goto error; - } else { - if (self->use) { - maxrows = 1000; - if (!(r = PyTuple_New(maxrows))) goto error; - while (1) { - rowsadded = _mysql__fetch_row(self, &r, skiprows, - maxrows, convert_row); - if (rowsadded == -1) goto error; - skiprows += rowsadded; - if (rowsadded < maxrows) break; - if (_PyTuple_Resize(&r, skiprows + maxrows) == -1) - goto error; - } - } else { - /* XXX if overflow, maxrows<0? */ - maxrows = (int) mysql_num_rows(self->result); - if (!(r = PyTuple_New(maxrows))) goto error; - rowsadded = _mysql__fetch_row(self, &r, 0, - maxrows, convert_row); - if (rowsadded == -1) goto error; - } - } - return r; - error: - Py_XDECREF(r); - return NULL; -} - -static char _mysql_ResultObject_simple_fetch_row__doc__[] = -"simple_fetchrow()\n\ - Fetches one row as a tuple of strings.\n\ - NULL is returned as None.\n\ -"; - -static PyObject * -_mysql_ResultObject_simple_fetch_row( +_mysql_ResultObject_clear( _mysql_ResultObject *self, PyObject *unused) { - unsigned int n, i; - unsigned long *length; - PyObject *r=NULL; - MYSQL_ROW row; - - check_result_connection(self); - - if (!self->use) - row = mysql_fetch_row(self->result); - else { - Py_BEGIN_ALLOW_THREADS; - row = mysql_fetch_row(self->result); - Py_END_ALLOW_THREADS; - } - if (!row && mysql_errno(&(((_mysql_ConnectionObject *)(self->conn))->connection))) { - _mysql_Exception((_mysql_ConnectionObject *)self->conn); - goto error; + if (self->result) { + if (self->use) { + Py_BEGIN_ALLOW_THREADS; + while (mysql_fetch_row(self->result)); + Py_END_ALLOW_THREADS; + + if (mysql_errno(&(((_mysql_ConnectionObject *)(self->conn))->connection))) { + _mysql_Exception((_mysql_ConnectionObject *)self->conn); + return NULL; + } + } } - if (!row) { - Py_INCREF(Py_None); - return Py_None; + Py_XDECREF(self->fields); + self->fields = NULL; + Py_XDECREF(self->conn); + self->conn = NULL; + if (self->result) { + mysql_free_result(self->result); + self->result = NULL; } - - n = mysql_num_fields(self->result); - if (!(r = PyTuple_New(n))) return NULL; - length = mysql_fetch_lengths(self->result); - for (i=0; iresult); - _mysql_ResultObject_clear(self); MyFree(self); } @@ -633,6 +402,12 @@ _mysql_ResultObject_row_tell__doc__ }, { + "clear", + (PyCFunction)_mysql_ResultObject_clear, + METH_NOARGS, + _mysql_ResultObject_clear__doc__ + }, + { "describe", (PyCFunction)_mysql_ResultObject_describe, METH_NOARGS, @@ -644,12 +419,6 @@ METH_VARARGS | METH_KEYWORDS, _mysql_ResultObject_fetch_row__doc__ }, - { - "simple_fetch_row", - (PyCFunction)_mysql_ResultObject_simple_fetch_row, - METH_VARARGS | METH_KEYWORDS, - _mysql_ResultObject_simple_fetch_row__doc__ - }, { "field_flags", @@ -681,13 +450,6 @@ "Connection associated with result" }, { - "decoders", - T_OBJECT, - offsetof(_mysql_ResultObject, decoders), - RO, - "Field decoders for result set" - }, - { "fields", T_OBJECT, offsetof(_mysql_ResultObject, fields),