[pypy-commit] pypy default: backout the sqlite-cffi merge, will redo it differently

fijal noreply at buildbot.pypy.org
Wed Apr 3 19:37:52 CEST 2013


Author: Maciej Fijalkowski <fijall at gmail.com>
Branch: 
Changeset: r62978:084972330ddd
Date: 2013-04-03 19:37 +0200
http://bitbucket.org/pypy/pypy/changeset/084972330ddd/

Log:	backout the sqlite-cffi merge, will redo it differently

diff --git a/lib_pypy/_sqlite3.py b/lib_pypy/_sqlite3.py
--- a/lib_pypy/_sqlite3.py
+++ b/lib_pypy/_sqlite3.py
@@ -23,6 +23,9 @@
 #
 # Note: This software has been modified for use in PyPy.
 
+from ctypes import c_void_p, c_int, c_double, c_int64, c_char_p, c_char, cdll
+from ctypes import POINTER, byref, string_at, CFUNCTYPE, cast
+from ctypes import sizeof, c_ssize_t
 from collections import OrderedDict
 from functools import wraps
 import datetime
@@ -41,266 +44,210 @@
 else:
     _BLOB_TYPE = buffer
 
-from cffi import FFI
 
-ffi = FFI()
+def load_library(names):
+    for name in names:
+        try:
+            return cdll.LoadLibrary(name)
+        except OSError:
+            pass
+    else:
+        raise ImportError("Could not load C-library, tried: %s" % (names,))
 
-ffi.cdef("""
-#define SQLITE_OK ...
-#define SQLITE_ERROR ...
-#define SQLITE_INTERNAL ...
-#define SQLITE_PERM ...
-#define SQLITE_ABORT ...
-#define SQLITE_BUSY ...
-#define SQLITE_LOCKED ...
-#define SQLITE_NOMEM ...
-#define SQLITE_READONLY ...
-#define SQLITE_INTERRUPT ...
-#define SQLITE_IOERR ...
-#define SQLITE_CORRUPT ...
-#define SQLITE_NOTFOUND ...
-#define SQLITE_FULL ...
-#define SQLITE_CANTOPEN ...
-#define SQLITE_PROTOCOL ...
-#define SQLITE_EMPTY ...
-#define SQLITE_SCHEMA ...
-#define SQLITE_TOOBIG ...
-#define SQLITE_CONSTRAINT ...
-#define SQLITE_MISMATCH ...
-#define SQLITE_MISUSE ...
-#define SQLITE_NOLFS ...
-#define SQLITE_AUTH ...
-#define SQLITE_FORMAT ...
-#define SQLITE_RANGE ...
-#define SQLITE_NOTADB ...
-#define SQLITE_ROW ...
-#define SQLITE_DONE ...
-#define SQLITE_INTEGER ...
-#define SQLITE_FLOAT ...
-#define SQLITE_BLOB ...
-#define SQLITE_NULL ...
-#define SQLITE_TEXT ...
-#define SQLITE3_TEXT ...
+_lib = load_library(
+    "sqlite3.dll libsqlite3.so.0 libsqlite3.so libsqlite3.dylib".split())
+del load_library
 
-#define SQLITE_TRANSIENT ...
-#define SQLITE_UTF8 ...
+##########################################
+# BEGIN Wrapped SQLite C API and constants
+##########################################
 
-#define SQLITE_DENY ...
-#define SQLITE_IGNORE ...
+_lib.SQLITE_OK = 0
+_lib.SQLITE_ERROR = 1
+_lib.SQLITE_INTERNAL = 2
+_lib.SQLITE_PERM = 3
+_lib.SQLITE_ABORT = 4
+_lib.SQLITE_BUSY = 5
+_lib.SQLITE_LOCKED = 6
+_lib.SQLITE_NOMEM = 7
+_lib.SQLITE_READONLY = 8
+_lib.SQLITE_INTERRUPT = 9
+_lib.SQLITE_IOERR = 10
+_lib.SQLITE_CORRUPT = 11
+_lib.SQLITE_NOTFOUND = 12
+_lib.SQLITE_FULL = 13
+_lib.SQLITE_CANTOPEN = 14
+_lib.SQLITE_PROTOCOL = 15
+_lib.SQLITE_EMPTY = 16
+_lib.SQLITE_SCHEMA = 17
+_lib.SQLITE_TOOBIG = 18
+_lib.SQLITE_CONSTRAINT = 19
+_lib.SQLITE_MISMATCH = 20
+_lib.SQLITE_MISUSE = 21
+_lib.SQLITE_NOLFS = 22
+_lib.SQLITE_AUTH = 23
+_lib.SQLITE_FORMAT = 24
+_lib.SQLITE_RANGE = 25
+_lib.SQLITE_NOTADB = 26
+_lib.SQLITE_ROW = 100
+_lib.SQLITE_DONE = 101
 
-#define SQLITE_CREATE_INDEX ...
-#define SQLITE_CREATE_TABLE ...
-#define SQLITE_CREATE_TEMP_INDEX ...
-#define SQLITE_CREATE_TEMP_TABLE ...
-#define SQLITE_CREATE_TEMP_TRIGGER ...
-#define SQLITE_CREATE_TEMP_VIEW ...
-#define SQLITE_CREATE_TRIGGER ...
-#define SQLITE_CREATE_VIEW ...
-#define SQLITE_DELETE ...
-#define SQLITE_DROP_INDEX ...
-#define SQLITE_DROP_TABLE ...
-#define SQLITE_DROP_TEMP_INDEX ...
-#define SQLITE_DROP_TEMP_TABLE ...
-#define SQLITE_DROP_TEMP_TRIGGER ...
-#define SQLITE_DROP_TEMP_VIEW ...
-#define SQLITE_DROP_TRIGGER ...
-#define SQLITE_DROP_VIEW ...
-#define SQLITE_INSERT ...
-#define SQLITE_PRAGMA ...
-#define SQLITE_READ ...
-#define SQLITE_SELECT ...
-#define SQLITE_TRANSACTION ...
-#define SQLITE_UPDATE ...
-#define SQLITE_ATTACH ...
-#define SQLITE_DETACH ...
-#define SQLITE_ALTER_TABLE ...
-#define SQLITE_REINDEX ...
-#define SQLITE_ANALYZE ...
-#define SQLITE_CREATE_VTABLE ...
-#define SQLITE_DROP_VTABLE ...
-#define SQLITE_FUNCTION ...
+_lib.SQLITE_INTEGER = 1
+_lib.SQLITE_FLOAT = 2
+_lib.SQLITE_TEXT = 3
+_lib.SQLITE_BLOB = 4
+_lib.SQLITE_NULL = 5
 
-const char *sqlite3_libversion(void);
+_lib.SQLITE_UTF8 = 1
 
-typedef ... sqlite3;
-typedef ... sqlite3_stmt;
-typedef ... sqlite3_context;
-typedef ... sqlite3_value;
-typedef int64_t sqlite3_int64;
-typedef uint64_t sqlite3_uint64;
+_lib.SQLITE_TRANSIENT = cast(-1, c_void_p)
 
-int sqlite3_open(
-    const char *filename,   /* Database filename (UTF-8) */
-    sqlite3 **ppDb          /* OUT: SQLite db handle */
-);
+SQLITE_OK       = _lib.SQLITE_OK
 
-int sqlite3_close(sqlite3 *);
+SQLITE_DENY     = 1
+SQLITE_IGNORE   = 2
 
-int sqlite3_busy_timeout(sqlite3*, int ms);
-int sqlite3_prepare_v2(
-    sqlite3 *db,            /* Database handle */
-    const char *zSql,       /* SQL statement, UTF-8 encoded */
-    int nByte,              /* Maximum length of zSql in bytes. */
-    sqlite3_stmt **ppStmt,  /* OUT: Statement handle */
-    const char **pzTail     /* OUT: Pointer to unused portion of zSql */
-);
-int sqlite3_finalize(sqlite3_stmt *pStmt);
-int sqlite3_column_count(sqlite3_stmt *pStmt);
-const char *sqlite3_column_name(sqlite3_stmt*, int N);
-int sqlite3_get_autocommit(sqlite3*);
-int sqlite3_reset(sqlite3_stmt *pStmt);
-int sqlite3_step(sqlite3_stmt*);
-int sqlite3_errcode(sqlite3 *db);
-const char *sqlite3_errmsg(sqlite3*);
-int sqlite3_changes(sqlite3*);
+SQLITE_CREATE_INDEX             = 1
+SQLITE_CREATE_TABLE             = 2
+SQLITE_CREATE_TEMP_INDEX        = 3
+SQLITE_CREATE_TEMP_TABLE        = 4
+SQLITE_CREATE_TEMP_TRIGGER      = 5
+SQLITE_CREATE_TEMP_VIEW         = 6
+SQLITE_CREATE_TRIGGER           = 7
+SQLITE_CREATE_VIEW              = 8
+SQLITE_DELETE                   = 9
+SQLITE_DROP_INDEX               = 10
+SQLITE_DROP_TABLE               = 11
+SQLITE_DROP_TEMP_INDEX          = 12
+SQLITE_DROP_TEMP_TABLE          = 13
+SQLITE_DROP_TEMP_TRIGGER        = 14
+SQLITE_DROP_TEMP_VIEW           = 15
+SQLITE_DROP_TRIGGER             = 16
+SQLITE_DROP_VIEW                = 17
+SQLITE_INSERT                   = 18
+SQLITE_PRAGMA                   = 19
+SQLITE_READ                     = 20
+SQLITE_SELECT                   = 21
+SQLITE_TRANSACTION              = 22
+SQLITE_UPDATE                   = 23
+SQLITE_ATTACH                   = 24
+SQLITE_DETACH                   = 25
+SQLITE_ALTER_TABLE              = 26
+SQLITE_REINDEX                  = 27
+SQLITE_ANALYZE                  = 28
 
-int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*));
-int sqlite3_bind_double(sqlite3_stmt*, int, double);
-int sqlite3_bind_int(sqlite3_stmt*, int, int);
-int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64);
-int sqlite3_bind_null(sqlite3_stmt*, int);
-int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int n, void(*)(void*));
-int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void(*)(void*));
-int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*);
-int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n);
+# SQLite C API
 
-const void *sqlite3_column_blob(sqlite3_stmt*, int iCol);
-int sqlite3_column_bytes(sqlite3_stmt*, int iCol);
-double sqlite3_column_double(sqlite3_stmt*, int iCol);
-int sqlite3_column_int(sqlite3_stmt*, int iCol);
-sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol);
-const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);
-const void *sqlite3_column_text16(sqlite3_stmt*, int iCol);
-int sqlite3_column_type(sqlite3_stmt*, int iCol);
-const char *sqlite3_column_decltype(sqlite3_stmt*,int);
+_lib.sqlite3_value_int.argtypes = [c_void_p]
+_lib.sqlite3_value_int.restype = c_int
 
-void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
-int sqlite3_create_collation(
-    sqlite3*,
-    const char *zName,
-    int eTextRep,
-    void*,
-    int(*xCompare)(void*,int,const void*,int,const void*)
-);
-int sqlite3_set_authorizer(
-    sqlite3*,
-    int (*xAuth)(void*,int,const char*,const char*,const char*,const char*),
-    void *pUserData
-);
-int sqlite3_create_function(
-    sqlite3 *db,
-    const char *zFunctionName,
-    int nArg,
-    int eTextRep,
-    void *pApp,
-    void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
-    void (*xStep)(sqlite3_context*,int,sqlite3_value**),
-    void (*xFinal)(sqlite3_context*)
-);
-void *sqlite3_aggregate_context(sqlite3_context*, int nBytes);
+_lib.sqlite3_value_int64.argtypes = [c_void_p]
+_lib.sqlite3_value_int64.restype = c_int64
 
-sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*);
-int sqlite3_bind_parameter_count(sqlite3_stmt*);
-const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int);
-int sqlite3_total_changes(sqlite3*);
+_lib.sqlite3_value_blob.argtypes = [c_void_p]
+_lib.sqlite3_value_blob.restype = c_void_p
 
-int sqlite3_prepare(
-    sqlite3 *db,            /* Database handle */
-    const char *zSql,       /* SQL statement, UTF-8 encoded */
-    int nByte,              /* Maximum length of zSql in bytes. */
-    sqlite3_stmt **ppStmt,  /* OUT: Statement handle */
-    const char **pzTail     /* OUT: Pointer to unused portion of zSql */
-);
+_lib.sqlite3_value_bytes.argtypes = [c_void_p]
+_lib.sqlite3_value_bytes.restype = c_int
 
-void sqlite3_result_blob(sqlite3_context*, const void*, int, void(*)(void*));
-void sqlite3_result_double(sqlite3_context*, double);
-void sqlite3_result_error(sqlite3_context*, const char*, int);
-void sqlite3_result_error16(sqlite3_context*, const void*, int);
-void sqlite3_result_error_toobig(sqlite3_context*);
-void sqlite3_result_error_nomem(sqlite3_context*);
-void sqlite3_result_error_code(sqlite3_context*, int);
-void sqlite3_result_int(sqlite3_context*, int);
-void sqlite3_result_int64(sqlite3_context*, sqlite3_int64);
-void sqlite3_result_null(sqlite3_context*);
-void sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*));
-void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*));
-void sqlite3_result_text16le(sqlite3_context*,const void*, int,void(*)(void*));
-void sqlite3_result_text16be(sqlite3_context*,const void*, int,void(*)(void*));
-void sqlite3_result_value(sqlite3_context*, sqlite3_value*);
-void sqlite3_result_zeroblob(sqlite3_context*, int n);
+_lib.sqlite3_value_double.argtypes = [c_void_p]
+_lib.sqlite3_value_double.restype = c_double
 
-const void *sqlite3_value_blob(sqlite3_value*);
-int sqlite3_value_bytes(sqlite3_value*);
-int sqlite3_value_bytes16(sqlite3_value*);
-double sqlite3_value_double(sqlite3_value*);
-int sqlite3_value_int(sqlite3_value*);
-sqlite3_int64 sqlite3_value_int64(sqlite3_value*);
-const unsigned char *sqlite3_value_text(sqlite3_value*);
-const void *sqlite3_value_text16(sqlite3_value*);
-const void *sqlite3_value_text16le(sqlite3_value*);
-const void *sqlite3_value_text16be(sqlite3_value*);
-int sqlite3_value_type(sqlite3_value*);
-int sqlite3_value_numeric_type(sqlite3_value*);
-""")
+_lib.sqlite3_value_text.argtypes = [c_void_p]
+_lib.sqlite3_value_text.restype = c_char_p
 
