use sqlite3_ext::{ Connection, FromValue, Result, ValueRef, ValueType, function::Context, sqlite3_ext_fn, sqlite3_ext_main, }; fn add(a: i32, b: i32) -> i32 { a + b } #[sqlite3_ext_fn(n_args = 2, deterministic)] fn add_sqlite(ctx: &mut Context, args: &mut [&mut ValueRef]) -> Result<()> { if args[0].value_type() != ValueType::Integer || args[1].value_type() != ValueType::Integer { return Err(sqlite3_ext::Error::Module( "myadd requires both arguments to be an integer".to_string(), )); } let a = args[0].get_i32(); let b = args[1].get_i32(); let result = add(a, b); ctx.set_result(result)?; Ok(()) } #[sqlite3_ext_main(persistent)] fn init(db: &Connection) -> Result<()> { db.create_scalar_function("myadd", &ADD_SQLITE_OPTS, add_sqlite)?; Ok(()) } #[cfg(all(test, feature = "static"))] mod test { use super::*; use sqlite3_ext::{Database, Error, FallibleIterator, FallibleIteratorMut}; fn setup() -> Result { let conn = Database::open(":memory:")?; init(&conn)?; Ok(conn) } #[test] fn test_working() -> Result<()> { let conn = setup()?; let results: Vec = conn .prepare("SELECT myadd(10, 20)")? .query(())? .map(|row| Ok(row[0].get_i64())) .collect()?; assert_eq!(results, vec![30]); Ok(()) } #[test] fn test_wrong_arg_number() -> Result<()> { let conn = setup()?; let result = conn.prepare("SELECT myadd(10)"); assert_eq!( result.unwrap_err(), Error::Sqlite( 1, Some("wrong number of arguments to function myadd()".to_string()), ) ); Ok(()) } #[test] fn test_wrong_arg_type() -> Result<()> { let conn = setup()?; let mut statement = conn.prepare("SELECT myadd(10, 'hello!')")?; let result = statement.query(())?.next(); assert_eq!( result.unwrap_err(), Error::Sqlite( 1, Some("myadd requires both arguments to be an integer".to_string()), ) ); Ok(()) } }