├── .gitignore ├── .gitattributes ├── .rustfmt.toml ├── renovate.json ├── .zed └── settings.json ├── examples ├── test_memvfs.sql ├── memvfs.Dockerfile └── memvfs.rs ├── src ├── lib.rs ├── logger.rs ├── flags.rs ├── mock.rs └── vfs.rs ├── CHANGELOG.md ├── Cargo.toml ├── .github └── workflows │ └── rust.yml ├── README.md ├── Cargo.lock └── sqlite3 └── sqlite3ext.h /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | sqlite3.h linguist-vendored 2 | sqlite3ext.h linguist-vendored 3 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | # https://rust-lang.github.io/rustfmt 2 | max_width = 100 3 | struct_lit_width = 40 4 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": ["github>orbitinghail/renovate-config"] 4 | } 5 | -------------------------------------------------------------------------------- /.zed/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "lsp": { 3 | "rust-analyzer": { 4 | "initialization_options": { 5 | "cargo": { 6 | "features": "all" 7 | } 8 | } 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/test_memvfs.sql: -------------------------------------------------------------------------------- 1 | -- Load the memvfs extension and open a new connection using it 2 | -- Build the memvfs extension using the following command: 3 | -- cargo build --example memvfs --features dynamic 4 | 5 | -- uncomment to enable verbose logs 6 | -- .log stderr 7 | 8 | .load target/debug/examples/libmemvfs.so 9 | .open main.db 10 | .mode table 11 | .log stdout 12 | 13 | .databases 14 | .vfsinfo 15 | 16 | CREATE TABLE t1(a, b); 17 | INSERT INTO t1 VALUES(1, 2); 18 | INSERT INTO t1 VALUES(3, 4); 19 | SELECT * FROM t1; 20 | pragma hello_vfs=1234; 21 | 22 | select * from dbstat; 23 | 24 | vacuum; 25 | drop table t1; 26 | vacuum; 27 | 28 | select * from dbstat; 29 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | extern crate alloc; 3 | 4 | pub mod vars { 5 | include!(concat!(env!("OUT_DIR"), "/vars.rs")); 6 | } 7 | 8 | mod ffi { 9 | #![allow(non_camel_case_types)] 10 | #![allow(non_snake_case)] 11 | #![allow(unused)] 12 | #![allow(clippy::type_complexity)] 13 | include!(concat!(env!("OUT_DIR"), "/bindings.rs")); 14 | } 15 | 16 | mod mock; 17 | 18 | pub mod flags; 19 | pub mod logger; 20 | pub mod vfs; 21 | pub use ffi::sqlite3_api_routines; 22 | 23 | #[cfg(test)] 24 | mod tests { 25 | #[test] 26 | fn sanity() { 27 | // verify that we are linked against rusqlite in tests 28 | assert_eq!( 29 | unsafe { super::ffi::sqlite3_libversion_number() }, 30 | rusqlite::version_number() 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes will be documented in this file. 4 | 5 | ## 0.5.0 - 2025-11-26 6 | 7 | - BREAKING: remove register_logger method from Vfs 8 | - return SqliteLogger instance from register_dynamic/register_static 9 | 10 | ## 0.4.1 - 2025-06-19 11 | 12 | - expose SqliteApi in public API 13 | 14 | ## 0.4.0 - 2025-06-19 15 | 16 | - relax min SQLite version to 3.43.0 17 | 18 | ## 0.3.1 - 2025-06-16 19 | 20 | - dependency bump 21 | 22 | ## 0.3.0 - 2025-05-26 23 | 24 | - `register_dynamic` and `register_static` now require the VFS name to be passed in as a CString. 25 | 26 | ## 0.2.0 - 2025-05-19 27 | 28 | - `PragmaErr` now requires an explicit error code and external construction. 29 | 30 | ## 0.1.2 - 2025-04-09 31 | 32 | - updating dependencies 33 | 34 | ## 0.1.1 - 2025-04-09 35 | 36 | - bug: support cross-compilation to arm 37 | 38 | ## 0.1.0 - 2025-03-29 39 | 40 | - Initial release 41 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sqlite-plugin" 3 | version = "0.5.0" 4 | edition = "2024" 5 | authors = ["orbitinghail "] 6 | license = "MIT OR Apache-2.0" 7 | repository = "https://github.com/orbitinghail/sqlite-plugin" 8 | homepage = "https://github.com/orbitinghail/sqlite-plugin" 9 | rust-version = "1.85" 10 | 11 | description = "A Rust crate for building a custom SQLite virtual file system (VFS)." 12 | keywords = ["sqlite", "vfs", "extension"] 13 | 14 | [lints.clippy] 15 | disallowed_methods = "deny" 16 | uninlined-format-args = "warn" 17 | doc-markdown = "warn" 18 | map-unwrap-or = "warn" 19 | 20 | [dev-dependencies] 21 | rusqlite = { version = "=0.37.0", features = ["blob", "trace", "bundled"] } 22 | log = { version = "=0.4.28", features = ["std"] } 23 | parking_lot = "=0.12.5" 24 | 25 | [build-dependencies] 26 | bindgen = { version = "0.72", default-features = false } 27 | 28 | [features] 29 | default = ["static"] 30 | static = [] 31 | dynamic = [] 32 | 33 | [[example]] 34 | name = "memvfs" 35 | crate-type = ["cdylib"] 36 | required-features = ["dynamic"] 37 | -------------------------------------------------------------------------------- /src/logger.rs: -------------------------------------------------------------------------------- 1 | use alloc::ffi::CString; 2 | use core::ffi::{c_char, c_int}; 3 | 4 | use crate::vars; 5 | 6 | #[allow(non_snake_case)] 7 | type Sqlite3Log = unsafe extern "C" fn(iErrCode: c_int, arg2: *const c_char, ...); 8 | 9 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 10 | pub enum SqliteLogLevel { 11 | Error = 1, 12 | Warn, 13 | Notice, 14 | } 15 | 16 | impl SqliteLogLevel { 17 | fn into_err_code(self) -> c_int { 18 | match self { 19 | Self::Notice => vars::SQLITE_NOTICE, 20 | Self::Warn => vars::SQLITE_WARNING, 21 | Self::Error => vars::SQLITE_INTERNAL, 22 | } 23 | } 24 | } 25 | 26 | #[derive(Clone, Copy)] 27 | pub struct SqliteLogger { 28 | log: Sqlite3Log, 29 | } 30 | 31 | impl SqliteLogger { 32 | pub(crate) fn new(log: Sqlite3Log) -> Self { 33 | Self { log } 34 | } 35 | 36 | /// Log bytes directly to the `SQLite3` log handle. 37 | /// Note that `SQLite` silently truncates writes larger than 38 | /// roughly 230 bytes by default. It's recommended that you 39 | /// split your log messages by lines before calling this method. 40 | pub fn log(&self, level: SqliteLogLevel, msg: &str) { 41 | let code = level.into_err_code(); 42 | let z_format = CString::new(msg).unwrap(); 43 | unsafe { (self.log)(code, z_format.as_ptr()) } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | pull_request: 7 | branches: ["main"] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | permissions: 13 | contents: read 14 | checks: write 15 | 16 | jobs: 17 | build: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - name: system dependencies 21 | run: | 22 | sudo apt-get update 23 | sudo apt-get install -y clang libclang-dev llvm mold libncurses-dev build-essential 24 | 25 | - uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # v1 26 | with: 27 | toolchain: 1.85 28 | components: clippy, rustfmt 29 | 30 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 31 | with: 32 | token: ${{secrets.GITHUB_TOKEN}} 33 | 34 | - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2 35 | 36 | - name: Install Nextest 37 | run: curl -LsSf https://get.nexte.st/latest/linux | tar zxf - -C /usr/local/bin 38 | 39 | - name: Build 40 | run: cargo build --all-features 41 | 42 | - name: Test 43 | run: cargo nextest run --all-features 44 | 45 | - name: Test memvfs 46 | run: | 47 | cargo build --example memvfs --features dynamic 48 | cat examples/test_memvfs.sql | sqlite3 49 | 50 | - name: Clippy 51 | uses: auguwu/clippy-action@94a9ff2f6920180b89e5c03d121d0af04a9d3e03 # 1.4.0 52 | with: 53 | token: ${{secrets.GITHUB_TOKEN}} 54 | 55 | - name: Cargo fmt 56 | run: cargo fmt --check 57 | 58 | - name: Cargo doctests 59 | run: cargo test --doc 60 | -------------------------------------------------------------------------------- /examples/memvfs.Dockerfile: -------------------------------------------------------------------------------- 1 | # Start from the official Rust image 2 | FROM rust:1.85 3 | 4 | # Install essential build tools, clang/llvm, and SQLite dependencies 5 | RUN apt-get update && \ 6 | apt-get install -y \ 7 | clang libclang-dev llvm \ 8 | wget unzip build-essential tcl-dev zlib1g-dev && \ 9 | rm -rf /var/lib/apt/lists/* 10 | 11 | # Define SQLite version to install (3.45.3 as an example, which is > 3.44.0) 12 | # You can update these ARGs if a newer SQLite version is needed/preferred 13 | ARG SQLITE_YEAR=2025 14 | ARG SQLITE_FILENAME_VERSION=3490200 15 | ARG SQLITE_TARBALL_FILENAME=sqlite-autoconf-${SQLITE_FILENAME_VERSION}.tar.gz 16 | 17 | # Download, compile, and install SQLite from source 18 | RUN cd /tmp && \ 19 | wget "https://www.sqlite.org/${SQLITE_YEAR}/${SQLITE_TARBALL_FILENAME}" && \ 20 | tar xvfz "${SQLITE_TARBALL_FILENAME}" && \ 21 | cd "sqlite-autoconf-${SQLITE_FILENAME_VERSION}" && \ 22 | ./configure --prefix=/usr/local \ 23 | CFLAGS="-DSQLITE_ENABLE_COLUMN_METADATA=1 \ 24 | -DSQLITE_ENABLE_LOAD_EXTENSION=1 \ 25 | -DSQLITE_ENABLE_FTS5=1 \ 26 | -DSQLITE_ENABLE_DBSTAT_VTAB=1 \ 27 | -DSQLITE_ENABLE_NULL_TRIM=1 \ 28 | -DSQLITE_ENABLE_RTREE=1" && \ 29 | make -j$(nproc) && \ 30 | make install && \ 31 | # Update the linker cache to recognize the new SQLite library 32 | ldconfig && \ 33 | rm -rf /tmp/* 34 | 35 | # Set the working directory in the container 36 | WORKDIR /code 37 | 38 | COPY . . 39 | 40 | RUN cargo build --example memvfs --features dynamic 41 | 42 | CMD ["/bin/bash", "-c", "cat examples/test_memvfs.sql | sqlite3"] 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

SQLite Plugin

2 |

3 | docs.rs 4 |   5 | Build Status 6 |   7 | crates.io 8 |

