1use crate::{types::StepResult, ExtResult, ResultCode, Value};
2use std::{
3 ffi::{c_char, c_void, CStr, CString},
4 num::NonZeroUsize,
5 sync::Arc,
6};
7
8pub type RegisterModuleFn = unsafe extern "C" fn(
9 ctx: *mut c_void,
10 name: *const c_char,
11 module: VTabModuleImpl,
12 kind: VTabKind,
13) -> ResultCode;
14
15#[repr(C)]
16#[derive(Clone, Debug)]
17pub struct VTabModuleImpl {
18 pub name: *const c_char,
19 pub create: VtabFnCreate,
20 pub open: VtabFnOpen,
21 pub close: VtabFnClose,
22 pub filter: VtabFnFilter,
23 pub column: VtabFnColumn,
24 pub next: VtabFnNext,
25 pub eof: VtabFnEof,
26 pub update: VtabFnUpdate,
27 pub rowid: VtabRowIDFn,
28 pub destroy: VtabFnDestroy,
29 pub best_idx: BestIdxFn,
30}
31
32#[repr(C)]
33pub struct VTabCreateResult {
34 pub code: ResultCode,
35 pub schema: *const c_char,
36 pub table: *const c_void,
37}
38
39#[cfg(feature = "core_only")]
40impl VTabModuleImpl {
41 pub fn create(&self, args: Vec<Value>) -> crate::ExtResult<(String, *const c_void)> {
42 let result = unsafe { (self.create)(args.as_ptr(), args.len() as i32) };
43 for arg in args {
44 unsafe { arg.__free_internal_type() };
45 }
46 if !result.code.is_ok() {
47 return Err(result.code);
48 }
49 let schema = unsafe { std::ffi::CString::from_raw(result.schema as *mut _) };
50 Ok((schema.to_string_lossy().to_string(), result.table))
51 }
52
53 pub fn create_schema(&self, args: Vec<Value>) -> crate::ExtResult<String> {
61 self.create(args).and_then(|(schema, table)| {
62 let result = unsafe { (self.destroy)(table) };
64 if result.is_ok() {
65 Ok(schema)
66 } else {
67 Err(result)
68 }
69 })
70 }
71}
72
73pub type VtabFnCreate = unsafe extern "C" fn(args: *const Value, argc: i32) -> VTabCreateResult;
74
75pub type VtabFnOpen = unsafe extern "C" fn(table: *const c_void, conn: *mut Conn) -> *const c_void;
76
77pub type VtabFnClose = unsafe extern "C" fn(cursor: *const c_void) -> ResultCode;
78
79pub type VtabFnFilter = unsafe extern "C" fn(
80 cursor: *const c_void,
81 argc: i32,
82 argv: *const Value,
83 idx_str: *const c_char,
84 idx_num: i32,
85) -> ResultCode;
86
87pub type VtabFnColumn = unsafe extern "C" fn(cursor: *const c_void, idx: u32) -> Value;
88
89pub type VtabFnNext = unsafe extern "C" fn(cursor: *const c_void) -> ResultCode;
90
91pub type VtabFnEof = unsafe extern "C" fn(cursor: *const c_void) -> bool;
92
93pub type VtabRowIDFn = unsafe extern "C" fn(cursor: *const c_void) -> i64;
94
95pub type VtabFnUpdate = unsafe extern "C" fn(
96 table: *const c_void,
97 argc: i32,
98 argv: *const Value,
99 p_out_rowid: *mut i64,
100) -> ResultCode;
101
102pub type VtabFnDestroy = unsafe extern "C" fn(table: *const c_void) -> ResultCode;
103
104pub type BestIdxFn = unsafe extern "C" fn(
105 constraints: *const ConstraintInfo,
106 constraint_len: i32,
107 order_by: *const OrderByInfo,
108 order_by_len: i32,
109) -> ExtIndexInfo;
110
111#[repr(C)]
112#[derive(Clone, Copy, Debug, PartialEq)]
113pub enum VTabKind {
114 VirtualTable,
115 TableValuedFunction,
116}
117
118pub trait VTabModule: 'static {
119 type Table: VTable;
120 const VTAB_KIND: VTabKind;
121 const NAME: &'static str;
122
123 fn create(args: &[Value]) -> Result<(String, Self::Table), ResultCode>;
126}
127
128pub trait VTable {
129 type Cursor: VTabCursor<Error = Self::Error>;
130 type Error: std::fmt::Display;
131
132 fn open(&self, _conn: Option<Arc<Connection>>) -> Result<Self::Cursor, Self::Error>;
135 fn update(&mut self, _rowid: i64, _args: &[Value]) -> Result<(), Self::Error> {
136 Ok(())
137 }
138 fn insert(&mut self, _args: &[Value]) -> Result<i64, Self::Error> {
139 Ok(0)
140 }
141 fn delete(&mut self, _rowid: i64) -> Result<(), Self::Error> {
142 Ok(())
143 }
144 fn destroy(&mut self) -> Result<(), Self::Error> {
145 Ok(())
146 }
147 fn best_index(_constraints: &[ConstraintInfo], _order_by: &[OrderByInfo]) -> IndexInfo {
148 IndexInfo {
149 idx_num: 0,
150 idx_str: None,
151 order_by_consumed: false,
152 estimated_cost: 1_000_000.0,
153 estimated_rows: u32::MAX,
154 constraint_usages: _constraints
155 .iter()
156 .map(|_| ConstraintUsage {
157 argv_index: Some(0),
158 omit: false,
159 })
160 .collect(),
161 }
162 }
163}
164
165pub trait VTabCursor: Sized {
166 type Error: std::fmt::Display;
167 fn filter(&mut self, args: &[Value], idx_info: Option<(&str, i32)>) -> ResultCode;
168 fn rowid(&self) -> i64;
169 fn column(&self, idx: u32) -> Result<Value, Self::Error>;
170 fn eof(&self) -> bool;
171 fn next(&mut self) -> ResultCode;
172 fn close(&self) -> ResultCode {
173 ResultCode::OK
174 }
175}
176
177#[repr(u8)]
178#[derive(Copy, Clone, Debug, PartialEq, Eq)]
179pub enum ConstraintOp {
180 Eq = 2,
181 Lt = 4,
182 Le = 8,
183 Gt = 16,
184 Ge = 32,
185 Match = 64,
186 Like = 65,
187 Glob = 66,
188 Regexp = 67,
189 Ne = 68,
190 IsNot = 69,
191 IsNotNull = 70,
192 IsNull = 71,
193 Is = 72,
194 In = 73,
195}
196
197#[repr(C)]
198#[derive(Copy, Clone)]
199pub struct OrderByInfo {
202 pub column_index: u32,
204 pub desc: bool,
206}
207
208#[derive(Debug, Clone)]
211pub struct IndexInfo {
212 pub idx_num: i32,
214 pub idx_str: Option<String>,
216 pub order_by_consumed: bool,
218 pub estimated_cost: f64,
220 pub estimated_rows: u32,
222 pub constraint_usages: Vec<ConstraintUsage>,
224}
225impl Default for IndexInfo {
226 fn default() -> Self {
227 Self {
228 idx_num: 0,
229 idx_str: None,
230 order_by_consumed: false,
231 estimated_cost: 1_000_000.0,
232 estimated_rows: u32::MAX,
233 constraint_usages: Vec::new(),
234 }
235 }
236}
237
238impl IndexInfo {
239 pub fn to_ffi(self) -> ExtIndexInfo {
244 let len = self.constraint_usages.len();
245 let ptr = Box::into_raw(self.constraint_usages.into_boxed_slice()) as *mut ConstraintUsage;
246 let idx_str_len = self.idx_str.as_ref().map(|s| s.len()).unwrap_or(0);
247 let c_idx_str = self
248 .idx_str
249 .and_then(|s| std::ffi::CString::new(s).ok())
250 .map(|cs| cs.into_raw())
251 .unwrap_or(std::ptr::null_mut());
252 ExtIndexInfo {
253 idx_num: self.idx_num,
254 estimated_cost: self.estimated_cost,
255 estimated_rows: self.estimated_rows,
256 order_by_consumed: self.order_by_consumed,
257 constraint_usages_ptr: ptr,
258 constraint_usage_len: len,
259 idx_str: c_idx_str as *mut _,
260 idx_str_len,
261 }
262 }
263
264 pub unsafe fn from_ffi(ffi: ExtIndexInfo) -> Self {
269 let constraint_usages = unsafe {
270 Box::from_raw(std::slice::from_raw_parts_mut(
271 ffi.constraint_usages_ptr,
272 ffi.constraint_usage_len,
273 ))
274 .to_vec()
275 };
276 let idx_str = if ffi.idx_str.is_null() {
277 None
278 } else {
279 Some(unsafe {
280 std::ffi::CString::from_raw(ffi.idx_str as *mut _)
281 .to_string_lossy()
282 .into_owned()
283 })
284 };
285 Self {
286 idx_num: ffi.idx_num,
287 idx_str,
288 order_by_consumed: ffi.order_by_consumed,
289 estimated_cost: ffi.estimated_cost,
290 estimated_rows: ffi.estimated_rows,
291 constraint_usages,
292 }
293 }
294}
295
296#[repr(C)]
297#[derive(Clone, Debug)]
298pub struct ExtIndexInfo {
300 pub idx_num: i32,
301 pub idx_str: *const u8,
302 pub idx_str_len: usize,
303 pub order_by_consumed: bool,
304 pub estimated_cost: f64,
305 pub estimated_rows: u32,
306 pub constraint_usages_ptr: *mut ConstraintUsage,
307 pub constraint_usage_len: usize,
308}
309
310#[derive(Debug, Clone, Copy)]
313pub struct ConstraintUsage {
314 pub argv_index: Option<u32>,
316 pub omit: bool,
318}
319
320#[derive(Clone, Copy, Debug)]
321#[repr(C)]
322pub struct ConstraintInfo {
325 pub column_index: u32,
327 pub op: ConstraintOp,
329 pub usable: bool,
331 pub plan_info: u32,
334}
335
336impl ConstraintInfo {
337 #[inline(always)]
338 pub fn pack_plan_info(pred_idx: u32, is_right_side: bool) -> u32 {
339 ((pred_idx) << 1) | (is_right_side as u32)
340 }
341 #[inline(always)]
342 pub fn unpack_plan_info(&self) -> (usize, bool) {
343 ((self.plan_info >> 1) as usize, (self.plan_info & 1) != 0)
344 }
345}
346
347pub type PrepareStmtFn = unsafe extern "C" fn(api: *mut Conn, sql: *const c_char) -> *mut Stmt;
348pub type ExecuteFn = unsafe extern "C" fn(
349 ctx: *mut Conn,
350 sql: *const c_char,
351 args: *mut Value,
352 arg_count: i32,
353 last_insert_rowid: *mut i64,
354) -> ResultCode;
355pub type GetColumnNamesFn =
356 unsafe extern "C" fn(ctx: *mut Stmt, count: *mut i32) -> *mut *mut c_char;
357pub type BindArgsFn = unsafe extern "C" fn(ctx: *mut Stmt, idx: i32, arg: Value) -> ResultCode;
358pub type StmtStepFn = unsafe extern "C" fn(ctx: *mut Stmt) -> ResultCode;
359pub type StmtGetRowValuesFn = unsafe extern "C" fn(ctx: *mut Stmt);
360pub type FreeCurrentRowFn = unsafe extern "C" fn(ctx: *mut Stmt);
361pub type CloseConnectionFn = unsafe extern "C" fn(ctx: *mut c_void);
362pub type CloseStmtFn = unsafe extern "C" fn(ctx: *mut Stmt);
363
364#[repr(C)]
367#[derive(Debug, Clone)]
368pub struct Conn {
369 pub _ctx: *mut c_void,
371 pub _prepare_stmt: PrepareStmtFn,
372 pub _execute: ExecuteFn,
373 pub _close: CloseConnectionFn,
374}
375
376impl Conn {
377 pub fn new(
378 ctx: *mut c_void,
379 prepare_stmt: PrepareStmtFn,
380 exec_fn: ExecuteFn,
381 close: CloseConnectionFn,
382 ) -> Self {
383 Conn {
384 _ctx: ctx,
385 _prepare_stmt: prepare_stmt,
386 _execute: exec_fn,
387 _close: close,
388 }
389 }
390
391 pub unsafe fn from_ptr(ptr: *mut Conn) -> crate::ExtResult<&'static mut Self> {
394 if ptr.is_null() {
395 return Err(ResultCode::Error);
396 }
397 Ok(unsafe { &mut *(ptr) })
398 }
399
400 pub fn close(&mut self) {
401 if self._ctx.is_null() {
402 return;
403 }
404 unsafe { (self._close)(self._ctx) };
405 self._ctx = std::ptr::null_mut();
406 }
407
408 pub fn execute(&self, sql: &str, args: &[Value]) -> crate::ExtResult<Option<usize>> {
411 let Ok(sql) = CString::new(sql) else {
412 return Err(ResultCode::Error);
413 };
414 let arg_count = args.len() as i32;
415 let args = args.as_ptr();
416 let last_insert_rowid = 0;
417 if let ResultCode::OK = unsafe {
418 (self._execute)(
419 self as *const _ as *mut Conn,
420 sql.as_ptr(),
421 args as *mut Value,
422 arg_count,
423 &last_insert_rowid as *const _ as *mut i64,
424 )
425 } {
426 return Ok(Some(last_insert_rowid as usize));
427 }
428 Err(ResultCode::Error)
429 }
430
431 pub fn prepare_stmt(&self, sql: &str) -> *mut Stmt {
432 let Ok(sql) = CString::new(sql) else {
433 return std::ptr::null_mut();
434 };
435 unsafe { (self._prepare_stmt)(self as *const _ as *mut Conn, sql.as_ptr()) }
436 }
437}
438
439#[derive(Debug)]
442#[repr(transparent)]
443pub struct Statement(*mut Stmt);
444
445impl Drop for Statement {
446 fn drop(&mut self) {
447 if self.0.is_null() {
448 return;
449 }
450 unsafe { (*self.0).close() }
451 }
452}
453
454#[derive(Debug)]
458#[repr(transparent)]
459pub struct Connection(*mut Conn);
460
461impl Connection {
462 pub fn new(ctx: *mut Conn) -> Self {
463 Connection(ctx)
464 }
465
466 pub fn prepare(self: &Arc<Self>, sql: &str) -> ExtResult<Statement> {
468 let stmt = unsafe { (*self.0).prepare_stmt(sql) };
469 if stmt.is_null() {
470 return Err(ResultCode::Error);
471 }
472 Ok(Statement(stmt))
473 }
474
475 pub fn execute(self: &Arc<Self>, sql: &str, args: &[Value]) -> crate::ExtResult<Option<usize>> {
478 if self.0.is_null() {
479 return Err(ResultCode::Error);
480 }
481 unsafe { (*self.0).execute(sql, args) }
482 }
483}
484
485impl Statement {
486 pub fn bind_at(&self, idx: NonZeroUsize, arg: Value) {
492 unsafe {
493 (*self.0).bind_args(idx, arg);
494 }
495 }
496
497 pub fn step(&self) -> StepResult {
505 unsafe { (*self.0).step() }
506 }
507
508 pub fn get_row(&mut self) -> &[Value] {
515 unsafe { (*self.0).get_row() }
516 }
517
518 pub fn get_column_names(&self) -> Vec<String> {
520 unsafe { (*self.0).get_column_names() }
521 }
522
523 pub fn close(self) {
525 if self.0.is_null() {
526 return;
527 }
528 unsafe { (*self.0).close() }
529 }
530}
531
532#[repr(C)]
535pub struct Stmt {
536 pub _conn: *mut c_void,
538 pub _ctx: *mut c_void,
540 pub _bind_args_fn: BindArgsFn,
541 pub _step: StmtStepFn,
542 pub _get_row_values: StmtGetRowValuesFn,
543 pub _get_column_names: GetColumnNamesFn,
544 pub _free_current_row: FreeCurrentRowFn,
545 pub _close: CloseStmtFn,
546 pub current_row: *mut Value,
547 pub current_row_len: i32,
548}
549
550impl Stmt {
551 #[allow(clippy::too_many_arguments)]
552 pub fn new(
553 conn: *mut c_void,
554 ctx: *mut c_void,
555 bind: BindArgsFn,
556 step: StmtStepFn,
557 rows: StmtGetRowValuesFn,
558 names: GetColumnNamesFn,
559 free_row: FreeCurrentRowFn,
560 close: CloseStmtFn,
561 ) -> Self {
562 Stmt {
563 _conn: conn,
564 _ctx: ctx,
565 _bind_args_fn: bind,
566 _step: step,
567 _get_row_values: rows,
568 _get_column_names: names,
569 _free_current_row: free_row,
570 _close: close,
571 current_row: std::ptr::null_mut(),
572 current_row_len: -1,
573 }
574 }
575
576 pub fn close(&mut self) {
578 if self._ctx.is_null() {
580 return;
581 }
582 unsafe { (self._close)(self as *const Stmt as *mut Stmt) };
583 self._ctx = std::ptr::null_mut();
584 }
585
586 pub unsafe fn from_ptr(ptr: *mut Stmt) -> ExtResult<&'static mut Self> {
589 if ptr.is_null() {
590 return Err(ResultCode::Error);
591 }
592 Ok(unsafe { &mut *(ptr) })
593 }
594
595 pub fn to_ptr(&self) -> *mut Stmt {
597 self as *const Stmt as *mut Stmt
598 }
599
600 fn bind_args(&self, idx: NonZeroUsize, arg: Value) {
603 unsafe {
604 (self._bind_args_fn)(self.to_ptr(), idx.get() as i32, arg);
605 };
606 }
607
608 fn step(&self) -> StepResult {
610 unsafe { (self._step)(self.to_ptr()) }.into()
611 }
612
613 pub unsafe fn free_current_row(&mut self) {
620 if self.current_row.is_null() || self.current_row_len <= 0 {
621 return;
622 }
623 (self._free_current_row)(self.to_ptr());
625 self.current_row = std::ptr::null_mut();
626 self.current_row_len = -1;
627 }
628
629 pub fn get_row(&self) -> &[Value] {
632 unsafe { (self._get_row_values)(self.to_ptr()) };
633 if self.current_row.is_null() || self.current_row_len < 1 {
634 return &[];
635 }
636 let col_count = self.current_row_len;
637 unsafe { std::slice::from_raw_parts(self.current_row, col_count as usize) }
638 }
639
640 pub fn get_column_names(&self) -> Vec<String> {
642 let mut count_value: i32 = 0;
643 let count: *mut i32 = &mut count_value;
644 let col_names = unsafe { (self._get_column_names)(self.to_ptr(), count) };
645 if col_names.is_null() || count_value == 0 {
646 return Vec::new();
647 }
648 let mut names = Vec::new();
649 let slice = unsafe { std::slice::from_raw_parts(col_names, count_value as usize) };
650 for x in slice {
651 let name = unsafe { CStr::from_ptr(*x) };
652 if let Ok(s) = name.to_str() {
653 names.push(s.to_string());
654 }
655 }
656 unsafe { free_column_names(col_names, count_value) };
657 names
658 }
659}
660
661pub unsafe fn free_column_names(names: *mut *mut c_char, count: i32) {
667 if names.is_null() || count < 1 {
668 return;
669 }
670 let slice = std::slice::from_raw_parts_mut(names, count as usize);
671
672 for name in slice {
673 if !name.is_null() {
674 let _ = CString::from_raw(*name);
675 }
676 }
677 let _ = Box::from_raw(names);
678}