Filewatcher File Search
FTP Search
  
Directory 
  
Content Search 
   
pkg://MySQL-python-1.4-2.src.rpm:23421/MySQLmodule-1.4.tar.gz  info  downloads

./MySQLmodule-1.4/ 40775    765    765           0  6605365776  11544 5ustar  wwwwww./MySQLmodule-1.4/Credits100644    765    765       20076  6603125152  13160 0ustar  wwwwwwOriginal copyright notices from the top of the mySQLmodule-0.1.4
----------------------------------------------------------------

/*
   An interface to the mySQL database system for Python

   Copyright (C) 1997  Joseph Skinner <joe@earthlink.co.nz>

    Query cursor code and some modifications
   Copyright (C) 1997  James Henstridge <james@daa.com.au>

    Based on mSQLmodule which was by the following people:
   Portions copyright (C) 1995  Thawte Consulting, cc
   (Those portions covered by the next copyright notice)
   Portions copyright (C) 1994  Anthony Baxter.

   *******************************************************************

   Ported to mySQL Skinner (joe@earthlight.co.nz) Janurary 1997

   -- STATUS : BETA

   --VERSION 0.1.0
   - converted source
   - added support for some of the types not included in mSQL
   - added support for return of auto_increment values
   an auto_increment value is returned as a result from an insert

   VERSION 0.1.2 (1997-04-02)
   - added support for varchar
   - added support for username and passwords

   VERSION 0.1.4 (1997-09-09)
   - added decimal and float types
   - added a query cursor.  (Mainly to support mysqldb - the Python
     DB API module for MySQL)  See New file.
   - changed behavior so that currently unhandled types are returned
     as strings, so that the python programmer can figure out what to
     do with them.

   -- TODO
   - support timestamps
   - support all unsupported mysql types

 */
/*   This mSQLmodule copyright (it only applies to those sections):

 ******************************************************
 *
 *   Based on a prior work by Anthony Baxter
 *   Updated, fixed and extended by David Gibson working for
 *   Thawte Consulting cc, South Africa.
 *
 *   Copyright 1995 Thawte Consulting cc
 *   Portions copyright (C) 1994 Anthony Baxter.
 *
 *   Permission is hereby granted, free of charge, to any person obtaining
 *   a copy of this source file to use, copy, modify, merge, or publish it
 *   subject to the following conditions:
 *
 *   The above copyright notice and this permission notice shall be included
 *   in all copies or in any new file that contains a substantial portion of
 *   this file.
 *
 *   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.
 *
 ******************************************************

 mSQLmodule ChangeLog:- (slightly mangled by msql2mysql)
 Modified by David Gibson December 1995

 - listdbs and listtables now return a list of strings
 - new Python naming conventions introduced
 - queries now return data types in native Python format (String,Float,Int)
 - solved spurious 'function requires at least one argument' error: old
 getargs would not handle optional arguments.  ParseTuple is being used now.
 (so method table has got 1's in now)
 (old Parse routine still needed for subscript handling)
 - mysql_free_result now called after query!
 - assignment to subscript trapped correctly.  Ditto len()
 - added DbType to the module dictionary
 - mySQL.error object introduced
 ******************************************************
 */


----- Original New file from mySQLmodule ---------------------------

Release 0.1.4 1997-09-09

Added Support for the following types
	float -- from  Mitch Chapman <mchapman@OhioEE.com>
	decimal -- me (I had to do something in this release :-) )

Added a connect patch from  Giles Francis Hall <ghall@interaccess.com>

  
	I just thought you might want to have this small piece of code which 
	traps an connect() error.  I was wondering what was happening with 
	my programs when they the SQL server was down yet things seemed to 
	go happily along their way returning empty data sets ;) ...
	  
	*****
	  
	if(!(mysql_connect(&newhandle, dbname, dbuser, dbpass))) {
	  PyErr_SetString(mySQLError, "connect(): could not connect to MySQL");
	  return NULL;
	}
  
Added Support for the python dbi standard from 
James Henstridge <james@daa.com.au>

These modifications by James Henstridge <james@daa.com.au>:
This release adds an extra object called a querycursor.

The querycursor object is used to look at a large result from a query.
It doesn't convert the complete result to a list, only converting the
sections asked for.  They are created as a result from the querycursor
function.
  ie  curs = connection.querycursor('select * from table')

