Skip to content

Commit b50cf77

Browse files
committed
conflicts, my bad
2 parents 9d5e90e + 2344863 commit b50cf77

5 files changed

Lines changed: 97 additions & 2 deletions

File tree

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,6 @@ crate-type = ["cdylib"]
3939
name = "characters"
4040
crate-type = ["cdylib"]
4141

42+
[[example]]
43+
name = "load_permanent"
44+
crate-type = ["cdylib"]

examples/load_permanent.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//! cargo build --example load_permanent
2+
//! sqlite3 :memory: '.read examples/test.sql'
3+
4+
use sqlite_loadable::prelude::*;
5+
use sqlite_loadable::{api, define_scalar_function, Result};
6+
7+
// This function will be registered as a scalar function named "hello", and will be called on
8+
// every invocation. It's goal is to return a string of "hello, NAME!" where NAME is the
9+
// text value of the 1st argument.
10+
pub fn hello(context: *mut sqlite3_context, values: &[*mut sqlite3_value]) -> Result<()> {
11+
let name = api::value_text(values.get(0).expect("1st argument as name"))?;
12+
13+
api::result_text(context, format!("hello, {}!", name))?;
14+
Ok(())
15+
}
16+
17+
// Exposes a extern C function named "sqlite3_hello_init" in the compiled dynamic library,
18+
// the "entrypoint" that SQLite will use to load the extension.
19+
// Notice the naming sequence - "sqlite3_" followed by "hello" then "_init". Since the
20+
// compiled file is named "libhello.dylib" (or .so/.dll depending on your operating system),
21+
// SQLite by default will look for an entrypoint called "sqlite3_hello_init".
22+
// See "Loading an Extension" for more details <https://www.sqlite.org/loadext.html#loading_an_extension>
23+
#[sqlite_entrypoint_permanent]
24+
pub fn sqlite3_hello_init(db: *mut sqlite3) -> Result<()> {
25+
let flags = FunctionFlags::UTF8 | FunctionFlags::DETERMINISTIC;
26+
define_scalar_function(db, "hello", 1, hello, flags)?;
27+
Ok(())
28+
}

sqlite-loadable-macros/src/lib.rs

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use syn::{parse_macro_input, spanned::Spanned, Item};
55
use proc_macro::TokenStream;
66
use quote::quote_spanned;
77

8-
/// Wraps an entrypoint function to expose an unsafe extern "C" function of the same name.
8+
/// Wraps an entrypoint function to expose an unsafe extern "C" function of the same name.
99
#[proc_macro_attribute]
1010
pub fn sqlite_entrypoint(_attr: TokenStream, item: TokenStream) -> TokenStream {
1111
let ast = parse_macro_input!(item as syn::Item);
@@ -26,7 +26,7 @@ pub fn sqlite_entrypoint(_attr: TokenStream, item: TokenStream) -> TokenStream {
2626

2727
/// # Safety
2828
///
29-
/// Should only be called by underlying SQLite C APIs,
29+
/// Should only be called by underlying SQLite C APIs,
3030
/// like sqlite3_auto_extension and sqlite3_cancel_auto_extension.
3131
#[no_mangle]
3232
pub unsafe extern "C" fn #c_entrypoint(
@@ -44,3 +44,43 @@ pub fn sqlite_entrypoint(_attr: TokenStream, item: TokenStream) -> TokenStream {
4444
_ => panic!("Only function items are allowed on sqlite_entrypoint"),
4545
}
4646
}
47+
48+
/// Wraps an entrypoint function to expose an unsafe extern "C" function of the same name.
49+
#[proc_macro_attribute]
50+
pub fn sqlite_entrypoint_permanent(_attr: TokenStream, item: TokenStream) -> TokenStream {
51+
let ast = parse_macro_input!(item as syn::Item);
52+
match ast {
53+
Item::Fn(mut func) => {
54+
let c_entrypoint = func.sig.ident.clone();
55+
56+
let original_funcname = func.sig.ident.to_string();
57+
func.sig.ident = Ident::new(
58+
format!("_{}", original_funcname).as_str(),
59+
func.sig.ident.span(),
60+
);
61+
62+
let prefixed_original_function = func.sig.ident.clone();
63+
64+
quote_spanned! {func.span()=>
65+
#func
66+
67+
/// # Safety
68+
///
69+
/// Should only be called by underlying SQLite C APIs,
70+
/// like sqlite3_auto_extension and sqlite3_cancel_auto_extension.
71+
#[no_mangle]
72+
pub unsafe extern "C" fn #c_entrypoint(
73+
db: *mut sqlite3,
74+
pz_err_msg: *mut *mut c_char,
75+
p_api: *mut sqlite3_api_routines,
76+
) -> c_uint {
77+
register_entrypoint_load_permanently(db, pz_err_msg, p_api, #prefixed_original_function)
78+
}
79+
80+
81+
}
82+
.into()
83+
}
84+
_ => panic!("Only function items are allowed on sqlite_entrypoint"),
85+
}
86+
}

src/entrypoints.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,24 @@ where
2626
Err(err) => err.code_extended(),
2727
}
2828
}
29+
30+
/// Low-level wrapper around an entrypoint to a SQLite extension that loads permanently. You
31+
/// shouldn't have to use this directly - the sqlite_entrypoint_permanent macro will do this
32+
/// for you.
33+
pub fn register_entrypoint_load_permanently<F>(
34+
db: *mut sqlite3,
35+
_pz_err_msg: *mut *mut c_char,
36+
p_api: *mut sqlite3_api_routines,
37+
callback: F,
38+
) -> c_uint
39+
where
40+
F: Fn(*mut sqlite3) -> Result<()>,
41+
{
42+
unsafe {
43+
faux_sqlite_extension_init2(p_api);
44+
}
45+
match callback(db) {
46+
Ok(()) => 256, // https://www.sqlite.org/rescode.html#ok_load_permanently
47+
Err(err) => err.code_extended(),
48+
}
49+
}

src/prelude.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@
33
#[doc(inline)]
44
pub use crate::entrypoints::register_entrypoint;
55
#[doc(inline)]
6+
pub use crate::entrypoints::register_entrypoint_load_permanently;
7+
#[doc(inline)]
68
pub use sqlite3ext_sys::{
79
sqlite3, sqlite3_api_routines, sqlite3_context, sqlite3_value, sqlite3_vtab,
810
sqlite3_vtab_cursor,
911
};
1012
pub use sqlite_loadable_macros::sqlite_entrypoint;
13+
pub use sqlite_loadable_macros::sqlite_entrypoint_permanent;
1114

1215
pub use std::os::raw::{c_char, c_uint};
1316

0 commit comments

Comments
 (0)