Merge remote-tracking branch 'origin/master' into deprecated

This commit is contained in:
gwenn 2022-12-11 09:56:22 +01:00
commit 312bf41e90
28 changed files with 3706 additions and 1751 deletions

View File

@ -44,7 +44,7 @@ winsqlite3 = []
openssl-sys = { version = "0.9", optional = true } openssl-sys = { version = "0.9", optional = true }
[build-dependencies] [build-dependencies]
bindgen = { version = "0.61", optional = true, default-features = false, features = ["runtime"] } bindgen = { version = "0.63", optional = true, default-features = false, features = ["runtime"] }
pkg-config = { version = "0.3.19", optional = true } pkg-config = { version = "0.3.19", optional = true }
cc = { version = "1.0", optional = true } cc = { version = "1.0", optional = true }
vcpkg = { version = "0.2", optional = true } vcpkg = { version = "0.2", optional = true }

View File

@ -1,9 +1,9 @@
/* automatically generated by rust-bindgen 0.60.1 */ /* automatically generated by rust-bindgen 0.62.0 */
pub const SQLITE_VERSION: &[u8; 7usize] = b"3.39.4\0"; pub const SQLITE_VERSION: &[u8; 7usize] = b"3.40.0\0";
pub const SQLITE_VERSION_NUMBER: i32 = 3039004; pub const SQLITE_VERSION_NUMBER: i32 = 3040000;
pub const SQLITE_SOURCE_ID: &[u8; 85usize] = pub const SQLITE_SOURCE_ID: &[u8; 85usize] =
b"2022-09-29 15:55:41 a29f9949895322123f7c38fbe94c649a9d6e6c9cd0c3b41c96d694552f26b309\0"; b"2022-11-16 12:10:08 89c459e766ea7e9165d0beeb124708b955a4950d0f4792f457465d71b158d318\0";
pub const SQLITE_OK: i32 = 0; pub const SQLITE_OK: i32 = 0;
pub const SQLITE_ERROR: i32 = 1; pub const SQLITE_ERROR: i32 = 1;
pub const SQLITE_INTERNAL: i32 = 2; pub const SQLITE_INTERNAL: i32 = 2;
@ -474,7 +474,7 @@ pub const FTS5_TOKENIZE_DOCUMENT: i32 = 4;
pub const FTS5_TOKENIZE_AUX: i32 = 8; pub const FTS5_TOKENIZE_AUX: i32 = 8;
pub const FTS5_TOKEN_COLOCATED: i32 = 1; pub const FTS5_TOKEN_COLOCATED: i32 = 1;
extern "C" { extern "C" {
pub static mut sqlite3_version: [::std::os::raw::c_char; 0usize]; pub static sqlite3_version: [::std::os::raw::c_char; 0usize];
} }
extern "C" { extern "C" {
pub fn sqlite3_libversion() -> *const ::std::os::raw::c_char; pub fn sqlite3_libversion() -> *const ::std::os::raw::c_char;
@ -659,6 +659,7 @@ pub struct sqlite3_mutex {
pub struct sqlite3_api_routines { pub struct sqlite3_api_routines {
_unused: [u8; 0], _unused: [u8; 0],
} }
pub type sqlite3_filename = *const ::std::os::raw::c_char;
pub type sqlite3_syscall_ptr = ::std::option::Option<unsafe extern "C" fn()>; pub type sqlite3_syscall_ptr = ::std::option::Option<unsafe extern "C" fn()>;
#[repr(C)] #[repr(C)]
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
@ -672,7 +673,7 @@ pub struct sqlite3_vfs {
pub xOpen: ::std::option::Option< pub xOpen: ::std::option::Option<
unsafe extern "C" fn( unsafe extern "C" fn(
arg1: *mut sqlite3_vfs, arg1: *mut sqlite3_vfs,
zName: *const ::std::os::raw::c_char, zName: sqlite3_filename,
arg2: *mut sqlite3_file, arg2: *mut sqlite3_file,
flags: ::std::os::raw::c_int, flags: ::std::os::raw::c_int,
pOutFlags: *mut ::std::os::raw::c_int, pOutFlags: *mut ::std::os::raw::c_int,
@ -1023,44 +1024,38 @@ extern "C" {
} }
extern "C" { extern "C" {
pub fn sqlite3_uri_parameter( pub fn sqlite3_uri_parameter(
zFilename: *const ::std::os::raw::c_char, z: sqlite3_filename,
zParam: *const ::std::os::raw::c_char, zParam: *const ::std::os::raw::c_char,
) -> *const ::std::os::raw::c_char; ) -> *const ::std::os::raw::c_char;
} }
extern "C" { extern "C" {
pub fn sqlite3_uri_boolean( pub fn sqlite3_uri_boolean(
zFile: *const ::std::os::raw::c_char, z: sqlite3_filename,
zParam: *const ::std::os::raw::c_char, zParam: *const ::std::os::raw::c_char,
bDefault: ::std::os::raw::c_int, bDefault: ::std::os::raw::c_int,
) -> ::std::os::raw::c_int; ) -> ::std::os::raw::c_int;
} }
extern "C" { extern "C" {
pub fn sqlite3_uri_int64( pub fn sqlite3_uri_int64(
arg1: *const ::std::os::raw::c_char, arg1: sqlite3_filename,
arg2: *const ::std::os::raw::c_char, arg2: *const ::std::os::raw::c_char,
arg3: sqlite3_int64, arg3: sqlite3_int64,
) -> sqlite3_int64; ) -> sqlite3_int64;
} }
extern "C" { extern "C" {
pub fn sqlite3_uri_key( pub fn sqlite3_uri_key(
zFilename: *const ::std::os::raw::c_char, z: sqlite3_filename,
N: ::std::os::raw::c_int, N: ::std::os::raw::c_int,
) -> *const ::std::os::raw::c_char; ) -> *const ::std::os::raw::c_char;
} }
extern "C" { extern "C" {
pub fn sqlite3_filename_database( pub fn sqlite3_filename_database(arg1: sqlite3_filename) -> *const ::std::os::raw::c_char;
arg1: *const ::std::os::raw::c_char,
) -> *const ::std::os::raw::c_char;
} }
extern "C" { extern "C" {
pub fn sqlite3_filename_journal( pub fn sqlite3_filename_journal(arg1: sqlite3_filename) -> *const ::std::os::raw::c_char;
arg1: *const ::std::os::raw::c_char,
) -> *const ::std::os::raw::c_char;
} }
extern "C" { extern "C" {
pub fn sqlite3_filename_wal( pub fn sqlite3_filename_wal(arg1: sqlite3_filename) -> *const ::std::os::raw::c_char;
arg1: *const ::std::os::raw::c_char,
) -> *const ::std::os::raw::c_char;
} }
extern "C" { extern "C" {
pub fn sqlite3_database_file_object(arg1: *const ::std::os::raw::c_char) -> *mut sqlite3_file; pub fn sqlite3_database_file_object(arg1: *const ::std::os::raw::c_char) -> *mut sqlite3_file;
@ -1072,10 +1067,10 @@ extern "C" {
zWal: *const ::std::os::raw::c_char, zWal: *const ::std::os::raw::c_char,
nParam: ::std::os::raw::c_int, nParam: ::std::os::raw::c_int,
azParam: *mut *const ::std::os::raw::c_char, azParam: *mut *const ::std::os::raw::c_char,
) -> *mut ::std::os::raw::c_char; ) -> sqlite3_filename;
} }
extern "C" { extern "C" {
pub fn sqlite3_free_filename(arg1: *mut ::std::os::raw::c_char); pub fn sqlite3_free_filename(arg1: sqlite3_filename);
} }
extern "C" { extern "C" {
pub fn sqlite3_errcode(db: *mut sqlite3) -> ::std::os::raw::c_int; pub fn sqlite3_errcode(db: *mut sqlite3) -> ::std::os::raw::c_int;
@ -1619,6 +1614,9 @@ extern "C" {
extern "C" { extern "C" {
pub fn sqlite3_value_frombind(arg1: *mut sqlite3_value) -> ::std::os::raw::c_int; pub fn sqlite3_value_frombind(arg1: *mut sqlite3_value) -> ::std::os::raw::c_int;
} }
extern "C" {
pub fn sqlite3_value_encoding(arg1: *mut sqlite3_value) -> ::std::os::raw::c_int;
}
extern "C" { extern "C" {
pub fn sqlite3_value_subtype(arg1: *mut sqlite3_value) -> ::std::os::raw::c_uint; pub fn sqlite3_value_subtype(arg1: *mut sqlite3_value) -> ::std::os::raw::c_uint;
} }
@ -1894,7 +1892,7 @@ extern "C" {
pub fn sqlite3_db_filename( pub fn sqlite3_db_filename(
db: *mut sqlite3, db: *mut sqlite3,
zDbName: *const ::std::os::raw::c_char, zDbName: *const ::std::os::raw::c_char,
) -> *const ::std::os::raw::c_char; ) -> sqlite3_filename;
} }
extern "C" { extern "C" {
pub fn sqlite3_db_readonly( pub fn sqlite3_db_readonly(

File diff suppressed because it is too large Load Diff

View File

@ -146,9 +146,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()]. ** [sqlite_version()] and [sqlite_source_id()].
*/ */
#define SQLITE_VERSION "3.39.4" #define SQLITE_VERSION "3.40.0"
#define SQLITE_VERSION_NUMBER 3039004 #define SQLITE_VERSION_NUMBER 3040000
#define SQLITE_SOURCE_ID "2022-09-29 15:55:41 a29f9949895322123f7c38fbe94c649a9d6e6c9cd0c3b41c96d694552f26b309" #define SQLITE_SOURCE_ID "2022-11-16 12:10:08 89c459e766ea7e9165d0beeb124708b955a4950d0f4792f457465d71b158d318"
/* /*
** CAPI3REF: Run-Time Library Version Numbers ** CAPI3REF: Run-Time Library Version Numbers
@ -670,13 +670,17 @@ SQLITE_API int sqlite3_exec(
** **
** SQLite uses one of these integer values as the second ** SQLite uses one of these integer values as the second
** argument to calls it makes to the xLock() and xUnlock() methods ** argument to calls it makes to the xLock() and xUnlock() methods
** of an [sqlite3_io_methods] object. ** of an [sqlite3_io_methods] object. These values are ordered from
** lest restrictive to most restrictive.
**
** The argument to xLock() is always SHARED or higher. The argument to
** xUnlock is either SHARED or NONE.
*/ */
#define SQLITE_LOCK_NONE 0 #define SQLITE_LOCK_NONE 0 /* xUnlock() only */
#define SQLITE_LOCK_SHARED 1 #define SQLITE_LOCK_SHARED 1 /* xLock() or xUnlock() */
#define SQLITE_LOCK_RESERVED 2 #define SQLITE_LOCK_RESERVED 2 /* xLock() only */
#define SQLITE_LOCK_PENDING 3 #define SQLITE_LOCK_PENDING 3 /* xLock() only */
#define SQLITE_LOCK_EXCLUSIVE 4 #define SQLITE_LOCK_EXCLUSIVE 4 /* xLock() only */
/* /*
** CAPI3REF: Synchronization Type Flags ** CAPI3REF: Synchronization Type Flags
@ -754,7 +758,14 @@ struct sqlite3_file {
** <li> [SQLITE_LOCK_PENDING], or ** <li> [SQLITE_LOCK_PENDING], or
** <li> [SQLITE_LOCK_EXCLUSIVE]. ** <li> [SQLITE_LOCK_EXCLUSIVE].
** </ul> ** </ul>
** xLock() increases the lock. xUnlock() decreases the lock. ** xLock() upgrades the database file lock. In other words, xLock() moves the
** database file lock in the direction NONE toward EXCLUSIVE. The argument to
** xLock() is always on of SHARED, RESERVED, PENDING, or EXCLUSIVE, never
** SQLITE_LOCK_NONE. If the database file lock is already at or above the
** requested lock, then the call to xLock() is a no-op.
** xUnlock() downgrades the database file lock to either SHARED or NONE.
* If the lock is already at or below the requested lock state, then the call
** to xUnlock() is a no-op.
** The xCheckReservedLock() method checks whether any database connection, ** The xCheckReservedLock() method checks whether any database connection,
** either in this process or in some other process, is holding a RESERVED, ** either in this process or in some other process, is holding a RESERVED,
** PENDING, or EXCLUSIVE lock on the file. It returns true ** PENDING, or EXCLUSIVE lock on the file. It returns true
@ -859,9 +870,8 @@ struct sqlite3_io_methods {
** opcode causes the xFileControl method to write the current state of ** opcode causes the xFileControl method to write the current state of
** the lock (one of [SQLITE_LOCK_NONE], [SQLITE_LOCK_SHARED], ** the lock (one of [SQLITE_LOCK_NONE], [SQLITE_LOCK_SHARED],
** [SQLITE_LOCK_RESERVED], [SQLITE_LOCK_PENDING], or [SQLITE_LOCK_EXCLUSIVE]) ** [SQLITE_LOCK_RESERVED], [SQLITE_LOCK_PENDING], or [SQLITE_LOCK_EXCLUSIVE])
** into an integer that the pArg argument points to. This capability ** into an integer that the pArg argument points to.
** is used during testing and is only available when the SQLITE_TEST ** This capability is only available if SQLite is compiled with [SQLITE_DEBUG].
** compile-time option is used.
** **
** <li>[[SQLITE_FCNTL_SIZE_HINT]] ** <li>[[SQLITE_FCNTL_SIZE_HINT]]
** The [SQLITE_FCNTL_SIZE_HINT] opcode is used by SQLite to give the VFS ** The [SQLITE_FCNTL_SIZE_HINT] opcode is used by SQLite to give the VFS
@ -1253,6 +1263,26 @@ typedef struct sqlite3_mutex sqlite3_mutex;
*/ */
typedef struct sqlite3_api_routines sqlite3_api_routines; typedef struct sqlite3_api_routines sqlite3_api_routines;
/*
** CAPI3REF: File Name
**
** Type [sqlite3_filename] is used by SQLite to pass filenames to the
** xOpen method of a [VFS]. It may be cast to (const char*) and treated
** as a normal, nul-terminated, UTF-8 buffer containing the filename, but
** may also be passed to special APIs such as:
**
** <ul>
** <li> sqlite3_filename_database()
** <li> sqlite3_filename_journal()
** <li> sqlite3_filename_wal()
** <li> sqlite3_uri_parameter()
** <li> sqlite3_uri_boolean()
** <li> sqlite3_uri_int64()
** <li> sqlite3_uri_key()
** </ul>
*/
typedef const char *sqlite3_filename;
/* /*
** CAPI3REF: OS Interface Object ** CAPI3REF: OS Interface Object
** **
@ -1431,7 +1461,7 @@ struct sqlite3_vfs {
sqlite3_vfs *pNext; /* Next registered VFS */ sqlite3_vfs *pNext; /* Next registered VFS */
const char *zName; /* Name of this virtual file system */ const char *zName; /* Name of this virtual file system */
void *pAppData; /* Pointer to application-specific data */ void *pAppData; /* Pointer to application-specific data */
int (*xOpen)(sqlite3_vfs*, const char *zName, sqlite3_file*, int (*xOpen)(sqlite3_vfs*, sqlite3_filename zName, sqlite3_file*,
int flags, int *pOutFlags); int flags, int *pOutFlags);
int (*xDelete)(sqlite3_vfs*, const char *zName, int syncDir); int (*xDelete)(sqlite3_vfs*, const char *zName, int syncDir);
int (*xAccess)(sqlite3_vfs*, const char *zName, int flags, int *pResOut); int (*xAccess)(sqlite3_vfs*, const char *zName, int flags, int *pResOut);
@ -2309,6 +2339,7 @@ struct sqlite3_mem_methods {
** <ul> ** <ul>
** <li> The [PRAGMA writable_schema=ON] statement. ** <li> The [PRAGMA writable_schema=ON] statement.
** <li> The [PRAGMA journal_mode=OFF] statement. ** <li> The [PRAGMA journal_mode=OFF] statement.
** <li> The [PRAGMA schema_version=N] statement.
** <li> Writes to the [sqlite_dbpage] virtual table. ** <li> Writes to the [sqlite_dbpage] virtual table.
** <li> Direct writes to [shadow tables]. ** <li> Direct writes to [shadow tables].
** </ul> ** </ul>
@ -3424,6 +3455,9 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** <dd>The database is opened [shared cache] enabled, overriding ** <dd>The database is opened [shared cache] enabled, overriding
** the default shared cache setting provided by ** the default shared cache setting provided by
** [sqlite3_enable_shared_cache()].)^ ** [sqlite3_enable_shared_cache()].)^
** The [use of shared cache mode is discouraged] and hence shared cache
** capabilities may be omitted from many builds of SQLite. In such cases,
** this option is a no-op.
** **
** ^(<dt>[SQLITE_OPEN_PRIVATECACHE]</dt> ** ^(<dt>[SQLITE_OPEN_PRIVATECACHE]</dt>
** <dd>The database is opened [shared cache] disabled, overriding ** <dd>The database is opened [shared cache] disabled, overriding
@ -3439,7 +3473,7 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** to return an extended result code.</dd> ** to return an extended result code.</dd>
** **
** [[OPEN_NOFOLLOW]] ^(<dt>[SQLITE_OPEN_NOFOLLOW]</dt> ** [[OPEN_NOFOLLOW]] ^(<dt>[SQLITE_OPEN_NOFOLLOW]</dt>
** <dd>The database filename is not allowed to be a symbolic link</dd> ** <dd>The database filename is not allowed to contain a symbolic link</dd>
** </dl>)^ ** </dl>)^
** **
** If the 3rd parameter to sqlite3_open_v2() is not one of the ** If the 3rd parameter to sqlite3_open_v2() is not one of the
@ -3698,10 +3732,10 @@ SQLITE_API int sqlite3_open_v2(
** **
** See the [URI filename] documentation for additional information. ** See the [URI filename] documentation for additional information.
*/ */
SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam); SQLITE_API const char *sqlite3_uri_parameter(sqlite3_filename z, const char *zParam);
SQLITE_API int sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault); SQLITE_API int sqlite3_uri_boolean(sqlite3_filename z, const char *zParam, int bDefault);
SQLITE_API sqlite3_int64 sqlite3_uri_int64(const char*, const char*, sqlite3_int64); SQLITE_API sqlite3_int64 sqlite3_uri_int64(sqlite3_filename, const char*, sqlite3_int64);
SQLITE_API const char *sqlite3_uri_key(const char *zFilename, int N); SQLITE_API const char *sqlite3_uri_key(sqlite3_filename z, int N);
/* /*
** CAPI3REF: Translate filenames ** CAPI3REF: Translate filenames
@ -3730,9 +3764,9 @@ SQLITE_API const char *sqlite3_uri_key(const char *zFilename, int N);
** return value from [sqlite3_db_filename()], then the result is ** return value from [sqlite3_db_filename()], then the result is
** undefined and is likely a memory access violation. ** undefined and is likely a memory access violation.
*/ */
SQLITE_API const char *sqlite3_filename_database(const char*); SQLITE_API const char *sqlite3_filename_database(sqlite3_filename);
SQLITE_API const char *sqlite3_filename_journal(const char*); SQLITE_API const char *sqlite3_filename_journal(sqlite3_filename);
SQLITE_API const char *sqlite3_filename_wal(const char*); SQLITE_API const char *sqlite3_filename_wal(sqlite3_filename);
/* /*
** CAPI3REF: Database File Corresponding To A Journal ** CAPI3REF: Database File Corresponding To A Journal
@ -3798,14 +3832,14 @@ SQLITE_API sqlite3_file *sqlite3_database_file_object(const char*);
** then the corresponding [sqlite3_module.xClose() method should also be ** then the corresponding [sqlite3_module.xClose() method should also be
** invoked prior to calling sqlite3_free_filename(Y). ** invoked prior to calling sqlite3_free_filename(Y).
*/ */
SQLITE_API char *sqlite3_create_filename( SQLITE_API sqlite3_filename sqlite3_create_filename(
const char *zDatabase, const char *zDatabase,
const char *zJournal, const char *zJournal,
const char *zWal, const char *zWal,
int nParam, int nParam,
const char **azParam const char **azParam
); );
SQLITE_API void sqlite3_free_filename(char*); SQLITE_API void sqlite3_free_filename(sqlite3_filename);
/* /*
** CAPI3REF: Error Codes And Messages ** CAPI3REF: Error Codes And Messages
@ -5508,6 +5542,16 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int6
** then the conversion is performed. Otherwise no conversion occurs. ** then the conversion is performed. Otherwise no conversion occurs.
** The [SQLITE_INTEGER | datatype] after conversion is returned.)^ ** The [SQLITE_INTEGER | datatype] after conversion is returned.)^
** **
** ^(The sqlite3_value_encoding(X) interface returns one of [SQLITE_UTF8],
** [SQLITE_UTF16BE], or [SQLITE_UTF16LE] according to the current encoding
** of the value X, assuming that X has type TEXT.)^ If sqlite3_value_type(X)
** returns something other than SQLITE_TEXT, then the return value from
** sqlite3_value_encoding(X) is meaningless. ^Calls to
** sqlite3_value_text(X), sqlite3_value_text16(X), sqlite3_value_text16be(X),
** sqlite3_value_text16le(X), sqlite3_value_bytes(X), or
** sqlite3_value_bytes16(X) might change the encoding of the value X and
** thus change the return from subsequent calls to sqlite3_value_encoding(X).
**
** ^Within the [xUpdate] method of a [virtual table], the ** ^Within the [xUpdate] method of a [virtual table], the
** sqlite3_value_nochange(X) interface returns true if and only if ** sqlite3_value_nochange(X) interface returns true if and only if
** the column corresponding to X is unchanged by the UPDATE operation ** the column corresponding to X is unchanged by the UPDATE operation
@ -5572,6 +5616,7 @@ SQLITE_API int sqlite3_value_type(sqlite3_value*);
SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*); SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*);
SQLITE_API int sqlite3_value_nochange(sqlite3_value*); SQLITE_API int sqlite3_value_nochange(sqlite3_value*);
SQLITE_API int sqlite3_value_frombind(sqlite3_value*); SQLITE_API int sqlite3_value_frombind(sqlite3_value*);
SQLITE_API int sqlite3_value_encoding(sqlite3_value*);
/* /*
** CAPI3REF: Finding The Subtype Of SQL Values ** CAPI3REF: Finding The Subtype Of SQL Values
@ -5625,7 +5670,7 @@ SQLITE_API void sqlite3_value_free(sqlite3_value*);
** **
** ^The sqlite3_aggregate_context(C,N) routine returns a NULL pointer ** ^The sqlite3_aggregate_context(C,N) routine returns a NULL pointer
** when first called if N is less than or equal to zero or if a memory ** when first called if N is less than or equal to zero or if a memory
** allocate error occurs. ** allocation error occurs.
** **
** ^(The amount of space allocated by sqlite3_aggregate_context(C,N) is ** ^(The amount of space allocated by sqlite3_aggregate_context(C,N) is
** determined by the N parameter on first successful call. Changing the ** determined by the N parameter on first successful call. Changing the
@ -5830,9 +5875,10 @@ typedef void (*sqlite3_destructor_type)(void*);
** of [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE]. ** of [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE].
** ^SQLite takes the text result from the application from ** ^SQLite takes the text result from the application from
** the 2nd parameter of the sqlite3_result_text* interfaces. ** the 2nd parameter of the sqlite3_result_text* interfaces.
** ^If the 3rd parameter to the sqlite3_result_text* interfaces ** ^If the 3rd parameter to any of the sqlite3_result_text* interfaces
** is negative, then SQLite takes result text from the 2nd parameter ** other than sqlite3_result_text64() is negative, then SQLite computes
** through the first zero character. ** the string length itself by searching the 2nd parameter for the first
** zero character.
** ^If the 3rd parameter to the sqlite3_result_text* interfaces ** ^If the 3rd parameter to the sqlite3_result_text* interfaces
** is non-negative, then as many bytes (not characters) of the text ** is non-negative, then as many bytes (not characters) of the text
** pointed to by the 2nd parameter are taken as the application-defined ** pointed to by the 2nd parameter are taken as the application-defined
@ -6328,7 +6374,7 @@ SQLITE_API const char *sqlite3_db_name(sqlite3 *db, int N);
** <li> [sqlite3_filename_wal()] ** <li> [sqlite3_filename_wal()]
** </ul> ** </ul>
*/ */
SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName); SQLITE_API sqlite3_filename sqlite3_db_filename(sqlite3 *db, const char *zDbName);
/* /*
** CAPI3REF: Determine if a database is read-only ** CAPI3REF: Determine if a database is read-only
@ -6465,7 +6511,7 @@ SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
** function C that is invoked prior to each autovacuum of the database ** function C that is invoked prior to each autovacuum of the database
** file. ^The callback is passed a copy of the generic data pointer (P), ** file. ^The callback is passed a copy of the generic data pointer (P),
** the schema-name of the attached database that is being autovacuumed, ** the schema-name of the attached database that is being autovacuumed,
** the the size of the database file in pages, the number of free pages, ** the size of the database file in pages, the number of free pages,
** and the number of bytes per page, respectively. The callback should ** and the number of bytes per page, respectively. The callback should
** return the number of free pages that should be removed by the ** return the number of free pages that should be removed by the
** autovacuum. ^If the callback returns zero, then no autovacuum happens. ** autovacuum. ^If the callback returns zero, then no autovacuum happens.
@ -6586,6 +6632,11 @@ SQLITE_API void *sqlite3_update_hook(
** to the same database. Sharing is enabled if the argument is true ** to the same database. Sharing is enabled if the argument is true
** and disabled if the argument is false.)^ ** and disabled if the argument is false.)^
** **
** This interface is omitted if SQLite is compiled with
** [-DSQLITE_OMIT_SHARED_CACHE]. The [-DSQLITE_OMIT_SHARED_CACHE]
** compile-time option is recommended because the
** [use of shared cache mode is discouraged].
**
** ^Cache sharing is enabled and disabled for an entire process. ** ^Cache sharing is enabled and disabled for an entire process.
** This is a change as of SQLite [version 3.5.0] ([dateof:3.5.0]). ** This is a change as of SQLite [version 3.5.0] ([dateof:3.5.0]).
** In prior versions of SQLite, ** In prior versions of SQLite,
@ -6684,7 +6735,7 @@ SQLITE_API int sqlite3_db_release_memory(sqlite3*);
** ^The soft heap limit may not be greater than the hard heap limit. ** ^The soft heap limit may not be greater than the hard heap limit.
** ^If the hard heap limit is enabled and if sqlite3_soft_heap_limit(N) ** ^If the hard heap limit is enabled and if sqlite3_soft_heap_limit(N)
** is invoked with a value of N that is greater than the hard heap limit, ** is invoked with a value of N that is greater than the hard heap limit,
** the the soft heap limit is set to the value of the hard heap limit. ** the soft heap limit is set to the value of the hard heap limit.
** ^The soft heap limit is automatically enabled whenever the hard heap ** ^The soft heap limit is automatically enabled whenever the hard heap
** limit is enabled. ^When sqlite3_hard_heap_limit64(N) is invoked and ** limit is enabled. ^When sqlite3_hard_heap_limit64(N) is invoked and
** the soft heap limit is outside the range of 1..N, then the soft heap ** the soft heap limit is outside the range of 1..N, then the soft heap
@ -8979,7 +9030,7 @@ typedef struct sqlite3_backup sqlite3_backup;
** if the application incorrectly accesses the destination [database connection] ** if the application incorrectly accesses the destination [database connection]
** and so no error code is reported, but the operations may malfunction ** and so no error code is reported, but the operations may malfunction
** nevertheless. Use of the destination database connection while a ** nevertheless. Use of the destination database connection while a
** backup is in progress might also also cause a mutex deadlock. ** backup is in progress might also cause a mutex deadlock.
** **
** If running in [shared cache mode], the application must ** If running in [shared cache mode], the application must
** guarantee that the shared cache used by the destination database ** guarantee that the shared cache used by the destination database
@ -9407,7 +9458,7 @@ SQLITE_API int sqlite3_wal_checkpoint_v2(
*/ */
#define SQLITE_CHECKPOINT_PASSIVE 0 /* Do as much as possible w/o blocking */ #define SQLITE_CHECKPOINT_PASSIVE 0 /* Do as much as possible w/o blocking */
#define SQLITE_CHECKPOINT_FULL 1 /* Wait for writers, then checkpoint */ #define SQLITE_CHECKPOINT_FULL 1 /* Wait for writers, then checkpoint */
#define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for for readers */ #define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for readers */
#define SQLITE_CHECKPOINT_TRUNCATE 3 /* Like RESTART but also truncate WAL */ #define SQLITE_CHECKPOINT_TRUNCATE 3 /* Like RESTART but also truncate WAL */
/* /*

View File

@ -331,9 +331,9 @@ struct sqlite3_api_routines {
const char *(*filename_journal)(const char*); const char *(*filename_journal)(const char*);
const char *(*filename_wal)(const char*); const char *(*filename_wal)(const char*);
/* Version 3.32.0 and later */ /* Version 3.32.0 and later */
char *(*create_filename)(const char*,const char*,const char*, const char *(*create_filename)(const char*,const char*,const char*,
int,const char**); int,const char**);
void (*free_filename)(char*); void (*free_filename)(const char*);
sqlite3_file *(*database_file_object)(const char*); sqlite3_file *(*database_file_object)(const char*);
/* Version 3.34.0 and later */ /* Version 3.34.0 and later */
int (*txn_state)(sqlite3*,const char*); int (*txn_state)(sqlite3*,const char*);
@ -357,6 +357,8 @@ struct sqlite3_api_routines {
unsigned char *(*serialize)(sqlite3*,const char *,sqlite3_int64*, unsigned char *(*serialize)(sqlite3*,const char *,sqlite3_int64*,
unsigned int); unsigned int);
const char *(*db_name)(sqlite3*,int); const char *(*db_name)(sqlite3*,int);
/* Version 3.40.0 and later */
int (*value_encoding)(sqlite3_value*);
}; };
/* /*
@ -681,6 +683,8 @@ typedef int (*sqlite3_loadext_entry)(
#define sqlite3_serialize sqlite3_api->serialize #define sqlite3_serialize sqlite3_api->serialize
#endif #endif
#define sqlite3_db_name sqlite3_api->db_name #define sqlite3_db_name sqlite3_api->db_name
/* Version 3.40.0 and later */
#define sqlite3_value_encoding sqlite3_api->value_encoding
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)

View File

@ -9,7 +9,7 @@ export SQLITE3_LIB_DIR="$SCRIPT_DIR/sqlite3"
export SQLITE3_INCLUDE_DIR="$SQLITE3_LIB_DIR" export SQLITE3_INCLUDE_DIR="$SQLITE3_LIB_DIR"
# Download and extract amalgamation # Download and extract amalgamation
SQLITE=sqlite-amalgamation-3390400 SQLITE=sqlite-amalgamation-3400000
curl -O https://sqlite.org/2022/$SQLITE.zip curl -O https://sqlite.org/2022/$SQLITE.zip
unzip -p "$SQLITE.zip" "$SQLITE/sqlite3.c" > "$SQLITE3_LIB_DIR/sqlite3.c" unzip -p "$SQLITE.zip" "$SQLITE/sqlite3.c" > "$SQLITE3_LIB_DIR/sqlite3.c"
unzip -p "$SQLITE.zip" "$SQLITE/sqlite3.h" > "$SQLITE3_LIB_DIR/sqlite3.h" unzip -p "$SQLITE.zip" "$SQLITE/sqlite3.h" > "$SQLITE3_LIB_DIR/sqlite3.h"

View File

@ -336,7 +336,7 @@ mod test {
backup.step(-1)?; backup.step(-1)?;
} }
let the_answer: i64 = dst.query_row("SELECT x FROM foo", [], |r| r.get(0))?; let the_answer: i64 = dst.one_column("SELECT x FROM foo")?;
assert_eq!(42, the_answer); assert_eq!(42, the_answer);
src.execute_batch("INSERT INTO foo VALUES(43)")?; src.execute_batch("INSERT INTO foo VALUES(43)")?;
@ -346,7 +346,7 @@ mod test {
backup.run_to_completion(5, Duration::from_millis(250), None)?; backup.run_to_completion(5, Duration::from_millis(250), None)?;
} }
let the_answer: i64 = dst.query_row("SELECT SUM(x) FROM foo", [], |r| r.get(0))?; let the_answer: i64 = dst.one_column("SELECT SUM(x) FROM foo")?;
assert_eq!(42 + 43, the_answer); assert_eq!(42 + 43, the_answer);
Ok(()) Ok(())
} }
@ -368,7 +368,7 @@ mod test {
backup.step(-1)?; backup.step(-1)?;
} }
let the_answer: i64 = dst.query_row("SELECT x FROM foo", [], |r| r.get(0))?; let the_answer: i64 = dst.one_column("SELECT x FROM foo")?;
assert_eq!(42, the_answer); assert_eq!(42, the_answer);
src.execute_batch("INSERT INTO foo VALUES(43)")?; src.execute_batch("INSERT INTO foo VALUES(43)")?;
@ -379,7 +379,7 @@ mod test {
backup.run_to_completion(5, Duration::from_millis(250), None)?; backup.run_to_completion(5, Duration::from_millis(250), None)?;
} }
let the_answer: i64 = dst.query_row("SELECT SUM(x) FROM foo", [], |r| r.get(0))?; let the_answer: i64 = dst.one_column("SELECT SUM(x) FROM foo")?;
assert_eq!(42 + 43, the_answer); assert_eq!(42 + 43, the_answer);
Ok(()) Ok(())
} }
@ -406,7 +406,7 @@ mod test {
backup.step(-1)?; backup.step(-1)?;
} }
let the_answer: i64 = dst.query_row("SELECT x FROM foo", [], |r| r.get(0))?; let the_answer: i64 = dst.one_column("SELECT x FROM foo")?;
assert_eq!(42, the_answer); assert_eq!(42, the_answer);
src.execute_batch("INSERT INTO foo VALUES(43)")?; src.execute_batch("INSERT INTO foo VALUES(43)")?;
@ -421,7 +421,7 @@ mod test {
backup.run_to_completion(5, Duration::from_millis(250), None)?; backup.run_to_completion(5, Duration::from_millis(250), None)?;
} }
let the_answer: i64 = dst.query_row("SELECT SUM(x) FROM foo", [], |r| r.get(0))?; let the_answer: i64 = dst.one_column("SELECT SUM(x) FROM foo")?;
assert_eq!(42 + 43, the_answer); assert_eq!(42 + 43, the_answer);
Ok(()) Ok(())
} }

