summaryrefslogtreecommitdiffstats
path: root/third_party/rust/rusqlite/examples/loadable_extension.rs
blob: e913240573d7fcf9fe86329ba1681556a1bb8d47 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
//! Adaptation of https://sqlite.org/loadext.html#programming_loadable_extensions
use std::os::raw::{c_char, c_int};

use rusqlite::ffi;
use rusqlite::functions::FunctionFlags;
use rusqlite::types::{ToSqlOutput, Value};
use rusqlite::{to_sqlite_error, Connection, Result};

/// # build
/// ```sh
/// cargo build --example loadable_extension --features "loadable_extension modern_sqlite functions vtab trace"
/// ```
/// # test
/// ```sh
/// sqlite> .log on
/// sqlite> .load target/debug/examples/libloadable_extension.so
/// (28) Rusqlite extension initialized
/// sqlite> SELECT rusqlite_test_function();
/// Rusqlite extension loaded correctly!
/// ```
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[no_mangle]
pub extern "C" fn sqlite3_extension_init(
    db: *mut ffi::sqlite3,
    pz_err_msg: *mut *mut c_char,
    p_api: *mut ffi::sqlite3_api_routines,
) -> c_int {
    if p_api.is_null() {
        return ffi::SQLITE_ERROR;
    } else if let Err(err) = extension_init(db, p_api) {
        return unsafe { to_sqlite_error(&err, pz_err_msg) };
    }
    ffi::SQLITE_OK
}

fn extension_init(db: *mut ffi::sqlite3, p_api: *mut ffi::sqlite3_api_routines) -> Result<()> {
    let db = unsafe { Connection::extension_init2(db, p_api)? };
    db.create_scalar_function(
        "rusqlite_test_function",
        0,
        FunctionFlags::SQLITE_DETERMINISTIC,
        |_ctx| {
            Ok(ToSqlOutput::Owned(Value::Text(
                "Rusqlite extension loaded correctly!".to_string(),
            )))
        },
    )?;
    rusqlite::trace::log(ffi::SQLITE_WARNING, "Rusqlite extension initialized");
    Ok(())
}