Fix MyManager. Add patch for MySQLdb and Python3.2. Add DelayedQuery.
authorRadek Brich <radek.brich@devl.cz>
Fri, 25 Nov 2011 18:17:27 +0100
changeset 18 a9e12b7cc207
parent 17 f768a3529ee7
child 19 e526ca146fa9
Fix MyManager. Add patch for MySQLdb and Python3.2. Add DelayedQuery.
README
extras/MySQL-python-1.2.3-python3.patch
extras/test-db.sql
mytoolkit/mymanager.py
pgtoolkit/delayedquery.py
--- a/README	Mon Nov 21 10:47:24 2011 +0100
+++ b/README	Fri Nov 25 18:17:27 2011 +0100
@@ -15,3 +15,20 @@
 pgbrowser-gtk.py - Database browser - graphical interface (GTK)
 pgconsole-gtk.py - Query console - graphical interface (GTK)
 pgdiff-cli.py - Command line diff tool
+
+MySQL - mytoolkit
+-----------------
+
+Package mytoolkit contains PgManager drop-in replacement for MySQL,
+MyManager. This may be useful if MySQL interoperability is needed
+for some reason.
+
+MyManager requires MySQLdb module, which works only with Python 2.5 - 2.7.
+You need patch for Python 3.2 - see `extras/MySQL-python-1.2.3-python3.patch`.
+
+MyManager may be merged into PgManager in future, but currently there
+isn't any generally usable python module for MySQL. MySQLdb isn't developed
+anymore thus being practically dead, while alternatives are not mature enough.
+
+Just use PostgreSQL with Python, it's better option in most use cases.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extras/MySQL-python-1.2.3-python3.patch	Fri Nov 25 18:17:27 2011 +0100
@@ -0,0 +1,2074 @@
+Python3 patch for MySQL-python-1.2.3.
+Based on https://github.com/davispuh/MySQL-for-Python-3
+Removed incompatible (and absolutely unnecessary) changes.
+diff -ru MySQL-python-1.2.3/_mysql.c MySQL-Python-1.2.3-python3/_mysql.c
+--- MySQL-python-1.2.3/_mysql.c	2010-06-17 09:21:56.000000000 +0200
++++ MySQL-Python-1.2.3-python3/_mysql.c	2011-10-05 12:49:58.000000000 +0200
+@@ -31,33 +31,17 @@
+ #if defined(MS_WINDOWS)
+ #include <winsock2.h>
+ #include <windows.h>
+-#include <config-win.h>
+-#else
+-#include "my_config.h"
+ #endif
++#include "my_config.h"
+ #include "mysql.h"
+ #include "mysqld_error.h"
+ #include "errmsg.h"
+ 
+-#if PY_VERSION_HEX < 0x02020000
+-# define MyTuple_Resize(t,n,d) _PyTuple_Resize(t, n, d)
+-# define MyMember(a,b,c,d,e) {a,b,c,d}
+-# define MyMemberlist(x) struct memberlist x
+-# define MyAlloc(s,t) PyObject_New(s,&t)
+-# define MyFree(o) PyObject_Del(o)
+-#else
+ # define MyTuple_Resize(t,n,d) _PyTuple_Resize(t, n)
+ # define MyMember(a,b,c,d,e) {a,b,c,d,e}
+ # define MyMemberlist(x) struct PyMemberDef x
+ # define MyAlloc(s,t) (s *) t.tp_alloc(&t,0)
+-# define MyFree(ob) ob->ob_type->tp_free((PyObject *)ob) 
+-#endif
+-
+-#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
+-typedef int Py_ssize_t;
+-#define PY_SSIZE_T_MAX INT_MAX
+-#define PY_SSIZE_T_MIN INT_MIN
+-#endif
++# define MyFree(ob) ob->ob_base.ob_type->tp_free((PyObject *)ob) 
+ 
+ static PyObject *_mysql_MySQLError;
+ static PyObject *_mysql_Warning;
+@@ -86,7 +70,7 @@
+ 
+ typedef struct {
+ 	PyObject_HEAD
+-	PyObject *conn;
++	_mysql_ConnectionObject *conn;
+ 	MYSQL_RES *result;
+ 	int nfields;
+ 	int use;
+@@ -102,17 +86,16 @@
+ #define check_server_init(x) if (!_mysql_server_init_done) _mysql_server_init_done = 1
+ #endif
+ 
+-PyObject *
+-_mysql_Exception(_mysql_ConnectionObject *c)
++PyObject * _mysql_Exception(_mysql_ConnectionObject *c)
+ {
+-	PyObject *t, *e;
++	PyObject *t, *e, *code, *message;
+ 	int merr;
+ 
+ 	if (!(t = PyTuple_New(2))) return NULL;
+ 	if (!_mysql_server_init_done) {
+ 		e = _mysql_InternalError;
+-		PyTuple_SET_ITEM(t, 0, PyInt_FromLong(-1L));
+-		PyTuple_SET_ITEM(t, 1, PyString_FromString("server not initialized"));
++		PyTuple_SET_ITEM(t, 0, PyLong_FromLong(-1L));
++		PyTuple_SET_ITEM(t, 1, PyUnicode_FromString("server not initialized"));
+ 		PyErr_SetObject(e, t);
+ 		Py_DECREF(t);
+ 		return NULL;
+@@ -121,8 +104,8 @@
+ 	if (!merr)
+ 		e = _mysql_InterfaceError;
+ 	else if (merr > CR_MAX_ERROR) {
+-		PyTuple_SET_ITEM(t, 0, PyInt_FromLong(-1L));
+-		PyTuple_SET_ITEM(t, 1, PyString_FromString("error totally whack"));
++		PyTuple_SET_ITEM(t, 0, PyLong_FromLong(-1L));
++		PyTuple_SET_ITEM(t, 1, PyUnicode_FromString("error totally whack"));
+ 		PyErr_SetObject(_mysql_InterfaceError, t);
+ 		Py_DECREF(t);
+ 		return NULL;
+@@ -209,8 +192,12 @@
+ 			e = _mysql_OperationalError;
+ 		break;
+ 	}
+-	PyTuple_SET_ITEM(t, 0, PyInt_FromLong((long)merr));
+-	PyTuple_SET_ITEM(t, 1, PyString_FromString(mysql_error(&(c->connection))));
++	code = PyLong_FromLong((long)merr);
++	message = PyUnicode_FromString(mysql_error(&(c->connection)));
++	PyTuple_SET_ITEM(t, 0, code );
++	PyTuple_SET_ITEM(t, 1, message );
++	PyObject_SetAttrString(e, "code", code );
++	PyObject_SetAttrString(e, "message", message );
+ 	PyErr_SetObject(e, t);
+ 	Py_DECREF(t);
+ 	return NULL;
+@@ -259,7 +246,7 @@
+ 		cmd_args_c = (char **) PyMem_Malloc(cmd_argc*sizeof(char *));
+ 		for (i=0; i< cmd_argc; i++) {
+ 			item = PySequence_GetItem(cmd_args, i);
+-			s = PyString_AsString(item);
++			s = PyBytes_AsString(item);
+ 			Py_DECREF(item);
+ 			if (!s) {
+ 				PyErr_SetString(PyExc_TypeError,
+@@ -284,7 +271,7 @@
+ 		groups_c = (char **) PyMem_Malloc((1+groupc)*sizeof(char *));
+ 		for (i=0; i< groupc; i++) {
+ 			item = PySequence_GetItem(groups, i);
+-			s = PyString_AsString(item);
++			s = PyBytes_AsString(item);
+ 			Py_DECREF(item);
+ 			if (!s) {
+ 				PyErr_SetString(PyExc_TypeError,
+@@ -339,7 +326,7 @@
+ 	PyObject *flag;
+ 	if (!PyArg_ParseTuple(args, "")) return NULL;
+ 	check_server_init(NULL);
+-	if (!(flag=PyInt_FromLong((long)mysql_thread_safe()))) return NULL;
++	if (!(flag=PyLong_FromLong((long)mysql_thread_safe()))) return NULL;
+ 	return flag;
+ }
+ #endif
+@@ -372,7 +359,7 @@
+ 		return -1;
+ 	if (!conv) conv = PyDict_New();
+ 	if (!conv) return -1;
+-	self->conn = (PyObject *) conn;
++	self->conn = conn;
+ 	Py_INCREF(conn);
+ 	self->use = use;
+ 	Py_BEGIN_ALLOW_THREADS ;
+@@ -392,7 +379,7 @@
+ 	fields = mysql_fetch_fields(result);
+ 	for (i=0; i<n; i++) {
+ 		PyObject *tmp, *fun;
+-		tmp = PyInt_FromLong((long) fields[i].type);
++		tmp = PyLong_FromLong((long) fields[i].type);
+ 		if (!tmp) return -1;
+ 		fun = PyObject_GetItem(conv, tmp);
+ 		Py_DECREF(tmp);
+@@ -413,8 +400,8 @@
+ 					PyObject *pmask=NULL;
+ 					pmask = PyTuple_GET_ITEM(t, 0);
+ 					fun2 = PyTuple_GET_ITEM(t, 1);
+-					if (PyInt_Check(pmask)) {
+-						mask = PyInt_AS_LONG(pmask);
++					if (PyNumber_Check(pmask)) {
++						mask = PyLong_AS_LONG(pmask);
+ 						if (mask & fields[i].flags) {
+ 							Py_DECREF(t);
+ 							break;
+@@ -440,7 +427,6 @@
+ 	return 0;
+ }
+ 
+-#if PY_VERSION_HEX >= 0x02020000
+ static int _mysql_ResultObject_traverse(
+ 	_mysql_ResultObject *self,
+ 	visitproc visit,
+@@ -451,10 +437,9 @@
+ 		if (!(r = visit(self->converter, arg))) return r;
+ 	}
+ 	if (self->conn)
+-		return visit(self->conn, arg);
++		return visit((PyObject *)self->conn, arg);
+ 	return 0;
+ }
+-#endif
+ 
+ static int _mysql_ResultObject_clear(
+ 	_mysql_ResultObject *self)
+@@ -515,7 +500,7 @@
+ 		return -1;
+ 
+ #define _stringsuck(d,t,s) {t=PyMapping_GetItemString(s,#d);\
+-        if(t){d=PyString_AsString(t);Py_DECREF(t);}\
++        if(t){d=PyBytes_AsString(t);Py_DECREF(t);}\
+         PyErr_Clear();}
+ 
+ 	if (ssl) {
+@@ -663,7 +648,6 @@
+ 	return (PyObject *) c;
+ }
+ 
+-#if PY_VERSION_HEX >= 0x02020000
+ static int _mysql_ConnectionObject_traverse(
+ 	_mysql_ConnectionObject *self,
+ 	visitproc visit,
+@@ -673,7 +657,7 @@
+ 		return visit(self->converter, arg);
+ 	return 0;
+ }
+-#endif
++
+ 
+ static int _mysql_ConnectionObject_clear(
+ 	_mysql_ConnectionObject *self)
+@@ -862,7 +846,7 @@
+ #endif
+ 	Py_END_ALLOW_THREADS
+ 	if (err > 0) return _mysql_Exception(self);
+-	return PyInt_FromLong(err);
++	return PyLong_FromLong(err);
+ }		
+ 
+ #if MYSQL_VERSION_ID >= 40100
+@@ -885,7 +869,7 @@
+ 	err = mysql_set_server_option(&(self->connection), flags);
+ 	Py_END_ALLOW_THREADS
+ 	if (err) return _mysql_Exception(self);
+-	return PyInt_FromLong(err);
++	return PyLong_FromLong(err);
+ }		
+ 
+ static char _mysql_ConnectionObject_sqlstate__doc__[] =
+@@ -906,7 +890,7 @@
+ 	PyObject *args)
+ {
+ 	if (!PyArg_ParseTuple(args, "")) return NULL;
+-	return PyString_FromString(mysql_sqlstate(&(self->connection)));
++	return PyUnicode_FromString(mysql_sqlstate(&(self->connection)));
+ }		
+ 
+ static char _mysql_ConnectionObject_warning_count__doc__[] =
+@@ -921,7 +905,7 @@
+ 	PyObject *args)
+ {
+ 	if (!PyArg_ParseTuple(args, "")) return NULL;
+-	return PyInt_FromLong(mysql_warning_count(&(self->connection)));
++	return PyLong_FromLong(mysql_warning_count(&(self->connection)));
+ }		
+ 
+ #endif
+@@ -939,7 +923,7 @@
+ {
+ 	if (!PyArg_ParseTuple(args, "")) return NULL;
+ 	check_connection(self);
+-	return PyInt_FromLong((long)mysql_errno(&(self->connection)));
++	return PyLong_FromLong((long)mysql_errno(&(self->connection)));
+ }
+ 
+ static char _mysql_ConnectionObject_error__doc__[] =
+@@ -955,7 +939,7 @@
+ {
+ 	if (!PyArg_ParseTuple(args, "")) return NULL;
+ 	check_connection(self);
+-	return PyString_FromString(mysql_error(&(self->connection)));
++	return PyUnicode_FromString(mysql_error(&(self->connection)));
+ }
+ 
+ static char _mysql_escape_string__doc__[] =
+@@ -966,18 +950,14 @@
+ probably better off using connection.escape(o) instead, since\n\
+ it will escape entire sequences as well as strings.";
+ 
+-static PyObject *
+-_mysql_escape_string(
+-	_mysql_ConnectionObject *self,
+-	PyObject *args)
++static PyObject *_mysql_ConnectionObject_escape_string(	_mysql_ConnectionObject *self,	PyObject *args)
+ {
+ 	PyObject *str;
+ 	char *in, *out;
+ 	int len, size;
+ 	if (!PyArg_ParseTuple(args, "s#:escape_string", &in, &size)) return NULL;
+-	str = PyString_FromStringAndSize((char *) NULL, size*2+1);
+-	if (!str) return PyErr_NoMemory();
+-	out = PyString_AS_STRING(str);
++	out = PyMem_New(char, size*2+1);
++	if (!out) return PyErr_NoMemory();
+ #if MYSQL_VERSION_ID < 32321
+ 	len = mysql_escape_string(out, in, size);
+ #else
+@@ -987,10 +967,16 @@
+ 	else
+ 		len = mysql_escape_string(out, in, size);
+ #endif
+-	if (_PyString_Resize(&str, len) < 0) return NULL;
++	str = PyUnicode_FromString(out);
++	PyMem_Del(out);
+ 	return (str);
+ }
+ 
++static PyObject *_mysql_escape_string( PyObject *self, PyObject *args)
++{
++	return _mysql_ConnectionObject_escape_string(NULL, args);
++}
++
+ static char _mysql_string_literal__doc__[] =
+ "string_literal(obj) -- converts object obj into a SQL string literal.\n\
+ This means, any special SQL characters are escaped, and it is enclosed\n\
+@@ -1001,43 +987,50 @@
+ Use connection.string_literal(obj), if you use it at all.\n\
+ _mysql.string_literal(obj) cannot handle character sets.";
+ 
+-static PyObject *
+-_mysql_string_literal(
+-	_mysql_ConnectionObject *self,
+-	PyObject *args)
++static PyObject *_mysql_ConnectionObject_string_literal(_mysql_ConnectionObject *self, PyObject *args)
+ {
+-	PyObject *str, *s, *o, *d;
++	PyObject *str, *s, *s2, *o, *d;
+ 	char *in, *out;
+ 	int len, size;
+ 	if (!PyArg_ParseTuple(args, "O|O:string_literal", &o, &d)) return NULL;
+ 	s = PyObject_Str(o);
+ 	if (!s) return NULL;
+-	in = PyString_AsString(s);
+-	size = PyString_GET_SIZE(s);
+-	str = PyString_FromStringAndSize((char *) NULL, size*2+3);
+-	if (!str) return PyErr_NoMemory();
+-	out = PyString_AS_STRING(str);
++	s2 = PyUnicode_AsUTF8String(s);
++	if (!s2) return NULL;
++	in = PyBytes_AsString(s2);
++	if (!in) return NULL;
++	size = PyBytes_GET_SIZE(s2);
++	out = PyMem_New(char, size*2+3);
++	if (!out) return PyErr_NoMemory();
+ #if MYSQL_VERSION_ID < 32321
+ 	len = mysql_escape_string(out+1, in, size);
+ #else
+ 	check_server_init(NULL);
+-	if (self && self->open)
++	if (self && self->open )
++	{
+ 		len = mysql_real_escape_string(&(self->connection), out+1, in, size);
+-	else
++	} else
++	{
+ 		len = mysql_escape_string(out+1, in, size);
++	};
+ #endif
+ 	*out = *(out+len+1) = '\'';
+-	if (_PyString_Resize(&str, len+2) < 0) return NULL;
++	*(out+len+2) = '\0';
++	str = PyUnicode_FromString(out);
++	PyMem_Del(out);
++	Py_DECREF(s2);
+ 	Py_DECREF(s);
+ 	return (str);
+ }
+ 
++static PyObject *_mysql_string_literal(PyObject *self, PyObject *args)
++{
++	return _mysql_ConnectionObject_string_literal(NULL, args);
++}
++
+ static PyObject *_mysql_NULL;
+ 
+-static PyObject *
+-_escape_item(
+-	PyObject *item,
+-	PyObject *d)
++static PyObject *_escape_item( PyObject *item, PyObject *d)
+ {
+ 	PyObject *quoted=NULL, *itemtype, *itemconv;
+ 	if (!(itemtype = PyObject_Type(item)))
+@@ -1047,11 +1040,11 @@
+ 	if (!itemconv) {
+ 		PyErr_Clear();
+ 		itemconv = PyObject_GetItem(d,
+-				 (PyObject *) &PyString_Type);
++			(PyObject *) &PyUnicode_Type);
+ 	}
+ 	if (!itemconv) {
+ 		PyErr_SetString(PyExc_TypeError,
+-				"no default type converter defined");
++			"no default type converter defined");
+ 		goto error;
+ 	}
+ 	quoted = PyObject_CallFunction(itemconv, "OO", item, d);
+@@ -1064,32 +1057,33 @@
+ "escape(obj, dict) -- escape any special characters in object obj\n\
+ using mapping dict to provide quoting functions for each type.\n\
+ Returns a SQL literal string.";
+-static PyObject *
+-_mysql_escape(
+-	PyObject *self,
+-	PyObject *args)
++static PyObject *_mysql_ConnectionObject_escape( _mysql_ConnectionObject *self, PyObject *args)
+ {
+ 	PyObject *o=NULL, *d=NULL;
+ 	if (!PyArg_ParseTuple(args, "O|O:escape", &o, &d))
+ 		return NULL;
+ 	if (d) {
+ 		if (!PyMapping_Check(d)) {
+-			PyErr_SetString(PyExc_TypeError,
+-					"argument 2 must be a mapping");
++			PyErr_SetString(PyExc_TypeError, "argument 2 must be a mapping");
+ 			return NULL;
+ 		}
+ 		return _escape_item(o, d);
+-	} else {
+-		if (!self) {
+-			PyErr_SetString(PyExc_TypeError,
+-					"argument 2 must be a mapping");
++	} else
++	{
++		if (!self)
++		{
++			PyErr_SetString(PyExc_TypeError, "argument 2 must be a mapping");
+ 			return NULL;
+ 		}
+-		return _escape_item(o,
+-			   ((_mysql_ConnectionObject *) self)->converter);
++		return _escape_item(o, self->converter);
+ 	}
+ }
+ 
++static PyObject *_mysql_escape(	PyObject *self,	PyObject *args)
++{
++	return _mysql_ConnectionObject_escape(NULL, args);
++}
++
+ static char _mysql_escape_sequence__doc__[] =
+ "escape_sequence(seq, dict) -- escape any special characters in sequence\n\
+ seq using mapping dict to provide quoting functions for each type.\n\
+@@ -1160,20 +1154,28 @@
+ the Cursor.description attribute.\n\
+ ";
+ 
+-static PyObject *
+-_mysql_ResultObject_describe(
+-	_mysql_ResultObject *self,
+-	PyObject *args)
++static PyObject * _mysql_ResultObject_describe ( _mysql_ResultObject *self, PyObject *args)
+ {
+-	PyObject *d;
++	PyObject *d = NULL;
+ 	MYSQL_FIELD *fields;
+ 	unsigned int i, n;
+-	if (!PyArg_ParseTuple(args, "")) return NULL;
++	
++	if ( !PyArg_ParseTuple(args, ""))
++	{
++		return NULL;
++	};
++
+ 	check_result_connection(self);
++
+ 	n = mysql_num_fields(self->result);
+ 	fields = mysql_fetch_fields(self->result);
+-	if (!(d = PyTuple_New(n))) return NULL;
+-	for (i=0; i<n; i++) {
++	d = PyTuple_New(n);
++	if ( d == NULL )
++	{
++		return NULL;
++	};
++	for (i = 0; i < n; i++)
++	{
+ 		PyObject *t;
+ 		t = Py_BuildValue("(siiiiii)",
+ 				  fields[i].name,
+@@ -1183,13 +1185,16 @@
+ 				  (long) fields[i].length,
+ 				  (long) fields[i].decimals,
+ 				  (long) !(IS_NOT_NULL(fields[i].flags)));
+-		if (!t) goto error;
++		if (t == NULL)
++		{
++			Py_XDECREF(d);
++			d = NULL;
++			break;
++		};
+ 		PyTuple_SET_ITEM(d, i, t);
+-	}
++	};
++	
+ 	return d;
+-  error:
+-	Py_XDECREF(d);
+-	return NULL;
+ }
+ 	
+ static char _mysql_ResultObject_field_flags__doc__[] =
+@@ -1211,7 +1216,7 @@
+ 	if (!(d = PyTuple_New(n))) return NULL;
+ 	for (i=0; i<n; i++) {
+ 		PyObject *f;
+-		if (!(f = PyInt_FromLong((long)fields[i].flags))) goto error;
++		if (!(f = PyLong_FromLong((long)fields[i].flags))) goto error;
+ 		PyTuple_SET_ITEM(d, i, f);
+ 	}
+ 	return d;
+@@ -1221,27 +1226,44 @@
+ }
+ 
+ static PyObject *
+-_mysql_field_to_python(
+-	PyObject *converter,
+-	char *rowitem,
+-	unsigned long length)
++_mysql_field_to_python(PyObject *converter, char *rowitem, unsigned long length, const char *charset)
+ {
+ 	PyObject *v;
+-	if (rowitem) {
+-		if (converter != Py_None)
+-			v = PyObject_CallFunction(converter,
+-						  "s#",
+-						  rowitem,
+-						  (int)length);
+-		else
+-			v = PyString_FromStringAndSize(rowitem,
+-						       (int)length);
+-		if (!v)
+-			return NULL;
+-	} else {
++	if (rowitem)
++	{
++		if (charset)
++		{
++			PyObject *rowitem_unicode = PyUnicode_Decode(rowitem, (int)length, charset, "replace");
++			if (rowitem_unicode == NULL)
++			{
++				return NULL;
++			} else
++			{
++				if (converter != Py_None)
++		        {
++					v = PyObject_CallFunction(converter, "O", rowitem_unicode);
++			                Py_XDECREF(rowitem_unicode);
++		        } else
++				{
++					v = rowitem_unicode;
++				};
++			};
++		} else
++		{
++			if (converter != Py_None)
++		    {
++				v = PyObject_CallFunction(converter, "s#", rowitem, (int)length);
++		    } else
++		    {
++				v = PyUnicode_FromStringAndSize(rowitem, (int)length);
++			    if (!v) return NULL;
++			};
++		};
++	} else
++	{
+ 		Py_INCREF(Py_None);
+ 		v = Py_None;
+-	}
++	};
+ 	return v;
+ }
+ 
+@@ -1253,14 +1275,22 @@
+ 	unsigned int n, i;
+ 	unsigned long *length;
+ 	PyObject *r, *c;
++	const char *charset;
+ 
+ 	n = mysql_num_fields(self->result);
+ 	if (!(r = PyTuple_New(n))) return NULL;
+ 	length = mysql_fetch_lengths(self->result);
++
++#if MYSQL_VERSION_ID >= 32321
++	charset = mysql_character_set_name(&(self->conn->connection));
++#else
++	charset = "latin1";
++#endif
++
+ 	for (i=0; i<n; i++) {
+ 		PyObject *v;
+ 		c = PyTuple_GET_ITEM(self->converter, i);
+-		v = _mysql_field_to_python(c, row[i], length[i]);
++		v = _mysql_field_to_python(c, row[i], length[i], charset);
+ 		if (!v) goto error;
+ 		PyTuple_SET_ITEM(r, i, v);
+ 	}
+@@ -1278,16 +1308,24 @@
+ 	unsigned int n, i;
+ 	unsigned long *length;
+ 	PyObject *r, *c;
+-        MYSQL_FIELD *fields;
++    MYSQL_FIELD *fields;
++	const char *charset;
+ 
+ 	n = mysql_num_fields(self->result);
+ 	if (!(r = PyDict_New())) return NULL;
+ 	length = mysql_fetch_lengths(self->result);
+         fields = mysql_fetch_fields(self->result);
++
++#if MYSQL_VERSION_ID >= 32321
++	charset = mysql_character_set_name(&(self->conn->connection));
++#else
++	charset = "latin1";
++#endif
++
+ 	for (i=0; i<n; i++) {
+ 		PyObject *v;
+ 		c = PyTuple_GET_ITEM(self->converter, i);
+-		v = _mysql_field_to_python(c, row[i], length[i]);
++		v = _mysql_field_to_python(c, row[i], length[i], charset);
+ 		if (!v) goto error;
+ 		if (!PyMapping_HasKeyString(r, fields[i].name)) {
+ 			PyMapping_SetItemString(r, fields[i].name, v);
+@@ -1317,16 +1355,24 @@
+ 	unsigned int n, i;
+ 	unsigned long *length;
+ 	PyObject *r, *c;
+-        MYSQL_FIELD *fields;
++    MYSQL_FIELD *fields;
++	const char *charset;
+ 
+ 	n = mysql_num_fields(self->result);
+ 	if (!(r = PyDict_New())) return NULL;
+ 	length = mysql_fetch_lengths(self->result);
+         fields = mysql_fetch_fields(self->result);
++
++#if MYSQL_VERSION_ID >= 32321
++	charset = mysql_character_set_name(&(self->conn->connection));
++#else
++	charset = "latin1";
++#endif
++
+ 	for (i=0; i<n; i++) {
+ 		PyObject *v;
+ 		c = PyTuple_GET_ITEM(self->converter, i);
+-		v = _mysql_field_to_python(c, row[i], length[i]);
++		v = _mysql_field_to_python(c, row[i], length[i], charset);
+ 		if (!v) goto error;
+ 		{
+ 			int len=0;
+@@ -1354,8 +1400,8 @@
+ _mysql__fetch_row(
+ 	_mysql_ResultObject *self,
+ 	PyObject **r,
+-	int skiprows,
+-	int maxrows,
++	unsigned int skiprows,
++	unsigned int maxrows,
+ 	_PYFUNC *convert_row)
+ {
+ 	unsigned int i;
+@@ -1516,7 +1562,7 @@
+ #else
+ 	s = "latin1";
+ #endif
+-	return PyString_FromString(s);
++	return PyUnicode_FromString(s);
+ }
+ 
+ #if MYSQL_VERSION_ID >= 50007
+@@ -1578,15 +1624,15 @@
+ 	mysql_get_character_set_info(&(self->connection), &cs);
+ 	if (!(result = PyDict_New())) return NULL;
+ 	if (cs.csname)
+-		PyDict_SetItemString(result, "name", PyString_FromString(cs.csname));
++		PyDict_SetItemString(result, "name", PyUnicode_FromString(cs.csname));
+ 	if (cs.name)
+-		PyDict_SetItemString(result, "collation", PyString_FromString(cs.name));
++		PyDict_SetItemString(result, "collation", PyUnicode_FromString(cs.name));
+ 	if (cs.comment)
+-		PyDict_SetItemString(result, "comment", PyString_FromString(cs.comment));
++		PyDict_SetItemString(result, "comment", PyUnicode_FromString(cs.comment));
+ 	if (cs.dir)
+-		PyDict_SetItemString(result, "dir", PyString_FromString(cs.dir));
+-	PyDict_SetItemString(result, "mbminlen", PyInt_FromLong(cs.mbminlen));
+-	PyDict_SetItemString(result, "mbmaxlen", PyInt_FromLong(cs.mbmaxlen));
++		PyDict_SetItemString(result, "dir", PyUnicode_FromString(cs.dir));
++	PyDict_SetItemString(result, "mbminlen", PyLong_FromLong(cs.mbminlen));
++	PyDict_SetItemString(result, "mbmaxlen", PyLong_FromLong(cs.mbmaxlen));
+ 	return result;
+ }
+ #endif
+@@ -1596,12 +1642,12 @@
+ the client library version.";
+ static PyObject *
+ _mysql_get_client_info(
+-	PyObject *self,
+-	PyObject *args)
++					   PyObject *self,
++					   PyObject *args)
+ {
+ 	if (!PyArg_ParseTuple(args, "")) return NULL;
+ 	check_server_init(NULL);
+-	return PyString_FromString(mysql_get_client_info());
++	return PyUnicode_FromString(mysql_get_client_info());
+ }
+ 
+ static char _mysql_ConnectionObject_get_host_info__doc__[] =
+@@ -1616,7 +1662,7 @@
+ {
+ 	if (!PyArg_ParseTuple(args, "")) return NULL;
+ 	check_connection(self);
+-	return PyString_FromString(mysql_get_host_info(&(self->connection)));
++	return PyUnicode_FromString(mysql_get_host_info(&(self->connection)));
+ }
+ 
+ static char _mysql_ConnectionObject_get_proto_info__doc__[] =
+@@ -1631,7 +1677,7 @@
+ {
+ 	if (!PyArg_ParseTuple(args, "")) return NULL;
+ 	check_connection(self);
+-	return PyInt_FromLong((long)mysql_get_proto_info(&(self->connection)));
++	return PyLong_FromLong((long)mysql_get_proto_info(&(self->connection)));
+ }
+ 
+ static char _mysql_ConnectionObject_get_server_info__doc__[] =
+@@ -1646,7 +1692,7 @@
+ {
+ 	if (!PyArg_ParseTuple(args, "")) return NULL;
+ 	check_connection(self);
+-	return PyString_FromString(mysql_get_server_info(&(self->connection)));
++	return PyUnicode_FromString(mysql_get_server_info(&(self->connection)));
+ }
+ 
+ static char _mysql_ConnectionObject_info__doc__[] =
+@@ -1664,7 +1710,7 @@
+ 	if (!PyArg_ParseTuple(args, "")) return NULL;
+ 	check_connection(self);
+ 	s = mysql_info(&(self->connection));
+-	if (s) return PyString_FromString(s);
++	if (s) return PyUnicode_FromString(s);
+ 	Py_INCREF(Py_None);
+ 	return Py_None;
+ }
+@@ -1739,9 +1785,9 @@
+ 	if (!PyArg_ParseTuple(args, "")) return NULL;
+ 	check_connection(self);
+ #if MYSQL_VERSION_ID < 32224
+-	return PyInt_FromLong((long)mysql_num_fields(&(self->connection)));
++	return PyNumber_FromLong((long)mysql_num_fields(&(self->connection)));
+ #else
+-	return PyInt_FromLong((long)mysql_field_count(&(self->connection)));
++	return PyLong_FromLong((long)mysql_field_count(&(self->connection)));
+ #endif
+ }	
+ 
+@@ -1755,7 +1801,7 @@
+ {
+ 	if (!PyArg_ParseTuple(args, "")) return NULL;
+ 	check_result_connection(self);
+-	return PyInt_FromLong((long)mysql_num_fields(self->result));
++	return PyLong_FromLong((long)mysql_num_fields(self->result));
+ }	
+ 
+ static char _mysql_ResultObject_num_rows__doc__[] =
+@@ -1907,7 +1953,7 @@
+ 	s = mysql_stat(&(self->connection));
+ 	Py_END_ALLOW_THREADS
+ 	if (!s) return _mysql_Exception(self);
+-	return PyString_FromString(s);
++	return PyUnicode_FromString(s);
+ }
+ 
+ static char _mysql_ConnectionObject_store_result__doc__[] =
+@@ -1968,7 +2014,7 @@
+ 	Py_BEGIN_ALLOW_THREADS
+ 	pid = mysql_thread_id(&(self->connection));
+ 	Py_END_ALLOW_THREADS
+-	return PyInt_FromLong((long)pid);
++	return PyLong_FromLong((long)pid);
+ }
+ 
+ static char _mysql_ConnectionObject_use_result__doc__[] =
+@@ -2033,7 +2079,7 @@
+ 	else
+ 		sprintf(buf, "<_mysql.connection closed at %lx>",
+ 			(long)self);
+-	return PyString_FromString(buf);
++	return PyUnicode_FromString(buf);
+ }
+ 
+ static char _mysql_ResultObject_data_seek__doc__[] =
+@@ -2089,7 +2135,7 @@
+ 		return NULL;
+ 	}
+ 	r = mysql_row_tell(self->result);
+-	return PyInt_FromLong(r-self->result->data->data);
++	return PyLong_FromLong(r-self->result->data->data);
+ }
+ 
+ static void
+@@ -2109,7 +2155,7 @@
+ 	char buf[300];
+ 	sprintf(buf, "<_mysql.result object at %lx>",
+ 		(long)self);
+-	return PyString_FromString(buf);
++	return PyUnicode_FromString(buf);
+ }
+ 
+ static PyMethodDef _mysql_ConnectionObject_methods[] = {
+@@ -2207,13 +2253,13 @@
+ 	},
+ 	{
+ 		"escape",
+-		(PyCFunction)_mysql_escape,
++		(PyCFunction)_mysql_ConnectionObject_escape,
+ 		METH_VARARGS,
+ 		_mysql_escape__doc__
+ 	},
+ 	{
+ 		"escape_string",
+-		(PyCFunction)_mysql_escape_string,
++		(PyCFunction)_mysql_ConnectionObject_escape_string,
+ 		METH_VARARGS,
+ 		_mysql_escape_string__doc__
+ 	},
+@@ -2309,7 +2355,7 @@
+ 	},
+ 	{
+ 		"string_literal",
+-		(PyCFunction)_mysql_string_literal,
++		(PyCFunction)_mysql_ConnectionObject_string_literal,
+ 		METH_VARARGS,
+ 		_mysql_string_literal__doc__},
+ 	{
+@@ -2332,7 +2378,7 @@
+ 		"open",
+ 		T_INT,
+ 		offsetof(_mysql_ConnectionObject,open),
+-		RO,
++		READONLY,
+ 		"True if connection is open"
+ 		),
+ 	MyMember(
+@@ -2346,20 +2392,20 @@
+ 		"server_capabilities",
+ 		T_UINT,
+ 		offsetof(_mysql_ConnectionObject,connection.server_capabilities),
+-		RO,
++		READONLY,
+ 		"Capabilites of server; consult MySQLdb.constants.CLIENT"
+ 		),
+ 	MyMember(
+ 		 "port",
+ 		 T_UINT,
+ 		 offsetof(_mysql_ConnectionObject,connection.port),
+-		 RO,
++		 READONLY,
+ 		 "TCP/IP port of the server connection"
+ 		 ),
+ 	MyMember(
+ 		 "client_flag",
+ 		 T_UINT,
+-		 RO,
++		 READONLY,
+ 		 offsetof(_mysql_ConnectionObject,connection.client_flag),
+ 		 "Client flags; refer to MySQLdb.constants.CLIENT"
+ 		 ),
+@@ -2423,127 +2469,22 @@
+ 		"converter",
+ 		T_OBJECT,
+ 		offsetof(_mysql_ResultObject,converter),
+-		RO,
++		READONLY,
+ 		"Type conversion mapping"
+ 		),
+ 	{NULL} /* Sentinel */
+ };
+-                                                                        
+-static PyObject *
+-_mysql_ConnectionObject_getattr(
+-	_mysql_ConnectionObject *self,
+-	char *name)
+-{
+-	PyObject *res;
+-
+-	res = Py_FindMethod(_mysql_ConnectionObject_methods, (PyObject *)self, name);
+-	if (res != NULL)
+-		return res;
+-	PyErr_Clear();
+-	if (strcmp(name, "closed") == 0)
+-		return PyInt_FromLong((long)!(self->open));
+-#if PY_VERSION_HEX < 0x02020000
+-	return PyMember_Get((char *)self, _mysql_ConnectionObject_memberlist, name);
+-#else
+-	{
+-		MyMemberlist(*l);
+-		for (l = _mysql_ConnectionObject_memberlist; l->name != NULL; l++) {
+-			if (strcmp(l->name, name) == 0)
+-				return PyMember_GetOne((char *)self, l);
+-		}
+-		PyErr_SetString(PyExc_AttributeError, name);
+-		return NULL;
+-	}
+-#endif
+-}
+-
+-static PyObject *
+-_mysql_ResultObject_getattr(
+-	_mysql_ResultObject *self,
+-	char *name)
+-{
+-	PyObject *res;
+-
+-	res = Py_FindMethod(_mysql_ResultObject_methods, (PyObject *)self, name);
+-	if (res != NULL)
+-		return res;
+-	PyErr_Clear();
+-#if PY_VERSION_HEX < 0x02020000
+-	return PyMember_Get((char *)self, _mysql_ResultObject_memberlist, name);
+-#else
+-	{
+-		MyMemberlist(*l);
+-		for (l = _mysql_ResultObject_memberlist; l->name != NULL; l++) {
+-			if (strcmp(l->name, name) == 0)
+-				return PyMember_GetOne((char *)self, l);
+-		}
+-		PyErr_SetString(PyExc_AttributeError, name);
+-		return NULL;
+-	}
+-#endif
+-}
+-
+-static int
+-_mysql_ConnectionObject_setattr(
+-	_mysql_ConnectionObject *self,
+-	char *name,
+-	PyObject *v)
+-{
+-	if (v == NULL) {
+-		PyErr_SetString(PyExc_AttributeError,
+-				"can't delete connection attributes");
+-		return -1;
+-	}
+-#if PY_VERSION_HEX < 0x02020000
+-	return PyMember_Set((char *)self, _mysql_ConnectionObject_memberlist, name, v);
+-#else
+-        {
+-		MyMemberlist(*l);
+-		for (l = _mysql_ConnectionObject_memberlist; l->name != NULL; l++)
+-			if (strcmp(l->name, name) == 0)
+-				return PyMember_SetOne((char *)self, l, v);
+-	}
+-        PyErr_SetString(PyExc_AttributeError, name);
+-        return -1;
+-#endif
+-}
+-
+-static int
+-_mysql_ResultObject_setattr(
+-	_mysql_ResultObject *self,
+-	char *name,
+-	PyObject *v)
+-{
+-	if (v == NULL) {
+-		PyErr_SetString(PyExc_AttributeError,
+-				"can't delete connection attributes");
+-		return -1;
+-	}
+-#if PY_VERSION_HEX < 0x02020000
+-	return PyMember_Set((char *)self, _mysql_ResultObject_memberlist, name, v);
+-#else
+-        {
+-		MyMemberlist(*l);
+-		for (l = _mysql_ResultObject_memberlist; l->name != NULL; l++)
+-			if (strcmp(l->name, name) == 0)
+-				return PyMember_SetOne((char *)self, l, v);
+-	}
+-        PyErr_SetString(PyExc_AttributeError, name);
+-        return -1;
+-#endif
+-}
+ 
+ PyTypeObject _mysql_ConnectionObject_Type = {
+-	PyObject_HEAD_INIT(NULL)
+-	0,
++	PyVarObject_HEAD_INIT(NULL, 0)
+ 	"_mysql.connection", /* (char *)tp_name For printing */
+ 	sizeof(_mysql_ConnectionObject),
+ 	0,
+ 	(destructor)_mysql_ConnectionObject_dealloc, /* tp_dealloc */
+-	0, /*tp_print*/
+-	(getattrfunc)_mysql_ConnectionObject_getattr, /* tp_getattr */
+-	(setattrfunc)_mysql_ConnectionObject_setattr, /* tp_setattr */
+-	0, /*tp_compare*/
++	0, /* tp_print*/
++	0, /* tp_getattr */
++	0, /* tp_setattr */
++	0, /* tp_compare*/
+ 	(reprfunc)_mysql_ConnectionObject_repr, /* tp_repr */
+ 	
+ 	/* Method suites for standard classes */
+@@ -2564,33 +2505,20 @@
+ 	0, /* (PyBufferProcs *) tp_as_buffer */
+ 	
+ 	/* Flags to define presence of optional/expanded features */
+-#if PY_VERSION_HEX < 0x02020000
+-	Py_TPFLAGS_DEFAULT, /* (long) tp_flags */
+-#else
+ 	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE,
+-#endif
+ 	_mysql_connect__doc__, /* (char *) tp_doc Documentation string */
+-#if PY_VERSION_HEX >= 0x02000000	
+ 	/* Assigned meaning in release 2.0 */
+-#if PY_VERSION_HEX >= 0x02020000
+ 	/* call function for all accessible objects */
+ 	(traverseproc) _mysql_ConnectionObject_traverse, /* tp_traverse */
+ 	
+ 	/* delete references to contained objects */
+ 	(inquiry) _mysql_ConnectionObject_clear, /* tp_clear */
+-#else
+-	/* not supporting pre-2.2 GC */
+-	0,
+-	0,
+-#endif
+-#if PY_VERSION_HEX >= 0x02010000	
+ 	/* Assigned meaning in release 2.1 */
+ 	/* rich comparisons */
+ 	0, /* (richcmpfunc) tp_richcompare */
+ 	
+ 	/* weak reference enabler */
+ 	0, /* (long) tp_weaklistoffset */
+-#if PY_VERSION_HEX >= 0x02020000
+ 	/* Added in release 2.2 */
+ 	/* Iterators */
+ 	0, /* (getiterfunc) tp_iter */
+@@ -2612,22 +2540,18 @@
+ 	0, /* (PyObject *) tp_bases */
+ 	0, /* (PyObject *) tp_mro method resolution order */
+ 	0, /* (PyObject *) tp_defined */
+-#endif /* python 2.2 */
+-#endif /* python 2.1 */
+-#endif /* python 2.0 */
+ } ;
+ 
+ PyTypeObject _mysql_ResultObject_Type = {
+-	PyObject_HEAD_INIT(NULL)
+-	0,
++	 PyVarObject_HEAD_INIT(NULL, 0)
+ 	"_mysql.result",
+ 	sizeof(_mysql_ResultObject),
+ 	0,
+ 	(destructor)_mysql_ResultObject_dealloc, /* tp_dealloc */
+-	0, /*tp_print*/
+-	(getattrfunc)_mysql_ResultObject_getattr, /* tp_getattr */
+-	(setattrfunc)_mysql_ResultObject_setattr, /* tp_setattr */
+-	0, /*tp_compare*/
++	0, /* tp_print */
++	0, /* tp_getattr */
++	0, /* tp_setattr */
++	0, /* tp_compare */
+ 	(reprfunc)_mysql_ResultObject_repr, /* tp_repr */
+ 	
+ 	/* Method suites for standard classes */
+@@ -2648,34 +2572,20 @@
+ 	0, /* (PyBufferProcs *) tp_as_buffer */
+ 	
+ 	/* Flags to define presence of optional/expanded features */
+-#if PY_VERSION_HEX < 0x02020000
+-	Py_TPFLAGS_DEFAULT, /* (long) tp_flags */
+-#else
+ 	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE,
+-#endif
+-	
+ 	_mysql_ResultObject__doc__, /* (char *) tp_doc Documentation string */
+-#if PY_VERSION_HEX >= 0x02000000	
+ 	/* Assigned meaning in release 2.0 */
+-#if PY_VERSION_HEX >= 0x02020000
+ 	/* call function for all accessible objects */
+ 	(traverseproc) _mysql_ResultObject_traverse, /* tp_traverse */
+ 	
+ 	/* delete references to contained objects */
+ 	(inquiry) _mysql_ResultObject_clear, /* tp_clear */
+-#else
+-	/* not supporting pre-2.2 GC */
+-	0,
+-	0,
+-#endif
+-#if PY_VERSION_HEX >= 0x02010000	
+ 	/* Assigned meaning in release 2.1 */
+ 	/* rich comparisons */
+ 	0, /* (richcmpfunc) tp_richcompare */
+ 	
+ 	/* weak reference enabler */
+ 	0, /* (long) tp_weaklistoffset */
+-#if PY_VERSION_HEX >= 0x02020000
+ 	/* Added in release 2.2 */
+ 	/* Iterators */
+ 	0, /* (getiterfunc) tp_iter */
+@@ -2697,13 +2607,9 @@
+ 	0, /* (PyObject *) tp_bases */
+ 	0, /* (PyObject *) tp_mro method resolution order */
+ 	0, /* (PyObject *) tp_defined */
+-#endif /* python 2.2 */
+-#endif /* python 2.1 */
+-#endif /* python 2.0 */
+ };
+ 
+-static PyMethodDef
+-_mysql_methods[] = {
++static PyMethodDef _mysql_methods[] = {
+ 	{ 
+ 		"connect",
+ 		(PyCFunction)_mysql_connect,
+@@ -2775,16 +2681,11 @@
+ 	{NULL, NULL} /* sentinel */
+ };
+ 
+-static PyObject *
+-_mysql_NewException(
+-	PyObject *dict,
+-	PyObject *edict,
+-	char *name)
++static PyObject *_mysql_NewException( PyObject *dict, PyObject *edict, char *name)
+ {
+ 	PyObject *e;
+ 
+-	if (!(e = PyDict_GetItemString(edict, name)))
+-		return NULL;
++	if (!(e = PyDict_GetItemString(edict, name))) return NULL;
+ 	if (PyDict_SetItemString(dict, name, e)) return NULL;
+ 	return e;
+ }
+@@ -2806,85 +2707,111 @@
+ (as of 3.23) are NOT implemented.\n\
+ ";
+ 
+-DL_EXPORT(void)
+-init_mysql(void)
++/* module definition */
++static struct PyModuleDef mysqlModule = {
++	PyModuleDef_HEAD_INIT,
++	"_mysql",	/* name of module */
++	_mysql___doc__,		/* module documentation */
++	-1, /* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */
++	_mysql_methods,
++	NULL,
++	NULL,
++	NULL,
++	NULL
++};
++
++int CheckError()
++{
++	if ( PyErr_Occurred() )
++	{
++		PyErr_Print();
++		PyErr_SetString(PyExc_ImportError, "_mysql: init failed");
++		PyErr_Print();
++		return -1;
++	};
++	return 0;
++};
++
++PyMODINIT_FUNC PyInit__mysql(void)
+ {
+ 	PyObject *dict, *module, *emod, *edict;
+-	module = Py_InitModule4("_mysql", _mysql_methods, _mysql___doc__,
+-				(PyObject *)NULL, PYTHON_API_VERSION);
+-	if (!module) return; /* this really should never happen */
+-	_mysql_ConnectionObject_Type.ob_type = &PyType_Type;
+-	_mysql_ResultObject_Type.ob_type = &PyType_Type;
+-#if PY_VERSION_HEX >= 0x02020000
++
++	_mysql_ConnectionObject_Type.ob_base.ob_base.ob_type = &PyType_Type;
++	_mysql_ResultObject_Type.ob_base.ob_base.ob_type = &PyType_Type;
+ 	_mysql_ConnectionObject_Type.tp_alloc = PyType_GenericAlloc;
+ 	_mysql_ConnectionObject_Type.tp_new = PyType_GenericNew;
+-	_mysql_ConnectionObject_Type.tp_free = _PyObject_GC_Del; 
++	_mysql_ConnectionObject_Type.tp_free = PyObject_GC_Del; 
+ 	_mysql_ResultObject_Type.tp_alloc = PyType_GenericAlloc;
+ 	_mysql_ResultObject_Type.tp_new = PyType_GenericNew;
+-	_mysql_ResultObject_Type.tp_free = _PyObject_GC_Del;
+-#endif
++	_mysql_ResultObject_Type.tp_free = PyObject_GC_Del;
+ 
+-	if (!(dict = PyModule_GetDict(module))) goto error;
+-	if (PyDict_SetItemString(dict, "version_info",
+-			       PyRun_String(QUOTE(version_info), Py_eval_input,
+-				       dict, dict)))
+-		goto error;
+-	if (PyDict_SetItemString(dict, "__version__",
+-			       PyString_FromString(QUOTE(__version__))))
+-		goto error;
+-	if (PyDict_SetItemString(dict, "connection",
+-			       (PyObject *)&_mysql_ConnectionObject_Type))
+-		goto error;
+-	Py_INCREF(&_mysql_ConnectionObject_Type);
+-	if (PyDict_SetItemString(dict, "result",
+-			       (PyObject *)&_mysql_ResultObject_Type))
+-		goto error;	
+-	Py_INCREF(&_mysql_ResultObject_Type);
+-	if (!(emod = PyImport_ImportModule("_mysql_exceptions")))
+-		goto error;
+-	if (!(edict = PyModule_GetDict(emod))) goto error;
+-	if (!(_mysql_MySQLError =
+-	      _mysql_NewException(dict, edict, "MySQLError")))
+-		goto error;
+-	if (!(_mysql_Warning =
+-	      _mysql_NewException(dict, edict, "Warning")))
+-		goto error;
+-	if (!(_mysql_Error =
+-	      _mysql_NewException(dict, edict, "Error")))
+-		goto error;
+-	if (!(_mysql_InterfaceError =
+-	      _mysql_NewException(dict, edict, "InterfaceError")))
+-		goto error;
+-	if (!(_mysql_DatabaseError =
+-	      _mysql_NewException(dict, edict, "DatabaseError")))
+-		goto error;
+-	if (!(_mysql_DataError =
+-	      _mysql_NewException(dict, edict, "DataError")))
+-		goto error;
+-	if (!(_mysql_OperationalError =
+-	      _mysql_NewException(dict, edict, "OperationalError")))
+-		goto error;
+-	if (!(_mysql_IntegrityError =
+-	      _mysql_NewException(dict, edict, "IntegrityError")))
+-		goto error;
+-	if (!(_mysql_InternalError =
+-	      _mysql_NewException(dict, edict, "InternalError")))
+-		goto error;
+-	if (!(_mysql_ProgrammingError =
+-	      _mysql_NewException(dict, edict, "ProgrammingError")))
+-		goto error;
+-	if (!(_mysql_NotSupportedError =
+-	      _mysql_NewException(dict, edict, "NotSupportedError")))
+-		goto error;
+-	Py_DECREF(emod);
+-	if (!(_mysql_NULL = PyString_FromString("NULL")))
+-		goto error;
+-	if (PyDict_SetItemString(dict, "NULL", _mysql_NULL)) goto error;
+-  error:
+-	if (PyErr_Occurred())
+-		PyErr_SetString(PyExc_ImportError,
+-				"_mysql: init failed");
+-	return;
++	if (PyType_Ready(&_mysql_ConnectionObject_Type) < 0) return NULL;
++	if (PyType_Ready(&_mysql_ResultObject_Type) < 0) return NULL;
++
++	module = PyModule_Create2(&mysqlModule,PYTHON_API_VERSION);
++	
++	if (module == NULL)
++	{
++		return NULL;
++	};
++	
++	dict = PyModule_GetDict(module);
++	if (PyDict_SetItemString(dict, "version_info", PyRun_String(QUOTE(version_info), Py_eval_input, dict, dict)) == -1)
++	{
++	} else if (PyDict_SetItemString(dict, "__version__",
++			       PyUnicode_FromString(QUOTE(__version__))) == -1 )
++	{
++	} else if (PyDict_SetItemString(dict, "connection",
++			       (PyObject *)&_mysql_ConnectionObject_Type) == -1 )
++	{
++
++	} else
++	{
++		Py_INCREF(&_mysql_ConnectionObject_Type);
++		if ( PyDict_SetItemString(dict, "result",(PyObject *)&_mysql_ResultObject_Type) == -1 )
++		{
++
++		} else
++		{
++			Py_INCREF(&_mysql_ResultObject_Type);
++			emod = PyImport_ImportModule("_mysql_exceptions");
++			if (emod != NULL )
++			{
++				edict = PyModule_GetDict(emod);
++				_mysql_MySQLError = _mysql_NewException(dict, edict, "MySQLError");
++				if (_mysql_MySQLError != NULL)
++				{
++					_mysql_Warning = _mysql_NewException(dict, edict, "Warning");
++					_mysql_Error = _mysql_NewException(dict, edict, "Error");
++					_mysql_InterfaceError = _mysql_NewException(dict, edict, "InterfaceError");
++					_mysql_DatabaseError = _mysql_NewException(dict, edict, "DatabaseError");
++					_mysql_DataError = _mysql_NewException(dict, edict, "DataError");
++					_mysql_OperationalError = _mysql_NewException(dict, edict, "OperationalError");
++					_mysql_IntegrityError = _mysql_NewException(dict, edict, "IntegrityError");
++					_mysql_InternalError = _mysql_NewException(dict, edict, "InternalError");
++					_mysql_ProgrammingError = _mysql_NewException(dict, edict, "ProgrammingError");
++					_mysql_NotSupportedError = _mysql_NewException(dict, edict, "NotSupportedError");
++					if ( _mysql_Warning != NULL && _mysql_Error != NULL && _mysql_InterfaceError != NULL &&
++						 _mysql_DatabaseError != NULL && _mysql_DataError != NULL && _mysql_OperationalError != NULL &&
++						 _mysql_IntegrityError != NULL && _mysql_InternalError != NULL &&
++						 _mysql_ProgrammingError != NULL && _mysql_NotSupportedError != NULL
++					   )
++					{
++						Py_DECREF(emod);
++					    _mysql_NULL = PyUnicode_FromString("NULL");
++					    PyDict_SetItemString(dict, "NULL", _mysql_NULL);
++					};
++				};
++			};
++		};
++	};
++
++	if ( CheckError() == -1 )
++	{
++		return NULL;
++	};
++
++	return module;
+ }
+ 
+ 
+diff -ru MySQL-python-1.2.3/MySQLdb/connections.py MySQL-Python-1.2.3-python3/MySQLdb/connections.py
+--- MySQL-python-1.2.3/MySQLdb/connections.py	2010-06-17 09:21:56.000000000 +0200
++++ MySQL-Python-1.2.3-python3/MySQLdb/connections.py	2011-11-25 16:44:27.000000000 +0100
+@@ -6,7 +6,7 @@
+ override Connection.default_cursor with a non-standard Cursor class.
+ 
+ """
+-import cursors
++import MySQLdb.cursors
+ from _mysql_exceptions import Warning, Error, InterfaceError, DataError, \
+      DatabaseError, OperationalError, IntegrityError, InternalError, \
+      NotSupportedError, ProgrammingError
+@@ -33,7 +33,7 @@
+         connection.messages.append(error)
+     del cursor
+     del connection
+-    raise errorclass, errorvalue
++    raise errorclass(errorvalue)
+ 
+ re_numeric_part = re.compile(r"^(\d+)")
+ 
+@@ -57,7 +57,7 @@
+ 
+     """MySQL Database Connection Object"""
+ 
+-    default_cursor = cursors.Cursor
++    default_cursor = MySQLdb.cursors.Cursor
+     
+     def __init__(self, *args, **kwargs):
+         """
+@@ -109,17 +109,9 @@
+         cursorclass
+           class object, used to create cursors (keyword only)
+ 
+-        use_unicode
+-          If True, text-like columns are returned as unicode objects
+-          using the connection's character set.  Otherwise, text-like
+-          columns are returned as strings.  columns are returned as
+-          normal strings. Unicode objects will always be encoded to
+-          the connection's character set regardless of this setting.
+-
+         charset
+           If supplied, the connection character set will be changed
+-          to this character set (MySQL-4.1 and newer). This implies
+-          use_unicode=True.
++          to this character set (MySQL-4.1 and newer).
+ 
+         sql_mode
+           If supplied, the session SQL mode will be changed to this
+@@ -143,15 +135,15 @@
+         documentation for the MySQL C API for some hints on what they do.
+ 
+         """
+-        from constants import CLIENT, FIELD_TYPE
+-        from converters import conversions
++        from .constants import CLIENT, FIELD_TYPE
++        from .converters import conversions
+         from weakref import proxy, WeakValueDictionary
+         
+         import types
+ 
+         kwargs2 = kwargs.copy()
+         
+-        if kwargs.has_key('conv'):
++        if 'conv' in kwargs:
+             conv = kwargs['conv']
+         else:
+             conv = conversions
+@@ -165,14 +157,8 @@
+         kwargs2['conv'] = conv2
+ 
+         self.cursorclass = kwargs2.pop('cursorclass', self.default_cursor)
+-        charset = kwargs2.pop('charset', '')
++        charset = kwargs2.pop('charset', 'utf8')
+ 
+-        if charset:
+-            use_unicode = True
+-        else:
+-            use_unicode = False
+-            
+-        use_unicode = kwargs2.pop('use_unicode', use_unicode)
+         sql_mode = kwargs2.pop('sql_mode', '')
+ 
+         client_flag = kwargs.get('client_flag', 0)
+@@ -197,19 +183,13 @@
+                 return db.string_literal(obj)
+             return string_literal
+ 
+-        def _get_unicode_literal():
+-            def unicode_literal(u, dummy=None):
+-                return db.literal(u.encode(unicode_literal.charset))
+-            return unicode_literal
+-
+-        def _get_string_decoder():
+-            def string_decoder(s):
+-                return s.decode(string_decoder.charset)
+-            return string_decoder
+-        
++        def _get_bytes_literal():
++            def bytes_literal(u, dummy=None):
++                return db.literal(u.decode(bytes_literal.charset))
++            return bytes_literal
++       
+         string_literal = _get_string_literal()
+-        self.unicode_literal = unicode_literal = _get_unicode_literal()
+-        self.string_decoder = string_decoder = _get_string_decoder()
++        self.bytes_literal = bytes_literal = _get_bytes_literal()
+         if not charset:
+             charset = self.character_set_name()
+         self.set_character_set(charset)
+@@ -217,14 +197,9 @@
+         if sql_mode:
+             self.set_sql_mode(sql_mode)
+ 
+-        if use_unicode:
+-            self.converter[FIELD_TYPE.STRING].append((None, string_decoder))
+-            self.converter[FIELD_TYPE.VAR_STRING].append((None, string_decoder))
+-            self.converter[FIELD_TYPE.VARCHAR].append((None, string_decoder))
+-            self.converter[FIELD_TYPE.BLOB].append((None, string_decoder))
+-
+-        self.encoders[types.StringType] = string_literal
+-        self.encoders[types.UnicodeType] = unicode_literal
++        self.encoders[str] = string_literal
++        self.encoders[bytes] = bytes_literal
++        
+         self._transactional = self.server_capabilities & CLIENT.TRANSACTIONS
+         if self._transactional:
+             # PEP-249 requires autocommit to be initially off
+@@ -297,8 +272,7 @@
+                     raise NotSupportedError("server is too old to set charset")
+                 self.query('SET NAMES %s' % charset)
+                 self.store_result()
+-        self.string_decoder.charset = charset
+-        self.unicode_literal.charset = charset
++        self.bytes_literal.charset = charset
+ 
+     def set_sql_mode(self, sql_mode):
+         """Set the connection sql_mode. See MySQL documentation for
+Pouze v MySQL-python-1.2.3/MySQLdb/constants: .cvsignore
+Pouze v MySQL-Python-1.2.3-python3/MySQLdb/constants: __pycache__
+diff -ru MySQL-python-1.2.3/MySQLdb/converters.py MySQL-Python-1.2.3-python3/MySQLdb/converters.py
+--- MySQL-python-1.2.3/MySQLdb/converters.py	2010-06-17 09:21:56.000000000 +0200
++++ MySQL-Python-1.2.3-python3/MySQLdb/converters.py	2011-10-05 12:49:58.000000000 +0200
+@@ -33,9 +33,9 @@
+ """
+ 
+ from _mysql import string_literal, escape_sequence, escape_dict, escape, NULL
+-from constants import FIELD_TYPE, FLAG
+-from times import *
+-import types
++from .constants import FIELD_TYPE, FLAG
++from .times import *
++import datetime
+ import array
+ 
+ try:
+@@ -55,16 +55,16 @@
+     """Convert something into a string via str()."""
+     return str(s)
+ 
+-def Unicode2Str(s, d):
+-    """Convert a unicode object to a string using the default encoding.
++def Bytes2Str(s, d):
++    """Convert a bytes object to a string using the default encoding.
+     This is only used as a placeholder for the real function, which
+     is connection-dependent."""
+-    return s.encode()
++    return s.decode()
+ 
+ Long2Int = Thing2Str
+ 
+ def Float2Str(o, d):
+-    return '%.15g' % o
++    return '{:f}'.format(o)
+ 
+ def None2NULL(o, d):
+     """Convert None to NULL."""
+@@ -115,30 +115,30 @@
+     return Thing2Literal(o.tostring(), d)
+ 
+ conversions = {
+-    types.IntType: Thing2Str,
+-    types.LongType: Long2Int,
+-    types.FloatType: Float2Str,
+-    types.NoneType: None2NULL,
+-    types.TupleType: escape_sequence,
+-    types.ListType: escape_sequence,
+-    types.DictType: escape_dict,
+-    types.InstanceType: Instance2Str,
++    int: Long2Int,
++    float: Float2Str,
++    type(None): None2NULL,
++    tuple: escape_sequence,
++    list: escape_sequence,
++    dict: escape_dict,
++    object: Instance2Str,
+     array.ArrayType: array2Str,
+-    types.StringType: Thing2Literal, # default
+-    types.UnicodeType: Unicode2Str,
+-    types.ObjectType: Instance2Str,
+-    types.BooleanType: Bool2Str,
+-    DateTimeType: DateTime2literal,
+-    DateTimeDeltaType: DateTimeDelta2literal,
++    str: Thing2Literal, # default
++    bytes: Bytes2Str,
++    bool: Bool2Str,
++    datetime.date: DateTime2literal,
++    datetime.time: DateTime2literal,
++    datetime.datetime: DateTime2literal,
++    datetime.timedelta: DateTimeDelta2literal,
+     set: Set2Str,
+     FIELD_TYPE.TINY: int,
+     FIELD_TYPE.SHORT: int,
+-    FIELD_TYPE.LONG: long,
++    FIELD_TYPE.LONG: int,
+     FIELD_TYPE.FLOAT: float,
+     FIELD_TYPE.DOUBLE: float,
+     FIELD_TYPE.DECIMAL: float,
+     FIELD_TYPE.NEWDECIMAL: float,
+-    FIELD_TYPE.LONGLONG: long,
++    FIELD_TYPE.LONGLONG: int,
+     FIELD_TYPE.INT24: int,
+     FIELD_TYPE.YEAR: int,
+     FIELD_TYPE.SET: Str2Set,
+diff -ru MySQL-python-1.2.3/MySQLdb/cursors.py MySQL-Python-1.2.3-python3/MySQLdb/cursors.py
+--- MySQL-python-1.2.3/MySQLdb/cursors.py	2010-06-17 09:21:56.000000000 +0200
++++ MySQL-Python-1.2.3-python3/MySQLdb/cursors.py	2011-11-25 16:53:26.000000000 +0100
+@@ -7,7 +7,6 @@
+ 
+ import re
+ import sys
+-from types import ListType, TupleType, UnicodeType
+ 
+ 
+ restr = (r"\svalues\s*"
+@@ -153,13 +152,19 @@
+         del self.messages[:]
+         db = self._get_db()
+         charset = db.character_set_name()
+-        if isinstance(query, unicode):
+-            query = query.encode(charset)
++
+         if args is not None:
++            if isinstance(query, bytes):
++                query = query.decode();
++
+             query = query % db.literal(args)
++
++        if isinstance(query, str):
++            query = query.encode(charset);
++
+         try:
+             r = self._query(query)
+-        except TypeError, m:
++        except TypeError as m:
+             if m.args[0] in ("not enough arguments for format string",
+                              "not all arguments converted"):
+                 self.messages.append((ProgrammingError, m.args[0]))
+@@ -197,8 +202,6 @@
+         del self.messages[:]
+         db = self._get_db()
+         if not args: return
+-        charset = db.character_set_name()
+-        if isinstance(query, unicode): query = query.encode(charset)
+         m = insert_values.search(query)
+         if not m:
+             r = 0
+@@ -210,7 +213,7 @@
+         qv = m.group(1)
+         try:
+             q = [ qv % db.literal(a) for a in args ]
+-        except TypeError, msg:
++        except TypeError as msg:
+             if msg.args[0] in ("not enough arguments for format string",
+                                "not all arguments converted"):
+                 self.errorhandler(self, ProgrammingError, msg.args[0])
+@@ -259,7 +262,7 @@
+         for index, arg in enumerate(args):
+             q = "SET @_%s_%d=%s" % (procname, index,
+                                          db.literal(arg))
+-            if isinstance(q, unicode):
++            if isinstance(q, str):
+                 q = q.encode(charset)
+             self._query(q)
+             self.nextset()
+@@ -267,7 +270,7 @@
+         q = "CALL %s(%s)" % (procname,
+                              ','.join(['@_%s_%d' % (procname, i)
+                                        for i in range(len(args))]))
+-        if type(q) is UnicodeType:
++        if type(q) is str:
+             q = q.encode(charset)
+         self._query(q)
+         self._executed = q
+@@ -363,7 +366,7 @@
+             r = value
+         else:
+             self.errorhandler(self, ProgrammingError,
+-                              "unknown scroll mode %s" % `mode`)
++                              "unknown scroll mode %r" % mode)
+         if r < 0 or r >= len(self._rows):
+             self.errorhandler(self, IndexError, "out of range")
+         self.rownumber = r
+Pouze v MySQL-python-1.2.3/MySQLdb: .cvsignore
+diff -ru MySQL-python-1.2.3/MySQLdb/__init__.py MySQL-Python-1.2.3-python3/MySQLdb/__init__.py
+--- MySQL-python-1.2.3/MySQLdb/__init__.py	2010-06-17 09:21:56.000000000 +0200
++++ MySQL-Python-1.2.3-python3/MySQLdb/__init__.py	2011-10-05 12:49:58.000000000 +0200
+@@ -14,7 +14,7 @@
+ """
+ 
+ __revision__ = """$Revision: 603 $"""[11:-2]
+-from release import __version__, version_info, __author__
++from .release import __version__, version_info, __author__
+ 
+ import _mysql
+ 
+@@ -77,7 +77,7 @@
+ 
+ def Connect(*args, **kwargs):
+     """Factory function for connections.Connection."""
+-    from connections import Connection
++    from .connections import Connection
+     return Connection(*args, **kwargs)
+ 
+ connect = Connection = Connect
+diff -ru MySQL-python-1.2.3/_mysql_exceptions.py MySQL-Python-1.2.3-python3/_mysql_exceptions.py
+--- MySQL-python-1.2.3/_mysql_exceptions.py	2010-06-17 09:21:56.000000000 +0200
++++ MySQL-Python-1.2.3-python3/_mysql_exceptions.py	2011-10-05 12:49:58.000000000 +0200
+@@ -5,9 +5,8 @@
+     http://www.python.org/topics/database/DatabaseAPI-2.0.html
+ """
+ 
+-from exceptions import Exception, StandardError, Warning
+ 
+-class MySQLError(StandardError):
++class MySQLError(Exception):
+     
+     """Exception related to operation with MySQL."""
+ 
+@@ -80,4 +79,3 @@
+     has transactions turned off."""
+ 
+ 
+-del Exception, StandardError
+diff -ru MySQL-python-1.2.3/MySQL_python.egg-info/SOURCES.txt MySQL-Python-1.2.3-python3/MySQL_python.egg-info/SOURCES.txt
+--- MySQL-python-1.2.3/MySQL_python.egg-info/SOURCES.txt	2010-06-17 09:22:07.000000000 +0200
++++ MySQL-Python-1.2.3-python3/MySQL_python.egg-info/SOURCES.txt	2011-11-25 16:54:24.000000000 +0100
+@@ -1,4 +1,3 @@
+-.cvsignore
+ HISTORY
+ MANIFEST.in
+ README
+@@ -17,14 +16,12 @@
+ MySQL_python.egg-info/SOURCES.txt
+ MySQL_python.egg-info/dependency_links.txt
+ MySQL_python.egg-info/top_level.txt
+-MySQLdb/.cvsignore
+ MySQLdb/__init__.py
+ MySQLdb/connections.py
+ MySQLdb/converters.py
+ MySQLdb/cursors.py
+ MySQLdb/release.py
+ MySQLdb/times.py
+-MySQLdb/constants/.cvsignore
+ MySQLdb/constants/CLIENT.py
+ MySQLdb/constants/CR.py
+ MySQLdb/constants/ER.py
+@@ -32,7 +29,6 @@
+ MySQLdb/constants/FLAG.py
+ MySQLdb/constants/REFRESH.py
+ MySQLdb/constants/__init__.py
+-doc/.cvsignore
+ doc/FAQ.txt
+ doc/MySQLdb.txt
+ tests/capabilities.py
+diff -ru MySQL-python-1.2.3/README MySQL-Python-1.2.3-python3/README
+--- MySQL-python-1.2.3/README	2010-06-17 09:21:56.000000000 +0200
++++ MySQL-Python-1.2.3-python3/README	2011-10-05 12:49:58.000000000 +0200
+@@ -8,22 +8,18 @@
+ Prerequisites
+ -------------
+ 
+-+ Python 2.3.4 or higher
+++ Python 3.2 or higher
+ 
+   * http://www.python.org/
+ 
+-  * Versions lower than 2.3 WON'T WORK.
+-
+-  * 2.4 is the primary test environment.
+-
+   * Red Hat Linux:
+ 
+     - Make sure you have the Python development headers and libraries
+       (python-devel).
+ 
+-+ setuptools
+++ distribute
+ 
+-  * http://pypi.python.org/pypi/setuptools
++  * http://pypi.python.org/pypi/distribute/
+ 
+ + MySQL 3.23.32 or higher
+ 
+diff -ru MySQL-python-1.2.3/setup_common.py MySQL-Python-1.2.3-python3/setup_common.py
+--- MySQL-python-1.2.3/setup_common.py	2010-06-17 09:21:56.000000000 +0200
++++ MySQL-Python-1.2.3-python3/setup_common.py	2011-10-05 12:49:58.000000000 +0200
+@@ -1,4 +1,4 @@
+-from ConfigParser import SafeConfigParser
++from configparser import SafeConfigParser
+ 
+ def get_metadata_and_options():
+     config = SafeConfigParser()
+@@ -7,8 +7,8 @@
+     metadata = dict(config.items('metadata'))
+     options = dict(config.items('options'))
+ 
+-    metadata['py_modules'] = filter(None, metadata['py_modules'].split('\n'))
+-    metadata['classifiers'] = filter(None, metadata['classifiers'].split('\n'))
++    metadata['py_modules'] = list(filter(None, metadata['py_modules'].split('\n')))
++    metadata['classifiers'] = list(filter(None, metadata['classifiers'].split('\n')))
+ 
+     return metadata, options
+ 
+@@ -30,3 +30,4 @@
+ __version__ = "%(version)s"
+ """ % metadata)
+     rel.close()
++
+diff -ru MySQL-python-1.2.3/setup_posix.py MySQL-Python-1.2.3-python3/setup_posix.py
+--- MySQL-python-1.2.3/setup_posix.py	2010-06-17 09:21:56.000000000 +0200
++++ MySQL-Python-1.2.3-python3/setup_posix.py	2011-11-25 17:13:11.000000000 +0100
+@@ -1,4 +1,4 @@
+-from ConfigParser import SafeConfigParser
++from configparser import SafeConfigParser
+ 
+ # This dequote() business is required for some older versions
+ # of mysql_config
+@@ -98,5 +98,5 @@
+     return metadata, ext_options
+ 
+ if __name__ == "__main__":
+-    print """You shouldn't be running this directly; it is used by setup.py."""
++    print("""You shouldn't be running this directly; it is used by setup.py.""")
+ 
+diff -ru MySQL-python-1.2.3/setup.py MySQL-Python-1.2.3-python3/setup.py
+--- MySQL-python-1.2.3/setup.py	2010-06-17 09:21:56.000000000 +0200
++++ MySQL-Python-1.2.3-python3/setup.py	2011-10-05 12:49:58.000000000 +0200
+@@ -4,8 +4,8 @@
+ import sys
+ from setuptools import setup, Extension
+ 
+-if not hasattr(sys, "hexversion") or sys.hexversion < 0x02030000:
+-    raise Error("Python 2.3 or newer is required")
++if not hasattr(sys, "hexversion") or sys.hexversion < 0x03000000:
++    raise Exception("Python 3.0 or newer is required")
+ 
+ if os.name == "posix":
+     from setup_posix import get_config
+diff -ru MySQL-python-1.2.3/setup_windows.py MySQL-Python-1.2.3-python3/setup_windows.py
+--- MySQL-python-1.2.3/setup_windows.py	2010-06-17 09:21:56.000000000 +0200
++++ MySQL-Python-1.2.3-python3/setup_windows.py	2011-10-05 12:49:58.000000000 +0200
+@@ -1,11 +1,11 @@
+ def get_config():
+-    import os, sys, _winreg
++    import os, sys, winreg
+     from setup_common import get_metadata_and_options, enabled, create_release_file
+ 
+     metadata, options = get_metadata_and_options()
+ 
+-    serverKey = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, options['registry_key'])
+-    mysql_root, dummy = _winreg.QueryValueEx(serverKey,'Location')
++    serverKey = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, options['registry_key'])
++    mysql_root, dummy = winreg.QueryValueEx(serverKey,options['location'])
+ 
+     extra_objects = []
+     static = enabled(options, 'static')
+@@ -15,7 +15,7 @@
+     else:
+         client = "mysqlclient"
+ 
+-    library_dirs = [ os.path.join(mysql_root, r'lib\opt') ]
++    library_dirs = [ os.path.join(mysql_root, r'lib') ]
+     libraries = [ 'kernel32', 'advapi32', 'wsock32', client ]
+     include_dirs = [ os.path.join(mysql_root, r'include') ]
+     extra_compile_args = [ '/Zl' ]
+@@ -43,5 +43,5 @@
+     return metadata, ext_options
+ 
+ if __name__ == "__main__":
+-    print """You shouldn't be running this directly; it is used by setup.py."""
++    print ("""You shouldn't be running this directly; it is used by setup.py.""")
+     
+diff -ru MySQL-python-1.2.3/site.cfg MySQL-Python-1.2.3-python3/site.cfg
+--- MySQL-python-1.2.3/site.cfg	2010-06-17 09:21:56.000000000 +0200
++++ MySQL-Python-1.2.3-python3/site.cfg	2011-10-05 12:49:58.000000000 +0200
+@@ -15,4 +15,6 @@
+ # The Windows registry key for MySQL.
+ # This has to be set for Windows builds to work.
+ # Only change this if you have a different version.
+-registry_key = SOFTWARE\MySQL AB\MySQL Server 5.0
++registry_key = SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{2DB39FB6-161E-44E7-B2A1-B654C85EFBC1}
++location = InstallLocation
++
+diff -ru MySQL-python-1.2.3/tests/capabilities.py MySQL-Python-1.2.3-python3/tests/capabilities.py
+--- MySQL-python-1.2.3/tests/capabilities.py	2010-06-17 09:21:55.000000000 +0200
++++ MySQL-Python-1.2.3-python3/tests/capabilities.py	2011-10-05 12:49:58.000000000 +0200
+@@ -25,7 +25,7 @@
+         self.connection = db
+         self.cursor = db.cursor()
+         self.BLOBText = ''.join([chr(i) for i in range(256)] * 100);
+-        self.BLOBUText = u''.join([unichr(i) for i in range(16384)])
++        self.BLOBUText = ''.join([chr(i) for i in range(16384)])
+         self.BLOBBinary = self.db_module.Binary(''.join([chr(i) for i in range(256)] * 16))
+ 
+     leak_test = True
+@@ -35,11 +35,11 @@
+             import gc
+             del self.cursor
+             orphans = gc.collect()
+-            self.failIf(orphans, "%d orphaned objects found after deleting cursor" % orphans)
++            self.assertFalse(orphans, "%d orphaned objects found after deleting cursor" % orphans)
+             
+             del self.connection
+             orphans = gc.collect()
+-            self.failIf(orphans, "%d orphaned objects found after deleting connection" % orphans)
++            self.assertFalse(orphans, "%d orphaned objects found after deleting connection" % orphans)
+             
+     def table_exists(self, name):
+         try:
+@@ -81,18 +81,18 @@
+         self.create_table(columndefs)
+         insert_statement = ('INSERT INTO %s VALUES (%s)' % 
+                             (self.table,
+-                             ','.join(['%s'] * len(columndefs))))
++                             ','.join(['{!s}'] * len(columndefs))))
+         data = [ [ generator(i,j) for j in range(len(columndefs)) ]
+                  for i in range(self.rows) ]
+         if self.debug:
+-            print data
++            print(data)
+         self.cursor.executemany(insert_statement, data)
+         self.connection.commit()
+         # verify
+         self.cursor.execute('select * from %s' % self.table)
+         l = self.cursor.fetchall()
+         if self.debug:
+-            print l
++            print(l)
+         self.assertEquals(len(l), self.rows)
+         try:
+             for i in range(self.rows):
+@@ -110,7 +110,7 @@
+         self.create_table(columndefs)
+         insert_statement = ('INSERT INTO %s VALUES (%s)' % 
+                             (self.table,
+-                             ','.join(['%s'] * len(columndefs))))
++                             ','.join(['{!s}'] * len(columndefs))))
+         data = [ [ generator(i,j) for j in range(len(columndefs)) ]
+                  for i in range(self.rows) ]
+         self.cursor.executemany(insert_statement, data)
+@@ -122,33 +122,33 @@
+         for i in range(self.rows):
+             for j in range(len(columndefs)):
+                 self.assertEquals(l[i][j], generator(i,j))
+-        delete_statement = 'delete from %s where col1=%%s' % self.table
++        delete_statement = 'delete from %s where col1={!s}' % self.table
+         self.cursor.execute(delete_statement, (0,))
+         self.cursor.execute('select col1 from %s where col1=%s' % \
+                             (self.table, 0))
+         l = self.cursor.fetchall()
+-        self.failIf(l, "DELETE didn't work")
++        self.assertFalse(l, "DELETE didn't work")
+         self.connection.rollback()
+         self.cursor.execute('select col1 from %s where col1=%s' % \
+                             (self.table, 0))
+         l = self.cursor.fetchall()
+-        self.failUnless(len(l) == 1, "ROLLBACK didn't work")
++        self.assertTrue(len(l) == 1, "ROLLBACK didn't work")
+         self.cursor.execute('drop table %s' % (self.table))
+ 
+     def test_truncation(self):
+         columndefs = ( 'col1 INT', 'col2 VARCHAR(255)')
+         def generator(row, col):
+             if col == 0: return row
+-            else: return ('%i' % (row%10))*((255-self.rows/2)+row)
++            else: return ('{:d}'.format(row%10))*(round(255-self.rows/2)+row)
+         self.create_table(columndefs)
+         insert_statement = ('INSERT INTO %s VALUES (%s)' % 
+                             (self.table,
+-                             ','.join(['%s'] * len(columndefs))))
++                             ','.join(['{!s}'] * len(columndefs))))
+ 
+         try:
+             self.cursor.execute(insert_statement, (0, '0'*256))
+         except Warning:
+-            if self.debug: print self.cursor.messages
++            if self.debug: print(self.cursor.messages)
+         except self.connection.DataError:
+             pass
+         else:
+@@ -163,7 +163,7 @@
+                     data.append(generator(i,j))
+                 self.cursor.execute(insert_statement,tuple(data))
+         except Warning:
+-            if self.debug: print self.cursor.messages
++            if self.debug: print(self.cursor.messages)
+         except self.connection.DataError:
+             pass
+         else:
+@@ -176,7 +176,7 @@
+                      for i in range(self.rows) ]
+             self.cursor.executemany(insert_statement, data)
+         except Warning:
+-            if self.debug: print self.cursor.messages
++            if self.debug: print(self.cursor.messages)
+         except self.connection.DataError:
+             pass
+         else:
+diff -ru MySQL-python-1.2.3/tests/dbapi20.py MySQL-Python-1.2.3-python3/tests/dbapi20.py
+--- MySQL-python-1.2.3/tests/dbapi20.py	2010-06-17 09:21:55.000000000 +0200
++++ MySQL-Python-1.2.3-python3/tests/dbapi20.py	2011-10-05 12:49:58.000000000 +0200
+@@ -177,8 +177,8 @@
+     def test_Exceptions(self):
+         # Make sure required exceptions exist, and are in the
+         # defined heirarchy.
+-        self.failUnless(issubclass(self.driver.Warning,StandardError))
+-        self.failUnless(issubclass(self.driver.Error,StandardError))
++        self.failUnless(issubclass(self.driver.Warning,Exception))
++        self.failUnless(issubclass(self.driver.Error,Exception))
+         self.failUnless(
+             issubclass(self.driver.InterfaceError,self.driver.Error)
+             )
+@@ -382,27 +382,27 @@
+ 
+         if self.driver.paramstyle == 'qmark':
+             cur.execute(
+-                'insert into %sbooze values (?)' % self.table_prefix,
++                'insert into %sbooze values ({!s})' % self.table_prefix,
+                 ("Cooper's",)
+                 )
+         elif self.driver.paramstyle == 'numeric':
+             cur.execute(
+-                'insert into %sbooze values (:1)' % self.table_prefix,
++                'insert into %sbooze values ({:1})' % self.table_prefix,
+                 ("Cooper's",)
+                 )
+         elif self.driver.paramstyle == 'named':
+             cur.execute(
+-                'insert into %sbooze values (:beer)' % self.table_prefix, 
++                'insert into %sbooze values ({beer})' % self.table_prefix, 
+                 {'beer':"Cooper's"}
+                 )
+         elif self.driver.paramstyle == 'format':
+             cur.execute(
+-                'insert into %sbooze values (%%s)' % self.table_prefix,
++                'insert into %sbooze values ({!s})' % self.table_prefix,
+                 ("Cooper's",)
+                 )
+         elif self.driver.paramstyle == 'pyformat':
+             cur.execute(
+-                'insert into %sbooze values (%%(beer)s)' % self.table_prefix,
++                'insert into %sbooze values ({beer})' % self.table_prefix,
+                 {'beer':"Cooper's"}
+                 )
+         else:
+@@ -432,27 +432,27 @@
+             margs = [ {'beer': "Cooper's"}, {'beer': "Boag's"} ]
+             if self.driver.paramstyle == 'qmark':
+                 cur.executemany(
+-                    'insert into %sbooze values (?)' % self.table_prefix,
++                    'insert into %sbooze values ({!s})' % self.table_prefix,
+                     largs
+                     )
+             elif self.driver.paramstyle == 'numeric':
+                 cur.executemany(
+-                    'insert into %sbooze values (:1)' % self.table_prefix,
++                    'insert into %sbooze values ({:1})' % self.table_prefix,
+                     largs
+                     )
+             elif self.driver.paramstyle == 'named':
+                 cur.executemany(
+-                    'insert into %sbooze values (:beer)' % self.table_prefix,
++                    'insert into %sbooze values ({beer})' % self.table_prefix,
+                     margs
+                     )
+             elif self.driver.paramstyle == 'format':
+                 cur.executemany(
+-                    'insert into %sbooze values (%%s)' % self.table_prefix,
++                    'insert into %sbooze values ({!s})' % self.table_prefix,
+                     largs
+                     )
+             elif self.driver.paramstyle == 'pyformat':
+                 cur.executemany(
+-                    'insert into %sbooze values (%%(beer)s)' % (
++                    'insert into %sbooze values ({beer})' % (
+                         self.table_prefix
+                         ),
+                     margs
+@@ -706,7 +706,7 @@
+             that returns two result sets, first the 
+ 	    number of rows in booze then "name from booze"
+         '''
+-        raise NotImplementedError,'Helper not implemented'
++        raise NotImplementedError('Helper not implemented')
+         #sql="""
+         #    create procedure deleteme as
+         #    begin
+@@ -718,7 +718,7 @@
+ 
+     def help_nextset_tearDown(self,cur):
+         'If cleaning up is needed after nextSetTest'
+-        raise NotImplementedError,'Helper not implemented'
++        raise NotImplementedError('Helper not implemented')
+         #cur.execute("drop procedure deleteme")
+ 
+     def test_nextset(self):
+@@ -751,7 +751,7 @@
+             con.close()
+ 
+     def test_nextset(self):
+-        raise NotImplementedError,'Drivers need to override this test'
++        raise NotImplementedError('Drivers need to override this test')
+ 
+     def test_arraysize(self):
+         # Not much here - rest of the tests for this are in test_fetchmany
+@@ -786,7 +786,7 @@
+ 
+     def test_setoutputsize(self):
+         # Real test for setoutputsize is driver dependant
+-        raise NotImplementedError,'Driver need to override this test'
++        raise NotImplementedError('Driver need to override this test')
+ 
+     def test_None(self):
+         con = self._connect()
+diff -ru MySQL-python-1.2.3/tests/test_MySQLdb_capabilities.py MySQL-Python-1.2.3-python3/tests/test_MySQLdb_capabilities.py
+--- MySQL-python-1.2.3/tests/test_MySQLdb_capabilities.py	2010-06-17 09:21:55.000000000 +0200
++++ MySQL-Python-1.2.3-python3/tests/test_MySQLdb_capabilities.py	2011-10-05 12:49:58.000000000 +0200
+@@ -41,8 +41,7 @@
+         db = self.connection
+         c = self.cursor
+         self.create_table(('pos INT', 'tree CHAR(20)'))
+-        c.executemany("INSERT INTO %s (pos,tree) VALUES (%%s,%%s)" % self.table,
+-                      list(enumerate('ash birch cedar larch pine'.split())))
++        c.executemany("INSERT INTO %s (pos,tree) VALUES ({!s},{!s})" % self.table, list(enumerate('ash birch cedar larch pine'.split())) )
+         db.commit()
+         
+         c.execute("""
+@@ -77,9 +76,9 @@
+         from MySQLdb.constants import ER
+         try:
+             self.cursor.execute("describe some_non_existent_table");
+-        except self.connection.ProgrammingError, msg:
+-            self.failUnless(msg[0] == ER.NO_SUCH_TABLE)
+-    
++        except self.connection.ProgrammingError as msg:
++            self.assertTrue(msg.code == ER.NO_SUCH_TABLE)
++        
+     def test_ping(self):
+         self.connection.ping()
+         
+@@ -90,4 +89,4 @@
+         gc.enable()
+         gc.set_debug(gc.DEBUG_LEAK)
+     unittest.main()
+-    print '''"Huh-huh, he said 'unit'." -- Butthead'''
++    print ('''"Huh-huh, he said 'unit'." -- Butthead''')
+diff -ru MySQL-python-1.2.3/tests/test_MySQLdb_dbapi20.py MySQL-Python-1.2.3-python3/tests/test_MySQLdb_dbapi20.py
+--- MySQL-python-1.2.3/tests/test_MySQLdb_dbapi20.py	2010-06-17 09:21:55.000000000 +0200
++++ MySQL-Python-1.2.3-python3/tests/test_MySQLdb_dbapi20.py	2011-10-05 12:49:58.000000000 +0200
+@@ -202,4 +202,4 @@
+     
+ if __name__ == '__main__':
+     unittest.main()
+-    print '''"Huh-huh, he said 'unit'." -- Butthead'''
++    print ('''"Huh-huh, he said 'unit'." -- Butthead''')
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extras/test-db.sql	Fri Nov 25 18:17:27 2011 +0100
@@ -0,0 +1,6 @@
+CREATE TABLE test
+(
+  id serial NOT NULL,
+  name varchar,
+  PRIMARY KEY (id)
+);
--- a/mytoolkit/mymanager.py	Mon Nov 21 10:47:24 2011 +0100
+++ b/mytoolkit/mymanager.py	Fri Nov 25 18:17:27 2011 +0100
@@ -67,13 +67,12 @@
 
 The row returned by fetchone_dict() is special dict object, which can be accessed
 using item or attribute access, that is row['now'] or row.now.
+
 """
 
 from contextlib import contextmanager
 import logging
 import threading
-import select
-import socket
 
 import MySQLdb
 import MySQLdb.cursors
@@ -199,7 +198,10 @@
             conn = None
             while len(self.conn_pool[name]) and conn is None:
                 conn = self.conn_pool[name].pop()
-                if conn.closed:
+                try:
+                    conn.ping()
+                except MySQLdb.MySQLError:
+                    conn.close()
                     conn = None
 
             if conn is None:
@@ -226,7 +228,11 @@
                 return
 
             # connection returned to the pool must not be in transaction
-            conn.rollback()
+            try:
+                conn.rollback()
+            except OperationalError:
+                conn.close()
+                return
 
             self.conn_pool[name].append(conn)
         finally:
@@ -253,7 +259,7 @@
         conn = MySQLdb.connect(cursorclass=Cursor, **ci.parameters)
         if not ci.isolation_level is None:
             if ci.isolation_level == 'AUTOCOMMIT':
-                conn.autocommit()
+                conn.autocommit(True)
             else:
                 curs = conn.cursor()
                 curs.execute('SET SESSION TRANSACTION ISOLATION LEVEL ' + ci.isolation_level)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pgtoolkit/delayedquery.py	Fri Nov 25 18:17:27 2011 +0100
@@ -0,0 +1,46 @@
+import threading
+import time
+
+
+class DelayedQueryThread(threading.Thread):
+    def __init__(self, targetdbm, targetname, delay, query, args):
+        threading.Thread.__init__(self)
+        self.targetdbm = targetdbm
+        self.targetname = targetname
+        self.delay = delay
+        self.query = query
+        self.args = args
+        
+    def run(self):
+        time.sleep(self.delay)
+        with self.targetdbm.cursor(self.targetname) as curs:
+            curs.execute(self.query, self.args)
+
+
+class DelayedQuery:
+    def __init__(self, targetdbm):
+        '''Initialize DelayedQuery.
+        
+        targetdbm -- PgManager-like object
+        
+        '''
+        self.targetdbm = targetdbm
+        self.queryids = set()
+
+    def add(self, delay, query, args, targetname='default', queryid=None):
+        '''Add query to schedule.
+        
+        delay -- how long to wait, in seconds
+        query, args -- query to be run after delay
+        targetname -- name of connection in PgManager
+        queryid -- discard if query with same id is already scheduled
+        
+        '''
+        if queryid is not None:
+            if queryid in self.queryids:
+                return
+            self.queryids.add(queryid)
+        
+        t = DelayedQueryThread(self.targetdbm, targetname, delay, query, args)
+        t.start()
+