Merge 'Syntactic improvements' from Jorge López Tello

This is a purely syntactic PR. It doesn't change behavior, just rewrites
some loops and removes unneeded parts, like lifetime annotations and
references. Mainly because the Clippy and IDE warnings get annoying.
Don't worry about the number of commits, I just separated based on type
of change.

Closes #732
This commit is contained in:
Pekka Enberg 2025-01-19 12:17:28 +02:00
commit f5e5428d45
38 changed files with 387 additions and 416 deletions

View file

@ -101,17 +101,16 @@ impl Cursor {
// For DDL and DML statements,
// we need to execute the statement immediately
if stmt_is_ddl || stmt_is_dml {
loop {
match stmt.borrow_mut().step().map_err(|e| {
PyErr::new::<OperationalError, _>(format!("Step error: {:?}", e))
})? {
limbo_core::StepResult::IO => {
self.conn.io.run_once().map_err(|e| {
PyErr::new::<OperationalError, _>(format!("IO error: {:?}", e))
})?;
}
_ => break,
}
while stmt
.borrow_mut()
.step()
.map_err(|e| PyErr::new::<OperationalError, _>(format!("Step error: {:?}", e)))?
.eq(&limbo_core::StepResult::IO)
{
self.conn
.io
.run_once()
.map_err(|e| PyErr::new::<OperationalError, _>(format!("IO error: {:?}", e)))?;
}
}
@ -268,20 +267,26 @@ impl Connection {
#[allow(clippy::arc_with_non_send_sync)]
#[pyfunction]
pub fn connect(path: &str) -> Result<Connection> {
#[inline(always)]
fn open_or(
io: Arc<dyn limbo_core::IO>,
path: &str,
) -> std::result::Result<Arc<limbo_core::Database>, PyErr> {
limbo_core::Database::open_file(io, path).map_err(|e| {
PyErr::new::<DatabaseError, _>(format!("Failed to open database: {:?}", e))
})
}
match path {
":memory:" => {
let io: Arc<dyn limbo_core::IO> = Arc::new(limbo_core::MemoryIO::new()?);
let db = limbo_core::Database::open_file(io.clone(), path).map_err(|e| {
PyErr::new::<DatabaseError, _>(format!("Failed to open database: {:?}", e))
})?;
let db = open_or(io.clone(), path)?;
let conn: Rc<limbo_core::Connection> = db.connect();
Ok(Connection { conn, io })
}
path => {
let io: Arc<dyn limbo_core::IO> = Arc::new(limbo_core::PlatformIO::new()?);
let db = limbo_core::Database::open_file(io.clone(), path).map_err(|e| {
PyErr::new::<DatabaseError, _>(format!("Failed to open database: {:?}", e))
})?;
let db = open_or(io.clone(), path)?;
let conn: Rc<limbo_core::Connection> = db.connect();
Ok(Connection { conn, io })
}

View file

@ -11,7 +11,7 @@ use std::sync::Arc;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("SQL conversion failure: `{0}`")]
ToSqlConversionFailure(crate::BoxError),
ToSqlConversionFailure(BoxError),
}
impl From<limbo_core::LimboError> for Error {

View file

@ -153,7 +153,7 @@ impl From<u32> for Value {
}
impl TryFrom<u64> for Value {
type Error = crate::Error;
type Error = Error;
fn try_from(value: u64) -> Result<Value> {
if value > i64::MAX as u64 {

View file

@ -135,7 +135,7 @@ pub enum Command {
impl Command {
fn min_args(&self) -> usize {
(match self {
1 + match self {
Self::Quit
| Self::Schema
| Self::Help
@ -150,7 +150,7 @@ impl Command {
| Self::NullValue
| Self::LoadExtension => 1,
Self::Import => 2,
} + 1) // argv0
} // argv0
}
fn usage(&self) -> &str {
@ -337,7 +337,7 @@ impl Limbo {
.map_err(|e| e.to_string())
}
fn display_in_memory(&mut self) -> std::io::Result<()> {
fn display_in_memory(&mut self) -> io::Result<()> {
if self.opts.db_file == ":memory:" {
self.writeln("Connected to a transient in-memory database.")?;
self.writeln("Use \".open FILENAME\" to reopen on a persistent database")?;
@ -345,7 +345,7 @@ impl Limbo {
Ok(())
}
fn show_info(&mut self) -> std::io::Result<()> {
fn show_info(&mut self) -> io::Result<()> {
let opts = format!("{}", self.opts);
self.writeln(opts)
}

View file

@ -214,7 +214,7 @@ pub enum ScalarFunc {
}
impl Display for ScalarFunc {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let str = match self {
Self::Cast => "cast".to_string(),
Self::Changes => "changes".to_string(),
@ -343,7 +343,7 @@ impl MathFunc {
}
impl Display for MathFunc {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let str = match self {
Self::Acos => "acos".to_string(),
Self::Acosh => "acosh".to_string(),

View file

@ -4,7 +4,6 @@ use pest_derive::Parser;
use serde::de;
use serde::forward_to_deserialize_any;
use std::collections::VecDeque;
use std::f64;
use crate::json::error::{self, Error, Result};

View file

@ -36,7 +36,7 @@ pub fn get_json(json_value: &OwnedValue) -> crate::Result<OwnedValue> {
}
let json_val = get_json_value(json_value)?;
let json = crate::json::to_string(&json_val).unwrap();
let json = to_string(&json_val).unwrap();
Ok(OwnedValue::Text(LimboText::json(Rc::new(json))))
}
@ -52,7 +52,7 @@ pub fn get_json(json_value: &OwnedValue) -> crate::Result<OwnedValue> {
OwnedValue::Null => Ok(OwnedValue::Null),
_ => {
let json_val = get_json_value(json_value)?;
let json = crate::json::to_string(&json_val).unwrap();
let json = to_string(&json_val).unwrap();
Ok(OwnedValue::Text(LimboText::json(Rc::new(json))))
}
@ -61,7 +61,7 @@ pub fn get_json(json_value: &OwnedValue) -> crate::Result<OwnedValue> {
fn get_json_value(json_value: &OwnedValue) -> crate::Result<Val> {
match json_value {
OwnedValue::Text(ref t) => match crate::json::from_str::<Val>(&t.value) {
OwnedValue::Text(ref t) => match from_str::<Val>(&t.value) {
Ok(json) => Ok(json),
Err(_) => {
crate::bail_parse_error!("malformed JSON")
@ -92,17 +92,17 @@ pub fn json_array(values: &[OwnedValue]) -> crate::Result<OwnedValue> {
if t.subtype == TextSubtype::Json {
s.push_str(&t.value);
} else {
match crate::json::to_string(&*t.value) {
match to_string(&*t.value) {
Ok(json) => s.push_str(&json),
Err(_) => crate::bail_parse_error!("malformed JSON"),
}
}
}
OwnedValue::Integer(i) => match crate::json::to_string(&i) {
OwnedValue::Integer(i) => match to_string(&i) {
Ok(json) => s.push_str(&json),
Err(_) => crate::bail_parse_error!("malformed JSON"),
},
OwnedValue::Float(f) => match crate::json::to_string(&f) {
OwnedValue::Float(f) => match to_string(&f) {
Ok(json) => s.push_str(&json),
Err(_) => crate::bail_parse_error!("malformed JSON"),
},
@ -152,7 +152,7 @@ pub fn json_arrow_extract(value: &OwnedValue, path: &OwnedValue) -> crate::Resul
let extracted = json_extract_single(&json, path, false)?;
if let Some(val) = extracted {
let json = crate::json::to_string(val).unwrap();
let json = to_string(val).unwrap();
Ok(OwnedValue::Text(LimboText::json(Rc::new(json))))
} else {
@ -190,7 +190,7 @@ pub fn json_extract(value: &OwnedValue, paths: &[OwnedValue]) -> crate::Result<O
let json = get_json_value(value)?;
let extracted = json_extract_single(&json, &paths[0], true)?.unwrap_or_else(|| &Val::Null);
return convert_json_to_db_type(&extracted, false);
return convert_json_to_db_type(extracted, false);
}
let json = get_json_value(value)?;
@ -209,7 +209,7 @@ pub fn json_extract(value: &OwnedValue, paths: &[OwnedValue]) -> crate::Result<O
return Ok(OwnedValue::Null);
}
result.push_str(&crate::json::to_string(&extracted).unwrap());
result.push_str(&to_string(&extracted).unwrap());
result.push(',');
}
}
@ -228,6 +228,7 @@ pub fn json_extract(value: &OwnedValue, paths: &[OwnedValue]) -> crate::Result<O
/// > an INTEGER one for a JSON true value,
/// > the dequoted text for a JSON string value,
/// > and a text representation for JSON object and array values.
///
/// https://sqlite.org/json1.html#the_json_extract_function
///
/// *all_as_db* - if true, objects and arrays will be returned as pure TEXT without the JSON subtype
@ -245,7 +246,7 @@ fn convert_json_to_db_type(extracted: &Val, all_as_db: bool) -> crate::Result<Ow
}
Val::String(s) => Ok(OwnedValue::Text(LimboText::new(Rc::new(s.clone())))),
_ => {
let json = crate::json::to_string(&extracted).unwrap();
let json = to_string(&extracted).unwrap();
if all_as_db {
Ok(OwnedValue::Text(LimboText::new(Rc::new(json))))
} else {
@ -369,12 +370,12 @@ fn json_extract_single<'a>(
}
}
Ok(Some(&current_element))
Ok(Some(current_element))
}
pub fn json_error_position(json: &OwnedValue) -> crate::Result<OwnedValue> {
match json {
OwnedValue::Text(t) => match crate::json::from_str::<Val>(&t.value) {
OwnedValue::Text(t) => match from_str::<Val>(&t.value) {
Ok(_) => Ok(OwnedValue::Integer(0)),
Err(JsonError::Message { location, .. }) => {
if let Some(loc) = location {

View file

@ -45,7 +45,7 @@ use util::parse_schema_rows;
pub use error::LimboError;
use translate::select::prepare_select_plan;
pub type Result<T, E = error::LimboError> = std::result::Result<T, E>;
pub type Result<T, E = LimboError> = std::result::Result<T, E>;
use crate::translate::optimizer::optimize_plan;
pub use io::OpenFlags;
@ -86,7 +86,7 @@ impl Database {
pub fn open_file(io: Arc<dyn IO>, path: &str) -> Result<Arc<Database>> {
use storage::wal::WalFileShared;
let file = io.open_file(path, io::OpenFlags::Create, true)?;
let file = io.open_file(path, OpenFlags::Create, true)?;
maybe_init_database_file(&file, &io)?;
let page_io = Rc::new(FileStorage::new(file));
let wal_path = format!("{}-wal", path);
@ -194,7 +194,7 @@ impl Database {
}
pub fn maybe_init_database_file(file: &Rc<dyn File>, io: &Arc<dyn IO>) -> Result<()> {
if file.size().unwrap() == 0 {
if file.size()? == 0 {
// init db
let db_header = DatabaseHeader::default();
let page1 = allocate_page(
@ -223,8 +223,7 @@ pub fn maybe_init_database_file(file: &Rc<dyn File>, io: &Arc<dyn IO>) -> Result
let completion = Completion::Write(WriteCompletion::new(Box::new(move |_| {
*flag_complete.borrow_mut() = true;
})));
file.pwrite(0, contents.buffer.clone(), Rc::new(completion))
.unwrap();
file.pwrite(0, contents.buffer.clone(), Rc::new(completion))?;
}
let mut limit = 100;
loop {
@ -475,6 +474,7 @@ impl Statement {
}
}
#[derive(PartialEq)]
pub enum StepResult<'a> {
Row(Row<'a>),
IO,
@ -483,12 +483,13 @@ pub enum StepResult<'a> {
Busy,
}
#[derive(PartialEq)]
pub struct Row<'a> {
pub values: Vec<Value<'a>>,
}
impl<'a> Row<'a> {
pub fn get<T: crate::types::FromValue<'a> + 'a>(&self, idx: usize) -> Result<T> {
pub fn get<T: types::FromValue<'a> + 'a>(&self, idx: usize) -> Result<T> {
let value = &self.values[idx];
T::from_value(value)
}
@ -509,9 +510,9 @@ impl Rows {
}
pub(crate) struct SymbolTable {
pub functions: HashMap<String, Rc<crate::function::ExternalFunc>>,
pub functions: HashMap<String, Rc<function::ExternalFunc>>,
#[cfg(not(target_family = "wasm"))]
extensions: Vec<(libloading::Library, *const ExtensionApi)>,
extensions: Vec<(Library, *const ExtensionApi)>,
}
impl std::fmt::Debug for SymbolTable {
@ -563,7 +564,7 @@ impl SymbolTable {
&self,
name: &str,
_arg_count: usize,
) -> Option<Rc<crate::function::ExternalFunc>> {
) -> Option<Rc<function::ExternalFunc>> {
self.functions.get(name).cloned()
}
}
@ -589,7 +590,7 @@ impl Iterator for QueryRunner<'_> {
match self.parser.next() {
Ok(Some(cmd)) => Some(self.conn.run_cmd(cmd)),
Ok(None) => None,
Err(err) => Some(Result::Err(LimboError::from(err))),
Err(err) => Some(Err(LimboError::from(err))),
}
}
}

View file

@ -29,6 +29,12 @@ pub struct Parameters {
pub list: Vec<Parameter>,
}
impl Default for Parameters {
fn default() -> Self {
Self::new()
}
}
impl Parameters {
pub fn new() -> Self {
Self {
@ -76,7 +82,7 @@ impl Parameters {
log::trace!("anonymous parameter at {index}");
index
}
name if name.starts_with(&['$', ':', '@', '#']) => {
name if name.starts_with(['$', ':', '@', '#']) => {
match self
.list
.iter()
@ -97,7 +103,7 @@ impl Parameters {
}
}
index => {
// SAFETY: Garanteed from parser that the index is bigger that 0.
// SAFETY: Guaranteed from parser that the index is bigger than 0.
let index: NonZero<usize> = index.parse().unwrap();
if index > self.index {
self.index = index.checked_add(1).unwrap();

View file

@ -167,7 +167,7 @@ impl BTreeCursor {
/// Check if the table is empty.
/// This is done by checking if the root page has no cells.
fn is_empty_table(&mut self) -> Result<CursorResult<bool>> {
fn is_empty_table(&self) -> Result<CursorResult<bool>> {
let page = self.pager.read_page(self.root_page)?;
return_if_locked!(page);
@ -473,7 +473,7 @@ impl BTreeCursor {
&record.values[..record.values.len() - 1] >= &index_key.values
}
SeekOp::EQ => {
&record.values[..record.values.len() - 1] == &index_key.values
record.values[..record.values.len() - 1] == index_key.values
}
};
self.stack.advance();
@ -542,7 +542,7 @@ impl BTreeCursor {
match contents.rightmost_pointer() {
Some(right_most_pointer) => {
self.stack.set_cell_index(contents.cell_count() as i32 + 1);
let mem_page = self.pager.read_page(right_most_pointer as usize).unwrap();
let mem_page = self.pager.read_page(right_most_pointer as usize)?;
self.stack.push(mem_page);
continue;
}
@ -643,7 +643,7 @@ impl BTreeCursor {
};
if target_leaf_page_is_in_the_left_subtree {
// we don't advance in case of index tree internal nodes because we will visit this node going up
let mem_page = self.pager.read_page(*left_child_page as usize).unwrap();
let mem_page = self.pager.read_page(*left_child_page as usize)?;
self.stack.push(mem_page);
found_cell = true;
break;
@ -663,7 +663,7 @@ impl BTreeCursor {
match contents.rightmost_pointer() {
Some(right_most_pointer) => {
self.stack.advance();
let mem_page = self.pager.read_page(right_most_pointer as usize).unwrap();
let mem_page = self.pager.read_page(right_most_pointer as usize)?;
self.stack.push(mem_page);
continue;
}
@ -716,7 +716,7 @@ impl BTreeCursor {
// insert
let overflow = {
let contents = page.get().contents.as_mut().unwrap();
log::debug!(
debug!(
"insert_into_page(overflow, cell_count={})",
contents.cell_count()
);
@ -1027,15 +1027,13 @@ impl BTreeCursor {
// Right page pointer is u32 in right most pointer, and in cell is u32 too, so we can use a *u32 to hold where we want to change this value
let mut right_pointer = PAGE_HEADER_OFFSET_RIGHTMOST_PTR;
for cell_idx in 0..parent_contents.cell_count() {
let cell = parent_contents
.cell_get(
cell_idx,
self.pager.clone(),
self.payload_overflow_threshold_max(page_type.clone()),
self.payload_overflow_threshold_min(page_type.clone()),
self.usable_space(),
)
.unwrap();
let cell = parent_contents.cell_get(
cell_idx,
self.pager.clone(),
self.payload_overflow_threshold_max(page_type.clone()),
self.payload_overflow_threshold_min(page_type.clone()),
self.usable_space(),
)?;
let found = match cell {
BTreeCell::TableInteriorCell(interior) => {
interior._left_child_page as usize == current_idx
@ -1128,16 +1126,14 @@ impl BTreeCursor {
for page in new_pages.iter_mut().take(new_pages_len - 1) {
let contents = page.get().contents.as_mut().unwrap();
assert!(contents.cell_count() == 1);
let last_cell = contents
.cell_get(
contents.cell_count() - 1,
self.pager.clone(),
self.payload_overflow_threshold_max(contents.page_type()),
self.payload_overflow_threshold_min(contents.page_type()),
self.usable_space(),
)
.unwrap();
assert_eq!(contents.cell_count(), 1);
let last_cell = contents.cell_get(
contents.cell_count() - 1,
self.pager.clone(),
self.payload_overflow_threshold_max(contents.page_type()),
self.payload_overflow_threshold_min(contents.page_type()),
self.usable_space(),
)?;
let last_cell_pointer = match last_cell {
BTreeCell::TableInteriorCell(interior) => interior._left_child_page,
_ => unreachable!(),
@ -1170,8 +1166,7 @@ impl BTreeCursor {
self.payload_overflow_threshold_max(contents.page_type()),
self.payload_overflow_threshold_min(contents.page_type()),
self.usable_space(),
)
.unwrap();
)?;
if is_leaf {
// create a new divider cell and push
@ -1353,7 +1348,7 @@ impl BTreeCursor {
/// Defragment a page. This means packing all the cells to the end of the page.
fn defragment_page(&self, page: &PageContent, db_header: Ref<DatabaseHeader>) {
log::debug!("defragment_page");
debug!("defragment_page");
let cloned_page = page.clone();
// TODO(pere): usable space should include offset probably
let usable_space = (db_header.page_size - db_header.reserved_space as u16) as u64;
@ -1505,8 +1500,8 @@ impl BTreeCursor {
}
// Next should always be 0 (NULL) at this point since we have reached the end of the freeblocks linked list
assert!(
next == 0,
assert_eq!(
next, 0,
"corrupted page: freeblocks list not in ascending order"
);
@ -1555,7 +1550,7 @@ impl BTreeCursor {
}
let payload_overflow_threshold_max = self.payload_overflow_threshold_max(page_type.clone());
log::debug!(
debug!(
"fill_cell_payload(record_size={}, payload_overflow_threshold_max={})",
record_buf.len(),
payload_overflow_threshold_max
@ -1565,7 +1560,7 @@ impl BTreeCursor {
cell_payload.extend_from_slice(record_buf.as_slice());
return;
}
log::debug!("fill_cell_payload(overflow)");
debug!("fill_cell_payload(overflow)");
let payload_overflow_threshold_min = self.payload_overflow_threshold_min(page_type);
// see e.g. https://github.com/sqlite/sqlite/blob/9591d3fe93936533c8c3b0dc4d025ac999539e11/src/dbstat.c#L371

View file

@ -48,7 +48,7 @@ impl DatabaseStorage for FileStorage {
let buffer_size = buffer.borrow().len();
assert!(buffer_size >= 512);
assert!(buffer_size <= 65536);
assert!((buffer_size & (buffer_size - 1)) == 0);
assert_eq!(buffer_size & (buffer_size - 1), 0);
let pos = (page_idx - 1) * buffer_size;
self.file.pwrite(pos, buffer, c)?;
Ok(())

View file

@ -216,7 +216,7 @@ impl Pager {
}
/// Reads a page from the database.
pub fn read_page(&self, page_idx: usize) -> crate::Result<PageRef> {
pub fn read_page(&self, page_idx: usize) -> Result<PageRef> {
trace!("read_page(page_idx = {})", page_idx);
let mut page_cache = self.page_cache.write().unwrap();
let page_key = PageCacheKey::new(page_idx, Some(self.wal.borrow().get_max_frame()));
@ -295,7 +295,7 @@ impl Pager {
}
pub fn add_dirty(&self, page_id: usize) {
// TODO: cehck duplicates?
// TODO: check duplicates?
let mut dirty_pages = RefCell::borrow_mut(&self.dirty_pages);
dirty_pages.insert(page_id);
}
@ -312,7 +312,7 @@ impl Pager {
PageCacheKey::new(*page_id, Some(self.wal.borrow().get_max_frame()));
let page = cache.get(&page_key).expect("we somehow added a page to dirty list but we didn't mark it as dirty, causing cache to drop it.");
let page_type = page.get().contents.as_ref().unwrap().maybe_page_type();
log::trace!("cacheflush(page={}, page_type={:?}", page_id, page_type);
trace!("cacheflush(page={}, page_type={:?}", page_id, page_type);
self.wal.borrow_mut().append_frame(
page.clone(),
db_size,
@ -374,7 +374,7 @@ impl Pager {
pub fn checkpoint(&self) -> Result<CheckpointStatus> {
loop {
let state = self.checkpoint_state.borrow().clone();
log::trace!("pager_checkpoint(state={:?})", state);
trace!("pager_checkpoint(state={:?})", state);
match state {
CheckpointState::Checkpoint => {
let in_flight = self.checkpoint_inflight.clone();
@ -404,12 +404,12 @@ impl Pager {
}
CheckpointState::CheckpointDone => {
let in_flight = self.checkpoint_inflight.clone();
if *in_flight.borrow() > 0 {
return Ok(CheckpointStatus::IO);
return if *in_flight.borrow() > 0 {
Ok(CheckpointStatus::IO)
} else {
self.checkpoint_state.replace(CheckpointState::Checkpoint);
return Ok(CheckpointStatus::Done);
}
Ok(CheckpointStatus::Done)
};
}
}
}
@ -437,7 +437,7 @@ impl Pager {
}
/*
Get's a new page that increasing the size of the page or uses a free page.
Gets a new page that increasing the size of the page or uses a free page.
Currently free list pages are not yet supported.
*/
#[allow(clippy::readonly_write_lock)]

View file

@ -263,7 +263,7 @@ fn finish_read_database_header(
) -> Result<()> {
let buf = buf.borrow();
let buf = buf.as_slice();
let mut header = std::cell::RefCell::borrow_mut(&header);
let mut header = RefCell::borrow_mut(&header);
header.magic.copy_from_slice(&buf[0..16]);
header.page_size = u16::from_be_bytes([buf[16], buf[17]]);
header.write_version = buf[18];
@ -705,12 +705,12 @@ pub fn begin_write_btree_page(
page: &PageRef,
write_counter: Rc<RefCell<usize>>,
) -> Result<()> {
log::trace!("begin_write_btree_page(page={})", page.get().id);
trace!("begin_write_btree_page(page={})", page.get().id);
let page_source = &pager.page_io;
let page_finish = page.clone();
let page_id = page.get().id;
log::trace!("begin_write_btree_page(page_id={})", page_id);
trace!("begin_write_btree_page(page_id={})", page_id);
let buffer = {
let page = page.get();
let contents = page.contents.as_ref().unwrap();
@ -721,7 +721,7 @@ pub fn begin_write_btree_page(
let write_complete = {
let buf_copy = buffer.clone();
Box::new(move |bytes_written: i32| {
log::trace!("finish_write_btree_page");
trace!("finish_write_btree_page");
let buf_copy = buf_copy.clone();
let buf_len = buf_copy.borrow().len();
*write_counter.borrow_mut() -= 1;
@ -926,7 +926,7 @@ pub enum SerialType {
}
impl TryFrom<u64> for SerialType {
type Error = crate::error::LimboError;
type Error = LimboError;
fn try_from(value: u64) -> Result<Self> {
match value {
@ -1165,7 +1165,7 @@ pub fn begin_read_wal_frame(
buffer_pool: Rc<BufferPool>,
page: PageRef,
) -> Result<()> {
log::trace!(
trace!(
"begin_read_wal_frame(offset={}, page={})",
offset,
page.get().id
@ -1340,7 +1340,7 @@ pub fn checksum_wal(
input: (u32, u32),
native_endian: bool, // Sqlite interprets big endian as "native"
) -> (u32, u32) {
assert!(buf.len() % 8 == 0, "buffer must be a multiple of 8");
assert_eq!(buf.len() % 8, 0, "buffer must be a multiple of 8");
let mut s0: u32 = input.0;
let mut s1: u32 = input.1;
let mut i = 0;
@ -1366,7 +1366,7 @@ pub fn checksum_wal(
impl WalHeader {
pub fn as_bytes(&self) -> &[u8] {
unsafe { std::mem::transmute::<&WalHeader, &[u8; std::mem::size_of::<WalHeader>()]>(self) }
unsafe { std::mem::transmute::<&WalHeader, &[u8; size_of::<WalHeader>()]>(self) }
}
}

View file

@ -66,7 +66,7 @@ impl LimboRwLock {
}
}
/// Locks exlusively. Returns true if it was successful, false if it couldn't lock it
/// Locks exclusively. Returns true if it was successful, false if it couldn't lock it
pub fn write(&mut self) -> bool {
let lock = self.lock.load(Ordering::SeqCst);
match lock {
@ -197,7 +197,7 @@ struct OngoingCheckpoint {
#[allow(dead_code)]
pub struct WalFile {
io: Arc<dyn crate::io::IO>,
io: Arc<dyn IO>,
buffer_pool: Rc<BufferPool>,
sync_state: RefCell<SyncState>,
@ -222,7 +222,7 @@ pub struct WalFile {
/// that needs to be communicated between threads so this struct does the job.
#[allow(dead_code)]
pub struct WalFileShared {
wal_header: Arc<RwLock<sqlite3_ondisk::WalHeader>>,
wal_header: Arc<RwLock<WalHeader>>,
min_frame: u64,
max_frame: u64,
nbackfills: u64,
@ -237,7 +237,7 @@ pub struct WalFileShared {
pages_in_frames: Vec<u64>,
last_checksum: (u32, u32), // Check of last frame in WAL, this is a cumulative checksum over all frames in the WAL
file: Rc<dyn File>,
/// read_locks is a list of read locks that can coexist with the max_frame nubmer stored in
/// read_locks is a list of read locks that can coexist with the max_frame number stored in
/// value. There is a limited amount because and unbounded amount of connections could be
/// fatal. Therefore, for now we copy how SQLite behaves with limited amounts of read max
/// frames that is equal to 5
@ -293,7 +293,7 @@ impl Wal for WalFile {
self.max_frame_read_lock_index = max_read_mark_index as usize;
self.max_frame = max_read_mark as u64;
self.min_frame = shared.nbackfills + 1;
log::trace!(
trace!(
"begin_read_tx(min_frame={}, max_frame={}, lock={})",
self.min_frame,
self.max_frame,
@ -424,7 +424,7 @@ impl Wal for WalFile {
);
'checkpoint_loop: loop {
let state = self.ongoing_checkpoint.state;
log::debug!("checkpoint(state={:?})", state);
debug!("checkpoint(state={:?})", state);
match state {
CheckpointState::Start => {
// TODO(pere): check what frames are safe to checkpoint between many readers!
@ -447,7 +447,7 @@ impl Wal for WalFile {
self.ongoing_checkpoint.max_frame = max_safe_frame;
self.ongoing_checkpoint.current_page = 0;
self.ongoing_checkpoint.state = CheckpointState::ReadFrame;
log::trace!(
trace!(
"checkpoint_start(min_frame={}, max_frame={})",
self.ongoing_checkpoint.max_frame,
self.ongoing_checkpoint.min_frame
@ -475,11 +475,9 @@ impl Wal for WalFile {
if *frame >= self.ongoing_checkpoint.min_frame
&& *frame <= self.ongoing_checkpoint.max_frame
{
log::debug!(
debug!(
"checkpoint page(state={:?}, page={}, frame={})",
state,
page,
*frame
state, page, *frame
);
self.ongoing_checkpoint.page.get().id = page as usize;
@ -553,13 +551,13 @@ impl Wal for WalFile {
match state {
SyncState::NotSyncing => {
let shared = self.shared.write().unwrap();
log::debug!("wal_sync");
debug!("wal_sync");
{
let syncing = self.syncing.clone();
*syncing.borrow_mut() = true;
let completion = Completion::Sync(SyncCompletion {
complete: Box::new(move |_| {
log::debug!("wal_sync finish");
debug!("wal_sync finish");
*syncing.borrow_mut() = false;
}),
});
@ -671,7 +669,7 @@ impl WalFileShared {
};
let native = cfg!(target_endian = "big"); // if target_endian is
// already big then we don't care but if isn't, header hasn't yet been
// encoded to big endian, therefore we wan't to swap bytes to compute this
// encoded to big endian, therefore we want to swap bytes to compute this
// checksum.
let checksums = (0, 0);
let checksums = checksum_wal(

View file

@ -290,9 +290,9 @@ fn emit_program_for_delete(
Ok(())
}
fn emit_delete_insns<'a>(
fn emit_delete_insns(
program: &mut ProgramBuilder,
t_ctx: &mut TranslateCtx<'a>,
t_ctx: &mut TranslateCtx,
source: &SourceOperator,
limit: &Option<usize>,
) -> Result<()> {

View file

@ -1602,7 +1602,7 @@ pub fn translate_expr(
} else {
// must be a float
program.emit_insn(Insn::Real {
value: val.parse().unwrap(),
value: val.parse()?,
dest: target_register,
});
}
@ -1830,7 +1830,7 @@ fn wrap_eval_jump_expr_zero_or_null(
}
pub fn maybe_apply_affinity(col_type: Type, target_register: usize, program: &mut ProgramBuilder) {
if col_type == crate::schema::Type::Real {
if col_type == Type::Real {
program.emit_insn(Insn::RealAffinity {
register: target_register,
})

View file

@ -74,7 +74,7 @@ pub fn translate_insert(
// Check if rowid was provided (through INTEGER PRIMARY KEY as a rowid alias)
let rowid_alias_index = table.columns.iter().position(|c| c.is_rowid_alias);
let has_user_provided_rowid = {
assert!(column_mappings.len() == table.columns.len());
assert_eq!(column_mappings.len(), table.columns.len());
if let Some(index) = rowid_alias_index {
column_mappings[index].value_index.is_some()
} else {

View file

@ -780,7 +780,7 @@ pub fn close_loop(
target_pc: lj_meta.label_match_flag_set_true,
});
assert!(program.offset() == jump_offset);
assert_eq!(program.offset(), jump_offset);
}
close_loop(program, t_ctx, left)?;

View file

@ -291,12 +291,12 @@ fn check_automatic_pk_index_required(
for result in primary_key_column_results {
if let Err(e) = result {
crate::bail_parse_error!("{}", e);
bail_parse_error!("{}", e);
}
let column_name = result.unwrap();
let column_name = result?;
let column_def = columns.get(&ast::Name(column_name.clone()));
if column_def.is_none() {
crate::bail_parse_error!("No such column: {}", column_name);
bail_parse_error!("No such column: {}", column_name);
}
if matches!(
@ -326,10 +326,7 @@ fn check_automatic_pk_index_required(
ast::ColumnConstraint::PrimaryKey { .. }
) {
if primary_key_definition.is_some() {
crate::bail_parse_error!(
"table {} has more than one primary key",
tbl_name
);
bail_parse_error!("table {} has more than one primary key", tbl_name);
}
let typename = col_def.col_type.as_ref().map(|t| t.name.as_str());
primary_key_definition =
@ -340,7 +337,7 @@ fn check_automatic_pk_index_required(
// Check if table has rowid
if options.contains(ast::TableOptions::WITHOUT_ROWID) {
crate::bail_parse_error!("WITHOUT ROWID tables are not supported yet");
bail_parse_error!("WITHOUT ROWID tables are not supported yet");
}
// Check if we need an automatic index
@ -364,7 +361,7 @@ fn check_automatic_pk_index_required(
}
}
ast::CreateTableBody::AsSelect(_) => {
crate::bail_parse_error!("CREATE TABLE AS SELECT not supported yet")
bail_parse_error!("CREATE TABLE AS SELECT not supported yet")
}
}
}
@ -578,11 +575,11 @@ fn update_pragma(
PragmaName::CacheSize => {
let cache_size = match value {
ast::Expr::Literal(ast::Literal::Numeric(numeric_value)) => {
numeric_value.parse::<i64>().unwrap()
numeric_value.parse::<i64>()?
}
ast::Expr::Unary(ast::UnaryOperator::Negative, expr) => match *expr {
ast::Expr::Literal(ast::Literal::Numeric(numeric_value)) => {
-numeric_value.parse::<i64>().unwrap()
-numeric_value.parse::<i64>()?
}
_ => bail_parse_error!("Not a valid value"),
},

View file

@ -97,7 +97,7 @@ pub struct DeletePlan {
}
impl Display for Plan {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Select(select_plan) => write!(f, "{}", select_plan.source),
Delete(delete_plan) => write!(f, "{}", delete_plan.source),

View file

@ -12,7 +12,7 @@ use crate::{
};
use sqlite3_parser::ast::{self, Expr, FromClause, JoinType, Limit};
pub const ROWID: &'static str = "rowid";
pub const ROWID: &str = "rowid";
pub struct OperatorIdCounter {
id: usize,
@ -29,7 +29,7 @@ impl OperatorIdCounter {
}
}
pub fn resolve_aggregates(expr: &ast::Expr, aggs: &mut Vec<Aggregate>) -> bool {
pub fn resolve_aggregates(expr: &Expr, aggs: &mut Vec<Aggregate>) -> bool {
if aggs
.iter()
.any(|a| exprs_are_equivalent(&a.original_expr, expr))
@ -37,7 +37,7 @@ pub fn resolve_aggregates(expr: &ast::Expr, aggs: &mut Vec<Aggregate>) -> bool {
return true;
}
match expr {
ast::Expr::FunctionCall { name, args, .. } => {
Expr::FunctionCall { name, args, .. } => {
let args_count = if let Some(args) = &args {
args.len()
} else {
@ -63,7 +63,7 @@ pub fn resolve_aggregates(expr: &ast::Expr, aggs: &mut Vec<Aggregate>) -> bool {
}
}
}
ast::Expr::FunctionCallStar { name, .. } => {
Expr::FunctionCallStar { name, .. } => {
if let Ok(Func::Agg(f)) =
Func::resolve_function(normalize_ident(name.0.as_str()).as_str(), 0)
{
@ -77,13 +77,13 @@ pub fn resolve_aggregates(expr: &ast::Expr, aggs: &mut Vec<Aggregate>) -> bool {
false
}
}
ast::Expr::Binary(lhs, _, rhs) => {
Expr::Binary(lhs, _, rhs) => {
let mut contains_aggregates = false;
contains_aggregates |= resolve_aggregates(lhs, aggs);
contains_aggregates |= resolve_aggregates(rhs, aggs);
contains_aggregates
}
ast::Expr::Unary(_, expr) => {
Expr::Unary(_, expr) => {
let mut contains_aggregates = false;
contains_aggregates |= resolve_aggregates(expr, aggs);
contains_aggregates
@ -93,12 +93,9 @@ pub fn resolve_aggregates(expr: &ast::Expr, aggs: &mut Vec<Aggregate>) -> bool {
}
}
pub fn bind_column_references(
expr: &mut ast::Expr,
referenced_tables: &[TableReference],
) -> Result<()> {
pub fn bind_column_references(expr: &mut Expr, referenced_tables: &[TableReference]) -> Result<()> {
match expr {
ast::Expr::Id(id) => {
Expr::Id(id) => {
// true and false are special constants that are effectively aliases for 1 and 0
// and not identifiers of columns
if id.0.eq_ignore_ascii_case("true") || id.0.eq_ignore_ascii_case("false") {
@ -106,7 +103,7 @@ pub fn bind_column_references(
}
let normalized_id = normalize_ident(id.0.as_str());
if referenced_tables.len() > 0 {
if !referenced_tables.is_empty() {
if let Some(row_id_expr) =
parse_row_id(&normalized_id, 0, || referenced_tables.len() != 1)?
{
@ -133,7 +130,7 @@ pub fn bind_column_references(
crate::bail_parse_error!("Column {} not found", id.0);
}
let (tbl_idx, col_idx, is_rowid_alias) = match_result.unwrap();
*expr = ast::Expr::Column {
*expr = Expr::Column {
database: None, // TODO: support different databases
table: tbl_idx,
column: col_idx,
@ -141,7 +138,7 @@ pub fn bind_column_references(
};
Ok(())
}
ast::Expr::Qualified(tbl, id) => {
Expr::Qualified(tbl, id) => {
let normalized_table_name = normalize_ident(tbl.0.as_str());
let matching_tbl_idx = referenced_tables.iter().position(|t| {
t.table_identifier
@ -169,7 +166,7 @@ pub fn bind_column_references(
.columns()
.get(col_idx.unwrap())
.unwrap();
*expr = ast::Expr::Column {
*expr = Expr::Column {
database: None, // TODO: support different databases
table: tbl_idx,
column: col_idx.unwrap(),
@ -177,7 +174,7 @@ pub fn bind_column_references(
};
Ok(())
}
ast::Expr::Between {
Expr::Between {
lhs,
not: _,
start,
@ -188,12 +185,12 @@ pub fn bind_column_references(
bind_column_references(end, referenced_tables)?;
Ok(())
}
ast::Expr::Binary(expr, _operator, expr1) => {
Expr::Binary(expr, _operator, expr1) => {
bind_column_references(expr, referenced_tables)?;
bind_column_references(expr1, referenced_tables)?;
Ok(())
}
ast::Expr::Case {
Expr::Case {
base,
when_then_pairs,
else_expr,
@ -210,9 +207,9 @@ pub fn bind_column_references(
}
Ok(())
}
ast::Expr::Cast { expr, type_name: _ } => bind_column_references(expr, referenced_tables),
ast::Expr::Collate(expr, _string) => bind_column_references(expr, referenced_tables),
ast::Expr::FunctionCall {
Expr::Cast { expr, type_name: _ } => bind_column_references(expr, referenced_tables),
Expr::Collate(expr, _string) => bind_column_references(expr, referenced_tables),
Expr::FunctionCall {
name: _,
distinctness: _,
args,
@ -227,11 +224,11 @@ pub fn bind_column_references(
Ok(())
}
// Already bound earlier
ast::Expr::Column { .. } | ast::Expr::RowId { .. } => Ok(()),
ast::Expr::DoublyQualified(_, _, _) => todo!(),
ast::Expr::Exists(_) => todo!(),
ast::Expr::FunctionCallStar { .. } => Ok(()),
ast::Expr::InList { lhs, not: _, rhs } => {
Expr::Column { .. } | Expr::RowId { .. } => Ok(()),
Expr::DoublyQualified(_, _, _) => todo!(),
Expr::Exists(_) => todo!(),
Expr::FunctionCallStar { .. } => Ok(()),
Expr::InList { lhs, not: _, rhs } => {
bind_column_references(lhs, referenced_tables)?;
if let Some(rhs) = rhs {
for arg in rhs {
@ -240,36 +237,36 @@ pub fn bind_column_references(
}
Ok(())
}
ast::Expr::InSelect { .. } => todo!(),
ast::Expr::InTable { .. } => todo!(),
ast::Expr::IsNull(expr) => {
Expr::InSelect { .. } => todo!(),
Expr::InTable { .. } => todo!(),
Expr::IsNull(expr) => {
bind_column_references(expr, referenced_tables)?;
Ok(())
}
ast::Expr::Like { lhs, rhs, .. } => {
Expr::Like { lhs, rhs, .. } => {
bind_column_references(lhs, referenced_tables)?;
bind_column_references(rhs, referenced_tables)?;
Ok(())
}
ast::Expr::Literal(_) => Ok(()),
ast::Expr::Name(_) => todo!(),
ast::Expr::NotNull(expr) => {
Expr::Literal(_) => Ok(()),
Expr::Name(_) => todo!(),
Expr::NotNull(expr) => {
bind_column_references(expr, referenced_tables)?;
Ok(())
}
ast::Expr::Parenthesized(expr) => {
Expr::Parenthesized(expr) => {
for e in expr.iter_mut() {
bind_column_references(e, referenced_tables)?;
}
Ok(())
}
ast::Expr::Raise(_, _) => todo!(),
ast::Expr::Subquery(_) => todo!(),
ast::Expr::Unary(_, expr) => {
Expr::Raise(_, _) => todo!(),
Expr::Subquery(_) => todo!(),
Expr::Unary(_, expr) => {
bind_column_references(expr, referenced_tables)?;
Ok(())
}
ast::Expr::Variable(_) => Ok(()),
Expr::Variable(_) => Ok(()),
}
}
@ -413,7 +410,7 @@ struct JoinParseResult {
source_operator: SourceOperator,
is_outer_join: bool,
using: Option<ast::DistinctNames>,
predicates: Option<Vec<ast::Expr>>,
predicates: Option<Vec<Expr>>,
}
fn parse_join(
@ -457,22 +454,20 @@ fn parse_join(
assert!(!left_tables.is_empty());
let right_table = &tables[table_index];
let right_cols = &right_table.columns();
let mut distinct_names = None;
let mut distinct_names: Option<ast::DistinctNames> = None;
// TODO: O(n^2) maybe not great for large tables or big multiway joins
for right_col in right_cols.iter() {
let mut found_match = false;
for left_table in left_tables.iter() {
for left_col in left_table.columns().iter() {
if left_col.name == right_col.name {
if distinct_names.is_none() {
distinct_names =
Some(ast::DistinctNames::new(ast::Name(left_col.name.clone())));
} else {
if let Some(distinct_names) = distinct_names.as_mut() {
distinct_names
.as_mut()
.unwrap()
.insert(ast::Name(left_col.name.clone()))
.unwrap();
} else {
distinct_names =
Some(ast::DistinctNames::new(ast::Name(left_col.name.clone())));
}
found_match = true;
break;
@ -483,10 +478,11 @@ fn parse_join(
}
}
}
if distinct_names.is_none() {
if let Some(distinct_names) = distinct_names {
Some(ast::JoinConstraint::Using(distinct_names))
} else {
crate::bail_parse_error!("No columns found to NATURAL join on");
}
Some(ast::JoinConstraint::Using(distinct_names.unwrap()))
} else {
constraint
};
@ -540,15 +536,15 @@ fn parse_join(
}
let (left_table_idx, left_col_idx, left_col) = left_col.unwrap();
let (right_col_idx, right_col) = right_col.unwrap();
using_predicates.push(ast::Expr::Binary(
Box::new(ast::Expr::Column {
using_predicates.push(Expr::Binary(
Box::new(Expr::Column {
database: None,
table: left_table_idx,
column: left_col_idx,
is_rowid_alias: left_col.is_rowid_alias,
}),
ast::Operator::Equals,
Box::new(ast::Expr::Column {
Box::new(Expr::Column {
database: None,
table: right_table.table_index,
column: right_col_idx,
@ -586,12 +582,9 @@ pub fn parse_limit(limit: Limit) -> Option<usize> {
}
}
pub fn break_predicate_at_and_boundaries(
predicate: ast::Expr,
out_predicates: &mut Vec<ast::Expr>,
) {
pub fn break_predicate_at_and_boundaries(predicate: Expr, out_predicates: &mut Vec<Expr>) {
match predicate {
ast::Expr::Binary(left, ast::Operator::And, right) => {
Expr::Binary(left, ast::Operator::And, right) => {
break_predicate_at_and_boundaries(*left, out_predicates);
break_predicate_at_and_boundaries(*right, out_predicates);
}
@ -601,7 +594,7 @@ pub fn break_predicate_at_and_boundaries(
}
}
fn parse_row_id<F>(column_name: &str, table_id: usize, fn_check: F) -> Result<Option<ast::Expr>>
fn parse_row_id<F>(column_name: &str, table_id: usize, fn_check: F) -> Result<Option<Expr>>
where
F: FnOnce() -> bool,
{
@ -610,7 +603,7 @@ where
crate::bail_parse_error!("ROWID is ambiguous");
}
return Ok(Some(ast::Expr::RowId {
return Ok(Some(Expr::RowId {
database: None, // TODO: support different databases
table: table_id,
}));

View file

@ -69,10 +69,10 @@ pub fn prepare_select_plan(
let mut aggregate_expressions = Vec::new();
for (result_column_idx, column) in columns.iter_mut().enumerate() {
match column {
ast::ResultColumn::Star => {
ResultColumn::Star => {
plan.source.select_star(&mut plan.result_columns);
}
ast::ResultColumn::TableStar(name) => {
ResultColumn::TableStar(name) => {
let name_normalized = normalize_ident(name.0.as_str());
let referenced_table = plan
.referenced_tables
@ -96,7 +96,7 @@ pub fn prepare_select_plan(
});
}
}
ast::ResultColumn::Expr(ref mut expr, maybe_alias) => {
ResultColumn::Expr(ref mut expr, maybe_alias) => {
bind_column_references(expr, &plan.referenced_tables)?;
match expr {
ast::Expr::FunctionCall {

View file

@ -12,9 +12,9 @@ use super::{
/// Emit the subqueries contained in the FROM clause.
/// This is done first so the results can be read in the main query loop.
pub fn emit_subqueries<'a>(
pub fn emit_subqueries(
program: &mut ProgramBuilder,
t_ctx: &mut TranslateCtx<'a>,
t_ctx: &mut TranslateCtx,
referenced_tables: &mut [TableReference],
source: &mut SourceOperator,
) -> Result<()> {

View file

@ -146,13 +146,13 @@ impl OwnedValue {
let Some(text) = v.to_text() else {
return OwnedValue::Null;
};
OwnedValue::build_text(std::rc::Rc::new(text))
OwnedValue::build_text(Rc::new(text))
}
ExtValueType::Blob => {
let Some(blob) = v.to_blob() else {
return OwnedValue::Null;
};
OwnedValue::Blob(std::rc::Rc::new(blob))
OwnedValue::Blob(Rc::new(blob))
}
ExtValueType::Error => {
let Some(err) = v.to_error() else {
@ -241,7 +241,7 @@ impl PartialOrd<OwnedValue> for OwnedValue {
}
}
impl std::cmp::PartialOrd<AggContext> for AggContext {
impl PartialOrd<AggContext> for AggContext {
fn partial_cmp(&self, other: &AggContext) -> Option<std::cmp::Ordering> {
match (self, other) {
(Self::Avg(a, _), Self::Avg(b, _)) => a.partial_cmp(b),
@ -255,9 +255,9 @@ impl std::cmp::PartialOrd<AggContext> for AggContext {
}
}
impl std::cmp::Eq for OwnedValue {}
impl Eq for OwnedValue {}
impl std::cmp::Ord for OwnedValue {
impl Ord for OwnedValue {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.partial_cmp(other).unwrap()
}
@ -660,7 +660,7 @@ mod tests {
let header_length = record.values.len() + 1;
let header = &buf[0..header_length];
// First byte should be header size
assert!(header[0] == header_length as u8); // Header should be larger than number of values
assert_eq!(header[0], header_length as u8); // Header should be larger than number of values
// Check that correct serial types were chosen
assert_eq!(header[1] as u64, u64::from(SerialType::I8));

View file

@ -328,7 +328,7 @@ pub mod tests {
Add,
Box::new(Expr::Literal(Literal::Numeric("826".to_string()))),
);
assert!(super::exprs_are_equivalent(&expr1, &expr2));
assert!(exprs_are_equivalent(&expr1, &expr2));
}
#[test]
@ -343,7 +343,7 @@ pub mod tests {
Add,
Box::new(Expr::Literal(Literal::Numeric("123".to_string()))),
);
assert!(super::exprs_are_equivalent(&expr1, &expr2));
assert!(exprs_are_equivalent(&expr1, &expr2));
}
#[test]
@ -358,7 +358,7 @@ pub mod tests {
Subtract,
Box::new(Expr::Literal(Literal::Numeric("364".to_string()))),
);
assert!(!super::exprs_are_equivalent(&expr3, &expr4));
assert!(!exprs_are_equivalent(&expr3, &expr4));
}
#[test]
@ -373,7 +373,7 @@ pub mod tests {
Subtract,
Box::new(Expr::Literal(Literal::Numeric("22.0".to_string()))),
);
assert!(super::exprs_are_equivalent(&expr3, &expr4));
assert!(exprs_are_equivalent(&expr3, &expr4));
}
#[test]
@ -392,7 +392,7 @@ pub mod tests {
order_by: None,
filter_over: None,
};
assert!(super::exprs_are_equivalent(&func1, &func2));
assert!(exprs_are_equivalent(&func1, &func2));
let func3 = Expr::FunctionCall {
name: Id("SUM".to_string()),
@ -401,7 +401,7 @@ pub mod tests {
order_by: None,
filter_over: None,
};
assert!(!super::exprs_are_equivalent(&func1, &func3));
assert!(!exprs_are_equivalent(&func1, &func3));
}
#[test]
@ -420,7 +420,7 @@ pub mod tests {
order_by: None,
filter_over: None,
};
assert!(!super::exprs_are_equivalent(&sum, &sum_distinct));
assert!(!exprs_are_equivalent(&sum, &sum_distinct));
}
#[test]
@ -435,7 +435,7 @@ pub mod tests {
Multiply,
Box::new(Expr::Literal(Literal::Numeric("42".to_string()))),
);
assert!(super::exprs_are_equivalent(&expr1, &expr2));
assert!(exprs_are_equivalent(&expr1, &expr2));
}
#[test]
@ -450,7 +450,7 @@ pub mod tests {
Add,
Box::new(Expr::Literal(Literal::Numeric("683".to_string()))),
);
assert!(super::exprs_are_equivalent(&expr1, &expr2));
assert!(exprs_are_equivalent(&expr1, &expr2));
}
#[test]
fn test_expressions_parenthesized_equivalent() {
@ -464,7 +464,7 @@ pub mod tests {
Add,
Box::new(Expr::Literal(Literal::Numeric("7".to_string()))),
);
assert!(super::exprs_are_equivalent(&expr7, &expr8));
assert!(exprs_are_equivalent(&expr7, &expr8));
}
#[test]
@ -483,7 +483,7 @@ pub mod tests {
rhs: Box::new(Expr::Literal(Literal::String("%john%".to_string()))),
escape: Some(Box::new(Expr::Literal(Literal::String("\\".to_string())))),
};
assert!(super::exprs_are_equivalent(&expr1, &expr2));
assert!(exprs_are_equivalent(&expr1, &expr2));
}
#[test]
@ -502,7 +502,7 @@ pub mod tests {
rhs: Box::new(Expr::Literal(Literal::String("%john%".to_string()))),
escape: Some(Box::new(Expr::Literal(Literal::String("#".to_string())))),
};
assert!(!super::exprs_are_equivalent(&expr1, &expr2));
assert!(!exprs_are_equivalent(&expr1, &expr2));
}
#[test]
fn test_expressions_equivalent_between() {
@ -518,7 +518,7 @@ pub mod tests {
start: Box::new(Expr::Literal(Literal::Numeric("18".to_string()))),
end: Box::new(Expr::Literal(Literal::Numeric("65".to_string()))),
};
assert!(super::exprs_are_equivalent(&expr1, &expr2));
assert!(exprs_are_equivalent(&expr1, &expr2));
// differing BETWEEN bounds
let expr3 = Expr::Between {
@ -527,7 +527,7 @@ pub mod tests {
start: Box::new(Expr::Literal(Literal::Numeric("20".to_string()))),
end: Box::new(Expr::Literal(Literal::Numeric("65".to_string()))),
};
assert!(!super::exprs_are_equivalent(&expr1, &expr3));
assert!(!exprs_are_equivalent(&expr1, &expr3));
}
#[test]
fn test_cast_exprs_equivalent() {
@ -546,16 +546,16 @@ pub mod tests {
size: None,
}),
};
assert!(super::exprs_are_equivalent(&cast1, &cast2));
assert!(exprs_are_equivalent(&cast1, &cast2));
}
#[test]
fn test_ident_equivalency() {
assert!(super::check_ident_equivalency("\"foo\"", "foo"));
assert!(super::check_ident_equivalency("[foo]", "foo"));
assert!(super::check_ident_equivalency("`FOO`", "foo"));
assert!(super::check_ident_equivalency("\"foo\"", "`FOO`"));
assert!(!super::check_ident_equivalency("\"foo\"", "[bar]"));
assert!(!super::check_ident_equivalency("foo", "\"bar\""));
assert!(check_ident_equivalency("\"foo\"", "foo"));
assert!(check_ident_equivalency("[foo]", "foo"));
assert!(check_ident_equivalency("`FOO`", "foo"));
assert!(check_ident_equivalency("\"foo\"", "`FOO`"));
assert!(!check_ident_equivalency("\"foo\"", "[bar]"));
assert!(!check_ident_equivalency("foo", "\"bar\""));
}
}

View file

@ -83,7 +83,7 @@ impl ProgramBuilder {
let cursor = self.next_free_cursor_id;
self.next_free_cursor_id += 1;
self.cursor_ref.push((table_identifier, cursor_type));
assert!(self.cursor_ref.len() == self.next_free_cursor_id);
assert_eq!(self.cursor_ref.len(), self.next_free_cursor_id);
cursor
}

View file

@ -579,14 +579,14 @@ fn parse_modifier(modifier: &str) -> Result<Modifier> {
if parts[0].len() == digits_in_date {
let date = parse_modifier_date(parts[0])?;
Ok(Modifier::DateOffset {
years: sign * date.year() as i32,
years: sign * date.year(),
months: sign * date.month() as i32,
days: sign * date.day() as i32,
})
} else {
// time values are either 12, 8 or 5 digits
let time = parse_modifier_time(parts[0])?;
let time_delta = (sign * (time.num_seconds_from_midnight() as i32)) as i32;
let time_delta = sign * (time.num_seconds_from_midnight() as i32);
Ok(Modifier::TimeOffset(TimeDelta::seconds(time_delta.into())))
}
}
@ -596,7 +596,7 @@ fn parse_modifier(modifier: &str) -> Result<Modifier> {
// Convert time to total seconds (with sign)
let time_delta = sign * (time.num_seconds_from_midnight() as i32);
Ok(Modifier::DateTimeOffset {
years: sign * (date.year() as i32),
years: sign * (date.year()),
months: sign * (date.month() as i32),
days: sign * date.day() as i32,
seconds: time_delta,
@ -1401,7 +1401,7 @@ mod tests {
fn format(dt: NaiveDateTime) -> String {
dt.format("%Y-%m-%d %H:%M:%S").to_string()
}
fn weekday_sunday_based(dt: &chrono::NaiveDateTime) -> u32 {
fn weekday_sunday_based(dt: &NaiveDateTime) -> u32 {
dt.weekday().num_days_from_sunday()
}
@ -1438,8 +1438,7 @@ mod tests {
&[text("2023-06-15 12:30:45"), text("subsec")],
DateTimeOutput::Time,
);
let result =
chrono::NaiveTime::parse_from_str(&result.to_string(), "%H:%M:%S%.3f").unwrap();
let result = NaiveTime::parse_from_str(&result.to_string(), "%H:%M:%S%.3f").unwrap();
assert_eq!(time.time(), result);
}
@ -1537,8 +1536,7 @@ mod tests {
DateTimeOutput::DateTime,
);
let result =
chrono::NaiveDateTime::parse_from_str(&result.to_string(), "%Y-%m-%d %H:%M:%S%.3f")
.unwrap();
NaiveDateTime::parse_from_str(&result.to_string(), "%Y-%m-%d %H:%M:%S%.3f").unwrap();
assert_eq!(expected, result);
}

View file

@ -10,7 +10,7 @@ pub fn construct_like_escape_arg(escape_value: &OwnedValue) -> Result<char, Limb
let mut escape_chars = text.value.chars();
match (escape_chars.next(), escape_chars.next()) {
(Some(escape), None) => Ok(escape),
_ => Result::Err(LimboError::Constraint(
_ => Err(LimboError::Constraint(
"ESCAPE expression must be a single character".to_string(),
)),
}
@ -143,7 +143,7 @@ fn construct_glob_regex(pattern: &str) -> Result<Regex, LimboError> {
}
};
while let Some(next_ch) = chars.next() {
for next_ch in chars.by_ref() {
match next_ch {
']' => {
bracket_closed = true;
@ -175,7 +175,7 @@ fn construct_glob_regex(pattern: &str) -> Result<Regex, LimboError> {
if bracket_closed {
Ok(Regex::new(&regex_pattern).unwrap())
} else {
Result::Err(LimboError::Constraint(
Err(LimboError::Constraint(
"blob pattern is not closed".to_string(),
))
}

View file

@ -889,26 +889,26 @@ impl Program {
}
}
log::trace!("Halt auto_commit {}", self.auto_commit);
if self.auto_commit {
return match pager.end_tx() {
return if self.auto_commit {
match pager.end_tx() {
Ok(crate::storage::wal::CheckpointStatus::IO) => Ok(StepResult::IO),
Ok(crate::storage::wal::CheckpointStatus::Done) => Ok(StepResult::Done),
Err(e) => Err(e),
};
}
} else {
return Ok(StepResult::Done);
}
Ok(StepResult::Done)
};
}
Insn::Transaction { write } => {
let connection = self.connection.upgrade().unwrap();
let current_state = connection.transaction_state.borrow().clone();
let (new_transaction_state, updated) = match (&current_state, write) {
(crate::TransactionState::Write, true) => (TransactionState::Write, false),
(crate::TransactionState::Write, false) => (TransactionState::Write, false),
(crate::TransactionState::Read, true) => (TransactionState::Write, true),
(crate::TransactionState::Read, false) => (TransactionState::Read, false),
(crate::TransactionState::None, true) => (TransactionState::Write, true),
(crate::TransactionState::None, false) => (TransactionState::Read, true),
(TransactionState::Write, true) => (TransactionState::Write, false),
(TransactionState::Write, false) => (TransactionState::Write, false),
(TransactionState::Read, true) => (TransactionState::Write, true),
(TransactionState::Read, false) => (TransactionState::Read, false),
(TransactionState::None, true) => (TransactionState::Write, true),
(TransactionState::None, false) => (TransactionState::Read, true),
};
if updated && matches!(current_state, TransactionState::None) {
@ -1481,7 +1481,7 @@ impl Program {
_ => unreachable!(),
})
.collect();
let cursor = sorter::Sorter::new(order);
let cursor = Sorter::new(order);
sorter_cursors.insert(*cursor_id, cursor);
state.pc += 1;
}
@ -1630,7 +1630,7 @@ impl Program {
},
crate::function::Func::Scalar(scalar_func) => match scalar_func {
ScalarFunc::Cast => {
assert!(arg_count == 2);
assert_eq!(arg_count, 2);
assert!(*start_reg + 1 < state.registers.len());
let reg_value_argument = state.registers[*start_reg].clone();
let OwnedValue::Text(reg_value_type) =
@ -1715,7 +1715,7 @@ impl Program {
&state.registers[*start_reg + 2],
) {
Ok(x) => x,
Err(e) => return Result::Err(e),
Err(e) => return Err(e),
};
OwnedValue::Integer(exec_like_with_escape(
@ -1920,7 +1920,7 @@ impl Program {
state.registers[*dest] = OwnedValue::build_text(Rc::new(version));
}
ScalarFunc::Replace => {
assert!(arg_count == 3);
assert_eq!(arg_count, 3);
let source = &state.registers[*start_reg];
let pattern = &state.registers[*start_reg + 1];
let replacement = &state.registers[*start_reg + 2];
@ -2326,7 +2326,7 @@ fn trace_insn(program: &Program, addr: InsnReference, insn: &Insn) {
addr,
insn,
String::new(),
program.comments.get(&(addr as u32)).copied()
program.comments.get(&{ addr }).copied()
)
);
}
@ -2337,7 +2337,7 @@ fn print_insn(program: &Program, addr: InsnReference, insn: &Insn, indent: Strin
addr,
insn,
indent,
program.comments.get(&(addr as u32)).copied(),
program.comments.get(&{ addr }).copied(),
);
println!("{}", s);
}
@ -3195,10 +3195,7 @@ fn checked_cast_text_to_numeric(text: &str) -> std::result::Result<OwnedValue, (
// try casting to numeric if not possible return integer 0
fn cast_text_to_numeric(text: &str) -> OwnedValue {
match checked_cast_text_to_numeric(text) {
Ok(value) => value,
Err(_) => OwnedValue::Integer(0),
}
checked_cast_text_to_numeric(text).unwrap_or(OwnedValue::Integer(0))
}
// Check if float can be losslessly converted to 51-bit integer

View file

@ -35,7 +35,7 @@ pub trait ArbitraryFrom<T> {
pub(crate) fn frequency<
'a,
T,
R: rand::Rng,
R: Rng,
N: Sum + PartialOrd + Copy + Default + SampleUniform + SubAssign,
>(
choices: Vec<(N, Box<dyn Fn(&mut R) -> T + 'a>)>,
@ -55,23 +55,20 @@ pub(crate) fn frequency<
}
/// one_of is a helper function for composing different generators with equal probability of occurence.
pub(crate) fn one_of<'a, T, R: rand::Rng>(
choices: Vec<Box<dyn Fn(&mut R) -> T + 'a>>,
rng: &mut R,
) -> T {
pub(crate) fn one_of<'a, T, R: Rng>(choices: Vec<Box<dyn Fn(&mut R) -> T + 'a>>, rng: &mut R) -> T {
let index = rng.gen_range(0..choices.len());
choices[index](rng)
}
/// pick is a helper function for uniformly picking a random element from a slice
pub(crate) fn pick<'a, T, R: rand::Rng>(choices: &'a [T], rng: &mut R) -> &'a T {
pub(crate) fn pick<'a, T, R: Rng>(choices: &'a [T], rng: &mut R) -> &'a T {
let index = rng.gen_range(0..choices.len());
&choices[index]
}
/// pick_index is typically used for picking an index from a slice to later refer to the element
/// at that index.
pub(crate) fn pick_index<R: rand::Rng>(choices: usize, rng: &mut R) -> usize {
pub(crate) fn pick_index<R: Rng>(choices: usize, rng: &mut R) -> usize {
rng.gen_range(0..choices)
}

View file

@ -330,7 +330,7 @@ impl Interaction {
);
return Err(err.unwrap());
}
let rows = rows.unwrap();
let rows = rows?;
assert!(rows.is_some());
let mut rows = rows.unwrap();
let mut out = Vec::new();

View file

@ -44,7 +44,7 @@ impl Arbitrary for ColumnType {
}
impl ArbitraryFrom<&Table> for Vec<Value> {
fn arbitrary_from<R: rand::Rng>(rng: &mut R, table: &Table) -> Self {
fn arbitrary_from<R: Rng>(rng: &mut R, table: &Table) -> Self {
let mut row = Vec::new();
for column in table.columns.iter() {
let value = Value::arbitrary_from(rng, &column.column_type);

View file

@ -64,10 +64,7 @@ fn main() -> Result<(), String> {
let cli_opts = SimulatorCLI::parse();
cli_opts.validate()?;
let seed = match cli_opts.seed {
Some(seed) => seed,
None => rand::thread_rng().next_u64(),
};
let seed = cli_opts.seed.unwrap_or_else(|| thread_rng().next_u64());
let output_dir = match &cli_opts.output_dir {
Some(dir) => Path::new(dir).to_path_buf(),

View file

@ -3,7 +3,7 @@ use std::sync::{Arc, Mutex};
use limbo_core::{LimboError, Result};
use crate::generation::{
self, pick_index,
pick_index,
plan::{Interaction, InteractionPlan, InteractionPlanState, ResultSet},
};
@ -45,7 +45,7 @@ impl ExecutionHistory {
pub(crate) struct ExecutionResult {
pub(crate) history: ExecutionHistory,
pub(crate) error: Option<limbo_core::LimboError>,
pub(crate) error: Option<LimboError>,
}
impl ExecutionResult {
@ -87,7 +87,7 @@ pub(crate) fn execute_plans(
if now.elapsed().as_secs() >= env.opts.max_time_simulation as u64 {
return ExecutionResult::new(
history,
Some(limbo_core::LimboError::InternalError(
Some(LimboError::InternalError(
"maximum time for simulation reached".into(),
)),
);
@ -170,7 +170,7 @@ fn execute_interaction(
) -> Result<ExecutionContinuation> {
log::info!("executing: {}", interaction);
match interaction {
generation::plan::Interaction::Query(_) => {
Interaction::Query(_) => {
let conn = match &mut env.connections[connection_index] {
SimConnection::Connected(conn) => conn,
SimConnection::Disconnected => unreachable!(),
@ -181,11 +181,11 @@ fn execute_interaction(
log::debug!("{:?}", results);
stack.push(results);
}
generation::plan::Interaction::Assertion(_) => {
Interaction::Assertion(_) => {
interaction.execute_assertion(stack, env)?;
stack.clear();
}
generation::plan::Interaction::Assumption(_) => {
Interaction::Assumption(_) => {
let assumption_result = interaction.execute_assumption(stack, env);
stack.clear();

View file

@ -55,7 +55,7 @@ impl SimulatorFile {
}
}
impl limbo_core::File for SimulatorFile {
impl File for SimulatorFile {
fn lock_file(&self, exclusive: bool) -> Result<()> {
if *self.fault.borrow() {
return Err(limbo_core::LimboError::InternalError(
@ -88,7 +88,7 @@ impl limbo_core::File for SimulatorFile {
fn pwrite(
&self,
pos: usize,
buffer: Rc<std::cell::RefCell<limbo_core::Buffer>>,
buffer: Rc<RefCell<limbo_core::Buffer>>,
c: Rc<limbo_core::Completion>,
) -> Result<()> {
*self.nr_pwrite_calls.borrow_mut() += 1;

View file

@ -78,7 +78,7 @@ impl IO for SimulatorIO {
"Injected fault".into(),
));
}
self.inner.run_once().unwrap();
self.inner.run_once()?;
Ok(())
}

View file

@ -46,7 +46,7 @@ pub struct sqlite3 {
pub(crate) err_mask: ffi::c_int,
pub(crate) malloc_failed: bool,
pub(crate) e_open_state: u8,
pub(crate) p_err: *mut std::ffi::c_void,
pub(crate) p_err: *mut ffi::c_void,
}
impl sqlite3 {
@ -107,7 +107,7 @@ pub unsafe extern "C" fn sqlite3_open(
if db_out.is_null() {
return SQLITE_MISUSE;
}
let filename = ffi::CStr::from_ptr(filename);
let filename = CStr::from_ptr(filename);
let filename = match filename.to_str() {
Ok(s) => s,
Err(_) => return SQLITE_MISUSE,
@ -164,14 +164,9 @@ pub unsafe extern "C" fn sqlite3_trace_v2(
_db: *mut sqlite3,
_mask: ffi::c_uint,
_callback: Option<
unsafe extern "C" fn(
ffi::c_uint,
*mut std::ffi::c_void,
*mut std::ffi::c_void,
*mut std::ffi::c_void,
),
unsafe extern "C" fn(ffi::c_uint, *mut ffi::c_void, *mut ffi::c_void, *mut ffi::c_void),
>,
_context: *mut std::ffi::c_void,
_context: *mut ffi::c_void,
) -> ffi::c_int {
stub!();
}
@ -181,7 +176,7 @@ pub unsafe extern "C" fn sqlite3_progress_handler(
_db: *mut sqlite3,
_n: ffi::c_int,
_callback: Option<unsafe extern "C" fn() -> ffi::c_int>,
_context: *mut std::ffi::c_void,
_context: *mut ffi::c_void,
) -> ffi::c_int {
stub!();
}
@ -195,15 +190,13 @@ pub unsafe extern "C" fn sqlite3_busy_timeout(_db: *mut sqlite3, _ms: ffi::c_int
pub unsafe extern "C" fn sqlite3_set_authorizer(
_db: *mut sqlite3,
_callback: Option<unsafe extern "C" fn() -> ffi::c_int>,
_context: *mut std::ffi::c_void,
_context: *mut ffi::c_void,
) -> ffi::c_int {
stub!();
}
#[no_mangle]
pub unsafe extern "C" fn sqlite3_context_db_handle(
_context: *mut std::ffi::c_void,
) -> *mut std::ffi::c_void {
pub unsafe extern "C" fn sqlite3_context_db_handle(_context: *mut ffi::c_void) -> *mut ffi::c_void {
stub!();
}
@ -219,7 +212,7 @@ pub unsafe extern "C" fn sqlite3_prepare_v2(
return SQLITE_MISUSE;
}
let db: &mut sqlite3 = &mut *db;
let sql = ffi::CStr::from_ptr(sql);
let sql = CStr::from_ptr(sql);
let sql = match sql.to_str() {
Ok(s) => s,
Err(_) => return SQLITE_MISUSE,
@ -242,7 +235,7 @@ pub unsafe extern "C" fn sqlite3_finalize(stmt: *mut sqlite3_stmt) -> ffi::c_int
}
#[no_mangle]
pub unsafe extern "C" fn sqlite3_step(stmt: *mut sqlite3_stmt) -> std::ffi::c_int {
pub unsafe extern "C" fn sqlite3_step(stmt: *mut sqlite3_stmt) -> ffi::c_int {
let stmt = &mut *stmt;
if let Ok(result) = stmt.stmt.step() {
match result {
@ -262,10 +255,10 @@ pub unsafe extern "C" fn sqlite3_step(stmt: *mut sqlite3_stmt) -> std::ffi::c_in
type exec_callback = Option<
unsafe extern "C" fn(
context: *mut std::ffi::c_void,
n_column: std::ffi::c_int,
argv: *mut *mut std::ffi::c_char,
colv: *mut *mut std::ffi::c_char,
context: *mut ffi::c_void,
n_column: ffi::c_int,
argv: *mut *mut ffi::c_char,
colv: *mut *mut ffi::c_char,
) -> ffi::c_int,
>;
@ -274,14 +267,14 @@ pub unsafe extern "C" fn sqlite3_exec(
db: *mut sqlite3,
sql: *const ffi::c_char,
_callback: exec_callback,
_context: *mut std::ffi::c_void,
_err: *mut *mut std::ffi::c_char,
_context: *mut ffi::c_void,
_err: *mut *mut ffi::c_char,
) -> ffi::c_int {
if db.is_null() || sql.is_null() {
return SQLITE_MISUSE;
}
let db: &mut sqlite3 = &mut *db;
let sql = ffi::CStr::from_ptr(sql);
let sql = CStr::from_ptr(sql);
let sql = match sql.to_str() {
Ok(s) => s,
Err(_) => return SQLITE_MISUSE,
@ -317,8 +310,8 @@ pub unsafe extern "C" fn sqlite3_stmt_busy(_stmt: *mut sqlite3_stmt) -> ffi::c_i
#[no_mangle]
pub unsafe extern "C" fn sqlite3_serialize(
_db: *mut sqlite3,
_schema: *const std::ffi::c_char,
_out: *mut *mut std::ffi::c_void,
_schema: *const ffi::c_char,
_out: *mut *mut ffi::c_void,
_out_bytes: *mut ffi::c_int,
_flags: ffi::c_uint,
) -> ffi::c_int {
@ -328,8 +321,8 @@ pub unsafe extern "C" fn sqlite3_serialize(
#[no_mangle]
pub unsafe extern "C" fn sqlite3_deserialize(
_db: *mut sqlite3,
_schema: *const std::ffi::c_char,
_in_: *const std::ffi::c_void,
_schema: *const ffi::c_char,
_in_: *const ffi::c_void,
_in_bytes: ffi::c_int,
_flags: ffi::c_uint,
) -> ffi::c_int {
@ -381,12 +374,12 @@ pub unsafe extern "C" fn sqlite3_limit(
}
#[no_mangle]
pub unsafe extern "C" fn sqlite3_malloc64(_n: ffi::c_int) -> *mut std::ffi::c_void {
pub unsafe extern "C" fn sqlite3_malloc64(_n: ffi::c_int) -> *mut ffi::c_void {
stub!();
}
#[no_mangle]
pub unsafe extern "C" fn sqlite3_free(_ptr: *mut std::ffi::c_void) {
pub unsafe extern "C" fn sqlite3_free(_ptr: *mut ffi::c_void) {
stub!();
}
@ -404,52 +397,50 @@ pub unsafe extern "C" fn sqlite3_errcode(_db: *mut sqlite3) -> ffi::c_int {
}
#[no_mangle]
pub unsafe extern "C" fn sqlite3_errstr(_err: ffi::c_int) -> *const std::ffi::c_char {
pub unsafe extern "C" fn sqlite3_errstr(_err: ffi::c_int) -> *const ffi::c_char {
sqlite3_errstr_impl(_err)
}
#[no_mangle]
pub unsafe extern "C" fn sqlite3_user_data(
_context: *mut std::ffi::c_void,
) -> *mut std::ffi::c_void {
pub unsafe extern "C" fn sqlite3_user_data(_context: *mut ffi::c_void) -> *mut ffi::c_void {
stub!();
}
#[no_mangle]
pub unsafe extern "C" fn sqlite3_backup_init(
_dest_db: *mut sqlite3,
_dest_name: *const std::ffi::c_char,
_dest_name: *const ffi::c_char,
_source_db: *mut sqlite3,
_source_name: *const std::ffi::c_char,
) -> *mut std::ffi::c_void {
_source_name: *const ffi::c_char,
) -> *mut ffi::c_void {
stub!();
}
#[no_mangle]
pub unsafe extern "C" fn sqlite3_backup_step(
_backup: *mut std::ffi::c_void,
_backup: *mut ffi::c_void,
_n_pages: ffi::c_int,
) -> ffi::c_int {
stub!();
}
#[no_mangle]
pub unsafe extern "C" fn sqlite3_backup_remaining(_backup: *mut std::ffi::c_void) -> ffi::c_int {
pub unsafe extern "C" fn sqlite3_backup_remaining(_backup: *mut ffi::c_void) -> ffi::c_int {
stub!();
}
#[no_mangle]
pub unsafe extern "C" fn sqlite3_backup_pagecount(_backup: *mut std::ffi::c_void) -> ffi::c_int {
pub unsafe extern "C" fn sqlite3_backup_pagecount(_backup: *mut ffi::c_void) -> ffi::c_int {
stub!();
}
#[no_mangle]
pub unsafe extern "C" fn sqlite3_backup_finish(_backup: *mut std::ffi::c_void) -> ffi::c_int {
pub unsafe extern "C" fn sqlite3_backup_finish(_backup: *mut ffi::c_void) -> ffi::c_int {
stub!();
}
#[no_mangle]
pub unsafe extern "C" fn sqlite3_expanded_sql(_stmt: *mut sqlite3_stmt) -> *mut std::ffi::c_char {
pub unsafe extern "C" fn sqlite3_expanded_sql(_stmt: *mut sqlite3_stmt) -> *mut ffi::c_char {
stub!();
}
@ -473,7 +464,7 @@ pub unsafe extern "C" fn sqlite3_bind_parameter_count(_stmt: *mut sqlite3_stmt)
pub unsafe extern "C" fn sqlite3_bind_parameter_name(
_stmt: *mut sqlite3_stmt,
_idx: ffi::c_int,
) -> *const std::ffi::c_char {
) -> *const ffi::c_char {
stub!();
}
@ -507,9 +498,9 @@ pub unsafe extern "C" fn sqlite3_bind_double(
pub unsafe extern "C" fn sqlite3_bind_text(
_stmt: *mut sqlite3_stmt,
_idx: ffi::c_int,
_text: *const std::ffi::c_char,
_text: *const ffi::c_char,
_len: ffi::c_int,
_destroy: *mut std::ffi::c_void,
_destroy: *mut ffi::c_void,
) -> ffi::c_int {
stub!();
}
@ -518,9 +509,9 @@ pub unsafe extern "C" fn sqlite3_bind_text(
pub unsafe extern "C" fn sqlite3_bind_blob(
_stmt: *mut sqlite3_stmt,
_idx: ffi::c_int,
_blob: *const std::ffi::c_void,
_blob: *const ffi::c_void,
_len: ffi::c_int,
_destroy: *mut std::ffi::c_void,
_destroy: *mut ffi::c_void,
) -> ffi::c_int {
stub!();
}
@ -542,7 +533,7 @@ pub unsafe extern "C" fn sqlite3_column_count(_stmt: *mut sqlite3_stmt) -> ffi::
pub unsafe extern "C" fn sqlite3_column_decltype(
_stmt: *mut sqlite3_stmt,
_idx: ffi::c_int,
) -> *const std::ffi::c_char {
) -> *const ffi::c_char {
stub!();
}
@ -550,7 +541,7 @@ pub unsafe extern "C" fn sqlite3_column_decltype(
pub unsafe extern "C" fn sqlite3_column_name(
_stmt: *mut sqlite3_stmt,
_idx: ffi::c_int,
) -> *const std::ffi::c_char {
) -> *const ffi::c_char {
stub!();
}
@ -568,7 +559,7 @@ pub unsafe extern "C" fn sqlite3_column_double(_stmt: *mut sqlite3_stmt, _idx: f
pub unsafe extern "C" fn sqlite3_column_blob(
_stmt: *mut sqlite3_stmt,
_idx: ffi::c_int,
) -> *const std::ffi::c_void {
) -> *const ffi::c_void {
stub!();
}
@ -581,7 +572,7 @@ pub unsafe extern "C" fn sqlite3_column_bytes(
}
#[no_mangle]
pub unsafe extern "C" fn sqlite3_value_type(value: *mut std::ffi::c_void) -> ffi::c_int {
pub unsafe extern "C" fn sqlite3_value_type(value: *mut ffi::c_void) -> ffi::c_int {
let value = value as *mut limbo_core::Value;
let value = &*value;
match value {
@ -594,7 +585,7 @@ pub unsafe extern "C" fn sqlite3_value_type(value: *mut std::ffi::c_void) -> ffi
}
#[no_mangle]
pub unsafe extern "C" fn sqlite3_value_int64(value: *mut std::ffi::c_void) -> i64 {
pub unsafe extern "C" fn sqlite3_value_int64(value: *mut ffi::c_void) -> i64 {
let value = value as *mut limbo_core::Value;
let value = &*value;
match value {
@ -604,7 +595,7 @@ pub unsafe extern "C" fn sqlite3_value_int64(value: *mut std::ffi::c_void) -> i6
}
#[no_mangle]
pub unsafe extern "C" fn sqlite3_value_double(value: *mut std::ffi::c_void) -> f64 {
pub unsafe extern "C" fn sqlite3_value_double(value: *mut ffi::c_void) -> f64 {
let value = value as *mut limbo_core::Value;
let value = &*value;
match value {
@ -614,9 +605,7 @@ pub unsafe extern "C" fn sqlite3_value_double(value: *mut std::ffi::c_void) -> f
}
#[no_mangle]
pub unsafe extern "C" fn sqlite3_value_text(
value: *mut std::ffi::c_void,
) -> *const std::ffi::c_uchar {
pub unsafe extern "C" fn sqlite3_value_text(value: *mut ffi::c_void) -> *const ffi::c_uchar {
let value = value as *mut limbo_core::Value;
let value = &*value;
match value {
@ -626,19 +615,17 @@ pub unsafe extern "C" fn sqlite3_value_text(
}
#[no_mangle]
pub unsafe extern "C" fn sqlite3_value_blob(
value: *mut std::ffi::c_void,
) -> *const std::ffi::c_void {
pub unsafe extern "C" fn sqlite3_value_blob(value: *mut ffi::c_void) -> *const ffi::c_void {
let value = value as *mut limbo_core::Value;
let value = &*value;
match value {
limbo_core::Value::Blob(blob) => blob.as_ptr() as *const std::ffi::c_void,
limbo_core::Value::Blob(blob) => blob.as_ptr() as *const ffi::c_void,
_ => std::ptr::null(),
}
}
#[no_mangle]
pub unsafe extern "C" fn sqlite3_value_bytes(value: *mut std::ffi::c_void) -> ffi::c_int {
pub unsafe extern "C" fn sqlite3_value_bytes(value: *mut ffi::c_void) -> ffi::c_int {
let value = value as *mut limbo_core::Value;
let value = &*value;
match value {
@ -650,8 +637,8 @@ pub unsafe extern "C" fn sqlite3_value_bytes(value: *mut std::ffi::c_void) -> ff
#[no_mangle]
pub unsafe extern "C" fn sqlite3_column_text(
stmt: *mut sqlite3_stmt,
idx: std::ffi::c_int,
) -> *const std::ffi::c_uchar {
idx: ffi::c_int,
) -> *const ffi::c_uchar {
let stmt = &mut *stmt;
let row = stmt.row.borrow();
let row = match row.as_ref() {
@ -665,11 +652,11 @@ pub unsafe extern "C" fn sqlite3_column_text(
}
pub struct TabResult {
az_result: Vec<*mut std::ffi::c_char>,
az_result: Vec<*mut ffi::c_char>,
n_row: usize,
n_column: usize,
z_err_msg: Option<CString>,
rc: std::ffi::c_int,
rc: ffi::c_int,
}
impl TabResult {
@ -697,11 +684,11 @@ impl TabResult {
#[no_mangle]
unsafe extern "C" fn sqlite_get_table_cb(
context: *mut std::ffi::c_void,
n_column: std::ffi::c_int,
argv: *mut *mut std::ffi::c_char,
colv: *mut *mut std::ffi::c_char,
) -> std::ffi::c_int {
context: *mut ffi::c_void,
n_column: ffi::c_int,
argv: *mut *mut ffi::c_char,
colv: *mut *mut ffi::c_char,
) -> ffi::c_int {
let res = &mut *(context as *mut TabResult);
if res.n_row == 0 {
@ -729,7 +716,7 @@ unsafe extern "C" fn sqlite_get_table_cb(
let value_cstring = if !value.is_null() {
let len = libc::strlen(value);
let mut buf = Vec::with_capacity(len + 1);
libc::strncpy(buf.as_mut_ptr() as *mut std::ffi::c_char, value, len);
libc::strncpy(buf.as_mut_ptr() as *mut ffi::c_char, value, len);
buf.set_len(len + 1);
CString::from_vec_with_nul(buf).unwrap()
} else {
@ -745,12 +732,12 @@ unsafe extern "C" fn sqlite_get_table_cb(
#[no_mangle]
pub unsafe extern "C" fn sqlite3_get_table(
db: *mut sqlite3,
sql: *const std::ffi::c_char,
paz_result: *mut *mut *mut std::ffi::c_char,
pn_row: *mut std::ffi::c_int,
pn_column: *mut std::ffi::c_int,
pz_err_msg: *mut *mut std::ffi::c_char,
) -> std::ffi::c_int {
sql: *const ffi::c_char,
paz_result: *mut *mut *mut ffi::c_char,
pn_row: *mut ffi::c_int,
pn_column: *mut ffi::c_int,
pz_err_msg: *mut *mut ffi::c_char,
) -> ffi::c_int {
if db.is_null() || sql.is_null() || paz_result.is_null() {
return SQLITE_ERROR;
}
@ -781,8 +768,8 @@ pub unsafe extern "C" fn sqlite3_get_table(
}
*paz_result = res.az_result.as_mut_ptr();
*pn_row = res.n_row as std::ffi::c_int;
*pn_column = res.n_column as std::ffi::c_int;
*pn_row = res.n_row as ffi::c_int;
*pn_column = res.n_column as ffi::c_int;
std::mem::forget(res);
@ -790,60 +777,60 @@ pub unsafe extern "C" fn sqlite3_get_table(
}
#[no_mangle]
pub unsafe extern "C" fn sqlite3_free_table(paz_result: *mut *mut *mut std::ffi::c_char) {
pub unsafe extern "C" fn sqlite3_free_table(paz_result: *mut *mut *mut ffi::c_char) {
let res = &mut *(paz_result as *mut TabResult);
res.free();
}
#[no_mangle]
pub unsafe extern "C" fn sqlite3_result_null(_context: *mut std::ffi::c_void) {
pub unsafe extern "C" fn sqlite3_result_null(_context: *mut ffi::c_void) {
stub!();
}
#[no_mangle]
pub unsafe extern "C" fn sqlite3_result_int64(_context: *mut std::ffi::c_void, _val: i64) {
pub unsafe extern "C" fn sqlite3_result_int64(_context: *mut ffi::c_void, _val: i64) {
stub!();
}
#[no_mangle]
pub unsafe extern "C" fn sqlite3_result_double(_context: *mut std::ffi::c_void, _val: f64) {
pub unsafe extern "C" fn sqlite3_result_double(_context: *mut ffi::c_void, _val: f64) {
stub!();
}
#[no_mangle]
pub unsafe extern "C" fn sqlite3_result_text(
_context: *mut std::ffi::c_void,
_text: *const std::ffi::c_char,
_context: *mut ffi::c_void,
_text: *const ffi::c_char,
_len: ffi::c_int,
_destroy: *mut std::ffi::c_void,
_destroy: *mut ffi::c_void,
) {
stub!();
}
#[no_mangle]
pub unsafe extern "C" fn sqlite3_result_blob(
_context: *mut std::ffi::c_void,
_blob: *const std::ffi::c_void,
_context: *mut ffi::c_void,
_blob: *const ffi::c_void,
_len: ffi::c_int,
_destroy: *mut std::ffi::c_void,
_destroy: *mut ffi::c_void,
) {
stub!();
}
#[no_mangle]
pub unsafe extern "C" fn sqlite3_result_error_nomem(_context: *mut std::ffi::c_void) {
pub unsafe extern "C" fn sqlite3_result_error_nomem(_context: *mut ffi::c_void) {
stub!();
}
#[no_mangle]
pub unsafe extern "C" fn sqlite3_result_error_toobig(_context: *mut std::ffi::c_void) {
pub unsafe extern "C" fn sqlite3_result_error_toobig(_context: *mut ffi::c_void) {
stub!();
}
#[no_mangle]
pub unsafe extern "C" fn sqlite3_result_error(
_context: *mut std::ffi::c_void,
_err: *const std::ffi::c_char,
_context: *mut ffi::c_void,
_err: *const ffi::c_char,
_len: ffi::c_int,
) {
stub!();
@ -851,29 +838,29 @@ pub unsafe extern "C" fn sqlite3_result_error(
#[no_mangle]
pub unsafe extern "C" fn sqlite3_aggregate_context(
_context: *mut std::ffi::c_void,
_context: *mut ffi::c_void,
_n: ffi::c_int,
) -> *mut std::ffi::c_void {
) -> *mut ffi::c_void {
stub!();
}
#[no_mangle]
pub unsafe extern "C" fn sqlite3_blob_open(
_db: *mut sqlite3,
_db_name: *const std::ffi::c_char,
_table_name: *const std::ffi::c_char,
_column_name: *const std::ffi::c_char,
_db_name: *const ffi::c_char,
_table_name: *const ffi::c_char,
_column_name: *const ffi::c_char,
_rowid: i64,
_flags: ffi::c_int,
_blob_out: *mut *mut std::ffi::c_void,
_blob_out: *mut *mut ffi::c_void,
) -> ffi::c_int {
stub!();
}
#[no_mangle]
pub unsafe extern "C" fn sqlite3_blob_read(
_blob: *mut std::ffi::c_void,
_data: *mut std::ffi::c_void,
_blob: *mut ffi::c_void,
_data: *mut ffi::c_void,
_n: ffi::c_int,
_offset: ffi::c_int,
) -> ffi::c_int {
@ -882,8 +869,8 @@ pub unsafe extern "C" fn sqlite3_blob_read(
#[no_mangle]
pub unsafe extern "C" fn sqlite3_blob_write(
_blob: *mut std::ffi::c_void,
_data: *const std::ffi::c_void,
_blob: *mut ffi::c_void,
_data: *const ffi::c_void,
_n: ffi::c_int,
_offset: ffi::c_int,
) -> ffi::c_int {
@ -891,19 +878,19 @@ pub unsafe extern "C" fn sqlite3_blob_write(
}
#[no_mangle]
pub unsafe extern "C" fn sqlite3_blob_bytes(_blob: *mut std::ffi::c_void) -> ffi::c_int {
pub unsafe extern "C" fn sqlite3_blob_bytes(_blob: *mut ffi::c_void) -> ffi::c_int {
stub!();
}
#[no_mangle]
pub unsafe extern "C" fn sqlite3_blob_close(_blob: *mut std::ffi::c_void) -> ffi::c_int {
pub unsafe extern "C" fn sqlite3_blob_close(_blob: *mut ffi::c_void) -> ffi::c_int {
stub!();
}
#[no_mangle]
pub unsafe extern "C" fn sqlite3_stricmp(
_a: *const std::ffi::c_char,
_b: *const std::ffi::c_char,
_a: *const ffi::c_char,
_b: *const ffi::c_char,
) -> ffi::c_int {
stub!();
}
@ -911,9 +898,9 @@ pub unsafe extern "C" fn sqlite3_stricmp(
#[no_mangle]
pub unsafe extern "C" fn sqlite3_create_collation_v2(
_db: *mut sqlite3,
_name: *const std::ffi::c_char,
_name: *const ffi::c_char,
_enc: ffi::c_int,
_context: *mut std::ffi::c_void,
_context: *mut ffi::c_void,
_cmp: Option<unsafe extern "C" fn() -> ffi::c_int>,
_destroy: Option<unsafe extern "C" fn()>,
) -> ffi::c_int {
@ -923,10 +910,10 @@ pub unsafe extern "C" fn sqlite3_create_collation_v2(
#[no_mangle]
pub unsafe extern "C" fn sqlite3_create_function_v2(
_db: *mut sqlite3,
_name: *const std::ffi::c_char,
_name: *const ffi::c_char,
_n_args: ffi::c_int,
_enc: ffi::c_int,
_context: *mut std::ffi::c_void,
_context: *mut ffi::c_void,
_func: Option<unsafe extern "C" fn()>,
_step: Option<unsafe extern "C" fn()>,
_final_: Option<unsafe extern "C" fn()>,
@ -938,10 +925,10 @@ pub unsafe extern "C" fn sqlite3_create_function_v2(
#[no_mangle]
pub unsafe extern "C" fn sqlite3_create_window_function(
_db: *mut sqlite3,
_name: *const std::ffi::c_char,
_name: *const ffi::c_char,
_n_args: ffi::c_int,
_enc: ffi::c_int,
_context: *mut std::ffi::c_void,
_context: *mut ffi::c_void,
_x_step: Option<unsafe extern "C" fn()>,
_x_final: Option<unsafe extern "C" fn()>,
_x_value: Option<unsafe extern "C" fn()>,
@ -952,7 +939,7 @@ pub unsafe extern "C" fn sqlite3_create_window_function(
}
#[no_mangle]
pub unsafe extern "C" fn sqlite3_errmsg(_db: *mut sqlite3) -> *const std::ffi::c_char {
pub unsafe extern "C" fn sqlite3_errmsg(_db: *mut sqlite3) -> *const ffi::c_char {
if _db.is_null() {
return sqlite3_errstr(SQLITE_NOMEM);
}
@ -965,7 +952,7 @@ pub unsafe extern "C" fn sqlite3_errmsg(_db: *mut sqlite3) -> *const std::ffi::c
let err_msg = if (*_db).err_code != SQLITE_OK {
if !(*_db).p_err.is_null() {
(*_db).p_err as *const std::ffi::c_char
(*_db).p_err as *const ffi::c_char
} else {
std::ptr::null()
}
@ -994,7 +981,7 @@ pub unsafe extern "C" fn sqlite3_extended_errcode(_db: *mut sqlite3) -> ffi::c_i
}
#[no_mangle]
pub unsafe extern "C" fn sqlite3_complete(_sql: *const std::ffi::c_char) -> ffi::c_int {
pub unsafe extern "C" fn sqlite3_complete(_sql: *const ffi::c_char) -> ffi::c_int {
stub!();
}
@ -1004,7 +991,7 @@ pub unsafe extern "C" fn sqlite3_threadsafe() -> ffi::c_int {
}
#[no_mangle]
pub unsafe extern "C" fn sqlite3_libversion() -> *const std::ffi::c_char {
pub unsafe extern "C" fn sqlite3_libversion() -> *const ffi::c_char {
c"3.42.0".as_ptr()
}
@ -1013,7 +1000,7 @@ pub unsafe extern "C" fn sqlite3_libversion_number() -> ffi::c_int {
3042000
}
fn sqlite3_errstr_impl(rc: i32) -> *const std::ffi::c_char {
fn sqlite3_errstr_impl(rc: i32) -> *const ffi::c_char {
const ERROR_MESSAGES: [&str; 29] = [
"not an error", // SQLITE_OK
"SQL logic error", // SQLITE_ERROR
@ -1055,18 +1042,18 @@ fn sqlite3_errstr_impl(rc: i32) -> *const std::ffi::c_char {
const NO_MORE_ROWS_AVAILABLE: &str = "no more rows available";
match rc {
SQLITE_ABORT_ROLLBACK => ABORT_ROLLBACK.as_ptr() as *const std::ffi::c_char,
SQLITE_ROW => ANOTHER_ROW_AVAILABLE.as_ptr() as *const std::ffi::c_char,
SQLITE_DONE => NO_MORE_ROWS_AVAILABLE.as_ptr() as *const std::ffi::c_char,
SQLITE_ABORT_ROLLBACK => ABORT_ROLLBACK.as_ptr() as *const ffi::c_char,
SQLITE_ROW => ANOTHER_ROW_AVAILABLE.as_ptr() as *const ffi::c_char,
SQLITE_DONE => NO_MORE_ROWS_AVAILABLE.as_ptr() as *const ffi::c_char,
_ => {
let rc = rc & 0xff;
if rc >= 0
&& rc < ERROR_MESSAGES.len() as i32
&& !ERROR_MESSAGES[rc as usize].is_empty()
{
ERROR_MESSAGES[rc as usize].as_ptr() as *const std::ffi::c_char
ERROR_MESSAGES[rc as usize].as_ptr() as *const ffi::c_char
} else {
UNKNOWN_ERROR.as_ptr() as *const std::ffi::c_char
UNKNOWN_ERROR.as_ptr() as *const ffi::c_char
}
}
}
@ -1075,7 +1062,7 @@ fn sqlite3_errstr_impl(rc: i32) -> *const std::ffi::c_char {
#[no_mangle]
pub unsafe extern "C" fn sqlite3_wal_checkpoint(
_db: *mut sqlite3,
_db_name: *const std::ffi::c_char,
_db_name: *const ffi::c_char,
) -> ffi::c_int {
sqlite3_wal_checkpoint_v2(
_db,
@ -1089,7 +1076,7 @@ pub unsafe extern "C" fn sqlite3_wal_checkpoint(
#[no_mangle]
pub unsafe extern "C" fn sqlite3_wal_checkpoint_v2(
db: *mut sqlite3,
_db_name: *const std::ffi::c_char,
_db_name: *const ffi::c_char,
_mode: ffi::c_int,
_log_size: *mut ffi::c_int,
_checkpoint_count: *mut ffi::c_int,

View file

@ -332,7 +332,7 @@ mod tests {
for i in 0..iterations {
let insert_query = format!("INSERT INTO test VALUES ({})", i);
do_flush(&conn, &tmp_db)?;
conn.checkpoint().unwrap();
conn.checkpoint()?;
match conn.query(insert_query) {
Ok(Some(ref mut rows)) => loop {
match rows.next_row()? {
@ -351,7 +351,7 @@ mod tests {
}
do_flush(&conn, &tmp_db)?;
conn.clear_page_cache().unwrap();
conn.clear_page_cache()?;
let list_query = "SELECT * FROM test LIMIT 1";
let mut current_index = 0;
match conn.query(list_query) {
@ -391,7 +391,7 @@ mod tests {
// threshold is 1000 by default
fn insert(i: usize, conn: &Rc<Connection>, tmp_db: &TempDatabase) -> anyhow::Result<()> {
log::debug!("inserting {}", i);
debug!("inserting {}", i);
let insert_query = format!("INSERT INTO test VALUES ({})", i);
match conn.query(insert_query) {
Ok(Some(ref mut rows)) => loop {
@ -408,16 +408,16 @@ mod tests {
eprintln!("{}", err);
}
};
log::debug!("inserted {}", i);
debug!("inserted {}", i);
tmp_db.io.run_once()?;
Ok(())
}
fn count(conn: &Rc<Connection>, tmp_db: &TempDatabase) -> anyhow::Result<usize> {
log::debug!("counting");
debug!("counting");
let list_query = "SELECT count(x) FROM test";
loop {
if let Some(ref mut rows) = conn.query(list_query).unwrap() {
if let Some(ref mut rows) = conn.query(list_query)? {
loop {
match rows.next_row()? {
StepResult::Row(row) => {
@ -426,7 +426,7 @@ mod tests {
Value::Integer(i) => *i as i32,
_ => unreachable!(),
};
log::debug!("counted {}", count);
debug!("counted {}", count);
return Ok(count as usize);
}
StepResult::IO => {
@ -443,14 +443,14 @@ mod tests {
{
let conn = tmp_db.connect_limbo();
insert(1, &conn, &tmp_db).unwrap();
assert_eq!(count(&conn, &tmp_db).unwrap(), 1);
insert(1, &conn, &tmp_db)?;
assert_eq!(count(&conn, &tmp_db)?, 1);
conn.close()?;
}
{
let conn = tmp_db.connect_limbo();
assert_eq!(
count(&conn, &tmp_db).unwrap(),
count(&conn, &tmp_db)?,
1,
"failed to read from wal from another connection"
);
@ -619,7 +619,7 @@ mod tests {
let mut stmt = conn.prepare("select ?")?;
stmt.bind_at(1.try_into().unwrap(), Value::Integer(1));
stmt.bind_at(1.try_into()?, Value::Integer(1));
loop {
match stmt.step()? {
@ -633,7 +633,7 @@ mod tests {
stmt.reset();
stmt.bind_at(1.try_into().unwrap(), Value::Integer(2));
stmt.bind_at(1.try_into()?, Value::Integer(2));
loop {
match stmt.step()? {
@ -656,14 +656,14 @@ mod tests {
let mut stmt = conn.prepare("select ?, ?1, :named, ?3, ?4")?;
stmt.bind_at(1.try_into().unwrap(), Value::Text(&"hello".to_string()));
stmt.bind_at(1.try_into()?, Value::Text(&"hello".to_string()));
let i = stmt.parameters().index(":named").unwrap();
stmt.bind_at(i, Value::Integer(42));
stmt.bind_at(3.try_into().unwrap(), Value::Blob(&vec![0x1, 0x2, 0x3]));
stmt.bind_at(3.try_into()?, Value::Blob(&vec![0x1, 0x2, 0x3]));
stmt.bind_at(4.try_into().unwrap(), Value::Float(0.5));
stmt.bind_at(4.try_into()?, Value::Float(0.5));
assert_eq!(stmt.parameters().count(), 4);