9 | 10 | `sqlite-plugin` provides a streamlined and flexible way to implement SQLite virtual file systems (VFS) in Rust. Inspired by [sqlite-vfs], it offers a distinct design with key enhancements: 11 | 12 | - **Centralized Control**: The `Vfs` trait intercepts all file operations at the VFS level, rather than delegating them directly to file handles. This simplifies shared state management and enables more advanced behaviors. 13 | - **Custom Pragmas**: Easily define and handle custom SQLite pragmas to extend database functionality. 14 | - **Integrated Logging**: Seamlessly forward logs to SQLite’s built-in logging system for unified diagnostics. 15 | 16 | [sqlite-vfs]: https://github.com/rklaehn/sqlite-vfs 17 | 18 | ## License 19 | 20 | Licensed under either of 21 | 22 | - Apache License, Version 2.0 ([LICENSE-APACHE] or https://www.apache.org/licenses/LICENSE-2.0) 23 | - MIT license ([LICENSE-MIT] or https://opensource.org/licenses/MIT) 24 | 25 | at your option. 26 | 27 | [LICENSE-APACHE]: ./LICENSE-APACHE 28 | [LICENSE-MIT]: ./LICENSE-MIT 29 | 30 | ### Contribution 31 | 32 | Unless you explicitly state otherwise, any contribution intentionally submitted 33 | for inclusion in the work by you shall be dual licensed as above, without any 34 | additional terms or conditions. 35 | -------------------------------------------------------------------------------- /src/flags.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::{self, Debug, Formatter}; 2 | 3 | use crate::vars; 4 | 5 | #[derive(Debug, PartialEq, Eq)] 6 | pub enum OpenKind { 7 | Unknown, 8 | MainDb, 9 | MainJournal, 10 | TempDb, 11 | TempJournal, 12 | TransientDb, 13 | SubJournal, 14 | SuperJournal, 15 | Wal, 16 | } 17 | 18 | impl OpenKind { 19 | pub fn is_temp(&self) -> bool { 20 | matches!(self, Self::TempDb | Self::TempJournal | Self::TransientDb) 21 | } 22 | } 23 | 24 | impl From for OpenKind { 25 | fn from(flags: i32) -> Self { 26 | match flags { 27 | flags if flags & vars::SQLITE_OPEN_MAIN_DB > 0 => Self::MainDb, 28 | flags if flags & vars::SQLITE_OPEN_MAIN_JOURNAL > 0 => Self::MainJournal, 29 | flags if flags & vars::SQLITE_OPEN_TEMP_DB > 0 => Self::TempDb, 30 | flags if flags & vars::SQLITE_OPEN_TEMP_JOURNAL > 0 => Self::TempJournal, 31 | flags if flags & vars::SQLITE_OPEN_TRANSIENT_DB > 0 => Self::TransientDb, 32 | flags if flags & vars::SQLITE_OPEN_SUBJOURNAL > 0 => Self::SubJournal, 33 | flags if flags & vars::SQLITE_OPEN_SUPER_JOURNAL > 0 => Self::SuperJournal, 34 | flags if flags & vars::SQLITE_OPEN_WAL > 0 => Self::Wal, 35 | _ => Self::Unknown, 36 | } 37 | } 38 | } 39 | 40 | #[derive(Debug, PartialEq, Eq)] 41 | pub enum CreateMode { 42 | None, 43 | Create, 44 | MustCreate, 45 | } 46 | 47 | #[derive(Debug, PartialEq, Eq)] 48 | pub enum OpenMode { 49 | ReadOnly, 50 | ReadWrite { create: CreateMode }, 51 | } 52 | 53 | impl From for OpenMode { 54 | fn from(flags: i32) -> Self { 55 | const MUST_CREATE: i32 = vars::SQLITE_OPEN_CREATE | vars::SQLITE_OPEN_EXCLUSIVE; 56 | match flags { 57 | flags if flags & vars::SQLITE_OPEN_READONLY > 0 => Self::ReadOnly, 58 | flags if flags & vars::SQLITE_OPEN_READWRITE > 0 => Self::ReadWrite { 59 | create: match flags { 60 | flags if flags & MUST_CREATE == MUST_CREATE => CreateMode::MustCreate, 61 | flags if flags & vars::SQLITE_OPEN_CREATE > 0 => CreateMode::Create, 62 | _ => CreateMode::None, 63 | }, 64 | }, 65 | _ => Self::ReadOnly, 66 | } 67 | } 68 | } 69 | 70 | impl OpenMode { 71 | pub fn must_create(&self) -> bool { 72 | matches!(self, Self::ReadWrite { create: CreateMode::MustCreate }) 73 | } 74 | pub fn is_readonly(&self) -> bool { 75 | matches!(self, Self::ReadOnly) 76 | } 77 | } 78 | 79 | #[derive(Clone, Copy)] 80 | pub struct OpenOpts { 81 | flags: i32, 82 | } 83 | 84 | impl OpenOpts { 85 | pub fn new(flags: i32) -> Self { 86 | Self { flags } 87 | } 88 | 89 | pub fn flags(&self) -> i32 { 90 | self.flags 91 | } 92 | 93 | pub fn kind(&self) -> OpenKind { 94 | self.flags.into() 95 | } 96 | 97 | pub fn mode(&self) -> OpenMode { 98 | self.flags.into() 99 | } 100 | 101 | pub fn delete_on_close(&self) -> bool { 102 | self.flags & vars::SQLITE_OPEN_DELETEONCLOSE > 0 103 | } 104 | 105 | pub fn set_readonly(&mut self) { 106 | self.flags &= !vars::SQLITE_OPEN_READWRITE; 107 | self.flags |= vars::SQLITE_OPEN_READONLY; 108 | } 109 | } 110 | 111 | impl From for OpenOpts { 112 | fn from(flags: i32) -> Self { 113 | Self::new(flags) 114 | } 115 | } 116 | 117 | impl Debug for OpenOpts { 118 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 119 | f.debug_struct("OpenOpts") 120 | .field("flags", &self.flags) 121 | .field("kind", &self.kind()) 122 | .field("mode", &self.mode()) 123 | .field("delete_on_close", &self.delete_on_close()) 124 | .finish() 125 | } 126 | } 127 | 128 | #[derive(Debug, PartialEq, Eq)] 129 | pub enum AccessFlags { 130 | Exists, 131 | Read, 132 | ReadWrite, 133 | } 134 | 135 | impl From for AccessFlags { 136 | fn from(flags: i32) -> Self { 137 | match flags { 138 | flags if flags == vars::SQLITE_ACCESS_EXISTS => Self::Exists, 139 | flags if flags & vars::SQLITE_ACCESS_READ > 0 => Self::Read, 140 | flags if flags & vars::SQLITE_ACCESS_READWRITE > 0 => Self::ReadWrite, 141 | _ => Self::Exists, 142 | } 143 | } 144 | } 145 | 146 | /// Represents one of the 5 `SQLite` locking levels. 147 | /// See [SQLite documentation](https://www.sqlite.org/lockingv3.html) for more information. 148 | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] 149 | pub enum LockLevel { 150 | /// No locks are held; the database may be neither read nor written. 151 | Unlocked, 152 | 153 | /// The database may be read but not written. Multiple Shared locks can 154 | /// coexist at once. 155 | Shared, 156 | 157 | /// A shared lock with the intention to upgrade to an exclusive lock. Only 158 | /// one Reserved lock can exist at once. 159 | Reserved, 160 | 161 | /// A lock in the process of upgrading to a reserved lock. Can coexist with 162 | /// Shared locks, but no new shared locks can be taken. 163 | Pending, 164 | 165 | /// The database may be read or written, but no other locks can be held. 166 | Exclusive, 167 | } 168 | 169 | impl From for LockLevel { 170 | fn from(lock: i32) -> Self { 171 | match lock { 172 | vars::SQLITE_LOCK_NONE => Self::Unlocked, 173 | vars::SQLITE_LOCK_SHARED => Self::Shared, 174 | vars::SQLITE_LOCK_RESERVED => Self::Reserved, 175 | vars::SQLITE_LOCK_PENDING => Self::Pending, 176 | vars::SQLITE_LOCK_EXCLUSIVE => Self::Exclusive, 177 | _ => panic!("invalid lock level: {}", lock), 178 | } 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /examples/memvfs.rs: -------------------------------------------------------------------------------- 1 | // cargo build --example memvfs --features dynamic 2 | 3 | use std::{ffi::c_void, os::raw::c_char, sync::Arc}; 4 | 5 | use parking_lot::Mutex; 6 | use sqlite_plugin::{ 7 | flags::{AccessFlags, LockLevel, OpenOpts}, 8 | logger::{SqliteLogLevel, SqliteLogger}, 9 | sqlite3_api_routines, vars, 10 | vfs::{Pragma, PragmaErr, RegisterOpts, Vfs, VfsHandle, VfsResult, register_dynamic}, 11 | }; 12 | 13 | #[derive(Debug, Clone)] 14 | struct File { 15 | name: Option, 16 | data: Arc>>, 17 | delete_on_close: bool, 18 | opts: OpenOpts, 19 | } 20 | 21 | impl File { 22 | fn is_named(&self, s: &str) -> bool { 23 | self.name.as_ref().is_some_and(|f| f == s) 24 | } 25 | } 26 | 27 | impl VfsHandle for File { 28 | fn readonly(&self) -> bool { 29 | self.opts.mode().is_readonly() 30 | } 31 | 32 | fn in_memory(&self) -> bool { 33 | true 34 | } 35 | } 36 | 37 | struct MemVfs { 38 | files: Arc>>, 39 | } 40 | 41 | impl Vfs for MemVfs { 42 | type Handle = File; 43 | 44 | fn open(&self, path: Option<&str>, opts: OpenOpts) -> VfsResult { 45 | log::debug!("open: path={:?}, opts={:?}", path, opts); 46 | let mode = opts.mode(); 47 | if mode.is_readonly() { 48 | // readonly makes no sense since an in-memory VFS is not backed by 49 | // any pre-existing data. 50 | return Err(vars::SQLITE_CANTOPEN); 51 | } 52 | 53 | if let Some(path) = path { 54 | let mut files = self.files.lock(); 55 | 56 | for file in files.iter() { 57 | if file.is_named(path) { 58 | if mode.must_create() { 59 | return Err(vars::SQLITE_CANTOPEN); 60 | } 61 | return Ok(file.clone()); 62 | } 63 | } 64 | 65 | let file = File { 66 | name: Some(path.to_owned()), 67 | data: Default::default(), 68 | delete_on_close: opts.delete_on_close(), 69 | opts, 70 | }; 71 | files.push(file.clone()); 72 | Ok(file) 73 | } else { 74 | let file = File { 75 | name: None, 76 | data: Default::default(), 77 | delete_on_close: opts.delete_on_close(), 78 | opts, 79 | }; 80 | Ok(file) 81 | } 82 | } 83 | 84 | fn delete(&self, path: &str) -> VfsResult<()> { 85 | log::debug!("delete: path={}", path); 86 | let mut found = false; 87 | self.files.lock().retain(|file| { 88 | if file.is_named(path) { 89 | found = true; 90 | false 91 | } else { 92 | true 93 | } 94 | }); 95 | if !found { 96 | return Err(vars::SQLITE_IOERR_DELETE_NOENT); 97 | } 98 | Ok(()) 99 | } 100 | 101 | fn access(&self, path: &str, flags: AccessFlags) -> VfsResult { 102 | log::debug!("access: path={}, flags={:?}", path, flags); 103 | Ok(self.files.lock().iter().any(|f| f.is_named(path))) 104 | } 105 | 106 | fn file_size(&self, handle: &mut Self::Handle) -> VfsResult { 107 | log::debug!("file_size: file={:?}", handle.name); 108 | Ok(handle.data.lock().len()) 109 | } 110 | 111 | fn truncate(&self, handle: &mut Self::Handle, size: usize) -> VfsResult<()> { 112 | log::debug!("truncate: file={:?}, size={}", handle.name, size); 113 | let mut data = handle.data.lock(); 114 | if size > data.len() { 115 | data.resize(size, 0); 116 | } else { 117 | data.truncate(size); 118 | } 119 | Ok(()) 120 | } 121 | 122 | fn lock(&self, handle: &mut Self::Handle, level: LockLevel) -> VfsResult<()> { 123 | log::debug!("lock: file={:?}, level={:?}", handle.name, level); 124 | Ok(()) 125 | } 126 | 127 | fn unlock(&self, handle: &mut Self::Handle, level: LockLevel) -> VfsResult<()> { 128 | log::debug!("unlock: file={:?}, level={:?}", handle.name, level); 129 | Ok(()) 130 | } 131 | 132 | fn write(&self, handle: &mut Self::Handle, offset: usize, buf: &[u8]) -> VfsResult { 133 | log::debug!( 134 | "write: file={:?}, offset={}, len={}", 135 | handle.name, 136 | offset, 137 | buf.len() 138 | ); 139 | let mut data = handle.data.lock(); 140 | if offset + buf.len() > data.len() { 141 | data.resize(offset + buf.len(), 0); 142 | } 143 | data[offset..offset + buf.len()].copy_from_slice(buf); 144 | Ok(buf.len()) 145 | } 146 | 147 | fn read(&self, handle: &mut Self::Handle, offset: usize, buf: &mut [u8]) -> VfsResult { 148 | log::debug!( 149 | "read: file={:?}, offset={}, len={}", 150 | handle.name, 151 | offset, 152 | buf.len() 153 | ); 154 | let data = handle.data.lock(); 155 | if offset > data.len() { 156 | return Ok(0); 157 | } 158 | let len = buf.len().min(data.len() - offset); 159 | buf[..len].copy_from_slice(&data[offset..offset + len]); 160 | Ok(len) 161 | } 162 | 163 | fn sync(&self, handle: &mut Self::Handle) -> VfsResult<()> { 164 | log::debug!("sync: file={:?}", handle.name); 165 | Ok(()) 166 | } 167 | 168 | fn close(&self, handle: Self::Handle) -> VfsResult<()> { 169 | log::debug!("close: file={:?}", handle.name); 170 | if handle.delete_on_close { 171 | if let Some(ref name) = handle.name { 172 | self.delete(name)?; 173 | } 174 | } 175 | Ok(()) 176 | } 177 | 178 | fn pragma( 179 | &self, 180 | handle: &mut Self::Handle, 181 | pragma: Pragma<'_>, 182 | ) -> Result, PragmaErr> { 183 | log::debug!("pragma: file={:?}, pragma={:?}", handle.name, pragma); 184 | Err(PragmaErr::NotFound) 185 | } 186 | } 187 | 188 | fn setup_logger(logger: SqliteLogger) { 189 | struct LogCompat { 190 | logger: Mutex, 191 | } 192 | 193 | impl log::Log for LogCompat { 194 | fn enabled(&self, _metadata: &log::Metadata) -> bool { 195 | true 196 | } 197 | 198 | fn log(&self, record: &log::Record) { 199 | let level = match record.level() { 200 | log::Level::Error => SqliteLogLevel::Error, 201 | log::Level::Warn => SqliteLogLevel::Warn, 202 | _ => SqliteLogLevel::Notice, 203 | }; 204 | let msg = format!("{}", record.args()); 205 | self.logger.lock().log(level, &msg); 206 | } 207 | 208 | fn flush(&self) {} 209 | } 210 | 211 | let log = LogCompat { logger: Mutex::new(logger) }; 212 | log::set_boxed_logger(Box::new(log)).expect("failed to setup global logger"); 213 | } 214 | 215 | /// This function is called by `SQLite` when the extension is loaded. It registers 216 | /// the memvfs VFS with `SQLite`. 217 | /// # Safety 218 | /// This function should only be called by sqlite's extension loading mechanism. 219 | #[unsafe(no_mangle)] 220 | pub unsafe extern "C" fn sqlite3_memvfs_init( 221 | _db: *mut c_void, 222 | _pz_err_msg: *mut *mut c_char, 223 | p_api: *mut sqlite3_api_routines, 224 | ) -> std::os::raw::c_int { 225 | match unsafe { 226 | register_dynamic( 227 | p_api, 228 | c"mem".to_owned(), 229 | MemVfs { files: Default::default() }, 230 | RegisterOpts { make_default: true }, 231 | ) 232 | } { 233 | Ok(logger) => setup_logger(logger), 234 | Err(err) => return err, 235 | }; 236 | 237 | // set the log level to trace 238 | log::set_max_level(log::LevelFilter::Trace); 239 | 240 | vars::SQLITE_OK_LOAD_PERMANENTLY 241 | } 242 | -------------------------------------------------------------------------------- /src/mock.rs: -------------------------------------------------------------------------------- 1 | #![cfg(test)] 2 | 3 | // tests use std 4 | extern crate std; 5 | 6 | use core::fmt::{self, Display}; 7 | use std::boxed::Box; 8 | use std::collections::HashMap; 9 | use std::println; 10 | use std::{string::String, vec::Vec}; 11 | 12 | use alloc::borrow::{Cow, ToOwned}; 13 | use alloc::format; 14 | use alloc::sync::Arc; 15 | use parking_lot::{Mutex, MutexGuard}; 16 | 17 | use crate::flags::{self, AccessFlags, OpenOpts}; 18 | use crate::logger::{SqliteLogLevel, SqliteLogger}; 19 | use crate::vars; 20 | use crate::vfs::{ 21 | DEFAULT_DEVICE_CHARACTERISTICS, DEFAULT_SECTOR_SIZE, Pragma, PragmaErr, Vfs, VfsHandle, 22 | VfsResult, 23 | }; 24 | 25 | pub struct File { 26 | pub name: String, 27 | pub data: Vec, 28 | pub delete_on_close: bool, 29 | } 30 | 31 | #[allow(unused_variables)] 32 | pub trait Hooks { 33 | fn canonical_path(&mut self, path: &str) {} 34 | fn open(&mut self, path: &Option<&str>, opts: &OpenOpts) {} 35 | fn delete(&mut self, path: &str) {} 36 | fn access(&mut self, path: &str, flags: AccessFlags) {} 37 | fn file_size(&mut self, handle: MockHandle) {} 38 | fn truncate(&mut self, handle: MockHandle, size: usize) {} 39 | fn write(&mut self, handle: MockHandle, offset: usize, buf: &[u8]) {} 40 | fn read(&mut self, handle: MockHandle, offset: usize, buf: &[u8]) {} 41 | fn sync(&mut self, handle: MockHandle) {} 42 | fn close(&mut self, handle: MockHandle) {} 43 | fn pragma( 44 | &mut self, 45 | handle: MockHandle, 46 | pragma: Pragma<'_>, 47 | ) -> Result, PragmaErr> { 48 | Err(PragmaErr::NotFound) 49 | } 50 | fn sector_size(&mut self) {} 51 | fn device_characteristics(&mut self) { 52 | println!("device_characteristics"); 53 | } 54 | } 55 | 56 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 57 | pub struct MockHandle { 58 | id: usize, 59 | readonly: bool, 60 | } 61 | 62 | impl Display for MockHandle { 63 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 64 | write!(f, "MockHandle({})", self.id) 65 | } 66 | } 67 | 68 | impl MockHandle { 69 | pub fn new(id: usize, readonly: bool) -> Self { 70 | Self { id, readonly } 71 | } 72 | } 73 | 74 | impl VfsHandle for MockHandle { 75 | fn readonly(&self) -> bool { 76 | self.readonly 77 | } 78 | 79 | fn in_memory(&self) -> bool { 80 | false 81 | } 82 | } 83 | 84 | // MockVfs implements a very simple in-memory VFS for testing purposes. 85 | // See the memvfs example for a more complete implementation. 86 | pub struct MockVfs { 87 | state: Arc>, 88 | } 89 | 90 | pub struct MockState { 91 | next_id: usize, 92 | files: HashMap, 93 | hooks: Box, 94 | log: Option, 95 | } 96 | 97 | impl MockState { 98 | pub fn new(hooks: Box) -> Self { 99 | MockState { 100 | next_id: 0, 101 | files: HashMap::new(), 102 | hooks, 103 | log: None, 104 | } 105 | } 106 | 107 | pub fn setup_logger(&mut self, logger: SqliteLogger) { 108 | self.log = Some(logger) 109 | } 110 | } 111 | 112 | impl MockVfs { 113 | pub fn new(state: Arc>) -> Self { 114 | Self { state } 115 | } 116 | 117 | fn state(&self) -> MutexGuard<'_, MockState> { 118 | self.state.lock() 119 | } 120 | } 121 | 122 | impl MockState { 123 | fn log(&self, f: fmt::Arguments<'_>) { 124 | if let Some(log) = &self.log { 125 | log.log(SqliteLogLevel::Notice, &format!("{f}")); 126 | } else { 127 | panic!("MockVfs is missing registered log handler") 128 | } 129 | } 130 | 131 | fn next_id(&mut self) -> usize { 132 | let id = self.next_id; 133 | self.next_id += 1; 134 | id 135 | } 136 | } 137 | 138 | impl Vfs for MockVfs { 139 | // a simple usize that represents a file handle. 140 | type Handle = MockHandle; 141 | 142 | fn canonical_path<'a>(&self, path: Cow<'a, str>) -> VfsResult> { 143 | let mut state = self.state(); 144 | state.log(format_args!("canonical_path: path={path:?}")); 145 | state.hooks.canonical_path(&path); 146 | Ok(path) 147 | } 148 | 149 | fn open(&self, path: Option<&str>, opts: flags::OpenOpts) -> VfsResult { 150 | let mut state = self.state(); 151 | state.log(format_args!("open: path={path:?} opts={opts:?}")); 152 | state.hooks.open(&path, &opts); 153 | 154 | let id = state.next_id(); 155 | let file_handle = MockHandle::new(id, opts.mode().is_readonly()); 156 | 157 | if let Some(path) = path { 158 | // if file is already open return existing handle 159 | for (handle, file) in state.files.iter() { 160 | if file.name == path { 161 | return Ok(*handle); 162 | } 163 | } 164 | state.files.insert( 165 | file_handle, 166 | File { 167 | name: path.to_owned(), 168 | data: Vec::new(), 169 | delete_on_close: opts.delete_on_close(), 170 | }, 171 | ); 172 | } 173 | Ok(file_handle) 174 | } 175 | 176 | fn delete(&self, path: &str) -> VfsResult<()> { 177 | let mut state = self.state(); 178 | state.log(format_args!("delete: path={path:?}")); 179 | state.hooks.delete(path); 180 | state.files.retain(|_, file| file.name != path); 181 | Ok(()) 182 | } 183 | 184 | fn access(&self, path: &str, flags: AccessFlags) -> VfsResult { 185 | let mut state = self.state(); 186 | state.log(format_args!("access: path={path:?} flags={flags:?}")); 187 | state.hooks.access(path, flags); 188 | Ok(state.files.values().any(|file| file.name == path)) 189 | } 190 | 191 | fn file_size(&self, meta: &mut Self::Handle) -> VfsResult { 192 | let mut state = self.state(); 193 | state.log(format_args!("file_size: handle={meta:?}")); 194 | state.hooks.file_size(*meta); 195 | Ok(state.files.get(meta).map_or(0, |file| file.data.len())) 196 | } 197 | 198 | fn truncate(&self, meta: &mut Self::Handle, size: usize) -> VfsResult<()> { 199 | let mut state = self.state(); 200 | state.log(format_args!("truncate: handle={meta:?} size={size:?}")); 201 | state.hooks.truncate(*meta, size); 202 | if let Some(file) = state.files.get_mut(meta) { 203 | if size > file.data.len() { 204 | file.data.resize(size, 0); 205 | } else { 206 | file.data.truncate(size); 207 | } 208 | } 209 | Ok(()) 210 | } 211 | 212 | fn write(&self, meta: &mut Self::Handle, offset: usize, buf: &[u8]) -> VfsResult { 213 | let mut state = self.state(); 214 | state.log(format_args!( 215 | "write: handle={:?} offset={:?} buf.len={}", 216 | meta, 217 | offset, 218 | buf.len() 219 | )); 220 | state.hooks.write(*meta, offset, buf); 221 | if let Some(file) = state.files.get_mut(meta) { 222 | if offset + buf.len() > file.data.len() { 223 | file.data.resize(offset + buf.len(), 0); 224 | } 225 | file.data[offset..offset + buf.len()].copy_from_slice(buf); 226 | Ok(buf.len()) 227 | } else { 228 | Err(vars::SQLITE_IOERR_WRITE) 229 | } 230 | } 231 | 232 | fn read(&self, meta: &mut Self::Handle, offset: usize, buf: &mut [u8]) -> VfsResult { 233 | let mut state = self.state(); 234 | state.log(format_args!( 235 | "read: handle={:?} offset={:?} buf.len={}", 236 | meta, 237 | offset, 238 | buf.len() 239 | )); 240 | state.hooks.read(*meta, offset, buf); 241 | if let Some(file) = state.files.get(meta) { 242 | if offset > file.data.len() { 243 | return Ok(0); 244 | } 245 | let len = buf.len().min(file.data.len() - offset); 246 | buf[..len].copy_from_slice(&file.data[offset..offset + len]); 247 | Ok(len) 248 | } else { 249 | Err(vars::SQLITE_IOERR_READ) 250 | } 251 | } 252 | 253 | fn sync(&self, meta: &mut Self::Handle) -> VfsResult<()> { 254 | let mut state = self.state(); 255 | state.log(format_args!("sync: handle={meta:?}")); 256 | state.hooks.sync(*meta); 257 | Ok(()) 258 | } 259 | 260 | fn close(&self, meta: Self::Handle) -> VfsResult<()> { 261 | let mut state = self.state(); 262 | state.log(format_args!("close: handle={meta:?}")); 263 | state.hooks.close(meta); 264 | if let Some(file) = state.files.get(&meta) { 265 | if file.delete_on_close { 266 | state.files.remove(&meta); 267 | } 268 | } 269 | Ok(()) 270 | } 271 | 272 | fn pragma( 273 | &self, 274 | meta: &mut Self::Handle, 275 | pragma: Pragma<'_>, 276 | ) -> Result, PragmaErr> { 277 | let mut state = self.state(); 278 | state.log(format_args!("pragma: handle={meta:?} pragma={pragma:?}")); 279 | state.hooks.pragma(*meta, pragma) 280 | } 281 | 282 | fn sector_size(&self) -> i32 { 283 | let mut state = self.state(); 284 | state.log(format_args!("sector_size")); 285 | state.hooks.sector_size(); 286 | DEFAULT_SECTOR_SIZE 287 | } 288 | 289 | fn device_characteristics(&self) -> i32 { 290 | let mut state = self.state(); 291 | state.log(format_args!("device_characteristics")); 292 | state.hooks.device_characteristics(); 293 | DEFAULT_DEVICE_CHARACTERISTICS 294 | } 295 | } 296 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "1.1.4" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "bindgen" 16 | version = "0.72.1" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" 19 | dependencies = [ 20 | "bitflags", 21 | "cexpr", 22 | "clang-sys", 23 | "itertools", 24 | "proc-macro2", 25 | "quote", 26 | "regex", 27 | "rustc-hash", 28 | "shlex", 29 | "syn", 30 | ] 31 | 32 | [[package]] 33 | name = "bitflags" 34 | version = "2.10.0" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" 37 | 38 | [[package]] 39 | name = "cc" 40 | version = "1.2.47" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "cd405d82c84ff7f35739f175f67d8b9fb7687a0e84ccdc78bd3568839827cf07" 43 | dependencies = [ 44 | "find-msvc-tools", 45 | "shlex", 46 | ] 47 | 48 | [[package]] 49 | name = "cexpr" 50 | version = "0.6.0" 51 | source = "registry+https://github.com/rust-lang/crates.io-index" 52 | checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" 53 | dependencies = [ 54 | "nom", 55 | ] 56 | 57 | [[package]] 58 | name = "cfg-if" 59 | version = "1.0.4" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" 62 | 63 | [[package]] 64 | name = "clang-sys" 65 | version = "1.8.1" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" 68 | dependencies = [ 69 | "glob", 70 | "libc", 71 | ] 72 | 73 | [[package]] 74 | name = "either" 75 | version = "1.15.0" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" 78 | 79 | [[package]] 80 | name = "fallible-iterator" 81 | version = "0.3.0" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" 84 | 85 | [[package]] 86 | name = "fallible-streaming-iterator" 87 | version = "0.1.9" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" 90 | 91 | [[package]] 92 | name = "find-msvc-tools" 93 | version = "0.1.5" 94 | source = "registry+https://github.com/rust-lang/crates.io-index" 95 | checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" 96 | 97 | [[package]] 98 | name = "foldhash" 99 | version = "0.1.5" 100 | source = "registry+https://github.com/rust-lang/crates.io-index" 101 | checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" 102 | 103 | [[package]] 104 | name = "glob" 105 | version = "0.3.3" 106 | source = "registry+https://github.com/rust-lang/crates.io-index" 107 | checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" 108 | 109 | [[package]] 110 | name = "hashbrown" 111 | version = "0.15.5" 112 | source = "registry+https://github.com/rust-lang/crates.io-index" 113 | checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" 114 | dependencies = [ 115 | "foldhash", 116 | ] 117 | 118 | [[package]] 119 | name = "hashlink" 120 | version = "0.10.0" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" 123 | dependencies = [ 124 | "hashbrown", 125 | ] 126 | 127 | [[package]] 128 | name = "itertools" 129 | version = "0.13.0" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" 132 | dependencies = [ 133 | "either", 134 | ] 135 | 136 | [[package]] 137 | name = "libc" 138 | version = "0.2.177" 139 | source = "registry+https://github.com/rust-lang/crates.io-index" 140 | checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" 141 | 142 | [[package]] 143 | name = "libsqlite3-sys" 144 | version = "0.35.0" 145 | source = "registry+https://github.com/rust-lang/crates.io-index" 146 | checksum = "133c182a6a2c87864fe97778797e46c7e999672690dc9fa3ee8e241aa4a9c13f" 147 | dependencies = [ 148 | "cc", 149 | "pkg-config", 150 | "vcpkg", 151 | ] 152 | 153 | [[package]] 154 | name = "lock_api" 155 | version = "0.4.14" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" 158 | dependencies = [ 159 | "scopeguard", 160 | ] 161 | 162 | [[package]] 163 | name = "log" 164 | version = "0.4.28" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" 167 | 168 | [[package]] 169 | name = "memchr" 170 | version = "2.7.6" 171 | source = "registry+https://github.com/rust-lang/crates.io-index" 172 | checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" 173 | 174 | [[package]] 175 | name = "minimal-lexical" 176 | version = "0.2.1" 177 | source = "registry+https://github.com/rust-lang/crates.io-index" 178 | checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" 179 | 180 | [[package]] 181 | name = "nom" 182 | version = "7.1.3" 183 | source = "registry+https://github.com/rust-lang/crates.io-index" 184 | checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" 185 | dependencies = [ 186 | "memchr", 187 | "minimal-lexical", 188 | ] 189 | 190 | [[package]] 191 | name = "parking_lot" 192 | version = "0.12.5" 193 | source = "registry+https://github.com/rust-lang/crates.io-index" 194 | checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" 195 | dependencies = [ 196 | "lock_api", 197 | "parking_lot_core", 198 | ] 199 | 200 | [[package]] 201 | name = "parking_lot_core" 202 | version = "0.9.12" 203 | source = "registry+https://github.com/rust-lang/crates.io-index" 204 | checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" 205 | dependencies = [ 206 | "cfg-if", 207 | "libc", 208 | "redox_syscall", 209 | "smallvec", 210 | "windows-link", 211 | ] 212 | 213 | [[package]] 214 | name = "pkg-config" 215 | version = "0.3.32" 216 | source = "registry+https://github.com/rust-lang/crates.io-index" 217 | checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" 218 | 219 | [[package]] 220 | name = "proc-macro2" 221 | version = "1.0.103" 222 | source = "registry+https://github.com/rust-lang/crates.io-index" 223 | checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" 224 | dependencies = [ 225 | "unicode-ident", 226 | ] 227 | 228 | [[package]] 229 | name = "quote" 230 | version = "1.0.42" 231 | source = "registry+https://github.com/rust-lang/crates.io-index" 232 | checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" 233 | dependencies = [ 234 | "proc-macro2", 235 | ] 236 | 237 | [[package]] 238 | name = "redox_syscall" 239 | version = "0.5.18" 240 | source = "registry+https://github.com/rust-lang/crates.io-index" 241 | checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" 242 | dependencies = [ 243 | "bitflags", 244 | ] 245 | 246 | [[package]] 247 | name = "regex" 248 | version = "1.12.2" 249 | source = "registry+https://github.com/rust-lang/crates.io-index" 250 | checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" 251 | dependencies = [ 252 | "aho-corasick", 253 | "memchr", 254 | "regex-automata", 255 | "regex-syntax", 256 | ] 257 | 258 | [[package]] 259 | name = "regex-automata" 260 | version = "0.4.13" 261 | source = "registry+https://github.com/rust-lang/crates.io-index" 262 | checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" 263 | dependencies = [ 264 | "aho-corasick", 265 | "memchr", 266 | "regex-syntax", 267 | ] 268 | 269 | [[package]] 270 | name = "regex-syntax" 271 | version = "0.8.8" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" 274 | 275 | [[package]] 276 | name = "rusqlite" 277 | version = "0.37.0" 278 | source = "registry+https://github.com/rust-lang/crates.io-index" 279 | checksum = "165ca6e57b20e1351573e3729b958bc62f0e48025386970b6e4d29e7a7e71f3f" 280 | dependencies = [ 281 | "bitflags", 282 | "fallible-iterator", 283 | "fallible-streaming-iterator", 284 | "hashlink", 285 | "libsqlite3-sys", 286 | "smallvec", 287 | ] 288 | 289 | [[package]] 290 | name = "rustc-hash" 291 | version = "2.1.1" 292 | source = "registry+https://github.com/rust-lang/crates.io-index" 293 | checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" 294 | 295 | [[package]] 296 | name = "scopeguard" 297 | version = "1.2.0" 298 | source = "registry+https://github.com/rust-lang/crates.io-index" 299 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 300 | 301 | [[package]] 302 | name = "shlex" 303 | version = "1.3.0" 304 | source = "registry+https://github.com/rust-lang/crates.io-index" 305 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 306 | 307 | [[package]] 308 | name = "smallvec" 309 | version = "1.15.1" 310 | source = "registry+https://github.com/rust-lang/crates.io-index" 311 | checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" 312 | 313 | [[package]] 314 | name = "sqlite-plugin" 315 | version = "0.5.0" 316 | dependencies = [ 317 | "bindgen", 318 | "log", 319 | "parking_lot", 320 | "rusqlite", 321 | ] 322 | 323 | [[package]] 324 | name = "syn" 325 | version = "2.0.111" 326 | source = "registry+https://github.com/rust-lang/crates.io-index" 327 | checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" 328 | dependencies = [ 329 | "proc-macro2", 330 | "quote", 331 | "unicode-ident", 332 | ] 333 | 334 | [[package]] 335 | name = "unicode-ident" 336 | version = "1.0.22" 337 | source = "registry+https://github.com/rust-lang/crates.io-index" 338 | checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" 339 | 340 | [[package]] 341 | name = "vcpkg" 342 | version = "0.2.15" 343 | source = "registry+https://github.com/rust-lang/crates.io-index" 344 | checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 345 | 346 | [[package]] 347 | name = "windows-link" 348 | version = "0.2.1" 349 | source = "registry+https://github.com/rust-lang/crates.io-index" 350 | checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" 351 | -------------------------------------------------------------------------------- /src/vfs.rs: -------------------------------------------------------------------------------- 1 | use crate::flags::{AccessFlags, LockLevel, OpenOpts}; 2 | use crate::logger::SqliteLogger; 3 | use crate::vars::SQLITE_ERROR; 4 | use crate::{ffi, vars}; 5 | use alloc::borrow::Cow; 6 | use alloc::boxed::Box; 7 | use alloc::ffi::CString; 8 | use alloc::format; 9 | use alloc::string::String; 10 | use core::mem::{self, ManuallyDrop, MaybeUninit, size_of}; 11 | use core::slice; 12 | use core::{ 13 | ffi::{CStr, c_char, c_int, c_void}, 14 | ptr::null_mut, 15 | }; 16 | 17 | /// The minimim supported `SQLite` version. 18 | // If you need to make this earlier, make sure the tests are testing the earlier version 19 | pub const MIN_SQLITE_VERSION_NUMBER: i32 = 3043000; 20 | 21 | const DEFAULT_MAX_PATH_LEN: i32 = 512; 22 | pub const DEFAULT_SECTOR_SIZE: i32 = 4096; 23 | 24 | pub const DEFAULT_DEVICE_CHARACTERISTICS: i32 = 25 | // writes of any size are atomic 26 | vars::SQLITE_IOCAP_ATOMIC | 27 | // after reboot following a crash or power loss, the only bytes in a file that were written 28 | // at the application level might have changed and that adjacent bytes, even bytes within 29 | // the same sector are guaranteed to be unchanged 30 | vars::SQLITE_IOCAP_POWERSAFE_OVERWRITE | 31 | // when data is appended to a file, the data is appended first then the size of the file is 32 | // extended, never the other way around 33 | vars::SQLITE_IOCAP_SAFE_APPEND | 34 | // information is written to disk in the same order as calls to xWrite() 35 | vars::SQLITE_IOCAP_SEQUENTIAL; 36 | 37 | /// A `SQLite3` extended error code 38 | pub type SqliteErr = i32; 39 | 40 | pub type VfsResult = Result; 41 | 42 | // FileWrapper needs to be repr(C) and have sqlite3_file as it's first member 43 | // because it's a "subclass" of sqlite3_file 44 | #[repr(C)] 45 | struct FileWrapper { 46 | file: ffi::sqlite3_file, 47 | vfs: *mut ffi::sqlite3_vfs, 48 | handle: MaybeUninit, 49 | } 50 | 51 | struct AppData { 52 | base_vfs: *mut ffi::sqlite3_vfs, 53 | vfs: Vfs, 54 | io_methods: ffi::sqlite3_io_methods, 55 | sqlite_api: SqliteApi, 56 | } 57 | 58 | #[derive(Debug)] 59 | pub struct Pragma<'a> { 60 | pub name: &'a str, 61 | pub arg: Option<&'a str>, 62 | } 63 | 64 | #[derive(Debug)] 65 | pub enum PragmaErr { 66 | NotFound, 67 | Fail(SqliteErr, Option), 68 | } 69 | 70 | impl PragmaErr { 71 | pub fn required_arg(p: &Pragma<'_>) -> Self { 72 | PragmaErr::Fail( 73 | SQLITE_ERROR, 74 | Some(format!( 75 | "argument required (e.g. `pragma {} = ...`)", 76 | p.name 77 | )), 78 | ) 79 | } 80 | } 81 | 82 | fn fallible(mut cb: impl FnMut() -> Result) -> i32 { 83 | cb().unwrap_or_else(|err| err) 84 | } 85 | 86 | unsafe fn lossy_cstr<'a>(p: *const c_char) -> VfsResult> { 87 | unsafe { 88 | p.as_ref() 89 | .map(|p| CStr::from_ptr(p).to_string_lossy()) 90 | .ok_or(vars::SQLITE_INTERNAL) 91 | } 92 | } 93 | 94 | macro_rules! unwrap_appdata { 95 | ($p_vfs:expr, $t_vfs:ty) => { 96 | unsafe { 97 | let out: VfsResult<&AppData<$t_vfs>> = (*$p_vfs) 98 | .pAppData 99 | .cast::>() 100 | .as_ref() 101 | .ok_or(vars::SQLITE_INTERNAL); 102 | out 103 | } 104 | }; 105 | } 106 | 107 | macro_rules! unwrap_vfs { 108 | ($p_vfs:expr, $t_vfs:ty) => {{ 109 | let out: VfsResult<&$t_vfs> = unwrap_appdata!($p_vfs, $t_vfs).map(|app_data| &app_data.vfs); 110 | out 111 | }}; 112 | } 113 | 114 | macro_rules! unwrap_base_vfs { 115 | ($p_vfs:expr, $t_vfs:ty) => {{ 116 | let out: VfsResult<&mut ffi::sqlite3_vfs> = 117 | unwrap_appdata!($p_vfs, $t_vfs).and_then(|app_data| { 118 | unsafe { app_data.base_vfs.as_mut() }.ok_or(vars::SQLITE_INTERNAL) 119 | }); 120 | out 121 | }}; 122 | } 123 | 124 | macro_rules! unwrap_file { 125 | ($p_file:expr, $t_vfs:ty) => { 126 | unsafe { 127 | let out: VfsResult<&mut FileWrapper<<$t_vfs>::Handle>> = $p_file 128 | .cast::::Handle>>() 129 | .as_mut() 130 | .ok_or(vars::SQLITE_INTERNAL); 131 | out 132 | } 133 | }; 134 | } 135 | 136 | pub trait VfsHandle: Send { 137 | fn readonly(&self) -> bool; 138 | fn in_memory(&self) -> bool; 139 | } 140 | 141 | #[allow(unused_variables)] 142 | pub trait Vfs: Send + Sync { 143 | type Handle: VfsHandle; 144 | 145 | /// construct a canonical version of the given path 146 | fn canonical_path<'a>(&self, path: Cow<'a, str>) -> VfsResult> { 147 | Ok(path) 148 | } 149 | 150 | // file system operations 151 | fn open(&self, path: Option<&str>, opts: OpenOpts) -> VfsResult; 152 | fn delete(&self, path: &str) -> VfsResult<()>; 153 | fn access(&self, path: &str, flags: AccessFlags) -> VfsResult; 154 | 155 | // file operations 156 | fn file_size(&self, handle: &mut Self::Handle) -> VfsResult; 157 | fn truncate(&self, handle: &mut Self::Handle, size: usize) -> VfsResult<()>; 158 | fn write(&self, handle: &mut Self::Handle, offset: usize, data: &[u8]) -> VfsResult; 159 | fn read(&self, handle: &mut Self::Handle, offset: usize, data: &mut [u8]) -> VfsResult; 160 | 161 | fn lock(&self, handle: &mut Self::Handle, level: LockLevel) -> VfsResult<()> { 162 | Ok(()) 163 | } 164 | 165 | fn unlock(&self, handle: &mut Self::Handle, level: LockLevel) -> VfsResult<()> { 166 | Ok(()) 167 | } 168 | 169 | fn sync(&self, handle: &mut Self::Handle) -> VfsResult<()> { 170 | Ok(()) 171 | } 172 | 173 | fn close(&self, handle: Self::Handle) -> VfsResult<()>; 174 | 175 | fn pragma( 176 | &self, 177 | handle: &mut Self::Handle, 178 | pragma: Pragma<'_>, 179 | ) -> Result, PragmaErr> { 180 | Err(PragmaErr::NotFound) 181 | } 182 | 183 | // system queries 184 | fn sector_size(&self) -> i32 { 185 | DEFAULT_SECTOR_SIZE 186 | } 187 | 188 | fn device_characteristics(&self) -> i32 { 189 | DEFAULT_DEVICE_CHARACTERISTICS 190 | } 191 | } 192 | 193 | #[derive(Clone)] 194 | pub struct SqliteApi { 195 | register: unsafe extern "C" fn(arg1: *mut ffi::sqlite3_vfs, arg2: c_int) -> c_int, 196 | find: unsafe extern "C" fn(arg1: *const c_char) -> *mut ffi::sqlite3_vfs, 197 | mprintf: unsafe extern "C" fn(arg1: *const c_char, ...) -> *mut c_char, 198 | log: unsafe extern "C" fn(arg1: c_int, arg2: *const c_char, ...), 199 | libversion_number: unsafe extern "C" fn() -> c_int, 200 | } 201 | 202 | impl SqliteApi { 203 | #[cfg(feature = "static")] 204 | pub fn new_static() -> Self { 205 | Self { 206 | register: ffi::sqlite3_vfs_register, 207 | find: ffi::sqlite3_vfs_find, 208 | mprintf: ffi::sqlite3_mprintf, 209 | log: ffi::sqlite3_log, 210 | libversion_number: ffi::sqlite3_libversion_number, 211 | } 212 | } 213 | 214 | /// Initializes SqliteApi from a filled `sqlite3_api_routines` object. 215 | /// # Safety 216 | /// `api` must be a valid, aligned pointer to a `sqlite3_api_routines` struct 217 | #[cfg(feature = "dynamic")] 218 | pub unsafe fn new_dynamic(api: &ffi::sqlite3_api_routines) -> VfsResult { 219 | Ok(Self { 220 | register: api.vfs_register.ok_or(vars::SQLITE_INTERNAL)?, 221 | find: api.vfs_find.ok_or(vars::SQLITE_INTERNAL)?, 222 | mprintf: api.mprintf.ok_or(vars::SQLITE_INTERNAL)?, 223 | log: api.log.ok_or(vars::SQLITE_INTERNAL)?, 224 | libversion_number: api.libversion_number.ok_or(vars::SQLITE_INTERNAL)?, 225 | }) 226 | } 227 | 228 | /// Copies the provided string into a memory buffer allocated by `sqlite3_mprintf`. 229 | /// Writes the pointer to the memory buffer to `out` if `out` is not null. 230 | /// # Safety 231 | /// 1. the out pointer must not be null 232 | /// 2. it is the callers responsibility to eventually free the allocated buffer 233 | pub unsafe fn mprintf(&self, s: &str, out: *mut *const c_char) -> VfsResult<()> { 234 | let s = CString::new(s).map_err(|_| vars::SQLITE_INTERNAL)?; 235 | let p = unsafe { (self.mprintf)(s.as_ptr()) }; 236 | if p.is_null() { 237 | Err(vars::SQLITE_NOMEM) 238 | } else { 239 | unsafe { 240 | *out = p; 241 | } 242 | Ok(()) 243 | } 244 | } 245 | } 246 | 247 | pub struct RegisterOpts { 248 | /// If true, make this vfs the default vfs for `SQLite`. 249 | pub make_default: bool, 250 | } 251 | 252 | #[cfg(feature = "static")] 253 | pub fn register_static( 254 | name: CString, 255 | vfs: T, 256 | opts: RegisterOpts, 257 | ) -> VfsResult { 258 | register_inner(SqliteApi::new_static(), name, vfs, opts) 259 | } 260 | 261 | /// Register a vfs with `SQLite` using the dynamic API. This API is available when 262 | /// `SQLite` is initializing extensions. 263 | /// # Safety 264 | /// `p_api` must be a valid, aligned pointer to a `sqlite3_api_routines` struct 265 | #[cfg(feature = "dynamic")] 266 | pub unsafe fn register_dynamic( 267 | p_api: *mut ffi::sqlite3_api_routines, 268 | name: CString, 269 | vfs: T, 270 | opts: RegisterOpts, 271 | ) -> VfsResult { 272 | let api = unsafe { p_api.as_ref() }.ok_or(vars::SQLITE_INTERNAL)?; 273 | let sqlite_api = unsafe { SqliteApi::new_dynamic(api)? }; 274 | register_inner(sqlite_api, name, vfs, opts) 275 | } 276 | 277 | fn register_inner( 278 | sqlite_api: SqliteApi, 279 | name: CString, 280 | vfs: T, 281 | opts: RegisterOpts, 282 | ) -> VfsResult { 283 | let version = unsafe { (sqlite_api.libversion_number)() }; 284 | if version < MIN_SQLITE_VERSION_NUMBER { 285 | panic!( 286 | "sqlite3 must be at least version {}, found version {}", 287 | MIN_SQLITE_VERSION_NUMBER, version 288 | ); 289 | } 290 | 291 | let io_methods = ffi::sqlite3_io_methods { 292 | iVersion: 3, 293 | xClose: Some(x_close::), 294 | xRead: Some(x_read::), 295 | xWrite: Some(x_write::), 296 | xTruncate: Some(x_truncate::), 297 | xSync: Some(x_sync::), 298 | xFileSize: Some(x_file_size::), 299 | xLock: Some(x_lock::), 300 | xUnlock: Some(x_unlock::), 301 | xCheckReservedLock: None, 302 | xFileControl: Some(x_file_control::), 303 | xSectorSize: Some(x_sector_size::), 304 | xDeviceCharacteristics: Some(x_device_characteristics::), 305 | xShmMap: None, 306 | xShmLock: None, 307 | xShmBarrier: None, 308 | xShmUnmap: None, 309 | xFetch: None, 310 | xUnfetch: None, 311 | }; 312 | 313 | let logger = SqliteLogger::new(sqlite_api.log); 314 | 315 | let p_name = ManuallyDrop::new(name).as_ptr(); 316 | let base_vfs = unsafe { (sqlite_api.find)(null_mut()) }; 317 | let vfs_register = sqlite_api.register; 318 | let p_appdata = Box::into_raw(Box::new(AppData { base_vfs, vfs, io_methods, sqlite_api })); 319 | 320 | let filewrapper_size: c_int = size_of::>() 321 | .try_into() 322 | .map_err(|_| vars::SQLITE_INTERNAL)?; 323 | 324 | let p_vfs = Box::into_raw(Box::new(ffi::sqlite3_vfs { 325 | iVersion: 3, 326 | szOsFile: filewrapper_size, 327 | mxPathname: DEFAULT_MAX_PATH_LEN, 328 | pNext: null_mut(), 329 | zName: p_name, 330 | pAppData: p_appdata.cast(), 331 | xOpen: Some(x_open::), 332 | xDelete: Some(x_delete::), 333 | xAccess: Some(x_access::), 334 | xFullPathname: Some(x_full_pathname::), 335 | xDlOpen: Some(x_dlopen::), 336 | xDlError: Some(x_dlerror::), 337 | xDlSym: Some(x_dlsym::), 338 | xDlClose: Some(x_dlclose::), 339 | xRandomness: Some(x_randomness::), 340 | xSleep: Some(x_sleep::), 341 | xCurrentTime: Some(x_current_time::), 342 | xGetLastError: None, 343 | xCurrentTimeInt64: Some(x_current_time_int64::), 344 | xSetSystemCall: None, 345 | xGetSystemCall: None, 346 | xNextSystemCall: None, 347 | })); 348 | 349 | let result = unsafe { vfs_register(p_vfs, opts.make_default.into()) }; 350 | if result != vars::SQLITE_OK { 351 | // cleanup memory 352 | unsafe { 353 | drop(Box::from_raw(p_vfs)); 354 | drop(Box::from_raw(p_appdata)); 355 | drop(CString::from_raw(p_name as *mut c_char)); 356 | }; 357 | Err(result) 358 | } else { 359 | Ok(logger) 360 | } 361 | } 362 | 363 | unsafe extern "C" fn x_open( 364 | p_vfs: *mut ffi::sqlite3_vfs, 365 | z_name: ffi::sqlite3_filename, 366 | p_file: *mut ffi::sqlite3_file, 367 | flags: c_int, 368 | p_out_flags: *mut c_int, 369 | ) -> c_int { 370 | fallible(|| { 371 | let opts = flags.into(); 372 | let name = unsafe { lossy_cstr(z_name) }.ok(); 373 | let vfs = unwrap_vfs!(p_vfs, T)?; 374 | let handle = vfs.open(name.as_ref().map(|s| s.as_ref()), opts)?; 375 | 376 | let out_file = unwrap_file!(p_file, T)?; 377 | let appdata = unwrap_appdata!(p_vfs, T)?; 378 | 379 | if let Some(p_out_flags) = unsafe { p_out_flags.as_mut() } { 380 | let mut out_flags = flags; 381 | if handle.readonly() { 382 | out_flags |= vars::SQLITE_OPEN_READONLY; 383 | } 384 | if handle.in_memory() { 385 | out_flags |= vars::SQLITE_OPEN_MEMORY; 386 | } 387 | *p_out_flags = out_flags; 388 | } 389 | 390 | out_file.file.pMethods = &appdata.io_methods; 391 | out_file.vfs = p_vfs; 392 | out_file.handle.write(handle); 393 | 394 | Ok(vars::SQLITE_OK) 395 | }) 396 | } 397 | 398 | unsafe extern "C" fn x_delete( 399 | p_vfs: *mut ffi::sqlite3_vfs, 400 | z_name: ffi::sqlite3_filename, 401 | _sync_dir: c_int, 402 | ) -> c_int { 403 | fallible(|| { 404 | let name = unsafe { lossy_cstr(z_name)? }; 405 | let vfs = unwrap_vfs!(p_vfs, T)?; 406 | vfs.delete(&name)?; 407 | Ok(vars::SQLITE_OK) 408 | }) 409 | } 410 | 411 | unsafe extern "C" fn x_access( 412 | p_vfs: *mut ffi::sqlite3_vfs, 413 | z_name: ffi::sqlite3_filename, 414 | flags: c_int, 415 | p_res_out: *mut c_int, 416 | ) -> c_int { 417 | fallible(|| { 418 | let name = unsafe { lossy_cstr(z_name)? }; 419 | let vfs = unwrap_vfs!(p_vfs, T)?; 420 | let result = vfs.access(&name, flags.into())?; 421 | let out = unsafe { p_res_out.as_mut() }.ok_or(vars::SQLITE_IOERR_ACCESS)?; 422 | *out = result as i32; 423 | Ok(vars::SQLITE_OK) 424 | }) 425 | } 426 | 427 | unsafe extern "C" fn x_full_pathname( 428 | p_vfs: *mut ffi::sqlite3_vfs, 429 | z_name: ffi::sqlite3_filename, 430 | n_out: c_int, 431 | z_out: *mut c_char, 432 | ) -> c_int { 433 | fallible(|| { 434 | let name = unsafe { lossy_cstr(z_name)? }; 435 | let vfs = unwrap_vfs!(p_vfs, T)?; 436 | let full_name = vfs.canonical_path(name)?; 437 | let n_out = n_out.try_into().map_err(|_| vars::SQLITE_INTERNAL)?; 438 | let out = unsafe { slice::from_raw_parts_mut(z_out as *mut u8, n_out) }; 439 | let from = &full_name.as_bytes()[..full_name.len().min(n_out - 1)]; 440 | // copy the name into the output buffer 441 | out[..from.len()].copy_from_slice(from); 442 | // add the trailing null byte 443 | out[from.len()] = 0; 444 | Ok(vars::SQLITE_OK) 445 | }) 446 | } 447 | 448 | // file operations 449 | 450 | unsafe extern "C" fn x_close(p_file: *mut ffi::sqlite3_file) -> c_int { 451 | fallible(|| { 452 | let file = unwrap_file!(p_file, T)?; 453 | let vfs = unwrap_vfs!(file.vfs, T)?; 454 | let handle = mem::replace(&mut file.handle, MaybeUninit::uninit()); 455 | let handle = unsafe { handle.assume_init() }; 456 | vfs.close(handle)?; 457 | Ok(vars::SQLITE_OK) 458 | }) 459 | } 460 | 461 | unsafe extern "C" fn x_read( 462 | p_file: *mut ffi::sqlite3_file, 463 | buf: *mut c_void, 464 | i_amt: c_int, 465 | i_ofst: ffi::sqlite_int64, 466 | ) -> c_int { 467 | fallible(|| { 468 | let file = unwrap_file!(p_file, T)?; 469 | let vfs = unwrap_vfs!(file.vfs, T)?; 470 | let buf_len: usize = i_amt.try_into().map_err(|_| vars::SQLITE_IOERR_READ)?; 471 | let offset: usize = i_ofst.try_into().map_err(|_| vars::SQLITE_IOERR_READ)?; 472 | let buf = unsafe { slice::from_raw_parts_mut(buf.cast::(), buf_len) }; 473 | vfs.read(unsafe { file.handle.assume_init_mut() }, offset, buf)?; 474 | Ok(vars::SQLITE_OK) 475 | }) 476 | } 477 | 478 | unsafe extern "C" fn x_write( 479 | p_file: *mut ffi::sqlite3_file, 480 | buf: *const c_void, 481 | i_amt: c_int, 482 | i_ofst: ffi::sqlite_int64, 483 | ) -> c_int { 484 | fallible(|| { 485 | let file = unwrap_file!(p_file, T)?; 486 | let vfs = unwrap_vfs!(file.vfs, T)?; 487 | let buf_len: usize = i_amt.try_into().map_err(|_| vars::SQLITE_IOERR_WRITE)?; 488 | let offset: usize = i_ofst.try_into().map_err(|_| vars::SQLITE_IOERR_WRITE)?; 489 | let buf = unsafe { slice::from_raw_parts(buf.cast::(), buf_len) }; 490 | let n = vfs.write(unsafe { file.handle.assume_init_mut() }, offset, buf)?; 491 | if n != buf_len { 492 | return Err(vars::SQLITE_IOERR_WRITE); 493 | } 494 | Ok(vars::SQLITE_OK) 495 | }) 496 | } 497 | 498 | unsafe extern "C" fn x_truncate( 499 | p_file: *mut ffi::sqlite3_file, 500 | size: ffi::sqlite_int64, 501 | ) -> c_int { 502 | fallible(|| { 503 | let file = unwrap_file!(p_file, T)?; 504 | let vfs = unwrap_vfs!(file.vfs, T)?; 505 | let size: usize = size.try_into().map_err(|_| vars::SQLITE_IOERR_TRUNCATE)?; 506 | vfs.truncate(unsafe { file.handle.assume_init_mut() }, size)?; 507 | Ok(vars::SQLITE_OK) 508 | }) 509 | } 510 | 511 | unsafe extern "C" fn x_sync(p_file: *mut ffi::sqlite3_file, _flags: c_int) -> c_int { 512 | fallible(|| { 513 | let file = unwrap_file!(p_file, T)?; 514 | let vfs = unwrap_vfs!(file.vfs, T)?; 515 | vfs.sync(unsafe { file.handle.assume_init_mut() })?; 516 | Ok(vars::SQLITE_OK) 517 | }) 518 | } 519 | 520 | unsafe extern "C" fn x_file_size( 521 | p_file: *mut ffi::sqlite3_file, 522 | p_size: *mut ffi::sqlite3_int64, 523 | ) -> c_int { 524 | fallible(|| { 525 | let file = unwrap_file!(p_file, T)?; 526 | let vfs = unwrap_vfs!(file.vfs, T)?; 527 | let size = vfs.file_size(unsafe { file.handle.assume_init_mut() })?; 528 | let p_size = unsafe { p_size.as_mut() }.ok_or(vars::SQLITE_INTERNAL)?; 529 | *p_size = size.try_into().map_err(|_| vars::SQLITE_IOERR_FSTAT)?; 530 | Ok(vars::SQLITE_OK) 531 | }) 532 | } 533 | 534 | unsafe extern "C" fn x_lock(p_file: *mut ffi::sqlite3_file, raw_lock: c_int) -> c_int { 535 | fallible(|| { 536 | let level: LockLevel = raw_lock.into(); 537 | let file = unwrap_file!(p_file, T)?; 538 | let vfs = unwrap_vfs!(file.vfs, T)?; 539 | vfs.lock(unsafe { file.handle.assume_init_mut() }, level)?; 540 | Ok(vars::SQLITE_OK) 541 | }) 542 | } 543 | 544 | unsafe extern "C" fn x_unlock(p_file: *mut ffi::sqlite3_file, raw_lock: c_int) -> c_int { 545 | fallible(|| { 546 | let level: LockLevel = raw_lock.into(); 547 | let file = unwrap_file!(p_file, T)?; 548 | let vfs = unwrap_vfs!(file.vfs, T)?; 549 | vfs.unlock(unsafe { file.handle.assume_init_mut() }, level)?; 550 | Ok(vars::SQLITE_OK) 551 | }) 552 | } 553 | 554 | unsafe extern "C" fn x_file_control( 555 | p_file: *mut ffi::sqlite3_file, 556 | op: c_int, 557 | p_arg: *mut c_void, 558 | ) -> c_int { 559 | /* 560 | Other interesting ops: 561 | SIZE_HINT: hint of how large the database will grow during the current transaction 562 | COMMIT_PHASETWO: after transaction commits before file unlocks (only used in WAL mode) 563 | VFS_NAME: should return this vfs's name + / + base vfs's name 564 | 565 | Atomic write support: (requires SQLITE_IOCAP_BATCH_ATOMIC device characteristic) 566 | Docs: https://www3.sqlite.org/cgi/src/technote/714f6cbbf78c8a1351cbd48af2b438f7f824b336 567 | BEGIN_ATOMIC_WRITE: start an atomic write operation 568 | COMMIT_ATOMIC_WRITE: commit an atomic write operation 569 | ROLLBACK_ATOMIC_WRITE: rollback an atomic write operation 570 | */ 571 | 572 | if op == vars::SQLITE_FCNTL_PRAGMA { 573 | return fallible(|| { 574 | let file = unwrap_file!(p_file, T)?; 575 | let vfs = unwrap_vfs!(file.vfs, T)?; 576 | 577 | // p_arg is a pointer to an array of strings 578 | // the second value is the pragma name 579 | // the third value is either null or the pragma arg 580 | let args = p_arg.cast::<*const c_char>(); 581 | let name = unsafe { lossy_cstr(*args.add(1)) }?; 582 | let arg = unsafe { 583 | (*args.add(2)) 584 | .as_ref() 585 | .map(|p| CStr::from_ptr(p).to_string_lossy()) 586 | }; 587 | let pragma = Pragma { name: &name, arg: arg.as_deref() }; 588 | 589 | let (result, msg) = match vfs.pragma(unsafe { file.handle.assume_init_mut() }, pragma) { 590 | Ok(msg) => (Ok(vars::SQLITE_OK), msg), 591 | Err(PragmaErr::NotFound) => (Err(vars::SQLITE_NOTFOUND), None), 592 | Err(PragmaErr::Fail(err, msg)) => (Err(err), msg), 593 | }; 594 | 595 | if let Some(msg) = msg { 596 | // write the msg back to the first element of the args array. 597 | // SQLite is responsible for eventually freeing the result 598 | let appdata = unwrap_appdata!(file.vfs, T)?; 599 | unsafe { appdata.sqlite_api.mprintf(&msg, args)? }; 600 | } 601 | 602 | result 603 | }); 604 | } 605 | vars::SQLITE_NOTFOUND 606 | } 607 | 608 | // system queries 609 | 610 | unsafe extern "C" fn x_sector_size(p_file: *mut ffi::sqlite3_file) -> c_int { 611 | fallible(|| { 612 | let file = unwrap_file!(p_file, T)?; 613 | let vfs = unwrap_vfs!(file.vfs, T)?; 614 | Ok(vfs.sector_size()) 615 | }) 616 | } 617 | 618 | unsafe extern "C" fn x_device_characteristics(p_file: *mut ffi::sqlite3_file) -> c_int { 619 | fallible(|| { 620 | let file = unwrap_file!(p_file, T)?; 621 | let vfs = unwrap_vfs!(file.vfs, T)?; 622 | Ok(vfs.device_characteristics()) 623 | }) 624 | } 625 | 626 | // the following functions are wrappers around the base vfs functions 627 | 628 | unsafe extern "C" fn x_dlopen( 629 | p_vfs: *mut ffi::sqlite3_vfs, 630 | z_path: *const c_char, 631 | ) -> *mut c_void { 632 | if let Ok(vfs) = unwrap_base_vfs!(p_vfs, T) { 633 | if let Some(x_dlopen) = vfs.xDlOpen { 634 | return unsafe { x_dlopen(vfs, z_path) }; 635 | } 636 | } 637 | null_mut() 638 | } 639 | 640 | unsafe extern "C" fn x_dlerror( 641 | p_vfs: *mut ffi::sqlite3_vfs, 642 | n_byte: c_int, 643 | z_err_msg: *mut c_char, 644 | ) { 645 | if let Ok(vfs) = unwrap_base_vfs!(p_vfs, T) { 646 | if let Some(x_dlerror) = vfs.xDlError { 647 | unsafe { x_dlerror(vfs, n_byte, z_err_msg) }; 648 | } 649 | } 650 | } 651 | 652 | unsafe extern "C" fn x_dlsym( 653 | p_vfs: *mut ffi::sqlite3_vfs, 654 | p_handle: *mut c_void, 655 | z_symbol: *const c_char, 656 | ) -> Option 657 | { 658 | if let Ok(vfs) = unwrap_base_vfs!(p_vfs, T) { 659 | if let Some(x_dlsym) = vfs.xDlSym { 660 | return unsafe { x_dlsym(vfs, p_handle, z_symbol) }; 661 | } 662 | } 663 | None 664 | } 665 | 666 | unsafe extern "C" fn x_dlclose(p_vfs: *mut ffi::sqlite3_vfs, p_handle: *mut c_void) { 667 | if let Ok(vfs) = unwrap_base_vfs!(p_vfs, T) { 668 | if let Some(x_dlclose) = vfs.xDlClose { 669 | unsafe { x_dlclose(vfs, p_handle) }; 670 | } 671 | } 672 | } 673 | 674 | unsafe extern "C" fn x_randomness( 675 | p_vfs: *mut ffi::sqlite3_vfs, 676 | n_byte: c_int, 677 | z_out: *mut c_char, 678 | ) -> c_int { 679 | if let Ok(vfs) = unwrap_base_vfs!(p_vfs, T) { 680 | if let Some(x_randomness) = vfs.xRandomness { 681 | return unsafe { x_randomness(vfs, n_byte, z_out) }; 682 | } 683 | } 684 | vars::SQLITE_INTERNAL 685 | } 686 | 687 | unsafe extern "C" fn x_sleep(p_vfs: *mut ffi::sqlite3_vfs, microseconds: c_int) -> c_int { 688 | if let Ok(vfs) = unwrap_base_vfs!(p_vfs, T) { 689 | if let Some(x_sleep) = vfs.xSleep { 690 | return unsafe { x_sleep(vfs, microseconds) }; 691 | } 692 | } 693 | vars::SQLITE_INTERNAL 694 | } 695 | 696 | unsafe extern "C" fn x_current_time( 697 | p_vfs: *mut ffi::sqlite3_vfs, 698 | p_time: *mut f64, 699 | ) -> c_int { 700 | if let Ok(vfs) = unwrap_base_vfs!(p_vfs, T) { 701 | if let Some(x_current_time) = vfs.xCurrentTime { 702 | return unsafe { x_current_time(vfs, p_time) }; 703 | } 704 | } 705 | vars::SQLITE_INTERNAL 706 | } 707 | 708 | unsafe extern "C" fn x_current_time_int64( 709 | p_vfs: *mut ffi::sqlite3_vfs, 710 | p_time: *mut i64, 711 | ) -> c_int { 712 | if let Ok(vfs) = unwrap_base_vfs!(p_vfs, T) { 713 | if let Some(x_current_time_int64) = vfs.xCurrentTimeInt64 { 714 | return unsafe { x_current_time_int64(vfs, p_time) }; 715 | } 716 | } 717 | vars::SQLITE_INTERNAL 718 | } 719 | 720 | #[cfg(test)] 721 | mod tests { 722 | // tests use std 723 | extern crate std; 724 | 725 | use super::*; 726 | use crate::{ 727 | flags::{CreateMode, OpenKind, OpenMode}, 728 | mock::*, 729 | }; 730 | use alloc::{sync::Arc, vec::Vec}; 731 | use parking_lot::Mutex; 732 | use rusqlite::{Connection, OpenFlags}; 733 | use std::{boxed::Box, io::Write, println}; 734 | 735 | fn log_handler(_: i32, arg2: &str) { 736 | println!("{arg2}"); 737 | } 738 | 739 | #[test] 740 | fn sanity() -> Result<(), Box> { 741 | unsafe { 742 | rusqlite::trace::config_log(Some(log_handler)).unwrap(); 743 | } 744 | 745 | struct H {} 746 | impl Hooks for H { 747 | fn open(&mut self, path: &Option<&str>, opts: &OpenOpts) { 748 | let path = path.unwrap(); 749 | if path == "main.db" { 750 | assert!(!opts.delete_on_close()); 751 | assert_eq!(opts.kind(), OpenKind::MainDb); 752 | assert_eq!( 753 | opts.mode(), 754 | OpenMode::ReadWrite { create: CreateMode::Create } 755 | ); 756 | } else if path == "main.db-journal" { 757 | assert!(!opts.delete_on_close()); 758 | assert_eq!(opts.kind(), OpenKind::MainJournal); 759 | assert_eq!( 760 | opts.mode(), 761 | OpenMode::ReadWrite { create: CreateMode::Create } 762 | ); 763 | } else { 764 | panic!("unexpected path: {}", path); 765 | } 766 | } 767 | } 768 | 769 | let shared = Arc::new(Mutex::new(MockState::new(Box::new(H {})))); 770 | let vfs = MockVfs::new(shared.clone()); 771 | let logger = register_static( 772 | CString::new("mock").unwrap(), 773 | vfs, 774 | RegisterOpts { make_default: true }, 775 | ) 776 | .map_err(|_| "failed to register vfs")?; 777 | 778 | // setup the logger 779 | shared.lock().setup_logger(logger); 780 | 781 | // create a sqlite connection using the mock vfs 782 | let conn = Connection::open_with_flags_and_vfs( 783 | "main.db", 784 | OpenFlags::SQLITE_OPEN_READ_WRITE | OpenFlags::SQLITE_OPEN_CREATE, 785 | "mock", 786 | )?; 787 | 788 | conn.execute("create table t (val int)", [])?; 789 | conn.execute("insert into t (val) values (1)", [])?; 790 | conn.execute("insert into t (val) values (2)", [])?; 791 | 792 | conn.execute("pragma mock_test", [])?; 793 | 794 | let n: i64 = conn.query_row("select sum(val) from t", [], |row| row.get(0))?; 795 | assert_eq!(n, 3); 796 | 797 | // the blob api is interesting and stress tests reading/writing pages and journaling 798 | conn.execute("create table b (data blob)", [])?; 799 | println!("inserting zero blob"); 800 | conn.execute("insert into b values (zeroblob(8192))", [])?; 801 | let rowid = conn.last_insert_rowid(); 802 | let mut blob = conn.blob_open(rusqlite::MAIN_DB, "b", "data", rowid, false)?; 803 | 804 | // write some data to the blob 805 | println!("writing to blob"); 806 | let n = blob.write(b"hello")?; 807 | assert_eq!(n, 5); 808 | 809 | // query the table for the blob and print it 810 | let mut stmt = conn.prepare("select data from b")?; 811 | let mut rows = stmt.query([])?; 812 | while let Some(row) = rows.next()? { 813 | let data: Vec = row.get(0)?; 814 | assert_eq!(&data[0..5], b"hello"); 815 | } 816 | 817 | Ok(()) 818 | } 819 | } 820 | -------------------------------------------------------------------------------- /sqlite3/sqlite3ext.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2006 June 7 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** 7 | ** May you do good and not evil. 8 | ** May you find forgiveness for yourself and forgive others. 9 | ** May you share freely, never taking more than you give. 10 | ** 11 | ************************************************************************* 12 | ** This header file defines the SQLite interface for use by 13 | ** shared libraries that want to be imported as extensions into 14 | ** an SQLite instance. Shared libraries that intend to be loaded 15 | ** as extensions by SQLite should #include this file instead of 16 | ** sqlite3.h. 17 | */ 18 | #ifndef SQLITE3EXT_H 19 | #define SQLITE3EXT_H 20 | #include "sqlite3.h" 21 | 22 | /* 23 | ** The following structure holds pointers to all of the SQLite API 24 | ** routines. 25 | ** 26 | ** WARNING: In order to maintain backwards compatibility, add new 27 | ** interfaces to the end of this structure only. If you insert new 28 | ** interfaces in the middle of this structure, then older different 29 | ** versions of SQLite will not be able to load each other's shared 30 | ** libraries! 31 | */ 32 | struct sqlite3_api_routines { 33 | void * (*aggregate_context)(sqlite3_context*,int nBytes); 34 | int (*aggregate_count)(sqlite3_context*); 35 | int (*bind_blob)(sqlite3_stmt*,int,const void*,int n,void(*)(void*)); 36 | int (*bind_double)(sqlite3_stmt*,int,double); 37 | int (*bind_int)(sqlite3_stmt*,int,int); 38 | int (*bind_int64)(sqlite3_stmt*,int,sqlite_int64); 39 | int (*bind_null)(sqlite3_stmt*,int); 40 | int (*bind_parameter_count)(sqlite3_stmt*); 41 | int (*bind_parameter_index)(sqlite3_stmt*,const char*zName); 42 | const char * (*bind_parameter_name)(sqlite3_stmt*,int); 43 | int (*bind_text)(sqlite3_stmt*,int,const char*,int n,void(*)(void*)); 44 | int (*bind_text16)(sqlite3_stmt*,int,const void*,int,void(*)(void*)); 45 | int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*); 46 | int (*busy_handler)(sqlite3*,int(*)(void*,int),void*); 47 | int (*busy_timeout)(sqlite3*,int ms); 48 | int (*changes)(sqlite3*); 49 | int (*close)(sqlite3*); 50 | int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*, 51 | int eTextRep,const char*)); 52 | int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*, 53 | int eTextRep,const void*)); 54 | const void * (*column_blob)(sqlite3_stmt*,int iCol); 55 | int (*column_bytes)(sqlite3_stmt*,int iCol); 56 | int (*column_bytes16)(sqlite3_stmt*,int iCol); 57 | int (*column_count)(sqlite3_stmt*pStmt); 58 | const char * (*column_database_name)(sqlite3_stmt*,int); 59 | const void * (*column_database_name16)(sqlite3_stmt*,int); 60 | const char * (*column_decltype)(sqlite3_stmt*,int i); 61 | const void * (*column_decltype16)(sqlite3_stmt*,int); 62 | double (*column_double)(sqlite3_stmt*,int iCol); 63 | int (*column_int)(sqlite3_stmt*,int iCol); 64 | sqlite_int64 (*column_int64)(sqlite3_stmt*,int iCol); 65 | const char * (*column_name)(sqlite3_stmt*,int); 66 | const void * (*column_name16)(sqlite3_stmt*,int); 67 | const char * (*column_origin_name)(sqlite3_stmt*,int); 68 | const void * (*column_origin_name16)(sqlite3_stmt*,int); 69 | const char * (*column_table_name)(sqlite3_stmt*,int); 70 | const void * (*column_table_name16)(sqlite3_stmt*,int); 71 | const unsigned char * (*column_text)(sqlite3_stmt*,int iCol); 72 | const void * (*column_text16)(sqlite3_stmt*,int iCol); 73 | int (*column_type)(sqlite3_stmt*,int iCol); 74 | sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol); 75 | void * (*commit_hook)(sqlite3*,int(*)(void*),void*); 76 | int (*complete)(const char*sql); 77 | int (*complete16)(const void*sql); 78 | int (*create_collation)(sqlite3*,const char*,int,void*, 79 | int(*)(void*,int,const void*,int,const void*)); 80 | int (*create_collation16)(sqlite3*,const void*,int,void*, 81 | int(*)(void*,int,const void*,int,const void*)); 82 | int (*create_function)(sqlite3*,const char*,int,int,void*, 83 | void (*xFunc)(sqlite3_context*,int,sqlite3_value**), 84 | void (*xStep)(sqlite3_context*,int,sqlite3_value**), 85 | void (*xFinal)(sqlite3_context*)); 86 | int (*create_function16)(sqlite3*,const void*,int,int,void*, 87 | void (*xFunc)(sqlite3_context*,int,sqlite3_value**), 88 | void (*xStep)(sqlite3_context*,int,sqlite3_value**), 89 | void (*xFinal)(sqlite3_context*)); 90 | int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*); 91 | int (*data_count)(sqlite3_stmt*pStmt); 92 | sqlite3 * (*db_handle)(sqlite3_stmt*); 93 | int (*declare_vtab)(sqlite3*,const char*); 94 | int (*enable_shared_cache)(int); 95 | int (*errcode)(sqlite3*db); 96 | const char * (*errmsg)(sqlite3*); 97 | const void * (*errmsg16)(sqlite3*); 98 | int (*exec)(sqlite3*,const char*,sqlite3_callback,void*,char**); 99 | int (*expired)(sqlite3_stmt*); 100 | int (*finalize)(sqlite3_stmt*pStmt); 101 | void (*free)(void*); 102 | void (*free_table)(char**result); 103 | int (*get_autocommit)(sqlite3*); 104 | void * (*get_auxdata)(sqlite3_context*,int); 105 | int (*get_table)(sqlite3*,const char*,char***,int*,int*,char**); 106 | int (*global_recover)(void); 107 | void (*interruptx)(sqlite3*); 108 | sqlite_int64 (*last_insert_rowid)(sqlite3*); 109 | const char * (*libversion)(void); 110 | int (*libversion_number)(void); 111 | void *(*malloc)(int); 112 | char * (*mprintf)(const char*,...); 113 | int (*open)(const char*,sqlite3**); 114 | int (*open16)(const void*,sqlite3**); 115 | int (*prepare)(sqlite3*,const char*,int,sqlite3_stmt**,const char**); 116 | int (*prepare16)(sqlite3*,const void*,int,sqlite3_stmt**,const void**); 117 | void * (*profile)(sqlite3*,void(*)(void*,const char*,sqlite_uint64),void*); 118 | void (*progress_handler)(sqlite3*,int,int(*)(void*),void*); 119 | void *(*realloc)(void*,int); 120 | int (*reset)(sqlite3_stmt*pStmt); 121 | void (*result_blob)(sqlite3_context*,const void*,int,void(*)(void*)); 122 | void (*result_double)(sqlite3_context*,double); 123 | void (*result_error)(sqlite3_context*,const char*,int); 124 | void (*result_error16)(sqlite3_context*,const void*,int); 125 | void (*result_int)(sqlite3_context*,int); 126 | void (*result_int64)(sqlite3_context*,sqlite_int64); 127 | void (*result_null)(sqlite3_context*); 128 | void (*result_text)(sqlite3_context*,const char*,int,void(*)(void*)); 129 | void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*)); 130 | void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*)); 131 | void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*)); 132 | void (*result_value)(sqlite3_context*,sqlite3_value*); 133 | void * (*rollback_hook)(sqlite3*,void(*)(void*),void*); 134 | int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*, 135 | const char*,const char*),void*); 136 | void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*)); 137 | char * (*xsnprintf)(int,char*,const char*,...); 138 | int (*step)(sqlite3_stmt*); 139 | int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*, 140 | char const**,char const**,int*,int*,int*); 141 | void (*thread_cleanup)(void); 142 | int (*total_changes)(sqlite3*); 143 | void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*); 144 | int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*); 145 | void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*, 146 | sqlite_int64),void*); 147 | void * (*user_data)(sqlite3_context*); 148 | const void * (*value_blob)(sqlite3_value*); 149 | int (*value_bytes)(sqlite3_value*); 150 | int (*value_bytes16)(sqlite3_value*); 151 | double (*value_double)(sqlite3_value*); 152 | int (*value_int)(sqlite3_value*); 153 | sqlite_int64 (*value_int64)(sqlite3_value*); 154 | int (*value_numeric_type)(sqlite3_value*); 155 | const unsigned char * (*value_text)(sqlite3_value*); 156 | const void * (*value_text16)(sqlite3_value*); 157 | const void * (*value_text16be)(sqlite3_value*); 158 | const void * (*value_text16le)(sqlite3_value*); 159 | int (*value_type)(sqlite3_value*); 160 | char *(*vmprintf)(const char*,va_list); 161 | /* Added ??? */ 162 | int (*overload_function)(sqlite3*, const char *zFuncName, int nArg); 163 | /* Added by 3.3.13 */ 164 | int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**); 165 | int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**); 166 | int (*clear_bindings)(sqlite3_stmt*); 167 | /* Added by 3.4.1 */ 168 | int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*, 169 | void (*xDestroy)(void *)); 170 | /* Added by 3.5.0 */ 171 | int (*bind_zeroblob)(sqlite3_stmt*,int,int); 172 | int (*blob_bytes)(sqlite3_blob*); 173 | int (*blob_close)(sqlite3_blob*); 174 | int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64, 175 | int,sqlite3_blob**); 176 | int (*blob_read)(sqlite3_blob*,void*,int,int); 177 | int (*blob_write)(sqlite3_blob*,const void*,int,int); 178 | int (*create_collation_v2)(sqlite3*,const char*,int,void*, 179 | int(*)(void*,int,const void*,int,const void*), 180 | void(*)(void*)); 181 | int (*file_control)(sqlite3*,const char*,int,void*); 182 | sqlite3_int64 (*memory_highwater)(int); 183 | sqlite3_int64 (*memory_used)(void); 184 | sqlite3_mutex *(*mutex_alloc)(int); 185 | void (*mutex_enter)(sqlite3_mutex*); 186 | void (*mutex_free)(sqlite3_mutex*); 187 | void (*mutex_leave)(sqlite3_mutex*); 188 | int (*mutex_try)(sqlite3_mutex*); 189 | int (*open_v2)(const char*,sqlite3**,int,const char*); 190 | int (*release_memory)(int); 191 | void (*result_error_nomem)(sqlite3_context*); 192 | void (*result_error_toobig)(sqlite3_context*); 193 | int (*sleep)(int); 194 | void (*soft_heap_limit)(int); 195 | sqlite3_vfs *(*vfs_find)(const char*); 196 | int (*vfs_register)(sqlite3_vfs*,int); 197 | int (*vfs_unregister)(sqlite3_vfs*); 198 | int (*xthreadsafe)(void); 199 | void (*result_zeroblob)(sqlite3_context*,int); 200 | void (*result_error_code)(sqlite3_context*,int); 201 | int (*test_control)(int, ...); 202 | void (*randomness)(int,void*); 203 | sqlite3 *(*context_db_handle)(sqlite3_context*); 204 | int (*extended_result_codes)(sqlite3*,int); 205 | int (*limit)(sqlite3*,int,int); 206 | sqlite3_stmt *(*next_stmt)(sqlite3*,sqlite3_stmt*); 207 | const char *(*sql)(sqlite3_stmt*); 208 | int (*status)(int,int*,int*,int); 209 | int (*backup_finish)(sqlite3_backup*); 210 | sqlite3_backup *(*backup_init)(sqlite3*,const char*,sqlite3*,const char*); 211 | int (*backup_pagecount)(sqlite3_backup*); 212 | int (*backup_remaining)(sqlite3_backup*); 213 | int (*backup_step)(sqlite3_backup*,int); 214 | const char *(*compileoption_get)(int); 215 | int (*compileoption_used)(const char*); 216 | int (*create_function_v2)(sqlite3*,const char*,int,int,void*, 217 | void (*xFunc)(sqlite3_context*,int,sqlite3_value**), 218 | void (*xStep)(sqlite3_context*,int,sqlite3_value**), 219 | void (*xFinal)(sqlite3_context*), 220 | void(*xDestroy)(void*)); 221 | int (*db_config)(sqlite3*,int,...); 222 | sqlite3_mutex *(*db_mutex)(sqlite3*); 223 | int (*db_status)(sqlite3*,int,int*,int*,int); 224 | int (*extended_errcode)(sqlite3*); 225 | void (*log)(int,const char*,...); 226 | sqlite3_int64 (*soft_heap_limit64)(sqlite3_int64); 227 | const char *(*sourceid)(void); 228 | int (*stmt_status)(sqlite3_stmt*,int,int); 229 | int (*strnicmp)(const char*,const char*,int); 230 | int (*unlock_notify)(sqlite3*,void(*)(void**,int),void*); 231 | int (*wal_autocheckpoint)(sqlite3*,int); 232 | int (*wal_checkpoint)(sqlite3*,const char*); 233 | void *(*wal_hook)(sqlite3*,int(*)(void*,sqlite3*,const char*,int),void*); 234 | int (*blob_reopen)(sqlite3_blob*,sqlite3_int64); 235 | int (*vtab_config)(sqlite3*,int op,...); 236 | int (*vtab_on_conflict)(sqlite3*); 237 | /* Version 3.7.16 and later */ 238 | int (*close_v2)(sqlite3*); 239 | const char *(*db_filename)(sqlite3*,const char*); 240 | int (*db_readonly)(sqlite3*,const char*); 241 | int (*db_release_memory)(sqlite3*); 242 | const char *(*errstr)(int); 243 | int (*stmt_busy)(sqlite3_stmt*); 244 | int (*stmt_readonly)(sqlite3_stmt*); 245 | int (*stricmp)(const char*,const char*); 246 | int (*uri_boolean)(const char*,const char*,int); 247 | sqlite3_int64 (*uri_int64)(const char*,const char*,sqlite3_int64); 248 | const char *(*uri_parameter)(const char*,const char*); 249 | char *(*xvsnprintf)(int,char*,const char*,va_list); 250 | int (*wal_checkpoint_v2)(sqlite3*,const char*,int,int*,int*); 251 | /* Version 3.8.7 and later */ 252 | int (*auto_extension)(void(*)(void)); 253 | int (*bind_blob64)(sqlite3_stmt*,int,const void*,sqlite3_uint64, 254 | void(*)(void*)); 255 | int (*bind_text64)(sqlite3_stmt*,int,const char*,sqlite3_uint64, 256 | void(*)(void*),unsigned char); 257 | int (*cancel_auto_extension)(void(*)(void)); 258 | int (*load_extension)(sqlite3*,const char*,const char*,char**); 259 | void *(*malloc64)(sqlite3_uint64); 260 | sqlite3_uint64 (*msize)(void*); 261 | void *(*realloc64)(void*,sqlite3_uint64); 262 | void (*reset_auto_extension)(void); 263 | void (*result_blob64)(sqlite3_context*,const void*,sqlite3_uint64, 264 | void(*)(void*)); 265 | void (*result_text64)(sqlite3_context*,const char*,sqlite3_uint64, 266 | void(*)(void*), unsigned char); 267 | int (*strglob)(const char*,const char*); 268 | /* Version 3.8.11 and later */ 269 | sqlite3_value *(*value_dup)(const sqlite3_value*); 270 | void (*value_free)(sqlite3_value*); 271 | int (*result_zeroblob64)(sqlite3_context*,sqlite3_uint64); 272 | int (*bind_zeroblob64)(sqlite3_stmt*, int, sqlite3_uint64); 273 | /* Version 3.9.0 and later */ 274 | unsigned int (*value_subtype)(sqlite3_value*); 275 | void (*result_subtype)(sqlite3_context*,unsigned int); 276 | /* Version 3.10.0 and later */ 277 | int (*status64)(int,sqlite3_int64*,sqlite3_int64*,int); 278 | int (*strlike)(const char*,const char*,unsigned int); 279 | int (*db_cacheflush)(sqlite3*); 280 | /* Version 3.12.0 and later */ 281 | int (*system_errno)(sqlite3*); 282 | /* Version 3.14.0 and later */ 283 | int (*trace_v2)(sqlite3*,unsigned,int(*)(unsigned,void*,void*,void*),void*); 284 | char *(*expanded_sql)(sqlite3_stmt*); 285 | /* Version 3.18.0 and later */ 286 | void (*set_last_insert_rowid)(sqlite3*,sqlite3_int64); 287 | /* Version 3.20.0 and later */ 288 | int (*prepare_v3)(sqlite3*,const char*,int,unsigned int, 289 | sqlite3_stmt**,const char**); 290 | int (*prepare16_v3)(sqlite3*,const void*,int,unsigned int, 291 | sqlite3_stmt**,const void**); 292 | int (*bind_pointer)(sqlite3_stmt*,int,void*,const char*,void(*)(void*)); 293 | void (*result_pointer)(sqlite3_context*,void*,const char*,void(*)(void*)); 294 | void *(*value_pointer)(sqlite3_value*,const char*); 295 | int (*vtab_nochange)(sqlite3_context*); 296 | int (*value_nochange)(sqlite3_value*); 297 | const char *(*vtab_collation)(sqlite3_index_info*,int); 298 | /* Version 3.24.0 and later */ 299 | int (*keyword_count)(void); 300 | int (*keyword_name)(int,const char**,int*); 301 | int (*keyword_check)(const char*,int); 302 | sqlite3_str *(*str_new)(sqlite3*); 303 | char *(*str_finish)(sqlite3_str*); 304 | void (*str_appendf)(sqlite3_str*, const char *zFormat, ...); 305 | void (*str_vappendf)(sqlite3_str*, const char *zFormat, va_list); 306 | void (*str_append)(sqlite3_str*, const char *zIn, int N); 307 | void (*str_appendall)(sqlite3_str*, const char *zIn); 308 | void (*str_appendchar)(sqlite3_str*, int N, char C); 309 | void (*str_reset)(sqlite3_str*); 310 | int (*str_errcode)(sqlite3_str*); 311 | int (*str_length)(sqlite3_str*); 312 | char *(*str_value)(sqlite3_str*); 313 | /* Version 3.25.0 and later */ 314 | int (*create_window_function)(sqlite3*,const char*,int,int,void*, 315 | void (*xStep)(sqlite3_context*,int,sqlite3_value**), 316 | void (*xFinal)(sqlite3_context*), 317 | void (*xValue)(sqlite3_context*), 318 | void (*xInv)(sqlite3_context*,int,sqlite3_value**), 319 | void(*xDestroy)(void*)); 320 | /* Version 3.26.0 and later */ 321 | const char *(*normalized_sql)(sqlite3_stmt*); 322 | /* Version 3.28.0 and later */ 323 | int (*stmt_isexplain)(sqlite3_stmt*); 324 | int (*value_frombind)(sqlite3_value*); 325 | /* Version 3.30.0 and later */ 326 | int (*drop_modules)(sqlite3*,const char**); 327 | /* Version 3.31.0 and later */ 328 | sqlite3_int64 (*hard_heap_limit64)(sqlite3_int64); 329 | const char *(*uri_key)(const char*,int); 330 | const char *(*filename_database)(const char*); 331 | const char *(*filename_journal)(const char*); 332 | const char *(*filename_wal)(const char*); 333 | /* Version 3.32.0 and later */ 334 | const char *(*create_filename)(const char*,const char*,const char*, 335 | int,const char**); 336 | void (*free_filename)(const char*); 337 | sqlite3_file *(*database_file_object)(const char*); 338 | /* Version 3.34.0 and later */ 339 | int (*txn_state)(sqlite3*,const char*); 340 | /* Version 3.36.1 and later */ 341 | sqlite3_int64 (*changes64)(sqlite3*); 342 | sqlite3_int64 (*total_changes64)(sqlite3*); 343 | /* Version 3.37.0 and later */ 344 | int (*autovacuum_pages)(sqlite3*, 345 | unsigned int(*)(void*,const char*,unsigned int,unsigned int,unsigned int), 346 | void*, void(*)(void*)); 347 | /* Version 3.38.0 and later */ 348 | int (*error_offset)(sqlite3*); 349 | int (*vtab_rhs_value)(sqlite3_index_info*,int,sqlite3_value**); 350 | int (*vtab_distinct)(sqlite3_index_info*); 351 | int (*vtab_in)(sqlite3_index_info*,int,int); 352 | int (*vtab_in_first)(sqlite3_value*,sqlite3_value**); 353 | int (*vtab_in_next)(sqlite3_value*,sqlite3_value**); 354 | /* Version 3.39.0 and later */ 355 | int (*deserialize)(sqlite3*,const char*,unsigned char*, 356 | sqlite3_int64,sqlite3_int64,unsigned); 357 | unsigned char *(*serialize)(sqlite3*,const char *,sqlite3_int64*, 358 | unsigned int); 359 | const char *(*db_name)(sqlite3*,int); 360 | /* Version 3.40.0 and later */ 361 | int (*value_encoding)(sqlite3_value*); 362 | /* Version 3.41.0 and later */ 363 | int (*is_interrupted)(sqlite3*); 364 | /* Version 3.43.0 and later */ 365 | int (*stmt_explain)(sqlite3_stmt*,int); 366 | /* Version 3.44.0 and later */ 367 | void *(*get_clientdata)(sqlite3*,const char*); 368 | int (*set_clientdata)(sqlite3*, const char*, void*, void(*)(void*)); 369 | }; 370 | 371 | /* 372 | ** This is the function signature used for all extension entry points. It 373 | ** is also defined in the file "loadext.c". 374 | */ 375 | typedef int (*sqlite3_loadext_entry)( 376 | sqlite3 *db, /* Handle to the database. */ 377 | char **pzErrMsg, /* Used to set error string on failure. */ 378 | const sqlite3_api_routines *pThunk /* Extension API function pointers. */ 379 | ); 380 | 381 | /* 382 | ** The following macros redefine the API routines so that they are 383 | ** redirected through the global sqlite3_api structure. 384 | ** 385 | ** This header file is also used by the loadext.c source file 386 | ** (part of the main SQLite library - not an extension) so that 387 | ** it can get access to the sqlite3_api_routines structure 388 | ** definition. But the main library does not want to redefine 389 | ** the API. So the redefinition macros are only valid if the 390 | ** SQLITE_CORE macros is undefined. 391 | */ 392 | #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) 393 | #define sqlite3_aggregate_context sqlite3_api->aggregate_context 394 | #ifndef SQLITE_OMIT_DEPRECATED 395 | #define sqlite3_aggregate_count sqlite3_api->aggregate_count 396 | #endif 397 | #define sqlite3_bind_blob sqlite3_api->bind_blob 398 | #define sqlite3_bind_double sqlite3_api->bind_double 399 | #define sqlite3_bind_int sqlite3_api->bind_int 400 | #define sqlite3_bind_int64 sqlite3_api->bind_int64 401 | #define sqlite3_bind_null sqlite3_api->bind_null 402 | #define sqlite3_bind_parameter_count sqlite3_api->bind_parameter_count 403 | #define sqlite3_bind_parameter_index sqlite3_api->bind_parameter_index 404 | #define sqlite3_bind_parameter_name sqlite3_api->bind_parameter_name 405 | #define sqlite3_bind_text sqlite3_api->bind_text 406 | #define sqlite3_bind_text16 sqlite3_api->bind_text16 407 | #define sqlite3_bind_value sqlite3_api->bind_value 408 | #define sqlite3_busy_handler sqlite3_api->busy_handler 409 | #define sqlite3_busy_timeout sqlite3_api->busy_timeout 410 | #define sqlite3_changes sqlite3_api->changes 411 | #define sqlite3_close sqlite3_api->close 412 | #define sqlite3_collation_needed sqlite3_api->collation_needed 413 | #define sqlite3_collation_needed16 sqlite3_api->collation_needed16 414 | #define sqlite3_column_blob sqlite3_api->column_blob 415 | #define sqlite3_column_bytes sqlite3_api->column_bytes 416 | #define sqlite3_column_bytes16 sqlite3_api->column_bytes16 417 | #define sqlite3_column_count sqlite3_api->column_count 418 | #define sqlite3_column_database_name sqlite3_api->column_database_name 419 | #define sqlite3_column_database_name16 sqlite3_api->column_database_name16 420 | #define sqlite3_column_decltype sqlite3_api->column_decltype 421 | #define sqlite3_column_decltype16 sqlite3_api->column_decltype16 422 | #define sqlite3_column_double sqlite3_api->column_double 423 | #define sqlite3_column_int sqlite3_api->column_int 424 | #define sqlite3_column_int64 sqlite3_api->column_int64 425 | #define sqlite3_column_name sqlite3_api->column_name 426 | #define sqlite3_column_name16 sqlite3_api->column_name16 427 | #define sqlite3_column_origin_name sqlite3_api->column_origin_name 428 | #define sqlite3_column_origin_name16 sqlite3_api->column_origin_name16 429 | #define sqlite3_column_table_name sqlite3_api->column_table_name 430 | #define sqlite3_column_table_name16 sqlite3_api->column_table_name16 431 | #define sqlite3_column_text sqlite3_api->column_text 432 | #define sqlite3_column_text16 sqlite3_api->column_text16 433 | #define sqlite3_column_type sqlite3_api->column_type 434 | #define sqlite3_column_value sqlite3_api->column_value 435 | #define sqlite3_commit_hook sqlite3_api->commit_hook 436 | #define sqlite3_complete sqlite3_api->complete 437 | #define sqlite3_complete16 sqlite3_api->complete16 438 | #define sqlite3_create_collation sqlite3_api->create_collation 439 | #define sqlite3_create_collation16 sqlite3_api->create_collation16 440 | #define sqlite3_create_function sqlite3_api->create_function 441 | #define sqlite3_create_function16 sqlite3_api->create_function16 442 | #define sqlite3_create_module sqlite3_api->create_module 443 | #define sqlite3_create_module_v2 sqlite3_api->create_module_v2 444 | #define sqlite3_data_count sqlite3_api->data_count 445 | #define sqlite3_db_handle sqlite3_api->db_handle 446 | #define sqlite3_declare_vtab sqlite3_api->declare_vtab 447 | #define sqlite3_enable_shared_cache sqlite3_api->enable_shared_cache 448 | #define sqlite3_errcode sqlite3_api->errcode 449 | #define sqlite3_errmsg sqlite3_api->errmsg 450 | #define sqlite3_errmsg16 sqlite3_api->errmsg16 451 | #define sqlite3_exec sqlite3_api->exec 452 | #ifndef SQLITE_OMIT_DEPRECATED 453 | #define sqlite3_expired sqlite3_api->expired 454 | #endif 455 | #define sqlite3_finalize sqlite3_api->finalize 456 | #define sqlite3_free sqlite3_api->free 457 | #define sqlite3_free_table sqlite3_api->free_table 458 | #define sqlite3_get_autocommit sqlite3_api->get_autocommit 459 | #define sqlite3_get_auxdata sqlite3_api->get_auxdata 460 | #define sqlite3_get_table sqlite3_api->get_table 461 | #ifndef SQLITE_OMIT_DEPRECATED 462 | #define sqlite3_global_recover sqlite3_api->global_recover 463 | #endif 464 | #define sqlite3_interrupt sqlite3_api->interruptx 465 | #define sqlite3_last_insert_rowid sqlite3_api->last_insert_rowid 466 | #define sqlite3_libversion sqlite3_api->libversion 467 | #define sqlite3_libversion_number sqlite3_api->libversion_number 468 | #define sqlite3_malloc sqlite3_api->malloc 469 | #define sqlite3_mprintf sqlite3_api->mprintf 470 | #define sqlite3_open sqlite3_api->open 471 | #define sqlite3_open16 sqlite3_api->open16 472 | #define sqlite3_prepare sqlite3_api->prepare 473 | #define sqlite3_prepare16 sqlite3_api->prepare16 474 | #define sqlite3_prepare_v2 sqlite3_api->prepare_v2 475 | #define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2 476 | #define sqlite3_profile sqlite3_api->profile 477 | #define sqlite3_progress_handler sqlite3_api->progress_handler 478 | #define sqlite3_realloc sqlite3_api->realloc 479 | #define sqlite3_reset sqlite3_api->reset 480 | #define sqlite3_result_blob sqlite3_api->result_blob 481 | #define sqlite3_result_double sqlite3_api->result_double 482 | #define sqlite3_result_error sqlite3_api->result_error 483 | #define sqlite3_result_error16 sqlite3_api->result_error16 484 | #define sqlite3_result_int sqlite3_api->result_int 485 | #define sqlite3_result_int64 sqlite3_api->result_int64 486 | #define sqlite3_result_null sqlite3_api->result_null 487 | #define sqlite3_result_text sqlite3_api->result_text 488 | #define sqlite3_result_text16 sqlite3_api->result_text16 489 | #define sqlite3_result_text16be sqlite3_api->result_text16be 490 | #define sqlite3_result_text16le sqlite3_api->result_text16le 491 | #define sqlite3_result_value sqlite3_api->result_value 492 | #define sqlite3_rollback_hook sqlite3_api->rollback_hook 493 | #define sqlite3_set_authorizer sqlite3_api->set_authorizer 494 | #define sqlite3_set_auxdata sqlite3_api->set_auxdata 495 | #define sqlite3_snprintf sqlite3_api->xsnprintf 496 | #define sqlite3_step sqlite3_api->step 497 | #define sqlite3_table_column_metadata sqlite3_api->table_column_metadata 498 | #define sqlite3_thread_cleanup sqlite3_api->thread_cleanup 499 | #define sqlite3_total_changes sqlite3_api->total_changes 500 | #define sqlite3_trace sqlite3_api->trace 501 | #ifndef SQLITE_OMIT_DEPRECATED 502 | #define sqlite3_transfer_bindings sqlite3_api->transfer_bindings 503 | #endif 504 | #define sqlite3_update_hook sqlite3_api->update_hook 505 | #define sqlite3_user_data sqlite3_api->user_data 506 | #define sqlite3_value_blob sqlite3_api->value_blob 507 | #define sqlite3_value_bytes sqlite3_api->value_bytes 508 | #define sqlite3_value_bytes16 sqlite3_api->value_bytes16 509 | #define sqlite3_value_double sqlite3_api->value_double 510 | #define sqlite3_value_int sqlite3_api->value_int 511 | #define sqlite3_value_int64 sqlite3_api->value_int64 512 | #define sqlite3_value_numeric_type sqlite3_api->value_numeric_type 513 | #define sqlite3_value_text sqlite3_api->value_text 514 | #define sqlite3_value_text16 sqlite3_api->value_text16 515 | #define sqlite3_value_text16be sqlite3_api->value_text16be 516 | #define sqlite3_value_text16le sqlite3_api->value_text16le 517 | #define sqlite3_value_type sqlite3_api->value_type 518 | #define sqlite3_vmprintf sqlite3_api->vmprintf 519 | #define sqlite3_vsnprintf sqlite3_api->xvsnprintf 520 | #define sqlite3_overload_function sqlite3_api->overload_function 521 | #define sqlite3_prepare_v2 sqlite3_api->prepare_v2 522 | #define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2 523 | #define sqlite3_clear_bindings sqlite3_api->clear_bindings 524 | #define sqlite3_bind_zeroblob sqlite3_api->bind_zeroblob 525 | #define sqlite3_blob_bytes sqlite3_api->blob_bytes 526 | #define sqlite3_blob_close sqlite3_api->blob_close 527 | #define sqlite3_blob_open sqlite3_api->blob_open 528 | #define sqlite3_blob_read sqlite3_api->blob_read 529 | #define sqlite3_blob_write sqlite3_api->blob_write 530 | #define sqlite3_create_collation_v2 sqlite3_api->create_collation_v2 531 | #define sqlite3_file_control sqlite3_api->file_control 532 | #define sqlite3_memory_highwater sqlite3_api->memory_highwater 533 | #define sqlite3_memory_used sqlite3_api->memory_used 534 | #define sqlite3_mutex_alloc sqlite3_api->mutex_alloc 535 | #define sqlite3_mutex_enter sqlite3_api->mutex_enter 536 | #define sqlite3_mutex_free sqlite3_api->mutex_free 537 | #define sqlite3_mutex_leave sqlite3_api->mutex_leave 538 | #define sqlite3_mutex_try sqlite3_api->mutex_try 539 | #define sqlite3_open_v2 sqlite3_api->open_v2 540 | #define sqlite3_release_memory sqlite3_api->release_memory 541 | #define sqlite3_result_error_nomem sqlite3_api->result_error_nomem 542 | #define sqlite3_result_error_toobig sqlite3_api->result_error_toobig 543 | #define sqlite3_sleep sqlite3_api->sleep 544 | #define sqlite3_soft_heap_limit sqlite3_api->soft_heap_limit 545 | #define sqlite3_vfs_find sqlite3_api->vfs_find 546 | #define sqlite3_vfs_register sqlite3_api->vfs_register 547 | #define sqlite3_vfs_unregister sqlite3_api->vfs_unregister 548 | #define sqlite3_threadsafe sqlite3_api->xthreadsafe 549 | #define sqlite3_result_zeroblob sqlite3_api->result_zeroblob 550 | #define sqlite3_result_error_code sqlite3_api->result_error_code 551 | #define sqlite3_test_control sqlite3_api->test_control 552 | #define sqlite3_randomness sqlite3_api->randomness 553 | #define sqlite3_context_db_handle sqlite3_api->context_db_handle 554 | #define sqlite3_extended_result_codes sqlite3_api->extended_result_codes 555 | #define sqlite3_limit sqlite3_api->limit 556 | #define sqlite3_next_stmt sqlite3_api->next_stmt 557 | #define sqlite3_sql sqlite3_api->sql 558 | #define sqlite3_status sqlite3_api->status 559 | #define sqlite3_backup_finish sqlite3_api->backup_finish 560 | #define sqlite3_backup_init sqlite3_api->backup_init 561 | #define sqlite3_backup_pagecount sqlite3_api->backup_pagecount 562 | #define sqlite3_backup_remaining sqlite3_api->backup_remaining 563 | #define sqlite3_backup_step sqlite3_api->backup_step 564 | #define sqlite3_compileoption_get sqlite3_api->compileoption_get 565 | #define sqlite3_compileoption_used sqlite3_api->compileoption_used 566 | #define sqlite3_create_function_v2 sqlite3_api->create_function_v2 567 | #define sqlite3_db_config sqlite3_api->db_config 568 | #define sqlite3_db_mutex sqlite3_api->db_mutex 569 | #define sqlite3_db_status sqlite3_api->db_status 570 | #define sqlite3_extended_errcode sqlite3_api->extended_errcode 571 | #define sqlite3_log sqlite3_api->log 572 | #define sqlite3_soft_heap_limit64 sqlite3_api->soft_heap_limit64 573 | #define sqlite3_sourceid sqlite3_api->sourceid 574 | #define sqlite3_stmt_status sqlite3_api->stmt_status 575 | #define sqlite3_strnicmp sqlite3_api->strnicmp 576 | #define sqlite3_unlock_notify sqlite3_api->unlock_notify 577 | #define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint 578 | #define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint 579 | #define sqlite3_wal_hook sqlite3_api->wal_hook 580 | #define sqlite3_blob_reopen sqlite3_api->blob_reopen 581 | #define sqlite3_vtab_config sqlite3_api->vtab_config 582 | #define sqlite3_vtab_on_conflict sqlite3_api->vtab_on_conflict 583 | /* Version 3.7.16 and later */ 584 | #define sqlite3_close_v2 sqlite3_api->close_v2 585 | #define sqlite3_db_filename sqlite3_api->db_filename 586 | #define sqlite3_db_readonly sqlite3_api->db_readonly 587 | #define sqlite3_db_release_memory sqlite3_api->db_release_memory 588 | #define sqlite3_errstr sqlite3_api->errstr 589 | #define sqlite3_stmt_busy sqlite3_api->stmt_busy 590 | #define sqlite3_stmt_readonly sqlite3_api->stmt_readonly 591 | #define sqlite3_stricmp sqlite3_api->stricmp 592 | #define sqlite3_uri_boolean sqlite3_api->uri_boolean 593 | #define sqlite3_uri_int64 sqlite3_api->uri_int64 594 | #define sqlite3_uri_parameter sqlite3_api->uri_parameter 595 | #define sqlite3_uri_vsnprintf sqlite3_api->xvsnprintf 596 | #define sqlite3_wal_checkpoint_v2 sqlite3_api->wal_checkpoint_v2 597 | /* Version 3.8.7 and later */ 598 | #define sqlite3_auto_extension sqlite3_api->auto_extension 599 | #define sqlite3_bind_blob64 sqlite3_api->bind_blob64 600 | #define sqlite3_bind_text64 sqlite3_api->bind_text64 601 | #define sqlite3_cancel_auto_extension sqlite3_api->cancel_auto_extension 602 | #define sqlite3_load_extension sqlite3_api->load_extension 603 | #define sqlite3_malloc64 sqlite3_api->malloc64 604 | #define sqlite3_msize sqlite3_api->msize 605 | #define sqlite3_realloc64 sqlite3_api->realloc64 606 | #define sqlite3_reset_auto_extension sqlite3_api->reset_auto_extension 607 | #define sqlite3_result_blob64 sqlite3_api->result_blob64 608 | #define sqlite3_result_text64 sqlite3_api->result_text64 609 | #define sqlite3_strglob sqlite3_api->strglob 610 | /* Version 3.8.11 and later */ 611 | #define sqlite3_value_dup sqlite3_api->value_dup 612 | #define sqlite3_value_free sqlite3_api->value_free 613 | #define sqlite3_result_zeroblob64 sqlite3_api->result_zeroblob64 614 | #define sqlite3_bind_zeroblob64 sqlite3_api->bind_zeroblob64 615 | /* Version 3.9.0 and later */ 616 | #define sqlite3_value_subtype sqlite3_api->value_subtype 617 | #define sqlite3_result_subtype sqlite3_api->result_subtype 618 | /* Version 3.10.0 and later */ 619 | #define sqlite3_status64 sqlite3_api->status64 620 | #define sqlite3_strlike sqlite3_api->strlike 621 | #define sqlite3_db_cacheflush sqlite3_api->db_cacheflush 622 | /* Version 3.12.0 and later */ 623 | #define sqlite3_system_errno sqlite3_api->system_errno 624 | /* Version 3.14.0 and later */ 625 | #define sqlite3_trace_v2 sqlite3_api->trace_v2 626 | #define sqlite3_expanded_sql sqlite3_api->expanded_sql 627 | /* Version 3.18.0 and later */ 628 | #define sqlite3_set_last_insert_rowid sqlite3_api->set_last_insert_rowid 629 | /* Version 3.20.0 and later */ 630 | #define sqlite3_prepare_v3 sqlite3_api->prepare_v3 631 | #define sqlite3_prepare16_v3 sqlite3_api->prepare16_v3 632 | #define sqlite3_bind_pointer sqlite3_api->bind_pointer 633 | #define sqlite3_result_pointer sqlite3_api->result_pointer 634 | #define sqlite3_value_pointer sqlite3_api->value_pointer 635 | /* Version 3.22.0 and later */ 636 | #define sqlite3_vtab_nochange sqlite3_api->vtab_nochange 637 | #define sqlite3_value_nochange sqlite3_api->value_nochange 638 | #define sqlite3_vtab_collation sqlite3_api->vtab_collation 639 | /* Version 3.24.0 and later */ 640 | #define sqlite3_keyword_count sqlite3_api->keyword_count 641 | #define sqlite3_keyword_name sqlite3_api->keyword_name 642 | #define sqlite3_keyword_check sqlite3_api->keyword_check 643 | #define sqlite3_str_new sqlite3_api->str_new 644 | #define sqlite3_str_finish sqlite3_api->str_finish 645 | #define sqlite3_str_appendf sqlite3_api->str_appendf 646 | #define sqlite3_str_vappendf sqlite3_api->str_vappendf 647 | #define sqlite3_str_append sqlite3_api->str_append 648 | #define sqlite3_str_appendall sqlite3_api->str_appendall 649 | #define sqlite3_str_appendchar sqlite3_api->str_appendchar 650 | #define sqlite3_str_reset sqlite3_api->str_reset 651 | #define sqlite3_str_errcode sqlite3_api->str_errcode 652 | #define sqlite3_str_length sqlite3_api->str_length 653 | #define sqlite3_str_value sqlite3_api->str_value 654 | /* Version 3.25.0 and later */ 655 | #define sqlite3_create_window_function sqlite3_api->create_window_function 656 | /* Version 3.26.0 and later */ 657 | #define sqlite3_normalized_sql sqlite3_api->normalized_sql 658 | /* Version 3.28.0 and later */ 659 | #define sqlite3_stmt_isexplain sqlite3_api->stmt_isexplain 660 | #define sqlite3_value_frombind sqlite3_api->value_frombind 661 | /* Version 3.30.0 and later */ 662 | #define sqlite3_drop_modules sqlite3_api->drop_modules 663 | /* Version 3.31.0 and later */ 664 | #define sqlite3_hard_heap_limit64 sqlite3_api->hard_heap_limit64 665 | #define sqlite3_uri_key sqlite3_api->uri_key 666 | #define sqlite3_filename_database sqlite3_api->filename_database 667 | #define sqlite3_filename_journal sqlite3_api->filename_journal 668 | #define sqlite3_filename_wal sqlite3_api->filename_wal 669 | /* Version 3.32.0 and later */ 670 | #define sqlite3_create_filename sqlite3_api->create_filename 671 | #define sqlite3_free_filename sqlite3_api->free_filename 672 | #define sqlite3_database_file_object sqlite3_api->database_file_object 673 | /* Version 3.34.0 and later */ 674 | #define sqlite3_txn_state sqlite3_api->txn_state 675 | /* Version 3.36.1 and later */ 676 | #define sqlite3_changes64 sqlite3_api->changes64 677 | #define sqlite3_total_changes64 sqlite3_api->total_changes64 678 | /* Version 3.37.0 and later */ 679 | #define sqlite3_autovacuum_pages sqlite3_api->autovacuum_pages 680 | /* Version 3.38.0 and later */ 681 | #define sqlite3_error_offset sqlite3_api->error_offset 682 | #define sqlite3_vtab_rhs_value sqlite3_api->vtab_rhs_value 683 | #define sqlite3_vtab_distinct sqlite3_api->vtab_distinct 684 | #define sqlite3_vtab_in sqlite3_api->vtab_in 685 | #define sqlite3_vtab_in_first sqlite3_api->vtab_in_first 686 | #define sqlite3_vtab_in_next sqlite3_api->vtab_in_next 687 | /* Version 3.39.0 and later */ 688 | #ifndef SQLITE_OMIT_DESERIALIZE 689 | #define sqlite3_deserialize sqlite3_api->deserialize 690 | #define sqlite3_serialize sqlite3_api->serialize 691 | #endif 692 | #define sqlite3_db_name sqlite3_api->db_name 693 | /* Version 3.40.0 and later */ 694 | #define sqlite3_value_encoding sqlite3_api->value_encoding 695 | /* Version 3.41.0 and later */ 696 | #define sqlite3_is_interrupted sqlite3_api->is_interrupted 697 | /* Version 3.43.0 and later */ 698 | #define sqlite3_stmt_explain sqlite3_api->stmt_explain 699 | /* Version 3.44.0 and later */ 700 | #define sqlite3_get_clientdata sqlite3_api->get_clientdata 701 | #define sqlite3_set_clientdata sqlite3_api->set_clientdata 702 | #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ 703 | 704 | #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) 705 | /* This case when the file really is being compiled as a loadable 706 | ** extension */ 707 | # define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0; 708 | # define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v; 709 | # define SQLITE_EXTENSION_INIT3 \ 710 | extern const sqlite3_api_routines *sqlite3_api; 711 | #else 712 | /* This case when the file is being statically linked into the 713 | ** application */ 714 | # define SQLITE_EXTENSION_INIT1 /*no-op*/ 715 | # define SQLITE_EXTENSION_INIT2(v) (void)v; /* unused parameter */ 716 | # define SQLITE_EXTENSION_INIT3 /*no-op*/ 717 | #endif 718 | 719 | #endif /* SQLITE3EXT_H */ 720 | --------------------------------------------------------------------------------