merged the sqlite-integration branch.
This is based on pysqlite2.1.3, and provides a DB-API interface in
the standard library. You'll need sqlite 3.2.2 or later to build
this - if you have an earlier version, the C extension module will
not be built.
diff --git a/Lib/sqlite3/__init__.py b/Lib/sqlite3/__init__.py
new file mode 100644
index 0000000..41ef2b7
--- /dev/null
+++ b/Lib/sqlite3/__init__.py
@@ -0,0 +1,24 @@
+#-*- coding: ISO-8859-1 -*-
+# pysqlite2/__init__.py: the pysqlite2 package.
+#
+# 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.
+
+from dbapi2 import *
diff --git a/Lib/sqlite3/dbapi2.py b/Lib/sqlite3/dbapi2.py
new file mode 100644
index 0000000..e0c8a84
--- /dev/null
+++ b/Lib/sqlite3/dbapi2.py
@@ -0,0 +1,84 @@
+#-*- coding: ISO-8859-1 -*-
+# pysqlite2/dbapi2.py: the DB-API 2.0 interface
+#
+# 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.
+
+import datetime
+
+paramstyle = "qmark"
+
+threadsafety = 1
+
+apilevel = "2.0"
+
+from _sqlite3 import *
+
+import datetime, time
+
+Date = datetime.date
+
+Time = datetime.time
+
+Timestamp = datetime.datetime
+
+def DateFromTicks(ticks):
+    return apply(Date,time.localtime(ticks)[:3])
+
+def TimeFromTicks(ticks):
+    return apply(Time,time.localtime(ticks)[3:6])
+
+def TimestampFromTicks(ticks):
+    return apply(Timestamp,time.localtime(ticks)[:6])
+
+_major, _minor, _micro = version.split(".")
+version_info = (int(_major), int(_minor), _micro)
+_major, _minor, _micro = sqlite_version.split(".")
+sqlite_version_info = (int(_major), int(_minor), _micro)
+
+Binary = buffer
+
+def adapt_date(val):
+    return val.isoformat()
+
+def adapt_datetime(val):
+    return val.isoformat(" ")
+
+def convert_date(val):
+    return datetime.date(*map(int, val.split("-")))
+
+def convert_timestamp(val):
+    datepart, timepart = val.split(" ")
+    year, month, day = map(int, datepart.split("-"))
+    timepart_full = timepart.split(".")
+    hours, minutes, seconds = map(int, timepart_full[0].split(":"))
+    if len(timepart_full) == 2:
+        microseconds = int(float("0." + timepart_full[1]) * 1000000)
+    else:
+        microseconds = 0
+
+    val = datetime.datetime(year, month, day, hours, minutes, seconds, microseconds)
+    return val
+
+
+register_adapter(datetime.date, adapt_date)
+register_adapter(datetime.datetime, adapt_datetime)
+register_converter("date", convert_date)
+register_converter("timestamp", convert_timestamp)
diff --git a/Lib/sqlite3/test/__init__.py b/Lib/sqlite3/test/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/sqlite3/test/__init__.py
diff --git a/Lib/sqlite3/test/dbapi.py b/Lib/sqlite3/test/dbapi.py
new file mode 100644
index 0000000..0d47977
--- /dev/null
+++ b/Lib/sqlite3/test/dbapi.py
@@ -0,0 +1,726 @@
+#-*- coding: ISO-8859-1 -*-
+# pysqlite2/test/dbapi.py: tests for DB-API compliance
+#
+# 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.
+
+import unittest
+import threading
+import sqlite3 as sqlite
+
+class ModuleTests(unittest.TestCase):
+    def CheckAPILevel(self):
+        self.assertEqual(sqlite.apilevel, "2.0",
+                         "apilevel is %s, should be 2.0" % sqlite.apilevel)
+
+    def CheckThreadSafety(self):
+        self.assertEqual(sqlite.threadsafety, 1,
+                         "threadsafety is %d, should be 1" % sqlite.threadsafety)
+
+    def CheckParamStyle(self):
+        self.assertEqual(sqlite.paramstyle, "qmark",
+                         "paramstyle is '%s', should be 'qmark'" %
+                         sqlite.paramstyle)
+
+    def CheckWarning(self):
+        self.assert_(issubclass(sqlite.Warning, StandardError),
+                     "Warning is not a subclass of StandardError")
+
+    def CheckError(self):
+        self.failUnless(issubclass(sqlite.Error, StandardError),
+                        "Error is not a subclass of StandardError")
+
+    def CheckInterfaceError(self):
+        self.failUnless(issubclass(sqlite.InterfaceError, sqlite.Error),
+                        "InterfaceError is not a subclass of Error")
+
+    def CheckDatabaseError(self):
+        self.failUnless(issubclass(sqlite.DatabaseError, sqlite.Error),
+                        "DatabaseError is not a subclass of Error")
+
+    def CheckDataError(self):
+        self.failUnless(issubclass(sqlite.DataError, sqlite.DatabaseError),
+                        "DataError is not a subclass of DatabaseError")
+
+    def CheckOperationalError(self):
+        self.failUnless(issubclass(sqlite.OperationalError, sqlite.DatabaseError),
+                        "OperationalError is not a subclass of DatabaseError")
+
+    def CheckIntegrityError(self):
+        self.failUnless(issubclass(sqlite.IntegrityError, sqlite.DatabaseError),
+                        "IntegrityError is not a subclass of DatabaseError")
+
+    def CheckInternalError(self):
+        self.failUnless(issubclass(sqlite.InternalError, sqlite.DatabaseError),
+                        "InternalError is not a subclass of DatabaseError")
+
+    def CheckProgrammingError(self):
+        self.failUnless(issubclass(sqlite.ProgrammingError, sqlite.DatabaseError),
+                        "ProgrammingError is not a subclass of DatabaseError")
+
+    def CheckNotSupportedError(self):
+        self.failUnless(issubclass(sqlite.NotSupportedError,
+                                   sqlite.DatabaseError),
+                        "NotSupportedError is not a subclass of DatabaseError")
+
+class ConnectionTests(unittest.TestCase):
+    def setUp(self):
+        self.cx = sqlite.connect(":memory:")
+        cu = self.cx.cursor()
+        cu.execute("create table test(id integer primary key, name text)")
+        cu.execute("insert into test(name) values (?)", ("foo",))
+
+    def tearDown(self):
+        self.cx.close()
+
+    def CheckCommit(self):
+        self.cx.commit()
+
+    def CheckCommitAfterNoChanges(self):
+        """
+        A commit should also work when no changes were made to the database.
+        """
+        self.cx.commit()
+        self.cx.commit()
+
+    def CheckRollback(self):
+        self.cx.rollback()
+
+    def CheckRollbackAfterNoChanges(self):
+        """
+        A rollback should also work when no changes were made to the database.
+        """
+        self.cx.rollback()
+        self.cx.rollback()
+
+    def CheckCursor(self):
+        cu = self.cx.cursor()
+
+    def CheckFailedOpen(self):
+        YOU_CANNOT_OPEN_THIS = "/foo/bar/bla/23534/mydb.db"
+        try:
+            con = sqlite.connect(YOU_CANNOT_OPEN_THIS)
+        except sqlite.OperationalError:
+            return
+        self.fail("should have raised an OperationalError")
+
+    def CheckClose(self):
+        self.cx.close()
+
+    def CheckExceptions(self):
+        # Optional DB-API extension.
+        self.failUnlessEqual(self.cx.Warning, sqlite.Warning)
+        self.failUnlessEqual(self.cx.Error, sqlite.Error)
+        self.failUnlessEqual(self.cx.InterfaceError, sqlite.InterfaceError)
+        self.failUnlessEqual(self.cx.DatabaseError, sqlite.DatabaseError)
+        self.failUnlessEqual(self.cx.DataError, sqlite.DataError)
+        self.failUnlessEqual(self.cx.OperationalError, sqlite.OperationalError)
+        self.failUnlessEqual(self.cx.IntegrityError, sqlite.IntegrityError)
+        self.failUnlessEqual(self.cx.InternalError, sqlite.InternalError)
+        self.failUnlessEqual(self.cx.ProgrammingError, sqlite.ProgrammingError)
+        self.failUnlessEqual(self.cx.NotSupportedError, sqlite.NotSupportedError)
+
+class CursorTests(unittest.TestCase):
+    def setUp(self):
+        self.cx = sqlite.connect(":memory:")
+        self.cu = self.cx.cursor()
+        self.cu.execute("create table test(id integer primary key, name text, income number)")
+        self.cu.execute("insert into test(name) values (?)", ("foo",))
+
+    def tearDown(self):
+        self.cu.close()
+        self.cx.close()
+
+    def CheckExecuteNoArgs(self):
+        self.cu.execute("delete from test")
+
+    def CheckExecuteIllegalSql(self):
+        try:
+            self.cu.execute("select asdf")
+            self.fail("should have raised an OperationalError")
+        except sqlite.OperationalError:
+            return
+        except:
+            self.fail("raised wrong exception")
+
+    def CheckExecuteTooMuchSql(self):
+        try:
+            self.cu.execute("select 5+4; select 4+5")
+            self.fail("should have raised a Warning")
+        except sqlite.Warning:
+            return
+        except:
+            self.fail("raised wrong exception")
+
+    def CheckExecuteTooMuchSql2(self):
+        self.cu.execute("select 5+4; -- foo bar")
+
+    def CheckExecuteTooMuchSql3(self):
+        self.cu.execute("""
+            select 5+4;
+
+            /*
+            foo
+            */
+            """)
+
+    def CheckExecuteWrongSqlArg(self):
+        try:
+            self.cu.execute(42)
+            self.fail("should have raised a ValueError")
+        except ValueError:
+            return
+        except:
+            self.fail("raised wrong exception.")
+
+    def CheckExecuteArgInt(self):
+        self.cu.execute("insert into test(id) values (?)", (42,))
+
+    def CheckExecuteArgFloat(self):
+        self.cu.execute("insert into test(income) values (?)", (2500.32,))
+
+    def CheckExecuteArgString(self):
+        self.cu.execute("insert into test(name) values (?)", ("Hugo",))
+
+    def CheckExecuteWrongNoOfArgs1(self):
+        # too many parameters
+        try:
+            self.cu.execute("insert into test(id) values (?)", (17, "Egon"))
+            self.fail("should have raised ProgrammingError")
+        except sqlite.ProgrammingError:
+            pass
+
+    def CheckExecuteWrongNoOfArgs2(self):
+        # too little parameters
+        try:
+            self.cu.execute("insert into test(id) values (?)")
+            self.fail("should have raised ProgrammingError")
+        except sqlite.ProgrammingError:
+            pass
+
+    def CheckExecuteWrongNoOfArgs3(self):
+        # no parameters, parameters are needed
+        try:
+            self.cu.execute("insert into test(id) values (?)")
+            self.fail("should have raised ProgrammingError")
+        except sqlite.ProgrammingError:
+            pass
+
+    def CheckExecuteDictMapping(self):
+        self.cu.execute("insert into test(name) values ('foo')")
+        self.cu.execute("select name from test where name=:name", {"name": "foo"})
+        row = self.cu.fetchone()
+        self.failUnlessEqual(row[0], "foo")
+
+    def CheckExecuteDictMappingTooLittleArgs(self):
+        self.cu.execute("insert into test(name) values ('foo')")
+        try:
+            self.cu.execute("select name from test where name=:name and id=:id", {"name": "foo"})
+            self.fail("should have raised ProgrammingError")
+        except sqlite.ProgrammingError:
+            pass
+
+    def CheckExecuteDictMappingNoArgs(self):
+        self.cu.execute("insert into test(name) values ('foo')")
+        try:
+            self.cu.execute("select name from test where name=:name")
+            self.fail("should have raised ProgrammingError")
+        except sqlite.ProgrammingError:
+            pass
+
+    def CheckExecuteDictMappingUnnamed(self):
+        self.cu.execute("insert into test(name) values ('foo')")
+        try:
+            self.cu.execute("select name from test where name=?", {"name": "foo"})
+            self.fail("should have raised ProgrammingError")
+        except sqlite.ProgrammingError:
+            pass
+
+    def CheckClose(self):
+        self.cu.close()
+
+    def CheckRowcountExecute(self):
+        self.cu.execute("delete from test")
+        self.cu.execute("insert into test(name) values ('foo')")
+        self.cu.execute("insert into test(name) values ('foo')")
+        self.cu.execute("update test set name='bar'")
+        self.failUnlessEqual(self.cu.rowcount, 2)
+
+    def CheckRowcountExecutemany(self):
+        self.cu.execute("delete from test")
+        self.cu.executemany("insert into test(name) values (?)", [(1,), (2,), (3,)])
+        self.failUnlessEqual(self.cu.rowcount, 3)
+
+    # Checks for executemany:
+    # Sequences are required by the DB-API, iterators
+    # enhancements in pysqlite.
+
+    def CheckExecuteManySequence(self):
+        self.cu.executemany("insert into test(income) values (?)", [(x,) for x in range(100, 110)])
+
+    def CheckExecuteManyIterator(self):
+        class MyIter:
+            def __init__(self):
+                self.value = 5
+
+            def next(self):
+                if self.value == 10:
+                    raise StopIteration
+                else:
+                    self.value += 1
+                    return (self.value,)
+
+        self.cu.executemany("insert into test(income) values (?)", MyIter())
+
+    def CheckExecuteManyGenerator(self):
+        def mygen():
+            for i in range(5):
+                yield (i,)
+
+        self.cu.executemany("insert into test(income) values (?)", mygen())
+
+    def CheckExecuteManyWrongSqlArg(self):
+        try:
+            self.cu.executemany(42, [(3,)])
+            self.fail("should have raised a ValueError")
+        except ValueError:
+            return
+        except:
+            self.fail("raised wrong exception.")
+
+    def CheckExecuteManySelect(self):
+        try:
+            self.cu.executemany("select ?", [(3,)])
+            self.fail("should have raised a ProgrammingError")
+        except sqlite.ProgrammingError:
+            return
+        except:
+            self.fail("raised wrong exception.")
+
+    def CheckExecuteManyNotIterable(self):
+        try:
+            self.cu.executemany("insert into test(income) values (?)", 42)
+            self.fail("should have raised a TypeError")
+        except TypeError:
+            return
+        except Exception, e:
+            print "raised", e.__class__
+            self.fail("raised wrong exception.")
+
+    def CheckFetchIter(self):
+        # Optional DB-API extension.
+        self.cu.execute("delete from test")
+        self.cu.execute("insert into test(id) values (?)", (5,))
+        self.cu.execute("insert into test(id) values (?)", (6,))
+        self.cu.execute("select id from test order by id")
+        lst = []
+        for row in self.cu:
+            lst.append(row[0])
+        self.failUnlessEqual(lst[0], 5)
+        self.failUnlessEqual(lst[1], 6)
+
+    def CheckFetchone(self):
+        self.cu.execute("select name from test")
+        row = self.cu.fetchone()
+        self.failUnlessEqual(row[0], "foo")
+        row = self.cu.fetchone()
+        self.failUnlessEqual(row, None)
+
+    def CheckFetchoneNoStatement(self):
+        cur = self.cx.cursor()
+        row = cur.fetchone()
+        self.failUnlessEqual(row, None)
+
+    def CheckArraySize(self):
+        # must default ot 1
+        self.failUnlessEqual(self.cu.arraysize, 1)
+
+        # now set to 2
+        self.cu.arraysize = 2
+
+        # now make the query return 3 rows
+        self.cu.execute("delete from test")
+        self.cu.execute("insert into test(name) values ('A')")
+        self.cu.execute("insert into test(name) values ('B')")
+        self.cu.execute("insert into test(name) values ('C')")
+        self.cu.execute("select name from test")
+        res = self.cu.fetchmany()
+
+        self.failUnlessEqual(len(res), 2)
+
+    def CheckFetchmany(self):
+        self.cu.execute("select name from test")
+        res = self.cu.fetchmany(100)
+        self.failUnlessEqual(len(res), 1)
+        res = self.cu.fetchmany(100)
+        self.failUnlessEqual(res, [])
+
+    def CheckFetchall(self):
+        self.cu.execute("select name from test")
+        res = self.cu.fetchall()
+        self.failUnlessEqual(len(res), 1)
+        res = self.cu.fetchall()
+        self.failUnlessEqual(res, [])
+
+    def CheckSetinputsizes(self):
+        self.cu.setinputsizes([3, 4, 5])
+
+    def CheckSetoutputsize(self):
+        self.cu.setoutputsize(5, 0)
+
+    def CheckSetoutputsizeNoColumn(self):
+        self.cu.setoutputsize(42)
+
+    def CheckCursorConnection(self):
+        # Optional DB-API extension.
+        self.failUnlessEqual(self.cu.connection, self.cx)
+
+    def CheckWrongCursorCallable(self):
+        try:
+            def f(): pass
+            cur = self.cx.cursor(f)
+            self.fail("should have raised a TypeError")
+        except TypeError:
+            return
+        self.fail("should have raised a ValueError")
+
+    def CheckCursorWrongClass(self):
+        class Foo: pass
+        foo = Foo()
+        try:
+            cur = sqlite.Cursor(foo)
+            self.fail("should have raised a ValueError")
+        except TypeError:
+            pass
+
+class ThreadTests(unittest.TestCase):
+    def setUp(self):
+        self.con = sqlite.connect(":memory:")
+        self.cur = self.con.cursor()
+        self.cur.execute("create table test(id integer primary key, name text, bin binary, ratio number, ts timestamp)")
+
+    def tearDown(self):
+        self.cur.close()
+        self.con.close()
+
+    def CheckConCursor(self):
+        def run(con, errors):
+            try:
+                cur = con.cursor()
+                errors.append("did not raise ProgrammingError")
+                return
+            except sqlite.ProgrammingError:
+                return
+            except:
+                errors.append("raised wrong exception")
+
+        errors = []
+        t = threading.Thread(target=run, kwargs={"con": self.con, "errors": errors})
+        t.start()
+        t.join()
+        if len(errors) > 0:
+            self.fail("\n".join(errors))
+
+    def CheckConCommit(self):
+        def run(con, errors):
+            try:
+                con.commit()
+                errors.append("did not raise ProgrammingError")
+                return
+            except sqlite.ProgrammingError:
+                return
+            except:
+                errors.append("raised wrong exception")
+
+        errors = []
+        t = threading.Thread(target=run, kwargs={"con": self.con, "errors": errors})
+        t.start()
+        t.join()
+        if len(errors) > 0:
+            self.fail("\n".join(errors))
+
+    def CheckConRollback(self):
+        def run(con, errors):
+            try:
+                con.rollback()
+                errors.append("did not raise ProgrammingError")
+                return
+            except sqlite.ProgrammingError:
+                return
+            except:
+                errors.append("raised wrong exception")
+
+        errors = []
+        t = threading.Thread(target=run, kwargs={"con": self.con, "errors": errors})
+        t.start()
+        t.join()
+        if len(errors) > 0:
+            self.fail("\n".join(errors))
+
+    def CheckConClose(self):
+        def run(con, errors):
+            try:
+                con.close()
+                errors.append("did not raise ProgrammingError")
+                return
+            except sqlite.ProgrammingError:
+                return
+            except:
+                errors.append("raised wrong exception")
+
+        errors = []
+        t = threading.Thread(target=run, kwargs={"con": self.con, "errors": errors})
+        t.start()
+        t.join()
+        if len(errors) > 0:
+            self.fail("\n".join(errors))
+
+    def CheckCurImplicitBegin(self):
+        def run(cur, errors):
+            try:
+                cur.execute("insert into test(name) values ('a')")
+                errors.append("did not raise ProgrammingError")
+                return
+            except sqlite.ProgrammingError:
+                return
+            except:
+                errors.append("raised wrong exception")
+
+        errors = []
+        t = threading.Thread(target=run, kwargs={"cur": self.cur, "errors": errors})
+        t.start()
+        t.join()
+        if len(errors) > 0:
+            self.fail("\n".join(errors))
+
+    def CheckCurClose(self):
+        def run(cur, errors):
+            try:
+                cur.close()
+                errors.append("did not raise ProgrammingError")
+                return
+            except sqlite.ProgrammingError:
+                return
+            except:
+                errors.append("raised wrong exception")
+
+        errors = []
+        t = threading.Thread(target=run, kwargs={"cur": self.cur, "errors": errors})
+        t.start()
+        t.join()
+        if len(errors) > 0:
+            self.fail("\n".join(errors))
+
+    def CheckCurExecute(self):
+        def run(cur, errors):
+            try:
+                cur.execute("select name from test")
+                errors.append("did not raise ProgrammingError")
+                return
+            except sqlite.ProgrammingError:
+                return
+            except:
+                errors.append("raised wrong exception")
+
+        errors = []
+        self.cur.execute("insert into test(name) values ('a')")
+        t = threading.Thread(target=run, kwargs={"cur": self.cur, "errors": errors})
+        t.start()
+        t.join()
+        if len(errors) > 0:
+            self.fail("\n".join(errors))
+
+    def CheckCurIterNext(self):
+        def run(cur, errors):
+            try:
+                row = cur.fetchone()
+                errors.append("did not raise ProgrammingError")
+                return
+            except sqlite.ProgrammingError:
+                return
+            except:
+                errors.append("raised wrong exception")
+
+        errors = []
+        self.cur.execute("insert into test(name) values ('a')")
+        self.cur.execute("select name from test")
+        t = threading.Thread(target=run, kwargs={"cur": self.cur, "errors": errors})
+        t.start()
+        t.join()
+        if len(errors) > 0:
+            self.fail("\n".join(errors))
+
+class ConstructorTests(unittest.TestCase):
+    def CheckDate(self):
+        d = sqlite.Date(2004, 10, 28)
+
+    def CheckTime(self):
+        t = sqlite.Time(12, 39, 35)
+
+    def CheckTimestamp(self):
+        ts = sqlite.Timestamp(2004, 10, 28, 12, 39, 35)
+
+    def CheckDateFromTicks(self):
+        d = sqlite.DateFromTicks(42)
+
+    def CheckTimeFromTicks(self):
+        t = sqlite.TimeFromTicks(42)
+
+    def CheckTimestampFromTicks(self):
+        ts = sqlite.TimestampFromTicks(42)
+
+    def CheckBinary(self):
+        b = sqlite.Binary(chr(0) + "'")
+
+class ExtensionTests(unittest.TestCase):
+    def CheckScriptStringSql(self):
+        con = sqlite.connect(":memory:")
+        cur = con.cursor()
+        cur.executescript("""
+            -- bla bla
+            /* a stupid comment */
+            create table a(i);
+            insert into a(i) values (5);
+            """)
+        cur.execute("select i from a")
+        res = cur.fetchone()[0]
+        self.failUnlessEqual(res, 5)
+
+    def CheckScriptStringUnicode(self):
+        con = sqlite.connect(":memory:")
+        cur = con.cursor()
+        cur.executescript(u"""
+            create table a(i);
+            insert into a(i) values (5);
+            select i from a;
+            delete from a;
+            insert into a(i) values (6);
+            """)
+        cur.execute("select i from a")
+        res = cur.fetchone()[0]
+        self.failUnlessEqual(res, 6)
+
+    def CheckScriptErrorIncomplete(self):
+        con = sqlite.connect(":memory:")
+        cur = con.cursor()
+        raised = False
+        try:
+            cur.executescript("create table test(sadfsadfdsa")
+        except sqlite.ProgrammingError:
+            raised = True
+        self.failUnlessEqual(raised, True, "should have raised an exception")
+
+    def CheckScriptErrorNormal(self):
+        con = sqlite.connect(":memory:")
+        cur = con.cursor()
+        raised = False
+        try:
+            cur.executescript("create table test(sadfsadfdsa); select foo from hurz;")
+        except sqlite.OperationalError:
+            raised = True
+        self.failUnlessEqual(raised, True, "should have raised an exception")
+
+    def CheckConnectionExecute(self):
+        con = sqlite.connect(":memory:")
+        result = con.execute("select 5").fetchone()[0]
+        self.failUnlessEqual(result, 5, "Basic test of Connection.execute")
+
+    def CheckConnectionExecutemany(self):
+        con = sqlite.connect(":memory:")
+        con.execute("create table test(foo)")
+        con.executemany("insert into test(foo) values (?)", [(3,), (4,)])
+        result = con.execute("select foo from test order by foo").fetchall()
+        self.failUnlessEqual(result[0][0], 3, "Basic test of Connection.executemany")
+        self.failUnlessEqual(result[1][0], 4, "Basic test of Connection.executemany")
+
+    def CheckConnectionExecutescript(self):
+        con = sqlite.connect(":memory:")
+        con.executescript("create table test(foo); insert into test(foo) values (5);")
+        result = con.execute("select foo from test").fetchone()[0]
+        self.failUnlessEqual(result, 5, "Basic test of Connection.executescript")
+
+class ClosedTests(unittest.TestCase):
+    def setUp(self):
+        pass
+
+    def tearDown(self):
+        pass
+
+    def CheckClosedConCursor(self):
+        con = sqlite.connect(":memory:")
+        con.close()
+        try:
+            cur = con.cursor()
+            self.fail("Should have raised a ProgrammingError")
+        except sqlite.ProgrammingError:
+            pass
+        except:
+            self.fail("Should have raised a ProgrammingError")
+
+    def CheckClosedConCommit(self):
+        con = sqlite.connect(":memory:")
+        con.close()
+        try:
+            con.commit()
+            self.fail("Should have raised a ProgrammingError")
+        except sqlite.ProgrammingError:
+            pass
+        except:
+            self.fail("Should have raised a ProgrammingError")
+
+    def CheckClosedConRollback(self):
+        con = sqlite.connect(":memory:")
+        con.close()
+        try:
+            con.rollback()
+            self.fail("Should have raised a ProgrammingError")
+        except sqlite.ProgrammingError:
+            pass
+        except:
+            self.fail("Should have raised a ProgrammingError")
+
+    def CheckClosedCurExecute(self):
+        con = sqlite.connect(":memory:")
+        cur = con.cursor()
+        con.close()
+        try:
+            cur.execute("select 4")
+            self.fail("Should have raised a ProgrammingError")
+        except sqlite.ProgrammingError:
+            pass
+        except:
+            self.fail("Should have raised a ProgrammingError")
+
+def suite():
+    module_suite = unittest.makeSuite(ModuleTests, "Check")
+    connection_suite = unittest.makeSuite(ConnectionTests, "Check")
+    cursor_suite = unittest.makeSuite(CursorTests, "Check")
+    thread_suite = unittest.makeSuite(ThreadTests, "Check")
+    constructor_suite = unittest.makeSuite(ConstructorTests, "Check")
+    ext_suite = unittest.makeSuite(ExtensionTests, "Check")
+    closed_suite = unittest.makeSuite(ClosedTests, "Check")
+    return unittest.TestSuite((module_suite, connection_suite, cursor_suite, thread_suite, constructor_suite, ext_suite, closed_suite))
+
+def test():
+    runner = unittest.TextTestRunner()
+    runner.run(suite())
+
+if __name__ == "__main__":
+    test()
diff --git a/Lib/sqlite3/test/factory.py b/Lib/sqlite3/test/factory.py
new file mode 100644
index 0000000..8778056
--- /dev/null
+++ b/Lib/sqlite3/test/factory.py
@@ -0,0 +1,164 @@
+#-*- coding: ISO-8859-1 -*-
+# pysqlite2/test/factory.py: tests for the various factories in pysqlite
+#
+# 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.
+
+import unittest
+import sqlite3 as sqlite
+
+class MyConnection(sqlite.Connection):
+    def __init__(self, *args, **kwargs):
+        sqlite.Connection.__init__(self, *args, **kwargs)
+
+def dict_factory(cursor, row):
+    d = {}
+    for idx, col in enumerate(cursor.description):
+        d[col[0]] = row[idx]
+    return d
+
+class MyCursor(sqlite.Cursor):
+    def __init__(self, *args, **kwargs):
+        sqlite.Cursor.__init__(self, *args, **kwargs)
+        self.row_factory = dict_factory
+
+class ConnectionFactoryTests(unittest.TestCase):
+    def setUp(self):
+        self.con = sqlite.connect(":memory:", factory=MyConnection)
+
+    def tearDown(self):
+        self.con.close()
+
+    def CheckIsInstance(self):
+        self.failUnless(isinstance(self.con,
+                                   MyConnection),
+                        "connection is not instance of MyConnection")
+
+class CursorFactoryTests(unittest.TestCase):
+    def setUp(self):
+        self.con = sqlite.connect(":memory:")
+
+    def tearDown(self):
+        self.con.close()
+
+    def CheckIsInstance(self):
+        cur = self.con.cursor(factory=MyCursor)
+        self.failUnless(isinstance(cur,
+                                   MyCursor),
+                        "cursor is not instance of MyCursor")
+
+class RowFactoryTestsBackwardsCompat(unittest.TestCase):
+    def setUp(self):
+        self.con = sqlite.connect(":memory:")
+
+    def CheckIsProducedByFactory(self):
+        cur = self.con.cursor(factory=MyCursor)
+        cur.execute("select 4+5 as foo")
+        row = cur.fetchone()
+        self.failUnless(isinstance(row,
+                                   dict),
+                        "row is not instance of dict")
+        cur.close()
+
+    def tearDown(self):
+        self.con.close()
+
+class RowFactoryTests(unittest.TestCase):
+    def setUp(self):
+        self.con = sqlite.connect(":memory:")
+
+    def CheckCustomFactory(self):
+        self.con.row_factory = lambda cur, row: list(row)
+        row = self.con.execute("select 1, 2").fetchone()
+        self.failUnless(isinstance(row,
+                                   list),
+                        "row is not instance of list")
+
+    def CheckSqliteRow(self):
+        self.con.row_factory = sqlite.Row
+        row = self.con.execute("select 1 as a, 2 as b").fetchone()
+        self.failUnless(isinstance(row,
+                                   sqlite.Row),
+                        "row is not instance of sqlite.Row")
+
+        col1, col2 = row["a"], row["b"]
+        self.failUnless(col1 == 1, "by name: wrong result for column 'a'")
+        self.failUnless(col2 == 2, "by name: wrong result for column 'a'")
+
+        col1, col2 = row["A"], row["B"]
+        self.failUnless(col1 == 1, "by name: wrong result for column 'A'")
+        self.failUnless(col2 == 2, "by name: wrong result for column 'B'")
+
+        col1, col2 = row[0], row[1]
+        self.failUnless(col1 == 1, "by index: wrong result for column 0")
+        self.failUnless(col2 == 2, "by index: wrong result for column 1")
+
+    def tearDown(self):
+        self.con.close()
+
+class TextFactoryTests(unittest.TestCase):
+    def setUp(self):
+        self.con = sqlite.connect(":memory:")
+
+    def CheckUnicode(self):
+        austria = unicode("Österreich", "latin1")
+        row = self.con.execute("select ?", (austria,)).fetchone()
+        self.failUnless(type(row[0]) == unicode, "type of row[0] must be unicode")
+
+    def CheckString(self):
+        self.con.text_factory = str
+        austria = unicode("Österreich", "latin1")
+        row = self.con.execute("select ?", (austria,)).fetchone()
+        self.failUnless(type(row[0]) == str, "type of row[0] must be str")
+        self.failUnless(row[0] == austria.encode("utf-8"), "column must equal original data in UTF-8")
+
+    def CheckCustom(self):
+        self.con.text_factory = lambda x: unicode(x, "utf-8", "ignore")
+        austria = unicode("Österreich", "latin1")
+        row = self.con.execute("select ?", (austria.encode("latin1"),)).fetchone()
+        self.failUnless(type(row[0]) == unicode, "type of row[0] must be unicode")
+        self.failUnless(row[0].endswith(u"reich"), "column must contain original data")
+
+    def CheckOptimizedUnicode(self):
+        self.con.text_factory = sqlite.OptimizedUnicode
+        austria = unicode("Österreich", "latin1")
+        germany = unicode("Deutchland")
+        a_row = self.con.execute("select ?", (austria,)).fetchone()
+        d_row = self.con.execute("select ?", (germany,)).fetchone()
+        self.failUnless(type(a_row[0]) == unicode, "type of non-ASCII row must be unicode")
+        self.failUnless(type(d_row[0]) == str, "type of ASCII-only row must be str")
+
+    def tearDown(self):
+        self.con.close()
+
+def suite():
+    connection_suite = unittest.makeSuite(ConnectionFactoryTests, "Check")
+    cursor_suite = unittest.makeSuite(CursorFactoryTests, "Check")
+    row_suite_compat = unittest.makeSuite(RowFactoryTestsBackwardsCompat, "Check")
+    row_suite = unittest.makeSuite(RowFactoryTests, "Check")
+    text_suite = unittest.makeSuite(TextFactoryTests, "Check")
+    return unittest.TestSuite((connection_suite, cursor_suite, row_suite_compat, row_suite, text_suite))
+
+def test():
+    runner = unittest.TextTestRunner()
+    runner.run(suite())
+
+if __name__ == "__main__":
+    test()
diff --git a/Lib/sqlite3/test/transactions.py b/Lib/sqlite3/test/transactions.py
new file mode 100644
index 0000000..28202cb
--- /dev/null
+++ b/Lib/sqlite3/test/transactions.py
@@ -0,0 +1,154 @@
+#-*- coding: ISO-8859-1 -*-
+# pysqlite2/test/transactions.py: tests transactions
+#
+# 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.
+
+import os, unittest
+import sqlite3 as sqlite
+
+def get_db_path():
+    return "testdb"
+
+class TransactionTests(unittest.TestCase):
+    def setUp(self):
+        try:
+            os.remove(get_db_path())
+        except:
+            pass
+
+        self.con1 = sqlite.connect(get_db_path(), timeout=0.1)
+        self.cur1 = self.con1.cursor()
+
+        self.con2 = sqlite.connect(get_db_path(), timeout=0.1)
+        self.cur2 = self.con2.cursor()
+
+    def tearDown(self):
+        self.cur1.close()
+        self.con1.close()
+
+        self.cur2.close()
+        self.con2.close()
+
+    def CheckDMLdoesAutoCommitBefore(self):
+        self.cur1.execute("create table test(i)")
+        self.cur1.execute("insert into test(i) values (5)")
+        self.cur1.execute("create table test2(j)")
+        self.cur2.execute("select i from test")
+        res = self.cur2.fetchall()
+        self.failUnlessEqual(len(res), 1)
+
+    def CheckInsertStartsTransaction(self):
+        self.cur1.execute("create table test(i)")
+        self.cur1.execute("insert into test(i) values (5)")
+        self.cur2.execute("select i from test")
+        res = self.cur2.fetchall()
+        self.failUnlessEqual(len(res), 0)
+
+    def CheckUpdateStartsTransaction(self):
+        self.cur1.execute("create table test(i)")
+        self.cur1.execute("insert into test(i) values (5)")
+        self.con1.commit()
+        self.cur1.execute("update test set i=6")
+        self.cur2.execute("select i from test")
+        res = self.cur2.fetchone()[0]
+        self.failUnlessEqual(res, 5)
+
+    def CheckDeleteStartsTransaction(self):
+        self.cur1.execute("create table test(i)")
+        self.cur1.execute("insert into test(i) values (5)")
+        self.con1.commit()
+        self.cur1.execute("delete from test")
+        self.cur2.execute("select i from test")
+        res = self.cur2.fetchall()
+        self.failUnlessEqual(len(res), 1)
+
+    def CheckReplaceStartsTransaction(self):
+        self.cur1.execute("create table test(i)")
+        self.cur1.execute("insert into test(i) values (5)")
+        self.con1.commit()
+        self.cur1.execute("replace into test(i) values (6)")
+        self.cur2.execute("select i from test")
+        res = self.cur2.fetchall()
+        self.failUnlessEqual(len(res), 1)
+        self.failUnlessEqual(res[0][0], 5)
+
+    def CheckToggleAutoCommit(self):
+        self.cur1.execute("create table test(i)")
+        self.cur1.execute("insert into test(i) values (5)")
+        self.con1.isolation_level = None
+        self.failUnlessEqual(self.con1.isolation_level, None)
+        self.cur2.execute("select i from test")
+        res = self.cur2.fetchall()
+        self.failUnlessEqual(len(res), 1)
+
+        self.con1.isolation_level = "DEFERRED"
+        self.failUnlessEqual(self.con1.isolation_level , "DEFERRED")
+        self.cur1.execute("insert into test(i) values (5)")
+        self.cur2.execute("select i from test")
+        res = self.cur2.fetchall()
+        self.failUnlessEqual(len(res), 1)
+
+    def CheckRaiseTimeout(self):
+        self.cur1.execute("create table test(i)")
+        self.cur1.execute("insert into test(i) values (5)")
+        try:
+            self.cur2.execute("insert into test(i) values (5)")
+            self.fail("should have raised an OperationalError")
+        except sqlite.OperationalError:
+            pass
+        except:
+            self.fail("should have raised an OperationalError")
+
+class SpecialCommandTests(unittest.TestCase):
+    def setUp(self):
+        self.con = sqlite.connect(":memory:")
+        self.cur = self.con.cursor()
+
+    def CheckVacuum(self):
+        self.cur.execute("create table test(i)")
+        self.cur.execute("insert into test(i) values (5)")
+        self.cur.execute("vacuum")
+
+    def CheckDropTable(self):
+        self.cur.execute("create table test(i)")
+        self.cur.execute("insert into test(i) values (5)")
+        self.cur.execute("drop table test")
+
+    def CheckPragma(self):
+        self.cur.execute("create table test(i)")
+        self.cur.execute("insert into test(i) values (5)")
+        self.cur.execute("pragma count_changes=1")
+
+    def tearDown(self):
+        self.cur.close()
+        self.con.close()
+
+def suite():
+    default_suite = unittest.makeSuite(TransactionTests, "Check")
+    special_command_suite = unittest.makeSuite(SpecialCommandTests, "Check")
+    return unittest.TestSuite((default_suite, special_command_suite))
+
+def test():
+    runner = unittest.TextTestRunner()
+    runner.run(suite())
+
+if __name__ == "__main__":
+    test()
diff --git a/Lib/sqlite3/test/types.py b/Lib/sqlite3/test/types.py
new file mode 100644
index 0000000..e49f7dd
--- /dev/null
+++ b/Lib/sqlite3/test/types.py
@@ -0,0 +1,339 @@
+#-*- coding: ISO-8859-1 -*-
+# pysqlite2/test/types.py: tests for type conversion and detection
+#
+# 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.
+
+import datetime
+import unittest
+import sqlite3 as sqlite
+
+class SqliteTypeTests(unittest.TestCase):
+    def setUp(self):
+        self.con = sqlite.connect(":memory:")
+        self.cur = self.con.cursor()
+        self.cur.execute("create table test(i integer, s varchar, f number, b blob)")
+
+    def tearDown(self):
+        self.cur.close()
+        self.con.close()
+
+    def CheckString(self):
+        self.cur.execute("insert into test(s) values (?)", (u"Österreich",))
+        self.cur.execute("select s from test")
+        row = self.cur.fetchone()
+        self.failUnlessEqual(row[0], u"Österreich")
+
+    def CheckSmallInt(self):
+        self.cur.execute("insert into test(i) values (?)", (42,))
+        self.cur.execute("select i from test")
+        row = self.cur.fetchone()
+        self.failUnlessEqual(row[0], 42)
+
+    def CheckLargeInt(self):
+        num = 2**40
+        self.cur.execute("insert into test(i) values (?)", (num,))
+        self.cur.execute("select i from test")
+        row = self.cur.fetchone()
+        self.failUnlessEqual(row[0], num)
+
+    def CheckFloat(self):
+        val = 3.14
+        self.cur.execute("insert into test(f) values (?)", (val,))
+        self.cur.execute("select f from test")
+        row = self.cur.fetchone()
+        self.failUnlessEqual(row[0], val)
+
+    def CheckBlob(self):
+        val = buffer("Guglhupf")
+        self.cur.execute("insert into test(b) values (?)", (val,))
+        self.cur.execute("select b from test")
+        row = self.cur.fetchone()
+        self.failUnlessEqual(row[0], val)
+
+    def CheckUnicodeExecute(self):
+        self.cur.execute(u"select 'Österreich'")
+        row = self.cur.fetchone()
+        self.failUnlessEqual(row[0], u"Österreich")
+
+class DeclTypesTests(unittest.TestCase):
+    class Foo:
+        def __init__(self, _val):
+            self.val = _val
+
+        def __cmp__(self, other):
+            if not isinstance(other, DeclTypesTests.Foo):
+                raise ValueError
+            if self.val == other.val:
+                return 0
+            else:
+                return 1
+
+        def __conform__(self, protocol):
+            if protocol is sqlite.PrepareProtocol:
+                return self.val
+            else:
+                return None
+
+        def __str__(self):
+            return "<%s>" % self.val
+
+    def setUp(self):
+        self.con = sqlite.connect(":memory:", detect_types=sqlite.PARSE_DECLTYPES)
+        self.cur = self.con.cursor()
+        self.cur.execute("create table test(i int, s str, f float, b bool, u unicode, foo foo, bin blob)")
+
+        # override float, make them always return the same number
+        sqlite.converters["float"] = lambda x: 47.2
+
+        # and implement two custom ones
+        sqlite.converters["bool"] = lambda x: bool(int(x))
+        sqlite.converters["foo"] = DeclTypesTests.Foo
+
+    def tearDown(self):
+        del sqlite.converters["float"]
+        del sqlite.converters["bool"]
+        del sqlite.converters["foo"]
+        self.cur.close()
+        self.con.close()
+
+    def CheckString(self):
+        # default
+        self.cur.execute("insert into test(s) values (?)", ("foo",))
+        self.cur.execute("select s from test")
+        row = self.cur.fetchone()
+        self.failUnlessEqual(row[0], "foo")
+
+    def CheckSmallInt(self):
+        # default
+        self.cur.execute("insert into test(i) values (?)", (42,))
+        self.cur.execute("select i from test")
+        row = self.cur.fetchone()
+        self.failUnlessEqual(row[0], 42)
+
+    def CheckLargeInt(self):
+        # default
+        num = 2**40
+        self.cur.execute("insert into test(i) values (?)", (num,))
+        self.cur.execute("select i from test")
+        row = self.cur.fetchone()
+        self.failUnlessEqual(row[0], num)
+
+    def CheckFloat(self):
+        # custom
+        val = 3.14
+        self.cur.execute("insert into test(f) values (?)", (val,))
+        self.cur.execute("select f from test")
+        row = self.cur.fetchone()
+        self.failUnlessEqual(row[0], 47.2)
+
+    def CheckBool(self):
+        # custom
+        self.cur.execute("insert into test(b) values (?)", (False,))
+        self.cur.execute("select b from test")
+        row = self.cur.fetchone()
+        self.failUnlessEqual(row[0], False)
+
+        self.cur.execute("delete from test")
+        self.cur.execute("insert into test(b) values (?)", (True,))
+        self.cur.execute("select b from test")
+        row = self.cur.fetchone()
+        self.failUnlessEqual(row[0], True)
+
+    def CheckUnicode(self):
+        # default
+        val = u"\xd6sterreich"
+        self.cur.execute("insert into test(u) values (?)", (val,))
+        self.cur.execute("select u from test")
+        row = self.cur.fetchone()
+        self.failUnlessEqual(row[0], val)
+
+    def CheckFoo(self):
+        val = DeclTypesTests.Foo("bla")
+        self.cur.execute("insert into test(foo) values (?)", (val,))
+        self.cur.execute("select foo from test")
+        row = self.cur.fetchone()
+        self.failUnlessEqual(row[0], val)
+
+    def CheckUnsupportedSeq(self):
+        class Bar: pass
+        val = Bar()
+        try:
+            self.cur.execute("insert into test(f) values (?)", (val,))
+            self.fail("should have raised an InterfaceError")
+        except sqlite.InterfaceError:
+            pass
+        except:
+            self.fail("should have raised an InterfaceError")
+
+    def CheckUnsupportedDict(self):
+        class Bar: pass
+        val = Bar()
+        try:
+            self.cur.execute("insert into test(f) values (:val)", {"val": val})
+            self.fail("should have raised an InterfaceError")
+        except sqlite.InterfaceError:
+            pass
+        except:
+            self.fail("should have raised an InterfaceError")
+
+    def CheckBlob(self):
+        # default
+        val = buffer("Guglhupf")
+        self.cur.execute("insert into test(bin) values (?)", (val,))
+        self.cur.execute("select bin from test")
+        row = self.cur.fetchone()
+        self.failUnlessEqual(row[0], val)
+
+class ColNamesTests(unittest.TestCase):
+    def setUp(self):
+        self.con = sqlite.connect(":memory:", detect_types=sqlite.PARSE_COLNAMES|sqlite.PARSE_DECLTYPES)
+        self.cur = self.con.cursor()
+        self.cur.execute("create table test(x foo)")
+
+        sqlite.converters["foo"] = lambda x: "[%s]" % x
+        sqlite.converters["bar"] = lambda x: "<%s>" % x
+        sqlite.converters["exc"] = lambda x: 5/0
+
+    def tearDown(self):
+        del sqlite.converters["foo"]
+        del sqlite.converters["bar"]
+        del sqlite.converters["exc"]
+        self.cur.close()
+        self.con.close()
+
+    def CheckDeclType(self):
+        self.cur.execute("insert into test(x) values (?)", ("xxx",))
+        self.cur.execute("select x from test")
+        val = self.cur.fetchone()[0]
+        self.failUnlessEqual(val, "[xxx]")
+
+    def CheckNone(self):
+        self.cur.execute("insert into test(x) values (?)", (None,))
+        self.cur.execute("select x from test")
+        val = self.cur.fetchone()[0]
+        self.failUnlessEqual(val, None)
+
+    def CheckExc(self):
+        # Exceptions in type converters result in returned Nones
+        self.cur.execute('select 5 as "x [exc]"')
+        val = self.cur.fetchone()[0]
+        self.failUnlessEqual(val, None)
+
+    def CheckColName(self):
+        self.cur.execute("insert into test(x) values (?)", ("xxx",))
+        self.cur.execute('select x as "x [bar]" from test')
+        val = self.cur.fetchone()[0]
+        self.failUnlessEqual(val, "<xxx>")
+
+        # Check if the stripping of colnames works. Everything after the first
+        # whitespace should be stripped.
+        self.failUnlessEqual(self.cur.description[0][0], "x")
+
+    def CheckCursorDescriptionNoRow(self):
+        """
+        cursor.description should at least provide the column name(s), even if
+        no row returned.
+        """
+        self.cur.execute("select * from test where 0 = 1")
+        self.assert_(self.cur.description[0][0] == "x")
+
+class ObjectAdaptationTests(unittest.TestCase):
+    def cast(obj):
+        return float(obj)
+    cast = staticmethod(cast)
+
+    def setUp(self):
+        self.con = sqlite.connect(":memory:")
+        try:
+            del sqlite.adapters[int]
+        except:
+            pass
+        sqlite.register_adapter(int, ObjectAdaptationTests.cast)
+        self.cur = self.con.cursor()
+
+    def tearDown(self):
+        del sqlite.adapters[(int, sqlite.PrepareProtocol)]
+        self.cur.close()
+        self.con.close()
+
+    def CheckCasterIsUsed(self):
+        self.cur.execute("select ?", (4,))
+        val = self.cur.fetchone()[0]
+        self.failUnlessEqual(type(val), float)
+
+class DateTimeTests(unittest.TestCase):
+    def setUp(self):
+        self.con = sqlite.connect(":memory:", detect_types=sqlite.PARSE_DECLTYPES)
+        self.cur = self.con.cursor()
+        self.cur.execute("create table test(d date, ts timestamp)")
+
+    def tearDown(self):
+        self.cur.close()
+        self.con.close()
+
+    def CheckSqliteDate(self):
+        d = sqlite.Date(2004, 2, 14)
+        self.cur.execute("insert into test(d) values (?)", (d,))
+        self.cur.execute("select d from test")
+        d2 = self.cur.fetchone()[0]
+        self.failUnlessEqual(d, d2)
+
+    def CheckSqliteTimestamp(self):
+        ts = sqlite.Timestamp(2004, 2, 14, 7, 15, 0)
+        self.cur.execute("insert into test(ts) values (?)", (ts,))
+        self.cur.execute("select ts from test")
+        ts2 = self.cur.fetchone()[0]
+        self.failUnlessEqual(ts, ts2)
+
+    def CheckSqlTimestamp(self):
+        # The date functions are only available in SQLite version 3.1 or later
+        if sqlite.sqlite_version_info < (3, 1):
+            return
+
+        # SQLite's current_timestamp uses UTC time, while datetime.datetime.now() uses local time.
+        now = datetime.datetime.now()
+        self.cur.execute("insert into test(ts) values (current_timestamp)")
+        self.cur.execute("select ts from test")
+        ts = self.cur.fetchone()[0]
+        self.failUnlessEqual(type(ts), datetime.datetime)
+        self.failUnlessEqual(ts.year, now.year)
+
+    def CheckDateTimeSubSeconds(self):
+        ts = sqlite.Timestamp(2004, 2, 14, 7, 15, 0, 500000)
+        self.cur.execute("insert into test(ts) values (?)", (ts,))
+        self.cur.execute("select ts from test")
+        ts2 = self.cur.fetchone()[0]
+        self.failUnlessEqual(ts, ts2)
+
+def suite():
+    sqlite_type_suite = unittest.makeSuite(SqliteTypeTests, "Check")
+    decltypes_type_suite = unittest.makeSuite(DeclTypesTests, "Check")
+    colnames_type_suite = unittest.makeSuite(ColNamesTests, "Check")
+    adaptation_suite = unittest.makeSuite(ObjectAdaptationTests, "Check")
+    date_suite = unittest.makeSuite(DateTimeTests, "Check")
+    return unittest.TestSuite((sqlite_type_suite, decltypes_type_suite, colnames_type_suite, adaptation_suite, date_suite))
+
+def test():
+    runner = unittest.TextTestRunner()
+    runner.run(suite())
+
+if __name__ == "__main__":
+    test()
diff --git a/Lib/sqlite3/test/userfunctions.py b/Lib/sqlite3/test/userfunctions.py
new file mode 100644
index 0000000..ff7db9c
--- /dev/null
+++ b/Lib/sqlite3/test/userfunctions.py
@@ -0,0 +1,330 @@
+#-*- coding: ISO-8859-1 -*-
+# pysqlite2/test/userfunctions.py: tests for user-defined functions and
+#                                  aggregates.
+#
+# 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.
+
+import unittest
+import sqlite3 as sqlite
+
+def func_returntext():
+    return "foo"
+def func_returnunicode():
+    return u"bar"
+def func_returnint():
+    return 42
+def func_returnfloat():
+    return 3.14
+def func_returnnull():
+    return None
+def func_returnblob():
+    return buffer("blob")
+def func_raiseexception():
+    5/0
+
+def func_isstring(v):
+    return type(v) is unicode
+def func_isint(v):
+    return type(v) is int
+def func_isfloat(v):
+    return type(v) is float
+def func_isnone(v):
+    return type(v) is type(None)
+def func_isblob(v):
+    return type(v) is buffer
+
+class AggrNoStep:
+    def __init__(self):
+        pass
+
+class AggrNoFinalize:
+    def __init__(self):
+        pass
+
+    def step(self, x):
+        pass
+
+class AggrExceptionInInit:
+    def __init__(self):
+        5/0
+
+    def step(self, x):
+        pass
+
+    def finalize(self):
+        pass
+
+class AggrExceptionInStep:
+    def __init__(self):
+        pass
+
+    def step(self, x):
+        5/0
+
+    def finalize(self):
+        return 42
+
+class AggrExceptionInFinalize:
+    def __init__(self):
+        pass
+
+    def step(self, x):
+        pass
+
+    def finalize(self):
+        5/0
+
+class AggrCheckType:
+    def __init__(self):
+        self.val = None
+
+    def step(self, whichType, val):
+        theType = {"str": unicode, "int": int, "float": float, "None": type(None), "blob": buffer}
+        self.val = int(theType[whichType] is type(val))
+
+    def finalize(self):
+        return self.val
+
+class AggrSum:
+    def __init__(self):
+        self.val = 0.0
+
+    def step(self, val):
+        self.val += val
+
+    def finalize(self):
+        return self.val
+
+class FunctionTests(unittest.TestCase):
+    def setUp(self):
+        self.con = sqlite.connect(":memory:")
+
+        self.con.create_function("returntext", 0, func_returntext)
+        self.con.create_function("returnunicode", 0, func_returnunicode)
+        self.con.create_function("returnint", 0, func_returnint)
+        self.con.create_function("returnfloat", 0, func_returnfloat)
+        self.con.create_function("returnnull", 0, func_returnnull)
+        self.con.create_function("returnblob", 0, func_returnblob)
+        self.con.create_function("raiseexception", 0, func_raiseexception)
+
+        self.con.create_function("isstring", 1, func_isstring)
+        self.con.create_function("isint", 1, func_isint)
+        self.con.create_function("isfloat", 1, func_isfloat)
+        self.con.create_function("isnone", 1, func_isnone)
+        self.con.create_function("isblob", 1, func_isblob)
+
+    def tearDown(self):
+        self.con.close()
+
+    def CheckFuncRefCount(self):
+        def getfunc():
+            def f():
+                return val
+            return f
+        self.con.create_function("reftest", 0, getfunc())
+        cur = self.con.cursor()
+        cur.execute("select reftest()")
+
+    def CheckFuncReturnText(self):
+        cur = self.con.cursor()
+        cur.execute("select returntext()")
+        val = cur.fetchone()[0]
+        self.failUnlessEqual(type(val), unicode)
+        self.failUnlessEqual(val, "foo")
+
+    def CheckFuncReturnUnicode(self):
+        cur = self.con.cursor()
+        cur.execute("select returnunicode()")
+        val = cur.fetchone()[0]
+        self.failUnlessEqual(type(val), unicode)
+        self.failUnlessEqual(val, u"bar")
+
+    def CheckFuncReturnInt(self):
+        cur = self.con.cursor()
+        cur.execute("select returnint()")
+        val = cur.fetchone()[0]
+        self.failUnlessEqual(type(val), int)
+        self.failUnlessEqual(val, 42)
+
+    def CheckFuncReturnFloat(self):
+        cur = self.con.cursor()
+        cur.execute("select returnfloat()")
+        val = cur.fetchone()[0]
+        self.failUnlessEqual(type(val), float)
+        if val < 3.139 or val > 3.141:
+            self.fail("wrong value")
+
+    def CheckFuncReturnNull(self):
+        cur = self.con.cursor()
+        cur.execute("select returnnull()")
+        val = cur.fetchone()[0]
+        self.failUnlessEqual(type(val), type(None))
+        self.failUnlessEqual(val, None)
+
+    def CheckFuncReturnBlob(self):
+        cur = self.con.cursor()
+        cur.execute("select returnblob()")
+        val = cur.fetchone()[0]
+        self.failUnlessEqual(type(val), buffer)
+        self.failUnlessEqual(val, buffer("blob"))
+
+    def CheckFuncException(self):
+        cur = self.con.cursor()
+        cur.execute("select raiseexception()")
+        val = cur.fetchone()[0]
+        self.failUnlessEqual(val, None)
+
+    def CheckParamString(self):
+        cur = self.con.cursor()
+        cur.execute("select isstring(?)", ("foo",))
+        val = cur.fetchone()[0]
+        self.failUnlessEqual(val, 1)
+
+    def CheckParamInt(self):
+        cur = self.con.cursor()
+        cur.execute("select isint(?)", (42,))
+        val = cur.fetchone()[0]
+        self.failUnlessEqual(val, 1)
+
+    def CheckParamFloat(self):
+        cur = self.con.cursor()
+        cur.execute("select isfloat(?)", (3.14,))
+        val = cur.fetchone()[0]
+        self.failUnlessEqual(val, 1)
+
+    def CheckParamNone(self):
+        cur = self.con.cursor()
+        cur.execute("select isnone(?)", (None,))
+        val = cur.fetchone()[0]
+        self.failUnlessEqual(val, 1)
+
+    def CheckParamBlob(self):
+        cur = self.con.cursor()
+        cur.execute("select isblob(?)", (buffer("blob"),))
+        val = cur.fetchone()[0]
+        self.failUnlessEqual(val, 1)
+
+class AggregateTests(unittest.TestCase):
+    def setUp(self):
+        self.con = sqlite.connect(":memory:")
+        cur = self.con.cursor()
+        cur.execute("""
+            create table test(
+                t text,
+                i integer,
+                f float,
+                n,
+                b blob
+                )
+            """)
+        cur.execute("insert into test(t, i, f, n, b) values (?, ?, ?, ?, ?)",
+            ("foo", 5, 3.14, None, buffer("blob"),))
+
+        self.con.create_aggregate("nostep", 1, AggrNoStep)
+        self.con.create_aggregate("nofinalize", 1, AggrNoFinalize)
+        self.con.create_aggregate("excInit", 1, AggrExceptionInInit)
+        self.con.create_aggregate("excStep", 1, AggrExceptionInStep)
+        self.con.create_aggregate("excFinalize", 1, AggrExceptionInFinalize)
+        self.con.create_aggregate("checkType", 2, AggrCheckType)
+        self.con.create_aggregate("mysum", 1, AggrSum)
+
+    def tearDown(self):
+        #self.cur.close()
+        #self.con.close()
+        pass
+
+    def CheckAggrNoStep(self):
+        cur = self.con.cursor()
+        cur.execute("select nostep(t) from test")
+
+    def CheckAggrNoFinalize(self):
+        cur = self.con.cursor()
+        cur.execute("select nofinalize(t) from test")
+        val = cur.fetchone()[0]
+        self.failUnlessEqual(val, None)
+
+    def CheckAggrExceptionInInit(self):
+        cur = self.con.cursor()
+        cur.execute("select excInit(t) from test")
+        val = cur.fetchone()[0]
+        self.failUnlessEqual(val, None)
+
+    def CheckAggrExceptionInStep(self):
+        cur = self.con.cursor()
+        cur.execute("select excStep(t) from test")
+        val = cur.fetchone()[0]
+        self.failUnlessEqual(val, 42)
+
+    def CheckAggrExceptionInFinalize(self):
+        cur = self.con.cursor()
+        cur.execute("select excFinalize(t) from test")
+        val = cur.fetchone()[0]
+        self.failUnlessEqual(val, None)
+
+    def CheckAggrCheckParamStr(self):
+        cur = self.con.cursor()
+        cur.execute("select checkType('str', ?)", ("foo",))
+        val = cur.fetchone()[0]
+        self.failUnlessEqual(val, 1)
+
+    def CheckAggrCheckParamInt(self):
+        cur = self.con.cursor()
+        cur.execute("select checkType('int', ?)", (42,))
+        val = cur.fetchone()[0]
+        self.failUnlessEqual(val, 1)
+
+    def CheckAggrCheckParamFloat(self):
+        cur = self.con.cursor()
+        cur.execute("select checkType('float', ?)", (3.14,))
+        val = cur.fetchone()[0]
+        self.failUnlessEqual(val, 1)
+
+    def CheckAggrCheckParamNone(self):
+        cur = self.con.cursor()
+        cur.execute("select checkType('None', ?)", (None,))
+        val = cur.fetchone()[0]
+        self.failUnlessEqual(val, 1)
+
+    def CheckAggrCheckParamBlob(self):
+        cur = self.con.cursor()
+        cur.execute("select checkType('blob', ?)", (buffer("blob"),))
+        val = cur.fetchone()[0]
+        self.failUnlessEqual(val, 1)
+
+    def CheckAggrCheckAggrSum(self):
+        cur = self.con.cursor()
+        cur.execute("delete from test")
+        cur.executemany("insert into test(i) values (?)", [(10,), (20,), (30,)])
+        cur.execute("select mysum(i) from test")
+        val = cur.fetchone()[0]
+        self.failUnlessEqual(val, 60)
+
+def suite():
+    function_suite = unittest.makeSuite(FunctionTests, "Check")
+    aggregate_suite = unittest.makeSuite(AggregateTests, "Check")
+    return unittest.TestSuite((function_suite, aggregate_suite))
+
+def test():
+    runner = unittest.TextTestRunner()
+    runner.run(suite())
+
+if __name__ == "__main__":
+    test()
diff --git a/Lib/test/regrtest.py b/Lib/test/regrtest.py
index f229360..be60023 100755
--- a/Lib/test/regrtest.py
+++ b/Lib/test/regrtest.py
@@ -741,6 +741,7 @@
         test_pwd
         test_resource
         test_signal
