Merge remote-tracking branch 'jgallagher/master' into vtab

This commit is contained in:
gwenn 2016-12-31 09:15:26 +01:00
commit 46df930881
31 changed files with 209042 additions and 848 deletions

View File

@ -3,6 +3,7 @@ sudo: false
script: script:
- cargo build - cargo build
- cargo build --features bundled
- cargo test - cargo test
- cargo test --features backup - cargo test --features backup
- cargo test --features blob - cargo test --features blob
@ -11,5 +12,8 @@ script:
- cargo test --features functions - cargo test --features functions
- cargo test --features chrono - cargo test --features chrono
- cargo test --features serde_json - cargo test --features serde_json
- cargo test --features bundled
- cargo test --features "backup blob chrono functions load_extension serde_json trace"
- cargo test --features "backup blob chrono functions load_extension serde_json trace bundled"
- cargo test --features "csvtab functions vtab" - cargo test --features "csvtab functions vtab"
- cargo test --features "backup blob chrono csvtab functions load_extension serde_json trace vtab" - cargo test --features "backup blob chrono csvtab functions load_extension serde_json trace vtab"

View File

@ -14,3 +14,5 @@ rusqlite contributors
* [Ronald Kinard](https://github.com/Furyhunter) * [Ronald Kinard](https://github.com/Furyhunter)
* [maciejkula](https://github.com/maciejkula) * [maciejkula](https://github.com/maciejkula)
* [Xidorn Quan](https://github.com/upsuper) * [Xidorn Quan](https://github.com/upsuper)
* [Chip Collier](https://github.com/photex)
* [Omar Ferrer](https://github.com/chamakits)

View File

@ -1,6 +1,6 @@
[package] [package]
name = "rusqlite" name = "rusqlite"
version = "0.7.3" version = "0.8.0"
authors = ["John Gallagher <jgallagher@bignerdranch.com>"] authors = ["John Gallagher <jgallagher@bignerdranch.com>"]
description = "Ergonomic wrapper for SQLite" description = "Ergonomic wrapper for SQLite"
repository = "https://github.com/jgallagher/rusqlite" repository = "https://github.com/jgallagher/rusqlite"
@ -18,6 +18,7 @@ backup = []
blob = [] blob = []
functions = [] functions = []
trace = [] trace = []
bundled = ["libsqlite3-sys/bundled"]
vtab = [] vtab = []
csvtab = ["csv"] csvtab = ["csv"]
@ -36,7 +37,7 @@ regex = "0.1"
[dependencies.libsqlite3-sys] [dependencies.libsqlite3-sys]
path = "libsqlite3-sys" path = "libsqlite3-sys"
version = "0.5.0" version = "0.6.0"
[dependencies.csv] [dependencies.csv]
version = "0.14" version = "0.14"

View File

@ -1,10 +1,24 @@
# Version UPCOMING (...) # Version 0.8.0 (2016-12-31)
* BREAKING CHANGE: The `FromSql` trait has been redesigned. It now requires a single, safe * BREAKING CHANGE: The `FromSql` trait has been redesigned. It now requires a single, safe
method instead of the previous definition which required implementing one or two unsafe method instead of the previous definition which required implementing one or two unsafe
methods. methods.
* BREAKING CHANGE: The `ToSql` trait has been redesigned. It can now be implemented without
`unsafe`, and implementors can choose to return either borrowed or owned results.
* BREAKING CHANGE: The closure passed to `query_row`, `query_row_and_then`, `query_row_safe`,
and `query_row_named` now expects a `&Row` instead of a `Row`. The vast majority of calls
to these functions will probably not need to change; see
https://github.com/jgallagher/rusqlite/pull/184.
* BREAKING CHANGE: A few cases of the `Error` enum have sprouted additional information
(e.g., `FromSqlConversionFailure` now also includes the column index and the type returned
by SQLite).
* Added `#[deprecated(since = "...", note = "...")]` flags (new in Rust 1.9 for libraries) to * Added `#[deprecated(since = "...", note = "...")]` flags (new in Rust 1.9 for libraries) to
all deprecated APIs. all deprecated APIs.
* Added `query_row` convenience function to `Statement`.
* Added `bundled` feature which will build SQLite from source instead of attempting to link
against a SQLite that already exists on the system.
* Fixed a bug where using cached prepared statements resulted in attempting to close a connection
failing with `DatabaseBusy`; see https://github.com/jgallagher/rusqlite/issues/186.
# Version 0.7.3 (2016-06-01) # Version 0.7.3 (2016-06-01)

View File

@ -1,7 +1,7 @@
# Rusqlite # Rusqlite
[![Travis Build Status](https://api.travis-ci.org/jgallagher/rusqlite.svg?branch=master)](https://travis-ci.org/jgallagher/rusqlite) [![Travis Build Status](https://api.travis-ci.org/jgallagher/rusqlite.svg?branch=master)](https://travis-ci.org/jgallagher/rusqlite)
[![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/jgallagher/rusqlite?branch=master&svg=true)](https://ci.appveyor.com/project/jgallagher/rusqlite) [![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/jgallagher/rusqlite?branch=master&svg=true)](https://ci.appveyor.com/project/jgallagher/rusqlite) [![Latest Version](https://img.shields.io/crates/v/rusqlite.svg)](https://crates.io/crates/rusqlite)
Rusqlite is an ergonomic wrapper for using SQLite from Rust. It attempts to expose 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 an interface similar to [rust-postgres](https://github.com/sfackler/rust-postgres). View the full
@ -38,11 +38,11 @@ fn main() {
data: None data: None
}; };
conn.execute("INSERT INTO person (name, time_created, data) conn.execute("INSERT INTO person (name, time_created, data)
VALUES ($1, $2, $3)", VALUES (?1, ?2, ?3)",
&[&me.name, &me.time_created, &me.data]).unwrap(); &[&me.name, &me.time_created, &me.data]).unwrap();
let mut stmt = conn.prepare("SELECT id, name, time_created, data FROM person").unwrap(); let mut stmt = conn.prepare("SELECT id, name, time_created, data FROM person").unwrap();
let mut person_iter = stmt.query_map(&[], |row| { let person_iter = stmt.query_map(&[], |row| {
Person { Person {
id: row.get(0), id: row.get(0),
name: row.get(1), name: row.get(1),
@ -78,6 +78,7 @@ features](http://doc.crates.io/manifest.html#the-features-section). They are:
* `serde_json` implements [`FromSql`](http://jgallagher.github.io/rusqlite/rusqlite/types/trait.FromSql.html) * `serde_json` implements [`FromSql`](http://jgallagher.github.io/rusqlite/rusqlite/types/trait.FromSql.html)
and [`ToSql`](http://jgallagher.github.io/rusqlite/rusqlite/types/trait.ToSql.html) for the and [`ToSql`](http://jgallagher.github.io/rusqlite/rusqlite/types/trait.ToSql.html) for the
`Value` type from the [`serde_json` crate](https://crates.io/crates/serde_json). `Value` type from the [`serde_json` crate](https://crates.io/crates/serde_json).
* `bundled` uses a bundled version of sqlite3. This is a good option for cases where linking to sqlite3 is complicated, such as Windows.
## Author ## Author

View File

@ -16,6 +16,9 @@ build: false
test_script: test_script:
- cargo test --lib --verbose - cargo test --lib --verbose
- cargo test --lib --verbose --features bundled
- cargo test --lib --features "backup blob chrono functions load_extension serde_json trace"
- cargo test --lib --features "backup blob chrono functions load_extension serde_json trace bundled"
- cargo test --lib --features "backup blob chrono functions load_extension serde_json trace vtab" - cargo test --lib --features "backup blob chrono functions load_extension serde_json trace vtab"
cache: cache:

View File

@ -1,6 +1,6 @@
[package] [package]
name = "libsqlite3-sys" name = "libsqlite3-sys"
version = "0.5.0" version = "0.6.0"
authors = ["John Gallagher <jgallagher@bignerdranch.com>"] authors = ["John Gallagher <jgallagher@bignerdranch.com>"]
repository = "https://github.com/jgallagher/rusqlite" repository = "https://github.com/jgallagher/rusqlite"
description = "Native bindings to the libsqlite3 library" description = "Native bindings to the libsqlite3 library"
@ -8,8 +8,12 @@ license = "MIT"
links = "sqlite3" links = "sqlite3"
build = "build.rs" build = "build.rs"
[features]
bundled = []
[build-dependencies] [build-dependencies]
pkg-config = "~0.3" pkg-config = "~0.3"
gcc = "~0.3"
[dependencies] [dependencies]
libc = "~0.2" libc = "~0.2"

View File

@ -1,9 +1,11 @@
extern crate gcc;
extern crate pkg_config; extern crate pkg_config;
use std::env; #[cfg(not(feature = "bundled"))]
use std::fs;
fn main() { fn main() {
use std::env;
use std::fs;
// Allow users to specify where to find SQLite. // Allow users to specify where to find SQLite.
let lib_dir = match env::var("SQLITE3_LIB_DIR") { let lib_dir = match env::var("SQLITE3_LIB_DIR") {
Ok(dir) => dir, Ok(dir) => dir,
@ -24,3 +26,28 @@ fn main() {
println!("cargo:rustc-link-lib=sqlite3"); println!("cargo:rustc-link-lib=sqlite3");
println!("cargo:rustc-link-search={}", lib_dir); println!("cargo:rustc-link-search={}", lib_dir);
} }
#[cfg(feature = "bundled")]
fn main() {
gcc::Config::new()
.file("sqlite3/sqlite3.c")
.flag("-DSQLITE_CORE")
.flag("-DSQLITE_DEFAULT_FOREIGN_KEYS=1")
.flag("-DSQLITE_ENABLE_API_ARMOR")
.flag("-DSQLITE_ENABLE_COLUMN_METADATA")
.flag("-DSQLITE_ENABLE_DBSTAT_VTAB")
.flag("-DSQLITE_ENABLE_FTS3")
.flag("-DSQLITE_ENABLE_FTS3_PARENTHESIS")
.flag("-DSQLITE_ENABLE_FTS5")
.flag("-DSQLITE_ENABLE_JSON1")
.flag("-DSQLITE_ENABLE_LOAD_EXTENSION=1")
.flag("-DSQLITE_ENABLE_MEMORY_MANAGEMENT")
.flag("-DSQLITE_ENABLE_RTREE")
.flag("-DSQLITE_ENABLE_STAT2")
.flag("-DSQLITE_ENABLE_STAT4")
.flag("-DSQLITE_HAVE_ISNAN")
.flag("-DSQLITE_SOUNDEX")
.flag("-DSQLITE_THREADSAFE=1")
.flag("-DSQLITE_USE_URI")
.compile("libsqlite3.a");
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,546 @@
/*
** 2006 June 7
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This header file defines the SQLite interface for use by
** shared libraries that want to be imported as extensions into
** an SQLite instance. Shared libraries that intend to be loaded
** as extensions by SQLite should #include this file instead of
** sqlite3.h.
*/
#ifndef _SQLITE3EXT_H_
#define _SQLITE3EXT_H_
#include "sqlite3.h"
typedef struct sqlite3_api_routines sqlite3_api_routines;
/*
** The following structure holds pointers to all of the SQLite API
** routines.
**
** WARNING: In order to maintain backwards compatibility, add new
** interfaces to the end of this structure only. If you insert new
** interfaces in the middle of this structure, then older different
** versions of SQLite will not be able to load each other's shared
** libraries!
*/
struct sqlite3_api_routines {
void * (*aggregate_context)(sqlite3_context*,int nBytes);
int (*aggregate_count)(sqlite3_context*);
int (*bind_blob)(sqlite3_stmt*,int,const void*,int n,void(*)(void*));
int (*bind_double)(sqlite3_stmt*,int,double);
int (*bind_int)(sqlite3_stmt*,int,int);
int (*bind_int64)(sqlite3_stmt*,int,sqlite_int64);
int (*bind_null)(sqlite3_stmt*,int);
int (*bind_parameter_count)(sqlite3_stmt*);
int (*bind_parameter_index)(sqlite3_stmt*,const char*zName);
const char * (*bind_parameter_name)(sqlite3_stmt*,int);
int (*bind_text)(sqlite3_stmt*,int,const char*,int n,void(*)(void*));
int (*bind_text16)(sqlite3_stmt*,int,const void*,int,void(*)(void*));
int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*);
int (*busy_handler)(sqlite3*,int(*)(void*,int),void*);
int (*busy_timeout)(sqlite3*,int ms);
int (*changes)(sqlite3*);
int (*close)(sqlite3*);
int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*,
int eTextRep,const char*));
int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*,
int eTextRep,const void*));
const void * (*column_blob)(sqlite3_stmt*,int iCol);
int (*column_bytes)(sqlite3_stmt*,int iCol);
int (*column_bytes16)(sqlite3_stmt*,int iCol);
int (*column_count)(sqlite3_stmt*pStmt);
const char * (*column_database_name)(sqlite3_stmt*,int);
const void * (*column_database_name16)(sqlite3_stmt*,int);
const char * (*column_decltype)(sqlite3_stmt*,int i);
const void * (*column_decltype16)(sqlite3_stmt*,int);
double (*column_double)(sqlite3_stmt*,int iCol);
int (*column_int)(sqlite3_stmt*,int iCol);
sqlite_int64 (*column_int64)(sqlite3_stmt*,int iCol);
const char * (*column_name)(sqlite3_stmt*,int);
const void * (*column_name16)(sqlite3_stmt*,int);
const char * (*column_origin_name)(sqlite3_stmt*,int);
const void * (*column_origin_name16)(sqlite3_stmt*,int);
const char * (*column_table_name)(sqlite3_stmt*,int);
const void * (*column_table_name16)(sqlite3_stmt*,int);
const unsigned char * (*column_text)(sqlite3_stmt*,int iCol);
const void * (*column_text16)(sqlite3_stmt*,int iCol);
int (*column_type)(sqlite3_stmt*,int iCol);
sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol);
void * (*commit_hook)(sqlite3*,int(*)(void*),void*);
int (*complete)(const char*sql);
int (*complete16)(const void*sql);
int (*create_collation)(sqlite3*,const char*,int,void*,
int(*)(void*,int,const void*,int,const void*));
int (*create_collation16)(sqlite3*,const void*,int,void*,
int(*)(void*,int,const void*,int,const void*));
int (*create_function)(sqlite3*,const char*,int,int,void*,
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*));
int (*create_function16)(sqlite3*,const void*,int,int,void*,
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*));
int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*);
int (*data_count)(sqlite3_stmt*pStmt);
sqlite3 * (*db_handle)(sqlite3_stmt*);
int (*declare_vtab)(sqlite3*,const char*);
int (*enable_shared_cache)(int);
int (*errcode)(sqlite3*db);
const char * (*errmsg)(sqlite3*);
const void * (*errmsg16)(sqlite3*);
int (*exec)(sqlite3*,const char*,sqlite3_callback,void*,char**);
int (*expired)(sqlite3_stmt*);
int (*finalize)(sqlite3_stmt*pStmt);
void (*free)(void*);
void (*free_table)(char**result);
int (*get_autocommit)(sqlite3*);
void * (*get_auxdata)(sqlite3_context*,int);
int (*get_table)(sqlite3*,const char*,char***,int*,int*,char**);
int (*global_recover)(void);
void (*interruptx)(sqlite3*);
sqlite_int64 (*last_insert_rowid)(sqlite3*);
const char * (*libversion)(void);
int (*libversion_number)(void);
void *(*malloc)(int);
char * (*mprintf)(const char*,...);
int (*open)(const char*,sqlite3**);
int (*open16)(const void*,sqlite3**);
int (*prepare)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
int (*prepare16)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
void * (*profile)(sqlite3*,void(*)(void*,const char*,sqlite_uint64),void*);
void (*progress_handler)(sqlite3*,int,int(*)(void*),void*);
void *(*realloc)(void*,int);
int (*reset)(sqlite3_stmt*pStmt);
void (*result_blob)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_double)(sqlite3_context*,double);
void (*result_error)(sqlite3_context*,const char*,int);
void (*result_error16)(sqlite3_context*,const void*,int);
void (*result_int)(sqlite3_context*,int);
void (*result_int64)(sqlite3_context*,sqlite_int64);
void (*result_null)(sqlite3_context*);
void (*result_text)(sqlite3_context*,const char*,int,void(*)(void*));
void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_value)(sqlite3_context*,sqlite3_value*);
void * (*rollback_hook)(sqlite3*,void(*)(void*),void*);
int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,
const char*,const char*),void*);
void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*));
char * (*snprintf)(int,char*,const char*,...);
int (*step)(sqlite3_stmt*);
int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,
char const**,char const**,int*,int*,int*);
void (*thread_cleanup)(void);
int (*total_changes)(sqlite3*);
void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*);
int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*);
void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*,
sqlite_int64),void*);
void * (*user_data)(sqlite3_context*);
const void * (*value_blob)(sqlite3_value*);
int (*value_bytes)(sqlite3_value*);
int (*value_bytes16)(sqlite3_value*);
double (*value_double)(sqlite3_value*);
int (*value_int)(sqlite3_value*);
sqlite_int64 (*value_int64)(sqlite3_value*);
int (*value_numeric_type)(sqlite3_value*);
const unsigned char * (*value_text)(sqlite3_value*);
const void * (*value_text16)(sqlite3_value*);
const void * (*value_text16be)(sqlite3_value*);
const void * (*value_text16le)(sqlite3_value*);
int (*value_type)(sqlite3_value*);
char *(*vmprintf)(const char*,va_list);
/* Added ??? */
int (*overload_function)(sqlite3*, const char *zFuncName, int nArg);
/* Added by 3.3.13 */
int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
int (*clear_bindings)(sqlite3_stmt*);
/* Added by 3.4.1 */
int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*,
void (*xDestroy)(void *));
/* Added by 3.5.0 */
int (*bind_zeroblob)(sqlite3_stmt*,int,int);
int (*blob_bytes)(sqlite3_blob*);
int (*blob_close)(sqlite3_blob*);
int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64,
int,sqlite3_blob**);
int (*blob_read)(sqlite3_blob*,void*,int,int);
int (*blob_write)(sqlite3_blob*,const void*,int,int);
int (*create_collation_v2)(sqlite3*,const char*,int,void*,
int(*)(void*,int,const void*,int,const void*),
void(*)(void*));
int (*file_control)(sqlite3*,const char*,int,void*);
sqlite3_int64 (*memory_highwater)(int);
sqlite3_int64 (*memory_used)(void);
sqlite3_mutex *(*mutex_alloc)(int);
void (*mutex_enter)(sqlite3_mutex*);
void (*mutex_free)(sqlite3_mutex*);
void (*mutex_leave)(sqlite3_mutex*);
int (*mutex_try)(sqlite3_mutex*);
int (*open_v2)(const char*,sqlite3**,int,const char*);
int (*release_memory)(int);
void (*result_error_nomem)(sqlite3_context*);
void (*result_error_toobig)(sqlite3_context*);
int (*sleep)(int);
void (*soft_heap_limit)(int);
sqlite3_vfs *(*vfs_find)(const char*);
int (*vfs_register)(sqlite3_vfs*,int);
int (*vfs_unregister)(sqlite3_vfs*);
int (*xthreadsafe)(void);
void (*result_zeroblob)(sqlite3_context*,int);
void (*result_error_code)(sqlite3_context*,int);
int (*test_control)(int, ...);
void (*randomness)(int,void*);
sqlite3 *(*context_db_handle)(sqlite3_context*);
int (*extended_result_codes)(sqlite3*,int);
int (*limit)(sqlite3*,int,int);
sqlite3_stmt *(*next_stmt)(sqlite3*,sqlite3_stmt*);
const char *(*sql)(sqlite3_stmt*);
int (*status)(int,int*,int*,int);
int (*backup_finish)(sqlite3_backup*);
sqlite3_backup *(*backup_init)(sqlite3*,const char*,sqlite3*,const char*);
int (*backup_pagecount)(sqlite3_backup*);
int (*backup_remaining)(sqlite3_backup*);
int (*backup_step)(sqlite3_backup*,int);
const char *(*compileoption_get)(int);
int (*compileoption_used)(const char*);
int (*create_function_v2)(sqlite3*,const char*,int,int,void*,
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*),
void(*xDestroy)(void*));
int (*db_config)(sqlite3*,int,...);
sqlite3_mutex *(*db_mutex)(sqlite3*);
int (*db_status)(sqlite3*,int,int*,int*,int);
int (*extended_errcode)(sqlite3*);
void (*log)(int,const char*,...);
sqlite3_int64 (*soft_heap_limit64)(sqlite3_int64);
const char *(*sourceid)(void);
int (*stmt_status)(sqlite3_stmt*,int,int);
int (*strnicmp)(const char*,const char*,int);
int (*unlock_notify)(sqlite3*,void(*)(void**,int),void*);
int (*wal_autocheckpoint)(sqlite3*,int);
int (*wal_checkpoint)(sqlite3*,const char*);
void *(*wal_hook)(sqlite3*,int(*)(void*,sqlite3*,const char*,int),void*);
int (*blob_reopen)(sqlite3_blob*,sqlite3_int64);
int (*vtab_config)(sqlite3*,int op,...);
int (*vtab_on_conflict)(sqlite3*);
/* Version 3.7.16 and later */
int (*close_v2)(sqlite3*);
const char *(*db_filename)(sqlite3*,const char*);
int (*db_readonly)(sqlite3*,const char*);
int (*db_release_memory)(sqlite3*);
const char *(*errstr)(int);
int (*stmt_busy)(sqlite3_stmt*);
int (*stmt_readonly)(sqlite3_stmt*);
int (*stricmp)(const char*,const char*);
int (*uri_boolean)(const char*,const char*,int);
sqlite3_int64 (*uri_int64)(const char*,const char*,sqlite3_int64);
const char *(*uri_parameter)(const char*,const char*);
char *(*vsnprintf)(int,char*,const char*,va_list);
int (*wal_checkpoint_v2)(sqlite3*,const char*,int,int*,int*);
/* Version 3.8.7 and later */
int (*auto_extension)(void(*)(void));
int (*bind_blob64)(sqlite3_stmt*,int,const void*,sqlite3_uint64,
void(*)(void*));
int (*bind_text64)(sqlite3_stmt*,int,const char*,sqlite3_uint64,
void(*)(void*),unsigned char);
int (*cancel_auto_extension)(void(*)(void));
int (*load_extension)(sqlite3*,const char*,const char*,char**);
void *(*malloc64)(sqlite3_uint64);
sqlite3_uint64 (*msize)(void*);
void *(*realloc64)(void*,sqlite3_uint64);
void (*reset_auto_extension)(void);
void (*result_blob64)(sqlite3_context*,const void*,sqlite3_uint64,
void(*)(void*));
void (*result_text64)(sqlite3_context*,const char*,sqlite3_uint64,
void(*)(void*), unsigned char);
int (*strglob)(const char*,const char*);
/* Version 3.8.11 and later */
sqlite3_value *(*value_dup)(const sqlite3_value*);
void (*value_free)(sqlite3_value*);
int (*result_zeroblob64)(sqlite3_context*,sqlite3_uint64);
int (*bind_zeroblob64)(sqlite3_stmt*, int, sqlite3_uint64);
/* Version 3.9.0 and later */
unsigned int (*value_subtype)(sqlite3_value*);
void (*result_subtype)(sqlite3_context*,unsigned int);
/* Version 3.10.0 and later */
int (*status64)(int,sqlite3_int64*,sqlite3_int64*,int);
int (*strlike)(const char*,const char*,unsigned int);
int (*db_cacheflush)(sqlite3*);
/* Version 3.12.0 and later */
int (*system_errno)(sqlite3*);
};
/*
** The following macros redefine the API routines so that they are
** redirected through the global sqlite3_api structure.
**
** This header file is also used by the loadext.c source file
** (part of the main SQLite library - not an extension) so that
** it can get access to the sqlite3_api_routines structure
** definition. But the main library does not want to redefine
** the API. So the redefinition macros are only valid if the
** SQLITE_CORE macros is undefined.
*/
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
#define sqlite3_aggregate_context sqlite3_api->aggregate_context
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_aggregate_count sqlite3_api->aggregate_count
#endif
#define sqlite3_bind_blob sqlite3_api->bind_blob
#define sqlite3_bind_double sqlite3_api->bind_double
#define sqlite3_bind_int sqlite3_api->bind_int
#define sqlite3_bind_int64 sqlite3_api->bind_int64
#define sqlite3_bind_null sqlite3_api->bind_null
#define sqlite3_bind_parameter_count sqlite3_api->bind_parameter_count
#define sqlite3_bind_parameter_index sqlite3_api->bind_parameter_index
#define sqlite3_bind_parameter_name sqlite3_api->bind_parameter_name
#define sqlite3_bind_text sqlite3_api->bind_text
#define sqlite3_bind_text16 sqlite3_api->bind_text16
#define sqlite3_bind_value sqlite3_api->bind_value
#define sqlite3_busy_handler sqlite3_api->busy_handler
#define sqlite3_busy_timeout sqlite3_api->busy_timeout
#define sqlite3_changes sqlite3_api->changes
#define sqlite3_close sqlite3_api->close
#define sqlite3_collation_needed sqlite3_api->collation_needed
#define sqlite3_collation_needed16 sqlite3_api->collation_needed16
#define sqlite3_column_blob sqlite3_api->column_blob
#define sqlite3_column_bytes sqlite3_api->column_bytes
#define sqlite3_column_bytes16 sqlite3_api->column_bytes16
#define sqlite3_column_count sqlite3_api->column_count
#define sqlite3_column_database_name sqlite3_api->column_database_name
#define sqlite3_column_database_name16 sqlite3_api->column_database_name16
#define sqlite3_column_decltype sqlite3_api->column_decltype
#define sqlite3_column_decltype16 sqlite3_api->column_decltype16
#define sqlite3_column_double sqlite3_api->column_double
#define sqlite3_column_int sqlite3_api->column_int
#define sqlite3_column_int64 sqlite3_api->column_int64
#define sqlite3_column_name sqlite3_api->column_name
#define sqlite3_column_name16 sqlite3_api->column_name16
#define sqlite3_column_origin_name sqlite3_api->column_origin_name
#define sqlite3_column_origin_name16 sqlite3_api->column_origin_name16
#define sqlite3_column_table_name sqlite3_api->column_table_name
#define sqlite3_column_table_name16 sqlite3_api->column_table_name16
#define sqlite3_column_text sqlite3_api->column_text
#define sqlite3_column_text16 sqlite3_api->column_text16
#define sqlite3_column_type sqlite3_api->column_type
#define sqlite3_column_value sqlite3_api->column_value
#define sqlite3_commit_hook sqlite3_api->commit_hook
#define sqlite3_complete sqlite3_api->complete
#define sqlite3_complete16 sqlite3_api->complete16
#define sqlite3_create_collation sqlite3_api->create_collation
#define sqlite3_create_collation16 sqlite3_api->create_collation16
#define sqlite3_create_function sqlite3_api->create_function
#define sqlite3_create_function16 sqlite3_api->create_function16
#define sqlite3_create_module sqlite3_api->create_module
#define sqlite3_create_module_v2 sqlite3_api->create_module_v2
#define sqlite3_data_count sqlite3_api->data_count
#define sqlite3_db_handle sqlite3_api->db_handle
#define sqlite3_declare_vtab sqlite3_api->declare_vtab
#define sqlite3_enable_shared_cache sqlite3_api->enable_shared_cache
#define sqlite3_errcode sqlite3_api->errcode
#define sqlite3_errmsg sqlite3_api->errmsg
#define sqlite3_errmsg16 sqlite3_api->errmsg16
#define sqlite3_exec sqlite3_api->exec
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_expired sqlite3_api->expired
#endif
#define sqlite3_finalize sqlite3_api->finalize
#define sqlite3_free sqlite3_api->free
#define sqlite3_free_table sqlite3_api->free_table
#define sqlite3_get_autocommit sqlite3_api->get_autocommit
#define sqlite3_get_auxdata sqlite3_api->get_auxdata
#define sqlite3_get_table sqlite3_api->get_table
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_global_recover sqlite3_api->global_recover
#endif
#define sqlite3_interrupt sqlite3_api->interruptx
#define sqlite3_last_insert_rowid sqlite3_api->last_insert_rowid
#define sqlite3_libversion sqlite3_api->libversion
#define sqlite3_libversion_number sqlite3_api->libversion_number
#define sqlite3_malloc sqlite3_api->malloc
#define sqlite3_mprintf sqlite3_api->mprintf
#define sqlite3_open sqlite3_api->open
#define sqlite3_open16 sqlite3_api->open16
#define sqlite3_prepare sqlite3_api->prepare
#define sqlite3_prepare16 sqlite3_api->prepare16
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
#define sqlite3_profile sqlite3_api->profile
#define sqlite3_progress_handler sqlite3_api->progress_handler
#define sqlite3_realloc sqlite3_api->realloc
#define sqlite3_reset sqlite3_api->reset
#define sqlite3_result_blob sqlite3_api->result_blob
#define sqlite3_result_double sqlite3_api->result_double
#define sqlite3_result_error sqlite3_api->result_error
#define sqlite3_result_error16 sqlite3_api->result_error16
#define sqlite3_result_int sqlite3_api->result_int
#define sqlite3_result_int64 sqlite3_api->result_int64
#define sqlite3_result_null sqlite3_api->result_null
#define sqlite3_result_text sqlite3_api->result_text
#define sqlite3_result_text16 sqlite3_api->result_text16
#define sqlite3_result_text16be sqlite3_api->result_text16be
#define sqlite3_result_text16le sqlite3_api->result_text16le
#define sqlite3_result_value sqlite3_api->result_value
#define sqlite3_rollback_hook sqlite3_api->rollback_hook
#define sqlite3_set_authorizer sqlite3_api->set_authorizer
#define sqlite3_set_auxdata sqlite3_api->set_auxdata
#define sqlite3_snprintf sqlite3_api->snprintf
#define sqlite3_step sqlite3_api->step
#define sqlite3_table_column_metadata sqlite3_api->table_column_metadata
#define sqlite3_thread_cleanup sqlite3_api->thread_cleanup
#define sqlite3_total_changes sqlite3_api->total_changes
#define sqlite3_trace sqlite3_api->trace
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_transfer_bindings sqlite3_api->transfer_bindings
#endif
#define sqlite3_update_hook sqlite3_api->update_hook
#define sqlite3_user_data sqlite3_api->user_data
#define sqlite3_value_blob sqlite3_api->value_blob
#define sqlite3_value_bytes sqlite3_api->value_bytes
#define sqlite3_value_bytes16 sqlite3_api->value_bytes16
#define sqlite3_value_double sqlite3_api->value_double
#define sqlite3_value_int sqlite3_api->value_int
#define sqlite3_value_int64 sqlite3_api->value_int64
#define sqlite3_value_numeric_type sqlite3_api->value_numeric_type
#define sqlite3_value_text sqlite3_api->value_text
#define sqlite3_value_text16 sqlite3_api->value_text16
#define sqlite3_value_text16be sqlite3_api->value_text16be
#define sqlite3_value_text16le sqlite3_api->value_text16le
#define sqlite3_value_type sqlite3_api->value_type
#define sqlite3_vmprintf sqlite3_api->vmprintf
#define sqlite3_vsnprintf sqlite3_api->vsnprintf
#define sqlite3_overload_function sqlite3_api->overload_function
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
#define sqlite3_clear_bindings sqlite3_api->clear_bindings
#define sqlite3_bind_zeroblob sqlite3_api->bind_zeroblob
#define sqlite3_blob_bytes sqlite3_api->blob_bytes
#define sqlite3_blob_close sqlite3_api->blob_close
#define sqlite3_blob_open sqlite3_api->blob_open
#define sqlite3_blob_read sqlite3_api->blob_read
#define sqlite3_blob_write sqlite3_api->blob_write
#define sqlite3_create_collation_v2 sqlite3_api->create_collation_v2
#define sqlite3_file_control sqlite3_api->file_control
#define sqlite3_memory_highwater sqlite3_api->memory_highwater
#define sqlite3_memory_used sqlite3_api->memory_used
#define sqlite3_mutex_alloc sqlite3_api->mutex_alloc
#define sqlite3_mutex_enter sqlite3_api->mutex_enter
#define sqlite3_mutex_free sqlite3_api->mutex_free
#define sqlite3_mutex_leave sqlite3_api->mutex_leave
#define sqlite3_mutex_try sqlite3_api->mutex_try
#define sqlite3_open_v2 sqlite3_api->open_v2
#define sqlite3_release_memory sqlite3_api->release_memory
#define sqlite3_result_error_nomem sqlite3_api->result_error_nomem
#define sqlite3_result_error_toobig sqlite3_api->result_error_toobig
#define sqlite3_sleep sqlite3_api->sleep
#define sqlite3_soft_heap_limit sqlite3_api->soft_heap_limit
#define sqlite3_vfs_find sqlite3_api->vfs_find
#define sqlite3_vfs_register sqlite3_api->vfs_register
#define sqlite3_vfs_unregister sqlite3_api->vfs_unregister
#define sqlite3_threadsafe sqlite3_api->xthreadsafe
#define sqlite3_result_zeroblob sqlite3_api->result_zeroblob
#define sqlite3_result_error_code sqlite3_api->result_error_code
#define sqlite3_test_control sqlite3_api->test_control
#define sqlite3_randomness sqlite3_api->randomness
#define sqlite3_context_db_handle sqlite3_api->context_db_handle
#define sqlite3_extended_result_codes sqlite3_api->extended_result_codes
#define sqlite3_limit sqlite3_api->limit
#define sqlite3_next_stmt sqlite3_api->next_stmt
#define sqlite3_sql sqlite3_api->sql
#define sqlite3_status sqlite3_api->status
#define sqlite3_backup_finish sqlite3_api->backup_finish
#define sqlite3_backup_init sqlite3_api->backup_init
#define sqlite3_backup_pagecount sqlite3_api->backup_pagecount
#define sqlite3_backup_remaining sqlite3_api->backup_remaining
#define sqlite3_backup_step sqlite3_api->backup_step
#define sqlite3_compileoption_get sqlite3_api->compileoption_get
#define sqlite3_compileoption_used sqlite3_api->compileoption_used
#define sqlite3_create_function_v2 sqlite3_api->create_function_v2
#define sqlite3_db_config sqlite3_api->db_config
#define sqlite3_db_mutex sqlite3_api->db_mutex
#define sqlite3_db_status sqlite3_api->db_status
#define sqlite3_extended_errcode sqlite3_api->extended_errcode
#define sqlite3_log sqlite3_api->log
#define sqlite3_soft_heap_limit64 sqlite3_api->soft_heap_limit64
#define sqlite3_sourceid sqlite3_api->sourceid
#define sqlite3_stmt_status sqlite3_api->stmt_status
#define sqlite3_strnicmp sqlite3_api->strnicmp
#define sqlite3_unlock_notify sqlite3_api->unlock_notify
#define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint
#define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint
#define sqlite3_wal_hook sqlite3_api->wal_hook
#define sqlite3_blob_reopen sqlite3_api->blob_reopen
#define sqlite3_vtab_config sqlite3_api->vtab_config
#define sqlite3_vtab_on_conflict sqlite3_api->vtab_on_conflict
/* Version 3.7.16 and later */
#define sqlite3_close_v2 sqlite3_api->close_v2
#define sqlite3_db_filename sqlite3_api->db_filename
#define sqlite3_db_readonly sqlite3_api->db_readonly
#define sqlite3_db_release_memory sqlite3_api->db_release_memory
#define sqlite3_errstr sqlite3_api->errstr
#define sqlite3_stmt_busy sqlite3_api->stmt_busy
#define sqlite3_stmt_readonly sqlite3_api->stmt_readonly
#define sqlite3_stricmp sqlite3_api->stricmp
#define sqlite3_uri_boolean sqlite3_api->uri_boolean
#define sqlite3_uri_int64 sqlite3_api->uri_int64
#define sqlite3_uri_parameter sqlite3_api->uri_parameter
#define sqlite3_uri_vsnprintf sqlite3_api->vsnprintf
#define sqlite3_wal_checkpoint_v2 sqlite3_api->wal_checkpoint_v2
/* Version 3.8.7 and later */
#define sqlite3_auto_extension sqlite3_api->auto_extension
#define sqlite3_bind_blob64 sqlite3_api->bind_blob64
#define sqlite3_bind_text64 sqlite3_api->bind_text64
#define sqlite3_cancel_auto_extension sqlite3_api->cancel_auto_extension
#define sqlite3_load_extension sqlite3_api->load_extension
#define sqlite3_malloc64 sqlite3_api->malloc64
#define sqlite3_msize sqlite3_api->msize
#define sqlite3_realloc64 sqlite3_api->realloc64
#define sqlite3_reset_auto_extension sqlite3_api->reset_auto_extension
#define sqlite3_result_blob64 sqlite3_api->result_blob64
#define sqlite3_result_text64 sqlite3_api->result_text64
#define sqlite3_strglob sqlite3_api->strglob
/* Version 3.8.11 and later */
#define sqlite3_value_dup sqlite3_api->value_dup
#define sqlite3_value_free sqlite3_api->value_free
#define sqlite3_result_zeroblob64 sqlite3_api->result_zeroblob64
#define sqlite3_bind_zeroblob64 sqlite3_api->bind_zeroblob64
/* Version 3.9.0 and later */
#define sqlite3_value_subtype sqlite3_api->value_subtype
#define sqlite3_result_subtype sqlite3_api->result_subtype
/* Version 3.10.0 and later */
#define sqlite3_status64 sqlite3_api->status64
#define sqlite3_strlike sqlite3_api->strlike
#define sqlite3_db_cacheflush sqlite3_api->db_cacheflush
/* Version 3.12.0 and later */
#define sqlite3_system_errno sqlite3_api->system_errno
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
/* This case when the file really is being compiled as a loadable
** extension */
# define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0;
# define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v;
# define SQLITE_EXTENSION_INIT3 \
extern const sqlite3_api_routines *sqlite3_api;
#else
/* This case when the file is being statically linked into the
** application */
# define SQLITE_EXTENSION_INIT1 /*no-op*/
# define SQLITE_EXTENSION_INIT2(v) (void)v; /* unused parameter */
# define SQLITE_EXTENSION_INIT3 /*no-op*/
#endif
#endif /* _SQLITE3EXT_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -18,7 +18,7 @@
//! # use std::path::Path; //! # use std::path::Path;
//! # use std::time; //! # use std::time;
//! //!
//! fn backupDb<P: AsRef<Path>>(src: &Connection, dst: P, progress: fn(backup::Progress)) //! fn backup_db<P: AsRef<Path>>(src: &Connection, dst: P, progress: fn(backup::Progress))
//! -> Result<()> { //! -> Result<()> {
//! let mut dst = try!(Connection::open(dst)); //! let mut dst = try!(Connection::open(dst));
//! let backup = try!(backup::Backup::new(src, &mut dst)); //! let backup = try!(backup::Backup::new(src, &mut dst));

