Merge p3yk branch with the trunk up to revision 45595. This breaks a fair
number of tests, all because of the codecs/_multibytecodecs issue described
here (it's not a Py3K issue, just something Py3K discovers):
http://mail.python.org/pipermail/python-dev/2006-April/064051.html

Hye-Shik Chang promised to look for a fix, so no need to fix it here. The
tests that are expected to break are:

test_codecencodings_cn
test_codecencodings_hk
test_codecencodings_jp
test_codecencodings_kr
test_codecencodings_tw
test_codecs
test_multibytecodec

This merge fixes an actual test failure (test_weakref) in this branch,
though, so I believe merging is the right thing to do anyway.
diff --git a/Modules/_sqlite/adapters.c b/Modules/_sqlite/adapters.c
new file mode 100644
index 0000000..e6fde03
--- /dev/null
+++ b/Modules/_sqlite/adapters.c
@@ -0,0 +1,40 @@
+/* adapters.c - default adapters
+ *
+ * Copyright (C) 2005 Gerhard Häring <gh@ghaering.de>
+ *
+ * This file is part of pysqlite.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * 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. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 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 or altered from any source distribution.
+ */
+
+#include "util.h"
+#include "module.h"
+#include "adapters.h"
+
+/* dummy, will be implemented in a later version */
+
+PyObject* adapt_date(PyObject* self, PyObject* args, PyObject* kwargs)
+{
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+PyObject* adapt_datetime(PyObject* self, PyObject* args, PyObject* kwargs)
+{
+    Py_INCREF(Py_None);
+    return Py_None;
+}
diff --git a/Modules/_sqlite/adapters.h b/Modules/_sqlite/adapters.h
new file mode 100644
index 0000000..d2e8479
--- /dev/null
+++ b/Modules/_sqlite/adapters.h
@@ -0,0 +1,33 @@
+/* adapters.h - default adapters
+ *
+ * Copyright (C) 2005 Gerhard Häring <gh@ghaering.de>
+ *
+ * This file is part of pysqlite.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * 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. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 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 or altered from any source distribution.
+ */
+
+#ifndef PYSQLITE_ADAPTERS_H
+#define PYSQLITE_ADAPTERS_H
+#include "Python.h"
+#include "pythread.h"
+#include "sqlite3.h"
+
+PyObject* adapt_date(PyObject* self, PyObject* args, PyObject* kwargs);
+PyObject* adapt_datetime(PyObject* self, PyObject* args, PyObject* kwargs);
+
+#endif
diff --git a/Modules/_sqlite/cache.c b/Modules/_sqlite/cache.c
new file mode 100644
index 0000000..d102e97
--- /dev/null
+++ b/Modules/_sqlite/cache.c
@@ -0,0 +1,362 @@
+/* cache .c - a LRU cache
+ *
+ * Copyright (C) 2004-2006 Gerhard Häring <gh@ghaering.de>
+ *
+ * This file is part of pysqlite.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * 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. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 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 or altered from any source distribution.
+ */
+
+#include "cache.h"
+
+/* only used internally */
+Node* new_node(PyObject* key, PyObject* data)
+{
+    Node* node;
+
+    node = (Node*) (NodeType.tp_alloc(&NodeType, 0));
+    if (!node) {
+        return NULL;
+    }
+
+    Py_INCREF(key);
+    node->key = key;
+
+    Py_INCREF(data);
+    node->data = data;
+
+    node->prev = NULL;
+    node->next = NULL;
+
+    return node;
+}
+
+void node_dealloc(Node* self)
+{
+    Py_DECREF(self->key);
+    Py_DECREF(self->data);
+
+    self->ob_type->tp_free((PyObject*)self);
+}
+
+int cache_init(Cache* self, PyObject* args, PyObject* kwargs)
+{
+    PyObject* factory;
+    int size = 10;
+
+    self->factory = NULL;
+
+    if (!PyArg_ParseTuple(args, "O|i", &factory, &size))
+    {
+        return -1; 
+    }
+
+    if (size < 5) {
+        size = 5;
+    }
+    self->size = size;
+    self->first = NULL;
+    self->last = NULL;
+
+    self->mapping = PyDict_New();
+    if (!self->mapping) {
+        return -1;
+    }
+
+    Py_INCREF(factory);
+    self->factory = factory;
+
+    self->decref_factory = 1;
+
+    return 0;
+}
+
+void cache_dealloc(Cache* self)
+{
+    Node* node;
+    Node* delete_node;
+
+    if (!self->factory) {
+        /* constructor failed, just get out of here */
+        return;
+    }
+
+    node = self->first;
+    while (node) {
+        delete_node = node;
+        node = node->next;
+        Py_DECREF(delete_node);
+    }
+
+    if (self->decref_factory) {
+        Py_DECREF(self->factory);
+    }
+    Py_DECREF(self->mapping);
+
+    self->ob_type->tp_free((PyObject*)self);
+}
+
+PyObject* cache_get(Cache* self, PyObject* args)
+{
+    PyObject* key = args;
+    Node* node;
+    Node* ptr;
+    PyObject* data;
+
+    node = (Node*)PyDict_GetItem(self->mapping, key);
+    if (node) {
+        node->count++;
+        if (node->prev && node->count > node->prev->count) {
+            ptr = node->prev;
+
+            while (ptr->prev && node->count > ptr->prev->count) {
+                ptr = ptr->prev;
+            }
+
+            if (node->next) {
+                node->next->prev = node->prev;
+            } else {
+                self->last = node->prev;
+            }
+            if (node->prev) {
+                node->prev->next = node->next;
+            }
+            if (ptr->prev) {
+                ptr->prev->next = node;
+            } else {
+                self->first = node;
+            }
+
+            node->next = ptr;
+            node->prev = ptr->prev;
+            if (!node->prev) {
+                self->first = node;
+            }
+            ptr->prev = node;
+        }
+    } else {
+        if (PyDict_Size(self->mapping) == self->size) {
+            if (self->last) {
+                node = self->last;
+
+                if (PyDict_DelItem(self->mapping, self->last->key) != 0) {
+                    return NULL;
+                }
+
+                if (node->prev) {
+                    node->prev->next = NULL;
+                }
+                self->last = node->prev;
+                node->prev = NULL;
+
+                Py_DECREF(node);
+            }
+        }
+
+        data = PyObject_CallFunction(self->factory, "O", key);
+
+        if (!data) {
+            return NULL;
+        }
+
+        node = new_node(key, data);
+        if (!node) {
+            return NULL;
+        }
+        node->prev = self->last;
+
+        Py_DECREF(data);
+
+        if (PyDict_SetItem(self->mapping, key, (PyObject*)node) != 0) {
+            Py_DECREF(node);
+            return NULL;
+        }
+
+        if (self->last) {
+            self->last->next = node;
+        } else {
+            self->first = node;
+        }
+        self->last = node;
+    }
+
+    Py_INCREF(node->data);
+    return node->data;
+}
+
+PyObject* cache_display(Cache* self, PyObject* args)
+{
+    Node* ptr;
+    PyObject* prevkey;
+    PyObject* nextkey;
+    PyObject* fmt_args;
+    PyObject* template;
+    PyObject* display_str;
+
+    ptr = self->first;
+
+    while (ptr) {
+        if (ptr->prev) {
+            prevkey = ptr->prev->key;
+        } else {
+            prevkey = Py_None;
+        }
+        Py_INCREF(prevkey);
+
+        if (ptr->next) {
+            nextkey = ptr->next->key;
+        } else {
+            nextkey = Py_None;
+        }
+        Py_INCREF(nextkey);
+
+        fmt_args = Py_BuildValue("OOO", prevkey, ptr->key, nextkey);
+        if (!fmt_args) {
+            return NULL;
+        }
+        template = PyString_FromString("%s <- %s ->%s\n");
+        if (!template) {
+            return NULL;
+        }
+        display_str = PyString_Format(template, fmt_args);
+        Py_DECREF(template);
+        Py_DECREF(fmt_args);
+        if (!display_str) {
+            return NULL;
+        }
+        PyObject_Print(display_str, stdout, Py_PRINT_RAW);
+        Py_DECREF(display_str);
+
+        Py_DECREF(prevkey);
+        Py_DECREF(nextkey);
+
+        ptr = ptr->next;
+    }
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyMethodDef cache_methods[] = {
+    {"get", (PyCFunction)cache_get, METH_O,
+        PyDoc_STR("Gets an entry from the cache.")},
+    {"display", (PyCFunction)cache_display, METH_NOARGS,
+        PyDoc_STR("For debugging only.")},
+    {NULL, NULL}
+};
+
+PyTypeObject NodeType = {
+        PyObject_HEAD_INIT(NULL)
+        0,                                              /* ob_size */
+        MODULE_NAME "Node",                             /* tp_name */
+        sizeof(Node),                                   /* tp_basicsize */
+        0,                                              /* tp_itemsize */
+        (destructor)node_dealloc,                       /* tp_dealloc */
+        0,                                              /* tp_print */
+        0,                                              /* tp_getattr */
+        0,                                              /* tp_setattr */
+        0,                                              /* tp_compare */
+        0,                                              /* tp_repr */
+        0,                                              /* tp_as_number */
+        0,                                              /* tp_as_sequence */
+        0,                                              /* tp_as_mapping */
+        0,                                              /* tp_hash */
+        0,                                              /* tp_call */
+        0,                                              /* tp_str */
+        0,                                              /* tp_getattro */
+        0,                                              /* tp_setattro */
+        0,                                              /* tp_as_buffer */
+        Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,         /* tp_flags */
+        0,                                              /* tp_doc */
+        0,                                              /* tp_traverse */
+        0,                                              /* tp_clear */
+        0,                                              /* tp_richcompare */
+        0,                                              /* tp_weaklistoffset */
+        0,                                              /* tp_iter */
+        0,                                              /* tp_iternext */
+        0,                                              /* tp_methods */
+        0,                                              /* tp_members */
+        0,                                              /* tp_getset */
+        0,                                              /* tp_base */
+        0,                                              /* tp_dict */
+        0,                                              /* tp_descr_get */
+        0,                                              /* tp_descr_set */
+        0,                                              /* tp_dictoffset */
+        (initproc)0,                                    /* tp_init */
+        0,                                              /* tp_alloc */
+        0,                                              /* tp_new */
+        0                                               /* tp_free */
+};
+
+PyTypeObject CacheType = {
+        PyObject_HEAD_INIT(NULL)
+        0,                                              /* ob_size */
+        MODULE_NAME ".Cache",                           /* tp_name */
+        sizeof(Cache),                                  /* tp_basicsize */
+        0,                                              /* tp_itemsize */
+        (destructor)cache_dealloc,                      /* tp_dealloc */
+        0,                                              /* tp_print */
+        0,                                              /* tp_getattr */
+        0,                                              /* tp_setattr */
+        0,                                              /* tp_compare */
+        0,                                              /* tp_repr */
+        0,                                              /* tp_as_number */
+        0,                                              /* tp_as_sequence */
+        0,                                              /* tp_as_mapping */
+        0,                                              /* tp_hash */
+        0,                                              /* tp_call */
+        0,                                              /* tp_str */
+        0,                                              /* tp_getattro */
+        0,                                              /* tp_setattro */
+        0,                                              /* tp_as_buffer */
+        Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,         /* tp_flags */
+        0,                                              /* tp_doc */
+        0,                                              /* tp_traverse */
+        0,                                              /* tp_clear */
+        0,                                              /* tp_richcompare */
+        0,                                              /* tp_weaklistoffset */
+        0,                                              /* tp_iter */
+        0,                                              /* tp_iternext */
+        cache_methods,                                  /* tp_methods */
+        0,                                              /* tp_members */
+        0,                                              /* tp_getset */
+        0,                                              /* tp_base */
+        0,                                              /* tp_dict */
+        0,                                              /* tp_descr_get */
+        0,                                              /* tp_descr_set */
+        0,                                              /* tp_dictoffset */
+        (initproc)cache_init,                           /* tp_init */
+        0,                                              /* tp_alloc */
+        0,                                              /* tp_new */
+        0                                               /* tp_free */
+};
+
+extern int cache_setup_types(void)
+{
+    int rc;
+
+    NodeType.tp_new = PyType_GenericNew;
+    CacheType.tp_new = PyType_GenericNew;
+
+    rc = PyType_Ready(&NodeType);
+    if (rc < 0) {
+        return rc;
+    }
+
+    rc = PyType_Ready(&CacheType);
+    return rc;
+}
diff --git a/Modules/_sqlite/cache.h b/Modules/_sqlite/cache.h
new file mode 100644
index 0000000..5cc16f3
--- /dev/null
+++ b/Modules/_sqlite/cache.h
@@ -0,0 +1,61 @@
+/* cache.h - definitions for the LRU cache
+ *
+ * Copyright (C) 2004-2005 Gerhard Häring <gh@ghaering.de>
+ *
+ * This file is part of pysqlite.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * 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. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 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 or altered from any source distribution.
+ */
+
+#ifndef PYSQLITE_CACHE_H
+#define PYSQLITE_CACHE_H
+#include "Python.h"
+
+typedef struct _Node
+{
+    PyObject_HEAD
+    PyObject* key;
+    PyObject* data;
+    long count;
+    struct _Node* prev;
+    struct _Node* next;
+} Node;
+
+typedef struct
+{
+    PyObject_HEAD
+    int size;
+    PyObject* mapping;
+    PyObject* factory;
+    Node* first;
+    Node* last;
+    int decref_factory;
+} Cache;
+
+extern PyTypeObject NodeType;
+extern PyTypeObject CacheType;
+
+int node_init(Node* self, PyObject* args, PyObject* kwargs);
+void node_dealloc(Node* self);
+
+int cache_init(Cache* self, PyObject* args, PyObject* kwargs);
+void cache_dealloc(Cache* self);
+PyObject* cache_get(Cache* self, PyObject* args);
+
+int cache_setup_types(void);
+
+#endif
diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c
new file mode 100644
index 0000000..78aad37
--- /dev/null
+++ b/Modules/_sqlite/connection.c
@@ -0,0 +1,1082 @@
+/* connection.c - the connection type
+ *
+ * Copyright (C) 2004-2006 Gerhard Häring <gh@ghaering.de>
+ *
+ * This file is part of pysqlite.
+ * 
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * 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. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 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 or altered from any source distribution.
+ */
+
+#include "cache.h"
+#include "module.h"
+#include "connection.h"
+#include "statement.h"
+#include "cursor.h"
+#include "prepare_protocol.h"
+#include "util.h"
+#include "sqlitecompat.h"
+
+#include "pythread.h"
+
+static int connection_set_isolation_level(Connection* self, PyObject* isolation_level);
+
+int connection_init(Connection* self, PyObject* args, PyObject* kwargs)
+{
+    static char *kwlist[] = {"database", "timeout", "detect_types", "isolation_level", "check_same_thread", "factory", "cached_statements", NULL, NULL};
+
+    char* database;
+    int detect_types = 0;
+    PyObject* isolation_level = NULL;
+    PyObject* factory = NULL;
+    int check_same_thread = 1;
+    int cached_statements = 100;
+    double timeout = 5.0;
+    int rc;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|diOiOi", kwlist,
+                                     &database, &timeout, &detect_types, &isolation_level, &check_same_thread, &factory, &cached_statements))
+    {
+        return -1; 
+    }
+
+    self->begin_statement = NULL;
+
+    self->statement_cache = NULL;
+
+    Py_INCREF(Py_None);
+    self->row_factory = Py_None;
+
+    Py_INCREF(&PyUnicode_Type);
+    self->text_factory = (PyObject*)&PyUnicode_Type;
+
+    Py_BEGIN_ALLOW_THREADS
+    rc = sqlite3_open(database, &self->db);
+    Py_END_ALLOW_THREADS
+
+    if (rc != SQLITE_OK) {
+        _seterror(self->db);
+        return -1;
+    }
+
+    if (!isolation_level) {
+        isolation_level = PyString_FromString("");
+    } else {
+        Py_INCREF(isolation_level);
+    }
+    self->isolation_level = NULL;
+    connection_set_isolation_level(self, isolation_level);
+    Py_DECREF(isolation_level);
+
+    self->statement_cache = (Cache*)PyObject_CallFunction((PyObject*)&CacheType, "Oi", self, cached_statements);
+    if (PyErr_Occurred()) {
+        return -1;
+    }
+
+    /* By default, the Cache class INCREFs the factory in its initializer, and
+     * decrefs it in its deallocator method. Since this would create a circular
+     * reference here, we're breaking it by decrementing self, and telling the
+     * cache class to not decref the factory (self) in its deallocator.
+     */
+    self->statement_cache->decref_factory = 0;
+    Py_DECREF(self);
+
+    self->inTransaction = 0;
+    self->detect_types = detect_types;
+    self->timeout = timeout;
+    (void)sqlite3_busy_timeout(self->db, (int)(timeout*1000));
+
+    self->thread_ident = PyThread_get_thread_ident();
+    self->check_same_thread = check_same_thread;
+
+    self->function_pinboard = PyDict_New();
+    if (!self->function_pinboard) {
+        return -1;
+    }
+
+    self->collations = PyDict_New();
+    if (!self->collations) {
+        return -1;
+    }
+
+    self->Warning = Warning;
+    self->Error = Error;
+    self->InterfaceError = InterfaceError;
+    self->DatabaseError = DatabaseError;
+    self->DataError = DataError;
+    self->OperationalError = OperationalError;
+    self->IntegrityError = IntegrityError;
+    self->InternalError = InternalError;
+    self->ProgrammingError = ProgrammingError;
+    self->NotSupportedError = NotSupportedError;
+
+    return 0;
+}
+
+void flush_statement_cache(Connection* self)
+{
+    Node* node;
+    Statement* statement;
+
+    node = self->statement_cache->first;
+
+    while (node) {
+        statement = (Statement*)(node->data);
+        (void)statement_finalize(statement);
+        node = node->next;
+    }
+
+    Py_DECREF(self->statement_cache);
+    self->statement_cache = (Cache*)PyObject_CallFunction((PyObject*)&CacheType, "O", self);
+    Py_DECREF(self);
+    self->statement_cache->decref_factory = 0;
+}
+
+void reset_all_statements(Connection* self)
+{
+    Node* node;
+    Statement* statement;
+
+    node = self->statement_cache->first;
+
+    while (node) {
+        statement = (Statement*)(node->data);
+        (void)statement_reset(statement);
+        node = node->next;
+    }
+}
+
+void connection_dealloc(Connection* self)
+{
+    Py_XDECREF(self->statement_cache);
+
+    /* Clean up if user has not called .close() explicitly. */
+    if (self->db) {
+        Py_BEGIN_ALLOW_THREADS
+        sqlite3_close(self->db);
+        Py_END_ALLOW_THREADS
+    }
+
+    if (self->begin_statement) {
+        PyMem_Free(self->begin_statement);
+    }
+    Py_XDECREF(self->isolation_level);
+    Py_XDECREF(self->function_pinboard);
+    Py_XDECREF(self->row_factory);
+    Py_XDECREF(self->text_factory);
+    Py_XDECREF(self->collations);
+
+    self->ob_type->tp_free((PyObject*)self);
+}
+
+PyObject* connection_cursor(Connection* self, PyObject* args, PyObject* kwargs)
+{
+    static char *kwlist[] = {"factory", NULL, NULL};
+    PyObject* factory = NULL;
+    PyObject* cursor;
+
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O", kwlist,
+                                     &factory)) {
+        return NULL;
+    }
+
+    if (!check_thread(self) || !check_connection(self)) {
+        return NULL;
+    }
+
+    if (factory == NULL) {
+        factory = (PyObject*)&CursorType;
+    }
+
+    cursor = PyObject_CallFunction(factory, "O", self);
+
+    if (cursor && self->row_factory != Py_None) {
+        Py_XDECREF(((Cursor*)cursor)->row_factory);
+        Py_INCREF(self->row_factory);
+        ((Cursor*)cursor)->row_factory = self->row_factory;
+    }
+
+    return cursor;
+}
+
+PyObject* connection_close(Connection* self, PyObject* args)
+{
+    int rc;
+
+    if (!check_thread(self)) {
+        return NULL;
+    }
+
+    flush_statement_cache(self);
+
+    if (self->db) {
+        Py_BEGIN_ALLOW_THREADS
+        rc = sqlite3_close(self->db);
+        Py_END_ALLOW_THREADS
+
+        if (rc != SQLITE_OK) {
+            _seterror(self->db);
+            return NULL;
+        } else {
+            self->db = NULL;
+        }
+    }
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+/*
+ * Checks if a connection object is usable (i. e. not closed).
+ *
+ * 0 => error; 1 => ok
+ */
+int check_connection(Connection* con)
+{
+    if (!con->db) {
+        PyErr_SetString(ProgrammingError, "Cannot operate on a closed database.");
+        return 0;
+    } else {
+        return 1;
+    }
+}
+
+PyObject* _connection_begin(Connection* self)
+{
+    int rc;
+    const char* tail;
+    sqlite3_stmt* statement;
+
+    Py_BEGIN_ALLOW_THREADS
+    rc = sqlite3_prepare(self->db, self->begin_statement, -1, &statement, &tail);
+    Py_END_ALLOW_THREADS
+
+    if (rc != SQLITE_OK) {
+        _seterror(self->db);
+        goto error;
+    }
+
+    rc = _sqlite_step_with_busyhandler(statement, self);
+    if (rc == SQLITE_DONE) {
+        self->inTransaction = 1;
+    } else {
+        _seterror(self->db);
+    }
+
+    Py_BEGIN_ALLOW_THREADS
+    rc = sqlite3_finalize(statement);
+    Py_END_ALLOW_THREADS
+
+    if (rc != SQLITE_OK && !PyErr_Occurred()) {
+        _seterror(self->db);
+    }
+
+error:
+    if (PyErr_Occurred()) {
+        return NULL;
+    } else {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+}
+
+PyObject* connection_commit(Connection* self, PyObject* args)
+{
+    int rc;
+    const char* tail;
+    sqlite3_stmt* statement;
+
+    if (!check_thread(self) || !check_connection(self)) {
+        return NULL;
+    }
+
+    if (self->inTransaction) {
+        Py_BEGIN_ALLOW_THREADS
+        rc = sqlite3_prepare(self->db, "COMMIT", -1, &statement, &tail);
+        Py_END_ALLOW_THREADS
+        if (rc != SQLITE_OK) {
+            _seterror(self->db);
+            goto error;
+        }
+
+        rc = _sqlite_step_with_busyhandler(statement, self);
+        if (rc == SQLITE_DONE) {
+            self->inTransaction = 0;
+        } else {
+            _seterror(self->db);
+        }
+
+        Py_BEGIN_ALLOW_THREADS
+        rc = sqlite3_finalize(statement);
+        Py_END_ALLOW_THREADS
+        if (rc != SQLITE_OK && !PyErr_Occurred()) {
+            _seterror(self->db);
+        }
+
+    }
+
+error:
+    if (PyErr_Occurred()) {
+        return NULL;
+    } else {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+}
+
+PyObject* connection_rollback(Connection* self, PyObject* args)
+{
+    int rc;
+    const char* tail;
+    sqlite3_stmt* statement;
+
+    if (!check_thread(self) || !check_connection(self)) {
+        return NULL;
+    }
+
+    if (self->inTransaction) {
+        reset_all_statements(self);
+
+        Py_BEGIN_ALLOW_THREADS
+        rc = sqlite3_prepare(self->db, "ROLLBACK", -1, &statement, &tail);
+        Py_END_ALLOW_THREADS
+        if (rc != SQLITE_OK) {
+            _seterror(self->db);
+            goto error;
+        }
+
+        rc = _sqlite_step_with_busyhandler(statement, self);
+        if (rc == SQLITE_DONE) {
+            self->inTransaction = 0;
+        } else {
+            _seterror(self->db);
+        }
+
+        Py_BEGIN_ALLOW_THREADS
+        rc = sqlite3_finalize(statement);
+        Py_END_ALLOW_THREADS
+        if (rc != SQLITE_OK && !PyErr_Occurred()) {
+            _seterror(self->db);
+        }
+
+    }
+
+error:
+    if (PyErr_Occurred()) {
+        return NULL;
+    } else {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+}
+
+void _set_result(sqlite3_context* context, PyObject* py_val)
+{
+    long longval;
+    const char* buffer;
+    Py_ssize_t buflen;
+    PyObject* stringval;
+
+    if (PyErr_Occurred()) {
+        /* Errors in callbacks are ignored, and we return NULL */
+        PyErr_Clear();
+        sqlite3_result_null(context);
+    } else if (py_val == Py_None) {
+        sqlite3_result_null(context);
+    } else if (PyInt_Check(py_val)) {
+        longval = PyInt_AsLong(py_val);
+        /* TODO: investigate what to do with range overflows - long vs. long long */
+        sqlite3_result_int64(context, (PY_LONG_LONG)longval);
+    } else if (PyFloat_Check(py_val)) {
+        sqlite3_result_double(context, PyFloat_AsDouble(py_val));
+    } else if (PyBuffer_Check(py_val)) {
+        if (PyObject_AsCharBuffer(py_val, &buffer, &buflen) != 0) {
+            PyErr_SetString(PyExc_ValueError, "could not convert BLOB to buffer");
+        }
+        sqlite3_result_blob(context, buffer, buflen, SQLITE_TRANSIENT);
+    } else if (PyString_Check(py_val)) {
+        sqlite3_result_text(context, PyString_AsString(py_val), -1, SQLITE_TRANSIENT);
+    } else if (PyUnicode_Check(py_val)) {
+        stringval = PyUnicode_AsUTF8String(py_val);
+        sqlite3_result_text(context, PyString_AsString(stringval), -1, SQLITE_TRANSIENT);
+        Py_DECREF(stringval);
+    } else {
+        /* TODO: raise error */
+    }
+}
+
+PyObject* _build_py_params(sqlite3_context *context, int argc, sqlite3_value** argv)
+{
+    PyObject* args;
+    int i;
+    sqlite3_value* cur_value;
+    PyObject* cur_py_value;
+    const char* val_str;
+    PY_LONG_LONG val_int;
+    Py_ssize_t buflen;
+    void* raw_buffer;
+
+    args = PyTuple_New(argc);
+    if (!args) {
+        return NULL;
+    }
+
+    for (i = 0; i < argc; i++) {
+        cur_value = argv[i];
+        switch (sqlite3_value_type(argv[i])) {
+            case SQLITE_INTEGER:
+                val_int = sqlite3_value_int64(cur_value);
+                cur_py_value = PyInt_FromLong((long)val_int);
+                break;
+            case SQLITE_FLOAT:
+                cur_py_value = PyFloat_FromDouble(sqlite3_value_double(cur_value));
+                break;
+            case SQLITE_TEXT:
+                val_str = (const char*)sqlite3_value_text(cur_value);
+                cur_py_value = PyUnicode_DecodeUTF8(val_str, strlen(val_str), NULL);
+                /* TODO: have a way to show errors here */
+                if (!cur_py_value) {
+                    Py_INCREF(Py_None);
+                    cur_py_value = Py_None;
+                }
+                break;
+            case SQLITE_BLOB:
+                buflen = sqlite3_value_bytes(cur_value);
+                cur_py_value = PyBuffer_New(buflen);
+                if (!cur_py_value) {
+                    /* TODO: error */
+                }
+                if (PyObject_AsWriteBuffer(cur_py_value, &raw_buffer, &buflen)) {
+                    /* TODO: error */
+                }
+                memcpy(raw_buffer, sqlite3_value_blob(cur_value), buflen);
+                break;
+            case SQLITE_NULL:
+            default:
+                Py_INCREF(Py_None);
+                cur_py_value = Py_None;
+        }
+        PyTuple_SetItem(args, i, cur_py_value);
+
+    }
+
+    return args;
+}
+
+void _func_callback(sqlite3_context* context, int argc, sqlite3_value** argv)
+{
+    PyObject* args;
+    PyObject* py_func;
+    PyObject* py_retval;
+
+
+    PyGILState_STATE threadstate;
+
+    threadstate = PyGILState_Ensure();
+
+    py_func = (PyObject*)sqlite3_user_data(context);
+
+    args = _build_py_params(context, argc, argv);
+
+    py_retval = PyObject_CallObject(py_func, args);
+    Py_DECREF(args);
+
+    _set_result(context, py_retval);
+    Py_XDECREF(py_retval);
+
+    PyGILState_Release(threadstate);
+}
+
+static void _step_callback(sqlite3_context *context, int argc, sqlite3_value** params)
+{
+    PyObject* args;
+    PyObject* function_result;
+    PyObject* aggregate_class;
+    PyObject** aggregate_instance;
+    PyObject* stepmethod;
+
+    PyGILState_STATE threadstate;
+
+    threadstate = PyGILState_Ensure();
+
+    aggregate_class = (PyObject*)sqlite3_user_data(context);
+
+    aggregate_instance = (PyObject**)sqlite3_aggregate_context(context, sizeof(PyObject*));
+
+    if (*aggregate_instance == 0) {
+        *aggregate_instance = PyObject_CallFunction(aggregate_class, "");
+
+        if (PyErr_Occurred())
+        {
+            PyErr_Clear();
+            *aggregate_instance = 0;
+            PyGILState_Release(threadstate);
+            return;
+        }
+    }
+
+    stepmethod = PyObject_GetAttrString(*aggregate_instance, "step");
+    if (!stepmethod)
+    {
+        PyGILState_Release(threadstate);
+        return;
+    }
+
+    args = _build_py_params(context, argc, params);
+
+    function_result = PyObject_CallObject(stepmethod, args);
+    Py_DECREF(args);
+    Py_DECREF(stepmethod);
+
+    if (function_result == NULL) {
+        PyErr_Clear();
+    } else {
+        Py_DECREF(function_result);
+    }
+
+    PyGILState_Release(threadstate);
+}
+
+void _final_callback(sqlite3_context* context)
+{
+    PyObject* args;
+    PyObject* function_result;
+    PyObject** aggregate_instance;
+    PyObject* aggregate_class;
+    PyObject* finalizemethod;
+
+    PyGILState_STATE threadstate;
+
+    threadstate = PyGILState_Ensure();
+
+    aggregate_class = (PyObject*)sqlite3_user_data(context);
+
+    aggregate_instance = (PyObject**)sqlite3_aggregate_context(context, sizeof(PyObject*));
+    if (!*aggregate_instance) {
+        /* this branch is executed if there was an exception in the aggregate's
+         * __init__ */
+
+        PyGILState_Release(threadstate);
+        return;
+    }
+
+    finalizemethod = PyObject_GetAttrString(*aggregate_instance, "finalize");
+
+    if (!finalizemethod) {
+        /*
+        PyErr_SetString(ProgrammingError, "finalize method missing");
+        goto error;
+        */
+        Py_INCREF(Py_None);
+        function_result = Py_None;
+    } else {
+        args = PyTuple_New(0);
+        if (!args)
+                return;
+        function_result = PyObject_CallObject(finalizemethod, args);
+        Py_DECREF(args);
+        Py_DECREF(finalizemethod);
+    }
+
+    _set_result(context, function_result);
+    Py_XDECREF(*aggregate_instance);
+    Py_XDECREF(function_result);
+
+    PyGILState_Release(threadstate);
+}
+
+
+PyObject* connection_create_function(Connection* self, PyObject* args, PyObject* kwargs)
+{
+    static char *kwlist[] = {"name", "narg", "func", NULL, NULL};
+
+    PyObject* func;
+    char* name;
+    int narg;
+    int rc;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "siO", kwlist,
+                                     &name, &narg, &func))
+    {
+        return NULL;
+    }
+
+    rc = sqlite3_create_function(self->db, name, narg, SQLITE_UTF8, (void*)func, _func_callback, NULL, NULL);
+
+    PyDict_SetItem(self->function_pinboard, func, Py_None);
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+PyObject* connection_create_aggregate(Connection* self, PyObject* args, PyObject* kwargs)
+{
+    PyObject* aggregate_class;
+
+    int n_arg;
+    char* name;
+    static char *kwlist[] = { "name", "n_arg", "aggregate_class", NULL };
+    int rc;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "siO:create_aggregate",
+                                      kwlist, &name, &n_arg, &aggregate_class)) {
+        return NULL;
+    }
+
+    rc = sqlite3_create_function(self->db, name, n_arg, SQLITE_UTF8, (void*)aggregate_class, 0, &_step_callback, &_final_callback);
+    if (rc != SQLITE_OK) {
+        _seterror(self->db);
+        return NULL;
+    } else {
+        PyDict_SetItem(self->function_pinboard, aggregate_class, Py_None);
+
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+}
+
+int check_thread(Connection* self)
+{
+    if (self->check_same_thread) {
+        if (PyThread_get_thread_ident() != self->thread_ident) {
+            PyErr_Format(ProgrammingError,
+                        "SQLite objects created in a thread can only be used in that same thread."
+                        "The object was created in thread id %ld and this is thread id %ld",
+                        self->thread_ident, PyThread_get_thread_ident());
+            return 0;
+        }
+
+    }
+
+    return 1;
+}
+
+static PyObject* connection_get_isolation_level(Connection* self, void* unused)
+{
+    Py_INCREF(self->isolation_level);
+    return self->isolation_level;
+}
+
+static PyObject* connection_get_total_changes(Connection* self, void* unused)
+{
+    if (!check_connection(self)) {
+        return NULL;
+    } else {
+        return Py_BuildValue("i", sqlite3_total_changes(self->db));
+    }
+}
+
+static int connection_set_isolation_level(Connection* self, PyObject* isolation_level)
+{
+    PyObject* empty;
+    PyObject* res;
+    PyObject* begin_statement;
+
+    Py_XDECREF(self->isolation_level);
+
+    if (self->begin_statement) {
+        PyMem_Free(self->begin_statement);
+        self->begin_statement = NULL;
+    }
+
+    if (isolation_level == Py_None) {
+        Py_INCREF(Py_None);
+        self->isolation_level = Py_None;
+
+        empty = PyTuple_New(0);
+        if (!empty) {
+            return -1;
+        }
+        res = connection_commit(self, empty);
+        if (!res) {
+            return -1;
+        }
+        Py_DECREF(empty);
+        Py_DECREF(res);
+
+        self->inTransaction = 0;
+    } else {
+        Py_INCREF(isolation_level);
+        self->isolation_level = isolation_level;
+
+        begin_statement = PyString_FromString("BEGIN ");
+        if (!begin_statement) {
+            return -1;
+        }
+        PyString_Concat(&begin_statement, isolation_level);
+        if (!begin_statement) {
+            return -1;
+        }
+
+        self->begin_statement = PyMem_Malloc(PyString_Size(begin_statement) + 2);
+        if (!self->begin_statement) {
+            return -1;
+        }
+
+        strcpy(self->begin_statement, PyString_AsString(begin_statement));
+        Py_DECREF(begin_statement);
+    }
+
+    return 0;
+}
+
+PyObject* connection_call(Connection* self, PyObject* args, PyObject* kwargs)
+{
+    PyObject* sql;
+    Statement* statement;
+    int rc;
+
+    if (!PyArg_ParseTuple(args, "O", &sql)) {
+        return NULL;
+    }
+
+    statement = PyObject_New(Statement, &StatementType);
+    if (!statement) {
+        return NULL;
+    }
+
+    rc = statement_create(statement, self, sql);
+
+    if (rc != SQLITE_OK) {
+        if (rc == PYSQLITE_TOO_MUCH_SQL) {
+            PyErr_SetString(Warning, "You can only execute one statement at a time.");
+        } else if (rc == PYSQLITE_SQL_WRONG_TYPE) {
+            PyErr_SetString(Warning, "SQL is of wrong type. Must be string or unicode.");
+        } else {
+            _seterror(self->db);
+        }
+
+        Py_DECREF(statement);
+        statement = 0;
+    }
+
+    return (PyObject*)statement;
+}
+
+PyObject* connection_execute(Connection* self, PyObject* args, PyObject* kwargs)
+{
+    PyObject* cursor = 0;
+    PyObject* result = 0;
+    PyObject* method = 0;
+
+    cursor = PyObject_CallMethod((PyObject*)self, "cursor", "");
+    if (!cursor) {
+        goto error;
+    }
+
+    method = PyObject_GetAttrString(cursor, "execute");
+    if (!method) {
+        Py_DECREF(cursor);
+        cursor = 0;
+        goto error;
+    }
+
+    result = PyObject_CallObject(method, args);
+    if (!result) {
+        Py_DECREF(cursor);
+        cursor = 0;
+    }
+
+error:
+    Py_XDECREF(result);
+    Py_XDECREF(method);
+
+    return cursor;
+}
+
+PyObject* connection_executemany(Connection* self, PyObject* args, PyObject* kwargs)
+{
+    PyObject* cursor = 0;
+    PyObject* result = 0;
+    PyObject* method = 0;
+
+    cursor = PyObject_CallMethod((PyObject*)self, "cursor", "");
+    if (!cursor) {
+        goto error;
+    }
+
+    method = PyObject_GetAttrString(cursor, "executemany");
+    if (!method) {
+        Py_DECREF(cursor);
+        cursor = 0;
+        goto error;
+    }
+
+    result = PyObject_CallObject(method, args);
+    if (!result) {
+        Py_DECREF(cursor);
+        cursor = 0;
+    }
+
+error:
+    Py_XDECREF(result);
+    Py_XDECREF(method);
+
+    return cursor;
+}
+
+PyObject* connection_executescript(Connection* self, PyObject* args, PyObject* kwargs)
+{
+    PyObject* cursor = 0;
+    PyObject* result = 0;
+    PyObject* method = 0;
+
+    cursor = PyObject_CallMethod((PyObject*)self, "cursor", "");
+    if (!cursor) {
+        goto error;
+    }
+
+    method = PyObject_GetAttrString(cursor, "executescript");
+    if (!method) {
+        Py_DECREF(cursor);
+        cursor = 0;
+        goto error;
+    }
+
+    result = PyObject_CallObject(method, args);
+    if (!result) {
+        Py_DECREF(cursor);
+        cursor = 0;
+    }
+
+error:
+    Py_XDECREF(result);
+    Py_XDECREF(method);
+
+    return cursor;
+}
+
+/* ------------------------- COLLATION CODE ------------------------ */
+
+static int
+collation_callback(
+        void* context,
+        int text1_length, const void* text1_data,
+        int text2_length, const void* text2_data)
+{
+    PyObject* callback = (PyObject*)context;
+    PyObject* string1 = 0;
+    PyObject* string2 = 0;
+    PyGILState_STATE gilstate;
+
+    PyObject* retval = NULL;
+    int result = 0;
+
+    gilstate = PyGILState_Ensure();
+
+    if (PyErr_Occurred()) {
+        goto finally;
+    }
+
+    string1 = PyString_FromStringAndSize((const char*)text1_data, text1_length);
+    string2 = PyString_FromStringAndSize((const char*)text2_data, text2_length);
+
+    if (!string1 || !string2) {
+        goto finally; /* failed to allocate strings */
+    }
+
+    retval = PyObject_CallFunctionObjArgs(callback, string1, string2, NULL);
+
+    if (!retval) {
+        /* execution failed */
+        goto finally;
+    }
+
+    result = PyInt_AsLong(retval);
+    if (PyErr_Occurred()) {
+        result = 0;
+    }
+
+finally:
+    Py_XDECREF(string1);
+    Py_XDECREF(string2);
+    Py_XDECREF(retval);
+
+    PyGILState_Release(gilstate);
+
+    return result;
+}
+
+static PyObject *
+connection_create_collation(Connection* self, PyObject* args)
+{
+    PyObject* callable;
+    PyObject* uppercase_name = 0;
+    PyObject* name;
+    PyObject* retval;
+    char* chk;
+    int rc;
+
+    if (!check_thread(self) || !check_connection(self)) {
+        goto finally;
+    }
+
+    if (!PyArg_ParseTuple(args, "O!O:create_collation(name, callback)", &PyString_Type, &name, &callable)) {
+        goto finally;
+    }
+
+    uppercase_name = PyObject_CallMethod(name, "upper", "");
+    if (!uppercase_name) {
+        goto finally;
+    }
+
+    chk = PyString_AsString(uppercase_name);
+    while (*chk) {
+        if ((*chk >= '0' && *chk <= '9')
+         || (*chk >= 'A' && *chk <= 'Z')
+         || (*chk == '_'))
+        {
+            chk++;
+        } else {
+            PyErr_SetString(ProgrammingError, "invalid character in collation name");
+            goto finally;
+        }
+    }
+
+    if (callable != Py_None && !PyCallable_Check(callable)) {
+        PyErr_SetString(PyExc_TypeError, "parameter must be callable");
+        goto finally;
+    }
+
+    if (callable != Py_None) {
+        PyDict_SetItem(self->collations, uppercase_name, callable);
+    } else {
+        PyDict_DelItem(self->collations, uppercase_name);
+    }
+
+    rc = sqlite3_create_collation(self->db,
+                                  PyString_AsString(uppercase_name),
+                                  SQLITE_UTF8,
+                                  (callable != Py_None) ? callable : NULL,
+                                  (callable != Py_None) ? collation_callback : NULL);
+    if (rc != SQLITE_OK) {
+        PyDict_DelItem(self->collations, uppercase_name);
+        _seterror(self->db);
+        goto finally;
+    }
+
+finally:
+    Py_XDECREF(uppercase_name);
+
+    if (PyErr_Occurred()) {
+        retval = NULL;
+    } else {
+        Py_INCREF(Py_None);
+        retval = Py_None;
+    }
+
+    return retval;
+}
+
+static char connection_doc[] =
+PyDoc_STR("<missing docstring>");
+
+static PyGetSetDef connection_getset[] = {
+    {"isolation_level",  (getter)connection_get_isolation_level, (setter)connection_set_isolation_level},
+    {"total_changes",  (getter)connection_get_total_changes, (setter)0},
+    {NULL}
+};
+
+static PyMethodDef connection_methods[] = {
+    {"cursor", (PyCFunction)connection_cursor, METH_VARARGS|METH_KEYWORDS,
+        PyDoc_STR("Return a cursor for the connection.")},
+    {"close", (PyCFunction)connection_close, METH_NOARGS,
+        PyDoc_STR("Closes the connection.")},
+    {"commit", (PyCFunction)connection_commit, METH_NOARGS,
+        PyDoc_STR("Commit the current transaction.")},
+    {"rollback", (PyCFunction)connection_rollback, METH_NOARGS,
+        PyDoc_STR("Roll back the current transaction.")},
+    {"create_function", (PyCFunction)connection_create_function, METH_VARARGS|METH_KEYWORDS,
+        PyDoc_STR("Creates a new function. Non-standard.")},
+    {"create_aggregate", (PyCFunction)connection_create_aggregate, METH_VARARGS|METH_KEYWORDS,
+        PyDoc_STR("Creates a new aggregate. Non-standard.")},
+    {"execute", (PyCFunction)connection_execute, METH_VARARGS,
+        PyDoc_STR("Executes a SQL statement. Non-standard.")},
+    {"executemany", (PyCFunction)connection_executemany, METH_VARARGS,
+        PyDoc_STR("Repeatedly executes a SQL statement. Non-standard.")},
+    {"executescript", (PyCFunction)connection_executescript, METH_VARARGS,
+        PyDoc_STR("Executes a multiple SQL statements at once. Non-standard.")},
+    {"create_collation", (PyCFunction)connection_create_collation, METH_VARARGS,
+        PyDoc_STR("Creates a collation function.")},
+    {NULL, NULL}
+};
+
+static struct PyMemberDef connection_members[] =
+{
+    {"Warning", T_OBJECT, offsetof(Connection, Warning), RO},
+    {"Error", T_OBJECT, offsetof(Connection, Error), RO},
+    {"InterfaceError", T_OBJECT, offsetof(Connection, InterfaceError), RO},
+    {"DatabaseError", T_OBJECT, offsetof(Connection, DatabaseError), RO},
+    {"DataError", T_OBJECT, offsetof(Connection, DataError), RO},
+    {"OperationalError", T_OBJECT, offsetof(Connection, OperationalError), RO},
+    {"IntegrityError", T_OBJECT, offsetof(Connection, IntegrityError), RO},
+    {"InternalError", T_OBJECT, offsetof(Connection, InternalError), RO},
+    {"ProgrammingError", T_OBJECT, offsetof(Connection, ProgrammingError), RO},
+    {"NotSupportedError", T_OBJECT, offsetof(Connection, NotSupportedError), RO},
+    {"row_factory", T_OBJECT, offsetof(Connection, row_factory)},
+    {"text_factory", T_OBJECT, offsetof(Connection, text_factory)},
+    {NULL}
+};
+
+PyTypeObject ConnectionType = {
+        PyObject_HEAD_INIT(NULL)
+        0,                                              /* ob_size */
+        MODULE_NAME ".Connection",                      /* tp_name */
+        sizeof(Connection),                             /* tp_basicsize */
+        0,                                              /* tp_itemsize */
+        (destructor)connection_dealloc,                 /* tp_dealloc */
+        0,                                              /* tp_print */
+        0,                                              /* tp_getattr */
+        0,                                              /* tp_setattr */
+        0,                                              /* tp_compare */
+        0,                                              /* tp_repr */
+        0,                                              /* tp_as_number */
+        0,                                              /* tp_as_sequence */
+        0,                                              /* tp_as_mapping */
+        0,                                              /* tp_hash */
+        (ternaryfunc)connection_call,                   /* tp_call */
+        0,                                              /* tp_str */
+        0,                                              /* tp_getattro */
+        0,                                              /* tp_setattro */
+        0,                                              /* tp_as_buffer */
+        Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,         /* tp_flags */
+        connection_doc,                                 /* tp_doc */
+        0,                                              /* tp_traverse */
+        0,                                              /* tp_clear */
+        0,                                              /* tp_richcompare */
+        0,                                              /* tp_weaklistoffset */
+        0,                                              /* tp_iter */
+        0,                                              /* tp_iternext */
+        connection_methods,                             /* tp_methods */
+        connection_members,                             /* tp_members */
+        connection_getset,                              /* tp_getset */
+        0,                                              /* tp_base */
+        0,                                              /* tp_dict */
+        0,                                              /* tp_descr_get */
+        0,                                              /* tp_descr_set */
+        0,                                              /* tp_dictoffset */
+        (initproc)connection_init,                      /* tp_init */
+        0,                                              /* tp_alloc */
+        0,                                              /* tp_new */
+        0                                               /* tp_free */
+};
+
+extern int connection_setup_types(void)
+{
+    ConnectionType.tp_new = PyType_GenericNew;
+    return PyType_Ready(&ConnectionType);
+}
diff --git a/Modules/_sqlite/connection.h b/Modules/_sqlite/connection.h
new file mode 100644
index 0000000..faae6e4
--- /dev/null
+++ b/Modules/_sqlite/connection.h
@@ -0,0 +1,106 @@
+/* connection.h - definitions for the connection type
+ *
+ * Copyright (C) 2004-2005 Gerhard Häring <gh@ghaering.de>
+ *
+ * This file is part of pysqlite.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * 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. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 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 or altered from any source distribution.
+ */
+
+#ifndef PYSQLITE_CONNECTION_H
+#define PYSQLITE_CONNECTION_H
+#include "Python.h"
+#include "pythread.h"
+#include "structmember.h"
+
+#include "cache.h"
+#include "module.h"
+
+#include "sqlite3.h"
+
+typedef struct
+{
+    PyObject_HEAD
+    sqlite3* db;
+
+    int inTransaction;
+    int detect_types;
+
+    /* the timeout value in seconds for database locks */
+    double timeout;
+
+    /* for internal use in the timeout handler: when did the timeout handler
+     * first get called with count=0? */
+    double timeout_started;
+
+    /* None for autocommit, otherwise a PyString with the isolation level */
+    PyObject* isolation_level;
+
+    /* NULL for autocommit, otherwise a string with the BEGIN statment; will be
+     * freed in connection destructor */
+    char* begin_statement;
+
+    int check_same_thread;
+    long thread_ident;
+
+    Cache* statement_cache;
+
+    PyObject* row_factory;
+
+    PyObject* text_factory;
+
+    /* remember references to functions/classes used in
+     * create_function/create/aggregate, use these as dictionary keys, so we
+     * can keep the total system refcount constant by clearing that dictionary
+     * in connection_dealloc */
+    PyObject* function_pinboard;
+
+    /* a dictionary of registered collation name => collation callable mappings */
+    PyObject* collations;
+
+    /* Exception objects */
+    PyObject* Warning;
+    PyObject* Error;
+    PyObject* InterfaceError;
+    PyObject* DatabaseError;
+    PyObject* DataError;
+    PyObject* OperationalError;
+    PyObject* IntegrityError;
+    PyObject* InternalError;
+    PyObject* ProgrammingError;
+    PyObject* NotSupportedError;
+} Connection;
+
+extern PyTypeObject ConnectionType;
+
+PyObject* connection_alloc(PyTypeObject* type, int aware);
+void connection_dealloc(Connection* self);
+PyObject* connection_cursor(Connection* self, PyObject* args, PyObject* kwargs);
+PyObject* connection_close(Connection* self, PyObject* args);
+PyObject* _connection_begin(Connection* self);
+PyObject* connection_begin(Connection* self, PyObject* args);
+PyObject* connection_commit(Connection* self, PyObject* args);
+PyObject* connection_rollback(Connection* self, PyObject* args);
+PyObject* connection_new(PyTypeObject* type, PyObject* args, PyObject* kw);
+int connection_init(Connection* self, PyObject* args, PyObject* kwargs);
+
+int check_thread(Connection* self);
+int check_connection(Connection* con);
+
+int connection_setup_types(void);
+
+#endif
diff --git a/Modules/_sqlite/converters.c b/Modules/_sqlite/converters.c
new file mode 100644
index 0000000..018063a
--- /dev/null
+++ b/Modules/_sqlite/converters.c
@@ -0,0 +1,40 @@
+/* converters.c - default converters
+ *
+ * Copyright (C) 2005 Gerhard Häring <gh@ghaering.de>
+ *
+ * This file is part of pysqlite.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * 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. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 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 or altered from any source distribution.
+ */
+
+#include "util.h"
+#include "module.h"
+#include "adapters.h"
+
+/* dummy, will be implemented in a later version */
+
+PyObject* convert_date(PyObject* self, PyObject* args, PyObject* kwargs)
+{
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+PyObject* convert_timestamp(PyObject* self, PyObject* args, PyObject* kwargs)
+{
+    Py_INCREF(Py_None);
+    return Py_None;
+}
diff --git a/Modules/_sqlite/converters.h b/Modules/_sqlite/converters.h
new file mode 100644
index 0000000..df3768a
--- /dev/null
+++ b/Modules/_sqlite/converters.h
@@ -0,0 +1,33 @@
+/* converters.h - default converters
+ *
+ * Copyright (C) 2005 Gerhard Häring <gh@ghaering.de>
+ *
+ * This file is part of pysqlite.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * 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. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 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 or altered from any source distribution.
+ */
+
+#ifndef PYSQLITE_CONVERTERS_H
+#define PYSQLITE_CONVERTERS_H
+#include "Python.h"
+#include "pythread.h"
+#include "sqlite3.h"
+
+PyObject* convert_date(PyObject* self, PyObject* args, PyObject* kwargs);
+PyObject* convert_timestamp(PyObject* self, PyObject* args, PyObject* kwargs);
+
+#endif
diff --git a/Modules/_sqlite/cursor.c b/Modules/_sqlite/cursor.c
new file mode 100644
index 0000000..c6b8c77
--- /dev/null
+++ b/Modules/_sqlite/cursor.c
@@ -0,0 +1,1027 @@
+/* cursor.c - the cursor type
+ *
+ * Copyright (C) 2004-2006 Gerhard Häring <gh@ghaering.de>
+ *
+ * This file is part of pysqlite.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * 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. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 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 or altered from any source distribution.
+ */
+
+#include "cursor.h"
+#include "module.h"
+#include "util.h"
+#include "sqlitecompat.h"
+
+/* used to decide wether to call PyInt_FromLong or PyLong_FromLongLong */
+#define INT32_MIN (-2147483647 - 1)
+#define INT32_MAX 2147483647
+
+PyObject* cursor_iternext(Cursor *self);
+
+static StatementKind detect_statement_type(char* statement)
+{
+    char buf[20];
+    char* src;
+    char* dst;
+
+    src = statement;
+    /* skip over whitepace */
+    while (*src == '\r' || *src == '\n' || *src == ' ' || *src == '\t') {
+        src++;
+    }
+
+    if (*src == 0)
+        return STATEMENT_INVALID;
+
+    dst = buf;
+    *dst = 0;
+    while (isalpha(*src) && dst - buf < sizeof(buf) - 2) {
+        *dst++ = tolower(*src++);
+    }
+
+    *dst = 0;
+
+    if (!strcmp(buf, "select")) {
+        return STATEMENT_SELECT;
+    } else if (!strcmp(buf, "insert")) {
+        return STATEMENT_INSERT;
+    } else if (!strcmp(buf, "update")) {
+        return STATEMENT_UPDATE;
+    } else if (!strcmp(buf, "delete")) {
+        return STATEMENT_DELETE;
+    } else if (!strcmp(buf, "replace")) {
+        return STATEMENT_REPLACE;
+    } else {
+        return STATEMENT_OTHER;
+    }
+}
+
+int cursor_init(Cursor* self, PyObject* args, PyObject* kwargs)
+{
+    Connection* connection;
+
+    if (!PyArg_ParseTuple(args, "O!", &ConnectionType, &connection))
+    {
+        return -1; 
+    }
+
+    Py_INCREF(connection);
+    self->connection = connection;
+    self->statement = NULL;
+    self->next_row = NULL;
+
+    self->row_cast_map = PyList_New(0);
+    if (!self->row_cast_map) {
+        return -1;
+    }
+
+    Py_INCREF(Py_None);
+    self->description = Py_None;
+
+    Py_INCREF(Py_None);
+    self->lastrowid= Py_None;
+
+    self->arraysize = 1;
+
+    self->rowcount = PyInt_FromLong(-1L);
+    if (!self->rowcount) {
+        return -1;
+    }
+
+    Py_INCREF(Py_None);
+    self->row_factory = Py_None;
+
+    if (!check_thread(self->connection)) {
+        return -1;
+    }
+
+    return 0;
+}
+
+void cursor_dealloc(Cursor* self)
+{
+    int rc;
+
+    /* Reset the statement if the user has not closed the cursor */
+    if (self->statement) {
+        rc = statement_reset(self->statement);
+        Py_DECREF(self->statement);
+    }
+
+    Py_XDECREF(self->connection);
+    Py_XDECREF(self->row_cast_map);
+    Py_XDECREF(self->description);
+    Py_XDECREF(self->lastrowid);
+    Py_XDECREF(self->rowcount);
+    Py_XDECREF(self->row_factory);
+    Py_XDECREF(self->next_row);
+
+    self->ob_type->tp_free((PyObject*)self);
+}
+
+int build_row_cast_map(Cursor* self)
+{
+    int i;
+    const char* type_start = (const char*)-1;
+    const char* pos;
+
+    const char* colname;
+    const char* decltype;
+    PyObject* py_decltype;
+    PyObject* converter;
+    PyObject* key;
+
+    if (!self->connection->detect_types) {
+        return 0;
+    }
+
+    Py_XDECREF(self->row_cast_map);
+    self->row_cast_map = PyList_New(0);
+
+    for (i = 0; i < sqlite3_column_count(self->statement->st); i++) {
+        converter = NULL;
+
+        if (self->connection->detect_types | PARSE_COLNAMES) {
+            colname = sqlite3_column_name(self->statement->st, i);
+
+            for (pos = colname; *pos != 0; pos++) {
+                if (*pos == '[') {
+                    type_start = pos + 1;
+                } else if (*pos == ']' && type_start != (const char*)-1) {
+                    key = PyString_FromStringAndSize(type_start, pos - type_start);
+                    if (!key) {
+                        /* creating a string failed, but it is too complicated
+                         * to propagate the error here, we just assume there is
+                         * no converter and proceed */
+                        break;
+                    }
+
+                    converter = PyDict_GetItem(converters, key);
+                    Py_DECREF(key);
+                    break;
+                }
+
+            }
+        }
+
+        if (!converter && self->connection->detect_types | PARSE_DECLTYPES) {
+            decltype = sqlite3_column_decltype(self->statement->st, i);
+            if (decltype) {
+                for (pos = decltype;;pos++) {
+                    if (*pos == ' ' || *pos == 0) {
+                        py_decltype = PyString_FromStringAndSize(decltype, pos - decltype);
+                        if (!py_decltype) {
+                            return -1;
+                        }
+                        break;
+                    }
+                }
+
+                converter = PyDict_GetItem(converters, py_decltype);
+                Py_DECREF(py_decltype);
+            }
+        }
+
+        if (!converter) {
+            converter = Py_None;
+        }
+
+        if (PyList_Append(self->row_cast_map, converter) != 0) {
+            if (converter != Py_None) {
+                Py_DECREF(converter);
+            }
+            Py_XDECREF(self->row_cast_map);
+            self->row_cast_map = NULL;
+
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+PyObject* _build_column_name(const char* colname)
+{
+    const char* pos;
+
+    if (!colname) {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    for (pos = colname;; pos++) {
+        if (*pos == 0 || *pos == ' ') {
+            return PyString_FromStringAndSize(colname, pos - colname);
+        }
+    }
+}
+
+PyObject* unicode_from_string(const char* val_str, int optimize)
+{
+    const char* check;
+    int is_ascii = 0;
+
+    if (optimize) {
+        is_ascii = 1;
+
+        check = val_str;
+        while (*check) {
+            if (*check & 0x80) {
+                is_ascii = 0;
+                break;
+            }
+
+            check++;
+        }
+    }
+
+    if (is_ascii) {
+        return PyString_FromString(val_str);
+    } else {
+        return PyUnicode_DecodeUTF8(val_str, strlen(val_str), NULL);
+    }
+}
+
+/*
+ * Returns a row from the currently active SQLite statement
+ *
+ * Precondidition:
+ * - sqlite3_step() has been called before and it returned SQLITE_ROW.
+ */
+PyObject* _fetch_one_row(Cursor* self)
+{
+    int i, numcols;
+    PyObject* row;
+    PyObject* item = NULL;
+    int coltype;
+    PY_LONG_LONG intval;
+    PyObject* converter;
+    PyObject* converted;
+    Py_ssize_t nbytes;
+    PyObject* buffer;
+    void* raw_buffer;
+    const char* val_str;
+    char buf[200];
+
+    Py_BEGIN_ALLOW_THREADS
+    numcols = sqlite3_data_count(self->statement->st);
+    Py_END_ALLOW_THREADS
+
+    row = PyTuple_New(numcols);
+    if (!row) {
+        return NULL;
+    }
+
+    for (i = 0; i < numcols; i++) {
+        if (self->connection->detect_types) {
+            converter = PyList_GetItem(self->row_cast_map, i);
+            if (!converter) {
+                converter = Py_None;
+            }
+        } else {
+            converter = Py_None;
+        }
+
+        if (converter != Py_None) {
+            val_str = (const char*)sqlite3_column_text(self->statement->st, i);
+            if (!val_str) {
+                Py_INCREF(Py_None);
+                converted = Py_None;
+            } else {
+                item = PyString_FromString(val_str);
+                if (!item) {
+                    return NULL;
+                }
+                converted = PyObject_CallFunction(converter, "O", item);
+                if (!converted) {
+                    /* TODO: have a way to log these errors */
+                    Py_INCREF(Py_None);
+                    converted = Py_None;
+                    PyErr_Clear();
+                }
+                Py_DECREF(item);
+            }
+        } else {
+            Py_BEGIN_ALLOW_THREADS
+            coltype = sqlite3_column_type(self->statement->st, i);
+            Py_END_ALLOW_THREADS
+            if (coltype == SQLITE_NULL) {
+                Py_INCREF(Py_None);
+                converted = Py_None;
+            } else if (coltype == SQLITE_INTEGER) {
+                intval = sqlite3_column_int64(self->statement->st, i);
+                if (intval < INT32_MIN || intval > INT32_MAX) {
+                    converted = PyLong_FromLongLong(intval);
+                } else {
+                    converted = PyInt_FromLong((long)intval);
+                }
+            } else if (coltype == SQLITE_FLOAT) {
+                converted = PyFloat_FromDouble(sqlite3_column_double(self->statement->st, i));
+            } else if (coltype == SQLITE_TEXT) {
+                val_str = (const char*)sqlite3_column_text(self->statement->st, i);
+                if ((self->connection->text_factory == (PyObject*)&PyUnicode_Type)
+                    || (self->connection->text_factory == OptimizedUnicode)) {
+
+                    converted = unicode_from_string(val_str,
+                        self->connection->text_factory == OptimizedUnicode ? 1 : 0);
+
+                    if (!converted) {
+                        PyOS_snprintf(buf, sizeof(buf) - 1, "Could not decode to UTF-8 column %s with text %s",
+                                    sqlite3_column_name(self->statement->st, i), val_str);
+                        PyErr_SetString(OperationalError, buf);
+                    }
+                } else if (self->connection->text_factory == (PyObject*)&PyString_Type) {
+                    converted = PyString_FromString(val_str);
+                } else {
+                    converted = PyObject_CallFunction(self->connection->text_factory, "s", val_str);
+                }
+            } else {
+                /* coltype == SQLITE_BLOB */
+                nbytes = sqlite3_column_bytes(self->statement->st, i);
+                buffer = PyBuffer_New(nbytes);
+                if (!buffer) {
+                    break;
+                }
+                if (PyObject_AsWriteBuffer(buffer, &raw_buffer, &nbytes)) {
+                    break;
+                }
+                memcpy(raw_buffer, sqlite3_column_blob(self->statement->st, i), nbytes);
+                converted = buffer;
+            }
+        }
+
+        PyTuple_SetItem(row, i, converted);
+    }
+
+    if (PyErr_Occurred()) {
+        Py_DECREF(row);
+        row = NULL;
+    }
+
+    return row;
+}
+
+PyObject* _query_execute(Cursor* self, int multiple, PyObject* args)
+{
+    PyObject* operation;
+    PyObject* operation_bytestr = NULL;
+    char* operation_cstr;
+    PyObject* parameters_list = NULL;
+    PyObject* parameters_iter = NULL;
+    PyObject* parameters = NULL;
+    int i;
+    int rc;
+    PyObject* func_args;
+    PyObject* result;
+    int numcols;
+    PY_LONG_LONG lastrowid;
+    int statement_type;
+    PyObject* descriptor;
+    PyObject* second_argument = NULL;
+    long rowcount = 0;
+
+    if (!check_thread(self->connection) || !check_connection(self->connection)) {
+        return NULL;
+    }
+
+    Py_XDECREF(self->next_row);
+    self->next_row = NULL;
+
+    if (multiple) {
+        /* executemany() */
+        if (!PyArg_ParseTuple(args, "OO", &operation, &second_argument)) {
+            return NULL; 
+        }
+
+        if (!PyString_Check(operation) && !PyUnicode_Check(operation)) {
+            PyErr_SetString(PyExc_ValueError, "operation parameter must be str or unicode");
+            return NULL;
+        }
+
+        if (PyIter_Check(second_argument)) {
+            /* iterator */
+            Py_INCREF(second_argument);
+            parameters_iter = second_argument;
+        } else {
+            /* sequence */
+            parameters_iter = PyObject_GetIter(second_argument);
+            if (!parameters_iter)
+            {
+                return NULL;
+            }
+        }
+    } else {
+        /* execute() */
+        if (!PyArg_ParseTuple(args, "O|O", &operation, &second_argument)) {
+            return NULL; 
+        }
+
+        if (!PyString_Check(operation) && !PyUnicode_Check(operation)) {
+            PyErr_SetString(PyExc_ValueError, "operation parameter must be str or unicode");
+            return NULL;
+        }
+
+        parameters_list = PyList_New(0);
+        if (!parameters_list) {
+            return NULL;
+        }
+
+        if (second_argument == NULL) {
+            second_argument = PyTuple_New(0);
+            if (!second_argument) {
+                goto error;
+            }
+        } else {
+            Py_INCREF(second_argument);
+        }
+        if (PyList_Append(parameters_list, second_argument) != 0) {
+            Py_DECREF(second_argument);
+            goto error;
+        }
+        Py_DECREF(second_argument);
+
+        parameters_iter = PyObject_GetIter(parameters_list);
+        if (!parameters_iter) {
+            goto error;
+        }
+    }
+
+    if (self->statement != NULL) {
+        /* There is an active statement */
+        rc = statement_reset(self->statement);
+    }
+
+    if (PyString_Check(operation)) {
+        operation_cstr = PyString_AsString(operation);
+    } else {
+        operation_bytestr = PyUnicode_AsUTF8String(operation);
+        if (!operation_bytestr) {
+            goto error;
+        }
+
+        operation_cstr = PyString_AsString(operation_bytestr);
+    }
+
+    /* reset description and rowcount */
+    Py_DECREF(self->description);
+    Py_INCREF(Py_None);
+    self->description = Py_None;
+
+    Py_DECREF(self->rowcount);
+    self->rowcount = PyInt_FromLong(-1L);
+    if (!self->rowcount) {
+        goto error;
+    }
+
+    statement_type = detect_statement_type(operation_cstr);
+    if (self->connection->begin_statement) {
+        switch (statement_type) {
+            case STATEMENT_UPDATE:
+            case STATEMENT_DELETE:
+            case STATEMENT_INSERT:
+            case STATEMENT_REPLACE:
+                if (!self->connection->inTransaction) {
+                    result = _connection_begin(self->connection);
+                    if (!result) {
+                        goto error;
+                    }
+                    Py_DECREF(result);
+                }
+                break;
+            case STATEMENT_OTHER:
+                /* it's a DDL statement or something similar
+                   - we better COMMIT first so it works for all cases */
+                if (self->connection->inTransaction) {
+                    func_args = PyTuple_New(0);
+                    if (!func_args) {
+                        goto error;
+                    }
+                    result = connection_commit(self->connection, func_args);
+                    Py_DECREF(func_args);
+                    if (!result) {
+                        goto error;
+                    }
+                    Py_DECREF(result);
+                }
+                break;
+            case STATEMENT_SELECT:
+                if (multiple) {
+                    PyErr_SetString(ProgrammingError,
+                                "You cannot execute SELECT statements in executemany().");
+                    goto error;
+                }
+                break;
+        }
+    }
+
+    func_args = PyTuple_New(1);
+    if (!func_args) {
+        goto error;
+    }
+    Py_INCREF(operation);
+    if (PyTuple_SetItem(func_args, 0, operation) != 0) {
+        goto error;
+    }
+
+    if (self->statement) {
+        (void)statement_reset(self->statement);
+        Py_DECREF(self->statement);
+    }
+
+    self->statement = (Statement*)cache_get(self->connection->statement_cache, func_args);
+    Py_DECREF(func_args);
+
+    if (!self->statement) {
+        goto error;
+    }
+
+    if (self->statement->in_use) {
+        Py_DECREF(self->statement);
+        self->statement = PyObject_New(Statement, &StatementType);
+        if (!self->statement) {
+            goto error;
+        }
+        rc = statement_create(self->statement, self->connection, operation);
+        if (rc != SQLITE_OK) {
+            self->statement = 0;
+            goto error;
+        }
+    }
+
+    statement_reset(self->statement);
+    statement_mark_dirty(self->statement);
+
+    while (1) {
+        parameters = PyIter_Next(parameters_iter);
+        if (!parameters) {
+            break;
+        }
+
+        statement_mark_dirty(self->statement);
+
+        statement_bind_parameters(self->statement, parameters);
+        if (PyErr_Occurred()) {
+            goto error;
+        }
+
+        if (build_row_cast_map(self) != 0) {
+            PyErr_SetString(OperationalError, "Error while building row_cast_map");
+            goto error;
+        }
+
+        rc = _sqlite_step_with_busyhandler(self->statement->st, self->connection);
+        if (rc != SQLITE_DONE && rc != SQLITE_ROW) {
+            rc = statement_reset(self->statement);
+            if (rc == SQLITE_SCHEMA) {
+                rc = statement_recompile(self->statement, parameters);
+                if (rc == SQLITE_OK) {
+                    rc = _sqlite_step_with_busyhandler(self->statement->st, self->connection);
+                } else {
+                    _seterror(self->connection->db);
+                    goto error;
+                }
+            } else {
+                _seterror(self->connection->db);
+                goto error;
+            }
+        }
+
+        if (rc == SQLITE_ROW || (rc == SQLITE_DONE && statement_type == STATEMENT_SELECT)) {
+            Py_BEGIN_ALLOW_THREADS
+            numcols = sqlite3_column_count(self->statement->st);
+            Py_END_ALLOW_THREADS
+
+            if (self->description == Py_None) {
+                Py_DECREF(self->description);
+                self->description = PyTuple_New(numcols);
+                if (!self->description) {
+                    goto error;
+                }
+                for (i = 0; i < numcols; i++) {
+                    descriptor = PyTuple_New(7);
+                    if (!descriptor) {
+                        goto error;
+                    }
+                    PyTuple_SetItem(descriptor, 0, _build_column_name(sqlite3_column_name(self->statement->st, i)));
+                    Py_INCREF(Py_None); PyTuple_SetItem(descriptor, 1, Py_None);
+                    Py_INCREF(Py_None); PyTuple_SetItem(descriptor, 2, Py_None);
+                    Py_INCREF(Py_None); PyTuple_SetItem(descriptor, 3, Py_None);
+                    Py_INCREF(Py_None); PyTuple_SetItem(descriptor, 4, Py_None);
+                    Py_INCREF(Py_None); PyTuple_SetItem(descriptor, 5, Py_None);
+                    Py_INCREF(Py_None); PyTuple_SetItem(descriptor, 6, Py_None);
+                    PyTuple_SetItem(self->description, i, descriptor);
+                }
+            }
+        }
+
+        if (rc == SQLITE_ROW) {
+            if (multiple) {
+                PyErr_SetString(ProgrammingError, "executemany() can only execute DML statements.");
+                goto error;
+            }
+
+            self->next_row = _fetch_one_row(self);
+        } else if (rc == SQLITE_DONE && !multiple) {
+            statement_reset(self->statement);
+            Py_DECREF(self->statement);
+            self->statement = 0;
+        }
+
+        switch (statement_type) {
+            case STATEMENT_UPDATE:
+            case STATEMENT_DELETE:
+            case STATEMENT_INSERT:
+            case STATEMENT_REPLACE:
+                Py_BEGIN_ALLOW_THREADS
+                rowcount += (long)sqlite3_changes(self->connection->db);
+                Py_END_ALLOW_THREADS
+                Py_DECREF(self->rowcount);
+                self->rowcount = PyInt_FromLong(rowcount);
+        }
+
+        Py_DECREF(self->lastrowid);
+        if (statement_type == STATEMENT_INSERT) {
+            Py_BEGIN_ALLOW_THREADS
+            lastrowid = sqlite3_last_insert_rowid(self->connection->db);
+            Py_END_ALLOW_THREADS
+            self->lastrowid = PyInt_FromLong((long)lastrowid);
+        } else {
+            Py_INCREF(Py_None);
+            self->lastrowid = Py_None;
+        }
+
+        if (multiple) {
+            rc = statement_reset(self->statement);
+        }
+        Py_XDECREF(parameters);
+    }
+
+error:
+    Py_XDECREF(operation_bytestr);
+    Py_XDECREF(parameters);
+    Py_XDECREF(parameters_iter);
+    Py_XDECREF(parameters_list);
+
+    if (PyErr_Occurred()) {
+        return NULL;
+    } else {
+        Py_INCREF(self);
+        return (PyObject*)self;
+    }
+}
+
+PyObject* cursor_execute(Cursor* self, PyObject* args)
+{
+    return _query_execute(self, 0, args);
+}
+
+PyObject* cursor_executemany(Cursor* self, PyObject* args)
+{
+    return _query_execute(self, 1, args);
+}
+
+PyObject* cursor_executescript(Cursor* self, PyObject* args)
+{
+    PyObject* script_obj;
+    PyObject* script_str = NULL;
+    const char* script_cstr;
+    sqlite3_stmt* statement;
+    int rc;
+    PyObject* func_args;
+    PyObject* result;
+    int statement_completed = 0;
+
+    if (!PyArg_ParseTuple(args, "O", &script_obj)) {
+        return NULL; 
+    }
+
+    if (!check_thread(self->connection) || !check_connection(self->connection)) {
+        return NULL;
+    }
+
+    if (PyString_Check(script_obj)) {
+        script_cstr = PyString_AsString(script_obj);
+    } else if (PyUnicode_Check(script_obj)) {
+        script_str = PyUnicode_AsUTF8String(script_obj);
+        if (!script_str) {
+            return NULL;
+        }
+
+        script_cstr = PyString_AsString(script_str);
+    } else {
+        PyErr_SetString(PyExc_ValueError, "script argument must be unicode or string.");
+        return NULL;
+    }
+
+    /* commit first */
+    func_args = PyTuple_New(0);
+    if (!func_args) {
+        goto error;
+    }
+    result = connection_commit(self->connection, func_args);
+    Py_DECREF(func_args);
+    if (!result) {
+        goto error;
+    }
+    Py_DECREF(result);
+
+    while (1) {
+        if (!sqlite3_complete(script_cstr)) {
+            break;
+        }
+        statement_completed = 1;
+
+        rc = sqlite3_prepare(self->connection->db,
+                             script_cstr,
+                             -1,
+                             &statement,
+                             &script_cstr);
+        if (rc != SQLITE_OK) {
+            _seterror(self->connection->db);
+            goto error;
+        }
+
+        /* execute statement, and ignore results of SELECT statements */
+        rc = SQLITE_ROW;
+        while (rc == SQLITE_ROW) {
+            rc = _sqlite_step_with_busyhandler(statement, self->connection);
+        }
+
+        if (rc != SQLITE_DONE) {
+            (void)sqlite3_finalize(statement);
+            _seterror(self->connection->db);
+            goto error;
+        }
+
+        rc = sqlite3_finalize(statement);
+        if (rc != SQLITE_OK) {
+            _seterror(self->connection->db);
+            goto error;
+        }
+    }
+
+error:
+    Py_XDECREF(script_str);
+
+    if (!statement_completed) {
+        PyErr_SetString(ProgrammingError, "you did not provide a complete SQL statement");
+    }
+
+    if (PyErr_Occurred()) {
+        return NULL;
+    } else {
+        Py_INCREF(self);
+        return (PyObject*)self;
+    }
+}
+
+PyObject* cursor_getiter(Cursor *self)
+{
+    Py_INCREF(self);
+    return (PyObject*)self;
+}
+
+PyObject* cursor_iternext(Cursor *self)
+{
+    PyObject* next_row_tuple;
+    PyObject* next_row;
+    int rc;
+
+    if (!check_thread(self->connection) || !check_connection(self->connection)) {
+        return NULL;
+    }
+
+    if (!self->next_row) {
+         if (self->statement) {
+            (void)statement_reset(self->statement);
+            Py_DECREF(self->statement);
+            self->statement = NULL;
+        }
+        return NULL;
+    }
+
+    next_row_tuple = self->next_row;
+    self->next_row = NULL;
+
+    if (self->row_factory != Py_None) {
+        next_row = PyObject_CallFunction(self->row_factory, "OO", self, next_row_tuple);
+        Py_DECREF(next_row_tuple);
+    } else {
+        next_row = next_row_tuple;
+    }
+
+    rc = _sqlite_step_with_busyhandler(self->statement->st, self->connection);
+    if (rc != SQLITE_DONE && rc != SQLITE_ROW) {
+        Py_DECREF(next_row);
+        _seterror(self->connection->db);
+        return NULL;
+    }
+
+    if (rc == SQLITE_ROW) {
+        self->next_row = _fetch_one_row(self);
+    }
+
+    return next_row;
+}
+
+PyObject* cursor_fetchone(Cursor* self, PyObject* args)
+{
+    PyObject* row;
+
+    row = cursor_iternext(self);
+    if (!row && !PyErr_Occurred()) {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    return row;
+}
+
+PyObject* cursor_fetchmany(Cursor* self, PyObject* args)
+{
+    PyObject* row;
+    PyObject* list;
+    int maxrows = self->arraysize;
+    int counter = 0;
+
+    if (!PyArg_ParseTuple(args, "|i", &maxrows)) {
+        return NULL; 
+    }
+
+    list = PyList_New(0);
+    if (!list) {
+        return NULL;
+    }
+
+    /* just make sure we enter the loop */
+    row = Py_None;
+
+    while (row) {
+        row = cursor_iternext(self);
+        if (row) {
+            PyList_Append(list, row);
+            Py_DECREF(row);
+        } else {
+            break;
+        }
+
+        if (++counter == maxrows) {
+            break;
+        }
+    }
+
+    if (PyErr_Occurred()) {
+        Py_DECREF(list);
+        return NULL;
+    } else {
+        return list;
+    }
+}
+
+PyObject* cursor_fetchall(Cursor* self, PyObject* args)
+{
+    PyObject* row;
+    PyObject* list;
+
+    list = PyList_New(0);
+    if (!list) {
+        return NULL;
+    }
+
+    /* just make sure we enter the loop */
+    row = (PyObject*)Py_None;
+
+    while (row) {
+        row = cursor_iternext(self);
+        if (row) {
+            PyList_Append(list, row);
+            Py_DECREF(row);
+        }
+    }
+
+    if (PyErr_Occurred()) {
+        Py_DECREF(list);
+        return NULL;
+    } else {
+        return list;
+    }
+}
+
+PyObject* pysqlite_noop(Connection* self, PyObject* args)
+{
+    /* don't care, return None */
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+PyObject* cursor_close(Cursor* self, PyObject* args)
+{
+    if (!check_thread(self->connection) || !check_connection(self->connection)) {
+        return NULL;
+    }
+
+    if (self->statement) {
+        (void)statement_reset(self->statement);
+        Py_DECREF(self->statement);
+        self->statement = 0;
+    }
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyMethodDef cursor_methods[] = {
+    {"execute", (PyCFunction)cursor_execute, METH_VARARGS,
+        PyDoc_STR("Executes a SQL statement.")},
+    {"executemany", (PyCFunction)cursor_executemany, METH_VARARGS,
+        PyDoc_STR("Repeatedly executes a SQL statement.")},
+    {"executescript", (PyCFunction)cursor_executescript, METH_VARARGS,
+        PyDoc_STR("Executes a multiple SQL statements at once. Non-standard.")},
+    {"fetchone", (PyCFunction)cursor_fetchone, METH_NOARGS,
+        PyDoc_STR("Fetches several rows from the resultset.")},
+    {"fetchmany", (PyCFunction)cursor_fetchmany, METH_VARARGS,
+        PyDoc_STR("Fetches all rows from the resultset.")},
+    {"fetchall", (PyCFunction)cursor_fetchall, METH_NOARGS,
+        PyDoc_STR("Fetches one row from the resultset.")},
+    {"close", (PyCFunction)cursor_close, METH_NOARGS,
+        PyDoc_STR("Closes the cursor.")},
+    {"setinputsizes", (PyCFunction)pysqlite_noop, METH_VARARGS,
+        PyDoc_STR("Required by DB-API. Does nothing in pysqlite.")},
+    {"setoutputsize", (PyCFunction)pysqlite_noop, METH_VARARGS,
+        PyDoc_STR("Required by DB-API. Does nothing in pysqlite.")},
+    {NULL, NULL}
+};
+
+static struct PyMemberDef cursor_members[] =
+{
+    {"connection", T_OBJECT, offsetof(Cursor, connection), RO},
+    {"description", T_OBJECT, offsetof(Cursor, description), RO},
+    {"arraysize", T_INT, offsetof(Cursor, arraysize), 0},
+    {"lastrowid", T_OBJECT, offsetof(Cursor, lastrowid), RO},
+    {"rowcount", T_OBJECT, offsetof(Cursor, rowcount), RO},
+    {"row_factory", T_OBJECT, offsetof(Cursor, row_factory), 0},
+    {NULL}
+};
+
+PyTypeObject CursorType = {
+        PyObject_HEAD_INIT(NULL)
+        0,                                              /* ob_size */
+        MODULE_NAME ".Cursor",                          /* tp_name */
+        sizeof(Cursor),                                 /* tp_basicsize */
+        0,                                              /* tp_itemsize */
+        (destructor)cursor_dealloc,                     /* tp_dealloc */
+        0,                                              /* tp_print */
+        0,                                              /* tp_getattr */
+        0,                                              /* tp_setattr */
+        0,                                              /* tp_compare */
+        0,                                              /* tp_repr */
+        0,                                              /* tp_as_number */
+        0,                                              /* tp_as_sequence */
+        0,                                              /* tp_as_mapping */
+        0,                                              /* tp_hash */
+        0,                                              /* tp_call */
+        0,                                              /* tp_str */
+        0,                                              /* tp_getattro */
+        0,                                              /* tp_setattro */
+        0,                                              /* tp_as_buffer */
+        Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_ITER|Py_TPFLAGS_BASETYPE, /* tp_flags */
+        0,                                              /* tp_doc */
+        0,                                              /* tp_traverse */
+        0,                                              /* tp_clear */
+        0,                                              /* tp_richcompare */
+        0,                                              /* tp_weaklistoffset */
+        (getiterfunc)cursor_getiter,                    /* tp_iter */
+        (iternextfunc)cursor_iternext,                  /* tp_iternext */
+        cursor_methods,                                 /* tp_methods */
+        cursor_members,                                 /* tp_members */
+        0,                                              /* tp_getset */
+        0,                                              /* tp_base */
+        0,                                              /* tp_dict */
+        0,                                              /* tp_descr_get */
+        0,                                              /* tp_descr_set */
+        0,                                              /* tp_dictoffset */
+        (initproc)cursor_init,                          /* tp_init */
+        0,                                              /* tp_alloc */
+        0,                                              /* tp_new */
+        0                                               /* tp_free */
+};
+
+extern int cursor_setup_types(void)
+{
+    CursorType.tp_new = PyType_GenericNew;
+    return PyType_Ready(&CursorType);
+}
diff --git a/Modules/_sqlite/cursor.h b/Modules/_sqlite/cursor.h
new file mode 100644
index 0000000..7f56799
--- /dev/null
+++ b/Modules/_sqlite/cursor.h
@@ -0,0 +1,71 @@
+/* cursor.h - definitions for the cursor type
+ *
+ * Copyright (C) 2004-2005 Gerhard Häring <gh@ghaering.de>
+ *
+ * This file is part of pysqlite.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * 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. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 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 or altered from any source distribution.
+ */
+
+#ifndef PYSQLITE_CURSOR_H
+#define PYSQLITE_CURSOR_H
+#include "Python.h"
+
+#include "statement.h"
+#include "connection.h"
+#include "module.h"
+
+typedef struct
+{
+    PyObject_HEAD
+    Connection* connection;
+    PyObject* description;
+    PyObject* row_cast_map;
+    int arraysize;
+    PyObject* lastrowid;
+    PyObject* rowcount;
+    PyObject* row_factory;
+    Statement* statement;
+
+    /* the next row to be returned, NULL if no next row available */
+    PyObject* next_row;
+} Cursor;
+
+typedef enum {
+    STATEMENT_INVALID, STATEMENT_INSERT, STATEMENT_DELETE,
+    STATEMENT_UPDATE, STATEMENT_REPLACE, STATEMENT_SELECT,
+    STATEMENT_OTHER
+} StatementKind;
+
+extern PyTypeObject CursorType;
+
+int cursor_init(Cursor* self, PyObject* args, PyObject* kwargs);
+void cursor_dealloc(Cursor* self);
+PyObject* cursor_execute(Cursor* self, PyObject* args);
+PyObject* cursor_executemany(Cursor* self, PyObject* args);
+PyObject* cursor_getiter(Cursor *self);
+PyObject* cursor_iternext(Cursor *self);
+PyObject* cursor_fetchone(Cursor* self, PyObject* args);
+PyObject* cursor_fetchmany(Cursor* self, PyObject* args);
+PyObject* cursor_fetchall(Cursor* self, PyObject* args);
+PyObject* pysqlite_noop(Connection* self, PyObject* args);
+PyObject* cursor_close(Cursor* self, PyObject* args);
+
+int cursor_setup_types(void);
+
+#define UNKNOWN (-1)
+#endif
diff --git a/Modules/_sqlite/microprotocols.c b/Modules/_sqlite/microprotocols.c
new file mode 100644
index 0000000..4956ac0
--- /dev/null
+++ b/Modules/_sqlite/microprotocols.c
@@ -0,0 +1,142 @@
+/* microprotocols.c - minimalist and non-validating protocols implementation
+ *
+ * Copyright (C) 2003-2004 Federico Di Gregorio <fog@debian.org>
+ *
+ * This file is part of psycopg and was adapted for pysqlite. Federico Di
+ * Gregorio gave the permission to use it within pysqlite under the following
+ * license:
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * 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. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 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 or altered from any source distribution.
+ */
+
+#include <Python.h>
+#include <structmember.h>
+
+#include "cursor.h"
+#include "microprotocols.h"
+#include "prepare_protocol.h"
+
+
+/** the adapters registry **/
+
+PyObject *psyco_adapters;
+
+/* microprotocols_init - initialize the adapters dictionary */
+
+int
+microprotocols_init(PyObject *dict)
+{
+    /* create adapters dictionary and put it in module namespace */
+    if ((psyco_adapters = PyDict_New()) == NULL) {
+        return -1;
+    }
+
+    return PyDict_SetItemString(dict, "adapters", psyco_adapters);
+}
+
+
+/* microprotocols_add - add a reverse type-caster to the dictionary */
+
+int
+microprotocols_add(PyTypeObject *type, PyObject *proto, PyObject *cast)
+{
+    PyObject* key;
+    int rc;
+
+    if (proto == NULL) proto = (PyObject*)&SQLitePrepareProtocolType;
+
+    key = Py_BuildValue("(OO)", (PyObject*)type, proto);
+    if (!key) {
+        return -1;
+    }
+
+    rc = PyDict_SetItem(psyco_adapters, key, cast);
+    Py_DECREF(key);
+
+    return rc;
+}
+
+/* microprotocols_adapt - adapt an object to the built-in protocol */
+
+PyObject *
+microprotocols_adapt(PyObject *obj, PyObject *proto, PyObject *alt)
+{
+    PyObject *adapter, *key;
+
+    /* we don't check for exact type conformance as specified in PEP 246
+       because the SQLitePrepareProtocolType type is abstract and there is no
+       way to get a quotable object to be its instance */
+
+    /* look for an adapter in the registry */
+    key = Py_BuildValue("(OO)", (PyObject*)obj->ob_type, proto);
+    if (!key) {
+        return NULL;
+    }
+    adapter = PyDict_GetItem(psyco_adapters, key);
+    Py_DECREF(key);
+    if (adapter) {
+        PyObject *adapted = PyObject_CallFunctionObjArgs(adapter, obj, NULL);
+        return adapted;
+    }
+
+    /* try to have the protocol adapt this object*/
+    if (PyObject_HasAttrString(proto, "__adapt__")) {
+        PyObject *adapted = PyObject_CallMethod(proto, "__adapt__", "O", obj);
+        if (adapted) {
+            if (adapted != Py_None) {
+                return adapted;
+            } else {
+                Py_DECREF(adapted);
+            }
+        }
+
+        if (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_TypeError))
+            return NULL;
+    }
+
+    /* and finally try to have the object adapt itself */
+    if (PyObject_HasAttrString(obj, "__conform__")) {
+        PyObject *adapted = PyObject_CallMethod(obj, "__conform__","O", proto);
+        if (adapted) {
+            if (adapted != Py_None) {
+                return adapted;
+            } else {
+                Py_DECREF(adapted);
+            }
+        }
+
+        if (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_TypeError)) {
+            return NULL;
+        }
+    }
+
+    /* else set the right exception and return NULL */
+    PyErr_SetString(ProgrammingError, "can't adapt");
+    return NULL;
+}
+
+/** module-level functions **/
+
+PyObject *
+psyco_microprotocols_adapt(Cursor *self, PyObject *args)
+{
+    PyObject *obj, *alt = NULL;
+    PyObject *proto = (PyObject*)&SQLitePrepareProtocolType;
+
+    if (!PyArg_ParseTuple(args, "O|OO", &obj, &proto, &alt)) return NULL;
+    return microprotocols_adapt(obj, proto, alt);
+}
diff --git a/Modules/_sqlite/microprotocols.h b/Modules/_sqlite/microprotocols.h
new file mode 100644
index 0000000..d2d9b65
--- /dev/null
+++ b/Modules/_sqlite/microprotocols.h
@@ -0,0 +1,59 @@
+/* microprotocols.c - definitions for minimalist and non-validating protocols
+ *
+ * Copyright (C) 2003-2004 Federico Di Gregorio <fog@debian.org>
+ *
+ * This file is part of psycopg and was adapted for pysqlite. Federico Di
+ * Gregorio gave the permission to use it within pysqlite under the following
+ * license:
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * 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. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 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 or altered from any source distribution.
+ */
+
+#ifndef PSYCOPG_MICROPROTOCOLS_H
+#define PSYCOPG_MICROPROTOCOLS_H 1
+
+#include <Python.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** adapters registry **/
+
+extern PyObject *psyco_adapters;
+
+/** the names of the three mandatory methods **/
+
+#define MICROPROTOCOLS_GETQUOTED_NAME "getquoted"
+#define MICROPROTOCOLS_GETSTRING_NAME "getstring"
+#define MICROPROTOCOLS_GETBINARY_NAME "getbinary"
+
+/** exported functions **/
+
+/* used by module.c to init the microprotocols system */
+extern int microprotocols_init(PyObject *dict);
+extern int microprotocols_add(
+    PyTypeObject *type, PyObject *proto, PyObject *cast);
+extern PyObject *microprotocols_adapt(
+    PyObject *obj, PyObject *proto, PyObject *alt);
+
+extern PyObject *
+    psyco_microprotocols_adapt(Cursor* self, PyObject *args);   
+#define psyco_microprotocols_adapt_doc \
+    "adapt(obj, protocol, alternate) -> adapt obj to given protocol"
+    
+#endif /* !defined(PSYCOPG_MICROPROTOCOLS_H) */
diff --git a/Modules/_sqlite/module.c b/Modules/_sqlite/module.c
new file mode 100644
index 0000000..1537e79
--- /dev/null
+++ b/Modules/_sqlite/module.c
@@ -0,0 +1,325 @@
+/* module.c - the module itself
+ *
+ * Copyright (C) 2004-2006 Gerhard Häring <gh@ghaering.de>
+ *
+ * This file is part of pysqlite.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * 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. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 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 or altered from any source distribution.
+ */
+
+#include "connection.h"
+#include "statement.h"
+#include "cursor.h"
+#include "cache.h"
+#include "prepare_protocol.h"
+#include "microprotocols.h"
+#include "row.h"
+
+#if SQLITE_VERSION_NUMBER >= 3003003
+#define HAVE_SHARED_CACHE
+#endif
+
+/* static objects at module-level */
+
+PyObject* Error, *Warning, *InterfaceError, *DatabaseError, *InternalError,
+    *OperationalError, *ProgrammingError, *IntegrityError, *DataError,
+    *NotSupportedError, *OptimizedUnicode;
+
+PyObject* converters;
+
+static PyObject* module_connect(PyObject* self, PyObject* args, PyObject*
+        kwargs)
+{
+    /* Python seems to have no way of extracting a single keyword-arg at
+     * C-level, so this code is redundant with the one in connection_init in
+     * connection.c and must always be copied from there ... */
+
+    static char *kwlist[] = {"database", "timeout", "detect_types", "isolation_level", "check_same_thread", "factory", "cached_statements", NULL, NULL};
+    char* database;
+    int detect_types = 0;
+    PyObject* isolation_level;
+    PyObject* factory = NULL;
+    int check_same_thread = 1;
+    int cached_statements;
+    double timeout = 5.0;
+
+    PyObject* result;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|diOiOi", kwlist,
+                                     &database, &timeout, &detect_types, &isolation_level, &check_same_thread, &factory, &cached_statements))
+    {
+        return NULL; 
+    }
+
+    if (factory == NULL) {
+        factory = (PyObject*)&ConnectionType;
+    }
+
+    result = PyObject_Call(factory, args, kwargs);
+
+    return result;
+}
+
+static PyObject* module_complete(PyObject* self, PyObject* args, PyObject*
+        kwargs)
+{
+    static char *kwlist[] = {"statement", NULL, NULL};
+    char* statement;
+
+    PyObject* result;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &statement))
+    {
+        return NULL; 
+    }
+
+    if (sqlite3_complete(statement)) {
+        result = Py_True;
+    } else {
+        result = Py_False;
+    }
+
+    Py_INCREF(result);
+
+    return result;
+}
+
+#ifdef HAVE_SHARED_CACHE
+static PyObject* module_enable_shared_cache(PyObject* self, PyObject* args, PyObject*
+        kwargs)
+{
+    static char *kwlist[] = {"do_enable", NULL, NULL};
+    int do_enable;
+    int rc;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &do_enable))
+    {
+        return NULL; 
+    }
+
+    rc = sqlite3_enable_shared_cache(do_enable);
+
+    if (rc != SQLITE_OK) {
+        PyErr_SetString(OperationalError, "Changing the shared_cache flag failed");
+        return NULL;
+    } else {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+}
+#endif /* HAVE_SHARED_CACHE */
+
+static PyObject* module_register_adapter(PyObject* self, PyObject* args, PyObject* kwargs)
+{
+    PyTypeObject* type;
+    PyObject* caster;
+
+    if (!PyArg_ParseTuple(args, "OO", &type, &caster)) {
+        return NULL;
+    }
+
+    microprotocols_add(type, (PyObject*)&SQLitePrepareProtocolType, caster);
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject* module_register_converter(PyObject* self, PyObject* args, PyObject* kwargs)
+{
+    PyObject* name;
+    PyObject* callable;
+
+    if (!PyArg_ParseTuple(args, "OO", &name, &callable)) {
+        return NULL;
+    }
+
+    if (PyDict_SetItem(converters, name, callable) != 0) {
+        return NULL;
+    }
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+void converters_init(PyObject* dict)
+{
+    converters = PyDict_New();
+    if (!converters) {
+        return;
+    }
+
+    PyDict_SetItemString(dict, "converters", converters);
+}
+
+static PyMethodDef module_methods[] = {
+    {"connect",  (PyCFunction)module_connect,  METH_VARARGS|METH_KEYWORDS, PyDoc_STR("Creates a connection.")},
+    {"complete_statement",  (PyCFunction)module_complete,  METH_VARARGS|METH_KEYWORDS, PyDoc_STR("Checks if a string contains a complete SQL statement.")},
+#ifdef HAVE_SHARED_CACHE
+    {"enable_shared_cache",  (PyCFunction)module_enable_shared_cache,  METH_VARARGS|METH_KEYWORDS, PyDoc_STR("Enable or disable shared cache mode for the calling thread.")},
+#endif
+    {"register_adapter", (PyCFunction)module_register_adapter, METH_VARARGS, PyDoc_STR("Registers an adapter with sqlite's adapter registry.")},
+    {"register_converter", (PyCFunction)module_register_converter, METH_VARARGS, PyDoc_STR("Registers a converter with sqlite.")},
+    {"adapt",  (PyCFunction)psyco_microprotocols_adapt, METH_VARARGS, psyco_microprotocols_adapt_doc},
+    {NULL, NULL}
+};
+
+PyMODINIT_FUNC init_sqlite3(void)
+{
+    PyObject *module, *dict;
+    PyObject *tmp_obj;
+
+    module = Py_InitModule("_sqlite3", module_methods);
+
+    if (!module ||
+        (row_setup_types() < 0) ||
+        (cursor_setup_types() < 0) ||
+        (connection_setup_types() < 0) ||
+        (cache_setup_types() < 0) ||
+        (statement_setup_types() < 0) ||
+        (prepare_protocol_setup_types() < 0)
+       ) {
+        return;
+    }
+
+    Py_INCREF(&ConnectionType);
+    PyModule_AddObject(module, "Connection", (PyObject*) &ConnectionType);
+    Py_INCREF(&CursorType);
+    PyModule_AddObject(module, "Cursor", (PyObject*) &CursorType);
+    Py_INCREF(&CacheType);
+    PyModule_AddObject(module, "Statement", (PyObject*)&StatementType);
+    Py_INCREF(&StatementType);
+    PyModule_AddObject(module, "Cache", (PyObject*) &CacheType);
+    Py_INCREF(&SQLitePrepareProtocolType);
+    PyModule_AddObject(module, "PrepareProtocol", (PyObject*) &SQLitePrepareProtocolType);
+    Py_INCREF(&RowType);
+    PyModule_AddObject(module, "Row", (PyObject*) &RowType);
+
+    if (!(dict = PyModule_GetDict(module))) {
+        goto error;
+    }
+
+    /*** Create DB-API Exception hierarchy */
+
+    if (!(Error = PyErr_NewException(MODULE_NAME ".Error", PyExc_StandardError, NULL))) {
+        goto error;
+    }
+    PyDict_SetItemString(dict, "Error", Error);
+
+    if (!(Warning = PyErr_NewException(MODULE_NAME ".Warning", PyExc_StandardError, NULL))) {
+        goto error;
+    }
+    PyDict_SetItemString(dict, "Warning", Warning);
+
+    /* Error subclasses */
+
+    if (!(InterfaceError = PyErr_NewException(MODULE_NAME ".InterfaceError", Error, NULL))) {
+        goto error;
+    }
+    PyDict_SetItemString(dict, "InterfaceError", InterfaceError);
+
+    if (!(DatabaseError = PyErr_NewException(MODULE_NAME ".DatabaseError", Error, NULL))) {
+        goto error;
+    }
+    PyDict_SetItemString(dict, "DatabaseError", DatabaseError);
+
+    /* DatabaseError subclasses */
+
+    if (!(InternalError = PyErr_NewException(MODULE_NAME ".InternalError", DatabaseError, NULL))) {
+        goto error;
+    }
+    PyDict_SetItemString(dict, "InternalError", InternalError);
+
+    if (!(OperationalError = PyErr_NewException(MODULE_NAME ".OperationalError", DatabaseError, NULL))) {
+        goto error;
+    }
+    PyDict_SetItemString(dict, "OperationalError", OperationalError);
+
+    if (!(ProgrammingError = PyErr_NewException(MODULE_NAME ".ProgrammingError", DatabaseError, NULL))) {
+        goto error;
+    }
+    PyDict_SetItemString(dict, "ProgrammingError", ProgrammingError);
+
+    if (!(IntegrityError = PyErr_NewException(MODULE_NAME ".IntegrityError", DatabaseError,NULL))) {
+        goto error;
+    }
+    PyDict_SetItemString(dict, "IntegrityError", IntegrityError);
+
+    if (!(DataError = PyErr_NewException(MODULE_NAME ".DataError", DatabaseError, NULL))) {
+        goto error;
+    }
+    PyDict_SetItemString(dict, "DataError", DataError);
+
+    if (!(NotSupportedError = PyErr_NewException(MODULE_NAME ".NotSupportedError", DatabaseError, NULL))) {
+        goto error;
+    }
+    PyDict_SetItemString(dict, "NotSupportedError", NotSupportedError);
+
+    /* We just need "something" unique for OptimizedUnicode. It does not really
+     * need to be a string subclass. Just anything that can act as a special
+     * marker for us. So I pulled PyCell_Type out of my magic hat.
+     */
+    Py_INCREF((PyObject*)&PyCell_Type);
+    OptimizedUnicode = (PyObject*)&PyCell_Type;
+    PyDict_SetItemString(dict, "OptimizedUnicode", OptimizedUnicode);
+
+    if (!(tmp_obj = PyInt_FromLong(PARSE_DECLTYPES))) {
+        goto error;
+    }
+    PyDict_SetItemString(dict, "PARSE_DECLTYPES", tmp_obj);
+
+    if (!(tmp_obj = PyInt_FromLong(PARSE_COLNAMES))) {
+        goto error;
+    }
+    PyDict_SetItemString(dict, "PARSE_COLNAMES", tmp_obj);
+
+    if (!(tmp_obj = PyString_FromString(PYSQLITE_VERSION))) {
+        goto error;
+    }
+    PyDict_SetItemString(dict, "version", tmp_obj);
+
+    if (!(tmp_obj = PyString_FromString(sqlite3_libversion()))) {
+        goto error;
+    }
+    PyDict_SetItemString(dict, "sqlite_version", tmp_obj);
+
+    /* initialize microprotocols layer */
+    microprotocols_init(dict);
+
+    /* initialize the default converters */
+    converters_init(dict);
+
+    /* Original comment form _bsddb.c in the Python core. This is also still
+     * needed nowadays for Python 2.3/2.4.
+     * 
+     * PyEval_InitThreads is called here due to a quirk in python 1.5
+     * - 2.2.1 (at least) according to Russell Williamson <merel@wt.net>:
+     * The global interepreter lock is not initialized until the first
+     * thread is created using thread.start_new_thread() or fork() is
+     * called.  that would cause the ALLOW_THREADS here to segfault due
+     * to a null pointer reference if no threads or child processes
+     * have been created.  This works around that and is a no-op if
+     * threads have already been initialized.
+     *  (see pybsddb-users mailing list post on 2002-08-07)
+     */
+    PyEval_InitThreads();
+
+error:
+    if (PyErr_Occurred())
+    {
+        PyErr_SetString(PyExc_ImportError, MODULE_NAME ": init failed");
+    }
+}
diff --git a/Modules/_sqlite/module.h b/Modules/_sqlite/module.h
new file mode 100644
index 0000000..6694735
--- /dev/null
+++ b/Modules/_sqlite/module.h
@@ -0,0 +1,55 @@
+/* module.h - definitions for the module
+ *
+ * Copyright (C) 2004-2005 Gerhard Häring <gh@ghaering.de>
+ *
+ * This file is part of pysqlite.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * 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. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 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 or altered from any source distribution.
+ */
+
+#ifndef PYSQLITE_MODULE_H
+#define PYSQLITE_MODULE_H
+#include "Python.h"
+
+#define PYSQLITE_VERSION "2.2.0"
+
+extern PyObject* Error;
+extern PyObject* Warning;
+extern PyObject* InterfaceError;
+extern PyObject* DatabaseError;
+extern PyObject* InternalError;
+extern PyObject* OperationalError;
+extern PyObject* ProgrammingError;
+extern PyObject* IntegrityError;
+extern PyObject* DataError;
+extern PyObject* NotSupportedError;
+
+extern PyObject* OptimizedUnicode;
+
+/* the functions time.time() and time.sleep() */
+extern PyObject* time_time;
+extern PyObject* time_sleep;
+
+/* A dictionary, mapping colum types (INTEGER, VARCHAR, etc.) to converter
+ * functions, that convert the SQL value to the appropriate Python value.
+ * The key is uppercase.
+ */
+extern PyObject* converters;
+
+#define PARSE_DECLTYPES 1
+#define PARSE_COLNAMES 2
+#endif
diff --git a/Modules/_sqlite/prepare_protocol.c b/Modules/_sqlite/prepare_protocol.c
new file mode 100644
index 0000000..26b663b
--- /dev/null
+++ b/Modules/_sqlite/prepare_protocol.c
@@ -0,0 +1,84 @@
+/* prepare_protocol.c - the protocol for preparing values for SQLite
+ *
+ * Copyright (C) 2005-2006 Gerhard Häring <gh@ghaering.de>
+ *
+ * This file is part of pysqlite.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * 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. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 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 or altered from any source distribution.
+ */
+
+#include "prepare_protocol.h"
+
+int prepare_protocol_init(SQLitePrepareProtocol* self, PyObject* args, PyObject* kwargs)
+{
+    return 0;
+}
+
+void prepare_protocol_dealloc(SQLitePrepareProtocol* self)
+{
+    self->ob_type->tp_free((PyObject*)self);
+}
+
+PyTypeObject SQLitePrepareProtocolType= {
+        PyObject_HEAD_INIT(NULL)
+        0,                                              /* ob_size */
+        MODULE_NAME ".PrepareProtocol",                 /* tp_name */
+        sizeof(SQLitePrepareProtocol),                  /* tp_basicsize */
+        0,                                              /* tp_itemsize */
+        (destructor)prepare_protocol_dealloc,           /* tp_dealloc */
+        0,                                              /* tp_print */
+        0,                                              /* tp_getattr */
+        0,                                              /* tp_setattr */
+        0,                                              /* tp_compare */
+        0,                                              /* tp_repr */
+        0,                                              /* tp_as_number */
+        0,                                              /* tp_as_sequence */
+        0,                                              /* tp_as_mapping */
+        0,                                              /* tp_hash */
+        0,                                              /* tp_call */
+        0,                                              /* tp_str */
+        0,                                              /* tp_getattro */
+        0,                                              /* tp_setattro */
+        0,                                              /* tp_as_buffer */
+        Py_TPFLAGS_DEFAULT,                             /* tp_flags */
+        0,                                              /* tp_doc */
+        0,                                              /* tp_traverse */
+        0,                                              /* tp_clear */
+        0,                                              /* tp_richcompare */
+        0,                                              /* tp_weaklistoffset */
+        0,                                              /* tp_iter */
+        0,                                              /* tp_iternext */
+        0,                                              /* tp_methods */
+        0,                                              /* tp_members */
+        0,                                              /* tp_getset */
+        0,                                              /* tp_base */
+        0,                                              /* tp_dict */
+        0,                                              /* tp_descr_get */
+        0,                                              /* tp_descr_set */
+        0,                                              /* tp_dictoffset */
+        (initproc)prepare_protocol_init,                /* tp_init */
+        0,                                              /* tp_alloc */
+        0,                                              /* tp_new */
+        0                                               /* tp_free */
+};
+
+extern int prepare_protocol_setup_types(void)
+{
+    SQLitePrepareProtocolType.tp_new = PyType_GenericNew;
+    SQLitePrepareProtocolType.ob_type= &PyType_Type;
+    return PyType_Ready(&SQLitePrepareProtocolType);
+}
diff --git a/Modules/_sqlite/prepare_protocol.h b/Modules/_sqlite/prepare_protocol.h
new file mode 100644
index 0000000..2fc4f61
--- /dev/null
+++ b/Modules/_sqlite/prepare_protocol.h
@@ -0,0 +1,41 @@
+/* prepare_protocol.h - the protocol for preparing values for SQLite
+ *
+ * Copyright (C) 2005 Gerhard Häring <gh@ghaering.de>
+ *
+ * This file is part of pysqlite.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * 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. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 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 or altered from any source distribution.
+ */
+
+#ifndef PYSQLITE_PREPARE_PROTOCOL_H
+#define PYSQLITE_PREPARE_PROTOCOL_H
+#include "Python.h"
+
+typedef struct
+{
+    PyObject_HEAD
+} SQLitePrepareProtocol;
+
+extern PyTypeObject SQLitePrepareProtocolType;
+
+int prepare_protocol_init(SQLitePrepareProtocol* self, PyObject* args, PyObject* kwargs);
+void prepare_protocol_dealloc(SQLitePrepareProtocol* self);
+
+int prepare_protocol_setup_types(void);
+
+#define UNKNOWN (-1)
+#endif
diff --git a/Modules/_sqlite/row.c b/Modules/_sqlite/row.c
new file mode 100644
index 0000000..80b6135
--- /dev/null
+++ b/Modules/_sqlite/row.c
@@ -0,0 +1,202 @@
+/* row.c - an enhanced tuple for database rows
+ *
+ * Copyright (C) 2005-2006 Gerhard Häring <gh@ghaering.de>
+ *
+ * This file is part of pysqlite.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * 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. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 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 or altered from any source distribution.
+ */
+
+#include "row.h"
+#include "cursor.h"
+#include "sqlitecompat.h"
+
+void row_dealloc(Row* self)
+{
+    Py_XDECREF(self->data);
+    Py_XDECREF(self->description);
+
+    self->ob_type->tp_free((PyObject*)self);
+}
+
+int row_init(Row* self, PyObject* args, PyObject* kwargs)
+{
+    PyObject* data;
+    Cursor* cursor;
+
+    self->data = 0;
+    self->description = 0;
+
+    if (!PyArg_ParseTuple(args, "OO", &cursor, &data)) {
+        return -1;
+    }
+
+    if (!PyObject_IsInstance((PyObject*)cursor, (PyObject*)&CursorType)) {
+        PyErr_SetString(PyExc_TypeError, "instance of cursor required for first argument");
+        return -1;
+    }
+
+    if (!PyTuple_Check(data)) {
+        PyErr_SetString(PyExc_TypeError, "tuple required for second argument");
+        return -1;
+    }
+
+    Py_INCREF(data);
+    self->data = data;
+
+    Py_INCREF(cursor->description);
+    self->description = cursor->description;
+
+    return 0;
+}
+
+PyObject* row_subscript(Row* self, PyObject* idx)
+{
+    long _idx;
+    char* key;
+    int nitems, i;
+    char* compare_key;
+
+    char* p1;
+    char* p2;
+
+    PyObject* item;
+
+    if (PyInt_Check(idx)) {
+        _idx = PyInt_AsLong(idx);
+        item = PyTuple_GetItem(self->data, _idx);
+        Py_XINCREF(item);
+        return item;
+    } else if (PyLong_Check(idx)) {
+        _idx = PyLong_AsLong(idx);
+        item = PyTuple_GetItem(self->data, _idx);
+        Py_XINCREF(item);
+        return item;
+    } else if (PyString_Check(idx)) {
+        key = PyString_AsString(idx);
+
+        nitems = PyTuple_Size(self->description);
+
+        for (i = 0; i < nitems; i++) {
+            compare_key = PyString_AsString(PyTuple_GET_ITEM(PyTuple_GET_ITEM(self->description, i), 0));
+            if (!compare_key) {
+                return NULL;
+            }
+
+            p1 = key;
+            p2 = compare_key;
+
+            while (1) {
+                if ((*p1 == (char)0) || (*p2 == (char)0)) {
+                    break;
+                }
+
+                if ((*p1 | 0x20) != (*p2 | 0x20)) {
+                    break;
+                }
+
+                p1++;
+                p2++;
+            }
+
+            if ((*p1 == (char)0) && (*p2 == (char)0)) {
+                /* found item */
+                item = PyTuple_GetItem(self->data, i);
+                Py_INCREF(item);
+                return item;
+            }
+
+        }
+
+        PyErr_SetString(PyExc_IndexError, "No item with that key");
+        return NULL;
+    } else if (PySlice_Check(idx)) {
+        PyErr_SetString(PyExc_ValueError, "slices not implemented, yet");
+        return NULL;
+    } else {
+        PyErr_SetString(PyExc_IndexError, "Index must be int or string");
+        return NULL;
+    }
+}
+
+Py_ssize_t row_length(Row* self, PyObject* args, PyObject* kwargs)
+{
+    return PyTuple_GET_SIZE(self->data);
+}
+
+static int row_print(Row* self, FILE *fp, int flags)
+{
+    return (&PyTuple_Type)->tp_print(self->data, fp, flags);
+}
+
+
+PyMappingMethods row_as_mapping = {
+    /* mp_length        */ (lenfunc)row_length,
+    /* mp_subscript     */ (binaryfunc)row_subscript,
+    /* mp_ass_subscript */ (objobjargproc)0,
+};
+
+
+PyTypeObject RowType = {
+        PyObject_HEAD_INIT(NULL)
+        0,                                              /* ob_size */
+        MODULE_NAME ".Row",                             /* tp_name */
+        sizeof(Row),                                    /* tp_basicsize */
+        0,                                              /* tp_itemsize */
+        (destructor)row_dealloc,                        /* tp_dealloc */
+        (printfunc)row_print,                           /* tp_print */
+        0,                                              /* tp_getattr */
+        0,                                              /* tp_setattr */
+        0,                                              /* tp_compare */
+        0,                                              /* tp_repr */
+        0,                                              /* tp_as_number */
+        0,                                              /* tp_as_sequence */
+        0,                                              /* tp_as_mapping */
+        0,                                              /* tp_hash */
+        0,                                              /* tp_call */
+        0,                                              /* tp_str */
+        0,                                              /* tp_getattro */
+        0,                                              /* tp_setattro */
+        0,                                              /* tp_as_buffer */
+        Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,         /* tp_flags */
+        0,                                              /* tp_doc */
+        0,                                              /* tp_traverse */
+        0,                                              /* tp_clear */
+        0,                                              /* tp_richcompare */
+        0,                                              /* tp_weaklistoffset */
+        0,                                              /* tp_iter */
+        0,                                              /* tp_iternext */
+        0,                                              /* tp_methods */
+        0,                                              /* tp_members */
+        0,                                              /* tp_getset */
+        0,                                              /* tp_base */
+        0,                                              /* tp_dict */
+        0,                                              /* tp_descr_get */
+        0,                                              /* tp_descr_set */
+        0,                                              /* tp_dictoffset */
+        (initproc)row_init,                             /* tp_init */
+        0,                                              /* tp_alloc */
+        0,                                              /* tp_new */
+        0                                               /* tp_free */
+};
+
+extern int row_setup_types(void)
+{
+    RowType.tp_new = PyType_GenericNew;
+    RowType.tp_as_mapping = &row_as_mapping;
+    return PyType_Ready(&RowType);
+}
diff --git a/Modules/_sqlite/row.h b/Modules/_sqlite/row.h
new file mode 100644
index 0000000..c6e083c
--- /dev/null
+++ b/Modules/_sqlite/row.h
@@ -0,0 +1,39 @@
+/* row.h - an enhanced tuple for database rows
+ *
+ * Copyright (C) 2005 Gerhard Häring <gh@ghaering.de>
+ *
+ * This file is part of pysqlite.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * 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. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 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 or altered from any source distribution.
+ */
+
+#ifndef PYSQLITE_ROW_H
+#define PYSQLITE_ROW_H
+#include "Python.h"
+
+typedef struct _Row
+{
+    PyObject_HEAD
+    PyObject* data;
+    PyObject* description;
+} Row;
+
+extern PyTypeObject RowType;
+
+int row_setup_types(void);
+
+#endif
diff --git a/Modules/_sqlite/sqlitecompat.h b/Modules/_sqlite/sqlitecompat.h
new file mode 100644
index 0000000..c379825
--- /dev/null
+++ b/Modules/_sqlite/sqlitecompat.h
@@ -0,0 +1,34 @@
+/* sqlitecompat.h - compatibility macros
+ *
+ * Copyright (C) 2006 Gerhard Häring <gh@ghaering.de>
+ *
+ * This file is part of pysqlite.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * 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. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 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 or altered from any source distribution.
+ */
+
+#ifndef PYSQLITE_COMPAT_H
+#define PYSQLITE_COMPAT_H
+
+/* define Py_ssize_t for pre-2.5 versions of Python */
+
+#if PY_VERSION_HEX < 0x02050000
+typedef int Py_ssize_t;
+typedef int (*lenfunc)(PyObject*);
+#endif
+
+#endif
diff --git a/Modules/_sqlite/statement.c b/Modules/_sqlite/statement.c
new file mode 100644
index 0000000..0c93651
--- /dev/null
+++ b/Modules/_sqlite/statement.c
@@ -0,0 +1,427 @@
+/* statement.c - the statement type
+ *
+ * Copyright (C) 2005-2006 Gerhard Häring <gh@ghaering.de>
+ *
+ * This file is part of pysqlite.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * 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. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 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 or altered from any source distribution.
+ */
+
+#include "statement.h"
+#include "cursor.h"
+#include "connection.h"
+#include "microprotocols.h"
+#include "prepare_protocol.h"
+#include "sqlitecompat.h"
+
+/* prototypes */
+int check_remaining_sql(const char* tail);
+
+typedef enum {
+    LINECOMMENT_1,
+    IN_LINECOMMENT,
+    COMMENTSTART_1,
+    IN_COMMENT,
+    COMMENTEND_1,
+    NORMAL
+} parse_remaining_sql_state;
+
+int statement_create(Statement* self, Connection* connection, PyObject* sql)
+{
+    const char* tail;
+    int rc;
+    PyObject* sql_str;
+    char* sql_cstr;
+
+    self->st = NULL;
+    self->in_use = 0;
+
+    if (PyString_Check(sql)) {
+        sql_str = sql;
+        Py_INCREF(sql_str);
+    } else if (PyUnicode_Check(sql)) {
+        sql_str = PyUnicode_AsUTF8String(sql);
+        if (!sql_str) {
+            rc = PYSQLITE_SQL_WRONG_TYPE;
+            return rc;
+        }
+    } else {
+        rc = PYSQLITE_SQL_WRONG_TYPE;
+        return rc;
+    }
+
+    self->sql = sql_str;
+
+    sql_cstr = PyString_AsString(sql_str);
+
+    rc = sqlite3_prepare(connection->db,
+                         sql_cstr,
+                         -1,
+                         &self->st,
+                         &tail);
+
+    self->db = connection->db;
+
+    if (rc == SQLITE_OK && check_remaining_sql(tail)) {
+        (void)sqlite3_finalize(self->st);
+        self->st = NULL;
+        rc = PYSQLITE_TOO_MUCH_SQL;
+    }
+
+    return rc;
+}
+
+int statement_bind_parameter(Statement* self, int pos, PyObject* parameter)
+{
+    int rc = SQLITE_OK;
+    long longval;
+#ifdef HAVE_LONG_LONG
+    PY_LONG_LONG longlongval;
+#endif
+    const char* buffer;
+    char* string;
+    Py_ssize_t buflen;
+    PyObject* stringval;
+
+    if (parameter == Py_None) {
+        rc = sqlite3_bind_null(self->st, pos);
+    } else if (PyInt_Check(parameter)) {
+        longval = PyInt_AsLong(parameter);
+        rc = sqlite3_bind_int64(self->st, pos, (sqlite_int64)longval);
+#ifdef HAVE_LONG_LONG
+    } else if (PyLong_Check(parameter)) {
+        longlongval = PyLong_AsLongLong(parameter);
+        /* in the overflow error case, longlongval is -1, and an exception is set */
+        rc = sqlite3_bind_int64(self->st, pos, (sqlite_int64)longlongval);
+#endif
+    } else if (PyFloat_Check(parameter)) {
+        rc = sqlite3_bind_double(self->st, pos, PyFloat_AsDouble(parameter));
+    } else if (PyBuffer_Check(parameter)) {
+        if (PyObject_AsCharBuffer(parameter, &buffer, &buflen) == 0) {
+            rc = sqlite3_bind_blob(self->st, pos, buffer, buflen, SQLITE_TRANSIENT);
+        } else {
+            PyErr_SetString(PyExc_ValueError, "could not convert BLOB to buffer");
+            rc = -1;
+        }
+    } else if PyString_Check(parameter) {
+        string = PyString_AsString(parameter);
+        rc = sqlite3_bind_text(self->st, pos, string, -1, SQLITE_TRANSIENT);
+    } else if PyUnicode_Check(parameter) {
+        stringval = PyUnicode_AsUTF8String(parameter);
+        string = PyString_AsString(stringval);
+        rc = sqlite3_bind_text(self->st, pos, string, -1, SQLITE_TRANSIENT);
+        Py_DECREF(stringval);
+    } else {
+        rc = -1;
+    }
+
+    return rc;
+}
+
+void statement_bind_parameters(Statement* self, PyObject* parameters)
+{
+    PyObject* current_param;
+    PyObject* adapted;
+    const char* binding_name;
+    int i;
+    int rc;
+    int num_params_needed;
+    int num_params;
+
+    Py_BEGIN_ALLOW_THREADS
+    num_params_needed = sqlite3_bind_parameter_count(self->st);
+    Py_END_ALLOW_THREADS
+
+    if (PyDict_Check(parameters)) {
+        /* parameters passed as dictionary */
+        for (i = 1; i <= num_params_needed; i++) {
+            Py_BEGIN_ALLOW_THREADS
+            binding_name = sqlite3_bind_parameter_name(self->st, i);
+            Py_END_ALLOW_THREADS
+            if (!binding_name) {
+                PyErr_Format(ProgrammingError, "Binding %d has no name, but you supplied a dictionary (which has only names).", i);
+                return;
+            }
+
+            binding_name++; /* skip first char (the colon) */
+            current_param = PyDict_GetItemString(parameters, binding_name);
+            if (!current_param) {
+                PyErr_Format(ProgrammingError, "You did not supply a value for binding %d.", i);
+                return;
+            }
+
+            Py_INCREF(current_param);
+            adapted = microprotocols_adapt(current_param, (PyObject*)&SQLitePrepareProtocolType, NULL);
+            if (adapted) {
+                Py_DECREF(current_param);
+            } else {
+                PyErr_Clear();
+                adapted = current_param;
+            }
+
+            rc = statement_bind_parameter(self, i, adapted);
+            Py_DECREF(adapted);
+
+            if (rc != SQLITE_OK) {
+                PyErr_Format(InterfaceError, "Error binding parameter :%s - probably unsupported type.", binding_name);
+                return;
+           }
+        }
+    } else {
+        /* parameters passed as sequence */
+        num_params = PySequence_Length(parameters);
+        if (num_params != num_params_needed) {
+            PyErr_Format(ProgrammingError, "Incorrect number of bindings supplied. The current statement uses %d, and there are %d supplied.",
+                         num_params_needed, num_params);
+            return;
+        }
+        for (i = 0; i < num_params; i++) {
+            current_param = PySequence_GetItem(parameters, i);
+            if (!current_param) {
+                return;
+            }
+            adapted = microprotocols_adapt(current_param, (PyObject*)&SQLitePrepareProtocolType, NULL);
+
+            if (adapted) {
+                Py_DECREF(current_param);
+            } else {
+                PyErr_Clear();
+                adapted = current_param;
+            }
+
+            rc = statement_bind_parameter(self, i + 1, adapted);
+            Py_DECREF(adapted);
+
+            if (rc != SQLITE_OK) {
+                PyErr_Format(InterfaceError, "Error binding parameter %d - probably unsupported type.", i);
+                return;
+            }
+        }
+    }
+}
+
+int statement_recompile(Statement* self, PyObject* params)
+{
+    const char* tail;
+    int rc;
+    char* sql_cstr;
+    sqlite3_stmt* new_st;
+
+    sql_cstr = PyString_AsString(self->sql);
+
+    rc = sqlite3_prepare(self->db,
+                         sql_cstr,
+                         -1,
+                         &new_st,
+                         &tail);
+
+    if (rc == SQLITE_OK) {
+        /* The efficient sqlite3_transfer_bindings is only available in SQLite
+         * version 3.2.2 or later. For older SQLite releases, that might not
+         * even define SQLITE_VERSION_NUMBER, we do it the manual way.
+         */
+        #ifdef SQLITE_VERSION_NUMBER
+        #if SQLITE_VERSION_NUMBER >= 3002002
+        (void)sqlite3_transfer_bindings(self->st, new_st);
+        #endif
+        #else
+        statement_bind_parameters(self, params);
+        #endif
+
+        (void)sqlite3_finalize(self->st);
+        self->st = new_st;
+    }
+
+    return rc;
+}
+
+int statement_finalize(Statement* self)
+{
+    int rc;
+
+    rc = SQLITE_OK;
+    if (self->st) {
+        Py_BEGIN_ALLOW_THREADS
+        rc = sqlite3_finalize(self->st);
+        Py_END_ALLOW_THREADS
+        self->st = NULL;
+    }
+
+    self->in_use = 0;
+
+    return rc;
+}
+
+int statement_reset(Statement* self)
+{
+    int rc;
+
+    rc = SQLITE_OK;
+
+    if (self->in_use && self->st) {
+        Py_BEGIN_ALLOW_THREADS
+        rc = sqlite3_reset(self->st);
+        Py_END_ALLOW_THREADS
+
+        if (rc == SQLITE_OK) {
+            self->in_use = 0;
+        }
+    }
+
+    return rc;
+}
+
+void statement_mark_dirty(Statement* self)
+{
+    self->in_use = 1;
+}
+
+void statement_dealloc(Statement* self)
+{
+    int rc;
+
+    if (self->st) {
+        Py_BEGIN_ALLOW_THREADS
+        rc = sqlite3_finalize(self->st);
+        Py_END_ALLOW_THREADS
+    }
+
+    self->st = NULL;
+
+    Py_XDECREF(self->sql);
+
+    self->ob_type->tp_free((PyObject*)self);
+}
+
+/*
+ * Checks if there is anything left in an SQL string after SQLite compiled it.
+ * This is used to check if somebody tried to execute more than one SQL command
+ * with one execute()/executemany() command, which the DB-API and we don't
+ * allow.
+ *
+ * Returns 1 if there is more left than should be. 0 if ok.
+ */
+int check_remaining_sql(const char* tail)
+{
+    const char* pos = tail;
+
+    parse_remaining_sql_state state = NORMAL;
+
+    for (;;) {
+        switch (*pos) {
+            case 0:
+                return 0;
+            case '-':
+                if (state == NORMAL) {
+                    state  = LINECOMMENT_1;
+                } else if (state == LINECOMMENT_1) {
+                    state = IN_LINECOMMENT;
+                }
+                break;
+            case ' ':
+            case '\t':
+                break;
+            case '\n':
+            case 13:
+                if (state == IN_LINECOMMENT) {
+                    state = NORMAL;
+                }
+                break;
+            case '/':
+                if (state == NORMAL) {
+                    state = COMMENTSTART_1;
+                } else if (state == COMMENTEND_1) {
+                    state = NORMAL;
+                } else if (state == COMMENTSTART_1) {
+                    return 1;
+                }
+                break;
+            case '*':
+                if (state == NORMAL) {
+                    return 1;
+                } else if (state == LINECOMMENT_1) {
+                    return 1;
+                } else if (state == COMMENTSTART_1) {
+                    state = IN_COMMENT;
+                } else if (state == IN_COMMENT) {
+                    state = COMMENTEND_1;
+                }
+                break;
+            default:
+                if (state == COMMENTEND_1) {
+                    state = IN_COMMENT;
+                } else if (state == IN_LINECOMMENT) {
+                } else if (state == IN_COMMENT) {
+                } else {
+                    return 1;
+                }
+        }
+
+        pos++;
+    }
+
+    return 0;
+}
+
+PyTypeObject StatementType = {
+        PyObject_HEAD_INIT(NULL)
+        0,                                              /* ob_size */
+        MODULE_NAME ".Statement",                       /* tp_name */
+        sizeof(Statement),                              /* tp_basicsize */
+        0,                                              /* tp_itemsize */
+        (destructor)statement_dealloc,                  /* tp_dealloc */
+        0,                                              /* tp_print */
+        0,                                              /* tp_getattr */
+        0,                                              /* tp_setattr */
+        0,                                              /* tp_compare */
+        0,                                              /* tp_repr */
+        0,                                              /* tp_as_number */
+        0,                                              /* tp_as_sequence */
+        0,                                              /* tp_as_mapping */
+        0,                                              /* tp_hash */
+        0,                                              /* tp_call */
+        0,                                              /* tp_str */
+        0,                                              /* tp_getattro */
+        0,                                              /* tp_setattro */
+        0,                                              /* tp_as_buffer */
+        Py_TPFLAGS_DEFAULT,                             /* tp_flags */
+        0,                                              /* tp_doc */
+        0,                                              /* tp_traverse */
+        0,                                              /* tp_clear */
+        0,                                              /* tp_richcompare */
+        0,                                              /* tp_weaklistoffset */
+        0,                                              /* tp_iter */
+        0,                                              /* tp_iternext */
+        0,                                              /* tp_methods */
+        0,                                              /* tp_members */
+        0,                                              /* tp_getset */
+        0,                                              /* tp_base */
+        0,                                              /* tp_dict */
+        0,                                              /* tp_descr_get */
+        0,                                              /* tp_descr_set */
+        0,                                              /* tp_dictoffset */
+        (initproc)0,                                    /* tp_init */
+        0,                                              /* tp_alloc */
+        0,                                              /* tp_new */
+        0                                               /* tp_free */
+};
+
+extern int statement_setup_types(void)
+{
+    StatementType.tp_new = PyType_GenericNew;
+    return PyType_Ready(&StatementType);
+}
diff --git a/Modules/_sqlite/statement.h b/Modules/_sqlite/statement.h
new file mode 100644
index 0000000..e45a0fc
--- /dev/null
+++ b/Modules/_sqlite/statement.h
@@ -0,0 +1,58 @@
+/* statement.h - definitions for the statement type
+ *
+ * Copyright (C) 2005 Gerhard Häring <gh@ghaering.de>
+ *
+ * This file is part of pysqlite.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * 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. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 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 or altered from any source distribution.
+ */
+
+#ifndef PYSQLITE_STATEMENT_H
+#define PYSQLITE_STATEMENT_H
+#include "Python.h"
+
+#include "connection.h"
+#include "sqlite3.h"
+
+#define PYSQLITE_TOO_MUCH_SQL (-100)
+#define PYSQLITE_SQL_WRONG_TYPE (-101)
+
+typedef struct
+{
+    PyObject_HEAD
+    sqlite3* db;
+    sqlite3_stmt* st;
+    PyObject* sql;
+    int in_use;
+} Statement;
+
+extern PyTypeObject StatementType;
+
+int statement_create(Statement* self, Connection* connection, PyObject* sql);
+void statement_dealloc(Statement* self);
+
+int statement_bind_parameter(Statement* self, int pos, PyObject* parameter);
+void statement_bind_parameters(Statement* self, PyObject* parameters);
+
+int statement_recompile(Statement* self, PyObject* parameters);
+int statement_finalize(Statement* self);
+int statement_reset(Statement* self);
+void statement_mark_dirty(Statement* self);
+
+int statement_setup_types(void);
+
+#endif
diff --git a/Modules/_sqlite/util.c b/Modules/_sqlite/util.c
new file mode 100644
index 0000000..33748a6
--- /dev/null
+++ b/Modules/_sqlite/util.c
@@ -0,0 +1,96 @@
+/* util.c - various utility functions
+ *
+ * Copyright (C) 2005-2006 Gerhard Häring <gh@ghaering.de>
+ *
+ * This file is part of pysqlite.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * 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. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 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 or altered from any source distribution.
+ */
+
+#include "module.h"
+#include "connection.h"
+
+int _sqlite_step_with_busyhandler(sqlite3_stmt* statement, Connection* connection
+)
+{
+    int rc;
+
+    Py_BEGIN_ALLOW_THREADS
+    rc = sqlite3_step(statement);
+    Py_END_ALLOW_THREADS
+
+    return rc;
+}
+
+/**
+ * Checks the SQLite error code and sets the appropriate DB-API exception.
+ * Returns the error code (0 means no error occured).
+ */
+int _seterror(sqlite3* db)
+{
+    int errorcode;
+
+    errorcode = sqlite3_errcode(db);
+
+    switch (errorcode)
+    {
+        case SQLITE_OK:
+            PyErr_Clear();
+            break;
+        case SQLITE_INTERNAL:
+        case SQLITE_NOTFOUND:
+            PyErr_SetString(InternalError, sqlite3_errmsg(db));
+            break;
+        case SQLITE_NOMEM:
+            (void)PyErr_NoMemory();
+            break;
+        case SQLITE_ERROR:
+        case SQLITE_PERM:
+        case SQLITE_ABORT:
+        case SQLITE_BUSY:
+        case SQLITE_LOCKED:
+        case SQLITE_READONLY:
+        case SQLITE_INTERRUPT:
+        case SQLITE_IOERR:
+        case SQLITE_FULL:
+        case SQLITE_CANTOPEN:
+        case SQLITE_PROTOCOL:
+        case SQLITE_EMPTY:
+        case SQLITE_SCHEMA:
+            PyErr_SetString(OperationalError, sqlite3_errmsg(db));
+            break;
+        case SQLITE_CORRUPT:
+            PyErr_SetString(DatabaseError, sqlite3_errmsg(db));
+            break;
+        case SQLITE_TOOBIG:
+            PyErr_SetString(DataError, sqlite3_errmsg(db));
+            break;
+        case SQLITE_CONSTRAINT:
+        case SQLITE_MISMATCH:
+            PyErr_SetString(IntegrityError, sqlite3_errmsg(db));
+            break;
+        case SQLITE_MISUSE:
+            PyErr_SetString(ProgrammingError, sqlite3_errmsg(db));
+            break;
+        default:
+            PyErr_SetString(DatabaseError, sqlite3_errmsg(db));
+            break;
+    }
+
+    return errorcode;
+}
+
diff --git a/Modules/_sqlite/util.h b/Modules/_sqlite/util.h
new file mode 100644
index 0000000..e99a4dd
--- /dev/null
+++ b/Modules/_sqlite/util.h
@@ -0,0 +1,38 @@
+/* util.h - various utility functions
+ *
+ * Copyright (C) 2005-2006 Gerhard Häring <gh@ghaering.de>
+ *
+ * This file is part of pysqlite.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * 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. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 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 or altered from any source distribution.
+ */
+
+#ifndef PYSQLITE_UTIL_H
+#define PYSQLITE_UTIL_H
+#include "Python.h"
+#include "pythread.h"
+#include "sqlite3.h"
+#include "connection.h"
+
+int _sqlite_step_with_busyhandler(sqlite3_stmt* statement, Connection* connection);
+
+/**
+ * Checks the SQLite error code and sets the appropriate DB-API exception.
+ * Returns the error code (0 means no error occured).
+ */
+int _seterror(sqlite3* db);
+#endif