mirror of
https://github.com/isar/rusqlite.git
synced 2024-11-22 07:09:20 +08:00
Add some missing wrappers (#1139)
* Add some missing wrappers: sqlite3_value_subtype sqlite3_result_subtype sqlite3_changes64 sqlite3_db_readonly sqlite3_txn_state sqlite3_stmt_isexplain sqlite3_vtab_config sqlite3_index_info.idxFlags sqlite3_index_info.colUsed sqlite3_index_info.idxStr sqlite3_vtab_collation * Mark series VTab as innocuous and csv as direct only
This commit is contained in:
parent
2afbdeeb52
commit
5e2c103a0c
@ -33,7 +33,7 @@ pub enum DbConfig {
|
||||
SQLITE_DBCONFIG_TRIGGER_EQP = 1008, // 3.22.0
|
||||
/// Activates or deactivates the "reset" flag for a database connection.
|
||||
/// Run VACUUM with this flag set to reset the database.
|
||||
SQLITE_DBCONFIG_RESET_DATABASE = 1009,
|
||||
SQLITE_DBCONFIG_RESET_DATABASE = 1009, // 3.24.0
|
||||
/// Activates or deactivates the "defensive" flag for a database connection.
|
||||
SQLITE_DBCONFIG_DEFENSIVE = 1010, // 3.26.0
|
||||
/// Activates or deactivates the "writable_schema" flag.
|
||||
|
@ -23,6 +23,7 @@ pub(super) unsafe fn set_result(ctx: *mut sqlite3_context, result: &ToSqlOutput<
|
||||
|
||||
#[cfg(feature = "blob")]
|
||||
ToSqlOutput::ZeroBlob(len) => {
|
||||
// TODO sqlite3_result_zeroblob64 // 3.8.11
|
||||
return ffi::sqlite3_result_zeroblob(ctx, len);
|
||||
}
|
||||
#[cfg(feature = "array")]
|
||||
@ -50,6 +51,7 @@ pub(super) unsafe fn set_result(ctx: *mut sqlite3_context, result: &ToSqlOutput<
|
||||
// TODO sqlite3_result_error
|
||||
Err(_) => return ffi::sqlite3_result_error_code(ctx, ffi::SQLITE_MISUSE),
|
||||
};
|
||||
// TODO sqlite3_result_text64 // 3.8.7
|
||||
ffi::sqlite3_result_text(ctx, c_str, len, destructor);
|
||||
}
|
||||
}
|
||||
@ -60,6 +62,7 @@ pub(super) unsafe fn set_result(ctx: *mut sqlite3_context, result: &ToSqlOutput<
|
||||
} else if length == 0 {
|
||||
ffi::sqlite3_result_zeroblob(ctx, 0);
|
||||
} else {
|
||||
// TODO sqlite3_result_blob64 // 3.8.7
|
||||
ffi::sqlite3_result_blob(
|
||||
ctx,
|
||||
b.as_ptr().cast::<c_void>(),
|
||||
|
@ -339,6 +339,7 @@ impl error::Error for Error {
|
||||
|
||||
#[cold]
|
||||
pub fn error_from_sqlite_code(code: c_int, message: Option<String>) -> Error {
|
||||
// TODO sqlite3_error_offset // 3.38.0, #1130
|
||||
Error::SqliteFailure(ffi::Error::new(code), message)
|
||||
}
|
||||
|
||||
|
@ -162,6 +162,19 @@ impl Context<'_> {
|
||||
unsafe { ValueRef::from_value(arg) }
|
||||
}
|
||||
|
||||
/// Returns the subtype of `idx`th argument.
|
||||
///
|
||||
/// # Failure
|
||||
///
|
||||
/// Will panic if `idx` is greater than or equal to
|
||||
/// [`self.len()`](Context::len).
|
||||
#[cfg(feature = "modern_sqlite")] // 3.9.0
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))]
|
||||
pub fn get_subtype(&self, idx: usize) -> std::os::raw::c_uint {
|
||||
let arg = self.args[idx];
|
||||
unsafe { ffi::sqlite3_value_subtype(arg) }
|
||||
}
|
||||
|
||||
/// Fetch or insert the auxiliary data associated with a particular
|
||||
/// parameter. This is intended to be an easier-to-use way of fetching it
|
||||
/// compared to calling [`get_aux`](Context::get_aux) and
|
||||
@ -234,6 +247,13 @@ impl Context<'_> {
|
||||
phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
/// Set the Subtype of an SQL function
|
||||
#[cfg(feature = "modern_sqlite")] // 3.9.0
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))]
|
||||
pub fn set_result_subtype(&self, sub_type: std::os::raw::c_uint) {
|
||||
unsafe { ffi::sqlite3_result_subtype(self.ctx, sub_type) };
|
||||
}
|
||||
}
|
||||
|
||||
/// A reference to a connection handle with a lifetime bound to something.
|
||||
@ -319,7 +339,7 @@ bitflags::bitflags! {
|
||||
/// Specifies UTF-16 using native byte order as the text encoding this SQL function prefers for its parameters.
|
||||
const SQLITE_UTF16 = ffi::SQLITE_UTF16;
|
||||
/// Means that the function always gives the same output when the input parameters are the same.
|
||||
const SQLITE_DETERMINISTIC = ffi::SQLITE_DETERMINISTIC;
|
||||
const SQLITE_DETERMINISTIC = ffi::SQLITE_DETERMINISTIC; // 3.8.3
|
||||
/// Means that the function may only be invoked from top-level SQL.
|
||||
const SQLITE_DIRECTONLY = 0x0000_0008_0000; // 3.30.0
|
||||
/// Indicates to SQLite that a function may call `sqlite3_value_subtype()` to inspect the sub-types of its arguments.
|
||||
|
@ -285,7 +285,7 @@ impl<'c> AuthAction<'c> {
|
||||
operation: TransactionOperation::from_str(operation_str),
|
||||
savepoint_name,
|
||||
},
|
||||
#[cfg(feature = "modern_sqlite")]
|
||||
#[cfg(feature = "modern_sqlite")] // 3.8.3
|
||||
(ffi::SQLITE_RECURSIVE, ..) => Self::Recursive,
|
||||
(code, arg1, arg2) => Self::Unknown { code, arg1, arg2 },
|
||||
}
|
||||
|
@ -222,6 +222,7 @@ impl InnerConnection {
|
||||
let mut c_stmt = ptr::null_mut();
|
||||
let (c_sql, len, _) = str_for_sqlite(sql.as_bytes())?;
|
||||
let mut c_tail = ptr::null();
|
||||
// TODO sqlite3_prepare_v3 (https://sqlite.org/c3ref/c_prepare_normalize.html) // 3.20.0, #728
|
||||
#[cfg(not(feature = "unlock_notify"))]
|
||||
let r = unsafe {
|
||||
ffi::sqlite3_prepare_v2(
|
||||
@ -277,7 +278,14 @@ impl InnerConnection {
|
||||
|
||||
#[inline]
|
||||
pub fn changes(&self) -> usize {
|
||||
unsafe { ffi::sqlite3_changes(self.db()) as usize }
|
||||
#[cfg(not(feature = "modern_sqlite"))]
|
||||
unsafe {
|
||||
ffi::sqlite3_changes(self.db()) as usize
|
||||
}
|
||||
#[cfg(feature = "modern_sqlite")] // 3.37.0
|
||||
unsafe {
|
||||
ffi::sqlite3_changes64(self.db()) as usize
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -308,6 +316,50 @@ impl InnerConnection {
|
||||
#[cfg(not(feature = "hooks"))]
|
||||
#[inline]
|
||||
fn remove_hooks(&mut self) {}
|
||||
|
||||
#[cfg(feature = "modern_sqlite")] // 3.7.11
|
||||
pub fn db_readonly(&self, db_name: super::DatabaseName<'_>) -> Result<bool> {
|
||||
let name = db_name.as_cstring()?;
|
||||
let r = unsafe { ffi::sqlite3_db_readonly(self.db, name.as_ptr()) };
|
||||
match r {
|
||||
0 => Ok(false),
|
||||
1 => Ok(true),
|
||||
-1 => Err(Error::SqliteFailure(
|
||||
ffi::Error::new(ffi::SQLITE_MISUSE),
|
||||
Some(format!("{:?} is not the name of a database", db_name)),
|
||||
)),
|
||||
_ => Err(error_from_sqlite_code(
|
||||
r,
|
||||
Some("Unexpected result".to_owned()),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "modern_sqlite")] // 3.37.0
|
||||
pub fn txn_state(
|
||||
&self,
|
||||
db_name: Option<super::DatabaseName<'_>>,
|
||||
) -> Result<super::transaction::TransactionState> {
|
||||
let r = if let Some(ref name) = db_name {
|
||||
let name = name.as_cstring()?;
|
||||
unsafe { ffi::sqlite3_txn_state(self.db, name.as_ptr()) }
|
||||
} else {
|
||||
unsafe { ffi::sqlite3_txn_state(self.db, ptr::null()) }
|
||||
};
|
||||
match r {
|
||||
0 => Ok(super::transaction::TransactionState::None),
|
||||
1 => Ok(super::transaction::TransactionState::Read),
|
||||
2 => Ok(super::transaction::TransactionState::Write),
|
||||
-1 => Err(Error::SqliteFailure(
|
||||
ffi::Error::new(ffi::SQLITE_MISUSE),
|
||||
Some(format!("{:?} is not the name of a valid schema", db_name)),
|
||||
)),
|
||||
_ => Err(error_from_sqlite_code(
|
||||
r,
|
||||
Some("Unexpected result".to_owned()),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for InnerConnection {
|
||||
|
19
src/lib.rs
19
src/lib.rs
@ -927,6 +927,13 @@ impl Connection {
|
||||
pub fn cache_flush(&self) -> Result<()> {
|
||||
self.db.borrow_mut().cache_flush()
|
||||
}
|
||||
|
||||
/// Determine if a database is read-only
|
||||
#[cfg(feature = "modern_sqlite")] // 3.7.11
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))]
|
||||
pub fn is_readonly(&self, db_name: DatabaseName<'_>) -> Result<bool> {
|
||||
self.db.borrow().db_readonly(db_name)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Connection {
|
||||
@ -1024,9 +1031,9 @@ bitflags::bitflags! {
|
||||
const SQLITE_OPEN_SHARED_CACHE = 0x0002_0000;
|
||||
/// The database is opened shared cache disabled.
|
||||
const SQLITE_OPEN_PRIVATE_CACHE = 0x0004_0000;
|
||||
/// The database filename is not allowed to be a symbolic link.
|
||||
/// The database filename is not allowed to be a symbolic link. (3.31.0)
|
||||
const SQLITE_OPEN_NOFOLLOW = 0x0100_0000;
|
||||
/// Extended result codes.
|
||||
/// Extended result codes. (3.37.0)
|
||||
const SQLITE_OPEN_EXRESCODE = 0x0200_0000;
|
||||
}
|
||||
}
|
||||
@ -2012,4 +2019,12 @@ mod test {
|
||||
let db = Connection::open_in_memory()?;
|
||||
db.cache_flush()
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "modern_sqlite")]
|
||||
pub fn db_readonly() -> Result<()> {
|
||||
let db = Connection::open_in_memory()?;
|
||||
assert!(!db.is_readonly(super::MAIN_DB)?);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -224,6 +224,14 @@ impl RawStatement {
|
||||
pub fn tail(&self) -> usize {
|
||||
self.tail
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(feature = "modern_sqlite")] // 3.28.0
|
||||
pub fn is_explain(&self) -> i32 {
|
||||
unsafe { ffi::sqlite3_stmt_isexplain(self.ptr) }
|
||||
}
|
||||
|
||||
// TODO sqlite3_normalized_sql (https://sqlite.org/c3ref/expanded_sql.html) // 3.27.0 + SQLITE_ENABLE_NORMALIZE
|
||||
}
|
||||
|
||||
impl Drop for RawStatement {
|
||||
|
@ -727,6 +727,7 @@ impl Statement<'_> {
|
||||
|
||||
#[cfg(feature = "blob")]
|
||||
ToSqlOutput::ZeroBlob(len) => {
|
||||
// TODO sqlite3_bind_zeroblob64 // 3.8.11
|
||||
return self
|
||||
.conn
|
||||
.decode_result(unsafe { ffi::sqlite3_bind_zeroblob(ptr, col as c_int, len) });
|
||||
@ -750,6 +751,7 @@ impl Statement<'_> {
|
||||
ValueRef::Real(r) => unsafe { ffi::sqlite3_bind_double(ptr, col as c_int, r) },
|
||||
ValueRef::Text(s) => unsafe {
|
||||
let (c_str, len, destructor) = str_for_sqlite(s)?;
|
||||
// TODO sqlite3_bind_text64 // 3.8.7
|
||||
ffi::sqlite3_bind_text(ptr, col as c_int, c_str, len, destructor)
|
||||
},
|
||||
ValueRef::Blob(b) => unsafe {
|
||||
@ -757,6 +759,7 @@ impl Statement<'_> {
|
||||
if length == 0 {
|
||||
ffi::sqlite3_bind_zeroblob(ptr, col as c_int, 0)
|
||||
} else {
|
||||
// TODO sqlite3_bind_blob64 // 3.8.7
|
||||
ffi::sqlite3_bind_blob(
|
||||
ptr,
|
||||
col as c_int,
|
||||
@ -838,6 +841,16 @@ impl Statement<'_> {
|
||||
self.stmt.get_status(status, true)
|
||||
}
|
||||
|
||||
/// Returns 1 if the prepared statement is an EXPLAIN statement,
|
||||
/// or 2 if the statement is an EXPLAIN QUERY PLAN,
|
||||
/// or 0 if it is an ordinary statement or a NULL pointer.
|
||||
#[inline]
|
||||
#[cfg(feature = "modern_sqlite")] // 3.28.0
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))]
|
||||
pub fn is_explain(&self) -> i32 {
|
||||
self.stmt.is_explain()
|
||||
}
|
||||
|
||||
#[cfg(feature = "extra_check")]
|
||||
#[inline]
|
||||
pub(crate) fn check_no_tail(&self) -> Result<()> {
|
||||
@ -985,15 +998,15 @@ pub enum StatementStatus {
|
||||
AutoIndex = 3,
|
||||
/// Equivalent to SQLITE_STMTSTATUS_VM_STEP
|
||||
VmStep = 4,
|
||||
/// Equivalent to SQLITE_STMTSTATUS_REPREPARE
|
||||
/// Equivalent to SQLITE_STMTSTATUS_REPREPARE (3.20.0)
|
||||
RePrepare = 5,
|
||||
/// Equivalent to SQLITE_STMTSTATUS_RUN
|
||||
/// Equivalent to SQLITE_STMTSTATUS_RUN (3.20.0)
|
||||
Run = 6,
|
||||
/// Equivalent to SQLITE_STMTSTATUS_FILTER_MISS
|
||||
FilterMiss = 7,
|
||||
/// Equivalent to SQLITE_STMTSTATUS_FILTER_HIT
|
||||
FilterHit = 8,
|
||||
/// Equivalent to SQLITE_STMTSTATUS_MEMUSED
|
||||
/// Equivalent to SQLITE_STMTSTATUS_MEMUSED (3.20.0)
|
||||
MemUsed = 99,
|
||||
}
|
||||
|
||||
@ -1508,4 +1521,13 @@ mod test {
|
||||
assert_eq!(expected, actual);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "modern_sqlite")]
|
||||
fn is_explain() -> Result<()> {
|
||||
let db = Connection::open_in_memory()?;
|
||||
let stmt = db.prepare("SELECT 1;")?;
|
||||
assert_eq!(0, stmt.is_explain());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -119,6 +119,8 @@ impl Connection {
|
||||
None => unsafe { ffi::sqlite3_profile(c.db(), None, ptr::null_mut()) },
|
||||
};
|
||||
}
|
||||
|
||||
// TODO sqlite3_trace_v2 (https://sqlite.org/c3ref/trace_v2.html) // 3.14.0, #977
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -375,6 +375,20 @@ impl Drop for Savepoint<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Transaction state of a database
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[non_exhaustive]
|
||||
#[cfg(feature = "modern_sqlite")] // 3.37.0
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))]
|
||||
pub enum TransactionState {
|
||||
/// Equivalent to SQLITE_TXN_NONE
|
||||
None,
|
||||
/// Equivalent to SQLITE_TXN_READ
|
||||
Read,
|
||||
/// Equivalent to SQLITE_TXN_WRITE
|
||||
Write,
|
||||
}
|
||||
|
||||
impl Connection {
|
||||
/// Begin a new transaction with the default behavior (DEFERRED).
|
||||
///
|
||||
@ -499,6 +513,16 @@ impl Connection {
|
||||
pub fn savepoint_with_name<T: Into<String>>(&mut self, name: T) -> Result<Savepoint<'_>> {
|
||||
Savepoint::with_name(self, name)
|
||||
}
|
||||
|
||||
/// Determine the transaction state of a database
|
||||
#[cfg(feature = "modern_sqlite")] // 3.37.0
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))]
|
||||
pub fn transaction_state(
|
||||
&self,
|
||||
db_name: Option<crate::DatabaseName<'_>>,
|
||||
) -> Result<TransactionState> {
|
||||
self.db.borrow().txn_state(db_name)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -710,4 +734,25 @@ mod test {
|
||||
assert_eq!(x, i);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "modern_sqlite")]
|
||||
fn txn_state() -> Result<()> {
|
||||
use super::TransactionState;
|
||||
use crate::DatabaseName;
|
||||
let db = Connection::open_in_memory()?;
|
||||
assert_eq!(
|
||||
TransactionState::None,
|
||||
db.transaction_state(Some(DatabaseName::Main))?
|
||||
);
|
||||
assert_eq!(TransactionState::None, db.transaction_state(None)?);
|
||||
db.execute_batch("BEGIN")?;
|
||||
assert_eq!(TransactionState::None, db.transaction_state(None)?);
|
||||
let _: i32 = db.pragma_query_value(None, "user_version", |row| row.get(0))?;
|
||||
assert_eq!(TransactionState::Read, db.transaction_state(None)?);
|
||||
db.pragma_update(None, "user_version", 1)?;
|
||||
assert_eq!(TransactionState::Write, db.transaction_state(None)?);
|
||||
db.execute_batch("ROLLBACK")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -257,4 +257,7 @@ impl<'a> ValueRef<'a> {
|
||||
_ => unreachable!("sqlite3_value_type returned invalid value"),
|
||||
}
|
||||
}
|
||||
|
||||
// TODO sqlite3_value_nochange // 3.22.0 & VTab xUpdate
|
||||
// TODO sqlite3_value_frombind // 3.28.0
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ use crate::ffi;
|
||||
use crate::types::Null;
|
||||
use crate::vtab::{
|
||||
dequote, escape_double_quote, parse_boolean, read_only_module, Context, CreateVTab, IndexInfo,
|
||||
VTab, VTabConnection, VTabCursor, Values,
|
||||
VTab, VTabConfig, VTabConnection, VTabCursor, Values,
|
||||
};
|
||||
use crate::{Connection, Error, Result};
|
||||
|
||||
@ -101,7 +101,7 @@ unsafe impl<'vtab> VTab<'vtab> for CsvTab {
|
||||
type Cursor = CsvTabCursor<'vtab>;
|
||||
|
||||
fn connect(
|
||||
_: &mut VTabConnection,
|
||||
db: &mut VTabConnection,
|
||||
_aux: Option<&()>,
|
||||
args: &[&[u8]],
|
||||
) -> Result<(String, CsvTab)> {
|
||||
@ -249,7 +249,7 @@ unsafe impl<'vtab> VTab<'vtab> for CsvTab {
|
||||
}
|
||||
schema = Some(sql);
|
||||
}
|
||||
|
||||
db.config(VTabConfig::DirectOnly)?;
|
||||
Ok((schema.unwrap(), vtab))
|
||||
}
|
||||
|
||||
|
104
src/vtab/mod.rs
104
src/vtab/mod.rs
@ -165,13 +165,32 @@ pub fn eponymous_only_module<'vtab, T: VTab<'vtab>>() -> &'static Module<'vtab,
|
||||
}
|
||||
}
|
||||
|
||||
/// Virtual table configuration options
|
||||
#[repr(i32)]
|
||||
#[non_exhaustive]
|
||||
#[cfg(feature = "modern_sqlite")] // 3.7.7
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum VTabConfig {
|
||||
/// Equivalent to SQLITE_VTAB_CONSTRAINT_SUPPORT
|
||||
ConstraintSupport = 1,
|
||||
/// Equivalent to SQLITE_VTAB_INNOCUOUS
|
||||
Innocuous = 2,
|
||||
/// Equivalent to SQLITE_VTAB_DIRECTONLY
|
||||
DirectOnly = 3,
|
||||
}
|
||||
|
||||
/// `feature = "vtab"`
|
||||
pub struct VTabConnection(*mut ffi::sqlite3);
|
||||
|
||||
impl VTabConnection {
|
||||
// TODO sqlite3_vtab_config (http://sqlite.org/c3ref/vtab_config.html)
|
||||
/// Configure various facets of the virtual table interface
|
||||
#[cfg(feature = "modern_sqlite")] // 3.7.7
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))]
|
||||
pub fn config(&mut self, config: VTabConfig) -> Result<()> {
|
||||
crate::error::check(unsafe { ffi::sqlite3_vtab_config(self.0, config as c_int) })
|
||||
}
|
||||
|
||||
// TODO sqlite3_vtab_on_conflict (http://sqlite.org/c3ref/vtab_on_conflict.html)
|
||||
// TODO sqlite3_vtab_on_conflict (http://sqlite.org/c3ref/vtab_on_conflict.html) & xUpdate
|
||||
|
||||
/// Get access to the underlying SQLite database connection handle.
|
||||
///
|
||||
@ -310,10 +329,24 @@ impl From<u8> for IndexConstraintOp {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "modern_sqlite")] // 3.9.0
|
||||
bitflags::bitflags! {
|
||||
/// Virtual table scan flags
|
||||
/// See [Function Flags](https://sqlite.org/c3ref/c_index_scan_unique.html) for details.
|
||||
#[repr(C)]
|
||||
pub struct IndexFlags: ::std::os::raw::c_int {
|
||||
/// Default
|
||||
const NONE = 0;
|
||||
/// Scan visits at most 1 row.
|
||||
const SQLITE_INDEX_SCAN_UNIQUE = ffi::SQLITE_INDEX_SCAN_UNIQUE;
|
||||
}
|
||||
}
|
||||
|
||||
/// Pass information into and receive the reply from the
|
||||
/// [`VTab::best_index`] method.
|
||||
///
|
||||
/// (See [SQLite doc](http://sqlite.org/c3ref/index_info.html))
|
||||
#[derive(Debug)]
|
||||
pub struct IndexInfo(*mut ffi::sqlite3_index_info);
|
||||
|
||||
impl IndexInfo {
|
||||
@ -376,6 +409,14 @@ impl IndexInfo {
|
||||
}
|
||||
}
|
||||
|
||||
/// String used to identify the index
|
||||
pub fn set_idx_str(&mut self, idx_str: &str) {
|
||||
unsafe {
|
||||
(*self.0).idxStr = alloc(idx_str);
|
||||
(*self.0).needToFreeIdxStr = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// True if output is already ordered
|
||||
#[inline]
|
||||
pub fn set_order_by_consumed(&mut self, order_by_consumed: bool) {
|
||||
@ -402,10 +443,60 @@ impl IndexInfo {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO idxFlags
|
||||
// TODO colUsed
|
||||
/// Mask of SQLITE_INDEX_SCAN_* flags.
|
||||
#[cfg(feature = "modern_sqlite")] // SQLite >= 3.9.0
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))]
|
||||
#[inline]
|
||||
pub fn set_idx_flags(&mut self, flags: IndexFlags) {
|
||||
unsafe { (*self.0).idxFlags = flags.bits() };
|
||||
}
|
||||
|
||||
// TODO sqlite3_vtab_collation (http://sqlite.org/c3ref/vtab_collation.html)
|
||||
/// Mask of columns used by statement
|
||||
#[cfg(feature = "modern_sqlite")] // SQLite >= 3.10.0
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))]
|
||||
#[inline]
|
||||
pub fn col_used(&self) -> u64 {
|
||||
unsafe { (*self.0).colUsed }
|
||||
}
|
||||
|
||||
/// Determine the collation for a virtual table constraint
|
||||
#[cfg(feature = "modern_sqlite")] // SQLite >= 3.22.0
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))]
|
||||
pub fn collation(&self, constraint_idx: usize) -> Result<&str> {
|
||||
use std::ffi::CStr;
|
||||
let idx = constraint_idx as c_int;
|
||||
let collation = unsafe { ffi::sqlite3_vtab_collation(self.0, idx) };
|
||||
if collation.is_null() {
|
||||
return Err(Error::SqliteFailure(
|
||||
ffi::Error::new(ffi::SQLITE_MISUSE),
|
||||
Some(format!("{} is out of range", constraint_idx)),
|
||||
));
|
||||
}
|
||||
Ok(unsafe { CStr::from_ptr(collation) }.to_str()?)
|
||||
}
|
||||
|
||||
/*/// Determine if a virtual table query is DISTINCT
|
||||
#[cfg(feature = "modern_sqlite")] // SQLite >= 3.38.0
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))]
|
||||
pub fn distinct(&self) -> c_int {
|
||||
unsafe { ffi::sqlite3_vtab_distinct(self.0) }
|
||||
}
|
||||
|
||||
/// Constraint values
|
||||
#[cfg(feature = "modern_sqlite")] // SQLite >= 3.38.0
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))]
|
||||
pub fn set_rhs_value(&mut self, constraint_idx: c_int, value: ValueRef) -> Result<()> {
|
||||
// TODO ValueRef to sqlite3_value
|
||||
crate::error::check(unsafe { ffi::sqlite3_vtab_rhs_value(self.O, constraint_idx, value) })
|
||||
}
|
||||
|
||||
/// Identify and handle IN constraints
|
||||
#[cfg(feature = "modern_sqlite")] // SQLite >= 3.38.0
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))]
|
||||
pub fn set_in_constraint(&mut self, constraint_idx: c_int, b_handle: c_int) -> bool {
|
||||
unsafe { ffi::sqlite3_vtab_in(self.0, constraint_idx, b_handle) != 0 }
|
||||
} // TODO sqlite3_vtab_in_first / sqlite3_vtab_in_next https://sqlite.org/c3ref/vtab_in_first.html
|
||||
*/
|
||||
}
|
||||
|
||||
/// Iterate on index constraint and its associated usage.
|
||||
@ -583,7 +674,7 @@ impl Context {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO sqlite3_vtab_nochange (http://sqlite.org/c3ref/vtab_nochange.html)
|
||||
// TODO sqlite3_vtab_nochange (http://sqlite.org/c3ref/vtab_nochange.html) // 3.22.0 & xColumn
|
||||
}
|
||||
|
||||
/// Wrapper to [`VTabCursor::filter`] arguments, the values
|
||||
@ -651,6 +742,7 @@ impl Values<'_> {
|
||||
iter: self.args.iter(),
|
||||
}
|
||||
}
|
||||
// TODO sqlite3_vtab_in_first / sqlite3_vtab_in_next https://sqlite.org/c3ref/vtab_in_first.html & 3.38.0
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a Values<'a> {
|
||||
|
@ -10,8 +10,8 @@ use std::os::raw::c_int;
|
||||
use crate::ffi;
|
||||
use crate::types::Type;
|
||||
use crate::vtab::{
|
||||
eponymous_only_module, Context, IndexConstraintOp, IndexInfo, VTab, VTabConnection, VTabCursor,
|
||||
Values,
|
||||
eponymous_only_module, Context, IndexConstraintOp, IndexInfo, VTab, VTabConfig, VTabConnection,
|
||||
VTabCursor, Values,
|
||||
};
|
||||
use crate::{Connection, Error, Result};
|
||||
|
||||
@ -57,13 +57,14 @@ unsafe impl<'vtab> VTab<'vtab> for SeriesTab {
|
||||
type Cursor = SeriesTabCursor<'vtab>;
|
||||
|
||||
fn connect(
|
||||
_: &mut VTabConnection,
|
||||
db: &mut VTabConnection,
|
||||
_aux: Option<&()>,
|
||||
_args: &[&[u8]],
|
||||
) -> Result<(String, SeriesTab)> {
|
||||
let vtab = SeriesTab {
|
||||
base: ffi::sqlite3_vtab::default(),
|
||||
};
|
||||
db.config(VTabConfig::Innocuous)?;
|
||||
Ok((
|
||||
"CREATE TABLE x(value,start hidden,stop hidden,step hidden)".to_owned(),
|
||||
vtab,
|
||||
@ -103,6 +104,8 @@ unsafe impl<'vtab> VTab<'vtab> for SeriesTab {
|
||||
let mut constraint_usage = info.constraint_usage(*j);
|
||||
constraint_usage.set_argv_index(n_arg);
|
||||
constraint_usage.set_omit(true);
|
||||
#[cfg(all(test, feature = "modern_sqlite"))]
|
||||
debug_assert_eq!(Ok("BINARY"), info.collation(*j));
|
||||
}
|
||||
if !(unusable_mask & !idx_num).is_empty() {
|
||||
return Err(Error::SqliteFailure(
|
||||
|
Loading…
Reference in New Issue
Block a user