+_lib.sqlite3_value_type.argtypes = [c_void_p]
+_lib.sqlite3_value_type.restype = c_int
 
-def _has_load_extension():
-    """Only available since 3.3.6"""
-    unverified_ffi = FFI()
-    unverified_ffi.cdef("""
-    typedef ... sqlite3;
-    int sqlite3_enable_load_extension(sqlite3 *db, int onoff);
-    """)
-    unverified_lib = unverified_ffi.dlopen('sqlite3')
-    return hasattr(unverified_lib, 'sqlite3_enable_load_extension')
+_lib.sqlite3_bind_blob.argtypes = [c_void_p, c_int, c_void_p, c_int, c_void_p]
+_lib.sqlite3_bind_blob.restype = c_int
+_lib.sqlite3_bind_double.argtypes = [c_void_p, c_int, c_double]
+_lib.sqlite3_bind_double.restype = c_int
+_lib.sqlite3_bind_int.argtypes = [c_void_p, c_int, c_int]
+_lib.sqlite3_bind_int.restype = c_int
+_lib.sqlite3_bind_int64.argtypes = [c_void_p, c_int, c_int64]
+_lib.sqlite3_bind_int64.restype = c_int
+_lib.sqlite3_bind_null.argtypes = [c_void_p, c_int]
+_lib.sqlite3_bind_null.restype = c_int
+_lib.sqlite3_bind_parameter_count.argtypes = [c_void_p]
+_lib.sqlite3_bind_parameter_count.restype = c_int
+_lib.sqlite3_bind_parameter_index.argtypes = [c_void_p, c_char_p]
+_lib.sqlite3_bind_parameter_index.restype = c_int
+_lib.sqlite3_bind_parameter_name.argtypes = [c_void_p, c_int]
+_lib.sqlite3_bind_parameter_name.restype = c_char_p
+_lib.sqlite3_bind_text.argtypes = [c_void_p, c_int, c_char_p, c_int, c_void_p]
+_lib.sqlite3_bind_text.restype = c_int
+_lib.sqlite3_busy_timeout.argtypes = [c_void_p, c_int]
+_lib.sqlite3_busy_timeout.restype = c_int
+_lib.sqlite3_changes.argtypes = [c_void_p]
+_lib.sqlite3_changes.restype = c_int
+_lib.sqlite3_close.argtypes = [c_void_p]
+_lib.sqlite3_close.restype = c_int
+_lib.sqlite3_column_blob.argtypes = [c_void_p, c_int]
+_lib.sqlite3_column_blob.restype = c_void_p
+_lib.sqlite3_column_bytes.argtypes = [c_void_p, c_int]
+_lib.sqlite3_column_bytes.restype = c_int
+_lib.sqlite3_column_count.argtypes = [c_void_p]
+_lib.sqlite3_column_count.restype = c_int
+_lib.sqlite3_column_decltype.argtypes = [c_void_p, c_int]
+_lib.sqlite3_column_decltype.restype = c_char_p
+_lib.sqlite3_column_double.argtypes = [c_void_p, c_int]
+_lib.sqlite3_column_double.restype = c_double
+_lib.sqlite3_column_int64.argtypes = [c_void_p, c_int]
+_lib.sqlite3_column_int64.restype = c_int64
+_lib.sqlite3_column_name.argtypes = [c_void_p, c_int]
+_lib.sqlite3_column_name.restype = c_char_p
+_lib.sqlite3_column_text.argtypes = [c_void_p, c_int]
+_lib.sqlite3_column_text.restype = POINTER(c_char)
+_lib.sqlite3_column_type.argtypes = [c_void_p, c_int]
+_lib.sqlite3_column_type.restype = c_int
+_lib.sqlite3_complete.argtypes = [c_char_p]
+_lib.sqlite3_complete.restype = c_int
+_lib.sqlite3_errcode.restype = c_int
+_lib.sqlite3_errmsg.argtypes = [c_void_p]
+_lib.sqlite3_errmsg.restype = c_char_p
+_lib.sqlite3_finalize.argtypes = [c_void_p]
+_lib.sqlite3_finalize.restype = c_int
+_lib.sqlite3_get_autocommit.argtypes = [c_void_p]
+_lib.sqlite3_get_autocommit.restype = c_int
+_lib.sqlite3_last_insert_rowid.argtypes = [c_void_p]
+_lib.sqlite3_last_insert_rowid.restype = c_int64
+_lib.sqlite3_libversion.argtypes = []
+_lib.sqlite3_libversion.restype = c_char_p
+_lib.sqlite3_open.argtypes = [c_char_p, c_void_p]
+_lib.sqlite3_open.restype = c_int
+_lib.sqlite3_prepare.argtypes = [c_void_p, c_char_p, c_int, c_void_p, POINTER(c_char_p)]
+_lib.sqlite3_prepare.restype = c_int
+_lib.sqlite3_prepare_v2.argtypes = [c_void_p, c_char_p, c_int, c_void_p, POINTER(c_char_p)]
+_lib.sqlite3_prepare_v2.restype = c_int
+_lib.sqlite3_step.argtypes = [c_void_p]
+_lib.sqlite3_step.restype = c_int
+_lib.sqlite3_reset.argtypes = [c_void_p]
+_lib.sqlite3_reset.restype = c_int
+_lib.sqlite3_total_changes.argtypes = [c_void_p]
+_lib.sqlite3_total_changes.restype = c_int
 
