diff --git a/.travis.yml b/.travis.yml index 2888475..3bb7065 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,7 @@ rust: - nightly matrix: + fast_finish: true allow_failures: - rust: nightly diff --git a/Cargo.toml b/Cargo.toml index 3d2068e..fb288ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "rusqlite" -version = "0.15.0" +version = "0.16.0" authors = ["John Gallagher "] +edition = "2018" description = "Ergonomic wrapper for SQLite" repository = "https://github.com/jgallagher/rusqlite" documentation = "http://docs.rs/rusqlite/" @@ -18,6 +19,9 @@ maintenance = { status = "actively-developed" } [lib] name = "rusqlite" +[workspace] +members = ["libsqlite3-sys"] + [features] load_extension = [] # hot-backup interface: 3.6.11 (2009-02-18) @@ -37,9 +41,13 @@ sqlcipher = ["libsqlite3-sys/sqlcipher"] unlock_notify = ["libsqlite3-sys/unlock_notify"] # xSavepoint, xRelease and xRollbackTo: 3.7.7 (2011-06-23) vtab = ["libsqlite3-sys/min_sqlite_version_3_7_7", "lazy_static"] +# xShadowName: 3.26.0 +vtab_v3 = ["vtab"] csvtab = ["csv", "vtab"] # pointer passing interfaces: 3.20.0 array = ["vtab"] +# session extension: 3.13.0 +session = ["libsqlite3-sys/session", "hooks", "fallible-streaming-iterator"] [dependencies] time = "0.1.0" @@ -50,6 +58,7 @@ serde_json = { version = "1.0", optional = true } csv = { version = "1.0", optional = true } lazy_static = { version = "1.0", optional = true } byteorder = { version = "1.2", features = ["i128"], optional = true } +fallible-streaming-iterator = { version = "0.1", optional = true } [dev-dependencies] tempdir = "0.3" @@ -58,7 +67,7 @@ regex = "1.0" [dependencies.libsqlite3-sys] path = "libsqlite3-sys" -version = "0.10" +version = "0.12" [[test]] name = "config_log" diff --git a/README.md b/README.md index 7629fe7..f2d983e 100644 --- a/README.md +++ b/README.md @@ -10,9 +10,6 @@ Rusqlite is an ergonomic wrapper for using SQLite from Rust. It attempts to expo an interface similar to [rust-postgres](https://github.com/sfackler/rust-postgres). ```rust -extern crate rusqlite; -extern crate time; - use rusqlite::types::ToSql; use rusqlite::{Connection, NO_PARAMS}; use time::Timespec; @@ -77,26 +74,26 @@ newer SQLite version; see details below. Rusqlite provides several features that are behind [Cargo features](https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section). They are: -* [`load_extension`](https://docs.rs/rusqlite/0.15.0/rusqlite/struct.LoadExtensionGuard.html) +* [`load_extension`](https://docs.rs/rusqlite/0.16.0/rusqlite/struct.LoadExtensionGuard.html) allows loading dynamic library-based SQLite extensions. -* [`backup`](https://docs.rs/rusqlite/0.15.0/rusqlite/backup/index.html) +* [`backup`](https://docs.rs/rusqlite/0.16.0/rusqlite/backup/index.html) allows use of SQLite's online backup API. Note: This feature requires SQLite 3.6.11 or later. -* [`functions`](https://docs.rs/rusqlite/0.15.0/rusqlite/functions/index.html) +* [`functions`](https://docs.rs/rusqlite/0.16.0/rusqlite/functions/index.html) allows you to load Rust closures into SQLite connections for use in queries. Note: This feature requires SQLite 3.7.3 or later. -* [`trace`](https://docs.rs/rusqlite/0.15.0/rusqlite/trace/index.html) +* [`trace`](https://docs.rs/rusqlite/0.16.0/rusqlite/trace/index.html) allows hooks into SQLite's tracing and profiling APIs. Note: This feature requires SQLite 3.6.23 or later. -* [`blob`](https://docs.rs/rusqlite/0.15.0/rusqlite/blob/index.html) +* [`blob`](https://docs.rs/rusqlite/0.16.0/rusqlite/blob/index.html) gives `std::io::{Read, Write, Seek}` access to SQL BLOBs. Note: This feature requires SQLite 3.7.4 or later. -* [`limits`](https://docs.rs/rusqlite/0.15.0/rusqlite/struct.Connection.html#method.limit) +* [`limits`](https://docs.rs/rusqlite/0.16.0/rusqlite/struct.Connection.html#method.limit) allows you to set and retrieve SQLite's per connection limits. -* `chrono` implements [`FromSql`](https://docs.rs/rusqlite/0.15.0/rusqlite/types/trait.FromSql.html) - and [`ToSql`](https://docs.rs/rusqlite/0.15.0/rusqlite/types/trait.ToSql.html) for various +* `chrono` implements [`FromSql`](https://docs.rs/rusqlite/0.16.0/rusqlite/types/trait.FromSql.html) + and [`ToSql`](https://docs.rs/rusqlite/0.16.0/rusqlite/types/trait.ToSql.html) for various types from the [`chrono` crate](https://crates.io/crates/chrono). -* `serde_json` implements [`FromSql`](https://docs.rs/rusqlite/0.15.0/rusqlite/types/trait.FromSql.html) - and [`ToSql`](https://docs.rs/rusqlite/0.15.0/rusqlite/types/trait.ToSql.html) for the +* `serde_json` implements [`FromSql`](https://docs.rs/rusqlite/0.16.0/rusqlite/types/trait.FromSql.html) + and [`ToSql`](https://docs.rs/rusqlite/0.16.0/rusqlite/types/trait.ToSql.html) for the `Value` type from the [`serde_json` crate](https://crates.io/crates/serde_json). * `bundled` uses a bundled version of sqlite3. This is a good option for cases where linking to sqlite3 is complicated, such as Windows. * `sqlcipher` looks for the SQLCipher library to link against instead of SQLite. This feature is mutually exclusive with `bundled`. @@ -106,6 +103,7 @@ features](https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-s * [`csvtab`](https://sqlite.org/csv.html), CSV virtual table written in Rust. * [`array`](https://sqlite.org/carray.html), The `rarray()` Table-Valued Function. * `i128_blob` allows storing values of type `i128` type in SQLite databases. Internally, the data is stored as a 16 byte big-endian blob, with the most significant bit flipped, which allows ordering and comparison between different blobs storing i128s to work as expected. +* [`session`](https://sqlite.org/sessionintro.html), Session module extension. ## Notes on building rusqlite and libsqlite3-sys @@ -118,11 +116,11 @@ You can adjust this behavior in a number of ways: * If you use the `bundled` feature, `libsqlite3-sys` will use the [gcc](https://crates.io/crates/gcc) crate to compile SQLite from source and link against that. This source is embedded in the `libsqlite3-sys` crate and - is currently SQLite 3.25.2 (as of `rusqlite` 0.15.0 / `libsqlite3-sys` - 0.10.0). This is probably the simplest solution to any build problems. You can enable this by adding the following in your `Cargo.toml` file: + is currently SQLite 3.26.0 (as of `rusqlite` 0.16.0 / `libsqlite3-sys` + 0.11.0). This is probably the simplest solution to any build problems. You can enable this by adding the following in your `Cargo.toml` file: ``` [dependencies.rusqlite] - version = "0.15.0" + version = "0.16.0" features = ["bundled"] ``` * You can set the `SQLITE3_LIB_DIR` to point to directory containing the SQLite diff --git a/appveyor.yml b/appveyor.yml index c412f1f..db0bfad 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -5,9 +5,9 @@ environment: - TARGET: x86_64-pc-windows-msvc VCPKG_DEFAULT_TRIPLET: x64-windows VCPKGRS_DYNAMIC: 1 - - TARGET: x86_64-pc-windows-msvc - VCPKG_DEFAULT_TRIPLET: x64-windows-static - RUSTFLAGS: -Ctarget-feature=+crt-static +# - TARGET: x86_64-pc-windows-msvc +# VCPKG_DEFAULT_TRIPLET: x64-windows-static +# RUSTFLAGS: -Ctarget-feature=+crt-static install: - appveyor-retry appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe - rustup-init.exe -y --default-host %TARGET% diff --git a/benches/lib.rs b/benches/lib.rs index fb03d09..d1b6d79 100644 --- a/benches/lib.rs +++ b/benches/lib.rs @@ -1,8 +1,6 @@ #![feature(test)] extern crate test; -extern crate rusqlite; - use rusqlite::Connection; use test::Bencher; diff --git a/libsqlite3-sys/Cargo.toml b/libsqlite3-sys/Cargo.toml index c8cdbf7..dccd5f5 100644 --- a/libsqlite3-sys/Cargo.toml +++ b/libsqlite3-sys/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "libsqlite3-sys" -version = "0.10.0" +version = "0.12.0" authors = ["John Gallagher "] +edition = "2018" repository = "https://github.com/jgallagher/rusqlite" description = "Native bindings to the libsqlite3 library" license = "MIT" @@ -18,11 +19,16 @@ sqlcipher = [] min_sqlite_version_3_6_8 = ["pkg-config", "vcpkg"] min_sqlite_version_3_6_23 = ["pkg-config", "vcpkg"] min_sqlite_version_3_7_7 = ["pkg-config", "vcpkg"] +min_sqlite_version_3_7_16 = ["pkg-config", "vcpkg"] # sqlite3_unlock_notify >= 3.6.12 unlock_notify = [] +# 3.13.0 +preupdate_hook = [] +# 3.13.0 +session = ["preupdate_hook"] [build-dependencies] -bindgen = { version = "0.43", optional = true } +bindgen = { version = "0.47", optional = true } pkg-config = { version = "0.3", optional = true } cc = { version = "1.0", optional = true } diff --git a/libsqlite3-sys/bindgen-bindings/bindgen_3.7.16.rs b/libsqlite3-sys/bindgen-bindings/bindgen_3.7.16.rs new file mode 100644 index 0000000..cd14895 --- /dev/null +++ b/libsqlite3-sys/bindgen-bindings/bindgen_3.7.16.rs @@ -0,0 +1,2297 @@ +/* automatically generated by rust-bindgen */ + +pub const __GNUC_VA_LIST: i32 = 1; +pub const SQLITE_VERSION: &'static [u8; 7usize] = b"3.7.16\x00"; +pub const SQLITE_VERSION_NUMBER: i32 = 3007016; +pub const SQLITE_SOURCE_ID: &'static [u8; 61usize] = + b"2013-03-18 11:39:23 66d5f2b76750f3520eb7a495f6247206758f5b90\x00"; +pub const SQLITE_OK: i32 = 0; +pub const SQLITE_ERROR: i32 = 1; +pub const SQLITE_INTERNAL: i32 = 2; +pub const SQLITE_PERM: i32 = 3; +pub const SQLITE_ABORT: i32 = 4; +pub const SQLITE_BUSY: i32 = 5; +pub const SQLITE_LOCKED: i32 = 6; +pub const SQLITE_NOMEM: i32 = 7; +pub const SQLITE_READONLY: i32 = 8; +pub const SQLITE_INTERRUPT: i32 = 9; +pub const SQLITE_IOERR: i32 = 10; +pub const SQLITE_CORRUPT: i32 = 11; +pub const SQLITE_NOTFOUND: i32 = 12; +pub const SQLITE_FULL: i32 = 13; +pub const SQLITE_CANTOPEN: i32 = 14; +pub const SQLITE_PROTOCOL: i32 = 15; +pub const SQLITE_EMPTY: i32 = 16; +pub const SQLITE_SCHEMA: i32 = 17; +pub const SQLITE_TOOBIG: i32 = 18; +pub const SQLITE_CONSTRAINT: i32 = 19; +pub const SQLITE_MISMATCH: i32 = 20; +pub const SQLITE_MISUSE: i32 = 21; +pub const SQLITE_NOLFS: i32 = 22; +pub const SQLITE_AUTH: i32 = 23; +pub const SQLITE_FORMAT: i32 = 24; +pub const SQLITE_RANGE: i32 = 25; +pub const SQLITE_NOTADB: i32 = 26; +pub const SQLITE_ROW: i32 = 100; +pub const SQLITE_DONE: i32 = 101; +pub const SQLITE_IOERR_READ: i32 = 266; +pub const SQLITE_IOERR_SHORT_READ: i32 = 522; +pub const SQLITE_IOERR_WRITE: i32 = 778; +pub const SQLITE_IOERR_FSYNC: i32 = 1034; +pub const SQLITE_IOERR_DIR_FSYNC: i32 = 1290; +pub const SQLITE_IOERR_TRUNCATE: i32 = 1546; +pub const SQLITE_IOERR_FSTAT: i32 = 1802; +pub const SQLITE_IOERR_UNLOCK: i32 = 2058; +pub const SQLITE_IOERR_RDLOCK: i32 = 2314; +pub const SQLITE_IOERR_DELETE: i32 = 2570; +pub const SQLITE_IOERR_BLOCKED: i32 = 2826; +pub const SQLITE_IOERR_NOMEM: i32 = 3082; +pub const SQLITE_IOERR_ACCESS: i32 = 3338; +pub const SQLITE_IOERR_CHECKRESERVEDLOCK: i32 = 3594; +pub const SQLITE_IOERR_LOCK: i32 = 3850; +pub const SQLITE_IOERR_CLOSE: i32 = 4106; +pub const SQLITE_IOERR_DIR_CLOSE: i32 = 4362; +pub const SQLITE_IOERR_SHMOPEN: i32 = 4618; +pub const SQLITE_IOERR_SHMSIZE: i32 = 4874; +pub const SQLITE_IOERR_SHMLOCK: i32 = 5130; +pub const SQLITE_IOERR_SHMMAP: i32 = 5386; +pub const SQLITE_IOERR_SEEK: i32 = 5642; +pub const SQLITE_IOERR_DELETE_NOENT: i32 = 5898; +pub const SQLITE_LOCKED_SHAREDCACHE: i32 = 262; +pub const SQLITE_BUSY_RECOVERY: i32 = 261; +pub const SQLITE_CANTOPEN_NOTEMPDIR: i32 = 270; +pub const SQLITE_CANTOPEN_ISDIR: i32 = 526; +pub const SQLITE_CANTOPEN_FULLPATH: i32 = 782; +pub const SQLITE_CORRUPT_VTAB: i32 = 267; +pub const SQLITE_READONLY_RECOVERY: i32 = 264; +pub const SQLITE_READONLY_CANTLOCK: i32 = 520; +pub const SQLITE_READONLY_ROLLBACK: i32 = 776; +pub const SQLITE_ABORT_ROLLBACK: i32 = 516; +pub const SQLITE_CONSTRAINT_CHECK: i32 = 275; +pub const SQLITE_CONSTRAINT_COMMITHOOK: i32 = 531; +pub const SQLITE_CONSTRAINT_FOREIGNKEY: i32 = 787; +pub const SQLITE_CONSTRAINT_FUNCTION: i32 = 1043; +pub const SQLITE_CONSTRAINT_NOTNULL: i32 = 1299; +pub const SQLITE_CONSTRAINT_PRIMARYKEY: i32 = 1555; +pub const SQLITE_CONSTRAINT_TRIGGER: i32 = 1811; +pub const SQLITE_CONSTRAINT_UNIQUE: i32 = 2067; +pub const SQLITE_CONSTRAINT_VTAB: i32 = 2323; +pub const SQLITE_OPEN_READONLY: i32 = 1; +pub const SQLITE_OPEN_READWRITE: i32 = 2; +pub const SQLITE_OPEN_CREATE: i32 = 4; +pub const SQLITE_OPEN_DELETEONCLOSE: i32 = 8; +pub const SQLITE_OPEN_EXCLUSIVE: i32 = 16; +pub const SQLITE_OPEN_AUTOPROXY: i32 = 32; +pub const SQLITE_OPEN_URI: i32 = 64; +pub const SQLITE_OPEN_MEMORY: i32 = 128; +pub const SQLITE_OPEN_MAIN_DB: i32 = 256; +pub const SQLITE_OPEN_TEMP_DB: i32 = 512; +pub const SQLITE_OPEN_TRANSIENT_DB: i32 = 1024; +pub const SQLITE_OPEN_MAIN_JOURNAL: i32 = 2048; +pub const SQLITE_OPEN_TEMP_JOURNAL: i32 = 4096; +pub const SQLITE_OPEN_SUBJOURNAL: i32 = 8192; +pub const SQLITE_OPEN_MASTER_JOURNAL: i32 = 16384; +pub const SQLITE_OPEN_NOMUTEX: i32 = 32768; +pub const SQLITE_OPEN_FULLMUTEX: i32 = 65536; +pub const SQLITE_OPEN_SHAREDCACHE: i32 = 131072; +pub const SQLITE_OPEN_PRIVATECACHE: i32 = 262144; +pub const SQLITE_OPEN_WAL: i32 = 524288; +pub const SQLITE_IOCAP_ATOMIC: i32 = 1; +pub const SQLITE_IOCAP_ATOMIC512: i32 = 2; +pub const SQLITE_IOCAP_ATOMIC1K: i32 = 4; +pub const SQLITE_IOCAP_ATOMIC2K: i32 = 8; +pub const SQLITE_IOCAP_ATOMIC4K: i32 = 16; +pub const SQLITE_IOCAP_ATOMIC8K: i32 = 32; +pub const SQLITE_IOCAP_ATOMIC16K: i32 = 64; +pub const SQLITE_IOCAP_ATOMIC32K: i32 = 128; +pub const SQLITE_IOCAP_ATOMIC64K: i32 = 256; +pub const SQLITE_IOCAP_SAFE_APPEND: i32 = 512; +pub const SQLITE_IOCAP_SEQUENTIAL: i32 = 1024; +pub const SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN: i32 = 2048; +pub const SQLITE_IOCAP_POWERSAFE_OVERWRITE: i32 = 4096; +pub const SQLITE_LOCK_NONE: i32 = 0; +pub const SQLITE_LOCK_SHARED: i32 = 1; +pub const SQLITE_LOCK_RESERVED: i32 = 2; +pub const SQLITE_LOCK_PENDING: i32 = 3; +pub const SQLITE_LOCK_EXCLUSIVE: i32 = 4; +pub const SQLITE_SYNC_NORMAL: i32 = 2; +pub const SQLITE_SYNC_FULL: i32 = 3; +pub const SQLITE_SYNC_DATAONLY: i32 = 16; +pub const SQLITE_FCNTL_LOCKSTATE: i32 = 1; +pub const SQLITE_GET_LOCKPROXYFILE: i32 = 2; +pub const SQLITE_SET_LOCKPROXYFILE: i32 = 3; +pub const SQLITE_LAST_ERRNO: i32 = 4; +pub const SQLITE_FCNTL_SIZE_HINT: i32 = 5; +pub const SQLITE_FCNTL_CHUNK_SIZE: i32 = 6; +pub const SQLITE_FCNTL_FILE_POINTER: i32 = 7; +pub const SQLITE_FCNTL_SYNC_OMITTED: i32 = 8; +pub const SQLITE_FCNTL_WIN32_AV_RETRY: i32 = 9; +pub const SQLITE_FCNTL_PERSIST_WAL: i32 = 10; +pub const SQLITE_FCNTL_OVERWRITE: i32 = 11; +pub const SQLITE_FCNTL_VFSNAME: i32 = 12; +pub const SQLITE_FCNTL_POWERSAFE_OVERWRITE: i32 = 13; +pub const SQLITE_FCNTL_PRAGMA: i32 = 14; +pub const SQLITE_FCNTL_BUSYHANDLER: i32 = 15; +pub const SQLITE_FCNTL_TEMPFILENAME: i32 = 16; +pub const SQLITE_ACCESS_EXISTS: i32 = 0; +pub const SQLITE_ACCESS_READWRITE: i32 = 1; +pub const SQLITE_ACCESS_READ: i32 = 2; +pub const SQLITE_SHM_UNLOCK: i32 = 1; +pub const SQLITE_SHM_LOCK: i32 = 2; +pub const SQLITE_SHM_SHARED: i32 = 4; +pub const SQLITE_SHM_EXCLUSIVE: i32 = 8; +pub const SQLITE_SHM_NLOCK: i32 = 8; +pub const SQLITE_CONFIG_SINGLETHREAD: i32 = 1; +pub const SQLITE_CONFIG_MULTITHREAD: i32 = 2; +pub const SQLITE_CONFIG_SERIALIZED: i32 = 3; +pub const SQLITE_CONFIG_MALLOC: i32 = 4; +pub const SQLITE_CONFIG_GETMALLOC: i32 = 5; +pub const SQLITE_CONFIG_SCRATCH: i32 = 6; +pub const SQLITE_CONFIG_PAGECACHE: i32 = 7; +pub const SQLITE_CONFIG_HEAP: i32 = 8; +pub const SQLITE_CONFIG_MEMSTATUS: i32 = 9; +pub const SQLITE_CONFIG_MUTEX: i32 = 10; +pub const SQLITE_CONFIG_GETMUTEX: i32 = 11; +pub const SQLITE_CONFIG_LOOKASIDE: i32 = 13; +pub const SQLITE_CONFIG_PCACHE: i32 = 14; +pub const SQLITE_CONFIG_GETPCACHE: i32 = 15; +pub const SQLITE_CONFIG_LOG: i32 = 16; +pub const SQLITE_CONFIG_URI: i32 = 17; +pub const SQLITE_CONFIG_PCACHE2: i32 = 18; +pub const SQLITE_CONFIG_GETPCACHE2: i32 = 19; +pub const SQLITE_CONFIG_COVERING_INDEX_SCAN: i32 = 20; +pub const SQLITE_CONFIG_SQLLOG: i32 = 21; +pub const SQLITE_DBCONFIG_LOOKASIDE: i32 = 1001; +pub const SQLITE_DBCONFIG_ENABLE_FKEY: i32 = 1002; +pub const SQLITE_DBCONFIG_ENABLE_TRIGGER: i32 = 1003; +pub const SQLITE_DENY: i32 = 1; +pub const SQLITE_IGNORE: i32 = 2; +pub const SQLITE_CREATE_INDEX: i32 = 1; +pub const SQLITE_CREATE_TABLE: i32 = 2; +pub const SQLITE_CREATE_TEMP_INDEX: i32 = 3; +pub const SQLITE_CREATE_TEMP_TABLE: i32 = 4; +pub const SQLITE_CREATE_TEMP_TRIGGER: i32 = 5; +pub const SQLITE_CREATE_TEMP_VIEW: i32 = 6; +pub const SQLITE_CREATE_TRIGGER: i32 = 7; +pub const SQLITE_CREATE_VIEW: i32 = 8; +pub const SQLITE_DELETE: i32 = 9; +pub const SQLITE_DROP_INDEX: i32 = 10; +pub const SQLITE_DROP_TABLE: i32 = 11; +pub const SQLITE_DROP_TEMP_INDEX: i32 = 12; +pub const SQLITE_DROP_TEMP_TABLE: i32 = 13; +pub const SQLITE_DROP_TEMP_TRIGGER: i32 = 14; +pub const SQLITE_DROP_TEMP_VIEW: i32 = 15; +pub const SQLITE_DROP_TRIGGER: i32 = 16; +pub const SQLITE_DROP_VIEW: i32 = 17; +pub const SQLITE_INSERT: i32 = 18; +pub const SQLITE_PRAGMA: i32 = 19; +pub const SQLITE_READ: i32 = 20; +pub const SQLITE_SELECT: i32 = 21; +pub const SQLITE_TRANSACTION: i32 = 22; +pub const SQLITE_UPDATE: i32 = 23; +pub const SQLITE_ATTACH: i32 = 24; +pub const SQLITE_DETACH: i32 = 25; +pub const SQLITE_ALTER_TABLE: i32 = 26; +pub const SQLITE_REINDEX: i32 = 27; +pub const SQLITE_ANALYZE: i32 = 28; +pub const SQLITE_CREATE_VTABLE: i32 = 29; +pub const SQLITE_DROP_VTABLE: i32 = 30; +pub const SQLITE_FUNCTION: i32 = 31; +pub const SQLITE_SAVEPOINT: i32 = 32; +pub const SQLITE_COPY: i32 = 0; +pub const SQLITE_LIMIT_LENGTH: i32 = 0; +pub const SQLITE_LIMIT_SQL_LENGTH: i32 = 1; +pub const SQLITE_LIMIT_COLUMN: i32 = 2; +pub const SQLITE_LIMIT_EXPR_DEPTH: i32 = 3; +pub const SQLITE_LIMIT_COMPOUND_SELECT: i32 = 4; +pub const SQLITE_LIMIT_VDBE_OP: i32 = 5; +pub const SQLITE_LIMIT_FUNCTION_ARG: i32 = 6; +pub const SQLITE_LIMIT_ATTACHED: i32 = 7; +pub const SQLITE_LIMIT_LIKE_PATTERN_LENGTH: i32 = 8; +pub const SQLITE_LIMIT_VARIABLE_NUMBER: i32 = 9; +pub const SQLITE_LIMIT_TRIGGER_DEPTH: i32 = 10; +pub const SQLITE_INTEGER: i32 = 1; +pub const SQLITE_FLOAT: i32 = 2; +pub const SQLITE_BLOB: i32 = 4; +pub const SQLITE_NULL: i32 = 5; +pub const SQLITE_TEXT: i32 = 3; +pub const SQLITE3_TEXT: i32 = 3; +pub const SQLITE_UTF8: i32 = 1; +pub const SQLITE_UTF16LE: i32 = 2; +pub const SQLITE_UTF16BE: i32 = 3; +pub const SQLITE_UTF16: i32 = 4; +pub const SQLITE_ANY: i32 = 5; +pub const SQLITE_UTF16_ALIGNED: i32 = 8; +pub const SQLITE_INDEX_CONSTRAINT_EQ: i32 = 2; +pub const SQLITE_INDEX_CONSTRAINT_GT: i32 = 4; +pub const SQLITE_INDEX_CONSTRAINT_LE: i32 = 8; +pub const SQLITE_INDEX_CONSTRAINT_LT: i32 = 16; +pub const SQLITE_INDEX_CONSTRAINT_GE: i32 = 32; +pub const SQLITE_INDEX_CONSTRAINT_MATCH: i32 = 64; +pub const SQLITE_MUTEX_FAST: i32 = 0; +pub const SQLITE_MUTEX_RECURSIVE: i32 = 1; +pub const SQLITE_MUTEX_STATIC_MASTER: i32 = 2; +pub const SQLITE_MUTEX_STATIC_MEM: i32 = 3; +pub const SQLITE_MUTEX_STATIC_MEM2: i32 = 4; +pub const SQLITE_MUTEX_STATIC_OPEN: i32 = 4; +pub const SQLITE_MUTEX_STATIC_PRNG: i32 = 5; +pub const SQLITE_MUTEX_STATIC_LRU: i32 = 6; +pub const SQLITE_MUTEX_STATIC_LRU2: i32 = 7; +pub const SQLITE_MUTEX_STATIC_PMEM: i32 = 7; +pub const SQLITE_TESTCTRL_FIRST: i32 = 5; +pub const SQLITE_TESTCTRL_PRNG_SAVE: i32 = 5; +pub const SQLITE_TESTCTRL_PRNG_RESTORE: i32 = 6; +pub const SQLITE_TESTCTRL_PRNG_RESET: i32 = 7; +pub const SQLITE_TESTCTRL_BITVEC_TEST: i32 = 8; +pub const SQLITE_TESTCTRL_FAULT_INSTALL: i32 = 9; +pub const SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS: i32 = 10; +pub const SQLITE_TESTCTRL_PENDING_BYTE: i32 = 11; +pub const SQLITE_TESTCTRL_ASSERT: i32 = 12; +pub const SQLITE_TESTCTRL_ALWAYS: i32 = 13; +pub const SQLITE_TESTCTRL_RESERVE: i32 = 14; +pub const SQLITE_TESTCTRL_OPTIMIZATIONS: i32 = 15; +pub const SQLITE_TESTCTRL_ISKEYWORD: i32 = 16; +pub const SQLITE_TESTCTRL_SCRATCHMALLOC: i32 = 17; +pub const SQLITE_TESTCTRL_LOCALTIME_FAULT: i32 = 18; +pub const SQLITE_TESTCTRL_EXPLAIN_STMT: i32 = 19; +pub const SQLITE_TESTCTRL_LAST: i32 = 19; +pub const SQLITE_STATUS_MEMORY_USED: i32 = 0; +pub const SQLITE_STATUS_PAGECACHE_USED: i32 = 1; +pub const SQLITE_STATUS_PAGECACHE_OVERFLOW: i32 = 2; +pub const SQLITE_STATUS_SCRATCH_USED: i32 = 3; +pub const SQLITE_STATUS_SCRATCH_OVERFLOW: i32 = 4; +pub const SQLITE_STATUS_MALLOC_SIZE: i32 = 5; +pub const SQLITE_STATUS_PARSER_STACK: i32 = 6; +pub const SQLITE_STATUS_PAGECACHE_SIZE: i32 = 7; +pub const SQLITE_STATUS_SCRATCH_SIZE: i32 = 8; +pub const SQLITE_STATUS_MALLOC_COUNT: i32 = 9; +pub const SQLITE_DBSTATUS_LOOKASIDE_USED: i32 = 0; +pub const SQLITE_DBSTATUS_CACHE_USED: i32 = 1; +pub const SQLITE_DBSTATUS_SCHEMA_USED: i32 = 2; +pub const SQLITE_DBSTATUS_STMT_USED: i32 = 3; +pub const SQLITE_DBSTATUS_LOOKASIDE_HIT: i32 = 4; +pub const SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE: i32 = 5; +pub const SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL: i32 = 6; +pub const SQLITE_DBSTATUS_CACHE_HIT: i32 = 7; +pub const SQLITE_DBSTATUS_CACHE_MISS: i32 = 8; +pub const SQLITE_DBSTATUS_CACHE_WRITE: i32 = 9; +pub const SQLITE_DBSTATUS_MAX: i32 = 9; +pub const SQLITE_STMTSTATUS_FULLSCAN_STEP: i32 = 1; +pub const SQLITE_STMTSTATUS_SORT: i32 = 2; +pub const SQLITE_STMTSTATUS_AUTOINDEX: i32 = 3; +pub const SQLITE_CHECKPOINT_PASSIVE: i32 = 0; +pub const SQLITE_CHECKPOINT_FULL: i32 = 1; +pub const SQLITE_CHECKPOINT_RESTART: i32 = 2; +pub const SQLITE_VTAB_CONSTRAINT_SUPPORT: i32 = 1; +pub const SQLITE_ROLLBACK: i32 = 1; +pub const SQLITE_FAIL: i32 = 3; +pub const SQLITE_REPLACE: i32 = 5; +pub type va_list = __builtin_va_list; +pub type __gnuc_va_list = __builtin_va_list; +extern "C" { + #[link_name = "sqlite3_version"] + pub static mut sqlite3_version: [::std::os::raw::c_char; 0usize]; +} +extern "C" { + pub fn sqlite3_libversion() -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn sqlite3_sourceid() -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn sqlite3_libversion_number() -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_compileoption_used(zOptName: *const ::std::os::raw::c_char) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_compileoption_get(N: ::std::os::raw::c_int) + -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn sqlite3_threadsafe() -> ::std::os::raw::c_int; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct sqlite3([u8; 0]); +pub type sqlite_int64 = ::std::os::raw::c_longlong; +pub type sqlite_uint64 = ::std::os::raw::c_ulonglong; +pub type sqlite3_int64 = sqlite_int64; +pub type sqlite3_uint64 = sqlite_uint64; +extern "C" { + pub fn sqlite3_close(arg1: *mut sqlite3) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_close_v2(arg1: *mut sqlite3) -> ::std::os::raw::c_int; +} +pub type sqlite3_callback = + ::std::option::Option ::std::os::raw::c_int>; +extern "C" { + pub fn sqlite3_exec(arg1: *mut sqlite3, + sql: *const ::std::os::raw::c_char, + callback: + ::std::option::Option + ::std::os::raw::c_int>, + arg2: *mut ::std::os::raw::c_void, + errmsg: *mut *mut ::std::os::raw::c_char) + -> ::std::os::raw::c_int; +} +#[repr(C)] +#[derive(Debug, Copy)] +pub struct sqlite3_file { + pub pMethods: *const sqlite3_file_sqlite3_io_methods, +} +#[repr(C)] +#[derive(Debug, Copy)] +pub struct sqlite3_file_sqlite3_io_methods { + pub iVersion: ::std::os::raw::c_int, + pub xClose: ::std::option::Option ::std::os::raw::c_int>, + pub xRead: ::std::option::Option ::std::os::raw::c_int>, + pub xWrite: ::std::option::Option ::std::os::raw::c_int>, + pub xTruncate: ::std::option::Option ::std::os::raw::c_int>, + pub xSync: ::std::option::Option ::std::os::raw::c_int>, + pub xFileSize: ::std::option::Option ::std::os::raw::c_int>, + pub xLock: ::std::option::Option ::std::os::raw::c_int>, + pub xUnlock: ::std::option::Option ::std::os::raw::c_int>, + pub xCheckReservedLock: ::std::option::Option + ::std::os::raw::c_int>, + pub xFileControl: ::std::option::Option ::std::os::raw::c_int>, + pub xSectorSize: ::std::option::Option ::std::os::raw::c_int>, + pub xDeviceCharacteristics: ::std::option::Option + ::std::os::raw::c_int>, + pub xShmMap: ::std::option::Option ::std::os::raw::c_int>, + pub xShmLock: ::std::option::Option ::std::os::raw::c_int>, + pub xShmBarrier: ::std::option::Option, + pub xShmUnmap: ::std::option::Option ::std::os::raw::c_int>, +} +#[test] +fn bindgen_test_layout_sqlite3_file_sqlite3_io_methods() { + assert_eq!(::std::mem::size_of::() , + 136usize); + assert_eq!(::std::mem::align_of::() , + 8usize); +} +impl Clone for sqlite3_file_sqlite3_io_methods { + fn clone(&self) -> Self { *self } +} +#[test] +fn bindgen_test_layout_sqlite3_file() { + assert_eq!(::std::mem::size_of::() , 8usize); + assert_eq!(::std::mem::align_of::() , 8usize); +} +impl Clone for sqlite3_file { + fn clone(&self) -> Self { *self } +} +pub type sqlite3_io_methods = sqlite3_file_sqlite3_io_methods; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct sqlite3_mutex([u8; 0]); +#[repr(C)] +#[derive(Debug, Copy)] +pub struct sqlite3_vfs { + pub iVersion: ::std::os::raw::c_int, + pub szOsFile: ::std::os::raw::c_int, + pub mxPathname: ::std::os::raw::c_int, + pub pNext: *mut sqlite3_vfs, + pub zName: *const ::std::os::raw::c_char, + pub pAppData: *mut ::std::os::raw::c_void, + pub xOpen: ::std::option::Option ::std::os::raw::c_int>, + pub xDelete: ::std::option::Option ::std::os::raw::c_int>, + pub xAccess: ::std::option::Option ::std::os::raw::c_int>, + pub xFullPathname: ::std::option::Option ::std::os::raw::c_int>, + pub xDlOpen: ::std::option::Option *mut ::std::os::raw::c_void>, + pub xDlError: ::std::option::Option, + pub xDlSym: ::std::option::Option + ::std::option::Option>, + pub xDlClose: ::std::option::Option, + pub xRandomness: ::std::option::Option ::std::os::raw::c_int>, + pub xSleep: ::std::option::Option ::std::os::raw::c_int>, + pub xCurrentTime: ::std::option::Option ::std::os::raw::c_int>, + pub xGetLastError: ::std::option::Option ::std::os::raw::c_int>, + pub xCurrentTimeInt64: ::std::option::Option + ::std::os::raw::c_int>, + pub xSetSystemCall: ::std::option::Option ::std::os::raw::c_int>, + pub xGetSystemCall: ::std::option::Option + ::std::option::Option>, + pub xNextSystemCall: ::std::option::Option + *const ::std::os::raw::c_char>, +} +#[test] +fn bindgen_test_layout_sqlite3_vfs() { + assert_eq!(::std::mem::size_of::() , 168usize); + assert_eq!(::std::mem::align_of::() , 8usize); +} +impl Clone for sqlite3_vfs { + fn clone(&self) -> Self { *self } +} +pub type sqlite3_syscall_ptr = ::std::option::Option; +extern "C" { + pub fn sqlite3_initialize() -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_shutdown() -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_os_init() -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_os_end() -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_config(arg1: ::std::os::raw::c_int, ...) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_db_config(arg1: *mut sqlite3, + op: ::std::os::raw::c_int, ...) + -> ::std::os::raw::c_int; +} +#[repr(C)] +#[derive(Debug, Copy)] +pub struct sqlite3_mem_methods { + pub xMalloc: ::std::option::Option *mut ::std::os::raw::c_void>, + pub xFree: ::std::option::Option, + pub xRealloc: ::std::option::Option *mut ::std::os::raw::c_void>, + pub xSize: ::std::option::Option ::std::os::raw::c_int>, + pub xRoundup: ::std::option::Option ::std::os::raw::c_int>, + pub xInit: ::std::option::Option ::std::os::raw::c_int>, + pub xShutdown: ::std::option::Option, + pub pAppData: *mut ::std::os::raw::c_void, +} +#[test] +fn bindgen_test_layout_sqlite3_mem_methods() { + assert_eq!(::std::mem::size_of::() , 64usize); + assert_eq!(::std::mem::align_of::() , 8usize); +} +impl Clone for sqlite3_mem_methods { + fn clone(&self) -> Self { *self } +} +extern "C" { + pub fn sqlite3_extended_result_codes(arg1: *mut sqlite3, + onoff: ::std::os::raw::c_int) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_last_insert_rowid(arg1: *mut sqlite3) -> sqlite3_int64; +} +extern "C" { + pub fn sqlite3_changes(arg1: *mut sqlite3) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_total_changes(arg1: *mut sqlite3) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_interrupt(arg1: *mut sqlite3); +} +extern "C" { + pub fn sqlite3_complete(sql: *const ::std::os::raw::c_char) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_complete16(sql: *const ::std::os::raw::c_void) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_busy_handler(arg1: *mut sqlite3, + arg2: + ::std::option::Option + ::std::os::raw::c_int>, + arg3: *mut ::std::os::raw::c_void) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_busy_timeout(arg1: *mut sqlite3, ms: ::std::os::raw::c_int) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_get_table(db: *mut sqlite3, + zSql: *const ::std::os::raw::c_char, + pazResult: *mut *mut *mut ::std::os::raw::c_char, + pnRow: *mut ::std::os::raw::c_int, + pnColumn: *mut ::std::os::raw::c_int, + pzErrmsg: *mut *mut ::std::os::raw::c_char) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_free_table(result: *mut *mut ::std::os::raw::c_char); +} +extern "C" { + pub fn sqlite3_mprintf(arg1: *const ::std::os::raw::c_char, ...) + -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn sqlite3_vmprintf(arg1: *const ::std::os::raw::c_char, + arg2: *mut __va_list_tag) + -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn sqlite3_snprintf(arg1: ::std::os::raw::c_int, + arg2: *mut ::std::os::raw::c_char, + arg3: *const ::std::os::raw::c_char, ...) + -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn sqlite3_vsnprintf(arg1: ::std::os::raw::c_int, + arg2: *mut ::std::os::raw::c_char, + arg3: *const ::std::os::raw::c_char, + arg4: *mut __va_list_tag) + -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn sqlite3_malloc(arg1: ::std::os::raw::c_int) + -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn sqlite3_realloc(arg1: *mut ::std::os::raw::c_void, + arg2: ::std::os::raw::c_int) + -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn sqlite3_free(arg1: *mut ::std::os::raw::c_void); +} +extern "C" { + pub fn sqlite3_memory_used() -> sqlite3_int64; +} +extern "C" { + pub fn sqlite3_memory_highwater(resetFlag: ::std::os::raw::c_int) + -> sqlite3_int64; +} +extern "C" { + pub fn sqlite3_randomness(N: ::std::os::raw::c_int, + P: *mut ::std::os::raw::c_void); +} +extern "C" { + pub fn sqlite3_set_authorizer(arg1: *mut sqlite3, + xAuth: + ::std::option::Option + ::std::os::raw::c_int>, + pUserData: *mut ::std::os::raw::c_void) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_trace(arg1: *mut sqlite3, + xTrace: + ::std::option::Option, + arg2: *mut ::std::os::raw::c_void) + -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn sqlite3_profile(arg1: *mut sqlite3, + xProfile: + ::std::option::Option, + arg2: *mut ::std::os::raw::c_void) + -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn sqlite3_progress_handler(arg1: *mut sqlite3, + arg2: ::std::os::raw::c_int, + arg3: + ::std::option::Option + ::std::os::raw::c_int>, + arg4: *mut ::std::os::raw::c_void); +} +extern "C" { + pub fn sqlite3_open(filename: *const ::std::os::raw::c_char, + ppDb: *mut *mut sqlite3) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_open16(filename: *const ::std::os::raw::c_void, + ppDb: *mut *mut sqlite3) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_open_v2(filename: *const ::std::os::raw::c_char, + ppDb: *mut *mut sqlite3, + flags: ::std::os::raw::c_int, + zVfs: *const ::std::os::raw::c_char) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_uri_parameter(zFilename: *const ::std::os::raw::c_char, + zParam: *const ::std::os::raw::c_char) + -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn sqlite3_uri_boolean(zFile: *const ::std::os::raw::c_char, + zParam: *const ::std::os::raw::c_char, + bDefault: ::std::os::raw::c_int) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_uri_int64(arg1: *const ::std::os::raw::c_char, + arg2: *const ::std::os::raw::c_char, + arg3: sqlite3_int64) -> sqlite3_int64; +} +extern "C" { + pub fn sqlite3_errcode(db: *mut sqlite3) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_extended_errcode(db: *mut sqlite3) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_errmsg(arg1: *mut sqlite3) + -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn sqlite3_errmsg16(arg1: *mut sqlite3) + -> *const ::std::os::raw::c_void; +} +extern "C" { + pub fn sqlite3_errstr(arg1: ::std::os::raw::c_int) + -> *const ::std::os::raw::c_char; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct sqlite3_stmt([u8; 0]); +extern "C" { + pub fn sqlite3_limit(arg1: *mut sqlite3, id: ::std::os::raw::c_int, + newVal: ::std::os::raw::c_int) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_prepare(db: *mut sqlite3, + zSql: *const ::std::os::raw::c_char, + nByte: ::std::os::raw::c_int, + ppStmt: *mut *mut sqlite3_stmt, + pzTail: *mut *const ::std::os::raw::c_char) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_prepare_v2(db: *mut sqlite3, + zSql: *const ::std::os::raw::c_char, + nByte: ::std::os::raw::c_int, + ppStmt: *mut *mut sqlite3_stmt, + pzTail: *mut *const ::std::os::raw::c_char) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_prepare16(db: *mut sqlite3, + zSql: *const ::std::os::raw::c_void, + nByte: ::std::os::raw::c_int, + ppStmt: *mut *mut sqlite3_stmt, + pzTail: *mut *const ::std::os::raw::c_void) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_prepare16_v2(db: *mut sqlite3, + zSql: *const ::std::os::raw::c_void, + nByte: ::std::os::raw::c_int, + ppStmt: *mut *mut sqlite3_stmt, + pzTail: *mut *const ::std::os::raw::c_void) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_sql(pStmt: *mut sqlite3_stmt) + -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn sqlite3_stmt_readonly(pStmt: *mut sqlite3_stmt) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_stmt_busy(arg1: *mut sqlite3_stmt) + -> ::std::os::raw::c_int; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct Mem([u8; 0]); +pub type sqlite3_value = Mem; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct sqlite3_context([u8; 0]); +extern "C" { + pub fn sqlite3_bind_blob(arg1: *mut sqlite3_stmt, + arg2: ::std::os::raw::c_int, + arg3: *const ::std::os::raw::c_void, + n: ::std::os::raw::c_int, + arg4: + ::std::option::Option) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_bind_double(arg1: *mut sqlite3_stmt, + arg2: ::std::os::raw::c_int, arg3: f64) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_bind_int(arg1: *mut sqlite3_stmt, + arg2: ::std::os::raw::c_int, + arg3: ::std::os::raw::c_int) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_bind_int64(arg1: *mut sqlite3_stmt, + arg2: ::std::os::raw::c_int, + arg3: sqlite3_int64) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_bind_null(arg1: *mut sqlite3_stmt, + arg2: ::std::os::raw::c_int) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_bind_text(arg1: *mut sqlite3_stmt, + arg2: ::std::os::raw::c_int, + arg3: *const ::std::os::raw::c_char, + n: ::std::os::raw::c_int, + arg4: + ::std::option::Option) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_bind_text16(arg1: *mut sqlite3_stmt, + arg2: ::std::os::raw::c_int, + arg3: *const ::std::os::raw::c_void, + arg4: ::std::os::raw::c_int, + arg5: + ::std::option::Option) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_bind_value(arg1: *mut sqlite3_stmt, + arg2: ::std::os::raw::c_int, + arg3: *const sqlite3_value) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_bind_zeroblob(arg1: *mut sqlite3_stmt, + arg2: ::std::os::raw::c_int, + n: ::std::os::raw::c_int) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_bind_parameter_count(arg1: *mut sqlite3_stmt) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_bind_parameter_name(arg1: *mut sqlite3_stmt, + arg2: ::std::os::raw::c_int) + -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn sqlite3_bind_parameter_index(arg1: *mut sqlite3_stmt, + zName: *const ::std::os::raw::c_char) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_clear_bindings(arg1: *mut sqlite3_stmt) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_column_count(pStmt: *mut sqlite3_stmt) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_column_name(arg1: *mut sqlite3_stmt, + N: ::std::os::raw::c_int) + -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn sqlite3_column_name16(arg1: *mut sqlite3_stmt, + N: ::std::os::raw::c_int) + -> *const ::std::os::raw::c_void; +} +extern "C" { + pub fn sqlite3_column_database_name(arg1: *mut sqlite3_stmt, + arg2: ::std::os::raw::c_int) + -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn sqlite3_column_database_name16(arg1: *mut sqlite3_stmt, + arg2: ::std::os::raw::c_int) + -> *const ::std::os::raw::c_void; +} +extern "C" { + pub fn sqlite3_column_table_name(arg1: *mut sqlite3_stmt, + arg2: ::std::os::raw::c_int) + -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn sqlite3_column_table_name16(arg1: *mut sqlite3_stmt, + arg2: ::std::os::raw::c_int) + -> *const ::std::os::raw::c_void; +} +extern "C" { + pub fn sqlite3_column_origin_name(arg1: *mut sqlite3_stmt, + arg2: ::std::os::raw::c_int) + -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn sqlite3_column_origin_name16(arg1: *mut sqlite3_stmt, + arg2: ::std::os::raw::c_int) + -> *const ::std::os::raw::c_void; +} +extern "C" { + pub fn sqlite3_column_decltype(arg1: *mut sqlite3_stmt, + arg2: ::std::os::raw::c_int) + -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn sqlite3_column_decltype16(arg1: *mut sqlite3_stmt, + arg2: ::std::os::raw::c_int) + -> *const ::std::os::raw::c_void; +} +extern "C" { + pub fn sqlite3_step(arg1: *mut sqlite3_stmt) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_data_count(pStmt: *mut sqlite3_stmt) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_column_blob(arg1: *mut sqlite3_stmt, + iCol: ::std::os::raw::c_int) + -> *const ::std::os::raw::c_void; +} +extern "C" { + pub fn sqlite3_column_bytes(arg1: *mut sqlite3_stmt, + iCol: ::std::os::raw::c_int) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_column_bytes16(arg1: *mut sqlite3_stmt, + iCol: ::std::os::raw::c_int) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_column_double(arg1: *mut sqlite3_stmt, + iCol: ::std::os::raw::c_int) -> f64; +} +extern "C" { + pub fn sqlite3_column_int(arg1: *mut sqlite3_stmt, + iCol: ::std::os::raw::c_int) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_column_int64(arg1: *mut sqlite3_stmt, + iCol: ::std::os::raw::c_int) -> sqlite3_int64; +} +extern "C" { + pub fn sqlite3_column_text(arg1: *mut sqlite3_stmt, + iCol: ::std::os::raw::c_int) + -> *const ::std::os::raw::c_uchar; +} +extern "C" { + pub fn sqlite3_column_text16(arg1: *mut sqlite3_stmt, + iCol: ::std::os::raw::c_int) + -> *const ::std::os::raw::c_void; +} +extern "C" { + pub fn sqlite3_column_type(arg1: *mut sqlite3_stmt, + iCol: ::std::os::raw::c_int) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_column_value(arg1: *mut sqlite3_stmt, + iCol: ::std::os::raw::c_int) + -> *mut sqlite3_value; +} +extern "C" { + pub fn sqlite3_finalize(pStmt: *mut sqlite3_stmt) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_reset(pStmt: *mut sqlite3_stmt) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_create_function(db: *mut sqlite3, + zFunctionName: + *const ::std::os::raw::c_char, + nArg: ::std::os::raw::c_int, + eTextRep: ::std::os::raw::c_int, + pApp: *mut ::std::os::raw::c_void, + xFunc: + ::std::option::Option, + xStep: + ::std::option::Option, + xFinal: + ::std::option::Option) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_create_function16(db: *mut sqlite3, + zFunctionName: + *const ::std::os::raw::c_void, + nArg: ::std::os::raw::c_int, + eTextRep: ::std::os::raw::c_int, + pApp: *mut ::std::os::raw::c_void, + xFunc: + ::std::option::Option, + xStep: + ::std::option::Option, + xFinal: + ::std::option::Option) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_create_function_v2(db: *mut sqlite3, + zFunctionName: + *const ::std::os::raw::c_char, + nArg: ::std::os::raw::c_int, + eTextRep: ::std::os::raw::c_int, + pApp: *mut ::std::os::raw::c_void, + xFunc: + ::std::option::Option, + xStep: + ::std::option::Option, + xFinal: + ::std::option::Option, + xDestroy: + ::std::option::Option) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_aggregate_count(arg1: *mut sqlite3_context) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_expired(arg1: *mut sqlite3_stmt) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_transfer_bindings(arg1: *mut sqlite3_stmt, + arg2: *mut sqlite3_stmt) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_global_recover() -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_thread_cleanup(); +} +extern "C" { + pub fn sqlite3_memory_alarm(arg1: + ::std::option::Option, + arg2: *mut ::std::os::raw::c_void, + arg3: sqlite3_int64) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_value_blob(arg1: *mut sqlite3_value) + -> *const ::std::os::raw::c_void; +} +extern "C" { + pub fn sqlite3_value_bytes(arg1: *mut sqlite3_value) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_value_bytes16(arg1: *mut sqlite3_value) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_value_double(arg1: *mut sqlite3_value) -> f64; +} +extern "C" { + pub fn sqlite3_value_int(arg1: *mut sqlite3_value) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_value_int64(arg1: *mut sqlite3_value) -> sqlite3_int64; +} +extern "C" { + pub fn sqlite3_value_text(arg1: *mut sqlite3_value) + -> *const ::std::os::raw::c_uchar; +} +extern "C" { + pub fn sqlite3_value_text16(arg1: *mut sqlite3_value) + -> *const ::std::os::raw::c_void; +} +extern "C" { + pub fn sqlite3_value_text16le(arg1: *mut sqlite3_value) + -> *const ::std::os::raw::c_void; +} +extern "C" { + pub fn sqlite3_value_text16be(arg1: *mut sqlite3_value) + -> *const ::std::os::raw::c_void; +} +extern "C" { + pub fn sqlite3_value_type(arg1: *mut sqlite3_value) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_value_numeric_type(arg1: *mut sqlite3_value) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_aggregate_context(arg1: *mut sqlite3_context, + nBytes: ::std::os::raw::c_int) + -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn sqlite3_user_data(arg1: *mut sqlite3_context) + -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn sqlite3_context_db_handle(arg1: *mut sqlite3_context) + -> *mut sqlite3; +} +extern "C" { + pub fn sqlite3_get_auxdata(arg1: *mut sqlite3_context, + N: ::std::os::raw::c_int) + -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn sqlite3_set_auxdata(arg1: *mut sqlite3_context, + N: ::std::os::raw::c_int, + arg2: *mut ::std::os::raw::c_void, + arg3: + ::std::option::Option); +} +pub type sqlite3_destructor_type = + ::std::option::Option; +extern "C" { + pub fn sqlite3_result_blob(arg1: *mut sqlite3_context, + arg2: *const ::std::os::raw::c_void, + arg3: ::std::os::raw::c_int, + arg4: + ::std::option::Option); +} +extern "C" { + pub fn sqlite3_result_double(arg1: *mut sqlite3_context, arg2: f64); +} +extern "C" { + pub fn sqlite3_result_error(arg1: *mut sqlite3_context, + arg2: *const ::std::os::raw::c_char, + arg3: ::std::os::raw::c_int); +} +extern "C" { + pub fn sqlite3_result_error16(arg1: *mut sqlite3_context, + arg2: *const ::std::os::raw::c_void, + arg3: ::std::os::raw::c_int); +} +extern "C" { + pub fn sqlite3_result_error_toobig(arg1: *mut sqlite3_context); +} +extern "C" { + pub fn sqlite3_result_error_nomem(arg1: *mut sqlite3_context); +} +extern "C" { + pub fn sqlite3_result_error_code(arg1: *mut sqlite3_context, + arg2: ::std::os::raw::c_int); +} +extern "C" { + pub fn sqlite3_result_int(arg1: *mut sqlite3_context, + arg2: ::std::os::raw::c_int); +} +extern "C" { + pub fn sqlite3_result_int64(arg1: *mut sqlite3_context, + arg2: sqlite3_int64); +} +extern "C" { + pub fn sqlite3_result_null(arg1: *mut sqlite3_context); +} +extern "C" { + pub fn sqlite3_result_text(arg1: *mut sqlite3_context, + arg2: *const ::std::os::raw::c_char, + arg3: ::std::os::raw::c_int, + arg4: + ::std::option::Option); +} +extern "C" { + pub fn sqlite3_result_text16(arg1: *mut sqlite3_context, + arg2: *const ::std::os::raw::c_void, + arg3: ::std::os::raw::c_int, + arg4: + ::std::option::Option); +} +extern "C" { + pub fn sqlite3_result_text16le(arg1: *mut sqlite3_context, + arg2: *const ::std::os::raw::c_void, + arg3: ::std::os::raw::c_int, + arg4: + ::std::option::Option); +} +extern "C" { + pub fn sqlite3_result_text16be(arg1: *mut sqlite3_context, + arg2: *const ::std::os::raw::c_void, + arg3: ::std::os::raw::c_int, + arg4: + ::std::option::Option); +} +extern "C" { + pub fn sqlite3_result_value(arg1: *mut sqlite3_context, + arg2: *mut sqlite3_value); +} +extern "C" { + pub fn sqlite3_result_zeroblob(arg1: *mut sqlite3_context, + n: ::std::os::raw::c_int); +} +extern "C" { + pub fn sqlite3_create_collation(arg1: *mut sqlite3, + zName: *const ::std::os::raw::c_char, + eTextRep: ::std::os::raw::c_int, + pArg: *mut ::std::os::raw::c_void, + xCompare: + ::std::option::Option + ::std::os::raw::c_int>) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_create_collation_v2(arg1: *mut sqlite3, + zName: *const ::std::os::raw::c_char, + eTextRep: ::std::os::raw::c_int, + pArg: *mut ::std::os::raw::c_void, + xCompare: + ::std::option::Option + ::std::os::raw::c_int>, + xDestroy: + ::std::option::Option) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_create_collation16(arg1: *mut sqlite3, + zName: *const ::std::os::raw::c_void, + eTextRep: ::std::os::raw::c_int, + pArg: *mut ::std::os::raw::c_void, + xCompare: + ::std::option::Option + ::std::os::raw::c_int>) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_collation_needed(arg1: *mut sqlite3, + arg2: *mut ::std::os::raw::c_void, + arg3: + ::std::option::Option) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_collation_needed16(arg1: *mut sqlite3, + arg2: *mut ::std::os::raw::c_void, + arg3: + ::std::option::Option) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_sleep(arg1: ::std::os::raw::c_int) + -> ::std::os::raw::c_int; +} +extern "C" { + #[link_name = "sqlite3_temp_directory"] + pub static mut sqlite3_temp_directory: *mut ::std::os::raw::c_char; +} +extern "C" { + #[link_name = "sqlite3_data_directory"] + pub static mut sqlite3_data_directory: *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn sqlite3_get_autocommit(arg1: *mut sqlite3) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_db_handle(arg1: *mut sqlite3_stmt) -> *mut sqlite3; +} +extern "C" { + pub fn sqlite3_db_filename(db: *mut sqlite3, + zDbName: *const ::std::os::raw::c_char) + -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn sqlite3_db_readonly(db: *mut sqlite3, + zDbName: *const ::std::os::raw::c_char) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_next_stmt(pDb: *mut sqlite3, pStmt: *mut sqlite3_stmt) + -> *mut sqlite3_stmt; +} +extern "C" { + pub fn sqlite3_commit_hook(arg1: *mut sqlite3, + arg2: + ::std::option::Option + ::std::os::raw::c_int>, + arg3: *mut ::std::os::raw::c_void) + -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn sqlite3_rollback_hook(arg1: *mut sqlite3, + arg2: + ::std::option::Option, + arg3: *mut ::std::os::raw::c_void) + -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn sqlite3_update_hook(arg1: *mut sqlite3, + arg2: + ::std::option::Option, + arg3: *mut ::std::os::raw::c_void) + -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn sqlite3_enable_shared_cache(arg1: ::std::os::raw::c_int) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_release_memory(arg1: ::std::os::raw::c_int) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_db_release_memory(arg1: *mut sqlite3) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_soft_heap_limit64(N: sqlite3_int64) -> sqlite3_int64; +} +extern "C" { + pub fn sqlite3_soft_heap_limit(N: ::std::os::raw::c_int); +} +extern "C" { + pub fn sqlite3_table_column_metadata(db: *mut sqlite3, + zDbName: + *const ::std::os::raw::c_char, + zTableName: + *const ::std::os::raw::c_char, + zColumnName: + *const ::std::os::raw::c_char, + pzDataType: + *mut *const ::std::os::raw::c_char, + pzCollSeq: + *mut *const ::std::os::raw::c_char, + pNotNull: *mut ::std::os::raw::c_int, + pPrimaryKey: + *mut ::std::os::raw::c_int, + pAutoinc: *mut ::std::os::raw::c_int) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_load_extension(db: *mut sqlite3, + zFile: *const ::std::os::raw::c_char, + zProc: *const ::std::os::raw::c_char, + pzErrMsg: *mut *mut ::std::os::raw::c_char) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_enable_load_extension(db: *mut sqlite3, + onoff: ::std::os::raw::c_int) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_auto_extension(xEntryPoint: + ::std::option::Option) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_reset_auto_extension(); +} +#[repr(C)] +#[derive(Debug, Copy)] +pub struct sqlite3_vtab { + pub pModule: *const sqlite3_module, + pub nRef: ::std::os::raw::c_int, + pub zErrMsg: *mut ::std::os::raw::c_char, +} +#[test] +fn bindgen_test_layout_sqlite3_vtab() { + assert_eq!(::std::mem::size_of::() , 24usize); + assert_eq!(::std::mem::align_of::() , 8usize); +} +impl Clone for sqlite3_vtab { + fn clone(&self) -> Self { *self } +} +#[repr(C)] +#[derive(Debug, Copy)] +pub struct sqlite3_index_info { + pub nConstraint: ::std::os::raw::c_int, + pub aConstraint: *mut sqlite3_index_info_sqlite3_index_constraint, + pub nOrderBy: ::std::os::raw::c_int, + pub aOrderBy: *mut sqlite3_index_info_sqlite3_index_orderby, + pub aConstraintUsage: *mut sqlite3_index_info_sqlite3_index_constraint_usage, + pub idxNum: ::std::os::raw::c_int, + pub idxStr: *mut ::std::os::raw::c_char, + pub needToFreeIdxStr: ::std::os::raw::c_int, + pub orderByConsumed: ::std::os::raw::c_int, + pub estimatedCost: f64, +} +#[repr(C)] +#[derive(Debug, Copy)] +pub struct sqlite3_index_info_sqlite3_index_constraint { + pub iColumn: ::std::os::raw::c_int, + pub op: ::std::os::raw::c_uchar, + pub usable: ::std::os::raw::c_uchar, + pub iTermOffset: ::std::os::raw::c_int, +} +#[test] +fn bindgen_test_layout_sqlite3_index_info_sqlite3_index_constraint() { + assert_eq!(::std::mem::size_of::() + , 12usize); + assert_eq!(::std::mem::align_of::() + , 4usize); +} +impl Clone for sqlite3_index_info_sqlite3_index_constraint { + fn clone(&self) -> Self { *self } +} +#[repr(C)] +#[derive(Debug, Copy)] +pub struct sqlite3_index_info_sqlite3_index_orderby { + pub iColumn: ::std::os::raw::c_int, + pub desc: ::std::os::raw::c_uchar, +} +#[test] +fn bindgen_test_layout_sqlite3_index_info_sqlite3_index_orderby() { + assert_eq!(::std::mem::size_of::() + , 8usize); + assert_eq!(::std::mem::align_of::() + , 4usize); +} +impl Clone for sqlite3_index_info_sqlite3_index_orderby { + fn clone(&self) -> Self { *self } +} +#[repr(C)] +#[derive(Debug, Copy)] +pub struct sqlite3_index_info_sqlite3_index_constraint_usage { + pub argvIndex: ::std::os::raw::c_int, + pub omit: ::std::os::raw::c_uchar, +} +#[test] +fn bindgen_test_layout_sqlite3_index_info_sqlite3_index_constraint_usage() { + assert_eq!(::std::mem::size_of::() + , 8usize); + assert_eq!(::std::mem::align_of::() + , 4usize); +} +impl Clone for sqlite3_index_info_sqlite3_index_constraint_usage { + fn clone(&self) -> Self { *self } +} +#[test] +fn bindgen_test_layout_sqlite3_index_info() { + assert_eq!(::std::mem::size_of::() , 72usize); + assert_eq!(::std::mem::align_of::() , 8usize); +} +impl Clone for sqlite3_index_info { + fn clone(&self) -> Self { *self } +} +#[repr(C)] +#[derive(Debug, Copy)] +pub struct sqlite3_vtab_cursor { + pub pVtab: *mut sqlite3_vtab, +} +#[test] +fn bindgen_test_layout_sqlite3_vtab_cursor() { + assert_eq!(::std::mem::size_of::() , 8usize); + assert_eq!(::std::mem::align_of::() , 8usize); +} +impl Clone for sqlite3_vtab_cursor { + fn clone(&self) -> Self { *self } +} +#[repr(C)] +#[derive(Debug, Copy)] +pub struct sqlite3_module { + pub iVersion: ::std::os::raw::c_int, + pub xCreate: ::std::option::Option ::std::os::raw::c_int>, + pub xConnect: ::std::option::Option ::std::os::raw::c_int>, + pub xBestIndex: ::std::option::Option ::std::os::raw::c_int>, + pub xDisconnect: ::std::option::Option ::std::os::raw::c_int>, + pub xDestroy: ::std::option::Option ::std::os::raw::c_int>, + pub xOpen: ::std::option::Option ::std::os::raw::c_int>, + pub xClose: ::std::option::Option ::std::os::raw::c_int>, + pub xFilter: ::std::option::Option ::std::os::raw::c_int>, + pub xNext: ::std::option::Option ::std::os::raw::c_int>, + pub xEof: ::std::option::Option ::std::os::raw::c_int>, + pub xColumn: ::std::option::Option ::std::os::raw::c_int>, + pub xRowid: ::std::option::Option ::std::os::raw::c_int>, + pub xUpdate: ::std::option::Option ::std::os::raw::c_int>, + pub xBegin: ::std::option::Option ::std::os::raw::c_int>, + pub xSync: ::std::option::Option ::std::os::raw::c_int>, + pub xCommit: ::std::option::Option ::std::os::raw::c_int>, + pub xRollback: ::std::option::Option ::std::os::raw::c_int>, + pub xFindFunction: ::std::option::Option, + ppArg: + *mut *mut ::std::os::raw::c_void) + -> ::std::os::raw::c_int>, + pub xRename: ::std::option::Option ::std::os::raw::c_int>, + pub xSavepoint: ::std::option::Option ::std::os::raw::c_int>, + pub xRelease: ::std::option::Option ::std::os::raw::c_int>, + pub xRollbackTo: ::std::option::Option ::std::os::raw::c_int>, +} +#[test] +fn bindgen_test_layout_sqlite3_module() { + assert_eq!(::std::mem::size_of::() , 184usize); + assert_eq!(::std::mem::align_of::() , 8usize); +} +impl Clone for sqlite3_module { + fn clone(&self) -> Self { *self } +} +extern "C" { + pub fn sqlite3_create_module(db: *mut sqlite3, + zName: *const ::std::os::raw::c_char, + p: *const sqlite3_module, + pClientData: *mut ::std::os::raw::c_void) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_create_module_v2(db: *mut sqlite3, + zName: *const ::std::os::raw::c_char, + p: *const sqlite3_module, + pClientData: *mut ::std::os::raw::c_void, + xDestroy: + ::std::option::Option) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_declare_vtab(arg1: *mut sqlite3, + zSQL: *const ::std::os::raw::c_char) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_overload_function(arg1: *mut sqlite3, + zFuncName: *const ::std::os::raw::c_char, + nArg: ::std::os::raw::c_int) + -> ::std::os::raw::c_int; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct sqlite3_blob([u8; 0]); +extern "C" { + pub fn sqlite3_blob_open(arg1: *mut sqlite3, + zDb: *const ::std::os::raw::c_char, + zTable: *const ::std::os::raw::c_char, + zColumn: *const ::std::os::raw::c_char, + iRow: sqlite3_int64, + flags: ::std::os::raw::c_int, + ppBlob: *mut *mut sqlite3_blob) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_blob_reopen(arg1: *mut sqlite3_blob, arg2: sqlite3_int64) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_blob_close(arg1: *mut sqlite3_blob) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_blob_bytes(arg1: *mut sqlite3_blob) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_blob_read(arg1: *mut sqlite3_blob, + Z: *mut ::std::os::raw::c_void, + N: ::std::os::raw::c_int, + iOffset: ::std::os::raw::c_int) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_blob_write(arg1: *mut sqlite3_blob, + z: *const ::std::os::raw::c_void, + n: ::std::os::raw::c_int, + iOffset: ::std::os::raw::c_int) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_vfs_find(zVfsName: *const ::std::os::raw::c_char) + -> *mut sqlite3_vfs; +} +extern "C" { + pub fn sqlite3_vfs_register(arg1: *mut sqlite3_vfs, + makeDflt: ::std::os::raw::c_int) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_vfs_unregister(arg1: *mut sqlite3_vfs) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_mutex_alloc(arg1: ::std::os::raw::c_int) + -> *mut sqlite3_mutex; +} +extern "C" { + pub fn sqlite3_mutex_free(arg1: *mut sqlite3_mutex); +} +extern "C" { + pub fn sqlite3_mutex_enter(arg1: *mut sqlite3_mutex); +} +extern "C" { + pub fn sqlite3_mutex_try(arg1: *mut sqlite3_mutex) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_mutex_leave(arg1: *mut sqlite3_mutex); +} +#[repr(C)] +#[derive(Debug, Copy)] +pub struct sqlite3_mutex_methods { + pub xMutexInit: ::std::option::Option ::std::os::raw::c_int>, + pub xMutexEnd: ::std::option::Option ::std::os::raw::c_int>, + pub xMutexAlloc: ::std::option::Option *mut sqlite3_mutex>, + pub xMutexFree: ::std::option::Option, + pub xMutexEnter: ::std::option::Option, + pub xMutexTry: ::std::option::Option ::std::os::raw::c_int>, + pub xMutexLeave: ::std::option::Option, + pub xMutexHeld: ::std::option::Option ::std::os::raw::c_int>, + pub xMutexNotheld: ::std::option::Option ::std::os::raw::c_int>, +} +#[test] +fn bindgen_test_layout_sqlite3_mutex_methods() { + assert_eq!(::std::mem::size_of::() , 72usize); + assert_eq!(::std::mem::align_of::() , 8usize); +} +impl Clone for sqlite3_mutex_methods { + fn clone(&self) -> Self { *self } +} +extern "C" { + pub fn sqlite3_mutex_held(arg1: *mut sqlite3_mutex) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_mutex_notheld(arg1: *mut sqlite3_mutex) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_db_mutex(arg1: *mut sqlite3) -> *mut sqlite3_mutex; +} +extern "C" { + pub fn sqlite3_file_control(arg1: *mut sqlite3, + zDbName: *const ::std::os::raw::c_char, + op: ::std::os::raw::c_int, + arg2: *mut ::std::os::raw::c_void) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_test_control(op: ::std::os::raw::c_int, ...) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_status(op: ::std::os::raw::c_int, + pCurrent: *mut ::std::os::raw::c_int, + pHighwater: *mut ::std::os::raw::c_int, + resetFlag: ::std::os::raw::c_int) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_db_status(arg1: *mut sqlite3, op: ::std::os::raw::c_int, + pCur: *mut ::std::os::raw::c_int, + pHiwtr: *mut ::std::os::raw::c_int, + resetFlg: ::std::os::raw::c_int) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_stmt_status(arg1: *mut sqlite3_stmt, + op: ::std::os::raw::c_int, + resetFlg: ::std::os::raw::c_int) + -> ::std::os::raw::c_int; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct sqlite3_pcache([u8; 0]); +#[repr(C)] +#[derive(Debug, Copy)] +pub struct sqlite3_pcache_page { + pub pBuf: *mut ::std::os::raw::c_void, + pub pExtra: *mut ::std::os::raw::c_void, +} +#[test] +fn bindgen_test_layout_sqlite3_pcache_page() { + assert_eq!(::std::mem::size_of::() , 16usize); + assert_eq!(::std::mem::align_of::() , 8usize); +} +impl Clone for sqlite3_pcache_page { + fn clone(&self) -> Self { *self } +} +#[repr(C)] +#[derive(Debug, Copy)] +pub struct sqlite3_pcache_methods2 { + pub iVersion: ::std::os::raw::c_int, + pub pArg: *mut ::std::os::raw::c_void, + pub xInit: ::std::option::Option ::std::os::raw::c_int>, + pub xShutdown: ::std::option::Option, + pub xCreate: ::std::option::Option *mut sqlite3_pcache>, + pub xCachesize: ::std::option::Option, + pub xPagecount: ::std::option::Option ::std::os::raw::c_int>, + pub xFetch: ::std::option::Option *mut sqlite3_pcache_page>, + pub xUnpin: ::std::option::Option, + pub xRekey: ::std::option::Option, + pub xTruncate: ::std::option::Option, + pub xDestroy: ::std::option::Option, + pub xShrink: ::std::option::Option, +} +#[test] +fn bindgen_test_layout_sqlite3_pcache_methods2() { + assert_eq!(::std::mem::size_of::() , 104usize); + assert_eq!(::std::mem::align_of::() , 8usize); +} +impl Clone for sqlite3_pcache_methods2 { + fn clone(&self) -> Self { *self } +} +#[repr(C)] +#[derive(Debug, Copy)] +pub struct sqlite3_pcache_methods { + pub pArg: *mut ::std::os::raw::c_void, + pub xInit: ::std::option::Option ::std::os::raw::c_int>, + pub xShutdown: ::std::option::Option, + pub xCreate: ::std::option::Option *mut sqlite3_pcache>, + pub xCachesize: ::std::option::Option, + pub xPagecount: ::std::option::Option ::std::os::raw::c_int>, + pub xFetch: ::std::option::Option *mut ::std::os::raw::c_void>, + pub xUnpin: ::std::option::Option, + pub xRekey: ::std::option::Option, + pub xTruncate: ::std::option::Option, + pub xDestroy: ::std::option::Option, +} +#[test] +fn bindgen_test_layout_sqlite3_pcache_methods() { + assert_eq!(::std::mem::size_of::() , 88usize); + assert_eq!(::std::mem::align_of::() , 8usize); +} +impl Clone for sqlite3_pcache_methods { + fn clone(&self) -> Self { *self } +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct sqlite3_backup([u8; 0]); +extern "C" { + pub fn sqlite3_backup_init(pDest: *mut sqlite3, + zDestName: *const ::std::os::raw::c_char, + pSource: *mut sqlite3, + zSourceName: *const ::std::os::raw::c_char) + -> *mut sqlite3_backup; +} +extern "C" { + pub fn sqlite3_backup_step(p: *mut sqlite3_backup, + nPage: ::std::os::raw::c_int) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_backup_finish(p: *mut sqlite3_backup) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_backup_remaining(p: *mut sqlite3_backup) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_backup_pagecount(p: *mut sqlite3_backup) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_unlock_notify(pBlocked: *mut sqlite3, + xNotify: + ::std::option::Option, + pNotifyArg: *mut ::std::os::raw::c_void) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_stricmp(arg1: *const ::std::os::raw::c_char, + arg2: *const ::std::os::raw::c_char) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_strnicmp(arg1: *const ::std::os::raw::c_char, + arg2: *const ::std::os::raw::c_char, + arg3: ::std::os::raw::c_int) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_log(iErrCode: ::std::os::raw::c_int, + zFormat: *const ::std::os::raw::c_char, ...); +} +extern "C" { + pub fn sqlite3_wal_hook(arg1: *mut sqlite3, + arg2: + ::std::option::Option + ::std::os::raw::c_int>, + arg3: *mut ::std::os::raw::c_void) + -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn sqlite3_wal_autocheckpoint(db: *mut sqlite3, + N: ::std::os::raw::c_int) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_wal_checkpoint(db: *mut sqlite3, + zDb: *const ::std::os::raw::c_char) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_wal_checkpoint_v2(db: *mut sqlite3, + zDb: *const ::std::os::raw::c_char, + eMode: ::std::os::raw::c_int, + pnLog: *mut ::std::os::raw::c_int, + pnCkpt: *mut ::std::os::raw::c_int) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_vtab_config(arg1: *mut sqlite3, + op: ::std::os::raw::c_int, ...) + -> ::std::os::raw::c_int; +} +extern "C" { + pub fn sqlite3_vtab_on_conflict(arg1: *mut sqlite3) + -> ::std::os::raw::c_int; +} +#[repr(C)] +#[derive(Debug, Copy)] +pub struct sqlite3_rtree_geometry { + pub pContext: *mut ::std::os::raw::c_void, + pub nParam: ::std::os::raw::c_int, + pub aParam: *mut f64, + pub pUser: *mut ::std::os::raw::c_void, + pub xDelUser: ::std::option::Option, +} +#[test] +fn bindgen_test_layout_sqlite3_rtree_geometry() { + assert_eq!(::std::mem::size_of::() , 40usize); + assert_eq!(::std::mem::align_of::() , 8usize); +} +impl Clone for sqlite3_rtree_geometry { + fn clone(&self) -> Self { *self } +} +extern "C" { + pub fn sqlite3_rtree_geometry_callback(db: *mut sqlite3, + zGeom: + *const ::std::os::raw::c_char, + xGeom: + ::std::option::Option + ::std::os::raw::c_int>, + pContext: + *mut ::std::os::raw::c_void) + -> ::std::os::raw::c_int; +} +#[repr(C)] +#[derive(Debug, Copy)] +pub struct __va_list_tag { + pub gp_offset: ::std::os::raw::c_uint, + pub fp_offset: ::std::os::raw::c_uint, + pub overflow_arg_area: *mut ::std::os::raw::c_void, + pub reg_save_area: *mut ::std::os::raw::c_void, +} +impl Clone for __va_list_tag { + fn clone(&self) -> Self { *self } +} +pub type __builtin_va_list = [__va_list_tag; 1usize]; + +pub const SQLITE_DETERMINISTIC: i32 = 2048; diff --git a/libsqlite3-sys/build.rs b/libsqlite3-sys/build.rs index 8766566..87998ef 100644 --- a/libsqlite3-sys/build.rs +++ b/libsqlite3-sys/build.rs @@ -1,22 +1,34 @@ +use std::env; +use std::path::Path; + fn main() { - build::main(); + let out_dir = env::var("OUT_DIR").unwrap(); + let out_path = Path::new(&out_dir).join("bindgen.rs"); + build::main(&out_dir, &out_path); } #[cfg(feature = "bundled")] mod build { - extern crate cc; + use cc; use std::path::Path; - use std::{env, fs}; - pub fn main() { + pub fn main(out_dir: &str, out_path: &Path) { if cfg!(feature = "sqlcipher") { panic!("Builds with bundled SQLCipher are not supported"); } - let out_dir = env::var("OUT_DIR").unwrap(); - let out_path = Path::new(&out_dir).join("bindgen.rs"); - fs::copy("sqlite3/bindgen_bundled_version.rs", out_path) - .expect("Could not copy bindings to output directory"); + #[cfg(feature = "buildtime_bindgen")] + { + use super::{bindings, HeaderLocation}; + let header = HeaderLocation::FromPath("sqlite3/sqlite3.h".to_owned()); + bindings::write_to_out_dir(header, out_path); + } + #[cfg(not(feature = "buildtime_bindgen"))] + { + use std::fs; + fs::copy("sqlite3/bindgen_bundled_version.rs", out_path) + .expect("Could not copy bindings to output directory"); + } let mut cfg = cc::Build::new(); cfg.file("sqlite3/sqlite3.c") @@ -42,62 +54,87 @@ mod build { if cfg!(feature = "unlock_notify") { cfg.flag("-DSQLITE_ENABLE_UNLOCK_NOTIFY"); } + if cfg!(feature = "preupdate_hook") { + cfg.flag("-DSQLITE_ENABLE_PREUPDATE_HOOK"); + } + if cfg!(feature = "session") { + cfg.flag("-DSQLITE_ENABLE_SESSION"); + } cfg.compile("libsqlite3.a"); println!("cargo:lib_dir={}", out_dir); } } +fn env_prefix() -> &'static str { + if cfg!(feature = "sqlcipher") { + "SQLCIPHER" + } else { + "SQLITE3" + } +} + +pub enum HeaderLocation { + FromEnvironment, + Wrapper, + FromPath(String), +} + +impl From for String { + fn from(header: HeaderLocation) -> String { + match header { + HeaderLocation::FromEnvironment => { + let prefix = env_prefix(); + let mut header = env::var(format!("{}_INCLUDE_DIR", prefix)).expect(&format!( + "{}_INCLUDE_DIR must be set if {}_LIB_DIR is set", + prefix, prefix + )); + header.push_str("/sqlite3.h"); + header + } + HeaderLocation::Wrapper => "wrapper.h".into(), + HeaderLocation::FromPath(path) => path, + } + } +} + #[cfg(not(feature = "bundled"))] mod build { - extern crate pkg_config; + use pkg_config; #[cfg(all(feature = "vcpkg", target_env = "msvc"))] extern crate vcpkg; + use super::{bindings, env_prefix, HeaderLocation}; use std::env; + use std::path::Path; - pub enum HeaderLocation { - FromEnvironment, - Wrapper, - FromPath(String), + pub fn main(_out_dir: &str, out_path: &Path) { + let header = find_sqlite(); + bindings::write_to_out_dir(header, out_path); } - impl From for String { - fn from(header: HeaderLocation) -> String { - match header { - HeaderLocation::FromEnvironment => { - let prefix = env_prefix(); - let mut header = env::var(format!("{}_INCLUDE_DIR", prefix)).expect(&format!( - "{}_INCLUDE_DIR must be set if {}_LIB_DIR is set", - prefix, prefix - )); - header.push_str("/sqlite3.h"); - header - } - HeaderLocation::Wrapper => "wrapper.h".into(), - HeaderLocation::FromPath(path) => path, - } + fn find_link_mode() -> &'static str { + // If the user specifies SQLITE_STATIC (or SQLCIPHER_STATIC), do static + // linking, unless it's explicitly set to 0. + match &env::var(format!("{}_STATIC", env_prefix())) { + Ok(v) if v != "0" => "static", + _ => "dylib", } } - - pub fn main() { - let header = find_sqlite(); - bindings::write_to_out_dir(header); - } - // Prints the necessary cargo link commands and returns the path to the header. fn find_sqlite() -> HeaderLocation { let link_lib = link_lib(); println!("cargo:rerun-if-env-changed={}_INCLUDE_DIR", env_prefix()); println!("cargo:rerun-if-env-changed={}_LIB_DIR", env_prefix()); + println!("cargo:rerun-if-env-changed={}_STATIC", env_prefix()); if cfg!(target_os = "windows") { println!("cargo:rerun-if-env-changed=PATH"); } // Allow users to specify where to find SQLite. if let Ok(dir) = env::var(format!("{}_LIB_DIR", env_prefix())) { - println!("cargo:rustc-link-lib={}", link_lib); + println!("cargo:rustc-link-lib={}={}", find_link_mode(), link_lib); println!("cargo:rustc-link-search={}", dir); return HeaderLocation::FromEnvironment; } @@ -122,9 +159,9 @@ mod build { Err(_) => { // No env var set and pkg-config couldn't help; just output the link-lib // request and hope that the library exists on the system paths. We used to - // output /usr/lib explicitly, but that can introduce other linking problems; see - // https://github.com/jgallagher/rusqlite/issues/207. - println!("cargo:rustc-link-lib={}", link_lib); + // output /usr/lib explicitly, but that can introduce other linking problems; + // see https://github.com/jgallagher/rusqlite/issues/207. + println!("cargo:rustc-link-lib={}={}", find_link_mode(), link_lib); HeaderLocation::Wrapper } } @@ -147,14 +184,6 @@ mod build { None } - fn env_prefix() -> &'static str { - if cfg!(feature = "sqlcipher") { - "SQLCIPHER" - } else { - "SQLITE3" - } - } - fn link_lib() -> &'static str { if cfg!(feature = "sqlcipher") { "sqlcipher" @@ -162,88 +191,98 @@ mod build { "sqlite3" } } +} - #[cfg(not(feature = "buildtime_bindgen"))] - mod bindings { - use super::HeaderLocation; +#[cfg(all(not(feature = "buildtime_bindgen"), not(feature = "bundled")))] +mod bindings { + use super::HeaderLocation; - use std::path::Path; - use std::{env, fs}; + use std::fs; + use std::path::Path; - static PREBUILT_BINDGEN_PATHS: &'static [&'static str] = &[ - "bindgen-bindings/bindgen_3.6.8.rs", - #[cfg(feature = "min_sqlite_version_3_6_23")] - "bindgen-bindings/bindgen_3.6.23.rs", - #[cfg(feature = "min_sqlite_version_3_7_7")] - "bindgen-bindings/bindgen_3.7.7.rs", - ]; + static PREBUILT_BINDGEN_PATHS: &'static [&'static str] = &[ + "bindgen-bindings/bindgen_3.6.8.rs", + #[cfg(feature = "min_sqlite_version_3_6_23")] + "bindgen-bindings/bindgen_3.6.23.rs", + #[cfg(feature = "min_sqlite_version_3_7_7")] + "bindgen-bindings/bindgen_3.7.7.rs", + #[cfg(feature = "min_sqlite_version_3_7_16")] + "bindgen-bindings/bindgen_3.7.16.rs", + ]; - pub fn write_to_out_dir(_header: HeaderLocation) { - let out_dir = env::var("OUT_DIR").unwrap(); - let out_path = Path::new(&out_dir).join("bindgen.rs"); - let in_path = PREBUILT_BINDGEN_PATHS[PREBUILT_BINDGEN_PATHS.len() - 1]; - fs::copy(in_path, out_path).expect("Could not copy bindings to output directory"); - } - } - - #[cfg(feature = "buildtime_bindgen")] - mod bindings { - extern crate bindgen; - - use self::bindgen::callbacks::{IntKind, ParseCallbacks}; - use super::HeaderLocation; - - use std::env; - use std::fs::OpenOptions; - use std::io::Write; - use std::path::Path; - - #[derive(Debug)] - struct SqliteTypeChooser; - - impl ParseCallbacks for SqliteTypeChooser { - fn int_macro(&self, _name: &str, value: i64) -> Option { - if value >= i32::min_value() as i64 && value <= i32::max_value() as i64 { - Some(IntKind::I32) - } else { - None - } - } - } - - pub fn write_to_out_dir(header: HeaderLocation) { - let header: String = header.into(); - let out_dir = env::var("OUT_DIR").unwrap(); - let mut output = Vec::new(); - bindgen::builder() - .header(header.clone()) - .parse_callbacks(Box::new(SqliteTypeChooser)) - .rustfmt_bindings(true) - .generate() - .expect(&format!("could not run bindgen on header {}", header)) - .write(Box::new(&mut output)) - .expect("could not write output of bindgen"); - let mut output = String::from_utf8(output).expect("bindgen output was not UTF-8?!"); - - // rusqlite's functions feature ors in the SQLITE_DETERMINISTIC flag when it can. This flag - // was added in SQLite 3.8.3, but oring it in in prior versions of SQLite is harmless. We - // don't want to not build just because this flag is missing (e.g., if we're linking against - // SQLite 3.7.x), so append the flag manually if it isn't present in bindgen's output. - if !output.contains("pub const SQLITE_DETERMINISTIC") { - output.push_str("\npub const SQLITE_DETERMINISTIC: i32 = 2048;\n"); - } - - let path = Path::new(&out_dir).join("bindgen.rs"); - - let mut file = OpenOptions::new() - .write(true) - .truncate(true) - .create(true) - .open(path.clone()) - .expect(&format!("Could not write to {:?}", path)); - - file.write_all(output.as_bytes()) - .expect(&format!("Could not write to {:?}", path)); - } + pub fn write_to_out_dir(_header: HeaderLocation, out_path: &Path) { + let in_path = PREBUILT_BINDGEN_PATHS[PREBUILT_BINDGEN_PATHS.len() - 1]; + fs::copy(in_path, out_path).expect("Could not copy bindings to output directory"); + } +} + +#[cfg(feature = "buildtime_bindgen")] +mod bindings { + use bindgen; + + use self::bindgen::callbacks::{IntKind, ParseCallbacks}; + use super::HeaderLocation; + + use std::fs::OpenOptions; + use std::io::Write; + use std::path::Path; + + #[derive(Debug)] + struct SqliteTypeChooser; + + impl ParseCallbacks for SqliteTypeChooser { + fn int_macro(&self, _name: &str, value: i64) -> Option { + if value >= i32::min_value() as i64 && value <= i32::max_value() as i64 { + Some(IntKind::I32) + } else { + None + } + } + } + + pub fn write_to_out_dir(header: HeaderLocation, out_path: &Path) { + let header: String = header.into(); + let mut output = Vec::new(); + let mut bindings = bindgen::builder() + .header(header.clone()) + .parse_callbacks(Box::new(SqliteTypeChooser)) + .rustfmt_bindings(true); + + if cfg!(feature = "unlock_notify") { + bindings = bindings.clang_arg("-DSQLITE_ENABLE_UNLOCK_NOTIFY"); + } + if cfg!(feature = "preupdate_hook") { + bindings = bindings.clang_arg("-DSQLITE_ENABLE_PREUPDATE_HOOK"); + } + if cfg!(feature = "session") { + bindings = bindings.clang_arg("-DSQLITE_ENABLE_SESSION"); + } + + bindings + .generate() + .expect(&format!("could not run bindgen on header {}", header)) + .write(Box::new(&mut output)) + .expect("could not write output of bindgen"); + let mut output = String::from_utf8(output).expect("bindgen output was not UTF-8?!"); + + // rusqlite's functions feature ors in the SQLITE_DETERMINISTIC flag when it + // can. This flag was added in SQLite 3.8.3, but oring it in in prior + // versions of SQLite is harmless. We don't want to not build just + // because this flag is missing (e.g., if we're linking against + // SQLite 3.7.x), so append the flag manually if it isn't present in bindgen's + // output. + if !output.contains("pub const SQLITE_DETERMINISTIC") { + output.push_str("\npub const SQLITE_DETERMINISTIC: i32 = 2048;\n"); + } + + let mut file = OpenOptions::new() + .write(true) + .truncate(true) + .create(true) + .open(out_path.clone()) + .expect(&format!("Could not write to {:?}", out_path)); + + file.write_all(output.as_bytes()) + .expect(&format!("Could not write to {:?}", out_path)); } } diff --git a/libsqlite3-sys/sqlite3/bindgen_bundled_version.rs b/libsqlite3-sys/sqlite3/bindgen_bundled_version.rs index e43f41b..95fe191 100644 --- a/libsqlite3-sys/sqlite3/bindgen_bundled_version.rs +++ b/libsqlite3-sys/sqlite3/bindgen_bundled_version.rs @@ -1,10 +1,10 @@ /* automatically generated by rust-bindgen */ pub const __GNUC_VA_LIST: i32 = 1; -pub const SQLITE_VERSION: &'static [u8; 7usize] = b"3.25.2\0"; -pub const SQLITE_VERSION_NUMBER: i32 = 3025002; +pub const SQLITE_VERSION: &'static [u8; 7usize] = b"3.26.0\0"; +pub const SQLITE_VERSION_NUMBER: i32 = 3026000; pub const SQLITE_SOURCE_ID: &'static [u8; 85usize] = - b"2018-09-25 19:08:10 fb90e7189ae6d62e77ba3a308ca5d683f90bbe633cf681865365b8e92792d1c7\0"; + b"2018-12-01 12:34:55 bf8c1b2b7a5960c282e543b9c293686dccff272512d08865f4600fb58238b4f9\0"; pub const SQLITE_OK: i32 = 0; pub const SQLITE_ERROR: i32 = 1; pub const SQLITE_INTERNAL: i32 = 2; @@ -228,7 +228,8 @@ pub const SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE: i32 = 1006; pub const SQLITE_DBCONFIG_ENABLE_QPSG: i32 = 1007; pub const SQLITE_DBCONFIG_TRIGGER_EQP: i32 = 1008; pub const SQLITE_DBCONFIG_RESET_DATABASE: i32 = 1009; -pub const SQLITE_DBCONFIG_MAX: i32 = 1009; +pub const SQLITE_DBCONFIG_DEFENSIVE: i32 = 1010; +pub const SQLITE_DBCONFIG_MAX: i32 = 1010; pub const SQLITE_DENY: i32 = 1; pub const SQLITE_IGNORE: i32 = 2; pub const SQLITE_CREATE_INDEX: i32 = 1; @@ -282,6 +283,7 @@ pub const SQLITE_LIMIT_VARIABLE_NUMBER: i32 = 9; pub const SQLITE_LIMIT_TRIGGER_DEPTH: i32 = 10; pub const SQLITE_LIMIT_WORKER_THREADS: i32 = 11; pub const SQLITE_PREPARE_PERSISTENT: i32 = 1; +pub const SQLITE_PREPARE_NORMALIZE: i32 = 2; pub const SQLITE_INTEGER: i32 = 1; pub const SQLITE_FLOAT: i32 = 2; pub const SQLITE_BLOB: i32 = 4; @@ -343,6 +345,7 @@ pub const SQLITE_TESTCTRL_RESERVE: i32 = 14; pub const SQLITE_TESTCTRL_OPTIMIZATIONS: i32 = 15; pub const SQLITE_TESTCTRL_ISKEYWORD: i32 = 16; pub const SQLITE_TESTCTRL_SCRATCHMALLOC: i32 = 17; +pub const SQLITE_TESTCTRL_INTERNAL_FUNCTIONS: i32 = 17; pub const SQLITE_TESTCTRL_LOCALTIME_FAULT: i32 = 18; pub const SQLITE_TESTCTRL_EXPLAIN_STMT: i32 = 19; pub const SQLITE_TESTCTRL_ONCE_RESET_THRESHOLD: i32 = 19; @@ -531,24 +534,34 @@ pub struct sqlite3_io_methods { unsafe extern "C" fn(arg1: *mut sqlite3_file, size: sqlite3_int64) -> ::std::os::raw::c_int, >, pub xSync: ::std::option::Option< - unsafe extern "C" fn(arg1: *mut sqlite3_file, flags: ::std::os::raw::c_int) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut sqlite3_file, + flags: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int, >, pub xFileSize: ::std::option::Option< - unsafe extern "C" fn(arg1: *mut sqlite3_file, pSize: *mut sqlite3_int64) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut sqlite3_file, + pSize: *mut sqlite3_int64, + ) -> ::std::os::raw::c_int, >, pub xLock: ::std::option::Option< - unsafe extern "C" fn(arg1: *mut sqlite3_file, arg2: ::std::os::raw::c_int) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut sqlite3_file, + arg2: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int, >, pub xUnlock: ::std::option::Option< - unsafe extern "C" fn(arg1: *mut sqlite3_file, arg2: ::std::os::raw::c_int) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut sqlite3_file, + arg2: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int, >, pub xCheckReservedLock: ::std::option::Option< - unsafe extern "C" fn(arg1: *mut sqlite3_file, pResOut: *mut ::std::os::raw::c_int) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut sqlite3_file, + pResOut: *mut ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int, >, pub xFileControl: ::std::option::Option< unsafe extern "C" fn( @@ -582,8 +595,10 @@ pub struct sqlite3_io_methods { >, pub xShmBarrier: ::std::option::Option, pub xShmUnmap: ::std::option::Option< - unsafe extern "C" fn(arg1: *mut sqlite3_file, deleteFlag: ::std::os::raw::c_int) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut sqlite3_file, + deleteFlag: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int, >, pub xFetch: ::std::option::Option< unsafe extern "C" fn( @@ -862,8 +877,10 @@ pub struct sqlite3_vfs { ) -> ::std::os::raw::c_int, >, pub xDlOpen: ::std::option::Option< - unsafe extern "C" fn(arg1: *mut sqlite3_vfs, zFilename: *const ::std::os::raw::c_char) - -> *mut ::std::os::raw::c_void, + unsafe extern "C" fn( + arg1: *mut sqlite3_vfs, + zFilename: *const ::std::os::raw::c_char, + ) -> *mut ::std::os::raw::c_void, >, pub xDlError: ::std::option::Option< unsafe extern "C" fn( @@ -896,8 +913,10 @@ pub struct sqlite3_vfs { ) -> ::std::os::raw::c_int, >, pub xSleep: ::std::option::Option< - unsafe extern "C" fn(arg1: *mut sqlite3_vfs, microseconds: ::std::os::raw::c_int) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut sqlite3_vfs, + microseconds: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int, >, pub xCurrentTime: ::std::option::Option< unsafe extern "C" fn(arg1: *mut sqlite3_vfs, arg2: *mut f64) -> ::std::os::raw::c_int, @@ -910,8 +929,10 @@ pub struct sqlite3_vfs { ) -> ::std::os::raw::c_int, >, pub xCurrentTimeInt64: ::std::option::Option< - unsafe extern "C" fn(arg1: *mut sqlite3_vfs, arg2: *mut sqlite3_int64) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut sqlite3_vfs, + arg2: *mut sqlite3_int64, + ) -> ::std::os::raw::c_int, >, pub xSetSystemCall: ::std::option::Option< unsafe extern "C" fn( @@ -921,12 +942,16 @@ pub struct sqlite3_vfs { ) -> ::std::os::raw::c_int, >, pub xGetSystemCall: ::std::option::Option< - unsafe extern "C" fn(arg1: *mut sqlite3_vfs, zName: *const ::std::os::raw::c_char) - -> sqlite3_syscall_ptr, + unsafe extern "C" fn( + arg1: *mut sqlite3_vfs, + zName: *const ::std::os::raw::c_char, + ) -> sqlite3_syscall_ptr, >, pub xNextSystemCall: ::std::option::Option< - unsafe extern "C" fn(arg1: *mut sqlite3_vfs, zName: *const ::std::os::raw::c_char) - -> *const ::std::os::raw::c_char, + unsafe extern "C" fn( + arg1: *mut sqlite3_vfs, + zName: *const ::std::os::raw::c_char, + ) -> *const ::std::os::raw::c_char, >, } #[test] @@ -1192,8 +1217,10 @@ pub struct sqlite3_mem_methods { >, pub xFree: ::std::option::Option, pub xRealloc: ::std::option::Option< - unsafe extern "C" fn(arg1: *mut ::std::os::raw::c_void, arg2: ::std::os::raw::c_int) - -> *mut ::std::os::raw::c_void, + unsafe extern "C" fn( + arg1: *mut ::std::os::raw::c_void, + arg2: ::std::os::raw::c_int, + ) -> *mut ::std::os::raw::c_void, >, pub xSize: ::std::option::Option< unsafe extern "C" fn(arg1: *mut ::std::os::raw::c_void) -> ::std::os::raw::c_int, @@ -1331,8 +1358,10 @@ extern "C" { pub fn sqlite3_busy_handler( arg1: *mut sqlite3, arg2: ::std::option::Option< - unsafe extern "C" fn(arg1: *mut ::std::os::raw::c_void, arg2: ::std::os::raw::c_int) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut ::std::os::raw::c_void, + arg2: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int, >, arg3: *mut ::std::os::raw::c_void, ) -> ::std::os::raw::c_int; @@ -1610,6 +1639,9 @@ extern "C" { extern "C" { pub fn sqlite3_expanded_sql(pStmt: *mut sqlite3_stmt) -> *mut ::std::os::raw::c_char; } +extern "C" { + pub fn sqlite3_normalized_sql(pStmt: *mut sqlite3_stmt) -> *const ::std::os::raw::c_char; +} extern "C" { pub fn sqlite3_stmt_readonly(pStmt: *mut sqlite3_stmt) -> ::std::os::raw::c_int; } @@ -2447,8 +2479,10 @@ pub struct sqlite3_module { ) -> ::std::os::raw::c_int, >, pub xBestIndex: ::std::option::Option< - unsafe extern "C" fn(pVTab: *mut sqlite3_vtab, arg1: *mut sqlite3_index_info) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + pVTab: *mut sqlite3_vtab, + arg1: *mut sqlite3_index_info, + ) -> ::std::os::raw::c_int, >, pub xDisconnect: ::std::option::Option< unsafe extern "C" fn(pVTab: *mut sqlite3_vtab) -> ::std::os::raw::c_int, @@ -2457,8 +2491,10 @@ pub struct sqlite3_module { unsafe extern "C" fn(pVTab: *mut sqlite3_vtab) -> ::std::os::raw::c_int, >, pub xOpen: ::std::option::Option< - unsafe extern "C" fn(pVTab: *mut sqlite3_vtab, ppCursor: *mut *mut sqlite3_vtab_cursor) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + pVTab: *mut sqlite3_vtab, + ppCursor: *mut *mut sqlite3_vtab_cursor, + ) -> ::std::os::raw::c_int, >, pub xClose: ::std::option::Option< unsafe extern "C" fn(arg1: *mut sqlite3_vtab_cursor) -> ::std::os::raw::c_int, @@ -2486,8 +2522,10 @@ pub struct sqlite3_module { ) -> ::std::os::raw::c_int, >, pub xRowid: ::std::option::Option< - unsafe extern "C" fn(arg1: *mut sqlite3_vtab_cursor, pRowid: *mut sqlite3_int64) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut sqlite3_vtab_cursor, + pRowid: *mut sqlite3_int64, + ) -> ::std::os::raw::c_int, >, pub xUpdate: ::std::option::Option< unsafe extern "C" fn( @@ -2525,27 +2563,38 @@ pub struct sqlite3_module { ) -> ::std::os::raw::c_int, >, pub xRename: ::std::option::Option< - unsafe extern "C" fn(pVtab: *mut sqlite3_vtab, zNew: *const ::std::os::raw::c_char) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + pVtab: *mut sqlite3_vtab, + zNew: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int, >, pub xSavepoint: ::std::option::Option< - unsafe extern "C" fn(pVTab: *mut sqlite3_vtab, arg1: ::std::os::raw::c_int) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + pVTab: *mut sqlite3_vtab, + arg1: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int, >, pub xRelease: ::std::option::Option< - unsafe extern "C" fn(pVTab: *mut sqlite3_vtab, arg1: ::std::os::raw::c_int) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + pVTab: *mut sqlite3_vtab, + arg1: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int, >, pub xRollbackTo: ::std::option::Option< - unsafe extern "C" fn(pVTab: *mut sqlite3_vtab, arg1: ::std::os::raw::c_int) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + pVTab: *mut sqlite3_vtab, + arg1: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int, + >, + pub xShadowName: ::std::option::Option< + unsafe extern "C" fn(arg1: *const ::std::os::raw::c_char) -> ::std::os::raw::c_int, >, } #[test] fn bindgen_test_layout_sqlite3_module() { assert_eq!( ::std::mem::size_of::(), - 184usize, + 192usize, concat!("Size of: ", stringify!(sqlite3_module)) ); assert_eq!( @@ -2783,6 +2832,16 @@ fn bindgen_test_layout_sqlite3_module() { stringify!(xRollbackTo) ) ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).xShadowName as *const _ as usize }, + 184usize, + concat!( + "Offset of field: ", + stringify!(sqlite3_module), + "::", + stringify!(xShadowName) + ) + ); } #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -3828,8 +3887,10 @@ pub struct sqlite3_pcache_methods { >, pub xShutdown: ::std::option::Option, pub xCreate: ::std::option::Option< - unsafe extern "C" fn(szPage: ::std::os::raw::c_int, bPurgeable: ::std::os::raw::c_int) - -> *mut sqlite3_pcache, + unsafe extern "C" fn( + szPage: ::std::os::raw::c_int, + bPurgeable: ::std::os::raw::c_int, + ) -> *mut sqlite3_pcache, >, pub xCachesize: ::std::option::Option< unsafe extern "C" fn(arg1: *mut sqlite3_pcache, nCachesize: ::std::os::raw::c_int), @@ -4586,8 +4647,10 @@ pub struct Fts5ExtensionApi { unsafe extern "C" fn(arg1: *mut Fts5Context) -> ::std::os::raw::c_int, >, pub xRowCount: ::std::option::Option< - unsafe extern "C" fn(arg1: *mut Fts5Context, pnRow: *mut sqlite3_int64) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut Fts5Context, + pnRow: *mut sqlite3_int64, + ) -> ::std::os::raw::c_int, >, pub xColumnTotalSize: ::std::option::Option< unsafe extern "C" fn( @@ -4618,12 +4681,16 @@ pub struct Fts5ExtensionApi { unsafe extern "C" fn(arg1: *mut Fts5Context) -> ::std::os::raw::c_int, >, pub xPhraseSize: ::std::option::Option< - unsafe extern "C" fn(arg1: *mut Fts5Context, iPhrase: ::std::os::raw::c_int) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut Fts5Context, + iPhrase: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int, >, pub xInstCount: ::std::option::Option< - unsafe extern "C" fn(arg1: *mut Fts5Context, pnInst: *mut ::std::os::raw::c_int) - -> ::std::os::raw::c_int, + unsafe extern "C" fn( + arg1: *mut Fts5Context, + pnInst: *mut ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int, >, pub xInst: ::std::option::Option< unsafe extern "C" fn( @@ -4669,14 +4736,14 @@ pub struct Fts5ExtensionApi { unsafe extern "C" fn( arg1: *mut Fts5Context, pAux: *mut ::std::os::raw::c_void, - xDelete: ::std::option::Option< - unsafe extern "C" fn(arg1: *mut ::std::os::raw::c_void), - >, + xDelete: ::std::option::Option, ) -> ::std::os::raw::c_int, >, pub xGetAuxdata: ::std::option::Option< - unsafe extern "C" fn(arg1: *mut Fts5Context, bClear: ::std::os::raw::c_int) - -> *mut ::std::os::raw::c_void, + unsafe extern "C" fn( + arg1: *mut Fts5Context, + bClear: ::std::os::raw::c_int, + ) -> *mut ::std::os::raw::c_void, >, pub xPhraseFirst: ::std::option::Option< unsafe extern "C" fn( @@ -5020,9 +5087,7 @@ pub struct fts5_api { zName: *const ::std::os::raw::c_char, pContext: *mut ::std::os::raw::c_void, pTokenizer: *mut fts5_tokenizer, - xDestroy: ::std::option::Option< - unsafe extern "C" fn(arg1: *mut ::std::os::raw::c_void), - >, + xDestroy: ::std::option::Option, ) -> ::std::os::raw::c_int, >, pub xFindTokenizer: ::std::option::Option< @@ -5039,9 +5104,7 @@ pub struct fts5_api { zName: *const ::std::os::raw::c_char, pContext: *mut ::std::os::raw::c_void, xFunction: fts5_extension_function, - xDestroy: ::std::option::Option< - unsafe extern "C" fn(arg1: *mut ::std::os::raw::c_void), - >, + xDestroy: ::std::option::Option, ) -> ::std::os::raw::c_int, >, } diff --git a/libsqlite3-sys/sqlite3/sqlite3.c b/libsqlite3-sys/sqlite3/sqlite3.c index 7b525c3..d015df2 100644 --- a/libsqlite3-sys/sqlite3/sqlite3.c +++ b/libsqlite3-sys/sqlite3/sqlite3.c @@ -1,6 +1,6 @@ /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite -** version 3.25.2. By combining all the individual C code files into this +** version 3.26.0. By combining all the individual C code files into this ** single large file, the entire code can be compiled as a single translation ** unit. This allows many compilers to do optimizations that would not be ** possible if the files were compiled separately. Performance improvements @@ -260,6 +260,9 @@ static const char * const sqlite3azCompileOpt[] = { #if SQLITE_ENABLE_FTS5 "ENABLE_FTS5", #endif +#if SQLITE_ENABLE_GEOPOLY + "ENABLE_GEOPOLY", +#endif #if SQLITE_ENABLE_HIDDEN_COLUMNS "ENABLE_HIDDEN_COLUMNS", #endif @@ -290,6 +293,9 @@ static const char * const sqlite3azCompileOpt[] = { #if SQLITE_ENABLE_MULTIPLEX "ENABLE_MULTIPLEX", #endif +#if SQLITE_ENABLE_NORMALIZE + "ENABLE_NORMALIZE", +#endif #if SQLITE_ENABLE_NULL_TRIM "ENABLE_NULL_TRIM", #endif @@ -1156,9 +1162,9 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.25.2" -#define SQLITE_VERSION_NUMBER 3025002 -#define SQLITE_SOURCE_ID "2018-09-25 19:08:10 fb90e7189ae6d62e77ba3a308ca5d683f90bbe633cf681865365b8e92792d1c7" +#define SQLITE_VERSION "3.26.0" +#define SQLITE_VERSION_NUMBER 3026000 +#define SQLITE_SOURCE_ID "2018-12-01 12:34:55 bf8c1b2b7a5960c282e543b9c293686dccff272512d08865f4600fb58238b4f9" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -3050,6 +3056,7 @@ struct sqlite3_mem_methods { ** is invoked. ** **
+** [[SQLITE_DBCONFIG_LOOKASIDE]] **
SQLITE_DBCONFIG_LOOKASIDE
**
^This option takes three additional arguments that determine the ** [lookaside memory allocator] configuration for the [database connection]. @@ -3072,6 +3079,7 @@ struct sqlite3_mem_methods { ** memory is in use leaves the configuration unchanged and returns ** [SQLITE_BUSY].)^
** +** [[SQLITE_DBCONFIG_ENABLE_FKEY]] **
SQLITE_DBCONFIG_ENABLE_FKEY
**
^This option is used to enable or disable the enforcement of ** [foreign key constraints]. There should be two additional arguments. @@ -3082,6 +3090,7 @@ struct sqlite3_mem_methods { ** following this call. The second parameter may be a NULL pointer, in ** which case the FK enforcement setting is not reported back.
** +** [[SQLITE_DBCONFIG_ENABLE_TRIGGER]] **
SQLITE_DBCONFIG_ENABLE_TRIGGER
**
^This option is used to enable or disable [CREATE TRIGGER | triggers]. ** There should be two additional arguments. @@ -3092,6 +3101,7 @@ struct sqlite3_mem_methods { ** following this call. The second parameter may be a NULL pointer, in ** which case the trigger setting is not reported back.
** +** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]] **
SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER
**
^This option is used to enable or disable the two-argument ** version of the [fts3_tokenizer()] function which is part of the @@ -3105,6 +3115,7 @@ struct sqlite3_mem_methods { ** following this call. The second parameter may be a NULL pointer, in ** which case the new setting is not reported back.
** +** [[SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION]] **
SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION
**
^This option is used to enable or disable the [sqlite3_load_extension()] ** interface independently of the [load_extension()] SQL function. @@ -3122,7 +3133,7 @@ struct sqlite3_mem_methods { ** be a NULL pointer, in which case the new setting is not reported back. **
** -**
SQLITE_DBCONFIG_MAINDBNAME
+** [[SQLITE_DBCONFIG_MAINDBNAME]]
SQLITE_DBCONFIG_MAINDBNAME
**
^This option is used to change the name of the "main" database ** schema. ^The sole argument is a pointer to a constant UTF8 string ** which will become the new schema name in place of "main". ^SQLite @@ -3131,6 +3142,7 @@ struct sqlite3_mem_methods { ** until after the database connection closes. **
** +** [[SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE]] **
SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE
**
Usually, when a database in wal mode is closed or detached from a ** database handle, SQLite checks if this will mean that there are now no @@ -3144,7 +3156,7 @@ struct sqlite3_mem_methods { ** have been disabled - 0 if they are not disabled, 1 if they are. **
** -**
SQLITE_DBCONFIG_ENABLE_QPSG
+** [[SQLITE_DBCONFIG_ENABLE_QPSG]]
SQLITE_DBCONFIG_ENABLE_QPSG
**
^(The SQLITE_DBCONFIG_ENABLE_QPSG option activates or deactivates ** the [query planner stability guarantee] (QPSG). When the QPSG is active, ** a single SQL query statement will always use the same algorithm regardless @@ -3160,7 +3172,7 @@ struct sqlite3_mem_methods { ** following this call. **
** -**
SQLITE_DBCONFIG_TRIGGER_EQP
+** [[SQLITE_DBCONFIG_TRIGGER_EQP]]
SQLITE_DBCONFIG_TRIGGER_EQP
**
By default, the output of EXPLAIN QUERY PLAN commands does not ** include output for any operations performed by trigger programs. This ** option is used to set or clear (the default) a flag that governs this @@ -3172,7 +3184,7 @@ struct sqlite3_mem_methods { ** it is not disabled, 1 if it is. **
** -**
SQLITE_DBCONFIG_RESET_DATABASE
+** [[SQLITE_DBCONFIG_RESET_DATABASE]]
SQLITE_DBCONFIG_RESET_DATABASE
**
Set the SQLITE_DBCONFIG_RESET_DATABASE flag and then run ** [VACUUM] in order to reset a database back to an empty database ** with no schema and no content. The following process works even for @@ -3191,6 +3203,18 @@ struct sqlite3_mem_methods { ** Because resetting a database is destructive and irreversible, the ** process requires the use of this obscure API and multiple steps to help ** ensure that it does not happen by accident. +** +** [[SQLITE_DBCONFIG_DEFENSIVE]]
SQLITE_DBCONFIG_DEFENSIVE
+**
The SQLITE_DBCONFIG_DEFENSIVE option activates or deactivates the +** "defensive" flag for a database connection. When the defensive +** flag is enabled, language features that allow ordinary SQL to +** deliberately corrupt the database file are disabled. The disabled +** features include but are not limited to the following: +**
    +**
  • The [PRAGMA writable_schema=ON] statement. +**
  • Writes to the [sqlite_dbpage] virtual table. +**
  • Direct writes to [shadow tables]. +**
**
**
*/ @@ -3204,7 +3228,8 @@ struct sqlite3_mem_methods { #define SQLITE_DBCONFIG_ENABLE_QPSG 1007 /* int int* */ #define SQLITE_DBCONFIG_TRIGGER_EQP 1008 /* int int* */ #define SQLITE_DBCONFIG_RESET_DATABASE 1009 /* int int* */ -#define SQLITE_DBCONFIG_MAX 1009 /* Largest DBCONFIG */ +#define SQLITE_DBCONFIG_DEFENSIVE 1010 /* int int* */ +#define SQLITE_DBCONFIG_MAX 1010 /* Largest DBCONFIG */ /* ** CAPI3REF: Enable Or Disable Extended Result Codes @@ -4642,9 +4667,19 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); ** on this hint by avoiding the use of [lookaside memory] so as not to ** deplete the limited store of lookaside memory. Future versions of ** SQLite may act on this hint differently. +** +** [[SQLITE_PREPARE_NORMALIZE]] ^(
SQLITE_PREPARE_NORMALIZE
+**
The SQLITE_PREPARE_NORMALIZE flag indicates that a normalized +** representation of the SQL statement should be calculated and then +** associated with the prepared statement, which can be obtained via +** the [sqlite3_normalized_sql()] interface.)^ The semantics used to +** normalize a SQL statement are unspecified and subject to change. +** At a minimum, literal values will be replaced with suitable +** placeholders. ** */ #define SQLITE_PREPARE_PERSISTENT 0x01 +#define SQLITE_PREPARE_NORMALIZE 0x02 /* ** CAPI3REF: Compiling An SQL Statement @@ -4802,6 +4837,11 @@ SQLITE_API int sqlite3_prepare16_v3( ** ^The sqlite3_expanded_sql(P) interface returns a pointer to a UTF-8 ** string containing the SQL text of prepared statement P with ** [bound parameters] expanded. +** ^The sqlite3_normalized_sql(P) interface returns a pointer to a UTF-8 +** string containing the normalized SQL text of prepared statement P. The +** semantics used to normalize a SQL statement are unspecified and subject +** to change. At a minimum, literal values will be replaced with suitable +** placeholders. ** ** ^(For example, if a prepared statement is created using the SQL ** text "SELECT $abc,:xyz" and if parameter $abc is bound to integer 2345 @@ -4817,14 +4857,16 @@ SQLITE_API int sqlite3_prepare16_v3( ** bound parameter expansions. ^The [SQLITE_OMIT_TRACE] compile-time ** option causes sqlite3_expanded_sql() to always return NULL. ** -** ^The string returned by sqlite3_sql(P) is managed by SQLite and is -** automatically freed when the prepared statement is finalized. +** ^The strings returned by sqlite3_sql(P) and sqlite3_normalized_sql(P) +** are managed by SQLite and are automatically freed when the prepared +** statement is finalized. ** ^The string returned by sqlite3_expanded_sql(P), on the other hand, ** is obtained from [sqlite3_malloc()] and must be free by the application ** by passing it to [sqlite3_free()]. */ SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt); SQLITE_API char *sqlite3_expanded_sql(sqlite3_stmt *pStmt); +SQLITE_API const char *sqlite3_normalized_sql(sqlite3_stmt *pStmt); /* ** CAPI3REF: Determine If An SQL Statement Writes The Database @@ -7314,6 +7356,9 @@ struct sqlite3_module { int (*xSavepoint)(sqlite3_vtab *pVTab, int); int (*xRelease)(sqlite3_vtab *pVTab, int); int (*xRollbackTo)(sqlite3_vtab *pVTab, int); + /* The methods above are in versions 1 and 2 of the sqlite_module object. + ** Those below are for version 3 and greater. */ + int (*xShadowName)(const char*); }; /* @@ -8236,6 +8281,7 @@ SQLITE_API int sqlite3_test_control(int op, ...); #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 #define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */ #define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */ +#define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 17 #define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 #define SQLITE_TESTCTRL_EXPLAIN_STMT 19 /* NOT USED */ #define SQLITE_TESTCTRL_ONCE_RESET_THRESHOLD 19 @@ -9648,6 +9694,7 @@ SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...); ** can use to customize and optimize their behavior. ** **
+** [[SQLITE_VTAB_CONSTRAINT_SUPPORT]] **
SQLITE_VTAB_CONSTRAINT_SUPPORT **
Calls of the form ** [sqlite3_vtab_config](db,SQLITE_VTAB_CONSTRAINT_SUPPORT,X) are supported, @@ -10417,7 +10464,7 @@ struct sqlite3_rtree_query_info { sqlite3_int64 iRowid; /* Rowid for current entry */ sqlite3_rtree_dbl rParentScore; /* Score of parent node */ int eParentWithin; /* Visibility of parent node */ - int eWithin; /* OUT: Visiblity */ + int eWithin; /* OUT: Visibility */ sqlite3_rtree_dbl rScore; /* OUT: Write the score here */ /* The following fields are only available in 3.8.11 and later */ sqlite3_value **apSqlParam; /* Original SQL values of parameters */ @@ -10913,12 +10960,38 @@ SQLITE_API int sqlite3session_isempty(sqlite3_session *pSession); ** consecutively. There is no chance that the iterator will visit a change ** the applies to table X, then one for table Y, and then later on visit ** another change for table X. +** +** The behavior of sqlite3changeset_start_v2() and its streaming equivalent +** may be modified by passing a combination of +** [SQLITE_CHANGESETSTART_INVERT | supported flags] as the 4th parameter. +** +** Note that the sqlite3changeset_start_v2() API is still experimental +** and therefore subject to change. */ SQLITE_API int sqlite3changeset_start( sqlite3_changeset_iter **pp, /* OUT: New changeset iterator handle */ int nChangeset, /* Size of changeset blob in bytes */ void *pChangeset /* Pointer to blob containing changeset */ ); +SQLITE_API int sqlite3changeset_start_v2( + sqlite3_changeset_iter **pp, /* OUT: New changeset iterator handle */ + int nChangeset, /* Size of changeset blob in bytes */ + void *pChangeset, /* Pointer to blob containing changeset */ + int flags /* SESSION_CHANGESETSTART_* flags */ +); + +/* +** CAPI3REF: Flags for sqlite3changeset_start_v2 +** +** The following flags may passed via the 4th parameter to +** [sqlite3changeset_start_v2] and [sqlite3changeset_start_v2_strm]: +** +**
SQLITE_CHANGESETAPPLY_INVERT
+** Invert the changeset while iterating through it. This is equivalent to +** inverting a changeset using sqlite3changeset_invert() before applying it. +** It is an error to specify this flag with a patchset. +*/ +#define SQLITE_CHANGESETSTART_INVERT 0x0002 /* @@ -11573,7 +11646,7 @@ SQLITE_API int sqlite3changeset_apply_v2( ), void *pCtx, /* First argument passed to xConflict */ void **ppRebase, int *pnRebase, /* OUT: Rebase data */ - int flags /* Combination of SESSION_APPLY_* flags */ + int flags /* SESSION_CHANGESETAPPLY_* flags */ ); /* @@ -11591,8 +11664,14 @@ SQLITE_API int sqlite3changeset_apply_v2( ** causes the sessions module to omit this savepoint. In this case, if the ** caller has an open transaction or savepoint when apply_v2() is called, ** it may revert the partially applied changeset by rolling it back. +** +**
SQLITE_CHANGESETAPPLY_INVERT
+** Invert the changeset before applying it. This is equivalent to inverting +** a changeset using sqlite3changeset_invert() before applying it. It is +** an error to specify this flag with a patchset. */ #define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001 +#define SQLITE_CHANGESETAPPLY_INVERT 0x0002 /* ** CAPI3REF: Constants Passed To The Conflict Handler @@ -11986,6 +12065,12 @@ SQLITE_API int sqlite3changeset_start_strm( int (*xInput)(void *pIn, void *pData, int *pnData), void *pIn ); +SQLITE_API int sqlite3changeset_start_v2_strm( + sqlite3_changeset_iter **pp, + int (*xInput)(void *pIn, void *pData, int *pnData), + void *pIn, + int flags +); SQLITE_API int sqlite3session_changeset_strm( sqlite3_session *pSession, int (*xOutput)(void *pOut, const void *pData, int nData), @@ -12012,6 +12097,45 @@ SQLITE_API int sqlite3rebaser_rebase_strm( void *pOut ); +/* +** CAPI3REF: Configure global parameters +** +** The sqlite3session_config() interface is used to make global configuration +** changes to the sessions module in order to tune it to the specific needs +** of the application. +** +** The sqlite3session_config() interface is not threadsafe. If it is invoked +** while any other thread is inside any other sessions method then the +** results are undefined. Furthermore, if it is invoked after any sessions +** related objects have been created, the results are also undefined. +** +** The first argument to the sqlite3session_config() function must be one +** of the SQLITE_SESSION_CONFIG_XXX constants defined below. The +** interpretation of the (void*) value passed as the second parameter and +** the effect of calling this function depends on the value of the first +** parameter. +** +**
+**
SQLITE_SESSION_CONFIG_STRMSIZE
+** By default, the sessions module streaming interfaces attempt to input +** and output data in approximately 1 KiB chunks. This operand may be used +** to set and query the value of this configuration setting. The pointer +** passed as the second argument must point to a value of type (int). +** If this value is greater than 0, it is used as the new streaming data +** chunk size for both input and output. Before returning, the (int) value +** pointed to by pArg is set to the final value of the streaming interface +** chunk size. +**
+** +** This function returns SQLITE_OK if successful, or an SQLite error code +** otherwise. +*/ +SQLITE_API int sqlite3session_config(int op, void *pArg); + +/* +** CAPI3REF: Values for sqlite3session_config(). +*/ +#define SQLITE_SESSION_CONFIG_STRMSIZE 1 /* ** Make sure we can call this stuff from C++. @@ -15277,9 +15401,6 @@ SQLITE_PRIVATE int sqlite3PagerWalSupported(Pager *pPager); SQLITE_PRIVATE int sqlite3PagerWalCallback(Pager *pPager); SQLITE_PRIVATE int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen); SQLITE_PRIVATE int sqlite3PagerCloseWal(Pager *pPager, sqlite3*); -# ifdef SQLITE_DIRECT_OVERFLOW_READ -SQLITE_PRIVATE int sqlite3PagerUseWal(Pager *pPager, Pgno); -# endif # ifdef SQLITE_ENABLE_SNAPSHOT SQLITE_PRIVATE int sqlite3PagerSnapshotGet(Pager *pPager, sqlite3_snapshot **ppSnapshot); SQLITE_PRIVATE int sqlite3PagerSnapshotOpen(Pager *pPager, sqlite3_snapshot *pSnapshot); @@ -15287,8 +15408,10 @@ SQLITE_PRIVATE int sqlite3PagerSnapshotRecover(Pager *pPager); SQLITE_PRIVATE int sqlite3PagerSnapshotCheck(Pager *pPager, sqlite3_snapshot *pSnapshot); SQLITE_PRIVATE void sqlite3PagerSnapshotUnlock(Pager *pPager); # endif -#else -# define sqlite3PagerUseWal(x,y) 0 +#endif + +#ifdef SQLITE_DIRECT_OVERFLOW_READ +SQLITE_PRIVATE int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno); #endif #ifdef SQLITE_ENABLE_ZIPVFS @@ -15533,6 +15656,10 @@ SQLITE_PRIVATE int sqlite3HeaderSizePcache1(void); /* Number of dirty pages as a percentage of the configured cache size */ SQLITE_PRIVATE int sqlite3PCachePercentDirty(PCache*); +#ifdef SQLITE_DIRECT_OVERFLOW_READ +SQLITE_PRIVATE int sqlite3PCacheIsDirty(PCache *pCache); +#endif + #endif /* _PCACHE_H_ */ /************** End of pcache.h **********************************************/ @@ -16038,12 +16165,14 @@ struct LookasideSlot { ** functions use a regular table table from hash.h.) ** ** Hash each FuncDef structure into one of the FuncDefHash.a[] slots. -** Collisions are on the FuncDef.u.pHash chain. +** Collisions are on the FuncDef.u.pHash chain. Use the SQLITE_FUNC_HASH() +** macro to compute a hash on the function name. */ #define SQLITE_FUNC_HASH_SZ 23 struct FuncDefHash { FuncDef *a[SQLITE_FUNC_HASH_SZ]; /* Hash table for functions */ }; +#define SQLITE_FUNC_HASH(C,L) (((C)+(L))%SQLITE_FUNC_HASH_SZ) #ifdef SQLITE_USER_AUTHENTICATION /* @@ -16104,7 +16233,7 @@ struct sqlite3 { Db *aDb; /* All backends */ int nDb; /* Number of backends currently in use */ u32 mDbFlags; /* flags recording internal state */ - u32 flags; /* flags settable by pragmas. See below */ + u64 flags; /* flags settable by pragmas. See below */ i64 lastRowid; /* ROWID of most recent insert (see above) */ i64 szMmap; /* Default mmap_size setting */ u32 nSchemaLock; /* Do not reset the schema when non-zero */ @@ -16270,14 +16399,17 @@ struct sqlite3 { #define SQLITE_TriggerEQP 0x01000000 /* Show trigger EXPLAIN QUERY PLAN */ #define SQLITE_ResetDatabase 0x02000000 /* Reset the database */ #define SQLITE_LegacyAlter 0x04000000 /* Legacy ALTER TABLE behaviour */ +#define SQLITE_NoSchemaError 0x08000000 /* Do not report schema parse errors*/ +#define SQLITE_Defensive 0x10000000 /* Input SQL is likely hostile */ /* Flags used only if debugging */ +#define HI(X) ((u64)(X)<<32) #ifdef SQLITE_DEBUG -#define SQLITE_SqlTrace 0x08000000 /* Debug print SQL as it executes */ -#define SQLITE_VdbeListing 0x10000000 /* Debug listings of VDBE programs */ -#define SQLITE_VdbeTrace 0x20000000 /* True to trace VDBE execution */ -#define SQLITE_VdbeAddopTrace 0x40000000 /* Trace sqlite3VdbeAddOp() calls */ -#define SQLITE_VdbeEQP 0x80000000 /* Debug EXPLAIN QUERY PLAN */ +#define SQLITE_SqlTrace HI(0x0001) /* Debug print SQL as it executes */ +#define SQLITE_VdbeListing HI(0x0002) /* Debug listings of VDBE progs */ +#define SQLITE_VdbeTrace HI(0x0004) /* True to trace VDBE execution */ +#define SQLITE_VdbeAddopTrace HI(0x0008) /* Trace sqlite3VdbeAddOp() calls */ +#define SQLITE_VdbeEQP HI(0x0010) /* Debug EXPLAIN QUERY PLAN */ #endif /* @@ -16411,8 +16543,9 @@ struct FuncDestructor { ** single query - might change over time */ #define SQLITE_FUNC_AFFINITY 0x4000 /* Built-in affinity() function */ #define SQLITE_FUNC_OFFSET 0x8000 /* Built-in sqlite_offset() function */ -#define SQLITE_FUNC_WINDOW 0x10000 /* Built-in window-only function */ -#define SQLITE_FUNC_WINDOW_SIZE 0x20000 /* Requires partition size as arg. */ +#define SQLITE_FUNC_WINDOW 0x00010000 /* Built-in window-only function */ +#define SQLITE_FUNC_WINDOW_SIZE 0x20000 /* Requires partition size as arg. */ +#define SQLITE_FUNC_INTERNAL 0x00040000 /* For use by NestedParse() only */ /* ** The following three macros, FUNCTION(), LIKEFUNC() and AGGREGATE() are @@ -16488,10 +16621,13 @@ struct FuncDestructor { #define AGGREGATE2(zName, nArg, arg, nc, xStep, xFinal, extraFlags) \ {nArg, SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL)|extraFlags, \ SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,xFinal,0,#zName, {0}} - #define WAGGREGATE(zName, nArg, arg, nc, xStep, xFinal, xValue, xInverse, f) \ {nArg, SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL)|f, \ SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,xValue,xInverse,#zName, {0}} +#define INTERNAL_FUNCTION(zName, nArg, xFunc) \ + {nArg, SQLITE_FUNC_INTERNAL|SQLITE_UTF8|SQLITE_FUNC_CONSTANT, \ + 0, 0, xFunc, 0, 0, 0, #zName, {0} } + /* ** All current savepoints are stored in a linked list starting at @@ -16676,6 +16812,9 @@ struct VTable { struct Table { char *zName; /* Name of the table or view */ Column *aCol; /* Information about each column */ +#ifdef SQLITE_ENABLE_NORMALIZE + Hash *pColHash; /* All columns indexed by name */ +#endif Index *pIndex; /* List of SQL indexes on this table. */ Select *pSelect; /* NULL for tables. Points to definition if a view. */ FKey *pFKey; /* Linked list of all foreign keys in this table */ @@ -16726,6 +16865,7 @@ struct Table { #define TF_StatsUsed 0x0100 /* Query planner decisions affected by ** Index.aiRowLogEst[] values */ #define TF_HasNotNull 0x0200 /* Contains NOT NULL constraints */ +#define TF_Shadow 0x0400 /* True for a shadow table */ /* ** Test to see whether or not a table is a virtual table. This is @@ -17012,6 +17152,12 @@ struct IndexSample { tRowcnt *anDLt; /* Est. number of distinct keys less than this sample */ }; +/* +** Possible values to use within the flags argument to sqlite3GetToken(). +*/ +#define SQLITE_TOKEN_QUOTED 0x1 /* Token is a quoted identifier. */ +#define SQLITE_TOKEN_KEYWORD 0x2 /* Token is a keyword. */ + /* ** Each token coming out of the lexer is an instance of ** this structure. Tokens are also used as part of an expression. @@ -17193,11 +17339,11 @@ struct Expr { ** TK_COLUMN: the value of p5 for OP_Column ** TK_AGG_FUNCTION: nesting depth */ AggInfo *pAggInfo; /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */ - Table *pTab; /* Table for TK_COLUMN expressions. Can be NULL - ** for a column of an index on an expression */ -#ifndef SQLITE_OMIT_WINDOWFUNC - Window *pWin; /* Window definition for window functions */ -#endif + union { + Table *pTab; /* TK_COLUMN: Table containing column. Can be NULL + ** for a column of an index on an expression */ + Window *pWin; /* TK_FUNCTION: Window definition for the func */ + } y; }; /* @@ -17227,6 +17373,7 @@ struct Expr { #define EP_Subquery 0x200000 /* Tree contains a TK_SELECT operator */ #define EP_Alias 0x400000 /* Is an alias for a result set column */ #define EP_Leaf 0x800000 /* Expr.pLeft, .pRight, .u.pSelect all NULL */ +#define EP_WinFunc 0x1000000 /* TK_FUNCTION with Expr.y.pWin set */ /* ** The EP_Propagate mask is a set of properties that automatically propagate @@ -17912,6 +18059,7 @@ struct AuthContext { */ #define OPFLAG_NCHANGE 0x01 /* OP_Insert: Set to update db->nChange */ /* Also used in P2 (not P5) of OP_Delete */ +#define OPFLAG_NOCHNG 0x01 /* OP_VColumn nochange for UPDATE */ #define OPFLAG_EPHEM 0x01 /* OP_Column: Ephemeral output is ok */ #define OPFLAG_LASTROWID 0x20 /* Set to update db->lastRowid */ #define OPFLAG_ISUPDATE 0x04 /* This OP_Insert is an sql UPDATE */ @@ -18130,6 +18278,7 @@ struct Sqlite3Config { int (*xTestCallback)(int); /* Invoked by sqlite3FaultSim() */ #endif int bLocaltimeFault; /* True to fail localtime() calls */ + int bInternalFunctions; /* Internal SQL functions are visible */ int iOnceResetThreshold; /* When to reset OP_Once counters */ u32 szSorterRef; /* Min size in bytes to use sorter-refs */ }; @@ -18383,6 +18532,7 @@ SQLITE_PRIVATE int sqlite3IsIdChar(u8); */ SQLITE_PRIVATE int sqlite3StrICmp(const char*,const char*); SQLITE_PRIVATE int sqlite3Strlen30(const char*); +#define sqlite3Strlen30NN(C) (strlen(C)&0x3fffffff) SQLITE_PRIVATE char *sqlite3ColumnType(Column*,char*); #define sqlite3StrNICmp sqlite3_strnicmp @@ -18499,6 +18649,7 @@ SQLITE_PRIVATE void *sqlite3TestTextToPtr(const char*); SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView*, const Expr*, u8); SQLITE_PRIVATE void sqlite3TreeViewBareExprList(TreeView*, const ExprList*, const char*); SQLITE_PRIVATE void sqlite3TreeViewExprList(TreeView*, const ExprList*, u8, const char*); +SQLITE_PRIVATE void sqlite3TreeViewSrcList(TreeView*, const SrcList*); SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView*, const Select*, u8); SQLITE_PRIVATE void sqlite3TreeViewWith(TreeView*, const With*, u8); #ifndef SQLITE_OMIT_WINDOWFUNC @@ -18731,11 +18882,15 @@ SQLITE_PRIVATE int sqlite3ExprIsInteger(Expr*, int*); SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr*); SQLITE_PRIVATE int sqlite3ExprNeedsNoAffinityChange(const Expr*, char); SQLITE_PRIVATE int sqlite3IsRowid(const char*); +#ifdef SQLITE_ENABLE_NORMALIZE +SQLITE_PRIVATE int sqlite3IsRowidN(const char*, int); +#endif SQLITE_PRIVATE void sqlite3GenerateRowDelete( Parse*,Table*,Trigger*,int,int,int,i16,u8,u8,u8,int); SQLITE_PRIVATE void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int, int*, int); SQLITE_PRIVATE int sqlite3GenerateIndexKey(Parse*, Index*, int, int, int, int*,Index*,int); SQLITE_PRIVATE void sqlite3ResolvePartIdxLabel(Parse*,int); +SQLITE_PRIVATE int sqlite3ExprReferencesUpdatedColumn(Expr*,int*,int); SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(Parse*,Table*,int*,int,int,int,int, u8,u8,int,int*,int*,Upsert*); #ifdef SQLITE_ENABLE_NULL_TRIM @@ -18756,6 +18911,9 @@ SQLITE_PRIVATE ExprList *sqlite3ExprListDup(sqlite3*,ExprList*,int); SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3*,SrcList*,int); SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3*,IdList*); SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3*,Select*,int); +#ifdef SQLITE_ENABLE_NORMALIZE +SQLITE_PRIVATE FuncDef *sqlite3FunctionSearchN(int,const char*,int); +#endif SQLITE_PRIVATE void sqlite3InsertBuiltinFuncs(FuncDef*,int); SQLITE_PRIVATE FuncDef *sqlite3FindFunction(sqlite3*,const char*,int,u8,u8); SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void); @@ -18913,6 +19071,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprAddCollateToken(Parse *pParse, Expr*, const Toke SQLITE_PRIVATE Expr *sqlite3ExprAddCollateString(Parse*,Expr*,const char*); SQLITE_PRIVATE Expr *sqlite3ExprSkipCollate(Expr*); SQLITE_PRIVATE int sqlite3CheckCollSeq(Parse *, CollSeq *); +SQLITE_PRIVATE int sqlite3WritableSchema(sqlite3*); SQLITE_PRIVATE int sqlite3CheckObjectName(Parse *, const char *); SQLITE_PRIVATE void sqlite3VdbeSetChanges(sqlite3 *, int); SQLITE_PRIVATE int sqlite3AddInt64(i64*,i64); @@ -18959,6 +19118,9 @@ SQLITE_PRIVATE void sqlite3AlterFunctions(void); SQLITE_PRIVATE void sqlite3AlterRenameTable(Parse*, SrcList*, Token*); SQLITE_PRIVATE void sqlite3AlterRenameColumn(Parse*, SrcList*, Token*, Token*); SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *, int *); +#ifdef SQLITE_ENABLE_NORMALIZE +SQLITE_PRIVATE int sqlite3GetTokenNormalized(const unsigned char *, int *, int *); +#endif SQLITE_PRIVATE void sqlite3NestedParse(Parse*, const char*, ...); SQLITE_PRIVATE void sqlite3ExpirePreparedStatements(sqlite3*, int); SQLITE_PRIVATE int sqlite3CodeSubselect(Parse*, Expr *, int, int); @@ -19116,6 +19278,9 @@ SQLITE_PRIVATE sqlite3_int64 sqlite3StmtCurrentTime(sqlite3_context*); SQLITE_PRIVATE int sqlite3VdbeParameterIndex(Vdbe*, const char*, int); SQLITE_PRIVATE int sqlite3TransferBindings(sqlite3_stmt *, sqlite3_stmt *); SQLITE_PRIVATE void sqlite3ParserReset(Parse*); +#ifdef SQLITE_ENABLE_NORMALIZE +SQLITE_PRIVATE void sqlite3Normalize(Vdbe*, const char*, int, u8); +#endif SQLITE_PRIVATE int sqlite3Reprepare(Vdbe*); SQLITE_PRIVATE void sqlite3ExprListCheckLength(Parse*, ExprList*, const char*); SQLITE_PRIVATE CollSeq *sqlite3BinaryCompareCollSeq(Parse *, Expr *, Expr *); @@ -19577,6 +19742,7 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = { 0, /* xTestCallback */ #endif 0, /* bLocaltimeFault */ + 0, /* bInternalFunctions */ 0x7ffffffe, /* iOnceResetThreshold */ SQLITE_DEFAULT_SORTERREF_SIZE /* szSorterRef */ }; @@ -20068,6 +20234,9 @@ struct Vdbe { yDbMask lockMask; /* Subset of btreeMask that requires a lock */ u32 aCounter[7]; /* Counters used by sqlite3_stmt_status() */ char *zSql; /* Text of the SQL statement that generated this */ +#ifdef SQLITE_ENABLE_NORMALIZE + char *zNormSql; /* Normalization of the associated SQL statement */ +#endif void *pFree; /* Free this when deleting the vdbe */ VdbeFrame *pFrame; /* Parent frame */ VdbeFrame *pDelFrame; /* List of frame objects to free on VM reset */ @@ -20130,7 +20299,9 @@ int sqlite2BtreeKeyCompare(BtCursor *, const void *, int, int, int *); SQLITE_PRIVATE int sqlite3VdbeIdxKeyCompare(sqlite3*,VdbeCursor*,UnpackedRecord*,int*); SQLITE_PRIVATE int sqlite3VdbeIdxRowid(sqlite3*, BtCursor*, i64*); SQLITE_PRIVATE int sqlite3VdbeExec(Vdbe*); +#ifndef SQLITE_OMIT_EXPLAIN SQLITE_PRIVATE int sqlite3VdbeList(Vdbe*); +#endif SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe*); SQLITE_PRIVATE int sqlite3VdbeChangeEncoding(Mem *, int); SQLITE_PRIVATE int sqlite3VdbeMemTooBig(Mem*); @@ -20169,7 +20340,9 @@ SQLITE_PRIVATE int sqlite3VdbeMemFinalize(Mem*, FuncDef*); #ifndef SQLITE_OMIT_WINDOWFUNC SQLITE_PRIVATE int sqlite3VdbeMemAggValue(Mem*, Mem*, FuncDef*); #endif +#ifndef SQLITE_OMIT_EXPLAIN SQLITE_PRIVATE const char *sqlite3OpcodeName(int); +#endif SQLITE_PRIVATE int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve); SQLITE_PRIVATE int sqlite3VdbeMemClearAndResize(Mem *pMem, int n); SQLITE_PRIVATE int sqlite3VdbeCloseStatement(Vdbe *, int); @@ -28296,6 +28469,42 @@ SQLITE_PRIVATE void sqlite3TreeViewWith(TreeView *pView, const With *pWith, u8 m } } +/* +** Generate a human-readable description of a SrcList object. +*/ +SQLITE_PRIVATE void sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc){ + int i; + for(i=0; inSrc; i++){ + const struct SrcList_item *pItem = &pSrc->a[i]; + StrAccum x; + char zLine[100]; + sqlite3StrAccumInit(&x, 0, zLine, sizeof(zLine), 0); + sqlite3_str_appendf(&x, "{%d,*}", pItem->iCursor); + if( pItem->zDatabase ){ + sqlite3_str_appendf(&x, " %s.%s", pItem->zDatabase, pItem->zName); + }else if( pItem->zName ){ + sqlite3_str_appendf(&x, " %s", pItem->zName); + } + if( pItem->pTab ){ + sqlite3_str_appendf(&x, " tabname=%Q", pItem->pTab->zName); + } + if( pItem->zAlias ){ + sqlite3_str_appendf(&x, " (AS %s)", pItem->zAlias); + } + if( pItem->fg.jointype & JT_LEFT ){ + sqlite3_str_appendf(&x, " LEFT-JOIN"); + } + sqlite3StrAccumFinish(&x); + sqlite3TreeViewItem(pView, zLine, inSrc-1); + if( pItem->pSelect ){ + sqlite3TreeViewSelect(pView, pItem->pSelect, 0); + } + if( pItem->fg.isTabFunc ){ + sqlite3TreeViewExprList(pView, pItem->u1.pFuncArg, 0, "func-args:"); + } + sqlite3TreeViewPop(pView); + } +} /* ** Generate a human-readable description of a Select object. @@ -28350,39 +28559,9 @@ SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 m } #endif if( p->pSrc && p->pSrc->nSrc ){ - int i; pView = sqlite3TreeViewPush(pView, (n--)>0); sqlite3TreeViewLine(pView, "FROM"); - for(i=0; ipSrc->nSrc; i++){ - struct SrcList_item *pItem = &p->pSrc->a[i]; - StrAccum x; - char zLine[100]; - sqlite3StrAccumInit(&x, 0, zLine, sizeof(zLine), 0); - sqlite3_str_appendf(&x, "{%d,*}", pItem->iCursor); - if( pItem->zDatabase ){ - sqlite3_str_appendf(&x, " %s.%s", pItem->zDatabase, pItem->zName); - }else if( pItem->zName ){ - sqlite3_str_appendf(&x, " %s", pItem->zName); - } - if( pItem->pTab ){ - sqlite3_str_appendf(&x, " tabname=%Q", pItem->pTab->zName); - } - if( pItem->zAlias ){ - sqlite3_str_appendf(&x, " (AS %s)", pItem->zAlias); - } - if( pItem->fg.jointype & JT_LEFT ){ - sqlite3_str_appendf(&x, " LEFT-JOIN"); - } - sqlite3StrAccumFinish(&x); - sqlite3TreeViewItem(pView, zLine, ipSrc->nSrc-1); - if( pItem->pSelect ){ - sqlite3TreeViewSelect(pView, pItem->pSelect, 0); - } - if( pItem->fg.isTabFunc ){ - sqlite3TreeViewExprList(pView, pItem->u1.pFuncArg, 0, "func-args:"); - } - sqlite3TreeViewPop(pView); - } + sqlite3TreeViewSrcList(pView, p->pSrc); sqlite3TreeViewPop(pView); } if( p->pWhere ){ @@ -28672,7 +28851,7 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m }else{ pFarg = pExpr->x.pList; #ifndef SQLITE_OMIT_WINDOWFUNC - pWin = pExpr->pWin; + pWin = pExpr->y.pWin; #else pWin = 0; #endif @@ -31500,6 +31679,20 @@ static unsigned int strHash(const char *z){ } return h; } +#ifdef SQLITE_ENABLE_NORMALIZE +static unsigned int strHashN(const char *z, int n){ + unsigned int h = 0; + int i; + for(i=0; iht ){ /*OPTIMIZATION-IF-TRUE*/ + struct _ht *pEntry; + h = strHashN(pKey, nKey) % pH->htsize; + pEntry = &pH->ht[h]; + elem = pEntry->chain; + count = pEntry->count; + }else{ + h = 0; + elem = pH->first; + count = pH->count; + } + if( pHash ) *pHash = h; + while( count-- ){ + assert( elem!=0 ); + if( sqlite3StrNICmp(elem->pKey,pKey,nKey)==0 ){ + return elem; + } + elem = elem->next; + } + return &nullElement; +} +#endif /* SQLITE_ENABLE_NORMALIZE */ /* Remove a single entry from the hash table given a pointer to that ** element and a hash on the element's key. @@ -31655,6 +31882,14 @@ SQLITE_PRIVATE void *sqlite3HashFind(const Hash *pH, const char *pKey){ assert( pKey!=0 ); return findElementWithHash(pH, pKey, 0)->data; } +#ifdef SQLITE_ENABLE_NORMALIZE +SQLITE_PRIVATE void *sqlite3HashFindN(const Hash *pH, const char *pKey, int nKey){ + assert( pH!=0 ); + assert( pKey!=0 ); + assert( nKey>=0 ); + return findElementWithHashN(pH, pKey, nKey, 0)->data; +} +#endif /* SQLITE_ENABLE_NORMALIZE */ /* Insert an element into the hash table pH. The key is pKey ** and the data is "data". @@ -32037,12 +32272,10 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ #define SQLITE_FSFLAGS_IS_MSDOS 0x1 /* -** If we are to be thread-safe, include the pthreads header and define -** the SQLITE_UNIX_THREADS macro. +** If we are to be thread-safe, include the pthreads header. */ #if SQLITE_THREADSAFE /* # include */ -# define SQLITE_UNIX_THREADS 1 #endif /* @@ -33218,8 +33451,7 @@ struct unixFileId { /* ** An instance of the following structure is allocated for each open -** inode. Or, on LinuxThreads, there is one of these structures for -** each inode opened by each thread. +** inode. ** ** A single inode can have multiple file descriptors, so each unixFile ** structure contains a pointer to an instance of this object and this @@ -33265,13 +33497,16 @@ struct unixInodeInfo { /* ** A lists of all unixInodeInfo objects. +** +** Must hold unixBigLock in order to read or write this variable. */ static unixInodeInfo *inodeList = 0; /* All unixInodeInfo objects */ #ifdef SQLITE_DEBUG /* -** True if the inode mutex is held, or not. Used only within assert() -** to help verify correct mutex usage. +** True if the inode mutex (on the unixFile.pFileMutex field) is held, or not. +** This routine is used only within assert() to help verify correct mutex +** usage. */ int unixFileMutexHeld(unixFile *pFile){ assert( pFile->pInode ); @@ -33399,8 +33634,8 @@ static void closePendingFds(unixFile *pFile){ /* ** Release a unixInodeInfo structure previously allocated by findInodeInfo(). ** -** The mutex entered using the unixEnterMutex() function must be held -** when this function is called. +** The global mutex must be held when this routine is called, but the mutex +** on the inode being deleted must NOT be held. */ static void releaseInodeInfo(unixFile *pFile){ unixInodeInfo *pInode = pFile->pInode; @@ -33435,8 +33670,7 @@ static void releaseInodeInfo(unixFile *pFile){ ** describes that file descriptor. Create a new one if necessary. The ** return value might be uninitialized if an error occurs. ** -** The mutex entered using the unixEnterMutex() function must be held -** when this function is called. +** The global mutex must held when calling this routine. ** ** Return an appropriate error code. */ @@ -33497,6 +33731,7 @@ static int findInodeInfo( #else fileId.ino = (u64)statbuf.st_ino; #endif + assert( unixMutexHeld() ); pInode = inodeList; while( pInode && memcmp(&fileId, &pInode->fileId, sizeof(fileId)) ){ pInode = pInode->pNext; @@ -33516,6 +33751,7 @@ static int findInodeInfo( } } pInode->nRef = 1; + assert( unixMutexHeld() ); pInode->pNext = inodeList; pInode->pPrev = 0; if( inodeList ) inodeList->pPrev = pInode; @@ -36313,18 +36549,18 @@ static int unixGetpagesize(void){ ** ** The following fields are read-only after the object is created: ** -** fid +** hShm ** zFilename ** -** Either unixShmNode.mutex must be held or unixShmNode.nRef==0 and +** Either unixShmNode.pShmMutex must be held or unixShmNode.nRef==0 and ** unixMutexHeld() is true when reading or writing any other field ** in this structure. */ struct unixShmNode { unixInodeInfo *pInode; /* unixInodeInfo that owns this SHM node */ - sqlite3_mutex *mutex; /* Mutex to access this object */ + sqlite3_mutex *pShmMutex; /* Mutex to access this object */ char *zFilename; /* Name of the mmapped file */ - int h; /* Open file descriptor */ + int hShm; /* Open file descriptor */ int szRegion; /* Size of shared-memory regions */ u16 nRegion; /* Size of array apRegion */ u8 isReadonly; /* True if read-only */ @@ -36346,16 +36582,16 @@ struct unixShmNode { ** The following fields are initialized when this object is created and ** are read-only thereafter: ** -** unixShm.pFile +** unixShm.pShmNode ** unixShm.id ** -** All other fields are read/write. The unixShm.pFile->mutex must be held -** while accessing any read/write fields. +** All other fields are read/write. The unixShm.pShmNode->pShmMutex must +** be held while accessing any read/write fields. */ struct unixShm { unixShmNode *pShmNode; /* The underlying unixShmNode object */ unixShm *pNext; /* Next unixShm with the same unixShmNode */ - u8 hasMutex; /* True if holding the unixShmNode mutex */ + u8 hasMutex; /* True if holding the unixShmNode->pShmMutex */ u8 id; /* Id of this connection within its unixShmNode */ u16 sharedMask; /* Mask of shared locks held */ u16 exclMask; /* Mask of exclusive locks held */ @@ -36385,7 +36621,8 @@ static int unixShmSystemLock( /* Access to the unixShmNode object is serialized by the caller */ pShmNode = pFile->pInode->pShmNode; - assert( pShmNode->nRef==0 || sqlite3_mutex_held(pShmNode->mutex) ); + assert( pShmNode->nRef==0 || sqlite3_mutex_held(pShmNode->pShmMutex) ); + assert( pShmNode->nRef>0 || unixMutexHeld() ); /* Shared locks never span more than one byte */ assert( n==1 || lockType!=F_RDLCK ); @@ -36393,13 +36630,13 @@ static int unixShmSystemLock( /* Locks are within range */ assert( n>=1 && n<=SQLITE_SHM_NLOCK ); - if( pShmNode->h>=0 ){ + if( pShmNode->hShm>=0 ){ /* Initialize the locking parameters */ f.l_type = lockType; f.l_whence = SEEK_SET; f.l_start = ofst; f.l_len = n; - rc = osSetPosixAdvisoryLock(pShmNode->h, &f, pFile); + rc = osSetPosixAdvisoryLock(pShmNode->hShm, &f, pFile); rc = (rc!=(-1)) ? SQLITE_OK : SQLITE_BUSY; } @@ -36471,18 +36708,18 @@ static void unixShmPurge(unixFile *pFd){ int nShmPerMap = unixShmRegionPerMap(); int i; assert( p->pInode==pFd->pInode ); - sqlite3_mutex_free(p->mutex); + sqlite3_mutex_free(p->pShmMutex); for(i=0; inRegion; i+=nShmPerMap){ - if( p->h>=0 ){ + if( p->hShm>=0 ){ osMunmap(p->apRegion[i], p->szRegion); }else{ sqlite3_free(p->apRegion[i]); } } sqlite3_free(p->apRegion); - if( p->h>=0 ){ - robust_close(pFd, p->h, __LINE__); - p->h = -1; + if( p->hShm>=0 ){ + robust_close(pFd, p->hShm, __LINE__); + p->hShm = -1; } p->pInode->pShmNode = 0; sqlite3_free(p); @@ -36524,7 +36761,7 @@ static int unixLockSharedMemory(unixFile *pDbFd, unixShmNode *pShmNode){ lock.l_start = UNIX_SHM_DMS; lock.l_len = 1; lock.l_type = F_WRLCK; - if( osFcntl(pShmNode->h, F_GETLK, &lock)!=0 ) { + if( osFcntl(pShmNode->hShm, F_GETLK, &lock)!=0 ) { rc = SQLITE_IOERR_LOCK; }else if( lock.l_type==F_UNLCK ){ if( pShmNode->isReadonly ){ @@ -36532,7 +36769,12 @@ static int unixLockSharedMemory(unixFile *pDbFd, unixShmNode *pShmNode){ rc = SQLITE_READONLY_CANTINIT; }else{ rc = unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1); - if( rc==SQLITE_OK && robust_ftruncate(pShmNode->h, 0) ){ + /* The first connection to attach must truncate the -shm file. We + ** truncate to 3 bytes (an arbitrary small number, less than the + ** -shm header size) rather than 0 as a system debugging aid, to + ** help detect if a -shm file truncation is legitimate or is the work + ** or a rogue process. */ + if( rc==SQLITE_OK && robust_ftruncate(pShmNode->hShm, 3) ){ rc = unixLogError(SQLITE_IOERR_SHMOPEN,"ftruncate",pShmNode->zFilename); } } @@ -36638,12 +36880,12 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ sqlite3_snprintf(nShmFilename, zShm, "%s-shm", zBasePath); sqlite3FileSuffix3(pDbFd->zPath, zShm); #endif - pShmNode->h = -1; + pShmNode->hShm = -1; pDbFd->pInode->pShmNode = pShmNode; pShmNode->pInode = pDbFd->pInode; if( sqlite3GlobalConfig.bCoreMutex ){ - pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); - if( pShmNode->mutex==0 ){ + pShmNode->pShmMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); + if( pShmNode->pShmMutex==0 ){ rc = SQLITE_NOMEM_BKPT; goto shm_open_err; } @@ -36651,11 +36893,11 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ if( pInode->bProcessLock==0 ){ if( 0==sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0) ){ - pShmNode->h = robust_open(zShm, O_RDWR|O_CREAT, (sStat.st_mode&0777)); + pShmNode->hShm = robust_open(zShm, O_RDWR|O_CREAT,(sStat.st_mode&0777)); } - if( pShmNode->h<0 ){ - pShmNode->h = robust_open(zShm, O_RDONLY, (sStat.st_mode&0777)); - if( pShmNode->h<0 ){ + if( pShmNode->hShm<0 ){ + pShmNode->hShm = robust_open(zShm, O_RDONLY, (sStat.st_mode&0777)); + if( pShmNode->hShm<0 ){ rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zShm); goto shm_open_err; } @@ -36666,7 +36908,7 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ ** is owned by the same user that owns the original database. Otherwise, ** the original owner will not be able to connect. */ - robustFchown(pShmNode->h, sStat.st_uid, sStat.st_gid); + robustFchown(pShmNode->hShm, sStat.st_uid, sStat.st_gid); rc = unixLockSharedMemory(pDbFd, pShmNode); if( rc!=SQLITE_OK && rc!=SQLITE_READONLY_CANTINIT ) goto shm_open_err; @@ -36686,13 +36928,13 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ ** the cover of the unixEnterMutex() mutex and the pointer from the ** new (struct unixShm) object to the pShmNode has been set. All that is ** left to do is to link the new object into the linked list starting - ** at pShmNode->pFirst. This must be done while holding the pShmNode->mutex - ** mutex. + ** at pShmNode->pFirst. This must be done while holding the + ** pShmNode->pShmMutex. */ - sqlite3_mutex_enter(pShmNode->mutex); + sqlite3_mutex_enter(pShmNode->pShmMutex); p->pNext = pShmNode->pFirst; pShmNode->pFirst = p; - sqlite3_mutex_leave(pShmNode->mutex); + sqlite3_mutex_leave(pShmNode->pShmMutex); return rc; /* Jump here on any error */ @@ -36744,7 +36986,7 @@ static int unixShmMap( p = pDbFd->pShm; pShmNode = p->pShmNode; - sqlite3_mutex_enter(pShmNode->mutex); + sqlite3_mutex_enter(pShmNode->pShmMutex); if( pShmNode->isUnlocked ){ rc = unixLockSharedMemory(pDbFd, pShmNode); if( rc!=SQLITE_OK ) goto shmpage_out; @@ -36752,8 +36994,8 @@ static int unixShmMap( } assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 ); assert( pShmNode->pInode==pDbFd->pInode ); - assert( pShmNode->h>=0 || pDbFd->pInode->bProcessLock==1 ); - assert( pShmNode->h<0 || pDbFd->pInode->bProcessLock==0 ); + assert( pShmNode->hShm>=0 || pDbFd->pInode->bProcessLock==1 ); + assert( pShmNode->hShm<0 || pDbFd->pInode->bProcessLock==0 ); /* Minimum number of regions required to be mapped. */ nReqRegion = ((iRegion+nShmPerMap) / nShmPerMap) * nShmPerMap; @@ -36765,12 +37007,12 @@ static int unixShmMap( pShmNode->szRegion = szRegion; - if( pShmNode->h>=0 ){ + if( pShmNode->hShm>=0 ){ /* The requested region is not mapped into this processes address space. ** Check to see if it has been allocated (i.e. if the wal-index file is ** large enough to contain the requested region). */ - if( osFstat(pShmNode->h, &sStat) ){ + if( osFstat(pShmNode->hShm, &sStat) ){ rc = SQLITE_IOERR_SHMSIZE; goto shmpage_out; } @@ -36798,7 +37040,7 @@ static int unixShmMap( assert( (nByte % pgsz)==0 ); for(iPg=(sStat.st_size/pgsz); iPg<(nByte/pgsz); iPg++){ int x = 0; - if( seekAndWriteFd(pShmNode->h, iPg*pgsz + pgsz-1, "", 1, &x)!=1 ){ + if( seekAndWriteFd(pShmNode->hShm, iPg*pgsz + pgsz-1,"",1,&x)!=1 ){ const char *zFile = pShmNode->zFilename; rc = unixLogError(SQLITE_IOERR_SHMSIZE, "write", zFile); goto shmpage_out; @@ -36821,22 +37063,22 @@ static int unixShmMap( int nMap = szRegion*nShmPerMap; int i; void *pMem; - if( pShmNode->h>=0 ){ + if( pShmNode->hShm>=0 ){ pMem = osMmap(0, nMap, pShmNode->isReadonly ? PROT_READ : PROT_READ|PROT_WRITE, - MAP_SHARED, pShmNode->h, szRegion*(i64)pShmNode->nRegion + MAP_SHARED, pShmNode->hShm, szRegion*(i64)pShmNode->nRegion ); if( pMem==MAP_FAILED ){ rc = unixLogError(SQLITE_IOERR_SHMMAP, "mmap", pShmNode->zFilename); goto shmpage_out; } }else{ - pMem = sqlite3_malloc64(szRegion); + pMem = sqlite3_malloc64(nMap); if( pMem==0 ){ rc = SQLITE_NOMEM_BKPT; goto shmpage_out; } - memset(pMem, 0, szRegion); + memset(pMem, 0, nMap); } for(i=0; iisReadonly && rc==SQLITE_OK ) rc = SQLITE_READONLY; - sqlite3_mutex_leave(pShmNode->mutex); + sqlite3_mutex_leave(pShmNode->pShmMutex); return rc; } @@ -36887,12 +37129,12 @@ static int unixShmLock( || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED) || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) ); assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 ); - assert( pShmNode->h>=0 || pDbFd->pInode->bProcessLock==1 ); - assert( pShmNode->h<0 || pDbFd->pInode->bProcessLock==0 ); + assert( pShmNode->hShm>=0 || pDbFd->pInode->bProcessLock==1 ); + assert( pShmNode->hShm<0 || pDbFd->pInode->bProcessLock==0 ); mask = (1<<(ofst+n)) - (1<1 || mask==(1<mutex); + sqlite3_mutex_enter(pShmNode->pShmMutex); if( flags & SQLITE_SHM_UNLOCK ){ u16 allMask = 0; /* Mask of locks held by siblings */ @@ -36965,7 +37207,7 @@ static int unixShmLock( } } } - sqlite3_mutex_leave(pShmNode->mutex); + sqlite3_mutex_leave(pShmNode->pShmMutex); OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x\n", p->id, osGetpid(0), p->sharedMask, p->exclMask)); return rc; @@ -37015,14 +37257,14 @@ static int unixShmUnmap( /* Remove connection p from the set of connections associated ** with pShmNode */ - sqlite3_mutex_enter(pShmNode->mutex); + sqlite3_mutex_enter(pShmNode->pShmMutex); for(pp=&pShmNode->pFirst; (*pp)!=p; pp = &(*pp)->pNext){} *pp = p->pNext; /* Free the connection p */ sqlite3_free(p); pDbFd->pShm = 0; - sqlite3_mutex_leave(pShmNode->mutex); + sqlite3_mutex_leave(pShmNode->pShmMutex); /* If pShmNode->nRef has reached 0, then close the underlying ** shared-memory file, too */ @@ -37031,7 +37273,7 @@ static int unixShmUnmap( assert( pShmNode->nRef>0 ); pShmNode->nRef--; if( pShmNode->nRef==0 ){ - if( deleteFlag && pShmNode->h>=0 ){ + if( deleteFlag && pShmNode->hShm>=0 ){ osUnlink(pShmNode->zFilename); } unixShmPurge(pDbFd); @@ -40449,8 +40691,7 @@ struct winFile { int nFetchOut; /* Number of outstanding xFetch references */ HANDLE hMap; /* Handle for accessing memory mapping */ void *pMapRegion; /* Area memory mapped */ - sqlite3_int64 mmapSize; /* Usable size of mapped region */ - sqlite3_int64 mmapSizeActual; /* Actual size of mapped region */ + sqlite3_int64 mmapSize; /* Size of mapped region */ sqlite3_int64 mmapSizeMax; /* Configured FCNTL_MMAP_SIZE value */ #endif }; @@ -43071,6 +43312,26 @@ static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){ DWORD lastErrno; #if SQLITE_MAX_MMAP_SIZE>0 sqlite3_int64 oldMmapSize; + if( pFile->nFetchOut>0 ){ + /* File truncation is a no-op if there are outstanding memory mapped + ** pages. This is because truncating the file means temporarily unmapping + ** the file, and that might delete memory out from under existing cursors. + ** + ** This can result in incremental vacuum not truncating the file, + ** if there is an active read cursor when the incremental vacuum occurs. + ** No real harm comes of this - the database file is not corrupted, + ** though some folks might complain that the file is bigger than it + ** needs to be. + ** + ** The only feasible work-around is to defer the truncation until after + ** all references to memory-mapped content are closed. That is doable, + ** but involves adding a few branches in the common write code path which + ** could slow down normal operations slightly. Hence, we have decided for + ** now to simply make trancations a no-op if there are pending reads. We + ** can maybe revisit this decision in the future. + */ + return SQLITE_OK; + } #endif assert( pFile ); @@ -44499,9 +44760,9 @@ shmpage_out: static int winUnmapfile(winFile *pFile){ assert( pFile!=0 ); OSTRACE(("UNMAP-FILE pid=%lu, pFile=%p, hMap=%p, pMapRegion=%p, " - "mmapSize=%lld, mmapSizeActual=%lld, mmapSizeMax=%lld\n", + "mmapSize=%lld, mmapSizeMax=%lld\n", osGetCurrentProcessId(), pFile, pFile->hMap, pFile->pMapRegion, - pFile->mmapSize, pFile->mmapSizeActual, pFile->mmapSizeMax)); + pFile->mmapSize, pFile->mmapSizeMax)); if( pFile->pMapRegion ){ if( !osUnmapViewOfFile(pFile->pMapRegion) ){ pFile->lastErrno = osGetLastError(); @@ -44513,7 +44774,6 @@ static int winUnmapfile(winFile *pFile){ } pFile->pMapRegion = 0; pFile->mmapSize = 0; - pFile->mmapSizeActual = 0; } if( pFile->hMap!=NULL ){ if( !osCloseHandle(pFile->hMap) ){ @@ -44624,7 +44884,6 @@ static int winMapfile(winFile *pFd, sqlite3_int64 nByte){ } pFd->pMapRegion = pNew; pFd->mmapSize = nMap; - pFd->mmapSizeActual = nMap; } OSTRACE(("MAP-FILE pid=%lu, pFile=%p, rc=SQLITE_OK\n", @@ -45426,7 +45685,6 @@ static int winOpen( pFile->hMap = NULL; pFile->pMapRegion = 0; pFile->mmapSize = 0; - pFile->mmapSizeActual = 0; pFile->mmapSizeMax = sqlite3GlobalConfig.szMmap; #endif @@ -47323,7 +47581,7 @@ bitvec_end: ** The PCache.pSynced variable is used to optimize searching for a dirty ** page to eject from the cache mid-transaction. It is better to eject ** a page that does not require a journal sync than one that does. -** Therefore, pSynced is maintained to that it *almost* always points +** Therefore, pSynced is maintained so that it *almost* always points ** to either the oldest page in the pDirty/pDirtyTail list that has a ** clear PGHDR_NEED_SYNC flag or to a page that is older than this one ** (so that the right page to eject can be found by following pDirtyPrev @@ -48147,6 +48405,15 @@ SQLITE_PRIVATE int sqlite3PCachePercentDirty(PCache *pCache){ return nCache ? (int)(((i64)nDirty * 100) / nCache) : 0; } +#ifdef SQLITE_DIRECT_OVERFLOW_READ +/* +** Return true if there are one or more dirty pages in the cache. Else false. +*/ +SQLITE_PRIVATE int sqlite3PCacheIsDirty(PCache *pCache){ + return (pCache->pDirty!=0); +} +#endif + #if defined(SQLITE_CHECK_PAGES) || defined(SQLITE_DEBUG) /* ** For all dirty pages currently in the cache, invoke the specified @@ -48270,7 +48537,8 @@ struct PgHdr1 { }; /* -** A page is pinned if it is no on the LRU list +** A page is pinned if it is not on the LRU list. To be "pinned" means +** that the page is in active use and must not be deallocated. */ #define PAGE_IS_PINNED(p) ((p)->pLruNext==0) #define PAGE_IS_UNPINNED(p) ((p)->pLruNext!=0) @@ -50910,19 +51178,30 @@ static const unsigned char aJournalMagic[] = { */ #define isOpen(pFd) ((pFd)->pMethods!=0) +#ifdef SQLITE_DIRECT_OVERFLOW_READ /* -** Return true if this pager uses a write-ahead log to read page pgno. -** Return false if the pager reads pgno directly from the database. +** Return true if page pgno can be read directly from the database file +** by the b-tree layer. This is the case if: +** +** * the database file is open, +** * there are no dirty pages in the cache, and +** * the desired page is not currently in the wal file. */ -#if !defined(SQLITE_OMIT_WAL) && defined(SQLITE_DIRECT_OVERFLOW_READ) -SQLITE_PRIVATE int sqlite3PagerUseWal(Pager *pPager, Pgno pgno){ - u32 iRead = 0; - int rc; - if( pPager->pWal==0 ) return 0; - rc = sqlite3WalFindFrame(pPager->pWal, pgno, &iRead); - return rc || iRead; +SQLITE_PRIVATE int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno){ + if( pPager->fd->pMethods==0 ) return 0; + if( sqlite3PCacheIsDirty(pPager->pPCache) ) return 0; +#ifndef SQLITE_OMIT_WAL + if( pPager->pWal ){ + u32 iRead = 0; + int rc; + rc = sqlite3WalFindFrame(pPager->pWal, pgno, &iRead); + return (rc==SQLITE_OK && iRead==0); + } +#endif + return 1; } #endif + #ifndef SQLITE_OMIT_WAL # define pagerUseWal(x) ((x)->pWal!=0) #else @@ -57106,7 +57385,11 @@ SQLITE_PRIVATE void sqlite3PagerSetCodec( void (*xCodecFree)(void*), void *pCodec ){ - if( pPager->xCodecFree ) pPager->xCodecFree(pPager->pCodec); + if( pPager->xCodecFree ){ + pPager->xCodecFree(pPager->pCodec); + }else{ + pager_reset(pPager); + } pPager->xCodec = pPager->memDb ? 0 : xCodec; pPager->xCodecSizeChng = xCodecSizeChng; pPager->xCodecFree = xCodecFree; @@ -65766,7 +66049,7 @@ static int lockBtree(BtShared *pBt){ pageSize-usableSize); return rc; } - if( (pBt->db->flags & SQLITE_WriteSchema)==0 && nPage>nPageFile ){ + if( sqlite3WritableSchema(pBt->db)==0 && nPage>nPageFile ){ rc = SQLITE_CORRUPT_BKPT; goto page1_init_failed; } @@ -66240,6 +66523,7 @@ static int relocatePage( eType==PTRMAP_BTREE || eType==PTRMAP_ROOTPAGE ); assert( sqlite3_mutex_held(pBt->mutex) ); assert( pDbPage->pBt==pBt ); + if( iDbPage<3 ) return SQLITE_CORRUPT_BKPT; /* Move page iDbPage from its current location to page number iFreePage */ TRACE(("AUTOVACUUM: Moving %d to free page %d (ptr page %d type %d)\n", @@ -67411,9 +67695,6 @@ static int accessPayload( /* Need to read this page properly. It contains some of the ** range of data that is being read (eOp==0) or written (eOp!=0). */ -#ifdef SQLITE_DIRECT_OVERFLOW_READ - sqlite3_file *fd; /* File from which to do direct overflow read */ -#endif int a = amt; if( a + offset > ovflSize ){ a = ovflSize - offset; @@ -67424,7 +67705,7 @@ static int accessPayload( ** ** 1) this is a read operation, and ** 2) data is required from the start of this overflow page, and - ** 3) there is no open write-transaction, and + ** 3) there are no dirty pages in the page-cache ** 4) the database is file-backed, and ** 5) the page is not in the WAL file ** 6) at least 4 bytes have already been read into the output buffer @@ -67435,11 +67716,10 @@ static int accessPayload( */ if( eOp==0 /* (1) */ && offset==0 /* (2) */ - && pBt->inTransaction==TRANS_READ /* (3) */ - && (fd = sqlite3PagerFile(pBt->pPager))->pMethods /* (4) */ - && 0==sqlite3PagerUseWal(pBt->pPager, nextPage) /* (5) */ + && sqlite3PagerDirectReadOk(pBt->pPager, nextPage) /* (3,4,5) */ && &pBuf[-4]>=pBufStart /* (6) */ ){ + sqlite3_file *fd = sqlite3PagerFile(pBt->pPager); u8 aSave[4]; u8 *aWrite = &pBuf[-4]; assert( aWrite>=pBufStart ); /* due to (6) */ @@ -74035,7 +74315,8 @@ SQLITE_PRIVATE int sqlite3VdbeMemStringify(Mem *pMem, u8 enc, u8 bForce){ assert( fg & MEM_Real ); sqlite3_snprintf(nByte, pMem->z, "%!.15g", pMem->u.r); } - pMem->n = sqlite3Strlen30(pMem->z); + assert( pMem->z!=0 ); + pMem->n = sqlite3Strlen30NN(pMem->z); pMem->enc = SQLITE_UTF8; pMem->flags |= MEM_Str|MEM_Term; if( bForce ) pMem->flags &= ~(MEM_Int|MEM_Real); @@ -75610,6 +75891,13 @@ SQLITE_PRIVATE void sqlite3VdbeSetSql(Vdbe *p, const char *z, int n, u8 prepFlag } assert( p->zSql==0 ); p->zSql = sqlite3DbStrNDup(p->db, z, n); +#ifdef SQLITE_ENABLE_NORMALIZE + assert( p->zNormSql==0 ); + if( p->zSql && (prepFlags & SQLITE_PREPARE_NORMALIZE)!=0 ){ + sqlite3Normalize(p, p->zSql, n, prepFlags); + assert( p->zNormSql!=0 || p->db->mallocFailed ); + } +#endif } /* @@ -75631,6 +75919,11 @@ SQLITE_PRIVATE void sqlite3VdbeSwap(Vdbe *pA, Vdbe *pB){ zTmp = pA->zSql; pA->zSql = pB->zSql; pB->zSql = zTmp; +#ifdef SQLITE_ENABLE_NORMALIZE + zTmp = pA->zNormSql; + pA->zNormSql = pB->zNormSql; + pB->zNormSql = zTmp; +#endif pB->expmask = pA->expmask; pB->prepFlags = pA->prepFlags; memcpy(pB->aCounter, pA->aCounter, sizeof(pB->aCounter)); @@ -78702,6 +78995,9 @@ SQLITE_PRIVATE void sqlite3VdbeClearObject(sqlite3 *db, Vdbe *p){ vdbeFreeOpArray(db, p->aOp, p->nOp); sqlite3DbFree(db, p->aColName); sqlite3DbFree(db, p->zSql); +#ifdef SQLITE_ENABLE_NORMALIZE + sqlite3DbFree(db, p->zNormSql); +#endif #ifdef SQLITE_ENABLE_STMT_SCANSTATUS { int i; @@ -80103,7 +80399,9 @@ SQLITE_PRIVATE int sqlite3VdbeIdxRowid(sqlite3 *db, BtCursor *pCur, i64 *rowid){ (void)getVarint32((u8*)m.z, szHdr); testcase( szHdr==3 ); testcase( szHdr==m.n ); - if( unlikely(szHdr<3 || (int)szHdr>m.n) ){ + testcase( szHdr>0x7fffffff ); + assert( m.n>=0 ); + if( unlikely(szHdr<3 || szHdr>(unsigned)m.n) ){ goto idx_rowid_corruption; } @@ -82114,6 +82412,16 @@ SQLITE_API char *sqlite3_expanded_sql(sqlite3_stmt *pStmt){ #endif } +#ifdef SQLITE_ENABLE_NORMALIZE +/* +** Return the normalized SQL associated with a prepared statement. +*/ +SQLITE_API const char *sqlite3_normalized_sql(sqlite3_stmt *pStmt){ + Vdbe *p = (Vdbe *)pStmt; + return p ? p->zNormSql : 0; +} +#endif /* SQLITE_ENABLE_NORMALIZE */ + #ifdef SQLITE_ENABLE_PREUPDATE_HOOK /* ** Allocate and populate an UnpackedRecord structure based on the serialized @@ -85553,17 +85861,25 @@ case OP_MakeRecord: { if( nVarintdb->aLimit[SQLITE_LIMIT_LENGTH] ){ - goto too_big; - } /* Make sure the output register has a buffer large enough to store ** the new record. The output register (pOp->p3) is not allowed to ** be one of the input registers (because the following call to ** sqlite3VdbeMemClearAndResize() could clobber the value before it is used). */ - if( sqlite3VdbeMemClearAndResize(pOut, (int)nByte) ){ - goto no_mem; + if( nByte+nZero<=pOut->szMalloc ){ + /* The output register is already large enough to hold the record. + ** No error checks or buffer enlargement is required */ + pOut->z = pOut->zMalloc; + }else{ + /* Need to make sure that the output is not too big and then enlarge + ** the output register to hold the full result */ + if( nByte+nZero>db->aLimit[SQLITE_LIMIT_LENGTH] ){ + goto too_big; + } + if( sqlite3VdbeMemClearAndResize(pOut, (int)nByte) ){ + goto no_mem; + } } zNewRecord = (u8 *)pOut->z; @@ -88419,7 +88735,7 @@ case OP_ParseSchema: { { zMaster = MASTER_NAME; initData.db = db; - initData.iDb = pOp->p1; + initData.iDb = iDb; initData.pzErrMsg = &p->zErrMsg; initData.mInitFlags = 0; zSql = sqlite3MPrintf(db, @@ -89616,10 +89932,11 @@ case OP_VFilter: { /* jump */ ** ** If the VColumn opcode is being used to fetch the value of ** an unchanging column during an UPDATE operation, then the P5 -** value is 1. Otherwise, P5 is 0. The P5 value is returned -** by sqlite3_vtab_nochange() routine and can be used -** by virtual table implementations to return special "no-change" -** marks which can be more efficient, depending on the virtual table. +** value is OPFLAG_NOCHNG. This will cause the sqlite3_vtab_nochange() +** function to return true inside the xColumn method of the virtual +** table implementation. The P5 column might also contain other +** bits (OPFLAG_LENGTHARG or OPFLAG_TYPEOFARG) but those bits are +** unused by OP_VColumn. */ case OP_VColumn: { sqlite3_vtab *pVtab; @@ -89641,7 +89958,8 @@ case OP_VColumn: { assert( pModule->xColumn ); memset(&sContext, 0, sizeof(sContext)); sContext.pOut = pDest; - if( pOp->p5 ){ + testcase( (pOp->p5 & OPFLAG_NOCHNG)==0 && pOp->p5!=0 ); + if( pOp->p5 & OPFLAG_NOCHNG ){ sqlite3VdbeMemSetNull(pDest); pDest->flags = MEM_Null|MEM_Zero; pDest->u.nZero = 0; @@ -94000,8 +94318,8 @@ static SQLITE_NOINLINE int walkExpr(Walker *pWalker, Expr *pExpr){ if( sqlite3WalkExprList(pWalker, pExpr->x.pList) ) return WRC_Abort; } #ifndef SQLITE_OMIT_WINDOWFUNC - if( !ExprHasProperty(pExpr, EP_Reduced) && pExpr->pWin ){ - Window *pWin = pExpr->pWin; + if( ExprHasProperty(pExpr, EP_WinFunc) ){ + Window *pWin = pExpr->y.pWin; if( sqlite3WalkExprList(pWalker, pWin->pPartition) ) return WRC_Abort; if( sqlite3WalkExprList(pWalker, pWin->pOrderBy) ) return WRC_Abort; if( sqlite3WalkExpr(pWalker, pWin->pFilter) ) return WRC_Abort; @@ -94274,7 +94592,7 @@ SQLITE_PRIVATE int sqlite3MatchSpanName( ** (even if X is implied). ** pExpr->iTable Set to the cursor number for the table obtained ** from pSrcList. -** pExpr->pTab Points to the Table structure of X.Y (even if +** pExpr->y.pTab Points to the Table structure of X.Y (even if ** X and/or Y are implied.) ** pExpr->iColumn Set to the column number within the table. ** pExpr->op Set to TK_COLUMN. @@ -94318,7 +94636,6 @@ static int lookupName( /* Initialize the node to no-match */ pExpr->iTable = -1; - pExpr->pTab = 0; ExprSetVVAProperty(pExpr, EP_NoReduce); /* Translate the schema name in zDb into a pointer to the corresponding @@ -94380,7 +94697,7 @@ static int lookupName( continue; } if( IN_RENAME_OBJECT && pItem->zAlias ){ - sqlite3RenameTokenRemap(pParse, 0, (void*)&pExpr->pTab); + sqlite3RenameTokenRemap(pParse, 0, (void*)&pExpr->y.pTab); } } if( 0==(cntTab++) ){ @@ -94406,13 +94723,13 @@ static int lookupName( } if( pMatch ){ pExpr->iTable = pMatch->iCursor; - pExpr->pTab = pMatch->pTab; + pExpr->y.pTab = pMatch->pTab; /* RIGHT JOIN not (yet) supported */ assert( (pMatch->fg.jointype & JT_RIGHT)==0 ); if( (pMatch->fg.jointype & JT_LEFT)!=0 ){ ExprSetProperty(pExpr, EP_CanBeNull); } - pSchema = pExpr->pTab->pSchema; + pSchema = pExpr->y.pTab->pSchema; } } /* if( pSrcList ) */ @@ -94469,7 +94786,7 @@ static int lookupName( testcase( iCol==(-1) ); if( IN_RENAME_OBJECT ){ pExpr->iColumn = iCol; - pExpr->pTab = pTab; + pExpr->y.pTab = pTab; eNewExprOp = TK_COLUMN; }else{ pExpr->iTable = pNC->uNC.pUpsert->regData + iCol; @@ -94491,7 +94808,7 @@ static int lookupName( testcase( iCol==32 ); pParse->newmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<pTab = pTab; + pExpr->y.pTab = pTab; pExpr->iColumn = (i16)iCol; eNewExprOp = TK_TRIGGER; #endif /* SQLITE_OMIT_TRIGGER */ @@ -94591,7 +94908,7 @@ static int lookupName( assert( pExpr->op==TK_ID ); if( ExprHasProperty(pExpr,EP_DblQuoted) ){ pExpr->op = TK_STRING; - pExpr->pTab = 0; + pExpr->y.pTab = 0; return WRC_Prune; } if( sqlite3ExprIdToTrueFalse(pExpr) ){ @@ -94669,9 +94986,9 @@ SQLITE_PRIVATE Expr *sqlite3CreateColumnExpr(sqlite3 *db, SrcList *pSrc, int iSr Expr *p = sqlite3ExprAlloc(db, TK_COLUMN, 0, 0); if( p ){ struct SrcList_item *pItem = &pSrc->a[iSrc]; - p->pTab = pItem->pTab; + p->y.pTab = pItem->pTab; p->iTable = pItem->iCursor; - if( p->pTab->iPKey==iCol ){ + if( p->y.pTab->iPKey==iCol ){ p->iColumn = -1; }else{ p->iColumn = (ynVar)iCol; @@ -94761,7 +95078,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ pItem = pSrcList->a; assert( HasRowid(pItem->pTab) && pItem->pTab->pSelect==0 ); pExpr->op = TK_COLUMN; - pExpr->pTab = pItem->pTab; + pExpr->y.pTab = pItem->pTab; pExpr->iTable = pItem->iCursor; pExpr->iColumn = -1; pExpr->affinity = SQLITE_AFF_INTEGER; @@ -94805,9 +95122,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ zColumn = pRight->u.zToken; if( IN_RENAME_OBJECT ){ sqlite3RenameTokenRemap(pParse, (void*)pExpr, (void*)pRight); - } - if( IN_RENAME_OBJECT ){ - sqlite3RenameTokenRemap(pParse, (void*)&pExpr->pTab, (void*)pLeft); + sqlite3RenameTokenRemap(pParse, (void*)&pExpr->y.pTab, (void*)pLeft); } } return lookupName(pParse, zDb, zTable, zColumn, pNC, pExpr); @@ -94889,6 +95204,15 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ notValid(pParse, pNC, "non-deterministic functions", NC_IdxExpr|NC_PartIdx); } + if( (pDef->funcFlags & SQLITE_FUNC_INTERNAL)!=0 + && pParse->nested==0 + && sqlite3Config.bInternalFunctions==0 + ){ + /* Internal-use-only functions are disallowed unless the + ** SQL is being compiled using sqlite3NestedParse() */ + no_such_func = 1; + pDef = 0; + } } if( 0==IN_RENAME_OBJECT ){ @@ -94897,18 +95221,18 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ || (pDef->xValue==0 && pDef->xInverse==0) || (pDef->xValue && pDef->xInverse && pDef->xSFunc && pDef->xFinalize) ); - if( pDef && pDef->xValue==0 && pExpr->pWin ){ + if( pDef && pDef->xValue==0 && ExprHasProperty(pExpr, EP_WinFunc) ){ sqlite3ErrorMsg(pParse, "%.*s() may not be used as a window function", nId, zId ); pNC->nErr++; }else if( (is_agg && (pNC->ncFlags & NC_AllowAgg)==0) - || (is_agg && (pDef->funcFlags & SQLITE_FUNC_WINDOW) && !pExpr->pWin) - || (is_agg && pExpr->pWin && (pNC->ncFlags & NC_AllowWin)==0) + || (is_agg && (pDef->funcFlags&SQLITE_FUNC_WINDOW) && !pExpr->y.pWin) + || (is_agg && pExpr->y.pWin && (pNC->ncFlags & NC_AllowWin)==0) ){ const char *zType; - if( (pDef->funcFlags & SQLITE_FUNC_WINDOW) || pExpr->pWin ){ + if( (pDef->funcFlags & SQLITE_FUNC_WINDOW) || pExpr->y.pWin ){ zType = "window"; }else{ zType = "aggregate"; @@ -94938,7 +95262,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ } if( is_agg ){ #ifndef SQLITE_OMIT_WINDOWFUNC - pNC->ncFlags &= ~(pExpr->pWin ? NC_AllowWin : NC_AllowAgg); + pNC->ncFlags &= ~(pExpr->y.pWin ? NC_AllowWin : NC_AllowAgg); #else pNC->ncFlags &= ~NC_AllowAgg; #endif @@ -94947,17 +95271,17 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ sqlite3WalkExprList(pWalker, pList); if( is_agg ){ #ifndef SQLITE_OMIT_WINDOWFUNC - if( pExpr->pWin ){ + if( pExpr->y.pWin ){ Select *pSel = pNC->pWinSelect; - sqlite3WalkExprList(pWalker, pExpr->pWin->pPartition); - sqlite3WalkExprList(pWalker, pExpr->pWin->pOrderBy); - sqlite3WalkExpr(pWalker, pExpr->pWin->pFilter); - sqlite3WindowUpdate(pParse, pSel->pWinDefn, pExpr->pWin, pDef); + sqlite3WalkExprList(pWalker, pExpr->y.pWin->pPartition); + sqlite3WalkExprList(pWalker, pExpr->y.pWin->pOrderBy); + sqlite3WalkExpr(pWalker, pExpr->y.pWin->pFilter); + sqlite3WindowUpdate(pParse, pSel->pWinDefn, pExpr->y.pWin, pDef); if( 0==pSel->pWin - || 0==sqlite3WindowCompare(pParse, pSel->pWin, pExpr->pWin) + || 0==sqlite3WindowCompare(pParse, pSel->pWin, pExpr->y.pWin) ){ - pExpr->pWin->pNextWin = pSel->pWin; - pSel->pWin = pExpr->pWin; + pExpr->y.pWin->pNextWin = pSel->pWin; + pSel->pWin = pExpr->y.pWin; } pNC->ncFlags |= NC_AllowWin; }else @@ -95380,13 +95704,13 @@ static int resolveOrderGroupBy( for(j=0; jpEList->nExpr; j++){ if( sqlite3ExprCompare(0, pE, pSelect->pEList->a[j].pExpr, -1)==0 ){ #ifndef SQLITE_OMIT_WINDOWFUNC - if( pE->pWin ){ + if( ExprHasProperty(pE, EP_WinFunc) ){ /* Since this window function is being changed into a reference ** to the same window function the result set, remove the instance ** of this window function from the Select.pWin list. */ Window **pp; for(pp=&pSelect->pWin; *pp; pp=&(*pp)->pNextWin){ - if( *pp==pE->pWin ){ + if( *pp==pE->y.pWin ){ *pp = (*pp)->pNextWin; } } @@ -95849,8 +96173,8 @@ SQLITE_PRIVATE char sqlite3ExprAffinity(Expr *pExpr){ return sqlite3AffinityType(pExpr->u.zToken, 0); } #endif - if( (op==TK_AGG_COLUMN || op==TK_COLUMN) && pExpr->pTab ){ - return sqlite3TableColumnAffinity(pExpr->pTab, pExpr->iColumn); + if( (op==TK_AGG_COLUMN || op==TK_COLUMN) && pExpr->y.pTab ){ + return sqlite3TableColumnAffinity(pExpr->y.pTab, pExpr->iColumn); } if( op==TK_SELECT_COLUMN ){ assert( pExpr->pLeft->flags&EP_xIsSelect ); @@ -95934,13 +96258,13 @@ SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr){ if( p->flags & EP_Generic ) break; if( (op==TK_AGG_COLUMN || op==TK_COLUMN || op==TK_REGISTER || op==TK_TRIGGER) - && p->pTab!=0 + && p->y.pTab!=0 ){ - /* op==TK_REGISTER && p->pTab!=0 happens when pExpr was originally + /* op==TK_REGISTER && p->y.pTab!=0 happens when pExpr was originally ** a TK_COLUMN but was previously evaluated and cached in a register */ int j = p->iColumn; if( j>=0 ){ - const char *zColl = p->pTab->aCol[j].zColl; + const char *zColl = p->y.pTab->aCol[j].zColl; pColl = sqlite3FindCollSeq(db, ENC(db), zColl, 0); } break; @@ -96843,6 +97167,10 @@ static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){ assert( p!=0 ); /* Sanity check: Assert that the IntValue is non-negative if it exists */ assert( !ExprHasProperty(p, EP_IntValue) || p->u.iValue>=0 ); + + assert( !ExprHasProperty(p, EP_WinFunc) || p->y.pWin!=0 || db->mallocFailed ); + assert( p->op!=TK_FUNCTION || ExprHasProperty(p, EP_TokenOnly|EP_Reduced) + || p->y.pWin==0 || ExprHasProperty(p, EP_WinFunc) ); #ifdef SQLITE_DEBUG if( ExprHasProperty(p, EP_Leaf) && !ExprHasProperty(p, EP_TokenOnly) ){ assert( p->pLeft==0 ); @@ -96861,8 +97189,9 @@ static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){ }else{ sqlite3ExprListDelete(db, p->x.pList); } - if( !ExprHasProperty(p, EP_Reduced) ){ - sqlite3WindowDelete(db, p->pWin); + if( ExprHasProperty(p, EP_WinFunc) ){ + assert( p->op==TK_FUNCTION ); + sqlite3WindowDelete(db, p->y.pWin); } } if( ExprHasProperty(p, EP_MemToken) ) sqlite3DbFree(db, p->u.zToken); @@ -96926,7 +97255,7 @@ static int dupedExprStructSize(Expr *p, int flags){ assert( (0xfff & (EP_Reduced|EP_TokenOnly))==0 ); if( 0==flags || p->op==TK_SELECT_COLUMN #ifndef SQLITE_OMIT_WINDOWFUNC - || p->pWin + || ExprHasProperty(p, EP_WinFunc) #endif ){ nSize = EXPR_FULLSIZE; @@ -96953,7 +97282,7 @@ static int dupedExprStructSize(Expr *p, int flags){ static int dupedExprNodeSize(Expr *p, int flags){ int nByte = dupedExprStructSize(p, flags) & 0xfff; if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){ - nByte += sqlite3Strlen30(p->u.zToken)+1; + nByte += sqlite3Strlen30NN(p->u.zToken)+1; } return ROUND8(nByte); } @@ -97056,22 +97385,24 @@ static Expr *exprDup(sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer){ } /* Fill in pNew->pLeft and pNew->pRight. */ - zAlloc += dupedExprNodeSize(p, dupFlags); - if( ExprHasProperty(pNew, EP_Reduced|EP_TokenOnly) ){ + if( ExprHasProperty(pNew, EP_Reduced|EP_TokenOnly|EP_WinFunc) ){ + zAlloc += dupedExprNodeSize(p, dupFlags); if( !ExprHasProperty(pNew, EP_TokenOnly|EP_Leaf) ){ pNew->pLeft = p->pLeft ? exprDup(db, p->pLeft, EXPRDUP_REDUCE, &zAlloc) : 0; pNew->pRight = p->pRight ? exprDup(db, p->pRight, EXPRDUP_REDUCE, &zAlloc) : 0; } - }else{ #ifndef SQLITE_OMIT_WINDOWFUNC - if( ExprHasProperty(p, EP_Reduced|EP_TokenOnly) ){ - pNew->pWin = 0; - }else{ - pNew->pWin = sqlite3WindowDup(db, pNew, p->pWin); + if( ExprHasProperty(p, EP_WinFunc) ){ + pNew->y.pWin = sqlite3WindowDup(db, pNew, p->y.pWin); + assert( ExprHasProperty(pNew, EP_WinFunc) ); } #endif /* SQLITE_OMIT_WINDOWFUNC */ + if( pzBuffer ){ + *pzBuffer = zAlloc; + } + }else{ if( !ExprHasProperty(p, EP_TokenOnly|EP_Leaf) ){ if( pNew->op==TK_SELECT_COLUMN ){ pNew->pLeft = p->pLeft; @@ -97083,9 +97414,6 @@ static Expr *exprDup(sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer){ pNew->pRight = sqlite3ExprDup(db, p->pRight, 0); } } - if( pzBuffer ){ - *pzBuffer = zAlloc; - } } return pNew; } @@ -97880,8 +98208,8 @@ SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr *p){ return 0; case TK_COLUMN: return ExprHasProperty(p, EP_CanBeNull) || - p->pTab==0 || /* Reference to column of index on expression */ - (p->iColumn>=0 && p->pTab->aCol[p->iColumn].notNull==0); + p->y.pTab==0 || /* Reference to column of index on expression */ + (p->iColumn>=0 && p->y.pTab->aCol[p->iColumn].notNull==0); default: return 1; } @@ -97936,6 +98264,14 @@ SQLITE_PRIVATE int sqlite3IsRowid(const char *z){ if( sqlite3StrICmp(z, "OID")==0 ) return 1; return 0; } +#ifdef SQLITE_ENABLE_NORMALIZE +SQLITE_PRIVATE int sqlite3IsRowidN(const char *z, int n){ + if( sqlite3StrNICmp(z, "_ROWID_", n)==0 ) return 1; + if( sqlite3StrNICmp(z, "ROWID", n)==0 ) return 1; + if( sqlite3StrNICmp(z, "OID", n)==0 ) return 1; + return 0; +} +#endif /* ** pX is the RHS of an IN operator. If pX is a SELECT statement @@ -99169,7 +99505,7 @@ expr_code_doover: ** constant. */ int iReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft,target); - int aff = sqlite3TableColumnAffinity(pExpr->pTab, pExpr->iColumn); + int aff = sqlite3TableColumnAffinity(pExpr->y.pTab, pExpr->iColumn); if( aff!=SQLITE_AFF_BLOB ){ static const char zAff[] = "B\000C\000D\000E"; assert( SQLITE_AFF_BLOB=='A' ); @@ -99193,7 +99529,7 @@ expr_code_doover: iTab = pParse->iSelfTab - 1; } } - return sqlite3ExprCodeGetColumn(pParse, pExpr->pTab, + return sqlite3ExprCodeGetColumn(pParse, pExpr->y.pTab, pExpr->iColumn, iTab, target, pExpr->op2); } @@ -99407,8 +99743,8 @@ expr_code_doover: CollSeq *pColl = 0; /* A collating sequence */ #ifndef SQLITE_OMIT_WINDOWFUNC - if( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) && pExpr->pWin ){ - return pExpr->pWin->regResult; + if( ExprHasProperty(pExpr, EP_WinFunc) ){ + return pExpr->y.pWin->regResult; } #endif @@ -99651,7 +99987,7 @@ expr_code_doover: ** p1==1 -> old.a p1==4 -> new.a ** p1==2 -> old.b p1==5 -> new.b */ - Table *pTab = pExpr->pTab; + Table *pTab = pExpr->y.pTab; int p1 = pExpr->iTable * (pTab->nCol+1) + 1 + pExpr->iColumn; assert( pExpr->iTable==0 || pExpr->iTable==1 ); @@ -99662,7 +99998,7 @@ expr_code_doover: sqlite3VdbeAddOp2(v, OP_Param, p1, target); VdbeComment((v, "r[%d]=%s.%s", target, (pExpr->iTable ? "new" : "old"), - (pExpr->iColumn<0 ? "rowid" : pExpr->pTab->aCol[pExpr->iColumn].zName) + (pExpr->iColumn<0 ? "rowid" : pExpr->y.pTab->aCol[pExpr->iColumn].zName) )); #ifndef SQLITE_OMIT_FLOATING_POINT @@ -100513,6 +100849,20 @@ SQLITE_PRIVATE int sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *pB, int iTa if( pA->op!=TK_COLUMN && pA->op!=TK_AGG_COLUMN && pA->u.zToken ){ if( pA->op==TK_FUNCTION ){ if( sqlite3StrICmp(pA->u.zToken,pB->u.zToken)!=0 ) return 2; +#ifndef SQLITE_OMIT_WINDOWFUNC + /* Justification for the assert(): + ** window functions have p->op==TK_FUNCTION but aggregate functions + ** have p->op==TK_AGG_FUNCTION. So any comparison between an aggregate + ** function and a window function should have failed before reaching + ** this point. And, it is not possible to have a window function and + ** a scalar function with the same name and number of arguments. So + ** if we reach this point, either A and B both window functions or + ** neither are a window functions. */ + assert( ExprHasProperty(pA,EP_WinFunc)==ExprHasProperty(pB,EP_WinFunc) ); + if( ExprHasProperty(pA,EP_WinFunc) ){ + if( sqlite3WindowCompare(pParse,pA->y.pWin,pB->y.pWin)!=0 ) return 2; + } +#endif }else if( pA->op==TK_COLLATE ){ if( sqlite3_stricmp(pA->u.zToken,pB->u.zToken)!=0 ) return 2; }else if( strcmp(pA->u.zToken,pB->u.zToken)!=0 ){ @@ -100532,21 +100882,6 @@ SQLITE_PRIVATE int sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *pB, int iTa if( pA->iTable!=pB->iTable && (pA->iTable!=iTab || NEVER(pB->iTable>=0)) ) return 2; } -#ifndef SQLITE_OMIT_WINDOWFUNC - /* Justification for the assert(): - ** window functions have p->op==TK_FUNCTION but aggregate functions - ** have p->op==TK_AGG_FUNCTION. So any comparison between an aggregate - ** function and a window function should have failed before reaching - ** this point. And, it is not possible to have a window function and - ** a scalar function with the same name and number of arguments. So - ** if we reach this point, either A and B both window functions or - ** neither are a window functions. */ - assert( (pA->pWin==0)==(pB->pWin==0) ); - - if( pA->pWin!=0 ){ - if( sqlite3WindowCompare(pParse,pA->pWin,pB->pWin)!=0 ) return 2; - } -#endif } return 0; } @@ -100687,8 +101022,8 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){ testcase( pExpr->op==TK_LE ); testcase( pExpr->op==TK_GT ); testcase( pExpr->op==TK_GE ); - if( (pExpr->pLeft->op==TK_COLUMN && IsVirtual(pExpr->pLeft->pTab)) - || (pExpr->pRight->op==TK_COLUMN && IsVirtual(pExpr->pRight->pTab)) + if( (pExpr->pLeft->op==TK_COLUMN && IsVirtual(pExpr->pLeft->y.pTab)) + || (pExpr->pRight->op==TK_COLUMN && IsVirtual(pExpr->pRight->y.pTab)) ){ return WRC_Prune; } @@ -100919,7 +101254,7 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){ && (k = addAggInfoColumn(pParse->db, pAggInfo))>=0 ){ pCol = &pAggInfo->aCol[k]; - pCol->pTab = pExpr->pTab; + pCol->pTab = pExpr->y.pTab; pCol->iTable = pExpr->iTable; pCol->iColumn = pExpr->iColumn; pCol->iMem = ++pParse->nMem; @@ -101802,10 +102137,16 @@ static void renameTokenCheckAll(Parse *pParse, void *pPtr){ #endif /* -** Add a new RenameToken object mapping parse tree element pPtr into -** token *pToken to the Parse object currently under construction. +** Remember that the parser tree element pPtr was created using +** the token pToken. ** -** Return a copy of pPtr. +** In other words, construct a new RenameToken object and add it +** to the list of RenameToken objects currently being built up +** in pParse->pRename. +** +** The pPtr argument is returned so that this routine can be used +** with tail recursion in tokenExpr() routine, for a small performance +** improvement. */ SQLITE_PRIVATE void *sqlite3RenameTokenMap(Parse *pParse, void *pPtr, Token *pToken){ RenameToken *pNew; @@ -101938,7 +102279,7 @@ static int renameColumnExprCb(Walker *pWalker, Expr *pExpr){ renameTokenFind(pWalker->pParse, p, (void*)pExpr); }else if( pExpr->op==TK_COLUMN && pExpr->iColumn==p->iCol - && p->pTab==pExpr->pTab + && p->pTab==pExpr->y.pTab ){ renameTokenFind(pWalker->pParse, p, (void*)pExpr); } @@ -102196,9 +102537,14 @@ static int renameResolveTrigger(Parse *pParse, const char *zDb){ db->aDb[sqlite3SchemaToIndex(db, pNew->pTabSchema)].zDbSName ); pParse->eTriggerOp = pNew->op; + /* ALWAYS() because if the table of the trigger does not exist, the + ** error would have been hit before this point */ + if( ALWAYS(pParse->pTriggerTab) ){ + rc = sqlite3ViewGetColumnNames(pParse, pParse->pTriggerTab); + } /* Resolve symbols in WHEN clause */ - if( pNew->pWhen ){ + if( rc==SQLITE_OK && pNew->pWhen ){ rc = sqlite3ResolveExprNames(&sNC, pNew->pWhen); } @@ -102312,15 +102658,8 @@ static void renameParseCleanup(Parse *pParse){ ** into zNew. The name should be quoted if bQuote is true. ** ** This function is used internally by the ALTER TABLE RENAME COLUMN command. -** Though accessible to application code, it is not intended for use by -** applications. The existance of this function, and the way it works, -** is subject to change without notice. -** -** If any of the parameters are out-of-bounds, then simply return NULL. -** An out-of-bounds parameter can only occur when the application calls -** this function directly. The parameters will always be well-formed when -** this routine is invoked by the bytecode for a legitimate ALTER TABLE -** statement. +** It is only accessible to SQL created using sqlite3NestedParse(). It is +** not reachable from ordinary SQL passed into sqlite3_prepare(). */ static void renameColumnFunc( sqlite3_context *context, @@ -102476,8 +102815,8 @@ renameColumnFunc_done: */ static int renameTableExprCb(Walker *pWalker, Expr *pExpr){ RenameCtx *p = pWalker->u.pRename; - if( pExpr->op==TK_COLUMN && p->pTab==pExpr->pTab ){ - renameTokenFind(pWalker->pParse, p, (void*)&pExpr->pTab); + if( pExpr->op==TK_COLUMN && p->pTab==pExpr->y.pTab ){ + renameTokenFind(pWalker->pParse, p, (void*)&pExpr->y.pTab); } return WRC_Continue; } @@ -102574,7 +102913,7 @@ static void renameTableFunc( }else{ /* Modify any FK definitions to point to the new table. */ #ifndef SQLITE_OMIT_FOREIGN_KEY - if( db->flags & SQLITE_ForeignKeys ){ + if( isLegacy==0 || (db->flags & SQLITE_ForeignKeys) ){ FKey *pFKey; for(pFKey=pTab->pFKey; pFKey; pFKey=pFKey->pNextFrom){ if( sqlite3_stricmp(pFKey->zTo, zOld)==0 ){ @@ -102728,9 +103067,9 @@ static void renameTableTest( */ SQLITE_PRIVATE void sqlite3AlterFunctions(void){ static FuncDef aAlterTableFuncs[] = { - FUNCTION(sqlite_rename_column, 9, 0, 0, renameColumnFunc), - FUNCTION(sqlite_rename_table, 7, 0, 0, renameTableFunc), - FUNCTION(sqlite_rename_test, 5, 0, 0, renameTableTest), + INTERNAL_FUNCTION(sqlite_rename_column, 9, renameColumnFunc), + INTERNAL_FUNCTION(sqlite_rename_table, 7, renameTableFunc), + INTERNAL_FUNCTION(sqlite_rename_test, 5, renameTableTest), }; sqlite3InsertBuiltinFuncs(aAlterTableFuncs, ArraySize(aAlterTableFuncs)); } @@ -104779,7 +105118,7 @@ static void attachFunc( if( pNew->pBt ) sqlite3BtreeClose(pNew->pBt); pNew->pBt = 0; pNew->pSchema = 0; - rc = sqlite3BtreeOpen(pVfs, "x", db, &pNew->pBt, 0, SQLITE_OPEN_MAIN_DB); + rc = sqlite3BtreeOpen(pVfs, "x\0", db, &pNew->pBt, 0, SQLITE_OPEN_MAIN_DB); }else{ /* This is a real ATTACH ** @@ -105459,6 +105798,7 @@ SQLITE_PRIVATE void sqlite3AuthRead( int iCol; /* Index of column in table */ assert( pExpr->op==TK_COLUMN || pExpr->op==TK_TRIGGER ); + assert( !IN_RENAME_OBJECT || db->xAuth==0 ); if( db->xAuth==0 ) return; iDb = sqlite3SchemaToIndex(pParse->db, pSchema); if( iDb<0 ){ @@ -105515,6 +105855,7 @@ SQLITE_PRIVATE int sqlite3AuthCheck( /* Don't do any authorization checks if the database is initialising ** or if the parser is being invoked from within sqlite3_declare_vtab. */ + assert( !IN_RENAME_OBJECT || db->xAuth==0 ); if( db->init.busy || IN_SPECIAL_PARSE ){ return SQLITE_OK; } @@ -105938,17 +106279,15 @@ SQLITE_PRIVATE Table *sqlite3LocateTable( if( p==0 ){ const char *zMsg = flags & LOCATE_VIEW ? "no such view" : "no such table"; #ifndef SQLITE_OMIT_VIRTUALTABLE - if( sqlite3FindDbName(db, zDbase)<1 ){ - /* If zName is the not the name of a table in the schema created using - ** CREATE, then check to see if it is the name of an virtual table that - ** can be an eponymous virtual table. */ - Module *pMod = (Module*)sqlite3HashFind(&db->aModule, zName); - if( pMod==0 && sqlite3_strnicmp(zName, "pragma_", 7)==0 ){ - pMod = sqlite3PragmaVtabRegister(db, zName); - } - if( pMod && sqlite3VtabEponymousTableInit(pParse, pMod) ){ - return pMod->pEpoTab; - } + /* If zName is the not the name of a table in the schema created using + ** CREATE, then check to see if it is the name of an virtual table that + ** can be an eponymous virtual table. */ + Module *pMod = (Module*)sqlite3HashFind(&db->aModule, zName); + if( pMod==0 && sqlite3_strnicmp(zName, "pragma_", 7)==0 ){ + pMod = sqlite3PragmaVtabRegister(db, zName); + } + if( pMod && sqlite3VtabEponymousTableInit(pParse, pMod) ){ + return pMod->pEpoTab; } #endif if( (flags & LOCATE_NOERR)==0 ){ @@ -106128,17 +106467,22 @@ SQLITE_PRIVATE void sqlite3ResetOneSchema(sqlite3 *db, int iDb){ SQLITE_PRIVATE void sqlite3ResetAllSchemasOfConnection(sqlite3 *db){ int i; sqlite3BtreeEnterAll(db); - assert( db->nSchemaLock==0 ); for(i=0; inDb; i++){ Db *pDb = &db->aDb[i]; if( pDb->pSchema ){ - sqlite3SchemaClear(pDb->pSchema); + if( db->nSchemaLock==0 ){ + sqlite3SchemaClear(pDb->pSchema); + }else{ + DbSetProperty(db, i, DB_ResetWanted); + } } } db->mDbFlags &= ~(DBFLAG_SchemaChange|DBFLAG_SchemaKnownOk); sqlite3VtabUnlockList(db); sqlite3BtreeLeaveAll(db); - sqlite3CollapseDatabaseArray(db); + if( db->nSchemaLock==0 ){ + sqlite3CollapseDatabaseArray(db); + } } /* @@ -106215,6 +106559,12 @@ static void SQLITE_NOINLINE deleteTable(sqlite3 *db, Table *pTable){ /* Delete the Table structure itself. */ +#ifdef SQLITE_ENABLE_NORMALIZE + if( pTable->pColHash ){ + sqlite3HashClear(pTable->pColHash); + sqlite3_free(pTable->pColHash); + } +#endif sqlite3DeleteColumnNames(db, pTable); sqlite3DbFree(db, pTable->zName); sqlite3DbFree(db, pTable->zColAff); @@ -106373,6 +106723,20 @@ SQLITE_PRIVATE int sqlite3TwoPartName( return iDb; } +/* +** True if PRAGMA writable_schema is ON +*/ +SQLITE_PRIVATE int sqlite3WritableSchema(sqlite3 *db){ + testcase( (db->flags&(SQLITE_WriteSchema|SQLITE_Defensive))==0 ); + testcase( (db->flags&(SQLITE_WriteSchema|SQLITE_Defensive))== + SQLITE_WriteSchema ); + testcase( (db->flags&(SQLITE_WriteSchema|SQLITE_Defensive))== + SQLITE_Defensive ); + testcase( (db->flags&(SQLITE_WriteSchema|SQLITE_Defensive))== + (SQLITE_WriteSchema|SQLITE_Defensive) ); + return (db->flags&(SQLITE_WriteSchema|SQLITE_Defensive))==SQLITE_WriteSchema; +} + /* ** This routine is used to check if the UTF-8 string zName is a legal ** unqualified name for a new schema object (table, index, view or @@ -106382,7 +106746,7 @@ SQLITE_PRIVATE int sqlite3TwoPartName( */ SQLITE_PRIVATE int sqlite3CheckObjectName(Parse *pParse, const char *zName){ if( !pParse->db->init.busy && pParse->nested==0 - && (pParse->db->flags & SQLITE_WriteSchema)==0 + && sqlite3WritableSchema(pParse->db)==0 && 0==sqlite3StrNICmp(zName, "sqlite_", 7) ){ sqlite3ErrorMsg(pParse, "object name reserved for internal use: %s", zName); return SQLITE_ERROR; @@ -107458,6 +107822,36 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){ recomputeColumnsNotIndexed(pPk); } +#ifndef SQLITE_OMIT_VIRTUALTABLE +/* +** Return true if zName is a shadow table name in the current database +** connection. +** +** zName is temporarily modified while this routine is running, but is +** restored to its original value prior to this routine returning. +*/ +static int isShadowTableName(sqlite3 *db, char *zName){ + char *zTail; /* Pointer to the last "_" in zName */ + Table *pTab; /* Table that zName is a shadow of */ + Module *pMod; /* Module for the virtual table */ + + zTail = strrchr(zName, '_'); + if( zTail==0 ) return 0; + *zTail = 0; + pTab = sqlite3FindTable(db, zName, 0); + *zTail = '_'; + if( pTab==0 ) return 0; + if( !IsVirtual(pTab) ) return 0; + pMod = (Module*)sqlite3HashFind(&db->aModule, pTab->azModuleArg[0]); + if( pMod==0 ) return 0; + if( pMod->pModule->iVersion<3 ) return 0; + if( pMod->pModule->xShadowName==0 ) return 0; + return pMod->pModule->xShadowName(zTail+1); +} +#else +# define isShadowTableName(x,y) 0 +#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */ + /* ** This routine is called to report the final ")" that terminates ** a CREATE TABLE statement. @@ -107497,6 +107891,10 @@ SQLITE_PRIVATE void sqlite3EndTable( p = pParse->pNewTable; if( p==0 ) return; + if( pSelect==0 && isShadowTableName(db, p->zName) ){ + p->tabFlags |= TF_Shadow; + } + /* If the db->init.busy is 1 it means we are reading the SQL off the ** "sqlite_master" or "sqlite_temp_master" table on the disk. ** So do not write to the disk again. Extract the root page number @@ -108004,7 +108402,7 @@ SQLITE_PRIVATE void sqlite3RootPageMoved(sqlite3 *db, int iDb, int iFrom, int iT static void destroyRootPage(Parse *pParse, int iTable, int iDb){ Vdbe *v = sqlite3GetVdbe(pParse); int r1 = sqlite3GetTempReg(pParse); - assert( iTable>1 ); + if( iTable<2 ) sqlite3ErrorMsg(pParse, "corrupt schema"); sqlite3VdbeAddOp3(v, OP_Destroy, iTable, r1, iDb); sqlite3MayAbort(pParse); #ifndef SQLITE_OMIT_AUTOVACUUM @@ -110459,6 +110857,21 @@ static FuncDef *functionSearch( } return 0; } +#ifdef SQLITE_ENABLE_NORMALIZE +SQLITE_PRIVATE FuncDef *sqlite3FunctionSearchN( + int h, /* Hash of the name */ + const char *zFunc, /* Name of function */ + int nFunc /* Length of the name */ +){ + FuncDef *p; + for(p=sqlite3BuiltinFunctions.a[h]; p; p=p->u.pHash){ + if( sqlite3StrNICmp(p->zName, zFunc, nFunc)==0 ){ + return p; + } + } + return 0; +} +#endif /* SQLITE_ENABLE_NORMALIZE */ /* ** Insert a new FuncDef into a FuncDefHash hash table. @@ -110472,7 +110885,7 @@ SQLITE_PRIVATE void sqlite3InsertBuiltinFuncs( FuncDef *pOther; const char *zName = aDef[i].zName; int nName = sqlite3Strlen30(zName); - int h = (zName[0] + nName) % SQLITE_FUNC_HASH_SZ; + int h = SQLITE_FUNC_HASH(zName[0], nName); assert( zName[0]>='a' && zName[0]<='z' ); pOther = functionSearch(h, zName); if( pOther ){ @@ -110551,7 +110964,7 @@ SQLITE_PRIVATE FuncDef *sqlite3FindFunction( */ if( !createFlag && (pBest==0 || (db->mDbFlags & DBFLAG_PreferBuiltin)!=0) ){ bestScore = 0; - h = (sqlite3UpperToLower[(u8)zName[0]] + nName) % SQLITE_FUNC_HASH_SZ; + h = SQLITE_FUNC_HASH(sqlite3UpperToLower[(u8)zName[0]], nName); p = functionSearch(h, zName); while( p ){ int score = matchQuality(p, nArg, enc); @@ -110699,32 +111112,49 @@ SQLITE_PRIVATE Table *sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){ return pTab; } +/* Return true if table pTab is read-only. +** +** A table is read-only if any of the following are true: +** +** 1) It is a virtual table and no implementation of the xUpdate method +** has been provided +** +** 2) It is a system table (i.e. sqlite_master), this call is not +** part of a nested parse and writable_schema pragma has not +** been specified +** +** 3) The table is a shadow table, the database connection is in +** defensive mode, and the current sqlite3_prepare() +** is for a top-level SQL statement. +*/ +static int tabIsReadOnly(Parse *pParse, Table *pTab){ + sqlite3 *db; + if( IsVirtual(pTab) ){ + return sqlite3GetVTable(pParse->db, pTab)->pMod->pModule->xUpdate==0; + } + if( (pTab->tabFlags & (TF_Readonly|TF_Shadow))==0 ) return 0; + db = pParse->db; + if( (pTab->tabFlags & TF_Readonly)!=0 ){ + return sqlite3WritableSchema(db)==0 && pParse->nested==0; + } + assert( pTab->tabFlags & TF_Shadow ); + return (db->flags & SQLITE_Defensive)!=0 +#ifndef SQLITE_OMIT_VIRTUALTABLE + && db->pVtabCtx==0 +#endif + && db->nVdbeExec==0; +} + /* ** Check to make sure the given table is writable. If it is not ** writable, generate an error message and return 1. If it is ** writable return 0; */ SQLITE_PRIVATE int sqlite3IsReadOnly(Parse *pParse, Table *pTab, int viewOk){ - /* A table is not writable under the following circumstances: - ** - ** 1) It is a virtual table and no implementation of the xUpdate method - ** has been provided, or - ** 2) It is a system table (i.e. sqlite_master), this call is not - ** part of a nested parse and writable_schema pragma has not - ** been specified. - ** - ** In either case leave an error message in pParse and return non-zero. - */ - if( ( IsVirtual(pTab) - && sqlite3GetVTable(pParse->db, pTab)->pMod->pModule->xUpdate==0 ) - || ( (pTab->tabFlags & TF_Readonly)!=0 - && (pParse->db->flags & SQLITE_WriteSchema)==0 - && pParse->nested==0 ) - ){ + if( tabIsReadOnly(pParse, pTab) ){ sqlite3ErrorMsg(pParse, "table %s may not be modified", pTab->zName); return 1; } - #ifndef SQLITE_OMIT_VIEW if( !viewOk && pTab->pSelect ){ sqlite3ErrorMsg(pParse,"cannot modify %s because it is a view",pTab->zName); @@ -114128,7 +114558,7 @@ static Expr *exprTableColumn( ){ Expr *pExpr = sqlite3Expr(db, TK_COLUMN, 0); if( pExpr ){ - pExpr->pTab = pTab; + pExpr->y.pTab = pTab; pExpr->iTable = iCursor; pExpr->iColumn = iCol; } @@ -115204,7 +115634,8 @@ SQLITE_PRIVATE void sqlite3TableAffinity(Vdbe *v, Table *pTab, int iReg){ }while( i>=0 && zColAff[i]==SQLITE_AFF_BLOB ); pTab->zColAff = zColAff; } - i = sqlite3Strlen30(zColAff); + assert( zColAff!=0 ); + i = sqlite3Strlen30NN(zColAff); if( i ){ if( iReg ){ sqlite3VdbeAddOp4(v, OP_Affinity, iReg, i, 0, zColAff, i); @@ -116184,14 +116615,15 @@ insert_cleanup: #endif /* -** Meanings of bits in of pWalker->eCode for checkConstraintUnchanged() +** Meanings of bits in of pWalker->eCode for +** sqlite3ExprReferencesUpdatedColumn() */ #define CKCNSTRNT_COLUMN 0x01 /* CHECK constraint uses a changing column */ #define CKCNSTRNT_ROWID 0x02 /* CHECK constraint references the ROWID */ -/* This is the Walker callback from checkConstraintUnchanged(). Set -** bit 0x01 of pWalker->eCode if -** pWalker->eCode to 0 if this expression node references any of the +/* This is the Walker callback from sqlite3ExprReferencesUpdatedColumn(). +* Set bit 0x01 of pWalker->eCode if pWalker->eCode to 0 and if this +** expression node references any of the ** columns that are being modifed by an UPDATE statement. */ static int checkConstraintExprNode(Walker *pWalker, Expr *pExpr){ @@ -116213,12 +116645,21 @@ static int checkConstraintExprNode(Walker *pWalker, Expr *pExpr){ ** only columns that are modified by the UPDATE are those for which ** aiChng[i]>=0, and also the ROWID is modified if chngRowid is true. ** -** Return true if CHECK constraint pExpr does not use any of the +** Return true if CHECK constraint pExpr uses any of the ** changing columns (or the rowid if it is changing). In other words, -** return true if this CHECK constraint can be skipped when validating +** return true if this CHECK constraint must be validated for ** the new row in the UPDATE statement. +** +** 2018-09-15: pExpr might also be an expression for an index-on-expressions. +** The operation of this routine is the same - return true if an only if +** the expression uses one or more of columns identified by the second and +** third arguments. */ -static int checkConstraintUnchanged(Expr *pExpr, int *aiChng, int chngRowid){ +SQLITE_PRIVATE int sqlite3ExprReferencesUpdatedColumn( + Expr *pExpr, /* The expression to be checked */ + int *aiChng, /* aiChng[x]>=0 if column x changed by the UPDATE */ + int chngRowid /* True if UPDATE changes the rowid */ +){ Walker w; memset(&w, 0, sizeof(w)); w.eCode = 0; @@ -116233,7 +116674,7 @@ static int checkConstraintUnchanged(Expr *pExpr, int *aiChng, int chngRowid){ testcase( w.eCode==CKCNSTRNT_COLUMN ); testcase( w.eCode==CKCNSTRNT_ROWID ); testcase( w.eCode==(CKCNSTRNT_ROWID|CKCNSTRNT_COLUMN) ); - return !w.eCode; + return w.eCode!=0; } /* @@ -116439,7 +116880,13 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( for(i=0; inExpr; i++){ int allOk; Expr *pExpr = pCheck->a[i].pExpr; - if( aiChng && checkConstraintUnchanged(pExpr, aiChng, pkChng) ) continue; + if( aiChng + && !sqlite3ExprReferencesUpdatedColumn(pExpr, aiChng, pkChng) + ){ + /* The check constraints do not reference any of the columns being + ** updated so there is no point it verifying the check constraint */ + continue; + } allOk = sqlite3VdbeMakeLabel(v); sqlite3VdbeVerifyAbortable(v, onError); sqlite3ExprIfTrue(pParse, pExpr, allOk, SQLITE_JUMPIFNULL); @@ -117940,12 +118387,15 @@ struct sqlite3_api_routines { int (*str_errcode)(sqlite3_str*); int (*str_length)(sqlite3_str*); char *(*str_value)(sqlite3_str*); + /* Version 3.25.0 and later */ int (*create_window_function)(sqlite3*,const char*,int,int,void*, void (*xStep)(sqlite3_context*,int,sqlite3_value**), void (*xFinal)(sqlite3_context*), void (*xValue)(sqlite3_context*), void (*xInv)(sqlite3_context*,int,sqlite3_value**), void(*xDestroy)(void*)); + /* Version 3.26.0 and later */ + const char *(*normalized_sql)(sqlite3_stmt*); }; /* @@ -118233,6 +118683,8 @@ typedef int (*sqlite3_loadext_entry)( #define sqlite3_str_value sqlite3_api->str_value /* Version 3.25.0 and later */ #define sqlite3_create_window_function sqlite3_api->create_window_function +/* Version 3.26.0 and later */ +#define sqlite3_normalized_sql sqlite3_api->normalized_sql #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) @@ -118321,6 +118773,7 @@ typedef int (*sqlite3_loadext_entry)( # define sqlite3_declare_vtab 0 # define sqlite3_vtab_config 0 # define sqlite3_vtab_on_conflict 0 +# define sqlite3_vtab_collation 0 #endif #ifdef SQLITE_OMIT_SHARED_CACHE @@ -118688,7 +119141,13 @@ static const sqlite3_api_routines sqlite3Apis = { sqlite3_str_length, sqlite3_str_value, /* Version 3.25.0 and later */ - sqlite3_create_window_function + sqlite3_create_window_function, + /* Version 3.26.0 and later */ +#ifdef SQLITE_ENABLE_NORMALIZE + sqlite3_normalized_sql +#else + 0 +#endif }; /* @@ -119138,10 +119597,9 @@ SQLITE_PRIVATE void sqlite3AutoLoadExtensions(sqlite3 *db){ #define PragTyp_ACTIVATE_EXTENSIONS 40 #define PragTyp_HEXKEY 41 #define PragTyp_KEY 42 -#define PragTyp_REKEY 43 -#define PragTyp_LOCK_STATUS 44 -#define PragTyp_PARSER_TRACE 45 -#define PragTyp_STATS 46 +#define PragTyp_LOCK_STATUS 43 +#define PragTyp_PARSER_TRACE 44 +#define PragTyp_STATS 45 /* Property flags associated with various pragma. */ #define PragFlg_NeedSchema 0x01 /* Force schema load before running */ @@ -119158,58 +119616,57 @@ SQLITE_PRIVATE void sqlite3AutoLoadExtensions(sqlite3 *db){ ** result column is different from the name of the pragma */ static const char *const pragCName[] = { - /* 0 */ "cache_size", /* Used by: default_cache_size */ - /* 1 */ "cid", /* Used by: table_info */ - /* 2 */ "name", - /* 3 */ "type", - /* 4 */ "notnull", - /* 5 */ "dflt_value", - /* 6 */ "pk", - /* 7 */ "tbl", /* Used by: stats */ - /* 8 */ "idx", - /* 9 */ "wdth", - /* 10 */ "hght", - /* 11 */ "flgs", - /* 12 */ "seqno", /* Used by: index_info */ - /* 13 */ "cid", - /* 14 */ "name", + /* 0 */ "id", /* Used by: foreign_key_list */ + /* 1 */ "seq", + /* 2 */ "table", + /* 3 */ "from", + /* 4 */ "to", + /* 5 */ "on_update", + /* 6 */ "on_delete", + /* 7 */ "match", + /* 8 */ "cid", /* Used by: table_xinfo */ + /* 9 */ "name", + /* 10 */ "type", + /* 11 */ "notnull", + /* 12 */ "dflt_value", + /* 13 */ "pk", + /* 14 */ "hidden", + /* table_info reuses 8 */ /* 15 */ "seqno", /* Used by: index_xinfo */ /* 16 */ "cid", /* 17 */ "name", /* 18 */ "desc", /* 19 */ "coll", /* 20 */ "key", - /* 21 */ "seq", /* Used by: index_list */ - /* 22 */ "name", - /* 23 */ "unique", - /* 24 */ "origin", - /* 25 */ "partial", - /* 26 */ "seq", /* Used by: database_list */ + /* 21 */ "tbl", /* Used by: stats */ + /* 22 */ "idx", + /* 23 */ "wdth", + /* 24 */ "hght", + /* 25 */ "flgs", + /* 26 */ "seq", /* Used by: index_list */ /* 27 */ "name", - /* 28 */ "file", - /* 29 */ "name", /* Used by: function_list */ - /* 30 */ "builtin", - /* 31 */ "name", /* Used by: module_list pragma_list */ - /* 32 */ "seq", /* Used by: collation_list */ - /* 33 */ "name", - /* 34 */ "id", /* Used by: foreign_key_list */ - /* 35 */ "seq", - /* 36 */ "table", - /* 37 */ "from", - /* 38 */ "to", - /* 39 */ "on_update", - /* 40 */ "on_delete", - /* 41 */ "match", - /* 42 */ "table", /* Used by: foreign_key_check */ - /* 43 */ "rowid", - /* 44 */ "parent", - /* 45 */ "fkid", - /* 46 */ "busy", /* Used by: wal_checkpoint */ - /* 47 */ "log", - /* 48 */ "checkpointed", - /* 49 */ "timeout", /* Used by: busy_timeout */ - /* 50 */ "database", /* Used by: lock_status */ - /* 51 */ "status", + /* 28 */ "unique", + /* 29 */ "origin", + /* 30 */ "partial", + /* 31 */ "table", /* Used by: foreign_key_check */ + /* 32 */ "rowid", + /* 33 */ "parent", + /* 34 */ "fkid", + /* index_info reuses 15 */ + /* 35 */ "seq", /* Used by: database_list */ + /* 36 */ "name", + /* 37 */ "file", + /* 38 */ "busy", /* Used by: wal_checkpoint */ + /* 39 */ "log", + /* 40 */ "checkpointed", + /* 41 */ "name", /* Used by: function_list */ + /* 42 */ "builtin", + /* collation_list reuses 26 */ + /* 43 */ "database", /* Used by: lock_status */ + /* 44 */ "status", + /* 45 */ "cache_size", /* Used by: default_cache_size */ + /* module_list pragma_list reuses 9 */ + /* 46 */ "timeout", /* Used by: busy_timeout */ }; /* Definitions of all built-in pragmas */ @@ -119219,7 +119676,7 @@ typedef struct PragmaName { u8 mPragFlg; /* Zero or more PragFlg_XXX values */ u8 iPragCName; /* Start of column names in pragCName[] */ u8 nPragCName; /* Num of col names. 0 means use pragma name */ - u32 iArg; /* Extra argument */ + u64 iArg; /* Extra argument */ } PragmaName; static const PragmaName aPragmaName[] = { #if defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD) @@ -119255,7 +119712,7 @@ static const PragmaName aPragmaName[] = { {/* zName: */ "busy_timeout", /* ePragTyp: */ PragTyp_BUSY_TIMEOUT, /* ePragFlg: */ PragFlg_Result0, - /* ColNames: */ 49, 1, + /* ColNames: */ 46, 1, /* iArg: */ 0 }, #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) {/* zName: */ "cache_size", @@ -119292,7 +119749,7 @@ static const PragmaName aPragmaName[] = { {/* zName: */ "collation_list", /* ePragTyp: */ PragTyp_COLLATION_LIST, /* ePragFlg: */ PragFlg_Result0, - /* ColNames: */ 32, 2, + /* ColNames: */ 26, 2, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_COMPILEOPTION_DIAGS) @@ -119327,14 +119784,14 @@ static const PragmaName aPragmaName[] = { {/* zName: */ "database_list", /* ePragTyp: */ PragTyp_DATABASE_LIST, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0, - /* ColNames: */ 26, 3, + /* ColNames: */ 35, 3, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED) {/* zName: */ "default_cache_size", /* ePragTyp: */ PragTyp_DEFAULT_CACHE_SIZE, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_NoColumns1, - /* ColNames: */ 0, 1, + /* ColNames: */ 45, 1, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) @@ -119364,14 +119821,14 @@ static const PragmaName aPragmaName[] = { {/* zName: */ "foreign_key_check", /* ePragTyp: */ PragTyp_FOREIGN_KEY_CHECK, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0, - /* ColNames: */ 42, 4, + /* ColNames: */ 31, 4, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_FOREIGN_KEY) {/* zName: */ "foreign_key_list", /* ePragTyp: */ PragTyp_FOREIGN_KEY_LIST, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, - /* ColNames: */ 34, 8, + /* ColNames: */ 0, 8, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) @@ -119407,7 +119864,7 @@ static const PragmaName aPragmaName[] = { {/* zName: */ "function_list", /* ePragTyp: */ PragTyp_FUNCTION_LIST, /* ePragFlg: */ PragFlg_Result0, - /* ColNames: */ 29, 2, + /* ColNames: */ 41, 2, /* iArg: */ 0 }, #endif #endif @@ -119416,12 +119873,12 @@ static const PragmaName aPragmaName[] = { /* ePragTyp: */ PragTyp_HEXKEY, /* ePragFlg: */ 0, /* ColNames: */ 0, 0, - /* iArg: */ 0 }, + /* iArg: */ 2 }, {/* zName: */ "hexrekey", /* ePragTyp: */ PragTyp_HEXKEY, /* ePragFlg: */ 0, /* ColNames: */ 0, 0, - /* iArg: */ 0 }, + /* iArg: */ 3 }, #endif #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) #if !defined(SQLITE_OMIT_CHECK) @@ -119443,12 +119900,12 @@ static const PragmaName aPragmaName[] = { {/* zName: */ "index_info", /* ePragTyp: */ PragTyp_INDEX_INFO, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, - /* ColNames: */ 12, 3, + /* ColNames: */ 15, 3, /* iArg: */ 0 }, {/* zName: */ "index_list", /* ePragTyp: */ PragTyp_INDEX_LIST, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, - /* ColNames: */ 21, 5, + /* ColNames: */ 26, 5, /* iArg: */ 0 }, {/* zName: */ "index_xinfo", /* ePragTyp: */ PragTyp_INDEX_INFO, @@ -119505,7 +119962,7 @@ static const PragmaName aPragmaName[] = { {/* zName: */ "lock_status", /* ePragTyp: */ PragTyp_LOCK_STATUS, /* ePragFlg: */ PragFlg_Result0, - /* ColNames: */ 50, 2, + /* ColNames: */ 43, 2, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) @@ -119531,7 +119988,7 @@ static const PragmaName aPragmaName[] = { {/* zName: */ "module_list", /* ePragTyp: */ PragTyp_MODULE_LIST, /* ePragFlg: */ PragFlg_Result0, - /* ColNames: */ 31, 1, + /* ColNames: */ 9, 1, /* iArg: */ 0 }, #endif #endif @@ -119564,7 +120021,7 @@ static const PragmaName aPragmaName[] = { {/* zName: */ "pragma_list", /* ePragTyp: */ PragTyp_PRAGMA_LIST, /* ePragFlg: */ PragFlg_Result0, - /* ColNames: */ 31, 1, + /* ColNames: */ 9, 1, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) @@ -119595,10 +120052,10 @@ static const PragmaName aPragmaName[] = { #endif #if defined(SQLITE_HAS_CODEC) {/* zName: */ "rekey", - /* ePragTyp: */ PragTyp_REKEY, + /* ePragTyp: */ PragTyp_KEY, /* ePragFlg: */ 0, /* ColNames: */ 0, 0, - /* iArg: */ 0 }, + /* iArg: */ 1 }, #endif #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) {/* zName: */ "reverse_unordered_selects", @@ -119651,7 +120108,7 @@ static const PragmaName aPragmaName[] = { {/* zName: */ "stats", /* ePragTyp: */ PragTyp_STATS, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq, - /* ColNames: */ 7, 5, + /* ColNames: */ 21, 5, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) @@ -119665,8 +120122,13 @@ static const PragmaName aPragmaName[] = { {/* zName: */ "table_info", /* ePragTyp: */ PragTyp_TABLE_INFO, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, - /* ColNames: */ 1, 6, + /* ColNames: */ 8, 6, /* iArg: */ 0 }, + {/* zName: */ "table_xinfo", + /* ePragTyp: */ PragTyp_TABLE_INFO, + /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, + /* ColNames: */ 8, 7, + /* iArg: */ 1 }, #endif #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) {/* zName: */ "temp_store", @@ -119679,6 +120141,18 @@ static const PragmaName aPragmaName[] = { /* ePragFlg: */ PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ 0 }, +#endif +#if defined(SQLITE_HAS_CODEC) + {/* zName: */ "textkey", + /* ePragTyp: */ PragTyp_KEY, + /* ePragFlg: */ 0, + /* ColNames: */ 0, 0, + /* iArg: */ 4 }, + {/* zName: */ "textrekey", + /* ePragTyp: */ PragTyp_KEY, + /* ePragFlg: */ 0, + /* ColNames: */ 0, 0, + /* iArg: */ 5 }, #endif {/* zName: */ "threads", /* ePragTyp: */ PragTyp_THREADS, @@ -119730,7 +120204,7 @@ static const PragmaName aPragmaName[] = { {/* zName: */ "wal_checkpoint", /* ePragTyp: */ PragTyp_WAL_CHECKPOINT, /* ePragFlg: */ PragFlg_NeedSchema, - /* ColNames: */ 46, 3, + /* ColNames: */ 38, 3, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) @@ -119738,10 +120212,10 @@ static const PragmaName aPragmaName[] = { /* ePragTyp: */ PragTyp_FLAG, /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1, /* ColNames: */ 0, 0, - /* iArg: */ SQLITE_WriteSchema }, + /* iArg: */ SQLITE_WriteSchema|SQLITE_NoSchemaError }, #endif }; -/* Number of pragmas: 61 on by default, 78 total. */ +/* Number of pragmas: 62 on by default, 81 total. */ /************** End of pragma.h **********************************************/ /************** Continuing where we left off in pragma.c *********************/ @@ -120753,7 +121227,7 @@ SQLITE_PRIVATE void sqlite3Pragma( setPragmaResultColumnNames(v, pPragma); returnSingleInt(v, (db->flags & pPragma->iArg)!=0 ); }else{ - int mask = pPragma->iArg; /* Mask of bits to set or clear. */ + u64 mask = pPragma->iArg; /* Mask of bits to set or clear. */ if( db->autoCommit==0 ){ /* Foreign key support may not be enabled or disabled while not ** in auto-commit mode. */ @@ -120802,15 +121276,17 @@ SQLITE_PRIVATE void sqlite3Pragma( Table *pTab; pTab = sqlite3LocateTable(pParse, LOCATE_NOERR, zRight, zDb); if( pTab ){ + int iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema); int i, k; int nHidden = 0; Column *pCol; Index *pPk = sqlite3PrimaryKeyIndex(pTab); - pParse->nMem = 6; - sqlite3CodeVerifySchema(pParse, iDb); + pParse->nMem = 7; + sqlite3CodeVerifySchema(pParse, iTabDb); sqlite3ViewGetColumnNames(pParse, pTab); for(i=0, pCol=pTab->aCol; inCol; i++, pCol++){ - if( IsHiddenColumn(pCol) ){ + int isHidden = IsHiddenColumn(pCol); + if( isHidden && pPragma->iArg==0 ){ nHidden++; continue; } @@ -120822,13 +121298,14 @@ SQLITE_PRIVATE void sqlite3Pragma( for(k=1; k<=pTab->nCol && pPk->aiColumn[k-1]!=i; k++){} } assert( pCol->pDflt==0 || pCol->pDflt->op==TK_SPAN ); - sqlite3VdbeMultiLoad(v, 1, "issisi", + sqlite3VdbeMultiLoad(v, 1, pPragma->iArg ? "issisii" : "issisi", i-nHidden, pCol->zName, sqlite3ColumnType(pCol,""), pCol->notNull ? 1 : 0, pCol->pDflt ? pCol->pDflt->u.zToken : 0, - k); + k, + isHidden); } } } @@ -120866,6 +121343,7 @@ SQLITE_PRIVATE void sqlite3Pragma( Table *pTab; pIdx = sqlite3FindIndex(db, zRight, zDb); if( pIdx ){ + int iIdxDb = sqlite3SchemaToIndex(db, pIdx->pSchema); int i; int mx; if( pPragma->iArg ){ @@ -120878,7 +121356,7 @@ SQLITE_PRIVATE void sqlite3Pragma( pParse->nMem = 3; } pTab = pIdx->pTable; - sqlite3CodeVerifySchema(pParse, iDb); + sqlite3CodeVerifySchema(pParse, iIdxDb); assert( pParse->nMem<=pPragma->nPragCName ); for(i=0; iaiColumn[i]; @@ -120902,8 +121380,9 @@ SQLITE_PRIVATE void sqlite3Pragma( int i; pTab = sqlite3FindTable(db, zRight, zDb); if( pTab ){ + int iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema); pParse->nMem = 5; - sqlite3CodeVerifySchema(pParse, iDb); + sqlite3CodeVerifySchema(pParse, iTabDb); for(pIdx=pTab->pIndex, i=0; pIdx; pIdx=pIdx->pNext, i++){ const char *azOrigin[] = { "c", "u", "pk" }; sqlite3VdbeMultiLoad(v, 1, "isisi", @@ -120950,6 +121429,7 @@ SQLITE_PRIVATE void sqlite3Pragma( pParse->nMem = 2; for(i=0; iu.pHash ){ + if( p->funcFlags & SQLITE_FUNC_INTERNAL ) continue; sqlite3VdbeMultiLoad(v, 1, "si", p->zName, 1); } } @@ -120991,9 +121471,10 @@ SQLITE_PRIVATE void sqlite3Pragma( if( pTab ){ pFK = pTab->pFKey; if( pFK ){ + int iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema); int i = 0; pParse->nMem = 8; - sqlite3CodeVerifySchema(pParse, iDb); + sqlite3CodeVerifySchema(pParse, iTabDb); while(pFK){ int j; for(j=0; jnCol; j++){ @@ -121038,9 +121519,9 @@ SQLITE_PRIVATE void sqlite3Pragma( pParse->nMem += 4; regKey = ++pParse->nMem; regRow = ++pParse->nMem; - sqlite3CodeVerifySchema(pParse, iDb); k = sqliteHashFirst(&db->aDb[iDb].pSchema->tblHash); while( k ){ + int iTabDb; if( zRight ){ pTab = sqlite3LocateTable(pParse, 0, zRight, zDb); k = 0; @@ -121049,21 +121530,23 @@ SQLITE_PRIVATE void sqlite3Pragma( k = sqliteHashNext(k); } if( pTab==0 || pTab->pFKey==0 ) continue; - sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); + iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema); + sqlite3CodeVerifySchema(pParse, iTabDb); + sqlite3TableLock(pParse, iTabDb, pTab->tnum, 0, pTab->zName); if( pTab->nCol+regRow>pParse->nMem ) pParse->nMem = pTab->nCol + regRow; - sqlite3OpenTable(pParse, 0, iDb, pTab, OP_OpenRead); + sqlite3OpenTable(pParse, 0, iTabDb, pTab, OP_OpenRead); sqlite3VdbeLoadString(v, regResult, pTab->zName); for(i=1, pFK=pTab->pFKey; pFK; i++, pFK=pFK->pNextFrom){ pParent = sqlite3FindTable(db, pFK->zTo, zDb); if( pParent==0 ) continue; pIdx = 0; - sqlite3TableLock(pParse, iDb, pParent->tnum, 0, pParent->zName); + sqlite3TableLock(pParse, iTabDb, pParent->tnum, 0, pParent->zName); x = sqlite3FkLocateIndex(pParse, pParent, pFK, &pIdx, 0); if( x==0 ){ if( pIdx==0 ){ - sqlite3OpenTable(pParse, i, iDb, pParent, OP_OpenRead); + sqlite3OpenTable(pParse, i, iTabDb, pParent, OP_OpenRead); }else{ - sqlite3VdbeAddOp3(v, OP_OpenRead, i, pIdx->tnum, iDb); + sqlite3VdbeAddOp3(v, OP_OpenRead, i, pIdx->tnum, iTabDb); sqlite3VdbeSetP4KeyInfo(pParse, pIdx); } }else{ @@ -121832,12 +122315,24 @@ SQLITE_PRIVATE void sqlite3Pragma( #endif #ifdef SQLITE_HAS_CODEC + /* Pragma iArg + ** ---------- ------ + ** key 0 + ** rekey 1 + ** hexkey 2 + ** hexrekey 3 + ** textkey 4 + ** textrekey 5 + */ case PragTyp_KEY: { - if( zRight ) sqlite3_key_v2(db, zDb, zRight, sqlite3Strlen30(zRight)); - break; - } - case PragTyp_REKEY: { - if( zRight ) sqlite3_rekey_v2(db, zDb, zRight, sqlite3Strlen30(zRight)); + if( zRight ){ + int n = pPragma->iArg<4 ? sqlite3Strlen30(zRight) : -1; + if( (pPragma->iArg & 1)==0 ){ + sqlite3_key_v2(db, zDb, zRight, n); + }else{ + sqlite3_rekey_v2(db, zDb, zRight, n); + } + } break; } case PragTyp_HEXKEY: { @@ -121849,7 +122344,7 @@ SQLITE_PRIVATE void sqlite3Pragma( iByte = (iByte<<4) + sqlite3HexToInt(zRight[i]); if( (i&1)!=0 ) zKey[i/2] = iByte; } - if( (zLeft[3] & 0xf)==0xb ){ + if( (pPragma->iArg & 1)==0 ){ sqlite3_key_v2(db, zDb, zKey, i/2); }else{ sqlite3_rekey_v2(db, zDb, zKey, i/2); @@ -122179,7 +122674,8 @@ static const sqlite3_module pragmaVtabModule = { 0, /* xRename - rename the table */ 0, /* xSavepoint */ 0, /* xRelease */ - 0 /* xRollbackTo */ + 0, /* xRollbackTo */ + 0 /* xShadowName */ }; /* @@ -122532,8 +123028,8 @@ SQLITE_PRIVATE int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFl rc = SQLITE_NOMEM_BKPT; sqlite3ResetAllSchemasOfConnection(db); } - if( rc==SQLITE_OK || (db->flags&SQLITE_WriteSchema)){ - /* Black magic: If the SQLITE_WriteSchema flag is set, then consider + if( rc==SQLITE_OK || (db->flags&SQLITE_NoSchemaError)){ + /* Black magic: If the SQLITE_NoSchemaError flag is set, then consider ** the schema loaded, even if errors occurred. In this situation the ** current sqlite3_prepare() operation will fail, but the following one ** will attempt to compile the supplied statement against whatever subset @@ -122914,6 +123410,294 @@ static int sqlite3LockAndPrepare( return rc; } +#ifdef SQLITE_ENABLE_NORMALIZE +/* +** Checks if the specified token is a table, column, or function name, +** based on the databases associated with the statement being prepared. +** If the function fails, zero is returned and pRc is filled with the +** error code. +*/ +static int shouldTreatAsIdentifier( + sqlite3 *db, /* Database handle. */ + const char *zToken, /* Pointer to start of token to be checked */ + int nToken, /* Length of token to be checked */ + int *pRc /* Pointer to error code upon failure */ +){ + int bFound = 0; /* Non-zero if token is an identifier name. */ + int i, j; /* Database and column loop indexes. */ + Schema *pSchema; /* Schema for current database. */ + Hash *pHash; /* Hash table of tables for current database. */ + HashElem *e; /* Hash element for hash table iteration. */ + Table *pTab; /* Database table for columns being checked. */ + + if( sqlite3IsRowidN(zToken, nToken) ){ + return 1; + } + if( nToken>0 ){ + int hash = SQLITE_FUNC_HASH(sqlite3UpperToLower[(u8)zToken[0]], nToken); + if( sqlite3FunctionSearchN(hash, zToken, nToken) ) return 1; + } + assert( db!=0 ); + sqlite3_mutex_enter(db->mutex); + sqlite3BtreeEnterAll(db); + for(i=0; inDb; i++){ + pHash = &db->aFunc; + if( sqlite3HashFindN(pHash, zToken, nToken) ){ + bFound = 1; + break; + } + pSchema = db->aDb[i].pSchema; + if( pSchema==0 ) continue; + pHash = &pSchema->tblHash; + if( sqlite3HashFindN(pHash, zToken, nToken) ){ + bFound = 1; + break; + } + for(e=sqliteHashFirst(pHash); e; e=sqliteHashNext(e)){ + pTab = sqliteHashData(e); + if( pTab==0 ) continue; + pHash = pTab->pColHash; + if( pHash==0 ){ + pTab->pColHash = pHash = sqlite3_malloc(sizeof(Hash)); + if( pHash ){ + sqlite3HashInit(pHash); + for(j=0; jnCol; j++){ + Column *pCol = &pTab->aCol[j]; + sqlite3HashInsert(pHash, pCol->zName, pCol); + } + }else{ + *pRc = SQLITE_NOMEM_BKPT; + bFound = 0; + goto done; + } + } + if( pHash && sqlite3HashFindN(pHash, zToken, nToken) ){ + bFound = 1; + goto done; + } + } + } +done: + sqlite3BtreeLeaveAll(db); + sqlite3_mutex_leave(db->mutex); + return bFound; +} + +/* +** Attempt to estimate the final output buffer size needed for the fully +** normalized version of the specified SQL string. This should take into +** account any potential expansion that could occur (e.g. via IN clauses +** being expanded, etc). This size returned is the total number of bytes +** including the NUL terminator. +*/ +static int estimateNormalizedSize( + const char *zSql, /* The original SQL string */ + int nSql, /* Length of original SQL string */ + u8 prepFlags /* The flags passed to sqlite3_prepare_v3() */ +){ + int nOut = nSql + 4; + const char *z = zSql; + while( nOut0 ){ + zOut[j++] = '"'; + continue; + }else if( k==nToken-1 ){ + zOut[j++] = '"'; + continue; + } + } + if( bKeyword ){ + zOut[j++] = sqlite3Toupper(zSql[iIn+k]); + }else{ + zOut[j++] = sqlite3Tolower(zSql[iIn+k]); + } + } + *piOut = j; +} + +/* +** Perform normalization of the SQL contained in the prepared statement and +** store the result in the zNormSql field. The schema for the associated +** databases are consulted while performing the normalization in order to +** determine if a token appears to be an identifier. All identifiers are +** left intact in the normalized SQL and all literals are replaced with a +** single '?'. +*/ +SQLITE_PRIVATE void sqlite3Normalize( + Vdbe *pVdbe, /* VM being reprepared */ + const char *zSql, /* The original SQL string */ + int nSql, /* Size of the input string in bytes */ + u8 prepFlags /* The flags passed to sqlite3_prepare_v3() */ +){ + sqlite3 *db; /* Database handle. */ + char *z; /* The output string */ + int nZ; /* Size of the output string in bytes */ + int i; /* Next character to read from zSql[] */ + int j; /* Next character to fill in on z[] */ + int tokenType = 0; /* Type of the next token */ + int prevTokenType = 0; /* Type of the previous token, except spaces */ + int n; /* Size of the next token */ + int nParen = 0; /* Nesting level of parenthesis */ + Hash inHash; /* Table of parenthesis levels to output index. */ + + db = sqlite3VdbeDb(pVdbe); + assert( db!=0 ); + assert( pVdbe->zNormSql==0 ); + if( zSql==0 ) return; + nZ = estimateNormalizedSize(zSql, nSql, prepFlags); + z = sqlite3DbMallocRawNN(db, nZ); + if( z==0 ) return; + sqlite3HashInit(&inHash); + for(i=j=0; i0 ){ + sqlite3HashInsert(&inHash, zSql+nParen, 0); + assert( jj+6=0 ); + assert( nZ-1-j=0 ); + /* Fall through */ + } + case TK_MINUS: + case TK_SEMI: + case TK_PLUS: + case TK_STAR: + case TK_SLASH: + case TK_REM: + case TK_EQ: + case TK_LE: + case TK_NE: + case TK_LSHIFT: + case TK_LT: + case TK_RSHIFT: + case TK_GT: + case TK_GE: + case TK_BITOR: + case TK_CONCAT: + case TK_COMMA: + case TK_BITAND: + case TK_BITNOT: + case TK_DOT: + case TK_IN: + case TK_IS: + case TK_NOT: + case TK_NULL: + case TK_ID: { + if( tokenType==TK_NULL ){ + if( prevTokenType==TK_IS || prevTokenType==TK_NOT ){ + /* NULL is a keyword in this case, not a literal value */ + }else{ + /* Here the NULL is a literal value */ + z[j++] = '?'; + break; + } + } + if( j>0 && sqlite3IsIdChar(z[j-1]) && sqlite3IsIdChar(zSql[i]) ){ + z[j++] = ' '; + } + if( tokenType==TK_ID ){ + int i2 = i, n2 = n, rc = SQLITE_OK; + if( nParen>0 ){ + assert( nParen0 && z[j-1]==' ' ){ j--; } + if( j>0 && z[j-1]!=';' ){ z[j++] = ';'; } + z[j] = 0; + assert( jzNormSql = z; + sqlite3HashClear(&inHash); +} +#endif /* SQLITE_ENABLE_NORMALIZE */ + /* ** Rerun the compilation of a statement after a schema change. ** @@ -123926,7 +124710,7 @@ static void selectExprDefer( struct ExprList_item *pItem = &pEList->a[i]; if( pItem->u.x.iOrderByCol==0 ){ Expr *pExpr = pItem->pExpr; - Table *pTab = pExpr->pTab; + Table *pTab = pExpr->y.pTab; if( pExpr->op==TK_COLUMN && pExpr->iColumn>=0 && pTab && !IsVirtual(pTab) && (pTab->aCol[pExpr->iColumn].colFlags & COLFLAG_SORTERREF) ){ @@ -123949,12 +124733,12 @@ static void selectExprDefer( Expr *pNew = sqlite3PExpr(pParse, TK_COLUMN, 0, 0); if( pNew ){ pNew->iTable = pExpr->iTable; - pNew->pTab = pExpr->pTab; + pNew->y.pTab = pExpr->y.pTab; pNew->iColumn = pPk ? pPk->aiColumn[k] : -1; pExtra = sqlite3ExprListAppend(pParse, pExtra, pNew); } } - pSort->aDefer[nDefer].pTab = pExpr->pTab; + pSort->aDefer[nDefer].pTab = pExpr->y.pTab; pSort->aDefer[nDefer].iCsr = pExpr->iTable; pSort->aDefer[nDefer].nKey = nKey; nDefer++; @@ -124803,7 +125587,7 @@ static const char *columnTypeImpl( break; } - assert( pTab && pExpr->pTab==pTab ); + assert( pTab && pExpr->y.pTab==pTab ); if( pS ){ /* The "table" is actually a sub-select or a view in the FROM clause ** of the SELECT statement. Return the declaration type and origin @@ -124988,7 +125772,7 @@ static void generateColumnNames( assert( p!=0 ); assert( p->op!=TK_AGG_COLUMN ); /* Agg processing has not run yet */ - assert( p->op!=TK_COLUMN || p->pTab!=0 ); /* Covering idx not yet coded */ + assert( p->op!=TK_COLUMN || p->y.pTab!=0 ); /* Covering idx not yet coded */ if( pEList->a[i].zName ){ /* An AS clause always takes first priority */ char *zName = pEList->a[i].zName; @@ -124996,7 +125780,7 @@ static void generateColumnNames( }else if( srcName && p->op==TK_COLUMN ){ char *zCol; int iCol = p->iColumn; - pTab = p->pTab; + pTab = p->y.pTab; assert( pTab!=0 ); if( iCol<0 ) iCol = pTab->iPKey; assert( iCol==-1 || (iCol>=0 && iColnCol) ); @@ -125087,7 +125871,7 @@ SQLITE_PRIVATE int sqlite3ColumnsFromExprList( if( pColExpr->op==TK_COLUMN ){ /* For columns use the column name name */ int iCol = pColExpr->iColumn; - Table *pTab = pColExpr->pTab; + Table *pTab = pColExpr->y.pTab; assert( pTab!=0 ); if( iCol<0 ) iCol = pTab->iPKey; zName = iCol>=0 ? pTab->aCol[iCol].zName : "rowid"; @@ -125441,6 +126225,13 @@ static void generateWithRecursiveQuery( Expr *pLimit; /* Saved LIMIT and OFFSET */ int regLimit, regOffset; /* Registers used by LIMIT and OFFSET */ +#ifndef SQLITE_OMIT_WINDOWFUNC + if( p->pWin ){ + sqlite3ErrorMsg(pParse, "cannot use window functions in recursive queries"); + return; + } +#endif + /* Obtain authorization to do a recursive query */ if( sqlite3AuthCheck(pParse, SQLITE_RECURSIVE, 0, 0, 0) ) return; @@ -127190,7 +127981,7 @@ static int flattenSubquery( #endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */ /* -** A structure to keep track of all of the column values that fixed to +** A structure to keep track of all of the column values that are fixed to ** a known value due to WHERE clause constraints of the form COLUMN=VALUE. */ typedef struct WhereConst WhereConst; @@ -127202,13 +127993,28 @@ struct WhereConst { }; /* -** Add a new entry to the pConst object +** Add a new entry to the pConst object. Except, do not add duplicate +** pColumn entires. */ static void constInsert( - WhereConst *pConst, - Expr *pColumn, - Expr *pValue + WhereConst *pConst, /* The WhereConst into which we are inserting */ + Expr *pColumn, /* The COLUMN part of the constraint */ + Expr *pValue /* The VALUE part of the constraint */ ){ + int i; + assert( pColumn->op==TK_COLUMN ); + + /* 2018-10-25 ticket [cf5ed20f] + ** Make sure the same pColumn is not inserted more than once */ + for(i=0; inConst; i++){ + const Expr *pExpr = pConst->apExpr[i*2]; + assert( pExpr->op==TK_COLUMN ); + if( pExpr->iTable==pColumn->iTable + && pExpr->iColumn==pColumn->iColumn + ){ + return; /* Already present. Return without doing anything. */ + } + } pConst->nConst++; pConst->apExpr = sqlite3DbReallocOrFree(pConst->pParse->db, pConst->apExpr, @@ -131190,6 +131996,57 @@ SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *v, Table *pTab, int i, int iReg){ #endif } +/* +** Check to see if column iCol of index pIdx references any of the +** columns defined by aXRef and chngRowid. Return true if it does +** and false if not. This is an optimization. False-positives are a +** performance degradation, but false-negatives can result in a corrupt +** index and incorrect answers. +** +** aXRef[j] will be non-negative if column j of the original table is +** being updated. chngRowid will be true if the rowid of the table is +** being updated. +*/ +static int indexColumnIsBeingUpdated( + Index *pIdx, /* The index to check */ + int iCol, /* Which column of the index to check */ + int *aXRef, /* aXRef[j]>=0 if column j is being updated */ + int chngRowid /* true if the rowid is being updated */ +){ + i16 iIdxCol = pIdx->aiColumn[iCol]; + assert( iIdxCol!=XN_ROWID ); /* Cannot index rowid */ + if( iIdxCol>=0 ){ + return aXRef[iIdxCol]>=0; + } + assert( iIdxCol==XN_EXPR ); + assert( pIdx->aColExpr!=0 ); + assert( pIdx->aColExpr->a[iCol].pExpr!=0 ); + return sqlite3ExprReferencesUpdatedColumn(pIdx->aColExpr->a[iCol].pExpr, + aXRef,chngRowid); +} + +/* +** Check to see if index pIdx is a partial index whose conditional +** expression might change values due to an UPDATE. Return true if +** the index is subject to change and false if the index is guaranteed +** to be unchanged. This is an optimization. False-positives are a +** performance degradation, but false-negatives can result in a corrupt +** index and incorrect answers. +** +** aXRef[j] will be non-negative if column j of the original table is +** being updated. chngRowid will be true if the rowid of the table is +** being updated. +*/ +static int indexWhereClauseMightChange( + Index *pIdx, /* The index to check */ + int *aXRef, /* aXRef[j]>=0 if column j is being updated */ + int chngRowid /* true if the rowid is being updated */ +){ + if( pIdx->pPartIdxWhere==0 ) return 0; + return sqlite3ExprReferencesUpdatedColumn(pIdx->pPartIdxWhere, + aXRef, chngRowid); +} + /* ** Process an UPDATE statement. ** @@ -131413,19 +132270,18 @@ SQLITE_PRIVATE void sqlite3Update( /* There is one entry in the aRegIdx[] array for each index on the table ** being updated. Fill in aRegIdx[] with a register number that will hold ** the key for accessing each index. - ** - ** FIXME: Be smarter about omitting indexes that use expressions. */ for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ int reg; - if( chngKey || hasFK>1 || pIdx->pPartIdxWhere || pIdx==pPk ){ + if( chngKey || hasFK>1 || pIdx==pPk + || indexWhereClauseMightChange(pIdx,aXRef,chngRowid) + ){ reg = ++pParse->nMem; pParse->nMem += pIdx->nColumn; }else{ reg = 0; for(i=0; inKeyCol; i++){ - i16 iIdxCol = pIdx->aiColumn[i]; - if( iIdxCol<0 || aXRef[iIdxCol]>=0 ){ + if( indexColumnIsBeingUpdated(pIdx, i, aXRef, chngRowid) ){ reg = ++pParse->nMem; pParse->nMem += pIdx->nColumn; if( (onError==OE_Replace) @@ -131974,7 +132830,7 @@ static void updateVirtualTable( sqlite3ExprCode(pParse, pChanges->a[aXRef[i]].pExpr, regArg+2+i); }else{ sqlite3VdbeAddOp3(v, OP_VColumn, iCsr, i, regArg+2+i); - sqlite3VdbeChangeP5(v, 1); /* Enable sqlite3_vtab_nochange() */ + sqlite3VdbeChangeP5(v, OPFLAG_NOCHNG);/* Enable sqlite3_vtab_nochange() */ } } if( HasRowid(pTab) ){ @@ -132475,7 +133331,8 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){ saved_mTrace = db->mTrace; db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks; db->mDbFlags |= DBFLAG_PreferBuiltin | DBFLAG_Vacuum; - db->flags &= ~(SQLITE_ForeignKeys | SQLITE_ReverseOrder | SQLITE_CountRows); + db->flags &= ~(SQLITE_ForeignKeys | SQLITE_ReverseOrder + | SQLITE_Defensive | SQLITE_CountRows); db->mTrace = 0; zDbMain = db->aDb[iDb].zDbSName; @@ -133017,7 +133874,6 @@ SQLITE_PRIVATE void sqlite3VtabBeginParse( Token *pModuleName, /* Name of the module for the virtual table */ int ifNotExists /* No error if the table already exists */ ){ - int iDb; /* The database the table is being created in */ Table *pTable; /* The new virtual table */ sqlite3 *db; /* Database connection */ @@ -133027,8 +133883,6 @@ SQLITE_PRIVATE void sqlite3VtabBeginParse( assert( 0==pTable->pIndex ); db = pParse->db; - iDb = sqlite3SchemaToIndex(db, pTable->pSchema); - assert( iDb>=0 ); assert( pTable->nModuleArg==0 ); addModuleArgument(db, pTable, sqlite3NameFromToken(db, pModuleName)); @@ -133048,6 +133902,8 @@ SQLITE_PRIVATE void sqlite3VtabBeginParse( ** The second call, to obtain permission to create the table, is made now. */ if( pTable->azModuleArg ){ + int iDb = sqlite3SchemaToIndex(db, pTable->pSchema); + assert( iDb>=0 ); /* The database the table is being created in */ sqlite3AuthCheck(pParse, SQLITE_CREATE_VTABLE, pTable->zName, pTable->azModuleArg[0], pParse->db->aDb[iDb].zDbSName); } @@ -133742,7 +134598,7 @@ SQLITE_PRIVATE FuncDef *sqlite3VtabOverloadFunction( /* Check to see the left operand is a column in a virtual table */ if( NEVER(pExpr==0) ) return pDef; if( pExpr->op!=TK_COLUMN ) return pDef; - pTab = pExpr->pTab; + pTab = pExpr->y.pTab; if( pTab==0 ) return pDef; if( !IsVirtual(pTab) ) return pDef; pVtab = sqlite3GetVTable(db, pTab)->pVtab; @@ -134362,12 +135218,33 @@ struct WhereLoopBuilder { int nRecValid; /* Number of valid fields currently in pRec */ #endif unsigned int bldFlags; /* SQLITE_BLDF_* flags */ + unsigned int iPlanLimit; /* Search limiter */ }; /* Allowed values for WhereLoopBuider.bldFlags */ #define SQLITE_BLDF_INDEXED 0x0001 /* An index is used */ #define SQLITE_BLDF_UNIQUE 0x0002 /* All keys of a UNIQUE index used */ +/* The WhereLoopBuilder.iPlanLimit is used to limit the number of +** index+constraint combinations the query planner will consider for a +** particular query. If this parameter is unlimited, then certain +** pathological queries can spend excess time in the sqlite3WhereBegin() +** routine. The limit is high enough that is should not impact real-world +** queries. +** +** SQLITE_QUERY_PLANNER_LIMIT is the baseline limit. The limit is +** increased by SQLITE_QUERY_PLANNER_LIMIT_INCR before each term of the FROM +** clause is processed, so that every table in a join is guaranteed to be +** able to propose a some index+constraint combinations even if the initial +** baseline limit was exhausted by prior tables of the join. +*/ +#ifndef SQLITE_QUERY_PLANNER_LIMIT +# define SQLITE_QUERY_PLANNER_LIMIT 20000 +#endif +#ifndef SQLITE_QUERY_PLANNER_LIMIT_INCR +# define SQLITE_QUERY_PLANNER_LIMIT_INCR 1000 +#endif + /* ** The WHERE clause processing routine has two halves. The ** first part does the start of the WHERE loop and the second @@ -134929,7 +135806,7 @@ static Expr *removeUnindexableInClauseTerms( for(i=iEq; inLTerm; i++){ if( pLoop->aLTerm[i]->pExpr==pX ){ int iField = pLoop->aLTerm[i]->iField - 1; - assert( pOrigRhs->a[iField].pExpr!=0 ); + if( pOrigRhs->a[iField].pExpr==0 ) continue; /* Duplicate PK column */ pRhs = sqlite3ExprListAppend(pParse, pRhs, pOrigRhs->a[iField].pExpr); pOrigRhs->a[iField].pExpr = 0; assert( pOrigLhs->a[iField].pExpr!=0 ); @@ -135621,7 +136498,7 @@ static int whereIndexExprTransNode(Walker *p, Expr *pExpr){ pExpr->op = TK_COLUMN; pExpr->iTable = pX->iIdxCur; pExpr->iColumn = pX->iIdxCol; - pExpr->pTab = 0; + pExpr->y.pTab = 0; return WRC_Prune; }else{ return WRC_Continue; @@ -137021,7 +137898,7 @@ static int isLikeOrGlob( ){ if( pLeft->op!=TK_COLUMN || sqlite3ExprAffinity(pLeft)!=SQLITE_AFF_TEXT - || IsVirtual(pLeft->pTab) /* Value might be numeric */ + || IsVirtual(pLeft->y.pTab) /* Value might be numeric */ ){ sqlite3ExprDelete(db, pPrefix); sqlite3ValueFree(pVal); @@ -137122,7 +137999,7 @@ static int isAuxiliaryVtabOperator( ** MATCH(expression,vtab_column) */ pCol = pList->a[1].pExpr; - if( pCol->op==TK_COLUMN && IsVirtual(pCol->pTab) ){ + if( pCol->op==TK_COLUMN && IsVirtual(pCol->y.pTab) ){ for(i=0; iu.zToken, aOp[i].zOp)==0 ){ *peOp2 = aOp[i].eOp2; @@ -137144,12 +138021,12 @@ static int isAuxiliaryVtabOperator( ** with function names in an arbitrary case. */ pCol = pList->a[0].pExpr; - if( pCol->op==TK_COLUMN && IsVirtual(pCol->pTab) ){ + if( pCol->op==TK_COLUMN && IsVirtual(pCol->y.pTab) ){ sqlite3_vtab *pVtab; sqlite3_module *pMod; void (*xNotUsed)(sqlite3_context*,int,sqlite3_value**); void *pNotUsed; - pVtab = sqlite3GetVTable(db, pCol->pTab)->pVtab; + pVtab = sqlite3GetVTable(db, pCol->y.pTab)->pVtab; assert( pVtab!=0 ); assert( pVtab->pModule!=0 ); pMod = (sqlite3_module *)pVtab->pModule; @@ -137167,10 +138044,10 @@ static int isAuxiliaryVtabOperator( int res = 0; Expr *pLeft = pExpr->pLeft; Expr *pRight = pExpr->pRight; - if( pLeft->op==TK_COLUMN && IsVirtual(pLeft->pTab) ){ + if( pLeft->op==TK_COLUMN && IsVirtual(pLeft->y.pTab) ){ res++; } - if( pRight && pRight->op==TK_COLUMN && IsVirtual(pRight->pTab) ){ + if( pRight && pRight->op==TK_COLUMN && IsVirtual(pRight->y.pTab) ){ res++; SWAP(Expr*, pLeft, pRight); } @@ -138122,6 +138999,7 @@ static void exprAnalyze( if( pExpr->op==TK_NOTNULL && pExpr->pLeft->op==TK_COLUMN && pExpr->pLeft->iColumn>=0 + && !ExprHasProperty(pExpr, EP_FromJoin) && OptimizationEnabled(db, SQLITE_Stat34) ){ Expr *pNewExpr; @@ -138313,6 +139191,7 @@ SQLITE_PRIVATE void sqlite3WhereTabFuncArgs( pArgs = pItem->u1.pFuncArg; if( pArgs==0 ) return; for(j=k=0; jnExpr; j++){ + Expr *pRhs; while( knCol && (pTab->aCol[k].colFlags & COLFLAG_HIDDEN)==0 ){k++;} if( k>=pTab->nCol ){ sqlite3ErrorMsg(pParse, "too many arguments on %s() - max %d", @@ -138323,9 +139202,10 @@ SQLITE_PRIVATE void sqlite3WhereTabFuncArgs( if( pColRef==0 ) return; pColRef->iTable = pItem->iCursor; pColRef->iColumn = k++; - pColRef->pTab = pTab; - pTerm = sqlite3PExpr(pParse, TK_EQ, pColRef, - sqlite3ExprDup(pParse->db, pArgs->a[j].pExpr, 0)); + pColRef->y.pTab = pTab; + pRhs = sqlite3PExpr(pParse, TK_UPLUS, + sqlite3ExprDup(pParse->db, pArgs->a[j].pExpr, 0), 0); + pTerm = sqlite3PExpr(pParse, TK_EQ, pColRef, pRhs); whereClauseInsert(pWC, pTerm, TERM_DYNAMIC); } } @@ -139188,7 +140068,6 @@ static void constructAutomaticIndex( translateColumnToCopy(pParse, addrTop, pLevel->iTabCur, pTabItem->regResult, 1); sqlite3VdbeGoto(v, addrTop); - pTabItem->fg.viaCoroutine = 0; }else{ sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1); VdbeCoverage(v); } @@ -139366,9 +140245,11 @@ static sqlite3_index_info *allocateIndexInfo( ** method of the virtual table with the sqlite3_index_info object that ** comes in as the 3rd argument to this function. ** -** If an error occurs, pParse is populated with an error message and a -** non-zero value is returned. Otherwise, 0 is returned and the output -** part of the sqlite3_index_info structure is left populated. +** If an error occurs, pParse is populated with an error message and an +** appropriate error code is returned. A return of SQLITE_CONSTRAINT from +** xBestIndex is not considered an error. SQLITE_CONSTRAINT indicates that +** the current configuration of "unusable" flags in sqlite3_index_info can +** not result in a valid plan. ** ** Whether or not an error is returned, it is the responsibility of the ** caller to eventually free p->idxStr if p->needToFreeIdxStr indicates @@ -139382,7 +140263,7 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){ rc = pVtab->pModule->xBestIndex(pVtab, p); TRACE_IDX_OUTPUTS(p); - if( rc!=SQLITE_OK ){ + if( rc!=SQLITE_OK && rc!=SQLITE_CONSTRAINT ){ if( rc==SQLITE_NOMEM ){ sqlite3OomFault(pParse->db); }else if( !pVtab->zErrMsg ){ @@ -139393,19 +140274,7 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){ } sqlite3_free(pVtab->zErrMsg); pVtab->zErrMsg = 0; - -#if 0 - /* This error is now caught by the caller. - ** Search for "xBestIndex malfunction" below */ - for(i=0; inConstraint; i++){ - if( !p->aConstraint[i].usable && p->aConstraintUsage[i].argvIndex>0 ){ - sqlite3ErrorMsg(pParse, - "table %s: xBestIndex returned an invalid plan", pTab->zName); - } - } -#endif - - return pParse->nErr; + return rc; } #endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) */ @@ -140460,6 +141329,14 @@ static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){ sqlite3 *db = pWInfo->pParse->db; int rc; + /* Stop the search once we hit the query planner search limit */ + if( pBuilder->iPlanLimit==0 ){ + WHERETRACE(0xffffffff,("=== query planner search limit reached ===\n")); + if( pBuilder->pOrSet ) pBuilder->pOrSet->n = 0; + return SQLITE_DONE; + } + pBuilder->iPlanLimit--; + /* If pBuilder->pOrSet is defined, then only keep track of the costs ** and prereqs. */ @@ -141470,7 +142347,17 @@ static int whereLoopAddVirtualOne( /* Invoke the virtual table xBestIndex() method */ rc = vtabBestIndex(pParse, pSrc->pTab, pIdxInfo); - if( rc ) return rc; + if( rc ){ + if( rc==SQLITE_CONSTRAINT ){ + /* If the xBestIndex method returns SQLITE_CONSTRAINT, that means + ** that the particular combination of parameters provided is unusable. + ** Make no entries in the loop table. + */ + WHERETRACE(0xffff, (" ^^^^--- non-viable plan rejected!\n")); + return SQLITE_OK; + } + return rc; + } mxTerm = -1; assert( pNew->nLSlot>=nConstraint ); @@ -141866,9 +142753,11 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){ /* Loop over the tables in the join, from left to right */ pNew = pBuilder->pNew; whereLoopInit(pNew); + pBuilder->iPlanLimit = SQLITE_QUERY_PLANNER_LIMIT; for(iTab=0, pItem=pTabList->a; pItemiTab = iTab; + pBuilder->iPlanLimit += SQLITE_QUERY_PLANNER_LIMIT_INCR; pNew->maskSelf = sqlite3WhereGetMask(&pWInfo->sMaskSet, pItem->iCursor); if( ((pItem->fg.jointype|priorJointype) & (JT_LEFT|JT_CROSS))!=0 ){ /* This condition is true when pItem is the FROM clause term on the @@ -141894,7 +142783,15 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){ rc = whereLoopAddOr(pBuilder, mPrereq, mUnusable); } mPrior |= pNew->maskSelf; - if( rc || db->mallocFailed ) break; + if( rc || db->mallocFailed ){ + if( rc==SQLITE_DONE ){ + /* We hit the query planner search limit set by iPlanLimit */ + sqlite3_log(SQLITE_WARNING, "abbreviated query algorithm search"); + rc = SQLITE_OK; + }else{ + break; + } + } } whereLoopClear(db, pNew); @@ -144276,12 +145173,12 @@ static int selectWindowRewriteExprCb(Walker *pWalker, Expr *pExpr){ switch( pExpr->op ){ case TK_FUNCTION: - if( pExpr->pWin==0 ){ + if( !ExprHasProperty(pExpr, EP_WinFunc) ){ break; }else{ Window *pWin; for(pWin=p->pWin; pWin; pWin=pWin->pNextWin){ - if( pExpr->pWin==pWin ){ + if( pExpr->y.pWin==pWin ){ assert( pWin->pOwner==pExpr ); return WRC_Prune; } @@ -144398,7 +145295,7 @@ static ExprList *exprListAppendList( */ SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){ int rc = SQLITE_OK; - if( p->pWin ){ + if( p->pWin && p->pPrior==0 ){ Vdbe *v = sqlite3GetVdbe(pParse); sqlite3 *db = pParse->db; Select *pSub = 0; /* The subquery */ @@ -144611,11 +145508,13 @@ windowAllocErr: */ SQLITE_PRIVATE void sqlite3WindowAttach(Parse *pParse, Expr *p, Window *pWin){ if( p ){ + assert( p->op==TK_FUNCTION ); /* This routine is only called for the parser. If pWin was not ** allocated due to an OOM, then the parser would fail before ever ** invoking this routine */ if( ALWAYS(pWin) ){ - p->pWin = pWin; + p->y.pWin = pWin; + ExprSetProperty(p, EP_WinFunc); pWin->pOwner = p; if( p->flags & EP_Distinct ){ sqlite3ErrorMsg(pParse, @@ -145778,7 +146677,7 @@ static void windowCodeDefaultStep( */ SQLITE_PRIVATE Window *sqlite3WindowDup(sqlite3 *db, Expr *pOwner, Window *p){ Window *pNew = 0; - if( p ){ + if( ALWAYS(p) ){ pNew = sqlite3DbMallocZero(db, sizeof(Window)); if( pNew ){ pNew->zName = sqlite3DbStrDup(db, p->zName); @@ -145930,6 +146829,7 @@ SQLITE_PRIVATE void sqlite3WindowCodeStep( ** input grammar file: */ /* #include */ +/* #include */ /************ Begin %include sections from the grammar ************************/ /* #include "sqliteInt.h" */ @@ -146031,13 +146931,10 @@ static void disableLookaside(Parse *pParse){ p->pLeft = p->pRight = 0; p->x.pList = 0; p->pAggInfo = 0; - p->pTab = 0; + p->y.pTab = 0; p->op2 = 0; p->iTable = 0; p->iColumn = 0; -#ifndef SQLITE_OMIT_WINDOWFUNC - p->pWin = 0; -#endif p->u.zToken = (char*)&p[1]; memcpy(p->u.zToken, t.z, t.n); p->u.zToken[t.n] = 0; @@ -150229,10 +151126,9 @@ SQLITE_PRIVATE void sqlite3Parser( yymajor = YYNOCODE; }else{ while( yypParser->yytos >= yypParser->yystack - && yymx != YYERRORSYMBOL && (yyact = yy_find_reduce_action( yypParser->yytos->stateno, - YYERRORSYMBOL)) >= YY_MIN_REDUCE + YYERRORSYMBOL)) > YY_MAX_SHIFTREDUCE ){ yy_pop_parser_stack(yypParser); } @@ -151199,6 +152095,73 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){ return i; } +#ifdef SQLITE_ENABLE_NORMALIZE +/* +** Return the length (in bytes) of the token that begins at z[0]. +** Store the token type in *tokenType before returning. If flags has +** SQLITE_TOKEN_NORMALIZE flag enabled, use the identifier token type +** for keywords. Add SQLITE_TOKEN_QUOTED to flags if the token was +** actually a quoted identifier. Add SQLITE_TOKEN_KEYWORD to flags +** if the token was recognized as a keyword; this is useful when the +** SQLITE_TOKEN_NORMALIZE flag is used, because it enables the caller +** to differentiate between a keyword being treated as an identifier +** (for normalization purposes) and an actual identifier. +*/ +SQLITE_PRIVATE int sqlite3GetTokenNormalized( + const unsigned char *z, + int *tokenType, + int *flags +){ + int n; + unsigned char iClass = aiClass[*z]; + if( iClass==CC_KYWD ){ + int i; + for(i=1; aiClass[z[i]]<=CC_KYWD; i++){} + if( IdChar(z[i]) ){ + /* This token started out using characters that can appear in keywords, + ** but z[i] is a character not allowed within keywords, so this must + ** be an identifier instead */ + i++; + while( IdChar(z[i]) ){ i++; } + *tokenType = TK_ID; + return i; + } + *tokenType = TK_ID; + n = keywordCode((char*)z, i, tokenType); + /* If the token is no longer considered to be an identifier, then it is a + ** keyword of some kind. Make the token back into an identifier and then + ** set the SQLITE_TOKEN_KEYWORD flag. Several non-identifier tokens are + ** used verbatim, including IN, IS, NOT, and NULL. */ + switch( *tokenType ){ + case TK_ID: { + /* do nothing, handled by caller */ + break; + } + case TK_IN: + case TK_IS: + case TK_NOT: + case TK_NULL: { + *flags |= SQLITE_TOKEN_KEYWORD; + break; + } + default: { + *tokenType = TK_ID; + *flags |= SQLITE_TOKEN_KEYWORD; + break; + } + } + }else{ + n = sqlite3GetToken(z, tokenType); + /* If the token is considered to be an identifier and the character class + ** of the first character is a quote, set the SQLITE_TOKEN_QUOTED flag. */ + if( *tokenType==TK_ID && (iClass==CC_QUOTE || iClass==CC_QUOTE2) ){ + *flags |= SQLITE_TOKEN_QUOTED; + } + } + return n; +} +#endif /* SQLITE_ENABLE_NORMALIZE */ + /* ** Run the parser on the given SQL string. The parser structure is ** passed in. An SQLITE_ status code is returned. If an error occurs @@ -152596,6 +153559,7 @@ SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){ { SQLITE_DBCONFIG_ENABLE_QPSG, SQLITE_EnableQPSG }, { SQLITE_DBCONFIG_TRIGGER_EQP, SQLITE_TriggerEQP }, { SQLITE_DBCONFIG_RESET_DATABASE, SQLITE_ResetDatabase }, + { SQLITE_DBCONFIG_DEFENSIVE, SQLITE_Defensive }, }; unsigned int i; rc = SQLITE_ERROR; /* IMP: R-42790-23372 */ @@ -154822,6 +155786,9 @@ static int openDatabase( #endif #if defined(SQLITE_ENABLE_QPSG) | SQLITE_EnableQPSG +#endif +#if defined(SQLITE_DEFAULT_DEFENSIVE) + | SQLITE_Defensive #endif ; sqlite3HashInit(&db->aCollSeq); @@ -155710,15 +156677,26 @@ SQLITE_API int sqlite3_test_control(int op, ...){ /* sqlite3_test_control(SQLITE_TESTCTRL_LOCALTIME_FAULT, int onoff); ** - ** If parameter onoff is non-zero, configure the wrappers so that all - ** subsequent calls to localtime() and variants fail. If onoff is zero, - ** undo this setting. + ** If parameter onoff is non-zero, subsequent calls to localtime() + ** and its variants fail. If onoff is zero, undo this setting. */ case SQLITE_TESTCTRL_LOCALTIME_FAULT: { sqlite3GlobalConfig.bLocaltimeFault = va_arg(ap, int); break; } + /* sqlite3_test_control(SQLITE_TESTCTRL_INTERNAL_FUNCS, int onoff); + ** + ** If parameter onoff is non-zero, internal-use-only SQL functions + ** are visible to ordinary SQL. This is useful for testing but is + ** unsafe because invalid parameters to those internal-use-only functions + ** can result in crashes or segfaults. + */ + case SQLITE_TESTCTRL_INTERNAL_FUNCTIONS: { + sqlite3GlobalConfig.bInternalFunctions = va_arg(ap, int); + break; + } + /* sqlite3_test_control(SQLITE_TESTCTRL_NEVER_CORRUPT, int); ** ** Set or clear a flag that indicates that the database file is always well- @@ -159161,7 +160139,7 @@ static int fts3ScanInteriorNode( const char *zCsr = zNode; /* Cursor to iterate through node */ const char *zEnd = &zCsr[nNode];/* End of interior node buffer */ char *zBuffer = 0; /* Buffer to load terms into */ - int nAlloc = 0; /* Size of allocated buffer */ + i64 nAlloc = 0; /* Size of allocated buffer */ int isFirstTerm = 1; /* True when processing first term on page */ sqlite3_int64 iChild; /* Block id of child node to descend to */ @@ -159199,14 +160177,14 @@ static int fts3ScanInteriorNode( zCsr += fts3GetVarint32(zCsr, &nSuffix); assert( nPrefix>=0 && nSuffix>=0 ); - if( &zCsr[nSuffix]>zEnd ){ + if( nPrefix>zCsr-zNode || nSuffix>zEnd-zCsr ){ rc = FTS_CORRUPT_VTAB; goto finish_scan; } - if( nPrefix+nSuffix>nAlloc ){ + if( (i64)nPrefix+nSuffix>nAlloc ){ char *zNew; - nAlloc = (nPrefix+nSuffix) * 2; - zNew = (char *)sqlite3_realloc(zBuffer, nAlloc); + nAlloc = ((i64)nPrefix+nSuffix) * 2; + zNew = (char *)sqlite3_realloc64(zBuffer, nAlloc); if( !zNew ){ rc = SQLITE_NOMEM; goto finish_scan; @@ -161186,8 +162164,23 @@ static int fts3RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){ return SQLITE_OK; } +/* +** Return true if zName is the extension on one of the shadow tables used +** by this module. +*/ +static int fts3ShadowName(const char *zName){ + static const char *azName[] = { + "content", "docsize", "segdir", "segments", "stat", + }; + unsigned int i; + for(i=0; i&pReader->aNode[pReader->nNode] + if( nSuffix<=0 + || (&pReader->aNode[pReader->nNode] - pNext)pReader->nTermAlloc ){ return FTS_CORRUPT_VTAB; } - if( nPrefix+nSuffix>pReader->nTermAlloc ){ - int nNew = (nPrefix+nSuffix)*2; - char *zNew = sqlite3_realloc(pReader->zTerm, nNew); + /* Both nPrefix and nSuffix were read by fts3GetVarint32() and so are + ** between 0 and 0x7FFFFFFF. But the sum of the two may cause integer + ** overflow - hence the (i64) casts. */ + if( (i64)nPrefix+nSuffix>(i64)pReader->nTermAlloc ){ + i64 nNew = ((i64)nPrefix+nSuffix)*2; + char *zNew = sqlite3_realloc64(pReader->zTerm, nNew); if( !zNew ){ return SQLITE_NOMEM; } @@ -168818,7 +169820,7 @@ static int fts3SegReaderNext( ** b-tree node. And that the final byte of the doclist is 0x00. If either ** of these statements is untrue, then the data structure is corrupt. */ - if( &pReader->aDoclist[pReader->nDoclist]>&pReader->aNode[pReader->nNode] + if( (&pReader->aNode[pReader->nNode] - pReader->aDoclist)nDoclist || (pReader->nPopulate==0 && pReader->aDoclist[pReader->nDoclist-1]) ){ return FTS_CORRUPT_VTAB; @@ -171144,6 +172146,9 @@ static int nodeReaderNext(NodeReader *p){ } p->iOff += fts3GetVarint32(&p->aNode[p->iOff], &nSuffix); + if( nPrefix>p->iOff || nSuffix>p->nNode-p->iOff ){ + return SQLITE_CORRUPT_VTAB; + } blobGrowBuffer(&p->term, nPrefix+nSuffix, &rc); if( rc==SQLITE_OK ){ memcpy(&p->term.a[nPrefix], &p->aNode[p->iOff], nSuffix); @@ -171151,6 +172156,9 @@ static int nodeReaderNext(NodeReader *p){ p->iOff += nSuffix; if( p->iChild==0 ){ p->iOff += fts3GetVarint32(&p->aNode[p->iOff], &p->nDoclist); + if( (p->nNode-p->iOff)nDoclist ){ + return SQLITE_CORRUPT_VTAB; + } p->aDoclist = &p->aNode[p->iOff]; p->iOff += p->nDoclist; } @@ -171158,7 +172166,6 @@ static int nodeReaderNext(NodeReader *p){ } assert( p->iOff<=p->nNode ); - return rc; } @@ -177568,6 +178575,9 @@ static int jsonEachConnect( #define JEACH_PARENT 5 #define JEACH_FULLKEY 6 #define JEACH_PATH 7 +/* The xBestIndex method assumes that the JSON and ROOT columns are +** the last two columns in the table. Should this ever changes, be +** sure to update the xBestIndex method. */ #define JEACH_JSON 8 #define JEACH_ROOT 9 @@ -177825,35 +178835,54 @@ static int jsonEachBestIndex( sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo ){ - int i; - int jsonIdx = -1; - int rootIdx = -1; + int i; /* Loop counter or computed array index */ + int aIdx[2]; /* Index of constraints for JSON and ROOT */ + int unusableMask = 0; /* Mask of unusable JSON and ROOT constraints */ + int idxMask = 0; /* Mask of usable == constraints JSON and ROOT */ const struct sqlite3_index_constraint *pConstraint; + /* This implementation assumes that JSON and ROOT are the last two + ** columns in the table */ + assert( JEACH_ROOT == JEACH_JSON+1 ); UNUSED_PARAM(tab); + aIdx[0] = aIdx[1] = -1; pConstraint = pIdxInfo->aConstraint; for(i=0; inConstraint; i++, pConstraint++){ - if( pConstraint->usable==0 ) continue; - if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue; - switch( pConstraint->iColumn ){ - case JEACH_JSON: jsonIdx = i; break; - case JEACH_ROOT: rootIdx = i; break; - default: /* no-op */ break; + int iCol; + int iMask; + if( pConstraint->iColumn < JEACH_JSON ) continue; + iCol = pConstraint->iColumn - JEACH_JSON; + assert( iCol==0 || iCol==1 ); + iMask = 1 << iCol; + if( pConstraint->usable==0 ){ + unusableMask |= iMask; + }else if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ ){ + aIdx[iCol] = i; + idxMask |= iMask; } } - if( jsonIdx<0 ){ + if( (unusableMask & ~idxMask)!=0 ){ + /* If there are any unusable constraints on JSON or ROOT, then reject + ** this entire plan */ + return SQLITE_CONSTRAINT; + } + if( aIdx[0]<0 ){ + /* No JSON input. Leave estimatedCost at the huge value that it was + ** initialized to to discourage the query planner from selecting this + ** plan. */ pIdxInfo->idxNum = 0; - pIdxInfo->estimatedCost = 1e99; }else{ pIdxInfo->estimatedCost = 1.0; - pIdxInfo->aConstraintUsage[jsonIdx].argvIndex = 1; - pIdxInfo->aConstraintUsage[jsonIdx].omit = 1; - if( rootIdx<0 ){ - pIdxInfo->idxNum = 1; + i = aIdx[0]; + pIdxInfo->aConstraintUsage[i].argvIndex = 1; + pIdxInfo->aConstraintUsage[i].omit = 1; + if( aIdx[1]<0 ){ + pIdxInfo->idxNum = 1; /* Only JSON supplied. Plan 1 */ }else{ - pIdxInfo->aConstraintUsage[rootIdx].argvIndex = 2; - pIdxInfo->aConstraintUsage[rootIdx].omit = 1; - pIdxInfo->idxNum = 3; + i = aIdx[1]; + pIdxInfo->aConstraintUsage[i].argvIndex = 2; + pIdxInfo->aConstraintUsage[i].omit = 1; + pIdxInfo->idxNum = 3; /* Both JSON and ROOT are supplied. Plan 3 */ } } return SQLITE_OK; @@ -177962,7 +178991,8 @@ static sqlite3_module jsonEachModule = { 0, /* xRename */ 0, /* xSavepoint */ 0, /* xRelease */ - 0 /* xRollbackTo */ + 0, /* xRollbackTo */ + 0 /* xShadowName */ }; /* The methods of the json_tree virtual table. */ @@ -177989,7 +179019,8 @@ static sqlite3_module jsonTreeModule = { 0, /* xRename */ 0, /* xSavepoint */ 0, /* xRelease */ - 0 /* xRollbackTo */ + 0, /* xRollbackTo */ + 0 /* xShadowName */ }; #endif /* SQLITE_OMIT_VIRTUALTABLE */ @@ -181419,8 +182450,24 @@ static int rtreeQueryStat1(sqlite3 *db, Rtree *pRtree){ return rc; } + +/* +** Return true if zName is the extension on one of the shadow tables used +** by this module. +*/ +static int rtreeShadowName(const char *zName){ + static const char *azName[] = { + "node", "parent", "rowid" + }; + unsigned int i; + for(i=0; iz[0] && safe_isspace(p->z[0]) ) p->z++; + while( safe_isspace(p->z[0]) ) p->z++; return p->z[0]; } @@ -182485,7 +183543,7 @@ static int geopolyParseNumber(GeoParse *p, GeoCoord *pVal){ if( c=='0' && z[j+1]>='0' && z[j+1]<='9' ) return 0; for(;; j++){ c = z[j]; - if( c>='0' && c<='9' ) continue; + if( safe_isdigit(c) ) continue; if( c=='.' ){ if( z[j-1]=='-' ) return 0; if( seenDP ) return 0; @@ -182507,7 +183565,17 @@ static int geopolyParseNumber(GeoParse *p, GeoCoord *pVal){ break; } if( z[j-1]<'0' ) return 0; - if( pVal ) *pVal = (GeoCoord)atof((const char*)p->z); + if( pVal ){ +#ifdef SQLITE_AMALGAMATION + /* The sqlite3AtoF() routine is much much faster than atof(), if it + ** is available */ + double r; + (void)sqlite3AtoF((const char*)p->z, &r, j, SQLITE_UTF8); + *pVal = r; +#else + *pVal = (GeoCoord)atof((const char*)p->z); +#endif + } p->z += j; return 1; } @@ -182565,12 +183633,10 @@ static GeoPoly *geopolyParseJson(const unsigned char *z, int *pRc){ && s.a[1]==s.a[s.nVertex*2-1] && (s.z++, geopolySkipSpace(&s)==0) ){ - int nByte; GeoPoly *pOut; int x = 1; s.nVertex--; /* Remove the redundant vertex at the end */ - nByte = sizeof(GeoPoly) * s.nVertex*2*sizeof(GeoCoord); - pOut = sqlite3_malloc64( nByte ); + pOut = sqlite3_malloc64( GEOPOLY_SZ(s.nVertex) ); x = 1; if( pOut==0 ) goto parse_json_err; pOut->nVertex = s.nVertex; @@ -182773,6 +183839,27 @@ static void geopolyXformFunc( } } +/* +** Compute the area enclosed by the polygon. +** +** This routine can also be used to detect polygons that rotate in +** the wrong direction. Polygons are suppose to be counter-clockwise (CCW). +** This routine returns a negative value for clockwise (CW) polygons. +*/ +static double geopolyArea(GeoPoly *p){ + double rArea = 0.0; + int ii; + for(ii=0; iinVertex-1; ii++){ + rArea += (p->a[ii*2] - p->a[ii*2+2]) /* (x0 - x1) */ + * (p->a[ii*2+1] + p->a[ii*2+3]) /* (y0 + y1) */ + * 0.5; + } + rArea += (p->a[ii*2] - p->a[0]) /* (xN - x0) */ + * (p->a[ii*2+1] + p->a[1]) /* (yN + y0) */ + * 0.5; + return rArea; +} + /* ** Implementation of the geopoly_area(X) function. ** @@ -182788,21 +183875,106 @@ static void geopolyAreaFunc( ){ GeoPoly *p = geopolyFuncParam(context, argv[0], 0); if( p ){ - double rArea = 0.0; - int ii; - for(ii=0; iinVertex-1; ii++){ - rArea += (p->a[ii*2] - p->a[ii*2+2]) /* (x0 - x1) */ - * (p->a[ii*2+1] + p->a[ii*2+3]) /* (y0 + y1) */ - * 0.5; - } - rArea += (p->a[ii*2] - p->a[0]) /* (xN - x0) */ - * (p->a[ii*2+1] + p->a[1]) /* (yN + y0) */ - * 0.5; - sqlite3_result_double(context, rArea); + sqlite3_result_double(context, geopolyArea(p)); sqlite3_free(p); } } +/* +** Implementation of the geopoly_ccw(X) function. +** +** If the rotation of polygon X is clockwise (incorrect) instead of +** counter-clockwise (the correct winding order according to RFC7946) +** then reverse the order of the vertexes in polygon X. +** +** In other words, this routine returns a CCW polygon regardless of the +** winding order of its input. +** +** Use this routine to sanitize historical inputs that that sometimes +** contain polygons that wind in the wrong direction. +*/ +static void geopolyCcwFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + GeoPoly *p = geopolyFuncParam(context, argv[0], 0); + if( p ){ + if( geopolyArea(p)<0.0 ){ + int ii, jj; + for(ii=2, jj=p->nVertex*2 - 2; iia[ii]; + p->a[ii] = p->a[jj]; + p->a[jj] = t; + t = p->a[ii+1]; + p->a[ii+1] = p->a[jj+1]; + p->a[jj+1] = t; + } + } + sqlite3_result_blob(context, p->hdr, + 4+8*p->nVertex, SQLITE_TRANSIENT); + sqlite3_free(p); + } +} + +#define GEOPOLY_PI 3.1415926535897932385 + +/* Fast approximation for sine(X) for X between -0.5*pi and 2*pi +*/ +static double geopolySine(double r){ + assert( r>=-0.5*GEOPOLY_PI && r<=2.0*GEOPOLY_PI ); + if( r>=1.5*GEOPOLY_PI ){ + r -= 2.0*GEOPOLY_PI; + } + if( r>=0.5*GEOPOLY_PI ){ + return -geopolySine(r-GEOPOLY_PI); + }else{ + double r2 = r*r; + double r3 = r2*r; + double r5 = r3*r2; + return 0.9996949*r - 0.1656700*r3 + 0.0075134*r5; + } +} + +/* +** Function: geopoly_regular(X,Y,R,N) +** +** Construct a simple, convex, regular polygon centered at X, Y +** with circumradius R and with N sides. +*/ +static void geopolyRegularFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + double x = sqlite3_value_double(argv[0]); + double y = sqlite3_value_double(argv[1]); + double r = sqlite3_value_double(argv[2]); + int n = sqlite3_value_int(argv[3]); + int i; + GeoPoly *p; + + if( n<3 || r<=0.0 ) return; + if( n>1000 ) n = 1000; + p = sqlite3_malloc64( sizeof(*p) + (n-1)*2*sizeof(GeoCoord) ); + if( p==0 ){ + sqlite3_result_error_nomem(context); + return; + } + i = 1; + p->hdr[0] = *(unsigned char*)&i; + p->hdr[1] = 0; + p->hdr[2] = (n>>8)&0xff; + p->hdr[3] = n&0xff; + for(i=0; ia[i*2] = x - r*geopolySine(rAngle-0.5*GEOPOLY_PI); + p->a[i*2+1] = y + r*geopolySine(rAngle); + } + sqlite3_result_blob(context, p->hdr, 4+8*n, SQLITE_TRANSIENT); + sqlite3_free(p); +} + /* ** If pPoly is a polygon, compute its bounding box. Then: ** @@ -182847,7 +184019,7 @@ static GeoPoly *geopolyBBox( if( pRc ) *pRc = SQLITE_OK; if( aCoord==0 ){ geopolyBboxFill: - pOut = sqlite3_realloc(p, sizeof(GeoPoly)+sizeof(GeoCoord)*6); + pOut = sqlite3_realloc(p, GEOPOLY_SZ(4)); if( pOut==0 ){ sqlite3_free(p); if( context ) sqlite3_result_error_nomem(context); @@ -183875,7 +185047,16 @@ static int geopolyUpdate( if( sqlite3_value_nochange(aData[2]) ){ sqlite3_bind_null(pUp, 2); }else{ - sqlite3_bind_value(pUp, 2, aData[2]); + GeoPoly *p = 0; + if( sqlite3_value_type(aData[2])==SQLITE_TEXT + && (p = geopolyFuncParam(0, aData[2], &rc))!=0 + && rc==SQLITE_OK + ){ + sqlite3_bind_blob(pUp, 2, p->hdr, 4+8*p->nVertex, SQLITE_TRANSIENT); + }else{ + sqlite3_bind_value(pUp, 2, aData[2]); + } + sqlite3_free(p); nChange = 1; } for(jj=1; jjnAux; jj++){ @@ -183919,7 +185100,7 @@ static int geopolyFindFunction( static sqlite3_module geopolyModule = { - 2, /* iVersion */ + 3, /* iVersion */ geopolyCreate, /* xCreate - create a table */ geopolyConnect, /* xConnect - connect to an existing table */ geopolyBestIndex, /* xBestIndex - Determine search strategy */ @@ -183942,25 +185123,29 @@ static sqlite3_module geopolyModule = { rtreeSavepoint, /* xSavepoint */ 0, /* xRelease */ 0, /* xRollbackTo */ + rtreeShadowName /* xShadowName */ }; static int sqlite3_geopoly_init(sqlite3 *db){ int rc = SQLITE_OK; static const struct { void (*xFunc)(sqlite3_context*,int,sqlite3_value**); - int nArg; + signed char nArg; + unsigned char bPure; const char *zName; } aFunc[] = { - { geopolyAreaFunc, 1, "geopoly_area" }, - { geopolyBlobFunc, 1, "geopoly_blob" }, - { geopolyJsonFunc, 1, "geopoly_json" }, - { geopolySvgFunc, -1, "geopoly_svg" }, - { geopolyWithinFunc, 2, "geopoly_within" }, - { geopolyContainsPointFunc, 3, "geopoly_contains_point" }, - { geopolyOverlapFunc, 2, "geopoly_overlap" }, - { geopolyDebugFunc, 1, "geopoly_debug" }, - { geopolyBBoxFunc, 1, "geopoly_bbox" }, - { geopolyXformFunc, 7, "geopoly_xform" }, + { geopolyAreaFunc, 1, 1, "geopoly_area" }, + { geopolyBlobFunc, 1, 1, "geopoly_blob" }, + { geopolyJsonFunc, 1, 1, "geopoly_json" }, + { geopolySvgFunc, -1, 1, "geopoly_svg" }, + { geopolyWithinFunc, 2, 1, "geopoly_within" }, + { geopolyContainsPointFunc, 3, 1, "geopoly_contains_point" }, + { geopolyOverlapFunc, 2, 1, "geopoly_overlap" }, + { geopolyDebugFunc, 1, 0, "geopoly_debug" }, + { geopolyBBoxFunc, 1, 1, "geopoly_bbox" }, + { geopolyXformFunc, 7, 1, "geopoly_xform" }, + { geopolyRegularFunc, 4, 1, "geopoly_regular" }, + { geopolyCcwFunc, 1, 1, "geopoly_ccw" }, }; static const struct { void (*xStep)(sqlite3_context*,int,sqlite3_value**); @@ -183971,8 +185156,9 @@ static int sqlite3_geopoly_init(sqlite3 *db){ }; int i; for(i=0; ipRbuVfs; + rbu_file *pIter; + assert( (p->openFlags & SQLITE_OPEN_MAIN_DB) ); + sqlite3_mutex_enter(pRbuVfs->mutex); + if( p->pRbu==0 ){ + for(pIter=pRbuVfs->pMain; pIter; pIter=pIter->pMainNext); + p->pMainNext = pRbuVfs->pMain; + pRbuVfs->pMain = p; + }else{ + for(pIter=pRbuVfs->pMainRbu; pIter && pIter!=p; pIter=pIter->pMainRbuNext){} + if( pIter==0 ){ + p->pMainRbuNext = pRbuVfs->pMainRbu; + pRbuVfs->pMainRbu = p; + } + } + sqlite3_mutex_leave(pRbuVfs->mutex); +} + +/* +** Remove an item from the main-db lists. +*/ +static void rbuMainlistRemove(rbu_file *p){ + rbu_file **pp; + sqlite3_mutex_enter(p->pRbuVfs->mutex); + for(pp=&p->pRbuVfs->pMain; *pp && *pp!=p; pp=&((*pp)->pMainNext)){} + if( *pp ) *pp = p->pMainNext; + p->pMainNext = 0; + for(pp=&p->pRbuVfs->pMainRbu; *pp && *pp!=p; pp=&((*pp)->pMainRbuNext)){} + if( *pp ) *pp = p->pMainRbuNext; + p->pMainRbuNext = 0; + sqlite3_mutex_leave(p->pRbuVfs->mutex); +} + +/* +** Given that zWal points to a buffer containing a wal file name passed to +** either the xOpen() or xAccess() VFS method, search the main-db list for +** a file-handle opened by the same database connection on the corresponding +** database file. +** +** If parameter bRbu is true, only search for file-descriptors with +** rbu_file.pDb!=0. +*/ +static rbu_file *rbuFindMaindb(rbu_vfs *pRbuVfs, const char *zWal, int bRbu){ + rbu_file *pDb; + sqlite3_mutex_enter(pRbuVfs->mutex); + if( bRbu ){ + for(pDb=pRbuVfs->pMainRbu; pDb && pDb->zWal!=zWal; pDb=pDb->pMainRbuNext){} + }else{ + for(pDb=pRbuVfs->pMain; pDb && pDb->zWal!=zWal; pDb=pDb->pMainNext){} + } + sqlite3_mutex_leave(pRbuVfs->mutex); + return pDb; +} + /* ** Close an rbu file. */ @@ -189641,17 +190892,14 @@ static int rbuVfsClose(sqlite3_file *pFile){ sqlite3_free(p->zDel); if( p->openFlags & SQLITE_OPEN_MAIN_DB ){ - rbu_file **pp; - sqlite3_mutex_enter(p->pRbuVfs->mutex); - for(pp=&p->pRbuVfs->pMain; *pp!=p; pp=&((*pp)->pMainNext)); - *pp = p->pMainNext; - sqlite3_mutex_leave(p->pRbuVfs->mutex); + rbuMainlistRemove(p); rbuUnlockShm(p); p->pReal->pMethods->xShmUnmap(p->pReal, 0); } else if( (p->openFlags & SQLITE_OPEN_DELETEONCLOSE) && p->pRbu ){ rbuUpdateTempSize(p, 0); } + assert( p->pMainNext==0 && p->pRbuVfs->pMain!=p ); /* Close the underlying file handle */ rc = p->pReal->pMethods->xClose(p->pReal); @@ -189910,6 +191158,9 @@ static int rbuVfsFileControl(sqlite3_file *pFile, int op, void *pArg){ }else if( rc==SQLITE_NOTFOUND ){ pRbu->pTargetFd = p; p->pRbu = pRbu; + if( p->openFlags & SQLITE_OPEN_MAIN_DB ){ + rbuMainlistAdd(p); + } if( p->pWalFd ) p->pWalFd->pRbu = pRbu; rc = SQLITE_OK; } @@ -190071,20 +191322,6 @@ static int rbuVfsShmUnmap(sqlite3_file *pFile, int delFlag){ return rc; } -/* -** Given that zWal points to a buffer containing a wal file name passed to -** either the xOpen() or xAccess() VFS method, return a pointer to the -** file-handle opened by the same database connection on the corresponding -** database file. -*/ -static rbu_file *rbuFindMaindb(rbu_vfs *pRbuVfs, const char *zWal){ - rbu_file *pDb; - sqlite3_mutex_enter(pRbuVfs->mutex); - for(pDb=pRbuVfs->pMain; pDb && pDb->zWal!=zWal; pDb=pDb->pMainNext){} - sqlite3_mutex_leave(pRbuVfs->mutex); - return pDb; -} - /* ** A main database named zName has just been opened. The following ** function returns a pointer to a buffer owned by SQLite that contains @@ -190163,7 +191400,7 @@ static int rbuVfsOpen( pFd->zWal = rbuMainToWal(zName, flags); } else if( flags & SQLITE_OPEN_WAL ){ - rbu_file *pDb = rbuFindMaindb(pRbuVfs, zName); + rbu_file *pDb = rbuFindMaindb(pRbuVfs, zName, 0); if( pDb ){ if( pDb->pRbu && pDb->pRbu->eStage==RBU_STAGE_OAL ){ /* This call is to open a *-wal file. Intead, open the *-oal. This @@ -190215,10 +191452,7 @@ static int rbuVfsOpen( ** mutex protected linked list of all such files. */ pFile->pMethods = &rbuvfs_io_methods; if( flags & SQLITE_OPEN_MAIN_DB ){ - sqlite3_mutex_enter(pRbuVfs->mutex); - pFd->pMainNext = pRbuVfs->pMain; - pRbuVfs->pMain = pFd; - sqlite3_mutex_leave(pRbuVfs->mutex); + rbuMainlistAdd(pFd); } }else{ sqlite3_free(pFd->zDel); @@ -190266,7 +191500,7 @@ static int rbuVfsAccess( ** file opened instead. */ if( rc==SQLITE_OK && flags==SQLITE_ACCESS_EXISTS ){ - rbu_file *pDb = rbuFindMaindb(pRbuVfs, zPath); + rbu_file *pDb = rbuFindMaindb(pRbuVfs, zPath, 1); if( pDb && pDb->pRbu && pDb->pRbu->eStage==RBU_STAGE_OAL ){ if( *pResOut ){ rc = SQLITE_CANTOPEN; @@ -190679,17 +191913,15 @@ static int statDisconnect(sqlite3_vtab *pVtab){ static int statBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ int i; - pIdxInfo->estimatedCost = 1.0e6; /* Initial cost estimate */ - /* Look for a valid schema=? constraint. If found, change the idxNum to ** 1 and request the value of that constraint be sent to xFilter. And ** lower the cost estimate to encourage the constrained version to be ** used. */ for(i=0; inConstraint; i++){ - if( pIdxInfo->aConstraint[i].usable==0 ) continue; - if( pIdxInfo->aConstraint[i].op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue; if( pIdxInfo->aConstraint[i].iColumn!=10 ) continue; + if( pIdxInfo->aConstraint[i].usable==0 ) return SQLITE_CONSTRAINT; + if( pIdxInfo->aConstraint[i].op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue; pIdxInfo->idxNum = 1; pIdxInfo->estimatedCost = 1.0; pIdxInfo->aConstraintUsage[i].argvIndex = 1; @@ -190739,7 +191971,7 @@ static int statOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ return SQLITE_OK; } -static void statClearPage(StatPage *p){ +static void statClearCells(StatPage *p){ int i; if( p->aCell ){ for(i=0; inCell; i++){ @@ -190747,6 +191979,12 @@ static void statClearPage(StatPage *p){ } sqlite3_free(p->aCell); } + p->nCell = 0; + p->aCell = 0; +} + +static void statClearPage(StatPage *p){ + statClearCells(p); sqlite3PagerUnref(p->pPg); sqlite3_free(p->zPath); memset(p, 0, sizeof(StatPage)); @@ -190809,22 +192047,33 @@ static int statDecodePage(Btree *pBt, StatPage *p){ u8 *aHdr = &aData[p->iPgno==1 ? 100 : 0]; p->flags = aHdr[0]; + if( p->flags==0x0A || p->flags==0x0D ){ + isLeaf = 1; + nHdr = 8; + }else if( p->flags==0x05 || p->flags==0x02 ){ + isLeaf = 0; + nHdr = 12; + }else{ + goto statPageIsCorrupt; + } + if( p->iPgno==1 ) nHdr += 100; p->nCell = get2byte(&aHdr[3]); p->nMxPayload = 0; - - isLeaf = (p->flags==0x0A || p->flags==0x0D); - nHdr = 12 - isLeaf*4 + (p->iPgno==1)*100; + szPage = sqlite3BtreeGetPageSize(pBt); nUnused = get2byte(&aHdr[5]) - nHdr - 2*p->nCell; nUnused += (int)aHdr[7]; iOff = get2byte(&aHdr[1]); while( iOff ){ + int iNext; + if( iOff>=szPage ) goto statPageIsCorrupt; nUnused += get2byte(&aData[iOff+2]); - iOff = get2byte(&aData[iOff]); + iNext = get2byte(&aData[iOff]); + if( iNext0 ) goto statPageIsCorrupt; + iOff = iNext; } p->nUnused = nUnused; p->iRightChildPg = isLeaf ? 0 : sqlite3Get4byte(&aHdr[8]); - szPage = sqlite3BtreeGetPageSize(pBt); if( p->nCell ){ int i; /* Used to iterate through cells */ @@ -190841,6 +192090,7 @@ static int statDecodePage(Btree *pBt, StatPage *p){ StatCell *pCell = &p->aCell[i]; iOff = get2byte(&aData[nHdr+i*2]); + if( iOff=szPage ) goto statPageIsCorrupt; if( !isLeaf ){ pCell->iChildPg = sqlite3Get4byte(&aData[iOff]); iOff += 4; @@ -190857,13 +192107,14 @@ static int statDecodePage(Btree *pBt, StatPage *p){ } if( nPayload>(u32)p->nMxPayload ) p->nMxPayload = nPayload; getLocalPayload(nUsable, p->flags, nPayload, &nLocal); + if( nLocal<0 ) goto statPageIsCorrupt; pCell->nLocal = nLocal; - assert( nLocal>=0 ); assert( nPayload>=(u32)nLocal ); assert( nLocal<=(nUsable-35) ); if( nPayload>(u32)nLocal ){ int j; int nOvfl = ((nPayload - nLocal) + nUsable-4 - 1) / (nUsable - 4); + if( iOff+nLocal>nUsable ) goto statPageIsCorrupt; pCell->nLastOvfl = (nPayload-nLocal) - (nOvfl-1) * (nUsable-4); pCell->nOvfl = nOvfl; pCell->aOvfl = sqlite3_malloc64(sizeof(u32)*nOvfl); @@ -190887,6 +192138,11 @@ static int statDecodePage(Btree *pBt, StatPage *p){ } return SQLITE_OK; + +statPageIsCorrupt: + p->flags = 0; + statClearCells(p); + return SQLITE_OK; } /* @@ -191182,6 +192438,7 @@ SQLITE_PRIVATE int sqlite3DbstatRegister(sqlite3 *db){ 0, /* xSavepoint */ 0, /* xRelease */ 0, /* xRollbackTo */ + 0 /* xShadowName */ }; return sqlite3_create_module(db, "dbstat", &dbstat_module, 0); } @@ -191312,9 +192569,8 @@ static int dbpageBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ if( p->iColumn!=DBPAGE_COLUMN_SCHEMA ) continue; if( p->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue; if( !p->usable ){ - /* No solution. Use the default SQLITE_BIG_DBL cost */ - pIdxInfo->estimatedRows = 0x7fffffff; - return SQLITE_OK; + /* No solution. */ + return SQLITE_CONSTRAINT; } iPlan = 2; pIdxInfo->aConstraintUsage[i].argvIndex = 1; @@ -191506,6 +192762,10 @@ static int dbpageUpdate( Pager *pPager; int szPage; + if( pTab->db->flags & SQLITE_Defensive ){ + zErr = "read-only"; + goto update_fail; + } if( argc==1 ){ zErr = "cannot delete"; goto update_fail; @@ -191596,6 +192856,7 @@ SQLITE_PRIVATE int sqlite3DbpageRegister(sqlite3 *db){ 0, /* xSavepoint */ 0, /* xRelease */ 0, /* xRollbackTo */ + 0 /* xShadowName */ }; return sqlite3_create_module(db, "sqlite_dbpage", &dbpage_module, 0); } @@ -191632,6 +192893,8 @@ typedef struct SessionInput SessionInput; # endif #endif +static int sessions_strm_chunk_size = SESSIONS_STRM_CHUNK_SIZE; + typedef struct SessionHook SessionHook; struct SessionHook { void *pCtx; @@ -191694,6 +192957,7 @@ struct sqlite3_changeset_iter { SessionInput in; /* Input buffer or stream */ SessionBuffer tblhdr; /* Buffer to hold apValue/zTab/abPK/ */ int bPatchset; /* True if this is a patchset */ + int bInvert; /* True to invert changeset */ int rc; /* Iterator error code */ sqlite3_stmt *pConflict; /* Points to conflicting row, if any */ char *zTab; /* Current table */ @@ -191850,6 +193114,42 @@ struct SessionTable { ** The records associated with INSERT changes are in the same format as for ** changesets. It is not possible for a record associated with an INSERT ** change to contain a field set to "undefined". +** +** REBASE BLOB FORMAT: +** +** A rebase blob may be output by sqlite3changeset_apply_v2() and its +** streaming equivalent for use with the sqlite3_rebaser APIs to rebase +** existing changesets. A rebase blob contains one entry for each conflict +** resolved using either the OMIT or REPLACE strategies within the apply_v2() +** call. +** +** The format used for a rebase blob is very similar to that used for +** changesets. All entries related to a single table are grouped together. +** +** Each group of entries begins with a table header in changeset format: +** +** 1 byte: Constant 0x54 (capital 'T') +** Varint: Number of columns in the table. +** nCol bytes: 0x01 for PK columns, 0x00 otherwise. +** N bytes: Unqualified table name (encoded using UTF-8). Nul-terminated. +** +** Followed by one or more entries associated with the table. +** +** 1 byte: Either SQLITE_INSERT (0x12), DELETE (0x09). +** 1 byte: Flag. 0x01 for REPLACE, 0x00 for OMIT. +** record: (in the record format defined above). +** +** In a rebase blob, the first field is set to SQLITE_INSERT if the change +** that caused the conflict was an INSERT or UPDATE, or to SQLITE_DELETE if +** it was a DELETE. The second field is set to 0x01 if the conflict +** resolution strategy was REPLACE, or 0x00 if it was OMIT. +** +** If the change that caused the conflict was a DELETE, then the single +** record is a copy of the old.* record from the original changeset. If it +** was an INSERT, then the single record is a copy of the new.* record. If +** the conflicting change was an UPDATE, then the single record is a copy +** of the new.* record with the PK fields filled in based on the original +** old.* record. */ /* @@ -193400,12 +194700,12 @@ SQLITE_API int sqlite3session_attach( static int sessionBufferGrow(SessionBuffer *p, int nByte, int *pRc){ if( *pRc==SQLITE_OK && p->nAlloc-p->nBufnAlloc ? p->nAlloc : 128; + i64 nNew = p->nAlloc ? p->nAlloc : 128; do { nNew = nNew*2; - }while( nNew<(p->nBuf+nByte) ); + }while( (nNew-p->nBuf)aBuf, nNew); + aNew = (u8 *)sqlite3_realloc64(p->aBuf, nNew); if( 0==aNew ){ *pRc = SQLITE_NOMEM; }else{ @@ -194003,12 +195303,12 @@ static int sessionGenerateChangeset( rc = sqlite3_reset(pSel); } - /* If the buffer is now larger than SESSIONS_STRM_CHUNK_SIZE, pass + /* If the buffer is now larger than sessions_strm_chunk_size, pass ** its contents to the xOutput() callback. */ if( xOutput && rc==SQLITE_OK && buf.nBuf>nNoop - && buf.nBuf>SESSIONS_STRM_CHUNK_SIZE + && buf.nBuf>sessions_strm_chunk_size ){ rc = xOutput(pOut, (void*)buf.aBuf, buf.nBuf); nNoop = -1; @@ -194147,7 +195447,8 @@ static int sessionChangesetStart( int (*xInput)(void *pIn, void *pData, int *pnData), void *pIn, int nChangeset, /* Size of buffer pChangeset in bytes */ - void *pChangeset /* Pointer to buffer containing changeset */ + void *pChangeset, /* Pointer to buffer containing changeset */ + int bInvert /* True to invert changeset */ ){ sqlite3_changeset_iter *pRet; /* Iterator to return */ int nByte; /* Number of bytes to allocate for iterator */ @@ -194167,6 +195468,7 @@ static int sessionChangesetStart( pRet->in.xInput = xInput; pRet->in.pIn = pIn; pRet->in.bEof = (xInput ? 0 : 1); + pRet->bInvert = bInvert; /* Populate the output variable and return success. */ *pp = pRet; @@ -194181,7 +195483,16 @@ SQLITE_API int sqlite3changeset_start( int nChangeset, /* Size of buffer pChangeset in bytes */ void *pChangeset /* Pointer to buffer containing changeset */ ){ - return sessionChangesetStart(pp, 0, 0, nChangeset, pChangeset); + return sessionChangesetStart(pp, 0, 0, nChangeset, pChangeset, 0); +} +SQLITE_API int sqlite3changeset_start_v2( + sqlite3_changeset_iter **pp, /* OUT: Changeset iterator handle */ + int nChangeset, /* Size of buffer pChangeset in bytes */ + void *pChangeset, /* Pointer to buffer containing changeset */ + int flags +){ + int bInvert = !!(flags & SQLITE_CHANGESETSTART_INVERT); + return sessionChangesetStart(pp, 0, 0, nChangeset, pChangeset, bInvert); } /* @@ -194192,7 +195503,16 @@ SQLITE_API int sqlite3changeset_start_strm( int (*xInput)(void *pIn, void *pData, int *pnData), void *pIn ){ - return sessionChangesetStart(pp, xInput, pIn, 0, 0); + return sessionChangesetStart(pp, xInput, pIn, 0, 0, 0); +} +SQLITE_API int sqlite3changeset_start_v2_strm( + sqlite3_changeset_iter **pp, /* OUT: Changeset iterator handle */ + int (*xInput)(void *pIn, void *pData, int *pnData), + void *pIn, + int flags +){ + int bInvert = !!(flags & SQLITE_CHANGESETSTART_INVERT); + return sessionChangesetStart(pp, xInput, pIn, 0, 0, bInvert); } /* @@ -194200,7 +195520,7 @@ SQLITE_API int sqlite3changeset_start_strm( ** object and the buffer is full, discard some data to free up space. */ static void sessionDiscardData(SessionInput *pIn){ - if( pIn->xInput && pIn->iNext>=SESSIONS_STRM_CHUNK_SIZE ){ + if( pIn->xInput && pIn->iNext>=sessions_strm_chunk_size ){ int nMove = pIn->buf.nBuf - pIn->iNext; assert( nMove>=0 ); if( nMove>0 ){ @@ -194223,7 +195543,7 @@ static int sessionInputBuffer(SessionInput *pIn, int nByte){ int rc = SQLITE_OK; if( pIn->xInput ){ while( !pIn->bEof && (pIn->iNext+nByte)>=pIn->nData && rc==SQLITE_OK ){ - int nNew = SESSIONS_STRM_CHUNK_SIZE; + int nNew = sessions_strm_chunk_size; if( pIn->bNoDiscard==0 ) sessionDiscardData(pIn); if( SQLITE_OK==sessionBufferGrow(&pIn->buf, nNew, &rc) ){ @@ -194571,10 +195891,10 @@ static int sessionChangesetNext( op = p->in.aData[p->in.iNext++]; } - if( p->zTab==0 ){ + if( p->zTab==0 || (p->bPatchset && p->bInvert) ){ /* The first record in the changeset is not a table header. Must be a ** corrupt changeset. */ - assert( p->in.iNext==1 ); + assert( p->in.iNext==1 || p->zTab ); return (p->rc = SQLITE_CORRUPT_BKPT); } @@ -194599,33 +195919,39 @@ static int sessionChangesetNext( *paRec = &p->in.aData[p->in.iNext]; p->in.iNext += *pnRec; }else{ + sqlite3_value **apOld = (p->bInvert ? &p->apValue[p->nCol] : p->apValue); + sqlite3_value **apNew = (p->bInvert ? p->apValue : &p->apValue[p->nCol]); /* If this is an UPDATE or DELETE, read the old.* record. */ if( p->op!=SQLITE_INSERT && (p->bPatchset==0 || p->op==SQLITE_DELETE) ){ u8 *abPK = p->bPatchset ? p->abPK : 0; - p->rc = sessionReadRecord(&p->in, p->nCol, abPK, p->apValue); + p->rc = sessionReadRecord(&p->in, p->nCol, abPK, apOld); if( p->rc!=SQLITE_OK ) return p->rc; } /* If this is an INSERT or UPDATE, read the new.* record. */ if( p->op!=SQLITE_DELETE ){ - p->rc = sessionReadRecord(&p->in, p->nCol, 0, &p->apValue[p->nCol]); + p->rc = sessionReadRecord(&p->in, p->nCol, 0, apNew); if( p->rc!=SQLITE_OK ) return p->rc; } - if( p->bPatchset && p->op==SQLITE_UPDATE ){ + if( (p->bPatchset || p->bInvert) && p->op==SQLITE_UPDATE ){ /* If this is an UPDATE that is part of a patchset, then all PK and ** modified fields are present in the new.* record. The old.* record ** is currently completely empty. This block shifts the PK fields from ** new.* to old.*, to accommodate the code that reads these arrays. */ for(i=0; inCol; i++){ - assert( p->apValue[i]==0 ); + assert( p->bPatchset==0 || p->apValue[i]==0 ); if( p->abPK[i] ){ + assert( p->apValue[i]==0 ); p->apValue[i] = p->apValue[i+p->nCol]; if( p->apValue[i]==0 ) return (p->rc = SQLITE_CORRUPT_BKPT); p->apValue[i+p->nCol] = 0; } } + }else if( p->bInvert ){ + if( p->op==SQLITE_INSERT ) p->op = SQLITE_DELETE; + else if( p->op==SQLITE_DELETE ) p->op = SQLITE_INSERT; } } @@ -194942,7 +196268,7 @@ static int sessionChangesetInvert( } assert( rc==SQLITE_OK ); - if( xOutput && sOut.nBuf>=SESSIONS_STRM_CHUNK_SIZE ){ + if( xOutput && sOut.nBuf>=sessions_strm_chunk_size ){ rc = xOutput(pOut, sOut.aBuf, sOut.nBuf); sOut.nBuf = 0; if( rc!=SQLITE_OK ) goto finished_invert; @@ -195021,7 +196347,8 @@ struct SessionApplyCtx { int bDeferConstraints; /* True to defer constraints */ SessionBuffer constraints; /* Deferred constraints are stored here */ SessionBuffer rebase; /* Rebase information (if any) here */ - int bRebaseStarted; /* If table header is already in rebase */ + u8 bRebaseStarted; /* If table header is already in rebase */ + u8 bRebase; /* True to collect rebase information */ }; /* @@ -195418,35 +196745,36 @@ static int sessionRebaseAdd( sqlite3_changeset_iter *pIter /* Iterator pointing at current change */ ){ int rc = SQLITE_OK; - int i; - int eOp = pIter->op; - if( p->bRebaseStarted==0 ){ - /* Append a table-header to the rebase buffer */ - const char *zTab = pIter->zTab; - sessionAppendByte(&p->rebase, 'T', &rc); - sessionAppendVarint(&p->rebase, p->nCol, &rc); - sessionAppendBlob(&p->rebase, p->abPK, p->nCol, &rc); - sessionAppendBlob(&p->rebase, (u8*)zTab, (int)strlen(zTab)+1, &rc); - p->bRebaseStarted = 1; - } - - assert( eType==SQLITE_CHANGESET_REPLACE||eType==SQLITE_CHANGESET_OMIT ); - assert( eOp==SQLITE_DELETE || eOp==SQLITE_INSERT || eOp==SQLITE_UPDATE ); - - sessionAppendByte(&p->rebase, - (eOp==SQLITE_DELETE ? SQLITE_DELETE : SQLITE_INSERT), &rc - ); - sessionAppendByte(&p->rebase, (eType==SQLITE_CHANGESET_REPLACE), &rc); - for(i=0; inCol; i++){ - sqlite3_value *pVal = 0; - if( eOp==SQLITE_DELETE || (eOp==SQLITE_UPDATE && p->abPK[i]) ){ - sqlite3changeset_old(pIter, i, &pVal); - }else{ - sqlite3changeset_new(pIter, i, &pVal); + if( p->bRebase ){ + int i; + int eOp = pIter->op; + if( p->bRebaseStarted==0 ){ + /* Append a table-header to the rebase buffer */ + const char *zTab = pIter->zTab; + sessionAppendByte(&p->rebase, 'T', &rc); + sessionAppendVarint(&p->rebase, p->nCol, &rc); + sessionAppendBlob(&p->rebase, p->abPK, p->nCol, &rc); + sessionAppendBlob(&p->rebase, (u8*)zTab, (int)strlen(zTab)+1, &rc); + p->bRebaseStarted = 1; } - sessionAppendValue(&p->rebase, pVal, &rc); - } + assert( eType==SQLITE_CHANGESET_REPLACE||eType==SQLITE_CHANGESET_OMIT ); + assert( eOp==SQLITE_DELETE || eOp==SQLITE_INSERT || eOp==SQLITE_UPDATE ); + + sessionAppendByte(&p->rebase, + (eOp==SQLITE_DELETE ? SQLITE_DELETE : SQLITE_INSERT), &rc + ); + sessionAppendByte(&p->rebase, (eType==SQLITE_CHANGESET_REPLACE), &rc); + for(i=0; inCol; i++){ + sqlite3_value *pVal = 0; + if( eOp==SQLITE_DELETE || (eOp==SQLITE_UPDATE && p->abPK[i]) ){ + sqlite3changeset_old(pIter, i, &pVal); + }else{ + sqlite3changeset_new(pIter, i, &pVal); + } + sessionAppendValue(&p->rebase, pVal, &rc); + } + } return rc; } @@ -195789,7 +197117,7 @@ static int sessionRetryConstraints( SessionBuffer cons = pApply->constraints; memset(&pApply->constraints, 0, sizeof(SessionBuffer)); - rc = sessionChangesetStart(&pIter2, 0, 0, cons.nBuf, cons.aBuf); + rc = sessionChangesetStart(&pIter2, 0, 0, cons.nBuf, cons.aBuf, 0); if( rc==SQLITE_OK ){ int nByte = 2*pApply->nCol*sizeof(sqlite3_value*); int rc2; @@ -195855,6 +197183,7 @@ static int sessionChangesetApply( pIter->in.bNoDiscard = 1; memset(&sApply, 0, sizeof(sApply)); + sApply.bRebase = (ppRebase && pnRebase); sqlite3_mutex_enter(sqlite3_db_mutex(db)); if( (flags & SQLITE_CHANGESETAPPLY_NOSAVEPOINT)==0 ){ rc = sqlite3_exec(db, "SAVEPOINT changeset_apply", 0, 0, 0); @@ -196005,7 +197334,8 @@ static int sessionChangesetApply( } } - if( rc==SQLITE_OK && bPatchset==0 && ppRebase && pnRebase ){ + assert( sApply.bRebase || sApply.rebase.nBuf==0 ); + if( rc==SQLITE_OK && bPatchset==0 && sApply.bRebase ){ *ppRebase = (void*)sApply.rebase.aBuf; *pnRebase = sApply.rebase.nBuf; sApply.rebase.aBuf = 0; @@ -196043,7 +197373,8 @@ SQLITE_API int sqlite3changeset_apply_v2( int flags ){ sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */ - int rc = sqlite3changeset_start(&pIter, nChangeset, pChangeset); + int bInverse = !!(flags & SQLITE_CHANGESETAPPLY_INVERT); + int rc = sessionChangesetStart(&pIter, 0, 0, nChangeset, pChangeset,bInverse); if( rc==SQLITE_OK ){ rc = sessionChangesetApply( db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase, flags @@ -196100,7 +197431,8 @@ SQLITE_API int sqlite3changeset_apply_v2_strm( int flags ){ sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */ - int rc = sqlite3changeset_start_strm(&pIter, xInput, pIn); + int bInverse = !!(flags & SQLITE_CHANGESETAPPLY_INVERT); + int rc = sessionChangesetStart(&pIter, xInput, pIn, 0, 0, bInverse); if( rc==SQLITE_OK ){ rc = sessionChangesetApply( db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase, flags @@ -196473,13 +197805,12 @@ static int sessionChangegroupOutput( sessionAppendByte(&buf, p->op, &rc); sessionAppendByte(&buf, p->bIndirect, &rc); sessionAppendBlob(&buf, p->aRecord, p->nRecord, &rc); + if( rc==SQLITE_OK && xOutput && buf.nBuf>=sessions_strm_chunk_size ){ + rc = xOutput(pOut, buf.aBuf, buf.nBuf); + buf.nBuf = 0; + } } } - - if( rc==SQLITE_OK && xOutput && buf.nBuf>=SESSIONS_STRM_CHUNK_SIZE ){ - rc = xOutput(pOut, buf.aBuf, buf.nBuf); - buf.nBuf = 0; - } } if( rc==SQLITE_OK ){ @@ -196870,7 +198201,7 @@ static int sessionRebase( sessionAppendByte(&sOut, pIter->bIndirect, &rc); sessionAppendBlob(&sOut, aRec, nRec, &rc); } - if( rc==SQLITE_OK && xOutput && sOut.nBuf>SESSIONS_STRM_CHUNK_SIZE ){ + if( rc==SQLITE_OK && xOutput && sOut.nBuf>sessions_strm_chunk_size ){ rc = xOutput(pOut, sOut.aBuf, sOut.nBuf); sOut.nBuf = 0; } @@ -196981,6 +198312,27 @@ SQLITE_API void sqlite3rebaser_delete(sqlite3_rebaser *p){ } } +/* +** Global configuration +*/ +SQLITE_API int sqlite3session_config(int op, void *pArg){ + int rc = SQLITE_OK; + switch( op ){ + case SQLITE_SESSION_CONFIG_STRMSIZE: { + int *pInt = (int*)pArg; + if( *pInt>0 ){ + sessions_strm_chunk_size = *pInt; + } + *pInt = sessions_strm_chunk_size; + break; + } + default: + rc = SQLITE_MISUSE; + break; + } + return rc; +} + #endif /* SQLITE_ENABLE_SESSION && SQLITE_ENABLE_PREUPDATE_HOOK */ /************** End of sqlite3session.c **************************************/ @@ -198415,6 +199767,7 @@ static void sqlite3Fts5UnicodeAscii(u8*, u8*); ** input grammar file: */ /* #include */ +/* #include */ /************ Begin %include sections from the grammar ************************/ /* #include "fts5Int.h" */ @@ -199742,10 +201095,9 @@ static void sqlite3Fts5Parser( fts5yymajor = fts5YYNOCODE; }else{ while( fts5yypParser->fts5yytos >= fts5yypParser->fts5yystack - && fts5yymx != fts5YYERRORSYMBOL && (fts5yyact = fts5yy_find_reduce_action( fts5yypParser->fts5yytos->stateno, - fts5YYERRORSYMBOL)) >= fts5YY_MIN_REDUCE + fts5YYERRORSYMBOL)) > fts5YY_MAX_SHIFTREDUCE ){ fts5yy_pop_parser_stack(fts5yypParser); } @@ -210695,7 +212047,7 @@ static int sqlite3Fts5IndexQuery( fts5CloseReader(p); } - *ppIter = &pRet->base; + *ppIter = (Fts5IndexIter*)pRet; sqlite3Fts5BufferFree(&buf); } return fts5IndexReturn(p); @@ -214444,12 +215796,27 @@ static void fts5SourceIdFunc( ){ assert( nArg==0 ); UNUSED_PARAM2(nArg, apUnused); - sqlite3_result_text(pCtx, "fts5: 2018-09-25 19:08:10 fb90e7189ae6d62e77ba3a308ca5d683f90bbe633cf681865365b8e92792d1c7", -1, SQLITE_TRANSIENT); + sqlite3_result_text(pCtx, "fts5: 2018-12-01 12:34:55 bf8c1b2b7a5960c282e543b9c293686dccff272512d08865f4600fb58238b4f9", -1, SQLITE_TRANSIENT); +} + +/* +** Return true if zName is the extension on one of the shadow tables used +** by this module. +*/ +static int fts5ShadowName(const char *zName){ + static const char *azName[] = { + "config", "content", "data", "docsize", "idx" + }; + unsigned int i; + for(i=0; iiInstPos; int *po = &pCsr->iInstOff; + assert( sqlite3Fts5IterEof(pIter)==0 ); + assert( pCsr->bEof==0 ); while( eDetail==FTS5_DETAIL_NONE || sqlite3Fts5PoslistNext64(pIter->pData, pIter->nData, po, pp) ){ @@ -218525,7 +219895,7 @@ static int fts5VocabInstanceNext(Fts5VocabCursor *pCsr){ rc = sqlite3Fts5IterNextScan(pCsr->pIter); if( rc==SQLITE_OK ){ rc = fts5VocabInstanceNewTerm(pCsr); - if( eDetail==FTS5_DETAIL_NONE ) break; + if( pCsr->bEof || eDetail==FTS5_DETAIL_NONE ) break; } if( rc ){ pCsr->bEof = 1; @@ -218840,6 +220210,7 @@ static int sqlite3Fts5VocabInit(Fts5Global *pGlobal, sqlite3 *db){ /* xSavepoint */ 0, /* xRelease */ 0, /* xRollbackTo */ 0, + /* xShadowName */ 0 }; void *p = (void*)pGlobal; @@ -218847,8 +220218,6 @@ static int sqlite3Fts5VocabInit(Fts5Global *pGlobal, sqlite3 *db){ } - - #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS5) */ @@ -219122,6 +220491,7 @@ static sqlite3_module stmtModule = { 0, /* xSavepoint */ 0, /* xRelease */ 0, /* xRollbackTo */ + 0, /* xShadowName */ }; #endif /* SQLITE_OMIT_VIRTUALTABLE */ @@ -219154,9 +220524,9 @@ SQLITE_API int sqlite3_stmt_init( #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */ /************** End of stmt.c ************************************************/ -#if __LINE__!=219157 +#if __LINE__!=220527 #undef SQLITE_SOURCE_ID -#define SQLITE_SOURCE_ID "2018-09-25 19:08:10 fb90e7189ae6d62e77ba3a308ca5d683f90bbe633cf681865365b8e92792alt2" +#define SQLITE_SOURCE_ID "2018-12-01 12:34:55 bf8c1b2b7a5960c282e543b9c293686dccff272512d08865f4600fb58238alt2" #endif /* Return the source-id for this library */ SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } diff --git a/libsqlite3-sys/sqlite3/sqlite3.h b/libsqlite3-sys/sqlite3/sqlite3.h index 4612ecd..f36ae57 100644 --- a/libsqlite3-sys/sqlite3/sqlite3.h +++ b/libsqlite3-sys/sqlite3/sqlite3.h @@ -123,9 +123,9 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.25.2" -#define SQLITE_VERSION_NUMBER 3025002 -#define SQLITE_SOURCE_ID "2018-09-25 19:08:10 fb90e7189ae6d62e77ba3a308ca5d683f90bbe633cf681865365b8e92792d1c7" +#define SQLITE_VERSION "3.26.0" +#define SQLITE_VERSION_NUMBER 3026000 +#define SQLITE_SOURCE_ID "2018-12-01 12:34:55 bf8c1b2b7a5960c282e543b9c293686dccff272512d08865f4600fb58238b4f9" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -2017,6 +2017,7 @@ struct sqlite3_mem_methods { ** is invoked. ** **
+** [[SQLITE_DBCONFIG_LOOKASIDE]] **
SQLITE_DBCONFIG_LOOKASIDE
**
^This option takes three additional arguments that determine the ** [lookaside memory allocator] configuration for the [database connection]. @@ -2039,6 +2040,7 @@ struct sqlite3_mem_methods { ** memory is in use leaves the configuration unchanged and returns ** [SQLITE_BUSY].)^
** +** [[SQLITE_DBCONFIG_ENABLE_FKEY]] **
SQLITE_DBCONFIG_ENABLE_FKEY
**
^This option is used to enable or disable the enforcement of ** [foreign key constraints]. There should be two additional arguments. @@ -2049,6 +2051,7 @@ struct sqlite3_mem_methods { ** following this call. The second parameter may be a NULL pointer, in ** which case the FK enforcement setting is not reported back.
** +** [[SQLITE_DBCONFIG_ENABLE_TRIGGER]] **
SQLITE_DBCONFIG_ENABLE_TRIGGER
**
^This option is used to enable or disable [CREATE TRIGGER | triggers]. ** There should be two additional arguments. @@ -2059,6 +2062,7 @@ struct sqlite3_mem_methods { ** following this call. The second parameter may be a NULL pointer, in ** which case the trigger setting is not reported back.
** +** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]] **
SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER
**
^This option is used to enable or disable the two-argument ** version of the [fts3_tokenizer()] function which is part of the @@ -2072,6 +2076,7 @@ struct sqlite3_mem_methods { ** following this call. The second parameter may be a NULL pointer, in ** which case the new setting is not reported back.
** +** [[SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION]] **
SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION
**
^This option is used to enable or disable the [sqlite3_load_extension()] ** interface independently of the [load_extension()] SQL function. @@ -2089,7 +2094,7 @@ struct sqlite3_mem_methods { ** be a NULL pointer, in which case the new setting is not reported back. **
** -**
SQLITE_DBCONFIG_MAINDBNAME
+** [[SQLITE_DBCONFIG_MAINDBNAME]]
SQLITE_DBCONFIG_MAINDBNAME
**
^This option is used to change the name of the "main" database ** schema. ^The sole argument is a pointer to a constant UTF8 string ** which will become the new schema name in place of "main". ^SQLite @@ -2098,6 +2103,7 @@ struct sqlite3_mem_methods { ** until after the database connection closes. **
** +** [[SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE]] **
SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE
**
Usually, when a database in wal mode is closed or detached from a ** database handle, SQLite checks if this will mean that there are now no @@ -2111,7 +2117,7 @@ struct sqlite3_mem_methods { ** have been disabled - 0 if they are not disabled, 1 if they are. **
** -**
SQLITE_DBCONFIG_ENABLE_QPSG
+** [[SQLITE_DBCONFIG_ENABLE_QPSG]]
SQLITE_DBCONFIG_ENABLE_QPSG
**
^(The SQLITE_DBCONFIG_ENABLE_QPSG option activates or deactivates ** the [query planner stability guarantee] (QPSG). When the QPSG is active, ** a single SQL query statement will always use the same algorithm regardless @@ -2127,7 +2133,7 @@ struct sqlite3_mem_methods { ** following this call. **
** -**
SQLITE_DBCONFIG_TRIGGER_EQP
+** [[SQLITE_DBCONFIG_TRIGGER_EQP]]
SQLITE_DBCONFIG_TRIGGER_EQP
**
By default, the output of EXPLAIN QUERY PLAN commands does not ** include output for any operations performed by trigger programs. This ** option is used to set or clear (the default) a flag that governs this @@ -2139,7 +2145,7 @@ struct sqlite3_mem_methods { ** it is not disabled, 1 if it is. **
** -**
SQLITE_DBCONFIG_RESET_DATABASE
+** [[SQLITE_DBCONFIG_RESET_DATABASE]]
SQLITE_DBCONFIG_RESET_DATABASE
**
Set the SQLITE_DBCONFIG_RESET_DATABASE flag and then run ** [VACUUM] in order to reset a database back to an empty database ** with no schema and no content. The following process works even for @@ -2158,6 +2164,18 @@ struct sqlite3_mem_methods { ** Because resetting a database is destructive and irreversible, the ** process requires the use of this obscure API and multiple steps to help ** ensure that it does not happen by accident. +** +** [[SQLITE_DBCONFIG_DEFENSIVE]]
SQLITE_DBCONFIG_DEFENSIVE
+**
The SQLITE_DBCONFIG_DEFENSIVE option activates or deactivates the +** "defensive" flag for a database connection. When the defensive +** flag is enabled, language features that allow ordinary SQL to +** deliberately corrupt the database file are disabled. The disabled +** features include but are not limited to the following: +**
    +**
  • The [PRAGMA writable_schema=ON] statement. +**
  • Writes to the [sqlite_dbpage] virtual table. +**
  • Direct writes to [shadow tables]. +**
**
**
*/ @@ -2171,7 +2189,8 @@ struct sqlite3_mem_methods { #define SQLITE_DBCONFIG_ENABLE_QPSG 1007 /* int int* */ #define SQLITE_DBCONFIG_TRIGGER_EQP 1008 /* int int* */ #define SQLITE_DBCONFIG_RESET_DATABASE 1009 /* int int* */ -#define SQLITE_DBCONFIG_MAX 1009 /* Largest DBCONFIG */ +#define SQLITE_DBCONFIG_DEFENSIVE 1010 /* int int* */ +#define SQLITE_DBCONFIG_MAX 1010 /* Largest DBCONFIG */ /* ** CAPI3REF: Enable Or Disable Extended Result Codes @@ -3609,9 +3628,19 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); ** on this hint by avoiding the use of [lookaside memory] so as not to ** deplete the limited store of lookaside memory. Future versions of ** SQLite may act on this hint differently. +** +** [[SQLITE_PREPARE_NORMALIZE]] ^(
SQLITE_PREPARE_NORMALIZE
+**
The SQLITE_PREPARE_NORMALIZE flag indicates that a normalized +** representation of the SQL statement should be calculated and then +** associated with the prepared statement, which can be obtained via +** the [sqlite3_normalized_sql()] interface.)^ The semantics used to +** normalize a SQL statement are unspecified and subject to change. +** At a minimum, literal values will be replaced with suitable +** placeholders. **
*/ #define SQLITE_PREPARE_PERSISTENT 0x01 +#define SQLITE_PREPARE_NORMALIZE 0x02 /* ** CAPI3REF: Compiling An SQL Statement @@ -3769,6 +3798,11 @@ SQLITE_API int sqlite3_prepare16_v3( ** ^The sqlite3_expanded_sql(P) interface returns a pointer to a UTF-8 ** string containing the SQL text of prepared statement P with ** [bound parameters] expanded. +** ^The sqlite3_normalized_sql(P) interface returns a pointer to a UTF-8 +** string containing the normalized SQL text of prepared statement P. The +** semantics used to normalize a SQL statement are unspecified and subject +** to change. At a minimum, literal values will be replaced with suitable +** placeholders. ** ** ^(For example, if a prepared statement is created using the SQL ** text "SELECT $abc,:xyz" and if parameter $abc is bound to integer 2345 @@ -3784,14 +3818,16 @@ SQLITE_API int sqlite3_prepare16_v3( ** bound parameter expansions. ^The [SQLITE_OMIT_TRACE] compile-time ** option causes sqlite3_expanded_sql() to always return NULL. ** -** ^The string returned by sqlite3_sql(P) is managed by SQLite and is -** automatically freed when the prepared statement is finalized. +** ^The strings returned by sqlite3_sql(P) and sqlite3_normalized_sql(P) +** are managed by SQLite and are automatically freed when the prepared +** statement is finalized. ** ^The string returned by sqlite3_expanded_sql(P), on the other hand, ** is obtained from [sqlite3_malloc()] and must be free by the application ** by passing it to [sqlite3_free()]. */ SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt); SQLITE_API char *sqlite3_expanded_sql(sqlite3_stmt *pStmt); +SQLITE_API const char *sqlite3_normalized_sql(sqlite3_stmt *pStmt); /* ** CAPI3REF: Determine If An SQL Statement Writes The Database @@ -6281,6 +6317,9 @@ struct sqlite3_module { int (*xSavepoint)(sqlite3_vtab *pVTab, int); int (*xRelease)(sqlite3_vtab *pVTab, int); int (*xRollbackTo)(sqlite3_vtab *pVTab, int); + /* The methods above are in versions 1 and 2 of the sqlite_module object. + ** Those below are for version 3 and greater. */ + int (*xShadowName)(const char*); }; /* @@ -7203,6 +7242,7 @@ SQLITE_API int sqlite3_test_control(int op, ...); #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 #define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */ #define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */ +#define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 17 #define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 #define SQLITE_TESTCTRL_EXPLAIN_STMT 19 /* NOT USED */ #define SQLITE_TESTCTRL_ONCE_RESET_THRESHOLD 19 @@ -8615,6 +8655,7 @@ SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...); ** can use to customize and optimize their behavior. ** **
+** [[SQLITE_VTAB_CONSTRAINT_SUPPORT]] **
SQLITE_VTAB_CONSTRAINT_SUPPORT **
Calls of the form ** [sqlite3_vtab_config](db,SQLITE_VTAB_CONSTRAINT_SUPPORT,X) are supported, @@ -9384,7 +9425,7 @@ struct sqlite3_rtree_query_info { sqlite3_int64 iRowid; /* Rowid for current entry */ sqlite3_rtree_dbl rParentScore; /* Score of parent node */ int eParentWithin; /* Visibility of parent node */ - int eWithin; /* OUT: Visiblity */ + int eWithin; /* OUT: Visibility */ sqlite3_rtree_dbl rScore; /* OUT: Write the score here */ /* The following fields are only available in 3.8.11 and later */ sqlite3_value **apSqlParam; /* Original SQL values of parameters */ @@ -9880,12 +9921,38 @@ SQLITE_API int sqlite3session_isempty(sqlite3_session *pSession); ** consecutively. There is no chance that the iterator will visit a change ** the applies to table X, then one for table Y, and then later on visit ** another change for table X. +** +** The behavior of sqlite3changeset_start_v2() and its streaming equivalent +** may be modified by passing a combination of +** [SQLITE_CHANGESETSTART_INVERT | supported flags] as the 4th parameter. +** +** Note that the sqlite3changeset_start_v2() API is still experimental +** and therefore subject to change. */ SQLITE_API int sqlite3changeset_start( sqlite3_changeset_iter **pp, /* OUT: New changeset iterator handle */ int nChangeset, /* Size of changeset blob in bytes */ void *pChangeset /* Pointer to blob containing changeset */ ); +SQLITE_API int sqlite3changeset_start_v2( + sqlite3_changeset_iter **pp, /* OUT: New changeset iterator handle */ + int nChangeset, /* Size of changeset blob in bytes */ + void *pChangeset, /* Pointer to blob containing changeset */ + int flags /* SESSION_CHANGESETSTART_* flags */ +); + +/* +** CAPI3REF: Flags for sqlite3changeset_start_v2 +** +** The following flags may passed via the 4th parameter to +** [sqlite3changeset_start_v2] and [sqlite3changeset_start_v2_strm]: +** +**
SQLITE_CHANGESETAPPLY_INVERT
+** Invert the changeset while iterating through it. This is equivalent to +** inverting a changeset using sqlite3changeset_invert() before applying it. +** It is an error to specify this flag with a patchset. +*/ +#define SQLITE_CHANGESETSTART_INVERT 0x0002 /* @@ -10540,7 +10607,7 @@ SQLITE_API int sqlite3changeset_apply_v2( ), void *pCtx, /* First argument passed to xConflict */ void **ppRebase, int *pnRebase, /* OUT: Rebase data */ - int flags /* Combination of SESSION_APPLY_* flags */ + int flags /* SESSION_CHANGESETAPPLY_* flags */ ); /* @@ -10558,8 +10625,14 @@ SQLITE_API int sqlite3changeset_apply_v2( ** causes the sessions module to omit this savepoint. In this case, if the ** caller has an open transaction or savepoint when apply_v2() is called, ** it may revert the partially applied changeset by rolling it back. +** +**
SQLITE_CHANGESETAPPLY_INVERT
+** Invert the changeset before applying it. This is equivalent to inverting +** a changeset using sqlite3changeset_invert() before applying it. It is +** an error to specify this flag with a patchset. */ #define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001 +#define SQLITE_CHANGESETAPPLY_INVERT 0x0002 /* ** CAPI3REF: Constants Passed To The Conflict Handler @@ -10953,6 +11026,12 @@ SQLITE_API int sqlite3changeset_start_strm( int (*xInput)(void *pIn, void *pData, int *pnData), void *pIn ); +SQLITE_API int sqlite3changeset_start_v2_strm( + sqlite3_changeset_iter **pp, + int (*xInput)(void *pIn, void *pData, int *pnData), + void *pIn, + int flags +); SQLITE_API int sqlite3session_changeset_strm( sqlite3_session *pSession, int (*xOutput)(void *pOut, const void *pData, int nData), @@ -10979,6 +11058,45 @@ SQLITE_API int sqlite3rebaser_rebase_strm( void *pOut ); +/* +** CAPI3REF: Configure global parameters +** +** The sqlite3session_config() interface is used to make global configuration +** changes to the sessions module in order to tune it to the specific needs +** of the application. +** +** The sqlite3session_config() interface is not threadsafe. If it is invoked +** while any other thread is inside any other sessions method then the +** results are undefined. Furthermore, if it is invoked after any sessions +** related objects have been created, the results are also undefined. +** +** The first argument to the sqlite3session_config() function must be one +** of the SQLITE_SESSION_CONFIG_XXX constants defined below. The +** interpretation of the (void*) value passed as the second parameter and +** the effect of calling this function depends on the value of the first +** parameter. +** +**
+**
SQLITE_SESSION_CONFIG_STRMSIZE
+** By default, the sessions module streaming interfaces attempt to input +** and output data in approximately 1 KiB chunks. This operand may be used +** to set and query the value of this configuration setting. The pointer +** passed as the second argument must point to a value of type (int). +** If this value is greater than 0, it is used as the new streaming data +** chunk size for both input and output. Before returning, the (int) value +** pointed to by pArg is set to the final value of the streaming interface +** chunk size. +**
+** +** This function returns SQLITE_OK if successful, or an SQLite error code +** otherwise. +*/ +SQLITE_API int sqlite3session_config(int op, void *pArg); + +/* +** CAPI3REF: Values for sqlite3session_config(). +*/ +#define SQLITE_SESSION_CONFIG_STRMSIZE 1 /* ** Make sure we can call this stuff from C++. diff --git a/libsqlite3-sys/sqlite3/sqlite3ext.h b/libsqlite3-sys/sqlite3/sqlite3ext.h index 35d9950..34c41fd 100644 --- a/libsqlite3-sys/sqlite3/sqlite3ext.h +++ b/libsqlite3-sys/sqlite3/sqlite3ext.h @@ -310,12 +310,15 @@ struct sqlite3_api_routines { int (*str_errcode)(sqlite3_str*); int (*str_length)(sqlite3_str*); char *(*str_value)(sqlite3_str*); + /* Version 3.25.0 and later */ int (*create_window_function)(sqlite3*,const char*,int,int,void*, void (*xStep)(sqlite3_context*,int,sqlite3_value**), void (*xFinal)(sqlite3_context*), void (*xValue)(sqlite3_context*), void (*xInv)(sqlite3_context*,int,sqlite3_value**), void(*xDestroy)(void*)); + /* Version 3.26.0 and later */ + const char *(*normalized_sql)(sqlite3_stmt*); }; /* @@ -603,6 +606,8 @@ typedef int (*sqlite3_loadext_entry)( #define sqlite3_str_value sqlite3_api->str_value /* Version 3.25.0 and later */ #define sqlite3_create_window_function sqlite3_api->create_window_function +/* Version 3.26.0 and later */ +#define sqlite3_normalized_sql sqlite3_api->normalized_sql #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) diff --git a/libsqlite3-sys/src/error.rs b/libsqlite3-sys/src/error.rs index 0be28ed..9e318e0 100644 --- a/libsqlite3-sys/src/error.rs +++ b/libsqlite3-sys/src/error.rs @@ -98,7 +98,7 @@ impl Error { } impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "Error code {}: {}", @@ -115,9 +115,10 @@ impl error::Error for Error { } // Result codes. -// Note: These are not public because our bindgen bindings export whichever constants are present -// in the current version of SQLite. We repeat them here so we don't have to worry about which -// version of SQLite added which constants, and we only use them to implement code_to_str below. +// Note: These are not public because our bindgen bindings export whichever +// constants are present in the current version of SQLite. We repeat them here +// so we don't have to worry about which version of SQLite added which +// constants, and we only use them to implement code_to_str below. const SQLITE_NOTICE: c_int = 27; const SQLITE_WARNING: c_int = 28; diff --git a/libsqlite3-sys/src/lib.rs b/libsqlite3-sys/src/lib.rs index 0e76759..795d604 100644 --- a/libsqlite3-sys/src/lib.rs +++ b/libsqlite3-sys/src/lib.rs @@ -16,32 +16,36 @@ pub fn SQLITE_TRANSIENT() -> sqlite3_destructor_type { } /// Run-Time Limit Categories -#[repr(C)] +#[repr(i32)] pub enum Limit { /// The maximum size of any string or BLOB or table row, in bytes. - SQLITE_LIMIT_LENGTH = SQLITE_LIMIT_LENGTH as isize, + SQLITE_LIMIT_LENGTH = SQLITE_LIMIT_LENGTH, /// The maximum length of an SQL statement, in bytes. - SQLITE_LIMIT_SQL_LENGTH = SQLITE_LIMIT_SQL_LENGTH as isize, - /// The maximum number of columns in a table definition or in the result set of a SELECT - /// or the maximum number of columns in an index or in an ORDER BY or GROUP BY clause. - SQLITE_LIMIT_COLUMN = SQLITE_LIMIT_COLUMN as isize, + SQLITE_LIMIT_SQL_LENGTH = SQLITE_LIMIT_SQL_LENGTH, + /// The maximum number of columns in a table definition or in the result set + /// of a SELECT or the maximum number of columns in an index or in an + /// ORDER BY or GROUP BY clause. + SQLITE_LIMIT_COLUMN = SQLITE_LIMIT_COLUMN, /// The maximum depth of the parse tree on any expression. - SQLITE_LIMIT_EXPR_DEPTH = SQLITE_LIMIT_EXPR_DEPTH as isize, + SQLITE_LIMIT_EXPR_DEPTH = SQLITE_LIMIT_EXPR_DEPTH, /// The maximum number of terms in a compound SELECT statement. - SQLITE_LIMIT_COMPOUND_SELECT = SQLITE_LIMIT_COMPOUND_SELECT as isize, - /// The maximum number of instructions in a virtual machine program used to implement an SQL statement. - SQLITE_LIMIT_VDBE_OP = SQLITE_LIMIT_VDBE_OP as isize, + SQLITE_LIMIT_COMPOUND_SELECT = SQLITE_LIMIT_COMPOUND_SELECT, + /// The maximum number of instructions in a virtual machine program used to + /// implement an SQL statement. + SQLITE_LIMIT_VDBE_OP = SQLITE_LIMIT_VDBE_OP, /// The maximum number of arguments on a function. - SQLITE_LIMIT_FUNCTION_ARG = SQLITE_LIMIT_FUNCTION_ARG as isize, + SQLITE_LIMIT_FUNCTION_ARG = SQLITE_LIMIT_FUNCTION_ARG, /// The maximum number of attached databases. - SQLITE_LIMIT_ATTACHED = SQLITE_LIMIT_ATTACHED as isize, - /// The maximum length of the pattern argument to the LIKE or GLOB operators. - SQLITE_LIMIT_LIKE_PATTERN_LENGTH = SQLITE_LIMIT_LIKE_PATTERN_LENGTH as isize, + SQLITE_LIMIT_ATTACHED = SQLITE_LIMIT_ATTACHED, + /// The maximum length of the pattern argument to the LIKE or GLOB + /// operators. + SQLITE_LIMIT_LIKE_PATTERN_LENGTH = SQLITE_LIMIT_LIKE_PATTERN_LENGTH, /// The maximum index number of any parameter in an SQL statement. - SQLITE_LIMIT_VARIABLE_NUMBER = SQLITE_LIMIT_VARIABLE_NUMBER as isize, + SQLITE_LIMIT_VARIABLE_NUMBER = SQLITE_LIMIT_VARIABLE_NUMBER, /// The maximum depth of recursion for triggers. SQLITE_LIMIT_TRIGGER_DEPTH = 10, - /// The maximum number of auxiliary worker threads that a single prepared statement may start. + /// The maximum number of auxiliary worker threads that a single prepared + /// statement may start. SQLITE_LIMIT_WORKER_THREADS = 11, } diff --git a/libsqlite3-sys/upgrade.sh b/libsqlite3-sys/upgrade.sh index f5b84bc..72b8d1e 100755 --- a/libsqlite3-sys/upgrade.sh +++ b/libsqlite3-sys/upgrade.sh @@ -4,7 +4,7 @@ cd $SCRIPT_DIR export SQLITE3_LIB_DIR=$SCRIPT_DIR/sqlite3 # Download and extract amalgamation -SQLITE=sqlite-amalgamation-3250200 +SQLITE=sqlite-amalgamation-3260000 curl -O http://sqlite.org/2018/$SQLITE.zip unzip -p $SQLITE.zip $SQLITE/sqlite3.c > $SQLITE3_LIB_DIR/sqlite3.c unzip -p $SQLITE.zip $SQLITE/sqlite3.h > $SQLITE3_LIB_DIR/sqlite3.h @@ -22,5 +22,5 @@ find $SCRIPT_DIR/target -type f -name bindgen.rs -exec cp {} $SQLITE3_LIB_DIR/bi # Sanity check cd $SCRIPT_DIR/.. cargo update -cargo test --features "backup blob chrono functions limits load_extension serde_json trace bundled" +cargo test --features "backup blob chrono functions limits load_extension serde_json trace vtab bundled" echo 'You should increment the version in libsqlite3-sys/Cargo.toml' diff --git a/src/backup.rs b/src/backup.rs index e3646e8..0253ecb 100644 --- a/src/backup.rs +++ b/src/backup.rs @@ -23,8 +23,8 @@ //! dst: P, //! progress: fn(backup::Progress), //! ) -> Result<()> { -//! let mut dst = try!(Connection::open(dst)); -//! let backup = try!(backup::Backup::new(src, &mut dst)); +//! let mut dst = Connection::open(dst)?; +//! let backup = backup::Backup::new(src, &mut dst)?; //! backup.run_to_completion(5, time::Duration::from_millis(250), Some(progress)) //! } //! ``` @@ -37,10 +37,10 @@ use std::os::raw::c_int; use std::thread; use std::time::Duration; -use ffi; +use crate::ffi; -use error::{error_from_handle, error_from_sqlite_code}; -use {Connection, DatabaseName, Result}; +use crate::error::{error_from_handle, error_from_sqlite_code}; +use crate::{Connection, DatabaseName, Result}; impl Connection { /// Back up the `name` database to the given destination path. @@ -57,22 +57,17 @@ impl Connection { /// or if the backup fails. pub fn backup>( &self, - name: DatabaseName, + name: DatabaseName<'_>, dst_path: P, progress: Option, ) -> Result<()> { use self::StepResult::{Busy, Done, Locked, More}; - let mut dst = try!(Connection::open(dst_path)); - let backup = try!(Backup::new_with_names( - self, - name, - &mut dst, - DatabaseName::Main - )); + let mut dst = Connection::open(dst_path)?; + let backup = Backup::new_with_names(self, name, &mut dst, DatabaseName::Main)?; let mut r = More; while r == More { - r = try!(backup.step(100)); + r = backup.step(100)?; if let Some(f) = progress { f(backup.progress()); } @@ -100,18 +95,18 @@ impl Connection { /// or if the restore fails. pub fn restore, F: Fn(Progress)>( &mut self, - name: DatabaseName, + name: DatabaseName<'_>, src_path: P, progress: Option, ) -> Result<()> { use self::StepResult::{Busy, Done, Locked, More}; - let src = try!(Connection::open(src_path)); - let restore = try!(Backup::new_with_names(&src, DatabaseName::Main, self, name)); + let src = Connection::open(src_path)?; + let restore = Backup::new_with_names(&src, DatabaseName::Main, self, name)?; let mut r = More; let mut busy_count = 0i32; 'restore_loop: while r == More || r == Busy { - r = try!(restore.step(100)); + r = restore.step(100)?; if let Some(ref f) = progress { f(restore.progress()); } @@ -197,12 +192,12 @@ impl<'a, 'b> Backup<'a, 'b> { /// `NULL`. pub fn new_with_names( from: &'a Connection, - from_name: DatabaseName, + from_name: DatabaseName<'_>, to: &'b mut Connection, - to_name: DatabaseName, + to_name: DatabaseName<'_>, ) -> Result> { - let to_name = try!(to_name.to_cstring()); - let from_name = try!(from_name.to_cstring()); + let to_name = to_name.to_cstring()?; + let from_name = from_name.to_cstring()?; let to_db = to.db.borrow_mut().db; @@ -287,7 +282,7 @@ impl<'a, 'b> Backup<'a, 'b> { assert!(pages_per_step > 0, "pages_per_step must be positive"); loop { - let r = try!(self.step(pages_per_step)); + let r = self.step(pages_per_step)?; if let Some(progress) = progress { progress(self.progress()) } @@ -308,8 +303,8 @@ impl<'a, 'b> Drop for Backup<'a, 'b> { #[cfg(test)] mod test { use super::Backup; + use crate::{Connection, DatabaseName, NO_PARAMS}; use std::time::Duration; - use {Connection, DatabaseName, NO_PARAMS}; #[test] fn test_backup() { diff --git a/src/blob.rs b/src/blob.rs index 8ed630a..9692d60 100644 --- a/src/blob.rs +++ b/src/blob.rs @@ -14,9 +14,6 @@ //! ## Example //! //! ```rust -//! extern crate libsqlite3_sys; -//! extern crate rusqlite; -//! //! use rusqlite::blob::ZeroBlob; //! use rusqlite::{Connection, DatabaseName, NO_PARAMS}; //! use std::io::{Read, Seek, SeekFrom, Write}; @@ -64,7 +61,7 @@ use std::ptr; use super::ffi; use super::types::{ToSql, ToSqlOutput}; -use {Connection, DatabaseName, Result}; +use crate::{Connection, DatabaseName, Result}; /// Handle to an open BLOB. pub struct Blob<'conn> { @@ -84,7 +81,7 @@ impl Connection { /// fails. pub fn blob_open<'a>( &'a self, - db: DatabaseName, + db: DatabaseName<'_>, table: &str, column: &str, row_id: i64, @@ -92,9 +89,9 @@ impl Connection { ) -> Result> { let mut c = self.db.borrow_mut(); let mut blob = ptr::null_mut(); - let db = try!(db.to_cstring()); - let table = try!(super::str_to_cstring(table)); - let column = try!(super::str_to_cstring(column)); + let db = db.to_cstring()?; + let table = super::str_to_cstring(table)?; + let column = super::str_to_cstring(column)?; let rc = unsafe { ffi::sqlite3_blob_open( c.db(), @@ -254,7 +251,7 @@ impl<'conn> Drop for Blob<'conn> { pub struct ZeroBlob(pub i32); impl ToSql for ZeroBlob { - fn to_sql(&self) -> Result { + fn to_sql(&self) -> Result> { let ZeroBlob(length) = *self; Ok(ToSqlOutput::ZeroBlob(length)) } @@ -262,16 +259,16 @@ impl ToSql for ZeroBlob { #[cfg(test)] mod test { + use crate::{Connection, DatabaseName, Result}; use std::io::{BufRead, BufReader, BufWriter, Read, Seek, SeekFrom, Write}; - use {Connection, DatabaseName, Result}; fn db_with_test_blob() -> Result<(Connection, i64)> { - let db = try!(Connection::open_in_memory()); + let db = Connection::open_in_memory()?; let sql = "BEGIN; CREATE TABLE test (content BLOB); INSERT INTO test VALUES (ZEROBLOB(10)); END;"; - try!(db.execute_batch(sql)); + db.execute_batch(sql)?; let rowid = db.last_insert_rowid(); Ok((db, rowid)) } diff --git a/src/busy.rs b/src/busy.rs index 84a810b..e0ee835 100644 --- a/src/busy.rs +++ b/src/busy.rs @@ -1,11 +1,12 @@ ///! Busy handler (when the database is locked) use std::mem; use std::os::raw::{c_int, c_void}; +use std::panic::catch_unwind; use std::ptr; use std::time::Duration; -use ffi; -use {Connection, InnerConnection, Result}; +use crate::ffi; +use crate::{Connection, InnerConnection, Result}; impl Connection { /// Set a busy handler that sleeps for a specified amount of time when a @@ -48,7 +49,7 @@ impl Connection { pub fn busy_handler(&self, callback: Option bool>) -> Result<()> { unsafe extern "C" fn busy_handler_callback(p_arg: *mut c_void, count: c_int) -> c_int { let handler_fn: fn(i32) -> bool = mem::transmute(p_arg); - if handler_fn(count) { + if let Ok(true) = catch_unwind(|| handler_fn(count)) { 1 } else { 0 @@ -74,14 +75,14 @@ impl InnerConnection { #[cfg(test)] mod test { - extern crate tempdir; use self::tempdir::TempDir; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::sync_channel; use std::thread; use std::time::Duration; + use tempdir; - use {Connection, Error, ErrorCode, TransactionBehavior, NO_PARAMS}; + use crate::{Connection, Error, ErrorCode, TransactionBehavior, NO_PARAMS}; #[test] fn test_default_busy() { diff --git a/src/cache.rs b/src/cache.rs index 075bc6e..da128a8 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -1,10 +1,10 @@ //! Prepared statements cache for faster execution. +use crate::raw_statement::RawStatement; +use crate::{Connection, Result, Statement}; use lru_cache::LruCache; -use raw_statement::RawStatement; use std::cell::RefCell; use std::ops::{Deref, DerefMut}; -use {Connection, Result, Statement}; impl Connection { /// Prepare a SQL statement for execution, returning a previously prepared @@ -16,14 +16,14 @@ impl Connection { /// # use rusqlite::{Connection, Result}; /// fn insert_new_people(conn: &Connection) -> Result<()> { /// { - /// let mut stmt = try!(conn.prepare_cached("INSERT INTO People (name) VALUES (?)")); - /// try!(stmt.execute(&["Joe Smith"])); + /// let mut stmt = conn.prepare_cached("INSERT INTO People (name) VALUES (?)")?; + /// stmt.execute(&["Joe Smith"])?; /// } /// { /// // This will return the same underlying SQLite statement handle without /// // having to prepare it again. - /// let mut stmt = try!(conn.prepare_cached("INSERT INTO People (name) VALUES (?)")); - /// try!(stmt.execute(&["Bob Jones"])); + /// let mut stmt = conn.prepare_cached("INSERT INTO People (name) VALUES (?)")?; + /// stmt.execute(&["Bob Jones"])?; /// } /// Ok(()) /// } @@ -33,7 +33,7 @@ impl Connection { /// /// Will return `Err` if `sql` cannot be converted to a C-compatible string /// or if the underlying SQLite call fails. - pub fn prepare_cached<'a>(&'a self, sql: &str) -> Result> { + pub fn prepare_cached(&self, sql: &str) -> Result> { self.cache.get(self, sql) } @@ -152,7 +152,7 @@ impl StatementCache { #[cfg(test)] mod test { use super::StatementCache; - use {Connection, NO_PARAMS}; + use crate::{Connection, NO_PARAMS}; impl StatementCache { fn clear(&self) { diff --git a/src/context.rs b/src/context.rs index e4ce95a..a7bb468 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,69 +1,18 @@ //! Code related to `sqlite3_context` common to `functions` and `vtab` modules. -use std::ffi::CStr; -use std::os::raw::{c_char, c_int, c_void}; +use std::os::raw::{c_int, c_void}; #[cfg(feature = "array")] use std::rc::Rc; -use ffi; -use ffi::sqlite3_context; -use ffi::sqlite3_value; +use crate::ffi; +use crate::ffi::sqlite3_context; -use str_to_cstring; -use types::{ToSqlOutput, ValueRef}; +use crate::str_to_cstring; +use crate::types::{ToSqlOutput, ValueRef}; #[cfg(feature = "array")] -use vtab::array::{free_array, ARRAY_TYPE}; +use crate::vtab::array::{free_array, ARRAY_TYPE}; -impl<'a> ValueRef<'a> { - pub(crate) unsafe fn from_value(value: *mut sqlite3_value) -> ValueRef<'a> { - use std::slice::from_raw_parts; - - match ffi::sqlite3_value_type(value) { - ffi::SQLITE_NULL => ValueRef::Null, - ffi::SQLITE_INTEGER => ValueRef::Integer(ffi::sqlite3_value_int64(value)), - ffi::SQLITE_FLOAT => ValueRef::Real(ffi::sqlite3_value_double(value)), - ffi::SQLITE_TEXT => { - let text = ffi::sqlite3_value_text(value); - assert!( - !text.is_null(), - "unexpected SQLITE_TEXT value type with NULL data" - ); - let s = CStr::from_ptr(text as *const c_char); - - // sqlite3_value_text returns UTF8 data, so our unwrap here should be fine. - let s = s - .to_str() - .expect("sqlite3_value_text returned invalid UTF-8"); - ValueRef::Text(s) - } - ffi::SQLITE_BLOB => { - let (blob, len) = ( - ffi::sqlite3_value_blob(value), - ffi::sqlite3_value_bytes(value), - ); - - assert!( - len >= 0, - "unexpected negative return from sqlite3_value_bytes" - ); - if len > 0 { - assert!( - !blob.is_null(), - "unexpected SQLITE_BLOB value type with NULL data" - ); - ValueRef::Blob(from_raw_parts(blob as *const u8, len as usize)) - } else { - // The return value from sqlite3_value_blob() for a zero-length BLOB - // is a NULL pointer. - ValueRef::Blob(&[]) - } - } - _ => unreachable!("sqlite3_value_type returned invalid value"), - } - } -} - -pub(crate) unsafe fn set_result<'a>(ctx: *mut sqlite3_context, result: &ToSqlOutput<'a>) { +pub(crate) unsafe fn set_result(ctx: *mut sqlite3_context, result: &ToSqlOutput<'_>) { let value = match *result { ToSqlOutput::Borrowed(v) => v, ToSqlOutput::Owned(ref v) => ValueRef::from(v), diff --git a/src/error.rs b/src/error.rs index 5708458..509bc76 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,18 +1,14 @@ +use crate::types::Type; +use crate::{errmsg_to_string, ffi}; use std::error; use std::fmt; use std::os::raw::c_int; use std::path::PathBuf; use std::str; -use types::Type; -use {errmsg_to_string, ffi}; - -/// Old name for `Error`. `SqliteError` is deprecated. -#[deprecated(since = "0.6.0", note = "Use Error instead")] -pub type SqliteError = Error; /// Enum listing possible errors from rusqlite. #[derive(Debug)] -#[allow(enum_variant_names)] +#[allow(clippy::enum_variant_names)] pub enum Error { /// An error from an underlying SQLite call. SqliteFailure(ffi::Error, Option), @@ -23,7 +19,7 @@ pub enum Error { /// Error when the value of a particular column is requested, but it cannot /// be converted to the requested Rust type. - FromSqlConversionFailure(usize, Type, Box), + FromSqlConversionFailure(usize, Type, Box), /// Error when SQLite gives us an integral value outside the range of the /// requested type (e.g., trying to get the value 1000 into a `u8`). @@ -82,10 +78,10 @@ pub enum Error { /// `create_scalar_function`). #[cfg(feature = "functions")] #[allow(dead_code)] - UserFunctionError(Box), + UserFunctionError(Box), /// Error available for the implementors of the `ToSql` trait. - ToSqlConversionFailure(Box), + ToSqlConversionFailure(Box), /// Error when the SQL is not a `SELECT`, is not read-only. InvalidQuery, @@ -96,8 +92,57 @@ pub enum Error { #[allow(dead_code)] ModuleError(String), + #[cfg(feature = "functions")] + UnwindingPanic, + + /// An error returned when `Context::get_aux` attempts to retrieve data + /// of a different type than what had been stored using `Context::set_aux`. + #[cfg(feature = "functions")] + GetAuxWrongType, + /// Error when the SQL contains multiple statements. - MultipleStatement, + MultipleStatement,} + +impl PartialEq for Error { + fn eq(&self, other: &Error) -> bool { + match (self, other) { + (Error::SqliteFailure(e1, s1), Error::SqliteFailure(e2, s2)) => e1 == e2 && s1 == s2, + (Error::SqliteSingleThreadedMode, Error::SqliteSingleThreadedMode) => true, + (Error::IntegralValueOutOfRange(i1, n1), Error::IntegralValueOutOfRange(i2, n2)) => { + i1 == i2 && n1 == n2 + } + (Error::Utf8Error(e1), Error::Utf8Error(e2)) => e1 == e2, + (Error::NulError(e1), Error::NulError(e2)) => e1 == e2, + (Error::InvalidParameterName(n1), Error::InvalidParameterName(n2)) => n1 == n2, + (Error::InvalidPath(p1), Error::InvalidPath(p2)) => p1 == p2, + (Error::ExecuteReturnedResults, Error::ExecuteReturnedResults) => true, + (Error::QueryReturnedNoRows, Error::QueryReturnedNoRows) => true, + (Error::InvalidColumnIndex(i1), Error::InvalidColumnIndex(i2)) => i1 == i2, + (Error::InvalidColumnName(n1), Error::InvalidColumnName(n2)) => n1 == n2, + (Error::InvalidColumnType(i1, t1), Error::InvalidColumnType(i2, t2)) => { + i1 == i2 && t1 == t2 + } + (Error::StatementChangedRows(n1), Error::StatementChangedRows(n2)) => n1 == n2, + #[cfg(feature = "functions")] + ( + Error::InvalidFunctionParameterType(i1, t1), + Error::InvalidFunctionParameterType(i2, t2), + ) => i1 == i2 && t1 == t2, + #[cfg(feature = "vtab")] + ( + Error::InvalidFilterParameterType(i1, t1), + Error::InvalidFilterParameterType(i2, t2), + ) => i1 == i2 && t1 == t2, + (Error::InvalidQuery, Error::InvalidQuery) => true, + #[cfg(feature = "vtab")] + (Error::ModuleError(s1), Error::ModuleError(s2)) => s1 == s2, + #[cfg(feature = "functions")] + (Error::UnwindingPanic, Error::UnwindingPanic) => true, + #[cfg(feature = "functions")] + (Error::GetAuxWrongType, Error::GetAuxWrongType) => true, + (_, _) => false, + } + } } impl From for Error { @@ -113,7 +158,7 @@ impl From<::std::ffi::NulError> for Error { } impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { Error::SqliteFailure(ref err, None) => err.fmt(f), Error::SqliteFailure(_, Some(ref s)) => write!(f, "{}", s), @@ -158,6 +203,10 @@ impl fmt::Display for Error { Error::InvalidQuery => write!(f, "Query is not read-only"), #[cfg(feature = "vtab")] Error::ModuleError(ref desc) => write!(f, "{}", desc), + #[cfg(feature = "functions")] + Error::UnwindingPanic => write!(f, "unwinding panic"), + #[cfg(feature = "functions")] + Error::GetAuxWrongType => write!(f, "get_aux called with wrong type"), Error::MultipleStatement => write!(f, "Multiple statements provided"), } } @@ -196,11 +245,15 @@ impl error::Error for Error { Error::InvalidQuery => "query is not read-only", #[cfg(feature = "vtab")] Error::ModuleError(ref desc) => desc, + #[cfg(feature = "functions")] + Error::UnwindingPanic => "unwinding panic", + #[cfg(feature = "functions")] + Error::GetAuxWrongType => "get_aux called with wrong type", Error::MultipleStatement => "multiple statements provided", } } - fn cause(&self) -> Option<&error::Error> { + fn cause(&self) -> Option<&dyn error::Error> { match *self { Error::SqliteFailure(ref err, _) => Some(err), Error::Utf8Error(ref err) => Some(err), @@ -232,6 +285,12 @@ impl error::Error for Error { #[cfg(feature = "vtab")] Error::ModuleError(_) => None, + + #[cfg(feature = "functions")] + Error::UnwindingPanic => None, + + #[cfg(feature = "functions")] + Error::GetAuxWrongType => None, } } } @@ -250,3 +309,12 @@ pub fn error_from_handle(db: *mut ffi::sqlite3, code: c_int) -> Error { }; error_from_sqlite_code(code, message) } + +macro_rules! check { + ($funcall:expr) => {{ + let rc = $funcall; + if rc != crate::ffi::SQLITE_OK { + Err(crate::error::error_from_sqlite_code(rc, None))?; + } + }}; +} diff --git a/src/functions.rs b/src/functions.rs index b8cc4f0..a94b9a7 100644 --- a/src/functions.rs +++ b/src/functions.rs @@ -9,10 +9,6 @@ //! module. //! //! ```rust -//! extern crate libsqlite3_sys; -//! extern crate rusqlite; -//! extern crate regex; -//! //! use regex::Regex; //! use rusqlite::{Connection, Error, Result, NO_PARAMS}; //! use std::collections::HashMap; @@ -20,7 +16,7 @@ //! fn add_regexp_function(db: &Connection) -> Result<()> { //! let mut cached_regexes = HashMap::new(); //! db.create_scalar_function("regexp", 2, true, move |ctx| { -//! let regex_s = try!(ctx.get::(0)); +//! let regex_s = ctx.get::(0)?; //! let entry = cached_regexes.entry(regex_s.clone()); //! let regex = { //! use std::collections::hash_map::Entry::{Occupied, Vacant}; @@ -33,7 +29,7 @@ //! } //! }; //! -//! let text = try!(ctx.get::(1)); +//! let text = ctx.get::(1)?; //! Ok(regex.is_match(&text)) //! }) //! } @@ -55,17 +51,18 @@ //! ``` use std::error::Error as StdError; use std::os::raw::{c_int, c_void}; +use std::panic::{catch_unwind, RefUnwindSafe, UnwindSafe}; use std::ptr; use std::slice; -use ffi; -use ffi::sqlite3_context; -use ffi::sqlite3_value; +use crate::ffi; +use crate::ffi::sqlite3_context; +use crate::ffi::sqlite3_value; -use context::set_result; -use types::{FromSql, FromSqlError, ToSql, ValueRef}; +use crate::context::set_result; +use crate::types::{FromSql, FromSqlError, ToSql, ValueRef}; -use {str_to_cstring, Connection, Error, InnerConnection, Result}; +use crate::{str_to_cstring, Connection, Error, InnerConnection, Result}; unsafe fn report_error(ctx: *mut sqlite3_context, err: &Error) { // Extended constraint error codes were added in SQLite 3.7.16. We don't have @@ -137,6 +134,10 @@ impl<'a> Context<'a> { FromSqlError::Other(err) => { Error::FromSqlConversionFailure(idx, value.data_type(), err) } + #[cfg(feature = "i128_blob")] + FromSqlError::InvalidI128Size(_) => { + Error::FromSqlConversionFailure(idx, value.data_type(), Box::new(err)) + } }) } @@ -153,32 +154,32 @@ impl<'a> Context<'a> { /// Sets the auxilliary data associated with a particular parameter. See /// https://www.sqlite.org/c3ref/get_auxdata.html for a discussion of /// this feature, or the unit tests of this module for an example. - pub fn set_aux(&self, arg: c_int, value: T) { - let boxed = Box::into_raw(Box::new(value)); + pub fn set_aux(&self, arg: c_int, value: T) { + let boxed = Box::into_raw(Box::new((std::any::TypeId::of::(), value))); unsafe { ffi::sqlite3_set_auxdata( self.ctx, arg, boxed as *mut c_void, - Some(free_boxed_value::), + Some(free_boxed_value::<(std::any::TypeId, T)>), ) }; } /// Gets the auxilliary data that was associated with a given parameter - /// via `set_aux`. Returns `None` if no data has been associated. - /// - /// # Unsafety - /// - /// This function is unsafe as there is no guarantee that the type `T` - /// requested matches the type `T` that was provided to `set_aux`. The - /// types must be identical. - pub unsafe fn get_aux(&self, arg: c_int) -> Option<&T> { - let p = ffi::sqlite3_get_auxdata(self.ctx, arg) as *mut T; + /// via `set_aux`. Returns `Ok(None)` if no data has been associated, + /// and . + pub fn get_aux(&self, arg: c_int) -> Result> { + let p = unsafe { ffi::sqlite3_get_auxdata(self.ctx, arg) as *mut (std::any::TypeId, T) }; if p.is_null() { - None + Ok(None) } else { - Some(&*p) + let id_val = unsafe { &*p }; + if std::any::TypeId::of::() != id_val.0 { + Err(Error::GetAuxWrongType) + } else { + Ok(Some(&id_val.1)) + } } } } @@ -189,6 +190,7 @@ impl<'a> Context<'a> { /// result. Implementations should be stateless. pub trait Aggregate where + A: RefUnwindSafe + UnwindSafe, T: ToSql, { /// Initializes the aggregation context. Will be called prior to the first @@ -198,14 +200,14 @@ where /// "step" function called once for each row in an aggregate group. May be /// called 0 times if there are no rows. - fn step(&self, &mut Context, &mut A) -> Result<()>; + fn step(&self, _: &mut Context<'_>, _: &mut A) -> Result<()>; /// Computes and returns the final result. Will be called exactly once for /// each invocation of the function. If `step()` was called at least /// once, will be given `Some(A)` (the same `A` as was created by /// `init` and given to `step`); if `step()` was not called (because /// the function is running against 0 rows), will be given `None`. - fn finalize(&self, Option) -> Result; + fn finalize(&self, _: Option) -> Result; } impl Connection { @@ -224,12 +226,12 @@ impl Connection { /// ```rust /// # use rusqlite::{Connection, Result, NO_PARAMS}; /// fn scalar_function_example(db: Connection) -> Result<()> { - /// try!(db.create_scalar_function("halve", 1, true, |ctx| { - /// let value = try!(ctx.get::(0)); + /// db.create_scalar_function("halve", 1, true, |ctx| { + /// let value = ctx.get::(0)?; /// Ok(value / 2f64) - /// })); + /// })?; /// - /// let six_halved: f64 = try!(db.query_row("SELECT halve(6)", NO_PARAMS, |r| r.get(0))); + /// let six_halved: f64 = db.query_row("SELECT halve(6)", NO_PARAMS, |r| r.get(0))?; /// assert_eq!(six_halved, 3f64); /// Ok(()) /// } @@ -246,7 +248,7 @@ impl Connection { x_func: F, ) -> Result<()> where - F: FnMut(&Context) -> Result + Send + 'static, + F: FnMut(&Context<'_>) -> Result + Send + UnwindSafe + 'static, T: ToSql, { self.db @@ -267,6 +269,7 @@ impl Connection { aggr: D, ) -> Result<()> where + A: RefUnwindSafe + UnwindSafe, D: Aggregate, T: ToSql, { @@ -297,7 +300,7 @@ impl InnerConnection { x_func: F, ) -> Result<()> where - F: FnMut(&Context) -> Result + Send + 'static, + F: FnMut(&Context<'_>) -> Result + Send + UnwindSafe + 'static, T: ToSql, { unsafe extern "C" fn call_boxed_closure( @@ -305,28 +308,36 @@ impl InnerConnection { argc: c_int, argv: *mut *mut sqlite3_value, ) where - F: FnMut(&Context) -> Result, + F: FnMut(&Context<'_>) -> Result, T: ToSql, { - let ctx = Context { - ctx, - args: slice::from_raw_parts(argv, argc as usize), + let r = catch_unwind(|| { + let boxed_f: *mut F = ffi::sqlite3_user_data(ctx) as *mut F; + assert!(!boxed_f.is_null(), "Internal error - null function pointer"); + let ctx = Context { + ctx, + args: slice::from_raw_parts(argv, argc as usize), + }; + (*boxed_f)(&ctx) + }); + let t = match r { + Err(_) => { + report_error(ctx, &Error::UnwindingPanic); + return; + } + Ok(r) => r, }; - let boxed_f: *mut F = ffi::sqlite3_user_data(ctx.ctx) as *mut F; - assert!(!boxed_f.is_null(), "Internal error - null function pointer"); - - let t = (*boxed_f)(&ctx); let t = t.as_ref().map(|t| ToSql::to_sql(t)); match t { - Ok(Ok(ref value)) => set_result(ctx.ctx, value), - Ok(Err(err)) => report_error(ctx.ctx, &err), - Err(err) => report_error(ctx.ctx, err), + Ok(Ok(ref value)) => set_result(ctx, value), + Ok(Err(err)) => report_error(ctx, &err), + Err(err) => report_error(ctx, err), } } let boxed_f: *mut F = Box::into_raw(Box::new(x_func)); - let c_name = try!(str_to_cstring(fn_name)); + let c_name = str_to_cstring(fn_name)?; let mut flags = ffi::SQLITE_UTF8; if deterministic { flags |= ffi::SQLITE_DETERMINISTIC; @@ -355,6 +366,7 @@ impl InnerConnection { aggr: D, ) -> Result<()> where + A: RefUnwindSafe + UnwindSafe, D: Aggregate, T: ToSql, { @@ -374,15 +386,10 @@ impl InnerConnection { argc: c_int, argv: *mut *mut sqlite3_value, ) where + A: RefUnwindSafe + UnwindSafe, D: Aggregate, T: ToSql, { - let boxed_aggr: *mut D = ffi::sqlite3_user_data(ctx) as *mut D; - assert!( - !boxed_aggr.is_null(), - "Internal error - null aggregate pointer" - ); - let pac = match aggregate_context(ctx, ::std::mem::size_of::<*mut A>()) { Some(pac) => pac, None => { @@ -391,32 +398,40 @@ impl InnerConnection { } }; - if (*pac as *mut A).is_null() { - *pac = Box::into_raw(Box::new((*boxed_aggr).init())); - } - - let mut ctx = Context { - ctx, - args: slice::from_raw_parts(argv, argc as usize), + let r = catch_unwind(|| { + let boxed_aggr: *mut D = ffi::sqlite3_user_data(ctx) as *mut D; + assert!( + !boxed_aggr.is_null(), + "Internal error - null aggregate pointer" + ); + if (*pac as *mut A).is_null() { + *pac = Box::into_raw(Box::new((*boxed_aggr).init())); + } + let mut ctx = Context { + ctx, + args: slice::from_raw_parts(argv, argc as usize), + }; + (*boxed_aggr).step(&mut ctx, &mut **pac) + }); + let r = match r { + Err(_) => { + report_error(ctx, &Error::UnwindingPanic); + return; + } + Ok(r) => r, }; - - match (*boxed_aggr).step(&mut ctx, &mut **pac) { + match r { Ok(_) => {} - Err(err) => report_error(ctx.ctx, &err), + Err(err) => report_error(ctx, &err), }; } unsafe extern "C" fn call_boxed_final(ctx: *mut sqlite3_context) where + A: RefUnwindSafe + UnwindSafe, D: Aggregate, T: ToSql, { - let boxed_aggr: *mut D = ffi::sqlite3_user_data(ctx) as *mut D; - assert!( - !boxed_aggr.is_null(), - "Internal error - null aggregate pointer" - ); - // Within the xFinal callback, it is customary to set N=0 in calls to // sqlite3_aggregate_context(C,N) so that no pointless memory allocations occur. let a: Option = match aggregate_context(ctx, 0) { @@ -431,7 +446,21 @@ impl InnerConnection { None => None, }; - let t = (*boxed_aggr).finalize(a); + let r = catch_unwind(|| { + let boxed_aggr: *mut D = ffi::sqlite3_user_data(ctx) as *mut D; + assert!( + !boxed_aggr.is_null(), + "Internal error - null aggregate pointer" + ); + (*boxed_aggr).finalize(a) + }); + let t = match r { + Err(_) => { + report_error(ctx, &Error::UnwindingPanic); + return; + } + Ok(r) => r, + }; let t = t.as_ref().map(|t| ToSql::to_sql(t)); match t { Ok(Ok(ref value)) => set_result(ctx, value), @@ -441,7 +470,7 @@ impl InnerConnection { } let boxed_aggr: *mut D = Box::into_raw(Box::new(aggr)); - let c_name = try!(str_to_cstring(fn_name)); + let c_name = str_to_cstring(fn_name)?; let mut flags = ffi::SQLITE_UTF8; if deterministic { flags |= ffi::SQLITE_DETERMINISTIC; @@ -463,7 +492,7 @@ impl InnerConnection { } fn remove_function(&mut self, fn_name: &str, n_arg: c_int) -> Result<()> { - let c_name = try!(str_to_cstring(fn_name)); + let c_name = str_to_cstring(fn_name)?; let r = unsafe { ffi::sqlite3_create_function_v2( self.db(), @@ -483,19 +512,19 @@ impl InnerConnection { #[cfg(test)] mod test { - extern crate regex; + use regex; use self::regex::Regex; use std::collections::HashMap; use std::f64::EPSILON; use std::os::raw::c_double; - use functions::{Aggregate, Context}; - use {Connection, Error, Result, NO_PARAMS}; + use crate::functions::{Aggregate, Context}; + use crate::{Connection, Error, Result, NO_PARAMS}; - fn half(ctx: &Context) -> Result { - assert!(ctx.len() == 1, "called with unexpected number of arguments"); - let value = try!(ctx.get::(0)); + fn half(ctx: &Context<'_>) -> Result { + assert_eq!(ctx.len(), 1, "called with unexpected number of arguments"); + let value = ctx.get::(0)?; Ok(value / 2f64) } @@ -523,13 +552,13 @@ mod test { // This implementation of a regexp scalar function uses SQLite's auxilliary data // (https://www.sqlite.org/c3ref/get_auxdata.html) to avoid recompiling the regular // expression multiple times within one query. - fn regexp_with_auxilliary(ctx: &Context) -> Result { - assert!(ctx.len() == 2, "called with unexpected number of arguments"); + fn regexp_with_auxilliary(ctx: &Context<'_>) -> Result { + assert_eq!(ctx.len(), 2, "called with unexpected number of arguments"); - let saved_re: Option<&Regex> = unsafe { ctx.get_aux(0) }; + let saved_re: Option<&Regex> = ctx.get_aux(0)?; let new_re = match saved_re { None => { - let s = try!(ctx.get::(0)); + let s = ctx.get::(0)?; match Regex::new(&s) { Ok(r) => Some(r), Err(err) => return Err(Error::UserFunctionError(Box::new(err))), @@ -605,9 +634,9 @@ mod test { // until the function is removed. let mut cached_regexes = HashMap::new(); db.create_scalar_function("regexp", 2, true, move |ctx| { - assert!(ctx.len() == 2, "called with unexpected number of arguments"); + assert_eq!(ctx.len(), 2, "called with unexpected number of arguments"); - let regex_s = try!(ctx.get::(0)); + let regex_s = ctx.get::(0)?; let entry = cached_regexes.entry(regex_s.clone()); let regex = { use std::collections::hash_map::Entry::{Occupied, Vacant}; @@ -620,7 +649,7 @@ mod test { } }; - let text = try!(ctx.get::(1)); + let text = ctx.get::(1)?; Ok(regex.is_match(&text)) }) .unwrap(); @@ -648,7 +677,7 @@ mod test { let mut ret = String::new(); for idx in 0..ctx.len() { - let s = try!(ctx.get::(idx)); + let s = ctx.get::(idx)?; ret.push_str(&s); } @@ -666,6 +695,31 @@ mod test { } } + #[test] + fn test_get_aux_type_checking() { + let db = Connection::open_in_memory().unwrap(); + db.create_scalar_function("example", 2, false, |ctx| { + if !ctx.get::(1)? { + ctx.set_aux::(0, 100); + } else { + assert_eq!(ctx.get_aux::(0), Err(Error::GetAuxWrongType)); + assert_eq!(ctx.get_aux::(0), Ok(Some(&100))); + } + Ok(true) + }) + .unwrap(); + + let res: bool = db + .query_row( + "SELECT example(0, i) FROM (SELECT 0 as i UNION SELECT 1)", + NO_PARAMS, + |r| r.get(0), + ) + .unwrap(); + // Doesn't actually matter, we'll assert in the function if there's a problem. + assert!(res); + } + struct Sum; struct Count; @@ -674,8 +728,8 @@ mod test { 0 } - fn step(&self, ctx: &mut Context, sum: &mut i64) -> Result<()> { - *sum += try!(ctx.get::(0)); + fn step(&self, ctx: &mut Context<'_>, sum: &mut i64) -> Result<()> { + *sum += ctx.get::(0)?; Ok(()) } @@ -689,7 +743,7 @@ mod test { 0 } - fn step(&self, _ctx: &mut Context, sum: &mut i64) -> Result<()> { + fn step(&self, _ctx: &mut Context<'_>, sum: &mut i64) -> Result<()> { *sum += 1; Ok(()) } diff --git a/src/hooks.rs b/src/hooks.rs index c36b313..3131462 100644 --- a/src/hooks.rs +++ b/src/hooks.rs @@ -2,89 +2,29 @@ #![allow(non_camel_case_types)] use std::os::raw::{c_char, c_int, c_void}; +use std::panic::catch_unwind; use std::ptr; -use ffi; +use crate::ffi; -use {Connection, InnerConnection}; +use crate::{Connection, InnerConnection}; -/// Authorizer Action Codes -#[derive(Debug, PartialEq)] +/// Action Codes +#[derive(Clone, Copy, Debug, PartialEq)] +#[repr(i32)] pub enum Action { UNKNOWN = -1, - SQLITE_CREATE_INDEX = ffi::SQLITE_CREATE_INDEX as isize, - SQLITE_CREATE_TABLE = ffi::SQLITE_CREATE_TABLE as isize, - SQLITE_CREATE_TEMP_INDEX = ffi::SQLITE_CREATE_TEMP_INDEX as isize, - SQLITE_CREATE_TEMP_TABLE = ffi::SQLITE_CREATE_TEMP_TABLE as isize, - SQLITE_CREATE_TEMP_TRIGGER = ffi::SQLITE_CREATE_TEMP_TRIGGER as isize, - SQLITE_CREATE_TEMP_VIEW = ffi::SQLITE_CREATE_TEMP_VIEW as isize, - SQLITE_CREATE_TRIGGER = ffi::SQLITE_CREATE_TRIGGER as isize, - SQLITE_CREATE_VIEW = ffi::SQLITE_CREATE_VIEW as isize, - SQLITE_DELETE = ffi::SQLITE_DELETE as isize, - SQLITE_DROP_INDEX = ffi::SQLITE_DROP_INDEX as isize, - SQLITE_DROP_TABLE = ffi::SQLITE_DROP_TABLE as isize, - SQLITE_DROP_TEMP_INDEX = ffi::SQLITE_DROP_TEMP_INDEX as isize, - SQLITE_DROP_TEMP_TABLE = ffi::SQLITE_DROP_TEMP_TABLE as isize, - SQLITE_DROP_TEMP_TRIGGER = ffi::SQLITE_DROP_TEMP_TRIGGER as isize, - SQLITE_DROP_TEMP_VIEW = ffi::SQLITE_DROP_TEMP_VIEW as isize, - SQLITE_DROP_TRIGGER = ffi::SQLITE_DROP_TRIGGER as isize, - SQLITE_DROP_VIEW = ffi::SQLITE_DROP_VIEW as isize, - SQLITE_INSERT = ffi::SQLITE_INSERT as isize, - SQLITE_PRAGMA = ffi::SQLITE_PRAGMA as isize, - SQLITE_READ = ffi::SQLITE_READ as isize, - SQLITE_SELECT = ffi::SQLITE_SELECT as isize, - SQLITE_TRANSACTION = ffi::SQLITE_TRANSACTION as isize, - SQLITE_UPDATE = ffi::SQLITE_UPDATE as isize, - SQLITE_ATTACH = ffi::SQLITE_ATTACH as isize, - SQLITE_DETACH = ffi::SQLITE_DETACH as isize, - SQLITE_ALTER_TABLE = ffi::SQLITE_ALTER_TABLE as isize, - SQLITE_REINDEX = ffi::SQLITE_REINDEX as isize, - SQLITE_ANALYZE = ffi::SQLITE_ANALYZE as isize, - SQLITE_CREATE_VTABLE = ffi::SQLITE_CREATE_VTABLE as isize, - SQLITE_DROP_VTABLE = ffi::SQLITE_DROP_VTABLE as isize, - SQLITE_FUNCTION = ffi::SQLITE_FUNCTION as isize, - SQLITE_SAVEPOINT = ffi::SQLITE_SAVEPOINT as isize, - SQLITE_COPY = ffi::SQLITE_COPY as isize, - SQLITE_RECURSIVE = 33, + SQLITE_DELETE = ffi::SQLITE_DELETE, + SQLITE_INSERT = ffi::SQLITE_INSERT, + SQLITE_UPDATE = ffi::SQLITE_UPDATE, } impl From for Action { fn from(code: i32) -> Action { match code { - ffi::SQLITE_CREATE_INDEX => Action::SQLITE_CREATE_INDEX, - ffi::SQLITE_CREATE_TABLE => Action::SQLITE_CREATE_TABLE, - ffi::SQLITE_CREATE_TEMP_INDEX => Action::SQLITE_CREATE_TEMP_INDEX, - ffi::SQLITE_CREATE_TEMP_TABLE => Action::SQLITE_CREATE_TEMP_TABLE, - ffi::SQLITE_CREATE_TEMP_TRIGGER => Action::SQLITE_CREATE_TEMP_TRIGGER, - ffi::SQLITE_CREATE_TEMP_VIEW => Action::SQLITE_CREATE_TEMP_VIEW, - ffi::SQLITE_CREATE_TRIGGER => Action::SQLITE_CREATE_TRIGGER, - ffi::SQLITE_CREATE_VIEW => Action::SQLITE_CREATE_VIEW, ffi::SQLITE_DELETE => Action::SQLITE_DELETE, - ffi::SQLITE_DROP_INDEX => Action::SQLITE_DROP_INDEX, - ffi::SQLITE_DROP_TABLE => Action::SQLITE_DROP_TABLE, - ffi::SQLITE_DROP_TEMP_INDEX => Action::SQLITE_DROP_TEMP_INDEX, - ffi::SQLITE_DROP_TEMP_TABLE => Action::SQLITE_DROP_TEMP_TABLE, - ffi::SQLITE_DROP_TEMP_TRIGGER => Action::SQLITE_DROP_TEMP_TRIGGER, - ffi::SQLITE_DROP_TEMP_VIEW => Action::SQLITE_DROP_TEMP_VIEW, - ffi::SQLITE_DROP_TRIGGER => Action::SQLITE_DROP_TRIGGER, - ffi::SQLITE_DROP_VIEW => Action::SQLITE_DROP_VIEW, ffi::SQLITE_INSERT => Action::SQLITE_INSERT, - ffi::SQLITE_PRAGMA => Action::SQLITE_PRAGMA, - ffi::SQLITE_READ => Action::SQLITE_READ, - ffi::SQLITE_SELECT => Action::SQLITE_SELECT, - ffi::SQLITE_TRANSACTION => Action::SQLITE_TRANSACTION, ffi::SQLITE_UPDATE => Action::SQLITE_UPDATE, - ffi::SQLITE_ATTACH => Action::SQLITE_ATTACH, - ffi::SQLITE_DETACH => Action::SQLITE_DETACH, - ffi::SQLITE_ALTER_TABLE => Action::SQLITE_ALTER_TABLE, - ffi::SQLITE_REINDEX => Action::SQLITE_REINDEX, - ffi::SQLITE_ANALYZE => Action::SQLITE_ANALYZE, - ffi::SQLITE_CREATE_VTABLE => Action::SQLITE_CREATE_VTABLE, - ffi::SQLITE_DROP_VTABLE => Action::SQLITE_DROP_VTABLE, - ffi::SQLITE_FUNCTION => Action::SQLITE_FUNCTION, - ffi::SQLITE_SAVEPOINT => Action::SQLITE_SAVEPOINT, - ffi::SQLITE_COPY => Action::SQLITE_COPY, - 33 => Action::SQLITE_RECURSIVE, _ => Action::UNKNOWN, } } @@ -146,8 +86,11 @@ impl InnerConnection { where F: FnMut() -> bool, { - let boxed_hook: *mut F = p_arg as *mut F; - if (*boxed_hook)() { + let r = catch_unwind(|| { + let boxed_hook: *mut F = p_arg as *mut F; + (*boxed_hook)() + }); + if let Ok(true) = r { 1 } else { 0 @@ -192,8 +135,10 @@ impl InnerConnection { where F: FnMut(), { - let boxed_hook: *mut F = p_arg as *mut F; - (*boxed_hook)(); + let _ = catch_unwind(|| { + let boxed_hook: *mut F = p_arg as *mut F; + (*boxed_hook)(); + }); } let free_rollback_hook = if hook.is_some() { @@ -239,8 +184,6 @@ impl InnerConnection { use std::ffi::CStr; use std::str; - let boxed_hook: *mut F = p_arg as *mut F; - let action = Action::from(action_code); let db_name = { let c_slice = CStr::from_ptr(db_str).to_bytes(); @@ -251,7 +194,10 @@ impl InnerConnection { str::from_utf8_unchecked(c_slice) }; - (*boxed_hook)(action, db_name, tbl_name, row_id); + let _ = catch_unwind(|| { + let boxed_hook: *mut F = p_arg as *mut F; + (*boxed_hook)(action, db_name, tbl_name, row_id); + }); } let free_update_hook = if hook.is_some() { @@ -289,23 +235,23 @@ fn free_boxed_hook(p: *mut c_void) { #[cfg(test)] mod test { use super::Action; + use crate::Connection; use std::sync::atomic::{AtomicBool, Ordering}; - use Connection; #[test] fn test_commit_hook() { let db = Connection::open_in_memory().unwrap(); lazy_static! { - static ref called: AtomicBool = AtomicBool::new(false); + static ref CALLED: AtomicBool = AtomicBool::new(false); } db.commit_hook(Some(|| { - called.store(true, Ordering::Relaxed); + CALLED.store(true, Ordering::Relaxed); false })); db.execute_batch("BEGIN; CREATE TABLE foo (t TEXT); COMMIT;") .unwrap(); - assert!(called.load(Ordering::Relaxed)); + assert!(CALLED.load(Ordering::Relaxed)); } #[test] @@ -326,14 +272,14 @@ mod test { let db = Connection::open_in_memory().unwrap(); lazy_static! { - static ref called: AtomicBool = AtomicBool::new(false); + static ref CALLED: AtomicBool = AtomicBool::new(false); } db.rollback_hook(Some(|| { - called.store(true, Ordering::Relaxed); + CALLED.store(true, Ordering::Relaxed); })); db.execute_batch("BEGIN; CREATE TABLE foo (t TEXT); ROLLBACK;") .unwrap(); - assert!(called.load(Ordering::Relaxed)); + assert!(CALLED.load(Ordering::Relaxed)); } #[test] @@ -341,17 +287,17 @@ mod test { let db = Connection::open_in_memory().unwrap(); lazy_static! { - static ref called: AtomicBool = AtomicBool::new(false); + static ref CALLED: AtomicBool = AtomicBool::new(false); } db.update_hook(Some(|action, db: &str, tbl: &str, row_id| { assert_eq!(Action::SQLITE_INSERT, action); assert_eq!("main", db); assert_eq!("foo", tbl); assert_eq!(1, row_id); - called.store(true, Ordering::Relaxed); + CALLED.store(true, Ordering::Relaxed); })); db.execute_batch("CREATE TABLE foo (t TEXT)").unwrap(); db.execute_batch("INSERT INTO foo VALUES ('lisa')").unwrap(); - assert!(called.load(Ordering::Relaxed)); + assert!(CALLED.load(Ordering::Relaxed)); } } diff --git a/src/inner_connection.rs b/src/inner_connection.rs new file mode 100644 index 0000000..1591b82 --- /dev/null +++ b/src/inner_connection.rs @@ -0,0 +1,409 @@ +use std::ffi::CString; +use std::mem; +use std::os::raw::c_int; +#[cfg(feature = "load_extension")] +use std::path::Path; +use std::ptr; +use std::str; +use std::sync::atomic::{AtomicBool, Ordering, ATOMIC_BOOL_INIT}; +use std::sync::{Arc, Mutex, Once, ONCE_INIT}; + +use super::ffi; +use super::str_to_cstring; +use super::{Connection, InterruptHandle, OpenFlags, Result}; +use crate::error::{error_from_handle, error_from_sqlite_code, Error}; +use crate::raw_statement::RawStatement; +use crate::statement::Statement; +use crate::unlock_notify; +use crate::version::version_number; + +pub struct InnerConnection { + pub db: *mut ffi::sqlite3, + // It's unsafe to call `sqlite3_close` while another thread is performing + // a `sqlite3_interrupt`, and vice versa, so we take this mutex during + // those functions. This protects a copy of the `db` pointer (which is + // cleared on closing), however the main copy, `db`, is unprotected. + // Otherwise, a long running query would prevent calling interrupt, as + // interrupt would only acquire the lock after the query's completion. + interrupt_lock: Arc>, + #[cfg(feature = "hooks")] + pub free_commit_hook: Option, + #[cfg(feature = "hooks")] + pub free_rollback_hook: Option, + #[cfg(feature = "hooks")] + pub free_update_hook: Option, + owned: bool, +} + +impl InnerConnection { + #[cfg(not(feature = "hooks"))] + pub fn new(db: *mut ffi::sqlite3, owned: bool) -> InnerConnection { + InnerConnection { + db, + interrupt_lock: Arc::new(Mutex::new(db)), + owned, + } + } + + #[cfg(feature = "hooks")] + pub fn new(db: *mut ffi::sqlite3, owned: bool) -> InnerConnection { + InnerConnection { + db, + interrupt_lock: Arc::new(Mutex::new(db)), + free_commit_hook: None, + free_rollback_hook: None, + free_update_hook: None, + owned, + } + } + + pub fn open_with_flags(c_path: &CString, flags: OpenFlags) -> Result { + #[cfg(not(feature = "bundled"))] + ensure_valid_sqlite_version(); + ensure_safe_sqlite_threading_mode()?; + + // Replicate the check for sane open flags from SQLite, because the check in + // SQLite itself wasn't added until version 3.7.3. + debug_assert_eq!(1 << OpenFlags::SQLITE_OPEN_READ_ONLY.bits, 0x02); + debug_assert_eq!(1 << OpenFlags::SQLITE_OPEN_READ_WRITE.bits, 0x04); + debug_assert_eq!( + 1 << (OpenFlags::SQLITE_OPEN_READ_WRITE | OpenFlags::SQLITE_OPEN_CREATE).bits, + 0x40 + ); + if (1 << (flags.bits & 0x7)) & 0x46 == 0 { + return Err(Error::SqliteFailure( + ffi::Error::new(ffi::SQLITE_MISUSE), + None, + )); + } + + unsafe { + let mut db: *mut ffi::sqlite3 = mem::uninitialized(); + let r = ffi::sqlite3_open_v2(c_path.as_ptr(), &mut db, flags.bits(), ptr::null()); + if r != ffi::SQLITE_OK { + let e = if db.is_null() { + error_from_sqlite_code(r, None) + } else { + let e = error_from_handle(db, r); + ffi::sqlite3_close(db); + e + }; + + return Err(e); + } + let r = ffi::sqlite3_busy_timeout(db, 5000); + if r != ffi::SQLITE_OK { + let e = error_from_handle(db, r); + ffi::sqlite3_close(db); + return Err(e); + } + + // attempt to turn on extended results code; don't fail if we can't. + ffi::sqlite3_extended_result_codes(db, 1); + + Ok(InnerConnection::new(db, true)) + } + } + + pub fn db(&self) -> *mut ffi::sqlite3 { + self.db + } + + pub fn decode_result(&mut self, code: c_int) -> Result<()> { + InnerConnection::decode_result_raw(self.db(), code) + } + + fn decode_result_raw(db: *mut ffi::sqlite3, code: c_int) -> Result<()> { + if code == ffi::SQLITE_OK { + Ok(()) + } else { + Err(error_from_handle(db, code)) + } + } + + pub fn close(&mut self) -> Result<()> { + if self.db.is_null() { + return Ok(()); + } + self.remove_hooks(); + let mut shared_handle = self.interrupt_lock.lock().unwrap(); + assert!( + !shared_handle.is_null(), + "Bug: Somehow interrupt_lock was cleared before the DB was closed" + ); + if !self.owned { + self.db = ptr::null_mut(); + return Ok(()); + } + unsafe { + let r = ffi::sqlite3_close(self.db); + // Need to use _raw because _guard has a reference out, and + // decode_result takes &mut self. + let r = InnerConnection::decode_result_raw(self.db, r); + if r.is_ok() { + *shared_handle = ptr::null_mut(); + self.db = ptr::null_mut(); + } + r + } + } + + pub fn get_interrupt_handle(&self) -> InterruptHandle { + InterruptHandle { + db_lock: Arc::clone(&self.interrupt_lock), + } + } + + pub fn execute_batch(&mut self, sql: &str) -> Result<()> { + let c_sql = str_to_cstring(sql)?; + unsafe { + let r = ffi::sqlite3_exec( + self.db(), + c_sql.as_ptr(), + None, + ptr::null_mut(), + ptr::null_mut(), + ); + self.decode_result(r) + } + } + + #[cfg(feature = "load_extension")] + pub fn enable_load_extension(&mut self, onoff: c_int) -> Result<()> { + let r = unsafe { ffi::sqlite3_enable_load_extension(self.db, onoff) }; + self.decode_result(r) + } + + #[cfg(feature = "load_extension")] + pub fn load_extension(&self, dylib_path: &Path, entry_point: Option<&str>) -> Result<()> { + use std::os::raw::c_char; + + let dylib_str = super::path_to_cstring(dylib_path)?; + unsafe { + let mut errmsg: *mut c_char = mem::uninitialized(); + let r = if let Some(entry_point) = entry_point { + let c_entry = str_to_cstring(entry_point)?; + ffi::sqlite3_load_extension( + self.db, + dylib_str.as_ptr(), + c_entry.as_ptr(), + &mut errmsg, + ) + } else { + ffi::sqlite3_load_extension(self.db, dylib_str.as_ptr(), ptr::null(), &mut errmsg) + }; + if r == ffi::SQLITE_OK { + Ok(()) + } else { + let message = super::errmsg_to_string(&*errmsg); + ffi::sqlite3_free(errmsg as *mut ::std::os::raw::c_void); + Err(error_from_sqlite_code(r, Some(message))) + } + } + } + + pub fn last_insert_rowid(&self) -> i64 { + unsafe { ffi::sqlite3_last_insert_rowid(self.db()) } + } + + pub fn prepare<'a>(&mut self, conn: &'a Connection, sql: &str) -> Result> { + if sql.len() >= ::std::i32::MAX as usize { + return Err(error_from_sqlite_code(ffi::SQLITE_TOOBIG, None)); + } + let mut c_stmt: *mut ffi::sqlite3_stmt = unsafe { mem::uninitialized() }; + let c_sql = str_to_cstring(sql)?; + let len_with_nul = (sql.len() + 1) as c_int; + let mut c_tail = ptr::null(); + let r = unsafe { + if cfg!(feature = "unlock_notify") { + let mut rc; + loop { + rc = ffi::sqlite3_prepare_v2( + self.db(), + c_sql.as_ptr(), + len_with_nul, + &mut c_stmt, + &mut c_tail, + ); + if !unlock_notify::is_locked(self.db, rc) { + break; + } + rc = unlock_notify::wait_for_unlock_notify(self.db); + if rc != ffi::SQLITE_OK { + break; + } + } + rc + } else { + ffi::sqlite3_prepare_v2( + self.db(), + c_sql.as_ptr(), + len_with_nul, + &mut c_stmt, + &mut c_tail, + ) + } + }; + if !c_tail.is_null() && unsafe { *c_tail == 0 } { + // '\0' when there is no ';' at the end + c_tail = ptr::null(); // TODO ignore spaces, comments, ... at the end + } + self.decode_result(r) + .map(|_| Statement::new(conn, RawStatement::new(c_stmt, c_tail))) + } + + pub fn changes(&mut self) -> usize { + unsafe { ffi::sqlite3_changes(self.db()) as usize } + } + + pub fn is_autocommit(&self) -> bool { + unsafe { ffi::sqlite3_get_autocommit(self.db()) != 0 } + } + + #[cfg(feature = "bundled")] // 3.8.6 + pub fn is_busy(&self) -> bool { + let db = self.db(); + unsafe { + let mut stmt = ffi::sqlite3_next_stmt(db, ptr::null_mut()); + while !stmt.is_null() { + if ffi::sqlite3_stmt_busy(stmt) != 0 { + return true; + } + stmt = ffi::sqlite3_next_stmt(db, stmt); + } + } + false + } + + #[cfg(not(feature = "hooks"))] + fn remove_hooks(&mut self) {} +} + +impl Drop for InnerConnection { + #[allow(unused_must_use)] + fn drop(&mut self) { + use std::thread::panicking; + + if let Err(e) = self.close() { + if panicking() { + eprintln!("Error while closing SQLite connection: {:?}", e); + } else { + panic!("Error while closing SQLite connection: {:?}", e); + } + } + } +} + +#[cfg(not(feature = "bundled"))] +static SQLITE_VERSION_CHECK: Once = ONCE_INIT; +#[cfg(not(feature = "bundled"))] +pub static BYPASS_VERSION_CHECK: AtomicBool = ATOMIC_BOOL_INIT; + +#[cfg(not(feature = "bundled"))] +fn ensure_valid_sqlite_version() { + use crate::version::version; + + SQLITE_VERSION_CHECK.call_once(|| { + let version_number = version_number(); + + // Check our hard floor. + if version_number < 3_006_008 { + panic!("rusqlite requires SQLite 3.6.8 or newer"); + } + + // Check that the major version number for runtime and buildtime match. + let buildtime_major = ffi::SQLITE_VERSION_NUMBER / 1_000_000; + let runtime_major = version_number / 1_000_000; + if buildtime_major != runtime_major { + panic!( + "rusqlite was built against SQLite {} but is running with SQLite {}", + str::from_utf8(ffi::SQLITE_VERSION).unwrap(), + version() + ); + } + + if BYPASS_VERSION_CHECK.load(Ordering::Relaxed) { + return; + } + + // Check that the runtime version number is compatible with the version number + // we found at build-time. + if version_number < ffi::SQLITE_VERSION_NUMBER { + panic!( + "\ +rusqlite was built against SQLite {} but the runtime SQLite version is {}. To fix this, either: +* Recompile rusqlite and link against the SQLite version you are using at runtime, or +* Call rusqlite::bypass_sqlite_version_check() prior to your first connection attempt. Doing this + means you're sure everything will work correctly even though the runtime version is older than + the version we found at build time.", + str::from_utf8(ffi::SQLITE_VERSION).unwrap(), + version() + ); + } + }); +} + +static SQLITE_INIT: Once = ONCE_INIT; +pub static BYPASS_SQLITE_INIT: AtomicBool = ATOMIC_BOOL_INIT; + +fn ensure_safe_sqlite_threading_mode() -> Result<()> { + // Ensure SQLite was compiled in thredsafe mode. + if unsafe { ffi::sqlite3_threadsafe() == 0 } { + return Err(Error::SqliteSingleThreadedMode); + } + + // Now we know SQLite is _capable_ of being in Multi-thread of Serialized mode, + // but it's possible someone configured it to be in Single-thread mode + // before calling into us. That would mean we're exposing an unsafe API via + // a safe one (in Rust terminology), which is no good. We have two options + // to protect against this, depending on the version of SQLite we're linked + // with: + // + // 1. If we're on 3.7.0 or later, we can ask SQLite for a mutex and check for + // the magic value 8. This isn't documented, but it's what SQLite + // returns for its mutex allocation function in Single-thread mode. + // 2. If we're prior to SQLite 3.7.0, AFAIK there's no way to check the + // threading mode. The check we perform for >= 3.7.0 will segfault. + // Instead, we insist on being able to call sqlite3_config and + // sqlite3_initialize ourself, ensuring we know the threading + // mode. This will fail if someone else has already initialized SQLite + // even if they initialized it safely. That's not ideal either, which is + // why we expose bypass_sqlite_initialization above. + if version_number() >= 3_007_000 { + const SQLITE_SINGLETHREADED_MUTEX_MAGIC: usize = 8; + let is_singlethreaded = unsafe { + let mutex_ptr = ffi::sqlite3_mutex_alloc(0); + let is_singlethreaded = mutex_ptr as usize == SQLITE_SINGLETHREADED_MUTEX_MAGIC; + ffi::sqlite3_mutex_free(mutex_ptr); + is_singlethreaded + }; + if is_singlethreaded { + Err(Error::SqliteSingleThreadedMode) + } else { + Ok(()) + } + } else { + SQLITE_INIT.call_once(|| { + if BYPASS_SQLITE_INIT.load(Ordering::Relaxed) { + return; + } + + unsafe { + let msg = "\ +Could not ensure safe initialization of SQLite. +To fix this, either: +* Upgrade SQLite to at least version 3.7.0 +* Ensure that SQLite has been initialized in Multi-thread or Serialized mode and call + rusqlite::bypass_sqlite_initialization() prior to your first connection attempt."; + + if ffi::sqlite3_config(ffi::SQLITE_CONFIG_MULTITHREAD) != ffi::SQLITE_OK { + panic!(msg); + } + if ffi::sqlite3_initialize() != ffi::SQLITE_OK { + panic!(msg); + } + } + }); + Ok(()) + } +} diff --git a/src/lib.rs b/src/lib.rs index 3ce06ea..392009b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,11 +2,8 @@ //! expose an interface similar to [rust-postgres](https://github.com/sfackler/rust-postgres). //! //! ```rust -//! extern crate rusqlite; -//! extern crate time; -//! //! use rusqlite::types::ToSql; -//! use rusqlite::{Connection, NO_PARAMS}; +//! use rusqlite::{params, Connection}; //! use time::Timespec; //! //! #[derive(Debug)] @@ -27,7 +24,7 @@ //! time_created TEXT NOT NULL, //! data BLOB //! )", -//! NO_PARAMS, +//! params![], //! ) //! .unwrap(); //! let me = Person { @@ -39,7 +36,7 @@ //! conn.execute( //! "INSERT INTO person (name, time_created, data) //! VALUES (?1, ?2, ?3)", -//! &[&me.name as &ToSql, &me.time_created, &me.data], +//! params![me.name, me.time_created, me.data], //! ) //! .unwrap(); //! @@ -47,7 +44,7 @@ //! .prepare("SELECT id, name, time_created, data FROM person") //! .unwrap(); //! let person_iter = stmt -//! .query_map(NO_PARAMS, |row| Person { +//! .query_map(params![], |row| Person { //! id: row.get(0), //! name: row.get(1), //! time_created: row.get(2), @@ -62,58 +59,44 @@ //! ``` #![allow(unknown_lints)] -extern crate libsqlite3_sys as ffi; -extern crate lru_cache; +pub use libsqlite3_sys as ffi; + #[macro_use] extern crate bitflags; #[cfg(any(test, feature = "vtab"))] #[macro_use] extern crate lazy_static; -#[cfg(feature = "i128_blob")] -extern crate byteorder; - use std::cell::RefCell; use std::convert; use std::default::Default; use std::ffi::{CStr, CString}; use std::fmt; -use std::mem; use std::os::raw::{c_char, c_int}; use std::path::{Path, PathBuf}; -use std::ptr; use std::result; use std::str; -use std::sync::atomic::{AtomicBool, Ordering, ATOMIC_BOOL_INIT}; -use std::sync::{Arc, Mutex, Once, ONCE_INIT}; +use std::sync::atomic::Ordering; +use std::sync::{Arc, Mutex}; -use cache::StatementCache; -use error::{error_from_handle, error_from_sqlite_code}; -use raw_statement::RawStatement; -use types::{ToSql, ValueRef}; - -pub use statement::Statement; - -pub use row::{AndThenRows, MappedRows, Row, RowIndex, Rows}; - -pub use transaction::{DropBehavior, Savepoint, Transaction, TransactionBehavior}; -#[allow(deprecated)] -pub use transaction::{SqliteTransaction, SqliteTransactionBehavior}; - -pub use error::Error; -#[allow(deprecated)] -pub use error::SqliteError; -pub use ffi::ErrorCode; - -pub use cache::CachedStatement; -pub use version::*; +use crate::cache::StatementCache; +use crate::inner_connection::{InnerConnection, BYPASS_SQLITE_INIT}; +use crate::raw_statement::RawStatement; +use crate::types::ValueRef; +pub use crate::cache::CachedStatement; +pub use crate::error::Error; +pub use crate::ffi::ErrorCode; #[cfg(feature = "hooks")] -pub use hooks::*; +pub use crate::hooks::*; #[cfg(feature = "load_extension")] -#[allow(deprecated)] -pub use load_extension_guard::{LoadExtensionGuard, SqliteLoadExtensionGuard}; +pub use crate::load_extension_guard::LoadExtensionGuard; +pub use crate::row::{AndThenRows, MappedRows, Row, RowIndex, Rows}; +pub use crate::statement::{Statement, StatementStatus}; +pub use crate::transaction::{DropBehavior, Savepoint, Transaction, TransactionBehavior}; +pub use crate::types::ToSql; +pub use crate::version::*; #[cfg(feature = "backup")] pub mod backup; @@ -123,17 +106,21 @@ mod busy; mod cache; #[cfg(any(feature = "functions", feature = "vtab"))] mod context; +#[macro_use] mod error; #[cfg(feature = "functions")] pub mod functions; #[cfg(feature = "hooks")] mod hooks; +mod inner_connection; #[cfg(feature = "limits")] pub mod limits; #[cfg(feature = "load_extension")] mod load_extension_guard; mod raw_statement; mod row; +#[cfg(feature = "session")] +pub mod session; mod statement; #[cfg(feature = "trace")] pub mod trace; @@ -147,26 +134,112 @@ pub mod vtab; // Number of cached prepared statements we'll hold on to. const STATEMENT_CACHE_DEFAULT_CAPACITY: usize = 16; /// To be used when your statement has no [parameter](https://sqlite.org/lang_expr.html#varparam). -pub const NO_PARAMS: &'static [&'static ToSql] = &[]; +pub const NO_PARAMS: &[&dyn ToSql] = &[]; -/// Old name for `Result`. `SqliteResult` is deprecated. -#[deprecated(since = "0.6.0", note = "Use Result instead")] -pub type SqliteResult = Result; +/// A macro making it more convenient to pass heterogeneous lists +/// of parameters as a `&[&dyn ToSql]`. +/// +/// # Example +/// +/// ```rust,no_run +/// # use rusqlite::{Result, Connection, params}; +/// +/// struct Person { +/// name: String, +/// age_in_years: u8, +/// data: Option>, +/// } +/// +/// fn add_person(conn: &Connection, person: &Person) -> Result<()> { +/// conn.execute("INSERT INTO person (name, age_in_years, data) +/// VALUES (?1, ?2, ?3)", +/// params![person.name, person.age_in_years, person.data])?; +/// Ok(()) +/// } +/// ``` +#[macro_export] +macro_rules! params { + () => { + $crate::NO_PARAMS + }; + ($($param:expr),+ $(,)?) => { + &[$(&$param as &dyn $crate::ToSql),+] + }; +} + +/// A macro making it more convenient to pass lists of named parameters +/// as a `&[(&str, &dyn ToSql)]`. +/// +/// # Example +/// +/// ```rust,no_run +/// # use rusqlite::{Result, Connection, named_params}; +/// +/// struct Person { +/// name: String, +/// age_in_years: u8, +/// data: Option>, +/// } +/// +/// fn add_person(conn: &Connection, person: &Person) -> Result<()> { +/// conn.execute_named( +/// "INSERT INTO person (name, age_in_years, data) +/// VALUES (:name, :age, :data)", +/// named_params!{ +/// ":name": person.name, +/// ":age": person.age_in_years, +/// ":data": person.data, +/// } +/// )?; +/// Ok(()) +/// } +/// ``` +#[macro_export] +macro_rules! named_params { + () => { + &[] + }; + // Note: It's a lot more work to support this as part of the same macro as + // `params!`, unfortunately. + ($($param_name:literal: $param_val:expr),+ $(,)?) => { + &[$(($param_name, &$param_val as &dyn $crate::ToSql)),+] + }; +} /// A typedef of the result returned by many methods. pub type Result = result::Result; +/// See the [method documentation](#tymethod.optional). +pub trait OptionalExtension { + /// Converts a `Result` into a `Result>`. + /// + /// By default, Rusqlite treats 0 rows being returned from a query that is + /// expected to return 1 row as an error. This method will + /// handle that error, and give you back an `Option` instead. + fn optional(self) -> Result>; +} + +impl OptionalExtension for Result { + fn optional(self) -> Result> { + match self { + Ok(value) => Ok(Some(value)), + Err(Error::QueryReturnedNoRows) => Ok(None), + Err(e) => Err(e), + } + } +} + unsafe fn errmsg_to_string(errmsg: *const c_char) -> String { let c_slice = CStr::from_ptr(errmsg).to_bytes(); String::from_utf8_lossy(c_slice).into_owned() } fn str_to_cstring(s: &str) -> Result { - Ok(try!(CString::new(s))) + Ok(CString::new(s)?) } fn path_to_cstring(p: &Path) -> Result { - let s = try!(p.to_str().ok_or_else(|| Error::InvalidPath(p.to_owned()))); + let s = p.to_str().ok_or_else(|| Error::InvalidPath(p.to_owned()))?; str_to_cstring(s) } @@ -185,7 +258,12 @@ pub enum DatabaseName<'a> { // Currently DatabaseName is only used by the backup and blob mods, so hide // this (private) impl to avoid dead code warnings. -#[cfg(any(feature = "backup", feature = "blob"))] +#[cfg(any( + feature = "backup", + feature = "blob", + feature = "session", + feature = "bundled" +))] impl<'a> DatabaseName<'a> { fn to_cstring(&self) -> Result { use self::DatabaseName::{Attached, Main, Temp}; @@ -197,10 +275,6 @@ impl<'a> DatabaseName<'a> { } } -/// Old name for `Connection`. `SqliteConnection` is deprecated. -#[deprecated(since = "0.6.0", note = "Use Connection instead")] -pub type SqliteConnection = Connection; - /// A connection to a SQLite database. pub struct Connection { db: RefCell, @@ -253,7 +327,7 @@ impl Connection { /// Will return `Err` if `path` cannot be converted to a C-compatible /// string or if the underlying SQLite open call fails. pub fn open_with_flags>(path: P, flags: OpenFlags) -> Result { - let c_path = try!(path_to_cstring(path.as_ref())); + let c_path = path_to_cstring(path.as_ref())?; InnerConnection::open_with_flags(&c_path, flags).map(|db| Connection { db: RefCell::new(db), cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY), @@ -270,7 +344,7 @@ impl Connection { /// /// Will return `Err` if the underlying SQLite open call fails. pub fn open_in_memory_with_flags(flags: OpenFlags) -> Result { - let c_memory = try!(str_to_cstring(":memory:")); + let c_memory = str_to_cstring(":memory:")?; InnerConnection::open_with_flags(&c_memory, flags).map(|db| Connection { db: RefCell::new(db), cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY), @@ -357,7 +431,7 @@ impl Connection { /// /// Will return `Err` if `sql` cannot be converted to a C-compatible string /// or if the underlying SQLite call fails. - pub fn execute_named(&self, sql: &str, params: &[(&str, &ToSql)]) -> Result { + pub fn execute_named(&self, sql: &str, params: &[(&str, &dyn ToSql)]) -> Result { self.prepare(sql).and_then(|mut stmt| { stmt.check_no_tail() .and_then(|_| stmt.execute_named(params)) @@ -391,6 +465,10 @@ impl Connection { /// If the query returns more than one row, all rows except the first are /// ignored. /// + /// Returns `Err(QueryReturnedNoRows)` if no results are returned. If the + /// query truly is optional, you can call `.optional()` on the result of + /// this to get a `Result>`. + /// /// # Failure /// /// Will return `Err` if `sql` cannot be converted to a C-compatible string @@ -399,10 +477,10 @@ impl Connection { where P: IntoIterator, P::Item: ToSql, - F: FnOnce(&Row) -> T, + F: FnOnce(&Row<'_, '_>) -> T, { - let mut stmt = try!(self.prepare(sql)); - try!(stmt.check_no_tail()); + let mut stmt = self.prepare(sql)?; + stmt.check_no_tail()?; stmt.query_row(params, f) } @@ -412,17 +490,21 @@ impl Connection { /// If the query returns more than one row, all rows except the first are /// ignored. /// + /// Returns `Err(QueryReturnedNoRows)` if no results are returned. If the + /// query truly is optional, you can call `.optional()` on the result of + /// this to get a `Result>`. + /// /// # Failure /// /// Will return `Err` if `sql` cannot be converted to a C-compatible string /// or if the underlying SQLite call fails. - pub fn query_row_named(&self, sql: &str, params: &[(&str, &ToSql)], f: F) -> Result + pub fn query_row_named(&self, sql: &str, params: &[(&str, &dyn ToSql)], f: F) -> Result where - F: FnOnce(&Row) -> T, + F: FnOnce(&Row<'_, '_>) -> T, { - let mut stmt = try!(self.prepare(sql)); - try!(stmt.check_no_tail()); - let mut rows = try!(stmt.query_named(params)); + let mut stmt = self.prepare(sql)?; + stmt.check_no_tail()?; + let mut rows = stmt.query_named(params)?; rows.get_expected_row().map(|r| f(&r)) } @@ -456,12 +538,12 @@ impl Connection { where P: IntoIterator, P::Item: ToSql, - F: FnOnce(&Row) -> result::Result, + F: FnOnce(&Row<'_, '_>) -> result::Result, E: convert::From, { - let mut stmt = try!(self.prepare(sql)); - try!(stmt.check_no_tail()); - let mut rows = try!(stmt.query(params)); + let mut stmt = self.prepare(sql)?; + stmt.check_no_tail()?; + let mut rows = stmt.query(params)?; rows.get_expected_row().map_err(E::from).and_then(|r| f(&r)) } @@ -473,9 +555,9 @@ impl Connection { /// ```rust,no_run /// # use rusqlite::{Connection, Result}; /// fn insert_new_people(conn: &Connection) -> Result<()> { - /// let mut stmt = try!(conn.prepare("INSERT INTO People (name) VALUES (?)")); - /// try!(stmt.execute(&["Joe Smith"])); - /// try!(stmt.execute(&["Bob Jones"])); + /// let mut stmt = conn.prepare("INSERT INTO People (name) VALUES (?)")?; + /// stmt.execute(&["Joe Smith"])?; + /// stmt.execute(&["Bob Jones"])?; /// Ok(()) /// } /// ``` @@ -484,7 +566,7 @@ impl Connection { /// /// Will return `Err` if `sql` cannot be converted to a C-compatible string /// or if the underlying SQLite call fails. - pub fn prepare<'a>(&'a self, sql: &str) -> Result> { + pub fn prepare(&self, sql: &str) -> Result> { self.db.borrow_mut().prepare(self, sql) } @@ -512,8 +594,8 @@ impl Connection { /// # use rusqlite::{Connection, Result}; /// # use std::path::{Path}; /// fn load_my_extension(conn: &Connection) -> Result<()> { - /// try!(conn.load_extension_enable()); - /// try!(conn.load_extension(Path::new("my_sqlite_extension"), None)); + /// conn.load_extension_enable()?; + /// conn.load_extension(Path::new("my_sqlite_extension"), None)?; /// conn.load_extension_disable() /// } /// ``` @@ -552,7 +634,7 @@ impl Connection { /// # use rusqlite::{Connection, Result, LoadExtensionGuard}; /// # use std::path::{Path}; /// fn load_my_extension(conn: &Connection) -> Result<()> { - /// let _guard = try!(LoadExtensionGuard::new(conn)); + /// let _guard = LoadExtensionGuard::new(conn)?; /// /// conn.load_extension("my_sqlite_extension", None) /// } @@ -585,6 +667,20 @@ impl Connection { self.db.borrow().db() } + /// Create a `Connection` from a raw handle. + /// + /// The underlying SQLite database connection handle will not be closed when + /// the returned connection is dropped/closed. + pub unsafe fn from_handle(db: *mut ffi::sqlite3) -> Result { + let db_path = db_filename(db); + let db = InnerConnection::new(db, false); + Ok(Connection { + db: RefCell::new(db), + cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY), + path: db_path, + }) + } + /// Get access to a handle that can be used to interrupt long running /// queries from another thread. pub fn get_interrupt_handle(&self) -> InterruptHandle { @@ -595,7 +691,10 @@ impl Connection { self.db.borrow_mut().decode_result(code) } - fn changes(&self) -> usize { + /// Return the number of rows modified, inserted or deleted by the most + /// recently completed INSERT, UPDATE or DELETE statement on the database + /// connection. + pub fn changes(&self) -> usize { self.db.borrow_mut().changes() } @@ -613,34 +712,13 @@ impl 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") .field("path", &self.path) .finish() } } -struct InnerConnection { - db: *mut ffi::sqlite3, - // It's unsafe to call `sqlite3_close` while another thread is performing - // a `sqlite3_interrupt`, and vice versa, so we take this mutex during - // those functions. This protects a copy of the `db` pointer (which is - // cleared on closing), however the main copy, `db`, is unprotected. - // Otherwise, a long running query would prevent calling interrupt, as - // interrupt would only acquire the lock after the query's completion. - interrupt_lock: Arc>, - #[cfg(feature = "hooks")] - free_commit_hook: Option, - #[cfg(feature = "hooks")] - free_rollback_hook: Option, - #[cfg(feature = "hooks")] - free_update_hook: Option, -} - -/// Old name for `OpenFlags`. `SqliteOpenFlags` is deprecated. -#[deprecated(since = "0.6.0", note = "Use OpenFlags instead")] -pub type SqliteOpenFlags = OpenFlags; - bitflags! { #[doc = "Flags for opening SQLite database connections."] #[doc = "See [sqlite3_open_v2](http://www.sqlite.org/c3ref/open.html) for details."] @@ -667,13 +745,6 @@ impl Default for OpenFlags { } } -static SQLITE_INIT: Once = ONCE_INIT; -#[cfg(not(feature = "bundled"))] -static SQLITE_VERSION_CHECK: Once = ONCE_INIT; -static BYPASS_SQLITE_INIT: AtomicBool = ATOMIC_BOOL_INIT; -#[cfg(not(feature = "bundled"))] -static BYPASS_VERSION_CHECK: AtomicBool = ATOMIC_BOOL_INIT; - /// rusqlite's check for a safe SQLite threading mode requires SQLite 3.7.0 or /// later. If you are running against a SQLite older than that, rusqlite /// attempts to ensure safety by performing configuration and initialization of @@ -704,347 +775,7 @@ pub unsafe fn bypass_sqlite_initialization() { /// your first connection attempt. pub unsafe fn bypass_sqlite_version_check() { #[cfg(not(feature = "bundled"))] - BYPASS_VERSION_CHECK.store(true, Ordering::Relaxed); -} - -#[cfg(not(feature = "bundled"))] -fn ensure_valid_sqlite_version() { - SQLITE_VERSION_CHECK.call_once(|| { - let version_number = version_number(); - - // Check our hard floor. - if version_number < 3_006_008 { - panic!("rusqlite requires SQLite 3.6.8 or newer"); - } - - // Check that the major version number for runtime and buildtime match. - let buildtime_major = ffi::SQLITE_VERSION_NUMBER / 1_000_000; - let runtime_major = version_number / 1_000_000; - if buildtime_major != runtime_major { - panic!( - "rusqlite was built against SQLite {} but is running with SQLite {}", - str::from_utf8(ffi::SQLITE_VERSION).unwrap(), - version() - ); - } - - if BYPASS_VERSION_CHECK.load(Ordering::Relaxed) { - return; - } - - // Check that the runtime version number is compatible with the version number - // we found at build-time. - if version_number < ffi::SQLITE_VERSION_NUMBER { - panic!( - "\ -rusqlite was built against SQLite {} but the runtime SQLite version is {}. To fix this, either: -* Recompile rusqlite and link against the SQLite version you are using at runtime, or -* Call rusqlite::bypass_sqlite_version_check() prior to your first connection attempt. Doing this - means you're sure everything will work correctly even though the runtime version is older than - the version we found at build time.", - str::from_utf8(ffi::SQLITE_VERSION).unwrap(), - version() - ); - } - }); -} - -fn ensure_safe_sqlite_threading_mode() -> Result<()> { - // Ensure SQLite was compiled in thredsafe mode. - if unsafe { ffi::sqlite3_threadsafe() == 0 } { - return Err(Error::SqliteSingleThreadedMode); - } - - // Now we know SQLite is _capable_ of being in Multi-thread of Serialized mode, - // but it's possible someone configured it to be in Single-thread mode - // before calling into us. That would mean we're exposing an unsafe API via - // a safe one (in Rust terminology), which is no good. We have two options - // to protect against this, depending on the version of SQLite we're linked - // with: - // - // 1. If we're on 3.7.0 or later, we can ask SQLite for a mutex and check for - // the magic value 8. This isn't documented, but it's what SQLite - // returns for its mutex allocation function in Single-thread mode. - // 2. If we're prior to SQLite 3.7.0, AFAIK there's no way to check the - // threading mode. The check we perform for >= 3.7.0 will segfault. - // Instead, we insist on being able to call sqlite3_config and - // sqlite3_initialize ourself, ensuring we know the threading - // mode. This will fail if someone else has already initialized SQLite - // even if they initialized it safely. That's not ideal either, which is - // why we expose bypass_sqlite_initialization above. - if version_number() >= 3_007_000 { - const SQLITE_SINGLETHREADED_MUTEX_MAGIC: usize = 8; - let is_singlethreaded = unsafe { - let mutex_ptr = ffi::sqlite3_mutex_alloc(0); - let is_singlethreaded = mutex_ptr as usize == SQLITE_SINGLETHREADED_MUTEX_MAGIC; - ffi::sqlite3_mutex_free(mutex_ptr); - is_singlethreaded - }; - if is_singlethreaded { - Err(Error::SqliteSingleThreadedMode) - } else { - Ok(()) - } - } else { - SQLITE_INIT.call_once(|| { - if BYPASS_SQLITE_INIT.load(Ordering::Relaxed) { - return; - } - - unsafe { - let msg = "\ -Could not ensure safe initialization of SQLite. -To fix this, either: -* Upgrade SQLite to at least version 3.7.0 -* Ensure that SQLite has been initialized in Multi-thread or Serialized mode and call - rusqlite::bypass_sqlite_initialization() prior to your first connection attempt."; - - if ffi::sqlite3_config(ffi::SQLITE_CONFIG_MULTITHREAD) != ffi::SQLITE_OK { - panic!(msg); - } - if ffi::sqlite3_initialize() != ffi::SQLITE_OK { - panic!(msg); - } - } - }); - Ok(()) - } -} - -impl InnerConnection { - #[cfg(not(feature = "hooks"))] - fn new(db: *mut ffi::sqlite3) -> InnerConnection { - InnerConnection { - db, - interrupt_lock: Arc::new(Mutex::new(db)), - } - } - - #[cfg(feature = "hooks")] - fn new(db: *mut ffi::sqlite3) -> InnerConnection { - InnerConnection { - db: db, - interrupt_lock: Arc::new(Mutex::new(db)), - free_commit_hook: None, - free_rollback_hook: None, - free_update_hook: None, - } - } - - fn open_with_flags(c_path: &CString, flags: OpenFlags) -> Result { - #[cfg(not(feature = "bundled"))] - ensure_valid_sqlite_version(); - ensure_safe_sqlite_threading_mode()?; - - // Replicate the check for sane open flags from SQLite, because the check in - // SQLite itself wasn't added until version 3.7.3. - debug_assert_eq!(1 << OpenFlags::SQLITE_OPEN_READ_ONLY.bits, 0x02); - debug_assert_eq!(1 << OpenFlags::SQLITE_OPEN_READ_WRITE.bits, 0x04); - debug_assert_eq!( - 1 << (OpenFlags::SQLITE_OPEN_READ_WRITE | OpenFlags::SQLITE_OPEN_CREATE).bits, - 0x40 - ); - if (1 << (flags.bits & 0x7)) & 0x46 == 0 { - return Err(Error::SqliteFailure( - ffi::Error::new(ffi::SQLITE_MISUSE), - None, - )); - } - - unsafe { - let mut db: *mut ffi::sqlite3 = mem::uninitialized(); - let r = ffi::sqlite3_open_v2(c_path.as_ptr(), &mut db, flags.bits(), ptr::null()); - if r != ffi::SQLITE_OK { - let e = if db.is_null() { - error_from_sqlite_code(r, None) - } else { - let e = error_from_handle(db, r); - ffi::sqlite3_close(db); - e - }; - - return Err(e); - } - let r = ffi::sqlite3_busy_timeout(db, 5000); - if r != ffi::SQLITE_OK { - let e = error_from_handle(db, r); - ffi::sqlite3_close(db); - return Err(e); - } - - // attempt to turn on extended results code; don't fail if we can't. - ffi::sqlite3_extended_result_codes(db, 1); - - Ok(InnerConnection::new(db)) - } - } - - fn db(&self) -> *mut ffi::sqlite3 { - self.db - } - - fn decode_result(&mut self, code: c_int) -> Result<()> { - InnerConnection::decode_result_raw(self.db(), code) - } - - fn decode_result_raw(db: *mut ffi::sqlite3, code: c_int) -> Result<()> { - if code == ffi::SQLITE_OK { - Ok(()) - } else { - Err(error_from_handle(db, code)) - } - } - - fn close(&mut self) -> Result<()> { - if self.db.is_null() { - return Ok(()); - } - self.remove_hooks(); - let mut shared_handle = self.interrupt_lock.lock().unwrap(); - assert!( - !shared_handle.is_null(), - "Bug: Somehow interrupt_lock was cleared before the DB was closed" - ); - unsafe { - let r = ffi::sqlite3_close(self.db); - // Need to use _raw because _guard has a reference out, and - // decode_result takes &mut self. - let r = InnerConnection::decode_result_raw(self.db, r); - if r.is_ok() { - *shared_handle = ptr::null_mut(); - self.db = ptr::null_mut(); - } - r - } - } - - fn get_interrupt_handle(&self) -> InterruptHandle { - InterruptHandle { - db_lock: Arc::clone(&self.interrupt_lock), - } - } - - fn execute_batch(&mut self, sql: &str) -> Result<()> { - let c_sql = try!(str_to_cstring(sql)); - unsafe { - let r = ffi::sqlite3_exec( - self.db(), - c_sql.as_ptr(), - None, - ptr::null_mut(), - ptr::null_mut(), - ); - self.decode_result(r) - } - } - - #[cfg(feature = "load_extension")] - fn enable_load_extension(&mut self, onoff: c_int) -> Result<()> { - let r = unsafe { ffi::sqlite3_enable_load_extension(self.db, onoff) }; - self.decode_result(r) - } - - #[cfg(feature = "load_extension")] - fn load_extension(&self, dylib_path: &Path, entry_point: Option<&str>) -> Result<()> { - let dylib_str = try!(path_to_cstring(dylib_path)); - unsafe { - let mut errmsg: *mut c_char = mem::uninitialized(); - let r = if let Some(entry_point) = entry_point { - let c_entry = try!(str_to_cstring(entry_point)); - ffi::sqlite3_load_extension( - self.db, - dylib_str.as_ptr(), - c_entry.as_ptr(), - &mut errmsg, - ) - } else { - ffi::sqlite3_load_extension(self.db, dylib_str.as_ptr(), ptr::null(), &mut errmsg) - }; - if r == ffi::SQLITE_OK { - Ok(()) - } else { - let message = errmsg_to_string(&*errmsg); - ffi::sqlite3_free(errmsg as *mut ::std::os::raw::c_void); - Err(error_from_sqlite_code(r, Some(message))) - } - } - } - - fn last_insert_rowid(&self) -> i64 { - unsafe { ffi::sqlite3_last_insert_rowid(self.db()) } - } - - fn prepare<'a>(&mut self, conn: &'a Connection, sql: &str) -> Result> { - if sql.len() >= ::std::i32::MAX as usize { - return Err(error_from_sqlite_code(ffi::SQLITE_TOOBIG, None)); - } - let mut c_stmt: *mut ffi::sqlite3_stmt = unsafe { mem::uninitialized() }; - let c_sql = try!(str_to_cstring(sql)); - let len_with_nul = (sql.len() + 1) as c_int; - let mut c_tail = ptr::null(); - let r = unsafe { - if cfg!(feature = "unlock_notify") { - let mut rc; - loop { - rc = ffi::sqlite3_prepare_v2( - self.db(), - c_sql.as_ptr(), - len_with_nul, - &mut c_stmt, - &mut c_tail, - ); - if !unlock_notify::is_locked(self.db, rc) { - break; - } - rc = unlock_notify::wait_for_unlock_notify(self.db); - if rc != ffi::SQLITE_OK { - break; - } - } - rc - } else { - ffi::sqlite3_prepare_v2( - self.db(), - c_sql.as_ptr(), - len_with_nul, - &mut c_stmt, - &mut c_tail, - ) - } - }; - if !c_tail.is_null() && unsafe { *c_tail == 0 } { - // '\0' when there is no ';' at the end - c_tail = ptr::null(); // TODO ignore spaces, comments, ... at the end - } - self.decode_result(r) - .map(|_| Statement::new(conn, RawStatement::new(c_stmt, c_tail))) - } - - fn changes(&mut self) -> usize { - unsafe { ffi::sqlite3_changes(self.db()) as usize } - } - - fn is_autocommit(&self) -> bool { - unsafe { ffi::sqlite3_get_autocommit(self.db()) != 0 } - } - - #[cfg(feature = "bundled")] // 3.8.6 - fn is_busy(&self) -> bool { - let db = self.db(); - unsafe { - let mut stmt = ffi::sqlite3_next_stmt(db, ptr::null_mut()); - while !stmt.is_null() { - if ffi::sqlite3_stmt_busy(stmt) != 0 { - return true; - } - stmt = ffi::sqlite3_next_stmt(db, stmt); - } - } - false - } - - #[cfg(not(feature = "hooks"))] - fn remove_hooks(&mut self) {} + inner_connection::BYPASS_VERSION_CHECK.store(true, Ordering::Relaxed); } /// Allows interrupting a long-running computation. @@ -1066,41 +797,29 @@ impl InterruptHandle { } } -impl Drop for InnerConnection { - #[allow(unused_must_use)] - fn drop(&mut self) { - use std::thread::panicking; - - if let Err(e) = self.close() { - if panicking() { - eprintln!("Error while closing SQLite connection: {:?}", e); - } else { - panic!("Error while closing SQLite connection: {:?}", e); - } - } +#[cfg(feature = "bundled")] // 3.7.10 +unsafe fn db_filename(db: *mut ffi::sqlite3) -> Option { + let db_name = DatabaseName::Main.to_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) } } - -/// Old name for `Statement`. `SqliteStatement` is deprecated. -#[deprecated(since = "0.6.0", note = "Use Statement instead")] -pub type SqliteStatement<'conn> = Statement<'conn>; - -/// Old name for `Rows`. `SqliteRows` is deprecated. -#[deprecated(since = "0.6.0", note = "Use Rows instead")] -pub type SqliteRows<'stmt> = Rows<'stmt>; - -/// Old name for `Row`. `SqliteRow` is deprecated. -#[deprecated(since = "0.6.0", note = "Use Row instead")] -pub type SqliteRow<'a, 'stmt> = Row<'a, 'stmt>; +#[cfg(not(feature = "bundled"))] +unsafe fn db_filename(_: *mut ffi::sqlite3) -> Option { + None +} #[cfg(test)] mod test { - extern crate tempdir; use self::tempdir::TempDir; pub use super::*; - use ffi; + use crate::ffi; pub use std::error::Error as StdError; pub use std::fmt; + use tempdir; // this function is never called, but is still type checked; in // particular, calls with specific instantiations will require @@ -1146,11 +865,9 @@ mod test { let tx2 = db2.transaction().unwrap(); // SELECT first makes sqlite lock with a shared lock - let _ = tx1 - .query_row("SELECT x FROM foo LIMIT 1", NO_PARAMS, |_| ()) + tx1.query_row("SELECT x FROM foo LIMIT 1", NO_PARAMS, |_| ()) .unwrap(); - let _ = tx2 - .query_row("SELECT x FROM foo LIMIT 1", NO_PARAMS, |_| ()) + tx2.query_row("SELECT x FROM foo LIMIT 1", NO_PARAMS, |_| ()) .unwrap(); tx1.execute("INSERT INTO foo VALUES(?1)", &[1]).unwrap(); @@ -1294,9 +1011,8 @@ mod test { fn test_execute_select() { let db = checked_memory_handle(); let err = db.execute("SELECT 1 WHERE 1 < ?", &[1i32]).unwrap_err(); - match err { - Error::ExecuteReturnedResults => (), - _ => panic!("Unexpected error: {}", err), + if err != Error::ExecuteReturnedResults { + panic!("Unexpected error: {}", err); } } @@ -1435,6 +1151,30 @@ mod test { assert!(bad_query_result.is_err()); } + #[test] + fn test_optional() { + let db = checked_memory_handle(); + + let result: Result = db.query_row("SELECT 1 WHERE 0 <> 0", NO_PARAMS, |r| r.get(0)); + let result = result.optional(); + match result.unwrap() { + None => (), + _ => panic!("Unexpected result"), + } + + let result: Result = db.query_row("SELECT 1 WHERE 0 == 0", NO_PARAMS, |r| r.get(0)); + let result = result.optional(); + match result.unwrap() { + Some(1) => (), + _ => panic!("Unexpected result"), + } + + let bad_query_result: Result = + db.query_row("NOT A PROPER QUERY", NO_PARAMS, |r| r.get(0)); + let bad_query_result = bad_query_result.optional(); + assert!(bad_query_result.is_err()); + } + #[test] fn test_pragma_query_row() { let db = checked_memory_handle(); @@ -1548,65 +1288,31 @@ mod test { } #[test] + #[cfg(feature = "functions")] fn test_interrupt() { - use std::thread; - use std::time::Duration; - let tries = 15; - // Sadly, this is inherently finnicky. Even if sqlite gets the - // interrupt, it isn't guaranteed to stop. In practice, 15 tends to be - // more than enough so long as we move everything we can outside of - // the thread body, but it still is unfortunate. - for i in 0..tries { - let db = checked_memory_handle(); - db.execute_batch("CREATE TABLE dummy(id)").unwrap(); - let interrupt_handle = db.get_interrupt_handle(); - // generate an arbitrary query which will be very slow to execute. - let sql = format!( - "{};", - (0..100_000) - .into_iter() - .map(|i| format!("INSERT INTO dummy(id) VALUES({})", i)) - .collect::>() - .join(";\n") - ); + let db = checked_memory_handle(); - // Do this on the main thread to minimize the amount of time spent - // when interrupt won't do anything (because we haven't started - // executing the query). - let c_sql = str_to_cstring(&sql).unwrap(); + let interrupt_handle = db.get_interrupt_handle(); - let joiner = thread::spawn(move || unsafe { - let raw_db = db.db.borrow().db; - let r = ffi::sqlite3_exec( - raw_db, - c_sql.as_ptr(), - None, - ptr::null_mut(), - ptr::null_mut(), - ); - db.decode_result(r) - }); - - // Try a few times to make sure we don't catch it too early. + db.create_scalar_function("interrupt", 0, false, move |_| { interrupt_handle.interrupt(); - for &delay in &[10, 100, 1000] { - thread::sleep(Duration::from_millis(delay)); - interrupt_handle.interrupt(); - } - let result = joiner.join().unwrap(); + Ok(0) + }) + .unwrap(); - if i != tries - 1 && !result.is_err() { - continue; - } + let mut stmt = db + .prepare("SELECT interrupt() FROM (SELECT 1 UNION SELECT 2 UNION SELECT 3)") + .unwrap(); - match result.unwrap_err() { - Error::SqliteFailure(err, _) => { - assert_eq!(err.code, ErrorCode::OperationInterrupted); - return; - } - err => { - panic!("Unexpected error {}", err); - } + let result: Result> = stmt.query_map(NO_PARAMS, |r| r.get(0)).unwrap().collect(); + + match result.unwrap_err() { + Error::SqliteFailure(err, _) => { + assert_eq!(err.code, ErrorCode::OperationInterrupted); + return; + } + err => { + panic!("Unexpected error {}", err); } } } @@ -1635,12 +1341,7 @@ mod test { let mut insert_stmt = db.prepare("INSERT INTO foo(i, x) VALUES(?, ?)").unwrap(); for (i, v) in vals.iter().enumerate() { let i_to_insert = i as i64; - assert_eq!( - insert_stmt - .execute(&[&i_to_insert as &dyn ToSql, &v]) - .unwrap(), - 1 - ); + assert_eq!(insert_stmt.execute(params![i_to_insert, v]).unwrap(), 1); } let mut query = db.prepare("SELECT i, x FROM foo").unwrap(); @@ -1655,8 +1356,19 @@ mod test { } } + #[test] + fn test_from_handle() { + let db = checked_memory_handle(); + let handle = unsafe { db.handle() }; + { + let db = unsafe { Connection::from_handle(handle) }.unwrap(); + db.execute_batch("PRAGMA VACUUM").unwrap(); + } + db.close().unwrap(); + } + mod query_and_then_tests { - extern crate libsqlite3_sys as ffi; + use super::*; #[derive(Debug)] @@ -1666,7 +1378,7 @@ mod test { } impl fmt::Display for CustomError { - fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> ::std::result::Result<(), fmt::Error> { match *self { CustomError::SomeError => write!(f, "{}", self.description()), CustomError::Sqlite(ref se) => write!(f, "{}: {}", self.description(), se), @@ -1679,7 +1391,7 @@ mod test { "my custom error" } - fn cause(&self) -> Option<&StdError> { + fn cause(&self) -> Option<&dyn StdError> { match *self { CustomError::SomeError => None, CustomError::Sqlite(ref se) => Some(se), @@ -1884,7 +1596,7 @@ mod test { END;"; db.execute_batch(sql).unwrap(); - db.query_row("SELECT * FROM foo", NO_PARAMS, |r| { + db.query_row("SELECT * FROM foo", params![], |r| { assert_eq!(2, r.column_count()) }) .unwrap(); diff --git a/src/limits.rs b/src/limits.rs index d8fc2b0..cbb5e61 100644 --- a/src/limits.rs +++ b/src/limits.rs @@ -2,10 +2,10 @@ use std::os::raw::c_int; -use ffi; -pub use ffi::Limit; +use crate::ffi; +pub use crate::ffi::Limit; -use Connection; +use crate::Connection; impl Connection { /// Returns the current value of a limit. @@ -23,8 +23,8 @@ impl Connection { #[cfg(test)] mod test { - use ffi::Limit; - use Connection; + use crate::ffi::Limit; + use crate::Connection; #[test] fn test_limit() { @@ -57,13 +57,13 @@ mod test { assert_eq!(99, db.limit(Limit::SQLITE_LIMIT_VARIABLE_NUMBER)); // SQLITE_LIMIT_TRIGGER_DEPTH was added in SQLite 3.6.18. - if ::version_number() >= 3006018 { + if crate::version_number() >= 3_006_018 { db.set_limit(Limit::SQLITE_LIMIT_TRIGGER_DEPTH, 32); assert_eq!(32, db.limit(Limit::SQLITE_LIMIT_TRIGGER_DEPTH)); } // SQLITE_LIMIT_WORKER_THREADS was added in SQLite 3.8.7. - if ::version_number() >= 3008007 { + if crate::version_number() >= 3_008_007 { db.set_limit(Limit::SQLITE_LIMIT_WORKER_THREADS, 2); assert_eq!(2, db.limit(Limit::SQLITE_LIMIT_WORKER_THREADS)); } diff --git a/src/load_extension_guard.rs b/src/load_extension_guard.rs index 9843fcd..096e6ac 100644 --- a/src/load_extension_guard.rs +++ b/src/load_extension_guard.rs @@ -1,8 +1,4 @@ -use {Connection, Result}; - -/// Old name for `LoadExtensionGuard`. `SqliteLoadExtensionGuard` is deprecated. -#[deprecated(since = "0.6.0", note = "Use LoadExtensionGuard instead")] -pub type SqliteLoadExtensionGuard<'conn> = LoadExtensionGuard<'conn>; +use crate::{Connection, Result}; /// RAII guard temporarily enabling SQLite extensions to be loaded. /// @@ -12,7 +8,7 @@ pub type SqliteLoadExtensionGuard<'conn> = LoadExtensionGuard<'conn>; /// # use rusqlite::{Connection, Result, LoadExtensionGuard}; /// # use std::path::{Path}; /// fn load_my_extension(conn: &Connection) -> Result<()> { -/// let _guard = try!(LoadExtensionGuard::new(conn)); +/// let _guard = LoadExtensionGuard::new(conn)?; /// /// conn.load_extension(Path::new("my_sqlite_extension"), None) /// } @@ -25,7 +21,7 @@ impl<'conn> LoadExtensionGuard<'conn> { /// Attempt to enable loading extensions. Loading extensions will be /// disabled when this guard goes out of scope. Cannot be meaningfully /// nested. - pub fn new(conn: &Connection) -> Result { + pub fn new(conn: &Connection) -> Result> { conn.load_extension_enable() .map(|_| LoadExtensionGuard { conn }) } diff --git a/src/raw_statement.rs b/src/raw_statement.rs index 2fe2d3c..9e1dd29 100644 --- a/src/raw_statement.rs +++ b/src/raw_statement.rs @@ -1,5 +1,6 @@ use super::ffi; use super::unlock_notify; +use super::StatementStatus; use std::ffi::CStr; use std::os::raw::{c_char, c_int}; use std::ptr; @@ -101,6 +102,11 @@ impl RawStatement { } } + pub fn get_status(&self, status: StatementStatus, reset: bool) -> i32 { + assert!(!self.0.is_null()); + unsafe { ffi::sqlite3_stmt_status(self.0, status as i32, reset as i32) } + } + pub fn has_tail(&self) -> bool { !self.1.is_null() } diff --git a/src/row.rs b/src/row.rs index 095eaee..fca2509 100644 --- a/src/row.rs +++ b/src/row.rs @@ -2,7 +2,7 @@ use std::marker::PhantomData; use std::{convert, result}; use super::{Error, Result, Statement}; -use types::{FromSql, FromSqlError, ValueRef}; +use crate::types::{FromSql, FromSqlError, ValueRef}; /// An handle for the resulting rows of a query. pub struct Rows<'stmt> { @@ -27,7 +27,7 @@ impl<'stmt> Rows<'stmt> { /// This is a "streaming iterator". For a more natural interface, /// consider using `query_map` or `query_and_then` instead, which /// return types that implement `Iterator`. - #[cfg_attr(feature = "cargo-clippy", allow(should_implement_trait))] // cannot implement Iterator + #[allow(clippy::should_implement_trait)] // cannot implement Iterator pub fn next<'a>(&'a mut self) -> Option>> { self.stmt.and_then(|stmt| match stmt.step() { Ok(true) => Some(Ok(Row { @@ -73,7 +73,7 @@ pub struct MappedRows<'stmt, F> { impl<'stmt, T, F> MappedRows<'stmt, F> where - F: FnMut(&Row) -> T, + F: FnMut(&Row<'_, '_>) -> T, { pub(crate) fn new(rows: Rows<'stmt>, f: F) -> MappedRows<'stmt, F> { MappedRows { rows, map: f } @@ -82,7 +82,7 @@ where impl<'conn, T, F> Iterator for MappedRows<'conn, F> where - F: FnMut(&Row) -> T, + F: FnMut(&Row<'_, '_>) -> T, { type Item = Result; @@ -103,7 +103,7 @@ pub struct AndThenRows<'stmt, F> { impl<'stmt, T, E, F> AndThenRows<'stmt, F> where - F: FnMut(&Row) -> result::Result, + F: FnMut(&Row<'_, '_>) -> result::Result, { pub(crate) fn new(rows: Rows<'stmt>, f: F) -> AndThenRows<'stmt, F> { AndThenRows { rows, map: f } @@ -113,7 +113,7 @@ where impl<'stmt, T, E, F> Iterator for AndThenRows<'stmt, F> where E: convert::From, - F: FnMut(&Row) -> result::Result, + F: FnMut(&Row<'_, '_>) -> result::Result, { type Item = result::Result; @@ -165,7 +165,7 @@ impl<'a, 'stmt> Row<'a, 'stmt> { /// enabled), and the underlying SQLite column is a blob whose size is not /// 16 bytes, `Error::InvalidColumnType` will also be returned. pub fn get_checked(&self, idx: I) -> Result { - let idx = try!(idx.idx(self.stmt)); + let idx = idx.idx(self.stmt)?; let value = self.stmt.value_ref(idx); FromSql::column_result(value).map_err(|err| match err { FromSqlError::InvalidType => Error::InvalidColumnType(idx, value.data_type()), @@ -231,12 +231,12 @@ impl<'a, 'stmt> Row<'a, 'stmt> { pub trait RowIndex { /// Returns the index of the appropriate column, or `None` if no such /// column exists. - fn idx(&self, stmt: &Statement) -> Result; + fn idx(&self, stmt: &Statement<'_>) -> Result; } impl RowIndex for usize { #[inline] - fn idx(&self, stmt: &Statement) -> Result { + fn idx(&self, stmt: &Statement<'_>) -> Result { if *self >= stmt.column_count() { Err(Error::InvalidColumnIndex(*self)) } else { @@ -247,7 +247,7 @@ impl RowIndex for usize { impl<'a> RowIndex for &'a str { #[inline] - fn idx(&self, stmt: &Statement) -> Result { + fn idx(&self, stmt: &Statement<'_>) -> Result { stmt.column_index(*self) } } diff --git a/src/session.rs b/src/session.rs new file mode 100644 index 0000000..70a9f53 --- /dev/null +++ b/src/session.rs @@ -0,0 +1,894 @@ +//! [Session Extension](https://sqlite.org/sessionintro.html) +#![allow(non_camel_case_types)] + +use std::ffi::CStr; +use std::io::{Read, Write}; +use std::marker::PhantomData; +use std::mem; +use std::os::raw::{c_char, c_int, c_uchar, c_void}; +use std::panic::{catch_unwind, RefUnwindSafe}; +use std::ptr; +use std::slice::{from_raw_parts, from_raw_parts_mut}; + +use fallible_streaming_iterator::FallibleStreamingIterator; + +use crate::error::error_from_sqlite_code; +use crate::ffi; +use crate::hooks::Action; +use crate::types::ValueRef; +use crate::{errmsg_to_string, str_to_cstring, Connection, DatabaseName, Result}; + +// https://sqlite.org/session.html + +/// An instance of this object is a session that can be used to record changes +/// to a database. +pub struct Session<'conn> { + phantom: PhantomData<&'conn ()>, + s: *mut ffi::sqlite3_session, + filter: Option bool>>, +} + +impl<'conn> Session<'conn> { + /// Create a new session object + pub fn new(db: &'conn Connection) -> Result> { + Session::new_with_name(db, DatabaseName::Main) + } + + /// Create a new session object + pub fn new_with_name(db: &'conn Connection, name: DatabaseName<'_>) -> Result> { + let name = name.to_cstring()?; + + let db = db.db.borrow_mut().db; + + let mut s: *mut ffi::sqlite3_session = unsafe { mem::uninitialized() }; + check!(unsafe { ffi::sqlite3session_create(db, name.as_ptr(), &mut s) }); + + Ok(Session { + phantom: PhantomData, + s, + filter: None, + }) + } + + /// Set a table filter + pub fn table_filter(&mut self, filter: Option) + where + F: Fn(&str) -> bool + Send + RefUnwindSafe + 'static, + { + unsafe extern "C" fn call_boxed_closure( + p_arg: *mut c_void, + tbl_str: *const c_char, + ) -> c_int + where + F: Fn(&str) -> bool + RefUnwindSafe, + { + use std::ffi::CStr; + use std::str; + + let boxed_filter: *mut F = p_arg as *mut F; + let tbl_name = { + let c_slice = CStr::from_ptr(tbl_str).to_bytes(); + str::from_utf8_unchecked(c_slice) + }; + if let Ok(true) = catch_unwind(|| (*boxed_filter)(tbl_name)) { + 1 + } else { + 0 + } + } + + match filter { + Some(filter) => { + let boxed_filter = Box::new(filter); + unsafe { + ffi::sqlite3session_table_filter( + self.s, + Some(call_boxed_closure::), + &*boxed_filter as *const F as *mut _, + ); + } + self.filter = Some(boxed_filter); + } + _ => { + unsafe { ffi::sqlite3session_table_filter(self.s, None, ptr::null_mut()) } + self.filter = None; + } + }; + } + + /// Attach a table. `None` means all tables. + pub fn attach(&mut self, table: Option<&str>) -> Result<()> { + let table = if let Some(table) = table { + str_to_cstring(table)?.as_ptr() + } else { + ptr::null() + }; + unsafe { check!(ffi::sqlite3session_attach(self.s, table)) }; + Ok(()) + } + + /// Generate a Changeset + pub fn changeset(&mut self) -> Result { + let mut n = 0; + let mut cs: *mut c_void = unsafe { mem::uninitialized() }; + check!(unsafe { ffi::sqlite3session_changeset(self.s, &mut n, &mut cs) }); + Ok(Changeset { cs, n }) + } + + /// Write the set of changes represented by this session to `output`. + pub fn changeset_strm(&mut self, output: &mut dyn Write) -> Result<()> { + let output_ref = &output; + check!(unsafe { + ffi::sqlite3session_changeset_strm( + self.s, + Some(x_output), + output_ref as *const &mut dyn Write as *mut c_void, + ) + }); + Ok(()) + } + + /// Generate a Patchset + pub fn patchset(&mut self) -> Result { + let mut n = 0; + let mut ps: *mut c_void = unsafe { mem::uninitialized() }; + check!(unsafe { ffi::sqlite3session_patchset(self.s, &mut n, &mut ps) }); + // TODO Validate: same struct + Ok(Changeset { cs: ps, n }) + } + + /// Write the set of patches represented by this session to `output`. + pub fn patchset_strm(&mut self, output: &mut dyn Write) -> Result<()> { + let output_ref = &output; + check!(unsafe { + ffi::sqlite3session_patchset_strm( + self.s, + Some(x_output), + output_ref as *const &mut dyn Write as *mut c_void, + ) + }); + Ok(()) + } + + /// Load the difference between tables. + pub fn diff(&mut self, from: DatabaseName<'_>, table: &str) -> Result<()> { + let from = from.to_cstring()?; + let table = str_to_cstring(table)?.as_ptr(); + unsafe { + let mut errmsg: *mut c_char = mem::uninitialized(); + let r = ffi::sqlite3session_diff(self.s, from.as_ptr(), table, &mut errmsg); + if r != ffi::SQLITE_OK { + let message = errmsg_to_string(&*errmsg); + ffi::sqlite3_free(errmsg as *mut ::std::os::raw::c_void); + return Err(error_from_sqlite_code(r, Some(message))); + } + } + Ok(()) + } + + /// Test if a changeset has recorded any changes + pub fn is_empty(&self) -> bool { + unsafe { ffi::sqlite3session_isempty(self.s) != 0 } + } + + /// Query the current state of the session + pub fn is_enabled(&self) -> bool { + unsafe { ffi::sqlite3session_enable(self.s, -1) != 0 } + } + + /// Enable or disable the recording of changes + pub fn set_enabled(&mut self, enabled: bool) { + unsafe { + ffi::sqlite3session_enable(self.s, if enabled { 1 } else { 0 }); + } + } + + /// Query the current state of the indirect flag + pub fn is_indirect(&self) -> bool { + unsafe { ffi::sqlite3session_indirect(self.s, -1) != 0 } + } + + /// Set or clear the indirect change flag + pub fn set_indirect(&mut self, indirect: bool) { + unsafe { + ffi::sqlite3session_indirect(self.s, if indirect { 1 } else { 0 }); + } + } +} + +impl<'conn> Drop for Session<'conn> { + fn drop(&mut self) { + if self.filter.is_some() { + self.table_filter(None:: bool>); + } + unsafe { ffi::sqlite3session_delete(self.s) }; + } +} + +/// Invert a changeset +pub fn invert_strm(input: &mut dyn Read, output: &mut dyn Write) -> Result<()> { + let input_ref = &input; + let output_ref = &output; + check!(unsafe { + ffi::sqlite3changeset_invert_strm( + Some(x_input), + input_ref as *const &mut dyn Read as *mut c_void, + Some(x_output), + output_ref as *const &mut dyn Write as *mut c_void, + ) + }); + Ok(()) +} + +/// Combine two changesets +pub fn concat_strm( + input_a: &mut dyn Read, + input_b: &mut dyn Read, + output: &mut dyn Write, +) -> Result<()> { + let input_a_ref = &input_a; + let input_b_ref = &input_b; + let output_ref = &output; + check!(unsafe { + ffi::sqlite3changeset_concat_strm( + Some(x_input), + input_a_ref as *const &mut dyn Read as *mut c_void, + Some(x_input), + input_b_ref as *const &mut dyn Read as *mut c_void, + Some(x_output), + output_ref as *const &mut dyn Write as *mut c_void, + ) + }); + Ok(()) +} + +/// Changeset or Patchset +pub struct Changeset { + cs: *mut c_void, + n: c_int, +} + +impl Changeset { + /// Invert a changeset + pub fn invert(&self) -> Result { + let mut n = 0; + let mut cs: *mut c_void = unsafe { mem::uninitialized() }; + check!(unsafe { ffi::sqlite3changeset_invert(self.n, self.cs, &mut n, &mut cs) }); + Ok(Changeset { cs, n }) + } + + /// Create an iterator to traverse a changeset + pub fn iter(&self) -> Result> { + let mut it: *mut ffi::sqlite3_changeset_iter = unsafe { mem::uninitialized() }; + check!(unsafe { ffi::sqlite3changeset_start(&mut it, self.n, self.cs) }); + Ok(ChangesetIter { + phantom: PhantomData, + it, + item: None, + }) + } + + /// Concatenate two changeset objects + pub fn concat(a: &Changeset, b: &Changeset) -> Result { + let mut n = 0; + let mut cs: *mut c_void = unsafe { mem::uninitialized() }; + check!(unsafe { ffi::sqlite3changeset_concat(a.n, a.cs, b.n, b.cs, &mut n, &mut cs) }); + Ok(Changeset { cs, n }) + } +} + +impl Drop for Changeset { + fn drop(&mut self) { + unsafe { + ffi::sqlite3_free(self.cs); + } + } +} + +/// Cursor for iterating over the elements of a changeset or patchset. +pub struct ChangesetIter<'changeset> { + phantom: PhantomData<&'changeset ()>, + it: *mut ffi::sqlite3_changeset_iter, + item: Option, +} + +impl<'changeset> ChangesetIter<'changeset> { + /// Create an iterator on `input` + pub fn start_strm<'input>(input: &'input mut dyn Read) -> Result> { + let input_ref = &input; + let mut it: *mut ffi::sqlite3_changeset_iter = unsafe { mem::uninitialized() }; + check!(unsafe { + ffi::sqlite3changeset_start_strm( + &mut it, + Some(x_input), + input_ref as *const &mut dyn Read as *mut c_void, + ) + }); + Ok(ChangesetIter { + phantom: PhantomData, + it, + item: None, + }) + } +} + +impl<'changeset> FallibleStreamingIterator for ChangesetIter<'changeset> { + type Error = crate::error::Error; + type Item = ChangesetItem; + + fn advance(&mut self) -> Result<()> { + let rc = unsafe { ffi::sqlite3changeset_next(self.it) }; + match rc { + ffi::SQLITE_ROW => { + self.item = Some(ChangesetItem { it: self.it }); + Ok(()) + } + ffi::SQLITE_DONE => { + self.item = None; + Ok(()) + } + code => Err(error_from_sqlite_code(code, None)), + } + } + + fn get(&self) -> Option<&ChangesetItem> { + self.item.as_ref() + } +} + +pub struct Operation<'item> { + table_name: &'item str, + number_of_columns: i32, + code: Action, + indirect: bool, +} + +impl<'item> Operation<'item> { + pub fn table_name(&self) -> &str { + self.table_name + } + + pub fn number_of_columns(&self) -> i32 { + self.number_of_columns + } + + pub fn code(&self) -> Action { + self.code + } + + pub fn indirect(&self) -> bool { + self.indirect + } +} + +impl<'changeset> Drop for ChangesetIter<'changeset> { + fn drop(&mut self) { + unsafe { + ffi::sqlite3changeset_finalize(self.it); + } + } +} + +/// An item passed to a conflict-handler by `Connection::apply`, +/// or an item generated by `ChangesetIter::next`. +// TODO enum ? Delete, Insert, Update, ... +pub struct ChangesetItem { + it: *mut ffi::sqlite3_changeset_iter, +} + +impl ChangesetItem { + /// Obtain conflicting row values + /// + /// May only be called with an `SQLITE_CHANGESET_DATA` or + /// `SQLITE_CHANGESET_CONFLICT` conflict handler callback. + pub fn conflict(&self, col: usize) -> Result> { + unsafe { + let mut p_value: *mut ffi::sqlite3_value = mem::uninitialized(); + check!(ffi::sqlite3changeset_conflict( + self.it, + col as i32, + &mut p_value + )); + Ok(ValueRef::from_value(p_value)) + } + } + + /// Determine the number of foreign key constraint violations + /// + /// May only be called with an `SQLITE_CHANGESET_FOREIGN_KEY` conflict + /// handler callback. + pub fn fk_conflicts(&self) -> Result { + unsafe { + let mut p_out = 0; + check!(ffi::sqlite3changeset_fk_conflicts(self.it, &mut p_out)); + Ok(p_out) + } + } + + /// Obtain new.* Values + /// + /// May only be called if the type of change is either `SQLITE_UPDATE` or + /// `SQLITE_INSERT`. + pub fn new_value(&self, col: usize) -> Result> { + unsafe { + let mut p_value: *mut ffi::sqlite3_value = mem::uninitialized(); + check!(ffi::sqlite3changeset_new(self.it, col as i32, &mut p_value)); + Ok(ValueRef::from_value(p_value)) + } + } + + /// Obtain old.* Values + /// + /// May only be called if the type of change is either `SQLITE_DELETE` or + /// `SQLITE_UPDATE`. + pub fn old_value(&self, col: usize) -> Result> { + unsafe { + let mut p_value: *mut ffi::sqlite3_value = mem::uninitialized(); + check!(ffi::sqlite3changeset_old(self.it, col as i32, &mut p_value)); + Ok(ValueRef::from_value(p_value)) + } + } + + /// Obtain the current operation + pub fn op(&self) -> Result> { + let mut number_of_columns = 0; + let mut code = 0; + let mut indirect = 0; + let tab = unsafe { + let mut pz_tab: *const c_char = mem::uninitialized(); + check!(ffi::sqlite3changeset_op( + self.it, + &mut pz_tab, + &mut number_of_columns, + &mut code, + &mut indirect + )); + CStr::from_ptr(pz_tab) + }; + let table_name = tab.to_str()?; + Ok(Operation { + table_name, + number_of_columns, + code: Action::from(code), + indirect: indirect != 0, + }) + } + + /// Obtain the primary key definition of a table + pub fn pk(&self) -> Result<&[u8]> { + let mut number_of_columns = 0; + unsafe { + let mut pks: *mut c_uchar = mem::uninitialized(); + check!(ffi::sqlite3changeset_pk( + self.it, + &mut pks, + &mut number_of_columns + )); + Ok(from_raw_parts(pks, number_of_columns as usize)) + } + } +} + +/// Used to combine two or more changesets or +/// patchsets +pub struct Changegroup { + cg: *mut ffi::sqlite3_changegroup, +} + +impl Changegroup { + pub fn new() -> Result { + let mut cg: *mut ffi::sqlite3_changegroup = unsafe { mem::uninitialized() }; + check!(unsafe { ffi::sqlite3changegroup_new(&mut cg) }); + Ok(Changegroup { cg }) + } + + /// Add a changeset + pub fn add(&mut self, cs: &Changeset) -> Result<()> { + check!(unsafe { ffi::sqlite3changegroup_add(self.cg, cs.n, cs.cs) }); + Ok(()) + } + + /// Add a changeset read from `input` to this change group. + pub fn add_stream(&mut self, input: &mut dyn Read) -> Result<()> { + let input_ref = &input; + check!(unsafe { + ffi::sqlite3changegroup_add_strm( + self.cg, + Some(x_input), + input_ref as *const &mut dyn Read as *mut c_void, + ) + }); + Ok(()) + } + + /// Obtain a composite Changeset + pub fn output(&mut self) -> Result { + let mut n = 0; + let mut output: *mut c_void = unsafe { mem::uninitialized() }; + check!(unsafe { ffi::sqlite3changegroup_output(self.cg, &mut n, &mut output) }); + Ok(Changeset { cs: output, n }) + } + + /// Write the combined set of changes to `output`. + pub fn output_strm(&mut self, output: &mut dyn Write) -> Result<()> { + let output_ref = &output; + check!(unsafe { + ffi::sqlite3changegroup_output_strm( + self.cg, + Some(x_output), + output_ref as *const &mut dyn Write as *mut c_void, + ) + }); + Ok(()) + } +} + +impl Drop for Changegroup { + fn drop(&mut self) { + unsafe { + ffi::sqlite3changegroup_delete(self.cg); + } + } +} + +impl Connection { + /// Apply a changeset to a database + pub fn apply(&self, cs: &Changeset, filter: Option, conflict: C) -> Result<()> + where + F: Fn(&str) -> bool + Send + RefUnwindSafe + 'static, + C: Fn(ConflictType, ChangesetItem) -> ConflictAction + Send + RefUnwindSafe + 'static, + { + let db = self.db.borrow_mut().db; + + let filtered = filter.is_some(); + let tuple = &mut (filter, conflict); + check!(unsafe { + if filtered { + ffi::sqlite3changeset_apply( + db, + cs.n, + cs.cs, + Some(call_filter::), + Some(call_conflict::), + tuple as *mut (Option, C) as *mut c_void, + ) + } else { + ffi::sqlite3changeset_apply( + db, + cs.n, + cs.cs, + None, + Some(call_conflict::), + tuple as *mut (Option, C) as *mut c_void, + ) + } + }); + Ok(()) + } + + /// Apply a changeset to a database + pub fn apply_strm( + &self, + input: &mut dyn Read, + filter: Option, + conflict: C, + ) -> Result<()> + where + F: Fn(&str) -> bool + Send + RefUnwindSafe + 'static, + C: Fn(ConflictType, ChangesetItem) -> ConflictAction + Send + RefUnwindSafe + 'static, + { + let input_ref = &input; + let db = self.db.borrow_mut().db; + + let filtered = filter.is_some(); + let tuple = &mut (filter, conflict); + check!(unsafe { + if filtered { + ffi::sqlite3changeset_apply_strm( + db, + Some(x_input), + input_ref as *const &mut dyn Read as *mut c_void, + Some(call_filter::), + Some(call_conflict::), + tuple as *mut (Option, C) as *mut c_void, + ) + } else { + ffi::sqlite3changeset_apply_strm( + db, + Some(x_input), + input_ref as *const &mut dyn Read as *mut c_void, + None, + Some(call_conflict::), + tuple as *mut (Option, C) as *mut c_void, + ) + } + }); + Ok(()) + } +} + +/// Constants passed to the conflict handler +#[repr(i32)] +#[derive(Debug, PartialEq)] +pub enum ConflictType { + UNKNOWN = -1, + SQLITE_CHANGESET_DATA = ffi::SQLITE_CHANGESET_DATA, + SQLITE_CHANGESET_NOTFOUND = ffi::SQLITE_CHANGESET_NOTFOUND, + SQLITE_CHANGESET_CONFLICT = ffi::SQLITE_CHANGESET_CONFLICT, + SQLITE_CHANGESET_CONSTRAINT = ffi::SQLITE_CHANGESET_CONSTRAINT, + SQLITE_CHANGESET_FOREIGN_KEY = ffi::SQLITE_CHANGESET_FOREIGN_KEY, +} +impl From for ConflictType { + fn from(code: i32) -> ConflictType { + match code { + ffi::SQLITE_CHANGESET_DATA => ConflictType::SQLITE_CHANGESET_DATA, + ffi::SQLITE_CHANGESET_NOTFOUND => ConflictType::SQLITE_CHANGESET_NOTFOUND, + ffi::SQLITE_CHANGESET_CONFLICT => ConflictType::SQLITE_CHANGESET_CONFLICT, + ffi::SQLITE_CHANGESET_CONSTRAINT => ConflictType::SQLITE_CHANGESET_CONSTRAINT, + ffi::SQLITE_CHANGESET_FOREIGN_KEY => ConflictType::SQLITE_CHANGESET_FOREIGN_KEY, + _ => ConflictType::UNKNOWN, + } + } +} + +/// Constants returned by the conflict handler +#[repr(i32)] +#[derive(Debug, PartialEq)] +pub enum ConflictAction { + SQLITE_CHANGESET_OMIT = ffi::SQLITE_CHANGESET_OMIT, + SQLITE_CHANGESET_REPLACE = ffi::SQLITE_CHANGESET_REPLACE, + SQLITE_CHANGESET_ABORT = ffi::SQLITE_CHANGESET_ABORT, +} + +unsafe extern "C" fn call_filter(p_ctx: *mut c_void, tbl_str: *const c_char) -> c_int +where + F: Fn(&str) -> bool + Send + RefUnwindSafe + 'static, + C: Fn(ConflictType, ChangesetItem) -> ConflictAction + Send + RefUnwindSafe + 'static, +{ + use std::ffi::CStr; + use std::str; + + let tuple: *mut (Option, C) = p_ctx as *mut (Option, C); + let tbl_name = { + let c_slice = CStr::from_ptr(tbl_str).to_bytes(); + str::from_utf8_unchecked(c_slice) + }; + match *tuple { + (Some(ref filter), _) => { + if let Ok(true) = catch_unwind(|| filter(tbl_name)) { + 1 + } else { + 0 + } + } + _ => unimplemented!(), + } +} + +unsafe extern "C" fn call_conflict( + p_ctx: *mut c_void, + e_conflict: c_int, + p: *mut ffi::sqlite3_changeset_iter, +) -> c_int +where + F: Fn(&str) -> bool + Send + RefUnwindSafe + 'static, + C: Fn(ConflictType, ChangesetItem) -> ConflictAction + Send + RefUnwindSafe + 'static, +{ + let tuple: *mut (Option, C) = p_ctx as *mut (Option, C); + let conflict_type = ConflictType::from(e_conflict); + let item = ChangesetItem { it: p }; + if let Ok(action) = catch_unwind(|| (*tuple).1(conflict_type, item)) { + action as c_int + } else { + ffi::SQLITE_CHANGESET_ABORT + } +} + +unsafe extern "C" fn x_input(p_in: *mut c_void, data: *mut c_void, len: *mut c_int) -> c_int { + if p_in.is_null() { + return ffi::SQLITE_MISUSE; + } + let bytes: &mut [u8] = from_raw_parts_mut(data as *mut u8, len as usize); + let input = p_in as *mut &mut dyn Read; + match (*input).read(bytes) { + Ok(n) => { + *len = n as i32; // TODO Validate: n = 0 may not mean the reader will always no longer be able to + // produce bytes. + ffi::SQLITE_OK + } + Err(_) => ffi::SQLITE_IOERR_READ, // TODO check if err is a (ru)sqlite Error => propagate + } +} + +unsafe extern "C" fn x_output(p_out: *mut c_void, data: *const c_void, len: c_int) -> c_int { + if p_out.is_null() { + return ffi::SQLITE_MISUSE; + } + // The sessions module never invokes an xOutput callback with the third + // parameter set to a value less than or equal to zero. + let bytes: &[u8] = from_raw_parts(data as *const u8, len as usize); + let output = p_out as *mut &mut dyn Write; + match (*output).write_all(bytes) { + Ok(_) => ffi::SQLITE_OK, + Err(_) => ffi::SQLITE_IOERR_WRITE, // TODO check if err is a (ru)sqlite Error => propagate + } +} + +#[cfg(test)] +mod test { + use fallible_streaming_iterator::FallibleStreamingIterator; + use std::sync::atomic::{AtomicBool, Ordering}; + + use super::{Changeset, ChangesetIter, ConflictAction, ConflictType, Session}; + use crate::hooks::Action; + use crate::Connection; + + fn one_changeset() -> Changeset { + let db = Connection::open_in_memory().unwrap(); + db.execute_batch("CREATE TABLE foo(t TEXT PRIMARY KEY NOT NULL);") + .unwrap(); + + let mut session = Session::new(&db).unwrap(); + assert!(session.is_empty()); + + session.attach(None).unwrap(); + db.execute("INSERT INTO foo (t) VALUES (?);", &["bar"]) + .unwrap(); + + session.changeset().unwrap() + } + + fn one_changeset_strm() -> Vec { + let db = Connection::open_in_memory().unwrap(); + db.execute_batch("CREATE TABLE foo(t TEXT PRIMARY KEY NOT NULL);") + .unwrap(); + + let mut session = Session::new(&db).unwrap(); + assert!(session.is_empty()); + + session.attach(None).unwrap(); + db.execute("INSERT INTO foo (t) VALUES (?);", &["bar"]) + .unwrap(); + + let mut output = Vec::new(); + session.changeset_strm(&mut output).unwrap(); + output + } + + #[test] + fn test_changeset() { + let changeset = one_changeset(); + let mut iter = changeset.iter().unwrap(); + let item = iter.next().unwrap(); + assert!(item.is_some()); + + let item = item.unwrap(); + let op = item.op().unwrap(); + assert_eq!("foo", op.table_name()); + assert_eq!(1, op.number_of_columns()); + assert_eq!(Action::SQLITE_INSERT, op.code()); + assert_eq!(false, op.indirect()); + + let pk = item.pk().unwrap(); + assert_eq!(&[1], pk); + + let new_value = item.new_value(0).unwrap(); + assert_eq!(Ok("bar"), new_value.as_str()); + } + + #[test] + fn test_changeset_strm() { + let output = one_changeset_strm(); + assert!(!output.is_empty()); + assert_eq!(14, output.len()); + + let mut input = output.as_slice(); + let mut iter = ChangesetIter::start_strm(&mut input).unwrap(); + let item = iter.next().unwrap(); + assert!(item.is_some()); + } + + #[test] + fn test_changeset_apply() { + let changeset = one_changeset(); + + let db = Connection::open_in_memory().unwrap(); + db.execute_batch("CREATE TABLE foo(t TEXT PRIMARY KEY NOT NULL);") + .unwrap(); + + lazy_static! { + static ref CALLED: AtomicBool = AtomicBool::new(false); + } + db.apply( + &changeset, + None:: bool>, + |_conflict_type, _item| { + CALLED.store(true, Ordering::Relaxed); + ConflictAction::SQLITE_CHANGESET_OMIT + }, + ) + .unwrap(); + + assert!(!CALLED.load(Ordering::Relaxed)); + let check = db + .query_row("SELECT 1 FROM foo WHERE t = ?", &["bar"], |row| { + row.get::<_, i32>(0) + }) + .unwrap(); + assert_eq!(1, check); + + // conflict expected when same changeset applied again on the same db + db.apply( + &changeset, + None:: bool>, + |conflict_type, item| { + CALLED.store(true, Ordering::Relaxed); + assert_eq!(ConflictType::SQLITE_CHANGESET_CONFLICT, conflict_type); + let conflict = item.conflict(0).unwrap(); + assert_eq!(Ok("bar"), conflict.as_str()); + ConflictAction::SQLITE_CHANGESET_OMIT + }, + ) + .unwrap(); + assert!(CALLED.load(Ordering::Relaxed)); + } + + #[test] + fn test_changeset_apply_strm() { + let output = one_changeset_strm(); + + let db = Connection::open_in_memory().unwrap(); + db.execute_batch("CREATE TABLE foo(t TEXT PRIMARY KEY NOT NULL);") + .unwrap(); + + db.apply_strm( + &mut output.as_slice(), + None:: bool>, + |_conflict_type, _item| ConflictAction::SQLITE_CHANGESET_OMIT, + ) + .unwrap(); + + let check = db + .query_row("SELECT 1 FROM foo WHERE t = ?", &["bar"], |row| { + row.get::<_, i32>(0) + }) + .unwrap(); + assert_eq!(1, check); + } + + #[test] + fn test_session_empty() { + let db = Connection::open_in_memory().unwrap(); + db.execute_batch("CREATE TABLE foo(t TEXT PRIMARY KEY NOT NULL);") + .unwrap(); + + let mut session = Session::new(&db).unwrap(); + assert!(session.is_empty()); + + session.attach(None).unwrap(); + db.execute("INSERT INTO foo (t) VALUES (?);", &["bar"]) + .unwrap(); + + assert!(!session.is_empty()); + } + + #[test] + fn test_session_set_enabled() { + let db = Connection::open_in_memory().unwrap(); + + let mut session = Session::new(&db).unwrap(); + assert!(session.is_enabled()); + session.set_enabled(false); + assert!(!session.is_enabled()); + } + + #[test] + fn test_session_set_indirect() { + let db = Connection::open_in_memory().unwrap(); + + let mut session = Session::new(&db).unwrap(); + assert!(!session.is_indirect()); + session.set_indirect(true); + assert!(session.is_indirect()); + } +} diff --git a/src/statement.rs b/src/statement.rs index 89cd1cb..83e80f3 100644 --- a/src/statement.rs +++ b/src/statement.rs @@ -11,9 +11,9 @@ use super::str_to_cstring; use super::{ AndThenRows, Connection, Error, MappedRows, RawStatement, Result, Row, Rows, ValueRef, }; -use types::{ToSql, ToSqlOutput}; +use crate::types::{ToSql, ToSqlOutput}; #[cfg(feature = "array")] -use vtab::array::{free_array, ARRAY_TYPE}; +use crate::vtab::array::{free_array, ARRAY_TYPE}; /// A prepared statement. pub struct Statement<'conn> { @@ -70,10 +70,10 @@ impl<'conn> Statement<'conn> { /// ```rust,no_run /// # use rusqlite::{Connection, Result}; /// fn update_rows(conn: &Connection) -> Result<()> { - /// let mut stmt = try!(conn.prepare("UPDATE foo SET bar = 'baz' WHERE qux = ?")); + /// let mut stmt = conn.prepare("UPDATE foo SET bar = 'baz' WHERE qux = ?")?; /// - /// try!(stmt.execute(&[1i32])); - /// try!(stmt.execute(&[2i32])); + /// stmt.execute(&[1i32])?; + /// stmt.execute(&[2i32])?; /// /// Ok(()) /// } @@ -89,7 +89,7 @@ impl<'conn> Statement<'conn> { P: IntoIterator, P::Item: ToSql, { - try!(self.bind_parameters(params)); + self.bind_parameters(params)?; self.execute_with_bound_parameters() } @@ -107,18 +107,29 @@ impl<'conn> Statement<'conn> { /// ```rust,no_run /// # use rusqlite::{Connection, Result}; /// fn insert(conn: &Connection) -> Result { - /// let mut stmt = try!(conn.prepare("INSERT INTO test (name) VALUES (:name)")); + /// let mut stmt = conn.prepare("INSERT INTO test (name) VALUES (:name)")?; /// stmt.execute_named(&[(":name", &"one")]) /// } /// ``` /// + /// Note, the `named_params` macro is provided for syntactic convenience, + /// and so the above example could also be written as: + /// + /// ```rust,no_run + /// # use rusqlite::{Connection, Result, named_params}; + /// fn insert(conn: &Connection) -> Result { + /// let mut stmt = conn.prepare("INSERT INTO test (name) VALUES (:name)")?; + /// stmt.execute_named(named_params!{":name": "one"}) + /// } + /// ``` + /// /// # Failure /// /// Will return `Err` if binding parameters fails, the executed statement /// returns rows (in which case `query` should be used instead), or the /// underling SQLite call fails. - pub fn execute_named(&mut self, params: &[(&str, &ToSql)]) -> Result { - try!(self.bind_parameters_named(params)); + pub fn execute_named(&mut self, params: &[(&str, &dyn ToSql)]) -> Result { + self.bind_parameters_named(params)?; self.execute_with_bound_parameters() } @@ -140,7 +151,7 @@ impl<'conn> Statement<'conn> { P: IntoIterator, P::Item: ToSql, { - let changes = try!(self.execute(params)); + let changes = self.execute(params)?; match changes { 1 => Ok(self.conn.last_insert_rowid()), _ => Err(Error::StatementChangedRows(changes)), @@ -159,12 +170,12 @@ impl<'conn> Statement<'conn> { /// ```rust,no_run /// # use rusqlite::{Connection, Result, NO_PARAMS}; /// fn get_names(conn: &Connection) -> Result> { - /// let mut stmt = try!(conn.prepare("SELECT name FROM people")); - /// let mut rows = try!(stmt.query(NO_PARAMS)); + /// let mut stmt = conn.prepare("SELECT name FROM people")?; + /// let mut rows = stmt.query(NO_PARAMS)?; /// /// let mut names = Vec::new(); /// while let Some(result_row) = rows.next() { - /// let row = try!(result_row); + /// let row = result_row?; /// names.push(row.get(0)); /// } /// @@ -175,13 +186,13 @@ impl<'conn> Statement<'conn> { /// ## Failure /// /// Will return `Err` if binding parameters fails. - pub fn query<'a, P>(&'a mut self, params: P) -> Result> + pub fn query

(&mut self, params: P) -> Result> where P: IntoIterator, P::Item: ToSql, { - try!(self.check_readonly()); - try!(self.bind_parameters(params)); + self.check_readonly()?; + self.bind_parameters(params)?; Ok(Rows::new(self)) } @@ -196,8 +207,23 @@ impl<'conn> Statement<'conn> { /// ```rust,no_run /// # use rusqlite::{Connection, Result}; /// fn query(conn: &Connection) -> Result<()> { - /// let mut stmt = try!(conn.prepare("SELECT * FROM test where name = :name")); - /// let mut rows = try!(stmt.query_named(&[(":name", &"one")])); + /// let mut stmt = conn.prepare("SELECT * FROM test where name = :name")?; + /// let mut rows = stmt.query_named(&[(":name", &"one")])?; + /// while let Some(row) = rows.next() { + /// // ... + /// } + /// Ok(()) + /// } + /// ``` + /// + /// Note, the `named_params!` macro is provided for syntactic convenience, + /// and so the above example could also be written as: + /// + /// ```rust,no_run + /// # use rusqlite::{Connection, Result, named_params}; + /// fn query(conn: &Connection) -> Result<()> { + /// let mut stmt = conn.prepare("SELECT * FROM test where name = :name")?; + /// let mut rows = stmt.query_named(named_params!{ ":name": "one" })?; /// while let Some(row) = rows.next() { /// // ... /// } @@ -208,9 +234,9 @@ impl<'conn> Statement<'conn> { /// # Failure /// /// Will return `Err` if binding parameters fails. - pub fn query_named<'a>(&'a mut self, params: &[(&str, &ToSql)]) -> Result> { - try!(self.check_readonly()); - try!(self.bind_parameters_named(params)); + pub fn query_named<'a>(&'a mut self, params: &[(&str, &dyn ToSql)]) -> Result> { + self.check_readonly()?; + self.bind_parameters_named(params)?; Ok(Rows::new(self)) } @@ -222,12 +248,12 @@ impl<'conn> Statement<'conn> { /// ```rust,no_run /// # use rusqlite::{Connection, Result, NO_PARAMS}; /// fn get_names(conn: &Connection) -> Result> { - /// let mut stmt = try!(conn.prepare("SELECT name FROM people")); - /// let rows = try!(stmt.query_map(NO_PARAMS, |row| row.get(0))); + /// let mut stmt = conn.prepare("SELECT name FROM people")?; + /// let rows = stmt.query_map(NO_PARAMS, |row| row.get(0))?; /// /// let mut names = Vec::new(); /// for name_result in rows { - /// names.push(try!(name_result)); + /// names.push(name_result?); /// } /// /// Ok(names) @@ -237,11 +263,11 @@ impl<'conn> Statement<'conn> { /// ## Failure /// /// Will return `Err` if binding parameters fails. - pub fn query_map<'a, T, P, F>(&'a mut self, params: P, f: F) -> Result> + pub fn query_map(&mut self, params: P, f: F) -> Result> where P: IntoIterator, P::Item: ToSql, - F: FnMut(&Row) -> T, + F: FnMut(&Row<'_, '_>) -> T, { let rows = self.query(params)?; Ok(MappedRows::new(rows, f)) @@ -259,12 +285,12 @@ impl<'conn> Statement<'conn> { /// ```rust,no_run /// # use rusqlite::{Connection, Result}; /// fn get_names(conn: &Connection) -> Result> { - /// let mut stmt = try!(conn.prepare("SELECT name FROM people WHERE id = :id")); - /// let rows = try!(stmt.query_map_named(&[(":id", &"one")], |row| row.get(0))); + /// let mut stmt = conn.prepare("SELECT name FROM people WHERE id = :id")?; + /// let rows = stmt.query_map_named(&[(":id", &"one")], |row| row.get(0))?; /// /// let mut names = Vec::new(); /// for name_result in rows { - /// names.push(try!(name_result)); + /// names.push(name_result?); /// } /// /// Ok(names) @@ -276,11 +302,11 @@ impl<'conn> Statement<'conn> { /// Will return `Err` if binding parameters fails. pub fn query_map_named<'a, T, F>( &'a mut self, - params: &[(&str, &ToSql)], + params: &[(&str, &dyn ToSql)], f: F, ) -> Result> where - F: FnMut(&Row) -> T, + F: FnMut(&Row<'_, '_>) -> T, { let rows = self.query_named(params)?; Ok(MappedRows::new(rows, f)) @@ -293,16 +319,12 @@ impl<'conn> Statement<'conn> { /// # Failure /// /// Will return `Err` if binding parameters fails. - pub fn query_and_then<'a, T, E, P, F>( - &'a mut self, - params: P, - f: F, - ) -> Result> + pub fn query_and_then(&mut self, params: P, f: F) -> Result> where P: IntoIterator, P::Item: ToSql, E: convert::From, - F: FnMut(&Row) -> result::Result, + F: FnMut(&Row<'_, '_>) -> result::Result, { let rows = self.query(params)?; Ok(AndThenRows::new(rows, f)) @@ -330,13 +352,13 @@ impl<'conn> Statement<'conn> { /// } /// /// fn get_names(conn: &Connection) -> Result> { - /// let mut stmt = try!(conn.prepare("SELECT name FROM people WHERE id = :id")); + /// let mut stmt = conn.prepare("SELECT name FROM people WHERE id = :id")?; /// let rows = - /// try!(stmt.query_and_then_named(&[(":id", &"one")], |row| name_to_person(row.get(0)))); + /// stmt.query_and_then_named(&[(":id", &"one")], |row| name_to_person(row.get(0)))?; /// /// let mut persons = Vec::new(); /// for person_result in rows { - /// persons.push(try!(person_result)); + /// persons.push(person_result?); /// } /// /// Ok(persons) @@ -348,12 +370,12 @@ impl<'conn> Statement<'conn> { /// Will return `Err` if binding parameters fails. pub fn query_and_then_named<'a, T, E, F>( &'a mut self, - params: &[(&str, &ToSql)], + params: &[(&str, &dyn ToSql)], f: F, ) -> Result> where E: convert::From, - F: FnMut(&Row) -> result::Result, + F: FnMut(&Row<'_, '_>) -> result::Result, { let rows = self.query_named(params)?; Ok(AndThenRows::new(rows, f)) @@ -366,13 +388,8 @@ impl<'conn> Statement<'conn> { P: IntoIterator, P::Item: ToSql, { - let mut rows = try!(self.query(params)); - let exists = { - match rows.next() { - Some(_) => true, - None => false, - } - }; + let mut rows = self.query(params)?; + let exists = rows.next().is_some(); Ok(exists) } @@ -382,6 +399,10 @@ impl<'conn> Statement<'conn> { /// If the query returns more than one row, all rows except the first are /// ignored. /// + /// Returns `Err(QueryReturnedNoRows)` if no results are returned. If the + /// query truly is optional, you can call `.optional()` on the result of + /// this to get a `Result>`. + /// /// # Failure /// /// Will return `Err` if the underlying SQLite call fails. @@ -389,9 +410,9 @@ impl<'conn> Statement<'conn> { where P: IntoIterator, P::Item: ToSql, - F: FnOnce(&Row) -> T, + F: FnOnce(&Row<'_, '_>) -> T, { - let mut rows = try!(self.query(params)); + let mut rows = self.query(params)?; rows.get_expected_row().map(|r| f(&r)) } @@ -415,7 +436,7 @@ impl<'conn> Statement<'conn> { /// Will return Err if `name` is invalid. Will return Ok(None) if the name /// is valid but not a bound parameter of this statement. pub fn parameter_index(&self, name: &str) -> Result> { - let c_name = try!(str_to_cstring(name)); + let c_name = str_to_cstring(name)?; Ok(self.stmt.bind_parameter_index(&c_name)) } @@ -431,7 +452,7 @@ impl<'conn> Statement<'conn> { if index > expected { break; } - try!(self.bind_parameter(&p, index)); + self.bind_parameter(&p, index)?; } assert_eq!( index, expected, @@ -442,10 +463,10 @@ impl<'conn> Statement<'conn> { Ok(()) } - fn bind_parameters_named(&mut self, params: &[(&str, &ToSql)]) -> Result<()> { + fn bind_parameters_named(&mut self, params: &[(&str, &dyn ToSql)]) -> Result<()> { for &(name, value) in params { - if let Some(i) = try!(self.parameter_index(name)) { - try!(self.bind_parameter(value, i)); + if let Some(i) = self.parameter_index(name)? { + self.bind_parameter(value, i)?; } else { return Err(Error::InvalidParameterName(name.into())); } @@ -453,8 +474,8 @@ impl<'conn> Statement<'conn> { Ok(()) } - fn bind_parameter(&self, param: &ToSql, col: usize) -> Result<()> { - let value = try!(param.to_sql()); + fn bind_parameter(&self, param: &dyn ToSql, col: usize) -> Result<()> { + let value = param.to_sql()?; let ptr = unsafe { self.stmt.ptr() }; let value = match value { @@ -489,7 +510,7 @@ impl<'conn> Statement<'conn> { if length > ::std::i32::MAX as usize { ffi::SQLITE_TOOBIG } else { - let c_str = try!(str_to_cstring(s)); + let c_str = str_to_cstring(s)?; let destructor = if length > 0 { ffi::SQLITE_TRANSIENT() } else { @@ -571,6 +592,17 @@ impl<'conn> Statement<'conn> { } } + /// Get the value for one of the status counters for this statement. + pub fn get_status(&self, status: StatementStatus) -> i32 { + self.stmt.get_status(status, false) + } + + /// Reset the value of one of the status counters for this statement, + /// returning the value it had before resetting. + pub fn reset_status(&self, status: StatementStatus) -> i32 { + self.stmt.get_status(status, true) + } + pub(crate) fn check_no_tail(&self) -> Result<()> { if self.stmt.has_tail() { Err(Error::MultipleStatement) @@ -589,7 +621,7 @@ impl<'conn> Into for Statement<'conn> { } impl<'conn> fmt::Debug for Statement<'conn> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let sql = str::from_utf8(self.stmt.sql().to_bytes()); f.debug_struct("Statement") .field("conn", self.conn) @@ -607,11 +639,11 @@ impl<'conn> Drop for Statement<'conn> { } impl<'conn> Statement<'conn> { - pub(crate) fn new(conn: &Connection, stmt: RawStatement) -> Statement { + pub(crate) fn new(conn: &Connection, stmt: RawStatement) -> Statement<'_> { Statement { conn, stmt } } - pub(crate) fn value_ref(&self, col: usize) -> ValueRef { + pub(crate) fn value_ref(&self, col: usize) -> ValueRef<'_> { let raw = unsafe { self.stmt.ptr() }; match self.stmt.column_type(col) { @@ -679,9 +711,36 @@ impl<'conn> Statement<'conn> { } } +/// Prepared statement status counters. +/// +/// See https://www.sqlite.org/c3ref/c_stmtstatus_counter.html +/// for explanations of each. +/// +/// Note that depending on your version of SQLite, all of these +/// may not be available. +#[repr(i32)] +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum StatementStatus { + /// Equivalent to SQLITE_STMTSTATUS_FULLSCAN_STEP + FullscanStep = 1, + /// Equivalent to SQLITE_STMTSTATUS_SORT + Sort = 2, + /// Equivalent to SQLITE_STMTSTATUS_AUTOINDEX + AutoIndex = 3, + /// Equivalent to SQLITE_STMTSTATUS_VM_STEP + VmStep = 4, + /// Equivalent to SQLITE_STMTSTATUS_REPREPARE + RePrepare = 5, + /// Equivalent to SQLITE_STMTSTATUS_RUN + Run = 6, + /// Equivalent to SQLITE_STMTSTATUS_MEMUSED + MemUsed = 99, +} + #[cfg(test)] mod test { - use {Connection, Error, Result, NO_PARAMS}; + use crate::types::ToSql; + use crate::{Connection, Error, Result, NO_PARAMS}; #[test] fn test_execute_named() { @@ -803,6 +862,7 @@ mod test { assert_eq!(1, doubled_id); // second row should be Err + #[allow(clippy::match_wild_err_arm)] match rows.next().unwrap() { Ok(_) => panic!("invalid Ok"), Err(Error::SqliteSingleThreadedMode) => (), @@ -963,4 +1023,40 @@ mod test { stmt.bind_parameter(&1, 1).unwrap(); assert_eq!(Some("SELECT 1"), stmt.expanded_sql()); } + + #[test] + fn test_bind_parameters() { + let db = Connection::open_in_memory().unwrap(); + // dynamic slice: + db.query_row( + "SELECT ?1, ?2, ?3", + &[&1u8 as &dyn ToSql, &"one", &Some("one")], + |row| row.get::<_, u8>(0), + ) + .unwrap(); + // existing collection: + let data = vec![1, 2, 3]; + db.query_row("SELECT ?1, ?2, ?3", &data, |row| row.get::<_, u8>(0)) + .unwrap(); + db.query_row("SELECT ?1, ?2, ?3", data.as_slice(), |row| { + row.get::<_, u8>(0) + }) + .unwrap(); + db.query_row("SELECT ?1, ?2, ?3", data, |row| row.get::<_, u8>(0)) + .unwrap(); + + use std::collections::BTreeSet; + let data: BTreeSet = ["one", "two", "three"] + .iter() + .map(|s| s.to_string()) + .collect(); + db.query_row("SELECT ?1, ?2, ?3", &data, |row| row.get::<_, String>(0)) + .unwrap(); + + let data = [0; 3]; + db.query_row("SELECT ?1, ?2, ?3", &data, |row| row.get::<_, u8>(0)) + .unwrap(); + db.query_row("SELECT ?1, ?2, ?3", data.iter(), |row| row.get::<_, u8>(0)) + .unwrap(); + } } diff --git a/src/trace.rs b/src/trace.rs index a11396b..ddf537f 100644 --- a/src/trace.rs +++ b/src/trace.rs @@ -3,12 +3,13 @@ use std::ffi::{CStr, CString}; use std::mem; use std::os::raw::{c_char, c_int, c_void}; +use std::panic::catch_unwind; use std::ptr; use std::time::Duration; use super::ffi; -use error::error_from_sqlite_code; -use {Connection, Result}; +use crate::error::error_from_sqlite_code; +use crate::{Connection, Result}; /// Set up the process-wide SQLite error logging callback. /// This function is marked unsafe for two reasons: @@ -27,7 +28,7 @@ pub unsafe fn config_log(callback: Option) -> Result<()> { let callback: fn(c_int, &str) = unsafe { mem::transmute(p_arg) }; let s = String::from_utf8_lossy(c_slice); - callback(err, &s); + let _ = catch_unwind(|| callback(err, &s)); } let rc = match callback { @@ -72,7 +73,7 @@ impl Connection { let trace_fn: fn(&str) = mem::transmute(p_arg); let c_slice = CStr::from_ptr(z_sql).to_bytes(); let s = String::from_utf8_lossy(c_slice); - trace_fn(&s); + let _ = catch_unwind(|| trace_fn(&s)); } let c = self.db.borrow_mut(); @@ -106,7 +107,7 @@ impl Connection { nanoseconds / NANOS_PER_SEC, (nanoseconds % NANOS_PER_SEC) as u32, ); - profile_fn(&s, duration); + let _ = catch_unwind(|| profile_fn(&s, duration)); } let c = self.db.borrow_mut(); @@ -124,7 +125,7 @@ mod test { use std::sync::Mutex; use std::time::Duration; - use Connection; + use crate::Connection; #[test] fn test_trace() { diff --git a/src/transaction.rs b/src/transaction.rs index 05dd3d5..4ee1e82 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -1,10 +1,5 @@ +use crate::{Connection, Result}; use std::ops::Deref; -use {Connection, Result}; - -/// Old name for `TransactionBehavior`. `SqliteTransactionBehavior` is -/// deprecated. -#[deprecated(since = "0.6.0", note = "Use TransactionBehavior instead")] -pub type SqliteTransactionBehavior = TransactionBehavior; /// Options for transaction behavior. See [BEGIN /// TRANSACTION](http://www.sqlite.org/lang_transaction.html) for details. @@ -16,7 +11,7 @@ pub enum TransactionBehavior { } /// Options for how a Transaction or Savepoint should behave when it is dropped. -#[derive(Copy, Clone, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum DropBehavior { /// Roll back the changes. This is the default. Rollback, @@ -32,10 +27,6 @@ pub enum DropBehavior { Panic, } -/// Old name for `Transaction`. `SqliteTransaction` is deprecated. -#[deprecated(since = "0.6.0", note = "Use Transaction instead")] -pub type SqliteTransaction<'conn> = Transaction<'conn>; - /// Represents a transaction on a database connection. /// /// ## Note @@ -51,14 +42,15 @@ pub type SqliteTransaction<'conn> = Transaction<'conn>; /// # fn do_queries_part_1(_conn: &Connection) -> Result<()> { Ok(()) } /// # fn do_queries_part_2(_conn: &Connection) -> Result<()> { Ok(()) } /// fn perform_queries(conn: &mut Connection) -> Result<()> { -/// let tx = try!(conn.transaction()); +/// let tx = conn.transaction()?; /// -/// try!(do_queries_part_1(&tx)); // tx causes rollback if this fails -/// try!(do_queries_part_2(&tx)); // tx causes rollback if this fails +/// do_queries_part_1(&tx)?; // tx causes rollback if this fails +/// do_queries_part_2(&tx)?; // tx causes rollback if this fails /// /// tx.commit() /// } /// ``` +#[derive(Debug)] pub struct Transaction<'conn> { conn: &'conn Connection, drop_behavior: DropBehavior, @@ -79,10 +71,10 @@ pub struct Transaction<'conn> { /// # fn do_queries_part_1(_conn: &Connection) -> Result<()> { Ok(()) } /// # fn do_queries_part_2(_conn: &Connection) -> Result<()> { Ok(()) } /// fn perform_queries(conn: &mut Connection) -> Result<()> { -/// let sp = try!(conn.savepoint()); +/// let sp = conn.savepoint()?; /// -/// try!(do_queries_part_1(&sp)); // sp causes rollback if this fails -/// try!(do_queries_part_2(&sp)); // sp causes rollback if this fails +/// do_queries_part_1(&sp)?; // sp causes rollback if this fails +/// do_queries_part_2(&sp)?; // sp causes rollback if this fails /// /// sp.commit() /// } @@ -101,7 +93,7 @@ impl<'conn> Transaction<'conn> { /// Even though we don't mutate the connection, we take a `&mut Connection` /// so as to prevent nested or concurrent transactions on the same /// connection. - pub fn new(conn: &mut Connection, behavior: TransactionBehavior) -> Result { + pub fn new(conn: &mut Connection, behavior: TransactionBehavior) -> Result> { let query = match behavior { TransactionBehavior::Deferred => "BEGIN DEFERRED", TransactionBehavior::Immediate => "BEGIN IMMEDIATE", @@ -127,12 +119,12 @@ impl<'conn> Transaction<'conn> { /// # use rusqlite::{Connection, Result}; /// # fn perform_queries_part_1_succeeds(_conn: &Connection) -> bool { true } /// fn perform_queries(conn: &mut Connection) -> Result<()> { - /// let mut tx = try!(conn.transaction()); + /// let mut tx = conn.transaction()?; /// /// { - /// let sp = try!(tx.savepoint()); + /// let sp = tx.savepoint()?; /// if perform_queries_part_1_succeeds(&sp) { - /// try!(sp.commit()); + /// sp.commit()?; /// } /// // otherwise, sp will rollback /// } @@ -140,12 +132,12 @@ impl<'conn> Transaction<'conn> { /// tx.commit() /// } /// ``` - pub fn savepoint(&mut self) -> Result { + pub fn savepoint(&mut self) -> Result> { Savepoint::with_depth(self.conn, 1) } /// Create a new savepoint with a custom savepoint name. See `savepoint()`. - pub fn savepoint_with_name>(&mut self, name: T) -> Result { + pub fn savepoint_with_name>(&mut self, name: T) -> Result> { Savepoint::with_depth_and_name(self.conn, 1, name) } @@ -223,7 +215,7 @@ impl<'conn> Savepoint<'conn> { conn: &Connection, depth: u32, name: T, - ) -> Result { + ) -> Result> { let name = name.into(); conn.execute_batch(&format!("SAVEPOINT {}", name)) .map(|_| Savepoint { @@ -235,28 +227,28 @@ impl<'conn> Savepoint<'conn> { }) } - fn with_depth(conn: &Connection, depth: u32) -> Result { + fn with_depth(conn: &Connection, depth: u32) -> Result> { let name = format!("_rusqlite_sp_{}", depth); Savepoint::with_depth_and_name(conn, depth, name) } /// Begin a new savepoint. Can be nested. - pub fn new(conn: &mut Connection) -> Result { + pub fn new(conn: &mut Connection) -> Result> { Savepoint::with_depth(conn, 0) } /// Begin a new savepoint with a user-provided savepoint name. - pub fn with_name>(conn: &mut Connection, name: T) -> Result { + pub fn with_name>(conn: &mut Connection, name: T) -> Result> { Savepoint::with_depth_and_name(conn, 0, name) } /// Begin a nested savepoint. - pub fn savepoint(&mut self) -> Result { + pub fn savepoint(&mut self) -> Result> { Savepoint::with_depth(self.conn, self.depth + 1) } /// Begin a nested savepoint with a user-provided savepoint name. - pub fn savepoint_with_name>(&mut self, name: T) -> Result { + pub fn savepoint_with_name>(&mut self, name: T) -> Result> { Savepoint::with_depth_and_name(self.conn, self.depth + 1, name) } @@ -345,10 +337,10 @@ impl Connection { /// # fn do_queries_part_1(_conn: &Connection) -> Result<()> { Ok(()) } /// # fn do_queries_part_2(_conn: &Connection) -> Result<()> { Ok(()) } /// fn perform_queries(conn: &mut Connection) -> Result<()> { - /// let tx = try!(conn.transaction()); + /// let tx = conn.transaction()?; /// - /// try!(do_queries_part_1(&tx)); // tx causes rollback if this fails - /// try!(do_queries_part_2(&tx)); // tx causes rollback if this fails + /// do_queries_part_1(&tx)?; // tx causes rollback if this fails + /// do_queries_part_2(&tx)?; // tx causes rollback if this fails /// /// tx.commit() /// } @@ -357,7 +349,7 @@ impl Connection { /// # Failure /// /// Will return `Err` if the underlying SQLite call fails. - pub fn transaction(&mut self) -> Result { + pub fn transaction(&mut self) -> Result> { Transaction::new(self, TransactionBehavior::Deferred) } @@ -371,7 +363,7 @@ impl Connection { pub fn transaction_with_behavior( &mut self, behavior: TransactionBehavior, - ) -> Result { + ) -> Result> { Transaction::new(self, behavior) } @@ -388,10 +380,10 @@ impl Connection { /// # fn do_queries_part_1(_conn: &Connection) -> Result<()> { Ok(()) } /// # fn do_queries_part_2(_conn: &Connection) -> Result<()> { Ok(()) } /// fn perform_queries(conn: &mut Connection) -> Result<()> { - /// let sp = try!(conn.savepoint()); + /// let sp = conn.savepoint()?; /// - /// try!(do_queries_part_1(&sp)); // sp causes rollback if this fails - /// try!(do_queries_part_2(&sp)); // sp causes rollback if this fails + /// do_queries_part_1(&sp)?; // sp causes rollback if this fails + /// do_queries_part_2(&sp)?; // sp causes rollback if this fails /// /// sp.commit() /// } @@ -400,7 +392,7 @@ impl Connection { /// # Failure /// /// Will return `Err` if the underlying SQLite call fails. - pub fn savepoint(&mut self) -> Result { + pub fn savepoint(&mut self) -> Result> { Savepoint::new(self) } @@ -411,7 +403,7 @@ impl Connection { /// # Failure /// /// Will return `Err` if the underlying SQLite call fails. - pub fn savepoint_with_name>(&mut self, name: T) -> Result { + pub fn savepoint_with_name>(&mut self, name: T) -> Result> { Savepoint::with_name(self, name) } } @@ -419,7 +411,7 @@ impl Connection { #[cfg(test)] mod test { use super::DropBehavior; - use {Connection, NO_PARAMS}; + use crate::{Connection, NO_PARAMS}; fn checked_memory_handle() -> Connection { let db = Connection::open_in_memory().unwrap(); @@ -564,6 +556,16 @@ mod test { assert_current_sum(8, &db); } + #[test] + fn test_rc() { + use std::rc::Rc; + let mut conn = Connection::open_in_memory().unwrap(); + let rc_txn = Rc::new(conn.transaction().unwrap()); + + // This will compile only if Transaction is Debug + Rc::try_unwrap(rc_txn).unwrap(); + } + fn insert(x: i32, conn: &Connection) { conn.execute("INSERT INTO foo VALUES(?)", &[x]).unwrap(); } diff --git a/src/types/chrono.rs b/src/types/chrono.rs index 77f8031..4f73146 100644 --- a/src/types/chrono.rs +++ b/src/types/chrono.rs @@ -1,16 +1,16 @@ //! Convert most of the [Time Strings](http://sqlite.org/lang_datefunc.html) to chrono types. -extern crate chrono; +use chrono; use std::borrow::Cow; use self::chrono::{DateTime, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc}; -use types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef}; -use Result; +use crate::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef}; +use crate::Result; /// ISO 8601 calendar date without timezone => "YYYY-MM-DD" impl ToSql for NaiveDate { - fn to_sql(&self) -> Result { + fn to_sql(&self) -> Result> { let date_str = self.format("%Y-%m-%d").to_string(); Ok(ToSqlOutput::from(date_str)) } @@ -18,7 +18,7 @@ impl ToSql for NaiveDate { /// "YYYY-MM-DD" => ISO 8601 calendar date without timezone. impl FromSql for NaiveDate { - fn column_result(value: ValueRef) -> FromSqlResult { + fn column_result(value: ValueRef<'_>) -> FromSqlResult { value .as_str() .and_then(|s| match NaiveDate::parse_from_str(s, "%Y-%m-%d") { @@ -30,7 +30,7 @@ impl FromSql for NaiveDate { /// ISO 8601 time without timezone => "HH:MM:SS.SSS" impl ToSql for NaiveTime { - fn to_sql(&self) -> Result { + fn to_sql(&self) -> Result> { let date_str = self.format("%H:%M:%S%.f").to_string(); Ok(ToSqlOutput::from(date_str)) } @@ -38,7 +38,7 @@ impl ToSql for NaiveTime { /// "HH:MM"/"HH:MM:SS"/"HH:MM:SS.SSS" => ISO 8601 time without timezone. impl FromSql for NaiveTime { - fn column_result(value: ValueRef) -> FromSqlResult { + fn column_result(value: ValueRef<'_>) -> FromSqlResult { value.as_str().and_then(|s| { let fmt = match s.len() { 5 => "%H:%M", @@ -56,7 +56,7 @@ impl FromSql for NaiveTime { /// ISO 8601 combined date and time without timezone => /// "YYYY-MM-DD HH:MM:SS.SSS" impl ToSql for NaiveDateTime { - fn to_sql(&self) -> Result { + fn to_sql(&self) -> Result> { let date_str = self.format("%Y-%m-%dT%H:%M:%S%.f").to_string(); Ok(ToSqlOutput::from(date_str)) } @@ -66,7 +66,7 @@ impl ToSql for NaiveDateTime { /// and time without timezone. ("YYYY-MM-DDTHH:MM:SS"/"YYYY-MM-DDTHH:MM:SS.SSS" /// also supported) impl FromSql for NaiveDateTime { - fn column_result(value: ValueRef) -> FromSqlResult { + fn column_result(value: ValueRef<'_>) -> FromSqlResult { value.as_str().and_then(|s| { let fmt = if s.len() >= 11 && s.as_bytes()[10] == b'T' { "%Y-%m-%dT%H:%M:%S%.f" @@ -85,17 +85,17 @@ impl FromSql for NaiveDateTime { /// Date and time with time zone => UTC RFC3339 timestamp /// ("YYYY-MM-DDTHH:MM:SS.SSS+00:00"). impl ToSql for DateTime { - fn to_sql(&self) -> Result { + fn to_sql(&self) -> Result> { Ok(ToSqlOutput::from(self.with_timezone(&Utc).to_rfc3339())) } } /// RFC3339 ("YYYY-MM-DDTHH:MM:SS.SSS[+-]HH:MM") into `DateTime`. impl FromSql for DateTime { - fn column_result(value: ValueRef) -> FromSqlResult { + fn column_result(value: ValueRef<'_>) -> FromSqlResult { { // Try to parse value as rfc3339 first. - let s = try!(value.as_str()); + let s = value.as_str()?; // If timestamp looks space-separated, make a copy and replace it with 'T'. let s = if s.len() >= 11 && s.as_bytes()[10] == b' ' { @@ -121,8 +121,8 @@ impl FromSql for DateTime { /// RFC3339 ("YYYY-MM-DDTHH:MM:SS.SSS[+-]HH:MM") into `DateTime`. impl FromSql for DateTime { - fn column_result(value: ValueRef) -> FromSqlResult { - let utc_dt = try!(DateTime::::column_result(value)); + fn column_result(value: ValueRef<'_>) -> FromSqlResult { + let utc_dt = DateTime::::column_result(value)?; Ok(utc_dt.with_timezone(&Local)) } } @@ -132,7 +132,7 @@ mod test { use super::chrono::{ DateTime, Duration, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc, }; - use {Connection, NO_PARAMS}; + use crate::{Connection, Result, NO_PARAMS}; fn checked_memory_handle() -> Connection { let db = Connection::open_in_memory().unwrap(); @@ -263,4 +263,21 @@ mod test { .unwrap(); assert_eq!(local, v); } + + #[test] + fn test_sqlite_functions() { + let db = checked_memory_handle(); + let result: Result = + db.query_row("SELECT CURRENT_TIME", NO_PARAMS, |r| r.get(0)); + assert!(result.is_ok()); + let result: Result = + db.query_row("SELECT CURRENT_DATE", NO_PARAMS, |r| r.get(0)); + assert!(result.is_ok()); + let result: Result = + db.query_row("SELECT CURRENT_TIMESTAMP", NO_PARAMS, |r| r.get(0)); + assert!(result.is_ok()); + let result: Result> = + db.query_row("SELECT CURRENT_TIMESTAMP", NO_PARAMS, |r| r.get(0)); + assert!(result.is_ok()); + } } diff --git a/src/types/from_sql.rs b/src/types/from_sql.rs index 8f7e132..c917b7a 100644 --- a/src/types/from_sql.rs +++ b/src/types/from_sql.rs @@ -19,11 +19,23 @@ pub enum FromSqlError { InvalidI128Size(usize), /// An error case available for implementors of the `FromSql` trait. - Other(Box), + Other(Box), +} + +impl PartialEq for FromSqlError { + fn eq(&self, other: &FromSqlError) -> bool { + match (self, other) { + (FromSqlError::InvalidType, FromSqlError::InvalidType) => true, + (FromSqlError::OutOfRange(n1), FromSqlError::OutOfRange(n2)) => n1 == n2, + #[cfg(feature = "i128_blob")] + (FromSqlError::InvalidI128Size(s1), FromSqlError::InvalidI128Size(s2)) => s1 == s2, + (_, _) => false, + } + } } impl fmt::Display for FromSqlError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { FromSqlError::InvalidType => write!(f, "Invalid type"), FromSqlError::OutOfRange(i) => write!(f, "Value {} out of range", i), @@ -47,8 +59,9 @@ impl Error for FromSqlError { } } - #[cfg_attr(feature = "clippy", allow(match_same_arms))] - fn cause(&self) -> Option<&Error> { + #[allow(clippy::match_same_arms)] + #[allow(deprecated)] + fn cause(&self) -> Option<&dyn Error> { match *self { FromSqlError::Other(ref err) => err.cause(), FromSqlError::InvalidType | FromSqlError::OutOfRange(_) => None, @@ -73,11 +86,11 @@ pub type FromSqlResult = Result; /// fetching values as i64 and then doing the interpretation themselves or by /// defining a newtype and implementing `FromSql`/`ToSql` for it. pub trait FromSql: Sized { - fn column_result(value: ValueRef) -> FromSqlResult; + fn column_result(value: ValueRef<'_>) -> FromSqlResult; } impl FromSql for isize { - fn column_result(value: ValueRef) -> FromSqlResult { + fn column_result(value: ValueRef<'_>) -> FromSqlResult { i64::column_result(value).and_then(|i| { if i < isize::min_value() as i64 || i > isize::max_value() as i64 { Err(FromSqlError::OutOfRange(i)) @@ -91,7 +104,7 @@ impl FromSql for isize { macro_rules! from_sql_integral( ($t:ident) => ( impl FromSql for $t { - fn column_result(value: ValueRef) -> FromSqlResult { + fn column_result(value: ValueRef<'_>) -> FromSqlResult { i64::column_result(value).and_then(|i| { if i < i64::from($t::min_value()) || i > i64::from($t::max_value()) { Err(FromSqlError::OutOfRange(i)) @@ -112,13 +125,13 @@ from_sql_integral!(u16); from_sql_integral!(u32); impl FromSql for i64 { - fn column_result(value: ValueRef) -> FromSqlResult { + fn column_result(value: ValueRef<'_>) -> FromSqlResult { value.as_i64() } } impl FromSql for f64 { - fn column_result(value: ValueRef) -> FromSqlResult { + fn column_result(value: ValueRef<'_>) -> FromSqlResult { match value { ValueRef::Integer(i) => Ok(i as f64), ValueRef::Real(f) => Ok(f), @@ -128,7 +141,7 @@ impl FromSql for f64 { } impl FromSql for bool { - fn column_result(value: ValueRef) -> FromSqlResult { + fn column_result(value: ValueRef<'_>) -> FromSqlResult { i64::column_result(value).map(|i| match i { 0 => false, _ => true, @@ -137,20 +150,20 @@ impl FromSql for bool { } impl FromSql for String { - fn column_result(value: ValueRef) -> FromSqlResult { + fn column_result(value: ValueRef<'_>) -> FromSqlResult { value.as_str().map(|s| s.to_string()) } } impl FromSql for Vec { - fn column_result(value: ValueRef) -> FromSqlResult { + fn column_result(value: ValueRef<'_>) -> FromSqlResult { value.as_blob().map(|b| b.to_vec()) } } #[cfg(feature = "i128_blob")] impl FromSql for i128 { - fn column_result(value: ValueRef) -> FromSqlResult { + fn column_result(value: ValueRef<'_>) -> FromSqlResult { use byteorder::{BigEndian, ByteOrder}; value.as_blob().and_then(|bytes| { @@ -164,7 +177,7 @@ impl FromSql for i128 { } impl FromSql for Option { - fn column_result(value: ValueRef) -> FromSqlResult { + fn column_result(value: ValueRef<'_>) -> FromSqlResult { match value { ValueRef::Null => Ok(None), _ => FromSql::column_result(value).map(Some), @@ -173,7 +186,7 @@ impl FromSql for Option { } impl FromSql for Value { - fn column_result(value: ValueRef) -> FromSqlResult { + fn column_result(value: ValueRef<'_>) -> FromSqlResult { Ok(value.into()) } } @@ -181,7 +194,7 @@ impl FromSql for Value { #[cfg(test)] mod test { use super::FromSql; - use {Connection, Error}; + use crate::{Connection, Error}; fn checked_memory_handle() -> Connection { Connection::open_in_memory().unwrap() @@ -219,11 +232,11 @@ mod test { check_ranges::(&db, &[-32769, 32768], &[-32768, -1, 0, 1, 32767]); check_ranges::( &db, - &[-2147483649, 2147483648], - &[-2147483648, -1, 0, 1, 2147483647], + &[-2_147_483_649, 2_147_483_648], + &[-2_147_483_648, -1, 0, 1, 2_147_483_647], ); check_ranges::(&db, &[-2, -1, 256], &[0, 1, 255]); check_ranges::(&db, &[-2, -1, 65536], &[0, 1, 65535]); - check_ranges::(&db, &[-2, -1, 4294967296], &[0, 1, 4294967295]); + check_ranges::(&db, &[-2, -1, 4_294_967_296], &[0, 1, 4_294_967_295]); } } diff --git a/src/types/mod.rs b/src/types/mod.rs index 009f865..508b273 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -19,9 +19,6 @@ //! store timespecs as `f64`s: //! //! ```rust -//! extern crate rusqlite; -//! extern crate time; -//! //! use rusqlite::types::{FromSql, FromSqlResult, ToSql, ToSqlOutput, ValueRef}; //! use rusqlite::Result; //! @@ -77,7 +74,6 @@ mod value_ref; /// ## Example /// /// ```rust,no_run -/// # extern crate rusqlite; /// # use rusqlite::{Connection, Result}; /// # use rusqlite::types::{Null}; /// fn main() {} @@ -98,7 +94,7 @@ pub enum Type { } impl fmt::Display for Type { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { Type::Null => write!(f, "Null"), Type::Integer => write!(f, "Integer"), @@ -111,12 +107,12 @@ impl fmt::Display for Type { #[cfg(test)] mod test { - extern crate time; + use time; use super::Value; + use crate::{Connection, Error, NO_PARAMS}; use std::f64::EPSILON; use std::os::raw::{c_double, c_int}; - use {Connection, Error, NO_PARAMS}; fn checked_memory_handle() -> Connection { let db = Connection::open_in_memory().unwrap(); @@ -227,6 +223,7 @@ mod test { } #[test] + #[allow(clippy::cyclomatic_complexity)] fn test_mismatched_types() { fn is_invalid_column_type(err: Error) -> bool { match err { diff --git a/src/types/serde_json.rs b/src/types/serde_json.rs index dc3fc26..54275dd 100644 --- a/src/types/serde_json.rs +++ b/src/types/serde_json.rs @@ -1,21 +1,21 @@ //! `ToSql` and `FromSql` implementation for JSON `Value`. -extern crate serde_json; +use serde_json; use self::serde_json::Value; -use types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef}; -use Result; +use crate::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef}; +use crate::Result; /// Serialize JSON `Value` to text. impl ToSql for Value { - fn to_sql(&self) -> Result { + fn to_sql(&self) -> Result> { Ok(ToSqlOutput::from(serde_json::to_string(self).unwrap())) } } /// Deserialize text/blob to JSON `Value`. impl FromSql for Value { - fn column_result(value: ValueRef) -> FromSqlResult { + fn column_result(value: ValueRef<'_>) -> FromSqlResult { match value { ValueRef::Text(s) => serde_json::from_str(s), ValueRef::Blob(b) => serde_json::from_slice(b), @@ -28,8 +28,8 @@ impl FromSql for Value { #[cfg(test)] mod test { use super::serde_json; - use types::ToSql; - use {Connection, NO_PARAMS}; + use crate::types::ToSql; + use crate::{Connection, NO_PARAMS}; fn checked_memory_handle() -> Connection { let db = Connection::open_in_memory().unwrap(); @@ -46,7 +46,7 @@ mod test { let data: serde_json::Value = serde_json::from_str(json).unwrap(); db.execute( "INSERT INTO foo (t, b) VALUES (?, ?)", - &[&data as &ToSql, &json.as_bytes()], + &[&data as &dyn ToSql, &json.as_bytes()], ) .unwrap(); diff --git a/src/types/time.rs b/src/types/time.rs index 8a5f171..ed45a89 100644 --- a/src/types/time.rs +++ b/src/types/time.rs @@ -1,13 +1,14 @@ -extern crate time; +use time; -use types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef}; -use Result; +use crate::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef}; +use crate::Result; +const CURRENT_TIMESTAMP_FMT: &str = "%Y-%m-%d %H:%M:%S"; const SQLITE_DATETIME_FMT: &str = "%Y-%m-%dT%H:%M:%S.%fZ"; const SQLITE_DATETIME_FMT_LEGACY: &str = "%Y-%m-%d %H:%M:%S:%f %Z"; impl ToSql for time::Timespec { - fn to_sql(&self) -> Result { + fn to_sql(&self) -> Result> { let time_string = time::at_utc(*self) .strftime(SQLITE_DATETIME_FMT) .unwrap() @@ -17,14 +18,17 @@ impl ToSql for time::Timespec { } impl FromSql for time::Timespec { - fn column_result(value: ValueRef) -> FromSqlResult { + fn column_result(value: ValueRef<'_>) -> FromSqlResult { value .as_str() .and_then(|s| { - time::strptime(s, SQLITE_DATETIME_FMT).or_else(|err| { - time::strptime(s, SQLITE_DATETIME_FMT_LEGACY) - .or_else(|_| Err(FromSqlError::Other(Box::new(err)))) - }) + match s.len() { + 19 => time::strptime(s, CURRENT_TIMESTAMP_FMT), + _ => time::strptime(s, SQLITE_DATETIME_FMT).or_else(|err| { + time::strptime(s, SQLITE_DATETIME_FMT_LEGACY).or_else(|_| Err(err)) + }), + } + .or_else(|err| Err(FromSqlError::Other(Box::new(err)))) }) .map(|tm| tm.to_timespec()) } @@ -33,7 +37,7 @@ impl FromSql for time::Timespec { #[cfg(test)] mod test { use super::time; - use {Connection, NO_PARAMS}; + use crate::{Connection, Result, NO_PARAMS}; fn checked_memory_handle() -> Connection { let db = Connection::open_in_memory().unwrap(); @@ -50,10 +54,10 @@ mod test { ts_vec.push(time::Timespec::new(10_000, 0)); //January 1, 1970 2:46:40 AM ts_vec.push(time::Timespec::new(10_000, 1000)); //January 1, 1970 2:46:40 AM (and one microsecond) - ts_vec.push(time::Timespec::new(1500391124, 1_000_000)); //July 18, 2017 - ts_vec.push(time::Timespec::new(2000000000, 2_000_000)); //May 18, 2033 - ts_vec.push(time::Timespec::new(3000000000, 999_999_999)); //January 24, 2065 - ts_vec.push(time::Timespec::new(10000000000, 0)); //November 20, 2286 + ts_vec.push(time::Timespec::new(1_500_391_124, 1_000_000)); //July 18, 2017 + ts_vec.push(time::Timespec::new(2_000_000_000, 2_000_000)); //May 18, 2033 + ts_vec.push(time::Timespec::new(3_000_000_000, 999_999_999)); //January 24, 2065 + ts_vec.push(time::Timespec::new(10_000_000_000, 0)); //November 20, 2286 for ts in ts_vec { db.execute("INSERT INTO foo(t) VALUES (?)", &[&ts]).unwrap(); @@ -67,4 +71,12 @@ mod test { assert_eq!(from, ts); } } + + #[test] + fn test_sqlite_functions() { + let db = checked_memory_handle(); + let result: Result = + db.query_row("SELECT CURRENT_TIMESTAMP", NO_PARAMS, |r| r.get(0)); + assert!(result.is_ok()); + } } diff --git a/src/types/to_sql.rs b/src/types/to_sql.rs index 2793ba1..c2ae0cd 100644 --- a/src/types/to_sql.rs +++ b/src/types/to_sql.rs @@ -1,8 +1,8 @@ use super::{Null, Value, ValueRef}; -use std::borrow::Cow; #[cfg(feature = "array")] -use vtab::array::Array; -use Result; +use crate::vtab::array::Array; +use crate::Result; +use std::borrow::Cow; /// `ToSqlOutput` represents the possible output types for implementors of the /// `ToSql` trait. @@ -66,7 +66,7 @@ from_value!(Vec); from_value!(i128); impl<'a> ToSql for ToSqlOutput<'a> { - fn to_sql(&self) -> Result { + fn to_sql(&self) -> Result> { Ok(match *self { ToSqlOutput::Borrowed(v) => ToSqlOutput::Borrowed(v), ToSqlOutput::Owned(ref v) => ToSqlOutput::Borrowed(ValueRef::from(v)), @@ -81,7 +81,7 @@ impl<'a> ToSql for ToSqlOutput<'a> { /// A trait for types that can be converted into SQLite values. pub trait ToSql { - fn to_sql(&self) -> Result; + fn to_sql(&self) -> Result>; } // We should be able to use a generic impl like this: @@ -99,7 +99,7 @@ pub trait ToSql { macro_rules! to_sql_self( ($t:ty) => ( impl ToSql for $t { - fn to_sql(&self) -> Result { + fn to_sql(&self) -> Result> { Ok(ToSqlOutput::from(*self)) } } @@ -125,43 +125,43 @@ impl<'a, T: ?Sized> ToSql for &'a T where T: ToSql, { - fn to_sql(&self) -> Result { + fn to_sql(&self) -> Result> { (*self).to_sql() } } impl ToSql for String { - fn to_sql(&self) -> Result { + fn to_sql(&self) -> Result> { Ok(ToSqlOutput::from(self.as_str())) } } impl ToSql for str { - fn to_sql(&self) -> Result { + fn to_sql(&self) -> Result> { Ok(ToSqlOutput::from(self)) } } impl ToSql for Vec { - fn to_sql(&self) -> Result { + fn to_sql(&self) -> Result> { Ok(ToSqlOutput::from(self.as_slice())) } } impl ToSql for [u8] { - fn to_sql(&self) -> Result { + fn to_sql(&self) -> Result> { Ok(ToSqlOutput::from(self)) } } impl ToSql for Value { - fn to_sql(&self) -> Result { + fn to_sql(&self) -> Result> { Ok(ToSqlOutput::from(self)) } } impl ToSql for Option { - fn to_sql(&self) -> Result { + fn to_sql(&self) -> Result> { match *self { None => Ok(ToSqlOutput::from(Null)), Some(ref t) => t.to_sql(), @@ -170,7 +170,7 @@ impl ToSql for Option { } impl<'a> ToSql for Cow<'a, str> { - fn to_sql(&self) -> Result { + fn to_sql(&self) -> Result> { Ok(ToSqlOutput::from(self.as_ref())) } } @@ -207,8 +207,8 @@ mod test { #[cfg(feature = "i128_blob")] #[test] fn test_i128() { + use crate::{Connection, NO_PARAMS}; use std::i128; - use {Connection, NO_PARAMS}; let db = Connection::open_in_memory().unwrap(); db.execute_batch("CREATE TABLE foo (i128 BLOB, desc TEXT)") .unwrap(); diff --git a/src/types/value_ref.rs b/src/types/value_ref.rs index cd99bb2..a701256 100644 --- a/src/types/value_ref.rs +++ b/src/types/value_ref.rs @@ -1,5 +1,5 @@ use super::{Type, Value}; -use types::{FromSqlError, FromSqlResult}; +use crate::types::{FromSqlError, FromSqlResult}; /// A non-owning [dynamic type value](http://sqlite.org/datatype3.html). Typically the /// memory backing this value is owned by SQLite. @@ -70,7 +70,7 @@ impl<'a> ValueRef<'a> { } impl<'a> From> for Value { - fn from(borrowed: ValueRef) -> Value { + fn from(borrowed: ValueRef<'_>) -> Value { match borrowed { ValueRef::Null => Value::Null, ValueRef::Integer(i) => Value::Integer(i), @@ -82,13 +82,13 @@ impl<'a> From> for Value { } impl<'a> From<&'a str> for ValueRef<'a> { - fn from(s: &str) -> ValueRef { + fn from(s: &str) -> ValueRef<'_> { ValueRef::Text(s) } } impl<'a> From<&'a [u8]> for ValueRef<'a> { - fn from(s: &[u8]) -> ValueRef { + fn from(s: &[u8]) -> ValueRef<'_> { ValueRef::Blob(s) } } @@ -104,3 +104,56 @@ impl<'a> From<&'a Value> for ValueRef<'a> { } } } + +#[cfg(any(feature = "functions", feature = "session", feature = "vtab"))] +impl<'a> ValueRef<'a> { + pub(crate) unsafe fn from_value(value: *mut crate::ffi::sqlite3_value) -> ValueRef<'a> { + use crate::ffi; + use std::ffi::CStr; + use std::os::raw::c_char; + use std::slice::from_raw_parts; + + match ffi::sqlite3_value_type(value) { + ffi::SQLITE_NULL => ValueRef::Null, + ffi::SQLITE_INTEGER => ValueRef::Integer(ffi::sqlite3_value_int64(value)), + ffi::SQLITE_FLOAT => ValueRef::Real(ffi::sqlite3_value_double(value)), + ffi::SQLITE_TEXT => { + let text = ffi::sqlite3_value_text(value); + assert!( + !text.is_null(), + "unexpected SQLITE_TEXT value type with NULL data" + ); + let s = CStr::from_ptr(text as *const c_char); + + // sqlite3_value_text returns UTF8 data, so our unwrap here should be fine. + let s = s + .to_str() + .expect("sqlite3_value_text returned invalid UTF-8"); + ValueRef::Text(s) + } + ffi::SQLITE_BLOB => { + let (blob, len) = ( + ffi::sqlite3_value_blob(value), + ffi::sqlite3_value_bytes(value), + ); + + assert!( + len >= 0, + "unexpected negative return from sqlite3_value_bytes" + ); + if len > 0 { + assert!( + !blob.is_null(), + "unexpected SQLITE_BLOB value type with NULL data" + ); + ValueRef::Blob(from_raw_parts(blob as *const u8, len as usize)) + } else { + // The return value from sqlite3_value_blob() for a zero-length BLOB + // is a NULL pointer. + ValueRef::Blob(&[]) + } + } + _ => unreachable!("sqlite3_value_type returned invalid value"), + } + } +} diff --git a/src/unlock_notify.rs b/src/unlock_notify.rs index c4f2f1f..7295322 100644 --- a/src/unlock_notify.rs +++ b/src/unlock_notify.rs @@ -4,9 +4,11 @@ use std::os::raw::c_int; #[cfg(feature = "unlock_notify")] use std::os::raw::c_void; #[cfg(feature = "unlock_notify")] +use std::panic::catch_unwind; +#[cfg(feature = "unlock_notify")] use std::sync::{Condvar, Mutex}; -use ffi; +use crate::ffi; #[cfg(feature = "unlock_notify")] struct UnlockNotification { @@ -42,8 +44,10 @@ unsafe extern "C" fn unlock_notify_cb(ap_arg: *mut *mut c_void, n_arg: c_int) { use std::slice::from_raw_parts; let args = from_raw_parts(ap_arg, n_arg as usize); for arg in args { - let un: &mut UnlockNotification = &mut *(*arg as *mut UnlockNotification); - un.fired(); + let _ = catch_unwind(|| { + let un: &mut UnlockNotification = &mut *(*arg as *mut UnlockNotification); + un.fired() + }); } } @@ -99,10 +103,10 @@ pub fn wait_for_unlock_notify(_db: *mut ffi::sqlite3) -> c_int { #[cfg(feature = "unlock_notify")] #[cfg(test)] mod test { + use crate::{Connection, OpenFlags, Result, Transaction, TransactionBehavior, NO_PARAMS}; use std::sync::mpsc::sync_channel; use std::thread; use std::time; - use {Connection, OpenFlags, Result, Transaction, TransactionBehavior, NO_PARAMS}; #[test] fn test_unlock_notify() { diff --git a/src/version.rs b/src/version.rs index 39c7d6b..215900b 100644 --- a/src/version.rs +++ b/src/version.rs @@ -1,4 +1,4 @@ -use ffi; +use crate::ffi; use std::ffi::CStr; /// Returns the SQLite version as an integer; e.g., `3016002` for version diff --git a/src/vtab/array.rs b/src/vtab/array.rs index 23d3aa1..1b4bd46 100644 --- a/src/vtab/array.rs +++ b/src/vtab/array.rs @@ -5,13 +5,13 @@ use std::default::Default; use std::os::raw::{c_char, c_int, c_void}; use std::rc::Rc; -use ffi; -use types::{ToSql, ToSqlOutput, Value}; -use vtab::{ +use crate::ffi; +use crate::types::{ToSql, ToSqlOutput, Value}; +use crate::vtab::{ eponymous_only_module, Context, IndexConstraintOp, IndexInfo, Module, VTab, VTabConnection, VTabCursor, Values, }; -use {Connection, Result}; +use crate::{Connection, Result}; // http://sqlite.org/bindptr.html @@ -24,7 +24,7 @@ pub(crate) unsafe extern "C" fn free_array(p: *mut c_void) { pub type Array = Rc>; impl ToSql for Array { - fn to_sql(&self) -> Result { + fn to_sql(&self) -> Result> { Ok(ToSqlOutput::Array(self.clone())) } } @@ -129,9 +129,9 @@ impl ArrayTabCursor { } } impl VTabCursor for ArrayTabCursor { - fn filter(&mut self, idx_num: c_int, _idx_str: Option<&str>, args: &Values) -> Result<()> { + fn filter(&mut self, idx_num: c_int, _idx_str: Option<&str>, args: &Values<'_>) -> Result<()> { if idx_num > 0 { - self.ptr = try!(args.get_array(0)); + self.ptr = args.get_array(0)?; } else { self.ptr = None; } @@ -169,10 +169,10 @@ impl VTabCursor for ArrayTabCursor { #[cfg(test)] mod test { + use crate::types::Value; + use crate::vtab::array; + use crate::Connection; use std::rc::Rc; - use types::Value; - use vtab::array; - use Connection; #[test] fn test_array_module() { @@ -180,7 +180,7 @@ mod test { array::load_module(&db).unwrap(); let v = vec![1i64, 2, 3, 4]; - let values = v.into_iter().map(|i| Value::from(i)).collect(); + let values = v.into_iter().map(Value::from).collect(); let ptr = Rc::new(values); { let mut stmt = db.prepare("SELECT value from rarray(?);").unwrap(); diff --git a/src/vtab/csvtab.rs b/src/vtab/csvtab.rs index 041e57e..67659f3 100644 --- a/src/vtab/csvtab.rs +++ b/src/vtab/csvtab.rs @@ -1,20 +1,20 @@ //! CSV Virtual Table. //! //! Port of [csv](http://www.sqlite.org/cgi/src/finfo?name=ext/misc/csv.c) C extension. -extern crate csv; +use csv; use std::fs::File; use std::os::raw::c_int; use std::path::Path; use std::result; use std::str; -use ffi; -use types::Null; -use vtab::{ +use crate::ffi; +use crate::types::Null; +use crate::vtab::{ dequote, escape_double_quote, parse_boolean, read_only_module, Context, CreateVTab, IndexInfo, Module, VTab, VTabConnection, VTabCursor, Values, }; -use {Connection, Error, Result}; +use crate::{Connection, Error, Result}; /// Register the "csv" module. /// ```sql @@ -60,7 +60,7 @@ impl CSVTab { } fn parameter(c_slice: &[u8]) -> Result<(&str, &str)> { - let arg = try!(str::from_utf8(c_slice)).trim(); + let arg = str::from_utf8(c_slice)?.trim(); let mut split = arg.split('='); if let Some(key) = split.next() { if let Some(value) = split.next() { @@ -107,7 +107,7 @@ impl VTab for CSVTab { let args = &args[3..]; for c_slice in args { - let (param, value) = try!(CSVTab::parameter(c_slice)); + let (param, value) = CSVTab::parameter(c_slice)?; match param { "filename" => { if !Path::new(value).exists() { @@ -189,10 +189,10 @@ impl VTab for CSVTab { let mut cols: Vec = Vec::new(); if vtab.has_headers || (n_col.is_none() && schema.is_none()) { - let mut reader = try!(vtab.reader()); + let mut reader = vtab.reader()?; if vtab.has_headers { { - let headers = try!(reader.headers()); + let headers = reader.headers()?; // headers ignored if cols is not empty if n_col.is_none() && schema.is_none() { cols = headers @@ -204,7 +204,7 @@ impl VTab for CSVTab { vtab.offset_first_row = reader.position().clone(); } else { let mut record = csv::ByteRecord::new(); - if try!(reader.read_byte_record(&mut record)) { + if reader.read_byte_record(&mut record)? { for (i, _) in record.iter().enumerate() { cols.push(format!("c{}", i)); } @@ -245,7 +245,7 @@ impl VTab for CSVTab { } fn open(&self) -> Result { - Ok(CSVTabCursor::new(try!(self.reader()))) + Ok(CSVTabCursor::new(self.reader()?)) } } @@ -285,10 +285,15 @@ impl CSVTabCursor { impl VTabCursor for CSVTabCursor { // Only a full table scan is supported. So `filter` simply rewinds to // the beginning. - fn filter(&mut self, _idx_num: c_int, _idx_str: Option<&str>, _args: &Values) -> Result<()> { + fn filter( + &mut self, + _idx_num: c_int, + _idx_str: Option<&str>, + _args: &Values<'_>, + ) -> Result<()> { { let offset_first_row = self.vtab().offset_first_row.clone(); - try!(self.reader.seek(offset_first_row)); + self.reader.seek(offset_first_row)?; } self.row_number = 0; self.next() @@ -301,7 +306,7 @@ impl VTabCursor for CSVTabCursor { return Ok(()); } - self.eof = !try!(self.reader.read_record(&mut self.cols)); + self.eof = !self.reader.read_record(&mut self.cols)?; } self.row_number += 1; @@ -340,8 +345,8 @@ impl From for Error { #[cfg(test)] mod test { - use vtab::csvtab; - use {Connection, Result, NO_PARAMS}; + use crate::vtab::csvtab; + use crate::{Connection, Result, NO_PARAMS}; #[test] fn test_csv_module() { @@ -361,7 +366,7 @@ mod test { .query_map(NO_PARAMS, |row| row.get::<_, i32>(0)) .unwrap() .collect(); - let sum = ids.unwrap().iter().fold(0, |acc, &id| acc + id); + let sum = ids.unwrap().iter().sum::(); assert_eq!(sum, 15); } db.execute_batch("DROP TABLE vtab").unwrap(); diff --git a/src/vtab/mod.rs b/src/vtab/mod.rs index a219198..e58616f 100644 --- a/src/vtab/mod.rs +++ b/src/vtab/mod.rs @@ -17,12 +17,12 @@ use std::os::raw::{c_char, c_int, c_void}; use std::ptr; use std::slice; -use context::set_result; -use error::error_from_sqlite_code; -use ffi; -pub use ffi::{sqlite3_vtab, sqlite3_vtab_cursor}; -use types::{FromSql, FromSqlError, ToSql, ValueRef}; -use {str_to_cstring, Connection, Error, InnerConnection, Result}; +use crate::context::set_result; +use crate::error::error_from_sqlite_code; +use crate::ffi; +pub use crate::ffi::{sqlite3_vtab, sqlite3_vtab_cursor}; +use crate::types::{FromSql, FromSqlError, ToSql, ValueRef}; +use crate::{str_to_cstring, Connection, Error, InnerConnection, Result}; // let conn: Connection = ...; // let mod: Module = ...; // VTab builder @@ -99,6 +99,8 @@ pub fn read_only_module(version: c_int) -> Module { xSavepoint: None, xRelease: None, xRollbackTo: None, + #[cfg(any(feature = "bundled", feature = "vtab_v3"))] + xShadowName: None, }; Module { base: ffi_module, @@ -137,6 +139,8 @@ pub fn eponymous_only_module(version: c_int) -> Module { xSavepoint: None, xRelease: None, xRollbackTo: None, + #[cfg(any(feature = "bundled", feature = "vtab_v3"))] + xShadowName: None, }; Module { base: ffi_module, @@ -250,7 +254,7 @@ pub struct IndexInfo(*mut ffi::sqlite3_index_info); impl IndexInfo { /// Record WHERE clause constraints. - pub fn constraints(&self) -> IndexConstraintIter { + pub fn constraints(&self) -> IndexConstraintIter<'_> { let constraints = unsafe { slice::from_raw_parts((*self.0).aConstraint, (*self.0).nConstraint as usize) }; IndexConstraintIter { @@ -259,7 +263,7 @@ impl IndexInfo { } /// Information about the ORDER BY clause. - pub fn order_bys(&self) -> OrderByIter { + pub fn order_bys(&self) -> OrderByIter<'_> { let order_bys = unsafe { slice::from_raw_parts((*self.0).aOrderBy, (*self.0).nOrderBy as usize) }; OrderByIter { @@ -272,7 +276,7 @@ impl IndexInfo { unsafe { (*self.0).nOrderBy as usize } } - pub fn constraint_usage(&mut self, constraint_idx: usize) -> IndexConstraintUsage { + pub fn constraint_usage(&mut self, constraint_idx: usize) -> IndexConstraintUsage<'_> { let constraint_usages = unsafe { slice::from_raw_parts_mut((*self.0).aConstraintUsage, (*self.0).nConstraint as usize) }; @@ -412,7 +416,7 @@ impl<'a> OrderBy<'a> { pub trait VTabCursor: Sized { /// Begin a search of a virtual table. /// (See [SQLite doc](https://sqlite.org/vtab.html#the_xfilter_method)) - fn filter(&mut self, idx_num: c_int, idx_str: Option<&str>, args: &Values) -> Result<()>; + fn filter(&mut self, idx_num: c_int, idx_str: Option<&str>, args: &Values<'_>) -> Result<()>; /// Advance cursor to the next row of a result set initiated by `filter`. /// (See [SQLite doc](https://sqlite.org/vtab.html#the_xnext_method)) fn next(&mut self) -> Result<()>; @@ -467,6 +471,8 @@ impl<'a> Values<'a> { Error::FromSqlConversionFailure(idx, value.data_type(), err) } FromSqlError::OutOfRange(i) => Error::IntegralValueOutOfRange(idx, i), + #[cfg(feature = "i128_blob")] + FromSqlError::InvalidI128Size(_) => Error::InvalidColumnType(idx, value.data_type()), }) } @@ -474,7 +480,7 @@ impl<'a> Values<'a> { // So it seems not possible to enhance `ValueRef::from_value`. #[cfg(feature = "array")] pub(crate) fn get_array(&self, idx: usize) -> Result> { - use types::Value; + use crate::types::Value; let arg = self.args[idx]; let ptr = unsafe { ffi::sqlite3_value_pointer(arg, array::ARRAY_TYPE) }; if ptr.is_null() { @@ -489,7 +495,7 @@ impl<'a> Values<'a> { } } - pub fn iter(&self) -> ValueIter { + pub fn iter(&self) -> ValueIter<'_> { ValueIter { iter: self.args.iter(), } @@ -544,7 +550,7 @@ impl InnerConnection { module: &Module, aux: Option, ) -> Result<()> { - let c_name = try!(str_to_cstring(module_name)); + let c_name = str_to_cstring(module_name)?; let r = match aux { Some(aux) => { let boxed_aux: *mut T::Aux = Box::into_raw(Box::new(aux)); @@ -573,7 +579,7 @@ impl InnerConnection { } /// Escape double-quote (`"`) character occurences by doubling them (`""`). -pub fn escape_double_quote(identifier: &str) -> Cow { +pub fn escape_double_quote(identifier: &str) -> Cow<'_, str> { if identifier.contains('"') { // escape quote by doubling them Owned(identifier.replace("\"", "\"\"")) diff --git a/src/vtab/series.rs b/src/vtab/series.rs index 88fe393..722ed4b 100644 --- a/src/vtab/series.rs +++ b/src/vtab/series.rs @@ -4,13 +4,13 @@ use std::default::Default; use std::os::raw::c_int; -use ffi; -use types::Type; -use vtab::{ +use crate::ffi; +use crate::types::Type; +use crate::vtab::{ eponymous_only_module, Context, IndexConstraintOp, IndexInfo, Module, VTab, VTabConnection, VTabCursor, Values, }; -use {Connection, Result}; +use crate::{Connection, Result}; /// Register the "generate_series" module. pub fn load_module(conn: &Connection) -> Result<()> { @@ -184,23 +184,23 @@ impl SeriesTabCursor { } } impl VTabCursor for SeriesTabCursor { - fn filter(&mut self, idx_num: c_int, _idx_str: Option<&str>, args: &Values) -> Result<()> { + fn filter(&mut self, idx_num: c_int, _idx_str: Option<&str>, args: &Values<'_>) -> Result<()> { let idx_num = QueryPlanFlags::from_bits_truncate(idx_num); let mut i = 0; if idx_num.contains(QueryPlanFlags::START) { - self.min_value = try!(args.get(i)); + self.min_value = args.get(i)?; i += 1; } else { self.min_value = 0; } if idx_num.contains(QueryPlanFlags::STOP) { - self.max_value = try!(args.get(i)); + self.max_value = args.get(i)?; i += 1; } else { self.max_value = 0xffff_ffff; } if idx_num.contains(QueryPlanFlags::STEP) { - self.step = try!(args.get(i)); + self.step = args.get(i)?; if self.step < 1 { self.step = 1; } @@ -263,14 +263,14 @@ impl VTabCursor for SeriesTabCursor { #[cfg(test)] mod test { - use ffi; - use vtab::series; - use {Connection, NO_PARAMS}; + use crate::ffi; + use crate::vtab::series; + use crate::{Connection, NO_PARAMS}; #[test] fn test_series_module() { let version = unsafe { ffi::sqlite3_libversion_number() }; - if version < 3008012 { + if version < 3_008_012 { return; } diff --git a/tests/config_log.rs b/tests/config_log.rs index f4bdd5d..d51acf5 100644 --- a/tests/config_log.rs +++ b/tests/config_log.rs @@ -5,7 +5,6 @@ #[cfg(feature = "trace")] #[macro_use] extern crate lazy_static; -extern crate rusqlite; #[cfg(feature = "trace")] fn main() { diff --git a/tests/deny_single_threaded_sqlite_config.rs b/tests/deny_single_threaded_sqlite_config.rs index 55b6355..f6afdd5 100644 --- a/tests/deny_single_threaded_sqlite_config.rs +++ b/tests/deny_single_threaded_sqlite_config.rs @@ -1,9 +1,7 @@ //! Ensure we reject connections when SQLite is in single-threaded mode, as it //! would violate safety if multiple Rust threads tried to use connections. -extern crate libsqlite3_sys as ffi; -extern crate rusqlite; - +use rusqlite::ffi; use rusqlite::Connection; #[test] diff --git a/tests/vtab.rs b/tests/vtab.rs index 30d4ee7..55069b7 100644 --- a/tests/vtab.rs +++ b/tests/vtab.rs @@ -1,8 +1,5 @@ //! Ensure Virtual tables can be declared outside `rusqlite` crate. -#[cfg(feature = "vtab")] -extern crate rusqlite; - #[cfg(feature = "vtab")] #[test] fn test_dummy_module() { @@ -61,7 +58,7 @@ fn test_dummy_module() { &mut self, _idx_num: c_int, _idx_str: Option<&str>, - _args: &Values, + _args: &Values<'_>, ) -> Result<()> { self.row_id = 1; Ok(()) @@ -91,14 +88,14 @@ fn test_dummy_module() { .unwrap(); let version = version_number(); - if version < 3008012 { + if version < 3_008_012 { return; } let mut s = db.prepare("SELECT * FROM dummy()").unwrap(); let dummy = s - .query_row(&[] as &[&ToSql], |row| row.get::<_, i32>(0)) + .query_row(&[] as &[&dyn ToSql], |row| row.get::<_, i32>(0)) .unwrap(); assert_eq!(1, dummy); }