├── .gitattributes ├── .github └── workflows │ └── rust.yml ├── .gitignore ├── .rustfmt.toml ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── README.md ├── build.rs ├── examples ├── memvfs.rs └── test_memvfs.sql ├── renovate.json ├── sqlite3 ├── sqlite3.h └── sqlite3ext.h └── src ├── flags.rs ├── lib.rs ├── logger.rs ├── mock.rs └── vfs.rs /.gitattributes: -------------------------------------------------------------------------------- 1 | sqlite3.h linguist-vendored 2 | sqlite3ext.h linguist-vendored 3 | -------------------------------------------------------------------------------- /.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@888c2e1ea69ab0d4330cbf0af1ecc7b68f368cc1 # v1 26 | with: 27 | toolchain: 1.85 28 | components: clippy, rustfmt 29 | 30 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 31 | with: 32 | token: ${{secrets.GITHUB_TOKEN}} 33 | 34 | - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2 35 | 36 | - uses: taiki-e/install-action@6c6479b49816fcc0975a31af977bdc1f847c2920 # v2 37 | with: 38 | tool: cargo-nextest 39 | 40 | - name: Build 41 | run: cargo build 42 | 43 | - name: Test 44 | run: cargo nextest run 45 | 46 | - name: Test memvfs 47 | run: | 48 | cargo build --example memvfs --features dynamic 49 | cat examples/test_memvfs.sql | sqlite3 50 | 51 | - name: Clippy 52 | uses: auguwu/clippy-action@94a9ff2f6920180b89e5c03d121d0af04a9d3e03 # 1.4.0 53 | with: 54 | token: ${{secrets.GITHUB_TOKEN}} 55 | 56 | - name: Cargo fmt 57 | run: cargo fmt --check 58 | 59 | - name: Cargo doctests 60 | run: cargo test --doc 61 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | # https://rust-lang.github.io/rustfmt 2 | max_width = 100 3 | struct_lit_width = 40 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes will be documented in this file. 4 | 5 | ## 0.3.0 - 2025-05-26 6 | 7 | - `register_dynamic` and `register_static` now require the VFS name to be passed in as a CString. 8 | 9 | ## 0.2.0 - 2025-05-19 10 | 11 | - `PragmaErr` now requires an explicit error code and external construction. 12 | 13 | ## 0.1.2 - 2025-04-09 14 | 15 | - updating dependencies 16 | 17 | ## 0.1.1 - 2025-04-09 18 | 19 | - bug: support cross-compilation to arm 20 | 21 | ## 0.1.0 - 2025-03-29 22 | 23 | - Initial release 24 | -------------------------------------------------------------------------------- /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.3" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "autocfg" 16 | version = "1.4.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 19 | 20 | [[package]] 21 | name = "bindgen" 22 | version = "0.71.1" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" 25 | dependencies = [ 26 | "bitflags", 27 | "cexpr", 28 | "clang-sys", 29 | "itertools", 30 | "proc-macro2", 31 | "quote", 32 | "regex", 33 | "rustc-hash", 34 | "shlex", 35 | "syn", 36 | ] 37 | 38 | [[package]] 39 | name = "bitflags" 40 | version = "2.9.1" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" 43 | 44 | [[package]] 45 | name = "cc" 46 | version = "1.2.24" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "16595d3be041c03b09d08d0858631facccee9221e579704070e6e9e4915d3bc7" 49 | dependencies = [ 50 | "shlex", 51 | ] 52 | 53 | [[package]] 54 | name = "cexpr" 55 | version = "0.6.0" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" 58 | dependencies = [ 59 | "nom", 60 | ] 61 | 62 | [[package]] 63 | name = "cfg-if" 64 | version = "1.0.0" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 67 | 68 | [[package]] 69 | name = "clang-sys" 70 | version = "1.8.1" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" 73 | dependencies = [ 74 | "glob", 75 | "libc", 76 | ] 77 | 78 | [[package]] 79 | name = "either" 80 | version = "1.15.0" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" 83 | 84 | [[package]] 85 | name = "fallible-iterator" 86 | version = "0.3.0" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" 89 | 90 | [[package]] 91 | name = "fallible-streaming-iterator" 92 | version = "0.1.9" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" 95 | 96 | [[package]] 97 | name = "foldhash" 98 | version = "0.1.5" 99 | source = "registry+https://github.com/rust-lang/crates.io-index" 100 | checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" 101 | 102 | [[package]] 103 | name = "glob" 104 | version = "0.3.2" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" 107 | 108 | [[package]] 109 | name = "hashbrown" 110 | version = "0.15.3" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" 113 | dependencies = [ 114 | "foldhash", 115 | ] 116 | 117 | [[package]] 118 | name = "hashlink" 119 | version = "0.10.0" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" 122 | dependencies = [ 123 | "hashbrown", 124 | ] 125 | 126 | [[package]] 127 | name = "itertools" 128 | version = "0.13.0" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" 131 | dependencies = [ 132 | "either", 133 | ] 134 | 135 | [[package]] 136 | name = "libc" 137 | version = "0.2.172" 138 | source = "registry+https://github.com/rust-lang/crates.io-index" 139 | checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" 140 | 141 | [[package]] 142 | name = "libsqlite3-sys" 143 | version = "0.34.0" 144 | source = "registry+https://github.com/rust-lang/crates.io-index" 145 | checksum = "91632f3b4fb6bd1d72aa3d78f41ffecfcf2b1a6648d8c241dbe7dbfaf4875e15" 146 | dependencies = [ 147 | "cc", 148 | "pkg-config", 149 | "vcpkg", 150 | ] 151 | 152 | [[package]] 153 | name = "lock_api" 154 | version = "0.4.12" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" 157 | dependencies = [ 158 | "autocfg", 159 | "scopeguard", 160 | ] 161 | 162 | [[package]] 163 | name = "log" 164 | version = "0.4.27" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" 167 | 168 | [[package]] 169 | name = "memchr" 170 | version = "2.7.4" 171 | source = "registry+https://github.com/rust-lang/crates.io-index" 172 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 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.3" 193 | source = "registry+https://github.com/rust-lang/crates.io-index" 194 | checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" 195 | dependencies = [ 196 | "lock_api", 197 | "parking_lot_core", 198 | ] 199 | 200 | [[package]] 201 | name = "parking_lot_core" 202 | version = "0.9.10" 203 | source = "registry+https://github.com/rust-lang/crates.io-index" 204 | checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" 205 | dependencies = [ 206 | "cfg-if", 207 | "libc", 208 | "redox_syscall", 209 | "smallvec", 210 | "windows-targets", 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.95" 222 | source = "registry+https://github.com/rust-lang/crates.io-index" 223 | checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" 224 | dependencies = [ 225 | "unicode-ident", 226 | ] 227 | 228 | [[package]] 229 | name = "quote" 230 | version = "1.0.40" 231 | source = "registry+https://github.com/rust-lang/crates.io-index" 232 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 233 | dependencies = [ 234 | "proc-macro2", 235 | ] 236 | 237 | [[package]] 238 | name = "redox_syscall" 239 | version = "0.5.12" 240 | source = "registry+https://github.com/rust-lang/crates.io-index" 241 | checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" 242 | dependencies = [ 243 | "bitflags", 244 | ] 245 | 246 | [[package]] 247 | name = "regex" 248 | version = "1.11.1" 249 | source = "registry+https://github.com/rust-lang/crates.io-index" 250 | checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 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.9" 261 | source = "registry+https://github.com/rust-lang/crates.io-index" 262 | checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" 263 | dependencies = [ 264 | "aho-corasick", 265 | "memchr", 266 | "regex-syntax", 267 | ] 268 | 269 | [[package]] 270 | name = "regex-syntax" 271 | version = "0.8.5" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 274 | 275 | [[package]] 276 | name = "rusqlite" 277 | version = "0.36.0" 278 | source = "registry+https://github.com/rust-lang/crates.io-index" 279 | checksum = "3de23c3319433716cf134eed225fe9986bc24f63bed9be9f20c329029e672dc7" 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.0" 310 | source = "registry+https://github.com/rust-lang/crates.io-index" 311 | checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" 312 | 313 | [[package]] 314 | name = "sqlite-plugin" 315 | version = "0.3.0" 316 | dependencies = [ 317 | "bindgen", 318 | "log", 319 | "parking_lot", 320 | "rusqlite", 321 | ] 322 | 323 | [[package]] 324 | name = "syn" 325 | version = "2.0.101" 326 | source = "registry+https://github.com/rust-lang/crates.io-index" 327 | checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" 328 | dependencies = [ 329 | "proc-macro2", 330 | "quote", 331 | "unicode-ident", 332 | ] 333 | 334 | [[package]] 335 | name = "unicode-ident" 336 | version = "1.0.18" 337 | source = "registry+https://github.com/rust-lang/crates.io-index" 338 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 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-targets" 348 | version = "0.52.6" 349 | source = "registry+https://github.com/rust-lang/crates.io-index" 350 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 351 | dependencies = [ 352 | "windows_aarch64_gnullvm", 353 | "windows_aarch64_msvc", 354 | "windows_i686_gnu", 355 | "windows_i686_gnullvm", 356 | "windows_i686_msvc", 357 | "windows_x86_64_gnu", 358 | "windows_x86_64_gnullvm", 359 | "windows_x86_64_msvc", 360 | ] 361 | 362 | [[package]] 363 | name = "windows_aarch64_gnullvm" 364 | version = "0.52.6" 365 | source = "registry+https://github.com/rust-lang/crates.io-index" 366 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 367 | 368 | [[package]] 369 | name = "windows_aarch64_msvc" 370 | version = "0.52.6" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 373 | 374 | [[package]] 375 | name = "windows_i686_gnu" 376 | version = "0.52.6" 377 | source = "registry+https://github.com/rust-lang/crates.io-index" 378 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 379 | 380 | [[package]] 381 | name = "windows_i686_gnullvm" 382 | version = "0.52.6" 383 | source = "registry+https://github.com/rust-lang/crates.io-index" 384 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 385 | 386 | [[package]] 387 | name = "windows_i686_msvc" 388 | version = "0.52.6" 389 | source = "registry+https://github.com/rust-lang/crates.io-index" 390 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 391 | 392 | [[package]] 393 | name = "windows_x86_64_gnu" 394 | version = "0.52.6" 395 | source = "registry+https://github.com/rust-lang/crates.io-index" 396 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 397 | 398 | [[package]] 399 | name = "windows_x86_64_gnullvm" 400 | version = "0.52.6" 401 | source = "registry+https://github.com/rust-lang/crates.io-index" 402 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 403 | 404 | [[package]] 405 | name = "windows_x86_64_msvc" 406 | version = "0.52.6" 407 | source = "registry+https://github.com/rust-lang/crates.io-index" 408 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 409 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sqlite-plugin" 3 | version = "0.3.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.36.0", features = ["blob", "trace", "bundled"] } 22 | log = { version = "=0.4.27", features = ["std"] } 23 | parking_lot = "=0.12.3" 24 | 25 | [build-dependencies] 26 | bindgen = { version = "0.71", 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | extern crate bindgen; 2 | 3 | use std::env; 4 | use std::path::PathBuf; 5 | 6 | fn main() { 7 | println!("cargo:rerun-if-changed=sqlite3/sqlite3.h"); 8 | println!("cargo:rerun-if-changed=sqlite3/sqlite3ext.h"); 9 | 10 | let vars = bindgen::Builder::default() 11 | .header("sqlite3/sqlite3ext.h") 12 | .allowlist_item("SQLITE_.*") 13 | .use_core() 14 | .default_macro_constant_type(bindgen::MacroTypeVariation::Signed) 15 | .generate() 16 | .expect("Unable to generate bindings"); 17 | 18 | let bindings = bindgen::Builder::default() 19 | .header("sqlite3/sqlite3ext.h") 20 | .blocklist_item("SQLITE_.*") 21 | .use_core() 22 | .default_macro_constant_type(bindgen::MacroTypeVariation::Signed) 23 | .generate() 24 | .expect("Unable to generate bindings"); 25 | 26 | let out_path = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR should be defined")); 27 | vars.write_to_file(out_path.join("vars.rs")) 28 | .expect("Couldn't write vars!"); 29 | bindings 30 | .write_to_file(out_path.join("bindings.rs")) 31 | .expect("Couldn't write bindings!"); 32 | } 33 | -------------------------------------------------------------------------------- /examples/memvfs.rs: -------------------------------------------------------------------------------- 1 | // cargo build --example memvfs --features dynamic 2 | 3 | use std::{ 4 | ffi::{CStr, c_void}, 5 | os::raw::c_char, 6 | sync::Arc, 7 | }; 8 | 9 | use parking_lot::Mutex; 10 | use sqlite_plugin::{ 11 | flags::{AccessFlags, LockLevel, OpenOpts}, 12 | logger::{SqliteLogLevel, SqliteLogger}, 13 | sqlite3_api_routines, vars, 14 | vfs::{Pragma, PragmaErr, RegisterOpts, Vfs, VfsHandle, VfsResult, register_dynamic}, 15 | }; 16 | 17 | #[derive(Debug, Clone)] 18 | struct File { 19 | name: Option, 20 | data: Arc>>, 21 | delete_on_close: bool, 22 | opts: OpenOpts, 23 | } 24 | 25 | impl File { 26 | fn is_named(&self, s: &str) -> bool { 27 | self.name.as_ref().is_some_and(|f| f == s) 28 | } 29 | } 30 | 31 | impl VfsHandle for File { 32 | fn readonly(&self) -> bool { 33 | self.opts.mode().is_readonly() 34 | } 35 | 36 | fn in_memory(&self) -> bool { 37 | true 38 | } 39 | } 40 | 41 | struct MemVfs { 42 | files: Arc>>, 43 | } 44 | 45 | impl Vfs for MemVfs { 46 | type Handle = File; 47 | 48 | fn register_logger(&self, logger: SqliteLogger) { 49 | struct LogCompat { 50 | logger: Mutex, 51 | } 52 | 53 | impl log::Log for LogCompat { 54 | fn enabled(&self, _metadata: &log::Metadata) -> bool { 55 | true 56 | } 57 | 58 | fn log(&self, record: &log::Record) { 59 | let level = match record.level() { 60 | log::Level::Error => SqliteLogLevel::Error, 61 | log::Level::Warn => SqliteLogLevel::Warn, 62 | _ => SqliteLogLevel::Notice, 63 | }; 64 | let msg = format!("{}", record.args()); 65 | self.logger.lock().log(level, msg.as_bytes()); 66 | } 67 | 68 | fn flush(&self) {} 69 | } 70 | 71 | let log = LogCompat { logger: Mutex::new(logger) }; 72 | log::set_boxed_logger(Box::new(log)).expect("failed to setup global logger"); 73 | } 74 | 75 | fn open(&self, path: Option<&str>, opts: OpenOpts) -> VfsResult { 76 | log::debug!("open: path={:?}, opts={:?}", path, opts); 77 | let mode = opts.mode(); 78 | if mode.is_readonly() { 79 | // readonly makes no sense since an in-memory VFS is not backed by 80 | // any pre-existing data. 81 | return Err(vars::SQLITE_CANTOPEN); 82 | } 83 | 84 | if let Some(path) = path { 85 | let mut files = self.files.lock(); 86 | 87 | for file in files.iter() { 88 | if file.is_named(path) { 89 | if mode.must_create() { 90 | return Err(vars::SQLITE_CANTOPEN); 91 | } 92 | return Ok(file.clone()); 93 | } 94 | } 95 | 96 | let file = File { 97 | name: Some(path.to_owned()), 98 | data: Default::default(), 99 | delete_on_close: opts.delete_on_close(), 100 | opts, 101 | }; 102 | files.push(file.clone()); 103 | Ok(file) 104 | } else { 105 | let file = File { 106 | name: None, 107 | data: Default::default(), 108 | delete_on_close: opts.delete_on_close(), 109 | opts, 110 | }; 111 | Ok(file) 112 | } 113 | } 114 | 115 | fn delete(&self, path: &str) -> VfsResult<()> { 116 | log::debug!("delete: path={}", path); 117 | let mut found = false; 118 | self.files.lock().retain(|file| { 119 | if file.is_named(path) { 120 | found = true; 121 | false 122 | } else { 123 | true 124 | } 125 | }); 126 | if !found { 127 | return Err(vars::SQLITE_IOERR_DELETE_NOENT); 128 | } 129 | Ok(()) 130 | } 131 | 132 | fn access(&self, path: &str, flags: AccessFlags) -> VfsResult { 133 | log::debug!("access: path={}, flags={:?}", path, flags); 134 | Ok(self.files.lock().iter().any(|f| f.is_named(path))) 135 | } 136 | 137 | fn file_size(&self, handle: &mut Self::Handle) -> VfsResult { 138 | log::debug!("file_size: file={:?}", handle.name); 139 | Ok(handle.data.lock().len()) 140 | } 141 | 142 | fn truncate(&self, handle: &mut Self::Handle, size: usize) -> VfsResult<()> { 143 | log::debug!("truncate: file={:?}, size={}", handle.name, size); 144 | let mut data = handle.data.lock(); 145 | if size > data.len() { 146 | data.resize(size, 0); 147 | } else { 148 | data.truncate(size); 149 | } 150 | Ok(()) 151 | } 152 | 153 | fn lock(&self, handle: &mut Self::Handle, level: LockLevel) -> VfsResult<()> { 154 | log::debug!("lock: file={:?}, level={:?}", handle.name, level); 155 | Ok(()) 156 | } 157 | 158 | fn unlock(&self, handle: &mut Self::Handle, level: LockLevel) -> VfsResult<()> { 159 | log::debug!("unlock: file={:?}, level={:?}", handle.name, level); 160 | Ok(()) 161 | } 162 | 163 | fn write(&self, handle: &mut Self::Handle, offset: usize, buf: &[u8]) -> VfsResult { 164 | log::debug!( 165 | "write: file={:?}, offset={}, len={}", 166 | handle.name, 167 | offset, 168 | buf.len() 169 | ); 170 | let mut data = handle.data.lock(); 171 | if offset + buf.len() > data.len() { 172 | data.resize(offset + buf.len(), 0); 173 | } 174 | data[offset..offset + buf.len()].copy_from_slice(buf); 175 | Ok(buf.len()) 176 | } 177 | 178 | fn read(&self, handle: &mut Self::Handle, offset: usize, buf: &mut [u8]) -> VfsResult { 179 | log::debug!( 180 | "read: file={:?}, offset={}, len={}", 181 | handle.name, 182 | offset, 183 | buf.len() 184 | ); 185 | let data = handle.data.lock(); 186 | if offset > data.len() { 187 | return Ok(0); 188 | } 189 | let len = buf.len().min(data.len() - offset); 190 | buf[..len].copy_from_slice(&data[offset..offset + len]); 191 | Ok(len) 192 | } 193 | 194 | fn sync(&self, handle: &mut Self::Handle) -> VfsResult<()> { 195 | log::debug!("sync: file={:?}", handle.name); 196 | Ok(()) 197 | } 198 | 199 | fn close(&self, handle: Self::Handle) -> VfsResult<()> { 200 | log::debug!("close: file={:?}", handle.name); 201 | if handle.delete_on_close { 202 | if let Some(ref name) = handle.name { 203 | self.delete(name)?; 204 | } 205 | } 206 | Ok(()) 207 | } 208 | 209 | fn pragma( 210 | &self, 211 | handle: &mut Self::Handle, 212 | pragma: Pragma<'_>, 213 | ) -> Result, PragmaErr> { 214 | log::debug!("pragma: file={:?}, pragma={:?}", handle.name, pragma); 215 | Err(PragmaErr::NotFound) 216 | } 217 | } 218 | 219 | /// This function is called by `SQLite` when the extension is loaded. It registers 220 | /// the memvfs VFS with `SQLite`. 221 | /// # Safety 222 | /// This function should only be called by sqlite's extension loading mechanism. 223 | #[unsafe(no_mangle)] 224 | pub unsafe extern "C" fn sqlite3_memvfs_init( 225 | _db: *mut c_void, 226 | _pz_err_msg: *mut *mut c_char, 227 | p_api: *mut sqlite3_api_routines, 228 | ) -> std::os::raw::c_int { 229 | let vfs = MemVfs { files: Default::default() }; 230 | const MEMVFS_NAME: &CStr = c"mem"; 231 | if let Err(err) = unsafe { 232 | register_dynamic( 233 | p_api, 234 | MEMVFS_NAME.to_owned(), 235 | vfs, 236 | RegisterOpts { make_default: true }, 237 | ) 238 | } { 239 | return err; 240 | } 241 | 242 | // set the log level to trace 243 | log::set_max_level(log::LevelFilter::Trace); 244 | 245 | vars::SQLITE_OK_LOAD_PERMANENTLY 246 | } 247 | -------------------------------------------------------------------------------- /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 | 11 | .databases 12 | .vfsinfo 13 | 14 | CREATE TABLE t1(a, b); 15 | INSERT INTO t1 VALUES(1, 2); 16 | INSERT INTO t1 VALUES(3, 4); 17 | SELECT * FROM t1; 18 | pragma hello_vfs=1234; 19 | 20 | select * from dbstat; 21 | 22 | vacuum; 23 | drop table t1; 24 | vacuum; 25 | 26 | select * from dbstat; 27 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": ["github>orbitinghail/renovate-config"] 4 | } 5 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/logger.rs: -------------------------------------------------------------------------------- 1 | use alloc::ffi::CString; 2 | use core::ffi::{c_char, c_int}; 3 | 4 | use crate::vars; 5 | 6 | type Sqlite3Log = unsafe extern "C" fn(iErrCode: c_int, arg2: *const c_char, ...); 7 | 8 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 9 | pub enum SqliteLogLevel { 10 | Error = 1, 11 | Warn, 12 | Notice, 13 | } 14 | 15 | impl SqliteLogLevel { 16 | fn into_err_code(self) -> c_int { 17 | match self { 18 | Self::Notice => vars::SQLITE_NOTICE, 19 | Self::Warn => vars::SQLITE_WARNING, 20 | Self::Error => vars::SQLITE_INTERNAL, 21 | } 22 | } 23 | } 24 | 25 | #[derive(Clone, Copy)] 26 | pub struct SqliteLogger { 27 | log: Sqlite3Log, 28 | } 29 | 30 | impl SqliteLogger { 31 | pub(crate) fn new(log: Sqlite3Log) -> Self { 32 | Self { log } 33 | } 34 | 35 | /// Log bytes to the `SQLite3` log handle. 36 | /// This function will write each line separately to `SQLite3`. 37 | /// Note that `SQLite3` silently truncates log lines larger than roughly 38 | /// 230 bytes by default. 39 | pub fn log(&self, level: SqliteLogLevel, buf: &[u8]) { 40 | let code = level.into_err_code(); 41 | for line in buf.split(|b| *b == b'\n') { 42 | // skip if line only contains whitespace 43 | if line.iter().all(|b| b.is_ascii_whitespace()) { 44 | continue; 45 | } 46 | 47 | let z_format = CString::new(line).unwrap(); 48 | unsafe { (self.log)(code, z_format.as_ptr()) } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /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 | pub struct NoopHooks; 57 | impl Hooks for NoopHooks {} 58 | 59 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 60 | pub struct MockHandle { 61 | id: usize, 62 | readonly: bool, 63 | } 64 | 65 | impl Display for MockHandle { 66 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 67 | write!(f, "MockHandle({})", self.id) 68 | } 69 | } 70 | 71 | impl MockHandle { 72 | pub fn new(id: usize, readonly: bool) -> Self { 73 | Self { id, readonly } 74 | } 75 | } 76 | 77 | impl VfsHandle for MockHandle { 78 | fn readonly(&self) -> bool { 79 | self.readonly 80 | } 81 | 82 | fn in_memory(&self) -> bool { 83 | false 84 | } 85 | } 86 | 87 | // MockVfs implements a very simple in-memory VFS for testing purposes. 88 | // See the memvfs example for a more complete implementation. 89 | pub struct MockVfs { 90 | shared: Arc>, 91 | } 92 | 93 | struct Shared { 94 | next_id: usize, 95 | files: HashMap, 96 | hooks: Box, 97 | log: Option, 98 | } 99 | 100 | impl MockVfs { 101 | pub fn new(hooks: Box) -> Self { 102 | Self { 103 | shared: Arc::new(Mutex::new(Shared { 104 | next_id: 0, 105 | files: HashMap::new(), 106 | hooks, 107 | log: None, 108 | })), 109 | } 110 | } 111 | 112 | fn shared(&self) -> MutexGuard<'_, Shared> { 113 | self.shared.lock() 114 | } 115 | } 116 | 117 | impl Shared { 118 | fn log(&self, f: fmt::Arguments<'_>) { 119 | if let Some(log) = &self.log { 120 | let buf = format!("{f}"); 121 | log.log(SqliteLogLevel::Notice, buf.as_bytes()); 122 | } else { 123 | panic!("MockVfs is missing registered log handler") 124 | } 125 | } 126 | 127 | fn next_id(&mut self) -> usize { 128 | let id = self.next_id; 129 | self.next_id += 1; 130 | id 131 | } 132 | } 133 | 134 | impl Vfs for MockVfs { 135 | // a simple usize that represents a file handle. 136 | type Handle = MockHandle; 137 | 138 | fn register_logger(&self, logger: SqliteLogger) { 139 | let mut shared = self.shared(); 140 | shared.log = Some(logger); 141 | } 142 | 143 | fn canonical_path<'a>(&self, path: Cow<'a, str>) -> VfsResult> { 144 | let mut shared = self.shared(); 145 | shared.log(format_args!("canonical_path: path={path:?}")); 146 | shared.hooks.canonical_path(&path); 147 | Ok(path) 148 | } 149 | 150 | fn open(&self, path: Option<&str>, opts: flags::OpenOpts) -> VfsResult { 151 | let mut shared = self.shared(); 152 | shared.log(format_args!("open: path={path:?} opts={opts:?}")); 153 | shared.hooks.open(&path, &opts); 154 | 155 | let id = shared.next_id(); 156 | let file_handle = MockHandle::new(id, opts.mode().is_readonly()); 157 | 158 | if let Some(path) = path { 159 | // if file is already open return existing handle 160 | for (handle, file) in shared.files.iter() { 161 | if file.name == path { 162 | return Ok(*handle); 163 | } 164 | } 165 | shared.files.insert( 166 | file_handle, 167 | File { 168 | name: path.to_owned(), 169 | data: Vec::new(), 170 | delete_on_close: opts.delete_on_close(), 171 | }, 172 | ); 173 | } 174 | Ok(file_handle) 175 | } 176 | 177 | fn delete(&self, path: &str) -> VfsResult<()> { 178 | let mut shared = self.shared(); 179 | shared.log(format_args!("delete: path={path:?}")); 180 | shared.hooks.delete(path); 181 | shared.files.retain(|_, file| file.name != path); 182 | Ok(()) 183 | } 184 | 185 | fn access(&self, path: &str, flags: AccessFlags) -> VfsResult { 186 | let mut shared = self.shared(); 187 | shared.log(format_args!("access: path={path:?} flags={flags:?}")); 188 | shared.hooks.access(path, flags); 189 | Ok(shared.files.values().any(|file| file.name == path)) 190 | } 191 | 192 | fn file_size(&self, meta: &mut Self::Handle) -> VfsResult { 193 | let mut shared = self.shared(); 194 | shared.log(format_args!("file_size: handle={meta:?}")); 195 | shared.hooks.file_size(*meta); 196 | Ok(shared.files.get(meta).map_or(0, |file| file.data.len())) 197 | } 198 | 199 | fn truncate(&self, meta: &mut Self::Handle, size: usize) -> VfsResult<()> { 200 | let mut shared = self.shared(); 201 | shared.log(format_args!("truncate: handle={meta:?} size={size:?}")); 202 | shared.hooks.truncate(*meta, size); 203 | if let Some(file) = shared.files.get_mut(meta) { 204 | if size > file.data.len() { 205 | file.data.resize(size, 0); 206 | } else { 207 | file.data.truncate(size); 208 | } 209 | } 210 | Ok(()) 211 | } 212 | 213 | fn write(&self, meta: &mut Self::Handle, offset: usize, buf: &[u8]) -> VfsResult { 214 | let mut shared = self.shared(); 215 | shared.log(format_args!( 216 | "write: handle={:?} offset={:?} buf.len={}", 217 | meta, 218 | offset, 219 | buf.len() 220 | )); 221 | shared.hooks.write(*meta, offset, buf); 222 | if let Some(file) = shared.files.get_mut(meta) { 223 | if offset + buf.len() > file.data.len() { 224 | file.data.resize(offset + buf.len(), 0); 225 | } 226 | file.data[offset..offset + buf.len()].copy_from_slice(buf); 227 | Ok(buf.len()) 228 | } else { 229 | Err(vars::SQLITE_IOERR_WRITE) 230 | } 231 | } 232 | 233 | fn read(&self, meta: &mut Self::Handle, offset: usize, buf: &mut [u8]) -> VfsResult { 234 | let mut shared = self.shared(); 235 | shared.log(format_args!( 236 | "read: handle={:?} offset={:?} buf.len={}", 237 | meta, 238 | offset, 239 | buf.len() 240 | )); 241 | shared.hooks.read(*meta, offset, buf); 242 | if let Some(file) = shared.files.get(meta) { 243 | if offset > file.data.len() { 244 | return Ok(0); 245 | } 246 | let len = buf.len().min(file.data.len() - offset); 247 | buf[..len].copy_from_slice(&file.data[offset..offset + len]); 248 | Ok(len) 249 | } else { 250 | Err(vars::SQLITE_IOERR_READ) 251 | } 252 | } 253 | 254 | fn sync(&self, meta: &mut Self::Handle) -> VfsResult<()> { 255 | let mut shared = self.shared(); 256 | shared.log(format_args!("sync: handle={meta:?}")); 257 | shared.hooks.sync(*meta); 258 | Ok(()) 259 | } 260 | 261 | fn close(&self, meta: Self::Handle) -> VfsResult<()> { 262 | let mut shared = self.shared(); 263 | shared.log(format_args!("close: handle={meta:?}")); 264 | shared.hooks.close(meta); 265 | if let Some(file) = shared.files.get(&meta) { 266 | if file.delete_on_close { 267 | shared.files.remove(&meta); 268 | } 269 | } 270 | Ok(()) 271 | } 272 | 273 | fn pragma( 274 | &self, 275 | meta: &mut Self::Handle, 276 | pragma: Pragma<'_>, 277 | ) -> Result, PragmaErr> { 278 | let mut shared = self.shared(); 279 | shared.log(format_args!("pragma: handle={meta:?} pragma={pragma:?}")); 280 | shared.hooks.pragma(*meta, pragma) 281 | } 282 | 283 | fn sector_size(&self) -> i32 { 284 | let mut shared = self.shared(); 285 | shared.log(format_args!("sector_size")); 286 | shared.hooks.sector_size(); 287 | DEFAULT_SECTOR_SIZE 288 | } 289 | 290 | fn device_characteristics(&self) -> i32 { 291 | let mut shared = self.shared(); 292 | shared.log(format_args!("device_characteristics")); 293 | shared.hooks.device_characteristics(); 294 | DEFAULT_DEVICE_CHARACTERISTICS 295 | } 296 | } 297 | -------------------------------------------------------------------------------- /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 = 3044000; 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 | // uses sqlite3_mprintf to allocate memory for the string using sqlite's memory allocator 95 | // returns a pointer to the sqlite3 allocated string 96 | // # Safety 97 | // the returned pointer must be freed using sqlite3_free 98 | fn sqlite3_mprintf(api: &SqliteApi, s: &str) -> VfsResult<*mut c_char> { 99 | let s = CString::new(s).map_err(|_| vars::SQLITE_INTERNAL)?; 100 | let p = unsafe { (api.mprintf)(s.as_ptr()) }; 101 | if p.is_null() { 102 | Err(vars::SQLITE_NOMEM) 103 | } else { 104 | Ok(p) 105 | } 106 | } 107 | 108 | macro_rules! unwrap_appdata { 109 | ($p_vfs:expr, $t_vfs:ty) => { 110 | unsafe { 111 | let out: VfsResult<&AppData<$t_vfs>> = (*$p_vfs) 112 | .pAppData 113 | .cast::>() 114 | .as_ref() 115 | .ok_or(vars::SQLITE_INTERNAL); 116 | out 117 | } 118 | }; 119 | } 120 | 121 | macro_rules! unwrap_vfs { 122 | ($p_vfs:expr, $t_vfs:ty) => {{ 123 | let out: VfsResult<&$t_vfs> = unwrap_appdata!($p_vfs, $t_vfs).map(|app_data| &app_data.vfs); 124 | out 125 | }}; 126 | } 127 | 128 | macro_rules! unwrap_base_vfs { 129 | ($p_vfs:expr, $t_vfs:ty) => {{ 130 | let out: VfsResult<&mut ffi::sqlite3_vfs> = 131 | unwrap_appdata!($p_vfs, $t_vfs).and_then(|app_data| { 132 | unsafe { app_data.base_vfs.as_mut() }.ok_or(vars::SQLITE_INTERNAL) 133 | }); 134 | out 135 | }}; 136 | } 137 | 138 | macro_rules! unwrap_file { 139 | ($p_file:expr, $t_vfs:ty) => { 140 | unsafe { 141 | let out: VfsResult<&mut FileWrapper<<$t_vfs>::Handle>> = $p_file 142 | .cast::::Handle>>() 143 | .as_mut() 144 | .ok_or(vars::SQLITE_INTERNAL); 145 | out 146 | } 147 | }; 148 | } 149 | 150 | pub trait VfsHandle: Send { 151 | fn readonly(&self) -> bool; 152 | fn in_memory(&self) -> bool; 153 | } 154 | 155 | #[allow(unused_variables)] 156 | pub trait Vfs: Send + Sync { 157 | type Handle: VfsHandle; 158 | 159 | /// Register the provided logger with this Vfs. 160 | /// This function is guaranteed to only be called once per 161 | /// register_{static,dynamic} call. 162 | fn register_logger(&self, logger: SqliteLogger); 163 | 164 | /// construct a canonical version of the given path 165 | fn canonical_path<'a>(&self, path: Cow<'a, str>) -> VfsResult> { 166 | Ok(path) 167 | } 168 | 169 | // file system operations 170 | fn open(&self, path: Option<&str>, opts: OpenOpts) -> VfsResult; 171 | fn delete(&self, path: &str) -> VfsResult<()>; 172 | fn access(&self, path: &str, flags: AccessFlags) -> VfsResult; 173 | 174 | // file operations 175 | fn file_size(&self, handle: &mut Self::Handle) -> VfsResult; 176 | fn truncate(&self, handle: &mut Self::Handle, size: usize) -> VfsResult<()>; 177 | fn write(&self, handle: &mut Self::Handle, offset: usize, data: &[u8]) -> VfsResult; 178 | fn read(&self, handle: &mut Self::Handle, offset: usize, data: &mut [u8]) -> VfsResult; 179 | 180 | fn lock(&self, handle: &mut Self::Handle, level: LockLevel) -> VfsResult<()> { 181 | Ok(()) 182 | } 183 | 184 | fn unlock(&self, handle: &mut Self::Handle, level: LockLevel) -> VfsResult<()> { 185 | Ok(()) 186 | } 187 | 188 | fn sync(&self, handle: &mut Self::Handle) -> VfsResult<()> { 189 | Ok(()) 190 | } 191 | 192 | fn close(&self, handle: Self::Handle) -> VfsResult<()>; 193 | 194 | fn pragma( 195 | &self, 196 | handle: &mut Self::Handle, 197 | pragma: Pragma<'_>, 198 | ) -> Result, PragmaErr> { 199 | Err(PragmaErr::NotFound) 200 | } 201 | 202 | // system queries 203 | fn sector_size(&self) -> i32 { 204 | DEFAULT_SECTOR_SIZE 205 | } 206 | 207 | fn device_characteristics(&self) -> i32 { 208 | DEFAULT_DEVICE_CHARACTERISTICS 209 | } 210 | } 211 | 212 | #[derive(Clone)] 213 | pub(crate) struct SqliteApi { 214 | register: unsafe extern "C" fn(arg1: *mut ffi::sqlite3_vfs, arg2: c_int) -> c_int, 215 | find: unsafe extern "C" fn(arg1: *const c_char) -> *mut ffi::sqlite3_vfs, 216 | mprintf: unsafe extern "C" fn(arg1: *const c_char, ...) -> *mut c_char, 217 | log: unsafe extern "C" fn(arg1: c_int, arg2: *const c_char, ...), 218 | libversion_number: unsafe extern "C" fn() -> c_int, 219 | } 220 | 221 | impl SqliteApi { 222 | #[cfg(feature = "static")] 223 | fn new_static() -> Self { 224 | Self { 225 | register: ffi::sqlite3_vfs_register, 226 | find: ffi::sqlite3_vfs_find, 227 | mprintf: ffi::sqlite3_mprintf, 228 | log: ffi::sqlite3_log, 229 | libversion_number: ffi::sqlite3_libversion_number, 230 | } 231 | } 232 | 233 | #[cfg(feature = "dynamic")] 234 | fn new_dynamic(api: &ffi::sqlite3_api_routines) -> VfsResult { 235 | Ok(Self { 236 | register: api.vfs_register.ok_or(vars::SQLITE_INTERNAL)?, 237 | find: api.vfs_find.ok_or(vars::SQLITE_INTERNAL)?, 238 | mprintf: api.mprintf.ok_or(vars::SQLITE_INTERNAL)?, 239 | log: api.log.ok_or(vars::SQLITE_INTERNAL)?, 240 | libversion_number: api.libversion_number.ok_or(vars::SQLITE_INTERNAL)?, 241 | }) 242 | } 243 | } 244 | 245 | pub struct RegisterOpts { 246 | pub make_default: bool, 247 | } 248 | 249 | #[cfg(feature = "static")] 250 | pub fn register_static(name: CString, vfs: T, opts: RegisterOpts) -> VfsResult<()> { 251 | register_inner(SqliteApi::new_static(), name, vfs, opts) 252 | } 253 | 254 | /// Register a vfs with `SQLite` using the dynamic API. This API is available when 255 | /// `SQLite` is initializing extensions. 256 | /// # Safety 257 | /// `p_api` must be a valid, aligned pointer to a `sqlite3_api_routines` struct 258 | #[cfg(feature = "dynamic")] 259 | pub unsafe fn register_dynamic( 260 | p_api: *mut ffi::sqlite3_api_routines, 261 | name: CString, 262 | vfs: T, 263 | opts: RegisterOpts, 264 | ) -> VfsResult<()> { 265 | let api = unsafe { p_api.as_ref() }.ok_or(vars::SQLITE_INTERNAL)?; 266 | let sqlite_api = SqliteApi::new_dynamic(api)?; 267 | register_inner(sqlite_api, name, vfs, opts) 268 | } 269 | 270 | fn register_inner( 271 | sqlite_api: SqliteApi, 272 | name: CString, 273 | vfs: T, 274 | opts: RegisterOpts, 275 | ) -> VfsResult<()> { 276 | let version = unsafe { (sqlite_api.libversion_number)() }; 277 | if version < MIN_SQLITE_VERSION_NUMBER { 278 | panic!( 279 | "sqlite3 must be at least version {}, found version {}", 280 | MIN_SQLITE_VERSION_NUMBER, version 281 | ); 282 | } 283 | 284 | let io_methods = ffi::sqlite3_io_methods { 285 | iVersion: 3, 286 | xClose: Some(x_close::), 287 | xRead: Some(x_read::), 288 | xWrite: Some(x_write::), 289 | xTruncate: Some(x_truncate::), 290 | xSync: Some(x_sync::), 291 | xFileSize: Some(x_file_size::), 292 | xLock: Some(x_lock::), 293 | xUnlock: Some(x_unlock::), 294 | xCheckReservedLock: None, 295 | xFileControl: Some(x_file_control::), 296 | xSectorSize: Some(x_sector_size::), 297 | xDeviceCharacteristics: Some(x_device_characteristics::), 298 | xShmMap: None, 299 | xShmLock: None, 300 | xShmBarrier: None, 301 | xShmUnmap: None, 302 | xFetch: None, 303 | xUnfetch: None, 304 | }; 305 | 306 | vfs.register_logger(SqliteLogger::new(sqlite_api.log)); 307 | 308 | let p_name = ManuallyDrop::new(name).as_ptr(); 309 | let base_vfs = unsafe { (sqlite_api.find)(null_mut()) }; 310 | let vfs_register = sqlite_api.register; 311 | let p_appdata = Box::into_raw(Box::new(AppData { base_vfs, vfs, io_methods, sqlite_api })); 312 | 313 | let filewrapper_size: c_int = size_of::>() 314 | .try_into() 315 | .map_err(|_| vars::SQLITE_INTERNAL)?; 316 | 317 | let p_vfs = Box::into_raw(Box::new(ffi::sqlite3_vfs { 318 | iVersion: 3, 319 | szOsFile: filewrapper_size, 320 | mxPathname: DEFAULT_MAX_PATH_LEN, 321 | pNext: null_mut(), 322 | zName: p_name, 323 | pAppData: p_appdata.cast(), 324 | xOpen: Some(x_open::), 325 | xDelete: Some(x_delete::), 326 | xAccess: Some(x_access::), 327 | xFullPathname: Some(x_full_pathname::), 328 | xDlOpen: Some(x_dlopen::), 329 | xDlError: Some(x_dlerror::), 330 | xDlSym: Some(x_dlsym::), 331 | xDlClose: Some(x_dlclose::), 332 | xRandomness: Some(x_randomness::), 333 | xSleep: Some(x_sleep::), 334 | xCurrentTime: Some(x_current_time::), 335 | xGetLastError: None, 336 | xCurrentTimeInt64: Some(x_current_time_int64::), 337 | xSetSystemCall: None, 338 | xGetSystemCall: None, 339 | xNextSystemCall: None, 340 | })); 341 | 342 | let result = unsafe { vfs_register(p_vfs, opts.make_default.into()) }; 343 | if result != vars::SQLITE_OK { 344 | // cleanup memory 345 | unsafe { 346 | drop(Box::from_raw(p_vfs)); 347 | drop(Box::from_raw(p_appdata)); 348 | drop(CString::from_raw(p_name as *mut c_char)); 349 | }; 350 | Err(result) 351 | } else { 352 | Ok(()) 353 | } 354 | } 355 | 356 | unsafe extern "C" fn x_open( 357 | p_vfs: *mut ffi::sqlite3_vfs, 358 | z_name: ffi::sqlite3_filename, 359 | p_file: *mut ffi::sqlite3_file, 360 | flags: c_int, 361 | p_out_flags: *mut c_int, 362 | ) -> c_int { 363 | fallible(|| { 364 | let opts = flags.into(); 365 | let name = unsafe { lossy_cstr(z_name) }.ok(); 366 | let vfs = unwrap_vfs!(p_vfs, T)?; 367 | let handle = vfs.open(name.as_ref().map(|s| s.as_ref()), opts)?; 368 | 369 | let out_file = unwrap_file!(p_file, T)?; 370 | let appdata = unwrap_appdata!(p_vfs, T)?; 371 | 372 | if let Some(p_out_flags) = unsafe { p_out_flags.as_mut() } { 373 | let mut out_flags = flags; 374 | if handle.readonly() { 375 | out_flags |= vars::SQLITE_OPEN_READONLY; 376 | } 377 | if handle.in_memory() { 378 | out_flags |= vars::SQLITE_OPEN_MEMORY; 379 | } 380 | *p_out_flags = out_flags; 381 | } 382 | 383 | out_file.file.pMethods = &appdata.io_methods; 384 | out_file.vfs = p_vfs; 385 | out_file.handle.write(handle); 386 | 387 | Ok(vars::SQLITE_OK) 388 | }) 389 | } 390 | 391 | unsafe extern "C" fn x_delete( 392 | p_vfs: *mut ffi::sqlite3_vfs, 393 | z_name: ffi::sqlite3_filename, 394 | _sync_dir: c_int, 395 | ) -> c_int { 396 | fallible(|| { 397 | let name = unsafe { lossy_cstr(z_name)? }; 398 | let vfs = unwrap_vfs!(p_vfs, T)?; 399 | vfs.delete(&name)?; 400 | Ok(vars::SQLITE_OK) 401 | }) 402 | } 403 | 404 | unsafe extern "C" fn x_access( 405 | p_vfs: *mut ffi::sqlite3_vfs, 406 | z_name: ffi::sqlite3_filename, 407 | flags: c_int, 408 | p_res_out: *mut c_int, 409 | ) -> c_int { 410 | fallible(|| { 411 | let name = unsafe { lossy_cstr(z_name)? }; 412 | let vfs = unwrap_vfs!(p_vfs, T)?; 413 | let result = vfs.access(&name, flags.into())?; 414 | let out = unsafe { p_res_out.as_mut() }.ok_or(vars::SQLITE_IOERR_ACCESS)?; 415 | *out = result as i32; 416 | Ok(vars::SQLITE_OK) 417 | }) 418 | } 419 | 420 | unsafe extern "C" fn x_full_pathname( 421 | p_vfs: *mut ffi::sqlite3_vfs, 422 | z_name: ffi::sqlite3_filename, 423 | n_out: c_int, 424 | z_out: *mut c_char, 425 | ) -> c_int { 426 | fallible(|| { 427 | let name = unsafe { lossy_cstr(z_name)? }; 428 | let vfs = unwrap_vfs!(p_vfs, T)?; 429 | let full_name = vfs.canonical_path(name)?; 430 | let n_out = n_out.try_into().map_err(|_| vars::SQLITE_INTERNAL)?; 431 | let out = unsafe { slice::from_raw_parts_mut(z_out as *mut u8, n_out) }; 432 | let from = &full_name.as_bytes()[..full_name.len().min(n_out - 1)]; 433 | // copy the name into the output buffer 434 | out[..from.len()].copy_from_slice(from); 435 | // add the trailing null byte 436 | out[from.len()] = 0; 437 | Ok(vars::SQLITE_OK) 438 | }) 439 | } 440 | 441 | // file operations 442 | 443 | unsafe extern "C" fn x_close(p_file: *mut ffi::sqlite3_file) -> c_int { 444 | fallible(|| { 445 | let file = unwrap_file!(p_file, T)?; 446 | let vfs = unwrap_vfs!(file.vfs, T)?; 447 | let handle = mem::replace(&mut file.handle, MaybeUninit::uninit()); 448 | let handle = unsafe { handle.assume_init() }; 449 | vfs.close(handle)?; 450 | Ok(vars::SQLITE_OK) 451 | }) 452 | } 453 | 454 | unsafe extern "C" fn x_read( 455 | p_file: *mut ffi::sqlite3_file, 456 | buf: *mut c_void, 457 | i_amt: c_int, 458 | i_ofst: ffi::sqlite_int64, 459 | ) -> c_int { 460 | fallible(|| { 461 | let file = unwrap_file!(p_file, T)?; 462 | let vfs = unwrap_vfs!(file.vfs, T)?; 463 | let buf_len: usize = i_amt.try_into().map_err(|_| vars::SQLITE_IOERR_READ)?; 464 | let offset: usize = i_ofst.try_into().map_err(|_| vars::SQLITE_IOERR_READ)?; 465 | let buf = unsafe { slice::from_raw_parts_mut(buf.cast::(), buf_len) }; 466 | vfs.read(unsafe { file.handle.assume_init_mut() }, offset, buf)?; 467 | Ok(vars::SQLITE_OK) 468 | }) 469 | } 470 | 471 | unsafe extern "C" fn x_write( 472 | p_file: *mut ffi::sqlite3_file, 473 | buf: *const c_void, 474 | i_amt: c_int, 475 | i_ofst: ffi::sqlite_int64, 476 | ) -> c_int { 477 | fallible(|| { 478 | let file = unwrap_file!(p_file, T)?; 479 | let vfs = unwrap_vfs!(file.vfs, T)?; 480 | let buf_len: usize = i_amt.try_into().map_err(|_| vars::SQLITE_IOERR_WRITE)?; 481 | let offset: usize = i_ofst.try_into().map_err(|_| vars::SQLITE_IOERR_WRITE)?; 482 | let buf = unsafe { slice::from_raw_parts(buf.cast::(), buf_len) }; 483 | let n = vfs.write(unsafe { file.handle.assume_init_mut() }, offset, buf)?; 484 | if n != buf_len { 485 | return Err(vars::SQLITE_IOERR_WRITE); 486 | } 487 | Ok(vars::SQLITE_OK) 488 | }) 489 | } 490 | 491 | unsafe extern "C" fn x_truncate( 492 | p_file: *mut ffi::sqlite3_file, 493 | size: ffi::sqlite_int64, 494 | ) -> c_int { 495 | fallible(|| { 496 | let file = unwrap_file!(p_file, T)?; 497 | let vfs = unwrap_vfs!(file.vfs, T)?; 498 | let size: usize = size.try_into().map_err(|_| vars::SQLITE_IOERR_TRUNCATE)?; 499 | vfs.truncate(unsafe { file.handle.assume_init_mut() }, size)?; 500 | Ok(vars::SQLITE_OK) 501 | }) 502 | } 503 | 504 | unsafe extern "C" fn x_sync(p_file: *mut ffi::sqlite3_file, _flags: c_int) -> c_int { 505 | fallible(|| { 506 | let file = unwrap_file!(p_file, T)?; 507 | let vfs = unwrap_vfs!(file.vfs, T)?; 508 | vfs.sync(unsafe { file.handle.assume_init_mut() })?; 509 | Ok(vars::SQLITE_OK) 510 | }) 511 | } 512 | 513 | unsafe extern "C" fn x_file_size( 514 | p_file: *mut ffi::sqlite3_file, 515 | p_size: *mut ffi::sqlite3_int64, 516 | ) -> c_int { 517 | fallible(|| { 518 | let file = unwrap_file!(p_file, T)?; 519 | let vfs = unwrap_vfs!(file.vfs, T)?; 520 | let size = vfs.file_size(unsafe { file.handle.assume_init_mut() })?; 521 | let p_size = unsafe { p_size.as_mut() }.ok_or(vars::SQLITE_INTERNAL)?; 522 | *p_size = size.try_into().map_err(|_| vars::SQLITE_IOERR_FSTAT)?; 523 | Ok(vars::SQLITE_OK) 524 | }) 525 | } 526 | 527 | unsafe extern "C" fn x_lock(p_file: *mut ffi::sqlite3_file, raw_lock: c_int) -> c_int { 528 | fallible(|| { 529 | let level: LockLevel = raw_lock.into(); 530 | let file = unwrap_file!(p_file, T)?; 531 | let vfs = unwrap_vfs!(file.vfs, T)?; 532 | vfs.lock(unsafe { file.handle.assume_init_mut() }, level)?; 533 | Ok(vars::SQLITE_OK) 534 | }) 535 | } 536 | 537 | unsafe extern "C" fn x_unlock(p_file: *mut ffi::sqlite3_file, raw_lock: c_int) -> c_int { 538 | fallible(|| { 539 | let level: LockLevel = raw_lock.into(); 540 | let file = unwrap_file!(p_file, T)?; 541 | let vfs = unwrap_vfs!(file.vfs, T)?; 542 | vfs.unlock(unsafe { file.handle.assume_init_mut() }, level)?; 543 | Ok(vars::SQLITE_OK) 544 | }) 545 | } 546 | 547 | unsafe extern "C" fn x_file_control( 548 | p_file: *mut ffi::sqlite3_file, 549 | op: c_int, 550 | p_arg: *mut c_void, 551 | ) -> c_int { 552 | /* 553 | Other interesting ops: 554 | SIZE_HINT: hint of how large the database will grow during the current transaction 555 | COMMIT_PHASETWO: after transaction commits before file unlocks (only used in WAL mode) 556 | VFS_NAME: should return this vfs's name + / + base vfs's name 557 | 558 | Atomic write support: (requires SQLITE_IOCAP_BATCH_ATOMIC device characteristic) 559 | Docs: https://www3.sqlite.org/cgi/src/technote/714f6cbbf78c8a1351cbd48af2b438f7f824b336 560 | BEGIN_ATOMIC_WRITE: start an atomic write operation 561 | COMMIT_ATOMIC_WRITE: commit an atomic write operation 562 | ROLLBACK_ATOMIC_WRITE: rollback an atomic write operation 563 | */ 564 | 565 | if op == vars::SQLITE_FCNTL_PRAGMA { 566 | return fallible(|| { 567 | let file = unwrap_file!(p_file, T)?; 568 | let vfs = unwrap_vfs!(file.vfs, T)?; 569 | 570 | // p_arg is a pointer to an array of strings 571 | // the second value is the pragma name 572 | // the third value is either null or the pragma arg 573 | let args = p_arg.cast::<*const c_char>(); 574 | let name = unsafe { lossy_cstr(*args.add(1)) }?; 575 | let arg = unsafe { 576 | (*args.add(2)) 577 | .as_ref() 578 | .map(|p| CStr::from_ptr(p).to_string_lossy()) 579 | }; 580 | let pragma = Pragma { name: &name, arg: arg.as_deref() }; 581 | 582 | let (result, msg) = match vfs.pragma(unsafe { file.handle.assume_init_mut() }, pragma) { 583 | Ok(msg) => (Ok(vars::SQLITE_OK), msg), 584 | Err(PragmaErr::NotFound) => (Err(vars::SQLITE_NOTFOUND), None), 585 | Err(PragmaErr::Fail(err, msg)) => (Err(err), msg), 586 | }; 587 | 588 | if let Some(msg) = msg { 589 | // write the msg back to the first element of the args array. 590 | // SQLite is responsible for eventually freeing the result 591 | let appdata = unwrap_appdata!(file.vfs, T)?; 592 | unsafe { *args = sqlite3_mprintf(&appdata.sqlite_api, &msg)? }; 593 | } 594 | 595 | result 596 | }); 597 | } 598 | vars::SQLITE_NOTFOUND 599 | } 600 | 601 | // system queries 602 | 603 | unsafe extern "C" fn x_sector_size(p_file: *mut ffi::sqlite3_file) -> c_int { 604 | fallible(|| { 605 | let file = unwrap_file!(p_file, T)?; 606 | let vfs = unwrap_vfs!(file.vfs, T)?; 607 | Ok(vfs.sector_size()) 608 | }) 609 | } 610 | 611 | unsafe extern "C" fn x_device_characteristics(p_file: *mut ffi::sqlite3_file) -> c_int { 612 | fallible(|| { 613 | let file = unwrap_file!(p_file, T)?; 614 | let vfs = unwrap_vfs!(file.vfs, T)?; 615 | Ok(vfs.device_characteristics()) 616 | }) 617 | } 618 | 619 | // the following functions are wrappers around the base vfs functions 620 | 621 | unsafe extern "C" fn x_dlopen( 622 | p_vfs: *mut ffi::sqlite3_vfs, 623 | z_path: *const c_char, 624 | ) -> *mut c_void { 625 | if let Ok(vfs) = unwrap_base_vfs!(p_vfs, T) { 626 | if let Some(x_dlopen) = vfs.xDlOpen { 627 | return unsafe { x_dlopen(vfs, z_path) }; 628 | } 629 | } 630 | null_mut() 631 | } 632 | 633 | unsafe extern "C" fn x_dlerror( 634 | p_vfs: *mut ffi::sqlite3_vfs, 635 | n_byte: c_int, 636 | z_err_msg: *mut c_char, 637 | ) { 638 | if let Ok(vfs) = unwrap_base_vfs!(p_vfs, T) { 639 | if let Some(x_dlerror) = vfs.xDlError { 640 | unsafe { x_dlerror(vfs, n_byte, z_err_msg) }; 641 | } 642 | } 643 | } 644 | 645 | unsafe extern "C" fn x_dlsym( 646 | p_vfs: *mut ffi::sqlite3_vfs, 647 | p_handle: *mut c_void, 648 | z_symbol: *const c_char, 649 | ) -> Option< 650 | unsafe extern "C" fn(arg1: *mut ffi::sqlite3_vfs, arg2: *mut c_void, zSymbol: *const c_char), 651 | > { 652 | if let Ok(vfs) = unwrap_base_vfs!(p_vfs, T) { 653 | if let Some(x_dlsym) = vfs.xDlSym { 654 | return unsafe { x_dlsym(vfs, p_handle, z_symbol) }; 655 | } 656 | } 657 | None 658 | } 659 | 660 | unsafe extern "C" fn x_dlclose(p_vfs: *mut ffi::sqlite3_vfs, p_handle: *mut c_void) { 661 | if let Ok(vfs) = unwrap_base_vfs!(p_vfs, T) { 662 | if let Some(x_dlclose) = vfs.xDlClose { 663 | unsafe { x_dlclose(vfs, p_handle) }; 664 | } 665 | } 666 | } 667 | 668 | unsafe extern "C" fn x_randomness( 669 | p_vfs: *mut ffi::sqlite3_vfs, 670 | n_byte: c_int, 671 | z_out: *mut c_char, 672 | ) -> c_int { 673 | if let Ok(vfs) = unwrap_base_vfs!(p_vfs, T) { 674 | if let Some(x_randomness) = vfs.xRandomness { 675 | return unsafe { x_randomness(vfs, n_byte, z_out) }; 676 | } 677 | } 678 | vars::SQLITE_INTERNAL 679 | } 680 | 681 | unsafe extern "C" fn x_sleep(p_vfs: *mut ffi::sqlite3_vfs, microseconds: c_int) -> c_int { 682 | if let Ok(vfs) = unwrap_base_vfs!(p_vfs, T) { 683 | if let Some(x_sleep) = vfs.xSleep { 684 | return unsafe { x_sleep(vfs, microseconds) }; 685 | } 686 | } 687 | vars::SQLITE_INTERNAL 688 | } 689 | 690 | unsafe extern "C" fn x_current_time( 691 | p_vfs: *mut ffi::sqlite3_vfs, 692 | p_time: *mut f64, 693 | ) -> c_int { 694 | if let Ok(vfs) = unwrap_base_vfs!(p_vfs, T) { 695 | if let Some(x_current_time) = vfs.xCurrentTime { 696 | return unsafe { x_current_time(vfs, p_time) }; 697 | } 698 | } 699 | vars::SQLITE_INTERNAL 700 | } 701 | 702 | unsafe extern "C" fn x_current_time_int64( 703 | p_vfs: *mut ffi::sqlite3_vfs, 704 | p_time: *mut i64, 705 | ) -> c_int { 706 | if let Ok(vfs) = unwrap_base_vfs!(p_vfs, T) { 707 | if let Some(x_current_time_int64) = vfs.xCurrentTimeInt64 { 708 | return unsafe { x_current_time_int64(vfs, p_time) }; 709 | } 710 | } 711 | vars::SQLITE_INTERNAL 712 | } 713 | 714 | #[cfg(test)] 715 | mod tests { 716 | // tests use std 717 | extern crate std; 718 | 719 | use super::*; 720 | use crate::{ 721 | flags::{CreateMode, OpenKind, OpenMode}, 722 | mock::*, 723 | }; 724 | use alloc::vec::Vec; 725 | use rusqlite::{Connection, OpenFlags}; 726 | use std::{boxed::Box, io::Write, println}; 727 | 728 | fn log_handler(_: i32, arg2: &str) { 729 | println!("{arg2}"); 730 | } 731 | 732 | #[test] 733 | fn sanity() -> Result<(), Box> { 734 | unsafe { 735 | rusqlite::trace::config_log(Some(log_handler)).unwrap(); 736 | } 737 | 738 | struct H {} 739 | impl Hooks for H { 740 | fn open(&mut self, path: &Option<&str>, opts: &OpenOpts) { 741 | let path = path.unwrap(); 742 | if path == "main.db" { 743 | assert!(!opts.delete_on_close()); 744 | assert_eq!(opts.kind(), OpenKind::MainDb); 745 | assert_eq!( 746 | opts.mode(), 747 | OpenMode::ReadWrite { create: CreateMode::Create } 748 | ); 749 | } else if path == "main.db-journal" { 750 | assert!(!opts.delete_on_close()); 751 | assert_eq!(opts.kind(), OpenKind::MainJournal); 752 | assert_eq!( 753 | opts.mode(), 754 | OpenMode::ReadWrite { create: CreateMode::Create } 755 | ); 756 | } else { 757 | panic!("unexpected path: {}", path); 758 | } 759 | } 760 | } 761 | 762 | let vfs = MockVfs::new(Box::new(H {})); 763 | register_static( 764 | CString::new("mock").unwrap(), 765 | vfs, 766 | RegisterOpts { make_default: true }, 767 | ) 768 | .map_err(|_| "failed to register vfs")?; 769 | 770 | // create a sqlite connection using the mock vfs 771 | let conn = Connection::open_with_flags_and_vfs( 772 | "main.db", 773 | OpenFlags::SQLITE_OPEN_READ_WRITE | OpenFlags::SQLITE_OPEN_CREATE, 774 | "mock", 775 | )?; 776 | 777 | conn.execute("create table t (val int)", [])?; 778 | conn.execute("insert into t (val) values (1)", [])?; 779 | conn.execute("insert into t (val) values (2)", [])?; 780 | 781 | conn.execute("pragma mock_test", [])?; 782 | 783 | let n: i64 = conn.query_row("select sum(val) from t", [], |row| row.get(0))?; 784 | assert_eq!(n, 3); 785 | 786 | // the blob api is interesting and stress tests reading/writing pages and journaling 787 | conn.execute("create table b (data blob)", [])?; 788 | println!("inserting zero blob"); 789 | conn.execute("insert into b values (zeroblob(8192))", [])?; 790 | let rowid = conn.last_insert_rowid(); 791 | let mut blob = conn.blob_open(rusqlite::MAIN_DB, "b", "data", rowid, false)?; 792 | 793 | // write some data to the blob 794 | println!("writing to blob"); 795 | let n = blob.write(b"hello")?; 796 | assert_eq!(n, 5); 797 | 798 | // query the table for the blob and print it 799 | let mut stmt = conn.prepare("select data from b")?; 800 | let mut rows = stmt.query([])?; 801 | while let Some(row) = rows.next()? { 802 | let data: Vec = row.get(0)?; 803 | assert_eq!(&data[0..5], b"hello"); 804 | } 805 | 806 | Ok(()) 807 | } 808 | } 809 | --------------------------------------------------------------------------------