+_lib.sqlite3_result_blob.argtypes = [c_void_p, c_void_p, c_int, c_void_p]
+_lib.sqlite3_result_blob.restype = None
+_lib.sqlite3_result_int64.argtypes = [c_void_p, c_int64]
+_lib.sqlite3_result_int64.restype = None
+_lib.sqlite3_result_null.argtypes = [c_void_p]
+_lib.sqlite3_result_null.restype = None
+_lib.sqlite3_result_double.argtypes = [c_void_p, c_double]
+_lib.sqlite3_result_double.restype = None
+_lib.sqlite3_result_error.argtypes = [c_void_p, c_char_p, c_int]
+_lib.sqlite3_result_error.restype = None
+_lib.sqlite3_result_text.argtypes = [c_void_p, c_char_p, c_int, c_void_p]
+_lib.sqlite3_result_text.restype = None
 
-if _has_load_extension():
-    ffi.cdef("int sqlite3_enable_load_extension(sqlite3 *db, int onoff);")
+_HAS_LOAD_EXTENSION = hasattr(_lib, "sqlite3_enable_load_extension")
+if _HAS_LOAD_EXTENSION:
+    _lib.sqlite3_enable_load_extension.argtypes = [c_void_p, c_int]
+    _lib.sqlite3_enable_load_extension.restype = c_int
 
-lib = ffi.verify("""
-#include <sqlite3.h>
-""", libraries=['sqlite3'])
-
-exported_sqlite_symbols = [
-    'SQLITE_ALTER_TABLE',
-    'SQLITE_ANALYZE',
-    'SQLITE_ATTACH',
-    'SQLITE_CREATE_INDEX',
-    'SQLITE_CREATE_TABLE',
-    'SQLITE_CREATE_TEMP_INDEX',
-    'SQLITE_CREATE_TEMP_TABLE',
-    'SQLITE_CREATE_TEMP_TRIGGER',
-    'SQLITE_CREATE_TEMP_VIEW',
-    'SQLITE_CREATE_TRIGGER',
-    'SQLITE_CREATE_VIEW',
-    'SQLITE_DELETE',
-    'SQLITE_DENY',
-    'SQLITE_DETACH',
-    'SQLITE_DROP_INDEX',
-    'SQLITE_DROP_TABLE',
-    'SQLITE_DROP_TEMP_INDEX',
-    'SQLITE_DROP_TEMP_TABLE',
-    'SQLITE_DROP_TEMP_TRIGGER',
-    'SQLITE_DROP_TEMP_VIEW',
-    'SQLITE_DROP_TRIGGER',
-    'SQLITE_DROP_VIEW',
-    'SQLITE_IGNORE',
-    'SQLITE_INSERT',
-    'SQLITE_OK',
-    'SQLITE_PRAGMA',
-    'SQLITE_READ',
-    'SQLITE_REINDEX',
-    'SQLITE_SELECT',
-    'SQLITE_TRANSACTION',
-    'SQLITE_UPDATE',
-]
-
-for symbol in exported_sqlite_symbols:
-    globals()[symbol] = getattr(lib, symbol)
-
-_SQLITE_TRANSIENT = ffi.cast('void *', lib.SQLITE_TRANSIENT)
-
+##########################################
+# END Wrapped SQLite C API and constants
+##########################################
 
 # pysqlite version information
 version = "2.6.0"
@@ -310,7 +257,7 @@
 PARSE_DECLTYPES = 2
 
 # SQLite version information
-sqlite_version = ffi.string(lib.sqlite3_libversion())
+sqlite_version = str(_lib.sqlite3_libversion().decode('ascii'))
 
 
 class Error(StandardError):
@@ -387,36 +334,48 @@
             if len(self.cache) > self.maxcount:
                 self.cache.popitem(0)
 
-        if stat.in_use:
+        if stat._in_use:
             stat = Statement(self.connection, sql)
-        stat.set_row_factory(row_factory)
+        stat._row_factory = row_factory
         return stat
 
 
 class Connection(object):
-    def __init__(self, database, timeout=5.0, detect_types=0,
-                 isolation_level="", check_same_thread=True, factory=None,
-                 cached_statements=100):
-        db_star = ffi.new('sqlite3 **')
+    __initialized = False
+    _db = None
+
+    def __init__(self, database, timeout=5.0, detect_types=0, isolation_level="",
+                 check_same_thread=True, factory=None, cached_statements=100):
+        self.__initialized = True
+        self._db = c_void_p()
+
         if isinstance(database, unicode):
             database = database.encode('utf-8')
-        if lib.sqlite3_open(database, db_star) != lib.SQLITE_OK:
+        if _lib.sqlite3_open(database, byref(self._db)) != _lib.SQLITE_OK:
             raise OperationalError("Could not open database")
-        self.db = db_star[0]
         if timeout is not None:
             timeout = int(timeout * 1000)  # pysqlite2 uses timeout in seconds
-            lib.sqlite3_busy_timeout(self.db, timeout)
+            _lib.sqlite3_busy_timeout(self._db, timeout)
 
+        self.row_factory = None
         self.text_factory = _unicode_text_factory
-        self.closed = False
-        self.statements = []
-        self.statement_counter = 0
-        self.row_factory = None
-        self._isolation_level = isolation_level
-        self.detect_types = detect_types
-        self.statement_cache = _StatementCache(self, cached_statements)
 
-        self.cursors = []
+        self._detect_types = detect_types
+        self._in_transaction = False
+        self.isolation_level = isolation_level
+
+        self.__cursors = []
+        self.__cursors_counter = 0
+        self.__statements = []
+        self.__statements_counter = 0
+        self._statement_cache = _StatementCache(self, cached_statements)
+
+        self.__func_cache = {}
+        self.__aggregates = {}
+        self.__aggregate_instances = {}
+        self.__collations = {}
+        if check_same_thread:
+            self.__thread_ident = _thread_get_ident()
 
         self.Error = Error
         self.Warning = Warning
@@ -429,30 +388,25 @@
         self.DataError = DataError
         self.NotSupportedError = NotSupportedError
 
-        self.func_cache = {}
-        self._aggregates = {}
-        self.aggregate_instances = {}
-        self._collations = {}
-        if check_same_thread:
-            self.thread_ident = _thread_get_ident()
+    def __del__(self):
+        if self._db:
+            _lib.sqlite3_close(self._db)
 
     def close(self):
         self._check_thread()
-        if self.closed:
-            return
-        for statement in self.statements:
-            obj = statement()
-            if obj is not None:
-                obj.finalize()
 
-        self.closed = True
-        ret = lib.sqlite3_close(self.db)
-        self._reset_cursors()
-        if ret != lib.SQLITE_OK:
-            raise self._get_exception(ret)
+        self.__do_all_statements(Statement._finalize, True)
+
+        if self._db:
+            ret = _lib.sqlite3_close(self._db)
+            if ret != _lib.SQLITE_OK:
+                raise self._get_exception(ret)
+            self._db = None
 
     def _check_closed(self):
-        if getattr(self, 'closed', True):
+        if not self.__initialized:
+            raise ProgrammingError("Base Connection.__init__ not called.")
+        if not self._db:
             raise ProgrammingError("Cannot operate on a closed database.")
 
     def _check_closed_wrap(func):
@@ -463,13 +417,16 @@
         return wrapper
 
     def _check_thread(self):
-        if not hasattr(self, 'thread_ident'):
-            return
-        if self.thread_ident != _thread_get_ident():
+        try:
+            if self.__thread_ident == _thread_get_ident():
+                return
+        except AttributeError:
+            pass
+        else:
             raise ProgrammingError(
                 "SQLite objects created in a thread can only be used in that "
                 "same thread. The object was created in thread id %d and this "
-                "is thread id %d", self.thread_ident, _thread_get_ident())
+                "is thread id %d", self.__thread_ident, _thread_get_ident())
 
     def _check_thread_wrap(func):
         @wraps(func)
@@ -480,29 +437,29 @@
 
     def _get_exception(self, error_code=None):
         if error_code is None:
-            error_code = lib.sqlite3_errcode(self.db)
-        error_message = ffi.string(lib.sqlite3_errmsg(self.db))
+            error_code = _lib.sqlite3_errcode(self._db)
+        error_message = _lib.sqlite3_errmsg(self._db).decode('utf-8')
 
-        if error_code == lib.SQLITE_OK:
-            raise ValueError("error signalled but got lib.SQLITE_OK")
-        elif error_code in (lib.SQLITE_INTERNAL, lib.SQLITE_NOTFOUND):
+        if error_code == _lib.SQLITE_OK:
+            raise ValueError("error signalled but got SQLITE_OK")
+        elif error_code in (_lib.SQLITE_INTERNAL, _lib.SQLITE_NOTFOUND):
             exc = InternalError
-        elif error_code == lib.SQLITE_NOMEM:
+        elif error_code == _lib.SQLITE_NOMEM:
             exc = MemoryError
         elif error_code in (
-                lib.SQLITE_ERROR, lib.SQLITE_PERM, lib.SQLITE_ABORT,
-                lib.SQLITE_BUSY, lib.SQLITE_LOCKED, lib.SQLITE_READONLY,
-                lib.SQLITE_INTERRUPT, lib.SQLITE_IOERR, lib.SQLITE_FULL,
-                lib.SQLITE_CANTOPEN, lib.SQLITE_PROTOCOL, lib.SQLITE_EMPTY,
-                lib.SQLITE_SCHEMA):
+                _lib.SQLITE_ERROR, _lib.SQLITE_PERM, _lib.SQLITE_ABORT,
+                _lib.SQLITE_BUSY, _lib.SQLITE_LOCKED, _lib.SQLITE_READONLY,
+                _lib.SQLITE_INTERRUPT, _lib.SQLITE_IOERR, _lib.SQLITE_FULL,
+                _lib.SQLITE_CANTOPEN, _lib.SQLITE_PROTOCOL, _lib.SQLITE_EMPTY,
+                _lib.SQLITE_SCHEMA):
             exc = OperationalError
