diesel/sqlite/connection/
functions.rs1#[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#[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)] 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 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}