Skip to main content

diesel/sqlite/connection/
functions.rs

1#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
2extern crate libsqlite3_sys as ffi;
3
4#[cfg(all(target_family = "wasm", target_os = "unknown"))]
5use sqlite_wasm_rs as ffi;
6
7use super::raw::RawConnection;
8use super::{Sqlite, SqliteAggregateFunction, SqliteBindValue};
9use crate::backend::Backend;
10use crate::deserialize::{FromSqlRow, StaticallySizedRow};
11use crate::result::{DatabaseErrorKind, Error, QueryResult};
12use crate::row::{Field, PartialRow, Row, RowIndex, RowSealed};
13use crate::serialize::{IsNull, Output, ToSql};
14use crate::sql_types::HasSqlType;
15use crate::sqlite::SqliteFunctionBehavior;
16use crate::sqlite::SqliteValue;
17use crate::sqlite::connection::bind_collector::SqliteBindValueRef;
18use crate::sqlite::connection::sqlite_value::OwnedSqliteValue;
19use alloc::boxed::Box;
20use alloc::string::ToString;
21
22pub(super) fn register<ArgsSqlType, RetSqlType, Args, Ret, F>(
23    conn: &RawConnection,
24    fn_name: &str,
25    behavior: SqliteFunctionBehavior,
26    mut f: F,
27) -> QueryResult<()>
28where
29    F: FnMut(&RawConnection, Args) -> Ret + core::panic::UnwindSafe + Send + 'static,
30    Args: FromSqlRow<ArgsSqlType, Sqlite> + StaticallySizedRow<ArgsSqlType, Sqlite>,
31    Ret: ToSql<RetSqlType, Sqlite>,
32    Sqlite: HasSqlType<RetSqlType>,
33{
34    let fields_needed = Args::FIELD_COUNT;
35    if fields_needed > 127 {
36        return Err(Error::DatabaseError(
37            DatabaseErrorKind::UnableToSendCommand,
38            Box::new("SQLite functions cannot take more than 127 parameters".to_string()),
39        ));
40    }
41
42    conn.register_sql_function(fn_name, fields_needed, behavior, move |conn, args| {
43        let args = build_sql_function_args::<ArgsSqlType, Args>(args, conn.internal_connection)?;
44
45        Ok(f(conn, args))
46    })?;
47    Ok(())
48}
49
50pub(super) fn register_noargs<RetSqlType, Ret, F>(
51    conn: &RawConnection,
52    fn_name: &str,
53    behavior: SqliteFunctionBehavior,
54    mut f: F,
55) -> QueryResult<()>
56where
57    F: FnMut() -> Ret + core::panic::UnwindSafe + Send + 'static,
58    Ret: ToSql<RetSqlType, Sqlite>,
59    Sqlite: HasSqlType<RetSqlType>,
60{
61    conn.register_sql_function(fn_name, 0, behavior, move |_, _| Ok(f()))?;
62    Ok(())
63}
64
65pub(super) fn register_aggregate<ArgsSqlType, RetSqlType, Args, Ret, A>(
66    conn: &RawConnection,
67    fn_name: &str,
68    behavior: SqliteFunctionBehavior,
69) -> QueryResult<()>
70where
71    A: SqliteAggregateFunction<Args, Output = Ret> + 'static + Send + core::panic::UnwindSafe,
72    Args: FromSqlRow<ArgsSqlType, Sqlite> + StaticallySizedRow<ArgsSqlType, Sqlite>,
73    Ret: ToSql<RetSqlType, Sqlite>,
74    Sqlite: HasSqlType<RetSqlType>,
75{
76    let fields_needed = Args::FIELD_COUNT;
77    if fields_needed > 127 {
78        return Err(Error::DatabaseError(
79            DatabaseErrorKind::UnableToSendCommand,
80            Box::new("SQLite functions cannot take more than 127 parameters".to_string()),
81        ));
82    }
83
84    conn.register_aggregate_function::<ArgsSqlType, RetSqlType, Args, Ret, A>(
85        fn_name,
86        fields_needed,
87        behavior,
88    )?;
89
90    Ok(())
91}
92
93pub(super) fn build_sql_function_args<ArgsSqlType, Args>(
94    args: &mut [*mut ffi::sqlite3_value],
95    connection: core::ptr::NonNull<ffi::sqlite3>,
96) -> Result<Args, Error>
97where
98    Args: FromSqlRow<ArgsSqlType, Sqlite>,
99{
100    let row = FunctionRow::new(args, connection);
101    Args::build_from_row(&row).map_err(Error::DeserializationError)
102}
103
104// clippy is wrong here, the let binding is required
105// for lifetime reasons
106#[allow(clippy::let_unit_value)]
107pub(super) fn process_sql_function_result<RetSqlType, Ret>(
108    result: &'_ Ret,
109) -> QueryResult<SqliteBindValueRef<'_>>
110where
111    Ret: ToSql<RetSqlType, Sqlite>,
112    Sqlite: HasSqlType<RetSqlType>,
113{
114    let mut metadata_lookup = ();
115    let value = SqliteBindValue {
116        inner: SqliteBindValueRef::Null,
117    };
118    let mut buf = Output::new(value, &mut metadata_lookup);
119    let is_null = result.to_sql(&mut buf).map_err(Error::SerializationError)?;
120
121    if let IsNull::Yes = is_null {
122        Ok(SqliteBindValueRef::Null)
123    } else {
124        Ok(buf.into_inner().inner)
125    }
126}
127
128struct FunctionRow<'a> {
129    args: &'a [Option<OwnedSqliteValue>],
130    field_count: usize,
131    connection: core::ptr::NonNull<ffi::sqlite3>,
132}
133
134impl FunctionRow<'_> {
135    #[allow(unsafe_code)] // complicated ptr cast
136    fn new(
137        args: &mut [*mut ffi::sqlite3_value],
138        connection: core::ptr::NonNull<ffi::sqlite3>,
139    ) -> Self {
140        let lengths = args.len();
141        let args = unsafe {
142            core::slice::from_raw_parts(
143                // This cast is safe because:
144                // * Casting from a pointer to an array to a pointer to the first array
145                // element is safe
146                // * Casting from a raw pointer to `NonNull<T>` is safe,
147                // because `NonNull` is #[repr(transparent)]
148                // * Casting from `NonNull<T>` to `OwnedSqliteValue` is safe,
149                // as the struct is `#[repr(transparent)]
150                // * Casting from `NonNull<T>` to `Option<NonNull<T>>` as the documentation
151                // states: "This is so that enums may use this forbidden value as a discriminant –
152                // Option<NonNull<T>> has the same size as *mut T"
153                // * The last point remains true for `OwnedSqliteValue` as `#[repr(transparent)]
154                // guarantees the same layout as the inner type
155                args as *mut [*mut ffi::sqlite3_value] as *mut ffi::sqlite3_value
156                    as *mut Option<OwnedSqliteValue>,
157                lengths,
158            )
159        };
160
161        Self {
162            field_count: lengths,
163            args,
164            connection,
165        }
166    }
167}
168
169impl RowSealed for FunctionRow<'_> {}
170
171impl<'a> Row<'a, Sqlite> for FunctionRow<'a> {
172    type Field<'f>
173        = FunctionArgument<'f>
174    where
175        'a: 'f,
176        Self: 'f;
177    type InnerPartialRow = Self;
178
179    fn field_count(&self) -> usize {
180        self.field_count
181    }
182
183    fn get<'b, I>(&'b self, idx: I) -> Option<Self::Field<'b>>
184    where
185        'a: 'b,
186        Self: crate::row::RowIndex<I>,
187    {
188        let col_idx = self.idx(idx)?;
189        Some(FunctionArgument {
190            args: self.args,
191            col_idx,
192            connection: self.connection,
193        })
194    }
195
196    fn partial_row(&self, range: core::ops::Range<usize>) -> PartialRow<'_, Self::InnerPartialRow> {
197        PartialRow::new(self, range)
198    }
199}
200
201impl RowIndex<usize> for FunctionRow<'_> {
202    fn idx(&self, idx: usize) -> Option<usize> {
203        if idx < self.field_count() {
204            Some(idx)
205        } else {
206            None
207        }
208    }
209}
210
211impl<'a> RowIndex<&'a str> for FunctionRow<'_> {
212    fn idx(&self, _idx: &'a str) -> Option<usize> {
213        None
214    }
215}
216
217struct FunctionArgument<'a> {
218    args: &'a [Option<OwnedSqliteValue>],
219    col_idx: usize,
220    connection: core::ptr::NonNull<ffi::sqlite3>,
221}
222
223impl<'a> Field<'a, Sqlite> for FunctionArgument<'a> {
224    fn field_name(&self) -> Option<&str> {
225        None
226    }
227
228    fn is_null(&self) -> bool {
229        self.value().is_none()
230    }
231
232    fn value(&self) -> Option<<Sqlite as Backend>::RawValue<'_>> {
233        SqliteValue::from_function_row(self.args, self.col_idx, self.connection)
234    }
235}