-        elif error_code == lib.SQLITE_CORRUPT:
+        elif error_code == _lib.SQLITE_CORRUPT:
             exc = DatabaseError
-        elif error_code == lib.SQLITE_TOOBIG:
+        elif error_code == _lib.SQLITE_TOOBIG:
             exc = DataError
-        elif error_code in (lib.SQLITE_CONSTRAINT, lib.SQLITE_MISMATCH):
+        elif error_code in (_lib.SQLITE_CONSTRAINT, _lib.SQLITE_MISMATCH):
             exc = IntegrityError
-        elif error_code == lib.SQLITE_MISUSE:
+        elif error_code == _lib.SQLITE_MISUSE:
             exc = ProgrammingError
         else:
             exc = DatabaseError
@@ -510,26 +467,38 @@
         exc.error_code = error_code
         return exc
 
+    def _remember_cursor(self, cursor):
+        self.__cursors.append(weakref.ref(cursor))
+        self.__cursors_counter += 1
+        if self.__cursors_counter < 200:
+            return
+        self.__cursors_counter = 0
+        self.__cursors = [r for r in self.__cursors if r() is not None]
+
     def _remember_statement(self, statement):
-        self.statements.append(weakref.ref(statement))
-        self.statement_counter += 1
+        self.__statements.append(weakref.ref(statement))
+        self.__statements_counter += 1
+        if self.__statements_counter < 200:
+            return
+        self.__statements_counter = 0
+        self.__statements = [r for r in self.__statements if r() is not None]
 
-        if self.statement_counter % 100 == 0:
-            self.statements = [ref for ref in self.statements
-                               if ref() is not None]
+    def __do_all_statements(self, action, reset_cursors):
+        for weakref in self.__statements:
+            statement = weakref()
+            if statement is not None:
+                action(statement)
 
-    def _reset_cursors(self):
-        for cursor_ref in self.cursors:
-            cursor = cursor_ref()
-            if cursor:
-                cursor.reset = True
+        if reset_cursors:
+            for weakref in self.__cursors:
+                cursor = weakref()
+                if cursor is not None:
+                    cursor._reset = True
 
     @_check_thread_wrap
     @_check_closed_wrap
     def __call__(self, sql):
-        if not isinstance(sql, basestring):
-            raise Warning("SQL is of wrong type. Must be string or unicode.")
-        return self.statement_cache.get(sql, self.row_factory)
+        return self._statement_cache.get(sql, self.row_factory)
 
     def cursor(self, factory=None):
         self._check_thread()
@@ -558,73 +527,60 @@
         return _iterdump(self)
 
     def _begin(self):
-        self._check_closed()
-        if self._isolation_level is None:
-            return
-        if not lib.sqlite3_get_autocommit(self.db):
-            return
-        sql = "BEGIN " + self._isolation_level
-        statement_star = ffi.new('sqlite3_stmt **')
-        next_char = ffi.new('char **')
-        ret = lib.sqlite3_prepare_v2(self.db, sql, -1, statement_star,
-                                     next_char)
+        statement = c_void_p()
+        ret = _lib.sqlite3_prepare_v2(self._db, self.__begin_statement, -1,
+                                      byref(statement), None)
         try:
-            if ret != lib.SQLITE_OK:
+            if ret != _lib.SQLITE_OK:
                 raise self._get_exception(ret)
-            ret = lib.sqlite3_step(statement_star[0])
-            if ret != lib.SQLITE_DONE:
+            ret = _lib.sqlite3_step(statement)
+            if ret != _lib.SQLITE_DONE:
                 raise self._get_exception(ret)
+            self._in_transaction = True
         finally:
-            lib.sqlite3_finalize(statement_star[0])
+            _lib.sqlite3_finalize(statement)
 
     def commit(self):
         self._check_thread()
         self._check_closed()
-        if lib.sqlite3_get_autocommit(self.db):
+        if not self._in_transaction:
             return
 
-        for statement in self.statements:
-            obj = statement()
-            if obj is not None:
-                obj.reset()
+        self.__do_all_statements(Statement._reset, False)
 
-        statement_star = ffi.new('sqlite3_stmt **')
-        next_char = ffi.new('char **')
-        ret = lib.sqlite3_prepare_v2(self.db, b"COMMIT", -1, statement_star,
-                                     next_char)
+        statement = c_void_p()
+        ret = _lib.sqlite3_prepare_v2(self._db, b"COMMIT", -1,
+                                      byref(statement), None)
         try:
-            if ret != lib.SQLITE_OK:
+            if ret != _lib.SQLITE_OK:
                 raise self._get_exception(ret)
-            ret = lib.sqlite3_step(statement_star[0])
-            if ret != lib.SQLITE_DONE:
+            ret = _lib.sqlite3_step(statement)
+            if ret != _lib.SQLITE_DONE:
                 raise self._get_exception(ret)
+            self._in_transaction = False
         finally:
-            lib.sqlite3_finalize(statement_star[0])
+            _lib.sqlite3_finalize(statement)
 
     def rollback(self):
         self._check_thread()
         self._check_closed()
-        if lib.sqlite3_get_autocommit(self.db):
+        if not self._in_transaction:
             return
 
-        for statement in self.statements:
-            obj = statement()
-            if obj is not None:
-                obj.reset()
+        self.__do_all_statements(Statement._reset, True)
 
-        statement_star = ffi.new('sqlite3_stmt **')
-        next_char = ffi.new('char **')
-        ret = lib.sqlite3_prepare_v2(self.db, b"ROLLBACK", -1, statement_star,
-                                     next_char)
+        statement = c_void_p()
+        ret = _lib.sqlite3_prepare_v2(self._db, b"ROLLBACK", -1,
+                                      byref(statement), None)
         try:
-            if ret != lib.SQLITE_OK:
+            if ret != _lib.SQLITE_OK:
                 raise self._get_exception(ret)
-            ret = lib.sqlite3_step(statement_star[0])
-            if ret != lib.SQLITE_DONE:
+            ret = _lib.sqlite3_step(statement)
+            if ret != _lib.SQLITE_DONE:
                 raise self._get_exception(ret)
+            self._in_transaction = False
         finally:
-            lib.sqlite3_finalize(statement_star[0])
-            self._reset_cursors()
+            _lib.sqlite3_finalize(statement)
 
     def __enter__(self):
         return self
@@ -639,29 +595,34 @@
     @_check_closed_wrap
     def create_function(self, name, num_args, callback):
         try:
-            closure = self.func_cache[callback]
+            c_closure, _ = self.__func_cache[callback]
         except KeyError:
-            @ffi.callback("void(sqlite3_context*, int, sqlite3_value**)")
             def closure(context, nargs, c_params):
                 _function_callback(callback, context, nargs, c_params)
-            self.func_cache[callback] = closure
-        ret = lib.sqlite3_create_function(self.db, name, num_args,
-                                          lib.SQLITE_UTF8, ffi.NULL,
-                                          closure, ffi.NULL, ffi.NULL)
-        if ret != lib.SQLITE_OK:
+            c_closure = _FUNC(closure)
+            self.__func_cache[callback] = c_closure, closure
+
+        if isinstance(name, unicode):
+            name = name.encode('utf-8')
+        ret = _lib.sqlite3_create_function(self._db, name, num_args,
+                                           _lib.SQLITE_UTF8, None,
+                                           c_closure,
+                                           cast(None, _STEP),
+                                           cast(None, _FINAL))
+        if ret != _lib.SQLITE_OK:
             raise self.OperationalError("Error creating function")
 
     @_check_thread_wrap
     @_check_closed_wrap
     def create_aggregate(self, name, num_args, cls):
         try:
-            step_callback, final_callback = self._aggregates[cls]
+            c_step_callback, c_final_callback, _, _ = self.__aggregates[cls]
         except KeyError:
-            @ffi.callback("void(sqlite3_context*, int, sqlite3_value**)")
             def step_callback(context, argc, c_params):
-                res = lib.sqlite3_aggregate_context(context,
-                                                    ffi.sizeof("size_t"))
-                aggregate_ptr = ffi.cast("size_t[1]", res)
+                aggregate_ptr = cast(
+                    _lib.sqlite3_aggregate_context(
+                        context, sizeof(c_ssize_t)),
+                    POINTER(c_ssize_t))
 
                 if not aggregate_ptr[0]:
                     try:
@@ -669,13 +630,13 @@
                     except Exception:
                         msg = (b"user-defined aggregate's '__init__' "
                                b"method raised error")
-                        lib.sqlite3_result_error(context, msg, len(msg))
+                        _lib.sqlite3_result_error(context, msg, len(msg))
                         return
                     aggregate_id = id(aggregate)
-                    self.aggregate_instances[aggregate_id] = aggregate
+                    self.__aggregate_instances[aggregate_id] = aggregate
                     aggregate_ptr[0] = aggregate_id
                 else:
-                    aggregate = self.aggregate_instances[aggregate_ptr[0]]
+                    aggregate = self.__aggregate_instances[aggregate_ptr[0]]
 
                 params = _convert_params(context, argc, c_params)
                 try:
@@ -683,33 +644,41 @@
                 except Exception:
                     msg = (b"user-defined aggregate's 'step' "
                            b"method raised error")
-                    lib.sqlite3_result_error(context, msg, len(msg))
+                    _lib.sqlite3_result_error(context, msg, len(msg))
 
-            @ffi.callback("void(sqlite3_context*)")
             def final_callback(context):