View File

@ -52,10 +52,9 @@ use std::io;
use std::cmp::min; use std::cmp::min;
use std::mem; use std::mem;
use std::ptr; use std::ptr;
use libc::c_int;
use super::ffi; use super::ffi;
use super::types::ToSql; use super::types::{ToSql, ToSqlOutput};
use {Result, Connection, DatabaseName}; use {Result, Connection, DatabaseName};
/// Handle to an open BLOB. /// Handle to an open BLOB.
@ -240,9 +239,9 @@ impl<'conn> Drop for Blob<'conn> {
pub struct ZeroBlob(pub i32); pub struct ZeroBlob(pub i32);
impl ToSql for ZeroBlob { impl ToSql for ZeroBlob {
unsafe fn bind_parameter(&self, stmt: *mut ffi::sqlite3_stmt, col: c_int) -> c_int { fn to_sql(&self) -> Result<ToSqlOutput> {
let ZeroBlob(length) = *self; let ZeroBlob(length) = *self;
ffi::sqlite3_bind_zeroblob(stmt, col, length) Ok(ToSqlOutput::ZeroBlob(length))
} }
} }

View File

@ -44,6 +44,10 @@ impl Connection {
pub fn set_prepared_statement_cache_capacity(&self, capacity: usize) { pub fn set_prepared_statement_cache_capacity(&self, capacity: usize) {
self.cache.set_capacity(capacity) self.cache.set_capacity(capacity)
} }
pub fn flush_prepared_statement_cache(&self) {
self.cache.flush()
}
} }
/// Prepared statements LRU cache. /// Prepared statements LRU cache.
@ -133,6 +137,11 @@ impl StatementCache {
let sql = String::from_utf8_lossy(stmt.sql().to_bytes()).to_string(); let sql = String::from_utf8_lossy(stmt.sql().to_bytes()).to_string();
cache.insert(sql, stmt); cache.insert(sql, stmt);
} }
fn flush(&self) {
let mut cache = self.0.borrow_mut();
cache.clear()
}
} }
#[cfg(test)] #[cfg(test)]
@ -268,4 +277,12 @@ mod test {
.unwrap()); .unwrap());
} }
} }
#[test]
fn test_connection_close() {
let conn = Connection::open_in_memory().unwrap();
conn.prepare_cached("SELECT * FROM sqlite_master;").unwrap();
conn.close().expect("connection not closed");
}
} }