These objects have the following methods:
  fields()       -- Similar output to connection.listfields().
  insert_id()    -- Returns the insert ID of the query.
  fetchall()     -- Fetch a list of all the remaining records.
  fetchone()     -- Fetch the next record.
  fetchmany(num) -- Fetch a list of at most num records.
  seek(pos)      -- set the cursor position in the result.
  eof()          -- returns true if there are no more records.
  numrows()      -- returns the number of rows in the result.
  numfields()    -- returns the number of fields in the result.
  affectedrows() -- returns the number of affected rows.  -1 for select
                    queries

If the query doesn't return a result, all but the last method will raise
an exception.

As a change to the behavior of the old functions, instead of not returning
`unhandled' field values, they are returned as strings, in the hope that
the python programmer will be able to find some sensible way of using them.

Also included in this distribution is mysqldb.py, which is a Python DB API
compliant interface to MySQL databases, written as a frontend to mySQLmodule.
Mysqldb should be used for new programs, since it makes them more portable
across different databases.  More information on this specification can be
found at http://www.python.org/sigs/db-sig/DatabaseAPI.html

An example module mysqlshelve is included which uses mysqldb to implement an
object similar to Shelf, which stores the information in a MySQL table.


Release 0.1.3 1997-04-23

This code has been tested on a linux box using linux 2.1.55 libc6 
and mysql 3.20.29

Bug Fixes
	Fixes for my silly typos in timedate handling provided by  
	Karsten Thygesen <karthy@kom.auc.dk>

New commands 

	It includes the following new functionality.

	create(db_name)         create a new database
	drop(db_name)           drop a database
	reload()                reload the mysql tables
	shutdown()              shutdown mysql


Release 0.1.2 1997-04-03

New In this Release
	
Support for new mysql types
	varchar
	blobs
	time 
	date 
	timedate
	timestamp

Support for Username and Passwords

	authentification is handled as part of the connect option

	ie 

		import mySQL

		db = mySQL.connect(<OPTIONS>)

	where options can be either
	   nothing                       -- connect()
	   hostname                      -- connect('localhost')
	   hostname, username            -- connect('localhost', 'fred')
	   hostname, username, password  -- connect('localhost, 'fred','green')

IMPORTANT
	The support for date and time will not work with earlier versions of
	mysql which did not support these functions.  Therefore these may be 
	disabled by changing the #define 
		#define INCLUDE_DATE_TIME_SUPPORT 1
	to
		#undef INCLUDE_DATE_TIME_SUPPORT


	at the top the file.



----- Original Credits file from mySQLmodule ------------------------

All those people who have put word into the mySQL module

Monty 	-- lots of code and suggestions
James Henstridge <james@daa.com.au>) -- the new Python DB extensions
Mitch Chapman <mchapman@OhioEE.com> -- float support
Giles Francis Hall <ghall@interaccess.com> -- fix for connect bug
The orginal authors of the mSQL module
./MySQLmodule-1.4/MySQLmodule.c100664    765    765      114613  6605365352  14214 0ustar  wwwwww/*
   A Python interface to the MySQL database.

      Python: http://www.python.org
      MySQL:  http://www.tcx.se

   Copyright (C) 1998 Joerg Senekowitsch <senekow@ibm.net>

   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 <senekow@ibm.net>


 Notice: This code is based on mySQLmodule-0.1.4, which is
 
         Copyright (C) 1997  Joseph Skinner <joe@earthlink.co.nz>
	 Copyright (C) 1997  James Henstridge <james@daa.com.au>

	 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 int *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; j<cols; j++) {
 	    tf = &(mysql_fetch_field_direct(self->res,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; i<rows; i++) {
	    rowdict = PyDict_New();
	    if (rowdict == NULL) goto error;
	    rowlist = PyList_GetItem(datalist,i);
	    if (rowlist == NULL) goto error;
	    for (j=0; j<cols; j++) {
	        tf = &(mysql_fetch_field_direct(self->res,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");
}
./MySQLmodule-1.4/README100664    765    765       37400  6605362313  12525 0ustar  wwwwwwThe following is the documentation for MySQLmodule-1.4.

This module has been developed under Linux (RH50),
MySQL 3.21.30, and Python 1.5.1.

For copyright notices see MySQLmodule.c.

MySQLmodule-1.x is based on mySQLmodule-0.1.4 by

   Copyright (C) 1997  Joseph Skinner <joe@earthlink.co.nz>
   Copyright (C) 1997  James Henstridge <james@daa.com.au>

   mySQLmodule-0.1.4 is based on mSQLmodule, which is

   Portions copyright (C) 1995  Thawte Consulting, cc
   Portions copyright (C) 1994  Anthony Baxter.

See 'Credits' for details.

Joerg Senekowitsch (senekow@ibm.net), October 1998

-----------------------------------------------------------------------------
**CONTENTS**

 0. Why another Python/MySQL interface
 1. Compiling and installing the MySQL module
 2. Exported types, functions and classes
 3. Using the MySQL module
 4. Notes on server side storage
 5. Acknowledgements


**WHY ANOTHER PYTHON/MYSQL INTERFACE**

I recently came across a problem storing strings containing ASCII zero (\0) 
in the MySQL database. After first blaming Python (which threw the original 
error), I checked the mySQLmodule code and ran across a bunch of problems.
Some routines would not free allocated storage, some MySQL API functions 
were not available, and I disliked the fact that mySQLmodule would return 
different data structures depending on the method (and tuples at that!). 
So I ripped the code apart, left the framework, but made lots of changes. 
Because the changes include a change in (Python) methods and return 
values, I upped the major version number and changed the name of the 
module to MySQL, so that it would not clash with the original mySQL.

Rational for the change to return "list of lists" instead of
"tuple" or "list of tuple". I don't want my own DB access routines 
to worry about the return type and I want to be able to modify the
data in the returned table. 

Data = DBH['select * from MyTable']
if Data:
    rows = len(Data)
    cols = len(Data[0])
    for i in range(rows):
        for j in range(cols):
            if not Data[i][j]: Data[i][j] = DefaultElement()
    Do_Something(Data)

No need to complicate that by having tuples inside the outer list.
For those who prefer dictionaries, the STH method fetchdict()
will return a list of dictionaries. The dictionary keys are
qualified with the corresponding table name(s).


**COMPILING AND INSTALLING**

  i. See README.NT if you are installing on a WIN32 system.

 ii. Copy MySQLmodule.c into your Modules subdirectory of the Python
     source distribution.

iii. Add the following line to your Setup file in that directory:

     MySQL MySQLmodule.c -L/usr/local/lib/mysql/ -lmysqlclient \
	-I/usr/local/include/mysql

     Note that the location of the MySQL library and the include
     directory may be different on your particular system. You may
     build the module shared (insert below the *shared* indicator 
     in the Setup file).

 iv. If you have built Python before, simply run make in your 
     main Python directory. If not, follow the instructions on
     how to compile/install Python.

To build a dynamically loadable module without access to the python
source tree, use (Trond Eivind Glomsrød):

     gcc -shared -I/usr/include/python1.5 -I/usr/local/include/mysql \
     MySQLmodule.c -lmysqlclient -L/usr/lib/python1.5/config -lpython1.5 \
     -o MySQLmodule.so

and move the resulting MySQLmodule.so file into the PYTHONPATH. Again,
substitute the proper locations of your include and library files.

Note that the module is case sensitive, and that the name has been changed
to MySQL *deliberately* so as not to break any existing code that uses
the old mySQL module.


**EXPORTED TYPES AND OBJECTS**

The module (MySQL) exports the following:

  DBH_Type:    the type of the database object
  STH_Type:    the type of the cursor object
  error:       an exception raised in certain circumstances (rather than TypeError)
  __doc__:     the version accessible from Python
  connect([host[,user[,pass]]]) 
      a function returning a database object.  The optional arguments
      are the name of the host to connect to, the username for authenticating
      to MySQL, and the associated password. If no host is given, the function
      will assume 'localhost' (and use a fast Unix socket for the connection)
  escape(string)
      will (hopefully) return a string properly escaped to allow insertion
      into the DB. This routine calls mysql_escape_string(), which is broken
      in 3.21.29-gamma. Versions >=3.21.30 seem to work correctly.

MySQL.connect() returns a database handle (DBH) with the following methods:

(Note: in the following, 'Table' means 'list of lists' (except fetchdict))

  Table = DBH.listdbs([wild])
      returns a table giving the names of the databases on the
      MySQL host to which one has connected with MySQL.connect(). The optional
      argument is a MySQL wildcard string (same syntax as LIKE).

  DBH.selectdb(DB_Name[,storage])
      attaches this object to a particular database.  Queries executed
      will be directed to that database until another selectdb method
      call is made. The optional integer 'storage' can be used to keep
      query result sets on the server. Note that this negatively impacts
      the server performance, but allows clients with smaller memory 
      footprints, since records are only transferred upon request. 
      The default is 0, i.e. all records are transferred to the client.

  Table = DBH.listtables([wild])
      return a table with table names in the selected
      database.  Only valid after a selectdb call has been made.
      The optional argument can be used to restrict the returned set of
      tables (same syntax as LIKE).

  Table = DBH.listfields(table[,wild])
      return a table of the description(s) of the fields in the given table.
      The optional argument can be used to restrict the returned set
      of fields (same syntax as LIKE).

  Table = DBH.listprocesses()
      returns information about the running MySQL processes. Requires
      privileges (otherwise returns None).

  String = DBH.stat()
      returns status information from MySQL.

  DBH.create(DB_Definition)
      creates a new database.

  DBH.drop(DB_Name)
      could ruin your day.

  DBH.reload()
      reload MySQL privilege tables.

  DBH.shutdown()
      takes down the MySQL daemon.

  DBH.close()
      closes a DB connection.

  String = DBH.clientinfo()
      returns MySQLmodule version information.

  String = DBH.serverinfo()
      returns MySQL server information.

  String = DBH.hostinfo()
      returns information about the connecting host and connection type.

  Integer = DBH.protoinfo()
      returns the MySQL protocol version number (10).

  Table = DBH.do(query) or Table = DBH[query]
      return the result of the SQL query. Returns the result of the
      query or the number of affected rows (mySQL may lie about that).
      Both methods use the storage type established with DBH.selectdb().
      See code comments if you're running a WIN32 version.

  Integer = DBH.insert_id()
      access to the last generated auto_increment number. This number 
      can change if queries have been submitted between calls.
      See code comments if you're running a WIN32 version.

  STH = DBH.query(query[,storage])
      returns a statement handle for cursor methods (see below).
      The optional 'storage' parameter can be used to override
      the DBH default established with DBH.selectdb().

Methods for statement handles (STH):

  Table = STH.fetchrows([n])
      return the results of the DB query. If n < 0, all rows will be fetched.
      Otherwise, only the next n rows will be returned. The default is to
      return all rows.

  Table = STH.fetchdict([n])
      same as STH.fetchrows(), except that a list of dictionaries is returned
      with 'tablename.fieldname:data' pairs. 

  Table = STH.fields()
      return field descriptions of the result of the STH query. Currently
      MySQLmodule knows about "pri", "notnull", "auto_inc", "ukey", and "mkey".

  STH.seek(n)
      move cursor to row n (0 is the first row).
      Only available if client side result storage (=0, see DBH.selectdb)
      has been selected. Otherwise, will throw an exception.

  Integer = STH.numrows()
      returns how many rows are in the result of the STH query.
      Warning: in reality this number reflects how many records the
      *client* has received. For server side storage methods this
      number starts out at 0 and increases as the client fetches the
      rows. For client side storage, this number immediately gives
      the total number of rows for this query.

  Integer = STH.numfields()
      returns how many columns are in the result of the STH query.

  Integer = STH.affectedrows()
      returns how many rows have been affected by the last query.
      Note that MySQL lies about this number in certain cases.
      See code comments if you're running a WIN32 version.

  Integer = STH.insert_id()
      return the auto_increment value from an insert STH query.
      Note that this number is persistent as long as the STH exists.
      See code comments if you're running a WIN32 version.

  Integer = STH.eof()
      returns 1 if the last row has been read, otherwise 0.
      Always 1 if client side storage has been selected (default),
      and only marginally useful for server side storage, since
      the flag will change to true only _after_ an attempt has
      been made to read past the last record.


**USING THE MySQL MODULE**

import MySQL
DBH = MySQL.connect() # localhost
print DBH.listdbs()
DBH.selectdb('test')
print DBH.serverinfo()
print DBH.stat()
DBH["create table pytest (x int, y int, s char(20))"]
DBH["insert into pytest values (1,2,'abc')"]
DBH.do("insert into pytest values (3,4,'def')")
STH = DBH.query("insert into pytest values (5,6,'ghi')")
print STH.affectedrows()
print DBH['select * from pytest']
STH = DBH.query("select * from pytest")
print STH.numrows()
print STH.fields()
print STH.fetchrows(-1)
STH.seek(0)
print STH.fetchrows(1)
print STH.fetchrows(1)
STH.seek(0)
print STH.fetchrows(2)
print STH.fetchrows(2)
print STH.numfields()
STH.seek(0)
print STH.fetchdict(1)
print STH.fetchdict()
STH = DBH.query("select * from pytest",1)
print STH.fetchdict(1)
print STH.fetchdict() # compare to previous dicts
STH = DBH.query("select * from pytest",1)
print STH.fetchrows(1)
print STH.eof()
print STH.fetchrows()
print STH.eof()
DBH['drop table pytest']


**NOTES ON SERVER SIDE STORAGE**

MySQL offers two slightly different ways of accessing database data.

The default method in MySQLmodule is to use client side storage, i.e.
all queries, including the cursor (STH) methods fetch all data from the
server. Rows are accessed through STH.fetchrows(n) or STH.fetchdict(n)
individually (n=1), chunked (n>1), all at once (n<0), or, for the
wiseguys, none at all (n=0). STH.numrows() can tell *up front*, i.e. 
right after the query has been made, how many rows are in the result. 
STH.seek(k) can be used to access rows randomly. The drawback of client 
side storage is that it uses (client) memory to hold all the rows. 
Client side storage allows for constructs such as:

	STH = DBH.query("select * from Foo")
	N = STH.numrows()
	if N > 1000: raise Hell,"You must be joking!"
	for i in xrange(N):
		[Data] = STH.fetchdict(1)

Since the client has effectively transferred all rows, as far as the 
server is concerned all transactions on this channel have ceased
and the server is ready to accept new commands. It also means that
STH.eof() is always true (1) for client side storage.

Server side storage does not require that much client memory since
all records are transferred on a request basis. However, server side
storage has several drawbacks. Since now the possibility arises that
a client did not retrieve all rows, each new command must check whether
the server is ready to accept a new command. If not, the command must
clear the command channel by issuing enough reads to retrieve the
remaining rows. The (3.21) MySQL API does not offer some kind of "abort()" 
command. STH.numrows() no longer knows about how many rows were
selected by the query, so the above example code would fail.
STH.numrows() will, however, be updated as rows are read, e.g.:

	STH = DBH.query("select * from Foo")
	Data = STH.fetchrows(-1)
	print "Got",STH.numrows(),"rows." # len(Data) is the same

STH.eof() only makes sense with server side storage, but even here
it is not all that useful:

	STH = DBH.query("select 1")
	print STH.eof()          # will print 0
	Data = STH.fetchrows(1)  # retrieve the row
	print STH.eof()          # still 0 :-(
	Data = STH.fetchrows(1)  # must repeat. Data will be []
	print STH.eof()          # now we get 1, but we already 
                                 # knew that we've hit the end

One might consider this a bug. STH.seek(k) is no longer available and
will throw an error ("cannot seek on server"), i.e. rows must now be
read sequentially.

Server side storage also puts more strain on the server. In particular,
the server needs to remain in contact with the client until all rows
have been read. According to the MySQL manual clients are advised to
speedily retrieve the rows and not do any lengthy processing or, worse,
allow the user to stop the retrieval (e.g. by pressing Ctrl-S in an 
interactive interface).

For those who cannot decide which method is more suitable for their
application, MySQLmodule allows to mix both methods freely. The default
behavior can be set with DBH.selectdb() and can be changed for individual
cursor (STH) based queries. Note that incomplete server side queries 
will be cancelled by newly issued commands:

	STH = DBH.query("select * from Foo",1) # use server side storage
        Tables = DBH.listtables()              # stomp on previous results
        Data = STH.fetchrows()                 # nothing here anymore
   vs.
	STH = DBH.query("select * from Foo",0) # use client side storage
        Tables = DBH.listtables()              # won't interfere
        Data = STH.fetchrows()                 # no problem...

Server side storage also makes for brain warping code in MySQLmodule.
Normally (with client side storage) STH cursors are independent of
the database handle. Immediately after the query all data is transferred 
and the user is free to do whatever she likes with the DBH or STH handles.
With server side storage this becomes tricky. The memory chunk need for
the database connection is provided by the DBH handle. With server side
cursors, a pointer to this handle is stored in the STH handle. MySQLmodule
makes sure that this memory chunk is not deallocated before all outstanding
(server side) cursors are closed. This means that mysql_close() is not
necessarily called if the database handle DBH is destroyed:

	DBH = MySQL.connect()         # get a DB handle
	STH = DBH.query("select 1",1) # server side cursor
	del DBH                       # mysql_close() *not* called
	STH.fetchrows()               # will succeed!
        del STH                       # now mysql_close() will be called
	                              # in DBH_dealloc()

If you need to close the DB handle immediately, use DBH-close(). All further
attempts to communicate on this handle (even outstanding server side cursors)
will then receive a "... server has gone away" exception. Since mySQL 
cannot accept commands out of sequence, all DBH methods must check
for incomplete STH cursors. To access those the DBH handle contains a 
pointer to the STH cursor...sigh.


**ACKNOWLEDGEMENTS**

Thanks to the guys who developed Python (Guido et al.).
Python is a wonderful language.

Thanks to Monty Widenius and TcX Datakonsult AB for MySQL, which beat out 
mSQL, Oracle, Solid, and Postgres for my application.

Thanks to the individuals who contributed to the development
of the Python/MySQL interface:

   Joseph Skinner
   James Henstridge
   Thawte Consulting
   Anthony Baxter

Please see the file 'Credits' for the original mySQLmodule
copyright statements.
./MySQLmodule-1.4/README.NT100664    765    765        2005  6605365537  13031 0ustar  wwwwwwInstalling MySQLmodule-1.4 on WindowsNT.
----------------------------------------

The following instructions are based on information provided by 
Nigel Head (nhead@houbits.com).

  i. You'll need to get hold of libMySQL.dll from the win32-mysql 
     clients package at version 3.22.8 or higher; earlier versions 
     won't cut it. This is available at www.tcx.se (or its mirrors) 
     for free download. You should probably move the file into the 
     same location as your python15.dll.

 ii. Compile MySQLmodule-1.4 with the switch WIN32 enabled. 
     VC5 workspace and project files MySQL.dsp and MySQL.dsw are 
     included (but may have to be modified to suit your environment).

iii. Move the resulting MySQL.pyd file into your Python DLL directory.


Note: I don't use NT, nor do I have (or want) access to one of 
      these beasts. If this blows up on you, you're totally on 
      your own. You best chances for help are probably to send 
      a message to comp.lang.python.

Joerg Senekowitsch, October 1998
./MySQLmodule-1.4/MySQL.dsp100664    765    765        7716  6605363563  13321 0ustar  wwwwww# Microsoft Developer Studio Project File - Name="MySQL" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 5.00
# ** DO NOT EDIT **

# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102

CFG=MySQL - Win32 Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE 
!MESSAGE NMAKE /f "MySQL.mak".
!MESSAGE 
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE 
!MESSAGE NMAKE /f "MySQL.mak" CFG="MySQL - Win32 Debug"
!MESSAGE 
!MESSAGE Possible choices for configuration are:
!MESSAGE 
!MESSAGE "MySQL - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
!MESSAGE "MySQL - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
!MESSAGE 

# Begin Project
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""
CPP=cl.exe
MTL=midl.exe
RSC=rc.exe

!IF  "$(CFG)" == "MySQL - Win32 Release"

# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Release"
# PROP BASE Intermediate_Dir "Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Release"
# PROP Intermediate_Dir "Release"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c
# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\Include" /I "..\..\PC" /I "d:\mysql\include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "__WIN32__" /D "WIN32" /YX /FD /c
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o NUL /win32
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o NUL /win32
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
# ADD LINK32 ..\Release\python15.lib d:\mysql\lib\opt\libmysql.lib wsock32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 /out:"Release/MySQL.pyd"

!ELSEIF  "$(CFG)" == "MySQL - Win32 Debug"

# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "Debug"
# PROP BASE Intermediate_Dir "Debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "Debug"
# PROP Intermediate_Dir "Debug"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c
# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I "..\..\Include" /I "..\..\PC" /I "d:\mysql\include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "__WIN32__" /D "WIN32" /YX /FD /c
# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o NUL /win32
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o NUL /win32
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept
# ADD LINK32 ..\Debug\python15.lib d:\mysql\lib\debug\libmysql.lib wsock32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /out:"Debug/MySQL.pyd" /pdbtype:sept /export:initMySQL
# SUBTRACT LINK32 /pdb:none

!ENDIF 

# Begin Target

# Name "MySQL - Win32 Release"
# Name "MySQL - Win32 Debug"
# Begin Source File

SOURCE=.\MySQLmodule.c
# End Source File
# End Target
# End Project
./MySQLmodule-1.4/MySQL.dsw100664    765    765        1027  6605363563  13315 0ustar  wwwwwwMicrosoft Developer Studio Workspace File, Format Version 5.00
# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!

###############################################################################

Project: "MySQL"=".\MySQL.dsp" - Package Owner=<4>

Package=<5>
{{{
}}}

Package=<4>
{{{
}}}

###############################################################################

Global:

Package=<5>
{{{
}}}

Package=<3>
{{{
}}}

###############################################################################

Results 1 - 1
Help - FTP Sites List - Software Dir.
Searching half a billion files worldwide
© 1997-2009 MARUHN Internet Solutions