-                res = lib.sqlite3_aggregate_context(context,
-                                                    ffi.sizeof("size_t"))
-                aggregate_ptr = ffi.cast("size_t[1]", res)
+                aggregate_ptr = cast(
+                    _lib.sqlite3_aggregate_context(
+                        context, sizeof(c_ssize_t)),
+                    POINTER(c_ssize_t))
 
                 if aggregate_ptr[0]:
-                    aggregate = self.aggregate_instances[aggregate_ptr[0]]
+                    aggregate = self.__aggregate_instances[aggregate_ptr[0]]
                     try:
                         val = aggregate.finalize()
                     except Exception:
                         msg = (b"user-defined aggregate's 'finalize' "
                                b"method raised error")
-                        lib.sqlite3_result_error(context, msg, len(msg))
+                        _lib.sqlite3_result_error(context, msg, len(msg))
                     else:
                         _convert_result(context, val)
                     finally:
-                        del self.aggregate_instances[aggregate_ptr[0]]
+                        del self.__aggregate_instances[aggregate_ptr[0]]
 
-            self._aggregates[cls] = step_callback, final_callback
+            c_step_callback = _STEP(step_callback)
+            c_final_callback = _FINAL(final_callback)
 
-        ret = lib.sqlite3_create_function(self.db, name, num_args,
-                                          lib.SQLITE_UTF8, ffi.NULL, ffi.NULL,
-                                          step_callback, final_callback)
-        if ret != lib.SQLITE_OK:
+            self.__aggregates[cls] = (c_step_callback, c_final_callback,
+                                     step_callback, final_callback)
+
+        if isinstance(name, unicode):
+            name = name.encode('utf-8')
+        ret = _lib.sqlite3_create_function(self._db, name, num_args,
+                                           _lib.SQLITE_UTF8, None,
+                                           cast(None, _FUNC),
+                                           c_step_callback,
+                                           c_final_callback)
+        if ret != _lib.SQLITE_OK:
             raise self._get_exception(ret)
 
     @_check_thread_wrap
@@ -720,55 +689,60 @@
             raise ProgrammingError("invalid character in collation name")
 
         if callback is None:
-            del self._collations[name]
-            collation_callback = ffi.NULL
+            del self.__collations[name]
+            c_collation_callback = cast(None, _COLLATION)
         else:
             if not callable(callback):
                 raise TypeError("parameter must be callable")
 
-            @ffi.callback("int(void*, int, const void*, int, const void*)")
             def collation_callback(context, len1, str1, len2, str2):
-                text1 = ffi.buffer(str1, len1)[:]
-                text2 = ffi.buffer(str2, len2)[:]
+                text1 = string_at(str1, len1).decode('utf-8')
+                text2 = string_at(str2, len2).decode('utf-8')
 
                 return callback(text1, text2)
 
-            self._collations[name] = collation_callback
+            c_collation_callback = _COLLATION(collation_callback)
+            self.__collations[name] = c_collation_callback
 
-        ret = lib.sqlite3_create_collation(self.db, name, lib.SQLITE_UTF8,
-                                           ffi.NULL, collation_callback)
-        if ret != lib.SQLITE_OK:
+        if isinstance(name, unicode):
+            name = name.encode('utf-8')
+        ret = _lib.sqlite3_create_collation(self._db, name,
+                                              _lib.SQLITE_UTF8,
+                                              None,
+                                              c_collation_callback)
+        if ret != _lib.SQLITE_OK:
             raise self._get_exception(ret)
 
     @_check_thread_wrap
     @_check_closed_wrap
     def set_authorizer(self, callback):
         try:
-            authorizer = self.func_cache[callback]
+            c_authorizer, _ = self.__func_cache[callback]
         except KeyError:
-            @ffi.callback("int(void*, int, const char*, const char*, "
-                          "const char*, const char*)")
             def authorizer(userdata, action, arg1, arg2, dbname, source):
                 try:
                     return int(callback(action, arg1, arg2, dbname, source))
                 except Exception:
-                    return lib.SQLITE_DENY
-            self.func_cache[callback] = authorizer
+                    return SQLITE_DENY
+            c_authorizer = _AUTHORIZER(authorizer)
 
-        ret = lib.sqlite3_set_authorizer(self.db, authorizer, ffi.NULL)
-        if ret != lib.SQLITE_OK:
+            self.__func_cache[callback] = c_authorizer, authorizer
+
+        ret = _lib.sqlite3_set_authorizer(self._db,
+                                            c_authorizer,
+                                            None)
+        if ret != _lib.SQLITE_OK:
             raise self._get_exception(ret)
 
     @_check_thread_wrap
     @_check_closed_wrap
     def set_progress_handler(self, callable, nsteps):
         if callable is None:
-            progress_handler = ffi.NULL
+            c_progress_handler = cast(None, _PROGRESS)
         else:
             try:
-                progress_handler = self.func_cache[callable]
+                c_progress_handler, _ = self.__func_cache[callable]
             except KeyError:
-                @ffi.callback("int(void*)")
                 def progress_handler(userdata):
                     try:
                         ret = callable()
@@ -776,14 +750,23 @@
                     except Exception:
                         # abort query if error occurred
                         return 1
+                c_progress_handler = _PROGRESS(progress_handler)
 
-                self.func_cache[callable] = progress_handler
-        lib.sqlite3_progress_handler(self.db, nsteps, progress_handler,
-                                     ffi.NULL)
+                self.__func_cache[callable] = c_progress_handler, progress_handler
+        ret = _lib.sqlite3_progress_handler(self._db, nsteps,
+                                              c_progress_handler,
+                                              None)
+        if ret != _lib.SQLITE_OK:
+            raise self._get_exception(ret)
+
+    if sys.version_info[0] >= 3:
+        def __get_in_transaction(self):
+            return self._in_transaction
+        in_transaction = property(__get_in_transaction)
 
     def __get_total_changes(self):
         self._check_closed()
-        return lib.sqlite3_total_changes(self.db)
+        return _lib.sqlite3_total_changes(self._db)
     total_changes = property(__get_total_changes)
 
     def __get_isolation_level(self):
@@ -792,193 +775,190 @@
     def __set_isolation_level(self, val):
         if val is None:
             self.commit()
-        if isinstance(val, unicode):
-            val = str(val)
+        else:
+            self.__begin_statement = str("BEGIN " + val).encode('utf-8')
         self._isolation_level = val
     isolation_level = property(__get_isolation_level, __set_isolation_level)
 
-    if hasattr(lib, 'sqlite3_enable_load_extension'):
+    if _HAS_LOAD_EXTENSION:
         @_check_thread_wrap
         @_check_closed_wrap
         def enable_load_extension(self, enabled):
-            rc = lib.sqlite3_enable_load_extension(self.db, int(enabled))
-            if rc != lib.SQLITE_OK:
+            rc = _lib.sqlite3_enable_load_extension(self._db, int(enabled))
+            if rc != _lib.SQLITE_OK:
                 raise OperationalError("Error enabling load extension")
 
 
-DML, DQL, DDL = range(3)
+class Cursor(object):
+    __initialized = False
+    __statement = None
 
-
-class CursorLock(object):
-    def __init__(self, cursor):
-        self.cursor = cursor
-
-    def __enter__(self):
-        if self.cursor.locked:
-            raise ProgrammingError("Recursive use of cursors not allowed.")
-        self.cursor.locked = True
-
-    def __exit__(self, *args):
-        self.cursor.locked = False
-
-
-class Cursor(object):
     def __init__(self, con):
         if not isinstance(con, Connection):
             raise TypeError
-        con._check_thread()
-        con._check_closed()
-        con.cursors.append(weakref.ref(self))
-        self.connection = con
-        self._description = None
+        self.__connection = con
+
         self.arraysize = 1
         self.row_factory = None
-        self.rowcount = -1
-        self.statement = None
-        self.reset = False
-        self.locked = False
+        self._reset = False
+        self.__locked = False
+        self.__closed = False
+        self.__description = None
+        self.__rowcount = -1
 
-    def _check_closed(self):
-        if not getattr(self, 'connection', None):
+        con._check_thread()
+        con._remember_cursor(self)
+
+        self.__initialized = True
+
+    def __del__(self):
+        if self.__statement:
+            self.__statement._reset()
+
+    def close(self):
+        self.__connection._check_thread()
+        self.__connection._check_closed()
+        if self.__statement:
+            self.__statement._reset()
+            self.__statement = None
+        self.__closed = True
+
+    def __check_cursor(self):
+        if not self.__initialized:
+            raise ProgrammingError("Base Cursor.__init__ not called.")
+        if self.__closed:
             raise ProgrammingError("Cannot operate on a closed cursor.")
-        self.connection._check_thread()
-        self.connection._check_closed()
+        if self.__locked:
+            raise ProgrammingError("Recursive use of cursors not allowed.")
+        self.__connection._check_thread()
+        self.__connection._check_closed()
 
-    def _check_and_lock(self):
-        self._check_closed()
-        return CursorLock(self)
+    def __check_cursor_wrap(func):
+        @wraps(func)
+        def wrapper(self, *args, **kwargs):
+            self.__check_cursor()
+            return func(self, *args, **kwargs)
+        return wrapper
 
