mirror of
https://github.com/aljazceru/turso.git
synced 2026-01-04 17:04:18 +01:00
3
Cargo.lock
generated
3
Cargo.lock
generated
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use anyhow::Result;
|
||||
use limbo_core::Result;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<String> {
|
||||
pub fn get_date_from_time_value(time_value: &OwnedValue) -> crate::Result<String> {
|
||||
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<Strin
|
||||
}
|
||||
DateTimeError::Other(s) => {
|
||||
trace!("Other date time error: {}", s);
|
||||
anyhow::bail!(s)
|
||||
Err(crate::error::LimboError::InvalidDate(s))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
49
core/error.rs
Normal file
49
core/error.rs
Normal file
@@ -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)*)))
|
||||
};
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use anyhow::Result;
|
||||
use crate::Result;
|
||||
use cfg_block::cfg_block;
|
||||
use std::{
|
||||
cell::{Ref, RefCell, RefMut},
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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<T> = std::result::Result<T, error::LimboError>;
|
||||
|
||||
#[cfg(feature = "fs")]
|
||||
pub use io::PlatformIO;
|
||||
pub use io::{Buffer, Completion, File, WriteCompletion, IO};
|
||||
|
||||
@@ -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<Rc<RefCell<DatabaseHeader>>> {
|
||||
pub fn begin_open(page_source: &PageSource) -> Result<Rc<RefCell<DatabaseHeader>>> {
|
||||
sqlite3_ondisk::begin_read_database_header(page_source)
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ impl Pager {
|
||||
db_header: Rc<RefCell<DatabaseHeader>>,
|
||||
page_source: PageSource,
|
||||
io: Arc<dyn crate::io::IO>,
|
||||
) -> anyhow::Result<Self> {
|
||||
) -> Result<Self> {
|
||||
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<Rc<Page>> {
|
||||
pub fn read_page(&self, page_idx: usize) -> Result<Rc<Page>> {
|
||||
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) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use anyhow::Result;
|
||||
use crate::Result;
|
||||
use std::cell::{Ref, RefCell};
|
||||
|
||||
use crate::types::{Cursor, CursorResult, OwnedRecord, OwnedValue};
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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<u8> for PageType {
|
||||
type Error = anyhow::Error;
|
||||
type Error = crate::error::LimboError;
|
||||
|
||||
fn try_from(value: u8) -> Result<Self> {
|
||||
match value {
|
||||
@@ -212,7 +212,7 @@ impl TryFrom<u8> 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<u64> for SerialType {
|
||||
type Error = anyhow::Error;
|
||||
type Error = crate::error::LimboError;
|
||||
|
||||
fn try_from(value: u64) -> Result<Self> {
|
||||
match value {
|
||||
@@ -426,7 +426,7 @@ impl TryFrom<u64> 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<dyn PageIO>,
|
||||
@@ -72,10 +65,9 @@ impl PageIO for FileStorage {
|
||||
fn get(&self, page_idx: usize, c: Rc<Completion>) -> 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(())
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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<Sele
|
||||
let maybe_alias = maybe_alias.map(|als| &als.0);
|
||||
let table = match schema.get_table(table_name) {
|
||||
Some(table) => 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<Sele
|
||||
let maybe_alias = maybe_alias.as_ref().map(|als| &als.0);
|
||||
let table = match schema.get_table(table_name) {
|
||||
Some(table) => 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::<ast::Expr>::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();
|
||||
|
||||
@@ -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!(),
|
||||
|
||||
@@ -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<Self> {
|
||||
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<Self> {
|
||||
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())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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<limbo_core::Completion>) -> 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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user