/* A Python interface to the MySQL database. Python: http://www.python.org MySQL: http://www.tcx.se Copyright (C) 1998 Joerg Senekowitsch Based on mySQLmodule-0.1.4 (see below) WARNING: This code is not compatible with mySQLmodule-0.1.4!!! This interface tested with Python 1.5.1 and MySQL 3.21.30 on Linux RH5.0 (kernel 2.0.33). It works for me, YMMV. JS, May 1998 Version 1.4 (JS 10/03/1998): - Another segfault fix from Richard Fish (rjf@estinc.com). STH reference counting off by one for server side storage. - Patch to return unsigned longs from Ladislav Lhotka (lhotka@jcu.cz). If the mysql field has the UNSIGNED attribute set, we need to suppress the sign bit evaluation and return a PyLong instead of a PyInt. - The home-made escape_chars() has been replaced by a call to mysql_escape_string(), requiring at least 3.21.30 for correct operation. - listfields() and fields() now return the information: o usign - field has the UNSIGNED attribute set - Unsigned fields return PyLongs instead of PyInts. - Support compilation on NT systems by Nigel Head (nhead@houbits.com). See README.NT for details. Note: The NT port only works with MySQL version 3.22.8, while this module has been developed for the 3.21 API. Thus, some of the 3.22 functionality is missing. In particular, the return values of mysql_affected_rows() and mysql_insert_id() have changed to my_ulonglong in 3.22, which will be represented by this module as standard signed PyInt potentially resulting in some odd behavior for DBs with more than 2**31-1 rows and/or insert operations. Version 1.3 (JS 08/18/1998): - Fixed segfaults due to uninitialized field STH->res in DBH_query() (Thanks to Grisha Trubetskoy) - Added instructions on how to compile MySQLmodule as a shared module (Thanks to Trond Eivind Glomsrød) Version 1.2 (JS 07/16/1998): - Error checking on all mysql API calls - Tossed out useless includes which should make FreeBSD happy - Added __doc__ string and changed DbType and ResType to DBH_Type and STH_Type - DBH.do and DBH[...] now return affected_rows() if no result - listdbs(), listtables(), listfields(), and listprocesses() now return empty lists instead of None if there is no data. - Added DBHObjects: o close() o insert_id() - listfields() and fields() now return more information: o pri - primary key field o notnull - not null field o auto_inc - auto_increment field o ukey - unique key field o mkey - multiple key field Version 1.1a (JS 06/30/98) [never released]: - Fixed bug in protoinfo() - Additional error checking in listdbs(), listtables(), listfields(), listprocesses() Version 1.1 (JS 06/11/98): - Cleaner error handling after discussion with Monty (thanks!) - New feature: o DBH.selectdb(string[,int]) will now take optional integer parameter to switch between client side (0, default) and server side (>0) result set storage. This should improve the behavior of low memory clients. Note that this affects server performance negatively. o DBH.query(string[,int]) will allow overriding the DB selected result set storage method on a per cursor basis. Version 1.0a: - Hopefully all possible errors (Python & MySQL) are caught and handled. New features in version 1.0: - Name change to 'MySQL' so that this module does not clash with the old (incompatible) 'mySQL' module. - Plugged huge memory leak in listdbs, listtables, and listfields - Added wildcard argument to listdbs, listtables, and listfields - Allow \0 in queries (for inserting images!) - Added MySQLObjects: o escape() - Added DBHObjects: o stat() o clientinfo() [overwritten with module version string] o serverinfo() o hostinfo() o protoinfo() o listprocesses() o do() [former 'query'] o query() [former 'querycursor'] - Added STHObjects: o fetchrows([n]) [n<0: all records (default), n>0: only n records] o fetchdict([n]) [same as fetchrows(). Returns list of dicts] - Removed STHObjects: o fetchall() o fetchone() o fetchmany() All database results are returned in a 'table', i.e. a list of lists (or list of dictionaries for fetchdict()). ******************************************************************************** Copyright: Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed from any source distribution. Joerg Senekowitsch Notice: This code is based on mySQLmodule-0.1.4, which is Copyright (C) 1997 Joseph Skinner Copyright (C) 1997 James Henstridge mySQLmodule-0.1.4 is based on mSQLmodule, which is Portions copyright (C) 1995 Thawte Consulting, cc Portions copyright (C) 1994 Anthony Baxter. See the file 'Credits' for details. Disclaimer: THE AUTHOR MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THE SOFTWARE FOR ANY PURPOSE. IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE AUTHOR BE LIABLE TO YOU OR ANY OTHER PARTY FOR ANY SPECIAL, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE, STRICT LIABILITY OR ANY OTHER ACTION ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifdef WIN32 typedef void *HANDLE; #include "winsock.h" #endif #include "Python.h" #include "mysql.h" static char MySQL_Version[] = "MySQLmodule-1.4: A Python interface to the MySQL database."; typedef struct MySQL_DBH { PyObject_HEAD MYSQL *handle; MYSQL connection; int dbh_use_result; struct MySQL_STH *sth; } DBHObject; typedef struct MySQL_STH { PyObject_HEAD MYSQL_RES *res; #ifdef WIN32 my_ulonglong affected_rows; my_ulonglong insert_id; #else int affected_rows; int insert_id; #endif int sth_use_result; struct MySQL_DBH *dbh; } STHObject; staticforward PyTypeObject DBH_Type; staticforward PyTypeObject STH_Type; static PyObject *MySQLError; #define STHObject_Check(v) ((v)->ob_type == &STH_Type) #define DBHObject_Check(v) ((v)->ob_type == &DBH_Type) /********************************************************* ****** Helper routines *********************************************************/ static int clear_channel(self) STHObject *self; { if (self->res) { /* only if we have a result */ if (!mysql_eof(self->res)) { MYSQL_ROW dummy; while (dummy = mysql_fetch_row(self->res)) continue; /* check for error. make sure handle exists, though */ if (self->res->handle && mysql_error(self->res->handle)[0] != 0) { PyErr_SetString(MySQLError,mysql_error(self->res->handle)); return 1; } } } return 0; } /* catch empty cursors (STH) */ static int no_response(self) STHObject *self; { if (self->res == NULL) { PyErr_SetString(MySQLError,"no response body"); return 1; } return 0; } /* Take a MYSQL_ROW and turn it into a list */ static PyObject * pythonify_row(res, thisrow) MYSQL_RES *res; MYSQL_ROW thisrow; { PyObject *rowlist, *fieldobj; MYSQL_FIELD *tf; int i, n; unsigned long *lengths; n = mysql_num_fields(res); lengths = mysql_fetch_lengths(res); if (lengths == NULL) { if (res->handle && mysql_error(res->handle)[0] != 0) { PyErr_SetString(MySQLError,mysql_error(res->handle)); } else { PyErr_SetString(MySQLError, "pythonify_row: mysql_fetch_lengths() failed"); } return NULL; } rowlist = PyList_New(n); if (rowlist == NULL) return NULL; mysql_field_seek(res, 0); for (i = 0; i < n; i++) { tf = mysql_fetch_field(res); if (tf == NULL) { if (res->handle && mysql_error(res->handle)[0] != 0) { PyErr_SetString(MySQLError,mysql_error(res->handle)); } else { PyErr_SetString(MySQLError, "pythonify_row: mysql_fetch_field() failed"); } Py_XDECREF(rowlist); return NULL; } if (thisrow[i]) switch (tf->type) { case FIELD_TYPE_SHORT: case FIELD_TYPE_LONG: if (tf->flags & UNSIGNED_FLAG) { fieldobj = PyLong_FromString(thisrow[i], (char **)NULL, 10); } else { fieldobj = PyInt_FromLong(atol(thisrow[i])); } if (fieldobj == NULL) { Py_XDECREF(rowlist); return NULL; } break; case FIELD_TYPE_CHAR: case FIELD_TYPE_STRING: case FIELD_TYPE_VAR_STRING: case FIELD_TYPE_DATE: case FIELD_TYPE_TIME: case FIELD_TYPE_DATETIME: case FIELD_TYPE_TIMESTAMP: fieldobj = PyString_FromString(thisrow[i]); if (fieldobj == NULL) { Py_XDECREF(rowlist); return NULL; } break; case FIELD_TYPE_TINY_BLOB: case FIELD_TYPE_MEDIUM_BLOB: case FIELD_TYPE_LONG_BLOB: case FIELD_TYPE_BLOB: fieldobj = PyString_FromStringAndSize(thisrow[i],lengths[i]); if (fieldobj == NULL) { Py_XDECREF(rowlist); return NULL; } break; case FIELD_TYPE_DECIMAL: case FIELD_TYPE_DOUBLE: case FIELD_TYPE_FLOAT: fieldobj = PyFloat_FromDouble(atof(thisrow[i])); if (fieldobj == NULL) { Py_XDECREF(rowlist); return NULL; } break; default: fieldobj = PyString_FromString(thisrow[i]); if (fieldobj == NULL) { Py_XDECREF(rowlist); return NULL; } break; } else { Py_INCREF(Py_None); fieldobj = Py_None; } if (PyList_SetItem(rowlist, i, fieldobj) == -1) { Py_XDECREF(rowlist); return NULL; } } return rowlist; } /* Take a MYSQL_RES and turn it into a table (list of lists). Depending on whether (res) is local (from a mysql_store_result) or remote (from mysql_use_result) we might see client/server errors on the mysql_fetch_row() call. If we receive a NULL from mysql_fetch_row() we must check whether this is "no more data" or indeed an error. If the error was generated by pythonify_row, rowlist will be NULL and PyErr will already be set. */ static PyObject * pythonify_res(res, num) MYSQL_RES *res; int num; { PyObject *reslist; PyObject *rowlist = NULL; MYSQL_ROW thisrow; int i = 0; reslist = PyList_New(0); if (reslist == NULL) return NULL; while ((i != num) && (thisrow = mysql_fetch_row(res))) { i++; rowlist = pythonify_row(res, thisrow); if (rowlist == NULL) goto error; if (PyList_Append(reslist, rowlist) == -1) goto error; Py_XDECREF(rowlist); rowlist = NULL; } if (thisrow == NULL && res->handle && mysql_error(res->handle)[0] != 0) { PyErr_SetString(MySQLError,mysql_error(res->handle)); Py_XDECREF(reslist); return NULL; } return (reslist); error: Py_XDECREF(rowlist); Py_XDECREF(reslist); return NULL; } /* Take a MYSQL_RES and return a table of field data */ static PyObject * pythonify_res_fields(res) MYSQL_RES *res; { PyObject *reslist, *thislist; int i, n; char *type, flags[32]; MYSQL_FIELD *tf; reslist = PyList_New(0); if (reslist == NULL) return NULL; n = mysql_num_fields(res); for (i = 0; i < n; i++) { tf = mysql_fetch_field_direct(res, i); if (tf == NULL) { if (res->handle && mysql_error(res->handle)[0] != 0) { PyErr_SetString(MySQLError,mysql_error(res->handle)); } else { PyErr_SetString(MySQLError, "pythonify_res_fields: mysql_fetch_field_direct() failed"); } Py_XDECREF(reslist); return NULL; } switch (tf->type) { case FIELD_TYPE_SHORT: type = "short"; break; case FIELD_TYPE_LONG: type = "long"; break; case FIELD_TYPE_CHAR: type = "char"; break; case FIELD_TYPE_DOUBLE: type = "double"; break; case FIELD_TYPE_DECIMAL: type = "decimal"; break; case FIELD_TYPE_FLOAT: type = "float"; break; case FIELD_TYPE_TINY_BLOB: type = "tiny blob"; break; case FIELD_TYPE_MEDIUM_BLOB: type = "medium blob"; break; case FIELD_TYPE_LONG_BLOB: type = "long blob"; break; case FIELD_TYPE_BLOB: type = "blob"; break; case FIELD_TYPE_DATE: type = "date"; break; case FIELD_TYPE_TIME: type = "time"; break; case FIELD_TYPE_DATETIME: type = "datetime"; break; case FIELD_TYPE_TIMESTAMP: type = "timestamp"; break; case FIELD_TYPE_NULL: case FIELD_TYPE_LONGLONG: case FIELD_TYPE_INT24: type = "unhandled"; break; case FIELD_TYPE_VAR_STRING: type = "varchar"; break; case FIELD_TYPE_STRING: type = "string"; break; default: type = "????"; break; } flags[0] = 0; if (IS_PRI_KEY(tf->flags)) (void) strcpy(flags, "pri"); if (IS_NOT_NULL(tf->flags)) flags[0] ? (void) strcat(flags, " notnull") : (void) strcpy(flags, "notnull"); if ((tf->flags) & AUTO_INCREMENT_FLAG) flags[0] ? (void) strcat(flags, " auto_inc") : (void) strcpy(flags, "auto_inc"); if ((tf->flags) & UNSIGNED_FLAG) flags[0] ? (void) strcat(flags, " usign") : (void) strcpy(flags, "usign"); if ((tf->flags) & UNIQUE_KEY_FLAG) flags[0] ? (void) strcat(flags, " ukey") : (void) strcpy(flags, "ukey"); if ((tf->flags) & MULTIPLE_KEY_FLAG) flags[0] ? (void) strcat(flags, " mkey") : (void) strcpy(flags, "mkey"); thislist = Py_BuildValue("[sssis]", tf->name, tf->table, type, tf->length, flags); if (thislist == NULL) { Py_XDECREF(reslist); return NULL; } if (PyList_Append(reslist, thislist) == -1) { Py_XDECREF(thislist); Py_XDECREF(reslist); return NULL; } Py_DECREF(thislist); } return (reslist); } /* concat three strings */ static void mystrcpy(field, table, sep, name) char *field; char *table; char *sep; char *name; { char *s; s = field; while (!(*table == 0)) *s++ = *table++; while (!(*sep == 0)) *s++ = *sep++; while (!(*name == 0)) *s++ = *name++; *s = 0; } /******************************************************** ***** MySQL methods ********************************************************/ static PyObject * MySQL_connect(self, args) PyObject *self, *args; { char *dbhost = NULL; char *dbuser = NULL; char *dbpass = NULL; DBHObject *DBH; if (!PyArg_ParseTuple(args, "|sss:connect", &dbhost, &dbuser, &dbpass)) return NULL; DBH = PyObject_NEW(DBHObject, &DBH_Type); if (DBH == NULL) return NULL; DBH->dbh_use_result = 0; DBH->handle = &DBH->connection; DBH->sth = NULL; if (!(mysql_connect(DBH->handle, dbhost, dbuser, dbpass))) { if (mysql_error(DBH->handle)[0] != 0) { PyErr_SetString(MySQLError,mysql_error(DBH->handle)); } else { /* safety. should not be reached */ PyErr_SetString(MySQLError, "connect(): could not connect to MySQL"); } Py_XDECREF(DBH); return NULL; } return ((PyObject *) DBH); } static PyObject * MySQL_escape(self,args) PyObject *self, *args; { char *in = NULL; char *out = NULL; PyObject *str; unsigned int size, len; if (!PyArg_ParseTuple(args, "s#:escape", &in, &size)) return NULL; /* wonder whether one should use PyMem_* routines instead of malloc/free */ out = (char *) malloc(size*2+1); if (!out) { PyErr_SetString(MySQLError, "escape(): no memory"); return NULL; } len = mysql_escape_string(out,in,size); str = Py_BuildValue("s#",out,len); free(out); return (str); } static struct PyMethodDef MySQL_Methods[] = { {"connect", MySQL_connect, METH_VARARGS}, {"escape", MySQL_escape, METH_VARARGS}, {NULL, NULL} }; /************************************************************ ****** DBH methods ************************************************************/ static PyObject * DBH_selectdb(self, args) DBHObject *self; PyObject *args; { char *dbname; if (!PyArg_ParseTuple(args, "s|i:selectdb", &dbname, &(self->dbh_use_result))) return NULL; if (self->sth && clear_channel(self->sth)) return NULL; Py_XDECREF(self->sth); self->sth = NULL; if (mysql_select_db(self->handle, dbname)) { PyErr_SetString(MySQLError, mysql_error(self->handle)); return NULL; } Py_INCREF(Py_None); return (Py_None); } static PyObject * DBH_listdbs(self, args) DBHObject *self; PyObject *args; { MYSQL_RES *res; PyObject *resobj; char *wildcard = NULL; if (!PyArg_ParseTuple(args, "|s:listdbs", &wildcard)) return NULL; if (self->sth && clear_channel(self->sth)) return NULL; Py_XDECREF(self->sth); self->sth = NULL; if ((res = mysql_list_dbs(self->handle, wildcard)) == NULL) { if (mysql_error(self->handle)[0] != 0) { PyErr_SetString(MySQLError, mysql_error(self->handle)); return NULL; } else { resobj = PyList_New(0); return (resobj); } } resobj = pythonify_res(res,-1); mysql_free_result(res); return (resobj); } static PyObject * DBH_listtables(self, args) DBHObject *self; PyObject *args; { MYSQL_RES *res; PyObject *resobj; char *wildcard = NULL; if (!PyArg_ParseTuple(args, "|s:listtables", &wildcard)) return NULL; if (self->sth && clear_channel(self->sth)) return NULL; Py_XDECREF(self->sth); self->sth = NULL; if ((res = mysql_list_tables(self->handle, wildcard)) == NULL) { if (mysql_error(self->handle)[0] != 0) { PyErr_SetString(MySQLError, mysql_error(self->handle)); return NULL; } else { resobj = PyList_New(0); return (resobj); } } resobj = pythonify_res(res,-1); mysql_free_result(res); return (resobj); } static PyObject * DBH_listfields(self, args) DBHObject *self; PyObject *args; { char *tname; char *wildcard = NULL; MYSQL_RES *res; PyObject *resobj; if (!PyArg_ParseTuple(args, "s|s:listfields", &tname, &wildcard)) return NULL; if (self->sth && clear_channel(self->sth)) return NULL; Py_XDECREF(self->sth); self->sth = NULL; if ((res = mysql_list_fields(self->handle, tname, wildcard)) == NULL) { if (mysql_error(self->handle)[0] != 0) { PyErr_SetString(MySQLError, mysql_error(self->handle)); return NULL; } else { resobj = PyList_New(0); return (resobj); } } resobj = pythonify_res_fields(res); mysql_free_result(res); return (resobj); } static PyObject * DBH_listprocesses(self, args) DBHObject *self; PyObject *args; { MYSQL_RES *res; PyObject *resobj; if (!PyArg_ParseTuple(args, ":listprocesses")) return NULL; if (self->sth && clear_channel(self->sth)) return NULL; Py_XDECREF(self->sth); self->sth = NULL; if ((res = mysql_list_processes(self->handle)) == NULL) { if (mysql_error(self->handle)[0] != 0) { PyErr_SetString(MySQLError, mysql_error(self->handle)); return NULL; } else { resobj = PyList_New(0); return (resobj); } } resobj = pythonify_res(res,-1); mysql_free_result(res); return (resobj); } static PyObject * DBH_query_helper(self, query, size) DBHObject *self; char *query; unsigned int size; { MYSQL_RES *res; PyObject *resobj; if (self->sth && clear_channel(self->sth)) return NULL; Py_XDECREF(self->sth); self->sth = NULL; if (mysql_real_query(self->handle, query, size)) { PyErr_SetString(MySQLError, mysql_error(self->handle)); return NULL; } if (self->dbh_use_result) { res = mysql_use_result(self->handle); } else { res = mysql_store_result(self->handle); } if (mysql_error(self->handle)[0] != 0) { PyErr_SetString(MySQLError,mysql_error(self->handle)); if (res) mysql_free_result(res); return NULL; } if (res == NULL) { /* query did not return a result, return affected_rows */ return (PyInt_FromLong((long) mysql_affected_rows(self->handle))); } resobj = pythonify_res(res,-1); mysql_free_result(res); return (resobj); } static PyObject * DBH_do(self, args) DBHObject *self; PyObject *args; { char *query; unsigned int size; if (!PyArg_ParseTuple(args, "s#:do", &query, &size)) return NULL; return DBH_query_helper(self, query, size); } static PyObject * DBH_create(self, args) DBHObject *self; PyObject *args; { char *dbname; if (!PyArg_ParseTuple(args, "s:create", &dbname)) return NULL; if (self->sth && clear_channel(self->sth)) return NULL; Py_XDECREF(self->sth); self->sth = NULL; if (mysql_create_db(self->handle, dbname)) { PyErr_SetString(MySQLError, mysql_error(self->handle)); return NULL; } Py_INCREF(Py_None); return (Py_None); } static PyObject * DBH_close(self, args) DBHObject *self; PyObject *args; { if (!PyArg_ParseTuple(args, ":close")) return NULL; if (self->sth && clear_channel(self->sth)) return NULL; Py_XDECREF(self->sth); self->sth = NULL; mysql_close(self->handle); /* unconditional */ Py_INCREF(Py_None); return (Py_None); } static PyObject * DBH_insertid(self, args) DBHObject *self; PyObject *args; { if (!PyArg_ParseTuple(args, ":insertid")) return NULL; if (self->sth && clear_channel(self->sth)) return NULL; Py_XDECREF(self->sth); self->sth = NULL; return (PyInt_FromLong((long) mysql_insert_id(self->handle))); } static PyObject * DBH_stat(self, args) DBHObject *self; PyObject *args; { char *stat; if (!PyArg_ParseTuple(args, ":stat")) return NULL; if (self->sth && clear_channel(self->sth)) return NULL; Py_XDECREF(self->sth); self->sth = NULL; if ((stat = mysql_stat(self->handle)) == NULL) { PyErr_SetString(MySQLError, mysql_error(self->handle)); return NULL; } return (PyString_FromString(stat)); } static PyObject * DBH_clientinfo(self, args) DBHObject *self; PyObject *args; { char *info; if (!PyArg_ParseTuple(args, ":clientinfo")) return NULL; /* MySQL returns the server version on this call :-( if (self->sth && clear_channel(self->sth)) return NULL; Py_XDECREF(self->sth); self->sth = NULL; if ((info = mysql_get_client_info()) == NULL) { PyErr_SetString(MySQLError, mysql_error(self->handle)); return NULL; } */ return (PyString_FromString(MySQL_Version)); } static PyObject * DBH_hostinfo(self, args) DBHObject *self; PyObject *args; { char *info; if (!PyArg_ParseTuple(args, ":hostinfo")) return NULL; if (self->sth && clear_channel(self->sth)) return NULL; Py_XDECREF(self->sth); self->sth = NULL; if ((info = mysql_get_host_info(self->handle)) == NULL) { PyErr_SetString(MySQLError, mysql_error(self->handle)); return NULL; } return (PyString_FromString(info)); } static PyObject * DBH_serverinfo(self, args) DBHObject *self; PyObject *args; { char *info; if (!PyArg_ParseTuple(args, ":serverinfo")) return NULL; if (self->sth && clear_channel(self->sth)) return NULL; Py_XDECREF(self->sth); self->sth = NULL; if ((info = mysql_get_server_info(self->handle)) == NULL) { PyErr_SetString(MySQLError, mysql_error(self->handle)); return NULL; } return (PyString_FromString(info)); } static PyObject * DBH_protoinfo(self, args) DBHObject *self; PyObject *args; { int info; if (!PyArg_ParseTuple(args, ":protoinfo")) return NULL; if (self->sth && clear_channel(self->sth)) return NULL; Py_XDECREF(self->sth); self->sth = NULL; if (!(info = mysql_get_proto_info(self->handle))) { PyErr_SetString(MySQLError, mysql_error(self->handle)); return NULL; } return (PyInt_FromLong((long) info)); } static PyObject * DBH_drop(self, args) DBHObject *self; PyObject *args; { char *dbname; if (!PyArg_ParseTuple(args, "s:drop", &dbname)) return NULL; if (self->sth && clear_channel(self->sth)) return NULL; Py_XDECREF(self->sth); self->sth = NULL; if (mysql_drop_db(self->handle, dbname)) { PyErr_SetString(MySQLError, mysql_error(self->handle)); return NULL; } Py_INCREF(Py_None); return (Py_None); } static PyObject * DBH_reload(self, args) DBHObject *self; PyObject *args; { if (!PyArg_ParseTuple(args, ":reload")) return NULL; if (self->sth && clear_channel(self->sth)) return NULL; Py_XDECREF(self->sth); self->sth = NULL; if (mysql_reload(self->handle)) { PyErr_SetString(MySQLError, mysql_error(self->handle)); return NULL; } Py_INCREF(Py_None); return (Py_None); } static PyObject * DBH_shutdown(self, args) DBHObject *self; PyObject *args; { if (!PyArg_ParseTuple(args, ":shutdown")) return NULL; if (self->sth && clear_channel(self->sth)) return NULL; Py_XDECREF(self->sth); self->sth = NULL; if (mysql_shutdown(self->handle)) { PyErr_SetString(MySQLError, mysql_error(self->handle)); return NULL; } Py_INCREF(Py_None); return (Py_None); } /* return a cursor object (STH) for the given query */ static PyObject * DBH_query(self, args) DBHObject *self; PyObject *args; { STHObject *sth; char *query; unsigned int size; sth = PyObject_NEW(STHObject, &STH_Type); if (sth == NULL) return NULL; sth->sth_use_result = self->dbh_use_result; sth->dbh = NULL; sth->res = NULL; if (!PyArg_ParseTuple(args, "s#|i:query", &query, &size, &(sth->sth_use_result))) { Py_XDECREF(sth); return NULL; } if (self->sth && clear_channel(self->sth)) { Py_XDECREF(sth); return NULL; } Py_XDECREF(self->sth); self->sth = NULL; if (mysql_real_query(self->handle, query, size)) { PyErr_SetString(MySQLError, mysql_error(self->handle)); Py_XDECREF(sth); return NULL; } if (sth->sth_use_result) { sth->res = mysql_use_result(self->handle); if (mysql_error(self->handle)[0] != 0) { PyErr_SetString(MySQLError,mysql_error(self->handle)); Py_XDECREF(sth); return NULL; } sth->dbh = self; Py_XINCREF(self); self->sth = sth; Py_XINCREF(sth); } else { sth->res = mysql_store_result(self->handle); if (mysql_error(self->handle)[0] != 0) { PyErr_SetString(MySQLError,mysql_error(self->handle)); Py_XDECREF(sth); return NULL; } } sth->affected_rows = mysql_affected_rows(self->handle); sth->insert_id = mysql_insert_id(self->handle); return (PyObject *) sth; } /* For DBH['query'] syntax */ static PyObject * DBH_subscript(self, subs) PyObject *self, *subs; { char *query; unsigned int size; if (!PyArg_Parse(subs, "s#:subscript", &query, &size)) { PyErr_SetString(MySQLError, "subscript expects a query string"); return NULL; } return DBH_query_helper(self, query, size); } static int DBH_subscript_assign(self, subs, val) PyObject *self, *subs, *val; { PyErr_SetString(MySQLError, "MySQL handle is readonly"); return -1; /* -1 is error code in interpreter main loop! (ceval.c) */ } static int DBH_len(self, subs) PyObject *self, *subs; { PyErr_SetString(MySQLError, "len() of unsized object"); return -1; } static struct PyMethodDef DBH_methods[] = { {"selectdb", (PyCFunction) DBH_selectdb, METH_VARARGS}, {"do", (PyCFunction) DBH_do, METH_VARARGS}, {"query", (PyCFunction) DBH_query, METH_VARARGS}, {"listdbs", (PyCFunction) DBH_listdbs, METH_VARARGS}, {"listtables", (PyCFunction) DBH_listtables, METH_VARARGS}, {"listfields", (PyCFunction) DBH_listfields, METH_VARARGS}, {"listprocesses", (PyCFunction) DBH_listprocesses, METH_VARARGS}, {"create", (PyCFunction) DBH_create, METH_VARARGS}, {"stat", (PyCFunction) DBH_stat, METH_VARARGS}, {"clientinfo", (PyCFunction) DBH_clientinfo, METH_VARARGS}, {"hostinfo", (PyCFunction) DBH_hostinfo, METH_VARARGS}, {"serverinfo", (PyCFunction) DBH_serverinfo, METH_VARARGS}, {"protoinfo", (PyCFunction) DBH_protoinfo, METH_VARARGS}, {"drop", (PyCFunction) DBH_drop, METH_VARARGS}, {"reload", (PyCFunction) DBH_reload, METH_VARARGS}, {"insert_id", (PyCFunction) DBH_insertid, METH_VARARGS}, {"close", (PyCFunction) DBH_close, METH_VARARGS}, {"shutdown", (PyCFunction) DBH_shutdown, METH_VARARGS}, {NULL, NULL} /* sentinel */ }; static PyMappingMethods mysql_as_mapping = { (inquiry) DBH_len, /*length */ (binaryfunc) DBH_subscript, /*subscript */ (objobjargproc) DBH_subscript_assign, /*assign subscript */ }; static PyObject * DBH_getattr(self, name) DBHObject *self; char *name; { return Py_FindMethod(DBH_methods, (PyObject *) self, name); } static void DBH_dealloc(self) register DBHObject *self; { mysql_close(self->handle); PyMem_DEL(self); } static PyTypeObject DBH_Type = { PyObject_HEAD_INIT(NULL) 0, "DBHObject", sizeof(DBHObject), 0, (destructor) DBH_dealloc, /*tp_dealloc */ 0, /*tp_print */ (getattrfunc) DBH_getattr, /*tp_getattr */ 0, /*tp_setattr */ 0, /*tp_compare */ 0, /*tp_repr */ 0, /*tp_as_number */ 0, /*tp_as_sequence */ &mysql_as_mapping, /*tp_as_mapping */ }; /************************************************************* **** STH methods **************************************************************/ static PyObject * STH_fields(self, args) STHObject *self; PyObject *args; { PyObject *resobj; if ((!PyArg_ParseTuple(args, ":fields")) || no_response(self)) return NULL; resobj = pythonify_res_fields(self->res); if (resobj == NULL) { mysql_free_result(self->res); self->res = NULL; } return (resobj); } static PyObject * STH_fetchrows(self, args) STHObject *self; PyObject *args; { PyObject *resobj; int i = -1; if ((!PyArg_ParseTuple(args, "|i:fetchrows", &i)) || no_response(self)) return NULL; if (i < 0 && self->sth_use_result == 0) mysql_data_seek(self->res,0); resobj = pythonify_res(self->res, i); if (resobj == NULL) { mysql_free_result(self->res); self->res = NULL; } return (resobj); } static PyObject * STH_fetchdict(self,args) STHObject *self; PyObject *args; { int i = -1; int tlen = 0; int j, flen, rows, cols; char *fieldname = NULL; PyObject *rowdict = NULL; PyObject *datalist, *rowlist, *value; MYSQL_FIELD *tf; if ((!PyArg_ParseTuple(args, "|i:fetchdict", &i)) || no_response(self)) return NULL; if (i < 0 && self->sth_use_result == 0) mysql_data_seek(self->res,0); datalist = pythonify_res(self->res,i); if (datalist == NULL) { mysql_free_result(self->res); self->res = NULL; return NULL; } rows = PyList_Size(datalist); if (rows > 0) { cols = mysql_num_fields(self->res); for (j=0; jres,j); if (tf == NULL) { if (self->res->handle && mysql_error(self->res->handle)[0] != 0) { PyErr_SetString(MySQLError,mysql_error(self->res->handle)); } else { PyErr_SetString(MySQLError, "fetchdict: mysql_fetch_field_direct() failed"); } goto error; } flen = strlen(tf->table) + strlen(tf->name); tlen = flen > tlen ? flen : tlen; } if ((fieldname = (char *) malloc(tlen+2)) == NULL) { PyErr_SetString(MySQLError,"fetchdict(): no memory (fieldname)"); goto error; } for (i=0; ires,j); if (tf == NULL) { if (self->res->handle && mysql_error(self->res->handle)[0] != 0) { PyErr_SetString(MySQLError,mysql_error(self->res->handle)); } else { PyErr_SetString(MySQLError,"fetchdict(): mysql_fetch_field_direct() failed"); } goto error; } mystrcpy(fieldname,tf->table,".",tf->name); value = PyList_GetItem(rowlist,j); if (value == NULL) goto error; if (PyDict_SetItemString(rowdict,fieldname,value)) goto error; } if (PyList_SetItem(datalist,i,rowdict)) goto error; } free(fieldname); } return (datalist); error: Py_XDECREF(datalist); Py_XDECREF(rowdict); if (fieldname) free(fieldname); return NULL; } static PyObject * STH_seek(self, args) STHObject *self; PyObject *args; { unsigned int i; if ((!PyArg_ParseTuple(args, "i:seek", &i)) || no_response(self)) return NULL; if (self->sth_use_result) { PyErr_SetString(MySQLError, "STH_seek: cannot seek on server"); return NULL; } mysql_data_seek(self->res, i); Py_INCREF(Py_None); return Py_None; } static PyObject * STH_numrows(self, args) STHObject *self; PyObject *args; { if ((!PyArg_ParseTuple(args, ":numrows")) || no_response(self)) return NULL; return PyInt_FromLong((long) mysql_num_rows(self->res)); } static PyObject * STH_numfields(self, args) STHObject *self; PyObject *args; { if ((!PyArg_ParseTuple(args, ":numfields")) || no_response(self)) return NULL; return PyInt_FromLong((long) mysql_num_fields(self->res)); } static PyObject * STH_eof(self, args) STHObject *self; PyObject *args; { if ((!PyArg_ParseTuple(args, ":eof")) || no_response(self)) return NULL; if (mysql_eof(self->res)) { /* only useful if mysql_use_result() has been used */ Py_INCREF(Py_True); return Py_True; } else { Py_INCREF(Py_False); return Py_False; } } static PyObject * STH_affectedrows(self, args) STHObject *self; PyObject *args; { if (!PyArg_ParseTuple(args, ":affectedrows")) return NULL; return PyInt_FromLong((long) self->affected_rows); } static PyObject * STH_insertid(self, args) STHObject *self; PyObject *args; { if (!PyArg_ParseTuple(args, ":insertid")) return NULL; return PyInt_FromLong((long) self->insert_id); } static struct PyMethodDef STH_methods[] = { {"fields", (PyCFunction) STH_fields, METH_VARARGS}, {"fetchrows", (PyCFunction) STH_fetchrows, METH_VARARGS}, {"fetchdict", (PyCFunction) STH_fetchdict, METH_VARARGS}, {"seek", (PyCFunction) STH_seek, METH_VARARGS}, {"numrows", (PyCFunction) STH_numrows, METH_VARARGS}, {"numfields", (PyCFunction) STH_numfields, METH_VARARGS}, {"eof", (PyCFunction) STH_eof, METH_VARARGS}, {"affectedrows", (PyCFunction) STH_affectedrows, METH_VARARGS}, {"insert_id", (PyCFunction) STH_insertid, METH_VARARGS}, {NULL, NULL} }; static PyObject * STH_getattr(self, name) STHObject *self; char *name; { return Py_FindMethod(STH_methods, (PyObject *) self, name); } static void STH_dealloc(self) register STHObject *self; { if (self->res) mysql_free_result(self->res); Py_XDECREF(self->dbh); PyMem_DEL(self); } static PyTypeObject STH_Type = { PyObject_HEAD_INIT(NULL) 0, "STHObject", sizeof(STHObject), 0, (destructor) STH_dealloc, /*tp_dealloc */ 0, /*tp_print */ (getattrfunc) STH_getattr, /*tp_getattr */ 0, /*tp_setattr */ 0, /*tp_compare */ 0, /*tp_repr */ 0, /*tp_as_number */ 0, /*tp_as_sequence */ 0, /*tp_as_mapping */ }; /*********************************************************** ******** Module initialization ***********************************************************/ void initMySQL() { PyObject *module, *dict, *str; DBH_Type.ob_type = &PyType_Type; STH_Type.ob_type = &PyType_Type; module = Py_InitModule("MySQL", MySQL_Methods); dict = PyModule_GetDict(module); if (PyDict_SetItemString(dict, "DBH_Type", (PyObject *) & DBH_Type) != 0) Py_FatalError("Cannot add to MySQL dictionary"); if (PyDict_SetItemString(dict, "STH_Type", (PyObject *) & STH_Type) != 0) Py_FatalError("Cannot add to MySQL dictionary"); str = PyString_FromString(MySQL_Version); if (PyDict_SetItemString(dict, "__doc__", str) != 0) Py_FatalError("Cannot add to MySQL dictionary"); Py_XDECREF(str); MySQLError = PyErr_NewException("MySQL.error",NULL,NULL); if (PyDict_SetItemString(dict, "error", MySQLError) != 0) Py_FatalError("Cannot add to MySQL dictionary"); }