From 351242561dc799058aa59a44f5d90f24c91b29be Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Thu, 25 Jul 2024 15:42:11 +0300 Subject: [PATCH] Kill anyhow usage Switch anyhow to explicit `LimboError` type using thiserror crate, which lets us make error handling more structured. --- Cargo.lock | 3 -- bindings/wasm/Cargo.toml | 1 - bindings/wasm/lib.rs | 2 +- core/Cargo.toml | 1 - core/btree.rs | 3 +- core/datetime.rs | 6 ++-- core/error.rs | 49 ++++++++++++++++++++++++++ core/io/common.rs | 3 +- core/io/darwin.rs | 13 +++---- core/io/generic.rs | 3 +- core/io/linux.rs | 14 ++++---- core/io/mod.rs | 2 +- core/io/windows.rs | 3 +- core/lib.rs | 5 ++- core/pager.rs | 8 ++--- core/pseudo.rs | 2 +- core/schema.rs | 9 ++--- core/sqlite3_ondisk.rs | 32 ++++++++--------- core/storage.rs | 18 +++------- core/translate/expr.rs | 64 +++++++++++++++++----------------- core/translate/mod.rs | 2 +- core/translate/select.rs | 52 ++++++++++++--------------- core/translate/where_clause.rs | 23 +++++++----- core/types.rs | 9 ++--- core/vdbe/mod.rs | 11 +++--- core/vdbe/sorter.rs | 6 ++-- simulator/Cargo.toml | 1 - simulator/main.rs | 23 ++++++++---- 28 files changed, 207 insertions(+), 161 deletions(-) create mode 100644 core/error.rs diff --git a/Cargo.lock b/Cargo.lock index 81f6ad96d..5f1ae35fa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1024,7 +1024,6 @@ dependencies = [ name = "limbo-wasm" version = "0.0.2" dependencies = [ - "anyhow", "limbo_core", "wasm-bindgen", ] @@ -1033,7 +1032,6 @@ dependencies = [ name = "limbo_core" version = "0.0.2" dependencies = [ - "anyhow", "cfg_block", "chrono", "criterion", @@ -1061,7 +1059,6 @@ dependencies = [ name = "limbo_sim" version = "0.0.2" dependencies = [ - "anyhow", "limbo_core", "rand", "rand_chacha", diff --git a/bindings/wasm/Cargo.toml b/bindings/wasm/Cargo.toml index 4d42886d2..56fe24c58 100644 --- a/bindings/wasm/Cargo.toml +++ b/bindings/wasm/Cargo.toml @@ -11,6 +11,5 @@ crate-type = ["cdylib"] path = "lib.rs" [dependencies] -anyhow = "1.0.75" limbo_core = { path = "../../core", default-features = false } wasm-bindgen = "0.2" diff --git a/bindings/wasm/lib.rs b/bindings/wasm/lib.rs index ba56ccdc4..9b7582e7c 100644 --- a/bindings/wasm/lib.rs +++ b/bindings/wasm/lib.rs @@ -1,4 +1,4 @@ -use anyhow::Result; +use limbo_core::Result; use std::rc::Rc; use std::sync::Arc; use wasm_bindgen::prelude::*; diff --git a/core/Cargo.toml b/core/Cargo.toml index e8afdd76f..10813c173 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -28,7 +28,6 @@ rustix = "0.38.34" mimalloc = { version = "*", default-features = false } [dependencies] -anyhow = "1.0.75" cfg_block = "0.1.1" fallible-iterator = "0.3.0" libc = "0.2.155" diff --git a/core/btree.rs b/core/btree.rs index d3230e51d..41becb614 100644 --- a/core/btree.rs +++ b/core/btree.rs @@ -1,8 +1,7 @@ use crate::pager::Pager; use crate::sqlite3_ondisk::{BTreeCell, TableInteriorCell, TableLeafCell}; use crate::types::{Cursor, CursorResult, OwnedRecord}; - -use anyhow::Result; +use crate::Result; use std::cell::{Ref, RefCell}; use std::rc::Rc; diff --git a/core/datetime.rs b/core/datetime.rs index 786d711c0..7669f2efe 100644 --- a/core/datetime.rs +++ b/core/datetime.rs @@ -1,7 +1,7 @@ use crate::types::OwnedValue; -use anyhow; use chrono::{DateTime, NaiveDate, NaiveDateTime, NaiveTime, Timelike, Utc}; use log::trace; +use std::result::Result; use std::{error::Error, fmt::Display}; #[derive(Debug)] @@ -86,7 +86,7 @@ fn get_max_datetime_exclusive() -> NaiveDateTime { ) } -pub fn get_date_from_time_value(time_value: &OwnedValue) -> anyhow::Result { +pub fn get_date_from_time_value(time_value: &OwnedValue) -> crate::Result { let dt = match time_value { OwnedValue::Text(s) => get_date_time_from_time_value_string(s), OwnedValue::Integer(i) => get_date_time_from_time_value_integer(*i), @@ -106,7 +106,7 @@ pub fn get_date_from_time_value(time_value: &OwnedValue) -> anyhow::Result { trace!("Other date time error: {}", s); - anyhow::bail!(s) + Err(crate::error::LimboError::InvalidDate(s)) } } } diff --git a/core/error.rs b/core/error.rs new file mode 100644 index 000000000..765d6f4e6 --- /dev/null +++ b/core/error.rs @@ -0,0 +1,49 @@ +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum LimboError { + #[error("Corrupt database: {0}")] + Corrupt(String), + #[error("File is not a database")] + NotADB, + #[error("Internal error: {0}")] + InternalError(String), + #[error("Parse error: {0}")] + ParseError(String), + #[error("Parse error: {0}")] + LexerError(#[from] sqlite3_parser::lexer::sql::Error), + #[error("Conversion error: {0}")] + ConversionError(String), + #[error("Env variable error: {0}")] + EnvVarError(#[from] std::env::VarError), + #[error("I/O error: {0}")] + IOError(#[from] std::io::Error), + #[cfg(target_os = "linux")] + #[error("I/O error: {0}")] + LinuxIOError(String), + #[error("Locking error: {0}")] + LockingError(String), + #[cfg(target_os = "macos")] + #[error("I/O error: {0}")] + RustixIOError(#[from] rustix::io::Errno), + #[error("Parse error: {0}")] + ParseIntError(#[from] std::num::ParseIntError), + #[error("Parse error: {0}")] + ParseFloatError(#[from] std::num::ParseFloatError), + #[error("Parse error: {0}")] + InvalidDate(String), +} + +#[macro_export] +macro_rules! bail_parse_error { + ($($arg:tt)*) => { + return Err(crate::error::LimboError::ParseError(format!($($arg)*))) + }; +} + +#[macro_export] +macro_rules! bail_corrupt_error { + ($($arg:tt)*) => { + return Err(crate::error::LimboError::Corrupt(format!($($arg)*))) + }; +} diff --git a/core/io/common.rs b/core/io/common.rs index d69177ff8..a29c6b7ab 100644 --- a/core/io/common.rs +++ b/core/io/common.rs @@ -2,8 +2,7 @@ pub const ENV_DISABLE_FILE_LOCK: &str = "LIMBO_DISABLE_FILE_LOCK"; #[cfg(test)] pub mod tests { - use crate::IO; - use anyhow::Result; + use crate::{Result, IO}; use std::process::{Command, Stdio}; use tempfile::NamedTempFile; diff --git a/core/io/darwin.rs b/core/io/darwin.rs index 6c6bbf1de..2b63da31e 100644 --- a/core/io/darwin.rs +++ b/core/io/darwin.rs @@ -1,7 +1,8 @@ +use crate::error::LimboError; use crate::io::common; +use crate::Result; use super::{Completion, File, WriteCompletion, IO}; -use anyhow::{Ok, Result}; use libc::{c_short, fcntl, flock, F_SETLK}; use log::trace; use polling::{Event, Events, Poller}; @@ -137,11 +138,11 @@ impl File for DarwinFile { if lock_result == -1 { let err = std::io::Error::last_os_error(); if err.kind() == std::io::ErrorKind::WouldBlock { - return Err(anyhow::anyhow!( + return Err(LimboError::LockingError(format!( "Failed locking file. File is locked by another process" - )); + ))); } else { - return Err(anyhow::anyhow!("Failed locking file, {}", err)); + return Err(LimboError::LockingError(format!("Failed locking file, {}", err))); } } Ok(()) @@ -159,10 +160,10 @@ impl File for DarwinFile { let unlock_result = unsafe { fcntl(fd, F_SETLK, &flock) }; if unlock_result == -1 { - return Err(anyhow::anyhow!( + return Err(LimboError::LockingError(format!( "Failed to release file lock: {}", std::io::Error::last_os_error() - )); + ))); } Ok(()) } diff --git a/core/io/generic.rs b/core/io/generic.rs index fce69859a..0fb86d3d8 100644 --- a/core/io/generic.rs +++ b/core/io/generic.rs @@ -1,5 +1,4 @@ -use super::{Completion, File, WriteCompletion, IO}; -use anyhow::{Ok, Result}; +use crate::{Completion, File, Result, WriteCompletion, IO}; use log::trace; use std::cell::RefCell; use std::io::{Read, Seek, Write}; diff --git a/core/io/linux.rs b/core/io/linux.rs index 237538441..71a5cd58d 100644 --- a/core/io/linux.rs +++ b/core/io/linux.rs @@ -1,5 +1,5 @@ use super::{common, Completion, File, WriteCompletion, IO}; -use anyhow::{ensure, Result}; +use crate::{Result, LimboError}; use libc::{c_short, fcntl, flock, iovec, F_SETLK}; use log::{debug, trace}; use nix::fcntl::{FcntlArg, OFlag}; @@ -94,7 +94,9 @@ impl IO for LinuxIO { ring.submit_and_wait(1)?; while let Some(cqe) = ring.completion().next() { let result = cqe.result(); - ensure!(result >= 0, LinuxIOError::IOUringCQError(result)); + if result < 0 { + return Err(LimboError::LinuxIOError(format!("{}", LinuxIOError::IOUringCQError(result)))); + } let c = unsafe { Rc::from_raw(cqe.user_data() as *const Completion) }; c.complete(); } @@ -128,9 +130,9 @@ impl File for LinuxFile { if lock_result == -1 { let err = std::io::Error::last_os_error(); if err.kind() == std::io::ErrorKind::WouldBlock { - return Err(anyhow::anyhow!("File is locked by another process")); + return Err(LimboError::LockingError("File is locked by another process".into())); } else { - return Err(anyhow::anyhow!(err)); + return Err(LimboError::IOError(err)); } } Ok(()) @@ -148,10 +150,10 @@ impl File for LinuxFile { let unlock_result = unsafe { fcntl(fd, F_SETLK, &flock) }; if unlock_result == -1 { - return Err(anyhow::anyhow!( + return Err(LimboError::LockingError(format!( "Failed to release file lock: {}", std::io::Error::last_os_error() - )); + ))); } Ok(()) } diff --git a/core/io/mod.rs b/core/io/mod.rs index a7add6b01..98d903adc 100644 --- a/core/io/mod.rs +++ b/core/io/mod.rs @@ -1,4 +1,4 @@ -use anyhow::Result; +use crate::Result; use cfg_block::cfg_block; use std::{ cell::{Ref, RefCell, RefMut}, diff --git a/core/io/windows.rs b/core/io/windows.rs index 40ac91055..cd0119b9c 100644 --- a/core/io/windows.rs +++ b/core/io/windows.rs @@ -1,5 +1,4 @@ -use super::{Completion, File, WriteCompletion, IO}; -use anyhow::{Ok, Result}; +use crate::{Completion, File, Result, WriteCompletion, IO}; use log::trace; use std::cell::RefCell; use std::io::{Read, Seek, Write}; diff --git a/core/lib.rs b/core/lib.rs index 56549921f..593e8765e 100644 --- a/core/lib.rs +++ b/core/lib.rs @@ -1,6 +1,7 @@ mod btree; mod buffer_pool; mod datetime; +mod error; mod function; mod io; mod pager; @@ -17,7 +18,6 @@ mod vdbe; #[global_allocator] static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; -use anyhow::Result; use fallible_iterator::FallibleIterator; use log::trace; use pager::Pager; @@ -27,6 +27,9 @@ use sqlite3_parser::{ast::Cmd, lexer::sql::Parser}; use std::sync::Arc; use std::{cell::RefCell, rc::Rc}; +pub use error::LimboError; +pub type Result = std::result::Result; + #[cfg(feature = "fs")] pub use io::PlatformIO; pub use io::{Buffer, Completion, File, WriteCompletion, IO}; diff --git a/core/pager.rs b/core/pager.rs index 95e2531b0..591ee48af 100644 --- a/core/pager.rs +++ b/core/pager.rs @@ -1,7 +1,7 @@ use crate::buffer_pool::BufferPool; use crate::sqlite3_ondisk::BTreePage; use crate::sqlite3_ondisk::{self, DatabaseHeader}; -use crate::PageSource; +use crate::{PageSource, Result}; use log::trace; use sieve_cache::SieveCache; use std::cell::RefCell; @@ -106,7 +106,7 @@ pub struct Pager { } impl Pager { - pub fn begin_open(page_source: &PageSource) -> anyhow::Result>> { + pub fn begin_open(page_source: &PageSource) -> Result>> { sqlite3_ondisk::begin_read_database_header(page_source) } @@ -114,7 +114,7 @@ impl Pager { db_header: Rc>, page_source: PageSource, io: Arc, - ) -> anyhow::Result { + ) -> Result { let db_header = db_header.borrow(); let page_size = db_header.page_size as usize; let buffer_pool = Rc::new(BufferPool::new(page_size)); @@ -127,7 +127,7 @@ impl Pager { }) } - pub fn read_page(&self, page_idx: usize) -> anyhow::Result> { + pub fn read_page(&self, page_idx: usize) -> Result> { trace!("read_page(page_idx = {})", page_idx); let mut page_cache = self.page_cache.borrow_mut(); if let Some(page) = page_cache.get(&page_idx) { diff --git a/core/pseudo.rs b/core/pseudo.rs index dd8668d2e..dfd3212ad 100644 --- a/core/pseudo.rs +++ b/core/pseudo.rs @@ -1,4 +1,4 @@ -use anyhow::Result; +use crate::Result; use std::cell::{Ref, RefCell}; use crate::types::{Cursor, CursorResult, OwnedRecord, OwnedValue}; diff --git a/core/schema.rs b/core/schema.rs index 179dc45f3..d91a8a9e8 100644 --- a/core/schema.rs +++ b/core/schema.rs @@ -1,5 +1,4 @@ -use crate::util::normalize_ident; -use anyhow::Result; +use crate::{util::normalize_ident, Result}; use core::fmt; use fallible_iterator::FallibleIterator; use log::trace; @@ -130,7 +129,7 @@ impl BTreeTable { Some(Cmd::Stmt(Stmt::CreateTable { tbl_name, body, .. })) => { create_table(tbl_name, body, root_page) } - _ => anyhow::bail!("Expected CREATE TABLE statement"), + _ => todo!("Expected CREATE TABLE statement"), } } @@ -213,9 +212,7 @@ fn create_table( value.trim_matches('\'').to_owned() } _ => { - return Err(anyhow::anyhow!( - "Unsupported primary key expression" - )) + todo!("Unsupported primary key expression"); } }); } diff --git a/core/sqlite3_ondisk.rs b/core/sqlite3_ondisk.rs index 2abc08ba1..8a4216853 100644 --- a/core/sqlite3_ondisk.rs +++ b/core/sqlite3_ondisk.rs @@ -24,11 +24,11 @@ /// /// For more information, see: https://www.sqlite.org/fileformat.html use crate::buffer_pool::BufferPool; +use crate::error::LimboError; use crate::io::{Buffer, Completion, WriteCompletion}; use crate::pager::{Page, Pager}; use crate::types::{OwnedRecord, OwnedValue}; -use crate::PageSource; -use anyhow::{anyhow, Result}; +use crate::{PageSource, Result}; use log::trace; use std::cell::RefCell; use std::rc::Rc; @@ -204,7 +204,7 @@ pub enum PageType { } impl TryFrom for PageType { - type Error = anyhow::Error; + type Error = crate::error::LimboError; fn try_from(value: u8) -> Result { match value { @@ -212,7 +212,7 @@ impl TryFrom for PageType { 5 => Ok(Self::TableInterior), 10 => Ok(Self::IndexLeaf), 13 => Ok(Self::TableLeaf), - _ => Err(anyhow!("Invalid page type: {}", value)), + _ => Err(LimboError::Corrupt(format!("Invalid page type: {}", value))), } } } @@ -410,7 +410,7 @@ pub enum SerialType { } impl TryFrom for SerialType { - type Error = anyhow::Error; + type Error = crate::error::LimboError; fn try_from(value: u64) -> Result { match value { @@ -426,7 +426,7 @@ impl TryFrom for SerialType { 9 => Ok(Self::ConstInt1), n if value > 12 && value % 2 == 0 => Ok(Self::Blob(((n - 12) / 2) as usize)), n if value > 13 && value % 2 == 1 => Ok(Self::String(((n - 13) / 2) as usize)), - _ => Err(anyhow!("Invalid serial type: {}", value)), + _ => crate::bail_corrupt_error!("Invalid serial type: {}", value), } } } @@ -461,13 +461,13 @@ pub fn read_value(buf: &[u8], serial_type: &SerialType) -> Result<(OwnedValue, u SerialType::Null => Ok((OwnedValue::Null, 0)), SerialType::UInt8 => { if buf.is_empty() { - return Err(anyhow!("Invalid UInt8 value")); + crate::bail_corrupt_error!("Invalid UInt8 value"); } Ok((OwnedValue::Integer(buf[0] as i64), 1)) } SerialType::BEInt16 => { if buf.len() < 2 { - return Err(anyhow!("Invalid BEInt16 value")); + crate::bail_corrupt_error!("Invalid BEInt16 value"); } Ok(( OwnedValue::Integer(i16::from_be_bytes([buf[0], buf[1]]) as i64), @@ -476,7 +476,7 @@ pub fn read_value(buf: &[u8], serial_type: &SerialType) -> Result<(OwnedValue, u } SerialType::BEInt24 => { if buf.len() < 3 { - return Err(anyhow!("Invalid BEInt24 value")); + crate::bail_corrupt_error!("Invalid BEInt24 value"); } Ok(( OwnedValue::Integer(i32::from_be_bytes([0, buf[0], buf[1], buf[2]]) as i64), @@ -485,7 +485,7 @@ pub fn read_value(buf: &[u8], serial_type: &SerialType) -> Result<(OwnedValue, u } SerialType::BEInt32 => { if buf.len() < 4 { - return Err(anyhow!("Invalid BEInt32 value")); + crate::bail_corrupt_error!("Invalid BEInt32 value"); } Ok(( OwnedValue::Integer(i32::from_be_bytes([buf[0], buf[1], buf[2], buf[3]]) as i64), @@ -494,7 +494,7 @@ pub fn read_value(buf: &[u8], serial_type: &SerialType) -> Result<(OwnedValue, u } SerialType::BEInt48 => { if buf.len() < 6 { - return Err(anyhow!("Invalid BEInt48 value")); + crate::bail_corrupt_error!("Invalid BEInt48 value"); } Ok(( OwnedValue::Integer(i64::from_be_bytes([ @@ -505,7 +505,7 @@ pub fn read_value(buf: &[u8], serial_type: &SerialType) -> Result<(OwnedValue, u } SerialType::BEInt64 => { if buf.len() < 8 { - return Err(anyhow!("Invalid BEInt64 value")); + crate::bail_corrupt_error!("Invalid BEInt64 value"); } Ok(( OwnedValue::Integer(i64::from_be_bytes([ @@ -516,7 +516,7 @@ pub fn read_value(buf: &[u8], serial_type: &SerialType) -> Result<(OwnedValue, u } SerialType::BEFloat64 => { if buf.len() < 8 { - return Err(anyhow!("Invalid BEFloat64 value")); + crate::bail_corrupt_error!("Invalid BEFloat64 value"); } Ok(( OwnedValue::Float(f64::from_be_bytes([ @@ -529,13 +529,13 @@ pub fn read_value(buf: &[u8], serial_type: &SerialType) -> Result<(OwnedValue, u SerialType::ConstInt1 => Ok((OwnedValue::Integer(1), 0)), SerialType::Blob(n) => { if buf.len() < n { - return Err(anyhow!("Invalid Blob value")); + crate::bail_corrupt_error!("Invalid Blob value"); } Ok((OwnedValue::Blob(buf[0..n].to_vec().into()), n)) } SerialType::String(n) => { if buf.len() < n { - return Err(anyhow!("Invalid String value")); + crate::bail_corrupt_error!("Invalid String value"); } let bytes = buf[0..n].to_vec(); let value = unsafe { String::from_utf8_unchecked(bytes) }; @@ -555,7 +555,7 @@ fn read_varint(buf: &[u8]) -> Result<(u64, usize)> { } } None => { - return Err(anyhow!("Invalid varint")); + crate::bail_corrupt_error!("Invalid varint"); } } } diff --git a/core/storage.rs b/core/storage.rs index 75563515c..bb62df0bd 100644 --- a/core/storage.rs +++ b/core/storage.rs @@ -1,18 +1,11 @@ #[cfg(feature = "fs")] use crate::io::File; use crate::{ + error::LimboError, io::{Completion, WriteCompletion}, - Buffer, + Buffer, Result, }; -use anyhow::{ensure, Result}; use std::{cell::RefCell, rc::Rc}; -use thiserror::Error; - -#[derive(Debug, Error)] -pub enum StorageError { - #[error("file is not a database")] - NotADB, -} pub struct PageSource { io: Rc, @@ -72,10 +65,9 @@ impl PageIO for FileStorage { fn get(&self, page_idx: usize, c: Rc) -> Result<()> { let size = c.buf().len(); assert!(page_idx > 0); - ensure!( - (1 << 9..=1 << 16).contains(&size) && size & (size - 1) == 0, - StorageError::NotADB - ); + if size < 512 || size > 65536 || size & (size - 1) != 0 { + return Err(LimboError::NotADB.into()); + } let pos = (page_idx - 1) * size; self.file.pread(pos, c)?; Ok(()) diff --git a/core/translate/expr.rs b/core/translate/expr.rs index 05b0497c3..f6e16f2ca 100644 --- a/core/translate/expr.rs +++ b/core/translate/expr.rs @@ -1,4 +1,4 @@ -use anyhow::Result; +use crate::Result; use sqlite3_parser::ast::{self, Expr, UnaryOperator}; use crate::{ @@ -132,22 +132,22 @@ pub fn translate_expr( match func_type { Some(Func::Agg(_)) => { - anyhow::bail!("Parse error: aggregation function in non-aggregation context") + crate::bail_parse_error!("aggregation function in non-aggregation context") } Some(Func::Scalar(srf)) => { match srf { ScalarFunc::Coalesce => { let args = if let Some(args) = args { if args.len() < 2 { - anyhow::bail!( - "Parse error: {} function with less than 2 arguments", + crate::bail_parse_error!( + "{} function with less than 2 arguments", srf.to_string() ); } args } else { - anyhow::bail!( - "Parse error: {} function with no arguments", + crate::bail_parse_error!( + "{} function with no arguments", srf.to_string() ); }; @@ -180,15 +180,15 @@ pub fn translate_expr( ScalarFunc::Like => { let args = if let Some(args) = args { if args.len() < 2 { - anyhow::bail!( - "Parse error: {} function with less than 2 arguments", + crate::bail_parse_error!( + "{} function with less than 2 arguments", srf.to_string() ); } args } else { - anyhow::bail!( - "Parse error: {} function with no arguments", + crate::bail_parse_error!( + "{} function with no arguments", srf.to_string() ); }; @@ -213,15 +213,15 @@ pub fn translate_expr( | ScalarFunc::Length => { let args = if let Some(args) = args { if args.len() != 1 { - anyhow::bail!( - "Parse error: {} function with not exactly 1 argument", + crate::bail_parse_error!( + "{} function with not exactly 1 argument", srf.to_string() ); } args } else { - anyhow::bail!( - "Parse error: {} function with no arguments", + crate::bail_parse_error!( + "{} function with no arguments", srf.to_string() ); }; @@ -237,8 +237,8 @@ pub fn translate_expr( } ScalarFunc::Random => { if args.is_some() { - anyhow::bail!( - "Parse error: {} function with arguments", + crate::bail_parse_error!( + "{} function with arguments", srf.to_string() ); } @@ -254,7 +254,7 @@ pub fn translate_expr( let mut start_reg = 0; if let Some(args) = args { if args.len() > 1 { - anyhow::bail!("Parse error: date function with > 1 arguments. Modifiers are not yet supported."); + crate::bail_parse_error!("date function with > 1 arguments. Modifiers are not yet supported."); } else if args.len() == 1 { let arg_reg = program.alloc_register(); let _ = translate_expr( @@ -277,15 +277,15 @@ pub fn translate_expr( ScalarFunc::Trim | ScalarFunc::Round => { let args = if let Some(args) = args { if args.len() > 2 { - anyhow::bail!( - "Parse error: {} function with more than 2 arguments", + crate::bail_parse_error!( + "{} function with more than 2 arguments", srf.to_string() ); } args } else { - anyhow::bail!( - "Parse error: {} function with no arguments", + crate::bail_parse_error!( + "{} function with no arguments", srf.to_string() ); }; @@ -307,13 +307,13 @@ pub fn translate_expr( ScalarFunc::Min => { let args = if let Some(args) = args { if args.len() < 1 { - anyhow::bail!( - "Parse error: min function with less than one argument" + crate::bail_parse_error!( + "min function with less than one argument" ); } args } else { - anyhow::bail!("Parse error: min function with no arguments"); + crate::bail_parse_error!("min function with no arguments"); }; for arg in args { let reg = program.alloc_register(); @@ -334,13 +334,13 @@ pub fn translate_expr( ScalarFunc::Max => { let args = if let Some(args) = args { if args.len() < 1 { - anyhow::bail!( - "Parse error: max function with less than one argument" + crate::bail_parse_error!( + "max function with less than one argument" ); } args } else { - anyhow::bail!("Parse error: max function with no arguments"); + crate::bail_parse_error!("max function with no arguments"); }; for arg in args { let reg = program.alloc_register(); @@ -361,7 +361,7 @@ pub fn translate_expr( } } None => { - anyhow::bail!("Parse error: unknown function {}", name.0); + crate::bail_parse_error!("unknown function {}", name.0); } } } @@ -600,8 +600,8 @@ pub fn resolve_ident_qualified( Table::Pseudo(_) => todo!(), } } - anyhow::bail!( - "Parse error: column with qualified name {}.{} not found", + crate::bail_parse_error!( + "column with qualified name {}.{} not found", table_name, ident ); @@ -654,10 +654,10 @@ pub fn resolve_ident_table( return Ok(found[0]); } if found.is_empty() { - anyhow::bail!("Parse error: column with name {} not found", ident.as_str()); + crate::bail_parse_error!("column with name {} not found", ident.as_str()); } - anyhow::bail!("Parse error: ambiguous column name {}", ident.as_str()); + crate::bail_parse_error!("ambiguous column name {}", ident.as_str()); } pub fn maybe_apply_affinity(col_type: Type, target_register: usize, program: &mut ProgramBuilder) { diff --git a/core/translate/mod.rs b/core/translate/mod.rs index 4dc3421c3..f0abe0b62 100644 --- a/core/translate/mod.rs +++ b/core/translate/mod.rs @@ -10,7 +10,7 @@ use crate::schema::Schema; use crate::sqlite3_ondisk::{DatabaseHeader, MIN_PAGE_CACHE_SIZE}; use crate::util::normalize_ident; use crate::vdbe::{builder::ProgramBuilder, BranchOffset, Insn, Program}; -use anyhow::Result; +use crate::Result; use select::{build_select, translate_select}; use sqlite3_parser::ast; diff --git a/core/translate/select.rs b/core/translate/select.rs index a05b0bd42..54570c74d 100644 --- a/core/translate/select.rs +++ b/core/translate/select.rs @@ -1,6 +1,6 @@ use std::rc::Rc; -use anyhow::Result; +use crate::Result; use sqlite3_parser::ast::{self, JoinOperator, JoinType}; use crate::function::AggFunc; @@ -136,7 +136,7 @@ pub fn build_select<'a>(schema: &Schema, select: &'a ast::Select) -> Result table, - None => anyhow::bail!("Parse error: no such table: {}", table_name), + None => crate::bail_parse_error!("no such table: {}", table_name), }; let identifier = normalize_ident(maybe_alias.unwrap_or(table_name)); let mut joins = Vec::new(); @@ -161,7 +161,9 @@ pub fn build_select<'a>(schema: &Schema, select: &'a ast::Select) -> Result table, - None => anyhow::bail!("Parse error: no such table: {}", table_name), + None => { + crate::bail_parse_error!("no such table: {}", table_name) + } }; let identifier = normalize_ident(maybe_alias.unwrap_or(table_name)); @@ -715,11 +717,13 @@ fn translate_aggregation( let empty_args = &Vec::::new(); let args = info.args.as_ref().unwrap_or(empty_args); let dest = match func { - Func::Scalar(_) => anyhow::bail!("Parse error: single row function in aggregation"), + Func::Scalar(_) => { + crate::bail_parse_error!("single row function in aggregation") + } Func::Agg(agg_func) => match agg_func { AggFunc::Avg => { if args.len() != 1 { - anyhow::bail!("Parse error: avg bad number of arguments"); + crate::bail_parse_error!("avg bad number of arguments"); } let expr = &args[0]; let expr_reg = program.alloc_register(); @@ -751,7 +755,7 @@ fn translate_aggregation( } AggFunc::GroupConcat => { if args.len() != 1 && args.len() != 2 { - anyhow::bail!("Parse error: group_concat bad number of arguments"); + crate::bail_parse_error!("group_concat bad number of arguments"); } let expr_reg = program.alloc_register(); @@ -774,21 +778,15 @@ fn translate_aggregation( delimiter_expr = ast::Expr::Literal(ast::Literal::String(s.to_string())); } - _ => anyhow::bail!("Incorrect delimiter parameter"), + _ => crate::bail_parse_error!("Incorrect delimiter parameter"), }; } else { delimiter_expr = ast::Expr::Literal(ast::Literal::String(String::from("\",\""))); } - if let Err(error) = translate_expr(program, select, expr, expr_reg, cursor_hint) { - anyhow::bail!(error); - } - if let Err(error) = - translate_expr(program, select, &delimiter_expr, delimiter_reg, cursor_hint) - { - anyhow::bail!(error); - } + translate_expr(program, select, expr, expr_reg, cursor_hint)?; + translate_expr(program, select, &delimiter_expr, delimiter_reg, cursor_hint)?; program.emit_insn(Insn::AggStep { acc_reg: target_register, @@ -801,7 +799,7 @@ fn translate_aggregation( } AggFunc::Max => { if args.len() != 1 { - anyhow::bail!("Parse error: max bad number of arguments"); + crate::bail_parse_error!("max bad number of arguments"); } let expr = &args[0]; let expr_reg = program.alloc_register(); @@ -816,7 +814,7 @@ fn translate_aggregation( } AggFunc::Min => { if args.len() != 1 { - anyhow::bail!("Parse error: min bad number of arguments"); + crate::bail_parse_error!("min bad number of arguments"); } let expr = &args[0]; let expr_reg = program.alloc_register(); @@ -831,7 +829,7 @@ fn translate_aggregation( } AggFunc::StringAgg => { if args.len() != 2 { - anyhow::bail!("Parse error: string_agg bad number of arguments"); + crate::bail_parse_error!("string_agg bad number of arguments"); } let expr_reg = program.alloc_register(); @@ -843,7 +841,7 @@ fn translate_aggregation( match &args[1] { ast::Expr::Id(ident) => { if ident.0.starts_with('"') { - anyhow::bail!("Parse error: no such column: \",\" - should this be a string literal in single-quotes?"); + crate::bail_parse_error!("no such column: \",\" - should this be a string literal in single-quotes?"); } else { delimiter_expr = args[1].clone(); } @@ -851,17 +849,11 @@ fn translate_aggregation( ast::Expr::Literal(ast::Literal::String(s)) => { delimiter_expr = ast::Expr::Literal(ast::Literal::String(s.to_string())); } - _ => anyhow::bail!("Incorrect delimiter parameter"), + _ => crate::bail_parse_error!("Incorrect delimiter parameter"), }; - if let Err(error) = translate_expr(program, select, expr, expr_reg, cursor_hint) { - anyhow::bail!(error); - } - if let Err(error) = - translate_expr(program, select, &delimiter_expr, delimiter_reg, cursor_hint) - { - anyhow::bail!(error); - } + translate_expr(program, select, expr, expr_reg, cursor_hint)?; + translate_expr(program, select, &delimiter_expr, delimiter_reg, cursor_hint)?; program.emit_insn(Insn::AggStep { acc_reg: target_register, @@ -874,7 +866,7 @@ fn translate_aggregation( } AggFunc::Sum => { if args.len() != 1 { - anyhow::bail!("Parse error: sum bad number of arguments"); + crate::bail_parse_error!("sum bad number of arguments"); } let expr = &args[0]; let expr_reg = program.alloc_register(); @@ -889,7 +881,7 @@ fn translate_aggregation( } AggFunc::Total => { if args.len() != 1 { - anyhow::bail!("Parse error: total bad number of arguments"); + crate::bail_parse_error!("total bad number of arguments"); } let expr = &args[0]; let expr_reg = program.alloc_register(); diff --git a/core/translate/where_clause.rs b/core/translate/where_clause.rs index 6895d3331..6c16a78bb 100644 --- a/core/translate/where_clause.rs +++ b/core/translate/where_clause.rs @@ -1,15 +1,18 @@ -use anyhow::Result; -use sqlite3_parser::ast::{self}; - use crate::{ + error::LimboError, function::ScalarFunc, - translate::expr::{resolve_ident_qualified, resolve_ident_table, translate_expr}, - translate::select::Select, + translate::{ + expr::{resolve_ident_qualified, resolve_ident_table, translate_expr}, + select::Select, + }, vdbe::{builder::ProgramBuilder, BranchOffset, Insn}, + Result, }; use super::select::LoopInfo; +use sqlite3_parser::ast::{self}; + #[derive(Debug)] pub struct WhereTerm { pub expr: ast::Expr, @@ -60,10 +63,10 @@ pub fn split_constraint_to_terms<'a>( .loops .iter() .find(|t| t.identifier == *table) - .ok_or(anyhow::anyhow!( + .ok_or(LimboError::ParseError(format!( "Could not find cursor for table {}", table - ))? + )))? .open_cursor } None => { @@ -87,7 +90,9 @@ pub fn split_constraint_to_terms<'a>( .map(|t| t.open_cursor) .min() .ok_or_else(|| { - anyhow::anyhow!("No open cursors found in any of the loops") + LimboError::ParseError(format!( + "No open cursors found in any of the loops" + )) })?; *cursors.iter().max().unwrap_or(&outermost_cursor) @@ -377,7 +382,7 @@ fn translate_condition_expr( null_reg: reg, }); } else { - anyhow::bail!("Parse error: unsupported literal type in condition"); + crate::bail_parse_error!("unsupported literal type in condition"); } } _ => todo!(), diff --git a/core/types.rs b/core/types.rs index 256aa1475..4276e7021 100644 --- a/core/types.rs +++ b/core/types.rs @@ -1,7 +1,8 @@ use std::fmt::Display; use std::{cell::Ref, rc::Rc}; -use anyhow::Result; +use crate::error::LimboError; +use crate::Result; #[derive(Debug, Clone, PartialEq)] pub enum Value<'a> { @@ -257,7 +258,7 @@ impl<'a> FromValue<'a> for i64 { fn from_value(value: &Value<'a>) -> Result { match value { Value::Integer(i) => Ok(*i), - _ => anyhow::bail!("Expected integer value"), + _ => Err(LimboError::ConversionError("Expected integer value".into())), } } } @@ -266,7 +267,7 @@ impl<'a> FromValue<'a> for String { fn from_value(value: &Value<'a>) -> Result { match value { Value::Text(s) => Ok(s.to_string()), - _ => anyhow::bail!("Expected text value"), + _ => Err(LimboError::ConversionError("Expected text value".into())), } } } @@ -275,7 +276,7 @@ impl<'a> FromValue<'a> for &'a str { fn from_value(value: &Value<'a>) -> Result<&'a str> { match value { Value::Text(s) => Ok(s), - _ => anyhow::bail!("Expected text value"), + _ => Err(LimboError::ConversionError("Expected text value".into())), } } } diff --git a/core/vdbe/mod.rs b/core/vdbe/mod.rs index 8657ea34d..7aa52fd6d 100644 --- a/core/vdbe/mod.rs +++ b/core/vdbe/mod.rs @@ -4,13 +4,14 @@ pub mod sorter; use crate::btree::BTreeCursor; use crate::datetime::get_date_from_time_value; +use crate::error::LimboError; use crate::function::{AggFunc, ScalarFunc}; use crate::pager::Pager; use crate::pseudo::PseudoCursor; use crate::schema::Table; use crate::types::{AggContext, Cursor, CursorResult, OwnedRecord, OwnedValue, Record}; +use crate::Result; -use anyhow::Result; use regex::Regex; use std::borrow::BorrowMut; use std::cell::RefCell; @@ -380,7 +381,9 @@ impl Program { state.pc += 1; } _ => { - anyhow::bail!("IfPos: the value in the register is not an integer"); + return Err(LimboError::InternalError( + "IfPos: the value in the register is not an integer".into(), + )); } } } @@ -1141,10 +1144,10 @@ impl Program { state.registers[*dest] = OwnedValue::Text(Rc::new(date)) } Err(e) => { - anyhow::bail!( + return Err(LimboError::ParseError(format!( "Error encountered while parsing time value: {}", e - ) + ))); } } } diff --git a/core/vdbe/sorter.rs b/core/vdbe/sorter.rs index 2e0bf74d1..de4f90296 100644 --- a/core/vdbe/sorter.rs +++ b/core/vdbe/sorter.rs @@ -1,5 +1,7 @@ -use crate::types::{Cursor, CursorResult, OwnedRecord}; -use anyhow::Result; +use crate::{ + types::{Cursor, CursorResult, OwnedRecord}, + Result, +}; use std::{ cell::{Ref, RefCell}, collections::{BTreeMap, VecDeque}, diff --git a/simulator/Cargo.toml b/simulator/Cargo.toml index 88f87247f..afa718e41 100644 --- a/simulator/Cargo.toml +++ b/simulator/Cargo.toml @@ -15,7 +15,6 @@ name = "limbo_sim" path = "main.rs" [dependencies] -anyhow = "1.0.86" limbo_core = { path = "../core" } rand = "0.8.5" rand_chacha = "0.3.1" diff --git a/simulator/main.rs b/simulator/main.rs index 6bcfa1f9f..628ba5526 100644 --- a/simulator/main.rs +++ b/simulator/main.rs @@ -1,5 +1,4 @@ -use anyhow::Result; -use limbo_core::{Database, File, PlatformIO, IO}; +use limbo_core::{Database, File, PlatformIO, Result, IO}; use rand::prelude::*; use rand_chacha::ChaCha8Rng; use std::cell::RefCell; @@ -100,7 +99,9 @@ impl IO for SimulatorIO { fn run_once(&self) -> Result<()> { if *self.fault.borrow() { - return Err(anyhow::anyhow!("Injected fault")); + return Err(limbo_core::LimboError::InternalError( + "Injected fault".into(), + )); } self.inner.run_once().unwrap(); Ok(()) @@ -131,14 +132,18 @@ impl SimulatorFile { impl limbo_core::File for SimulatorFile { fn lock_file(&self, exclusive: bool) -> Result<()> { if *self.fault.borrow() { - return Err(anyhow::anyhow!("Injected fault")); + return Err(limbo_core::LimboError::InternalError( + "Injected fault".into(), + )); } self.inner.lock_file(exclusive) } fn unlock_file(&self) -> Result<()> { if *self.fault.borrow() { - return Err(anyhow::anyhow!("Injected fault")); + return Err(limbo_core::LimboError::InternalError( + "Injected fault".into(), + )); } self.inner.unlock_file() } @@ -146,7 +151,9 @@ impl limbo_core::File for SimulatorFile { fn pread(&self, pos: usize, c: Rc) -> Result<()> { if *self.fault.borrow() { *self.nr_pread_faults.borrow_mut() += 1; - return Err(anyhow::anyhow!("Injected fault")); + return Err(limbo_core::LimboError::InternalError( + "Injected fault".into(), + )); } self.inner.pread(pos, c) } @@ -159,7 +166,9 @@ impl limbo_core::File for SimulatorFile { ) -> Result<()> { if *self.fault.borrow() { *self.nr_pwrite_faults.borrow_mut() += 1; - return Err(anyhow::anyhow!("Injected fault")); + return Err(limbo_core::LimboError::InternalError( + "Injected fault".into(), + )); } self.inner.pwrite(pos, buffer, c) }