diff --git a/Cargo.toml b/Cargo.toml index 4e78840..d077e66 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ name = "rusqlite" [features] load_extension = ["libsqlite3-sys/load_extension"] backup = [] +blob = [] cache = ["lru-cache"] functions = [] trace = [] diff --git a/Changelog.md b/Changelog.md index 00f2b20..e47480a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -18,6 +18,7 @@ The old, prefixed names are still exported but are deprecated. * Adds a variety of `..._named` methods for executing queries using named placeholder parameters. * Adds `backup` feature that exposes SQLite's online backup API. +* Adds `blob` feature that exposes SQLite's Incremental I/O for BLOB API. * Adds `functions` feature that allows user-defined scalar functions to be added to open `SqliteConnection`s. diff --git a/README.md b/README.md index 30e91c7..a813174 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,8 @@ features](http://doc.crates.io/manifest.html#the-features-section). They are: allows you to load Rust closures into SQLite connections for use in queries. * [`trace`](http://jgallagher.github.io/rusqlite/rusqlite/trace/index.html) allows hooks into SQLite's tracing and profiling APIs. +* [`blob`](http://jgallagher.github.io/rusqlite/rusqlite/blob/index.html) + gives `std::io::{Read, Write, Seek}` access to SQL BLOBs. ### Design of Rows and Row diff --git a/libsqlite3-sys/src/bindgen.rs b/libsqlite3-sys/src/bindgen.rs index b2cf23e..a60be96 100644 --- a/libsqlite3-sys/src/bindgen.rs +++ b/libsqlite3-sys/src/bindgen.rs @@ -10,10 +10,10 @@ 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)] @@ -32,83 +32,110 @@ pub type sqlite3_io_methods = Struct_sqlite3_io_methods; #[derive(Copy)] 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 xWrite: ::std::option::Option ::libc::c_int>, - pub xUnfetch: ::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>, } impl ::std::clone::Clone for Struct_sqlite3_io_methods { @@ -130,104 +157,119 @@ pub struct Struct_sqlite3_vfs { 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 xDelete: ::std::option::Option ::libc::c_int>, - pub xGetSystemCall: ::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 sqlite3_syscall_ptr>, - pub xNextSystemCall: ::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>, } impl ::std::clone::Clone for Struct_sqlite3_vfs { @@ -242,20 +284,24 @@ pub type sqlite3_mem_methods = Struct_sqlite3_mem_methods; 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, } @@ -272,7 +318,8 @@ 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 ()>; + ::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; @@ -281,120 +328,138 @@ pub type sqlite3_module = Struct_sqlite3_module; #[derive(Copy)] 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 - ()>, - ppArg: - *mut *mut ::libc::c_void) + pub xFindFunction: ::std::option::Option + ()>, + ppArg: + *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>, } impl ::std::clone::Clone for Struct_sqlite3_module { @@ -496,23 +561,23 @@ pub struct Struct_sqlite3_mutex_methods { 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>, } impl ::std::clone::Clone for Struct_sqlite3_mutex_methods { @@ -542,48 +607,58 @@ pub type sqlite3_pcache_methods2 = Struct_sqlite3_pcache_methods2; 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 ()>, } impl ::std::clone::Clone for Struct_sqlite3_pcache_methods2 { @@ -597,42 +672,54 @@ pub type sqlite3_pcache_methods = Struct_sqlite3_pcache_methods; #[derive(Copy)] 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 ()>, } impl ::std::clone::Clone for Struct_sqlite3_pcache_methods { @@ -653,8 +740,8 @@ pub struct Struct_sqlite3_rtree_geometry { pub nParam: ::libc::c_int, pub aParam: *mut sqlite3_rtree_dbl, pub pUser: *mut ::libc::c_void, - pub xDelUser: ::std::option::Option ()>, } impl ::std::clone::Clone for Struct_sqlite3_rtree_geometry { @@ -670,8 +757,8 @@ pub struct Struct_sqlite3_rtree_query_info { 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, @@ -706,11 +793,13 @@ impl ::std::clone::Clone for Struct___va_list_tag { impl ::std::default::Default for Struct___va_list_tag { fn default() -> Self { unsafe { ::std::mem::zeroed() } } } +#[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; } +#[link(name = "sqlite3")] extern "C" { pub fn sqlite3_libversion() -> *const ::libc::c_char; pub fn sqlite3_sourceid() -> *const ::libc::c_char; @@ -724,14 +813,14 @@ extern "C" { 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; @@ -753,10 +842,10 @@ extern "C" { 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; @@ -780,53 +869,57 @@ extern "C" { arg3: *const ::libc::c_char, arg4: va_list) -> *mut ::libc::c_char; pub fn sqlite3_malloc(arg1: ::libc::c_int) -> *mut ::libc::c_void; + pub fn sqlite3_malloc64(arg1: sqlite3_uint64) -> *mut ::libc::c_void; pub fn sqlite3_realloc(arg1: *mut ::libc::c_void, arg2: ::libc::c_int) -> *mut ::libc::c_void; + pub fn sqlite3_realloc64(arg1: *mut ::libc::c_void, arg2: sqlite3_uint64) + -> *mut ::libc::c_void; pub fn sqlite3_free(arg1: *mut ::libc::c_void) -> (); + pub fn sqlite3_msize(arg1: *mut ::libc::c_void) -> sqlite3_uint64; 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) -> (); @@ -879,10 +972,18 @@ extern "C" { 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_blob64(arg1: *mut sqlite3_stmt, arg2: ::libc::c_int, + arg3: *const ::libc::c_void, + arg4: sqlite3_uint64, + arg5: + ::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, @@ -892,20 +993,28 @@ extern "C" { 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_text64(arg1: *mut sqlite3_stmt, arg2: ::libc::c_int, + arg3: *const ::libc::c_char, + arg4: sqlite3_uint64, + arg5: + ::std::option::Option ()>, + encoding: ::libc::c_uchar) -> ::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, @@ -978,24 +1087,24 @@ extern "C" { 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, @@ -1004,24 +1113,24 @@ extern "C" { 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, @@ -1030,28 +1139,28 @@ extern "C" { 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) @@ -1063,12 +1172,12 @@ extern "C" { 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; @@ -1102,16 +1211,23 @@ extern "C" { 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_blob64(arg1: *mut sqlite3_context, + arg2: *const ::libc::c_void, + arg3: sqlite3_uint64, + 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, @@ -1133,30 +1249,38 @@ extern "C" { arg2: *const ::libc::c_char, arg3: ::libc::c_int, arg4: - ::std::option::Option ()>) -> (); + pub fn sqlite3_result_text64(arg1: *mut sqlite3_context, + arg2: *const ::libc::c_char, + arg3: sqlite3_uint64, + arg4: + ::std::option::Option ()>, + encoding: ::libc::c_uchar) -> (); 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, @@ -1168,16 +1292,16 @@ extern "C" { eTextRep: ::libc::c_int, pArg: *mut ::libc::c_void, xCompare: - ::std::option::Option ::libc::c_int>) -> ::libc::c_int; @@ -1186,21 +1310,21 @@ extern "C" { 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, @@ -1208,43 +1332,43 @@ extern "C" { 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; @@ -1260,31 +1384,31 @@ extern "C" { -> *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; @@ -1332,8 +1456,8 @@ extern "C" { p: *const sqlite3_module, pClientData: *mut ::libc::c_void, xDestroy: - ::std::option::Option ()>) -> ::libc::c_int; pub fn sqlite3_declare_vtab(arg1: *mut sqlite3, @@ -1377,6 +1501,9 @@ extern "C" { 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_status64(op: ::libc::c_int, pCurrent: *mut sqlite3_int64, + pHighwater: *mut sqlite3_int64, + 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, @@ -1395,10 +1522,10 @@ extern "C" { 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; @@ -1413,14 +1540,14 @@ extern "C" { 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) @@ -1437,17 +1564,23 @@ extern "C" { 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_stmt_scanstatus(pStmt: *mut sqlite3_stmt, + idx: ::libc::c_int, + iScanStatusOp: ::libc::c_int, + pOut: *mut ::libc::c_void) + -> ::libc::c_int; + pub fn sqlite3_stmt_scanstatus_reset(arg1: *mut sqlite3_stmt) -> (); 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) @@ -1455,14 +1588,14 @@ extern "C" { 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/libsqlite3-sys/src/lib.rs b/libsqlite3-sys/src/lib.rs index 0eeacfa..ca49efb 100644 --- a/libsqlite3-sys/src/lib.rs +++ b/libsqlite3-sys/src/lib.rs @@ -1,3 +1,5 @@ +// bindgen.rs was created with bindgen 0.15.0 against sqlite3 3.8.10 + #![allow(non_snake_case)] extern crate libc; diff --git a/src/blob.rs b/src/blob.rs new file mode 100644 index 0000000..a13f3e7 --- /dev/null +++ b/src/blob.rs @@ -0,0 +1,362 @@ +//! Incremental BLOB I/O. +//! +//! Note that SQLite does not provide API-level access to change the size of a BLOB; that must +//! be performed through SQL statements. +//! +//! `Blob` conforms to `std::io::Read`, `std::io::Write`, and `std::io::Seek`, so it plays +//! nicely with other types that build on these (such as `std::io::BufReader` and +//! `std::io::BufWriter`). However, you must be careful with the size of the blob. For example, +//! when using a `BufWriter`, the `BufWriter` will accept more data than the `Blob` will allow, +//! so make sure to call `flush` and check for errors. (See the unit tests in this module for +//! an example.) +//! +//! ## Example +//! +//! ```rust +//! extern crate libsqlite3_sys; +//! extern crate rusqlite; +//! +//! use rusqlite::{Connection, DatabaseName}; +//! use std::io::{Read, Write, Seek, SeekFrom}; +//! +//! fn main() { +//! let db = Connection::open_in_memory().unwrap(); +//! db.execute_batch("CREATE TABLE test (content BLOB);").unwrap(); +//! db.execute("INSERT INTO test (content) VALUES (ZEROBLOB(10))", &[]).unwrap(); +//! +//! let rowid = db.last_insert_rowid(); +//! let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false).unwrap(); +//! +//! // Make sure to test that the number of bytes written matches what you expect; +//! // if you try to write too much, the data will be truncated to the size of the BLOB. +//! let bytes_written = blob.write(b"01234567").unwrap(); +//! assert_eq!(bytes_written, 8); +//! +//! // Same guidance - make sure you check the number of bytes read! +//! blob.seek(SeekFrom::Start(0)).unwrap(); +//! let mut buf = [0u8; 20]; +//! let bytes_read = blob.read(&mut buf[..]).unwrap(); +//! assert_eq!(bytes_read, 10); // note we read 10 bytes because the blob has size 10 +//! +//! db.execute("INSERT INTO test (content) VALUES (ZEROBLOB(64))", &[]).unwrap(); +//! +//! // given a new row ID, we can reopen the blob on that row +//! let rowid = db.last_insert_rowid(); +//! blob.reopen(rowid).unwrap(); +//! +//! assert_eq!(blob.size(), 64); +//! } +//! ``` +use std::io; +use std::cmp::min; +use std::mem; +use std::ptr; + +use super::ffi; +use {Result, Connection, DatabaseName}; + +/// Handle to an open BLOB. +pub struct Blob<'conn> { + conn: &'conn Connection, + blob: *mut ffi::sqlite3_blob, + pos: i32, +} + +impl Connection { + /// Open a handle to the BLOB located in `row`, `column`, `table` in database `db`. + /// + /// # Failure + /// + /// Will return `Err` if `db`/`table`/`column` cannot be converted to a C-compatible string + /// or if the underlying SQLite BLOB open call fails. + pub fn blob_open<'a>(&'a self, + db: DatabaseName, + table: &str, + column: &str, + row: i64, + read_only: bool) + -> Result> { + let mut c = self.db.borrow_mut(); + let mut blob = ptr::null_mut(); + let db = try!(db.to_cstring()); + let table = try!(super::str_to_cstring(table)); + let column = try!(super::str_to_cstring(column)); + let rc = unsafe { + ffi::sqlite3_blob_open(c.db(), + db.as_ptr(), + table.as_ptr(), + column.as_ptr(), + row, + if read_only { + 0 + } else { + 1 + }, + &mut blob) + }; + c.decode_result(rc).map(|_| { + Blob { + conn: self, + blob: blob, + pos: 0, + } + }) + } +} + +impl<'conn> Blob<'conn> { + /// Move a BLOB handle to a new row. + /// + /// # Failure + /// + /// Will return `Err` if the underlying SQLite BLOB reopen call fails. + pub fn reopen(&mut self, row: i64) -> Result<()> { + let rc = unsafe { ffi::sqlite3_blob_reopen(self.blob, row) }; + if rc != ffi::SQLITE_OK { + return self.conn.decode_result(rc); + } + self.pos = 0; + Ok(()) + } + + /// Return the size in bytes of the BLOB. + pub fn size(&self) -> i32 { + unsafe { ffi::sqlite3_blob_bytes(self.blob) } + } + + /// Close a BLOB handle. + /// + /// Calling `close` explicitly is not required (the BLOB will be closed when the + /// `Blob` is dropped), but it is available so you can get any errors that occur. + /// + /// # Failure + /// + /// Will return `Err` if the underlying SQLite close call fails. + pub fn close(mut self) -> Result<()> { + self.close_() + } + + fn close_(&mut self) -> Result<()> { + let rc = unsafe { ffi::sqlite3_blob_close(self.blob) }; + self.blob = ptr::null_mut(); + self.conn.decode_result(rc) + } +} + +impl<'conn> io::Read for Blob<'conn> { + /// Read data from a BLOB incrementally. Will return Ok(0) if the end of the blob + /// has been reached. + /// + /// # Failure + /// + /// Will return `Err` if the underlying SQLite read call fails. + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let max_allowed_len = (self.size() - self.pos) as usize; + let n = min(buf.len(), max_allowed_len) as i32; + if n <= 0 { + return Ok(0); + } + let rc = unsafe { + ffi::sqlite3_blob_read(self.blob, mem::transmute(buf.as_ptr()), n, self.pos) + }; + self.conn + .decode_result(rc) + .map(|_| { + self.pos += n; + n as usize + }) + .map_err(|err| io::Error::new(io::ErrorKind::Other, err)) + } +} + +impl<'conn> io::Write for Blob<'conn> { + /// Write data into a BLOB incrementally. Will return `Ok(0)` if the end of the blob + /// has been reached; consider using `Write::write_all(buf)` if you want to get an + /// error if the entirety of the buffer cannot be written. + /// + /// This function may only modify the contents of the BLOB; it is not possible to increase + /// the size of a BLOB using this API. + /// + /// # Failure + /// + /// Will return `Err` if the underlying SQLite write call fails. + fn write(&mut self, buf: &[u8]) -> io::Result { + let max_allowed_len = (self.size() - self.pos) as usize; + let n = min(buf.len(), max_allowed_len) as i32; + if n <= 0 { + return Ok(0); + } + let rc = unsafe { + ffi::sqlite3_blob_write(self.blob, mem::transmute(buf.as_ptr()), n, self.pos) + }; + self.conn + .decode_result(rc) + .map(|_| { + self.pos += n; + n as usize + }) + .map_err(|err| io::Error::new(io::ErrorKind::Other, err)) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +impl<'conn> io::Seek for Blob<'conn> { + /// Seek to an offset, in bytes, in BLOB. + fn seek(&mut self, pos: io::SeekFrom) -> io::Result { + let pos = match pos { + io::SeekFrom::Start(offset) => offset as i64, + io::SeekFrom::Current(offset) => self.pos as i64 + offset, + io::SeekFrom::End(offset) => self.size() as i64 + offset, + }; + + if pos < 0 { + Err(io::Error::new(io::ErrorKind::InvalidInput, + "invalid seek to negative position")) + } else if pos > self.size() as i64 { + Err(io::Error::new(io::ErrorKind::InvalidInput, + "invalid seek to position past end of blob")) + } else { + self.pos = pos as i32; + Ok(pos as u64) + } + } +} + +#[allow(unused_must_use)] +impl<'conn> Drop for Blob<'conn> { + fn drop(&mut self) { + self.close_(); + } +} + +#[cfg(test)] +mod test { + use std::io::{BufReader, BufRead, BufWriter, Read, Write, Seek, SeekFrom}; + use {Connection, DatabaseName, Result}; + + #[cfg_attr(rustfmt, rustfmt_skip)] + fn db_with_test_blob() -> Result<(Connection, i64)> { + let db = try!(Connection::open_in_memory()); + let sql = "BEGIN; + CREATE TABLE test (content BLOB); + INSERT INTO test VALUES (ZEROBLOB(10)); + END;"; + try!(db.execute_batch(sql)); + let rowid = db.last_insert_rowid(); + Ok((db, rowid)) + } + + #[test] + fn test_blob() { + let (db, rowid) = db_with_test_blob().unwrap(); + + let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false).unwrap(); + assert_eq!(4, blob.write(b"Clob").unwrap()); + assert_eq!(6, blob.write(b"567890xxxxxx").unwrap()); // cannot write past 10 + assert_eq!(0, blob.write(b"5678").unwrap()); // still cannot write past 10 + + blob.reopen(rowid).unwrap(); + blob.close().unwrap(); + + blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, true).unwrap(); + let mut bytes = [0u8; 5]; + assert_eq!(5, blob.read(&mut bytes[..]).unwrap()); + assert_eq!(&bytes, b"Clob5"); + assert_eq!(5, blob.read(&mut bytes[..]).unwrap()); + assert_eq!(&bytes, b"67890"); + assert_eq!(0, blob.read(&mut bytes[..]).unwrap()); + + blob.seek(SeekFrom::Start(2)).unwrap(); + assert_eq!(5, blob.read(&mut bytes[..]).unwrap()); + assert_eq!(&bytes, b"ob567"); + + // only first 4 bytes of `bytes` should be read into + blob.seek(SeekFrom::Current(-1)).unwrap(); + assert_eq!(4, blob.read(&mut bytes[..]).unwrap()); + assert_eq!(&bytes, b"78907"); + + blob.seek(SeekFrom::End(-6)).unwrap(); + assert_eq!(5, blob.read(&mut bytes[..]).unwrap()); + assert_eq!(&bytes, b"56789"); + + blob.reopen(rowid).unwrap(); + assert_eq!(5, blob.read(&mut bytes[..]).unwrap()); + assert_eq!(&bytes, b"Clob5"); + + // should not be able to seek negative or past end + assert!(blob.seek(SeekFrom::Current(-20)).is_err()); + assert!(blob.seek(SeekFrom::End(0)).is_ok()); + assert!(blob.seek(SeekFrom::Current(1)).is_err()); + + // write_all should detect when we return Ok(0) because there is no space left, + // and return a write error + blob.reopen(rowid).unwrap(); + assert!(blob.write_all(b"0123456789x").is_err()); + } + + #[test] + fn test_blob_in_bufreader() { + let (db, rowid) = db_with_test_blob().unwrap(); + + let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false).unwrap(); + assert_eq!(8, blob.write(b"one\ntwo\n").unwrap()); + + blob.reopen(rowid).unwrap(); + let mut reader = BufReader::new(blob); + + let mut line = String::new(); + assert_eq!(4, reader.read_line(&mut line).unwrap()); + assert_eq!("one\n", line); + + line.truncate(0); + assert_eq!(4, reader.read_line(&mut line).unwrap()); + assert_eq!("two\n", line); + + line.truncate(0); + assert_eq!(2, reader.read_line(&mut line).unwrap()); + assert_eq!("\0\0", line); + } + + #[test] + fn test_blob_in_bufwriter() { + let (db, rowid) = db_with_test_blob().unwrap(); + + { + let blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false).unwrap(); + let mut writer = BufWriter::new(blob); + + // trying to write too much and then flush should fail + assert_eq!(8, writer.write(b"01234567").unwrap()); + assert_eq!(8, writer.write(b"01234567").unwrap()); + assert!(writer.flush().is_err()); + } + + { + // ... but it should've written the first 10 bytes + let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false).unwrap(); + let mut bytes = [0u8; 10]; + assert_eq!(10, blob.read(&mut bytes[..]).unwrap()); + assert_eq!(b"0123456701", &bytes); + } + + { + let blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false).unwrap(); + let mut writer = BufWriter::new(blob); + + // trying to write_all too much should fail + writer.write_all(b"aaaaaaaaaabbbbb").unwrap(); + assert!(writer.flush().is_err()); + } + + { + // ... but it should've written the first 10 bytes + let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false).unwrap(); + let mut bytes = [0u8; 10]; + assert_eq!(10, blob.read(&mut bytes[..]).unwrap()); + assert_eq!(b"aaaaaaaaaa", &bytes); + } + } +} diff --git a/src/functions.rs b/src/functions.rs index 502b83a..196e639 100644 --- a/src/functions.rs +++ b/src/functions.rs @@ -407,26 +407,24 @@ impl InnerConnection { where F: FnMut(&Context) -> Result, T: ToResult { - extern "C" fn call_boxed_closure(ctx: *mut sqlite3_context, - argc: c_int, - argv: *mut *mut sqlite3_value) + unsafe extern "C" fn call_boxed_closure(ctx: *mut sqlite3_context, + argc: c_int, + argv: *mut *mut sqlite3_value) where F: FnMut(&Context) -> Result, T: ToResult { - unsafe { - let ctx = Context { - ctx: ctx, - args: slice::from_raw_parts(argv, argc as usize), - }; - let boxed_f: *mut F = mem::transmute(ffi::sqlite3_user_data(ctx.ctx)); - assert!(!boxed_f.is_null(), "Internal error - null function pointer"); - match (*boxed_f)(&ctx) { - Ok(r) => r.set_result(ctx.ctx), - Err(e) => { - ffi::sqlite3_result_error_code(ctx.ctx, e.code); - if let Ok(cstr) = str_to_cstring(&e.message) { - ffi::sqlite3_result_error(ctx.ctx, cstr.as_ptr(), -1); - } + let ctx = Context { + ctx: ctx, + args: slice::from_raw_parts(argv, argc as usize), + }; + let boxed_f: *mut F = mem::transmute(ffi::sqlite3_user_data(ctx.ctx)); + assert!(!boxed_f.is_null(), "Internal error - null function pointer"); + match (*boxed_f)(&ctx) { + Ok(r) => r.set_result(ctx.ctx), + Err(e) => { + ffi::sqlite3_result_error_code(ctx.ctx, e.code); + if let Ok(cstr) = str_to_cstring(&e.message) { + ffi::sqlite3_result_error(ctx.ctx, cstr.as_ptr(), -1); } } } diff --git a/src/lib.rs b/src/lib.rs index 976c24f..81ff23c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -70,7 +70,7 @@ use std::cell::{RefCell, Cell}; use std::ffi::{CStr, CString}; use std::result; use std::str; -use libc::{c_int, c_void, c_char}; +use libc::{c_int, c_char}; use types::{ToSql, FromSql}; @@ -87,6 +87,7 @@ mod named_params; #[cfg(feature = "backup")]pub mod backup; #[cfg(feature = "cache")] pub mod cache; #[cfg(feature = "functions")] pub mod functions; +#[cfg(feature = "blob")] pub mod blob; /// Old name for `Result`. `SqliteResult` is deprecated. pub type SqliteResult = Result; @@ -171,9 +172,9 @@ pub enum DatabaseName<'a> { Attached(&'a str), } -// Currently DatabaseName is only used by the backup mod, so hide this (private) +// Currently DatabaseName is only used by the backup and blob mods, so hide this (private) // impl to avoid dead code warnings. -#[cfg(feature = "backup")] +#[cfg(any(feature = "backup", feature = "blob"))] impl<'a> DatabaseName<'a> { fn to_cstring(self) -> Result { use self::DatabaseName::{Main, Temp, Attached}; @@ -632,22 +633,6 @@ impl InnerConnection { } } - unsafe fn decode_result_with_errmsg(&self, - code: c_int, - errmsg: *mut c_char) - -> Result<()> { - if code == ffi::SQLITE_OK { - Ok(()) - } else { - let message = errmsg_to_string(&*errmsg); - ffi::sqlite3_free(errmsg as *mut c_void); - Err(Error { - code: code, - message: message, - }) - } - } - fn close(&mut self) -> Result<()> { unsafe { let r = ffi::sqlite3_close(self.db()); @@ -659,13 +644,12 @@ impl InnerConnection { fn execute_batch(&mut self, sql: &str) -> Result<()> { let c_sql = try!(str_to_cstring(sql)); unsafe { - let mut errmsg: *mut c_char = mem::uninitialized(); let r = ffi::sqlite3_exec(self.db(), c_sql.as_ptr(), None, ptr::null_mut(), - &mut errmsg); - self.decode_result_with_errmsg(r, errmsg) + ptr::null_mut()); + self.decode_result(r) } } @@ -689,7 +673,16 @@ impl InnerConnection { } else { ffi::sqlite3_load_extension(self.db, dylib_str.as_ptr(), ptr::null(), &mut errmsg) }; - self.decode_result_with_errmsg(r, errmsg) + if r == ffi::SQLITE_OK { + Ok(()) + } else { + let message = errmsg_to_string(&*errmsg); + ffi::sqlite3_free(errmsg as *mut libc::c_void); + Err(Error { + code: r, + message: message, + }) + } } } diff --git a/src/trace.rs b/src/trace.rs index eac7fc3..0c49ce5 100644 --- a/src/trace.rs +++ b/src/trace.rs @@ -67,9 +67,9 @@ impl Connection { /// There can only be a single tracer defined for each database connection. /// Setting a new tracer clears the old one. pub fn trace(&mut self, trace_fn: Option) { - extern "C" fn trace_callback(p_arg: *mut c_void, z_sql: *const c_char) { - let trace_fn: fn(&str) = unsafe { mem::transmute(p_arg) }; - let c_slice = unsafe { CStr::from_ptr(z_sql).to_bytes() }; + unsafe extern "C" fn trace_callback(p_arg: *mut c_void, z_sql: *const c_char) { + let trace_fn: fn(&str) = mem::transmute(p_arg); + let c_slice = CStr::from_ptr(z_sql).to_bytes(); if let Ok(s) = str::from_utf8(c_slice) { trace_fn(s); } @@ -91,11 +91,11 @@ impl Connection { /// There can only be a single profiler defined for each database connection. /// Setting a new profiler clears the old one. pub fn profile(&mut self, profile_fn: Option) { - extern "C" fn profile_callback(p_arg: *mut c_void, - z_sql: *const c_char, - nanoseconds: u64) { - let profile_fn: fn(&str, Duration) = unsafe { mem::transmute(p_arg) }; - let c_slice = unsafe { CStr::from_ptr(z_sql).to_bytes() }; + unsafe extern "C" fn profile_callback(p_arg: *mut c_void, + z_sql: *const c_char, + nanoseconds: u64) { + let profile_fn: fn(&str, Duration) = mem::transmute(p_arg); + let c_slice = CStr::from_ptr(z_sql).to_bytes(); if let Ok(s) = str::from_utf8(c_slice) { const NANOS_PER_SEC: u64 = 1_000_000_000;