use super::{
ffi, function::ToContextResult, sqlite3_match_version, types::*, value::*, Connection,
};
pub use function::*;
pub use index_info::*;
pub use module::*;
use std::{ffi::c_void, ops::Deref, slice};
mod function;
mod index_info;
mod module;
pub(crate) mod stubs;
pub type DisconnectResult<T> = std::result::Result<(), (T, Error)>;
pub trait VTab<'vtab>: Sized {
type Aux: 'vtab;
type Cursor: VTabCursor + 'vtab;
fn connect(
db: &'vtab VTabConnection,
aux: &'vtab Self::Aux,
args: &[&str],
) -> Result<(String, Self)>;
fn best_index(&'vtab self, index_info: &mut IndexInfo) -> Result<()>;
fn open(&'vtab self) -> Result<Self::Cursor>;
fn disconnect(self) -> DisconnectResult<Self> {
Ok(())
}
}
pub trait CreateVTab<'vtab>: VTab<'vtab> {
const SHADOW_NAMES: &'static [&'static str] = &[];
fn create(
db: &'vtab VTabConnection,
aux: &'vtab Self::Aux,
args: &[&str],
) -> Result<(String, Self)>;
fn destroy(self) -> DisconnectResult<Self>;
}
pub trait UpdateVTab<'vtab>: VTab<'vtab> {
fn update(&'vtab self, info: &mut ChangeInfo) -> Result<i64>;
}
pub trait TransactionVTab<'vtab>: UpdateVTab<'vtab> {
type Transaction: VTabTransaction + 'vtab;
fn begin(&'vtab self) -> Result<Self::Transaction>;
}
pub trait FindFunctionVTab<'vtab>: VTab<'vtab> {
fn functions(&'vtab self) -> &'vtab VTabFunctionList<'vtab, Self>;
}
pub trait RenameVTab<'vtab>: VTab<'vtab> {
fn rename(&'vtab self, name: &str) -> Result<()>;
}
pub trait VTabCursor {
fn filter(
&mut self,
index_num: i32,
index_str: Option<&str>,
args: &mut [&mut ValueRef],
) -> Result<()>;
fn next(&mut self) -> Result<()>;
fn eof(&mut self) -> bool;
fn column(&mut self, idx: usize, context: &ColumnContext) -> Result<()>;
fn rowid(&mut self) -> Result<i64>;
}
pub trait VTabTransaction {
fn sync(&mut self) -> Result<()>;
fn commit(self) -> Result<()>;
fn rollback(self) -> Result<()>;
fn savepoint(&mut self, n: i32) -> Result<()>;
fn release(&mut self, n: i32) -> Result<()>;
fn rollback_to(&mut self, n: i32) -> Result<()>;
}
#[repr(transparent)]
pub struct VTabConnection {
db: ffi::sqlite3,
}
impl VTabConnection {
unsafe fn from_ptr<'a>(db: *mut ffi::sqlite3) -> &'a Self {
&*(db as *mut Self)
}
pub fn enable_constraints(&self) {
sqlite3_match_version! {
3_007_007 => unsafe {
let guard = self.lock();
Error::from_sqlite_desc(ffi::sqlite3_vtab_config()(
guard.as_mut_ptr(),
ffi::SQLITE_VTAB_CONSTRAINT_SUPPORT,
1,
), guard)
.unwrap()
},
_ => (),
}
}
pub fn set_risk_level(&self, level: super::RiskLevel) {
let _ = level;
sqlite3_match_version! {
3_031_000 => unsafe {
let guard = self.lock();
Error::from_sqlite_desc(ffi::sqlite3_vtab_config()(
guard.as_mut_ptr(),
match level {
super::RiskLevel::Innocuous => ffi::SQLITE_VTAB_INNOCUOUS,
super::RiskLevel::DirectOnly => ffi::SQLITE_VTAB_DIRECTONLY,
},
), guard)
.unwrap();
},
_ => (),
}
}
}
impl Deref for VTabConnection {
type Target = Connection;
fn deref(&self) -> &Connection {
unsafe { Connection::from_ptr(&self.db as *const _ as _) }
}
}
pub struct ChangeInfo {
#[cfg_attr(not(modern_sqlite), allow(unused))]
db: *mut ffi::sqlite3,
argc: usize,
argv: *mut *mut ValueRef,
}
impl ChangeInfo {
pub fn change_type(&self) -> ChangeType {
if self.args().is_empty() {
ChangeType::Delete
} else if self.rowid().is_null() {
ChangeType::Insert
} else {
ChangeType::Update
}
}
pub fn rowid(&self) -> &ValueRef {
debug_assert!(self.argc > 0);
unsafe { &**self.argv }
}
pub fn rowid_mut(&mut self) -> &mut ValueRef {
debug_assert!(self.argc > 0);
unsafe { &mut **self.argv }
}
pub fn args(&self) -> &[&ValueRef] {
debug_assert!(self.argc > 0);
unsafe { slice::from_raw_parts(self.argv.offset(1) as _, self.argc - 1) }
}
pub fn args_mut(&mut self) -> &mut [&mut ValueRef] {
debug_assert!(self.argc > 0);
unsafe { slice::from_raw_parts_mut(self.argv.offset(1) as _, self.argc - 1) }
}
pub fn conflict_mode(&self) -> ConflictMode {
sqlite3_match_version! {
3_007_007 => {
ConflictMode::from_sqlite(unsafe { ffi::sqlite3_vtab_on_conflict(self.db) })
}
_ => ConflictMode::Abort,
}
}
}
impl std::fmt::Debug for ChangeInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
f.debug_struct("ChangeInfo")
.field("change_type", &self.change_type())
.field("rowid", &self.rowid())
.field("args", &self.args())
.field("conflict_mode", &self.conflict_mode())
.finish()
}
}
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum ChangeType {
Insert,
Delete,
Update,
}
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum ConflictMode {
Rollback,
Ignore,
Fail,
Abort,
Replace,
}
impl ConflictMode {
#[cfg(modern_sqlite)]
fn from_sqlite(val: i32) -> Self {
match val {
1 => ConflictMode::Rollback,
2 => ConflictMode::Ignore,
3 => ConflictMode::Fail,
4 => ConflictMode::Abort,
5 => ConflictMode::Replace,
_ => panic!("invalid conflict mode"),
}
}
}
#[repr(transparent)]
pub struct ColumnContext {
base: ffi::sqlite3_context,
}
impl ColumnContext {
pub(crate) fn as_ptr(&self) -> *mut ffi::sqlite3_context {
&self.base as *const ffi::sqlite3_context as _
}
pub(crate) unsafe fn from_ptr<'a>(base: *mut ffi::sqlite3_context) -> &'a mut Self {
&mut *(base as *mut Self)
}
pub fn db(&self) -> &Connection {
unsafe { Connection::from_ptr(ffi::sqlite3_context_db_handle(self.as_ptr())) }
}
pub fn nochange(&self) -> bool {
crate::sqlite3_match_version! {
3_022_000 => (unsafe { ffi::sqlite3_vtab_nochange(self.as_ptr()) } != 0),
_ => false,
}
}
pub fn set_result(&self, val: impl ToContextResult) -> Result<()> {
unsafe { val.assign_to(self.as_ptr()) };
Ok(())
}
}