From ee48859794487579416c25c1a50af684e533d652 Mon Sep 17 00:00:00 2001 From: gwenn Date: Thu, 7 Apr 2022 10:43:16 +0200 Subject: [PATCH] Make possible to specify subtype of SQL function --- src/functions.rs | 90 +++++++++++++++++++++++++++++------------------- src/lib.rs | 2 +- 2 files changed, 55 insertions(+), 37 deletions(-) diff --git a/src/functions.rs b/src/functions.rs index 6de9612..5a85752 100644 --- a/src/functions.rs +++ b/src/functions.rs @@ -34,7 +34,7 @@ //! regexp.is_match(text) //! }; //! -//! Ok(is_match) +//! Ok((is_match, None)) //! }, //! ) //! } @@ -247,13 +247,6 @@ impl Context<'_> { phantom: PhantomData, }) } - - /// Set the Subtype of an SQL function - #[cfg(feature = "modern_sqlite")] // 3.9.0 - #[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))] - pub fn set_result_subtype(&self, sub_type: std::os::raw::c_uint) { - unsafe { ffi::sqlite3_result_subtype(self.ctx, sub_type) }; - } } /// A reference to a connection handle with a lifetime bound to something. @@ -275,6 +268,9 @@ impl Deref for ConnectionRef<'_> { type AuxInner = Arc; +/// Subtype of an SQL function +pub type SubType = Option; + /// Aggregate is the callback interface for user-defined /// aggregate function. /// @@ -304,7 +300,7 @@ where /// given `None`. /// /// The passed context will have no arguments. - fn finalize(&self, _: &mut Context<'_>, _: Option) -> Result; + fn finalize(&self, _: &mut Context<'_>, _: Option) -> Result<(T, SubType)>; } /// `WindowAggregate` is the callback interface for @@ -318,7 +314,7 @@ where { /// Returns the current value of the aggregate. Unlike xFinal, the /// implementation should not delete any context. - fn value(&self, _: Option<&A>) -> Result; + fn value(&self, _: Option<&A>) -> Result<(T, SubType)>; /// Removes a row from the current window. fn inverse(&self, _: &mut Context<'_>, _: &mut A) -> Result<()>; @@ -381,7 +377,7 @@ impl Connection { /// FunctionFlags::SQLITE_UTF8 | FunctionFlags::SQLITE_DETERMINISTIC, /// |ctx| { /// let value = ctx.get::(0)?; - /// Ok(value / 2f64) + /// Ok((value / 2f64, None)) /// }, /// )?; /// @@ -403,7 +399,7 @@ impl Connection { x_func: F, ) -> Result<()> where - F: FnMut(&Context<'_>) -> Result + Send + UnwindSafe + 'static, + F: FnMut(&Context<'_>) -> Result<(T, SubType)> + Send + UnwindSafe + 'static, T: ToSql, { self.db @@ -485,7 +481,7 @@ impl InnerConnection { x_func: F, ) -> Result<()> where - F: FnMut(&Context<'_>) -> Result + Send + UnwindSafe + 'static, + F: FnMut(&Context<'_>) -> Result<(T, SubType)> + Send + UnwindSafe + 'static, T: ToSql, { unsafe extern "C" fn call_boxed_closure( @@ -493,7 +489,7 @@ impl InnerConnection { argc: c_int, argv: *mut *mut sqlite3_value, ) where - F: FnMut(&Context<'_>) -> Result, + F: FnMut(&Context<'_>) -> Result<(T, SubType)>, T: ToSql, { let r = catch_unwind(|| { @@ -512,11 +508,17 @@ impl InnerConnection { } Ok(r) => r, }; - let t = t.as_ref().map(|t| ToSql::to_sql(t)); + let t = t.as_ref().map(|(t, sub_type)| (ToSql::to_sql(t), sub_type)); match t { - Ok(Ok(ref value)) => set_result(ctx, value), - Ok(Err(err)) => report_error(ctx, &err), + Ok((Ok(ref value), sub_type)) => { + set_result(ctx, value); + #[cfg(feature = "modern_sqlite")] // 3.9.0 + if let Some(sub_type) = sub_type { + ffi::sqlite3_result_subtype(ctx, *sub_type); + } + } + Ok((Err(err), _)) => report_error(ctx, &err), Err(err) => report_error(ctx, err), } } @@ -752,10 +754,16 @@ where } Ok(r) => r, }; - let t = t.as_ref().map(|t| ToSql::to_sql(t)); + let t = t.as_ref().map(|(t, sub_type)| (ToSql::to_sql(t), sub_type)); match t { - Ok(Ok(ref value)) => set_result(ctx, value), - Ok(Err(err)) => report_error(ctx, &err), + Ok((Ok(ref value), sub_type)) => { + set_result(ctx, value); + #[cfg(feature = "modern_sqlite")] // 3.9.0 + if let Some(sub_type) = sub_type { + ffi::sqlite3_result_subtype(ctx, *sub_type); + } + } + Ok((Err(err), _)) => report_error(ctx, &err), Err(err) => report_error(ctx, err), } } @@ -796,10 +804,16 @@ where } Ok(r) => r, }; - let t = t.as_ref().map(|t| ToSql::to_sql(t)); + let t = t.as_ref().map(|(t, sub_type)| (ToSql::to_sql(t), sub_type)); match t { - Ok(Ok(ref value)) => set_result(ctx, value), - Ok(Err(err)) => report_error(ctx, &err), + Ok((Ok(ref value), sub_type)) => { + set_result(ctx, value); + #[cfg(feature = "modern_sqlite")] // 3.9.0 + if let Some(sub_type) = sub_type { + ffi::sqlite3_result_subtype(ctx, *sub_type); + } + } + Ok((Err(err), _)) => report_error(ctx, &err), Err(err) => report_error(ctx, err), } } @@ -812,13 +826,13 @@ mod test { #[cfg(feature = "window")] use crate::functions::WindowAggregate; - use crate::functions::{Aggregate, Context, FunctionFlags}; + use crate::functions::{Aggregate, Context, FunctionFlags, SubType}; use crate::{Connection, Error, Result}; - fn half(ctx: &Context<'_>) -> Result { + fn half(ctx: &Context<'_>) -> Result<(c_double, SubType)> { assert_eq!(ctx.len(), 1, "called with unexpected number of arguments"); let value = ctx.get::(0)?; - Ok(value / 2f64) + Ok((value / 2f64, None)) } #[test] @@ -857,7 +871,7 @@ mod test { // This implementation of a regexp scalar function uses SQLite's auxiliary data // (https://www.sqlite.org/c3ref/get_auxdata.html) to avoid recompiling the regular // expression multiple times within one query. - fn regexp_with_auxilliary(ctx: &Context<'_>) -> Result { + fn regexp_with_auxilliary(ctx: &Context<'_>) -> Result<(bool, SubType)> { assert_eq!(ctx.len(), 2, "called with unexpected number of arguments"); type BoxError = Box; let regexp: std::sync::Arc = ctx @@ -874,7 +888,7 @@ mod test { regexp.is_match(text) }; - Ok(is_match) + Ok((is_match, None)) } #[test] @@ -925,7 +939,7 @@ mod test { ret.push_str(&s); } - Ok(ret) + Ok((ret, None)) }, )?; @@ -950,7 +964,7 @@ mod test { assert_eq!(ctx.get_aux::(0), Err(Error::GetAuxWrongType)); assert_eq!(*ctx.get_aux::(0)?.unwrap(), 100); } - Ok(true) + Ok((true, None)) })?; let res: bool = db.query_row( @@ -976,8 +990,12 @@ mod test { Ok(()) } - fn finalize(&self, _: &mut Context<'_>, sum: Option) -> Result> { - Ok(sum) + fn finalize( + &self, + _: &mut Context<'_>, + sum: Option, + ) -> Result<(Option, SubType)> { + Ok((sum, None)) } } @@ -991,8 +1009,8 @@ mod test { Ok(()) } - fn finalize(&self, _: &mut Context<'_>, sum: Option) -> Result { - Ok(sum.unwrap_or(0)) + fn finalize(&self, _: &mut Context<'_>, sum: Option) -> Result<(i64, SubType)> { + Ok((sum.unwrap_or(0), None)) } } @@ -1050,8 +1068,8 @@ mod test { Ok(()) } - fn value(&self, sum: Option<&i64>) -> Result> { - Ok(sum.copied()) + fn value(&self, sum: Option<&i64>) -> Result<(Option, SubType)> { + Ok((sum.copied(), None)) } } diff --git a/src/lib.rs b/src/lib.rs index 070b1a7..e1ea09f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1733,7 +1733,7 @@ mod test { crate::functions::FunctionFlags::default(), move |_| { interrupt_handle.interrupt(); - Ok(0) + Ok((0, None)) }, )?;