Introduce SqlFnArg

This commit is contained in:
gwenn 2024-01-21 10:13:07 +01:00
parent 83d67d5a29
commit f48c5781a1
6 changed files with 66 additions and 22 deletions

View File

@ -1,5 +1,6 @@
//! Code related to `sqlite3_context` common to `functions` and `vtab` modules. //! Code related to `sqlite3_context` common to `functions` and `vtab` modules.
use libsqlite3_sys::sqlite3_value;
use std::os::raw::{c_int, c_void}; use std::os::raw::{c_int, c_void};
#[cfg(feature = "array")] #[cfg(feature = "array")]
use std::rc::Rc; use std::rc::Rc;
@ -16,7 +17,11 @@ use crate::vtab::array::{free_array, ARRAY_TYPE};
// is often known to the compiler, and thus const prop/DCE can substantially // is often known to the compiler, and thus const prop/DCE can substantially
// simplify the function. // simplify the function.
#[inline] #[inline]
pub(super) unsafe fn set_result(ctx: *mut sqlite3_context, result: &ToSqlOutput<'_>) { pub(super) unsafe fn set_result(
ctx: *mut sqlite3_context,
args: &[*mut sqlite3_value],
result: &ToSqlOutput<'_>,
) {
let value = match *result { let value = match *result {
ToSqlOutput::Borrowed(v) => v, ToSqlOutput::Borrowed(v) => v,
ToSqlOutput::Owned(ref v) => ValueRef::from(v), ToSqlOutput::Owned(ref v) => ValueRef::from(v),
@ -26,6 +31,10 @@ pub(super) unsafe fn set_result(ctx: *mut sqlite3_context, result: &ToSqlOutput<
// TODO sqlite3_result_zeroblob64 // 3.8.11 // TODO sqlite3_result_zeroblob64 // 3.8.11
return ffi::sqlite3_result_zeroblob(ctx, len); return ffi::sqlite3_result_zeroblob(ctx, len);
} }
#[cfg(feature = "functions")]
ToSqlOutput::Arg(i) => {
return ffi::sqlite3_result_value(ctx, args[i]);
}
#[cfg(feature = "array")] #[cfg(feature = "array")]
ToSqlOutput::Array(ref a) => { ToSqlOutput::Array(ref a) => {
return ffi::sqlite3_result_pointer( return ffi::sqlite3_result_pointer(

View File

@ -149,6 +149,15 @@ impl Context<'_> {
unsafe { ValueRef::from_value(arg) } unsafe { ValueRef::from_value(arg) }
} }
/// Returns the `idx`th argument as a `SqlFnArg`.
/// To be used when the SQL function result is one of its arguments.
#[inline]
#[must_use]
pub fn get_arg(&self, idx: usize) -> SqlFnArg {
assert!(idx < self.len());
SqlFnArg { idx }
}
/// Returns the subtype of `idx`th argument. /// Returns the subtype of `idx`th argument.
/// ///
/// # Failure /// # Failure
@ -275,12 +284,26 @@ impl<T: ToSql> SqlFnOutput for (T, SubType) {
} }
} }
unsafe fn sql_result<T: SqlFnOutput>(ctx: *mut sqlite3_context, r: Result<T>) { /// n-th arg of an SQL scalar function
pub struct SqlFnArg {
idx: usize,
}
impl ToSql for SqlFnArg {
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
Ok(ToSqlOutput::Arg(self.idx))
}
}
unsafe fn sql_result<T: SqlFnOutput>(
ctx: *mut sqlite3_context,
args: &[*mut sqlite3_value],
r: Result<T>,
) {
let t = r.as_ref().map(SqlFnOutput::to_sql); let t = r.as_ref().map(SqlFnOutput::to_sql);
match t { match t {
Ok(Ok((ref value, sub_type))) => { Ok(Ok((ref value, sub_type))) => {
set_result(ctx, value); set_result(ctx, args, value);
if let Some(sub_type) = sub_type { if let Some(sub_type) = sub_type {
ffi::sqlite3_result_subtype(ctx, sub_type); ffi::sqlite3_result_subtype(ctx, sub_type);
} }
@ -514,13 +537,11 @@ impl InnerConnection {
F: FnMut(&Context<'_>) -> Result<T>, F: FnMut(&Context<'_>) -> Result<T>,
T: SqlFnOutput, T: SqlFnOutput,
{ {
let args = slice::from_raw_parts(argv, argc as usize);
let r = catch_unwind(|| { let r = catch_unwind(|| {
let boxed_f: *mut F = ffi::sqlite3_user_data(ctx).cast::<F>(); let boxed_f: *mut F = ffi::sqlite3_user_data(ctx).cast::<F>();
assert!(!boxed_f.is_null(), "Internal error - null function pointer"); assert!(!boxed_f.is_null(), "Internal error - null function pointer");
let ctx = Context { let ctx = Context { ctx, args };
ctx,
args: slice::from_raw_parts(argv, argc as usize),
};
(*boxed_f)(&ctx) (*boxed_f)(&ctx)
}); });
let t = match r { let t = match r {
@ -530,7 +551,7 @@ impl InnerConnection {
} }
Ok(r) => r, Ok(r) => r,
}; };
sql_result(ctx, t); sql_result(ctx, args, t);
} }
let boxed_f: *mut F = Box::into_raw(Box::new(x_func)); let boxed_f: *mut F = Box::into_raw(Box::new(x_func));
@ -767,7 +788,7 @@ where
} }
Ok(r) => r, Ok(r) => r,
}; };
sql_result(ctx, t); sql_result(ctx, &[], t);
} }
#[cfg(feature = "window")] #[cfg(feature = "window")]
@ -799,7 +820,7 @@ where
} }
Ok(r) => r, Ok(r) => r,
}; };
sql_result(ctx, t); sql_result(ctx, &[], t);
} }
#[cfg(test)] #[cfg(test)]
@ -809,8 +830,8 @@ mod test {
#[cfg(feature = "window")] #[cfg(feature = "window")]
use crate::functions::WindowAggregate; use crate::functions::WindowAggregate;
use crate::functions::{Aggregate, Context, FunctionFlags, SubType}; use crate::functions::{Aggregate, Context, FunctionFlags, SqlFnArg, SubType};
use crate::{Connection, Error, Result, ValueRef}; use crate::{Connection, Error, Result};
fn half(ctx: &Context<'_>) -> Result<c_double> { fn half(ctx: &Context<'_>) -> Result<c_double> {
assert_eq!(ctx.len(), 1, "called with unexpected number of arguments"); assert_eq!(ctx.len(), 1, "called with unexpected number of arguments");
@ -1093,9 +1114,9 @@ mod test {
fn test_getsubtype(ctx: &Context<'_>) -> Result<i32> { fn test_getsubtype(ctx: &Context<'_>) -> Result<i32> {
Ok(ctx.get_subtype(0) as i32) Ok(ctx.get_subtype(0) as i32)
} }
fn test_setsubtype<'a>(ctx: &'a Context<'_>) -> Result<(ValueRef<'a>, SubType)> { fn test_setsubtype(ctx: &Context<'_>) -> Result<(SqlFnArg, SubType)> {
use std::os::raw::c_uint; use std::os::raw::c_uint;
let value = ctx.get_raw(0); let value = ctx.get_arg(0);
let sub_type = ctx.get::<c_uint>(1)?; let sub_type = ctx.get::<c_uint>(1)?;
Ok((value, Some(sub_type))) Ok((value, Some(sub_type)))
} }

View File

@ -70,6 +70,13 @@ impl Sql {
Some(format!("Unsupported value \"{value:?}\"")), Some(format!("Unsupported value \"{value:?}\"")),
)); ));
} }
#[cfg(feature = "functions")]
ToSqlOutput::Arg(_) => {
return Err(Error::SqliteFailure(
ffi::Error::new(ffi::SQLITE_MISUSE),
Some(format!("Unsupported value \"{value:?}\"")),
));
}
#[cfg(feature = "array")] #[cfg(feature = "array")]
ToSqlOutput::Array(_) => { ToSqlOutput::Array(_) => {
return Err(Error::SqliteFailure( return Err(Error::SqliteFailure(

View File

@ -606,6 +606,13 @@ impl Statement<'_> {
.conn .conn
.decode_result(unsafe { ffi::sqlite3_bind_zeroblob(ptr, col as c_int, len) }); .decode_result(unsafe { ffi::sqlite3_bind_zeroblob(ptr, col as c_int, len) });
} }
#[cfg(feature = "functions")]
ToSqlOutput::Arg(_) => {
return Err(Error::SqliteFailure(
ffi::Error::new(ffi::SQLITE_MISUSE),
Some(format!("Unsupported value \"{value:?}\"")),
));
}
#[cfg(feature = "array")] #[cfg(feature = "array")]
ToSqlOutput::Array(a) => { ToSqlOutput::Array(a) => {
return self.conn.decode_result(unsafe { return self.conn.decode_result(unsafe {

View File

@ -22,6 +22,11 @@ pub enum ToSqlOutput<'a> {
#[cfg_attr(docsrs, doc(cfg(feature = "blob")))] #[cfg_attr(docsrs, doc(cfg(feature = "blob")))]
ZeroBlob(i32), ZeroBlob(i32),
/// n-th arg of an SQL scalar function
#[cfg(feature = "functions")]
#[cfg_attr(docsrs, doc(cfg(feature = "functions")))]
Arg(usize),
/// `feature = "array"` /// `feature = "array"`
#[cfg(feature = "array")] #[cfg(feature = "array")]
#[cfg_attr(docsrs, doc(cfg(feature = "array")))] #[cfg_attr(docsrs, doc(cfg(feature = "array")))]
@ -107,6 +112,8 @@ impl ToSql for ToSqlOutput<'_> {
#[cfg(feature = "blob")] #[cfg(feature = "blob")]
ToSqlOutput::ZeroBlob(i) => ToSqlOutput::ZeroBlob(i), ToSqlOutput::ZeroBlob(i) => ToSqlOutput::ZeroBlob(i),
#[cfg(feature = "functions")]
ToSqlOutput::Arg(i) => ToSqlOutput::Arg(i),
#[cfg(feature = "array")] #[cfg(feature = "array")]
ToSqlOutput::Array(ref a) => ToSqlOutput::Array(a.clone()), ToSqlOutput::Array(ref a) => ToSqlOutput::Array(a.clone()),
}) })
@ -292,13 +299,6 @@ impl ToSql for Value {
} }
} }
impl<'a> ToSql for ValueRef<'a> {
#[inline]
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
Ok(ToSqlOutput::Borrowed(*self))
}
}
impl<T: ToSql> ToSql for Option<T> { impl<T: ToSql> ToSql for Option<T> {
#[inline] #[inline]
fn to_sql(&self) -> Result<ToSqlOutput<'_>> { fn to_sql(&self) -> Result<ToSqlOutput<'_>> {

View File

@ -704,7 +704,7 @@ impl Context {
#[inline] #[inline]
pub fn set_result<T: ToSql>(&mut self, value: &T) -> Result<()> { pub fn set_result<T: ToSql>(&mut self, value: &T) -> Result<()> {
let t = value.to_sql()?; let t = value.to_sql()?;
unsafe { set_result(self.0, &t) }; unsafe { set_result(self.0, &[], &t) };
Ok(()) Ok(())
} }