├── .cargo └── config ├── .github └── workflows │ └── build.yml ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── src ├── lib.rs ├── options.rs ├── spreadsheet │ ├── cells.rs │ ├── manager.rs │ ├── mod.rs │ └── reader.rs ├── sqlite.h ├── sqlite.rs └── utils.rs └── tests ├── abcdef.xlsx ├── abcdef_colnames.xlsx └── lib.rs /.cargo/config: -------------------------------------------------------------------------------- 1 | [target.aarch64-unknown-linux-gnu] 2 | linker = "aarch64-linux-gnu-gcc" -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | tags: ["v*"] 7 | pull_request: 8 | branches: [ "master" ] 9 | 10 | env: 11 | CARGO_TERM_COLOR: always 12 | 13 | jobs: 14 | build-linux-x64: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | - name: Build debug 19 | run: cargo build --verbose 20 | - name: Run tests 21 | run: cargo test --verbose 22 | - name: Build release 23 | run: cargo build --release 24 | - uses: actions/upload-artifact@v4 25 | with: 26 | name: linux-x64 27 | path: target/release/libxlite.so 28 | 29 | build-linux-x86: 30 | runs-on: ubuntu-latest 31 | steps: 32 | - uses: actions/checkout@v3 33 | - name: Install target 34 | run: sudo apt install -y gcc-multilib && rustup target install i686-unknown-linux-gnu 35 | - name: Build release 36 | run: cargo build --release --target=i686-unknown-linux-gnu 37 | - uses: actions/upload-artifact@v4 38 | with: 39 | name: linux-x86 40 | path: target/i686-unknown-linux-gnu/release/libxlite.so 41 | 42 | build-linux-aarch64: 43 | runs-on: ubuntu-latest 44 | steps: 45 | - uses: actions/checkout@v3 46 | - name: Install target 47 | run: sudo apt install -y gcc-aarch64-linux-gnu && rustup target install aarch64-unknown-linux-gnu 48 | - name: Build release 49 | run: cargo build --release --target=aarch64-unknown-linux-gnu 50 | - uses: actions/upload-artifact@v4 51 | with: 52 | name: linux-aarch64 53 | path: target/aarch64-unknown-linux-gnu/release/libxlite.so 54 | 55 | build-windows-x64: 56 | runs-on: windows-latest 57 | steps: 58 | - uses: actions/checkout@v3 59 | - name: Build debug 60 | run: cargo build --verbose 61 | - name: Run tests 62 | run: cargo test --verbose 63 | - name: Build release 64 | run: cargo build --release 65 | - uses: actions/upload-artifact@v4 66 | with: 67 | name: windows-x64 68 | path: target/release/xlite.dll 69 | 70 | build-windows-x86: 71 | runs-on: windows-latest 72 | steps: 73 | - uses: actions/checkout@v3 74 | - name: Install target 75 | run: rustup target add i686-pc-windows-msvc 76 | - name: Build release 77 | run: cargo build --release --target=i686-pc-windows-msvc 78 | - uses: actions/upload-artifact@v4 79 | with: 80 | name: windows-x86 81 | path: target/i686-pc-windows-msvc/release/xlite.dll 82 | 83 | build-macos-x64: 84 | runs-on: macos-latest 85 | steps: 86 | - uses: actions/checkout@v3 87 | - name: Build debug 88 | run: cargo build --verbose 89 | - name: Run tests 90 | run: cargo test --verbose 91 | - name: Build release 92 | run: cargo build --release 93 | - uses: actions/upload-artifact@v4 94 | with: 95 | name: macos-x64 96 | path: target/release/libxlite.dylib 97 | 98 | build-macos-aarch64: 99 | runs-on: macos-latest 100 | steps: 101 | - uses: actions/checkout@v3 102 | - name: Install target 103 | run: rustup target add aarch64-apple-darwin 104 | - name: Build release 105 | run: cargo build --release --target=aarch64-apple-darwin 106 | - uses: actions/upload-artifact@v4 107 | with: 108 | name: macos-aarch64 109 | path: target/aarch64-apple-darwin/release/libxlite.dylib 110 | 111 | create-release: 112 | 113 | permissions: write-all 114 | if: startsWith(github.ref, 'refs/tags/v') 115 | needs: 116 | - build-linux-x64 117 | - build-linux-x86 118 | - build-linux-aarch64 119 | - build-windows-x64 120 | - build-windows-x86 121 | - build-macos-x64 122 | - build-macos-aarch64 123 | runs-on: ubuntu-latest 124 | 125 | steps: 126 | - name: Add current date to env 127 | run: echo "RELEASE_DATE=$(date +'%Y-%m-%dT%H:%M:%S')" >> $GITHUB_ENV 128 | - uses: actions/download-artifact@v4 129 | with: 130 | path: . 131 | - name: Archive linux-x64 132 | run: mv linux-x64/libxlite.so ./libxlite.so && tar -zcvf libxlite-linux-x64.tar.gz libxlite.so && rm libxlite.so 133 | - name: Archive linux-x86 134 | run: mv linux-x86/libxlite.so ./libxlite.so && tar -zcvf libxlite-linux-x86.tar.gz libxlite.so && rm libxlite.so 135 | - name: Archive linux-aarch64 136 | run: mv linux-aarch64/libxlite.so ./libxlite.so && tar -zcvf libxlite-linux-aarch64.tar.gz libxlite.so && rm libxlite.so 137 | - name: Archive windows-x64 138 | run: mv windows-x64/xlite.dll ./xlite.dll && zip xlite-windows-x64.zip xlite.dll && rm xlite.dll 139 | - name: Archive windows-x86 140 | run: mv windows-x86/xlite.dll ./xlite.dll && zip xlite-windows-x86.zip xlite.dll && rm xlite.dll 141 | - name: Archive macos-x64 142 | run: mv macos-x64/libxlite.dylib ./libxlite.dylib && zip libxlite-macos-x64.zip libxlite.dylib && rm libxlite.dylib 143 | - name: Archive macos-aarch64 144 | run: mv macos-aarch64/libxlite.dylib ./libxlite.dylib && zip libxlite-macos-aarch64.zip libxlite.dylib && rm libxlite.dylib 145 | 146 | - name: Create Release 147 | id: create_release 148 | uses: actions/create-release@v1 149 | env: 150 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 151 | with: 152 | tag_name: ${{ github.ref }} 153 | release_name: ${{ github.ref }} 154 | body: Release ${{ github.ref }} ${{ env.RELEASE_DATE }} 155 | draft: false 156 | prerelease: true 157 | 158 | - name: Upload linux-x64 artifact 159 | uses: actions/upload-release-asset@v1 160 | env: 161 | GITHUB_TOKEN: ${{ github.token }} 162 | with: 163 | upload_url: ${{ steps.create_release.outputs.upload_url }} 164 | asset_path: ./libxlite-linux-x64.tar.gz 165 | asset_name: libxlite-linux-x64.tar.gz 166 | asset_content_type: application/octet-stream 167 | 168 | - name: Upload linux-x86 artifact 169 | uses: actions/upload-release-asset@v1 170 | env: 171 | GITHUB_TOKEN: ${{ github.token }} 172 | with: 173 | upload_url: ${{ steps.create_release.outputs.upload_url }} 174 | asset_path: ./libxlite-linux-x86.tar.gz 175 | asset_name: libxlite-linux-x86.tar.gz 176 | asset_content_type: application/octet-stream 177 | 178 | - name: Upload linux-aarch64 artifact 179 | uses: actions/upload-release-asset@v1 180 | env: 181 | GITHUB_TOKEN: ${{ github.token }} 182 | with: 183 | upload_url: ${{ steps.create_release.outputs.upload_url }} 184 | asset_path: ./libxlite-linux-aarch64.tar.gz 185 | asset_name: libxlite-linux-aarch64.tar.gz 186 | asset_content_type: application/octet-stream 187 | 188 | - name: Upload windows-x64 artifact 189 | uses: actions/upload-release-asset@v1 190 | env: 191 | GITHUB_TOKEN: ${{ github.token }} 192 | with: 193 | upload_url: ${{ steps.create_release.outputs.upload_url }} 194 | asset_path: ./xlite-windows-x64.zip 195 | asset_name: xlite-windows-x64.zip 196 | asset_content_type: application/octet-stream 197 | 198 | - name: Upload windows-x86 artifact 199 | uses: actions/upload-release-asset@v1 200 | env: 201 | GITHUB_TOKEN: ${{ github.token }} 202 | with: 203 | upload_url: ${{ steps.create_release.outputs.upload_url }} 204 | asset_path: ./xlite-windows-x86.zip 205 | asset_name: xlite-windows-x86.zip 206 | asset_content_type: application/octet-stream 207 | 208 | - name: Upload macos-x64 artifact 209 | uses: actions/upload-release-asset@v1 210 | env: 211 | GITHUB_TOKEN: ${{ github.token }} 212 | with: 213 | upload_url: ${{ steps.create_release.outputs.upload_url }} 214 | asset_path: ./libxlite-macos-x64.zip 215 | asset_name: libxlite-macos-x64.zip 216 | asset_content_type: application/octet-stream 217 | 218 | - name: Upload macos-aarch64 artifact 219 | uses: actions/upload-release-asset@v1 220 | env: 221 | GITHUB_TOKEN: ${{ github.token }} 222 | with: 223 | upload_url: ${{ steps.create_release.outputs.upload_url }} 224 | asset_path: ./libxlite-macos-aarch64.zip 225 | asset_name: libxlite-macos-aarch64.zip 226 | asset_content_type: application/octet-stream 227 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | .DS_Store 4 | .idea 5 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xlite" 3 | version = "0.2.0" 4 | edition = "2021" 5 | 6 | [lib] 7 | crate-type = ["cdylib"] 8 | 9 | [dependencies] 10 | calamine = "0.19.1" 11 | nom = "7.1.3" 12 | 13 | [dev-dependencies] 14 | rusqlite = { version = "0.34.0", features = ["bundled", "load_extension"] } 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022 Sergey Khabibullin 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # XLite - query Excel (.xlsx, .xls) and Open Document spreadsheets (.ods) as SQLite virtual tables 2 | 3 | XLite is a SQLite extension written in Rust. The main purpose of this library is to allow working with spreadsheets from SQLite exposing them as [virtual tables](https://sqlite.org/vtab.html). 4 | 5 | ### Download 6 | 7 | ![build](https://github.com/x2bool/xlite/actions/workflows/build.yml/badge.svg) 8 | 9 | The following prebuilt libraries are available for [download](https://github.com/x2bool/xlite/releases): 10 | 11 | ![release](https://img.shields.io/github/v/release/x2bool/xlite?display_name=release) 12 | 13 | | | Linux | Windows | MacOS | 14 | |--|--|--|--| 15 | | x86 | [libxlite.so.tar.gz](https://github.com/x2bool/xlite/releases/latest/download/libxlite-linux-x86.tar.gz)️ | [xlite.dll.zip](https://github.com/x2bool/xlite/releases/latest/download/xlite-windows-x86.zip)️ | N/A | 16 | | x86-64 | [libxlite.so.tar.gz](https://github.com/x2bool/xlite/releases/latest/download/libxlite-linux-x64.tar.gz)️ | [xlite.dll.zip](https://github.com/x2bool/xlite/releases/latest/download/xlite-windows-x64.zip)️ | [libxlite.dylib.zip](https://github.com/x2bool/xlite/releases/latest/download/libxlite-macos-x64.zip) | 17 | | AArch64 (ARM64) | [libxlite.so.tar.gz](https://github.com/x2bool/xlite/releases/latest/download/libxlite-linux-aarch64.tar.gz)️ | | [libxlite.dylib.zip](https://github.com/x2bool/xlite/releases/latest/download/libxlite-macos-aarch64.zip) | 18 | 19 | This step will produce `libxlite.so` or `libxlite.dylib` or `xlite.dll` depending on your operation system. 20 | 21 | ### How to use 22 | 23 | Assuming you have sqlite3 command line tools installed, `libxlite` library in your current directory and some spreadsheet file on your disk you can load extension: 24 | 25 | ```bash 26 | sqlite3 # will open SQLite CLI 27 | > .load libxlite # or "xlite" on Windows 28 | ``` 29 | 30 | This will load `xlite` module, now it can be used to create virtual tables. 31 | 32 | Creating a virtual table (this sample uses the .xslx file from the tests directory): 33 | 34 | ```sql 35 | CREATE VIRTUAL TABLE test_data USING xlite ( 36 | FILENAME './tests/abcdef_colnames.xlsx', 37 | WORKSHEET 'Sheet1', 38 | RANGE 'A2:F', -- optional 39 | COLNAMES '1' -- optional 40 | ); 41 | ``` 42 | 43 | Explanation: this statement will create a virtual table based on the .xlsx file and the worksheet named "Sheet1". 44 | 45 | Optional `RANGE` parameter is used here to skip the first row in the table. `A2:F` meaning is `use columns from A to F but start from 2nd row`. 46 | 47 | Querying: 48 | 49 | ```sql 50 | SELECT A, B, C, D, E, F FROM test_data; 51 | ``` 52 | 53 | Columns are named according to their name (index) in the spreadsheet, unless an optional `COLNAMES` argument is provided - in this case column names will be taken from the row of spreadsheet specified by this option. 54 | 55 | ```sql 56 | SELECT COUNT(*), D FROM test_data GROUP BY D ORDER BY COUNT(*); 57 | ``` 58 | 59 | All operations supported by SQLite can be executed on spreadsheets as long as it is supported by the virtual table mechanism. 60 | 61 | Dropping: 62 | 63 | ```sql 64 | DROP TABLE test_data; 65 | ``` 66 | 67 | This statement will drop only the virtual table. Physical file won't be deleted. 68 | 69 | ### How to build 70 | 71 | ```bash 72 | cargo build --release 73 | ``` 74 | 75 | ### Limitations 76 | 77 | `INSERT`, `UPDATE` and `DELETE` statements are not supported right now. 78 | 79 | ### About 80 | 81 | This project is experimental and it is developed and maintained in my free time as a hobby project. 82 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_upper_case_globals)] 2 | #![allow(dead_code)] 3 | 4 | mod options; 5 | mod spreadsheet; 6 | pub(crate) mod sqlite; 7 | mod utils; 8 | 9 | use calamine::DataType; 10 | use std::ffi::c_void; 11 | use std::os::raw::{c_char, c_int, c_longlong}; 12 | use std::sync::{Arc, Mutex}; 13 | 14 | use crate::options::{parse_option, UsingOption}; 15 | use crate::spreadsheet::{ 16 | manager::{DataManager, DataManagerBuilder, DataManagerError}, 17 | reader::DataReader, 18 | }; 19 | use crate::sqlite::{ 20 | sqlite3, sqlite3_api_routines, sqlite3_context, sqlite3_index_info, sqlite3_int64, 21 | sqlite3_module, sqlite3_value, sqlite3_vtab, sqlite3_vtab_cursor, SQLITE_ERROR, SQLITE_OK, 22 | SQLITE_OK_LOAD_PERMANENTLY, 23 | }; 24 | use crate::utils::{ 25 | collect_options_from_args, declare_table, error_to_sqlite3_string, yield_result, 26 | }; 27 | 28 | #[no_mangle] 29 | static mut sqlite3_api: *mut sqlite3_api_routines = std::ptr::null_mut(); 30 | 31 | #[repr(C)] 32 | pub struct Module { 33 | // must be at the beginning 34 | base: sqlite3_module, 35 | name: &'static [u8], 36 | } 37 | 38 | #[repr(C)] 39 | pub struct VirtualTable { 40 | // must be at the beginning 41 | base: sqlite3_vtab, 42 | manager: Arc>, 43 | } 44 | 45 | #[repr(C)] 46 | struct VirtualCursor { 47 | // must be at the beginning 48 | base: sqlite3_vtab_cursor, 49 | reader: Arc>, 50 | } 51 | 52 | pub const XLITE_MODULE: Module = Module { 53 | base: sqlite3_module { 54 | iVersion: 0, 55 | xCreate: Some(x_create), 56 | xConnect: Some(x_connect), 57 | xBestIndex: Some(x_best_index), 58 | xDisconnect: Some(x_disconnect), 59 | xDestroy: Some(x_destroy), 60 | xOpen: Some(x_open), 61 | xClose: Some(x_close), 62 | xFilter: Some(x_filter), 63 | xNext: Some(x_next), 64 | xEof: Some(x_eof), 65 | xColumn: Some(x_column), 66 | xRowid: Some(x_rowid), 67 | xUpdate: None, 68 | xBegin: None, 69 | xSync: None, 70 | xCommit: None, 71 | xRollback: None, 72 | xFindFunction: None, 73 | xRename: None, 74 | xSavepoint: None, 75 | xRelease: None, 76 | xRollbackTo: None, 77 | xShadowName: None, 78 | }, 79 | name: b"xlite\0", 80 | }; 81 | 82 | #[no_mangle] 83 | pub unsafe extern "C" fn register_module( 84 | db: *mut sqlite3, 85 | pz_err_msg: *mut *mut c_char, 86 | p_api: *mut sqlite3_api_routines, 87 | ) -> c_int { 88 | let name = XLITE_MODULE.name; 89 | 90 | let result = ((*p_api).create_module.unwrap())( 91 | db, 92 | name.as_ptr() as *const c_char, 93 | &XLITE_MODULE as *const Module as *const sqlite3_module, 94 | std::ptr::null_mut(), 95 | ); 96 | 97 | if result != SQLITE_OK { 98 | let err = format!("Failed to create module, status: {}", result); 99 | if let Some(ptr) = error_to_sqlite3_string(sqlite3_api, err) { 100 | *pz_err_msg = ptr; 101 | } 102 | SQLITE_ERROR 103 | } else { 104 | SQLITE_OK_LOAD_PERMANENTLY 105 | } 106 | } 107 | 108 | #[no_mangle] 109 | pub unsafe extern "C" fn sqlite3_xlite_init( 110 | db: *mut sqlite3, 111 | pz_err_msg: *mut *mut c_char, 112 | p_api: *mut sqlite3_api_routines, 113 | ) -> c_int { 114 | sqlite3_api = p_api; 115 | 116 | let result = register_module(db, pz_err_msg, p_api); 117 | if result != SQLITE_OK { 118 | return result; 119 | } else { 120 | let result = ((*p_api).auto_extension.unwrap())(Some(std::mem::transmute( 121 | register_module as *const (), 122 | ))); 123 | if result != SQLITE_OK { 124 | return result; 125 | } 126 | } 127 | 128 | SQLITE_OK_LOAD_PERMANENTLY 129 | } 130 | 131 | #[no_mangle] 132 | unsafe extern "C" fn x_create( 133 | db: *mut sqlite3, 134 | _p_aux: *mut c_void, 135 | argc: c_int, 136 | argv: *const *const c_char, 137 | pp_vtab: *mut *mut sqlite3_vtab, 138 | pz_err: *mut *mut c_char, 139 | ) -> c_int { 140 | let options = collect_options_from_args(argc, argv); 141 | let manager = DataManagerBuilder::from_options(options).open(); 142 | 143 | let mut result: c_int = SQLITE_ERROR; 144 | 145 | match manager { 146 | Ok(mut manager) => { 147 | let columns = manager.get_columns(); 148 | result = declare_table(db, sqlite3_api, columns); 149 | 150 | let p_new: Box = Box::new(VirtualTable { 151 | base: sqlite3_vtab { 152 | pModule: std::ptr::null_mut(), 153 | nRef: 0, 154 | zErrMsg: std::ptr::null_mut(), 155 | }, 156 | manager: Arc::new(Mutex::new(manager)), 157 | }); 158 | *pp_vtab = Box::into_raw(p_new) as *mut sqlite3_vtab; 159 | } 160 | Err(err) => { 161 | let msg = match err { 162 | DataManagerError::NoFilename => "Filename is not provided".to_string(), 163 | DataManagerError::NoWorksheet => "Worksheet is not provided".to_string(), 164 | DataManagerError::Calamine(e) => format!("{}", e), 165 | }; 166 | if let Some(ptr) = error_to_sqlite3_string(sqlite3_api, msg) { 167 | *pz_err = ptr; 168 | return SQLITE_ERROR; 169 | } 170 | } 171 | } 172 | 173 | result 174 | } 175 | 176 | #[no_mangle] 177 | unsafe extern "C" fn x_connect( 178 | db: *mut sqlite3, 179 | p_aux: *mut c_void, 180 | argc: c_int, 181 | argv: *const *const c_char, 182 | pp_vtab: *mut *mut sqlite3_vtab, 183 | pz_err: *mut *mut c_char, 184 | ) -> c_int { 185 | x_create(db, p_aux, argc, argv, pp_vtab, pz_err) 186 | } 187 | 188 | #[no_mangle] 189 | unsafe extern "C" fn x_best_index( 190 | _p_vtab: *mut sqlite3_vtab, 191 | _arg1: *mut sqlite3_index_info, 192 | ) -> c_int { 193 | SQLITE_OK 194 | } 195 | 196 | #[no_mangle] 197 | unsafe extern "C" fn x_disconnect(p_vtab: *mut sqlite3_vtab) -> c_int { 198 | x_destroy(p_vtab) 199 | } 200 | 201 | #[no_mangle] 202 | unsafe extern "C" fn x_destroy(p_vtab: *mut sqlite3_vtab) -> c_int { 203 | if !p_vtab.is_null() { 204 | let table = Box::from_raw(p_vtab as *mut VirtualTable); 205 | drop(table); 206 | } 207 | 208 | SQLITE_OK 209 | } 210 | 211 | #[no_mangle] 212 | unsafe extern "C" fn x_open( 213 | p_vtab: *mut sqlite3_vtab, 214 | pp_cursor: *mut *mut sqlite3_vtab_cursor, 215 | ) -> c_int { 216 | let table = &mut *(p_vtab as *mut VirtualTable); 217 | let manager = Arc::clone(&table.manager); 218 | let mut lock = manager.lock().unwrap(); 219 | let reader = lock.read(); 220 | 221 | let cursor: Box = Box::new(VirtualCursor { 222 | base: sqlite3_vtab_cursor { pVtab: p_vtab }, 223 | reader: Arc::new(Mutex::new(reader)), 224 | }); 225 | *pp_cursor = Box::into_raw(cursor) as _; 226 | 227 | SQLITE_OK 228 | } 229 | 230 | #[no_mangle] 231 | unsafe extern "C" fn x_close(p_cursor: *mut sqlite3_vtab_cursor) -> c_int { 232 | if !p_cursor.is_null() { 233 | let cursor = Box::from_raw(p_cursor as *mut VirtualCursor); 234 | drop(cursor); 235 | } 236 | 237 | SQLITE_OK 238 | } 239 | 240 | #[no_mangle] 241 | unsafe extern "C" fn x_filter( 242 | _arg1: *mut sqlite3_vtab_cursor, 243 | _idx_num: c_int, 244 | _idx_str: *const c_char, 245 | _argc: c_int, 246 | _argv: *mut *mut sqlite3_value, 247 | ) -> c_int { 248 | SQLITE_OK 249 | } 250 | 251 | #[no_mangle] 252 | unsafe extern "C" fn x_next(p_cursor: *mut sqlite3_vtab_cursor) -> c_int { 253 | let cursor = &mut *(p_cursor as *mut VirtualCursor); 254 | let lock = Arc::clone(&cursor.reader); 255 | let mut reader = lock.lock().unwrap(); 256 | 257 | reader.move_next(); 258 | 259 | SQLITE_OK 260 | } 261 | 262 | #[no_mangle] 263 | unsafe extern "C" fn x_eof(p_cursor: *mut sqlite3_vtab_cursor) -> c_int { 264 | let cursor = &mut *(p_cursor as *mut VirtualCursor); 265 | let lock = Arc::clone(&cursor.reader); 266 | let reader = lock.lock().unwrap(); 267 | 268 | if reader.has_value() { 269 | 0 270 | } else { 271 | 1 272 | } 273 | } 274 | 275 | #[no_mangle] 276 | unsafe extern "C" fn x_column( 277 | p_cursor: *mut sqlite3_vtab_cursor, 278 | p_context: *mut sqlite3_context, 279 | column: c_int, 280 | ) -> c_int { 281 | let cursor = &mut *(p_cursor as *mut VirtualCursor); 282 | let lock = Arc::clone(&cursor.reader); 283 | let reader = lock.lock().unwrap(); 284 | 285 | let value = reader.get_value(column as usize); 286 | yield_result( 287 | p_context, 288 | sqlite3_api, 289 | match value { 290 | Some(data) => data, 291 | None => &DataType::Empty, 292 | }, 293 | ); 294 | 295 | SQLITE_OK 296 | } 297 | 298 | #[no_mangle] 299 | unsafe extern "C" fn x_rowid( 300 | p_cursor: *mut sqlite3_vtab_cursor, 301 | p_rowid: *mut sqlite3_int64, 302 | ) -> c_int { 303 | let cursor = &mut *(p_cursor as *mut VirtualCursor); 304 | let lock = Arc::clone(&cursor.reader); 305 | let reader = lock.lock().unwrap(); 306 | 307 | *p_rowid = reader.get_rowid() as c_longlong; 308 | 309 | SQLITE_OK 310 | } 311 | -------------------------------------------------------------------------------- /src/options.rs: -------------------------------------------------------------------------------- 1 | use nom::branch::alt; 2 | use nom::bytes::complete::{escaped, tag, tag_no_case}; 3 | use nom::character::complete::{alpha1, digit0, digit1, multispace0, multispace1, none_of}; 4 | use nom::combinator::{map, recognize}; 5 | use nom::{IResult, Parser}; 6 | use nom::sequence::{delimited, preceded, separated_pair, terminated, tuple}; 7 | 8 | pub enum UsingOption { 9 | File(String), 10 | Worksheet(String), 11 | Range(String), 12 | ColNames(String), 13 | } 14 | 15 | pub fn parse_option(input: &str) -> IResult<&str, UsingOption> { 16 | parse_with_spaces(alt(( 17 | parse_filename_option, 18 | parse_worksheet_option, 19 | parse_range_option, 20 | parse_colnames_option, 21 | ))).parse(input) 22 | } 23 | 24 | fn parse_filename_option(input: &str) -> IResult<&str, UsingOption> { 25 | let option = alt((tag_no_case("FILENAME"), tag_no_case("FILE"))); 26 | let path = parse_quoted; 27 | 28 | map(separated_pair(option, multispace1, path), 29 | |(_, p)| UsingOption::File(p.to_string()))(input) 30 | } 31 | 32 | fn parse_worksheet_option(input: &str) -> IResult<&str, UsingOption> { 33 | let option = alt((tag_no_case("WORKSHEET"), tag_no_case("SHEET"))); 34 | let name = parse_quoted; 35 | 36 | map(separated_pair(option, multispace1, name), 37 | |(_, n)| UsingOption::Worksheet(n.to_string()))(input) 38 | } 39 | 40 | fn parse_range_option(input: &str) -> IResult<&str, UsingOption> { 41 | let option = tag_no_case("RANGE"); 42 | 43 | let range = recognize( 44 | tuple((alpha1, digit0, tag(":"), alpha1, digit0)) 45 | ); 46 | 47 | let value = preceded( 48 | tag("'"), terminated(range, tag("'"))); 49 | 50 | map(separated_pair(option, multispace1, value), 51 | |t: (&str, &str)| UsingOption::Range(t.1.to_string()))(input) 52 | } 53 | 54 | fn parse_colnames_option(input: &str) -> IResult<&str, UsingOption> { 55 | let option = tag_no_case("COLNAMES"); 56 | 57 | let value = preceded( 58 | tag("'"), terminated(digit1, tag("'"))); 59 | 60 | map(separated_pair(option, multispace1, value), 61 | |t: (&str, &str)| UsingOption::ColNames(t.1.to_string()))(input) 62 | } 63 | 64 | fn parse_with_spaces<'a, T>(parser: impl Parser<&'a str, T, nom::error::Error<&'a str>>) 65 | -> impl Parser<&'a str, T, nom::error::Error<&'a str>> { 66 | preceded(multispace0, terminated(parser, multispace0)) 67 | } 68 | 69 | fn parse_quoted(input: &str) -> IResult<&str, &str> { 70 | let esc = escaped(none_of("\\\'"), '\\', tag("'")); 71 | let esc_or_empty = alt((esc, tag(""))); 72 | delimited(tag("'"), esc_or_empty, tag("'"))(input) 73 | } 74 | 75 | #[cfg(test)] 76 | mod tests { 77 | use super::*; 78 | 79 | #[test] 80 | fn parse_file_option_produces_filename() { 81 | let (output, option) = parse_filename_option("FILE '/path/to/file.xls'").unwrap(); 82 | 83 | assert_eq!(output, ""); 84 | match option { 85 | UsingOption::File(filename) => assert_eq!(filename, "/path/to/file.xls"), 86 | _ => panic!("Expected file option") 87 | } 88 | } 89 | 90 | #[test] 91 | fn parse_filename_option_produces_filename() { 92 | let (output, option) = parse_filename_option("FILENAME '/path/to/file.xls'").unwrap(); 93 | 94 | assert_eq!(output, ""); 95 | match option { 96 | UsingOption::File(filename) => assert_eq!(filename, "/path/to/file.xls"), 97 | _ => panic!("Expected file option") 98 | } 99 | } 100 | 101 | #[test] 102 | fn parse_worksheet_option_produces_sheet_name() { 103 | let (output, option) = parse_worksheet_option("WORKSHEET 'Sheet 1'").unwrap(); 104 | 105 | assert_eq!(output, ""); 106 | match option { 107 | UsingOption::Worksheet(sheet_name) => assert_eq!(sheet_name, "Sheet 1"), 108 | _ => panic!("Expected worksheet option") 109 | } 110 | } 111 | 112 | #[test] 113 | fn parse_sheet_option_produces_sheet_name() { 114 | let (output, option) = parse_worksheet_option("SHEET 'Sheet 1'").unwrap(); 115 | 116 | assert_eq!(output, ""); 117 | match option { 118 | UsingOption::Worksheet(sheet_name) => assert_eq!(sheet_name, "Sheet 1"), 119 | _ => panic!("Expected worksheet option") 120 | } 121 | } 122 | 123 | #[test] 124 | fn parse_range_option_produces_range() { 125 | let (output, option) = parse_range_option("RANGE 'A1:ZZ99'").unwrap(); 126 | 127 | assert_eq!(output, ""); 128 | match option { 129 | UsingOption::Range(range) => { 130 | assert_eq!(range, "A1:ZZ99"); 131 | }, 132 | _ => panic!("Expected range option") 133 | } 134 | } 135 | 136 | #[test] 137 | fn parse_colnames_option_produces_colname() { 138 | let (output, option) = parse_colnames_option("COLNAMES '152'").unwrap(); 139 | 140 | assert_eq!(output, ""); 141 | match option { 142 | UsingOption::ColNames(colname) => { 143 | assert_eq!(colname, "152"); 144 | }, 145 | _ => panic!("Expected colnames option") 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/spreadsheet/cells.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Copy, Clone)] 2 | pub struct CellIndex { 3 | x: u32, 4 | y: u32, 5 | } 6 | 7 | impl CellIndex { 8 | 9 | pub fn new(x: u32, y: u32) -> Self { 10 | Self { x, y } 11 | } 12 | 13 | pub fn try_parse(s: &str) -> Option { 14 | let chars = s.chars(); 15 | 16 | let mut alpha = String::new(); 17 | let mut num = String::new(); 18 | 19 | for ch in chars { 20 | if ch.is_ascii_alphabetic() { 21 | if num.len() == 0 { 22 | alpha.push(ch); 23 | } else { 24 | return None; 25 | } 26 | } else if ch.is_ascii_digit() { 27 | num.push(ch); 28 | } 29 | } 30 | 31 | if alpha.len() > 0 { 32 | let x = column_to_index(alpha.to_uppercase().as_str()); 33 | if num.len() > 0 { 34 | let y = row_to_index(num.as_str()); 35 | Some(CellIndex::new(x, y)) 36 | } else { 37 | Some(CellIndex::new(x, 0)) 38 | } 39 | } else { 40 | None 41 | } 42 | } 43 | 44 | pub fn get_x(&self) -> u32 { 45 | self.x 46 | } 47 | 48 | pub fn get_y(&self) -> u32 { 49 | self.y 50 | } 51 | 52 | pub fn get_x_as_string(&self) -> String { 53 | index_to_column(self.x) 54 | } 55 | 56 | pub fn get_y_as_string(&self) -> String { 57 | index_to_row(self.y) 58 | } 59 | 60 | pub fn to_zero_indexed(&self) -> (u32, u32) { 61 | (if self.y > 0 { self.y - 1 } else { 0 }, 62 | if self.x > 0 { self.x - 1 } else { 0 }) 63 | } 64 | 65 | } 66 | 67 | #[derive(Debug, Copy, Clone)] 68 | pub struct CellRange { 69 | start: CellIndex, 70 | end: CellIndex, 71 | } 72 | 73 | impl CellRange { 74 | 75 | pub fn new(start: CellIndex, end: CellIndex) -> Self { 76 | Self { start, end } 77 | } 78 | 79 | pub fn try_parse(s: &str) -> Option { 80 | let parts: Vec<&str> = s.split(":").collect(); 81 | if parts.len() == 2 { 82 | let start = CellIndex::try_parse(parts[0])?; 83 | let end = CellIndex::try_parse(parts[1])?; 84 | Some(CellRange::new(start, end)) 85 | } else { 86 | None 87 | } 88 | } 89 | 90 | pub fn get_start(&self) -> CellIndex { 91 | self.start 92 | } 93 | 94 | pub fn get_end(&self) -> CellIndex { 95 | self.end 96 | } 97 | 98 | } 99 | 100 | fn column_to_index(column: &str) -> u32 { 101 | let column = column.as_bytes(); 102 | let mut sum = 0; 103 | let len = column.len(); 104 | for i in 0..len { 105 | let c = column[len - i - 1]; 106 | sum += (c - b'A' + 1) as u32 * 26u32.pow(i as u32); 107 | } 108 | sum 109 | } 110 | 111 | fn index_to_column(index: u32) -> String { 112 | let mut out = String::new(); 113 | let mut index = index; 114 | while index > 0 { 115 | let x = (index - 1) % 26; 116 | out.insert(0, (b'A' + x as u8) as char); 117 | index = (index - x) / 26; 118 | } 119 | out 120 | } 121 | 122 | fn row_to_index(row: &str) -> u32 { 123 | row.parse::().unwrap() 124 | } 125 | 126 | fn index_to_row(index: u32) -> String { 127 | (index + 1).to_string() 128 | } 129 | 130 | #[cfg(test)] 131 | mod tests { 132 | use super::*; 133 | 134 | #[test] 135 | fn column_to_index_gives_correct_value_for_a() { 136 | let index = column_to_index("A"); 137 | assert_eq!(index, 1); 138 | } 139 | 140 | #[test] 141 | fn column_to_index_gives_correct_value_for_z() { 142 | let index = column_to_index("Z"); 143 | assert_eq!(index, 26); 144 | } 145 | 146 | #[test] 147 | fn index_to_column_gives_correct_value_for_0() { 148 | let column = index_to_column(1); 149 | assert_eq!(column, "A"); 150 | } 151 | 152 | #[test] 153 | fn index_to_column_gives_correct_value_for_25() { 154 | let column = index_to_column(26); 155 | assert_eq!(column, "Z") 156 | } 157 | 158 | #[test] 159 | fn index_to_column_gives_correct_value_for_26() { 160 | let column = index_to_column(27); 161 | assert_eq!(column, "AA") 162 | } 163 | 164 | #[test] 165 | fn index_to_column_gives_correct_value_for_27() { 166 | let column = index_to_column(28); 167 | assert_eq!(column, "AB") 168 | } 169 | 170 | #[test] 171 | fn index_to_column_gives_correct_value_for_775() { 172 | let column = index_to_column(774); 173 | assert_eq!(column, "ACT") 174 | } 175 | 176 | #[test] 177 | fn try_parse_cell_index_from_single_letter() { 178 | let index = CellIndex::try_parse("A").unwrap(); 179 | assert_eq!(index.x, 1); 180 | assert_eq!(index.y, 0); 181 | } 182 | 183 | #[test] 184 | fn try_parse_cell_index_from_letter_and_number() { 185 | let index = CellIndex::try_parse("A1").unwrap(); 186 | assert_eq!(index.x, 1); 187 | assert_eq!(index.y, 1); 188 | } 189 | 190 | #[test] 191 | fn try_parse_cell_range_from_letter_and_number() { 192 | let range = CellRange::try_parse("A1:Z9").unwrap(); 193 | assert_eq!(range.start.x, 1); 194 | assert_eq!(range.start.y, 1); 195 | assert_eq!(range.end.x, 26); 196 | assert_eq!(range.end.y, 9); 197 | } 198 | 199 | #[test] 200 | fn to_zero_indexed_gives_0_indexed_tuple_in_y_x_format() { 201 | let index = CellIndex::new(1, 9); 202 | let tuple = index.to_zero_indexed(); 203 | assert_eq!(tuple.0, 8); 204 | assert_eq!(tuple.1, 0); 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /src/spreadsheet/manager.rs: -------------------------------------------------------------------------------- 1 | use crate::options::UsingOption; 2 | use crate::spreadsheet::{ 3 | cells::{CellIndex, CellRange}, 4 | reader::DataReader, 5 | }; 6 | use calamine::{open_workbook_auto, DataType, Range, Reader, Sheets}; 7 | use std::fs::File; 8 | use std::io::BufReader; 9 | use std::path::Path; 10 | use std::str::FromStr; 11 | 12 | pub struct DataManager { 13 | sheets: Sheets>, 14 | worksheet: String, 15 | range: Option, 16 | colnames_row: Option, 17 | } 18 | 19 | pub enum DataManagerError { 20 | NoFilename, 21 | NoWorksheet, 22 | Calamine(calamine::Error), 23 | } 24 | 25 | impl DataManager { 26 | pub fn get_sheets(&mut self) -> &mut Sheets> { 27 | &mut self.sheets 28 | } 29 | 30 | pub fn get_effective_range(&mut self) -> Range { 31 | let range = self.sheets.worksheet_range(self.worksheet.as_str()); 32 | if let Some(Ok(r)) = range { 33 | match self.range { 34 | Some(sub) => { 35 | let start = sub.get_start(); 36 | let mut end = sub.get_end(); 37 | 38 | if end.get_y() == 0 { 39 | end = CellIndex::new(end.get_x(), r.height() as u32) 40 | } 41 | 42 | r.range(start.to_zero_indexed(), end.to_zero_indexed()) 43 | } 44 | None => r, 45 | } 46 | } else { 47 | Range::empty() 48 | } 49 | } 50 | 51 | pub fn get_columns(&mut self) -> Vec { 52 | let range = self.get_effective_range(); 53 | if range.get_size().1 > 0 { 54 | let row_workspace_sheet = self.colnames_row 55 | .and_then(|v| Some((v, self.sheets.worksheet_range(self.worksheet.as_str())))) 56 | .and_then(|(row, sheet)| Some((row, sheet?.ok()?))); 57 | (range.start().unwrap().1..=range.end().unwrap().1) 58 | .into_iter() 59 | .map(|n| { 60 | row_workspace_sheet 61 | .as_ref() 62 | .and_then(|(row, sheet)| sheet.get_value((*row, n)).map(|v| v.to_string())) 63 | .unwrap_or_else(|| CellIndex::new(n + 1, 1).get_x_as_string()) 64 | }) 65 | .collect() 66 | } else { 67 | Vec::new() 68 | } 69 | } 70 | 71 | pub fn read(&mut self) -> DataReader { 72 | let range = self.get_effective_range(); 73 | 74 | DataReader::new(range) 75 | } 76 | } 77 | 78 | #[derive(Default)] 79 | pub struct DataManagerBuilder { 80 | file: Option, 81 | worksheet: Option, 82 | range: Option, 83 | colnames_row: Option, 84 | } 85 | 86 | impl DataManagerBuilder { 87 | pub fn new() -> Self { 88 | Self::default() 89 | } 90 | 91 | pub fn from_options(options: Vec) -> Self { 92 | let mut builder = Self::new(); 93 | 94 | for option in options { 95 | match option { 96 | UsingOption::File(file) => { 97 | builder = builder.file(file); 98 | } 99 | UsingOption::Worksheet(worksheet) => { 100 | builder = builder.worksheet(worksheet); 101 | } 102 | UsingOption::Range(range) => { 103 | builder = builder.range(CellRange::try_parse(range.as_str()).unwrap()); 104 | } 105 | UsingOption::ColNames(colnames) => { 106 | // We subtract 1 to go from excel indexing (which starts at 1) to 0-based 107 | // indexing of the row. 108 | builder = builder.colnames_row(u32::from_str(colnames.as_str()).unwrap().saturating_sub(1)); 109 | }, 110 | } 111 | } 112 | 113 | builder 114 | } 115 | 116 | pub fn file(mut self, file: String) -> Self { 117 | self.file = Some(file); 118 | self 119 | } 120 | 121 | pub fn worksheet(mut self, name: String) -> Self { 122 | self.worksheet = Some(name); 123 | self 124 | } 125 | 126 | pub fn range(mut self, range: CellRange) -> Self { 127 | self.range = Some(range); 128 | self 129 | } 130 | 131 | pub fn colnames_row(mut self, row: u32) -> Self { 132 | self.colnames_row = Some(row); 133 | self 134 | } 135 | 136 | pub fn open(self) -> Result { 137 | if let Some(file) = self.file { 138 | if let Some(worksheet) = self.worksheet { 139 | match open_workbook_auto(Path::new(&file)) { 140 | Ok(sheets) => Ok(DataManager { 141 | sheets, 142 | worksheet, 143 | range: self.range, 144 | colnames_row: self.colnames_row, 145 | }), 146 | Err(err) => Err(DataManagerError::Calamine(err)), 147 | } 148 | } else { 149 | Err(DataManagerError::NoWorksheet) 150 | } 151 | } else { 152 | Err(DataManagerError::NoFilename) 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/spreadsheet/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod cells; 2 | pub mod manager; 3 | pub mod reader; 4 | -------------------------------------------------------------------------------- /src/spreadsheet/reader.rs: -------------------------------------------------------------------------------- 1 | use calamine::{DataType, Range, Rows}; 2 | use std::mem::transmute; 3 | 4 | pub struct DataReader { 5 | range: Range, 6 | state: DataReaderState<'static>, 7 | } 8 | 9 | struct DataReaderState<'a> { 10 | rows: Rows<'a, DataType>, 11 | row: Option<&'a [DataType]>, 12 | rowid: u32, 13 | } 14 | 15 | impl DataReader { 16 | pub fn new(range: Range) -> Self { 17 | let mut rows = range.rows(); 18 | let row = rows.next(); 19 | let rowid = range.start().unwrap_or((0, 0)).0; 20 | 21 | // transmute to static because we have the self-referencing struct 22 | let rows = unsafe { 23 | transmute::, Rows<'static, DataType>>(rows) 24 | }; 25 | let row = unsafe { 26 | transmute::, Option<&'static [DataType]>>(row) 27 | }; 28 | 29 | DataReader { 30 | range, 31 | state: DataReaderState { rows, row, rowid }, 32 | } 33 | } 34 | 35 | pub fn has_value(&self) -> bool { 36 | self.state.row.is_some() 37 | } 38 | 39 | pub fn get_value(&self, i: usize) -> Option<&DataType> { 40 | if let Some(ref row) = self.state.row { 41 | if i < row.len() { 42 | let col = &row[i]; 43 | return Some(col); 44 | } 45 | } 46 | None 47 | } 48 | 49 | pub fn get_rowid(&self) -> u32 { 50 | self.state.rowid 51 | } 52 | 53 | pub fn move_next(&mut self) { 54 | self.state.row = self.state.rows.next(); 55 | 56 | if self.state.row.is_some() { 57 | self.state.rowid += 1; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/sqlite.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 | char *(*create_filename)(const char*,const char*,const char*, 335 | int,const char**); 336 | void (*free_filename)(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 | }; 344 | 345 | /* 346 | ** This is the function signature used for all extension entry points. It 347 | ** is also defined in the file "loadext.c". 348 | */ 349 | typedef int (*sqlite3_loadext_entry)( 350 | sqlite3 *db, /* Handle to the database. */ 351 | char **pzErrMsg, /* Used to set error string on failure. */ 352 | const sqlite3_api_routines *pThunk /* Extension API function pointers. */ 353 | ); 354 | 355 | /* 356 | ** The following macros redefine the API routines so that they are 357 | ** redirected through the global sqlite3_api structure. 358 | ** 359 | ** This header file is also used by the loadext.c source file 360 | ** (part of the main SQLite library - not an extension) so that 361 | ** it can get access to the sqlite3_api_routines structure 362 | ** definition. But the main library does not want to redefine 363 | ** the API. So the redefinition macros are only valid if the 364 | ** SQLITE_CORE macros is undefined. 365 | */ 366 | #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) 367 | #define sqlite3_aggregate_context sqlite3_api->aggregate_context 368 | #ifndef SQLITE_OMIT_DEPRECATED 369 | #define sqlite3_aggregate_count sqlite3_api->aggregate_count 370 | #endif 371 | #define sqlite3_bind_blob sqlite3_api->bind_blob 372 | #define sqlite3_bind_double sqlite3_api->bind_double 373 | #define sqlite3_bind_int sqlite3_api->bind_int 374 | #define sqlite3_bind_int64 sqlite3_api->bind_int64 375 | #define sqlite3_bind_null sqlite3_api->bind_null 376 | #define sqlite3_bind_parameter_count sqlite3_api->bind_parameter_count 377 | #define sqlite3_bind_parameter_index sqlite3_api->bind_parameter_index 378 | #define sqlite3_bind_parameter_name sqlite3_api->bind_parameter_name 379 | #define sqlite3_bind_text sqlite3_api->bind_text 380 | #define sqlite3_bind_text16 sqlite3_api->bind_text16 381 | #define sqlite3_bind_value sqlite3_api->bind_value 382 | #define sqlite3_busy_handler sqlite3_api->busy_handler 383 | #define sqlite3_busy_timeout sqlite3_api->busy_timeout 384 | #define sqlite3_changes sqlite3_api->changes 385 | #define sqlite3_close sqlite3_api->close 386 | #define sqlite3_collation_needed sqlite3_api->collation_needed 387 | #define sqlite3_collation_needed16 sqlite3_api->collation_needed16 388 | #define sqlite3_column_blob sqlite3_api->column_blob 389 | #define sqlite3_column_bytes sqlite3_api->column_bytes 390 | #define sqlite3_column_bytes16 sqlite3_api->column_bytes16 391 | #define sqlite3_column_count sqlite3_api->column_count 392 | #define sqlite3_column_database_name sqlite3_api->column_database_name 393 | #define sqlite3_column_database_name16 sqlite3_api->column_database_name16 394 | #define sqlite3_column_decltype sqlite3_api->column_decltype 395 | #define sqlite3_column_decltype16 sqlite3_api->column_decltype16 396 | #define sqlite3_column_double sqlite3_api->column_double 397 | #define sqlite3_column_int sqlite3_api->column_int 398 | #define sqlite3_column_int64 sqlite3_api->column_int64 399 | #define sqlite3_column_name sqlite3_api->column_name 400 | #define sqlite3_column_name16 sqlite3_api->column_name16 401 | #define sqlite3_column_origin_name sqlite3_api->column_origin_name 402 | #define sqlite3_column_origin_name16 sqlite3_api->column_origin_name16 403 | #define sqlite3_column_table_name sqlite3_api->column_table_name 404 | #define sqlite3_column_table_name16 sqlite3_api->column_table_name16 405 | #define sqlite3_column_text sqlite3_api->column_text 406 | #define sqlite3_column_text16 sqlite3_api->column_text16 407 | #define sqlite3_column_type sqlite3_api->column_type 408 | #define sqlite3_column_value sqlite3_api->column_value 409 | #define sqlite3_commit_hook sqlite3_api->commit_hook 410 | #define sqlite3_complete sqlite3_api->complete 411 | #define sqlite3_complete16 sqlite3_api->complete16 412 | #define sqlite3_create_collation sqlite3_api->create_collation 413 | #define sqlite3_create_collation16 sqlite3_api->create_collation16 414 | #define sqlite3_create_function sqlite3_api->create_function 415 | #define sqlite3_create_function16 sqlite3_api->create_function16 416 | #define sqlite3_create_module sqlite3_api->create_module 417 | #define sqlite3_create_module_v2 sqlite3_api->create_module_v2 418 | #define sqlite3_data_count sqlite3_api->data_count 419 | #define sqlite3_db_handle sqlite3_api->db_handle 420 | #define sqlite3_declare_vtab sqlite3_api->declare_vtab 421 | #define sqlite3_enable_shared_cache sqlite3_api->enable_shared_cache 422 | #define sqlite3_errcode sqlite3_api->errcode 423 | #define sqlite3_errmsg sqlite3_api->errmsg 424 | #define sqlite3_errmsg16 sqlite3_api->errmsg16 425 | #define sqlite3_exec sqlite3_api->exec 426 | #ifndef SQLITE_OMIT_DEPRECATED 427 | #define sqlite3_expired sqlite3_api->expired 428 | #endif 429 | #define sqlite3_finalize sqlite3_api->finalize 430 | #define sqlite3_free sqlite3_api->free 431 | #define sqlite3_free_table sqlite3_api->free_table 432 | #define sqlite3_get_autocommit sqlite3_api->get_autocommit 433 | #define sqlite3_get_auxdata sqlite3_api->get_auxdata 434 | #define sqlite3_get_table sqlite3_api->get_table 435 | #ifndef SQLITE_OMIT_DEPRECATED 436 | #define sqlite3_global_recover sqlite3_api->global_recover 437 | #endif 438 | #define sqlite3_interrupt sqlite3_api->interruptx 439 | #define sqlite3_last_insert_rowid sqlite3_api->last_insert_rowid 440 | #define sqlite3_libversion sqlite3_api->libversion 441 | #define sqlite3_libversion_number sqlite3_api->libversion_number 442 | #define sqlite3_malloc sqlite3_api->malloc 443 | #define sqlite3_mprintf sqlite3_api->mprintf 444 | #define sqlite3_open sqlite3_api->open 445 | #define sqlite3_open16 sqlite3_api->open16 446 | #define sqlite3_prepare sqlite3_api->prepare 447 | #define sqlite3_prepare16 sqlite3_api->prepare16 448 | #define sqlite3_prepare_v2 sqlite3_api->prepare_v2 449 | #define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2 450 | #define sqlite3_profile sqlite3_api->profile 451 | #define sqlite3_progress_handler sqlite3_api->progress_handler 452 | #define sqlite3_realloc sqlite3_api->realloc 453 | #define sqlite3_reset sqlite3_api->reset 454 | #define sqlite3_result_blob sqlite3_api->result_blob 455 | #define sqlite3_result_double sqlite3_api->result_double 456 | #define sqlite3_result_error sqlite3_api->result_error 457 | #define sqlite3_result_error16 sqlite3_api->result_error16 458 | #define sqlite3_result_int sqlite3_api->result_int 459 | #define sqlite3_result_int64 sqlite3_api->result_int64 460 | #define sqlite3_result_null sqlite3_api->result_null 461 | #define sqlite3_result_text sqlite3_api->result_text 462 | #define sqlite3_result_text16 sqlite3_api->result_text16 463 | #define sqlite3_result_text16be sqlite3_api->result_text16be 464 | #define sqlite3_result_text16le sqlite3_api->result_text16le 465 | #define sqlite3_result_value sqlite3_api->result_value 466 | #define sqlite3_rollback_hook sqlite3_api->rollback_hook 467 | #define sqlite3_set_authorizer sqlite3_api->set_authorizer 468 | #define sqlite3_set_auxdata sqlite3_api->set_auxdata 469 | #define sqlite3_snprintf sqlite3_api->xsnprintf 470 | #define sqlite3_step sqlite3_api->step 471 | #define sqlite3_table_column_metadata sqlite3_api->table_column_metadata 472 | #define sqlite3_thread_cleanup sqlite3_api->thread_cleanup 473 | #define sqlite3_total_changes sqlite3_api->total_changes 474 | #define sqlite3_trace sqlite3_api->trace 475 | #ifndef SQLITE_OMIT_DEPRECATED 476 | #define sqlite3_transfer_bindings sqlite3_api->transfer_bindings 477 | #endif 478 | #define sqlite3_update_hook sqlite3_api->update_hook 479 | #define sqlite3_user_data sqlite3_api->user_data 480 | #define sqlite3_value_blob sqlite3_api->value_blob 481 | #define sqlite3_value_bytes sqlite3_api->value_bytes 482 | #define sqlite3_value_bytes16 sqlite3_api->value_bytes16 483 | #define sqlite3_value_double sqlite3_api->value_double 484 | #define sqlite3_value_int sqlite3_api->value_int 485 | #define sqlite3_value_int64 sqlite3_api->value_int64 486 | #define sqlite3_value_numeric_type sqlite3_api->value_numeric_type 487 | #define sqlite3_value_text sqlite3_api->value_text 488 | #define sqlite3_value_text16 sqlite3_api->value_text16 489 | #define sqlite3_value_text16be sqlite3_api->value_text16be 490 | #define sqlite3_value_text16le sqlite3_api->value_text16le 491 | #define sqlite3_value_type sqlite3_api->value_type 492 | #define sqlite3_vmprintf sqlite3_api->vmprintf 493 | #define sqlite3_vsnprintf sqlite3_api->xvsnprintf 494 | #define sqlite3_overload_function sqlite3_api->overload_function 495 | #define sqlite3_prepare_v2 sqlite3_api->prepare_v2 496 | #define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2 497 | #define sqlite3_clear_bindings sqlite3_api->clear_bindings 498 | #define sqlite3_bind_zeroblob sqlite3_api->bind_zeroblob 499 | #define sqlite3_blob_bytes sqlite3_api->blob_bytes 500 | #define sqlite3_blob_close sqlite3_api->blob_close 501 | #define sqlite3_blob_open sqlite3_api->blob_open 502 | #define sqlite3_blob_read sqlite3_api->blob_read 503 | #define sqlite3_blob_write sqlite3_api->blob_write 504 | #define sqlite3_create_collation_v2 sqlite3_api->create_collation_v2 505 | #define sqlite3_file_control sqlite3_api->file_control 506 | #define sqlite3_memory_highwater sqlite3_api->memory_highwater 507 | #define sqlite3_memory_used sqlite3_api->memory_used 508 | #define sqlite3_mutex_alloc sqlite3_api->mutex_alloc 509 | #define sqlite3_mutex_enter sqlite3_api->mutex_enter 510 | #define sqlite3_mutex_free sqlite3_api->mutex_free 511 | #define sqlite3_mutex_leave sqlite3_api->mutex_leave 512 | #define sqlite3_mutex_try sqlite3_api->mutex_try 513 | #define sqlite3_open_v2 sqlite3_api->open_v2 514 | #define sqlite3_release_memory sqlite3_api->release_memory 515 | #define sqlite3_result_error_nomem sqlite3_api->result_error_nomem 516 | #define sqlite3_result_error_toobig sqlite3_api->result_error_toobig 517 | #define sqlite3_sleep sqlite3_api->sleep 518 | #define sqlite3_soft_heap_limit sqlite3_api->soft_heap_limit 519 | #define sqlite3_vfs_find sqlite3_api->vfs_find 520 | #define sqlite3_vfs_register sqlite3_api->vfs_register 521 | #define sqlite3_vfs_unregister sqlite3_api->vfs_unregister 522 | #define sqlite3_threadsafe sqlite3_api->xthreadsafe 523 | #define sqlite3_result_zeroblob sqlite3_api->result_zeroblob 524 | #define sqlite3_result_error_code sqlite3_api->result_error_code 525 | #define sqlite3_test_control sqlite3_api->test_control 526 | #define sqlite3_randomness sqlite3_api->randomness 527 | #define sqlite3_context_db_handle sqlite3_api->context_db_handle 528 | #define sqlite3_extended_result_codes sqlite3_api->extended_result_codes 529 | #define sqlite3_limit sqlite3_api->limit 530 | #define sqlite3_next_stmt sqlite3_api->next_stmt 531 | #define sqlite3_sql sqlite3_api->sql 532 | #define sqlite3_status sqlite3_api->status 533 | #define sqlite3_backup_finish sqlite3_api->backup_finish 534 | #define sqlite3_backup_init sqlite3_api->backup_init 535 | #define sqlite3_backup_pagecount sqlite3_api->backup_pagecount 536 | #define sqlite3_backup_remaining sqlite3_api->backup_remaining 537 | #define sqlite3_backup_step sqlite3_api->backup_step 538 | #define sqlite3_compileoption_get sqlite3_api->compileoption_get 539 | #define sqlite3_compileoption_used sqlite3_api->compileoption_used 540 | #define sqlite3_create_function_v2 sqlite3_api->create_function_v2 541 | #define sqlite3_db_config sqlite3_api->db_config 542 | #define sqlite3_db_mutex sqlite3_api->db_mutex 543 | #define sqlite3_db_status sqlite3_api->db_status 544 | #define sqlite3_extended_errcode sqlite3_api->extended_errcode 545 | #define sqlite3_log sqlite3_api->log 546 | #define sqlite3_soft_heap_limit64 sqlite3_api->soft_heap_limit64 547 | #define sqlite3_sourceid sqlite3_api->sourceid 548 | #define sqlite3_stmt_status sqlite3_api->stmt_status 549 | #define sqlite3_strnicmp sqlite3_api->strnicmp 550 | #define sqlite3_unlock_notify sqlite3_api->unlock_notify 551 | #define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint 552 | #define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint 553 | #define sqlite3_wal_hook sqlite3_api->wal_hook 554 | #define sqlite3_blob_reopen sqlite3_api->blob_reopen 555 | #define sqlite3_vtab_config sqlite3_api->vtab_config 556 | #define sqlite3_vtab_on_conflict sqlite3_api->vtab_on_conflict 557 | /* Version 3.7.16 and later */ 558 | #define sqlite3_close_v2 sqlite3_api->close_v2 559 | #define sqlite3_db_filename sqlite3_api->db_filename 560 | #define sqlite3_db_readonly sqlite3_api->db_readonly 561 | #define sqlite3_db_release_memory sqlite3_api->db_release_memory 562 | #define sqlite3_errstr sqlite3_api->errstr 563 | #define sqlite3_stmt_busy sqlite3_api->stmt_busy 564 | #define sqlite3_stmt_readonly sqlite3_api->stmt_readonly 565 | #define sqlite3_stricmp sqlite3_api->stricmp 566 | #define sqlite3_uri_boolean sqlite3_api->uri_boolean 567 | #define sqlite3_uri_int64 sqlite3_api->uri_int64 568 | #define sqlite3_uri_parameter sqlite3_api->uri_parameter 569 | #define sqlite3_uri_vsnprintf sqlite3_api->xvsnprintf 570 | #define sqlite3_wal_checkpoint_v2 sqlite3_api->wal_checkpoint_v2 571 | /* Version 3.8.7 and later */ 572 | #define sqlite3_auto_extension sqlite3_api->auto_extension 573 | #define sqlite3_bind_blob64 sqlite3_api->bind_blob64 574 | #define sqlite3_bind_text64 sqlite3_api->bind_text64 575 | #define sqlite3_cancel_auto_extension sqlite3_api->cancel_auto_extension 576 | #define sqlite3_load_extension sqlite3_api->load_extension 577 | #define sqlite3_malloc64 sqlite3_api->malloc64 578 | #define sqlite3_msize sqlite3_api->msize 579 | #define sqlite3_realloc64 sqlite3_api->realloc64 580 | #define sqlite3_reset_auto_extension sqlite3_api->reset_auto_extension 581 | #define sqlite3_result_blob64 sqlite3_api->result_blob64 582 | #define sqlite3_result_text64 sqlite3_api->result_text64 583 | #define sqlite3_strglob sqlite3_api->strglob 584 | /* Version 3.8.11 and later */ 585 | #define sqlite3_value_dup sqlite3_api->value_dup 586 | #define sqlite3_value_free sqlite3_api->value_free 587 | #define sqlite3_result_zeroblob64 sqlite3_api->result_zeroblob64 588 | #define sqlite3_bind_zeroblob64 sqlite3_api->bind_zeroblob64 589 | /* Version 3.9.0 and later */ 590 | #define sqlite3_value_subtype sqlite3_api->value_subtype 591 | #define sqlite3_result_subtype sqlite3_api->result_subtype 592 | /* Version 3.10.0 and later */ 593 | #define sqlite3_status64 sqlite3_api->status64 594 | #define sqlite3_strlike sqlite3_api->strlike 595 | #define sqlite3_db_cacheflush sqlite3_api->db_cacheflush 596 | /* Version 3.12.0 and later */ 597 | #define sqlite3_system_errno sqlite3_api->system_errno 598 | /* Version 3.14.0 and later */ 599 | #define sqlite3_trace_v2 sqlite3_api->trace_v2 600 | #define sqlite3_expanded_sql sqlite3_api->expanded_sql 601 | /* Version 3.18.0 and later */ 602 | #define sqlite3_set_last_insert_rowid sqlite3_api->set_last_insert_rowid 603 | /* Version 3.20.0 and later */ 604 | #define sqlite3_prepare_v3 sqlite3_api->prepare_v3 605 | #define sqlite3_prepare16_v3 sqlite3_api->prepare16_v3 606 | #define sqlite3_bind_pointer sqlite3_api->bind_pointer 607 | #define sqlite3_result_pointer sqlite3_api->result_pointer 608 | #define sqlite3_value_pointer sqlite3_api->value_pointer 609 | /* Version 3.22.0 and later */ 610 | #define sqlite3_vtab_nochange sqlite3_api->vtab_nochange 611 | #define sqlite3_value_nochange sqlite3_api->value_nochange 612 | #define sqlite3_vtab_collation sqlite3_api->vtab_collation 613 | /* Version 3.24.0 and later */ 614 | #define sqlite3_keyword_count sqlite3_api->keyword_count 615 | #define sqlite3_keyword_name sqlite3_api->keyword_name 616 | #define sqlite3_keyword_check sqlite3_api->keyword_check 617 | #define sqlite3_str_new sqlite3_api->str_new 618 | #define sqlite3_str_finish sqlite3_api->str_finish 619 | #define sqlite3_str_appendf sqlite3_api->str_appendf 620 | #define sqlite3_str_vappendf sqlite3_api->str_vappendf 621 | #define sqlite3_str_append sqlite3_api->str_append 622 | #define sqlite3_str_appendall sqlite3_api->str_appendall 623 | #define sqlite3_str_appendchar sqlite3_api->str_appendchar 624 | #define sqlite3_str_reset sqlite3_api->str_reset 625 | #define sqlite3_str_errcode sqlite3_api->str_errcode 626 | #define sqlite3_str_length sqlite3_api->str_length 627 | #define sqlite3_str_value sqlite3_api->str_value 628 | /* Version 3.25.0 and later */ 629 | #define sqlite3_create_window_function sqlite3_api->create_window_function 630 | /* Version 3.26.0 and later */ 631 | #define sqlite3_normalized_sql sqlite3_api->normalized_sql 632 | /* Version 3.28.0 and later */ 633 | #define sqlite3_stmt_isexplain sqlite3_api->stmt_isexplain 634 | #define sqlite3_value_frombind sqlite3_api->value_frombind 635 | /* Version 3.30.0 and later */ 636 | #define sqlite3_drop_modules sqlite3_api->drop_modules 637 | /* Version 3.31.0 and later */ 638 | #define sqlite3_hard_heap_limit64 sqlite3_api->hard_heap_limit64 639 | #define sqlite3_uri_key sqlite3_api->uri_key 640 | #define sqlite3_filename_database sqlite3_api->filename_database 641 | #define sqlite3_filename_journal sqlite3_api->filename_journal 642 | #define sqlite3_filename_wal sqlite3_api->filename_wal 643 | /* Version 3.32.0 and later */ 644 | #define sqlite3_create_filename sqlite3_api->create_filename 645 | #define sqlite3_free_filename sqlite3_api->free_filename 646 | #define sqlite3_database_file_object sqlite3_api->database_file_object 647 | /* Version 3.34.0 and later */ 648 | #define sqlite3_txn_state sqlite3_api->txn_state 649 | #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ 650 | 651 | #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) 652 | /* This case when the file really is being compiled as a loadable 653 | ** extension */ 654 | # define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0; 655 | # define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v; 656 | # define SQLITE_EXTENSION_INIT3 \ 657 | extern const sqlite3_api_routines *sqlite3_api; 658 | #else 659 | /* This case when the file is being statically linked into the 660 | ** application */ 661 | # define SQLITE_EXTENSION_INIT1 /*no-op*/ 662 | # define SQLITE_EXTENSION_INIT2(v) (void)v; /* unused parameter */ 663 | # define SQLITE_EXTENSION_INIT3 /*no-op*/ 664 | #endif 665 | 666 | #endif /* SQLITE3EXT_H */ 667 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::{c_void, CStr, CString}; 2 | use std::os::raw::{c_char, c_int, c_longlong}; 3 | use std::ptr::copy_nonoverlapping; 4 | use calamine::DataType; 5 | use crate::{parse_option, sqlite3, sqlite3_api_routines, sqlite3_context, UsingOption}; 6 | 7 | pub unsafe fn read_string_from_raw(raw: *const c_char) -> String { 8 | let cstr = CStr::from_ptr(raw); 9 | cstr.to_str().unwrap_or_default().to_string() 10 | } 11 | 12 | pub unsafe fn collect_strings_from_raw(n: usize, args: *const *const c_char) -> Vec { 13 | let mut vec = Vec::with_capacity(n); 14 | 15 | let args = args as *mut *const c_char; 16 | for i in 0..n { 17 | let arg = *(args.offset(i as isize)); 18 | let s = read_string_from_raw(arg); 19 | vec.push(s); 20 | } 21 | 22 | vec 23 | } 24 | 25 | pub unsafe fn error_to_sqlite3_string(api: *mut sqlite3_api_routines, err: String) -> Option<*mut c_char> { 26 | let cstr = CString::new(err).ok()?; 27 | let len = cstr.as_bytes_with_nul().len(); 28 | 29 | let ptr = ((*api).malloc.unwrap())(len as c_int) as *mut c_char; 30 | if !ptr.is_null() { 31 | copy_nonoverlapping(cstr.as_ptr(), ptr, len); 32 | Some(ptr) 33 | } else { 34 | None 35 | } 36 | } 37 | 38 | pub unsafe fn declare_table(db: *mut sqlite3, api: *mut sqlite3_api_routines, columns: Vec) -> c_int { 39 | let mut sql = String::from("CREATE TABLE sheet("); 40 | for column in columns { 41 | sql.push('`'); 42 | sql.push_str(column.as_str()); 43 | sql.push_str("`,"); 44 | } 45 | sql.pop(); 46 | sql.push(')'); 47 | 48 | let cstr = CString::new(sql).unwrap(); 49 | ((*api).declare_vtab.unwrap())(db, cstr.as_ptr() as _) 50 | } 51 | 52 | pub unsafe fn collect_options_from_args(argc: c_int, argv: *const *const c_char) -> Vec { 53 | let mut options = vec![]; 54 | 55 | let args = collect_strings_from_raw(argc as usize, argv); 56 | for arg in args { 57 | match parse_option(arg.as_str()) { 58 | Ok((_, option)) => options.push(option), 59 | _ => {} 60 | } 61 | } 62 | 63 | options 64 | } 65 | 66 | pub unsafe fn yield_result(p_context: *mut sqlite3_context, api: *mut sqlite3_api_routines, value: &DataType) { 67 | match value { 68 | DataType::String(s) => { 69 | let cstr = CString::new(s.as_bytes()).unwrap(); 70 | let len = cstr.as_bytes().len(); 71 | let raw = cstr.into_raw(); 72 | 73 | unsafe extern "C" fn destructor(raw: *mut c_void) { 74 | drop(CString::from_raw(raw as *mut c_char)); 75 | } 76 | 77 | ((*api).result_text.unwrap())( 78 | p_context, 79 | raw, 80 | len as c_int, 81 | Some(destructor), 82 | ); 83 | } 84 | DataType::Int(n) => ((*api).result_int64.unwrap())(p_context, *n as c_longlong), 85 | DataType::Float(f) => ((*api).result_double.unwrap())(p_context, *f), 86 | DataType::DateTime(f) => ((*api).result_double.unwrap())(p_context, *f), 87 | DataType::Bool(b) => { 88 | ((*api).result_int.unwrap())(p_context, if *b { 1 } else { 0 }) 89 | } 90 | DataType::Empty => ((*api).result_null.unwrap())(p_context), 91 | DataType::Error(_e) => ((*api).result_null.unwrap())(p_context), 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /tests/abcdef.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x2bool/xlite/5d36c8dd3f367750aa9d84f3c7d54ae042f2a6bc/tests/abcdef.xlsx -------------------------------------------------------------------------------- /tests/abcdef_colnames.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x2bool/xlite/5d36c8dd3f367750aa9d84f3c7d54ae042f2a6bc/tests/abcdef_colnames.xlsx -------------------------------------------------------------------------------- /tests/lib.rs: -------------------------------------------------------------------------------- 1 | use rusqlite::{Connection, params}; 2 | 3 | #[cfg(target_os = "linux")] 4 | static LIB_PATH: &str = "./target/debug/libxlite.so"; 5 | 6 | #[cfg(target_os = "macos")] 7 | static LIB_PATH: &str = "./target/debug/libxlite.dylib"; 8 | 9 | #[cfg(target_os = "windows")] 10 | static LIB_PATH: &str = "./target/debug/xlite.dll"; 11 | 12 | fn init_connection() -> Connection { 13 | let connection = Connection::open_in_memory().unwrap(); 14 | 15 | unsafe { 16 | connection.load_extension_enable().unwrap(); 17 | connection.load_extension(LIB_PATH, None).unwrap(); 18 | connection.load_extension_disable().unwrap(); 19 | } 20 | 21 | connection 22 | } 23 | 24 | #[derive(PartialEq, Debug)] 25 | struct Abcd { 26 | alpha: String, 27 | number: f32, 28 | word: String, 29 | kind: String, 30 | } 31 | 32 | #[derive(PartialEq, Debug)] 33 | struct AbcdAgg { 34 | kind: String, 35 | count: i32, 36 | } 37 | 38 | #[derive(PartialEq, Debug)] 39 | struct Bc { 40 | number: f32, 41 | word: String, 42 | } 43 | 44 | #[test] 45 | fn test_load_extension() { 46 | init_connection(); 47 | } 48 | 49 | #[test] 50 | fn test_abcdef_file_simple() { 51 | let connection = init_connection(); 52 | connection.execute("\ 53 | CREATE VIRTUAL TABLE test_data USING xlite(\ 54 | FILENAME './tests/abcdef.xlsx',\ 55 | WORKSHEET 'Sheet1'\ 56 | );\ 57 | ", params![]).unwrap(); 58 | 59 | let mut query = connection.prepare("\ 60 | SELECT * FROM test_data;\ 61 | ").unwrap(); 62 | 63 | let rows = query.query_map(params![], |row| Ok(Abcd { 64 | alpha: row.get(0).unwrap(), 65 | number: row.get(1).unwrap(), 66 | word: row.get(2).unwrap(), 67 | kind: row.get(3).unwrap(), 68 | })).unwrap(); 69 | 70 | let data = rows 71 | .map(|r| r.unwrap()) 72 | .collect::>(); 73 | 74 | assert_eq!(data, vec![ 75 | Abcd { alpha: "A".to_string(), number: 10.0, word: "ten".to_string(), kind: "even".to_string() }, 76 | Abcd { alpha: "B".to_string(), number: 11.0, word: "eleven".to_string(), kind: "odd".to_string() }, 77 | Abcd { alpha: "C".to_string(), number: 12.0, word: "twelve".to_string(), kind: "even".to_string() }, 78 | Abcd { alpha: "D".to_string(), number: 13.0, word: "thirteen".to_string(), kind: "odd".to_string() }, 79 | Abcd { alpha: "E".to_string(), number: 14.0, word: "fourteen".to_string(), kind: "even".to_string() }, 80 | Abcd { alpha: "F".to_string(), number: 15.0, word: "fifteen".to_string(), kind: "odd".to_string() }, 81 | ]); 82 | } 83 | 84 | #[test] 85 | fn test_abcdef_file_with_range() { 86 | let connection = init_connection(); 87 | connection.execute("\ 88 | CREATE VIRTUAL TABLE test_data USING xlite(\ 89 | FILENAME './tests/abcdef.xlsx',\ 90 | WORKSHEET 'Sheet1',\ 91 | RANGE 'B2:C5' 92 | );\ 93 | ", params![]).unwrap(); 94 | 95 | let mut query = connection.prepare("\ 96 | SELECT B, C FROM test_data;\ 97 | ").unwrap(); 98 | 99 | let rows = query.query_map(params![], |row| Ok(Bc { 100 | number: row.get(0).unwrap(), 101 | word: row.get(1).unwrap(), 102 | })).unwrap(); 103 | 104 | let data = rows 105 | .map(|r| r.unwrap()) 106 | .collect::>(); 107 | 108 | assert_eq!(data, vec![ 109 | Bc { number: 11.0, word: "eleven".to_string() }, 110 | Bc { number: 12.0, word: "twelve".to_string() }, 111 | Bc { number: 13.0, word: "thirteen".to_string() }, 112 | Bc { number: 14.0, word: "fourteen".to_string() }, 113 | ]); 114 | } 115 | 116 | #[test] 117 | fn test_abcdef_file_aggregate() { 118 | let connection = init_connection(); 119 | connection.execute("\ 120 | CREATE VIRTUAL TABLE test_data USING xlite(\ 121 | FILENAME './tests/abcdef.xlsx',\ 122 | WORKSHEET 'Sheet1'\ 123 | );\ 124 | ", params![]).unwrap(); 125 | 126 | let mut query = connection.prepare("\ 127 | SELECT D, count(*) FROM test_data GROUP BY D ORDER BY D;\ 128 | ").unwrap(); 129 | 130 | let rows = query.query_map(params![], |row| Ok(AbcdAgg { 131 | kind: row.get(0).unwrap(), 132 | count: row.get(1).unwrap(), 133 | })).unwrap(); 134 | 135 | let data = rows 136 | .map(|r| r.unwrap()) 137 | .collect::>(); 138 | 139 | assert_eq!(data, vec![ 140 | AbcdAgg { kind: "even".to_string(), count: 3 }, 141 | AbcdAgg { kind: "odd".to_string(), count: 3 }, 142 | ]); 143 | } 144 | 145 | #[test] 146 | fn test_abcdef_file_with_colnames() { 147 | let connection = init_connection(); 148 | connection.execute("\ 149 | CREATE VIRTUAL TABLE test_data USING xlite(\ 150 | FILENAME './tests/abcdef_colnames.xlsx',\ 151 | WORKSHEET 'Sheet1',\ 152 | RANGE 'A2:D7', 153 | COLNAMES '1' 154 | );\ 155 | ", params![]).unwrap(); 156 | 157 | let mut query = connection.prepare("\ 158 | SELECT alpha, number, word, kind FROM test_data;\ 159 | ").unwrap(); 160 | 161 | let rows = query.query_map(params![], |row| Ok(Abcd { 162 | alpha: row.get(0).unwrap(), 163 | number: row.get(1).unwrap(), 164 | word: row.get(2).unwrap(), 165 | kind: row.get(3).unwrap(), 166 | })).unwrap(); 167 | 168 | let data = rows 169 | .map(|r| r.unwrap()) 170 | .collect::>(); 171 | 172 | assert_eq!(data, vec![ 173 | Abcd { alpha: "A".to_string(), number: 10.0, word: "ten".to_string(), kind: "even".to_string() }, 174 | Abcd { alpha: "B".to_string(), number: 11.0, word: "eleven".to_string(), kind: "odd".to_string() }, 175 | Abcd { alpha: "C".to_string(), number: 12.0, word: "twelve".to_string(), kind: "even".to_string() }, 176 | Abcd { alpha: "D".to_string(), number: 13.0, word: "thirteen".to_string(), kind: "odd".to_string() }, 177 | Abcd { alpha: "E".to_string(), number: 14.0, word: "fourteen".to_string(), kind: "even".to_string() }, 178 | Abcd { alpha: "F".to_string(), number: 15.0, word: "fifteen".to_string(), kind: "odd".to_string() }, 179 | ]); 180 | } 181 | --------------------------------------------------------------------------------