+        test_sqlite
         test_sunaudiodev
         test_threadsignals
         test_timing
@@ -763,6 +764,7 @@
         test_nis
         test_ntpath
         test_ossaudiodev
+        test_sqlite
         test_sunaudiodev
         """,
    'mac':
@@ -802,6 +804,7 @@
         test_pwd
         test_resource
         test_signal
+        test_sqlite
         test_sunaudiodev
         test_sundry
         test_tarfile
@@ -826,6 +829,7 @@
         test_openpty
         test_pyexpat
         test_sax
+        test_sqlite
         test_sunaudiodev
         test_sundry
         """,
@@ -848,6 +852,7 @@
         test_openpty
         test_pyexpat
         test_sax
+        test_sqlite
         test_sunaudiodev
         test_sundry
         """,
@@ -875,6 +880,7 @@
         test_pyexpat
         test_queue
         test_sax
+        test_sqlite
         test_sunaudiodev
         test_sundry
         test_thread
@@ -915,6 +921,7 @@
         test_pty
         test_pwd
         test_strop
+        test_sqlite
         test_sunaudiodev
         test_sundry
         test_thread
@@ -944,6 +951,7 @@
         test_ntpath
         test_ossaudiodev
         test_poll
+        test_sqlite
         test_sunaudiodev
         """,
     'sunos5':