-    def execute(self, sql, params=None):
-        if type(sql) is unicode:
-            sql = sql.encode("utf-8")
-
-        with self._check_and_lock():
-            self._description = None
-            self.reset = False
-            self.statement = self.connection.statement_cache.get(
+    def __execute(self, multiple, sql, many_params):
+        self.__locked = True
+        try:
+            self._reset = False
+            if not isinstance(sql, basestring):
+                raise ValueError("operation parameter must be str or unicode")
+            self.__description = None
+            self.__rowcount = -1
+            self.__statement = self.__connection._statement_cache.get(
                 sql, self.row_factory)
 
-            if self.connection._isolation_level is not None:
-                if self.statement.kind == DDL:
-                    self.connection.commit()
-                elif self.statement.kind == DML:
-                    self.connection._begin()
+            if self.__connection._isolation_level is not None:
+                if self.__statement._kind == Statement._DDL:
+                    if self.__connection._in_transaction:
+                        self.__connection.commit()
+                elif self.__statement._kind == Statement._DML:
+                    if not self.__connection._in_transaction:
+                        self.__connection._begin()
 
-            self.statement.set_params(params)
+            if multiple and self.__statement._kind != Statement._DML:
+                raise ProgrammingError("executemany is only for DML statements")
 
-            # Actually execute the SQL statement
-            ret = lib.sqlite3_step(self.statement.statement)
-            if ret not in (lib.SQLITE_DONE, lib.SQLITE_ROW):
-                self.statement.reset()
-                raise self.connection._get_exception(ret)
+            for params in many_params:
+                self.__statement._set_params(params)
 
-            if self.statement.kind == DML:
-                self.statement.reset()
+                # Actually execute the SQL statement
+                ret = _lib.sqlite3_step(self.__statement._statement)
+                if ret not in (_lib.SQLITE_DONE, _lib.SQLITE_ROW):
+                    self.__statement._reset()
+                    self.__connection._in_transaction = \
+                        not _lib.sqlite3_get_autocommit(self.__connection._db)
+                    raise self.__connection._get_exception(ret)
 
-            if self.statement.kind == DQL and ret == lib.SQLITE_ROW:
-                self.statement._build_row_cast_map()
-                self.statement._readahead(self)
-            else:
-                self.statement.item = None
-                self.statement.exhausted = True
+                if self.__statement._kind == Statement._DML:
+                    self.__statement._reset()
 
-            self.rowcount = -1
-            if self.statement.kind == DML:
-                self.rowcount = lib.sqlite3_changes(self.connection.db)
+                if self.__statement._kind == Statement._DQL and ret == _lib.SQLITE_ROW:
+                    self.__statement._build_row_cast_map()
+                    self.__statement._readahead(self)
 
+                if self.__statement._kind == Statement._DML:
+                    if self.__rowcount == -1:
+                        self.__rowcount = 0
+                    self.__rowcount += _lib.sqlite3_changes(self.__connection._db)
+        finally:
+            self.__locked = False
         return self
 
+    @__check_cursor_wrap
+    def execute(self, sql, params=[]):
+        return self.__execute(False, sql, [params])
+
+    @__check_cursor_wrap
     def executemany(self, sql, many_params):
-        if type(sql) is unicode:
-            sql = sql.encode("utf-8")
-
-        with self._check_and_lock():
-            self._description = None
-            self.reset = False
-            self.statement = self.connection.statement_cache.get(
-                sql, self.row_factory)
-
-            if self.statement.kind == DML:
-                self.connection._begin()
-            else:
-                raise ProgrammingError(
-                    "executemany is only for DML statements")
-
-            self.rowcount = 0
-            for params in many_params:
-                self.statement.set_params(params)
-                ret = lib.sqlite3_step(self.statement.statement)
-                if ret != lib.SQLITE_DONE:
-                    raise self.connection._get_exception(ret)
-                self.rowcount += lib.sqlite3_changes(self.connection.db)
-
-        return self
+        return self.__execute(True, sql, many_params)
 
     def executescript(self, sql):
-        self._description = None
-        self.reset = False
-        if type(sql) is unicode:
-            sql = sql.encode("utf-8")
-        self._check_closed()
-        statement_star = ffi.new('sqlite3_stmt **')
-        tail = ffi.new('char **')
+        self.__check_cursor()
+        self._reset = False
+        if isinstance(sql, unicode):
+            sql = sql.encode('utf-8')
+        elif not isinstance(sql, str):
+            raise ValueError("script argument must be unicode or string.")
+        sql = c_char_p(sql)
+        statement = c_void_p()
 
-        self.connection.commit()
+        self.__connection.commit()
         while True:
-            rc = lib.sqlite3_prepare(self.connection.db, sql, -1,
-                                     statement_star, tail)
-            sql = ffi.string(tail[0])
-            if rc != lib.SQLITE_OK:
-                raise self.connection._get_exception(rc)
+            rc = _lib.sqlite3_prepare(self.__connection._db, sql, -1,
+                                      byref(statement), byref(sql))
+            if rc != _lib.SQLITE_OK:
+                raise self.__connection._get_exception(rc)
 
-            rc = lib.SQLITE_ROW
-            while rc == lib.SQLITE_ROW:
-                if not statement_star[0]:
-                    rc = lib.SQLITE_OK
+            rc = _lib.SQLITE_ROW
+            while rc == _lib.SQLITE_ROW:
+                if not statement:
+                    rc = _lib.SQLITE_OK
                 else:
-                    rc = lib.sqlite3_step(statement_star[0])
+                    rc = _lib.sqlite3_step(statement)
 
-            if rc != lib.SQLITE_DONE:
-                lib.sqlite3_finalize(statement_star[0])
-                if rc == lib.SQLITE_OK:
-                    return self
+            if rc != _lib.SQLITE_DONE:
+                _lib.sqlite3_finalize(statement)
+                if rc == _lib.SQLITE_OK:
+                    break
                 else:
-                    raise self.connection._get_exception(rc)
-            rc = lib.sqlite3_finalize(statement_star[0])
-            if rc != lib.SQLITE_OK:
-                raise self.connection._get_exception(rc)
+                    raise self.__connection._get_exception(rc)
 
-            if not sql:
+            rc = _lib.sqlite3_finalize(statement)
+            if rc != _lib.SQLITE_OK:
+                raise self.__connection._get_exception(rc)
+
+            if not sql.value:
                 break
         return self
 
+    def __check_reset(self):
+        if self._reset:
+            raise self.__connection.InterfaceError(
+                    "Cursor needed to be reset because of commit/rollback "
+                    "and can no longer be fetched from.")
+
     def __iter__(self):
-        return iter(self.fetchone, None)
+        return self
 
-    def _check_reset(self):
-        if self.reset:
-            raise self.connection.InterfaceError(
-                "Cursor needed to be reset because of commit/rollback and can "
-                "no longer be fetched from.")
+    def __next__(self):
+        self.__check_cursor()
+        self.__check_reset()
+        if not self.__statement:
+            raise StopIteration
+        return self.__statement._next(self)
 
-    # do all statements
+    if sys.version_info[0] < 3:
+        next = __next__
+        del __next__
+
     def fetchone(self):
-        self._check_closed()
-        self._check_reset()
-
-        if self.statement is None:
-            return None
-
-        try:
-            return self.statement.next(self)
-        except StopIteration:
-            return None
+        return next(self, None)
 
     def fetchmany(self, size=None):
-        self._check_closed()
-        self._check_reset()
-        if self.statement is None:
-            return []
         if size is None:
             size = self.arraysize
         lst = []
@@ -989,29 +969,25 @@
         return lst
 
     def fetchall(self):
-        self._check_closed()
-        self._check_reset()
-        if self.statement is None:
-            return []
         return list(self)
 
-    def _getdescription(self):
-        if self._description is None:
-            self._description = self.statement._get_description()
-        return self._description
+    def __get_connection(self):
+        return self.__connection
+    connection = property(__get_connection)
 
-    def _getlastrowid(self):
-        return lib.sqlite3_last_insert_rowid(self.connection.db)
+    def __get_rowcount(self):
+        return self.__rowcount
+    rowcount = property(__get_rowcount)
 
-    def close(self):
-        if not self.connection:
-            return
-        self._check_closed()
-        if self.statement:
-            self.statement.reset()
-            self.statement = None
-        self.connection.cursors.remove(weakref.ref(self))
-        self.connection = None
+    def __get_description(self):
+        if self.__description is None:
+            self.__description = self.__statement._get_description()
+        return self.__description
+    description = property(__get_description)
+
+    def __get_lastrowid(self):
+        return _lib.sqlite3_last_insert_rowid(self.__connection._db)
+    lastrowid = property(__get_lastrowid)
 
     def setinputsizes(self, *args):
         pass
@@ -1019,92 +995,71 @@
     def setoutputsize(self, *args):
         pass
 
-    description = property(_getdescription)
-    lastrowid = property(_getlastrowid)
-
 
 class Statement(object):
+    _DML, _DQL, _DDL = range(3)
+
+    _statement = None
+
     def __init__(self, connection, sql):
-        self.statement = ffi.NULL
-        if not isinstance(sql, str):
-            raise ValueError("sql must be a string")
-        self.con = connection
-        self.sql = sql  # DEBUG ONLY
+        self.__con = connection
+        self.__con._remember_statement(self)
+
+        if not isinstance(sql, basestring):
+            raise Warning("SQL is of wrong type. Must be string or unicode.")
         first_word = self._statement_kind = sql.lstrip().split(" ")[0].upper()
         if first_word in ("INSERT", "UPDATE", "DELETE", "REPLACE"):
-            self.kind = DML
+            self._kind = Statement._DML
         elif first_word in ("SELECT", "PRAGMA"):
-            self.kind = DQL
+            self._kind = Statement._DQL
         else:
-            self.kind = DDL
-        self.exhausted = False
-        self.in_use = False
-        #
-        # set by set_row_factory
-        self.row_factory = None
+            self._kind = Statement._DDL
 
-        statement_star = ffi.new('sqlite3_stmt **')
-        next_char = ffi.new('char **')
-        ret = lib.sqlite3_prepare_v2(self.con.db, sql, -1,
-                                     statement_star, next_char)
-        self.statement = statement_star[0]
-        if ret == lib.SQLITE_OK and not self.statement:
-            # an empty statement, we work around that as it's the least trouble
-            ret = lib.sqlite3_prepare_v2(self.con.db, "select 42", -1,
-                                         statement_star, next_char)
-            self.statement = statement_star[0]
-            self.kind = DQL
+        self._in_use = False
+        self._row_factory = None
 
-        if ret != lib.SQLITE_OK:
-            raise self.con._get_exception(ret)
-        self.con._remember_statement(self)
-        if _check_remaining_sql(ffi.string(next_char[0])):
-            raise Warning("One and only one statement required: %r" % (
-                next_char[0],))
-        # sql_char should remain alive until here
+        if isinstance(sql, unicode):
+            sql = sql.encode('utf-8')
+        sql = c_char_p(sql)
+        self._statement = c_void_p()
 
-        self._build_row_cast_map()
+        ret = _lib.sqlite3_prepare_v2(self.__con._db, sql, -1,
+                                      byref(self._statement), byref(sql))
+        if ret == _lib.SQLITE_OK and self._statement.value is None:
+            # an empty statement, work around that, as it's the least trouble
+            sql = c_char_p(b"select 42")
+            ret = _lib.sqlite3_prepare_v2(self.__con._db, sql, -1,
+                                          byref(self._statement), byref(sql))
+            self._kind = Statement._DQL
+        if ret != _lib.SQLITE_OK:
+            raise self.__con._get_exception(ret)
 
-    def set_row_factory(self, row_factory):
-        self.row_factory = row_factory
+        sql = sql.value.decode('utf-8')
+        if _check_remaining_sql(sql):
+            raise Warning("You can only execute one statement at a time.")
 
-    def _build_row_cast_map(self):
-        self.row_cast_map = []
-        for i in xrange(lib.sqlite3_column_count(self.statement)):
-            converter = None
+    def __del__(self):
+        if self._statement:
+            _lib.sqlite3_finalize(self._statement)
 
-            if self.con.detect_types & PARSE_COLNAMES:
-                colname = lib.sqlite3_column_name(self.statement, i)
-                if colname != ffi.NULL:
-                    colname = ffi.string(colname)
-                    type_start = -1
-                    key = None
-                    for pos in range(len(colname)):
-                        if colname[pos] == '[':
-                            type_start = pos + 1
-                        elif colname[pos] == ']' and type_start != -1:
-                            key = colname[type_start:pos]
-                            converter = converters[key.upper()]
+    def _finalize(self):
+        if self._statement:
+            _lib.sqlite3_finalize(self._statement)
+            self._statement = None
+        self._in_use = False
 
-            if converter is None and self.con.detect_types & PARSE_DECLTYPES:
-                decltype = lib.sqlite3_column_decltype(self.statement, i)
-                if decltype is not ffi.NULL:
-                    # if multiple words, use first,
-                    # eg. "INTEGER NOT NULL" => "INTEGER"
-                    decltype = ffi.string(decltype).split()[0]
-                    if '(' in decltype:
-                        decltype = decltype[:decltype.index('(')]
-                    converter = converters.get(decltype.upper(), None)
-
-            self.row_cast_map.append(converter)
+    def _reset(self):
+        if self._in_use and self._statement:
+            _lib.sqlite3_reset(self._statement)
+            self._in_use = False
 
     if sys.version_info[0] < 3:
         def __check_decodable(self, param):
-            if self.con.text_factory in (unicode, OptimizedUnicode,
-                                         _unicode_text_factory):
+            if self.__con.text_factory in (unicode, OptimizedUnicode,
+                                           _unicode_text_factory):
                 for c in param:
                     if ord(c) & 0x80 != 0:
-                        raise self.con.ProgrammingError(
+                        raise self.__con.ProgrammingError(
                             "You must not use 8-bit bytestrings unless "
                             "you use a text_factory that can interpret "
                             "8-bit bytestrings (like text_factory = str). "
@@ -1119,149 +1074,171 @@
         param = adapt(param)
 
         if param is None:
-            lib.sqlite3_bind_null(self.statement, idx)
+            rc = _lib.sqlite3_bind_null(self._statement, idx)
         elif isinstance(param, (bool, int, long)):
             if -2147483648 <= param <= 2147483647:
-                lib.sqlite3_bind_int(self.statement, idx, param)
+                rc = _lib.sqlite3_bind_int(self._statement, idx, param)
             else:
-                lib.sqlite3_bind_int64(self.statement, idx, param)
+                rc = _lib.sqlite3_bind_int64(self._statement, idx, param)
         elif isinstance(param, float):
-            lib.sqlite3_bind_double(self.statement, idx, param)
+            rc = _lib.sqlite3_bind_double(self._statement, idx, param)
         elif isinstance(param, unicode):
             param = param.encode("utf-8")
-            lib.sqlite3_bind_text(self.statement, idx, param, len(param),
-                                  _SQLITE_TRANSIENT)
+            rc = _lib.sqlite3_bind_text(self._statement, idx, param,
+                                        len(param), _lib.SQLITE_TRANSIENT)
         elif isinstance(param, str):
             self.__check_decodable(param)
-            lib.sqlite3_bind_text(self.statement, idx, param, len(param),
-                                  _SQLITE_TRANSIENT)
+            rc = _lib.sqlite3_bind_text(self._statement, idx, param,
+                                        len(param), _lib.SQLITE_TRANSIENT)
         elif isinstance(param, (buffer, bytes)):
-            lib.sqlite3_bind_blob(self.statement, idx, str(param), len(param),
-                                  _SQLITE_TRANSIENT)
+            param = bytes(param)
+            rc = _lib.sqlite3_bind_blob(self._statement, idx, param,
+                                        len(param), _lib.SQLITE_TRANSIENT)
         else:
-            raise InterfaceError(
-                "parameter type %s is not supported" % str(type(param)))
+            rc = -1
+        return rc
 
-    def set_params(self, params):
-        ret = lib.sqlite3_reset(self.statement)
-        if ret != lib.SQLITE_OK:
-            raise self.con._get_exception(ret)
-        self.mark_dirty()
+    def _set_params(self, params):
+        self._in_use = True
 
-        if params is None:
-            if lib.sqlite3_bind_parameter_count(self.statement) != 0:
-                raise ProgrammingError("wrong number of arguments")
-            return
-
-        params_type = None
-        if isinstance(params, dict):
-            params_type = dict
-        else:
-            params_type = list
-
-        if params_type == list:
-            if len(params) != lib.sqlite3_bind_parameter_count(self.statement):
-                raise ProgrammingError("wrong number of arguments")
-
-            for i in range(len(params)):
-                self.__set_param(i + 1, params[i])
-        else:
-            param_count = lib.sqlite3_bind_parameter_count(self.statement)
-            for idx in range(1, param_count + 1):
-                param_name = lib.sqlite3_bind_parameter_name(self.statement,
-                                                             idx)
-                if param_name == ffi.NULL:
-                    raise ProgrammingError("need named parameters")
-                param_name = ffi.string(param_name)[1:]
+        num_params_needed = _lib.sqlite3_bind_parameter_count(self._statement)
+        if isinstance(params, (tuple, list)) or \
+                not isinstance(params, dict) and \
+                hasattr(params, '__getitem__'):
+            try:
+                num_params = len(params)
+            except TypeError:
+                num_params = -1
+            if num_params != num_params_needed:
+                raise ProgrammingError("Incorrect number of bindings supplied. "
+                                       "The current statement uses %d, and "
+                                       "there are %d supplied." %
+                                       (num_params_needed, num_params))
+            for i in range(num_params):
+                rc = self.__set_param(i + 1, params[i])
+                if rc != _lib.SQLITE_OK:
+                    raise InterfaceError("Error binding parameter %d - "
+                                         "probably unsupported type." % i)
+        elif isinstance(params, dict):
+            for i in range(1, num_params_needed + 1):
+                param_name = _lib.sqlite3_bind_parameter_name(self._statement, i)
+                if param_name is None:
+                    raise ProgrammingError("Binding %d has no name, but you "
+                                           "supplied a dictionary (which has "
+                                           "only names)." % i)
+                param_name = param_name.decode('utf-8')[1:]
                 try:
                     param = params[param_name]
                 except KeyError:
-                    raise ProgrammingError("missing parameter '%s'" % param)
-                self.__set_param(idx, param)
+                    raise ProgrammingError("You did not supply a value for "
+                                           "binding %d." % i)
+                rc = self.__set_param(i, param)
+                if rc != _lib.SQLITE_OK:
+                    raise InterfaceError("Error binding parameter :%s - "
+                                         "probably unsupported type." %
+                                         param_name)
+        else:
+            raise ValueError("parameters are of unsupported type")
 
-    def next(self, cursor):
-        self.con._check_closed()
-        self.con._check_thread()
-        if self.exhausted:
-            raise StopIteration
-        item = self.item
+    def _build_row_cast_map(self):
+        if not self.__con._detect_types:
+            return
+        self.__row_cast_map = []
+        for i in xrange(_lib.sqlite3_column_count(self._statement)):
+            converter = None
 
-        ret = lib.sqlite3_step(self.statement)
-        if ret == lib.SQLITE_DONE:
-            self.exhausted = True
-            self.item = None
-        elif ret != lib.SQLITE_ROW:
-            exc = self.con._get_exception(ret)
-            lib.sqlite3_reset(self.statement)
-            raise exc
+            if self.__con._detect_types & PARSE_COLNAMES:
+                colname = _lib.sqlite3_column_name(self._statement, i)
+                if colname is not None:
+                    colname = colname.decode('utf-8')
+                    type_start = -1
+                    key = None
+                    for pos in range(len(colname)):
+                        if colname[pos] == '[':
+                            type_start = pos + 1
+                        elif colname[pos] == ']' and type_start != -1:
+                            key = colname[type_start:pos]
+                            converter = converters[key.upper()]
 
-        self._readahead(cursor)
-        return item
+            if converter is None and self.__con._detect_types & PARSE_DECLTYPES:
+                decltype = _lib.sqlite3_column_decltype(self._statement, i)
+                if decltype is not None:
+                    decltype = decltype.decode('utf-8')
+                    # if multiple words, use first, eg.
+                    # "INTEGER NOT NULL" => "INTEGER"
+                    decltype = decltype.split()[0]
+                    if '(' in decltype:
+                        decltype = decltype[:decltype.index('(')]
+                    converter = converters.get(decltype.upper(), None)
+
+            self.__row_cast_map.append(converter)
 
     def _readahead(self, cursor):
-        self.column_count = lib.sqlite3_column_count(self.statement)
         row = []
-        for i in xrange(self.column_count):
-            typ = lib.sqlite3_column_type(self.statement, i)
+        num_cols = _lib.sqlite3_column_count(self._statement)
+        for i in xrange(num_cols):
+            if self.__con._detect_types:
+                converter = self.__row_cast_map[i]
+            else:
+                converter = None
 
-            converter = self.row_cast_map[i]
-            if converter is None:
-                if typ == lib.SQLITE_NULL:
-                    val = None
-                elif typ == lib.SQLITE_INTEGER:
-                    val = lib.sqlite3_column_int64(self.statement, i)
-                elif typ == lib.SQLITE_FLOAT:
-                    val = lib.sqlite3_column_double(self.statement, i)
-                elif typ == lib.SQLITE_TEXT:
-                    text = lib.sqlite3_column_text(self.statement, i)
-                    text_len = lib.sqlite3_column_bytes(self.statement, i)
-                    val = ffi.buffer(text, text_len)[:]
-                    val = self.con.text_factory(val)
-                elif typ == lib.SQLITE_BLOB:
-                    blob = lib.sqlite3_column_blob(self.statement, i)
-                    blob_len = lib.sqlite3_column_bytes(self.statement, i)
-                    val = _BLOB_TYPE(ffi.buffer(blob, blob_len))
-            else:
-                blob = lib.sqlite3_column_blob(self.statement, i)
+            if converter is not None:
+                blob = _lib.sqlite3_column_blob(self._statement, i)
                 if not blob:
                     val = None
                 else:
-                    blob_len = lib.sqlite3_column_bytes(self.statement, i)
-                    val = ffi.buffer(blob, blob_len)[:]
+                    blob_len = _lib.sqlite3_column_bytes(self._statement, i)
+                    val = bytes(string_at(blob, blob_len))
                     val = converter(val)
+            else:
+                typ = _lib.sqlite3_column_type(self._statement, i)
+                if typ == _lib.SQLITE_NULL:
+                    val = None
+                elif typ == _lib.SQLITE_INTEGER:
+                    val = _lib.sqlite3_column_int64(self._statement, i)
+                    val = int(val)
+                elif typ == _lib.SQLITE_FLOAT:
+                    val = _lib.sqlite3_column_double(self._statement, i)
+                elif typ == _lib.SQLITE_TEXT:
+                    text = _lib.sqlite3_column_text(self._statement, i)
+                    text_len = _lib.sqlite3_column_bytes(self._statement, i)
+                    val = string_at(text, text_len)
+                    val = self.__con.text_factory(val)
+                elif typ == _lib.SQLITE_BLOB:
+                    blob = _lib.sqlite3_column_blob(self._statement, i)
+                    blob_len = _lib.sqlite3_column_bytes(self._statement, i)
+                    val = _BLOB_TYPE(string_at(blob, blob_len))
             row.append(val)
 
         row = tuple(row)
-        if self.row_factory is not None:
-            row = self.row_factory(cursor, row)
-        self.item = row
+        if self._row_factory is not None:
+            row = self._row_factory(cursor, row)
+        self._item = row
 
-    def reset(self):
-        self.row_cast_map = None
-        ret = lib.sqlite3_reset(self.statement)
-        self.in_use = False
-        self.exhausted = False
-        return ret
+    def _next(self, cursor):
+        try:
+            item = self._item
+        except AttributeError:
+            raise StopIteration
+        del self._item
 
-    def finalize(self):
-        lib.sqlite3_finalize(self.statement)
-        self.statement = ffi.NULL
-        self.in_use = False
+        ret = _lib.sqlite3_step(self._statement)
+        if ret not in (_lib.SQLITE_DONE, _lib.SQLITE_ROW):
+            _lib.sqlite3_reset(self._statement)
+            raise self.__con._get_exception(ret)
+        elif ret == _lib.SQLITE_ROW:
+            self._readahead(cursor)
 
-    def mark_dirty(self):
-        self.in_use = True
-
-    def __del__(self):
-        lib.sqlite3_finalize(self.statement)
-        self.statement = ffi.NULL
+        return item
 
     def _get_description(self):
-        if self.kind == DML:
+        if self._kind == Statement._DML:
             return None
         desc = []
-        for i in xrange(lib.sqlite3_column_count(self.statement)):
-            name = lib.sqlite3_column_name(self.statement, i)
-            name = ffi.string(name).split("[")[0].strip()
+        for i in xrange(_lib.sqlite3_column_count(self._statement)):
+            name = _lib.sqlite3_column_name(self._statement, i)
+            if name is not None:
+                name = name.decode('utf-8').split("[")[0].strip()
             desc.append((name, None, None, None, None, None, None))
         return desc
 
@@ -1346,20 +1323,21 @@
 def _convert_params(con, nargs, params):
     _params = []
     for i in range(nargs):
-        typ = lib.sqlite3_value_type(params[i])
-        if typ == lib.SQLITE_NULL:
+        typ = _lib.sqlite3_value_type(params[i])
+        if typ == _lib.SQLITE_NULL:
             val = None
-        elif typ == lib.SQLITE_INTEGER:
-            val = lib.sqlite3_value_int64(params[i])
-        elif typ == lib.SQLITE_FLOAT:
-            val = lib.sqlite3_value_double(params[i])
-        elif typ == lib.SQLITE_TEXT:
-            val = lib.sqlite3_value_text(params[i])
-            val = unicode(ffi.string(val), 'utf-8')
-        elif typ == lib.SQLITE_BLOB:
-            blob = lib.sqlite3_value_blob(params[i])
-            blob_len = lib.sqlite3_value_bytes(params[i])
-            val = _BLOB_TYPE(ffi.buffer(blob, blob_len))
+        elif typ == _lib.SQLITE_INTEGER:
+            val = _lib.sqlite3_value_int64(params[i])
+            val = int(val)
+        elif typ == _lib.SQLITE_FLOAT:
+            val = _lib.sqlite3_value_double(params[i])
+        elif typ == _lib.SQLITE_TEXT:
+            val = _lib.sqlite3_value_text(params[i])
+            val = val.decode('utf-8')
+        elif typ == _lib.SQLITE_BLOB:
+            blob = _lib.sqlite3_value_blob(params[i])
+            blob_len = _lib.sqlite3_value_bytes(params[i])
+            val = _BLOB_TYPE(string_at(blob, blob_len))
         else:
             raise NotImplementedError
         _params.append(val)
@@ -1368,18 +1346,18 @@
 
 def _convert_result(con, val):
     if val is None:
-        lib.sqlite3_result_null(con)
+        _lib.sqlite3_result_null(con)
     elif isinstance(val, (bool, int, long)):
-        lib.sqlite3_result_int64(con, int(val))
+        _lib.sqlite3_result_int64(con, int(val))
     elif isinstance(val, float):
-        lib.sqlite3_result_double(con, val)
+        _lib.sqlite3_result_double(con, val)
     elif isinstance(val, unicode):
         val = val.encode('utf-8')
-        lib.sqlite3_result_text(con, val, len(val), _SQLITE_TRANSIENT)
+        _lib.sqlite3_result_text(con, val, len(val), _lib.SQLITE_TRANSIENT)
     elif isinstance(val, str):
-        lib.sqlite3_result_text(con, val, len(val), _SQLITE_TRANSIENT)
+        _lib.sqlite3_result_text(con, val, len(val), _lib.SQLITE_TRANSIENT)
     elif isinstance(val, (buffer, bytes)):
-        lib.sqlite3_result_blob(con, str(val), len(val), _SQLITE_TRANSIENT)
+        _lib.sqlite3_result_blob(con, bytes(val), len(val), _lib.SQLITE_TRANSIENT)
     else:
         raise NotImplementedError
 
@@ -1390,10 +1368,30 @@
         val = real_cb(*params)
     except Exception:
         msg = b"user-defined function raised exception"
-        lib.sqlite3_result_error(context, msg, len(msg))
+        _lib.sqlite3_result_error(context, msg, len(msg))
     else:
         _convert_result(context, val)
 
+_FUNC = CFUNCTYPE(None, c_void_p, c_int, POINTER(c_void_p))
+_STEP = CFUNCTYPE(None, c_void_p, c_int, POINTER(c_void_p))
+_FINAL = CFUNCTYPE(None, c_void_p)
+_lib.sqlite3_create_function.argtypes = [c_void_p, c_char_p, c_int, c_int, c_void_p, _FUNC, _STEP, _FINAL]
+_lib.sqlite3_create_function.restype = c_int
+
+_lib.sqlite3_aggregate_context.argtypes = [c_void_p, c_int]
+_lib.sqlite3_aggregate_context.restype = c_void_p
+
+_COLLATION = CFUNCTYPE(c_int, c_void_p, c_int, c_void_p, c_int, c_void_p)
+_lib.sqlite3_create_collation.argtypes = [c_void_p, c_char_p, c_int, c_void_p, _COLLATION]
+_lib.sqlite3_create_collation.restype = c_int
+
+_PROGRESS = CFUNCTYPE(c_int, c_void_p)
+_lib.sqlite3_progress_handler.argtypes = [c_void_p, c_int, _PROGRESS, c_void_p]
+_lib.sqlite3_progress_handler.restype = c_int
+
+_AUTHORIZER = CFUNCTYPE(c_int, c_void_p, c_int, c_char_p, c_char_p, c_char_p, c_char_p)
+_lib.sqlite3_set_authorizer.argtypes = [c_void_p, _AUTHORIZER, c_void_p]
+_lib.sqlite3_set_authorizer.restype = c_int
 
 converters = {}
 adapters = {}


More information about the pypy-commit mailing list