View File

@ -134,7 +134,7 @@
//! // Insert another BLOB, this time using a parameter passed in from //! // Insert another BLOB, this time using a parameter passed in from
//! // rust (potentially with a dynamic size). //! // rust (potentially with a dynamic size).
//! db.execute( //! db.execute(
//! "INSERT INTO test_table (content) VALUES (?)", //! "INSERT INTO test_table (content) VALUES (?1)",
//! [ZeroBlob(64)], //! [ZeroBlob(64)],
//! )?; //! )?;
//! //!
@ -175,7 +175,7 @@
//! // Insert another blob, this time using a parameter passed in from //! // Insert another blob, this time using a parameter passed in from
//! // rust (potentially with a dynamic size). //! // rust (potentially with a dynamic size).
//! db.execute( //! db.execute(
//! "INSERT INTO test_table (content) VALUES (?)", //! "INSERT INTO test_table (content) VALUES (?1)",
//! [ZeroBlob(64)], //! [ZeroBlob(64)],
//! )?; //! )?;
//! //!

View File

@ -17,13 +17,13 @@ impl Connection {
/// # use rusqlite::{Connection, Result}; /// # use rusqlite::{Connection, Result};
/// fn insert_new_people(conn: &Connection) -> Result<()> { /// fn insert_new_people(conn: &Connection) -> Result<()> {
/// { /// {
/// let mut stmt = conn.prepare_cached("INSERT INTO People (name) VALUES (?)")?; /// let mut stmt = conn.prepare_cached("INSERT INTO People (name) VALUES (?1)")?;
/// stmt.execute(["Joe Smith"])?; /// stmt.execute(["Joe Smith"])?;
/// } /// }
/// { /// {
/// // This will return the same underlying SQLite statement handle without /// // This will return the same underlying SQLite statement handle without
/// // having to prepare it again. /// // having to prepare it again.
/// let mut stmt = conn.prepare_cached("INSERT INTO People (name) VALUES (?)")?; /// let mut stmt = conn.prepare_cached("INSERT INTO People (name) VALUES (?1)")?;
/// stmt.execute(["Bob Jones"])?; /// stmt.execute(["Bob Jones"])?;
/// } /// }
/// Ok(()) /// Ok(())

View File

@ -820,9 +820,9 @@ mod test {
FunctionFlags::SQLITE_UTF8 | FunctionFlags::SQLITE_DETERMINISTIC, FunctionFlags::SQLITE_UTF8 | FunctionFlags::SQLITE_DETERMINISTIC,
half, half,
)?; )?;
let result: Result<f64> = db.query_row("SELECT half(6)", [], |r| r.get(0)); let result: f64 = db.one_column("SELECT half(6)")?;
assert!((3f64 - result?).abs() < f64::EPSILON); assert!((3f64 - result).abs() < f64::EPSILON);
Ok(()) Ok(())
} }
@ -835,11 +835,11 @@ mod test {
FunctionFlags::SQLITE_UTF8 | FunctionFlags::SQLITE_DETERMINISTIC, FunctionFlags::SQLITE_UTF8 | FunctionFlags::SQLITE_DETERMINISTIC,
half, half,
)?; )?;
let result: Result<f64> = db.query_row("SELECT half(6)", [], |r| r.get(0)); let result: f64 = db.one_column("SELECT half(6)")?;
assert!((3f64 - result?).abs() < f64::EPSILON); assert!((3f64 - result).abs() < f64::EPSILON);
db.remove_function("half", 1)?; db.remove_function("half", 1)?;
let result: Result<f64> = db.query_row("SELECT half(6)", [], |r| r.get(0)); let result: Result<f64> = db.one_column("SELECT half(6)");
result.unwrap_err(); result.unwrap_err();
Ok(()) Ok(())
} }
@ -885,18 +885,14 @@ mod test {
regexp_with_auxilliary, regexp_with_auxilliary,
)?; )?;
let result: Result<bool> = let result: bool = db.one_column("SELECT regexp('l.s[aeiouy]', 'lisa')")?;
db.query_row("SELECT regexp('l.s[aeiouy]', 'lisa')", [], |r| r.get(0));
assert!(result?); assert!(result);
let result: Result<i64> = db.query_row( let result: i64 =
"SELECT COUNT(*) FROM foo WHERE regexp('l.s[aeiouy]', x) == 1", db.one_column("SELECT COUNT(*) FROM foo WHERE regexp('l.s[aeiouy]', x) == 1")?;
[],
|r| r.get(0),
);
assert_eq!(2, result?); assert_eq!(2, result);
Ok(()) Ok(())
} }
@ -924,7 +920,7 @@ mod test {
("onetwo", "SELECT my_concat('one', 'two')"), ("onetwo", "SELECT my_concat('one', 'two')"),
("abc", "SELECT my_concat('a', 'b', 'c')"), ("abc", "SELECT my_concat('a', 'b', 'c')"),
] { ] {
let result: String = db.query_row(query, [], |r| r.get(0))?; let result: String = db.one_column(query)?;
assert_eq!(expected, result); assert_eq!(expected, result);
} }
Ok(()) Ok(())
@ -943,11 +939,8 @@ mod test {
Ok(true) Ok(true)
})?; })?;
let res: bool = db.query_row( let res: bool =
"SELECT example(0, i) FROM (SELECT 0 as i UNION SELECT 1)", db.one_column("SELECT example(0, i) FROM (SELECT 0 as i UNION SELECT 1)")?;
[],
|r| r.get(0),
)?;
// Doesn't actually matter, we'll assert in the function if there's a problem. // Doesn't actually matter, we'll assert in the function if there's a problem.
assert!(res); assert!(res);
Ok(()) Ok(())
@ -998,11 +991,11 @@ mod test {
// sum should return NULL when given no columns (contrast with count below) // sum should return NULL when given no columns (contrast with count below)
let no_result = "SELECT my_sum(i) FROM (SELECT 2 AS i WHERE 1 <> 1)"; let no_result = "SELECT my_sum(i) FROM (SELECT 2 AS i WHERE 1 <> 1)";
let result: Option<i64> = db.query_row(no_result, [], |r| r.get(0))?; let result: Option<i64> = db.one_column(no_result)?;
assert!(result.is_none()); assert!(result.is_none());
let single_sum = "SELECT my_sum(i) FROM (SELECT 2 AS i UNION ALL SELECT 2)"; let single_sum = "SELECT my_sum(i) FROM (SELECT 2 AS i UNION ALL SELECT 2)";
let result: i64 = db.query_row(single_sum, [], |r| r.get(0))?; let result: i64 = db.one_column(single_sum)?;
assert_eq!(4, result); assert_eq!(4, result);
let dual_sum = "SELECT my_sum(i), my_sum(j) FROM (SELECT 2 AS i, 1 AS j UNION ALL SELECT \ let dual_sum = "SELECT my_sum(i), my_sum(j) FROM (SELECT 2 AS i, 1 AS j UNION ALL SELECT \
@ -1024,11 +1017,11 @@ mod test {
// count should return 0 when given no columns (contrast with sum above) // count should return 0 when given no columns (contrast with sum above)
let no_result = "SELECT my_count(i) FROM (SELECT 2 AS i WHERE 1 <> 1)"; let no_result = "SELECT my_count(i) FROM (SELECT 2 AS i WHERE 1 <> 1)";
let result: i64 = db.query_row(no_result, [], |r| r.get(0))?; let result: i64 = db.one_column(no_result)?;
assert_eq!(result, 0); assert_eq!(result, 0);
let single_sum = "SELECT my_count(i) FROM (SELECT 2 AS i UNION ALL SELECT 2)"; let single_sum = "SELECT my_count(i) FROM (SELECT 2 AS i UNION ALL SELECT 2)";
let result: i64 = db.query_row(single_sum, [], |r| r.get(0))?; let result: i64 = db.one_column(single_sum)?;
assert_eq!(2, result); assert_eq!(2, result);
Ok(()) Ok(())
} }

View File

@ -62,7 +62,7 @@ use std::ffi::{CStr, CString};
use std::fmt; use std::fmt;
use std::os::raw::{c_char, c_int}; use std::os::raw::{c_char, c_int};
use std::path::{Path, PathBuf}; use std::path::Path;
use std::result; use std::result;
use std::str; use std::str;
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
@ -323,7 +323,6 @@ impl DatabaseName<'_> {
pub struct Connection { pub struct Connection {
db: RefCell<InnerConnection>, db: RefCell<InnerConnection>,
cache: StatementCache, cache: StatementCache,
path: Option<PathBuf>,
} }
unsafe impl Send for Connection {} unsafe impl Send for Connection {}
@ -420,7 +419,6 @@ impl Connection {
InnerConnection::open_with_flags(&c_path, flags, None).map(|db| Connection { InnerConnection::open_with_flags(&c_path, flags, None).map(|db| Connection {
db: RefCell::new(db), db: RefCell::new(db),
cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY), cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY),
path: Some(path.as_ref().to_path_buf()),
}) })
} }
@ -445,7 +443,6 @@ impl Connection {
InnerConnection::open_with_flags(&c_path, flags, Some(&c_vfs)).map(|db| Connection { InnerConnection::open_with_flags(&c_path, flags, Some(&c_vfs)).map(|db| Connection {
db: RefCell::new(db), db: RefCell::new(db),
cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY), cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY),
path: Some(path.as_ref().to_path_buf()),
}) })
} }
@ -527,7 +524,7 @@ impl Connection {
/// ```rust,no_run /// ```rust,no_run
/// # use rusqlite::{Connection}; /// # use rusqlite::{Connection};
/// fn update_rows(conn: &Connection) { /// fn update_rows(conn: &Connection) {
/// match conn.execute("UPDATE foo SET bar = 'baz' WHERE qux = ?", [1i32]) { /// match conn.execute("UPDATE foo SET bar = 'baz' WHERE qux = ?1", [1i32]) {
/// Ok(updated) => println!("{} rows were updated", updated), /// Ok(updated) => println!("{} rows were updated", updated),
/// Err(err) => println!("update failed: {}", err), /// Err(err) => println!("update failed: {}", err),
/// } /// }
@ -573,12 +570,23 @@ impl Connection {
/// Returns the path to the database file, if one exists and is known. /// Returns the path to the database file, if one exists and is known.
/// ///
/// Returns `Some("")` for a temporary or in-memory database.
///
/// Note that in some cases [PRAGMA /// Note that in some cases [PRAGMA
/// database_list](https://sqlite.org/pragma.html#pragma_database_list) is /// database_list](https://sqlite.org/pragma.html#pragma_database_list) is
/// likely to be more robust. /// likely to be more robust.
#[inline] #[inline]
pub fn path(&self) -> Option<&Path> { pub fn path(&self) -> Option<&str> {
self.path.as_deref() unsafe {
let db = self.handle();
let db_name = DatabaseName::Main.as_cstring().unwrap();
let db_filename = ffi::sqlite3_db_filename(db, db_name.as_ptr());
if db_filename.is_null() {
None
} else {
CStr::from_ptr(db_filename).to_str().ok()
}
}
} }
/// Attempts to free as much heap memory as possible from the database /// Attempts to free as much heap memory as possible from the database
@ -638,6 +646,12 @@ impl Connection {
stmt.query_row(params, f) stmt.query_row(params, f)
} }
// https://sqlite.org/tclsqlite.html#onecolumn
#[cfg(test)]
pub(crate) fn one_column<T: crate::types::FromSql>(&self, sql: &str) -> Result<T> {
self.query_row(sql, [], |r| r.get(0))
}
/// Convenience method to execute a query that is expected to return a /// Convenience method to execute a query that is expected to return a
/// single row, and execute a mapping via `f` on that returned row with /// single row, and execute a mapping via `f` on that returned row with
/// the possibility of failure. The `Result` type of `f` must implement /// the possibility of failure. The `Result` type of `f` must implement
@ -684,7 +698,7 @@ impl Connection {
/// ```rust,no_run /// ```rust,no_run
/// # use rusqlite::{Connection, Result}; /// # use rusqlite::{Connection, Result};
/// fn insert_new_people(conn: &Connection) -> Result<()> { /// fn insert_new_people(conn: &Connection) -> Result<()> {
/// let mut stmt = conn.prepare("INSERT INTO People (name) VALUES (?)")?; /// let mut stmt = conn.prepare("INSERT INTO People (name) VALUES (?1)")?;
/// stmt.execute(["Joe Smith"])?; /// stmt.execute(["Joe Smith"])?;
/// stmt.execute(["Bob Jones"])?; /// stmt.execute(["Bob Jones"])?;
/// Ok(()) /// Ok(())
@ -866,12 +880,10 @@ impl Connection {
/// This function is unsafe because improper use may impact the Connection. /// This function is unsafe because improper use may impact the Connection.
#[inline] #[inline]
pub unsafe fn from_handle(db: *mut ffi::sqlite3) -> Result<Connection> { pub unsafe fn from_handle(db: *mut ffi::sqlite3) -> Result<Connection> {
let db_path = db_filename(db);
let db = InnerConnection::new(db, false); let db = InnerConnection::new(db, false);
Ok(Connection { Ok(Connection {
db: RefCell::new(db), db: RefCell::new(db),
cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY), cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY),
path: db_path,
}) })
} }
@ -924,7 +936,7 @@ impl Connection {
impl fmt::Debug for Connection { impl fmt::Debug for Connection {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Connection") f.debug_struct("Connection")
.field("path", &self.path) .field("path", &self.path())
.finish() .finish()
} }
} }
@ -1115,16 +1127,6 @@ impl InterruptHandle {
} }
} }
unsafe fn db_filename(db: *mut ffi::sqlite3) -> Option<PathBuf> {
let db_name = DatabaseName::Main.as_cstring().unwrap();
let db_filename = ffi::sqlite3_db_filename(db, db_name.as_ptr());
if db_filename.is_null() {
None
} else {
CStr::from_ptr(db_filename).to_str().ok().map(PathBuf::from)
}
}
#[cfg(doctest)] #[cfg(doctest)]
doc_comment::doctest!("../README.md"); doc_comment::doctest!("../README.md");
@ -1212,9 +1214,9 @@ mod test {
let path_string = path.to_str().unwrap(); let path_string = path.to_str().unwrap();
let db = Connection::open(path_string)?; let db = Connection::open(path_string)?;
let the_answer: Result<i64> = db.query_row("SELECT x FROM foo", [], |r| r.get(0)); let the_answer: i64 = db.one_column("SELECT x FROM foo")?;
assert_eq!(42i64, the_answer?); assert_eq!(42i64, the_answer);
Ok(()) Ok(())
} }
@ -1226,6 +1228,21 @@ mod test {
db.close().unwrap(); db.close().unwrap();
} }
#[test]
fn test_path() -> Result<()> {
let tmp = tempfile::tempdir().unwrap();
let db = Connection::open("")?;
assert_eq!(Some(""), db.path());
let db = Connection::open_in_memory()?;
assert_eq!(Some(""), db.path());
let db = Connection::open("file:dummy.db?mode=memory&cache=shared")?;
assert_eq!(Some(""), db.path());
let path = tmp.path().join("file.db");
let db = Connection::open(path)?;
assert!(db.path().map(|p| p.ends_with("file.db")).unwrap_or(false));
Ok(())
}
#[test] #[test]
fn test_open_failure() { fn test_open_failure() {
let filename = "no_such_file.db"; let filename = "no_such_file.db";
@ -1269,9 +1286,9 @@ mod test {
} }
let db = Connection::open(&db_path)?; let db = Connection::open(&db_path)?;
let the_answer: Result<i64> = db.query_row("SELECT x FROM foo", [], |r| r.get(0)); let the_answer: i64 = db.one_column("SELECT x FROM foo")?;
assert_eq!(42i64, the_answer?); assert_eq!(42i64, the_answer);
Ok(()) Ok(())
} }
@ -1351,13 +1368,10 @@ mod test {
let db = Connection::open_in_memory()?; let db = Connection::open_in_memory()?;
db.execute_batch("CREATE TABLE foo(x INTEGER)")?; db.execute_batch("CREATE TABLE foo(x INTEGER)")?;
assert_eq!(1, db.execute("INSERT INTO foo(x) VALUES (?)", [1i32])?); assert_eq!(1, db.execute("INSERT INTO foo(x) VALUES (?1)", [1i32])?);
assert_eq!(1, db.execute("INSERT INTO foo(x) VALUES (?)", [2i32])?); assert_eq!(1, db.execute("INSERT INTO foo(x) VALUES (?1)", [2i32])?);
assert_eq!( assert_eq!(3i32, db.one_column::<i32>("SELECT SUM(x) FROM foo")?);
3i32,
db.query_row::<i32, _, _>("SELECT SUM(x) FROM foo", [], |r| r.get(0))?
);
Ok(()) Ok(())
} }
@ -1365,7 +1379,7 @@ mod test {
#[cfg(feature = "extra_check")] #[cfg(feature = "extra_check")]
fn test_execute_select() { fn test_execute_select() {
let db = checked_memory_handle(); let db = checked_memory_handle();
let err = db.execute("SELECT 1 WHERE 1 < ?", [1i32]).unwrap_err(); let err = db.execute("SELECT 1 WHERE 1 < ?1", [1i32]).unwrap_err();
assert_eq!( assert_eq!(
err, err,
Error::ExecuteReturnedResults, Error::ExecuteReturnedResults,
@ -1410,7 +1424,7 @@ mod test {
let db = Connection::open_in_memory()?; let db = Connection::open_in_memory()?;
db.execute_batch("CREATE TABLE foo(x INTEGER);")?; db.execute_batch("CREATE TABLE foo(x INTEGER);")?;
let mut insert_stmt = db.prepare("INSERT INTO foo(x) VALUES(?)")?; let mut insert_stmt = db.prepare("INSERT INTO foo(x) VALUES(?1)")?;
assert_eq!(insert_stmt.execute([1i32])?, 1); assert_eq!(insert_stmt.execute([1i32])?, 1);
assert_eq!(insert_stmt.execute([2i32])?, 1); assert_eq!(insert_stmt.execute([2i32])?, 1);
assert_eq!(insert_stmt.execute([3i32])?, 1); assert_eq!(insert_stmt.execute([3i32])?, 1);
@ -1419,7 +1433,7 @@ mod test {
assert_eq!(insert_stmt.execute(["goodbye"])?, 1); assert_eq!(insert_stmt.execute(["goodbye"])?, 1);
assert_eq!(insert_stmt.execute([types::Null])?, 1); assert_eq!(insert_stmt.execute([types::Null])?, 1);
let mut update_stmt = db.prepare("UPDATE foo SET x=? WHERE x<?")?; let mut update_stmt = db.prepare("UPDATE foo SET x=?1 WHERE x<?2")?;
assert_eq!(update_stmt.execute([3i32, 3i32])?, 2); assert_eq!(update_stmt.execute([3i32, 3i32])?, 2);
assert_eq!(update_stmt.execute([3i32, 3i32])?, 0); assert_eq!(update_stmt.execute([3i32, 3i32])?, 0);
assert_eq!(update_stmt.execute([8i32, 8i32])?, 3); assert_eq!(update_stmt.execute([8i32, 8i32])?, 3);
@ -1431,12 +1445,12 @@ mod test {
let db = Connection::open_in_memory()?; let db = Connection::open_in_memory()?;
db.execute_batch("CREATE TABLE foo(x INTEGER);")?; db.execute_batch("CREATE TABLE foo(x INTEGER);")?;
let mut insert_stmt = db.prepare("INSERT INTO foo(x) VALUES(?)")?; let mut insert_stmt = db.prepare("INSERT INTO foo(x) VALUES(?1)")?;
assert_eq!(insert_stmt.execute([1i32])?, 1); assert_eq!(insert_stmt.execute([1i32])?, 1);
assert_eq!(insert_stmt.execute([2i32])?, 1); assert_eq!(insert_stmt.execute([2i32])?, 1);
assert_eq!(insert_stmt.execute([3i32])?, 1); assert_eq!(insert_stmt.execute([3i32])?, 1);
let mut query = db.prepare("SELECT x FROM foo WHERE x < ? ORDER BY x DESC")?; let mut query = db.prepare("SELECT x FROM foo WHERE x < ?1 ORDER BY x DESC")?;
{ {
let mut rows = query.query([4i32])?; let mut rows = query.query([4i32])?;
let mut v = Vec::<i32>::new(); let mut v = Vec::<i32>::new();
@ -1492,12 +1506,9 @@ mod test {
END;"; END;";
db.execute_batch(sql)?; db.execute_batch(sql)?;
assert_eq!( assert_eq!(10i64, db.one_column::<i64>("SELECT SUM(x) FROM foo")?);
10i64,
db.query_row::<i64, _, _>("SELECT SUM(x) FROM foo", [], |r| r.get(0))?
);
let result: Result<i64> = db.query_row("SELECT x FROM foo WHERE x > 5", [], |r| r.get(0)); let result: Result<i64> = db.one_column("SELECT x FROM foo WHERE x > 5");
match result.unwrap_err() { match result.unwrap_err() {
Error::QueryReturnedNoRows => (), Error::QueryReturnedNoRows => (),
err => panic!("Unexpected error {}", err), err => panic!("Unexpected error {}", err),
@ -1513,21 +1524,21 @@ mod test {
fn test_optional() -> Result<()> { fn test_optional() -> Result<()> {
let db = Connection::open_in_memory()?; let db = Connection::open_in_memory()?;
let result: Result<i64> = db.query_row("SELECT 1 WHERE 0 <> 0", [], |r| r.get(0)); let result: Result<i64> = db.one_column("SELECT 1 WHERE 0 <> 0");
let result = result.optional(); let result = result.optional();
match result? { match result? {
None => (), None => (),
_ => panic!("Unexpected result"), _ => panic!("Unexpected result"),
} }
let result: Result<i64> = db.query_row("SELECT 1 WHERE 0 == 0", [], |r| r.get(0)); let result: Result<i64> = db.one_column("SELECT 1 WHERE 0 == 0");
let result = result.optional(); let result = result.optional();
match result? { match result? {
Some(1) => (), Some(1) => (),
_ => panic!("Unexpected result"), _ => panic!("Unexpected result"),
} }
let bad_query_result: Result<i64> = db.query_row("NOT A PROPER QUERY", [], |r| r.get(0)); let bad_query_result: Result<i64> = db.one_column("NOT A PROPER QUERY");
let bad_query_result = bad_query_result.optional(); let bad_query_result = bad_query_result.optional();
bad_query_result.unwrap_err(); bad_query_result.unwrap_err();
Ok(()) Ok(())
@ -1536,11 +1547,8 @@ mod test {
#[test] #[test]
fn test_pragma_query_row() -> Result<()> { fn test_pragma_query_row() -> Result<()> {
let db = Connection::open_in_memory()?; let db = Connection::open_in_memory()?;
assert_eq!( assert_eq!("memory", db.one_column::<String>("PRAGMA journal_mode")?);
"memory", let mode = db.one_column::<String>("PRAGMA journal_mode=off")?;
db.query_row::<String, _, _>("PRAGMA journal_mode", [], |r| r.get(0))?
);
let mode = db.query_row::<String, _, _>("PRAGMA journal_mode=off", [], |r| r.get(0))?;
if cfg!(features = "bundled") { if cfg!(features = "bundled") {
assert_eq!(mode, "off"); assert_eq!(mode, "off");
} else { } else {
@ -1707,7 +1715,7 @@ mod test {
let db = Connection::open_in_memory()?; let db = Connection::open_in_memory()?;
db.execute_batch("CREATE TABLE foo(i, x);")?; db.execute_batch("CREATE TABLE foo(i, x);")?;
let vals = ["foobar", "1234", "qwerty"]; let vals = ["foobar", "1234", "qwerty"];
let mut insert_stmt = db.prepare("INSERT INTO foo(i, x) VALUES(?, ?)")?; let mut insert_stmt = db.prepare("INSERT INTO foo(i, x) VALUES(?1, ?2)")?;
for (i, v) in vals.iter().enumerate() { for (i, v) in vals.iter().enumerate() {
let i_to_insert = i as i64; let i_to_insert = i as i64;
assert_eq!(insert_stmt.execute(params![i_to_insert, v])?, 1); assert_eq!(insert_stmt.execute(params![i_to_insert, v])?, 1);
@ -1973,7 +1981,7 @@ mod test {
let db = Connection::open_in_memory()?; let db = Connection::open_in_memory()?;
db.execute_batch("CREATE TABLE foo(x INTEGER);")?; db.execute_batch("CREATE TABLE foo(x INTEGER);")?;
let b: Box<dyn ToSql> = Box::new(5); let b: Box<dyn ToSql> = Box::new(5);
db.execute("INSERT INTO foo VALUES(?)", [b])?; db.execute("INSERT INTO foo VALUES(?1)", [b])?;
db.query_row("SELECT x FROM foo", [], |r| { db.query_row("SELECT x FROM foo", [], |r| {
assert_eq!(5, r.get_unwrap::<_, i32>(0)); assert_eq!(5, r.get_unwrap::<_, i32>(0));
Ok(()) Ok(())
@ -1985,10 +1993,10 @@ mod test {
let db = Connection::open_in_memory()?; let db = Connection::open_in_memory()?;
db.query_row( db.query_row(
"SELECT "SELECT
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10,
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19, ?20,
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?21, ?22, ?23, ?24, ?25, ?26, ?27, ?28, ?29, ?30,
?, ?, ?, ?;", ?31, ?32, ?33, ?34;",
params![ params![
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
@ -2030,10 +2038,7 @@ mod test {
fn test_returning() -> Result<()> { fn test_returning() -> Result<()> {
let db = Connection::open_in_memory()?; let db = Connection::open_in_memory()?;
db.execute_batch("CREATE TABLE foo(x INTEGER PRIMARY KEY)")?; db.execute_batch("CREATE TABLE foo(x INTEGER PRIMARY KEY)")?;
let row_id = let row_id = db.one_column::<i64>("INSERT INTO foo DEFAULT VALUES RETURNING ROWID")?;
db.query_row::<i64, _, _>("INSERT INTO foo DEFAULT VALUES RETURNING ROWID", [], |r| {
r.get(0)
})?;
assert_eq!(row_id, 1); assert_eq!(row_id, 1);
Ok(()) Ok(())
} }

View File

@ -70,7 +70,7 @@ use sealed::Sealed;
/// ```rust,no_run /// ```rust,no_run
/// # use rusqlite::{Connection, Result, params}; /// # use rusqlite::{Connection, Result, params};
/// fn update_rows(conn: &Connection) -> Result<()> { /// fn update_rows(conn: &Connection) -> Result<()> {
/// let mut stmt = conn.prepare("INSERT INTO test (a, b) VALUES (?, ?)")?; /// let mut stmt = conn.prepare("INSERT INTO test (a, b) VALUES (?1, ?2)")?;
/// ///
/// // Using a tuple: /// // Using a tuple:
/// stmt.execute((0, "foobar"))?; /// stmt.execute((0, "foobar"))?;
@ -354,7 +354,7 @@ impl_for_array_ref!(
/// fn query(conn: &Connection, ids: &BTreeSet<String>) -> Result<()> { /// fn query(conn: &Connection, ids: &BTreeSet<String>) -> Result<()> {
/// assert_eq!(ids.len(), 3, "Unrealistic sample code"); /// assert_eq!(ids.len(), 3, "Unrealistic sample code");
/// ///
/// let mut stmt = conn.prepare("SELECT * FROM users WHERE id IN (?, ?, ?)")?; /// let mut stmt = conn.prepare("SELECT * FROM users WHERE id IN (?1, ?2, ?3)")?;
/// let _rows = stmt.query(params_from_iter(ids.iter()))?; /// let _rows = stmt.query(params_from_iter(ids.iter()))?;
/// ///
/// // use _rows... /// // use _rows...

View File

@ -211,7 +211,7 @@ impl Connection {
/// (e.g. `integrity_check`). /// (e.g. `integrity_check`).
/// ///
/// Prefer [PRAGMA function](https://sqlite.org/pragma.html#pragfunc) introduced in SQLite 3.20: /// Prefer [PRAGMA function](https://sqlite.org/pragma.html#pragfunc) introduced in SQLite 3.20:
/// `SELECT * FROM pragma_table_info(?);` /// `SELECT * FROM pragma_table_info(?1);`
pub fn pragma<F, V>( pub fn pragma<F, V>(
&self, &self,
schema_name: Option<DatabaseName<'_>>, schema_name: Option<DatabaseName<'_>>,
@ -333,10 +333,7 @@ mod test {
#[cfg(feature = "modern_sqlite")] #[cfg(feature = "modern_sqlite")]
fn pragma_func_query_value() -> Result<()> { fn pragma_func_query_value() -> Result<()> {
let db = Connection::open_in_memory()?; let db = Connection::open_in_memory()?;
let user_version: i32 = let user_version: i32 = db.one_column("SELECT user_version FROM pragma_user_version")?;
db.query_row("SELECT user_version FROM pragma_user_version", [], |row| {
row.get(0)
})?;
assert_eq!(0, user_version); assert_eq!(0, user_version);
Ok(()) Ok(())
} }
@ -382,7 +379,7 @@ mod test {
#[cfg(feature = "modern_sqlite")] #[cfg(feature = "modern_sqlite")]
fn pragma_func() -> Result<()> { fn pragma_func() -> Result<()> {
let db = Connection::open_in_memory()?; let db = Connection::open_in_memory()?;
let mut table_info = db.prepare("SELECT * FROM pragma_table_info(?)")?; let mut table_info = db.prepare("SELECT * FROM pragma_table_info(?1)")?;
let mut columns = Vec::new(); let mut columns = Vec::new();
let mut rows = table_info.query(["sqlite_master"])?; let mut rows = table_info.query(["sqlite_master"])?;

View File

@ -346,6 +346,46 @@ impl<'stmt> AsRef<Statement<'stmt>> for Row<'stmt> {
} }
} }
/// Debug `Row` like an ordered `Map<Result<&str>, Result<(Type, ValueRef)>>`
/// with column name as key except that for `Type::Blob` only its size is
/// printed (not its content).
impl<'stmt> std::fmt::Debug for Row<'stmt> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut dm = f.debug_map();
for c in 0..self.stmt.column_count() {
let name = self.stmt.column_name(c);
dm.key(&name);
let value = self.get_ref(c);
match value {
Ok(value) => {
let dt = value.data_type();
match value {
ValueRef::Null => {
dm.value(&(dt, ()));
}
ValueRef::Integer(i) => {
dm.value(&(dt, i));
}
ValueRef::Real(f) => {
dm.value(&(dt, f));
}
ValueRef::Text(s) => {
dm.value(&(dt, String::from_utf8_lossy(s)));
}
ValueRef::Blob(b) => {
dm.value(&(dt, b.len()));
}
}
}
Err(ref _err) => {
dm.value(&value);
}
}
}
dm.finish()
}
}
mod sealed { mod sealed {
/// This trait exists just to ensure that the only impls of `trait Params` /// This trait exists just to ensure that the only impls of `trait Params`
/// that are allowed are ones in this crate. /// that are allowed are ones in this crate.

View File

@ -779,7 +779,7 @@ mod test {
assert!(session.is_empty()); assert!(session.is_empty());
session.attach(None)?; session.attach(None)?;
db.execute("INSERT INTO foo (t) VALUES (?);", ["bar"])?; db.execute("INSERT INTO foo (t) VALUES (?1);", ["bar"])?;
session.changeset() session.changeset()
} }
@ -792,7 +792,7 @@ mod test {
assert!(session.is_empty()); assert!(session.is_empty());
session.attach(None)?; session.attach(None)?;
db.execute("INSERT INTO foo (t) VALUES (?);", ["bar"])?; db.execute("INSERT INTO foo (t) VALUES (?1);", ["bar"])?;
let mut output = Vec::new(); let mut output = Vec::new();
session.changeset_strm(&mut output)?; session.changeset_strm(&mut output)?;
@ -852,7 +852,7 @@ mod test {
)?; )?;
assert!(!CALLED.load(Ordering::Relaxed)); assert!(!CALLED.load(Ordering::Relaxed));
let check = db.query_row("SELECT 1 FROM foo WHERE t = ?", ["bar"], |row| { let check = db.query_row("SELECT 1 FROM foo WHERE t = ?1", ["bar"], |row| {
row.get::<_, i32>(0) row.get::<_, i32>(0)
})?; })?;
assert_eq!(1, check); assert_eq!(1, check);
@ -887,7 +887,7 @@ mod test {
|_conflict_type, _item| ConflictAction::SQLITE_CHANGESET_OMIT, |_conflict_type, _item| ConflictAction::SQLITE_CHANGESET_OMIT,
)?; )?;
let check = db.query_row("SELECT 1 FROM foo WHERE t = ?", ["bar"], |row| { let check = db.query_row("SELECT 1 FROM foo WHERE t = ?1", ["bar"], |row| {
row.get::<_, i32>(0) row.get::<_, i32>(0)
})?; })?;
assert_eq!(1, check); assert_eq!(1, check);
@ -903,7 +903,7 @@ mod test {
assert!(session.is_empty()); assert!(session.is_empty());
session.attach(None)?; session.attach(None)?;
db.execute("INSERT INTO foo (t) VALUES (?);", ["bar"])?; db.execute("INSERT INTO foo (t) VALUES (?1);", ["bar"])?;
assert!(!session.is_empty()); assert!(!session.is_empty());
Ok(()) Ok(())

View File

@ -33,7 +33,7 @@ impl Statement<'_> {
/// ```rust,no_run /// ```rust,no_run
/// # use rusqlite::{Connection, Result, params}; /// # use rusqlite::{Connection, Result, params};
/// fn update_rows(conn: &Connection) -> Result<()> { /// fn update_rows(conn: &Connection) -> Result<()> {
/// let mut stmt = conn.prepare("UPDATE foo SET bar = ? WHERE qux = ?")?; /// let mut stmt = conn.prepare("UPDATE foo SET bar = ?1 WHERE qux = ?2")?;
/// // For a single parameter, or a parameter where all the values have /// // For a single parameter, or a parameter where all the values have
/// // the same type, just passing an array is simplest. /// // the same type, just passing an array is simplest.
/// stmt.execute([2i32])?; /// stmt.execute([2i32])?;
@ -58,7 +58,7 @@ impl Statement<'_> {
/// fn store_file(conn: &Connection, path: &str, data: &[u8]) -> Result<()> { /// fn store_file(conn: &Connection, path: &str, data: &[u8]) -> Result<()> {
/// # // no need to do it for real. /// # // no need to do it for real.
/// # fn sha256(_: &[u8]) -> [u8; 32] { [0; 32] } /// # fn sha256(_: &[u8]) -> [u8; 32] { [0; 32] }
/// let query = "INSERT OR REPLACE INTO files(path, hash, data) VALUES (?, ?, ?)"; /// let query = "INSERT OR REPLACE INTO files(path, hash, data) VALUES (?1, ?2, ?3)";
/// let mut stmt = conn.prepare_cached(query)?; /// let mut stmt = conn.prepare_cached(query)?;
/// let hash: [u8; 32] = sha256(data); /// let hash: [u8; 32] = sha256(data);
/// // The easiest way to pass positional parameters of have several /// // The easiest way to pass positional parameters of have several
@ -168,7 +168,7 @@ impl Statement<'_> {
/// ```rust,no_run /// ```rust,no_run
/// # use rusqlite::{Connection, Result}; /// # use rusqlite::{Connection, Result};
/// fn query(conn: &Connection, name: &str) -> Result<()> { /// fn query(conn: &Connection, name: &str) -> Result<()> {
/// let mut stmt = conn.prepare("SELECT * FROM test where name = ?")?; /// let mut stmt = conn.prepare("SELECT * FROM test where name = ?1")?;
/// let mut rows = stmt.query(rusqlite::params![name])?; /// let mut rows = stmt.query(rusqlite::params![name])?;
/// while let Some(row) = rows.next()? { /// while let Some(row) = rows.next()? {
/// // ... /// // ...
@ -182,7 +182,7 @@ impl Statement<'_> {
/// ```rust,no_run /// ```rust,no_run
/// # use rusqlite::{Connection, Result}; /// # use rusqlite::{Connection, Result};
/// fn query(conn: &Connection, name: &str) -> Result<()> { /// fn query(conn: &Connection, name: &str) -> Result<()> {
/// let mut stmt = conn.prepare("SELECT * FROM test where name = ?")?; /// let mut stmt = conn.prepare("SELECT * FROM test where name = ?1")?;
/// let mut rows = stmt.query([name])?; /// let mut rows = stmt.query([name])?;
/// while let Some(row) = rows.next()? { /// while let Some(row) = rows.next()? {
/// // ... /// // ...
@ -322,7 +322,7 @@ impl Statement<'_> {
/// ```rust,no_run /// ```rust,no_run
/// # use rusqlite::{Connection, Result}; /// # use rusqlite::{Connection, Result};
/// fn get_names(conn: &Connection) -> Result<Vec<String>> { /// fn get_names(conn: &Connection) -> Result<Vec<String>> {
/// let mut stmt = conn.prepare("SELECT name FROM people WHERE id = ?")?; /// let mut stmt = conn.prepare("SELECT name FROM people WHERE id = ?1")?;
/// let rows = stmt.query_and_then(["one"], |row| row.get::<_, String>(0))?; /// let rows = stmt.query_and_then(["one"], |row| row.get::<_, String>(0))?;
/// ///
/// let mut persons = Vec::new(); /// let mut persons = Vec::new();
@ -1010,8 +1010,7 @@ mod test {
let mut stmt = db.prepare("INSERT INTO test (x, y) VALUES (:x, :y)")?; let mut stmt = db.prepare("INSERT INTO test (x, y) VALUES (:x, :y)")?;
stmt.execute(&[(":x", &"one")])?; stmt.execute(&[(":x", &"one")])?;
let result: Option<String> = let result: Option<String> = db.one_column("SELECT y FROM test WHERE x = 'one'")?;
db.query_row("SELECT y FROM test WHERE x = 'one'", [], |row| row.get(0))?;
assert!(result.is_none()); assert!(result.is_none());
Ok(()) Ok(())
} }
@ -1057,8 +1056,7 @@ mod test {
stmt.execute(&[(":x", "one")])?; stmt.execute(&[(":x", "one")])?;
stmt.execute(&[(":y", "two")])?; stmt.execute(&[(":y", "two")])?;
let result: String = let result: String = db.one_column("SELECT x FROM test WHERE y = 'two'")?;
db.query_row("SELECT x FROM test WHERE y = 'two'", [], |row| row.get(0))?;
assert_eq!(result, "one"); assert_eq!(result, "one");
Ok(()) Ok(())
} }
@ -1067,7 +1065,7 @@ mod test {
fn test_insert() -> Result<()> { fn test_insert() -> Result<()> {
let db = Connection::open_in_memory()?; let db = Connection::open_in_memory()?;
db.execute_batch("CREATE TABLE foo(x INTEGER UNIQUE)")?; db.execute_batch("CREATE TABLE foo(x INTEGER UNIQUE)")?;
let mut stmt = db.prepare("INSERT OR IGNORE INTO foo (x) VALUES (?)")?; let mut stmt = db.prepare("INSERT OR IGNORE INTO foo (x) VALUES (?1)")?;
assert_eq!(stmt.insert([1i32])?, 1); assert_eq!(stmt.insert([1i32])?, 1);
assert_eq!(stmt.insert([2i32])?, 2); assert_eq!(stmt.insert([2i32])?, 2);
match stmt.insert([1i32]).unwrap_err() { match stmt.insert([1i32]).unwrap_err() {
@ -1107,7 +1105,7 @@ mod test {
INSERT INTO foo VALUES(2); INSERT INTO foo VALUES(2);
END;"; END;";
db.execute_batch(sql)?; db.execute_batch(sql)?;
let mut stmt = db.prepare("SELECT 1 FROM foo WHERE x = ?")?; let mut stmt = db.prepare("SELECT 1 FROM foo WHERE x = ?1")?;
assert!(stmt.exists([1i32])?); assert!(stmt.exists([1i32])?);
assert!(stmt.exists([2i32])?); assert!(stmt.exists([2i32])?);
assert!(!stmt.exists([0i32])?); assert!(!stmt.exists([0i32])?);
@ -1116,18 +1114,18 @@ mod test {
#[test] #[test]
fn test_tuple_params() -> Result<()> { fn test_tuple_params() -> Result<()> {
let db = Connection::open_in_memory()?; let db = Connection::open_in_memory()?;
let s = db.query_row("SELECT printf('[%s]', ?)", ("abc",), |r| { let s = db.query_row("SELECT printf('[%s]', ?1)", ("abc",), |r| {
r.get::<_, String>(0) r.get::<_, String>(0)
})?; })?;
assert_eq!(s, "[abc]"); assert_eq!(s, "[abc]");
let s = db.query_row( let s = db.query_row(
"SELECT printf('%d %s %d', ?, ?, ?)", "SELECT printf('%d %s %d', ?1, ?2, ?3)",
(1i32, "abc", 2i32), (1i32, "abc", 2i32),
|r| r.get::<_, String>(0), |r| r.get::<_, String>(0),
)?; )?;
assert_eq!(s, "1 abc 2"); assert_eq!(s, "1 abc 2");
let s = db.query_row( let s = db.query_row(
"SELECT printf('%d %s %d %d', ?, ?, ?, ?)", "SELECT printf('%d %s %d %d', ?1, ?2, ?3, ?4)",
(1, "abc", 2i32, 4i64), (1, "abc", 2i32, 4i64),
|r| r.get::<_, String>(0), |r| r.get::<_, String>(0),
)?; )?;
@ -1139,10 +1137,10 @@ mod test {
); );
let query = "SELECT printf( let query = "SELECT printf(
'%d %s | %d %s | %d %s | %d %s || %d %s | %d %s | %d %s | %d %s', '%d %s | %d %s | %d %s | %d %s || %d %s | %d %s | %d %s | %d %s',
?, ?, ?, ?, ?1, ?2, ?3, ?4,
?, ?, ?, ?, ?5, ?6, ?7, ?8,
?, ?, ?, ?, ?9, ?10, ?11, ?12,
?, ?, ?, ? ?13, ?14, ?15, ?16
)"; )";
let s = db.query_row(query, bigtup, |r| r.get::<_, String>(0))?; let s = db.query_row(query, bigtup, |r| r.get::<_, String>(0))?;
assert_eq!(s, "0 a | 1 b | 2 c | 3 d || 4 e | 5 f | 6 g | 7 h"); assert_eq!(s, "0 a | 1 b | 2 c | 3 d || 4 e | 5 f | 6 g | 7 h");
@ -1158,7 +1156,7 @@ mod test {
INSERT INTO foo VALUES(2, 4); INSERT INTO foo VALUES(2, 4);
END;"; END;";
db.execute_batch(sql)?; db.execute_batch(sql)?;
let mut stmt = db.prepare("SELECT y FROM foo WHERE x = ?")?; let mut stmt = db.prepare("SELECT y FROM foo WHERE x = ?1")?;
let y: Result<i64> = stmt.query_row([1i32], |r| r.get(0)); let y: Result<i64> = stmt.query_row([1i32], |r| r.get(0));
assert_eq!(3i64, y?); assert_eq!(3i64, y?);
Ok(()) Ok(())
@ -1195,7 +1193,7 @@ mod test {
#[test] #[test]
fn test_expanded_sql() -> Result<()> { fn test_expanded_sql() -> Result<()> {
let db = Connection::open_in_memory()?; let db = Connection::open_in_memory()?;
let stmt = db.prepare("SELECT ?")?; let stmt = db.prepare("SELECT ?1")?;
stmt.bind_parameter(&1, 1)?; stmt.bind_parameter(&1, 1)?;
assert_eq!(Some("SELECT 1".to_owned()), stmt.expanded_sql()); assert_eq!(Some("SELECT 1".to_owned()), stmt.expanded_sql());
Ok(()) Ok(())
@ -1297,8 +1295,8 @@ mod test {
assert_eq!("UTF-16le", encoding); assert_eq!("UTF-16le", encoding);
db.execute_batch("CREATE TABLE foo(x TEXT)")?; db.execute_batch("CREATE TABLE foo(x TEXT)")?;
let expected = "テスト"; let expected = "テスト";
db.execute("INSERT INTO foo(x) VALUES (?)", [&expected])?; db.execute("INSERT INTO foo(x) VALUES (?1)", [&expected])?;
let actual: String = db.query_row("SELECT x FROM foo", [], |row| row.get(0))?; let actual: String = db.one_column("SELECT x FROM foo")?;
assert_eq!(expected, actual); assert_eq!(expected, actual);
Ok(()) Ok(())
} }
@ -1307,7 +1305,7 @@ mod test {
fn test_nul_byte() -> Result<()> { fn test_nul_byte() -> Result<()> {
let db = Connection::open_in_memory()?; let db = Connection::open_in_memory()?;
let expected = "a\x00b"; let expected = "a\x00b";
let actual: String = db.query_row("SELECT ?", [expected], |row| row.get(0))?; let actual: String = db.query_row("SELECT ?1", [expected], |row| row.get(0))?;
assert_eq!(expected, actual); assert_eq!(expected, actual);
Ok(()) Ok(())
} }

View File

@ -144,13 +144,13 @@ mod test {
let mut db = Connection::open_in_memory()?; let mut db = Connection::open_in_memory()?;
db.trace(Some(tracer)); db.trace(Some(tracer));
{ {
let _ = db.query_row("SELECT ?", [1i32], |_| Ok(())); let _ = db.query_row("SELECT ?1", [1i32], |_| Ok(()));
let _ = db.query_row("SELECT ?", ["hello"], |_| Ok(())); let _ = db.query_row("SELECT ?1", ["hello"], |_| Ok(()));
} }
db.trace(None); db.trace(None);
{ {
let _ = db.query_row("SELECT ?", [2i32], |_| Ok(())); let _ = db.query_row("SELECT ?1", [2i32], |_| Ok(()));
let _ = db.query_row("SELECT ?", ["goodbye"], |_| Ok(())); let _ = db.query_row("SELECT ?1", ["goodbye"], |_| Ok(()));
} }
let traced_stmts = TRACED_STMTS.lock().unwrap(); let traced_stmts = TRACED_STMTS.lock().unwrap();

View File

@ -552,10 +552,7 @@ mod test {
} }
{ {
let tx = db.transaction()?; let tx = db.transaction()?;
assert_eq!( assert_eq!(2i32, tx.one_column::<i32>("SELECT SUM(x) FROM foo")?);
2i32,
tx.query_row::<i32, _, _>("SELECT SUM(x) FROM foo", [], |r| r.get(0))?
);
} }
Ok(()) Ok(())
} }
@ -591,10 +588,7 @@ mod test {
tx.commit()?; tx.commit()?;
} }
assert_eq!( assert_eq!(2i32, db.one_column::<i32>("SELECT SUM(x) FROM foo")?);
2i32,
db.query_row::<i32, _, _>("SELECT SUM(x) FROM foo", [], |r| r.get(0))?
);
Ok(()) Ok(())
} }
@ -619,10 +613,7 @@ mod test {
} }
{ {
let tx = db.transaction()?; let tx = db.transaction()?;
assert_eq!( assert_eq!(6i32, tx.one_column::<i32>("SELECT SUM(x) FROM foo")?);
6i32,
tx.query_row::<i32, _, _>("SELECT SUM(x) FROM foo", [], |r| r.get(0))?
);
} }
Ok(()) Ok(())
} }
@ -727,11 +718,11 @@ mod test {
} }
fn insert(x: i32, conn: &Connection) -> Result<usize> { fn insert(x: i32, conn: &Connection) -> Result<usize> {
conn.execute("INSERT INTO foo VALUES(?)", [x]) conn.execute("INSERT INTO foo VALUES(?1)", [x])
} }
fn assert_current_sum(x: i32, conn: &Connection) -> Result<()> { fn assert_current_sum(x: i32, conn: &Connection) -> Result<()> {
let i = conn.query_row::<i32, _, _>("SELECT SUM(x) FROM foo", [], |r| r.get(0))?; let i = conn.one_column::<i32>("SELECT SUM(x) FROM foo")?;
assert_eq!(x, i); assert_eq!(x, i);
Ok(()) Ok(())
} }

View File

@ -175,12 +175,12 @@ mod test {
#[test] #[test]
fn test_naive_date() -> Result<()> { fn test_naive_date() -> Result<()> {
let db = checked_memory_handle()?; let db = checked_memory_handle()?;
let date = NaiveDate::from_ymd(2016, 2, 23); let date = NaiveDate::from_ymd_opt(2016, 2, 23).unwrap();
db.execute("INSERT INTO foo (t) VALUES (?)", [date])?; db.execute("INSERT INTO foo (t) VALUES (?1)", [date])?;
let s: String = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?; let s: String = db.one_column("SELECT t FROM foo")?;
assert_eq!("2016-02-23", s); assert_eq!("2016-02-23", s);
let t: NaiveDate = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?; let t: NaiveDate = db.one_column("SELECT t FROM foo")?;
assert_eq!(date, t); assert_eq!(date, t);
Ok(()) Ok(())
} }
@ -188,12 +188,12 @@ mod test {
#[test] #[test]
fn test_naive_time() -> Result<()> { fn test_naive_time() -> Result<()> {
let db = checked_memory_handle()?; let db = checked_memory_handle()?;
let time = NaiveTime::from_hms(23, 56, 4); let time = NaiveTime::from_hms_opt(23, 56, 4).unwrap();
db.execute("INSERT INTO foo (t) VALUES (?)", [time])?; db.execute("INSERT INTO foo (t) VALUES (?1)", [time])?;
let s: String = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?; let s: String = db.one_column("SELECT t FROM foo")?;
assert_eq!("23:56:04", s); assert_eq!("23:56:04", s);
let v: NaiveTime = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?; let v: NaiveTime = db.one_column("SELECT t FROM foo")?;
assert_eq!(time, v); assert_eq!(time, v);
Ok(()) Ok(())
} }
@ -201,19 +201,19 @@ mod test {
#[test] #[test]
fn test_naive_date_time() -> Result<()> { fn test_naive_date_time() -> Result<()> {
let db = checked_memory_handle()?; let db = checked_memory_handle()?;
let date = NaiveDate::from_ymd(2016, 2, 23); let date = NaiveDate::from_ymd_opt(2016, 2, 23).unwrap();
let time = NaiveTime::from_hms(23, 56, 4); let time = NaiveTime::from_hms_opt(23, 56, 4).unwrap();
let dt = NaiveDateTime::new(date, time); let dt = NaiveDateTime::new(date, time);
db.execute("INSERT INTO foo (t) VALUES (?)", [dt])?; db.execute("INSERT INTO foo (t) VALUES (?1)", [dt])?;
let s: String = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?; let s: String = db.one_column("SELECT t FROM foo")?;
assert_eq!("2016-02-23 23:56:04", s); assert_eq!("2016-02-23 23:56:04", s);
let v: NaiveDateTime = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?; let v: NaiveDateTime = db.one_column("SELECT t FROM foo")?;
assert_eq!(dt, v); assert_eq!(dt, v);
db.execute("UPDATE foo set b = datetime(t)", [])?; // "YYYY-MM-DD HH:MM:SS" db.execute("UPDATE foo set b = datetime(t)", [])?; // "YYYY-MM-DD HH:MM:SS"
let hms: NaiveDateTime = db.query_row("SELECT b FROM foo", [], |r| r.get(0))?; let hms: NaiveDateTime = db.one_column("SELECT b FROM foo")?;
assert_eq!(dt, hms); assert_eq!(dt, hms);
Ok(()) Ok(())
} }
@ -221,28 +221,26 @@ mod test {
#[test] #[test]
fn test_date_time_utc() -> Result<()> { fn test_date_time_utc() -> Result<()> {
let db = checked_memory_handle()?; let db = checked_memory_handle()?;
let date = NaiveDate::from_ymd(2016, 2, 23); let date = NaiveDate::from_ymd_opt(2016, 2, 23).unwrap();
let time = NaiveTime::from_hms_milli(23, 56, 4, 789); let time = NaiveTime::from_hms_milli_opt(23, 56, 4, 789).unwrap();
let dt = NaiveDateTime::new(date, time); let dt = NaiveDateTime::new(date, time);
let utc = Utc.from_utc_datetime(&dt); let utc = Utc.from_utc_datetime(&dt);
db.execute("INSERT INTO foo (t) VALUES (?)", [utc])?; db.execute("INSERT INTO foo (t) VALUES (?1)", [utc])?;
let s: String = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?; let s: String = db.one_column("SELECT t FROM foo")?;
assert_eq!("2016-02-23 23:56:04.789+00:00", s); assert_eq!("2016-02-23 23:56:04.789+00:00", s);
let v1: DateTime<Utc> = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?; let v1: DateTime<Utc> = db.one_column("SELECT t FROM foo")?;
assert_eq!(utc, v1); assert_eq!(utc, v1);
let v2: DateTime<Utc> = let v2: DateTime<Utc> = db.one_column("SELECT '2016-02-23 23:56:04.789'")?;
db.query_row("SELECT '2016-02-23 23:56:04.789'", [], |r| r.get(0))?;
assert_eq!(utc, v2); assert_eq!(utc, v2);
let v3: DateTime<Utc> = db.query_row("SELECT '2016-02-23 23:56:04'", [], |r| r.get(0))?; let v3: DateTime<Utc> = db.one_column("SELECT '2016-02-23 23:56:04'")?;
assert_eq!(utc - Duration::milliseconds(789), v3); assert_eq!(utc - Duration::milliseconds(789), v3);
let v4: DateTime<Utc> = let v4: DateTime<Utc> = db.one_column("SELECT '2016-02-23 23:56:04.789+00:00'")?;
db.query_row("SELECT '2016-02-23 23:56:04.789+00:00'", [], |r| r.get(0))?;
assert_eq!(utc, v4); assert_eq!(utc, v4);
Ok(()) Ok(())
} }
@ -250,18 +248,18 @@ mod test {
#[test] #[test]
fn test_date_time_local() -> Result<()> { fn test_date_time_local() -> Result<()> {
let db = checked_memory_handle()?; let db = checked_memory_handle()?;
let date = NaiveDate::from_ymd(2016, 2, 23); let date = NaiveDate::from_ymd_opt(2016, 2, 23).unwrap();
let time = NaiveTime::from_hms_milli(23, 56, 4, 789); let time = NaiveTime::from_hms_milli_opt(23, 56, 4, 789).unwrap();
let dt = NaiveDateTime::new(date, time); let dt = NaiveDateTime::new(date, time);
let local = Local.from_local_datetime(&dt).single().unwrap(); let local = Local.from_local_datetime(&dt).single().unwrap();
db.execute("INSERT INTO foo (t) VALUES (?)", [local])?; db.execute("INSERT INTO foo (t) VALUES (?1)", [local])?;
// Stored string should be in UTC // Stored string should be in UTC
let s: String = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?; let s: String = db.one_column("SELECT t FROM foo")?;
assert!(s.ends_with("+00:00")); assert!(s.ends_with("+00:00"));
let v: DateTime<Local> = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?; let v: DateTime<Local> = db.one_column("SELECT t FROM foo")?;
assert_eq!(local, v); assert_eq!(local, v);
Ok(()) Ok(())
} }
@ -271,13 +269,13 @@ mod test {
let db = checked_memory_handle()?; let db = checked_memory_handle()?;
let time = DateTime::parse_from_rfc3339("2020-04-07T11:23:45+04:00").unwrap(); let time = DateTime::parse_from_rfc3339("2020-04-07T11:23:45+04:00").unwrap();
db.execute("INSERT INTO foo (t) VALUES (?)", [time])?; db.execute("INSERT INTO foo (t) VALUES (?1)", [time])?;
// Stored string should preserve timezone offset // Stored string should preserve timezone offset
let s: String = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?; let s: String = db.one_column("SELECT t FROM foo")?;
assert!(s.ends_with("+04:00")); assert!(s.ends_with("+04:00"));
let v: DateTime<FixedOffset> = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?; let v: DateTime<FixedOffset> = db.one_column("SELECT t FROM foo")?;
assert_eq!(time.offset(), v.offset()); assert_eq!(time.offset(), v.offset());
assert_eq!(time, v); assert_eq!(time, v);
Ok(()) Ok(())
@ -286,15 +284,13 @@ mod test {
#[test] #[test]
fn test_sqlite_functions() -> Result<()> { fn test_sqlite_functions() -> Result<()> {
let db = checked_memory_handle()?; let db = checked_memory_handle()?;
let result: Result<NaiveTime> = db.query_row("SELECT CURRENT_TIME", [], |r| r.get(0)); let result: Result<NaiveTime> = db.one_column("SELECT CURRENT_TIME");
result.unwrap(); result.unwrap();
let result: Result<NaiveDate> = db.query_row("SELECT CURRENT_DATE", [], |r| r.get(0)); let result: Result<NaiveDate> = db.one_column("SELECT CURRENT_DATE");
result.unwrap(); result.unwrap();
let result: Result<NaiveDateTime> = let result: Result<NaiveDateTime> = db.one_column("SELECT CURRENT_TIMESTAMP");
db.query_row("SELECT CURRENT_TIMESTAMP", [], |r| r.get(0));
result.unwrap(); result.unwrap();
let result: Result<DateTime<Utc>> = let result: Result<DateTime<Utc>> = db.one_column("SELECT CURRENT_TIMESTAMP");
db.query_row("SELECT CURRENT_TIMESTAMP", [], |r| r.get(0));
result.unwrap(); result.unwrap();
Ok(()) Ok(())
} }
@ -302,7 +298,7 @@ mod test {
#[test] #[test]
fn test_naive_date_time_param() -> Result<()> { fn test_naive_date_time_param() -> Result<()> {
let db = checked_memory_handle()?; let db = checked_memory_handle()?;
let result: Result<bool> = db.query_row("SELECT 1 WHERE ? BETWEEN datetime('now', '-1 minute') AND datetime('now', '+1 minute')", [Utc::now().naive_utc()], |r| r.get(0)); let result: Result<bool> = db.query_row("SELECT 1 WHERE ?1 BETWEEN datetime('now', '-1 minute') AND datetime('now', '+1 minute')", [Utc::now().naive_utc()], |r| r.get(0));
result.unwrap(); result.unwrap();
Ok(()) Ok(())
} }
@ -310,7 +306,7 @@ mod test {
#[test] #[test]
fn test_date_time_param() -> Result<()> { fn test_date_time_param() -> Result<()> {
let db = checked_memory_handle()?; let db = checked_memory_handle()?;
let result: Result<bool> = db.query_row("SELECT 1 WHERE ? BETWEEN datetime('now', '-1 minute') AND datetime('now', '+1 minute')", [Utc::now()], |r| r.get(0)); let result: Result<bool> = db.query_row("SELECT 1 WHERE ?1 BETWEEN datetime('now', '-1 minute') AND datetime('now', '+1 minute')", [Utc::now()], |r| r.get(0));
result.unwrap(); result.unwrap();
Ok(()) Ok(())
} }

View File

@ -244,7 +244,7 @@ mod test {
{ {
for n in out_of_range { for n in out_of_range {
let err = db let err = db
.query_row("SELECT ?", [n], |r| r.get::<_, T>(0)) .query_row("SELECT ?1", [n], |r| r.get::<_, T>(0))
.unwrap_err(); .unwrap_err();
match err { match err {
Error::IntegralValueOutOfRange(_, value) => assert_eq!(*n, value), Error::IntegralValueOutOfRange(_, value) => assert_eq!(*n, value),
@ -254,7 +254,7 @@ mod test {
for n in in_range { for n in in_range {
assert_eq!( assert_eq!(
*n, *n,
db.query_row("SELECT ?", [n], |r| r.get::<_, T>(0)) db.query_row("SELECT ?1", [n], |r| r.get::<_, T>(0))
.unwrap() .unwrap()
.into() .into()
); );

View File

@ -102,7 +102,7 @@ mod value_ref;
/// # use rusqlite::types::{Null}; /// # use rusqlite::types::{Null};
/// ///
/// fn insert_null(conn: &Connection) -> Result<usize> { /// fn insert_null(conn: &Connection) -> Result<usize> {
/// conn.execute("INSERT INTO people (name) VALUES (?)", [Null]) /// conn.execute("INSERT INTO people (name) VALUES (?1)", [Null])
/// } /// }
/// ``` /// ```
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
@ -153,9 +153,9 @@ mod test {
let db = checked_memory_handle()?; let db = checked_memory_handle()?;
let v1234 = vec![1u8, 2, 3, 4]; let v1234 = vec![1u8, 2, 3, 4];
db.execute("INSERT INTO foo(b) VALUES (?)", [&v1234])?; db.execute("INSERT INTO foo(b) VALUES (?1)", [&v1234])?;
let v: Vec<u8> = db.query_row("SELECT b FROM foo", [], |r| r.get(0))?; let v: Vec<u8> = db.one_column("SELECT b FROM foo")?;
assert_eq!(v, v1234); assert_eq!(v, v1234);
Ok(()) Ok(())
} }
@ -165,9 +165,9 @@ mod test {
let db = checked_memory_handle()?; let db = checked_memory_handle()?;
let empty = vec![]; let empty = vec![];
db.execute("INSERT INTO foo(b) VALUES (?)", [&empty])?; db.execute("INSERT INTO foo(b) VALUES (?1)", [&empty])?;
let v: Vec<u8> = db.query_row("SELECT b FROM foo", [], |r| r.get(0))?; let v: Vec<u8> = db.one_column("SELECT b FROM foo")?;
assert_eq!(v, empty); assert_eq!(v, empty);
Ok(()) Ok(())
} }
@ -177,9 +177,9 @@ mod test {
let db = checked_memory_handle()?; let db = checked_memory_handle()?;
let s = "hello, world!"; let s = "hello, world!";
db.execute("INSERT INTO foo(t) VALUES (?)", [&s])?; db.execute("INSERT INTO foo(t) VALUES (?1)", [&s])?;
let from: String = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?; let from: String = db.one_column("SELECT t FROM foo")?;
assert_eq!(from, s); assert_eq!(from, s);
Ok(()) Ok(())
} }
@ -189,9 +189,9 @@ mod test {
let db = checked_memory_handle()?; let db = checked_memory_handle()?;
let s = "hello, world!"; let s = "hello, world!";
db.execute("INSERT INTO foo(t) VALUES (?)", [s.to_owned()])?; db.execute("INSERT INTO foo(t) VALUES (?1)", [s.to_owned()])?;
let from: String = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?; let from: String = db.one_column("SELECT t FROM foo")?;
assert_eq!(from, s); assert_eq!(from, s);
Ok(()) Ok(())
} }
@ -200,12 +200,9 @@ mod test {
fn test_value() -> Result<()> { fn test_value() -> Result<()> {
let db = checked_memory_handle()?; let db = checked_memory_handle()?;
db.execute("INSERT INTO foo(i) VALUES (?)", [Value::Integer(10)])?; db.execute("INSERT INTO foo(i) VALUES (?1)", [Value::Integer(10)])?;
assert_eq!( assert_eq!(10i64, db.one_column::<i64>("SELECT i FROM foo")?);
10i64,
db.query_row::<i64, _, _>("SELECT i FROM foo", [], |r| r.get(0))?
);
Ok(()) Ok(())
} }
@ -216,8 +213,8 @@ mod test {
let s = Some("hello, world!"); let s = Some("hello, world!");
let b = Some(vec![1u8, 2, 3, 4]); let b = Some(vec![1u8, 2, 3, 4]);
db.execute("INSERT INTO foo(t) VALUES (?)", [&s])?; db.execute("INSERT INTO foo(t) VALUES (?1)", [&s])?;
db.execute("INSERT INTO foo(b) VALUES (?)", [&b])?; db.execute("INSERT INTO foo(b) VALUES (?1)", [&b])?;
let mut stmt = db.prepare("SELECT t, b FROM foo ORDER BY ROWID ASC")?; let mut stmt = db.prepare("SELECT t, b FROM foo ORDER BY ROWID ASC")?;
let mut rows = stmt.query([])?; let mut rows = stmt.query([])?;

View File

@ -1,24 +1,62 @@
//! [`ToSql`] and [`FromSql`] implementation for JSON `Value`. //! [`ToSql`] and [`FromSql`] implementation for JSON `Value`.
use serde_json::Value; use serde_json::{Number, Value};
use crate::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef}; use crate::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
use crate::Result; use crate::{Error, Result};
/// Serialize JSON `Value` to text. /// Serialize JSON `Value` to text:
///
///
/// | JSON | SQLite |
/// |----------|---------|
/// | Null | NULL |
/// | Bool | 'true' / 'false' |
/// | Number | INT or REAL except u64 |
/// | _ | TEXT |
impl ToSql for Value { impl ToSql for Value {
#[inline] #[inline]
fn to_sql(&self) -> Result<ToSqlOutput<'_>> { fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
Ok(ToSqlOutput::from(serde_json::to_string(self).unwrap())) match self {
Value::Null => Ok(ToSqlOutput::Borrowed(ValueRef::Null)),
Value::Number(n) if n.is_i64() => Ok(ToSqlOutput::from(n.as_i64().unwrap())),
Value::Number(n) if n.is_f64() => Ok(ToSqlOutput::from(n.as_f64().unwrap())),
_ => serde_json::to_string(self)
.map(ToSqlOutput::from)
.map_err(|err| Error::ToSqlConversionFailure(err.into())),
}
} }
} }
/// Deserialize text/blob to JSON `Value`. /// Deserialize SQLite value to JSON `Value`:
///
/// | SQLite | JSON |
/// |----------|---------|
/// | NULL | Null |
/// | 'null' | Null |
/// | 'true' | Bool |
/// | 1 | Number |
/// | 0.1 | Number |
/// | '"text"' | String |
/// | 'text' | _Error_ |
/// | '[0, 1]' | Array |
/// | '{"x": 1}' | Object |
impl FromSql for Value { impl FromSql for Value {
#[inline] #[inline]
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
let bytes = value.as_bytes()?; match value {
serde_json::from_slice(bytes).map_err(|err| FromSqlError::Other(Box::new(err))) ValueRef::Text(s) => serde_json::from_slice(s), // KO for b"text"
ValueRef::Blob(b) => serde_json::from_slice(b),
ValueRef::Integer(i) => Ok(Value::Number(Number::from(i))),
ValueRef::Real(f) => {
match Number::from_f64(f) {
Some(n) => Ok(Value::Number(n)),
_ => return Err(FromSqlError::InvalidType), // FIXME
}
}
ValueRef::Null => Ok(Value::Null),
}
.map_err(|err| FromSqlError::Other(Box::new(err)))
} }
} }
@ -26,6 +64,7 @@ impl FromSql for Value {
mod test { mod test {
use crate::types::ToSql; use crate::types::ToSql;
use crate::{Connection, Result}; use crate::{Connection, Result};
use serde_json::{Number, Value};
fn checked_memory_handle() -> Result<Connection> { fn checked_memory_handle() -> Result<Connection> {
let db = Connection::open_in_memory()?; let db = Connection::open_in_memory()?;
@ -38,16 +77,59 @@ mod test {
let db = checked_memory_handle()?; let db = checked_memory_handle()?;
let json = r#"{"foo": 13, "bar": "baz"}"#; let json = r#"{"foo": 13, "bar": "baz"}"#;
let data: serde_json::Value = serde_json::from_str(json).unwrap(); let data: Value = serde_json::from_str(json).unwrap();
db.execute( db.execute(
"INSERT INTO foo (t, b) VALUES (?, ?)", "INSERT INTO foo (t, b) VALUES (?1, ?2)",
[&data as &dyn ToSql, &json.as_bytes()], [&data as &dyn ToSql, &json.as_bytes()],
)?; )?;
let t: serde_json::Value = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?; let t: Value = db.one_column("SELECT t FROM foo")?;
assert_eq!(data, t); assert_eq!(data, t);
let b: serde_json::Value = db.query_row("SELECT b FROM foo", [], |r| r.get(0))?; let b: Value = db.one_column("SELECT b FROM foo")?;
assert_eq!(data, b); assert_eq!(data, b);
Ok(()) Ok(())
} }
#[test]
fn test_to_sql() -> Result<()> {
let db = Connection::open_in_memory()?;
let v: Option<String> = db.query_row("SELECT ?", [Value::Null], |r| r.get(0))?;
assert_eq!(None, v);
let v: String = db.query_row("SELECT ?", [Value::Bool(true)], |r| r.get(0))?;
assert_eq!("true", v);
let v: i64 = db.query_row("SELECT ?", [Value::Number(Number::from(1))], |r| r.get(0))?;
assert_eq!(1, v);
let v: f64 = db.query_row(
"SELECT ?",
[Value::Number(Number::from_f64(0.1).unwrap())],
|r| r.get(0),
)?;
assert_eq!(0.1, v);
let v: String =
db.query_row("SELECT ?", [Value::String("text".to_owned())], |r| r.get(0))?;
assert_eq!("\"text\"", v);
Ok(())
}
#[test]
fn test_from_sql() -> Result<()> {
let db = Connection::open_in_memory()?;
let v: Value = db.one_column("SELECT NULL")?;
assert_eq!(Value::Null, v);
let v: Value = db.one_column("SELECT 'null'")?;
assert_eq!(Value::Null, v);
let v: Value = db.one_column("SELECT 'true'")?;
assert_eq!(Value::Bool(true), v);
let v: Value = db.one_column("SELECT 1")?;
assert_eq!(Value::Number(Number::from(1)), v);
let v: Value = db.one_column("SELECT 0.1")?;
assert_eq!(Value::Number(Number::from_f64(0.1).unwrap()), v);
let v: Value = db.one_column("SELECT '\"text\"'")?;
assert_eq!(Value::String("text".to_owned()), v);
let v: Result<Value> = db.one_column("SELECT 'text'");
assert!(v.is_err());
Ok(())
}
} }

View File

@ -91,9 +91,9 @@ mod test {
ts_vec.push(make_datetime(10_000_000_000, 0)); //November 20, 2286 ts_vec.push(make_datetime(10_000_000_000, 0)); //November 20, 2286
for ts in ts_vec { for ts in ts_vec {
db.execute("INSERT INTO foo(t) VALUES (?)", [ts])?; db.execute("INSERT INTO foo(t) VALUES (?1)", [ts])?;
let from: OffsetDateTime = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?; let from: OffsetDateTime = db.one_column("SELECT t FROM foo")?;
db.execute("DELETE FROM foo", [])?; db.execute("DELETE FROM foo", [])?;
@ -143,7 +143,7 @@ mod test {
Ok(OffsetDateTime::parse("2013-10-07T04:23:19.120-04:00", &Rfc3339).unwrap()), Ok(OffsetDateTime::parse("2013-10-07T04:23:19.120-04:00", &Rfc3339).unwrap()),
), ),
] { ] {
let result: Result<OffsetDateTime> = db.query_row("SELECT ?", [s], |r| r.get(0)); let result: Result<OffsetDateTime> = db.query_row("SELECT ?1", [s], |r| r.get(0));
assert_eq!(result, t); assert_eq!(result, t);
} }
Ok(()) Ok(())
@ -152,8 +152,7 @@ mod test {
#[test] #[test]
fn test_sqlite_functions() -> Result<()> { fn test_sqlite_functions() -> Result<()> {
let db = Connection::open_in_memory()?; let db = Connection::open_in_memory()?;
let result: Result<OffsetDateTime> = let result: Result<OffsetDateTime> = db.one_column("SELECT CURRENT_TIMESTAMP");
db.query_row("SELECT CURRENT_TIMESTAMP", [], |r| r.get(0));
result.unwrap(); result.unwrap();
Ok(()) Ok(())
} }
@ -161,7 +160,7 @@ mod test {
#[test] #[test]
fn test_param() -> Result<()> { fn test_param() -> Result<()> {
let db = Connection::open_in_memory()?; let db = Connection::open_in_memory()?;
let result: Result<bool> = db.query_row("SELECT 1 WHERE ? BETWEEN datetime('now', '-1 minute') AND datetime('now', '+1 minute')", [OffsetDateTime::now_utc()], |r| r.get(0)); let result: Result<bool> = db.query_row("SELECT 1 WHERE ?1 BETWEEN datetime('now', '-1 minute') AND datetime('now', '+1 minute')", [OffsetDateTime::now_utc()], |r| r.get(0));
result.unwrap(); result.unwrap();
Ok(()) Ok(())
} }

View File

@ -368,10 +368,10 @@ mod test {
db.execute( db.execute(
" "
INSERT INTO foo(i128, desc) VALUES INSERT INTO foo(i128, desc) VALUES
(?, 'zero'), (?1, 'zero'),
(?, 'neg one'), (?, 'neg two'), (?2, 'neg one'), (?3, 'neg two'),
(?, 'pos one'), (?, 'pos two'), (?4, 'pos one'), (?5, 'pos two'),
(?, 'min'), (?, 'max')", (?6, 'min'), (?7, 'max')",
[0i128, -1i128, -2i128, 1i128, 2i128, i128::MIN, i128::MAX], [0i128, -1i128, -2i128, 1i128, 2i128, i128::MIN, i128::MAX],
)?; )?;
@ -410,11 +410,11 @@ mod test {
let id = Uuid::new_v4(); let id = Uuid::new_v4();
db.execute( db.execute(
"INSERT INTO foo (id, label) VALUES (?, ?)", "INSERT INTO foo (id, label) VALUES (?1, ?2)",
params![id, "target"], params![id, "target"],
)?; )?;
let mut stmt = db.prepare("SELECT id, label FROM foo WHERE id = ?")?; let mut stmt = db.prepare("SELECT id, label FROM foo WHERE id = ?1")?;
let mut rows = stmt.query(params![id])?; let mut rows = stmt.query(params![id])?;
let row = rows.next()?.unwrap(); let row = rows.next()?.unwrap();

View File

@ -49,7 +49,7 @@ mod test {
let url2 = "http://www.example2.com/👌"; let url2 = "http://www.example2.com/👌";
db.execute( db.execute(
"INSERT INTO urls (i, v) VALUES (0, ?), (1, ?), (2, ?), (3, ?)", "INSERT INTO urls (i, v) VALUES (0, ?1), (1, ?2), (2, ?3), (3, ?4)",
// also insert a non-hex encoded url (which might be present if it was // also insert a non-hex encoded url (which might be present if it was
// inserted separately) // inserted separately)
params![url0, url1, url2, "illegal"], params![url0, url1, url2, "illegal"],

View File

@ -109,8 +109,8 @@ mod test {
tx2.commit().unwrap(); tx2.commit().unwrap();
}); });
assert_eq!(tx.recv().unwrap(), 1); assert_eq!(tx.recv().unwrap(), 1);
let the_answer: Result<i64> = db1.query_row("SELECT x FROM foo", [], |r| r.get(0)); let the_answer: i64 = db1.one_column("SELECT x FROM foo")?;
assert_eq!(42i64, the_answer?); assert_eq!(42i64, the_answer);
child.join().unwrap(); child.join().unwrap();
Ok(()) Ok(())
} }

View File

@ -17,7 +17,7 @@
//! let v = [1i64, 2, 3, 4]; //! let v = [1i64, 2, 3, 4];
//! // Note: A `Rc<Vec<Value>>` must be used as the parameter. //! // Note: A `Rc<Vec<Value>>` must be used as the parameter.
//! let values = Rc::new(v.iter().copied().map(Value::from).collect::<Vec<Value>>()); //! let values = Rc::new(v.iter().copied().map(Value::from).collect::<Vec<Value>>());
//! let mut stmt = db.prepare("SELECT value from rarray(?);")?; //! let mut stmt = db.prepare("SELECT value from rarray(?1);")?;
//! let rows = stmt.query_map([values], |row| row.get::<_, i64>(0))?; //! let rows = stmt.query_map([values], |row| row.get::<_, i64>(0))?;
//! for value in rows { //! for value in rows {
//! println!("{}", value?); //! println!("{}", value?);
@ -206,7 +206,7 @@ mod test {
let values: Vec<Value> = v.into_iter().map(Value::from).collect(); let values: Vec<Value> = v.into_iter().map(Value::from).collect();
let ptr = Rc::new(values); let ptr = Rc::new(values);
{ {
let mut stmt = db.prepare("SELECT value from rarray(?);")?; let mut stmt = db.prepare("SELECT value from rarray(?1);")?;
let rows = stmt.query_map([&ptr], |row| row.get::<_, i64>(0))?; let rows = stmt.query_map([&ptr], |row| row.get::<_, i64>(0))?;
assert_eq!(2, Rc::strong_count(&ptr)); assert_eq!(2, Rc::strong_count(&ptr));

View File

@ -286,13 +286,13 @@ mod test {
let mut stmt = db.prepare("SELECT * FROM log;")?; let mut stmt = db.prepare("SELECT * FROM log;")?;
let mut rows = stmt.query([])?; let mut rows = stmt.query([])?;
while rows.next()?.is_some() {} while rows.next()?.is_some() {}
db.execute("DELETE FROM log WHERE a = ?", ["a1"])?; db.execute("DELETE FROM log WHERE a = ?1", ["a1"])?;
db.execute( db.execute(
"INSERT INTO log (a, b, c) VALUES (?, ?, ?)", "INSERT INTO log (a, b, c) VALUES (?1, ?2, ?3)",
["a", "b", "c"], ["a", "b", "c"],
)?; )?;
db.execute( db.execute(
"UPDATE log SET b = ?, c = ? WHERE a = ?", "UPDATE log SET b = ?1, c = ?2 WHERE a = ?3",
["bn", "cn", "a1"], ["bn", "cn", "a1"],
)?; )?;
Ok(()) Ok(())