@@ -962,6 +970,7 @@
         test_imgfile
         test_linuxaudiodev
         test_openpty
+        test_sqlite
         test_zipfile
         test_zlib
         """,
@@ -988,6 +997,7 @@
         test_openpty
         test_pyexpat
         test_sax
+        test_sqlite
         test_sunaudiodev
         test_zipfile
         test_zlib
@@ -1013,6 +1023,7 @@
         test_poll
         test_popen2
         test_resource
+        test_sqlite
         test_sunaudiodev
         """,
     'cygwin':
@@ -1034,6 +1045,7 @@
         test_nis
         test_ossaudiodev
         test_socketserver
+        test_sqlite
         test_sunaudiodev
         """,
     'os2emx':
@@ -1060,6 +1072,7 @@
         test_pty
         test_resource
         test_signal
+        test_sqlite
         test_sunaudiodev
         """,
     'freebsd4':
@@ -1086,6 +1099,7 @@
         test_scriptpackages
         test_socket_ssl
         test_socketserver
+        test_sqlite
         test_sunaudiodev
         test_tcl
         test_timeout
@@ -1115,6 +1129,7 @@
         test_macostools
         test_nis
         test_ossaudiodev
+        test_sqlite
         test_sunaudiodev
         test_tcl
         test_winreg
