diff --git a/Changelog.md b/Changelog.md index b7d2363..40c1642 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,9 @@ * Adds `types::Value` for dynamic column types. * Adds support for user-defined aggregate functions (behind the existing `functions` Cargo feature). * Introduces a `RowIndex` trait allowing columns to be fetched via index (as before) or name (new). +* Introduces `ZeroBlob` type under the `blob` module/feature exposing SQLite's zeroblob API. +* Adds CI testing for Windows via AppVeyor. +* Fixes a warning building libsqlite3-sys under Rust 1.6. # Version 0.6.0 (2015-12-17) diff --git a/README.md b/README.md index a813174..bc56bc3 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Rusqlite -[](https://travis-ci.org/jgallagher/rusqlite) +[](https://travis-ci.org/jgallagher/rusqlite) +[](https://ci.appveyor.com/project/jgallagher/rusqlite) Rusqlite is an ergonomic wrapper for using SQLite from Rust. It attempts to expose an interface similar to [rust-postgres](https://github.com/sfackler/rust-postgres). View the full diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..9fb260e --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,22 @@ +environment: + TARGET: 1.6.0-x86_64-pc-windows-gnu + MSYS2_BITS: 64 +install: + - ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-${env:TARGET}.exe" + - rust-%TARGET%.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust" + - SET PATH=%PATH%;C:\Program Files (x86)\Rust\bin + - if defined MSYS2_BITS set PATH=%PATH%;C:\msys64\mingw%MSYS2_BITS%\bin + - rustc -V + - cargo -V + - ps: Start-FileDownload 'http://sqlite.org/2016/sqlite-dll-win64-x64-3100200.zip' + - cmd: 7z e sqlite-dll-win64-x64-3100200.zip -y > nul + - SET SQLITE3_LIB_DIR=%APPVEYOR_BUILD_FOLDER% + +build: false + +test_script: + - cargo test --lib --verbose + - cargo test --lib --features "backup blob functions load_extension trace" + +cache: + - C:\Users\appveyor\.cargo diff --git a/libsqlite3-sys/src/bindgen.rs b/libsqlite3-sys/src/bindgen.rs index a60be96..1b60335 100644 --- a/libsqlite3-sys/src/bindgen.rs +++ b/libsqlite3-sys/src/bindgen.rs @@ -1,4 +1,4 @@ -#![allow(raw_pointer_derive, non_snake_case, non_camel_case_types)] +#![allow(non_snake_case, non_camel_case_types)] /* automatically generated by rust-bindgen */ pub type va_list = __builtin_va_list; diff --git a/src/blob.rs b/src/blob.rs index a13f3e7..f8b1710 100644 --- a/src/blob.rs +++ b/src/blob.rs @@ -17,6 +17,7 @@ //! extern crate rusqlite; //! //! use rusqlite::{Connection, DatabaseName}; +//! use rusqlite::blob::ZeroBlob; //! use std::io::{Read, Write, Seek, SeekFrom}; //! //! fn main() { @@ -38,7 +39,7 @@ //! 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(); +//! 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(); @@ -51,8 +52,10 @@ use std::io; use std::cmp::min; use std::mem; use std::ptr; +use libc::c_int; use super::ffi; +use super::types::ToSql; use {Result, Connection, DatabaseName}; /// Handle to an open BLOB. @@ -232,6 +235,19 @@ impl<'conn> Drop for Blob<'conn> { } } +/// BLOB of length N that is filled with zeroes. +/// Zeroblobs are intended to serve as placeholders for BLOBs whose content is later written using incremental BLOB I/O routines. +/// A negative value for the zeroblob results in a zero-length BLOB. +#[derive(Copy,Clone)] +pub struct ZeroBlob(pub i32); + +impl ToSql for ZeroBlob { + unsafe fn bind_parameter(&self, stmt: *mut ffi::sqlite3_stmt, col: c_int) -> c_int { + let ZeroBlob(length) = *self; + ffi::sqlite3_bind_zeroblob(stmt, col, length) + } +} + #[cfg(test)] mod test { use std::io::{BufReader, BufRead, BufWriter, Read, Write, Seek, SeekFrom}; diff --git a/src/functions.rs b/src/functions.rs index e4d4f9c..248cea5 100644 --- a/src/functions.rs +++ b/src/functions.rs @@ -509,9 +509,10 @@ impl InnerConnection { where D: Aggregate<A, T>, T: ToResult { - unsafe fn aggregate_context<A>(ctx: *mut sqlite3_context) -> Option<*mut *mut A> { - let pac = ffi::sqlite3_aggregate_context(ctx, ::std::mem::size_of::<*mut A>() as c_int) - as *mut *mut A; + unsafe fn aggregate_context<A>(ctx: *mut sqlite3_context, + bytes: usize) + -> Option<*mut *mut A> { + let pac = ffi::sqlite3_aggregate_context(ctx, bytes as c_int) as *mut *mut A; if pac.is_null() { return None; } @@ -545,7 +546,7 @@ impl InnerConnection { assert!(!boxed_aggr.is_null(), "Internal error - null aggregate pointer"); - let pac = match aggregate_context(ctx) { + let pac = match aggregate_context(ctx, ::std::mem::size_of::<*mut A>()) { Some(pac) => pac, None => { ffi::sqlite3_result_error_nomem(ctx); @@ -576,19 +577,18 @@ impl InnerConnection { assert!(!boxed_aggr.is_null(), "Internal error - null aggregate pointer"); - let pac = match aggregate_context(ctx) { - Some(pac) => pac, - None => { - ffi::sqlite3_result_error_nomem(ctx); - return; + // Within the xFinal callback, it is customary to set N=0 in calls to + // sqlite3_aggregate_context(C,N) so that no pointless memory allocations occur. + let a: Option<A> = match aggregate_context(ctx, 0) { + Some(pac) => { + if (*pac).is_null() { + None + } else { + let a = Box::from_raw(*pac); + Some(*a) + } } - }; - - let a: Option<A> = if (*pac).is_null() { - None - } else { - let a = Box::from_raw(*pac); - Some(*a) + None => None, }; match (*boxed_aggr).finalize(a) {