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:
gwenn 2022-03-17 19:58:02 +01:00 committed by GitHub
parent 2afbdeeb52
commit 5e2c103a0c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 287 additions and 21 deletions

View File

@ -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.

View File

@ -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>(),

View File

@ -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)
}

View File

@ -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.

View File

@ -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 },
}

View File

@ -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 {

View File

@ -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(())
}
}

View File

@ -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 {

View File

@ -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(())
}
}

View File

@ -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)]

View File

@ -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(())
}
}

View File

@ -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
}

View File

@ -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))
}

View File

@ -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> {

View File

@ -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(