@@ -1147,6 +1162,7 @@
         test_plistlib
         test_scriptpackages
         test_tcl
+        test_sqlite
         test_sunaudiodev
         test_unicode_file
         test_winreg
diff --git a/Lib/test/test_sqlite.py b/Lib/test/test_sqlite.py
new file mode 100644
index 0000000..1b1d0e5
--- /dev/null
+++ b/Lib/test/test_sqlite.py
@@ -0,0 +1,16 @@
+from test.test_support import run_unittest, TestSkipped
+import unittest
+
+try:
+    import _sqlite3
+except ImportError:
+    raise TestSkipped('no sqlite available')
+from sqlite3.test import (dbapi, types, userfunctions,
+                                factory, transactions)
+
+def test_main():
+    run_unittest(dbapi.suite(), types.suite(), userfunctions.suite(),
+                 factory.suite(), transactions.suite())
+
+if __name__ == "__main__":
+    test_main()
diff --git a/Misc/NEWS b/Misc/NEWS
index 974712a..b877fe6 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -489,6 +489,11 @@
 Library
 -------
 
+- Added the sqlite3 package. This is based on pysqlite2.1.3, and provides
+  a DB-API interface in the standard library. You'll need sqlite 3.2.2 or
+  later to build this - if you have an earlier version, the C extension 
+  module will not be built.
+
 - Bug #1460340: ``random.sample(dict)`` failed in various ways.  Dicts
   aren't officially supported here, and trying to use them will probably
   raise an exception some day.  But dicts have been allowed, and "mostly
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..0c7d4a3
--- /dev/null
+++ b/Modules/_sqlite/cache.c
@@ -0,0 +1,343 @@
+/* cache .c - a 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.
+ */
+
+#include "cache.h"
+
+/* only used internally */
+Node* new_node(PyObject* key, PyObject* data)
+{
+    Node* node;
+
+    node = (Node*) (NodeType.tp_alloc(&NodeType, 0));
+    /*node = PyObject_New(Node, &NodeType);*/
+    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();
+    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;
+    Node* node;
+    Node* ptr;
+    PyObject* data;
+
+    if (!PyArg_ParseTuple(args, "O", &key))
+    {
+        return NULL; 
+    }
+
+    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;
+                PyDict_DelItem(self->mapping, self->last->key);
+                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);
+        node->prev = self->last;
+
+        Py_DECREF(data);
+
+        if (self->last) {
+            self->last->next = node;
+        } else {
+            self->first = node;
+        }
+        self->last = node;
+        PyDict_SetItem(self->mapping, key, (PyObject*)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);
+        template = PyString_FromString("%s <- %s ->%s\n");
+        display_str = PyString_Format(template, fmt_args);
+        Py_DECREF(template);
+        Py_DECREF(fmt_args);
+        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_VARARGS,
+        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 */
+        "pysqlite2.dbapi2.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 */
+        "pysqlite2.dbapi2.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..e53f0b5
--- /dev/null
+++ b/Modules/_sqlite/connection.c
@@ -0,0 +1,922 @@
+/* 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 "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();
+
+    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);
+
+    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;
+    int 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;
+    int buflen;
+    void* raw_buffer;
+
+    args = PyTuple_New(argc);
+
+    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);
+        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 int connection_set_isolation_level(Connection* self, PyObject* isolation_level)
+{
+    PyObject* empty;
+    PyObject* res;
+    PyObject* begin_statement;
+
+    Py_XDECREF(self->isolation_level);
+
+    if (isolation_level == Py_None) {
+        Py_INCREF(Py_None);
+        self->begin_statement = NULL;
+        self->isolation_level = Py_None;
+
+        empty = PyTuple_New(0);
+        res = connection_commit(self, empty);
+        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;
+}
+
+static char connection_doc[] =
+PyDoc_STR("<missing docstring>");
+
+static PyGetSetDef connection_getset[] = {
+    {"isolation_level",  (getter)connection_get_isolation_level, (setter)connection_set_isolation_level},
+    {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.")},
+    {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 */
+        "pysqlite2.dbapi2.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..ef03bc4
--- /dev/null
+++ b/Modules/_sqlite/connection.h
@@ -0,0 +1,103 @@
+/* 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;
+
+    /* 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..c6ab25a
--- /dev/null
+++ b/Modules/_sqlite/cursor.c
@@ -0,0 +1,1067 @@
+/* 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 "microprotocols.h"
+#include "prepare_protocol.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);
+
+    Py_INCREF(Py_None);
+    self->description = Py_None;
+
+    Py_INCREF(Py_None);
+    self->lastrowid= Py_None;
+
+    self->arraysize = 1;
+
+    self->rowcount = PyInt_FromLong(-1L);
+
+    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);
+}
+
+void 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;
+    }
+
+    Py_DECREF(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);
+                    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);
+                        break;
+                    }
+                }
+
+                converter = PyDict_GetItem(converters, py_decltype);
+                Py_DECREF(py_decltype);
+            }
+        }
+
+        if (converter) {
+            PyList_Append(self->row_cast_map, converter);
+        } else {
+            PyList_Append(self->row_cast_map, Py_None);
+        }
+    }
+}
+
+int _bind_parameter(Cursor* 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;
+    int buflen;
+    PyObject* stringval;
+
+    if (parameter == Py_None) {
+        rc = sqlite3_bind_null(self->statement->st, pos);
+    } else if (PyInt_Check(parameter)) {
+        longval = PyInt_AsLong(parameter);
+        rc = sqlite3_bind_int64(self->statement->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->statement->st, pos, (sqlite_int64)longlongval);
+#endif
+    } else if (PyFloat_Check(parameter)) {
+        rc = sqlite3_bind_double(self->statement->st, pos, PyFloat_AsDouble(parameter));
+    } else if (PyBuffer_Check(parameter)) {
+        if (PyObject_AsCharBuffer(parameter, &buffer, &buflen) == 0) {
+            rc = sqlite3_bind_blob(self->statement->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->statement->st, pos, string, -1, SQLITE_TRANSIENT);
+    } else if PyUnicode_Check(parameter) {
+        stringval = PyUnicode_AsUTF8String(parameter);
+        string = PyString_AsString(stringval);
+        rc = sqlite3_bind_text(self->statement->st, pos, string, -1, SQLITE_TRANSIENT);
+        Py_DECREF(stringval);
+    } else {
+        rc = -1;
+    }
+
+    return rc;
+}
+
+PyObject* _build_column_name(const char* colname)
+{
+    const char* pos;
+
+    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;
+    int 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);
+
+    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);
+                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 num_params;
+    int i;
+    int rc;
+    PyObject* func_args;
+    PyObject* result;
+    int numcols;
+    PY_LONG_LONG lastrowid;
+    int statement_type;
+    PyObject* descriptor;
+    PyObject* current_param;
+    PyObject* adapted;
+    PyObject* second_argument = NULL;
+    int num_params_needed;
+    const char* binding_name;
+    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 (PyErr_Occurred())
+            {
+                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);
+        } else {
+            Py_INCREF(second_argument);
+        }
+        PyList_Append(parameters_list, second_argument);
+        Py_DECREF(second_argument);
+
+        parameters_iter = PyObject_GetIter(parameters_list);
+    }
+
+    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);
+
+    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);
+                    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;
+                }
+        }
+    }
+
+    func_args = PyTuple_New(1);
+    Py_INCREF(operation);
+    PyTuple_SetItem(func_args, 0, operation);
+
+    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);
+        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);
+
+    Py_BEGIN_ALLOW_THREADS
+    num_params_needed = sqlite3_bind_parameter_count(self->statement->st);
+    Py_END_ALLOW_THREADS
+
+    while (1) {
+        parameters = PyIter_Next(parameters_iter);
+        if (!parameters) {
+            break;
+        }
+
+        statement_mark_dirty(self->statement);
+
+        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->statement->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);
+                    goto error;
+                }
+
+                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);
+                    goto error;
+                }
+
+                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 = _bind_parameter(self, i, adapted);
+                Py_DECREF(adapted);
+
+                if (rc != SQLITE_OK) {
+                    PyErr_Format(InterfaceError, "Error binding parameter :%s - probably unsupported type.", binding_name);
+                    goto error;
+               }
+            }
+        } 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);
+                goto error;
+            }
+            for (i = 0; i < num_params; i++) {
+                current_param = PySequence_GetItem(parameters, i);
+                if (!current_param) {
+                    goto error;
+                }
+                adapted = microprotocols_adapt(current_param, (PyObject*)&SQLitePrepareProtocolType, NULL);
+
+                if (adapted) {
+                    Py_DECREF(current_param);
+                } else {
+                    PyErr_Clear();
+                    adapted = current_param;
+                }
+
+                rc = _bind_parameter(self, i + 1, adapted);
+                Py_DECREF(adapted);
+
+                if (rc != SQLITE_OK) {
+                    PyErr_Format(InterfaceError, "Error binding parameter %d - probably unsupported type.", i);
+                    goto error;
+                }
+            }
+        }
+
+        build_row_cast_map(self);
+
+        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);
+                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);
+                for (i = 0; i < numcols; i++) {
+                    descriptor = PyTuple_New(7);
+                    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_DECREF(parameters_iter);
+    Py_XDECREF(parameters_list);
+
+    if (PyErr_Occurred()) {
+        return NULL;
+    } else {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+}
+
+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_obj) {
+            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);
+    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(Py_None);
+        return Py_None;
+    }
+}
+
+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);
+
+    /* just make sure we enter the loop */
+    row = (PyObject*)1;
+
+    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);
+
+    /* just make sure we enter the loop */
+    row = (PyObject*)1;
+
+    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 */
+        "pysqlite2.dbapi2.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..5df41a1
--- /dev/null
+++ b/Modules/_sqlite/microprotocols.c
@@ -0,0 +1,141 @@
+/* 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;
+    }
+
+    PyDict_SetItemString(dict, "adapters", psyco_adapters);
+
+    return 0;
+}
+
+
+/* microprotocols_add - add a reverse type-caster to the dictionary */
+
+int
+microprotocols_add(PyTypeObject *type, PyObject *proto, PyObject *cast)
+{
+    PyObject* key;
+
+    if (proto == NULL) proto = (PyObject*)&SQLitePrepareProtocolType;
+
+    /*
+    Dprintf("microprotocols_add: cast %p for (%s, ?)",
+            cast, type->tp_name);
+    */
+
+    key = Py_BuildValue("(OO)", (PyObject*)type, proto);
+    PyDict_SetItem(psyco_adapters, key, cast);
+    Py_DECREF(key);
+
+    return 0;
+}
+
+/* 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);
+    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..70993d0
--- /dev/null
+++ b/Modules/_sqlite/module.c
@@ -0,0 +1,290 @@
+/* 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* time_time;
+PyObject* time_sleep;
+
+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;
+    }
+
+    PyDict_SetItem(converters, name, callable);
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+void converters_init(PyObject* dict)
+{
+    converters = PyDict_New();
+
+    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 pysqlite's adapter registry.")},
+    {"register_converter", (PyCFunction)module_register_converter, METH_VARARGS, PyDoc_STR("Registers a converter with pysqlite.")},
+    {"adapt",  (PyCFunction)psyco_microprotocols_adapt, METH_VARARGS, psyco_microprotocols_adapt_doc},
+    {NULL, NULL}
+};
+
+PyMODINIT_FUNC init_sqlite3(void)
+{
+    PyObject *module, *dict;
+    PyObject* time_module;
+
+    module = Py_InitModule("_sqlite3", module_methods);
+
+    if (
+        (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 */
+
+    Error = PyErr_NewException("sqlite3.Error", PyExc_StandardError, NULL);
+    PyDict_SetItemString(dict, "Error", Error);
+
+    Warning = PyErr_NewException("sqlite3.Warning", PyExc_StandardError, NULL);
+    PyDict_SetItemString(dict, "Warning", Warning);
+
+    /* Error subclasses */
+
+    InterfaceError = PyErr_NewException("sqlite3.InterfaceError", Error, NULL);
+    PyDict_SetItemString(dict, "InterfaceError", InterfaceError);
+
+    DatabaseError = PyErr_NewException("sqlite3.DatabaseError", Error, NULL);
+    PyDict_SetItemString(dict, "DatabaseError", DatabaseError);
+
+    /* DatabaseError subclasses */
+
+    InternalError = PyErr_NewException("sqlite3.InternalError", DatabaseError, NULL);
+    PyDict_SetItemString(dict, "InternalError", InternalError);
+
+    OperationalError = PyErr_NewException("sqlite3.OperationalError", DatabaseError, NULL);
+    PyDict_SetItemString(dict, "OperationalError", OperationalError);
+
+    ProgrammingError = PyErr_NewException("sqlite3.ProgrammingError", DatabaseError, NULL);
+    PyDict_SetItemString(dict, "ProgrammingError", ProgrammingError);
+
+    IntegrityError = PyErr_NewException("sqlite3.IntegrityError", DatabaseError,NULL);
+    PyDict_SetItemString(dict, "IntegrityError", IntegrityError);
+
+    DataError = PyErr_NewException("sqlite3.DataError", DatabaseError, NULL);
+    PyDict_SetItemString(dict, "DataError", DataError);
+
+    NotSupportedError = PyErr_NewException("sqlite3.NotSupportedError", DatabaseError, NULL);
+    PyDict_SetItemString(dict, "NotSupportedError", NotSupportedError);
+
+    Py_INCREF((PyObject*)&PyCell_Type);
+    OptimizedUnicode = (PyObject*)&PyCell_Type;
+    PyDict_SetItemString(dict, "OptimizedUnicode", OptimizedUnicode);
+
+    PyDict_SetItemString(dict, "PARSE_DECLTYPES", PyInt_FromLong(PARSE_DECLTYPES));
+    PyDict_SetItemString(dict, "PARSE_COLNAMES", PyInt_FromLong(PARSE_COLNAMES));
+
+    PyDict_SetItemString(dict, "version", PyString_FromString(PYSQLITE_VERSION));
+    PyDict_SetItemString(dict, "sqlite_version", PyString_FromString(sqlite3_libversion()));
+
+    /* initialize microprotocols layer */
+    microprotocols_init(dict);
+
+    /* initialize the default converters */
+    converters_init(dict);
+
+    time_module = PyImport_ImportModule("time");
+    time_time =  PyObject_GetAttrString(time_module, "time");
+    time_sleep = PyObject_GetAttrString(time_module, "sleep");
+
+    /* 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, "_sqlite3: init failed");
+    }
+}
diff --git a/Modules/_sqlite/module.h b/Modules/_sqlite/module.h
new file mode 100644
index 0000000..75fe29d
--- /dev/null
+++ b/Modules/_sqlite/module.h
@@ -0,0 +1,53 @@
+/* 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"
+
+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..2e8349c
--- /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 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 */
+        "pysqlite2.dbapi2.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..97b538d
--- /dev/null
+++ b/Modules/_sqlite/row.c
@@ -0,0 +1,195 @@
+/* row.c - 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.
+ */
+
+#include "row.h"
+#include "cursor.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);
+        if (item) {
+            Py_INCREF(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));
+
+            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;
+    }
+}
+
+int 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        */ (inquiry)row_length,
+    /* mp_subscript     */ (binaryfunc)row_subscript,
+    /* mp_ass_subscript */ (objobjargproc)0,
+};
+
+
+PyTypeObject RowType = {
+        PyObject_HEAD_INIT(NULL)
+        0,                                              /* ob_size */
+        "pysqlite2.dbapi2.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/statement.c b/Modules/_sqlite/statement.c
new file mode 100644
index 0000000..a2c0f13
--- /dev/null
+++ b/Modules/_sqlite/statement.c
@@ -0,0 +1,285 @@
+/* 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 "connection.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->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);
+        rc = PYSQLITE_TOO_MUCH_SQL;
+    }
+
+    return rc;
+}
+
+int statement_recompile(Statement* self)
+{
+    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) {
+        (void)sqlite3_transfer_bindings(self->st, new_st);
+
+        (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 */
+        "pysqlite2.dbapi2.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..8cf52eb
--- /dev/null
+++ b/Modules/_sqlite/statement.h
@@ -0,0 +1,55 @@
+/* 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_recompile(Statement* self);
+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..ec400a1
--- /dev/null
+++ b/Modules/_sqlite/util.c
@@ -0,0 +1,147 @@
+/* util.c - various utility functions
+ *
+ * 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 "module.h"
+#include "connection.h"
+
+/*
+ * it's not so trivial to write a portable sleep in C. For now, the simplest
+ * solution is to just use Python's sleep().
+ */
+void pysqlite_sleep(double seconds)
+{
+    PyObject* ret;
+
+    ret = PyObject_CallFunction(time_sleep, "d", seconds);
+    Py_DECREF(ret);
+}
+
+double pysqlite_time(void)
+{
+    PyObject* ret;
+    double time;
+
+    ret = PyObject_CallFunction(time_time, "");
+    time = PyFloat_AsDouble(ret);
+    Py_DECREF(ret);
+
+    return time;
+}
+
+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_ERROR:
+            PyErr_SetString(OperationalError, sqlite3_errmsg(db));
+            break;
+        case SQLITE_INTERNAL:
+            PyErr_SetString(InternalError, sqlite3_errmsg(db));
+            break;
+        case SQLITE_PERM:
+            PyErr_SetString(OperationalError, sqlite3_errmsg(db));
+            break;
+        case SQLITE_ABORT:
+            PyErr_SetString(OperationalError, sqlite3_errmsg(db));
+            break;
+        case SQLITE_BUSY:
+            PyErr_SetString(OperationalError, sqlite3_errmsg(db));
+            break;
+        case SQLITE_LOCKED:
+            PyErr_SetString(OperationalError, sqlite3_errmsg(db));
+            break;
+        case SQLITE_NOMEM:
+            (void)PyErr_NoMemory();
+            break;
+        case SQLITE_READONLY:
+            PyErr_SetString(OperationalError, sqlite3_errmsg(db));
+            break;
+        case SQLITE_INTERRUPT:
+            PyErr_SetString(OperationalError, sqlite3_errmsg(db));
+            break;
+        case SQLITE_IOERR:
+            PyErr_SetString(OperationalError, sqlite3_errmsg(db));
+            break;
+        case SQLITE_CORRUPT:
+            PyErr_SetString(DatabaseError, sqlite3_errmsg(db));
+            break;
+        case SQLITE_NOTFOUND:
+            PyErr_SetString(InternalError, sqlite3_errmsg(db));
+            break;
+        case SQLITE_FULL:
+            PyErr_SetString(OperationalError, sqlite3_errmsg(db));
+            break;
+        case SQLITE_CANTOPEN:
+            PyErr_SetString(OperationalError, sqlite3_errmsg(db));
+            break;
+        case SQLITE_PROTOCOL:
+            PyErr_SetString(OperationalError, sqlite3_errmsg(db));
+            break;
+        case SQLITE_EMPTY:
+            PyErr_SetString(OperationalError, sqlite3_errmsg(db));
+            break;
+        case SQLITE_SCHEMA:
+            PyErr_SetString(OperationalError, sqlite3_errmsg(db));
+            break;
+        case SQLITE_TOOBIG:
+            PyErr_SetString(DataError, sqlite3_errmsg(db));
+            break;
+        case SQLITE_CONSTRAINT:
+            PyErr_SetString(IntegrityError, sqlite3_errmsg(db));
+            break;
+        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));
+    }
+
+    return errorcode;
+}
+
diff --git a/Modules/_sqlite/util.h b/Modules/_sqlite/util.h
new file mode 100644
index 0000000..6e74b2d
--- /dev/null
+++ b/Modules/_sqlite/util.h
@@ -0,0 +1,40 @@
+/* util.h - various utility functions
+ *
+ * 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_UTIL_H
+#define PYSQLITE_UTIL_H
+#include "Python.h"
+#include "pythread.h"
+#include "sqlite3.h"
+#include "connection.h"
+
+void pysqlite_sleep(double seconds);
+
+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
diff --git a/setup.py b/setup.py
index 9272c82..e29e82c 100644
--- a/setup.py
+++ b/setup.py
@@ -689,6 +689,81 @@
             dblibs = []
             dblib_dir = None
 
+        # The sqlite interface
+
+        # We hunt for "#define SQLITE_VERSION_NUMBER nnnnn"
+        # We need to find a version >= 3002002 (> sqlite version 3.2.2)
+        sqlite_incdir = sqlite_libdir = None
+        sqlite_inc_paths = [ '/usr/include', 
+                             '/usr/include/sqlite',
+                             '/usr/include/sqlite3',
+                             '/usr/local/include',
+                             '/usr/local/include/sqlite',
+                             '/usr/local/include/sqlite3',
+                           ]
+        MIN_SQLITE_VERSION = 3002002
+        for d in sqlite_inc_paths + inc_dirs:
+            f = os.path.join(d, "sqlite3.h")
+            if os.path.exists(f):
+                if db_setup_debug: print "found %s"%f
+                f = open(f).read()
+                m = re.search(r"#define\WSQLITE_VERSION_NUMBER\W(\d+)", f)
+                if m:
+                    sqlite_version = int(m.group(1))
+                    if sqlite_version >= MIN_SQLITE_VERSION:
+                        # we win!
+                        print "%s/sqlite3.h: version %s"%(d, sqlite_version)
+                        sqlite_incdir = d
+                        break
+                    else:
+                        if db_setup_debug: 
+                            print "%s: version %d is too old, need >= %s"%(d,
+                                        sqlite_version, MIN_SQLITE_VERSION)
+        
+        if sqlite_incdir:
+            sqlite_dirs_to_check = [
+                os.path.join(sqlite_incdir, '..', 'lib64'),
+                os.path.join(sqlite_incdir, '..', 'lib'),
+                os.path.join(sqlite_incdir, '..', '..', 'lib64'),
+                os.path.join(sqlite_incdir, '..', '..', 'lib'),
+            ]
+            sqlite_libfile = self.compiler.find_library_file(
+                                sqlite_dirs_to_check + lib_dirs, 'sqlite3')
+            sqlite_libdir = [os.path.abspath(os.path.dirname(sqlite_libfile))]
+
+        if sqlite_incdir and sqlite_libdir:
+            sqlite_srcs = ['_sqlite/adapters.c',
+                '_sqlite/cache.c',
+                '_sqlite/connection.c',
+                '_sqlite/converters.c',
+                '_sqlite/cursor.c',
+                '_sqlite/microprotocols.c',
+                '_sqlite/module.c',
+                '_sqlite/prepare_protocol.c',
+                '_sqlite/row.c',
+                '_sqlite/statement.c',
+                '_sqlite/util.c', ]
+
+            PYSQLITE_VERSION = "2.1.3"
+            sqlite_defines = []
+            if sys.platform != "win32":
+                sqlite_defines.append(('PYSQLITE_VERSION', 
+                                        '"%s"' % PYSQLITE_VERSION))
+            else:
+                sqlite_defines.append(('PYSQLITE_VERSION', 
+                                        '\\"'+PYSQLITE_VERSION+'\\"'))
+            sqlite_defines.append(('PY_MAJOR_VERSION', 
+                                        str(sys.version_info[0])))
+            sqlite_defines.append(('PY_MINOR_VERSION', 
+                                        str(sys.version_info[1])))
+
+            exts.append(Extension('_sqlite3', sqlite_srcs,
+                                  define_macros=sqlite_defines,
+                                  include_dirs=["Modules/_sqlite", 
+                                                sqlite_incdir],
+                                  library_dirs=sqlite_libdir,
+                                  runtime_library_dirs=sqlite_libdir,
+                                  libraries=["sqlite3",]))
 
         # Look for Berkeley db 1.85.   Note that it is built as a different
         # module name so it can be included even when later versions are