View File

@ -1,4 +1,4 @@
use {Error, Result, Statement}; use {Error, Result, Row, Statement};
use types::ToSql; use types::ToSql;
impl<'conn> Statement<'conn> { impl<'conn> Statement<'conn> {
@ -34,11 +34,26 @@ impl<'conn> Statement<'conn> {
}; };
Ok(exists) Ok(exists)
} }
/// Convenience method to execute a query that is expected to return a single row.
///
/// If the query returns more than one row, all rows except the first are ignored.
///
/// # Failure
///
/// Will return `Err` if the underlying SQLite call fails.
pub fn query_row<T, F>(&mut self, params: &[&ToSql], f: F) -> Result<T>
where F: FnOnce(&Row) -> T
{
let mut rows = try!(self.query(params));
rows.get_expected_row().map(|r| f(&r))
}
} }
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use {Connection, Error}; use {Connection, Error, Result};
#[test] #[test]
fn test_insert() { fn test_insert() {
@ -88,4 +103,18 @@ mod test {
assert!(stmt.exists(&[&2i32]).unwrap()); assert!(stmt.exists(&[&2i32]).unwrap());
assert!(!stmt.exists(&[&0i32]).unwrap()); assert!(!stmt.exists(&[&0i32]).unwrap());
} }
#[test]
fn test_query_row() {
let db = Connection::open_in_memory().unwrap();
let sql = "BEGIN;
CREATE TABLE foo(x INTEGER, y INTEGER);
INSERT INTO foo VALUES(1, 3);
INSERT INTO foo VALUES(2, 4);
END;";
db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("SELECT y FROM foo WHERE x = ?").unwrap();
let y: Result<i64> = stmt.query_row(&[&1i32], |r| r.get(0));
assert_eq!(3i64, y.unwrap());
}
} }

View File

