Mercurial > p > mysql-python > mysqldb-2
diff src/results.c @ 55:e606fd52e866 MySQLdb
make things a little cleaner by moving to a src directory for the C code
author | kylev |
---|---|
date | Fri, 27 Feb 2009 19:14:09 +0000 |
parents | _mysql_results.c@4bfc4e612de0 |
children | 5a7c30cd9de2 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/results.c Fri Feb 27 19:14:09 2009 +0000 @@ -0,0 +1,750 @@ +/* -*- mode: C; indent-tabs-mode: t; c-basic-offset: 8; -*- */ + +#include "mysqlmod.h" + +static char _mysql_ResultObject__doc__[] = +"result(connection, use=0, converter={}) -- Result set from a query.\n\ +\n\ +Creating instances of this class directly is an excellent way to\n\ +shoot yourself in the foot. If using _mysql.connection directly,\n\ +use connection.store_result() or connection.use_result() instead.\n\ +If using MySQLdb.Connection, this is done by the cursor class.\n\ +Just forget you ever saw this. Forget... FOR-GET..."; + +int +_mysql_ResultObject_Initialize( + _mysql_ResultObject *self, + PyObject *args, + PyObject *kwargs) +{ + static char *kwlist[] = {"connection", "use", "converter", NULL}; + MYSQL_RES *result; + _mysql_ConnectionObject *conn=NULL; + int use=0; + PyObject *conv=NULL; + int n, i; + MYSQL_FIELD *fields; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|iO", kwlist, + &conn, &use, &conv)) + return -1; + if (!conv) conv = PyDict_New(); + if (!conv) return -1; + self->conn = (PyObject *) conn; + Py_INCREF(conn); + self->use = use; + Py_BEGIN_ALLOW_THREADS ; + if (use) + result = mysql_use_result(&(conn->connection)); + else + result = mysql_store_result(&(conn->connection)); + self->result = result; + Py_END_ALLOW_THREADS ; + if (!result) { + self->converter = PyTuple_New(0); + return 0; + } + n = mysql_num_fields(result); + self->nfields = n; + if (!(self->converter = PyTuple_New(n))) return -1; + fields = mysql_fetch_fields(result); + for (i=0; i<n; i++) { + PyObject *tmp, *fun; + tmp = PyInt_FromLong((long) fields[i].type); + if (!tmp) return -1; + fun = PyObject_GetItem(conv, tmp); + Py_DECREF(tmp); + if (!fun) { + PyErr_Clear(); + fun = Py_None; + Py_INCREF(Py_None); + } + if (PySequence_Check(fun)) { + int j, n2=PySequence_Size(fun); + PyObject *fun2=NULL; + for (j=0; j<n2; j++) { + PyObject *t = PySequence_GetItem(fun, j); + if (!t) continue; + if (!PyTuple_Check(t)) goto cleanup; + if (PyTuple_GET_SIZE(t) == 2) { + long mask; + PyObject *pmask=NULL; + pmask = PyTuple_GET_ITEM(t, 0); + fun2 = PyTuple_GET_ITEM(t, 1); + if (PyInt_Check(pmask)) { + mask = PyInt_AS_LONG(pmask); + if (mask & fields[i].flags) { + Py_DECREF(t); + break; + } + else { + goto cleanup; + } + } else { + Py_DECREF(t); + break; + } + } + cleanup: + Py_DECREF(t); + } + if (!fun2) fun2 = Py_None; + Py_INCREF(fun2); + Py_DECREF(fun); + fun = fun2; + } + PyTuple_SET_ITEM(self->converter, i, fun); + } + return 0; +} + +static int +_mysql_ResultObject_traverse( + _mysql_ResultObject *self, + visitproc visit, + void *arg) +{ + int r; + if (self->converter) { + if (!(r = visit(self->converter, arg))) return r; + } + if (self->conn) + return visit(self->conn, arg); + return 0; +} + +static int +_mysql_ResultObject_clear( + _mysql_ResultObject *self) +{ + Py_XDECREF(self->converter); + self->converter = 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\ +"; + +static PyObject * +_mysql_ResultObject_describe( + _mysql_ResultObject *self, + PyObject *unused) +{ + PyObject *d; + MYSQL_FIELD *fields; + unsigned int i, n; + + check_result_connection(self); + n = mysql_num_fields(self->result); + fields = mysql_fetch_fields(self->result); + if (!(d = PyTuple_New(n))) return NULL; + for (i=0; i<n; i++) { + PyObject *t; + t = Py_BuildValue("(siiiiii)", + fields[i].name, + (long) fields[i].type, + (long) fields[i].max_length, + (long) fields[i].length, + (long) fields[i].length, + (long) fields[i].decimals, + (long) !(IS_NOT_NULL(fields[i].flags))); + if (!t) goto error; + PyTuple_SET_ITEM(d, i, t); + } + return d; + error: + Py_XDECREF(d); + return NULL; +} + +static char _mysql_ResultObject_fields__doc__[] = +"Returns the sequence of 7-tuples required by the DB-API for\n\ +the Cursor.description attribute.\n\ +"; + +static PyObject * +_mysql_ResultObject_fields( + _mysql_ResultObject *self, + PyObject *unused) +{ + PyObject *arglist=NULL, *kwarglist=NULL; + PyObject *fields=NULL; + _mysql_FieldObject *field=NULL; + unsigned int i, n; + + check_result_connection(self); + kwarglist = PyDict_New(); + if (!kwarglist) goto error; + n = mysql_num_fields(self->result); + if (!(fields = PyTuple_New(n))) return NULL; + for (i=0; i<n; i++) { + arglist = Py_BuildValue("(Oi)", self, i); + if (!arglist) goto error; + field = MyAlloc(_mysql_FieldObject, _mysql_FieldObject_Type); + if (!field) goto error; + if (_mysql_FieldObject_Initialize(field, arglist, kwarglist)) + goto error; + Py_DECREF(arglist); + PyTuple_SET_ITEM(fields, i, (PyObject *) field); + } + Py_DECREF(kwarglist); + return fields; + error: + Py_XDECREF(arglist); + Py_XDECREF(kwarglist); + Py_XDECREF(fields); + return NULL; +} + +static char _mysql_ResultObject_field_flags__doc__[] = +"Returns a tuple of field flags, one for each column in the result.\n\ +" ; + +static PyObject * +_mysql_ResultObject_field_flags( + _mysql_ResultObject *self, + PyObject *unused) +{ + PyObject *d; + MYSQL_FIELD *fields; + unsigned int i, n; + + check_result_connection(self); + n = mysql_num_fields(self->result); + fields = mysql_fetch_fields(self->result); + if (!(d = PyTuple_New(n))) return NULL; + for (i=0; i<n; i++) { + PyObject *f; + if (!(f = PyInt_FromLong((long)fields[i].flags))) goto error; + PyTuple_SET_ITEM(d, i, f); + } + return d; + error: + Py_XDECREF(d); + return NULL; +} + +static PyObject * +_mysql_field_to_python( + PyObject *converter, + char *rowitem, + unsigned long length) +{ + PyObject *v; + if (rowitem) { + if (converter != Py_None) + v = PyObject_CallFunction(converter, + "s#", + rowitem, + (int)length); + else + v = PyString_FromStringAndSize(rowitem, + (int)length); + if (!v) + return NULL; + } else { + Py_INCREF(Py_None); + v = Py_None; + } + return v; +} + +static PyObject * +_mysql_row_to_tuple( + _mysql_ResultObject *self, + MYSQL_ROW row) +{ + unsigned int n, i; + unsigned long *length; + PyObject *r, *c; + + n = mysql_num_fields(self->result); + if (!(r = PyTuple_New(n))) return NULL; + length = mysql_fetch_lengths(self->result); + for (i=0; i<n; i++) { + PyObject *v; + c = PyTuple_GET_ITEM(self->converter, 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; i<n; i++) { + PyObject *v; + c = PyTuple_GET_ITEM(self->converter, 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; i<n; i++) { + PyObject *v; + c = PyTuple_GET_ITEM(self->converter, 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 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_num_fields__doc__[] = +"Returns the number of fields (column) in the result." ; + +static PyObject * +_mysql_ResultObject_num_fields( + _mysql_ResultObject *self, + PyObject *unused) +{ + check_result_connection(self); + return PyInt_FromLong((long)mysql_num_fields(self->result)); +} + +static char _mysql_ResultObject_num_rows__doc__[] = +"Returns the number of rows in the result set. Note that if\n\ +use=1, this will not return a valid value until the entire result\n\ +set has been read.\n\ +"; + +static PyObject * +_mysql_ResultObject_num_rows( + _mysql_ResultObject *self, + PyObject *unused) +{ + check_result_connection(self); + return PyLong_FromUnsignedLongLong(mysql_num_rows(self->result)); +} + + +static char _mysql_ResultObject_data_seek__doc__[] = +"data_seek(n) -- seek to row n of result set"; +static PyObject * +_mysql_ResultObject_data_seek( + _mysql_ResultObject *self, + PyObject *args) +{ + unsigned int row; + if (!PyArg_ParseTuple(args, "i:data_seek", &row)) return NULL; + check_result_connection(self); + mysql_data_seek(self->result, row); + Py_INCREF(Py_None); + return Py_None; +} + +static char _mysql_ResultObject_row_seek__doc__[] = +"row_seek(n) -- seek by offset n rows of result set"; +static PyObject * +_mysql_ResultObject_row_seek( + _mysql_ResultObject *self, + PyObject *args) +{ + int offset; + MYSQL_ROW_OFFSET r; + if (!PyArg_ParseTuple(args, "i:row_seek", &offset)) return NULL; + check_result_connection(self); + if (self->use) { + PyErr_SetString(_mysql_ProgrammingError, + "cannot be used with connection.use_result()"); + return NULL; + } + r = mysql_row_tell(self->result); + mysql_row_seek(self->result, r+offset); + Py_INCREF(Py_None); + return Py_None; +} + +static char _mysql_ResultObject_row_tell__doc__[] = +"row_tell() -- return the current row number of the result set."; +static PyObject * +_mysql_ResultObject_row_tell( + _mysql_ResultObject *self, + PyObject *unused) +{ + MYSQL_ROW_OFFSET r; + + check_result_connection(self); + if (self->use) { + PyErr_SetString(_mysql_ProgrammingError, + "cannot be used with connection.use_result()"); + return NULL; + } + r = mysql_row_tell(self->result); + return PyInt_FromLong(r-self->result->data->data); +} + +static void +_mysql_ResultObject_dealloc( + _mysql_ResultObject *self) +{ + PyObject_GC_UnTrack((PyObject *)self); + mysql_free_result(self->result); + _mysql_ResultObject_clear(self); + MyFree(self); +} + +static PyObject * +_mysql_ResultObject_repr( + _mysql_ResultObject *self) +{ + char buf[300]; + sprintf(buf, "<_mysql.result object at %lx>", + (long)self); + return PyString_FromString(buf); +} + +static PyMethodDef _mysql_ResultObject_methods[] = { + { + "data_seek", + (PyCFunction)_mysql_ResultObject_data_seek, + METH_VARARGS, + _mysql_ResultObject_data_seek__doc__ + }, + { + "row_seek", + (PyCFunction)_mysql_ResultObject_row_seek, + METH_VARARGS, + _mysql_ResultObject_row_seek__doc__ + }, + { + "row_tell", + (PyCFunction)_mysql_ResultObject_row_tell, + METH_NOARGS, + _mysql_ResultObject_row_tell__doc__ + }, + { + "describe", + (PyCFunction)_mysql_ResultObject_describe, + METH_NOARGS, + _mysql_ResultObject_describe__doc__ + }, + { + "fields", + (PyCFunction)_mysql_ResultObject_fields, + METH_NOARGS, + _mysql_ResultObject_fields__doc__ + }, + { + "fetch_row", + (PyCFunction)_mysql_ResultObject_fetch_row, + METH_VARARGS | METH_KEYWORDS, + _mysql_ResultObject_fetch_row__doc__ + }, + { + "field_flags", + (PyCFunction)_mysql_ResultObject_field_flags, + METH_NOARGS, + _mysql_ResultObject_field_flags__doc__ + }, + { + "num_fields", + (PyCFunction)_mysql_ResultObject_num_fields, + METH_NOARGS, + _mysql_ResultObject_num_fields__doc__ + }, + { + "num_rows", + (PyCFunction)_mysql_ResultObject_num_rows, + METH_NOARGS, + _mysql_ResultObject_num_rows__doc__ + }, + {NULL, NULL} /* sentinel */ +}; + +static struct PyMemberDef _mysql_ResultObject_memberlist[] = { + { + "converter", + T_OBJECT, + offsetof(_mysql_ResultObject, converter), + RO, + "Type conversion mapping" + }, + {NULL} /* Sentinel */ +}; + +static PyObject * +_mysql_ResultObject_getattr( + _mysql_ResultObject *self, + char *name) +{ + PyObject *res; + struct PyMemberDef *l; + + res = Py_FindMethod(_mysql_ResultObject_methods, (PyObject *)self, name); + if (res != NULL) + return res; + PyErr_Clear(); + + for (l = _mysql_ResultObject_memberlist; l->name != NULL; l++) { + if (strcmp(l->name, name) == 0) + return PyMember_GetOne((char *)self, l); + } + + PyErr_SetString(PyExc_AttributeError, name); + return NULL; +} + +static int +_mysql_ResultObject_setattr( + _mysql_ResultObject *self, + char *name, + PyObject *v) +{ + struct PyMemberDef *l; + + if (v == NULL) { + PyErr_SetString(PyExc_AttributeError, + "can't delete connection attributes"); + return -1; + } + + for (l = _mysql_ResultObject_memberlist; l->name != NULL; l++) + if (strcmp(l->name, name) == 0) + return PyMember_SetOne((char *)self, l, v); + + PyErr_SetString(PyExc_AttributeError, name); + return -1; +} + +PyTypeObject _mysql_ResultObject_Type = { + PyObject_HEAD_INIT(NULL) + 0, + "_mysql.result", + sizeof(_mysql_ResultObject), + 0, + (destructor)_mysql_ResultObject_dealloc, /* tp_dealloc */ + 0, /*tp_print*/ + (getattrfunc)_mysql_ResultObject_getattr, /* tp_getattr */ + (setattrfunc)_mysql_ResultObject_setattr, /* tp_setattr */ + 0, /*tp_compare*/ + (reprfunc)_mysql_ResultObject_repr, /* tp_repr */ + + /* Method suites for standard classes */ + + 0, /* (PyNumberMethods *) tp_as_number */ + 0, /* (PySequenceMethods *) tp_as_sequence */ + 0, /* (PyMappingMethods *) tp_as_mapping */ + + /* More standard operations (here for binary compatibility) */ + + 0, /* (hashfunc) tp_hash */ + 0, /* (ternaryfunc) tp_call */ + 0, /* (reprfunc) tp_str */ + 0, /* (getattrofunc) tp_getattro */ + 0, /* (setattrofunc) tp_setattro */ + + /* Functions to access object as input/output buffer */ + 0, /* (PyBufferProcs *) tp_as_buffer */ + + /* Flags to define presence of optional/expanded features */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, /* (long) tp_flags */ + + _mysql_ResultObject__doc__, /* (char *) tp_doc Documentation string */ + /* call function for all accessible objects */ + (traverseproc)_mysql_ResultObject_traverse, /* tp_traverse */ + /* delete references to contained objects */ + (inquiry)_mysql_ResultObject_clear, /* tp_clear */ + + /* rich comparisons */ + 0, /* (richcmpfunc) tp_richcompare */ + + /* weak reference enabler */ + 0, /* (long) tp_weaklistoffset */ + + /* Iterators */ + 0, /* (getiterfunc) tp_iter */ + 0, /* (iternextfunc) tp_iternext */ + + /* Attribute descriptor and subclassing stuff */ + (struct PyMethodDef *)_mysql_ResultObject_methods, /* tp_methods */ + (struct PyMemberDef *)_mysql_ResultObject_memberlist, /*tp_members */ + 0, /* (struct getsetlist *) tp_getset; */ + 0, /* (struct _typeobject *) tp_base; */ + 0, /* (PyObject *) tp_dict */ + 0, /* (descrgetfunc) tp_descr_get */ + 0, /* (descrsetfunc) tp_descr_set */ + 0, /* (long) tp_dictoffset */ + (initproc)_mysql_ResultObject_Initialize, /* tp_init */ + NULL, /* tp_alloc */ + NULL, /* tp_new */ + NULL, /* tp_free Low-level free-memory routine */ + 0, /* (PyObject *) tp_bases */ + 0, /* (PyObject *) tp_mro method resolution order */ + 0, /* (PyObject *) tp_defined */ +};