commit ca3a0f058008cff7993d092a022686c64d1a37ce Author: John Gallagher Date: Sun Oct 19 19:56:41 2014 -0400 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ca98cd9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target/ +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..957e1de --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "rusqlite" +version = "0.0.0" +authors = ["John Gallagher "] + +[lib] +name = "rusqlite" diff --git a/src/ffi/bindgen.rs b/src/ffi/bindgen.rs new file mode 100644 index 0000000..9d59042 --- /dev/null +++ b/src/ffi/bindgen.rs @@ -0,0 +1,1287 @@ +/* automatically generated by rust-bindgen */ + +pub enum Struct_sqlite3 { } +pub type sqlite3 = Struct_sqlite3; +pub type sqlite_int64 = ::libc::c_longlong; +pub type sqlite_uint64 = ::libc::c_ulonglong; +pub type sqlite3_int64 = sqlite_int64; +pub type sqlite3_uint64 = sqlite_uint64; +pub type sqlite3_callback = + ::std::option::Option ::libc::c_int>; +pub type sqlite3_file = Struct_sqlite3_file; +#[repr(C)] +pub struct Struct_sqlite3_file { + pub pMethods: *const Struct_sqlite3_io_methods, +} +pub type sqlite3_io_methods = Struct_sqlite3_io_methods; +#[repr(C)] +pub struct Struct_sqlite3_io_methods { + pub iVersion: ::libc::c_int, + pub xClose: ::std::option::Option ::libc::c_int>, + pub xRead: ::std::option::Option ::libc::c_int>, + pub xWrite: ::std::option::Option ::libc::c_int>, + pub xTruncate: ::std::option::Option ::libc::c_int>, + pub xSync: ::std::option::Option ::libc::c_int>, + pub xFileSize: ::std::option::Option ::libc::c_int>, + pub xLock: ::std::option::Option ::libc::c_int>, + pub xUnlock: ::std::option::Option ::libc::c_int>, + pub xCheckReservedLock: ::std::option::Option ::libc::c_int>, + pub xFileControl: ::std::option::Option ::libc::c_int>, + pub xSectorSize: ::std::option::Option ::libc::c_int>, + pub xDeviceCharacteristics: ::std::option::Option ::libc::c_int>, + pub xShmMap: ::std::option::Option ::libc::c_int>, + pub xShmLock: ::std::option::Option ::libc::c_int>, + pub xShmBarrier: ::std::option::Option, + pub xShmUnmap: ::std::option::Option ::libc::c_int>, + pub xFetch: ::std::option::Option ::libc::c_int>, + pub xUnfetch: ::std::option::Option ::libc::c_int>, +} +pub enum Struct_sqlite3_mutex { } +pub type sqlite3_mutex = Struct_sqlite3_mutex; +pub type sqlite3_vfs = Struct_sqlite3_vfs; +pub type sqlite3_syscall_ptr = ::std::option::Option; +#[repr(C)] +pub struct Struct_sqlite3_vfs { + pub iVersion: ::libc::c_int, + pub szOsFile: ::libc::c_int, + pub mxPathname: ::libc::c_int, + pub pNext: *mut sqlite3_vfs, + pub zName: *const ::libc::c_char, + pub pAppData: *mut ::libc::c_void, + pub xOpen: ::std::option::Option ::libc::c_int>, + pub xDelete: ::std::option::Option ::libc::c_int>, + pub xAccess: ::std::option::Option ::libc::c_int>, + pub xFullPathname: ::std::option::Option ::libc::c_int>, + pub xDlOpen: ::std::option::Option *mut ::libc::c_void>, + pub xDlError: ::std::option::Option, + pub xDlSym: ::std::option::Option + ::std::option::Option>, + pub xDlClose: ::std::option::Option, + pub xRandomness: ::std::option::Option ::libc::c_int>, + pub xSleep: ::std::option::Option ::libc::c_int>, + pub xCurrentTime: ::std::option::Option ::libc::c_int>, + pub xGetLastError: ::std::option::Option ::libc::c_int>, + pub xCurrentTimeInt64: ::std::option::Option ::libc::c_int>, + pub xSetSystemCall: ::std::option::Option ::libc::c_int>, + pub xGetSystemCall: ::std::option::Option sqlite3_syscall_ptr>, + pub xNextSystemCall: ::std::option::Option *const ::libc::c_char>, +} +pub type sqlite3_mem_methods = Struct_sqlite3_mem_methods; +#[repr(C)] +pub struct Struct_sqlite3_mem_methods { + pub xMalloc: ::std::option::Option *mut ::libc::c_void>, + pub xFree: ::std::option::Option, + pub xRealloc: ::std::option::Option *mut ::libc::c_void>, + pub xSize: ::std::option::Option ::libc::c_int>, + pub xRoundup: ::std::option::Option ::libc::c_int>, + pub xInit: ::std::option::Option ::libc::c_int>, + pub xShutdown: ::std::option::Option, + pub pAppData: *mut ::libc::c_void, +} +pub enum Struct_sqlite3_stmt { } +pub type sqlite3_stmt = Struct_sqlite3_stmt; +pub enum Struct_Mem { } +pub type sqlite3_value = Struct_Mem; +pub enum Struct_sqlite3_context { } +pub type sqlite3_context = Struct_sqlite3_context; +pub type sqlite3_destructor_type = + ::std::option::Option; +pub type sqlite3_vtab = Struct_sqlite3_vtab; +pub type sqlite3_index_info = Struct_sqlite3_index_info; +pub type sqlite3_vtab_cursor = Struct_sqlite3_vtab_cursor; +pub type sqlite3_module = Struct_sqlite3_module; +#[repr(C)] +pub struct Struct_sqlite3_module { + pub iVersion: ::libc::c_int, + pub xCreate: ::std::option::Option ::libc::c_int>, + pub xConnect: ::std::option::Option ::libc::c_int>, + pub xBestIndex: ::std::option::Option ::libc::c_int>, + pub xDisconnect: ::std::option::Option ::libc::c_int>, + pub xDestroy: ::std::option::Option ::libc::c_int>, + pub xOpen: ::std::option::Option ::libc::c_int>, + pub xClose: ::std::option::Option ::libc::c_int>, + pub xFilter: ::std::option::Option ::libc::c_int>, + pub xNext: ::std::option::Option ::libc::c_int>, + pub xEof: ::std::option::Option ::libc::c_int>, + pub xColumn: ::std::option::Option ::libc::c_int>, + pub xRowid: ::std::option::Option ::libc::c_int>, + pub xUpdate: ::std::option::Option ::libc::c_int>, + pub xBegin: ::std::option::Option ::libc::c_int>, + pub xSync: ::std::option::Option ::libc::c_int>, + pub xCommit: ::std::option::Option ::libc::c_int>, + pub xRollback: ::std::option::Option ::libc::c_int>, + pub xFindFunction: ::std::option::Option, + arg5: + *mut *mut ::libc::c_void) + -> ::libc::c_int>, + pub xRename: ::std::option::Option ::libc::c_int>, + pub xSavepoint: ::std::option::Option ::libc::c_int>, + pub xRelease: ::std::option::Option ::libc::c_int>, + pub xRollbackTo: ::std::option::Option ::libc::c_int>, +} +#[repr(C)] +pub struct Struct_sqlite3_index_info { + pub nConstraint: ::libc::c_int, + pub aConstraint: *mut Struct_sqlite3_index_constraint, + pub nOrderBy: ::libc::c_int, + pub aOrderBy: *mut Struct_sqlite3_index_orderby, + pub aConstraintUsage: *mut Struct_sqlite3_index_constraint_usage, + pub idxNum: ::libc::c_int, + pub idxStr: *mut ::libc::c_char, + pub needToFreeIdxStr: ::libc::c_int, + pub orderByConsumed: ::libc::c_int, + pub estimatedCost: ::libc::c_double, + pub estimatedRows: sqlite3_int64, +} +#[repr(C)] +pub struct Struct_sqlite3_index_constraint { + pub iColumn: ::libc::c_int, + pub op: ::libc::c_uchar, + pub usable: ::libc::c_uchar, + pub iTermOffset: ::libc::c_int, +} +#[repr(C)] +pub struct Struct_sqlite3_index_orderby { + pub iColumn: ::libc::c_int, + pub desc: ::libc::c_uchar, +} +#[repr(C)] +pub struct Struct_sqlite3_index_constraint_usage { + pub argvIndex: ::libc::c_int, + pub omit: ::libc::c_uchar, +} +#[repr(C)] +pub struct Struct_sqlite3_vtab { + pub pModule: *const sqlite3_module, + pub nRef: ::libc::c_int, + pub zErrMsg: *mut ::libc::c_char, +} +#[repr(C)] +pub struct Struct_sqlite3_vtab_cursor { + pub pVtab: *mut sqlite3_vtab, +} +pub enum Struct_sqlite3_blob { } +pub type sqlite3_blob = Struct_sqlite3_blob; +pub type sqlite3_mutex_methods = Struct_sqlite3_mutex_methods; +#[repr(C)] +pub struct Struct_sqlite3_mutex_methods { + pub xMutexInit: ::std::option::Option ::libc::c_int>, + pub xMutexEnd: ::std::option::Option ::libc::c_int>, + pub xMutexAlloc: ::std::option::Option *mut sqlite3_mutex>, + pub xMutexFree: ::std::option::Option, + pub xMutexEnter: ::std::option::Option, + pub xMutexTry: ::std::option::Option ::libc::c_int>, + pub xMutexLeave: ::std::option::Option, + pub xMutexHeld: ::std::option::Option ::libc::c_int>, + pub xMutexNotheld: ::std::option::Option ::libc::c_int>, +} +pub enum Struct_sqlite3_pcache { } +pub type sqlite3_pcache = Struct_sqlite3_pcache; +pub type sqlite3_pcache_page = Struct_sqlite3_pcache_page; +#[repr(C)] +pub struct Struct_sqlite3_pcache_page { + pub pBuf: *mut ::libc::c_void, + pub pExtra: *mut ::libc::c_void, +} +pub type sqlite3_pcache_methods2 = Struct_sqlite3_pcache_methods2; +#[repr(C)] +pub struct Struct_sqlite3_pcache_methods2 { + pub iVersion: ::libc::c_int, + pub pArg: *mut ::libc::c_void, + pub xInit: ::std::option::Option ::libc::c_int>, + pub xShutdown: ::std::option::Option, + pub xCreate: ::std::option::Option *mut sqlite3_pcache>, + pub xCachesize: ::std::option::Option, + pub xPagecount: ::std::option::Option ::libc::c_int>, + pub xFetch: ::std::option::Option *mut sqlite3_pcache_page>, + pub xUnpin: ::std::option::Option, + pub xRekey: ::std::option::Option, + pub xTruncate: ::std::option::Option, + pub xDestroy: ::std::option::Option, + pub xShrink: ::std::option::Option, +} +pub type sqlite3_pcache_methods = Struct_sqlite3_pcache_methods; +#[repr(C)] +pub struct Struct_sqlite3_pcache_methods { + pub pArg: *mut ::libc::c_void, + pub xInit: ::std::option::Option ::libc::c_int>, + pub xShutdown: ::std::option::Option, + pub xCreate: ::std::option::Option *mut sqlite3_pcache>, + pub xCachesize: ::std::option::Option, + pub xPagecount: ::std::option::Option ::libc::c_int>, + pub xFetch: ::std::option::Option *mut ::libc::c_void>, + pub xUnpin: ::std::option::Option, + pub xRekey: ::std::option::Option, + pub xTruncate: ::std::option::Option, + pub xDestroy: ::std::option::Option, +} +pub enum Struct_sqlite3_backup { } +pub type sqlite3_backup = Struct_sqlite3_backup; +pub type sqlite3_rtree_geometry = Struct_sqlite3_rtree_geometry; +pub type sqlite3_rtree_query_info = Struct_sqlite3_rtree_query_info; +pub type sqlite3_rtree_dbl = ::libc::c_double; +#[repr(C)] +pub struct Struct_sqlite3_rtree_geometry { + pub pContext: *mut ::libc::c_void, + pub nParam: ::libc::c_int, + pub aParam: *mut sqlite3_rtree_dbl, + pub pUser: *mut ::libc::c_void, + pub xDelUser: ::std::option::Option, +} +#[repr(C)] +pub struct Struct_sqlite3_rtree_query_info { + pub pContext: *mut ::libc::c_void, + pub nParam: ::libc::c_int, + pub aParam: *mut sqlite3_rtree_dbl, + pub pUser: *mut ::libc::c_void, + pub xDelUser: ::std::option::Option, + pub aCoord: *mut sqlite3_rtree_dbl, + pub anQueue: *mut ::libc::c_uint, + pub nCoord: ::libc::c_int, + pub iLevel: ::libc::c_int, + pub mxLevel: ::libc::c_int, + pub iRowid: sqlite3_int64, + pub rParentScore: sqlite3_rtree_dbl, + pub eParentWithin: ::libc::c_int, + pub eWithin: ::libc::c_int, + pub rScore: sqlite3_rtree_dbl, +} +//#[link(name = "sqlite3", kind = "framework")] +#[link(name = "sqlite3")] +extern "C" { + pub static mut sqlite3_version: *const ::libc::c_char; + pub static mut sqlite3_temp_directory: *mut ::libc::c_char; + pub static mut sqlite3_data_directory: *mut ::libc::c_char; + pub fn sqlite3_libversion() -> *const ::libc::c_char; + pub fn sqlite3_sourceid() -> *const ::libc::c_char; + pub fn sqlite3_libversion_number() -> ::libc::c_int; + pub fn sqlite3_compileoption_used(zOptName: *const ::libc::c_char) -> + ::libc::c_int; + pub fn sqlite3_compileoption_get(N: ::libc::c_int) -> + *const ::libc::c_char; + pub fn sqlite3_threadsafe() -> ::libc::c_int; + pub fn sqlite3_close(arg1: *mut sqlite3) -> ::libc::c_int; + pub fn sqlite3_close_v2(arg1: *mut sqlite3) -> ::libc::c_int; + pub fn sqlite3_exec(arg1: *mut sqlite3, sql: *const ::libc::c_char, + callback: + ::std::option::Option ::libc::c_int>, + arg2: *mut ::libc::c_void, + errmsg: *mut *mut ::libc::c_char) -> ::libc::c_int; + pub fn sqlite3_initialize() -> ::libc::c_int; + pub fn sqlite3_shutdown() -> ::libc::c_int; + pub fn sqlite3_os_init() -> ::libc::c_int; + pub fn sqlite3_os_end() -> ::libc::c_int; + pub fn sqlite3_config(arg1: ::libc::c_int, ...) -> ::libc::c_int; + pub fn sqlite3_db_config(arg1: *mut sqlite3, op: ::libc::c_int, ...) -> + ::libc::c_int; + pub fn sqlite3_extended_result_codes(arg1: *mut sqlite3, + onoff: ::libc::c_int) -> + ::libc::c_int; + pub fn sqlite3_last_insert_rowid(arg1: *mut sqlite3) -> sqlite3_int64; + pub fn sqlite3_changes(arg1: *mut sqlite3) -> ::libc::c_int; + pub fn sqlite3_total_changes(arg1: *mut sqlite3) -> ::libc::c_int; + pub fn sqlite3_interrupt(arg1: *mut sqlite3); + pub fn sqlite3_complete(sql: *const ::libc::c_char) -> ::libc::c_int; + pub fn sqlite3_complete16(sql: *const ::libc::c_void) -> ::libc::c_int; + pub fn sqlite3_busy_handler(arg1: *mut sqlite3, + arg2: + ::std::option::Option + ::libc::c_int>, + arg3: *mut ::libc::c_void) -> ::libc::c_int; + pub fn sqlite3_busy_timeout(arg1: *mut sqlite3, ms: ::libc::c_int) -> + ::libc::c_int; + pub fn sqlite3_get_table(db: *mut sqlite3, zSql: *const ::libc::c_char, + pazResult: *mut *mut *mut ::libc::c_char, + pnRow: *mut ::libc::c_int, + pnColumn: *mut ::libc::c_int, + pzErrmsg: *mut *mut ::libc::c_char) -> + ::libc::c_int; + pub fn sqlite3_free_table(result: *mut *mut ::libc::c_char); + pub fn sqlite3_mprintf(arg1: *const ::libc::c_char, ...) -> + *mut ::libc::c_char; + pub fn sqlite3_snprintf(arg1: ::libc::c_int, arg2: *mut ::libc::c_char, + arg3: *const ::libc::c_char, ...) -> + *mut ::libc::c_char; + pub fn sqlite3_malloc(arg1: ::libc::c_int) -> *mut ::libc::c_void; + pub fn sqlite3_realloc(arg1: *mut ::libc::c_void, arg2: ::libc::c_int) -> + *mut ::libc::c_void; + pub fn sqlite3_free(arg1: *mut ::libc::c_void); + pub fn sqlite3_memory_used() -> sqlite3_int64; + pub fn sqlite3_memory_highwater(resetFlag: ::libc::c_int) -> + sqlite3_int64; + pub fn sqlite3_randomness(N: ::libc::c_int, P: *mut ::libc::c_void); + pub fn sqlite3_set_authorizer(arg1: *mut sqlite3, + xAuth: + ::std::option::Option + ::libc::c_int>, + pUserData: *mut ::libc::c_void) -> + ::libc::c_int; + pub fn sqlite3_trace(arg1: *mut sqlite3, + xTrace: + ::std::option::Option, + arg2: *mut ::libc::c_void) -> *mut ::libc::c_void; + pub fn sqlite3_profile(arg1: *mut sqlite3, + xProfile: + ::std::option::Option, + arg2: *mut ::libc::c_void) -> *mut ::libc::c_void; + pub fn sqlite3_progress_handler(arg1: *mut sqlite3, arg2: ::libc::c_int, + arg3: + ::std::option::Option + ::libc::c_int>, + arg4: *mut ::libc::c_void); + pub fn sqlite3_open(filename: *const ::libc::c_char, + ppDb: *mut *mut sqlite3) -> ::libc::c_int; + pub fn sqlite3_open16(filename: *const ::libc::c_void, + ppDb: *mut *mut sqlite3) -> ::libc::c_int; + pub fn sqlite3_open_v2(filename: *const ::libc::c_char, + ppDb: *mut *mut sqlite3, flags: ::libc::c_int, + zVfs: *const ::libc::c_char) -> ::libc::c_int; + pub fn sqlite3_uri_parameter(zFilename: *const ::libc::c_char, + zParam: *const ::libc::c_char) -> + *const ::libc::c_char; + pub fn sqlite3_uri_boolean(zFile: *const ::libc::c_char, + zParam: *const ::libc::c_char, + bDefault: ::libc::c_int) -> ::libc::c_int; + pub fn sqlite3_uri_int64(arg1: *const ::libc::c_char, + arg2: *const ::libc::c_char, arg3: sqlite3_int64) + -> sqlite3_int64; + pub fn sqlite3_errcode(db: *mut sqlite3) -> ::libc::c_int; + pub fn sqlite3_extended_errcode(db: *mut sqlite3) -> ::libc::c_int; + pub fn sqlite3_errmsg(arg1: *mut sqlite3) -> *const ::libc::c_char; + pub fn sqlite3_errmsg16(arg1: *mut sqlite3) -> *const ::libc::c_void; + pub fn sqlite3_errstr(arg1: ::libc::c_int) -> *const ::libc::c_char; + pub fn sqlite3_limit(arg1: *mut sqlite3, id: ::libc::c_int, + newVal: ::libc::c_int) -> ::libc::c_int; + pub fn sqlite3_prepare(db: *mut sqlite3, zSql: *const ::libc::c_char, + nByte: ::libc::c_int, + ppStmt: *mut *mut sqlite3_stmt, + pzTail: *mut *const ::libc::c_char) -> + ::libc::c_int; + pub fn sqlite3_prepare_v2(db: *mut sqlite3, zSql: *const ::libc::c_char, + nByte: ::libc::c_int, + ppStmt: *mut *mut sqlite3_stmt, + pzTail: *mut *const ::libc::c_char) -> + ::libc::c_int; + pub fn sqlite3_prepare16(db: *mut sqlite3, zSql: *const ::libc::c_void, + nByte: ::libc::c_int, + ppStmt: *mut *mut sqlite3_stmt, + pzTail: *mut *const ::libc::c_void) -> + ::libc::c_int; + pub fn sqlite3_prepare16_v2(db: *mut sqlite3, zSql: *const ::libc::c_void, + nByte: ::libc::c_int, + ppStmt: *mut *mut sqlite3_stmt, + pzTail: *mut *const ::libc::c_void) -> + ::libc::c_int; + pub fn sqlite3_sql(pStmt: *mut sqlite3_stmt) -> *const ::libc::c_char; + pub fn sqlite3_stmt_readonly(pStmt: *mut sqlite3_stmt) -> ::libc::c_int; + pub fn sqlite3_stmt_busy(arg1: *mut sqlite3_stmt) -> ::libc::c_int; + pub fn sqlite3_bind_blob(arg1: *mut sqlite3_stmt, arg2: ::libc::c_int, + arg3: *const ::libc::c_void, n: ::libc::c_int, + arg4: + ::std::option::Option) + -> ::libc::c_int; + pub fn sqlite3_bind_double(arg1: *mut sqlite3_stmt, arg2: ::libc::c_int, + arg3: ::libc::c_double) -> ::libc::c_int; + pub fn sqlite3_bind_int(arg1: *mut sqlite3_stmt, arg2: ::libc::c_int, + arg3: ::libc::c_int) -> ::libc::c_int; + pub fn sqlite3_bind_int64(arg1: *mut sqlite3_stmt, arg2: ::libc::c_int, + arg3: sqlite3_int64) -> ::libc::c_int; + pub fn sqlite3_bind_null(arg1: *mut sqlite3_stmt, arg2: ::libc::c_int) -> + ::libc::c_int; + pub fn sqlite3_bind_text(arg1: *mut sqlite3_stmt, arg2: ::libc::c_int, + arg3: *const ::libc::c_char, n: ::libc::c_int, + arg4: + ::std::option::Option) + -> ::libc::c_int; + pub fn sqlite3_bind_text16(arg1: *mut sqlite3_stmt, arg2: ::libc::c_int, + arg3: *const ::libc::c_void, + arg4: ::libc::c_int, + arg5: + ::std::option::Option) + -> ::libc::c_int; + pub fn sqlite3_bind_value(arg1: *mut sqlite3_stmt, arg2: ::libc::c_int, + arg3: *const sqlite3_value) -> ::libc::c_int; + pub fn sqlite3_bind_zeroblob(arg1: *mut sqlite3_stmt, arg2: ::libc::c_int, + n: ::libc::c_int) -> ::libc::c_int; + pub fn sqlite3_bind_parameter_count(arg1: *mut sqlite3_stmt) -> + ::libc::c_int; + pub fn sqlite3_bind_parameter_name(arg1: *mut sqlite3_stmt, + arg2: ::libc::c_int) -> + *const ::libc::c_char; + pub fn sqlite3_bind_parameter_index(arg1: *mut sqlite3_stmt, + zName: *const ::libc::c_char) -> + ::libc::c_int; + pub fn sqlite3_clear_bindings(arg1: *mut sqlite3_stmt) -> ::libc::c_int; + pub fn sqlite3_column_count(pStmt: *mut sqlite3_stmt) -> ::libc::c_int; + pub fn sqlite3_column_name(arg1: *mut sqlite3_stmt, N: ::libc::c_int) -> + *const ::libc::c_char; + pub fn sqlite3_column_name16(arg1: *mut sqlite3_stmt, N: ::libc::c_int) -> + *const ::libc::c_void; + pub fn sqlite3_column_database_name(arg1: *mut sqlite3_stmt, + arg2: ::libc::c_int) -> + *const ::libc::c_char; + pub fn sqlite3_column_database_name16(arg1: *mut sqlite3_stmt, + arg2: ::libc::c_int) -> + *const ::libc::c_void; + pub fn sqlite3_column_table_name(arg1: *mut sqlite3_stmt, + arg2: ::libc::c_int) -> + *const ::libc::c_char; + pub fn sqlite3_column_table_name16(arg1: *mut sqlite3_stmt, + arg2: ::libc::c_int) -> + *const ::libc::c_void; + pub fn sqlite3_column_origin_name(arg1: *mut sqlite3_stmt, + arg2: ::libc::c_int) -> + *const ::libc::c_char; + pub fn sqlite3_column_origin_name16(arg1: *mut sqlite3_stmt, + arg2: ::libc::c_int) -> + *const ::libc::c_void; + pub fn sqlite3_column_decltype(arg1: *mut sqlite3_stmt, + arg2: ::libc::c_int) -> + *const ::libc::c_char; + pub fn sqlite3_column_decltype16(arg1: *mut sqlite3_stmt, + arg2: ::libc::c_int) -> + *const ::libc::c_void; + pub fn sqlite3_step(arg1: *mut sqlite3_stmt) -> ::libc::c_int; + pub fn sqlite3_data_count(pStmt: *mut sqlite3_stmt) -> ::libc::c_int; + pub fn sqlite3_column_blob(arg1: *mut sqlite3_stmt, iCol: ::libc::c_int) + -> *const ::libc::c_void; + pub fn sqlite3_column_bytes(arg1: *mut sqlite3_stmt, iCol: ::libc::c_int) + -> ::libc::c_int; + pub fn sqlite3_column_bytes16(arg1: *mut sqlite3_stmt, + iCol: ::libc::c_int) -> ::libc::c_int; + pub fn sqlite3_column_double(arg1: *mut sqlite3_stmt, iCol: ::libc::c_int) + -> ::libc::c_double; + pub fn sqlite3_column_int(arg1: *mut sqlite3_stmt, iCol: ::libc::c_int) -> + ::libc::c_int; + pub fn sqlite3_column_int64(arg1: *mut sqlite3_stmt, iCol: ::libc::c_int) + -> sqlite3_int64; + pub fn sqlite3_column_text(arg1: *mut sqlite3_stmt, iCol: ::libc::c_int) + -> *const ::libc::c_uchar; + pub fn sqlite3_column_text16(arg1: *mut sqlite3_stmt, iCol: ::libc::c_int) + -> *const ::libc::c_void; + pub fn sqlite3_column_type(arg1: *mut sqlite3_stmt, iCol: ::libc::c_int) + -> ::libc::c_int; + pub fn sqlite3_column_value(arg1: *mut sqlite3_stmt, iCol: ::libc::c_int) + -> *mut sqlite3_value; + pub fn sqlite3_finalize(pStmt: *mut sqlite3_stmt) -> ::libc::c_int; + pub fn sqlite3_reset(pStmt: *mut sqlite3_stmt) -> ::libc::c_int; + pub fn sqlite3_create_function(db: *mut sqlite3, + zFunctionName: *const ::libc::c_char, + nArg: ::libc::c_int, + eTextRep: ::libc::c_int, + pApp: *mut ::libc::c_void, + xFunc: + ::std::option::Option, + xStep: + ::std::option::Option, + xFinal: + ::std::option::Option) + -> ::libc::c_int; + pub fn sqlite3_create_function16(db: *mut sqlite3, + zFunctionName: *const ::libc::c_void, + nArg: ::libc::c_int, + eTextRep: ::libc::c_int, + pApp: *mut ::libc::c_void, + xFunc: + ::std::option::Option, + xStep: + ::std::option::Option, + xFinal: + ::std::option::Option) + -> ::libc::c_int; + pub fn sqlite3_create_function_v2(db: *mut sqlite3, + zFunctionName: *const ::libc::c_char, + nArg: ::libc::c_int, + eTextRep: ::libc::c_int, + pApp: *mut ::libc::c_void, + xFunc: + ::std::option::Option, + xStep: + ::std::option::Option, + xFinal: + ::std::option::Option, + xDestroy: + ::std::option::Option) + -> ::libc::c_int; + pub fn sqlite3_aggregate_count(arg1: *mut sqlite3_context) -> + ::libc::c_int; + pub fn sqlite3_expired(arg1: *mut sqlite3_stmt) -> ::libc::c_int; + pub fn sqlite3_transfer_bindings(arg1: *mut sqlite3_stmt, + arg2: *mut sqlite3_stmt) -> + ::libc::c_int; + pub fn sqlite3_global_recover() -> ::libc::c_int; + pub fn sqlite3_thread_cleanup(); + pub fn sqlite3_memory_alarm(arg1: + ::std::option::Option, + arg2: *mut ::libc::c_void, + arg3: sqlite3_int64) -> ::libc::c_int; + pub fn sqlite3_value_blob(arg1: *mut sqlite3_value) -> + *const ::libc::c_void; + pub fn sqlite3_value_bytes(arg1: *mut sqlite3_value) -> ::libc::c_int; + pub fn sqlite3_value_bytes16(arg1: *mut sqlite3_value) -> ::libc::c_int; + pub fn sqlite3_value_double(arg1: *mut sqlite3_value) -> ::libc::c_double; + pub fn sqlite3_value_int(arg1: *mut sqlite3_value) -> ::libc::c_int; + pub fn sqlite3_value_int64(arg1: *mut sqlite3_value) -> sqlite3_int64; + pub fn sqlite3_value_text(arg1: *mut sqlite3_value) -> + *const ::libc::c_uchar; + pub fn sqlite3_value_text16(arg1: *mut sqlite3_value) -> + *const ::libc::c_void; + pub fn sqlite3_value_text16le(arg1: *mut sqlite3_value) -> + *const ::libc::c_void; + pub fn sqlite3_value_text16be(arg1: *mut sqlite3_value) -> + *const ::libc::c_void; + pub fn sqlite3_value_type(arg1: *mut sqlite3_value) -> ::libc::c_int; + pub fn sqlite3_value_numeric_type(arg1: *mut sqlite3_value) -> + ::libc::c_int; + pub fn sqlite3_aggregate_context(arg1: *mut sqlite3_context, + nBytes: ::libc::c_int) -> + *mut ::libc::c_void; + pub fn sqlite3_user_data(arg1: *mut sqlite3_context) -> + *mut ::libc::c_void; + pub fn sqlite3_context_db_handle(arg1: *mut sqlite3_context) -> + *mut sqlite3; + pub fn sqlite3_get_auxdata(arg1: *mut sqlite3_context, N: ::libc::c_int) + -> *mut ::libc::c_void; + pub fn sqlite3_set_auxdata(arg1: *mut sqlite3_context, N: ::libc::c_int, + arg2: *mut ::libc::c_void, + arg3: + ::std::option::Option); + pub fn sqlite3_result_blob(arg1: *mut sqlite3_context, + arg2: *const ::libc::c_void, + arg3: ::libc::c_int, + arg4: + ::std::option::Option); + pub fn sqlite3_result_double(arg1: *mut sqlite3_context, + arg2: ::libc::c_double); + pub fn sqlite3_result_error(arg1: *mut sqlite3_context, + arg2: *const ::libc::c_char, + arg3: ::libc::c_int); + pub fn sqlite3_result_error16(arg1: *mut sqlite3_context, + arg2: *const ::libc::c_void, + arg3: ::libc::c_int); + pub fn sqlite3_result_error_toobig(arg1: *mut sqlite3_context); + pub fn sqlite3_result_error_nomem(arg1: *mut sqlite3_context); + pub fn sqlite3_result_error_code(arg1: *mut sqlite3_context, + arg2: ::libc::c_int); + pub fn sqlite3_result_int(arg1: *mut sqlite3_context, + arg2: ::libc::c_int); + pub fn sqlite3_result_int64(arg1: *mut sqlite3_context, + arg2: sqlite3_int64); + pub fn sqlite3_result_null(arg1: *mut sqlite3_context); + pub fn sqlite3_result_text(arg1: *mut sqlite3_context, + arg2: *const ::libc::c_char, + arg3: ::libc::c_int, + arg4: + ::std::option::Option); + pub fn sqlite3_result_text16(arg1: *mut sqlite3_context, + arg2: *const ::libc::c_void, + arg3: ::libc::c_int, + arg4: + ::std::option::Option); + pub fn sqlite3_result_text16le(arg1: *mut sqlite3_context, + arg2: *const ::libc::c_void, + arg3: ::libc::c_int, + arg4: + ::std::option::Option); + pub fn sqlite3_result_text16be(arg1: *mut sqlite3_context, + arg2: *const ::libc::c_void, + arg3: ::libc::c_int, + arg4: + ::std::option::Option); + pub fn sqlite3_result_value(arg1: *mut sqlite3_context, + arg2: *mut sqlite3_value); + pub fn sqlite3_result_zeroblob(arg1: *mut sqlite3_context, + n: ::libc::c_int); + pub fn sqlite3_create_collation(arg1: *mut sqlite3, + zName: *const ::libc::c_char, + eTextRep: ::libc::c_int, + pArg: *mut ::libc::c_void, + xCompare: + ::std::option::Option + ::libc::c_int>) + -> ::libc::c_int; + pub fn sqlite3_create_collation_v2(arg1: *mut sqlite3, + zName: *const ::libc::c_char, + eTextRep: ::libc::c_int, + pArg: *mut ::libc::c_void, + xCompare: + ::std::option::Option + ::libc::c_int>, + xDestroy: + ::std::option::Option) + -> ::libc::c_int; + pub fn sqlite3_create_collation16(arg1: *mut sqlite3, + zName: *const ::libc::c_void, + eTextRep: ::libc::c_int, + pArg: *mut ::libc::c_void, + xCompare: + ::std::option::Option + ::libc::c_int>) + -> ::libc::c_int; + pub fn sqlite3_collation_needed(arg1: *mut sqlite3, + arg2: *mut ::libc::c_void, + arg3: + ::std::option::Option) + -> ::libc::c_int; + pub fn sqlite3_collation_needed16(arg1: *mut sqlite3, + arg2: *mut ::libc::c_void, + arg3: + ::std::option::Option) + -> ::libc::c_int; + pub fn sqlite3_sleep(arg1: ::libc::c_int) -> ::libc::c_int; + pub fn sqlite3_get_autocommit(arg1: *mut sqlite3) -> ::libc::c_int; + pub fn sqlite3_db_handle(arg1: *mut sqlite3_stmt) -> *mut sqlite3; + pub fn sqlite3_db_filename(db: *mut sqlite3, + zDbName: *const ::libc::c_char) -> + *const ::libc::c_char; + pub fn sqlite3_db_readonly(db: *mut sqlite3, + zDbName: *const ::libc::c_char) -> + ::libc::c_int; + pub fn sqlite3_next_stmt(pDb: *mut sqlite3, pStmt: *mut sqlite3_stmt) -> + *mut sqlite3_stmt; + pub fn sqlite3_commit_hook(arg1: *mut sqlite3, + arg2: + ::std::option::Option + ::libc::c_int>, + arg3: *mut ::libc::c_void) -> + *mut ::libc::c_void; + pub fn sqlite3_rollback_hook(arg1: *mut sqlite3, + arg2: + ::std::option::Option, + arg3: *mut ::libc::c_void) -> + *mut ::libc::c_void; + pub fn sqlite3_update_hook(arg1: *mut sqlite3, + arg2: + ::std::option::Option, + arg3: *mut ::libc::c_void) -> + *mut ::libc::c_void; + pub fn sqlite3_enable_shared_cache(arg1: ::libc::c_int) -> ::libc::c_int; + pub fn sqlite3_release_memory(arg1: ::libc::c_int) -> ::libc::c_int; + pub fn sqlite3_db_release_memory(arg1: *mut sqlite3) -> ::libc::c_int; + pub fn sqlite3_soft_heap_limit64(N: sqlite3_int64) -> sqlite3_int64; + pub fn sqlite3_soft_heap_limit(N: ::libc::c_int); + pub fn sqlite3_table_column_metadata(db: *mut sqlite3, + zDbName: *const ::libc::c_char, + zTableName: *const ::libc::c_char, + zColumnName: *const ::libc::c_char, + pzDataType: + *mut *const ::libc::c_char, + pzCollSeq: + *mut *const ::libc::c_char, + pNotNull: *mut ::libc::c_int, + pPrimaryKey: *mut ::libc::c_int, + pAutoinc: *mut ::libc::c_int) -> + ::libc::c_int; + pub fn sqlite3_load_extension(db: *mut sqlite3, + zFile: *const ::libc::c_char, + zProc: *const ::libc::c_char, + pzErrMsg: *mut *mut ::libc::c_char) -> + ::libc::c_int; + pub fn sqlite3_enable_load_extension(db: *mut sqlite3, + onoff: ::libc::c_int) -> + ::libc::c_int; + pub fn sqlite3_auto_extension(xEntryPoint: + ::std::option::Option) + -> ::libc::c_int; + pub fn sqlite3_cancel_auto_extension(xEntryPoint: + ::std::option::Option) -> + ::libc::c_int; + pub fn sqlite3_reset_auto_extension(); + pub fn sqlite3_create_module(db: *mut sqlite3, + zName: *const ::libc::c_char, + p: *const sqlite3_module, + pClientData: *mut ::libc::c_void) -> + ::libc::c_int; + pub fn sqlite3_create_module_v2(db: *mut sqlite3, + zName: *const ::libc::c_char, + p: *const sqlite3_module, + pClientData: *mut ::libc::c_void, + xDestroy: + ::std::option::Option) + -> ::libc::c_int; + pub fn sqlite3_declare_vtab(arg1: *mut sqlite3, + zSQL: *const ::libc::c_char) -> ::libc::c_int; + pub fn sqlite3_overload_function(arg1: *mut sqlite3, + zFuncName: *const ::libc::c_char, + nArg: ::libc::c_int) -> ::libc::c_int; + pub fn sqlite3_blob_open(arg1: *mut sqlite3, zDb: *const ::libc::c_char, + zTable: *const ::libc::c_char, + zColumn: *const ::libc::c_char, + iRow: sqlite3_int64, flags: ::libc::c_int, + ppBlob: *mut *mut sqlite3_blob) -> ::libc::c_int; + pub fn sqlite3_blob_reopen(arg1: *mut sqlite3_blob, arg2: sqlite3_int64) + -> ::libc::c_int; + pub fn sqlite3_blob_close(arg1: *mut sqlite3_blob) -> ::libc::c_int; + pub fn sqlite3_blob_bytes(arg1: *mut sqlite3_blob) -> ::libc::c_int; + pub fn sqlite3_blob_read(arg1: *mut sqlite3_blob, Z: *mut ::libc::c_void, + N: ::libc::c_int, iOffset: ::libc::c_int) -> + ::libc::c_int; + pub fn sqlite3_blob_write(arg1: *mut sqlite3_blob, + z: *const ::libc::c_void, n: ::libc::c_int, + iOffset: ::libc::c_int) -> ::libc::c_int; + pub fn sqlite3_vfs_find(zVfsName: *const ::libc::c_char) -> + *mut sqlite3_vfs; + pub fn sqlite3_vfs_register(arg1: *mut sqlite3_vfs, + makeDflt: ::libc::c_int) -> ::libc::c_int; + pub fn sqlite3_vfs_unregister(arg1: *mut sqlite3_vfs) -> ::libc::c_int; + pub fn sqlite3_mutex_alloc(arg1: ::libc::c_int) -> *mut sqlite3_mutex; + pub fn sqlite3_mutex_free(arg1: *mut sqlite3_mutex); + pub fn sqlite3_mutex_enter(arg1: *mut sqlite3_mutex); + pub fn sqlite3_mutex_try(arg1: *mut sqlite3_mutex) -> ::libc::c_int; + pub fn sqlite3_mutex_leave(arg1: *mut sqlite3_mutex); + pub fn sqlite3_mutex_held(arg1: *mut sqlite3_mutex) -> ::libc::c_int; + pub fn sqlite3_mutex_notheld(arg1: *mut sqlite3_mutex) -> ::libc::c_int; + pub fn sqlite3_db_mutex(arg1: *mut sqlite3) -> *mut sqlite3_mutex; + pub fn sqlite3_file_control(arg1: *mut sqlite3, + zDbName: *const ::libc::c_char, + op: ::libc::c_int, arg2: *mut ::libc::c_void) + -> ::libc::c_int; + pub fn sqlite3_test_control(op: ::libc::c_int, ...) -> ::libc::c_int; + pub fn sqlite3_status(op: ::libc::c_int, pCurrent: *mut ::libc::c_int, + pHighwater: *mut ::libc::c_int, + resetFlag: ::libc::c_int) -> ::libc::c_int; + pub fn sqlite3_db_status(arg1: *mut sqlite3, op: ::libc::c_int, + pCur: *mut ::libc::c_int, + pHiwtr: *mut ::libc::c_int, + resetFlg: ::libc::c_int) -> ::libc::c_int; + pub fn sqlite3_stmt_status(arg1: *mut sqlite3_stmt, op: ::libc::c_int, + resetFlg: ::libc::c_int) -> ::libc::c_int; + pub fn sqlite3_backup_init(pDest: *mut sqlite3, + zDestName: *const ::libc::c_char, + pSource: *mut sqlite3, + zSourceName: *const ::libc::c_char) -> + *mut sqlite3_backup; + pub fn sqlite3_backup_step(p: *mut sqlite3_backup, nPage: ::libc::c_int) + -> ::libc::c_int; + pub fn sqlite3_backup_finish(p: *mut sqlite3_backup) -> ::libc::c_int; + pub fn sqlite3_backup_remaining(p: *mut sqlite3_backup) -> ::libc::c_int; + pub fn sqlite3_backup_pagecount(p: *mut sqlite3_backup) -> ::libc::c_int; + pub fn sqlite3_unlock_notify(pBlocked: *mut sqlite3, + xNotify: + ::std::option::Option, + pNotifyArg: *mut ::libc::c_void) -> + ::libc::c_int; + pub fn sqlite3_stricmp(arg1: *const ::libc::c_char, + arg2: *const ::libc::c_char) -> ::libc::c_int; + pub fn sqlite3_strnicmp(arg1: *const ::libc::c_char, + arg2: *const ::libc::c_char, arg3: ::libc::c_int) + -> ::libc::c_int; + pub fn sqlite3_strglob(zGlob: *const ::libc::c_char, + zStr: *const ::libc::c_char) -> ::libc::c_int; + pub fn sqlite3_log(iErrCode: ::libc::c_int, + zFormat: *const ::libc::c_char, ...); + pub fn sqlite3_wal_hook(arg1: *mut sqlite3, + arg2: + ::std::option::Option ::libc::c_int>, + arg3: *mut ::libc::c_void) -> *mut ::libc::c_void; + pub fn sqlite3_wal_autocheckpoint(db: *mut sqlite3, N: ::libc::c_int) -> + ::libc::c_int; + pub fn sqlite3_wal_checkpoint(db: *mut sqlite3, + zDb: *const ::libc::c_char) -> + ::libc::c_int; + pub fn sqlite3_wal_checkpoint_v2(db: *mut sqlite3, + zDb: *const ::libc::c_char, + eMode: ::libc::c_int, + pnLog: *mut ::libc::c_int, + pnCkpt: *mut ::libc::c_int) -> + ::libc::c_int; + pub fn sqlite3_vtab_config(arg1: *mut sqlite3, op: ::libc::c_int, ...) -> + ::libc::c_int; + pub fn sqlite3_vtab_on_conflict(arg1: *mut sqlite3) -> ::libc::c_int; + pub fn sqlite3_rtree_geometry_callback(db: *mut sqlite3, + zGeom: *const ::libc::c_char, + xGeom: + ::std::option::Option + ::libc::c_int>, + pContext: *mut ::libc::c_void) -> + ::libc::c_int; + pub fn sqlite3_rtree_query_callback(db: *mut sqlite3, + zQueryFunc: *const ::libc::c_char, + xQueryFunc: + ::std::option::Option + ::libc::c_int>, + pContext: *mut ::libc::c_void, + xDestructor: + ::std::option::Option) + -> ::libc::c_int; +} diff --git a/src/ffi/mod.rs b/src/ffi/mod.rs new file mode 100644 index 0000000..3a39c47 --- /dev/null +++ b/src/ffi/mod.rs @@ -0,0 +1,83 @@ +pub use self::bindgen::*; +use std::mem; +use libc::{c_int, c_void}; + +mod bindgen; + +pub const SQLITE_OK : c_int = 0; +pub const SQLITE_ERROR : c_int = 1; +pub const SQLITE_INTERNAL : c_int = 2; +pub const SQLITE_PERM : c_int = 3; +pub const SQLITE_ABORT : c_int = 4; +pub const SQLITE_BUSY : c_int = 5; +pub const SQLITE_LOCKED : c_int = 6; +pub const SQLITE_NOMEM : c_int = 7; +pub const SQLITE_READONLY : c_int = 8; +pub const SQLITE_INTERRUPT : c_int = 9; +pub const SQLITE_IOERR : c_int = 10; +pub const SQLITE_CORRUPT : c_int = 11; +pub const SQLITE_NOTFOUND : c_int = 12; +pub const SQLITE_FULL : c_int = 13; +pub const SQLITE_CANTOPEN : c_int = 14; +pub const SQLITE_PROTOCOL : c_int = 15; +pub const SQLITE_EMPTY : c_int = 16; +pub const SQLITE_SCHEMA : c_int = 17; +pub const SQLITE_TOOBIG : c_int = 18; +pub const SQLITE_CONSTRAINT: c_int = 19; +pub const SQLITE_MISMATCH : c_int = 20; +pub const SQLITE_MISUSE : c_int = 21; +pub const SQLITE_NOLFS : c_int = 22; +pub const SQLITE_AUTH : c_int = 23; +pub const SQLITE_FORMAT : c_int = 24; +pub const SQLITE_RANGE : c_int = 25; +pub const SQLITE_NOTADB : c_int = 26; +pub const SQLITE_NOTICE : c_int = 27; +pub const SQLITE_WARNING : c_int = 28; +pub const SQLITE_ROW : c_int = 100; +pub const SQLITE_DONE : c_int = 101; + +// SQLite datatype constants. +pub const SQLITE_NULL : c_int = 5; + +pub type SqliteDestructor = extern "C" fn(*mut c_void); + +pub fn SQLITE_TRANSIENT() -> SqliteDestructor { + unsafe { mem::transmute(-1i) } +} + +pub fn code_to_str(code: c_int) -> &'static str { + match code { + SQLITE_OK => "Successful result", + SQLITE_ERROR => "SQL error or missing database", + SQLITE_INTERNAL => "Internal logic error in SQLite", + SQLITE_PERM => "Access permission denied", + SQLITE_ABORT => "Callback routine requested an abort", + SQLITE_BUSY => "The database file is locked", + SQLITE_LOCKED => "A table in the database is locked", + SQLITE_NOMEM => "A malloc() failed", + SQLITE_READONLY => "Attempt to write a readonly database", + SQLITE_INTERRUPT => "Operation terminated by sqlite3_interrupt()", + SQLITE_IOERR => "Some kind of disk I/O error occurred", + SQLITE_CORRUPT => "The database disk image is malformed", + SQLITE_NOTFOUND => "Unknown opcode in sqlite3_file_control()", + SQLITE_FULL => "Insertion failed because database is full", + SQLITE_CANTOPEN => "Unable to open the database file", + SQLITE_PROTOCOL => "Database lock protocol error", + SQLITE_EMPTY => "Database is empty", + SQLITE_SCHEMA => "The database schema changed", + SQLITE_TOOBIG => "String or BLOB exceeds size limit", + SQLITE_CONSTRAINT=> "Abort due to constraint violation", + SQLITE_MISMATCH => "Data type mismatch", + SQLITE_MISUSE => "Library used incorrectly", + SQLITE_NOLFS => "Uses OS features not supported on host", + SQLITE_AUTH => "Authorization denied", + SQLITE_FORMAT => "Auxiliary database format error", + SQLITE_RANGE => "2nd parameter to sqlite3_bind out of range", + SQLITE_NOTADB => "File opened that is not a database file", + SQLITE_NOTICE => "Notifications from sqlite3_log()", + SQLITE_WARNING => "Warnings from sqlite3_log()", + SQLITE_ROW => "sqlite3_step() has another row ready", + SQLITE_DONE => "sqlite3_step() has finished executing", + _ => "Unknown error code", + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..e6cdedf --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,501 @@ +#![feature(globs)] +#![feature(unsafe_destructor)] +#![feature(macro_rules)] + +extern crate libc; + +use std::mem; +use std::ptr; +use std::fmt; +use std::rc::{Rc}; +use std::cell::{RefCell, Cell}; +use std::c_str::{CString}; +use libc::{c_int, c_void, c_char}; + +use types::{ToSql, FromSql}; + +pub use transaction::{SqliteTransaction}; +pub use transaction::{SqliteTransactionMode, + SqliteTransactionDeferred, + SqliteTransactionImmediate, + SqliteTransactionExclusive}; + +pub mod types; +mod transaction; +#[allow(dead_code,non_snake_case,non_camel_case_types)] mod ffi; + +pub type SqliteResult = Result; + +unsafe fn errmsg_to_string(errmsg: *const c_char) -> String { + let c_str = CString::new(errmsg, false); + c_str.as_str().unwrap_or("Invalid error message encoding").to_string() +} + +#[deriving(Show)] +pub struct SqliteError { + pub code: c_int, + pub message: String, +} + +impl SqliteError { + fn from_handle(db: *mut ffi::Struct_sqlite3, code: c_int) -> SqliteError { + let message = if db.is_null() { + ffi::code_to_str(code).to_string() + } else { + unsafe { errmsg_to_string(ffi::sqlite3_errmsg(db)) } + }; + SqliteError{ code: code, message: message } + } +} + +pub struct SqliteConnection { + db: RefCell, +} + +impl SqliteConnection { + pub fn open(path: &str) -> SqliteResult { + let flags = SQLITE_OPEN_READ_WRITE | SQLITE_OPEN_CREATE; + SqliteConnection::open_with_flags(path, flags) + } + + pub fn open_with_flags(path: &str, flags: SqliteOpenFlags) -> SqliteResult { + InnerSqliteConnection::open_with_flags(path, flags).map(|db| { + SqliteConnection{ db: RefCell::new(db) } + }) + } + + pub fn transaction<'a>(&'a self) -> SqliteResult> { + SqliteTransaction::new(self, SqliteTransactionDeferred) + } + + pub fn transaction_with_mode<'a>(&'a self, mode: SqliteTransactionMode) + -> SqliteResult> { + SqliteTransaction::new(self, mode) + } + + pub fn execute_batch(&self, sql: &str) -> SqliteResult<()> { + self.db.borrow_mut().execute_batch(sql) + } + + pub fn execute(&self, sql: &str, params: &[&ToSql]) -> SqliteResult { + self.prepare(sql).and_then(|mut stmt| stmt.execute(params)) + } + + pub fn last_insert_rowid(&self) -> i64 { + self.db.borrow_mut().last_insert_rowid() + } + + pub fn query_row(&self, sql: &str, params: &[&ToSql], + f: |SqliteResult| -> T) -> T { + f(self.prepare(sql).unwrap().query(params).unwrap().next().unwrap()) + } + + pub fn prepare<'a>(&'a self, sql: &str) -> SqliteResult> { + self.db.borrow_mut().prepare(self, sql) + } + + pub fn close(self) -> SqliteResult<()> { + self.db.borrow_mut().close() + } + + fn decode_result(&self, code: c_int) -> SqliteResult<()> { + self.db.borrow_mut().decode_result(code) + } + + fn changes(&self) -> uint { + self.db.borrow_mut().changes() + } +} + +impl fmt::Show for SqliteConnection { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "SqliteConnection()") + } +} + +struct InnerSqliteConnection { + db: *mut ffi::Struct_sqlite3, +} + +bitflags! { + #[repr(C)] flags SqliteOpenFlags: c_int { + const SQLITE_OPEN_READ_ONLY = 0x00000001, + const SQLITE_OPEN_READ_WRITE = 0x00000002, + const SQLITE_OPEN_CREATE = 0x00000004, + const SQLITE_OPEN_URI = 0x00000040, + const SQLITE_OPEN_MEMORY = 0x00000080, + const SQLITE_OPEN_NO_MUTEX = 0x00008000, + const SQLITE_OPEN_FULL_MUTEX = 0x00010000, + const SQLITE_OPEN_SHARED_CACHE = 0x00020000, + const SQLITE_OPEN_PRIVATE_CACHE = 0x00040000, + } +} + +impl InnerSqliteConnection { + fn open_with_flags(path: &str, flags: SqliteOpenFlags) -> SqliteResult { + path.with_c_str(|c_path| unsafe { + let mut db: *mut ffi::sqlite3 = mem::uninitialized(); + let r = ffi::sqlite3_open_v2(c_path, &mut db, flags.bits(), ptr::null()); + if r != ffi::SQLITE_OK { + let e = if db.is_null() { + SqliteError{ code: r, + message: ffi::code_to_str(r).to_string() } + } else { + ffi::sqlite3_close(db); + SqliteError::from_handle(db, r) + }; + + return Err(e); + } + Ok(InnerSqliteConnection{ db: db }) + }) + } + + fn decode_result(&mut self, code: c_int) -> SqliteResult<()> { + if code == ffi::SQLITE_OK { + Ok(()) + } else { + Err(SqliteError::from_handle(self.db, code)) + } + } + + fn close(&mut self) -> SqliteResult<()> { + let r = unsafe { ffi::sqlite3_close(self.db) }; + self.db = ptr::null_mut(); + self.decode_result(r) + } + + fn execute_batch(&mut self, sql: &str) -> SqliteResult<()> { + sql.with_c_str(|c_sql| unsafe { + let mut errmsg: *mut c_char = mem::uninitialized(); + let r = ffi::sqlite3_exec(self.db, c_sql, None, ptr::null_mut(), &mut errmsg); + if r == ffi::SQLITE_OK { + Ok(()) + } else { + let message = errmsg_to_string(&*errmsg); + ffi::sqlite3_free(errmsg as *mut c_void); + Err(SqliteError{ code: r, message: message }) + } + }) + } + + fn last_insert_rowid(&self) -> i64 { + unsafe { + ffi::sqlite3_last_insert_rowid(self.db) + } + } + + fn prepare<'a>(&mut self, + conn: &'a SqliteConnection, + sql: &str) -> SqliteResult> { + let mut c_stmt: *mut ffi::sqlite3_stmt = unsafe { mem::uninitialized() }; + let r = sql.with_c_str(|c_sql| unsafe { + let len_with_nul = (sql.len() + 1) as c_int; + ffi::sqlite3_prepare_v2(self.db, c_sql, len_with_nul, &mut c_stmt, ptr::null_mut()) + }); + self.decode_result(r).map(|_| { + SqliteStatement::new(conn, c_stmt) + }) + } + + fn changes(&mut self) -> uint { + unsafe{ ffi::sqlite3_changes(self.db) as uint } + } +} + +impl Drop for InnerSqliteConnection { + #[allow(unused_must_use)] + fn drop(&mut self) { + self.close(); + } +} + +pub struct SqliteStatement<'conn> { + conn: &'conn SqliteConnection, + stmt: *mut ffi::sqlite3_stmt, + needs_reset: bool, +} + +impl<'conn> SqliteStatement<'conn> { + fn new(conn: &SqliteConnection, stmt: *mut ffi::sqlite3_stmt) -> SqliteStatement { + SqliteStatement{ conn: conn, stmt: stmt, needs_reset: false } + } + + pub fn execute(&mut self, params: &[&ToSql]) -> SqliteResult { + self.reset_if_needed(); + + unsafe { + assert_eq!(params.len() as c_int, ffi::sqlite3_bind_parameter_count(self.stmt)); + + for (i, p) in params.iter().enumerate() { + try!(self.conn.decode_result(p.bind_parameter(self.stmt, (i + 1) as c_int))); + } + + self.needs_reset = true; + let r = ffi::sqlite3_step(self.stmt); + match r { + ffi::SQLITE_DONE => Ok(self.conn.changes()), + ffi::SQLITE_ROW => Err(SqliteError{ code: r, + message: "Unexpected row result - did you mean to call query?".to_string() }), + _ => Err(self.conn.decode_result(r).unwrap_err()), + } + } + } + + pub fn query<'a>(&'a mut self, params: &[&ToSql]) -> SqliteResult> { + self.reset_if_needed(); + + unsafe { + assert_eq!(params.len() as c_int, ffi::sqlite3_bind_parameter_count(self.stmt)); + + for (i, p) in params.iter().enumerate() { + try!(self.conn.decode_result(p.bind_parameter(self.stmt, (i + 1) as c_int))); + } + + self.needs_reset = true; + Ok(SqliteRows::new(self)) + } + } + + pub fn finalize(mut self) -> SqliteResult<()> { + self.finalize_() + } + + fn reset_if_needed(&mut self) { + if self.needs_reset { + unsafe { ffi::sqlite3_reset(self.stmt); }; + self.needs_reset = false; + } + } + + fn finalize_(&mut self) -> SqliteResult<()> { + let r = unsafe { ffi::sqlite3_finalize(self.stmt) }; + self.stmt = ptr::null_mut(); + self.conn.decode_result(r) + } +} + +impl<'conn> fmt::Show for SqliteStatement<'conn> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Statement( conn: {}, stmt: {} )", self.conn, self.stmt) + } +} + +#[unsafe_destructor] +impl<'conn> Drop for SqliteStatement<'conn> { + #[allow(unused_must_use)] + fn drop(&mut self) { + self.finalize_(); + } +} + +pub struct SqliteRows<'stmt> { + stmt: &'stmt SqliteStatement<'stmt>, + current_row: Rc>, + failed: bool, +} + +impl<'stmt> SqliteRows<'stmt> { + fn new(stmt: &'stmt SqliteStatement<'stmt>) -> SqliteRows<'stmt> { + SqliteRows{ stmt: stmt, current_row: Rc::new(Cell::new(0)), failed: false } + } +} + +impl<'stmt> Iterator>> for SqliteRows<'stmt> { + fn next(&mut self) -> Option>> { + if self.failed { + return None; + } + match unsafe { ffi::sqlite3_step(self.stmt.stmt) } { + ffi::SQLITE_ROW => { + let current_row = self.current_row.get() + 1; + self.current_row.set(current_row); + Some(Ok(SqliteRow{ + stmt: self.stmt, + current_row: self.current_row.clone(), + row_idx: current_row, + })) + }, + ffi::SQLITE_DONE => None, + code => { + self.failed = true; + Some(Err(self.stmt.conn.decode_result(code).unwrap_err())) + } + } + } +} + +pub struct SqliteRow<'stmt> { + stmt: &'stmt SqliteStatement<'stmt>, + current_row: Rc>, + row_idx: c_int, +} + +impl<'stmt> SqliteRow<'stmt> { + pub fn get(&self, idx: c_int) -> T { + self.get_opt(idx).unwrap() + } + + pub fn get_opt(&self, idx: c_int) -> SqliteResult { + if self.row_idx != self.current_row.get() { + return Err(SqliteError{ code: ffi::SQLITE_MISUSE, + message: "Cannot get values from a row after advancing to next row".to_string() }); + } + unsafe { + if idx < 0 || idx >= ffi::sqlite3_column_count(self.stmt.stmt) { + return Err(SqliteError{ code: ffi::SQLITE_MISUSE, + message: "Invalid column index".to_string() }); + } + Ok(FromSql::column_result(self.stmt.stmt, idx)) + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + fn checked_memory_handle() -> SqliteConnection { + SqliteConnection::open(":memory:").unwrap() + } + + #[test] + fn test_open() { + assert!(SqliteConnection::open(":memory:").is_ok()); + + let db = checked_memory_handle(); + assert!(db.close().is_ok()); + } + + #[test] + fn test_open_with_flags() { + for bad_flags in [ + SqliteOpenFlags::empty(), + SQLITE_OPEN_READ_ONLY | SQLITE_OPEN_READ_WRITE, + SQLITE_OPEN_READ_ONLY | SQLITE_OPEN_CREATE, + ].iter() { + assert!(SqliteConnection::open_with_flags(":memory:", *bad_flags).is_err()); + } + + assert!(SqliteConnection::open_with_flags( + "file::memory:", SQLITE_OPEN_READ_ONLY|SQLITE_OPEN_URI).is_ok()); + + assert!(SqliteConnection::open_with_flags( + "/invalid", SQLITE_OPEN_READ_ONLY|SQLITE_OPEN_MEMORY).is_ok()); + } + + #[test] + fn test_execute_batch() { + let db = checked_memory_handle(); + let sql = "BEGIN; + CREATE TABLE foo(x INTEGER); + INSERT INTO foo VALUES(1); + INSERT INTO foo VALUES(2); + INSERT INTO foo VALUES(3); + INSERT INTO foo VALUES(4); + END;"; + db.execute_batch(sql).unwrap(); + + db.execute_batch("UPDATE foo SET x = 3 WHERE x < 3").unwrap(); + + assert!(db.execute_batch("INVALID SQL").is_err()); + } + + #[test] + fn test_execute() { + let db = checked_memory_handle(); + db.execute_batch("CREATE TABLE foo(x INTEGER)").unwrap(); + + assert_eq!(db.execute("INSERT INTO foo(x) VALUES (?)", &[&1i32]).unwrap(), 1); + assert_eq!(db.execute("INSERT INTO foo(x) VALUES (?)", &[&2i32]).unwrap(), 1); + + assert_eq!(3i32, db.query_row("SELECT SUM(x) FROM foo", [], |r| r.unwrap().get(0))); + } + + #[test] + fn test_prepare_execute() { + let db = checked_memory_handle(); + db.execute_batch("CREATE TABLE foo(x INTEGER);").unwrap(); + + let mut insert_stmt = db.prepare("INSERT INTO foo(x) VALUES(?)").unwrap(); + assert_eq!(insert_stmt.execute(&[&1i32]).unwrap(), 1); + assert_eq!(insert_stmt.execute(&[&2i32]).unwrap(), 1); + assert_eq!(insert_stmt.execute(&[&3i32]).unwrap(), 1); + + assert_eq!(insert_stmt.execute(&[&"hello".to_string()]).unwrap(), 1); + assert_eq!(insert_stmt.execute(&[&"goodbye".to_string()]).unwrap(), 1); + assert_eq!(insert_stmt.execute(&[&types::Null]).unwrap(), 1); + + let mut update_stmt = db.prepare("UPDATE foo SET x=? WHERE x = rows.map(|r| r.unwrap().get(0)).collect(); + + assert_eq!(v.as_slice(), [3i32, 2, 1].as_slice()); + } + + { + let rows = query.query(&[&3i32]).unwrap(); + let v: Vec = rows.map(|r| r.unwrap().get(0)).collect(); + assert_eq!(v.as_slice(), [2i32, 1].as_slice()); + } + } + + #[test] + fn test_prepare_failures() { + let db = checked_memory_handle(); + db.execute_batch("CREATE TABLE foo(x INTEGER);").unwrap(); + + let err = db.prepare("SELECT * FROM does_not_exist").unwrap_err(); + assert!(err.message.as_slice().contains("does_not_exist")); + } + + #[test] + fn test_row_expiration() { + let db = checked_memory_handle(); + db.execute_batch("CREATE TABLE foo(x INTEGER)").unwrap(); + db.execute_batch("INSERT INTO foo(x) VALUES(1)").unwrap(); + db.execute_batch("INSERT INTO foo(x) VALUES(2)").unwrap(); + + let mut stmt = db.prepare("SELECT x FROM foo ORDER BY x").unwrap(); + let mut rows = stmt.query([]).unwrap(); + let first = rows.next().unwrap().unwrap(); + let second = rows.next().unwrap().unwrap(); + + assert_eq!(2i32, second.get(0)); + + let result = first.get_opt::(0); + assert!(result.unwrap_err().message.as_slice().contains("advancing to next row")); + } + + #[test] + fn test_last_insert_rowid() { + let db = checked_memory_handle(); + db.execute_batch("CREATE TABLE foo(x INTEGER PRIMARY KEY)").unwrap(); + db.execute_batch("INSERT INTO foo DEFAULT VALUES").unwrap(); + + assert_eq!(db.last_insert_rowid(), 1); + + let mut stmt = db.prepare("INSERT INTO foo DEFAULT VALUES").unwrap(); + for _ in range(0i, 9) { + stmt.execute([]).unwrap(); + } + assert_eq!(db.last_insert_rowid(), 10); + } +} diff --git a/src/transaction.rs b/src/transaction.rs new file mode 100644 index 0000000..ebb8aa7 --- /dev/null +++ b/src/transaction.rs @@ -0,0 +1,195 @@ +use {SqliteResult, SqliteConnection}; + +pub enum SqliteTransactionMode { + SqliteTransactionDeferred, + SqliteTransactionImmediate, + SqliteTransactionExclusive, +} + +pub struct SqliteTransaction<'conn> { + conn: &'conn SqliteConnection, + depth: u32, + commit: bool, + finished: bool, +} + +impl<'conn> SqliteTransaction<'conn> { + pub fn new(conn: &SqliteConnection, + mode: SqliteTransactionMode) -> SqliteResult { + let query = match mode { + SqliteTransactionDeferred => "BEGIN DEFERRED", + SqliteTransactionImmediate => "BEGIN IMMEDIATE", + SqliteTransactionExclusive => "BEGIN EXCLUSIVE", + }; + conn.execute_batch(query).map(|_| { + SqliteTransaction{ conn: conn, depth: 0, commit: false, finished: false } + }) + } + + pub fn savepoint<'a>(&'a self) -> SqliteResult> { + self.conn.execute_batch("SAVEPOINT sp").map(|_| { + SqliteTransaction{ + conn: self.conn, depth: self.depth + 1, commit: false, finished: false + } + }) + } + + pub fn will_commit(&self) -> bool { + self.commit + } + + pub fn will_rollback(&self) -> bool { + !self.commit + } + + pub fn set_commit(&mut self) { + self.commit = true + } + + pub fn set_rollback(&mut self) { + self.commit = false + } + + pub fn commit(mut self) -> SqliteResult<()> { + self.commit_() + } + + fn commit_(&mut self) -> SqliteResult<()> { + self.finished = true; + self.conn.execute_batch(if self.depth == 0 { "COMMIT" } else { "RELEASE sp" }) + } + + pub fn rollback(mut self) -> SqliteResult<()> { + self.rollback_() + } + + fn rollback_(&mut self) -> SqliteResult<()> { + self.finished = true; + self.conn.execute_batch(if self.depth == 0 { "ROLLBACK" } else { "ROLLBACK TO sp" }) + } + + pub fn finish(mut self) -> SqliteResult<()> { + self.finish_() + } + + fn finish_(&mut self) -> SqliteResult<()> { + match (self.finished, self.commit) { + (true, _) => Ok(()), + (false, true) => self.commit_(), + (false, false) => self.rollback_(), + } + } +} + +#[unsafe_destructor] +#[allow(unused_must_use)] +impl<'conn> Drop for SqliteTransaction<'conn> { + fn drop(&mut self) { + self.finish_(); + } +} + +#[cfg(test)] +mod test { + extern crate test; + use SqliteConnection; + + fn checked_memory_handle() -> SqliteConnection { + let db = SqliteConnection::open(":memory:").unwrap(); + db.execute_batch("CREATE TABLE foo (x INTEGER)").unwrap(); + db + } + + #[test] + fn test_drop() { + let db = checked_memory_handle(); + { + let _tx = db.transaction().unwrap(); + db.execute_batch("INSERT INTO foo VALUES(1)").unwrap(); + // default: rollback + } + { + let mut tx = db.transaction().unwrap(); + db.execute_batch("INSERT INTO foo VALUES(2)").unwrap(); + tx.set_commit() + } + { + let _tx = db.transaction().unwrap(); + assert_eq!(2i32, db.query_row("SELECT SUM(x) FROM foo", [], |r| r.unwrap().get(0))); + } + } + + #[test] + fn test_explicit_rollback_commit() { + let db = checked_memory_handle(); + { + let tx = db.transaction().unwrap(); + db.execute_batch("INSERT INTO foo VALUES(1)").unwrap(); + tx.rollback().unwrap(); + } + { + let tx = db.transaction().unwrap(); + db.execute_batch("INSERT INTO foo VALUES(2)").unwrap(); + tx.commit().unwrap(); + } + { + let _tx = db.transaction().unwrap(); + assert_eq!(2i32, db.query_row("SELECT SUM(x) FROM foo", [], |r| r.unwrap().get(0))); + } + } + + #[test] + fn test_savepoint() { + let db = checked_memory_handle(); + { + let mut tx = db.transaction().unwrap(); + db.execute_batch("INSERT INTO foo VALUES(1)").unwrap(); + tx.set_commit(); + { + let mut sp1 = tx.savepoint().unwrap(); + db.execute_batch("INSERT INTO foo VALUES(2)").unwrap(); + sp1.set_commit(); + { + let sp2 = sp1.savepoint().unwrap(); + db.execute_batch("INSERT INTO foo VALUES(4)").unwrap(); + // will rollback sp2 + { + let sp3 = sp2.savepoint().unwrap(); + db.execute_batch("INSERT INTO foo VALUES(8)").unwrap(); + sp3.commit().unwrap(); + // committed sp3, but will be erased by sp2 rollback + } + } + } + } + assert_eq!(3i32, db.query_row("SELECT SUM(x) FROM foo", [], |r| r.unwrap().get(0))); + } + + #[bench] + fn test_no_transaction_insert(bencher: &mut test::Bencher) { + let db = checked_memory_handle(); + + let mut stmt = db.prepare("INSERT INTO foo VALUES(1)").unwrap(); + + bencher.iter(|| { + for _ in range(0i32, 1000) { + stmt.execute([]).unwrap(); + } + }) + } + + #[bench] + fn test_transaction_insert(bencher: &mut test::Bencher) { + let db = checked_memory_handle(); + + let mut stmt = db.prepare("INSERT INTO foo VALUES(1)").unwrap(); + + bencher.iter(|| { + let mut tx = db.transaction().unwrap(); + tx.set_commit(); + for _ in range(0i32, 1000) { + stmt.execute([]).unwrap(); + } + }) + } +} diff --git a/src/types.rs b/src/types.rs new file mode 100644 index 0000000..13e58e9 --- /dev/null +++ b/src/types.rs @@ -0,0 +1,180 @@ +use libc::{c_int, c_double}; +use std::c_str::{CString}; +use std::mem; +use std::vec; +use super::ffi; + +pub trait ToSql { + unsafe fn bind_parameter(&self, stmt: *mut ffi::sqlite3_stmt, col: c_int) -> c_int; +} + +pub trait FromSql { + unsafe fn column_result(stmt: *mut ffi::sqlite3_stmt, col: c_int) -> Self; +} + +macro_rules! raw_to_impl( + ($t:ty, $f:ident) => ( + impl ToSql for $t { + unsafe fn bind_parameter(&self, stmt: *mut ffi::sqlite3_stmt, col: c_int) -> c_int { + ffi::$f(stmt, col, *self) + } + } + ) +) + +raw_to_impl!(c_int, sqlite3_bind_int) +raw_to_impl!(i64, sqlite3_bind_int64) +raw_to_impl!(c_double, sqlite3_bind_double) + +impl<'a> ToSql for &'a str { + unsafe fn bind_parameter(&self, stmt: *mut ffi::sqlite3_stmt, col: c_int) -> c_int { + self.with_c_str(|c_str| { + ffi::sqlite3_bind_text(stmt, col, c_str, -1, Some(ffi::SQLITE_TRANSIENT())) + }) + } +} + +impl ToSql for String { + unsafe fn bind_parameter(&self, stmt: *mut ffi::sqlite3_stmt, col: c_int) -> c_int { + self.as_slice().bind_parameter(stmt, col) + } +} + +impl<'a> ToSql for &'a [u8] { + unsafe fn bind_parameter(&self, stmt: *mut ffi::sqlite3_stmt, col: c_int) -> c_int { + ffi::sqlite3_bind_blob( + stmt, col, mem::transmute(self.as_ptr()), self.len() as c_int, + Some(ffi::SQLITE_TRANSIENT())) + } +} + +impl ToSql for Vec { + unsafe fn bind_parameter(&self, stmt: *mut ffi::sqlite3_stmt, col: c_int) -> c_int { + self.as_slice().bind_parameter(stmt, col) + } +} + +impl ToSql for Option { + unsafe fn bind_parameter(&self, stmt: *mut ffi::sqlite3_stmt, col: c_int) -> c_int { + match *self { + None => ffi::sqlite3_bind_null(stmt, col), + Some(ref t) => t.bind_parameter(stmt, col), + } + } +} + +pub struct Null; + +impl ToSql for Null { + unsafe fn bind_parameter(&self, stmt: *mut ffi::sqlite3_stmt, col: c_int) -> c_int { + ffi::sqlite3_bind_null(stmt, col) + } +} + +macro_rules! raw_from_impl( + ($t:ty, $f:ident) => ( + impl FromSql for $t { + unsafe fn column_result(stmt: *mut ffi::sqlite3_stmt, col: c_int) -> $t { + ffi::$f(stmt, col) + } + } + ) +) + +raw_from_impl!(c_int, sqlite3_column_int) +raw_from_impl!(i64, sqlite3_column_int64) +raw_from_impl!(c_double, sqlite3_column_double) + +impl FromSql for String { + unsafe fn column_result(stmt: *mut ffi::sqlite3_stmt, col: c_int) -> String { + let c_text = ffi::sqlite3_column_text(stmt, col); + if c_text.is_null() { + "".to_string() + } else { + match CString::new(mem::transmute(c_text), false).as_str() { + Some(s) => s.to_string(), + None => "".to_string(), + } + } + } +} + +impl FromSql for Vec { + unsafe fn column_result(stmt: *mut ffi::sqlite3_stmt, col: c_int) -> Vec { + let c_blob = ffi::sqlite3_column_blob(stmt, col); + let len = ffi::sqlite3_column_bytes(stmt, col); + + assert!(len >= 0); let len = len as uint; + + vec::raw::from_buf(mem::transmute(c_blob), len) + } +} + +impl FromSql for Option { + unsafe fn column_result(stmt: *mut ffi::sqlite3_stmt, col: c_int) -> Option { + if ffi::sqlite3_column_type(stmt, col) == ffi::SQLITE_NULL { + None + } else { + Some(FromSql::column_result(stmt, col)) + } + } +} + +#[cfg(test)] +mod test { + use SqliteConnection; + + fn checked_memory_handle() -> SqliteConnection { + let db = SqliteConnection::open(":memory:").unwrap(); + db.execute_batch("CREATE TABLE foo (b BLOB, t TEXT)").unwrap(); + db + } + + #[test] + fn test_blob() { + let db = checked_memory_handle(); + + let v1234 = vec![1u8,2,3,4]; + db.execute("INSERT INTO foo(b) VALUES (?)", &[&v1234]).unwrap(); + + let v: Vec = db.query_row("SELECT b FROM foo", [], |r| r.unwrap().get(0)); + assert_eq!(v, v1234); + } + + #[test] + fn test_str() { + let db = checked_memory_handle(); + + let s = "hello, world!"; + db.execute("INSERT INTO foo(t) VALUES (?)", &[&s.to_string()]).unwrap(); + + let from: String = db.query_row("SELECT t FROM foo", [], |r| r.unwrap().get(0)); + assert_eq!(from.as_slice(), s); + } + + #[test] + fn test_option() { + let db = checked_memory_handle(); + + let s = Some("hello, world!"); + let b = Some(vec![1u8,2,3,4]); + + db.execute("INSERT INTO foo(t) VALUES (?)", &[&s]).unwrap(); + db.execute("INSERT INTO foo(b) VALUES (?)", &[&b]).unwrap(); + + let mut stmt = db.prepare("SELECT t, b FROM foo ORDER BY ROWID ASC").unwrap(); + let mut rows = stmt.query([]).unwrap(); + + let row1 = rows.next().unwrap().unwrap(); + let s1: Option = row1.get(0); + let b1: Option> = row1.get(1); + assert_eq!(s.unwrap(), s1.unwrap().as_slice()); + assert!(b1.is_none()); + + let row2 = rows.next().unwrap().unwrap(); + let s2: Option = row2.get(0); + let b2: Option> = row2.get(1); + assert!(s2.is_none()); + assert_eq!(b, b2); + } +}