@ -4,6 +4,7 @@ use std::path::PathBuf;
use std::str; use std::str;
use libc::c_int; use libc::c_int;
use {ffi, errmsg_to_string}; use {ffi, errmsg_to_string};
use types::Type;
/// Old name for `Error`. `SqliteError` is deprecated. /// Old name for `Error`. `SqliteError` is deprecated.
#[deprecated(since = "0.6.0", note = "Use Error instead")] #[deprecated(since = "0.6.0", note = "Use Error instead")]
@ -11,6 +12,7 @@ pub type SqliteError = Error;
/// Enum listing possible errors from rusqlite. /// Enum listing possible errors from rusqlite.
#[derive(Debug)] #[derive(Debug)]
#[allow(enum_variant_names)]
pub enum Error { pub enum Error {
/// An error from an underlying SQLite call. /// An error from an underlying SQLite call.
SqliteFailure(ffi::Error, Option<String>), SqliteFailure(ffi::Error, Option<String>),
@ -19,8 +21,9 @@ pub enum Error {
/// allow single-threaded use only. /// allow single-threaded use only.
SqliteSingleThreadedMode, SqliteSingleThreadedMode,
/// An error case available for implementors of the `FromSql` trait. /// Error when the value of a particular column is requested, but it cannot be converted to
FromSqlConversionFailure(Box<error::Error + Send + Sync>), /// the requested Rust type.
FromSqlConversionFailure(usize, Type, Box<error::Error + Send + Sync>),
/// Error converting a string to UTF-8. /// Error converting a string to UTF-8.
Utf8Error(str::Utf8Error), Utf8Error(str::Utf8Error),
@ -51,7 +54,7 @@ pub enum Error {
/// Error when the value of a particular column is requested, but the type of the result in /// Error when the value of a particular column is requested, but the type of the result in
/// that column cannot be converted to the requested Rust type. /// that column cannot be converted to the requested Rust type.
InvalidColumnType, InvalidColumnType(c_int, Type),
/// Error when a query that was expected to insert one row did not insert any or insert many. /// Error when a query that was expected to insert one row did not insert any or insert many.
StatementChangedRows(c_int), StatementChangedRows(c_int),
@ -59,7 +62,7 @@ pub enum Error {
/// Error returned by `functions::Context::get` when the function argument cannot be converted /// Error returned by `functions::Context::get` when the function argument cannot be converted
/// to the requested type. /// to the requested type.
#[cfg(feature = "functions")] #[cfg(feature = "functions")]
InvalidFunctionParameterType, InvalidFunctionParameterType(usize, Type),
/// An error case available for implementors of custom user functions (e.g., /// An error case available for implementors of custom user functions (e.g.,
/// `create_scalar_function`). /// `create_scalar_function`).
@ -95,7 +98,13 @@ impl fmt::Display for Error {
write!(f, write!(f,
"SQLite was compiled or configured for single-threaded use only") "SQLite was compiled or configured for single-threaded use only")
} }
Error::FromSqlConversionFailure(ref err) => err.fmt(f), Error::FromSqlConversionFailure(i, ref t, ref err) => {
write!(f,
"Conversion error from type {} at index: {}, {}",
t,
i,
err)
}
Error::Utf8Error(ref err) => err.fmt(f), Error::Utf8Error(ref err) => err.fmt(f),
Error::NulError(ref err) => err.fmt(f), Error::NulError(ref err) => err.fmt(f),
Error::InvalidParameterName(ref name) => write!(f, "Invalid parameter name: {}", name), Error::InvalidParameterName(ref name) => write!(f, "Invalid parameter name: {}", name),
@ -106,11 +115,15 @@ impl fmt::Display for Error {
Error::QueryReturnedNoRows => write!(f, "Query returned no rows"), Error::QueryReturnedNoRows => write!(f, "Query returned no rows"),
Error::InvalidColumnIndex(i) => write!(f, "Invalid column index: {}", i), Error::InvalidColumnIndex(i) => write!(f, "Invalid column index: {}", i),
Error::InvalidColumnName(ref name) => write!(f, "Invalid column name: {}", name), Error::InvalidColumnName(ref name) => write!(f, "Invalid column name: {}", name),
Error::InvalidColumnType => write!(f, "Invalid column type"), Error::InvalidColumnType(i, ref t) => {
write!(f, "Invalid column type {} at index: {}", t, i)
}
Error::StatementChangedRows(i) => write!(f, "Query changed {} rows", i), Error::StatementChangedRows(i) => write!(f, "Query changed {} rows", i),
#[cfg(feature = "functions")] #[cfg(feature = "functions")]
Error::InvalidFunctionParameterType => write!(f, "Invalid function parameter type"), Error::InvalidFunctionParameterType(i, ref t) => {
write!(f, "Invalid function parameter type {} at index {}", t, i)
}
#[cfg(feature = "functions")] #[cfg(feature = "functions")]
Error::UserFunctionError(ref err) => err.fmt(f), Error::UserFunctionError(ref err) => err.fmt(f),
#[cfg(feature = "vtab")] #[cfg(feature = "vtab")]
@ -127,7 +140,7 @@ impl error::Error for Error {
Error::SqliteSingleThreadedMode => { Error::SqliteSingleThreadedMode => {
"SQLite was compiled or configured for single-threaded use only" "SQLite was compiled or configured for single-threaded use only"
} }
Error::FromSqlConversionFailure(ref err) => err.description(), Error::FromSqlConversionFailure(_, _, ref err) => err.description(),
Error::Utf8Error(ref err) => err.description(), Error::Utf8Error(ref err) => err.description(),
Error::InvalidParameterName(_) => "invalid parameter name", Error::InvalidParameterName(_) => "invalid parameter name",
Error::NulError(ref err) => err.description(), Error::NulError(ref err) => err.description(),
@ -138,11 +151,11 @@ impl error::Error for Error {
Error::QueryReturnedNoRows => "query returned no rows", Error::QueryReturnedNoRows => "query returned no rows",
Error::InvalidColumnIndex(_) => "invalid column index", Error::InvalidColumnIndex(_) => "invalid column index",
Error::InvalidColumnName(_) => "invalid column name", Error::InvalidColumnName(_) => "invalid column name",
Error::InvalidColumnType => "invalid column type", Error::InvalidColumnType(_, _) => "invalid column type",
Error::StatementChangedRows(_) => "query inserted zero or more than one row", Error::StatementChangedRows(_) => "query inserted zero or more than one row",
#[cfg(feature = "functions")] #[cfg(feature = "functions")]
Error::InvalidFunctionParameterType => "invalid function parameter type", Error::InvalidFunctionParameterType(_, _) => "invalid function parameter type",
#[cfg(feature = "functions")] #[cfg(feature = "functions")]
Error::UserFunctionError(ref err) => err.description(), Error::UserFunctionError(ref err) => err.description(),
#[cfg(feature = "vtab")] #[cfg(feature = "vtab")]
@ -150,11 +163,10 @@ impl error::Error for Error {
} }
} }
#[cfg_attr(feature="clippy", allow(match_same_arms))]
fn cause(&self) -> Option<&error::Error> { fn cause(&self) -> Option<&error::Error> {
match *self { match *self {
Error::SqliteFailure(ref err, _) => Some(err), Error::SqliteFailure(ref err, _) => Some(err),
Error::FromSqlConversionFailure(ref err) => Some(&**err), Error::FromSqlConversionFailure(_, _, ref err) => Some(&**err),
Error::Utf8Error(ref err) => Some(err), Error::Utf8Error(ref err) => Some(err),
Error::NulError(ref err) => Some(err), Error::NulError(ref err) => Some(err),
@ -164,12 +176,12 @@ impl error::Error for Error {
Error::QueryReturnedNoRows | Error::QueryReturnedNoRows |
Error::InvalidColumnIndex(_) | Error::InvalidColumnIndex(_) |
Error::InvalidColumnName(_) | Error::InvalidColumnName(_) |
Error::InvalidColumnType | Error::InvalidColumnType(_, _) |
Error::InvalidPath(_) | Error::InvalidPath(_) |
Error::StatementChangedRows(_) => None, Error::StatementChangedRows(_) => None,
#[cfg(feature = "functions")] #[cfg(feature = "functions")]
Error::InvalidFunctionParameterType => None, Error::InvalidFunctionParameterType(_, _) => None,
#[cfg(feature = "functions")] #[cfg(feature = "functions")]
Error::UserFunctionError(ref err) => Some(&**err), Error::UserFunctionError(ref err) => Some(&**err),

View File

@ -54,125 +54,79 @@ use std::ffi::CStr;
use std::mem; use std::mem;
use std::ptr; use std::ptr;
use std::slice; use std::slice;
use libc::{c_int, c_double, c_char, c_void}; use libc::{c_int, c_char, c_void};
use ffi; use ffi;
pub use ffi::sqlite3_context; use ffi::sqlite3_context;
pub use ffi::sqlite3_value; use ffi::sqlite3_value;
pub use ffi::sqlite3_value_type;
pub use ffi::sqlite3_value_numeric_type;
use types::{Null, FromSql, ValueRef}; use types::{ToSql, ToSqlOutput, FromSql, FromSqlError, ValueRef};
use {Result, Error, Connection, str_to_cstring, InnerConnection}; use {Result, Error, Connection, str_to_cstring, InnerConnection};
/// A trait for types that can be converted into the result of an SQL function. pub fn set_result<'a>(ctx: *mut sqlite3_context, result: &ToSqlOutput<'a>) {
pub trait ToResult { let value = match *result {
unsafe fn set_result(&self, ctx: *mut sqlite3_context); ToSqlOutput::Borrowed(v) => v,
} ToSqlOutput::Owned(ref v) => ValueRef::from(v),
macro_rules! raw_to_impl( #[cfg(feature = "blob")]
($t:ty, $f:ident) => ( ToSqlOutput::ZeroBlob(len) => {
impl ToResult for $t { return unsafe { ffi::sqlite3_result_zeroblob(ctx, len) };
unsafe fn set_result(&self, ctx: *mut sqlite3_context) { }
ffi::$f(ctx, *self) };
match value {
ValueRef::Null => unsafe { ffi::sqlite3_result_null(ctx) },
ValueRef::Integer(i) => unsafe { ffi::sqlite3_result_int64(ctx, i) },
ValueRef::Real(r) => unsafe { ffi::sqlite3_result_double(ctx, r) },
ValueRef::Text(ref s) => unsafe {
let length = s.len();
if length > ::std::i32::MAX as usize {
ffi::sqlite3_result_error_toobig(ctx);
} else {
let c_str = match str_to_cstring(s) {
Ok(c_str) => c_str,
// TODO sqlite3_result_error
Err(_) => return ffi::sqlite3_result_error_code(ctx, ffi::SQLITE_MISUSE),
};
let destructor = if length > 0 {
ffi::SQLITE_TRANSIENT()
} else {
ffi::SQLITE_STATIC()
};
ffi::sqlite3_result_text(ctx, c_str.as_ptr(), length as c_int, destructor);
} }
} },
) ValueRef::Blob(ref b) => unsafe {
); let length = b.len();
if length > ::std::i32::MAX as usize {
raw_to_impl!(c_int, sqlite3_result_int); ffi::sqlite3_result_error_toobig(ctx);
raw_to_impl!(i64, sqlite3_result_int64); } else if length == 0 {
raw_to_impl!(c_double, sqlite3_result_double); ffi::sqlite3_result_zeroblob(ctx, 0)
} else {
impl<'a> ToResult for bool { ffi::sqlite3_result_blob(ctx,
unsafe fn set_result(&self, ctx: *mut sqlite3_context) { b.as_ptr() as *const c_void,
if *self {
ffi::sqlite3_result_int(ctx, 1)
} else {
ffi::sqlite3_result_int(ctx, 0)
}
}
}
impl<'a> ToResult for &'a str {
unsafe fn set_result(&self, ctx: *mut sqlite3_context) {
let length = self.len();
if length > ::std::i32::MAX as usize {
ffi::sqlite3_result_error_toobig(ctx);
return;
}
match str_to_cstring(self) {
Ok(c_str) => {
ffi::sqlite3_result_text(ctx,
c_str.as_ptr(),
length as c_int, length as c_int,
ffi::SQLITE_TRANSIENT()) ffi::SQLITE_TRANSIENT());
}
},
}
}
pub unsafe fn report_error(ctx: *mut sqlite3_context, err: &Error) {
match err {
&Error::SqliteFailure(ref err, ref s) => {
ffi::sqlite3_result_error_code(ctx, err.extended_code);
if let Some(Ok(cstr)) = s.as_ref().map(|s| str_to_cstring(s)) {
ffi::sqlite3_result_error(ctx, cstr.as_ptr(), -1);
} }
// TODO sqlite3_result_error
Err(_) => ffi::sqlite3_result_error_code(ctx, ffi::SQLITE_MISUSE),
} }
} _ => {
} ffi::sqlite3_result_error_code(ctx, ffi::SQLITE_CONSTRAINT_FUNCTION);
if let Ok(cstr) = str_to_cstring(err.description()) {
impl ToResult for String { ffi::sqlite3_result_error(ctx, cstr.as_ptr(), -1);
unsafe fn set_result(&self, ctx: *mut sqlite3_context) { }
(&self[..]).set_result(ctx)
}
}
impl<'a> ToResult for &'a [u8] {
unsafe fn set_result(&self, ctx: *mut sqlite3_context) {
if self.len() > ::std::i32::MAX as usize {
ffi::sqlite3_result_error_toobig(ctx);
return;
} }
ffi::sqlite3_result_blob(ctx,
mem::transmute(self.as_ptr()),
self.len() as c_int,
ffi::SQLITE_TRANSIENT())
}
}
impl ToResult for Vec<u8> {
unsafe fn set_result(&self, ctx: *mut sqlite3_context) {
(&self[..]).set_result(ctx)
}
}
impl<T: ToResult> ToResult for Option<T> {
unsafe fn set_result(&self, ctx: *mut sqlite3_context) {
match *self {
None => ffi::sqlite3_result_null(ctx),
Some(ref t) => t.set_result(ctx),
}
}
}
impl ToResult for Null {
unsafe fn set_result(&self, ctx: *mut sqlite3_context) {
ffi::sqlite3_result_null(ctx)
}
}
/// Error indicating that a memory allocation failed.
#[derive(Copy,Clone)]
pub struct NoMem;
impl ToResult for NoMem {
unsafe fn set_result(&self, ctx: *mut sqlite3_context) {
ffi::sqlite3_result_error_nomem(ctx)
}
}
/// Error indicating that a string or BLOB is too long to represent.
#[derive(Copy,Clone)]
pub struct TooBig;
impl ToResult for TooBig {
unsafe fn set_result(&self, ctx: *mut sqlite3_context) {
ffi::sqlite3_result_error_toobig(ctx)
} }
} }
@ -186,7 +140,8 @@ impl<'a> ValueRef<'a> {
ffi::SQLITE_FLOAT => ValueRef::Real(ffi::sqlite3_value_double(value)), ffi::SQLITE_FLOAT => ValueRef::Real(ffi::sqlite3_value_double(value)),
ffi::SQLITE_TEXT => { ffi::SQLITE_TEXT => {
let text = ffi::sqlite3_value_text(value); let text = ffi::sqlite3_value_text(value);
assert!(!text.is_null(), "unexpected SQLITE_TEXT value type with NULL data"); assert!(!text.is_null(),
"unexpected SQLITE_TEXT value type with NULL data");
let s = CStr::from_ptr(text as *const c_char); let s = CStr::from_ptr(text as *const c_char);
// sqlite3_value_text returns UTF8 data, so our unwrap here should be fine. // sqlite3_value_text returns UTF8 data, so our unwrap here should be fine.
@ -195,10 +150,12 @@ impl<'a> ValueRef<'a> {
} }
ffi::SQLITE_BLOB => { ffi::SQLITE_BLOB => {
let blob = ffi::sqlite3_value_blob(value); let blob = ffi::sqlite3_value_blob(value);
assert!(!blob.is_null(), "unexpected SQLITE_BLOB value type with NULL data"); assert!(!blob.is_null(),
"unexpected SQLITE_BLOB value type with NULL data");
let len = ffi::sqlite3_value_bytes(value); let len = ffi::sqlite3_value_bytes(value);
assert!(len >= 0, "unexpected negative return from sqlite3_value_bytes"); assert!(len >= 0,
"unexpected negative return from sqlite3_value_bytes");
ValueRef::Blob(from_raw_parts(blob as *const u8, len as usize)) ValueRef::Blob(from_raw_parts(blob as *const u8, len as usize))
} }
@ -238,8 +195,12 @@ impl<'a> Context<'a> {
let arg = self.args[idx]; let arg = self.args[idx];
let value = unsafe { ValueRef::from_value(arg) }; let value = unsafe { ValueRef::from_value(arg) };
FromSql::column_result(value).map_err(|err| match err { FromSql::column_result(value).map_err(|err| match err {
Error::InvalidColumnType => Error::InvalidFunctionParameterType, FromSqlError::InvalidType => {
_ => err, Error::InvalidFunctionParameterType(idx, value.data_type())
}
FromSqlError::Other(err) => {
Error::FromSqlConversionFailure(idx, value.data_type(), err)
}
}) })
} }
@ -275,7 +236,7 @@ impl<'a> Context<'a> {
/// `A` is the type of the aggregation context and `T` is the type of the final result. /// `A` is the type of the aggregation context and `T` is the type of the final result.
/// Implementations should be stateless. /// Implementations should be stateless.
pub trait Aggregate<A, T> pub trait Aggregate<A, T>
where T: ToResult where T: ToSql
{ {
/// Initializes the aggregation context. Will be called prior to the first call /// Initializes the aggregation context. Will be called prior to the first call
/// to `step()` to set up the context for an invocation of the function. (Note: /// to `step()` to set up the context for an invocation of the function. (Note:
@ -309,10 +270,9 @@ impl Connection {
/// ///
/// ```rust /// ```rust
/// # use rusqlite::{Connection, Result}; /// # use rusqlite::{Connection, Result};
/// # type c_double = f64;
/// fn scalar_function_example(db: Connection) -> Result<()> { /// fn scalar_function_example(db: Connection) -> Result<()> {
/// try!(db.create_scalar_function("halve", 1, true, |ctx| { /// try!(db.create_scalar_function("halve", 1, true, |ctx| {
/// let value = try!(ctx.get::<c_double>(0)); /// let value = try!(ctx.get::<f64>(0));
/// Ok(value / 2f64) /// Ok(value / 2f64)
/// })); /// }));
/// ///
@ -332,7 +292,7 @@ impl Connection {
x_func: F) x_func: F)
-> Result<()> -> Result<()>
where F: FnMut(&Context) -> Result<T>, where F: FnMut(&Context) -> Result<T>,
T: ToResult T: ToSql
{ {
self.db.borrow_mut().create_scalar_function(fn_name, n_arg, deterministic, x_func) self.db.borrow_mut().create_scalar_function(fn_name, n_arg, deterministic, x_func)
} }
@ -349,7 +309,7 @@ impl Connection {
aggr: D) aggr: D)
-> Result<()> -> Result<()>
where D: Aggregate<A, T>, where D: Aggregate<A, T>,
T: ToResult T: ToSql
{ {
self.db self.db
.borrow_mut() .borrow_mut()
@ -377,13 +337,13 @@ impl InnerConnection {
x_func: F) x_func: F)
-> Result<()> -> Result<()>
where F: FnMut(&Context) -> Result<T>, where F: FnMut(&Context) -> Result<T>,
T: ToResult T: ToSql
{ {
unsafe extern "C" fn call_boxed_closure<F, T>(ctx: *mut sqlite3_context, unsafe extern "C" fn call_boxed_closure<F, T>(ctx: *mut sqlite3_context,
argc: c_int, argc: c_int,
argv: *mut *mut sqlite3_value) argv: *mut *mut sqlite3_value)
where F: FnMut(&Context) -> Result<T>, where F: FnMut(&Context) -> Result<T>,
T: ToResult T: ToSql
{ {
let ctx = Context { let ctx = Context {
ctx: ctx, ctx: ctx,
@ -391,20 +351,14 @@ impl InnerConnection {
}; };
let boxed_f: *mut F = mem::transmute(ffi::sqlite3_user_data(ctx.ctx)); let boxed_f: *mut F = mem::transmute(ffi::sqlite3_user_data(ctx.ctx));
assert!(!boxed_f.is_null(), "Internal error - null function pointer"); assert!(!boxed_f.is_null(), "Internal error - null function pointer");
match (*boxed_f)(&ctx) {
Ok(r) => r.set_result(ctx.ctx), let t = (*boxed_f)(&ctx);
Err(Error::SqliteFailure(err, s)) => { let t = t.as_ref().map(|t| ToSql::to_sql(t));
ffi::sqlite3_result_error_code(ctx.ctx, err.extended_code);
if let Some(Ok(cstr)) = s.map(|s| str_to_cstring(&s)) { match t {
ffi::sqlite3_result_error(ctx.ctx, cstr.as_ptr(), -1); Ok(Ok(ref value)) => set_result(ctx.ctx, value),
} Ok(Err(err)) => report_error(ctx.ctx, &err),
} Err(err) => report_error(ctx.ctx, err),
Err(err) => {
ffi::sqlite3_result_error_code(ctx.ctx, ffi::SQLITE_CONSTRAINT_FUNCTION);
if let Ok(cstr) = str_to_cstring(err.description()) {
ffi::sqlite3_result_error(ctx.ctx, cstr.as_ptr(), -1);
}
}
} }
} }
@ -435,7 +389,7 @@ impl InnerConnection {
aggr: D) aggr: D)
-> Result<()> -> Result<()>
where D: Aggregate<A, T>, where D: Aggregate<A, T>,
T: ToResult T: ToSql
{ {
unsafe fn aggregate_context<A>(ctx: *mut sqlite3_context, unsafe fn aggregate_context<A>(ctx: *mut sqlite3_context,
bytes: usize) bytes: usize)
@ -447,28 +401,11 @@ impl InnerConnection {
Some(pac) Some(pac)
} }
unsafe fn report_aggregate_error(ctx: *mut sqlite3_context, err: Error) {
match err {
Error::SqliteFailure(err, s) => {
ffi::sqlite3_result_error_code(ctx, err.extended_code);
if let Some(Ok(cstr)) = s.map(|s| str_to_cstring(&s)) {
ffi::sqlite3_result_error(ctx, cstr.as_ptr(), -1);
}
}
_ => {
ffi::sqlite3_result_error_code(ctx, ffi::SQLITE_CONSTRAINT_FUNCTION);
if let Ok(cstr) = str_to_cstring(err.description()) {
ffi::sqlite3_result_error(ctx, cstr.as_ptr(), -1);
}
}
}
}
unsafe extern "C" fn call_boxed_step<A, D, T>(ctx: *mut sqlite3_context, unsafe extern "C" fn call_boxed_step<A, D, T>(ctx: *mut sqlite3_context,
argc: c_int, argc: c_int,
argv: *mut *mut sqlite3_value) argv: *mut *mut sqlite3_value)
where D: Aggregate<A, T>, where D: Aggregate<A, T>,
T: ToResult T: ToSql
{ {
let boxed_aggr: *mut D = mem::transmute(ffi::sqlite3_user_data(ctx)); let boxed_aggr: *mut D = mem::transmute(ffi::sqlite3_user_data(ctx));
assert!(!boxed_aggr.is_null(), assert!(!boxed_aggr.is_null(),
@ -493,13 +430,13 @@ impl InnerConnection {
match (*boxed_aggr).step(&mut ctx, &mut **pac) { match (*boxed_aggr).step(&mut ctx, &mut **pac) {
Ok(_) => {} Ok(_) => {}
Err(err) => report_aggregate_error(ctx.ctx, err), Err(err) => report_error(ctx.ctx, &err),
}; };
} }
unsafe extern "C" fn call_boxed_final<A, D, T>(ctx: *mut sqlite3_context) unsafe extern "C" fn call_boxed_final<A, D, T>(ctx: *mut sqlite3_context)
where D: Aggregate<A, T>, where D: Aggregate<A, T>,
T: ToResult T: ToSql
{ {
let boxed_aggr: *mut D = mem::transmute(ffi::sqlite3_user_data(ctx)); let boxed_aggr: *mut D = mem::transmute(ffi::sqlite3_user_data(ctx));
assert!(!boxed_aggr.is_null(), assert!(!boxed_aggr.is_null(),
@ -519,10 +456,13 @@ impl InnerConnection {
None => None, None => None,
}; };
match (*boxed_aggr).finalize(a) { let t = (*boxed_aggr).finalize(a);
Ok(r) => r.set_result(ctx), let t = t.as_ref().map(|t| ToSql::to_sql(t));
Err(err) => report_aggregate_error(ctx, err), match t {
}; Ok(Ok(ref value)) => set_result(ctx, value),
Ok(Err(err)) => report_error(ctx, &err),
Err(err) => report_error(ctx, err),
}
} }
let boxed_aggr: *mut D = Box::into_raw(Box::new(aggr)); let boxed_aggr: *mut D = Box::into_raw(Box::new(aggr));
@ -725,9 +665,10 @@ mod test {
}) })
.unwrap(); .unwrap();
for &(expected, query) in &[("", "SELECT my_concat()"), for &(expected, query) in
("onetwo", "SELECT my_concat('one', 'two')"), &[("", "SELECT my_concat()"),
("abc", "SELECT my_concat('a', 'b', 'c')")] { ("onetwo", "SELECT my_concat('one', 'two')"),
("abc", "SELECT my_concat('a', 'b', 'c')")] {
let result: String = db.query_row(query, &[], |r| r.get(0)).unwrap(); let result: String = db.query_row(query, &[], |r| r.get(0)).unwrap();
assert_eq!(expected, result); assert_eq!(expected, result);
} }

View File

@ -32,11 +32,11 @@
//! data: None //! data: None
//! }; //! };
//! conn.execute("INSERT INTO person (name, time_created, data) //! conn.execute("INSERT INTO person (name, time_created, data)
//! VALUES ($1, $2, $3)", //! VALUES (?1, ?2, ?3)",
//! &[&me.name, &me.time_created, &me.data]).unwrap(); //! &[&me.name, &me.time_created, &me.data]).unwrap();
//! //!
//! let mut stmt = conn.prepare("SELECT id, name, time_created, data FROM person").unwrap(); //! let mut stmt = conn.prepare("SELECT id, name, time_created, data FROM person").unwrap();
//! let mut person_iter = stmt.query_map(&[], |row| { //! let person_iter = stmt.query_map(&[], |row| {
//! Person { //! Person {
//! id: row.get(0), //! id: row.get(0),
//! name: row.get(1), //! name: row.get(1),
@ -50,6 +50,7 @@
//! } //! }
//! } //! }
//! ``` //! ```
#![allow(unknown_lints)]
extern crate libc; extern crate libc;
extern crate libsqlite3_sys as ffi; extern crate libsqlite3_sys as ffi;
@ -71,9 +72,9 @@ use std::cell::RefCell;
use std::ffi::{CStr, CString}; use std::ffi::{CStr, CString};
use std::result; use std::result;
use std::str; use std::str;
use libc::{c_int, c_char}; use libc::{c_int, c_char, c_void};
use types::{ToSql, FromSql, ValueRef}; use types::{ToSql, ToSqlOutput, FromSql, FromSqlError, ValueRef};
use error::{error_from_sqlite_code, error_from_handle}; use error::{error_from_sqlite_code, error_from_handle};
use raw_statement::RawStatement; use raw_statement::RawStatement;
use cache::StatementCache; use cache::StatementCache;
@ -310,12 +311,10 @@ impl Connection {
/// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the /// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the
/// underlying SQLite call fails. /// underlying SQLite call fails.
pub fn query_row<T, F>(&self, sql: &str, params: &[&ToSql], f: F) -> Result<T> pub fn query_row<T, F>(&self, sql: &str, params: &[&ToSql], f: F) -> Result<T>
where F: FnOnce(Row) -> T where F: FnOnce(&Row) -> T
{ {
let mut stmt = try!(self.prepare(sql)); let mut stmt = try!(self.prepare(sql));
let mut rows = try!(stmt.query(params)); stmt.query_row(params, f)
rows.get_expected_row().map(f)
} }
/// Convenience method to execute a query that is expected to return a single row, /// Convenience method to execute a query that is expected to return a single row,
@ -346,13 +345,13 @@ impl Connection {
params: &[&ToSql], params: &[&ToSql],
f: F) f: F)
-> result::Result<T, E> -> result::Result<T, E>
where F: FnOnce(Row) -> result::Result<T, E>, where F: FnOnce(&Row) -> result::Result<T, E>,
E: convert::From<Error> E: convert::From<Error>
{ {
let mut stmt = try!(self.prepare(sql)); let mut stmt = try!(self.prepare(sql));
let mut rows = try!(stmt.query(params)); let mut rows = try!(stmt.query(params));
rows.get_expected_row().map_err(E::from).and_then(f) rows.get_expected_row().map_err(E::from).and_then(|r| f(&r))
} }
/// Convenience method to execute a query that is expected to return a single row. /// Convenience method to execute a query that is expected to return a single row.
@ -376,7 +375,7 @@ impl Connection {
/// does exactly the same thing. /// does exactly the same thing.
#[deprecated(since = "0.1.0", note = "Use query_row instead")] #[deprecated(since = "0.1.0", note = "Use query_row instead")]
pub fn query_row_safe<T, F>(&self, sql: &str, params: &[&ToSql], f: F) -> Result<T> pub fn query_row_safe<T, F>(&self, sql: &str, params: &[&ToSql], f: F) -> Result<T>
where F: FnOnce(Row) -> T where F: FnOnce(&Row) -> T
{ {
self.query_row(sql, params, f) self.query_row(sql, params, f)
} }
@ -412,6 +411,7 @@ impl Connection {
/// ///
/// Will return `Err` if the underlying SQLite call fails. /// Will return `Err` if the underlying SQLite call fails.
pub fn close(self) -> Result<()> { pub fn close(self) -> Result<()> {
self.flush_prepared_statement_cache();
let mut db = self.db.borrow_mut(); let mut db = self.db.borrow_mut();
db.close() db.close()
} }
@ -884,6 +884,55 @@ impl<'conn> Statement<'conn> {
self.finalize_() self.finalize_()
} }
fn bind_parameter(&self, param: &ToSql, col: c_int) -> Result<()> {
let value = try!(param.to_sql());
let ptr = unsafe { self.stmt.ptr() };
let value = match value {
ToSqlOutput::Borrowed(v) => v,
ToSqlOutput::Owned(ref v) => ValueRef::from(v),
#[cfg(feature = "blob")]
ToSqlOutput::ZeroBlob(len) => {
return self.conn
.decode_result(unsafe { ffi::sqlite3_bind_zeroblob(ptr, col, len) });
}
};
self.conn.decode_result(match value {
ValueRef::Null => unsafe { ffi::sqlite3_bind_null(ptr, col) },
ValueRef::Integer(i) => unsafe { ffi::sqlite3_bind_int64(ptr, col, i) },
ValueRef::Real(r) => unsafe { ffi::sqlite3_bind_double(ptr, col, r) },
ValueRef::Text(ref s) => unsafe {
let length = s.len();
if length > ::std::i32::MAX as usize {
ffi::SQLITE_TOOBIG
} else {
let c_str = try!(str_to_cstring(s));
let destructor = if length > 0 {
ffi::SQLITE_TRANSIENT()
} else {
ffi::SQLITE_STATIC()
};
ffi::sqlite3_bind_text(ptr, col, c_str.as_ptr(), length as c_int, destructor)
}
},
ValueRef::Blob(ref b) => unsafe {
let length = b.len();
if length > ::std::i32::MAX as usize {
ffi::SQLITE_TOOBIG
} else if length == 0 {
ffi::sqlite3_bind_zeroblob(ptr, col, 0)
} else {
ffi::sqlite3_bind_blob(ptr,
col,
b.as_ptr() as *const c_void,
length as c_int,
ffi::SQLITE_TRANSIENT())
}
},
})
}
fn bind_parameters(&mut self, params: &[&ToSql]) -> Result<()> { fn bind_parameters(&mut self, params: &[&ToSql]) -> Result<()> {
assert!(params.len() as c_int == self.stmt.bind_parameter_count(), assert!(params.len() as c_int == self.stmt.bind_parameter_count(),
"incorrect number of parameters to query(): expected {}, got {}", "incorrect number of parameters to query(): expected {}, got {}",
@ -891,9 +940,7 @@ impl<'conn> Statement<'conn> {
params.len()); params.len());
for (i, p) in params.iter().enumerate() { for (i, p) in params.iter().enumerate() {
try!(unsafe { try!(self.bind_parameter(*p, (i + 1) as c_int));
self.conn.decode_result(p.bind_parameter(self.stmt.ptr(), (i + 1) as c_int))
});
} }
Ok(()) Ok(())
@ -980,6 +1027,7 @@ pub struct Rows<'stmt> {
stmt: Option<&'stmt Statement<'stmt>>, stmt: Option<&'stmt Statement<'stmt>>,
} }
#[allow(should_implement_trait)]
impl<'stmt> Rows<'stmt> { impl<'stmt> Rows<'stmt> {
fn new(stmt: &'stmt Statement<'stmt>) -> Rows<'stmt> { fn new(stmt: &'stmt Statement<'stmt>) -> Rows<'stmt> {
Rows { stmt: Some(stmt) } Rows { stmt: Some(stmt) }
@ -1073,7 +1121,12 @@ impl<'a, 'stmt> Row<'a, 'stmt> {
pub fn get_checked<I: RowIndex, T: FromSql>(&self, idx: I) -> Result<T> { pub fn get_checked<I: RowIndex, T: FromSql>(&self, idx: I) -> Result<T> {
let idx = try!(idx.idx(self.stmt)); let idx = try!(idx.idx(self.stmt));
let value = unsafe { ValueRef::new(&self.stmt.stmt, idx) }; let value = unsafe { ValueRef::new(&self.stmt.stmt, idx) };
FromSql::column_result(value) FromSql::column_result(value).map_err(|err| match err {
FromSqlError::InvalidType => Error::InvalidColumnType(idx, value.data_type()),
FromSqlError::Other(err) => {
Error::FromSqlConversionFailure(idx as usize, value.data_type(), err)
}
})
} }
/// Return the number of columns in the current row. /// Return the number of columns in the current row.
@ -1141,7 +1194,6 @@ impl<'a> ValueRef<'a> {
// The return value from sqlite3_column_blob() for a zero-length BLOB is a NULL pointer. // The return value from sqlite3_column_blob() for a zero-length BLOB is a NULL pointer.
ValueRef::Blob(&[]) ValueRef::Blob(&[])
} }
} }
_ => unreachable!("sqlite3_column_type returned invalid value"), _ => unreachable!("sqlite3_column_type returned invalid value"),
} }
@ -1503,7 +1555,7 @@ mod test {
.collect(); .collect();
match bad_type.unwrap_err() { match bad_type.unwrap_err() {
Error::InvalidColumnType => (), Error::InvalidColumnType(_, _) => (),
err => panic!("Unexpected error {}", err), err => panic!("Unexpected error {}", err),
} }
@ -1563,7 +1615,7 @@ mod test {
.collect(); .collect();
match bad_type.unwrap_err() { match bad_type.unwrap_err() {
CustomError::Sqlite(Error::InvalidColumnType) => (), CustomError::Sqlite(Error::InvalidColumnType(_, _)) => (),
err => panic!("Unexpected error {}", err), err => panic!("Unexpected error {}", err),
} }
@ -1625,7 +1677,7 @@ mod test {
}); });
match bad_type.unwrap_err() { match bad_type.unwrap_err() {
CustomError::Sqlite(Error::InvalidColumnType) => (), CustomError::Sqlite(Error::InvalidColumnType(_, _)) => (),
err => panic!("Unexpected error {}", err), err => panic!("Unexpected error {}", err),
} }

View File

@ -38,12 +38,12 @@ impl Connection {
/// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the /// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the
/// underlying SQLite call fails. /// underlying SQLite call fails.
pub fn query_row_named<T, F>(&self, sql: &str, params: &[(&str, &ToSql)], f: F) -> Result<T> pub fn query_row_named<T, F>(&self, sql: &str, params: &[(&str, &ToSql)], f: F) -> Result<T>
where F: FnOnce(Row) -> T where F: FnOnce(&Row) -> T
{ {
let mut stmt = try!(self.prepare(sql)); let mut stmt = try!(self.prepare(sql));
let mut rows = try!(stmt.query_named(params)); let mut rows = try!(stmt.query_named(params));
rows.get_expected_row().map(f) rows.get_expected_row().map(|r| f(&r))
} }
} }
@ -94,7 +94,7 @@ impl<'conn> Statement<'conn> {
/// ## Example /// ## Example
/// ///
/// ```rust,no_run /// ```rust,no_run
/// # use rusqlite::{Connection, Result, Rows}; /// # use rusqlite::{Connection, Result};
/// fn query(conn: &Connection) -> Result<()> { /// fn query(conn: &Connection) -> Result<()> {
/// let mut stmt = try!(conn.prepare("SELECT * FROM test where name = :name")); /// let mut stmt = try!(conn.prepare("SELECT * FROM test where name = :name"));
/// let mut rows = try!(stmt.query_named(&[(":name", &"one")])); /// let mut rows = try!(stmt.query_named(&[(":name", &"one")]));
@ -204,7 +204,7 @@ impl<'conn> Statement<'conn> {
fn bind_parameters_named(&mut self, params: &[(&str, &ToSql)]) -> Result<()> { fn bind_parameters_named(&mut self, params: &[(&str, &ToSql)]) -> Result<()> {
for &(name, value) in params { for &(name, value) in params {
if let Some(i) = try!(self.parameter_index(name)) { if let Some(i) = try!(self.parameter_index(name)) {
try!(self.conn.decode_result(unsafe { value.bind_parameter(self.stmt.ptr(), i) })); try!(self.bind_parameter(value, i));
} else { } else {
return Err(Error::InvalidParameterName(name.into())); return Err(Error::InvalidParameterName(name.into()));
} }

View File

@ -44,8 +44,8 @@ pub type SqliteTransaction<'conn> = Transaction<'conn>;
/// ///
/// ```rust,no_run /// ```rust,no_run
/// # use rusqlite::{Connection, Result}; /// # use rusqlite::{Connection, Result};
/// # fn do_queries_part_1(conn: &Connection) -> Result<()> { Ok(()) } /// # fn do_queries_part_1(_conn: &Connection) -> Result<()> { Ok(()) }
/// # fn do_queries_part_2(conn: &Connection) -> Result<()> { Ok(()) } /// # fn do_queries_part_2(_conn: &Connection) -> Result<()> { Ok(()) }
/// fn perform_queries(conn: &mut Connection) -> Result<()> { /// fn perform_queries(conn: &mut Connection) -> Result<()> {
/// let tx = try!(conn.transaction()); /// let tx = try!(conn.transaction());
/// ///
@ -73,8 +73,8 @@ pub struct Transaction<'conn> {
/// ///
/// ```rust,no_run /// ```rust,no_run
/// # use rusqlite::{Connection, Result}; /// # use rusqlite::{Connection, Result};
/// # fn do_queries_part_1(conn: &Connection) -> Result<()> { Ok(()) } /// # fn do_queries_part_1(_conn: &Connection) -> Result<()> { Ok(()) }
/// # fn do_queries_part_2(conn: &Connection) -> Result<()> { Ok(()) } /// # fn do_queries_part_2(_conn: &Connection) -> Result<()> { Ok(()) }
/// fn perform_queries(conn: &mut Connection) -> Result<()> { /// fn perform_queries(conn: &mut Connection) -> Result<()> {
/// let sp = try!(conn.savepoint()); /// let sp = try!(conn.savepoint());
/// ///
@ -120,7 +120,7 @@ impl<'conn> Transaction<'conn> {
/// ///
/// ```rust,no_run /// ```rust,no_run
/// # use rusqlite::{Connection, Result}; /// # use rusqlite::{Connection, Result};
/// # fn perform_queries_part_1_succeeds(conn: &Connection) -> bool { true } /// # fn perform_queries_part_1_succeeds(_conn: &Connection) -> bool { true }
/// fn perform_queries(conn: &mut Connection) -> Result<()> { /// fn perform_queries(conn: &mut Connection) -> Result<()> {
/// let mut tx = try!(conn.transaction()); /// let mut tx = try!(conn.transaction());
/// ///
@ -328,8 +328,8 @@ impl Connection {
/// ///
/// ```rust,no_run /// ```rust,no_run
/// # use rusqlite::{Connection, Result}; /// # use rusqlite::{Connection, Result};
/// # fn do_queries_part_1(conn: &Connection) -> Result<()> { Ok(()) } /// # fn do_queries_part_1(_conn: &Connection) -> Result<()> { Ok(()) }
/// # fn do_queries_part_2(conn: &Connection) -> Result<()> { Ok(()) } /// # fn do_queries_part_2(_conn: &Connection) -> Result<()> { Ok(()) }
/// fn perform_queries(conn: &mut Connection) -> Result<()> { /// fn perform_queries(conn: &mut Connection) -> Result<()> {
/// let tx = try!(conn.transaction()); /// let tx = try!(conn.transaction());
/// ///
@ -369,8 +369,8 @@ impl Connection {
/// ///
/// ```rust,no_run /// ```rust,no_run
/// # use rusqlite::{Connection, Result}; /// # use rusqlite::{Connection, Result};
/// # fn do_queries_part_1(conn: &Connection) -> Result<()> { Ok(()) } /// # fn do_queries_part_1(_conn: &Connection) -> Result<()> { Ok(()) }
/// # fn do_queries_part_2(conn: &Connection) -> Result<()> { Ok(()) } /// # fn do_queries_part_2(_conn: &Connection) -> Result<()> { Ok(()) }
/// fn perform_queries(conn: &mut Connection) -> Result<()> { /// fn perform_queries(conn: &mut Connection) -> Result<()> {
/// let sp = try!(conn.savepoint()); /// let sp = try!(conn.savepoint());
/// ///
@ -401,7 +401,6 @@ impl Connection {
} }
#[cfg(test)] #[cfg(test)]
#[cfg_attr(feature="clippy", allow(similar_names))]
mod test { mod test {
use Connection; use Connection;
use super::DropBehavior; use super::DropBehavior;

View File

@ -4,42 +4,39 @@ extern crate chrono;
use std::borrow::Cow; use std::borrow::Cow;
use self::chrono::{NaiveDate, NaiveTime, NaiveDateTime, DateTime, TimeZone, UTC, Local}; use self::chrono::{NaiveDate, NaiveTime, NaiveDateTime, DateTime, TimeZone, UTC, Local};
use libc::c_int;
use {Error, Result}; use ::Result;
use types::{FromSql, ToSql, ValueRef}; use types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
use ffi::sqlite3_stmt;
/// ISO 8601 calendar date without timezone => "YYYY-MM-DD" /// ISO 8601 calendar date without timezone => "YYYY-MM-DD"
impl ToSql for NaiveDate { impl ToSql for NaiveDate {
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int { fn to_sql(&self) -> Result<ToSqlOutput> {
let date_str = self.format("%Y-%m-%d").to_string(); let date_str = self.format("%Y-%m-%d").to_string();
date_str.bind_parameter(stmt, col) Ok(ToSqlOutput::from(date_str))
} }
} }
/// "YYYY-MM-DD" => ISO 8601 calendar date without timezone. /// "YYYY-MM-DD" => ISO 8601 calendar date without timezone.
impl FromSql for NaiveDate { impl FromSql for NaiveDate {
fn column_result(value: ValueRef) -> Result<Self> { fn column_result(value: ValueRef) -> FromSqlResult<Self> {
value.as_str().and_then(|s| match NaiveDate::parse_from_str(s, "%Y-%m-%d") { value.as_str().and_then(|s| match NaiveDate::parse_from_str(s, "%Y-%m-%d") {
Ok(dt) => Ok(dt), Ok(dt) => Ok(dt),
Err(err) => Err(Error::FromSqlConversionFailure(Box::new(err))), Err(err) => Err(FromSqlError::Other(Box::new(err))),
}) })
} }
} }
/// ISO 8601 time without timezone => "HH:MM:SS.SSS" /// ISO 8601 time without timezone => "HH:MM:SS.SSS"
impl ToSql for NaiveTime { impl ToSql for NaiveTime {
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int { fn to_sql(&self) -> Result<ToSqlOutput> {
let date_str = self.format("%H:%M:%S%.f").to_string(); let date_str = self.format("%H:%M:%S%.f").to_string();
date_str.bind_parameter(stmt, col) Ok(ToSqlOutput::from(date_str))
} }
} }
/// "HH:MM"/"HH:MM:SS"/"HH:MM:SS.SSS" => ISO 8601 time without timezone. /// "HH:MM"/"HH:MM:SS"/"HH:MM:SS.SSS" => ISO 8601 time without timezone.
impl FromSql for NaiveTime { impl FromSql for NaiveTime {
fn column_result(value: ValueRef) -> Result<Self> { fn column_result(value: ValueRef) -> FromSqlResult<Self> {
value.as_str().and_then(|s| { value.as_str().and_then(|s| {
let fmt = match s.len() { let fmt = match s.len() {
5 => "%H:%M", 5 => "%H:%M",
@ -48,7 +45,7 @@ impl FromSql for NaiveTime {
}; };
match NaiveTime::parse_from_str(s, fmt) { match NaiveTime::parse_from_str(s, fmt) {
Ok(dt) => Ok(dt), Ok(dt) => Ok(dt),
Err(err) => Err(Error::FromSqlConversionFailure(Box::new(err))), Err(err) => Err(FromSqlError::Other(Box::new(err))),
} }
}) })
} }
@ -56,16 +53,16 @@ impl FromSql for NaiveTime {
/// ISO 8601 combined date and time without timezone => "YYYY-MM-DD HH:MM:SS.SSS" /// ISO 8601 combined date and time without timezone => "YYYY-MM-DD HH:MM:SS.SSS"
impl ToSql for NaiveDateTime { impl ToSql for NaiveDateTime {
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int { fn to_sql(&self) -> Result<ToSqlOutput> {
let date_str = self.format("%Y-%m-%dT%H:%M:%S%.f").to_string(); let date_str = self.format("%Y-%m-%dT%H:%M:%S%.f").to_string();
date_str.bind_parameter(stmt, col) Ok(ToSqlOutput::from(date_str))
} }
} }
/// "YYYY-MM-DD HH:MM:SS"/"YYYY-MM-DD HH:MM:SS.SSS" => ISO 8601 combined date and time /// "YYYY-MM-DD HH:MM:SS"/"YYYY-MM-DD HH:MM:SS.SSS" => ISO 8601 combined date and time
/// without timezone. ("YYYY-MM-DDTHH:MM:SS"/"YYYY-MM-DDTHH:MM:SS.SSS" also supported) /// without timezone. ("YYYY-MM-DDTHH:MM:SS"/"YYYY-MM-DDTHH:MM:SS.SSS" also supported)
impl FromSql for NaiveDateTime { impl FromSql for NaiveDateTime {
fn column_result(value: ValueRef) -> Result<Self> { fn column_result(value: ValueRef) -> FromSqlResult<Self> {
value.as_str().and_then(|s| { value.as_str().and_then(|s| {
let fmt = if s.len() >= 11 && s.as_bytes()[10] == b'T' { let fmt = if s.len() >= 11 && s.as_bytes()[10] == b'T' {
"%Y-%m-%dT%H:%M:%S%.f" "%Y-%m-%dT%H:%M:%S%.f"
@ -75,7 +72,7 @@ impl FromSql for NaiveDateTime {
match NaiveDateTime::parse_from_str(s, fmt) { match NaiveDateTime::parse_from_str(s, fmt) {
Ok(dt) => Ok(dt), Ok(dt) => Ok(dt),
Err(err) => Err(Error::FromSqlConversionFailure(Box::new(err))), Err(err) => Err(FromSqlError::Other(Box::new(err))),
} }
}) })
} }
@ -83,15 +80,14 @@ impl FromSql for NaiveDateTime {
/// Date and time with time zone => UTC RFC3339 timestamp ("YYYY-MM-DDTHH:MM:SS.SSS+00:00"). /// Date and time with time zone => UTC RFC3339 timestamp ("YYYY-MM-DDTHH:MM:SS.SSS+00:00").
impl<Tz: TimeZone> ToSql for DateTime<Tz> { impl<Tz: TimeZone> ToSql for DateTime<Tz> {
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int { fn to_sql(&self) -> Result<ToSqlOutput> {
let utc_dt = self.with_timezone(&UTC); Ok(ToSqlOutput::from(self.with_timezone(&UTC).to_rfc3339()))
utc_dt.to_rfc3339().bind_parameter(stmt, col)
} }
} }
/// RFC3339 ("YYYY-MM-DDTHH:MM:SS.SSS[+-]HH:MM") into DateTime<UTC>. /// RFC3339 ("YYYY-MM-DDTHH:MM:SS.SSS[+-]HH:MM") into DateTime<UTC>.
impl FromSql for DateTime<UTC> { impl FromSql for DateTime<UTC> {
fn column_result(value: ValueRef) -> Result<Self> { fn column_result(value: ValueRef) -> FromSqlResult<Self> {
{ {
// Try to parse value as rfc3339 first. // Try to parse value as rfc3339 first.
let s = try!(value.as_str()); let s = try!(value.as_str());
@ -121,7 +117,7 @@ impl FromSql for DateTime<UTC> {
/// RFC3339 ("YYYY-MM-DDTHH:MM:SS.SSS[+-]HH:MM") into DateTime<Local>. /// RFC3339 ("YYYY-MM-DDTHH:MM:SS.SSS[+-]HH:MM") into DateTime<Local>.
impl FromSql for DateTime<Local> { impl FromSql for DateTime<Local> {
fn column_result(value: ValueRef) -> Result<Self> { fn column_result(value: ValueRef) -> FromSqlResult<Self> {
let utc_dt = try!(DateTime::<UTC>::column_result(value)); let utc_dt = try!(DateTime::<UTC>::column_result(value));
Ok(utc_dt.with_timezone(&Local)) Ok(utc_dt.with_timezone(&Local))
} }

View File

@ -1,36 +1,75 @@
use super::{ValueRef, Value}; use super::{ValueRef, Value};
use ::Result; use std::error::Error;
use ::error::Error; use std::fmt;
/// Enum listing possible errors from `FromSql` trait.
#[derive(Debug)]
pub enum FromSqlError {
/// Error when an SQLite value is requested, but the type of the result cannot be converted to the
/// requested Rust type.
InvalidType,
/// An error case available for implementors of the `FromSql` trait.
Other(Box<Error + Send + Sync>),
}
impl fmt::Display for FromSqlError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
FromSqlError::InvalidType => write!(f, "Invalid type"),
FromSqlError::Other(ref err) => err.fmt(f),
}
}
}
impl Error for FromSqlError {
fn description(&self) -> &str {
match *self {
FromSqlError::InvalidType => "invalid type",
FromSqlError::Other(ref err) => err.description(),
}
}
#[cfg_attr(feature="clippy", allow(match_same_arms))]
fn cause(&self) -> Option<&Error> {
match *self {
FromSqlError::InvalidType => None,
FromSqlError::Other(ref err) => err.cause(),
}
}
}
/// Result type for implementors of the `FromSql` trait.
pub type FromSqlResult<T> = Result<T, FromSqlError>;
/// A trait for types that can be created from a SQLite value. /// A trait for types that can be created from a SQLite value.
pub trait FromSql: Sized { pub trait FromSql: Sized {
fn column_result(value: ValueRef) -> Result<Self>; fn column_result(value: ValueRef) -> FromSqlResult<Self>;
} }
impl FromSql for i32 { impl FromSql for i32 {
fn column_result(value: ValueRef) -> Result<Self> { fn column_result(value: ValueRef) -> FromSqlResult<Self> {
i64::column_result(value).map(|i| i as i32) i64::column_result(value).map(|i| i as i32)
} }
} }
impl FromSql for i64 { impl FromSql for i64 {
fn column_result(value: ValueRef) -> Result<Self> { fn column_result(value: ValueRef) -> FromSqlResult<Self> {
value.as_i64() value.as_i64()
} }
} }
impl FromSql for f64 { impl FromSql for f64 {
fn column_result(value: ValueRef) -> Result<Self> { fn column_result(value: ValueRef) -> FromSqlResult<Self> {
match value { match value {
ValueRef::Integer(i) => Ok(i as f64), ValueRef::Integer(i) => Ok(i as f64),
ValueRef::Real(f) => Ok(f), ValueRef::Real(f) => Ok(f),
_ => Err(Error::InvalidColumnType), _ => Err(FromSqlError::InvalidType),
} }
} }
} }
impl FromSql for bool { impl FromSql for bool {
fn column_result(value: ValueRef) -> Result<Self> { fn column_result(value: ValueRef) -> FromSqlResult<Self> {
i64::column_result(value).map(|i| match i { i64::column_result(value).map(|i| match i {
0 => false, 0 => false,
_ => true, _ => true,
@ -39,19 +78,19 @@ impl FromSql for bool {
} }
impl FromSql for String { impl FromSql for String {
fn column_result(value: ValueRef) -> Result<Self> { fn column_result(value: ValueRef) -> FromSqlResult<Self> {
value.as_str().map(|s| s.to_string()) value.as_str().map(|s| s.to_string())
} }
} }
impl FromSql for Vec<u8> { impl FromSql for Vec<u8> {
fn column_result(value: ValueRef) -> Result<Self> { fn column_result(value: ValueRef) -> FromSqlResult<Self> {
value.as_blob().map(|b| b.to_vec()) value.as_blob().map(|b| b.to_vec())
} }
} }
impl<T: FromSql> FromSql for Option<T> { impl<T: FromSql> FromSql for Option<T> {
fn column_result(value: ValueRef) -> Result<Self> { fn column_result(value: ValueRef) -> FromSqlResult<Self> {
match value { match value {
ValueRef::Null => Ok(None), ValueRef::Null => Ok(None),
_ => FromSql::column_result(value).map(Some), _ => FromSql::column_result(value).map(Some),
@ -60,7 +99,7 @@ impl<T: FromSql> FromSql for Option<T> {
} }
impl FromSql for Value { impl FromSql for Value {
fn column_result(value: ValueRef) -> Result<Self> { fn column_result(value: ValueRef) -> FromSqlResult<Self> {
Ok(value.into()) Ok(value.into())
} }
} }

View File

@ -50,12 +50,14 @@
//! `FromSql` for the cases where you want to know if a value was NULL (which gets translated to //! `FromSql` for the cases where you want to know if a value was NULL (which gets translated to
//! `None`). //! `None`).
pub use ffi::sqlite3_stmt; pub use self::from_sql::{FromSql, FromSqlError, FromSqlResult};
pub use self::to_sql::{ToSql, ToSqlOutput};
pub use self::from_sql::FromSql; pub use self::value::Value;
pub use self::to_sql::ToSql;
pub use self::value_ref::ValueRef; pub use self::value_ref::ValueRef;
use std::fmt;
mod value;
mod value_ref; mod value_ref;
mod from_sql; mod from_sql;
mod to_sql; mod to_sql;
@ -84,27 +86,28 @@ mod serde_json;
#[derive(Copy,Clone)] #[derive(Copy,Clone)]
pub struct Null; pub struct Null;
/// Owning [dynamic type value](http://sqlite.org/datatype3.html). Value's type is typically
/// dictated by SQLite (not by the caller).
///
/// See [`ValueRef`](enum.ValueRef.html) for a non-owning dynamic type value.
#[derive(Clone,Debug,PartialEq)] #[derive(Clone,Debug,PartialEq)]
pub enum Value { pub enum Type {
/// The value is a `NULL` value.
Null, Null,
/// The value is a signed integer. Integer,
Integer(i64), Real,
/// The value is a floating point number. Text,
Real(f64), Blob,
/// The value is a text string. }
Text(String),
/// The value is a blob of data impl fmt::Display for Type {
Blob(Vec<u8>), fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Type::Null => write!(f, "Null"),
Type::Integer => write!(f, "Integer"),
Type::Real => write!(f, "Real"),
Type::Text => write!(f, "Text"),
Type::Blob => write!(f, "Blob"),
}
}
} }
#[cfg(test)] #[cfg(test)]
#[cfg_attr(feature="clippy", allow(similar_names))]
mod test { mod test {
extern crate time; extern crate time;
@ -112,6 +115,7 @@ mod test {
use Error; use Error;
use libc::{c_int, c_double}; use libc::{c_int, c_double};
use std::f64::EPSILON; use std::f64::EPSILON;
use super::Value;
fn checked_memory_handle() -> Connection { fn checked_memory_handle() -> Connection {
let db = Connection::open_in_memory().unwrap(); let db = Connection::open_in_memory().unwrap();
@ -145,6 +149,17 @@ mod test {
fn test_str() { fn test_str() {
let db = checked_memory_handle(); let db = checked_memory_handle();
let s = "hello, world!";
db.execute("INSERT INTO foo(t) VALUES (?)", &[&s]).unwrap();
let from: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap();
assert_eq!(from, s);
}
#[test]
fn test_string() {
let db = checked_memory_handle();
let s = "hello, world!"; let s = "hello, world!";
db.execute("INSERT INTO foo(t) VALUES (?)", &[&s.to_owned()]).unwrap(); db.execute("INSERT INTO foo(t) VALUES (?)", &[&s.to_owned()]).unwrap();
@ -152,6 +167,16 @@ mod test {
assert_eq!(from, s); assert_eq!(from, s);
} }
#[test]
fn test_value() {
let db = checked_memory_handle();
db.execute("INSERT INTO foo(i) VALUES (?)", &[&Value::Integer(10)]).unwrap();
assert_eq!(10i64,
db.query_row("SELECT i FROM foo", &[], |r| r.get(0)).unwrap());
}
#[test] #[test]
fn test_option() { fn test_option() {
let db = checked_memory_handle(); let db = checked_memory_handle();
@ -183,11 +208,10 @@ mod test {
} }
#[test] #[test]
#[cfg_attr(feature="clippy", allow(cyclomatic_complexity))]
fn test_mismatched_types() { fn test_mismatched_types() {
fn is_invalid_column_type(err: Error) -> bool { fn is_invalid_column_type(err: Error) -> bool {
match err { match err {
Error::InvalidColumnType => true, Error::InvalidColumnType(_, _) => true,
_ => false, _ => false,
} }
} }

View File

@ -1,31 +1,27 @@
//! `ToSql` and `FromSql` implementation for JSON `Value`. //! `ToSql` and `FromSql` implementation for JSON `Value`.
extern crate serde_json; extern crate serde_json;
use libc::c_int;
use self::serde_json::Value; use self::serde_json::Value;
use {Error, Result}; use ::Result;
use types::{FromSql, ToSql, ValueRef}; use types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
use ffi::sqlite3_stmt;
/// Serialize JSON `Value` to text. /// Serialize JSON `Value` to text.
impl ToSql for Value { impl ToSql for Value {
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int { fn to_sql(&self) -> Result<ToSqlOutput> {
let s = serde_json::to_string(self).unwrap(); Ok(ToSqlOutput::from(serde_json::to_string(self).unwrap()))
s.bind_parameter(stmt, col)
} }
} }
/// Deserialize text/blob to JSON `Value`. /// Deserialize text/blob to JSON `Value`.
impl FromSql for Value { impl FromSql for Value {
fn column_result(value: ValueRef) -> Result<Self> { fn column_result(value: ValueRef) -> FromSqlResult<Self> {
match value { match value {
ValueRef::Text(ref s) => serde_json::from_str(s), ValueRef::Text(ref s) => serde_json::from_str(s),
ValueRef::Blob(ref b) => serde_json::from_slice(b), ValueRef::Blob(ref b) => serde_json::from_slice(b),
_ => return Err(Error::InvalidColumnType), _ => return Err(FromSqlError::InvalidType),
} }
.map_err(|err| Error::FromSqlConversionFailure(Box::new(err))) .map_err(|err| FromSqlError::Other(Box::new(err)))
} }
} }

View File

@ -1,25 +1,22 @@
extern crate time; extern crate time;
use libc::c_int; use Result;
use {Error, Result}; use types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
use types::{FromSql, ToSql, ValueRef};
use ffi::sqlite3_stmt;
const SQLITE_DATETIME_FMT: &'static str = "%Y-%m-%d %H:%M:%S"; const SQLITE_DATETIME_FMT: &'static str = "%Y-%m-%d %H:%M:%S";
impl ToSql for time::Timespec { impl ToSql for time::Timespec {
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int { fn to_sql(&self) -> Result<ToSqlOutput> {
let time_str = time::at_utc(*self).strftime(SQLITE_DATETIME_FMT).unwrap().to_string(); let time_string = time::at_utc(*self).strftime(SQLITE_DATETIME_FMT).unwrap().to_string();
time_str.bind_parameter(stmt, col) Ok(ToSqlOutput::from(time_string))
} }
} }
impl FromSql for time::Timespec { impl FromSql for time::Timespec {
fn column_result(value: ValueRef) -> Result<Self> { fn column_result(value: ValueRef) -> FromSqlResult<Self> {
value.as_str().and_then(|s| match time::strptime(s, SQLITE_DATETIME_FMT) { value.as_str().and_then(|s| match time::strptime(s, SQLITE_DATETIME_FMT) {
Ok(tm) => Ok(tm.to_timespec()), Ok(tm) => Ok(tm.to_timespec()),
Err(err) => Err(Error::FromSqlConversionFailure(Box::new(err))), Err(err) => Err(FromSqlError::Other(Box::new(err))),
}) })
} }
} }

View File

@ -1,95 +1,97 @@
use std::mem; use super::{Null, Value, ValueRef};
use ::Result;
use libc::{c_double, c_int}; /// `ToSqlOutput` represents the possible output types for implementors of the `ToSql` trait.
pub enum ToSqlOutput<'a> {
/// A borrowed SQLite-representable value.
Borrowed(ValueRef<'a>),
use super::Null; /// An owned SQLite-representable value.
use ::{ffi, str_to_cstring}; Owned(Value),
use ffi::sqlite3_stmt;
/// A BLOB of the given length that is filled with zeroes.
#[cfg(feature = "blob")]
ZeroBlob(i32),
}
impl<'a, T: ?Sized> From<&'a T> for ToSqlOutput<'a>
where &'a T: Into<ValueRef<'a>>
{
fn from(t: &'a T) -> Self {
ToSqlOutput::Borrowed(t.into())
}
}
impl<'a, T: Into<Value>> From<T> for ToSqlOutput<'a> {
fn from(t: T) -> Self {
ToSqlOutput::Owned(t.into())
}
}
/// A trait for types that can be converted into SQLite values. /// A trait for types that can be converted into SQLite values.
pub trait ToSql { pub trait ToSql {
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int; fn to_sql(&self) -> Result<ToSqlOutput>;
} }
macro_rules! raw_to_impl( // We should be able to use a generic impl like this:
($t:ty, $f:ident) => ( //
// impl<T: Copy> ToSql for T where T: Into<Value> {
// fn to_sql(&self) -> Result<ToSqlOutput> {
// Ok(ToSqlOutput::from((*self).into()))
// }
// }
//
// instead of the following macro, but this runs afoul of
// https://github.com/rust-lang/rust/issues/30191 and reports conflicting
// implementations even when there aren't any.
macro_rules! to_sql_self(
($t:ty) => (
impl ToSql for $t { impl ToSql for $t {
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int { fn to_sql(&self) -> Result<ToSqlOutput> {
ffi::$f(stmt, col, *self) Ok(ToSqlOutput::from(*self))
} }
} }
) )
); );
raw_to_impl!(c_int, sqlite3_bind_int); // i32 to_sql_self!(Null);
raw_to_impl!(i64, sqlite3_bind_int64); to_sql_self!(bool);
raw_to_impl!(c_double, sqlite3_bind_double); to_sql_self!(i32);
to_sql_self!(i64);
to_sql_self!(f64);
impl ToSql for bool { impl<'a, T: ?Sized> ToSql for &'a T
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int { where &'a T: Into<ToSqlOutput<'a>>
if *self { {
ffi::sqlite3_bind_int(stmt, col, 1) fn to_sql(&self) -> Result<ToSqlOutput> {
} else { Ok(ToSqlOutput::from((*self).into()))
ffi::sqlite3_bind_int(stmt, col, 0)
}
}
}
impl<'a> ToSql for &'a str {
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int {
let length = self.len();
if length > ::std::i32::MAX as usize {
return ffi::SQLITE_TOOBIG;
}
match str_to_cstring(self) {
Ok(c_str) => {
ffi::sqlite3_bind_text(stmt,
col,
c_str.as_ptr(),
length as c_int,
ffi::SQLITE_TRANSIENT())
}
Err(_) => ffi::SQLITE_MISUSE,
}
} }
} }
impl ToSql for String { impl ToSql for String {
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int { fn to_sql(&self) -> Result<ToSqlOutput> {
(&self[..]).bind_parameter(stmt, col) Ok(ToSqlOutput::from(self.as_str()))
}
}
impl<'a> ToSql for &'a [u8] {
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int {
if self.len() > ::std::i32::MAX as usize {
return ffi::SQLITE_TOOBIG;
}
ffi::sqlite3_bind_blob(stmt,
col,
mem::transmute(self.as_ptr()),
self.len() as c_int,
ffi::SQLITE_TRANSIENT())
} }
} }
impl ToSql for Vec<u8> { impl ToSql for Vec<u8> {
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int { fn to_sql(&self) -> Result<ToSqlOutput> {
(&self[..]).bind_parameter(stmt, col) Ok(ToSqlOutput::from(self.as_slice()))
}
}
impl ToSql for Value {
fn to_sql(&self) -> Result<ToSqlOutput> {
Ok(ToSqlOutput::from(self))
} }
} }
impl<T: ToSql> ToSql for Option<T> { impl<T: ToSql> ToSql for Option<T> {
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int { fn to_sql(&self) -> Result<ToSqlOutput> {
match *self { match *self {
None => ffi::sqlite3_bind_null(stmt, col), None => Ok(ToSqlOutput::from(Null)),
Some(ref t) => t.bind_parameter(stmt, col), Some(ref t) => t.to_sql(),
} }
} }
} }
impl ToSql for Null {
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int {
ffi::sqlite3_bind_null(stmt, col)
}
}

73
src/types/value.rs Normal file
View File

@ -0,0 +1,73 @@
use super::{Null, Type};
/// Owning [dynamic type value](http://sqlite.org/datatype3.html). Value's type is typically
/// dictated by SQLite (not by the caller).
///
/// See [`ValueRef`](enum.ValueRef.html) for a non-owning dynamic type value.
#[derive(Clone,Debug,PartialEq)]
pub enum Value {
/// The value is a `NULL` value.
Null,
/// The value is a signed integer.
Integer(i64),
/// The value is a floating point number.
Real(f64),
/// The value is a text string.
Text(String),
/// The value is a blob of data
Blob(Vec<u8>),
}
impl From<Null> for Value {
fn from(_: Null) -> Value {
Value::Null
}
}
impl From<bool> for Value {
fn from(i: bool) -> Value {
Value::Integer(i as i64)
}
}
impl From<i32> for Value {
fn from(i: i32) -> Value {
Value::Integer(i as i64)
}
}
impl From<i64> for Value {
fn from(i: i64) -> Value {
Value::Integer(i)
}
}
impl From<f64> for Value {
fn from(f: f64) -> Value {
Value::Real(f)
}
}
impl From<String> for Value {
fn from(s: String) -> Value {
Value::Text(s)
}
}
impl From<Vec<u8>> for Value {
fn from(v: Vec<u8>) -> Value {
Value::Blob(v)
}
}
impl Value {
pub fn data_type(&self) -> Type {
match *self {
Value::Null => Type::Null,
Value::Integer(_) => Type::Integer,
Value::Real(_) => Type::Real,
Value::Text(_) => Type::Text,
Value::Blob(_) => Type::Blob,
}
}
}

View File

@ -1,6 +1,5 @@
use ::Result; use ::types::{FromSqlError, FromSqlResult};
use ::error::Error; use super::{Value, Type};
use super::Value;
/// A non-owning [dynamic type value](http://sqlite.org/datatype3.html). Typically the /// A non-owning [dynamic type value](http://sqlite.org/datatype3.html). Typically the
/// memory backing this value is owned by SQLite. /// memory backing this value is owned by SQLite.
@ -20,40 +19,52 @@ pub enum ValueRef<'a> {
Blob(&'a [u8]), Blob(&'a [u8]),
} }
impl<'a> ValueRef<'a> {
pub fn data_type(&self) -> Type {
match *self {
ValueRef::Null => Type::Null,
ValueRef::Integer(_) => Type::Integer,
ValueRef::Real(_) => Type::Real,
ValueRef::Text(_) => Type::Text,
ValueRef::Blob(_) => Type::Blob,
}
}
}
impl<'a> ValueRef<'a> { impl<'a> ValueRef<'a> {
/// If `self` is case `Integer`, returns the integral value. Otherwise, returns /// If `self` is case `Integer`, returns the integral value. Otherwise, returns
/// `Err(Error::InvalidColumnType)`. /// `Err(Error::InvalidColumnType)`.
pub fn as_i64(&self) -> Result<i64> { pub fn as_i64(&self) -> FromSqlResult<i64> {
match *self { match *self {
ValueRef::Integer(i) => Ok(i), ValueRef::Integer(i) => Ok(i),
_ => Err(Error::InvalidColumnType), _ => Err(FromSqlError::InvalidType),
} }
} }
/// If `self` is case `Real`, returns the floating point value. Otherwise, returns /// If `self` is case `Real`, returns the floating point value. Otherwise, returns
/// `Err(Error::InvalidColumnType)`. /// `Err(Error::InvalidColumnType)`.
pub fn as_f64(&self) -> Result<f64> { pub fn as_f64(&self) -> FromSqlResult<f64> {
match *self { match *self {
ValueRef::Real(f) => Ok(f), ValueRef::Real(f) => Ok(f),
_ => Err(Error::InvalidColumnType), _ => Err(FromSqlError::InvalidType),
} }
} }
/// If `self` is case `Text`, returns the string value. Otherwise, returns /// If `self` is case `Text`, returns the string value. Otherwise, returns
/// `Err(Error::InvalidColumnType)`. /// `Err(Error::InvalidColumnType)`.
pub fn as_str(&self) -> Result<&str> { pub fn as_str(&self) -> FromSqlResult<&str> {
match *self { match *self {
ValueRef::Text(ref t) => Ok(t), ValueRef::Text(ref t) => Ok(t),
_ => Err(Error::InvalidColumnType), _ => Err(FromSqlError::InvalidType),
} }
} }
/// If `self` is case `Blob`, returns the byte slice. Otherwise, returns /// If `self` is case `Blob`, returns the byte slice. Otherwise, returns
/// `Err(Error::InvalidColumnType)`. /// `Err(Error::InvalidColumnType)`.
pub fn as_blob(&self) -> Result<&[u8]> { pub fn as_blob(&self) -> FromSqlResult<&[u8]> {
match *self { match *self {
ValueRef::Blob(ref b) => Ok(b), ValueRef::Blob(ref b) => Ok(b),
_ => Err(Error::InvalidColumnType), _ => Err(FromSqlError::InvalidType),
} }
} }
} }
@ -70,6 +81,18 @@ impl<'a> From<ValueRef<'a>> for Value {
} }
} }
impl<'a> From<&'a str> for ValueRef<'a> {
fn from(s: &str) -> ValueRef {
ValueRef::Text(s)
}
}
impl<'a> From<&'a [u8]> for ValueRef<'a> {
fn from(s: &[u8]) -> ValueRef {
ValueRef::Blob(s)
}
}
impl<'a> From<&'a Value> for ValueRef<'a> { impl<'a> From<&'a Value> for ValueRef<'a> {
fn from(value: &'a Value) -> ValueRef<'a> { fn from(value: &'a Value) -> ValueRef<'a> {
match *value { match *value {

View File

@ -10,8 +10,8 @@ use libc;
use {Connection, Error, Result, InnerConnection, str_to_cstring}; use {Connection, Error, Result, InnerConnection, str_to_cstring};
use error::error_from_sqlite_code; use error::error_from_sqlite_code;
use ffi; use ffi;
use functions::ToResult; use functions::{set_result, report_error};
use types::{FromSql, ValueRef}; use types::{FromSql, FromSqlError, ToSql, ValueRef};
// let conn: Connection = ...; // let conn: Connection = ...;
// let mod: Module = ...; // VTab builder // let mod: Module = ...; // VTab builder
@ -128,7 +128,7 @@ impl IndexInfo {
} }
} }
pub struct IndexConstraintIter<'a> { pub struct IndexConstraintIter<'a> {
iter: slice::Iter<'a, ffi::Struct_sqlite3_index_constraint>, iter: slice::Iter<'a, ffi::sqlite3_index_constraint>,
} }
impl<'a> Iterator for IndexConstraintIter<'a> { impl<'a> Iterator for IndexConstraintIter<'a> {
@ -143,7 +143,7 @@ impl<'a> Iterator for IndexConstraintIter<'a> {
} }
} }
pub struct IndexConstraint<'a>(&'a ffi::Struct_sqlite3_index_constraint); pub struct IndexConstraint<'a>(&'a ffi::sqlite3_index_constraint);
impl<'a> IndexConstraint<'a> { impl<'a> IndexConstraint<'a> {
/// Column constrained. -1 for ROWID /// Column constrained. -1 for ROWID
@ -160,7 +160,7 @@ impl<'a> IndexConstraint<'a> {
} }
} }
pub struct IndexConstraintUsage<'a>(&'a mut ffi::Struct_sqlite3_index_constraint_usage); pub struct IndexConstraintUsage<'a>(&'a mut ffi::sqlite3_index_constraint_usage);
impl<'a> IndexConstraintUsage<'a> { impl<'a> IndexConstraintUsage<'a> {
/// if `argv_index` > 0, constraint is part of argv to xFilter /// if `argv_index` > 0, constraint is part of argv to xFilter
@ -196,9 +196,11 @@ pub trait VTabCursor<V: VTab<Self>>: Sized {
pub struct Context(*mut ffi::sqlite3_context); pub struct Context(*mut ffi::sqlite3_context);
impl Context { impl Context {
pub fn set_result<T: ToResult>(&mut self, value: &T) { pub fn set_result<T: ToSql>(&mut self, value: &T) {
unsafe { let t = value.to_sql();
value.set_result(self.0); match t {
Ok(ref value) => set_result(self.0, value),
Err(err) => unsafe { report_error(self.0, &err) },
} }
} }
} }
@ -220,8 +222,12 @@ impl<'a> Values<'a> {
let arg = self.args[idx]; let arg = self.args[idx];
let value = unsafe { ValueRef::from_value(arg) }; let value = unsafe { ValueRef::from_value(arg) };
FromSql::column_result(value).map_err(|err| match err { FromSql::column_result(value).map_err(|err| match err {
Error::InvalidColumnType => Error::InvalidFunctionParameterType, FromSqlError::InvalidType => {
_ => err, Error::InvalidFunctionParameterType(idx, value.data_type())
}
FromSqlError::Other(err) => {
Error::FromSqlConversionFailure(idx, value.data_type(), err)
}
}) })
} }

View File

@ -1,7 +1,8 @@
//! This file contains unit tests for rusqlite::trace::config_log. This function affects //! This file contains unit tests for rusqlite::trace::config_log. This function affects
//! SQLite process-wide and so is not safe to run as a normal #[test] in the library. //! SQLite process-wide and so is not safe to run as a normal #[test] in the library.
#[macro_use] extern crate lazy_static; #[macro_use]
extern crate lazy_static;
extern crate libc; extern crate libc;
extern crate rusqlite; extern crate rusqlite;