1use super::*;
4use crate::{ffi, sqlite3_match_version, sqlite3_require_version, Connection};
5use sealed::sealed;
6use std::{ffi::CString, marker::PhantomData};
7
8union ModuleBytes {
9 bytes: [u8; std::mem::size_of::<ffi::sqlite3_module>()],
10 module: ffi::sqlite3_module,
11}
12
13const EMPTY_MODULE: ffi::sqlite3_module = unsafe {
19 ModuleBytes {
20 bytes: [0_u8; std::mem::size_of::<ffi::sqlite3_module>()],
21 }
22 .module
23};
24
25#[cfg(modern_sqlite)]
26fn set_version(m: &mut ffi::sqlite3_module, val: i32) {
27 m.iVersion = std::cmp::max(m.iVersion, val);
28}
29
30pub(super) struct Handle<'vtab, T: VTab<'vtab>> {
33 pub vtab: ffi::sqlite3_module,
34 pub aux: T::Aux,
35}
36
37impl<'vtab, T: VTab<'vtab>> Handle<'vtab, T> {
38 pub unsafe fn from_ptr<'a>(ptr: *mut c_void) -> &'a Self {
39 &*(ptr as *mut Self)
40 }
41}
42
43#[sealed]
48pub trait Module<'vtab, T: VTab<'vtab> + 'vtab>
49where
50 Self: Sized,
51{
52 #[doc(hidden)]
53 fn module(&mut self) -> &mut ffi::sqlite3_module;
54
55 #[doc(hidden)]
56 fn with_update(mut self) -> Self
57 where
58 T: UpdateVTab<'vtab>,
59 {
60 self.module().xUpdate = Some(stubs::vtab_update::<T>);
61 self
62 }
63
64 #[doc(hidden)]
65 fn with_transactions(mut self) -> Self
66 where
67 T: TransactionVTab<'vtab>,
68 {
69 self.with_initial_transaction();
70 let m = self.module();
71 m.xBegin = Some(stubs::vtab_begin::<T>);
72 m.xSync = Some(stubs::vtab_sync::<T>);
73 m.xCommit = Some(stubs::vtab_commit::<T>);
74 sqlite3_match_version! {
75 3_007_007 => {
76 set_version(m, 2);
77 m.xRollback = Some(stubs::vtab_rollback::<T>);
78 m.xSavepoint = Some(stubs::vtab_savepoint::<T>);
79 m.xRelease = Some(stubs::vtab_release::<T>);
80 m.xRollbackTo = Some(stubs::vtab_rollback_to::<T>);
81 }
82 _ => (),
83 }
84 self
85 }
86
87 #[doc(hidden)]
88 fn with_initial_transaction(&mut self)
89 where
90 T: TransactionVTab<'vtab>;
91
92 #[doc(hidden)]
93 fn with_find_function(mut self) -> Self
94 where
95 T: FindFunctionVTab<'vtab>,
96 {
97 self.module().xFindFunction = Some(stubs::vtab_find_function::<T>);
98 self
99 }
100
101 #[doc(hidden)]
102 fn with_rename(mut self) -> Self
103 where
104 T: RenameVTab<'vtab>,
105 {
106 self.module().xRename = Some(stubs::vtab_rename::<T>);
107 self
108 }
109}
110
111macro_rules! module_base {
112 ($(#[$attr:meta])* $name:ident < $ty:ident > { $($extra:tt)* }) => {
113 $(#[$attr])*
114 pub struct $name<'vtab, T: VTab<'vtab>> {
115 base: ffi::sqlite3_module,
116 phantom: PhantomData<&'vtab T>,
117 }
118
119 #[sealed]
120 impl<'vtab, T: $ty<'vtab>> Module<'vtab, T> for $name<'vtab, T> {
121 fn module(&mut self) -> &mut ffi::sqlite3_module {
122 &mut self.base
123 }
124
125 $($extra)*
126 }
127 };
128}
129
130module_base!(
131 StandardModule<CreateVTab> {
137 fn with_initial_transaction(&mut self)
138 where
139 T: TransactionVTab<'vtab>,
140 {
141 self.base.xCreate = Some(stubs::vtab_create_transaction::<T>);
143 }
144});
145
146module_base!(
147 EponymousModule<VTab> {
157 fn with_initial_transaction(&mut self)
158 where
159 T: TransactionVTab<'vtab>,
160 {
161 self.base.xConnect = Some(stubs::vtab_connect_transaction::<T>);
163 self.base.xCreate = Some(stubs::vtab_connect_transaction::<T>);
164 }
165});
166
167module_base!(
168 EponymousOnlyModule<VTab> {
183 fn with_initial_transaction(&mut self)
184 where
185 T: TransactionVTab<'vtab>,
186 {
187 }
189});
190
191impl<'vtab, T: CreateVTab<'vtab>> StandardModule<'vtab, T> {
192 #[doc(hidden)]
193 pub fn new() -> Self {
194 #[cfg_attr(not(modern_sqlite), allow(unused_mut))]
195 let mut ret = StandardModule {
196 base: ffi::sqlite3_module {
197 xCreate: Some(stubs::vtab_create::<T>),
198 xConnect: Some(stubs::vtab_connect::<T>),
199 xBestIndex: Some(stubs::vtab_best_index::<T>),
200 xDisconnect: Some(stubs::vtab_disconnect::<T>),
201 xDestroy: Some(stubs::vtab_destroy::<T>),
202 xOpen: Some(stubs::vtab_open::<T>),
203 xClose: Some(stubs::vtab_close::<T>),
204 xFilter: Some(stubs::vtab_filter::<T>),
205 xNext: Some(stubs::vtab_next::<T>),
206 xEof: Some(stubs::vtab_eof::<T>),
207 xColumn: Some(stubs::vtab_column::<T>),
208 xRowid: Some(stubs::vtab_rowid::<T>),
209 ..EMPTY_MODULE
210 },
211 phantom: PhantomData,
212 };
213 sqlite3_match_version! {
214 3_026_000 => {
215 if T::SHADOW_NAMES.len() > 0 {
216 set_version(&mut ret.base, 3);
217 ret.base.xShadowName = Some(stubs::vtab_shadow_name::<T>);
218 }
219 }
220 _ => (),
221 }
222 ret
223 }
224}
225
226impl<'vtab, T: VTab<'vtab>> EponymousModule<'vtab, T> {
227 #[doc(hidden)]
228 pub fn new() -> Self {
229 EponymousModule {
230 base: ffi::sqlite3_module {
231 xCreate: Some(stubs::vtab_connect::<T>),
232 xConnect: Some(stubs::vtab_connect::<T>),
233 xBestIndex: Some(stubs::vtab_best_index::<T>),
234 xDisconnect: Some(stubs::vtab_disconnect::<T>),
235 xDestroy: Some(stubs::vtab_disconnect::<T>),
236 xOpen: Some(stubs::vtab_open::<T>),
237 xClose: Some(stubs::vtab_close::<T>),
238 xFilter: Some(stubs::vtab_filter::<T>),
239 xNext: Some(stubs::vtab_next::<T>),
240 xEof: Some(stubs::vtab_eof::<T>),
241 xColumn: Some(stubs::vtab_column::<T>),
242 xRowid: Some(stubs::vtab_rowid::<T>),
243 ..EMPTY_MODULE
244 },
245 phantom: PhantomData,
246 }
247 }
248}
249
250impl<'vtab, T: VTab<'vtab>> EponymousOnlyModule<'vtab, T> {
251 #[doc(hidden)]
252 pub fn new() -> Result<Self> {
253 sqlite3_require_version!(
254 3_009_000,
255 Ok(EponymousOnlyModule {
256 base: ffi::sqlite3_module {
257 xConnect: Some(stubs::vtab_connect::<T>),
258 xBestIndex: Some(stubs::vtab_best_index::<T>),
259 xDisconnect: Some(stubs::vtab_disconnect::<T>),
260 xDestroy: Some(stubs::vtab_disconnect::<T>),
261 xOpen: Some(stubs::vtab_open::<T>),
262 xClose: Some(stubs::vtab_close::<T>),
263 xFilter: Some(stubs::vtab_filter::<T>),
264 xNext: Some(stubs::vtab_next::<T>),
265 xEof: Some(stubs::vtab_eof::<T>),
266 xColumn: Some(stubs::vtab_column::<T>),
267 xRowid: Some(stubs::vtab_rowid::<T>),
268 ..EMPTY_MODULE
269 },
270 phantom: PhantomData,
271 })
272 )
273 }
274}
275
276impl Connection {
277 pub fn create_module<'db: 'vtab, 'vtab, T: VTab<'vtab> + 'vtab, M: Module<'vtab, T> + 'vtab>(
279 &'db self,
280 name: &str,
281 mut vtab: M,
282 aux: T::Aux,
283 ) -> Result<()>
284 where
285 T::Aux: 'db,
286 {
287 let name = CString::new(name).unwrap();
288 let vtab = vtab.module().clone();
289 let handle = Box::new(Handle::<'vtab, T> { vtab, aux });
290 let guard = self.lock();
291 Error::from_sqlite_desc(
292 unsafe {
293 ffi::sqlite3_create_module_v2(
294 self.as_mut_ptr(),
295 name.as_ptr() as _,
296 &handle.vtab,
297 Box::into_raw(handle) as _,
298 Some(ffi::drop_boxed::<Handle<T>>),
299 )
300 },
301 guard,
302 )
303 }
304}