Implement new error tracing system

[ci] Update rust installation
next
Sayan Nandan 12 months ago
parent be540a7ded
commit a69093aa96
No known key found for this signature in database
GPG Key ID: 42EEDF4AE9D96B54

@ -36,6 +36,7 @@ jobs:
- name: Install Rust
run: |
rustup update ${{ matrix.rust }} --no-self-update
rustup update
rustup default ${{ matrix.rust }}
if: env.BUILD == 'true'
- name: Install perl modules

@ -109,6 +109,7 @@ jobs:
- name: Install Rust
run: |
rustup self update
rustup default stable
rustup target add ${{ matrix.rust }}
@ -153,6 +154,7 @@ jobs:
- name: Install Rust
run: |
rustup self update
rustup default stable
rustup target add ${{ matrix.rust }}

@ -25,7 +25,7 @@
*/
use {
crate::util::os::SysIOError,
crate::engine::error::RuntimeResult,
core::fmt,
serde::Deserialize,
std::{collections::HashMap, fs},
@ -294,9 +294,6 @@ impl DecodedEPInsecureConfig {
errors and misc
*/
/// Configuration result
pub type ConfigResult<T> = Result<T, ConfigError>;
#[derive(Debug)]
#[cfg_attr(test, derive(PartialEq))]
/// A configuration error (with an optional error origin source)
@ -332,10 +329,6 @@ impl fmt::Display for ConfigError {
"conflicting settings. please choose either CLI or ENV or configuration file"
),
ConfigErrorKind::ErrorString(e) => write!(f, "{e}"),
ConfigErrorKind::IoError(e) => write!(
f,
"an I/O error occurred while reading a configuration related file: `{e}`",
),
}
}
}
@ -369,20 +362,6 @@ pub enum ConfigErrorKind {
Conflict,
/// A custom error output
ErrorString(String),
/// An I/O error related to configuration
IoError(SysIOError),
}
direct_from! {
ConfigErrorKind => {
SysIOError as IoError,
}
}
impl From<std::io::Error> for ConfigError {
fn from(value: std::io::Error) -> Self {
Self::new(ConfigErrorKind::IoError(value.into()))
}
}
/// A configuration source implementation
@ -420,9 +399,9 @@ pub(super) trait ConfigurationSource {
fn argck_duplicate_values<CS: ConfigurationSource>(
v: &[String],
key: &'static str,
) -> ConfigResult<()> {
) -> RuntimeResult<()> {
if v.len() != 1 {
return Err(CS::err_too_many_values_for(key));
return Err(CS::err_too_many_values_for(key).into());
}
Ok(())
}
@ -438,14 +417,15 @@ enum ConnectionProtocol {
}
/// Parse an endpoint (`protocol@host:port`)
fn parse_endpoint(source: ConfigSource, s: &str) -> ConfigResult<(ConnectionProtocol, &str, u16)> {
fn parse_endpoint(source: ConfigSource, s: &str) -> RuntimeResult<(ConnectionProtocol, &str, u16)> {
let err = || {
Err(ConfigError::with_src(
source,
ConfigErrorKind::ErrorString(format!(
"invalid endpoint syntax. should be `protocol@hostname:port`"
)),
))
)
.into())
};
let x = s.split("@").collect::<Vec<&str>>();
if x.len() != 2 {
@ -475,7 +455,7 @@ fn decode_tls_ep(
pkey_pass: &str,
host: &str,
port: u16,
) -> ConfigResult<DecodedEPSecureConfig> {
) -> RuntimeResult<DecodedEPSecureConfig> {
let tls_key = fs::read_to_string(key_path)?;
let tls_cert = fs::read_to_string(cert_path)?;
let tls_priv_key_passphrase = fs::read_to_string(pkey_pass)?;
@ -493,7 +473,7 @@ fn arg_decode_tls_endpoint<CS: ConfigurationSource>(
args: &mut ParsedRawArgs,
host: &str,
port: u16,
) -> ConfigResult<DecodedEPSecureConfig> {
) -> RuntimeResult<DecodedEPSecureConfig> {
let _cert = args.remove(CS::KEY_TLS_CERT);
let _key = args.remove(CS::KEY_TLS_KEY);
let _passphrase = args.remove(CS::KEY_TLS_PKEY_PASS);
@ -508,7 +488,8 @@ fn arg_decode_tls_endpoint<CS: ConfigurationSource>(
CS::KEY_TLS_KEY,
CS::KEY_TLS_PKEY_PASS,
)),
));
)
.into());
}
};
argck_duplicate_values::<CS>(&tls_cert, CS::KEY_TLS_CERT)?;
@ -530,7 +511,7 @@ fn arg_decode_tls_endpoint<CS: ConfigurationSource>(
fn arg_decode_auth<CS: ConfigurationSource>(
src_args: &mut ParsedRawArgs,
config: &mut ModifyGuard<DecodedConfiguration>,
) -> ConfigResult<()> {
) -> RuntimeResult<()> {
let (Some(auth_driver), Some(mut root_key)) = (
src_args.remove(CS::KEY_AUTH_DRIVER),
src_args.remove(CS::KEY_AUTH_ROOT_PASSWORD),
@ -542,13 +523,14 @@ fn arg_decode_auth<CS: ConfigurationSource>(
CS::KEY_AUTH_DRIVER,
CS::KEY_AUTH_ROOT_PASSWORD
)),
));
)
.into());
};
argck_duplicate_values::<CS>(&auth_driver, CS::KEY_AUTH_DRIVER)?;
argck_duplicate_values::<CS>(&root_key, CS::KEY_AUTH_DRIVER)?;
let auth_plugin = match auth_driver[0].as_str() {
"pwd" => AuthDriver::Pwd,
_ => return Err(CS::err_invalid_value_for(CS::KEY_AUTH_DRIVER)),
_ => return Err(CS::err_invalid_value_for(CS::KEY_AUTH_DRIVER).into()),
};
config.auth = Some(DecodedAuth {
plugin: auth_plugin,
@ -561,14 +543,14 @@ fn arg_decode_auth<CS: ConfigurationSource>(
fn arg_decode_endpoints<CS: ConfigurationSource>(
args: &mut ParsedRawArgs,
config: &mut ModifyGuard<DecodedConfiguration>,
) -> ConfigResult<()> {
) -> RuntimeResult<()> {
let mut insecure = None;
let mut secure = None;
let Some(endpoints) = args.remove(CS::KEY_ENDPOINTS) else {
return Ok(());
};
if endpoints.len() > 2 {
return Err(CS::err_too_many_values_for(CS::KEY_ENDPOINTS));
return Err(CS::err_too_many_values_for(CS::KEY_ENDPOINTS).into());
}
for ep in endpoints {
let (proto, host, port) = parse_endpoint(CS::SOURCE, &ep)?;
@ -583,7 +565,8 @@ fn arg_decode_endpoints<CS: ConfigurationSource>(
return Err(CS::custom_err(format!(
"duplicate endpoints specified in `{}`",
CS::KEY_ENDPOINTS
)));
))
.into());
}
}
}
@ -599,12 +582,12 @@ fn arg_decode_endpoints<CS: ConfigurationSource>(
fn arg_decode_mode<CS: ConfigurationSource>(
mode: &[String],
config: &mut ModifyGuard<DecodedConfiguration>,
) -> ConfigResult<()> {
) -> RuntimeResult<()> {
argck_duplicate_values::<CS>(&mode, CS::KEY_RUN_MODE)?;
let mode = match mode[0].as_str() {
"dev" => ConfigMode::Dev,
"prod" => ConfigMode::Prod,
_ => return Err(CS::err_invalid_value_for(CS::KEY_RUN_MODE)),
_ => return Err(CS::err_invalid_value_for(CS::KEY_RUN_MODE).into()),
};
match config.system.as_mut() {
Some(s) => s.mode = Some(mode),
@ -622,7 +605,7 @@ fn arg_decode_mode<CS: ConfigurationSource>(
fn arg_decode_rs_window<CS: ConfigurationSource>(
mode: &[String],
config: &mut ModifyGuard<DecodedConfiguration>,
) -> ConfigResult<()> {
) -> RuntimeResult<()> {
argck_duplicate_values::<CS>(&mode, CS::KEY_SERVICE_WINDOW)?;
match mode[0].parse::<u64>() {
Ok(n) => match config.system.as_mut() {
@ -634,7 +617,7 @@ fn arg_decode_rs_window<CS: ConfigurationSource>(
})
}
},
Err(_) => return Err(CS::err_invalid_value_for(CS::KEY_SERVICE_WINDOW)),
Err(_) => return Err(CS::err_invalid_value_for(CS::KEY_SERVICE_WINDOW).into()),
}
Ok(())
}
@ -703,7 +686,7 @@ impl<T> CLIConfigParseReturn<T> {
/// - `--{option}={value}`
pub fn parse_cli_args<'a, T: 'a + AsRef<str>>(
src: impl Iterator<Item = T>,
) -> ConfigResult<CLIConfigParseReturn<ParsedRawArgs>> {
) -> RuntimeResult<CLIConfigParseReturn<ParsedRawArgs>> {
let mut args_iter = src.into_iter().skip(1);
let mut cli_args: ParsedRawArgs = HashMap::new();
while let Some(arg) = args_iter.next() {
@ -718,7 +701,8 @@ pub fn parse_cli_args<'a, T: 'a + AsRef<str>>(
return Err(ConfigError::with_src(
ConfigSource::Cli,
ConfigErrorKind::ErrorString(format!("unexpected argument `{arg}`")),
));
)
.into());
}
// x=1
let arg_key;
@ -734,13 +718,15 @@ pub fn parse_cli_args<'a, T: 'a + AsRef<str>>(
return Err(ConfigError::with_src(
ConfigSource::Cli,
ConfigErrorKind::ErrorString(format!("incorrectly formatted argument `{arg}`")),
));
)
.into());
} else {
let Some(value) = args_iter.next() else {
return Err(ConfigError::with_src(
ConfigSource::Cli,
ConfigErrorKind::ErrorString(format!("missing value for option `{arg}`")),
));
)
.into());
};
arg_key = arg;
arg_val = value.as_ref().to_string();
@ -767,7 +753,7 @@ pub fn parse_cli_args<'a, T: 'a + AsRef<str>>(
*/
/// Parse environment variables
pub fn parse_env_args() -> ConfigResult<Option<ParsedRawArgs>> {
pub fn parse_env_args() -> RuntimeResult<Option<ParsedRawArgs>> {
const KEYS: [&str; 8] = [
CSEnvArgs::KEY_AUTH_DRIVER,
CSEnvArgs::KEY_AUTH_ROOT_PASSWORD,
@ -788,7 +774,8 @@ pub fn parse_env_args() -> ConfigResult<Option<ParsedRawArgs>> {
return Err(ConfigError::with_src(
ConfigSource::Env,
ConfigErrorKind::ErrorString(format!("invalid value for `{key}`")),
))
)
.into())
}
},
};
@ -809,15 +796,15 @@ pub fn parse_env_args() -> ConfigResult<Option<ParsedRawArgs>> {
/// Apply the configuration changes to the given mutable config
fn apply_config_changes<CS: ConfigurationSource>(
args: &mut ParsedRawArgs,
) -> ConfigResult<ModifyGuard<DecodedConfiguration>> {
) -> RuntimeResult<ModifyGuard<DecodedConfiguration>> {
let mut config = ModifyGuard::new(DecodedConfiguration::default());
enum DecodeKind {
Simple {
key: &'static str,
f: fn(&[String], &mut ModifyGuard<DecodedConfiguration>) -> ConfigResult<()>,
f: fn(&[String], &mut ModifyGuard<DecodedConfiguration>) -> RuntimeResult<()>,
},
Complex {
f: fn(&mut ParsedRawArgs, &mut ModifyGuard<DecodedConfiguration>) -> ConfigResult<()>,
f: fn(&mut ParsedRawArgs, &mut ModifyGuard<DecodedConfiguration>) -> RuntimeResult<()>,
},
}
let decode_tasks = [
@ -856,7 +843,8 @@ fn apply_config_changes<CS: ConfigurationSource>(
Err(ConfigError::with_src(
CS::SOURCE,
ConfigErrorKind::ErrorString("found unknown arguments".into()),
))
)
.into())
} else {
Ok(config)
}
@ -933,7 +921,7 @@ fn validate_configuration<CS: ConfigurationSource>(
endpoints,
auth,
}: DecodedConfiguration,
) -> ConfigResult<Configuration> {
) -> RuntimeResult<Configuration> {
let Some(auth) = auth else {
return Err(ConfigError::with_src(
CS::SOURCE,
@ -941,7 +929,8 @@ fn validate_configuration<CS: ConfigurationSource>(
"root account must be configured with {}",
CS::KEY_AUTH_ROOT_PASSWORD
)),
));
)
.into());
};
// initialize our default configuration
let mut config = Configuration::default_dev_mode(auth);
@ -986,11 +975,11 @@ fn validate_configuration<CS: ConfigurationSource>(
if config.system.reliability_system_window == 0 => ConfigError::with_src(
CS::SOURCE,
ConfigErrorKind::ErrorString("invalid value for service window. must be nonzero".into()),
),
).into(),
if config.auth.root_key.len() < ROOT_PASSWORD_MIN_LEN => ConfigError::with_src(
CS::SOURCE,
ConfigErrorKind::ErrorString("the root password must have at least 16 characters".into()),
),
).into(),
);
Ok(config)
}
@ -1023,7 +1012,7 @@ impl ConfigReturn {
/// Apply the changes and validate the configuration
pub(super) fn apply_and_validate<CS: ConfigurationSource>(
mut args: ParsedRawArgs,
) -> ConfigResult<ConfigReturn> {
) -> RuntimeResult<ConfigReturn> {
let cfg = apply_config_changes::<CS>(&mut args)?;
if ModifyGuard::modified(&cfg) {
validate_configuration::<CS>(cfg.val).map(ConfigReturn::Config)
@ -1067,7 +1056,7 @@ pub(super) fn set_file_src(src: &str) {
s.borrow_mut().replace(src.to_string());
})
}
fn get_file_from_store(filename: &str) -> ConfigResult<String> {
fn get_file_from_store(filename: &str) -> RuntimeResult<String> {
let _f = filename;
let f;
#[cfg(test)]
@ -1122,7 +1111,7 @@ fn get_cli_from_store() -> Vec<String> {
/// - CLI args
/// - ENV variables
/// - Config file (if any)
pub fn check_configuration() -> ConfigResult<ConfigReturn> {
pub fn check_configuration() -> RuntimeResult<ConfigReturn> {
// read in our environment variables
let env_args = parse_env_args()?;
// read in our CLI args (since that can tell us whether we need a configuration file)
@ -1154,7 +1143,8 @@ pub fn check_configuration() -> ConfigResult<ConfigReturn> {
return Err(ConfigError::with_src(
ConfigSource::Cli,
ConfigErrorKind::Conflict,
));
)
.into());
}
return apply_and_validate::<CSCommandLine>(cfg_from_cli);
}
@ -1180,7 +1170,7 @@ fn check_config_file(
cfg_from_cli: &ParsedRawArgs,
env_args: &Option<ParsedRawArgs>,
cfg_file: &Vec<String>,
) -> ConfigResult<ConfigReturn> {
) -> RuntimeResult<ConfigReturn> {
if cfg_from_cli.len() == 1 && env_args.is_none() {
// yes, we only have the config file
argck_duplicate_values::<CSCommandLine>(&cfg_file, CSCommandLine::ARG_CONFIG_FILE)?;
@ -1214,9 +1204,6 @@ fn check_config_file(
return validate_configuration::<CSConfigFile>(config_from_file).map(ConfigReturn::Config);
} else {
// so there are more configuration options + a config file? (and maybe even env?)
return Err(ConfigError::with_src(
ConfigSource::Cli,
ConfigErrorKind::Conflict,
));
return Err(ConfigError::with_src(ConfigSource::Cli, ConfigErrorKind::Conflict).into());
}
}

@ -26,7 +26,7 @@
use crate::engine::{
core::{self, model::delta::DataDeltaKind},
error::{Error, QueryResult},
error::{QueryError, QueryResult},
fractal::GlobalInstanceLike,
idx::MTIndex,
ql::dml::del::DeleteStatement,
@ -55,7 +55,7 @@ pub fn delete(global: &impl GlobalInstanceLike, mut delete: DeleteStatement) ->
);
Ok(())
}
None => Err(Error::QPDmlRowNotFound),
None => Err(QueryError::QPDmlRowNotFound),
}
})
}

@ -30,7 +30,7 @@ use crate::engine::{
index::{DcFieldIndex, PrimaryIndexKey, Row},
model::{delta::DataDeltaKind, Fields, Model},
},
error::{Error, QueryResult},
error::{QueryError, QueryResult},
fractal::GlobalInstanceLike,
idx::{IndexBaseSpec, MTIndex, STIndex, STIndexSeq},
ql::dml::ins::{InsertData, InsertStatement},
@ -57,7 +57,7 @@ pub fn insert(global: &impl GlobalInstanceLike, insert: InsertStatement) -> Quer
);
Ok(())
} else {
Err(Error::QPDmlDuplicate)
Err(QueryError::QPDmlDuplicate)
}
})
}
@ -113,6 +113,6 @@ fn prepare_insert(
};
Ok((primary_key, prepared_data))
} else {
Err(Error::QPDmlValidationError)
Err(QueryError::QPDmlValidationError)
}
}

@ -33,7 +33,7 @@ use crate::{
engine::{
core::model::Model,
data::{lit::Lit, tag::DataTag},
error::{Error, QueryResult},
error::{QueryError, QueryResult},
ql::dml::WhereClause,
},
util::compiler,
@ -55,7 +55,7 @@ impl Model {
{
Ok(clause.rhs())
}
_ => compiler::cold_rerr(Error::QPDmlWhereHasUnindexedColumn),
_ => compiler::cold_rerr(QueryError::QPDmlWhereHasUnindexedColumn),
}
}
}

@ -27,7 +27,7 @@
use crate::engine::{
core::index::DcFieldIndex,
data::cell::{Datacell, VirtualDatacell},
error::{Error, QueryResult},
error::{QueryError, QueryResult},
fractal::GlobalInstanceLike,
idx::{STIndex, STIndexSeq},
ql::dml::sel::SelectStatement,
@ -51,7 +51,7 @@ where
match fields.st_get(key) {
Some(dc) => cellfn(dc),
None if key == mdl.p_key() => cellfn(&pkdc),
None => return Err(Error::QPUnknownField),
None => return Err(QueryError::QPUnknownField),
}
Ok(())
};
@ -68,7 +68,7 @@ where
}
}
}
None => return Err(Error::QPDmlRowNotFound),
None => return Err(QueryError::QPDmlRowNotFound),
}
Ok(())
})

@ -36,7 +36,7 @@ use {
lit::Lit,
tag::{DataTag, TagClass},
},
error::{Error, QueryResult},
error::{QueryError, QueryResult},
fractal::GlobalInstanceLike,
idx::STIndex,
ql::dml::upd::{AssignmentExpression, UpdateStatement},
@ -242,7 +242,7 @@ pub fn update(global: &impl GlobalInstanceLike, mut update: UpdateStatement) ->
// fetch row
let g = sync::atm::cpin();
let Some(row) = mdl.primary_index().select(key, &g) else {
return Err(Error::QPDmlRowNotFound);
return Err(QueryError::QPDmlRowNotFound);
};
// lock row
let mut row_data_wl = row.d_data().write();
@ -279,7 +279,7 @@ pub fn update(global: &impl GlobalInstanceLike, mut update: UpdateStatement) ->
_ => {
input_trace("fieldnotfound");
rollback_now = true;
ret = Err(Error::QPUnknownField);
ret = Err(QueryError::QPUnknownField);
break;
}
}
@ -313,20 +313,20 @@ pub fn update(global: &impl GlobalInstanceLike, mut update: UpdateStatement) ->
list.push(rhs.into());
} else {
rollback_now = true;
ret = Err(Error::SysOutOfMemory);
ret = Err(QueryError::SysOutOfMemory);
break;
}
}
} else {
input_trace("list;badtag");
rollback_now = true;
ret = Err(Error::QPDmlValidationError);
ret = Err(QueryError::QPDmlValidationError);
break;
}
}
_ => {
input_trace("unknown_reason;exitmainloop");
ret = Err(Error::QPDmlValidationError);
ret = Err(QueryError::QPDmlValidationError);
rollback_now = true;
break;
}

@ -38,7 +38,7 @@ use {
self::{model::Model, util::EntityLocator},
crate::engine::{
core::space::Space,
error::{Error, QueryResult},
error::{QueryError, QueryResult},
fractal::GlobalInstanceLike,
idx::{IndexST, STIndex},
},
@ -100,7 +100,7 @@ impl GlobalNS {
) -> QueryResult<T> {
let sread = self.index_space.read();
let Some(space) = sread.st_get(space) else {
return Err(Error::QPObjectNotFound);
return Err(QueryError::QPObjectNotFound);
};
f(space)
}

@ -33,7 +33,7 @@ use {
tag::{DataTag, TagClass},
DictEntryGeneric,
},
error::{Error, QueryResult},
error::{QueryError, QueryResult},
fractal::GlobalInstanceLike,
idx::{IndexST, IndexSTSeqCns, STIndex, STIndexSeq},
ql::{
@ -84,7 +84,7 @@ fn no_field(mr: &IWModel, new: &str) -> bool {
fn check_nullable(props: &mut HashMap<Box<str>, DictEntryGeneric>) -> QueryResult<bool> {
match props.remove("nullable") {
Some(DictEntryGeneric::Data(b)) if b.kind() == TagClass::Bool => Ok(b.bool()),
Some(_) => Err(Error::QPDdlInvalidProperties),
Some(_) => Err(QueryError::QPDdlInvalidProperties),
None => Ok(false),
}
}
@ -101,7 +101,7 @@ impl<'a> AlterPlan<'a> {
AlterKind::Remove(r) => {
let mut x = HashSet::new();
if !r.iter().all(|id| x.insert(id.as_str())) {
return Err(Error::QPDdlModelAlterIllegal);
return Err(QueryError::QPDdlModelAlterIllegal);
}
let mut not_found = false;
if r.iter().all(|id| {
@ -112,9 +112,9 @@ impl<'a> AlterPlan<'a> {
}) {
can_ignore!(AlterAction::Remove(r))
} else if not_found {
return Err(Error::QPUnknownField);
return Err(QueryError::QPUnknownField);
} else {
return Err(Error::QPDdlModelAlterIllegal);
return Err(QueryError::QPDdlModelAlterIllegal);
}
}
AlterKind::Add(new_fields) => {
@ -148,7 +148,7 @@ impl<'a> AlterPlan<'a> {
mv.guard_pk(&field_name)?;
// get the current field
let Some(current_field) = wm.fields().st_get(field_name.as_str()) else {
return Err(Error::QPUnknownField);
return Err(QueryError::QPUnknownField);
};
// check props
let is_nullable = check_nullable(&mut props)?;
@ -174,7 +174,7 @@ impl<'a> AlterPlan<'a> {
no_lock,
})
} else {
Err(Error::QPDdlModelAlterIllegal)
Err(QueryError::QPDdlModelAlterIllegal)
}
}
fn ldeltas(
@ -197,7 +197,7 @@ impl<'a> AlterPlan<'a> {
}
if layers.len() > current.layers().len() {
// simply a dumb tomato; ELIMINATE THESE DUMB TOMATOES
return Err(Error::QPDdlModelAlterIllegal);
return Err(QueryError::QPDdlModelAlterIllegal);
}
let mut no_lock = !(current.is_nullable() & !nullable);
let mut deltasize = (current.is_nullable() ^ nullable) as usize;
@ -216,7 +216,7 @@ impl<'a> AlterPlan<'a> {
// actually parse the new layer
okay &= props.is_empty();
let Some(new_parsed_layer) = Layer::get_layer(&ty) else {
return Err(Error::QPDdlInvalidTypeDefinition);
return Err(QueryError::QPDdlInvalidTypeDefinition);
};
match (
current_layer.tag.tag_selector(),
@ -233,7 +233,7 @@ impl<'a> AlterPlan<'a> {
}
_ => {
// can't cast this directly
return Err(Error::QPDdlInvalidTypeDefinition);
return Err(QueryError::QPDdlInvalidTypeDefinition);
}
}
*new_layer = new_parsed_layer;
@ -243,7 +243,7 @@ impl<'a> AlterPlan<'a> {
if okay {
Ok((deltasize != 0, new_field))
} else {
Err(Error::QPDdlModelAlterIllegal)
Err(QueryError::QPDdlModelAlterIllegal)
}
}
}
@ -263,7 +263,7 @@ impl Model {
// we have a legal plan; acquire exclusive if we need it
if !plan.no_lock {
// TODO(@ohsayan): allow this later on, once we define the syntax
return Err(Error::QPNeedLock);
return Err(QueryError::QPNeedLock);
}
// fine, we're good
let mut iwm = iwm;

@ -39,7 +39,7 @@ use {
tag::{DataTag, FullTag, TagClass, TagSelector},
uuid::Uuid,
},
error::{Error, QueryResult},
error::{QueryError, QueryResult},
fractal::{GenericTask, GlobalInstanceLike, Task},
idx::{IndexBaseSpec, IndexSTSeqCns, STIndex, STIndexSeq},
mem::VInline,
@ -134,7 +134,7 @@ impl Model {
}
fn guard_pk(&self, new: &str) -> QueryResult<()> {
if self.is_pk(new) {
Err(Error::QPDdlModelAlterIllegal)
Err(QueryError::QPDdlModelAlterIllegal)
} else {
Ok(())
}
@ -199,7 +199,7 @@ impl Model {
return Ok(Self::new_restore(Uuid::new(), last_pk.into(), tag, fields));
}
}
Err(Error::QPDdlModelBadDefinition)
Err(QueryError::QPDdlModelBadDefinition)
}
}
@ -213,7 +213,7 @@ impl Model {
global.namespace().with_space(space_name, |space| {
let mut w_space = space.models().write();
if w_space.st_contains(model_name) {
return Err(Error::QPDdlObjectAlreadyExists);
return Err(QueryError::QPDdlObjectAlreadyExists);
}
if G::FS_IS_NON_NULL {
let irm = model.intent_read_model();
@ -262,7 +262,7 @@ impl Model {
global.namespace().with_space(space_name, |space| {
let mut w_space = space.models().write();
let Some(model) = w_space.get(model_name) else {
return Err(Error::QPObjectNotFound);
return Err(QueryError::QPObjectNotFound);
};
if G::FS_IS_NON_NULL {
// prepare txn
@ -361,7 +361,7 @@ impl Field {
nullable,
})
} else {
Err(Error::QPDdlInvalidTypeDefinition)
Err(QueryError::QPDdlInvalidTypeDefinition)
}
}
#[inline(always)]

@ -28,7 +28,7 @@ use {
crate::engine::{
core::{model::Model, RWLIdx},
data::{dict, uuid::Uuid, DictEntryGeneric, DictGeneric},
error::{Error, QueryResult},
error::{QueryError, QueryResult},
fractal::{GenericTask, GlobalInstanceLike, Task},
idx::{IndexST, STIndex},
ql::ddl::{alt::AlterSpace, crt::CreateSpace, drop::DropSpace},
@ -108,7 +108,7 @@ impl Space {
{
Ok(())
} else {
Err(Error::QPDdlObjectAlreadyExists)
Err(QueryError::QPDdlObjectAlreadyExists)
}
}
pub fn get_uuid(&self) -> Uuid {
@ -127,7 +127,7 @@ impl Space {
) -> QueryResult<T> {
let mread = self.mns.read();
let Some(model) = mread.st_get(model) else {
return Err(Error::QPObjectNotFound);
return Err(QueryError::QPObjectNotFound);
};
f(model)
}
@ -174,7 +174,7 @@ impl Space {
None if props.is_empty() => IndexST::default(),
_ => {
// unknown properties
return Err(Error::QPDdlInvalidProperties);
return Err(QueryError::QPDdlInvalidProperties);
}
};
Ok(ProcedureCreate {
@ -200,7 +200,7 @@ impl Space {
// acquire access
let mut wl = global.namespace().spaces().write();
if wl.st_contains(&space_name) {
return Err(Error::QPDdlObjectAlreadyExists);
return Err(QueryError::QPDdlObjectAlreadyExists);
}
// commit txn
if G::FS_IS_NON_NULL {
@ -240,13 +240,13 @@ impl Space {
Some(DictEntryGeneric::Map(_)) if updated_props.len() == 1 => {}
Some(DictEntryGeneric::Data(l)) if updated_props.len() == 1 && l.is_null() => {}
None if updated_props.is_empty() => return Ok(()),
_ => return Err(Error::QPDdlInvalidProperties),
_ => return Err(QueryError::QPDdlInvalidProperties),
}
let mut space_props = space.meta.dict().write();
// create patch
let patch = match dict::rprepare_metadata_patch(&space_props, updated_props) {
Some(patch) => patch,
None => return Err(Error::QPDdlInvalidProperties),
None => return Err(QueryError::QPDdlInvalidProperties),
};
if G::FS_IS_NON_NULL {
// prepare txn
@ -276,11 +276,11 @@ impl Space {
let mut wgns = global.namespace().spaces().write();
let space = match wgns.get(space_name.as_str()) {
Some(space) => space,
None => return Err(Error::QPObjectNotFound),
None => return Err(QueryError::QPObjectNotFound),
};
let space_w = space.mns.write();
if space_w.st_len() != 0 {
return Err(Error::QPDdlNotEmpty);
return Err(QueryError::QPDdlNotEmpty);
}
// we can remove this
if G::FS_IS_NON_NULL {

@ -77,7 +77,7 @@ mod plan {
use crate::{
engine::{
core::model::{self, alt::AlterAction, Field, Layer},
error::Error,
error::QueryError,
},
vecfuse,
};
@ -164,7 +164,7 @@ mod plan {
|_| {}
)
.unwrap_err(),
Error::QPUnknownField
QueryError::QPUnknownField
);
}
#[test]
@ -176,7 +176,7 @@ mod plan {
|_| {}
)
.unwrap_err(),
Error::QPDdlModelAlterIllegal
QueryError::QPDdlModelAlterIllegal
);
}
#[test]
@ -188,7 +188,7 @@ mod plan {
|_| {}
)
.unwrap_err(),
Error::QPDdlModelAlterIllegal
QueryError::QPDdlModelAlterIllegal
);
}
#[test]
@ -200,7 +200,7 @@ mod plan {
|_| {}
)
.unwrap_err(),
Error::QPDdlModelAlterIllegal
QueryError::QPDdlModelAlterIllegal
);
}
#[test]
@ -212,7 +212,7 @@ mod plan {
|_| {}
)
.unwrap_err(),
Error::QPDdlModelAlterIllegal
QueryError::QPDdlModelAlterIllegal
);
}
#[test]
@ -224,7 +224,7 @@ mod plan {
|_| {}
)
.unwrap_err(),
Error::QPUnknownField
QueryError::QPUnknownField
);
}
fn bad_type_cast(orig_ty: &str, new_ty: &str) {
@ -235,7 +235,7 @@ mod plan {
super::with_plan(&create, &alter, |_| {}).expect_err(&format!(
"found no error in transformation: {orig_ty} -> {new_ty}"
)),
Error::QPDdlInvalidTypeDefinition,
QueryError::QPDdlInvalidTypeDefinition,
"failed to match error in transformation: {orig_ty} -> {new_ty}",
)
}
@ -353,7 +353,7 @@ mod plan {
mod exec {
use crate::engine::{
core::model::{DeltaVersion, Field, Layer},
error::Error,
error::QueryError,
fractal::test_utils::TestGlobal,
idx::{STIndex, STIndexSeq},
};
@ -445,7 +445,7 @@ mod exec {
|_| {},
)
.unwrap_err(),
Error::QPNeedLock
QueryError::QPNeedLock
);
}
}

@ -30,7 +30,7 @@ mod validation {
crate::engine::{
core::model::{DeltaVersion, Field, Layer},
data::tag::{DataTag, FullTag},
error::Error,
error::QueryError,
idx::STIndexSeq,
},
};
@ -89,7 +89,7 @@ mod validation {
"create model mymodel(primary username: string, primary contract_location: binary)"
)
.unwrap_err(),
Error::QPDdlModelBadDefinition
QueryError::QPDdlModelBadDefinition
);
}
@ -97,7 +97,7 @@ mod validation {
fn duplicate_fields() {
assert_eq!(
create("create model mymodel(primary username: string, username: binary)").unwrap_err(),
Error::QPDdlModelBadDefinition
QueryError::QPDdlModelBadDefinition
);
}
@ -105,7 +105,7 @@ mod validation {
fn illegal_props() {
assert_eq!(
create("create model mymodel(primary username: string, password: binary) with { lol_prop: false }").unwrap_err(),
Error::QPDdlModelBadDefinition
QueryError::QPDdlModelBadDefinition
);
}
@ -116,12 +116,12 @@ mod validation {
"create model mymodel(primary username_bytes: list { type: uint8 }, password: binary)"
)
.unwrap_err(),
Error::QPDdlModelBadDefinition
QueryError::QPDdlModelBadDefinition
);
assert_eq!(
create("create model mymodel(primary username: float32, password: binary)")
.unwrap_err(),
Error::QPDdlModelBadDefinition
QueryError::QPDdlModelBadDefinition
);
}
}

@ -42,7 +42,7 @@ fn layerview(layer_def: &str) -> QueryResult<Field> {
mod layer_spec_validation {
use {
super::layerview,
crate::engine::{core::model::Layer, error::Error},
crate::engine::{core::model::Layer, error::QueryError},
};
#[test]
@ -64,7 +64,7 @@ mod layer_spec_validation {
fn invalid_list() {
assert_eq!(
layerview("list").unwrap_err(),
Error::QPDdlInvalidTypeDefinition
QueryError::QPDdlInvalidTypeDefinition
);
}
@ -72,7 +72,7 @@ mod layer_spec_validation {
fn invalid_flat() {
assert_eq!(
layerview("string { type: string }").unwrap_err(),
Error::QPDdlInvalidTypeDefinition
QueryError::QPDdlInvalidTypeDefinition
);
}
}

@ -27,7 +27,7 @@
use crate::engine::{
core::space::{Space, SpaceMeta},
data::cell::Datacell,
error::Error,
error::QueryError,
fractal::test_utils::TestGlobal,
};
@ -122,7 +122,7 @@ fn alter_nx() {
|_| {},
)
.unwrap_err(),
Error::QPObjectNotFound
QueryError::QPObjectNotFound
);
}

@ -27,7 +27,7 @@
use crate::engine::{
core::space::{Space, SpaceMeta},
data::cell::Datacell,
error::Error,
error::QueryError,
fractal::test_utils::TestGlobal,
};
@ -73,7 +73,7 @@ fn exec_create_space_with_bad_env_type() {
let global = TestGlobal::new_with_tmp_nullfs_driver();
assert_eq!(
super::exec_create(&global, "create space myspace with { env: 100 }", |_| {}).unwrap_err(),
Error::QPDdlInvalidProperties
QueryError::QPDdlInvalidProperties
);
}
@ -87,6 +87,6 @@ fn exec_create_space_with_random_property() {
|_| {}
)
.unwrap_err(),
Error::QPDdlInvalidProperties
QueryError::QPDdlInvalidProperties
);
}

@ -24,7 +24,7 @@
*
*/
use crate::engine::{error::Error, fractal::test_utils::TestGlobal};
use crate::engine::{error::QueryError, fractal::test_utils::TestGlobal};
#[test]
fn simple_delete() {
@ -51,6 +51,6 @@ fn delete_nonexisting() {
"sayan",
)
.unwrap_err(),
Error::QPDmlRowNotFound
QueryError::QPDmlRowNotFound
);
}

@ -24,7 +24,7 @@
*
*/
use crate::engine::{data::cell::Datacell, error::Error, fractal::test_utils::TestGlobal};
use crate::engine::{data::cell::Datacell, error::QueryError, fractal::test_utils::TestGlobal};
#[derive(sky_macros::Wrapper, Debug)]
struct Tuple(Vec<(Box<str>, Datacell)>);
@ -83,6 +83,6 @@ fn insert_duplicate() {
assert_eq!(
super::exec_insert_only(&global, "insert into myspace.mymodel('sayan', 'pass123')")
.unwrap_err(),
Error::QPDmlDuplicate
QueryError::QPDmlDuplicate
);
}

@ -24,7 +24,7 @@
*
*/
use crate::engine::{data::cell::Datacell, error::Error, fractal::test_utils::TestGlobal};
use crate::engine::{data::cell::Datacell, error::QueryError, fractal::test_utils::TestGlobal};
#[test]
fn simple_select_wildcard() {
@ -97,6 +97,6 @@ fn select_nonexisting() {
"select username, password from myspace.mymodel where username = 'notsayan'",
)
.unwrap_err(),
Error::QPDmlRowNotFound
QueryError::QPDmlRowNotFound
);
}

@ -25,7 +25,7 @@
*/
use crate::engine::{
core::dml, data::cell::Datacell, error::Error, fractal::test_utils::TestGlobal,
core::dml, data::cell::Datacell, error::QueryError, fractal::test_utils::TestGlobal,
};
#[test]
@ -96,7 +96,7 @@ fn fail_operation_on_null() {
"select * from myspace.mymodel where username='sayan'"
)
.unwrap_err(),
Error::QPDmlValidationError
QueryError::QPDmlValidationError
);
assert_eq!(
dml::update_flow_trace(),
@ -116,7 +116,7 @@ fn fail_unknown_fields() {
"select * from myspace.mymodel where username='sayan'"
)
.unwrap_err(),
Error::QPUnknownField
QueryError::QPUnknownField
);
assert_eq!(dml::update_flow_trace(), ["fieldnotfound", "rollback"]);
// verify integrity
@ -142,7 +142,7 @@ fn fail_typedef_violation() {
"select * from myspace.mymodel where username = 'sayan'"
)
.unwrap_err(),
Error::QPDmlValidationError
QueryError::QPDmlValidationError
);
assert_eq!(
dml::update_flow_trace(),

@ -25,7 +25,7 @@
*/
use crate::engine::{
error::{Error, QueryResult},
error::{QueryError, QueryResult},
ql::ast::Entity,
};
@ -46,6 +46,6 @@ impl<'a> EntityLocator<'a> for Entity<'a> {
where
Self: 'a,
{
self.into_full_str().ok_or(Error::QPExpectedEntity)
self.into_full_str().ok_or(QueryError::QPExpectedEntity)
}
}

@ -24,27 +24,17 @@
*
*/
use {
super::{
storage::v1::{SDSSError, SDSSErrorKind},
txn::TransactionError,
},
crate::util::os::SysIOError,
std::fmt,
};
use {super::config::ConfigError, crate::util::os::SysIOError, std::fmt};
pub type QueryResult<T> = Result<T, Error>;
// stack
pub type CtxResult<T, E> = Result<T, CtxError<E>>;
pub type RuntimeResult<T> = CtxResult<T, RuntimeErrorKind>;
pub type RuntimeError = CtxError<RuntimeErrorKind>;
pub type RuntimeResult<T> = Result<T, super::fractal::error::Error>;
pub type QueryResult<T> = Result<T, QueryError>;
/// an enumeration of 'flat' errors that the server actually responds to the client with, since we do not want to send specific information
/// about anything (as that will be a security hole). The variants correspond with their actual response codes
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Error {
pub enum QueryError {
/// I/O error
SysIOError,
SysServerError,
/// out of memory
SysOutOfMemory,
/// unknown server error
@ -108,125 +98,131 @@ pub enum Error {
SysAuthError,
}
direct_from! {
Error[_] => {
SDSSError as StorageSubsystemError,
TransactionError as TransactionalError,
impl From<super::fractal::error::Error> for QueryError {
fn from(e: super::fractal::error::Error) -> Self {
match e.kind() {
ErrorKind::IoError(_) | ErrorKind::Storage(_) => QueryError::SysServerError,
ErrorKind::Txn(_) => QueryError::TransactionalError,
ErrorKind::Other(_) => QueryError::SysUnknownError,
ErrorKind::Config(_) => unreachable!("config error cannot propagate here"),
}
}
}
/*
contextual errors
*/
/// An error context
pub enum CtxErrorDescription {
A(&'static str),
B(Box<str>),
}
impl CtxErrorDescription {
fn inner(&self) -> &str {
match self {
Self::A(a) => a,
Self::B(b) => &b,
macro_rules! enumerate_err {
($(#[$attr:meta])* $vis:vis enum $errname:ident { $($(#[$varattr:meta])* $variant:ident = $errstring:expr),* $(,)? }) => {
$(#[$attr])*
$vis enum $errname { $($(#[$varattr])* $variant),* }
impl core::fmt::Display for $errname {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {$( Self::$variant => write!(f, "{}", $errstring),)*}
}
}
impl std::error::Error for $errname {}
}
}
impl PartialEq for CtxErrorDescription {
fn eq(&self, other: &Self) -> bool {
self.inner() == other.inner()
}
#[derive(Debug)]
#[cfg_attr(test, derive(PartialEq))]
/// A "master" error kind enumeration for all kinds of runtime errors
pub enum ErrorKind {
/// An I/O error
IoError(SysIOError),
/// An SDSS error
Storage(StorageError),
/// A transactional error
Txn(TransactionError),
/// other errors
Other(String),
/// configuration errors
Config(ConfigError),
}
impl fmt::Display for CtxErrorDescription {
impl fmt::Display for ErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.inner())
match self {
Self::IoError(io) => write!(f, "io error: {io}"),
Self::Storage(se) => write!(f, "storage error: {se}"),
Self::Txn(txe) => write!(f, "txn error: {txe}"),
Self::Other(oe) => write!(f, "error: {oe}"),
Self::Config(cfg) => write!(f, "config error: {cfg}"),
}
}
}
impl fmt::Debug for CtxErrorDescription {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.inner())
}
}
impl std::error::Error for ErrorKind {}
direct_from! {
CtxErrorDescription => {
&'static str as A,
String as B,
Box<str> as B,
}
}
#[derive(Debug)]
#[cfg_attr(test, derive(PartialEq))]
/// A contextual error
pub struct CtxError<E> {
kind: E,
ctx: Option<CtxErrorDescription>,
}
impl<E> CtxError<E> {
fn _new(kind: E, ctx: Option<CtxErrorDescription>) -> Self {
Self { kind, ctx }
}
pub fn new(kind: E) -> Self {
Self::_new(kind, None)
}
pub fn with_ctx(kind: E, ctx: impl Into<CtxErrorDescription>) -> Self {
Self::_new(kind, Some(ctx.into()))
}
pub fn add_ctx(self, ctx: impl Into<CtxErrorDescription>) -> Self {
Self::with_ctx(self.kind, ctx)
}
pub fn into_result<T>(self) -> CtxResult<T, E> {
Err(self)
}
pub fn result<T, F>(result: Result<T, F>) -> CtxResult<T, E>
where
E: From<F>,
{
result.map_err(|e| CtxError::new(e.into()))
}
pub fn result_ctx<T, F>(
result: Result<T, F>,
ctx: impl Into<CtxErrorDescription>,
) -> CtxResult<T, E>
where
E: From<F>,
{
result.map_err(|e| CtxError::with_ctx(e.into(), ctx))
ErrorKind => {
std::io::Error as IoError,
SysIOError as IoError,
}
}
macro_rules! impl_from_hack {
($($ty:ty),*) => {
$(impl<E> From<E> for CtxError<$ty> where E: Into<$ty> {fn from(e: E) -> Self { CtxError::new(e.into()) }})*
enumerate_err! {
#[derive(Debug, PartialEq)]
/// Errors that occur when restoring transactional data
pub enum TransactionError {
/// corrupted txn payload. has more bytes than expected
DecodeCorruptedPayloadMoreBytes = "txn-payload-unexpected-content",
/// transaction payload is corrupted. has lesser bytes than expected
DecodedUnexpectedEof = "txn-payload-unexpected-eof",
/// unknown transaction operation. usually indicates a corrupted payload
DecodeUnknownTxnOp = "txn-payload-unknown-payload",
/// While restoring a certain item, a non-resolvable conflict was encountered in the global state, because the item was
/// already present (when it was expected to not be present)
OnRestoreDataConflictAlreadyExists = "txn-payload-conflict-already-exists",
/// On restore, a certain item that was expected to be present was missing in the global state
OnRestoreDataMissing = "txn-payload-conflict-missing",
/// On restore, a certain item that was expected to match a certain value, has a different value
OnRestoreDataConflictMismatch = "txn-payload-conflict-mismatch",
/// out of memory
OutOfMemory = "txn-error-oom",
}
}
/*
Contextual error impls
*/
impl_from_hack!(RuntimeErrorKind, SDSSErrorKind);
#[derive(Debug)]
pub enum RuntimeErrorKind {
StorageSubsytem(SDSSError),
IoError(SysIOError),
OSSLErrorMulti(openssl::error::ErrorStack),
OSSLError(openssl::ssl::Error),
}
direct_from! {
RuntimeErrorKind => {
SDSSError as StorageSubsytem,
std::io::Error as IoError,
SysIOError as IoError,
openssl::error::ErrorStack as OSSLErrorMulti,
openssl::ssl::Error as OSSLError,
enumerate_err! {
#[derive(Debug, PartialEq)]
/// SDSS based storage engine errors
pub enum StorageError {
// header
/// version mismatch
HeaderDecodeVersionMismatch = "header-version-mismatch",
/// The entire header is corrupted
HeaderDecodeCorruptedHeader = "header-corrupted",
/// Expected header values were not matched with the current header
HeaderDecodeDataMismatch = "header-data-mismatch",
/// The time in the [header/dynrec/rtsig] is in the future
HeaderTimeConflict = "header-invalid-time",
// journal
/// While attempting to handle a basic failure (such as adding a journal entry), the recovery engine ran into an exceptional
/// situation where it failed to make a necessary repair the log
JournalWRecoveryStageOneFailCritical = "journal-recovery-failure",
/// An entry in the journal is corrupted
JournalLogEntryCorrupted = "journal-entry-corrupted",
/// The structure of the journal is corrupted
JournalCorrupted = "journal-corrupted",
/// when restoring the journal, a transactional error (i.e constraint violation) occurred
JournalRestoreTxnError = "journal-illegal-data",
// internal file structures
/// While attempting to decode a structure in an internal segment of a file, the storage engine ran into a possibly irrecoverable error
InternalDecodeStructureCorrupted = "structure-decode-corrupted",
/// the payload (non-static) part of a structure in an internal segment of a file is corrupted
InternalDecodeStructureCorruptedPayload = "structure-decode-corrupted-payload",
/// the data for an internal structure was decoded but is logically invalid
InternalDecodeStructureIllegalData = "structure-decode-illegal-data",
/// when attempting to flush a data batch, the batch journal crashed and a recovery event was triggered. But even then,
/// the data batch journal could not be fixed
DataBatchRecoveryFailStageOne = "batch-recovery-failure",
/// when attempting to restore a data batch from disk, the batch journal crashed and had a corruption, but it is irrecoverable
DataBatchRestoreCorruptedBatch = "batch-corrupted-batch",
/// when attempting to restore a data batch from disk, the driver encountered a corrupted entry
DataBatchRestoreCorruptedEntry = "batch-corrupted-entry",
/// we failed to close the data batch
DataBatchCloseError = "batch-persist-close-failed",
/// the data batch file is corrupted
DataBatchRestoreCorruptedBatchFile = "batch-corrupted-file",
/// the system database is corrupted
SysDBCorrupted = "sysdb-corrupted",
}
}

@ -27,7 +27,7 @@
use crate::engine::config::ConfigAuth;
use {
crate::engine::error::{Error, QueryResult},
crate::engine::error::{QueryError, QueryResult},
parking_lot::RwLock,
std::collections::{hash_map::Entry, HashMap},
};
@ -151,7 +151,7 @@ impl SysAuth {
));
Ok(())
}
Entry::Occupied(_) => Err(Error::SysAuthError),
Entry::Occupied(_) => Err(QueryError::SysAuthError),
}
}
/// Verify the user with the given details
@ -160,12 +160,12 @@ impl SysAuth {
if rcrypt::verify(password, self.root_key()).unwrap() {
return Ok(());
} else {
return Err(Error::SysAuthError);
return Err(QueryError::SysAuthError);
}
}
match self.users.get(username) {
Some(user) if rcrypt::verify(password, user.key()).unwrap() => Ok(()),
Some(_) | None => Err(Error::SysAuthError),
Some(_) | None => Err(QueryError::SysAuthError),
}
}
pub fn root_key(&self) -> &[u8] {

@ -0,0 +1,195 @@
/*
* Created on Sun Oct 01 2023
*
* This file is a part of Skytable
* Skytable (formerly known as TerrabaseDB or Skybase) is a free and open-source
* NoSQL database written by Sayan Nandan ("the Author") with the
* vision to provide flexibility in data modelling without compromising
* on performance, queryability or scalability.
*
* Copyright (c) 2023, Sayan Nandan <ohsayan@outlook.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
use core::fmt;
use std::cell::RefCell;
/// The current engine context
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum Subsystem {
Init, // the init system
Storage, // the storage engine
Database, // the database engine
Network, // the network layer
}
impl Subsystem {
pub const fn as_str(self) -> &'static str {
match self {
Self::Init => "init system",
Self::Storage => "storage error",
Self::Database => "engine error",
Self::Network => "network error",
}
}
}
/*
diagnostics
*/
#[derive(Clone)]
/// A dmsg
pub enum Dmsg {
A(Box<str>),
B(&'static str),
}
impl PartialEq for Dmsg {
fn eq(&self, other: &Self) -> bool {
self.as_ref() == other.as_ref()
}
}
impl AsRef<str> for Dmsg {
fn as_ref(&self) -> &str {
match self {
Self::A(a) => a,
Self::B(b) => b,
}
}
}
direct_from! {
Dmsg => {
String as A,
Box<str> as A,
&'static str as B,
}
}
impl fmt::Display for Dmsg {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
<str as fmt::Display>::fmt(self.as_ref(), f)
}
}
impl fmt::Debug for Dmsg {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
<str as fmt::Debug>::fmt(self.as_ref(), f)
}
}
/*
context
*/
macro_rules! exported {
($($vis:vis impl $ty:ty { $($(#[$attr:meta])* $fnvis:vis fn $fn:ident($($fnarg:ident: $fnarg_ty:ty),*) $(-> $fnret:ty)? $fnblock:block)*})*) => {
$(impl $ty { $( $(#[$attr])* $fnvis fn $fn($($fnarg: $fnarg_ty),*) $( -> $fnret)? $fnblock )*}
$($(#[$attr])* $vis fn $fn($($fnarg: $fnarg_ty),*) $( -> $fnret)? { <$ty>::$fn($($fnarg),*) })*)*
}
}
struct LocalContext {
origin: Option<Subsystem>,
dmsg: Option<Dmsg>,
}
fn if_test(f: impl FnOnce()) {
if cfg!(test) {
f()
}
}
/// A copy of the local context (might be either popped or cloned)
#[derive(Debug, PartialEq, Clone)]
pub struct LocalCtxInstance {
origin: Option<Subsystem>,
dmsg: Option<Dmsg>,
}
impl LocalCtxInstance {
fn new(origin: Option<Subsystem>, dmsg: Option<Dmsg>) -> Self {
Self { origin, dmsg }
}
pub fn origin(&self) -> Option<Subsystem> {
self.origin
}
pub fn dmsg(&self) -> Option<&Dmsg> {
self.dmsg.as_ref()
}
}
impl From<LocalContext> for LocalCtxInstance {
fn from(LocalContext { origin, dmsg }: LocalContext) -> Self {
Self { origin, dmsg }
}
}
exported! {
pub impl LocalContext {
// all
fn set(origin: Subsystem, msg: impl Into<Dmsg>) { Self::_ctx(|ctx| { ctx.origin = Some(origin); ctx.dmsg = Some(msg.into()) }) }
fn test_set(origin: Subsystem, msg: impl Into<Dmsg>) { if_test(|| Self::set(origin, msg)) }
// dmsg
/// set a local dmsg
fn set_dmsg(msg: impl Into<Dmsg>) { Self::_ctx(|ctx| ctx.dmsg = Some(msg.into())) }
/// (only in test) set a local dmsg
fn test_set_dmsg(msg: impl Into<Dmsg>) { if_test(|| Self::set_dmsg(msg)) }
/// Set a local dmsg iff not already set
fn set_dmsg_if_unset(msg: impl Into<Dmsg>) { Self::_ctx(|ctx| { ctx.dmsg.get_or_insert(msg.into()); }) }
/// (only in test) set a local dmsg iff not already set
fn test_set_dmsg_if_unset(msg: impl Into<Dmsg>) { if_test(|| Self::set_dmsg_if_unset(msg)) }
// origin
/// set a local origin
fn set_origin(origin: Subsystem) { Self::_ctx(|ctx| ctx.origin = Some(origin)) }
/// (only in test) set a local origin
fn test_set_origin(origin: Subsystem) { if_test(|| Self::set_origin(origin)) }
/// set origin iff unset
fn set_origin_if_unset(origin: Subsystem) { Self::_ctx(|ctx| { ctx.origin.get_or_insert(origin); }) }
/// (only in test) set a local origin iff not already set
fn test_set_origin_if_unset(origin: Subsystem) { if_test(|| Self::set_origin_if_unset(origin)) }
}
pub(super) impl LocalContext {
// alter context
/// pop the origin from the local context
fn pop_origin() -> Option<Subsystem> { Self::_ctx(|ctx| ctx.origin.take()) }
/// pop the dmsg from the local context
fn pop_dmsg() -> Option<Dmsg> { Self::_ctx(|ctx| ctx.dmsg.take()) }
/// pop the entire context
fn pop() -> LocalCtxInstance { Self::_ctx(|ctx| core::mem::replace(ctx, LocalContext::null()).into()) }
/// get the origin
fn get_origin() -> Option<Subsystem> { Self::_ctx(|ctx| ctx.origin.clone()) }
/// get the dmsg
fn get_dmsg() -> Option<Dmsg> { Self::_ctx(|ctx| ctx.dmsg.clone()) }
/// get a clone of the local context
fn cloned() -> LocalCtxInstance { Self::_ctx(|ctx| LocalCtxInstance::new(ctx.origin.clone(), ctx.dmsg.clone())) }
}
}
impl LocalContext {
fn _new(origin: Option<Subsystem>, dmsg: Option<Dmsg>) -> Self {
Self { origin, dmsg }
}
fn null() -> Self {
Self::_new(None, None)
}
fn _ctx<T>(f: impl FnOnce(&mut Self) -> T) -> T {
thread_local! { static CTX: RefCell<LocalContext> = RefCell::new(LocalContext::null()) }
CTX.with(|lctx| f(&mut lctx.borrow_mut()))
}
}

@ -0,0 +1,340 @@
/*
* Created on Mon Oct 02 2023
*
* This file is a part of Skytable
* Skytable (formerly known as TerrabaseDB or Skybase) is a free and open-source
* NoSQL database written by Sayan Nandan ("the Author") with the
* vision to provide flexibility in data modelling without compromising
* on performance, queryability or scalability.
*
* Copyright (c) 2023, Sayan Nandan <ohsayan@outlook.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
use {
super::context::{self, Dmsg, Subsystem},
crate::engine::{
config::ConfigError,
error::{ErrorKind, StorageError, TransactionError},
},
core::fmt,
};
#[derive(Debug)]
#[cfg_attr(test, derive(PartialEq))]
/// An error implementation with context tracing and propagation
///
/// - All errors that are classified in [`ErrorKind`] will automatically inherit all local context, unless explicitly orphaned,
/// or manually constructed (see [`IntoError::err_noinherit`])
/// - All other errors will generally take the context from parent
///
/// Error propagation and tracing relies on the fact that the first error that occurs will end the routine in question, entering
/// a new local context; if otherwise, it will fail. To manage such custom conditions, look at [`ErrorContext`] or manually
/// constructing [`Error`]s.
pub struct Error {
kind: ErrorKind,
origin: Option<Subsystem>,
dmsg: Option<Dmsg>,
}
impl Error {
/// Returns the diagnostic message
pub fn dmsg(&self) -> Option<&Dmsg> {
self.dmsg.as_ref()
}
/// Returns the origin
pub fn origin(&self) -> Option<Subsystem> {
self.origin
}
/// Returns the error kind
pub fn kind(&self) -> &ErrorKind {
&self.kind
}
/// Replace the origin in self
pub fn add_origin(self, origin: Subsystem) -> Self {
Self::_new(self.kind, Some(origin), self.dmsg)
}
/// Replace the dmsg in self
pub fn add_dmsg(self, dmsg: impl Into<Dmsg>) -> Self {
Self::_new(self.kind, self.origin, Some(dmsg.into()))
}
}
impl Error {
/// ctor
fn _new(kind: ErrorKind, origin: Option<Subsystem>, dmsg: Option<Dmsg>) -> Self {
Self { kind, origin, dmsg }
}
/// new full error
pub fn new(kind: ErrorKind, origin: Subsystem, dmsg: impl Into<Dmsg>) -> Self {
Self::_new(kind, Some(origin), Some(dmsg.into()))
}
/// new error with kind and no ctx
pub fn with_kind(kind: ErrorKind) -> Self {
Self::_new(kind, None, None)
}
/// new error with kind and origin
fn with_origin(kind: ErrorKind, origin: Subsystem) -> Self {
Self::_new(kind, Some(origin), None)
}
/// new error with kind and dmsg
fn with_dmsg(kind: ErrorKind, dmsg: impl Into<Dmsg>) -> Self {
Self::_new(kind, None, Some(dmsg.into()))
}
/// remove the dmsg from self
fn remove_dmsg(self) -> Self {
Self::_new(self.kind, self.origin, None)
}
/// remove the origin from self
fn remove_origin(self) -> Self {
Self::_new(self.kind, None, self.dmsg)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.origin {
Some(orig) => write!(f, "{} error: ", orig.as_str()),
None => write!(f, "runtime error: "),
}?;
match self.dmsg.as_ref() {
Some(dmsg) => write!(f, "{dmsg}; ")?,
None => {}
}
write!(f, "{}", self.kind)
}
}
impl std::error::Error for Error {}
/*
generic error casts
*/
// for all other direct error casts, always inherit context
impl<E: Into<ErrorKind>> From<E> for Error {
fn from(e: E) -> Self {
Self::_new(e.into(), context::get_origin(), context::get_dmsg())
}
}
/*
error casts used during result private context mutation
*/
// only used when you're modifying context
pub trait IntoError {
fn err_noinherit(self) -> Error;
fn err_inherit_parent(self) -> Error;
}
// error kinds do not carry any context
impl<E: Into<ErrorKind>> IntoError for E {
fn err_noinherit(self) -> Error {
Error::with_kind(self.into())
}
fn err_inherit_parent(self) -> Error {
Self::err_noinherit(self)
}
}
impl IntoError for Error {
fn err_noinherit(self) -> Error {
Error::with_kind(self.kind)
}
fn err_inherit_parent(self) -> Error {
self
}
}
/*
error context and tracing
*/
pub trait ErrorContext<T> {
// no inherit
/// set the origin (do not inherit parent or local)
fn set_origin(self, origin: Subsystem) -> Result<T, Error>;
/// set the dmsg (do not inherit parent or local)
fn set_dmsg(self, dmsg: impl Into<Dmsg>) -> Result<T, Error>;
/// set the origin and dmsg (do not inherit)
fn set_ctx(self, origin: Subsystem, dmsg: impl Into<Dmsg>) -> Result<T, Error>;
// inherit parent
/// set the origin (inherit rest from parent)
fn ip_set_origin(self, origin: Subsystem) -> Result<T, Error>;
/// set the dmsg (inherit rest from origin)
fn ip_set_dmsg(self, dmsg: impl Into<Dmsg>) -> Result<T, Error>;
// inherit local
/// set the origin (inherit rest from local)
fn il_set_origin(self, origin: Subsystem) -> Result<T, Error>;
/// set the dmsg (inherit rest from local)
fn il_set_dmsg(self, dmsg: impl Into<Dmsg>) -> Result<T, Error>;
/// inherit everything from local (assuming this has no context)
fn inherit_local(self) -> Result<T, Error>;
// inherit any
/// set the origin (inherit rest from either parent, then local)
fn inherit_set_origin(self, origin: Subsystem) -> Result<T, Error>;
/// set the dmsg (inherit rest from either parent, then local)
fn inherit_set_dmsg(self, dmsg: impl Into<Dmsg>) -> Result<T, Error>;
// orphan
/// orphan the entire context (if any)
fn orphan(self) -> Result<T, Error>;
/// orphan the origin (if any)
fn orphan_origin(self) -> Result<T, Error>;
/// orphan the dmsg (if any)
fn orphan_dmsg(self) -> Result<T, Error>;
}
impl<T, E> ErrorContext<T> for Result<T, E>
where
E: IntoError,
{
// no inherit
fn set_origin(self, origin: Subsystem) -> Result<T, Error> {
self.map_err(|e| e.err_noinherit().add_origin(origin))
}
fn set_dmsg(self, dmsg: impl Into<Dmsg>) -> Result<T, Error> {
self.map_err(|e| e.err_noinherit().add_dmsg(dmsg))
}
fn set_ctx(self, origin: Subsystem, dmsg: impl Into<Dmsg>) -> Result<T, Error> {
self.map_err(|e| Error::new(e.err_noinherit().kind, origin, dmsg))
}
// inherit local
fn il_set_origin(self, origin: Subsystem) -> Result<T, Error> {
self.map_err(|e| Error::_new(e.err_noinherit().kind, Some(origin), context::pop_dmsg()))
}
fn il_set_dmsg(self, dmsg: impl Into<Dmsg>) -> Result<T, Error> {
self.map_err(|e| {
Error::_new(
e.err_noinherit().kind,
context::pop_origin(),
Some(dmsg.into()),
)
})
}
fn inherit_local(self) -> Result<T, Error> {
self.map_err(|e| {
Error::_new(
e.err_noinherit().kind,
context::get_origin(),
context::get_dmsg(),
)
})
}
// inherit parent
fn ip_set_origin(self, origin: Subsystem) -> Result<T, Error> {
self.map_err(|e| e.err_inherit_parent().add_origin(origin))
}
fn ip_set_dmsg(self, dmsg: impl Into<Dmsg>) -> Result<T, Error> {
self.map_err(|e| e.err_inherit_parent().add_dmsg(dmsg))
}
// inherit any
fn inherit_set_dmsg(self, dmsg: impl Into<Dmsg>) -> Result<T, Error> {
self.map_err(|e| {
// inherit from parent
let mut e = e.err_inherit_parent();
// inherit from local if parent has no ctx
e.origin = e.origin.or_else(|| context::pop_origin());
e.add_dmsg(dmsg)
})
}
fn inherit_set_origin(self, origin: Subsystem) -> Result<T, Error> {
self.map_err(|e| {
// inherit from parent
let mut e = e.err_inherit_parent();
// inherit form local if parent has no ctx
e.dmsg = e.dmsg.or_else(|| context::pop_dmsg());
e.add_origin(origin)
})
}
fn orphan(self) -> Result<T, Error> {
self.map_err(|e| e.err_noinherit())
}
fn orphan_dmsg(self) -> Result<T, Error> {
self.map_err(|e| e.err_inherit_parent().remove_dmsg())
}
fn orphan_origin(self) -> Result<T, Error> {
self.map_err(|e| e.err_inherit_parent().remove_origin())
}
}
/*
foreign type casts
*/
macro_rules! impl_other_err_tostring {
($($ty:ty => $origin:ident),* $(,)?) => {
$(
impl From<$ty> for Error {
fn from(e: $ty) -> Self { Self::_new(ErrorKind::Other(e.to_string()), Some(Subsystem::$origin), context::pop_dmsg()) }
}
impl IntoError for $ty {
fn err_noinherit(self) -> Error { Error::with_kind(ErrorKind::Other(self.to_string())) }
fn err_inherit_parent(self) -> Error { Self::err_noinherit(self) }
}
)*
}
}
impl_other_err_tostring! {
openssl::ssl::Error => Network,
openssl::error::Error => Network,
openssl::error::ErrorStack => Network,
}
impl From<StorageError> for Error {
fn from(value: StorageError) -> Self {
Self::_new(
ErrorKind::Storage(value),
context::pop_origin(),
context::pop_dmsg(),
)
}
}
impl From<TransactionError> for Error {
fn from(value: TransactionError) -> Self {
Self::_new(
ErrorKind::Txn(value),
context::pop_origin(),
context::pop_dmsg(),
)
}
}
impl From<ConfigError> for Error {
fn from(e: ConfigError) -> Self {
Self::with_origin(ErrorKind::Config(e), Subsystem::Init)
}
}
impl IntoError for StorageError {
fn err_noinherit(self) -> Error {
Error::with_kind(ErrorKind::Storage(self))
}
fn err_inherit_parent(self) -> Error {
self.into()
}
}
impl IntoError for TransactionError {
fn err_noinherit(self) -> Error {
Error::with_kind(ErrorKind::Txn(self))
}
fn err_inherit_parent(self) -> Error {
self.into()
}
}

@ -30,17 +30,20 @@ use {
data::uuid::Uuid,
storage::{
self,
v1::{LocalFS, RawFSInterface, SDSSResult},
v1::{LocalFS, RawFSInterface},
},
txn::gns::GNSTransactionDriverAnyFS,
},
crate::engine::error::RuntimeResult,
parking_lot::{Mutex, RwLock},
std::{collections::HashMap, mem::MaybeUninit},
tokio::sync::mpsc::unbounded_channel,
};
pub mod config;
pub mod context;
mod drivers;
pub mod error;
mod mgr;
#[cfg(test)]
pub mod test_utils;
@ -115,7 +118,7 @@ pub trait GlobalInstanceLike {
space_uuid: Uuid,
model_name: &str,
model_uuid: Uuid,
) -> SDSSResult<()>;
) -> RuntimeResult<()>;
// taskmgr
fn taskmgr_post_high_priority(&self, task: Task<CriticalTask>);
fn taskmgr_post_standard_priority(&self, task: Task<GenericTask>);
@ -167,7 +170,7 @@ impl GlobalInstanceLike for Global {
space_uuid: Uuid,
model_name: &str,
model_uuid: Uuid,
) -> SDSSResult<()> {
) -> RuntimeResult<()> {
// create dir
LocalFS::fs_create_dir(&storage::v1::loader::SEInitState::model_dir(
space_name, space_uuid, model_name, model_uuid,

@ -125,7 +125,7 @@ impl<Fs: RawFSInterface> GlobalInstanceLike for TestGlobal<Fs> {
space_uuid: Uuid,
model_name: &str,
model_uuid: Uuid,
) -> storage::v1::SDSSResult<()> {
) -> crate::engine::error::RuntimeResult<()> {
// create model dir
Fs::fs_create_dir(&storage::v1::loader::SEInitState::model_dir(
space_name, space_uuid, model_name, model_uuid,

@ -27,10 +27,7 @@
mod protocol;
use {
crate::engine::{
error::{RuntimeError, RuntimeResult},
fractal::Global,
},
crate::engine::{error::RuntimeResult, fractal::error::ErrorContext, fractal::Global},
bytes::BytesMut,
openssl::{
pkey::PKey,
@ -149,10 +146,9 @@ impl Listener {
sig_shutdown: broadcast::Sender<()>,
) -> RuntimeResult<Self> {
let (sig_inflight, sig_inflight_wait) = mpsc::channel(1);
let listener = RuntimeError::result_ctx(
TcpListener::bind(binaddr).await,
format!("failed to bind to port `{binaddr}`"),
)?;
let listener = TcpListener::bind(binaddr)
.await
.set_dmsg(format!("failed to bind to port `{binaddr}`"))?;
Ok(Self {
global,
listener,
@ -234,8 +230,7 @@ impl Listener {
builder.check_private_key()?;
Ok::<_, openssl::error::ErrorStack>(builder.build())
};
let acceptor =
RuntimeError::result_ctx(build_acceptor(), "failed to initialize TLS socket")?;
let acceptor = build_acceptor().set_dmsg("failed to initialize TLS socket")?;
loop {
let stream = async {
let (stream, _) = self.accept().await?;

@ -37,7 +37,7 @@ use {
crate::{
engine::{
data::{cell::Datacell, lit::Lit},
error::{Error, QueryResult},
error::{QueryError, QueryResult},
},
util::{compiler, MaybeInit},
},
@ -421,7 +421,7 @@ impl<'a> Entity<'a> {
*c += 1;
Self::parse_uck_tokens_single(tok)
},
_ => return Err(Error::QPExpectedEntity),
_ => return Err(QueryError::QPExpectedEntity),
};
Ok(r)
}
@ -437,7 +437,7 @@ impl<'a> Entity<'a> {
Ok(e.assume_init())
}
} else {
Err(Error::QPExpectedEntity)
Err(QueryError::QPExpectedEntity)
}
}
#[inline(always)]
@ -531,7 +531,7 @@ pub fn compile_test<'a>(tok: &'a [Token<'a>]) -> QueryResult<Statement<'a>> {
#[inline(always)]
pub fn compile<'a, Qd: QueryData<'a>>(tok: &'a [Token<'a>], d: Qd) -> QueryResult<Statement<'a>> {
if compiler::unlikely(tok.len() < 2) {
return Err(Error::QLUnexpectedEndOfStatement);
return Err(QueryError::QLUnexpectedEndOfStatement);
}
let mut state = State::new(tok, d);
match state.fw_read() {
@ -540,12 +540,12 @@ pub fn compile<'a, Qd: QueryData<'a>>(tok: &'a [Token<'a>], d: Qd) -> QueryResul
Token![create] => match state.fw_read() {
Token![model] => ASTNode::from_state(&mut state).map(Statement::CreateModel),
Token![space] => ASTNode::from_state(&mut state).map(Statement::CreateSpace),
_ => compiler::cold_rerr(Error::QPUnknownStatement),
_ => compiler::cold_rerr(QueryError::QPUnknownStatement),
},
Token![alter] => match state.fw_read() {
Token![model] => ASTNode::from_state(&mut state).map(Statement::AlterModel),
Token![space] => ASTNode::from_state(&mut state).map(Statement::AlterSpace),
_ => compiler::cold_rerr(Error::QPUnknownStatement),
_ => compiler::cold_rerr(QueryError::QPUnknownStatement),
},
Token![drop] if state.remaining() >= 2 => ddl::drop::parse_drop(&mut state),
Token::Ident(id) if id.eq_ignore_ascii_case("inspect") => {
@ -556,6 +556,6 @@ pub fn compile<'a, Qd: QueryData<'a>>(tok: &'a [Token<'a>], d: Qd) -> QueryResul
Token![select] => ASTNode::from_state(&mut state).map(Statement::Select),
Token![update] => ASTNode::from_state(&mut state).map(Statement::Update),
Token![delete] => ASTNode::from_state(&mut state).map(Statement::Delete),
_ => compiler::cold_rerr(Error::QPUnknownStatement),
_ => compiler::cold_rerr(QueryError::QPUnknownStatement),
}
}

@ -27,7 +27,7 @@
#[cfg(test)]
use crate::engine::ql::{ast::InplaceData, lex::Token};
use crate::engine::{
error::{Error, QueryResult},
error::{QueryError, QueryResult},
ql::ast::{QueryData, State},
};
@ -47,7 +47,7 @@ pub trait ASTNode<'a>: Sized {
return if state.okay() {
r
} else {
Err(Error::QLInvalidSyntax)
Err(QueryError::QLInvalidSyntax)
};
}
r
@ -64,7 +64,7 @@ pub trait ASTNode<'a>: Sized {
return if state.okay() {
r
} else {
Err(Error::QLInvalidSyntax)
Err(QueryError::QLInvalidSyntax)
};
}
r
@ -86,7 +86,7 @@ pub trait ASTNode<'a>: Sized {
if state.exhausted() && state.okay() {
r
} else {
Err(Error::QLInvalidSyntax)
Err(QueryError::QLInvalidSyntax)
}
}
}

@ -29,7 +29,7 @@ use crate::engine::{
tag::{DataTag, TagClass},
DictGeneric,
},
error::{Error, QueryResult},
error::{QueryError, QueryResult},
ql::{
ast::{traits, QueryData, State},
ddl::syn,
@ -43,7 +43,7 @@ fn parse<'a, Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<UserMe
7 tokens
*/
if state.remaining() < 7 {
return Err(Error::QLInvalidSyntax);
return Err(QueryError::QLInvalidSyntax);
}
let token_buffer = state.current();
// initial sig
@ -54,7 +54,7 @@ fn parse<'a, Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<UserMe
state.poison_if_not(signature_okay);
state.cursor_ahead_by(2);
let Some(dict) = syn::parse_dict(state) else {
return Err(Error::QLInvalidCollectionSyntax);
return Err(QueryError::QLInvalidCollectionSyntax);
};
let maybe_username = unsafe {
// UNSAFE(@ohsayan): the dict parse ensures state correctness
@ -63,7 +63,7 @@ fn parse<'a, Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<UserMe
state.poison_if_not(maybe_username.kind().tag_class() == TagClass::Str);
if state.not_exhausted() & !state.okay() {
// we shouldn't have more tokens
return Err(Error::QLInvalidSyntax);
return Err(QueryError::QLInvalidSyntax);
}
Ok(UserMeta {
username: unsafe {
@ -129,7 +129,7 @@ impl<'a> UserDel<'a> {
}));
}
}
Err(Error::QLInvalidSyntax)
Err(QueryError::QLInvalidSyntax)
}
}

@ -29,7 +29,7 @@ use {
crate::{
engine::{
data::DictGeneric,
error::{Error, QueryResult},
error::{QueryError, QueryResult},
ql::{
ast::{Entity, QueryData, State},
lex::{Ident, Token},
@ -57,7 +57,7 @@ impl<'a> AlterSpace<'a> {
/// Parse alter space from tokens
fn parse<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<Self> {
if compiler::unlikely(state.remaining() <= 3) {
return compiler::cold_rerr(Error::QLUnexpectedEndOfStatement);
return compiler::cold_rerr(QueryError::QLUnexpectedEndOfStatement);
}
let space_name = state.fw_read();
state.poison_if_not(space_name.is_ident());
@ -67,7 +67,7 @@ impl<'a> AlterSpace<'a> {
state.cursor_ahead(); // ignore errors
if compiler::unlikely(!state.okay()) {
return Err(Error::QLInvalidSyntax);
return Err(QueryError::QLInvalidSyntax);
}
let space_name = unsafe {
@ -82,7 +82,7 @@ impl<'a> AlterSpace<'a> {
updated_props: d,
})
} else {
Err(Error::QLInvalidCollectionSyntax)
Err(QueryError::QLInvalidCollectionSyntax)
}
}
}
@ -114,7 +114,7 @@ impl<'a> AlterModel<'a> {
fn parse<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<Self> {
// alter model mymodel remove x
if state.remaining() <= 2 || !state.cursor_has_ident_rounded() {
return compiler::cold_rerr(Error::QLInvalidSyntax);
return compiler::cold_rerr(QueryError::QLInvalidSyntax);
// FIXME(@ohsayan): bad because no specificity
}
let model_name = Entity::parse_from_state_rounded_result(state)?;
@ -122,7 +122,7 @@ impl<'a> AlterModel<'a> {
Token![add] => AlterKind::alter_add(state),
Token![remove] => AlterKind::alter_remove(state),
Token![update] => AlterKind::alter_update(state),
_ => Err(Error::QPExpectedStatement),
_ => Err(QueryError::QPExpectedStatement),
};
kind.map(|kind| AlterModel::new(model_name, kind))
}
@ -148,7 +148,7 @@ impl<'a> AlterKind<'a> {
<remove> ::= <ident> | <openparen> (<ident> <comma>)*<closeparen>
*/
if compiler::unlikely(state.exhausted()) {
return compiler::cold_rerr(Error::QLUnexpectedEndOfStatement);
return compiler::cold_rerr(QueryError::QLUnexpectedEndOfStatement);
}
let r = match state.fw_read() {
@ -177,10 +177,10 @@ impl<'a> AlterKind<'a> {
if state.okay() {
cols.into_boxed_slice()
} else {
return Err(Error::QLInvalidSyntax);
return Err(QueryError::QLInvalidSyntax);
}
}
_ => return Err(Error::QLInvalidSyntax),
_ => return Err(QueryError::QLInvalidSyntax),
};
Ok(Self::Remove(r))
}

@ -29,7 +29,7 @@ use {
crate::{
engine::{
data::DictGeneric,
error::{Error, QueryResult},
error::{QueryError, QueryResult},
ql::{
ast::{Entity, QueryData, State},
lex::Ident,
@ -54,7 +54,7 @@ impl<'a> CreateSpace<'a> {
fn parse<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<Self> {
// smallest declaration: `create space myspace` -> >= 1 token
if compiler::unlikely(state.remaining() < 1) {
return compiler::cold_rerr(Error::QLUnexpectedEndOfStatement);
return compiler::cold_rerr(QueryError::QLUnexpectedEndOfStatement);
}
let space_name = state.fw_read();
state.poison_if_not(space_name.is_ident());
@ -76,7 +76,7 @@ impl<'a> CreateSpace<'a> {
props: d,
})
} else {
Err(Error::QLInvalidSyntax)
Err(QueryError::QLInvalidSyntax)
}
}
}
@ -110,7 +110,7 @@ impl<'a> CreateModel<'a> {
fn parse<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<Self> {
if compiler::unlikely(state.remaining() < 10) {
return compiler::cold_rerr(Error::QLUnexpectedEndOfStatement);
return compiler::cold_rerr(QueryError::QLUnexpectedEndOfStatement);
}
// model name; ignore errors
let mut model_uninit = MaybeInit::uninit();
@ -147,7 +147,7 @@ impl<'a> CreateModel<'a> {
props,
})
} else {
Err(Error::QLInvalidSyntax)
Err(QueryError::QLInvalidSyntax)
}
}
}

@ -25,7 +25,7 @@
*/
use crate::engine::{
error::{Error, QueryResult},
error::{QueryError, QueryResult},
ql::{
ast::{Entity, QueryData, State, Statement},
lex::{Ident, Token},
@ -62,7 +62,7 @@ impl<'a> DropSpace<'a> {
));
}
}
Err(Error::QLInvalidSyntax)
Err(QueryError::QLInvalidSyntax)
}
}
@ -84,7 +84,7 @@ impl<'a> DropModel<'a> {
if state.exhausted() {
return Ok(DropModel::new(e, force));
} else {
Err(Error::QLInvalidSyntax)
Err(QueryError::QLInvalidSyntax)
}
}
}
@ -97,7 +97,7 @@ pub fn parse_drop<'a, Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResu
match state.fw_read() {
Token![model] => DropModel::parse(state).map(Statement::DropModel),
Token![space] => return DropSpace::parse(state).map(Statement::DropSpace),
_ => Err(Error::QPUnknownStatement),
_ => Err(QueryError::QPUnknownStatement),
}
}

@ -26,7 +26,7 @@
use crate::{
engine::{
error::{Error, QueryResult},
error::{QueryError, QueryResult},
ql::{
ast::{Entity, QueryData, State, Statement},
lex::Token,
@ -47,7 +47,7 @@ pub fn parse_inspect<'a, Qd: QueryData<'a>>(
*/
if compiler::unlikely(state.remaining() < 1) {
return compiler::cold_rerr(Error::QLUnexpectedEndOfStatement);
return compiler::cold_rerr(QueryError::QLUnexpectedEndOfStatement);
}
match state.fw_read() {
@ -65,7 +65,7 @@ pub fn parse_inspect<'a, Qd: QueryData<'a>>(
}
_ => {
state.cursor_back();
Err(Error::QPExpectedStatement)
Err(QueryError::QPExpectedStatement)
}
}
}

@ -50,7 +50,7 @@ use crate::{
cell::Datacell,
dict::{DictEntryGeneric, DictGeneric},
},
error::{Error, QueryResult},
error::{QueryError, QueryResult},
ql::{
ast::{QueryData, State},
lex::{Ident, Token},
@ -369,7 +369,7 @@ impl<'a> FieldSpec<'a> {
pub fn parse<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<Self> {
if compiler::unlikely(state.remaining() < 2) {
// smallest field: `ident: type`
return Err(Error::QLUnexpectedEndOfStatement);
return Err(QueryError::QLUnexpectedEndOfStatement);
}
// check if primary or null
let is_primary = state.cursor_eq(Token![primary]);
@ -381,7 +381,7 @@ impl<'a> FieldSpec<'a> {
// field name
let field_name = match (state.fw_read(), state.fw_read()) {
(Token::Ident(id), Token![:]) => id,
_ => return Err(Error::QLInvalidSyntax),
_ => return Err(QueryError::QLInvalidSyntax),
};
// layers
let mut layers = Vec::new();
@ -394,7 +394,7 @@ impl<'a> FieldSpec<'a> {
primary: is_primary,
})
} else {
Err(Error::QLInvalidTypeDefinitionSyntax)
Err(QueryError::QLInvalidTypeDefinitionSyntax)
}
}
}
@ -420,7 +420,7 @@ impl<'a> ExpandedField<'a> {
pub(super) fn parse<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<Self> {
if compiler::unlikely(state.remaining() < 6) {
// smallest: fieldname { type: ident }
return Err(Error::QLUnexpectedEndOfStatement);
return Err(QueryError::QLUnexpectedEndOfStatement);
}
let field_name = state.fw_read();
state.poison_if_not(field_name.is_ident());
@ -433,7 +433,7 @@ impl<'a> ExpandedField<'a> {
// this has layers. fold them; but don't forget the colon
if compiler::unlikely(state.exhausted()) {
// we need more tokens
return Err(Error::QLUnexpectedEndOfStatement);
return Err(QueryError::QLUnexpectedEndOfStatement);
}
state.poison_if_not(state.cursor_eq(Token![:]));
state.cursor_ahead();
@ -460,7 +460,7 @@ impl<'a> ExpandedField<'a> {
layers,
})
} else {
Err(Error::QLInvalidSyntax)
Err(QueryError::QLInvalidSyntax)
}
}
#[inline(always)]
@ -478,7 +478,7 @@ impl<'a> ExpandedField<'a> {
alter model add myfield { type string }
*/
if compiler::unlikely(state.remaining() < 5) {
return compiler::cold_rerr(Error::QLUnexpectedEndOfStatement);
return compiler::cold_rerr(QueryError::QLUnexpectedEndOfStatement);
}
match state.read() {
Token::Ident(_) => {
@ -510,10 +510,10 @@ impl<'a> ExpandedField<'a> {
if state.okay() {
Ok(cols.into_boxed_slice())
} else {
Err(Error::QLInvalidSyntax)
Err(QueryError::QLInvalidSyntax)
}
}
_ => Err(Error::QPExpectedStatement),
_ => Err(QueryError::QPExpectedStatement),
}
}
}

@ -30,7 +30,7 @@ use {
super::WhereClause,
crate::{
engine::{
error::{Error, QueryResult},
error::{QueryError, QueryResult},
ql::ast::{Entity, QueryData, State},
},
util::{compiler, MaybeInit},
@ -81,7 +81,7 @@ impl<'a> DeleteStatement<'a> {
^1 ^2 ^3 ^4 ^5
*/
if compiler::unlikely(state.remaining() < 5) {
return compiler::cold_rerr(Error::QLUnexpectedEndOfStatement);
return compiler::cold_rerr(QueryError::QLUnexpectedEndOfStatement);
}
// from + entity
state.poison_if_not(state.cursor_eq(Token![from]));
@ -101,7 +101,7 @@ impl<'a> DeleteStatement<'a> {
wc,
})
} else {
compiler::cold_rerr(Error::QLInvalidSyntax)
compiler::cold_rerr(QueryError::QLInvalidSyntax)
}
}
}

@ -28,7 +28,7 @@ use {
crate::{
engine::{
data::cell::Datacell,
error::{Error, QueryResult},
error::{QueryError, QueryResult},
ql::{
ast::{Entity, QueryData, State},
lex::{Ident, Token},
@ -357,7 +357,7 @@ impl<'a> InsertStatement<'a> {
^1 ^2 ^3 ^4 ^5
*/
if compiler::unlikely(state.remaining() < 5) {
return compiler::cold_rerr(Error::QLUnexpectedEndOfStatement);
return compiler::cold_rerr(QueryError::QLUnexpectedEndOfStatement);
}
state.poison_if_not(state.cursor_eq(Token![into]));
state.cursor_ahead(); // ignore errors
@ -392,7 +392,7 @@ impl<'a> InsertStatement<'a> {
data,
})
} else {
compiler::cold_rerr(Error::QLInvalidSyntax)
compiler::cold_rerr(QueryError::QLInvalidSyntax)
}
}
}

@ -168,7 +168,7 @@ mod impls {
use {
super::{RelationalExpr, WhereClause},
crate::engine::{
error::{Error, QueryResult},
error::{QueryError, QueryResult},
ql::ast::{traits::ASTNode, QueryData, State},
},
};
@ -182,7 +182,7 @@ mod impls {
}
impl<'a> ASTNode<'a> for RelationalExpr<'a> {
fn _from_state<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<Self> {
Self::try_parse(state).ok_or(Error::QLIllegalRelExp)
Self::try_parse(state).ok_or(QueryError::QLIllegalRelExp)
}
}
}

@ -28,7 +28,7 @@ use {
super::{WhereClause, WhereClauseCollection},
crate::{
engine::{
error::{Error, QueryResult},
error::{QueryError, QueryResult},
ql::{
ast::{Entity, QueryData, State},
lex::{Ident, Token},
@ -104,7 +104,7 @@ impl<'a> SelectStatement<'a> {
1 2 3
*/
if compiler::unlikely(state.remaining() < 3) {
return compiler::cold_rerr(Error::QLUnexpectedEndOfStatement);
return compiler::cold_rerr(QueryError::QLUnexpectedEndOfStatement);
}
let mut select_fields = Vec::new();
let is_wildcard = state.cursor_eq(Token![*]);
@ -123,7 +123,7 @@ impl<'a> SelectStatement<'a> {
state.poison_if_not(is_wildcard | !select_fields.is_empty());
// we should have from + model
if compiler::unlikely(state.remaining() < 2 || !state.okay()) {
return compiler::cold_rerr(Error::QLInvalidSyntax);
return compiler::cold_rerr(QueryError::QLInvalidSyntax);
}
state.poison_if_not(state.cursor_eq(Token![from]));
state.cursor_ahead(); // ignore errors
@ -146,7 +146,7 @@ impl<'a> SelectStatement<'a> {
clause: WhereClause::new(clauses),
})
} else {
compiler::cold_rerr(Error::QLInvalidSyntax)
compiler::cold_rerr(QueryError::QLInvalidSyntax)
}
}
}

@ -32,7 +32,7 @@ use {
engine::{
core::query_meta::AssignmentOperator,
data::lit::Lit,
error::{Error, QueryResult},
error::{QueryError, QueryResult},
ql::{
ast::{Entity, QueryData, State},
lex::Ident,
@ -180,7 +180,7 @@ impl<'a> UpdateStatement<'a> {
^1 ^2 ^3 ^4 ^5^6 ^7^8^9
*/
if compiler::unlikely(state.remaining() < 9) {
return compiler::cold_rerr(Error::QLUnexpectedEndOfStatement);
return compiler::cold_rerr(QueryError::QLUnexpectedEndOfStatement);
}
// parse entity
let mut entity = MaybeInit::uninit();
@ -218,7 +218,7 @@ impl<'a> UpdateStatement<'a> {
wc: WhereClause::new(clauses),
})
} else {
compiler::cold_rerr(Error::QLInvalidSyntax)
compiler::cold_rerr(QueryError::QLInvalidSyntax)
}
}
}

@ -31,7 +31,7 @@ use {
crate::{
engine::{
data::lit::Lit,
error::{Error, QueryResult},
error::{QueryError, QueryResult},
mem::BufferedScanner,
},
util::compiler,
@ -51,7 +51,7 @@ type Slice<'a> = &'a [u8];
pub struct Lexer<'a> {
token_buffer: BufferedScanner<'a>,
tokens: Vec<Token<'a>>,
last_error: Option<Error>,
last_error: Option<QueryError>,
}
impl<'a> Lexer<'a> {
@ -66,7 +66,7 @@ impl<'a> Lexer<'a> {
/// set an error
#[inline(never)]
#[cold]
fn set_error(&mut self, e: Error) {
fn set_error(&mut self, e: QueryError) {
self.last_error = Some(e);
}
/// push in a new token
@ -116,7 +116,7 @@ impl<'a> Lexer<'a> {
fn scan_byte(&mut self, byte: u8) {
match symof(byte) {
Some(tok) => self.push_token(tok),
None => return self.set_error(Error::LexUnexpectedByte),
None => return self.set_error(QueryError::LexUnexpectedByte),
}
unsafe {
// UNSAFE(@ohsayan): we are sent a byte, so fw cursor
@ -210,13 +210,13 @@ impl<'a> InsecureLexer<'a> {
.token_buffer
.try_next_ascii_u64_lf_separated_or_restore_cursor()
else {
self.l.set_error(Error::LexInvalidLiteral);
self.l.set_error(QueryError::LexInvalidLiteral);
return;
};
let len = len as usize;
match self.l.token_buffer.try_next_variable_block(len) {
Some(block) => self.l.push_token(Lit::new_bin(block)),
None => self.l.set_error(Error::LexInvalidLiteral),
None => self.l.set_error(QueryError::LexInvalidLiteral),
}
}
fn scan_quoted_string(&mut self, quote_style: u8) {
@ -249,7 +249,7 @@ impl<'a> InsecureLexer<'a> {
// UNSAFE(@ohsayan): we move the cursor ahead, now we're moving it back
self.l.token_buffer.decr_cursor()
}
self.l.set_error(Error::LexInvalidLiteral);
self.l.set_error(QueryError::LexInvalidLiteral);
return;
}
}
@ -267,7 +267,7 @@ impl<'a> InsecureLexer<'a> {
}
match String::from_utf8(buf) {
Ok(s) if ended_with_quote => self.l.push_token(Lit::new_string(s)),
Err(_) | Ok(_) => self.l.set_error(Error::LexInvalidLiteral),
Err(_) | Ok(_) => self.l.set_error(QueryError::LexInvalidLiteral),
}
}
fn scan_unsigned_integer(&mut self) {
@ -288,7 +288,7 @@ impl<'a> InsecureLexer<'a> {
.token_buffer
.rounded_cursor_not_eof_matches(u8::is_ascii_alphanumeric),
) {
self.l.set_error(Error::LexInvalidLiteral);
self.l.set_error(QueryError::LexInvalidLiteral);
} else {
self.l.push_token(Lit::new_uint(int))
}
@ -312,7 +312,7 @@ impl<'a> InsecureLexer<'a> {
{
self.l.push_token(Lit::new_sint(int))
} else {
self.l.set_error(Error::LexInvalidLiteral)
self.l.set_error(QueryError::LexInvalidLiteral)
}
} else {
self.l.push_token(Token![-]);
@ -409,7 +409,7 @@ static SCAN_PARAM: [unsafe fn(&mut SecureLexer); 8] = unsafe {
let nb = slf.param_buffer.next_byte();
slf.l.push_token(Token::Lit(Lit::new_bool(nb == 1)));
if nb > 1 {
slf.l.set_error(Error::LexInvalidEscapedLiteral);
slf.l.set_error(QueryError::LexInvalidEscapedLiteral);
}
},
// uint
@ -418,7 +418,7 @@ static SCAN_PARAM: [unsafe fn(&mut SecureLexer); 8] = unsafe {
.try_next_ascii_u64_lf_separated_or_restore_cursor()
{
Some(int) => slf.l.push_token(Lit::new_uint(int)),
None => slf.l.set_error(Error::LexInvalidEscapedLiteral),
None => slf.l.set_error(QueryError::LexInvalidEscapedLiteral),
},
// sint
|slf| {
@ -426,7 +426,7 @@ static SCAN_PARAM: [unsafe fn(&mut SecureLexer); 8] = unsafe {
if okay {
slf.l.push_token(Lit::new_sint(int))
} else {
slf.l.set_error(Error::LexInvalidLiteral)
slf.l.set_error(QueryError::LexInvalidLiteral)
}
},
// float
@ -435,7 +435,7 @@ static SCAN_PARAM: [unsafe fn(&mut SecureLexer); 8] = unsafe {
.param_buffer
.try_next_ascii_u64_lf_separated_or_restore_cursor()
else {
slf.l.set_error(Error::LexInvalidEscapedLiteral);
slf.l.set_error(QueryError::LexInvalidEscapedLiteral);
return;
};
let body = match slf
@ -444,13 +444,13 @@ static SCAN_PARAM: [unsafe fn(&mut SecureLexer); 8] = unsafe {
{
Some(body) => body,
None => {
slf.l.set_error(Error::LexInvalidEscapedLiteral);
slf.l.set_error(QueryError::LexInvalidEscapedLiteral);
return;
}
};
match core::str::from_utf8(body).map(core::str::FromStr::from_str) {
Ok(Ok(fp)) => slf.l.push_token(Lit::new_float(fp)),
_ => slf.l.set_error(Error::LexInvalidEscapedLiteral),
_ => slf.l.set_error(QueryError::LexInvalidEscapedLiteral),
}
},
// binary
@ -459,7 +459,7 @@ static SCAN_PARAM: [unsafe fn(&mut SecureLexer); 8] = unsafe {
.param_buffer
.try_next_ascii_u64_lf_separated_or_restore_cursor()
else {
slf.l.set_error(Error::LexInvalidEscapedLiteral);
slf.l.set_error(QueryError::LexInvalidEscapedLiteral);
return;
};
match slf
@ -467,7 +467,7 @@ static SCAN_PARAM: [unsafe fn(&mut SecureLexer); 8] = unsafe {
.try_next_variable_block(size_of_body as usize)
{
Some(block) => slf.l.push_token(Lit::new_bin(block)),
None => slf.l.set_error(Error::LexInvalidEscapedLiteral),
None => slf.l.set_error(QueryError::LexInvalidEscapedLiteral),
}
},
// string
@ -476,7 +476,7 @@ static SCAN_PARAM: [unsafe fn(&mut SecureLexer); 8] = unsafe {
.param_buffer
.try_next_ascii_u64_lf_separated_or_restore_cursor()
else {
slf.l.set_error(Error::LexInvalidEscapedLiteral);
slf.l.set_error(QueryError::LexInvalidEscapedLiteral);
return;
};
match slf
@ -486,10 +486,10 @@ static SCAN_PARAM: [unsafe fn(&mut SecureLexer); 8] = unsafe {
{
// TODO(@ohsayan): obliterate this alloc
Some(Ok(s)) => slf.l.push_token(Lit::new_string(s.to_owned())),
_ => slf.l.set_error(Error::LexInvalidEscapedLiteral),
_ => slf.l.set_error(QueryError::LexInvalidEscapedLiteral),
}
},
// ecc
|s| s.l.set_error(Error::LexInvalidEscapedLiteral),
|s| s.l.set_error(QueryError::LexInvalidEscapedLiteral),
]
};

@ -29,7 +29,7 @@ use {
super::lex::{Ident, Token},
lex_insecure, lex_secure,
},
crate::engine::{data::lit::Lit, error::Error},
crate::engine::{data::lit::Lit, error::QueryError},
};
macro_rules! v(
@ -143,14 +143,23 @@ fn lex_string_escape_bs() {
#[test]
fn lex_string_bad_escape() {
let wth = br#" '\a should be an alert on windows apparently' "#;
assert_eq!(lex_insecure(wth).unwrap_err(), Error::LexInvalidLiteral);
assert_eq!(
lex_insecure(wth).unwrap_err(),
QueryError::LexInvalidLiteral
);
}
#[test]
fn lex_string_unclosed() {
let wth = br#" 'omg where did the end go "#;
assert_eq!(lex_insecure(wth).unwrap_err(), Error::LexInvalidLiteral);
assert_eq!(
lex_insecure(wth).unwrap_err(),
QueryError::LexInvalidLiteral
);
let wth = br#" 'see, we escaped the end\' "#;
assert_eq!(lex_insecure(wth).unwrap_err(), Error::LexInvalidLiteral);
assert_eq!(
lex_insecure(wth).unwrap_err(),
QueryError::LexInvalidLiteral
);
}
#[test]
fn lex_unsafe_literal_mini() {

@ -43,15 +43,15 @@ pub(super) use restore::{DecodedBatchEvent, DecodedBatchEventKind, NormalBatch};
pub use {persist::DataBatchPersistDriver, restore::DataBatchRestoreDriver};
use {
super::{rw::SDSSFileIO, spec, RawFSInterface, SDSSResult},
crate::engine::core::model::Model,
super::{rw::SDSSFileIO, spec, RawFSInterface},
crate::engine::{core::model::Model, error::RuntimeResult},
};
/// Re-initialize an existing batch journal and read all its data into model
pub fn reinit<Fs: RawFSInterface>(
name: &str,
model: &Model,
) -> SDSSResult<DataBatchPersistDriver<Fs>> {
) -> RuntimeResult<DataBatchPersistDriver<Fs>> {
let (f, _header) = SDSSFileIO::<Fs>::open::<spec::DataBatchJournalV1>(name)?;
// restore
let mut restore_driver = DataBatchRestoreDriver::new(f)?;
@ -60,7 +60,7 @@ pub fn reinit<Fs: RawFSInterface>(
}
/// Create a new batch journal
pub fn create<Fs: RawFSInterface>(path: &str) -> SDSSResult<DataBatchPersistDriver<Fs>> {
pub fn create<Fs: RawFSInterface>(path: &str) -> RuntimeResult<DataBatchPersistDriver<Fs>> {
let f = SDSSFileIO::<Fs>::create::<spec::DataBatchJournalV1>(path)?;
DataBatchPersistDriver::new(f, true)
}

@ -42,11 +42,11 @@ use {
cell::Datacell,
tag::{DataTag, TagClass, TagUnique},
},
error::{RuntimeResult, StorageError},
idx::STIndexSeq,
storage::v1::{
inf::PersistTypeDscr,
rw::{RawFSInterface, SDSSFileIO, SDSSFileTrackedWriter},
SDSSErrorKind, SDSSResult,
},
},
util::EndianQW,
@ -59,7 +59,7 @@ pub struct DataBatchPersistDriver<Fs: RawFSInterface> {
}
impl<Fs: RawFSInterface> DataBatchPersistDriver<Fs> {
pub fn new(mut file: SDSSFileIO<Fs>, is_new: bool) -> SDSSResult<Self> {
pub fn new(mut file: SDSSFileIO<Fs>, is_new: bool) -> RuntimeResult<Self> {
if !is_new {
file.fsynced_write(&[MARKER_BATCH_REOPEN])?;
}
@ -67,7 +67,7 @@ impl<Fs: RawFSInterface> DataBatchPersistDriver<Fs> {
f: SDSSFileTrackedWriter::new(file),
})
}
pub fn close(mut self) -> SDSSResult<()> {
pub fn close(mut self) -> RuntimeResult<()> {
if self
.f
.inner_file()
@ -76,10 +76,10 @@ impl<Fs: RawFSInterface> DataBatchPersistDriver<Fs> {
{
return Ok(());
} else {
return Err(SDSSErrorKind::DataBatchCloseError.into());
return Err(StorageError::DataBatchCloseError.into());
}
}
pub fn write_new_batch(&mut self, model: &Model, observed_len: usize) -> SDSSResult<()> {
pub fn write_new_batch(&mut self, model: &Model, observed_len: usize) -> RuntimeResult<()> {
// pin model
let irm = model.intent_read_model();
let schema_version = model.delta_state().schema_current_version();
@ -89,7 +89,7 @@ impl<Fs: RawFSInterface> DataBatchPersistDriver<Fs> {
// prepare computations
let mut i = 0;
let mut inconsistent_reads = 0;
let mut exec = || -> SDSSResult<()> {
let mut exec = || -> RuntimeResult<()> {
// write batch start
self.write_batch_start(
observed_len,
@ -154,7 +154,7 @@ impl<Fs: RawFSInterface> DataBatchPersistDriver<Fs> {
schema_version: DeltaVersion,
pk_tag: TagUnique,
col_cnt: usize,
) -> SDSSResult<()> {
) -> RuntimeResult<()> {
self.f
.unfsynced_write(&[MARKER_ACTUAL_BATCH_EVENT, pk_tag.value_u8()])?;
let observed_len_bytes = observed_len.u64_bytes_le();
@ -169,7 +169,7 @@ impl<Fs: RawFSInterface> DataBatchPersistDriver<Fs> {
&mut self,
observed_len: usize,
inconsistent_reads: usize,
) -> SDSSResult<()> {
) -> RuntimeResult<()> {
// [0xFD][actual_commit][checksum]
self.f.unfsynced_write(&[MARKER_END_OF_BATCH])?;
let actual_commit = (observed_len - inconsistent_reads).u64_bytes_le();
@ -180,7 +180,7 @@ impl<Fs: RawFSInterface> DataBatchPersistDriver<Fs> {
}
/// Attempt to fix the batch journal
// TODO(@ohsayan): declare an "international system disaster" when this happens
fn attempt_fix_data_batchfile(&mut self) -> SDSSResult<()> {
fn attempt_fix_data_batchfile(&mut self) -> RuntimeResult<()> {
/*
attempt to append 0xFF to the part of the file where a corruption likely occurred, marking
it recoverable
@ -189,13 +189,13 @@ impl<Fs: RawFSInterface> DataBatchPersistDriver<Fs> {
if f.fsynced_write(&[MARKER_RECOVERY_EVENT]).is_ok() {
return Ok(());
}
Err(SDSSErrorKind::DataBatchRecoveryFailStageOne.into())
Err(StorageError::DataBatchRecoveryFailStageOne.into())
}
}
impl<Fs: RawFSInterface> DataBatchPersistDriver<Fs> {
/// encode the primary key only. this means NO TAG is encoded.
fn encode_pk_only(&mut self, pk: &PrimaryIndexKey) -> SDSSResult<()> {
fn encode_pk_only(&mut self, pk: &PrimaryIndexKey) -> RuntimeResult<()> {
let buf = &mut self.f;
match pk.tag() {
TagUnique::UnsignedInt | TagUnique::SignedInt => {
@ -223,7 +223,7 @@ impl<Fs: RawFSInterface> DataBatchPersistDriver<Fs> {
Ok(())
}
/// Encode a single cell
fn encode_cell(&mut self, value: &Datacell) -> SDSSResult<()> {
fn encode_cell(&mut self, value: &Datacell) -> RuntimeResult<()> {
let ref mut buf = self.f;
buf.unfsynced_write(&[
PersistTypeDscr::translate_from_class(value.tag().tag_class()).value_u8(),
@ -275,7 +275,7 @@ impl<Fs: RawFSInterface> DataBatchPersistDriver<Fs> {
mdl: &Model,
irm: &IRModel,
row_data: &RowData,
) -> SDSSResult<()> {
) -> RuntimeResult<()> {
for field_name in irm.fields().stseq_ord_key() {
match row_data.fields().get(field_name) {
Some(cell) => {
@ -288,7 +288,7 @@ impl<Fs: RawFSInterface> DataBatchPersistDriver<Fs> {
Ok(())
}
/// Write the change type and txnid
fn write_batch_item_common_row_data(&mut self, delta: &DataDelta) -> SDSSResult<()> {
fn write_batch_item_common_row_data(&mut self, delta: &DataDelta) -> RuntimeResult<()> {
let change_type = [delta.change().value_u8()];
self.f.unfsynced_write(&change_type)?;
let txn_id = delta.data_version().value_u64().to_le_bytes();

@ -38,11 +38,11 @@ use {
cell::Datacell,
tag::{CUTag, TagClass, TagUnique},
},
error::{RuntimeResult, StorageError},
idx::{MTIndex, STIndex, STIndexSeq},
storage::v1::{
inf::PersistTypeDscr,
rw::{RawFSInterface, SDSSFileIO, SDSSFileTrackedReader},
SDSSErrorKind, SDSSResult,
},
},
crossbeam_epoch::pin,
@ -110,7 +110,7 @@ pub struct DataBatchRestoreDriver<F: RawFSInterface> {
}
impl<F: RawFSInterface> DataBatchRestoreDriver<F> {
pub fn new(f: SDSSFileIO<F>) -> SDSSResult<Self> {
pub fn new(f: SDSSFileIO<F>) -> RuntimeResult<Self> {
Ok(Self {
f: SDSSFileTrackedReader::new(f)?,
})
@ -121,7 +121,7 @@ impl<F: RawFSInterface> DataBatchRestoreDriver<F> {
pub(in crate::engine::storage::v1) fn read_data_batch_into_model(
&mut self,
model: &Model,
) -> SDSSResult<()> {
) -> RuntimeResult<()> {
self.read_all_batches_and_for_each(|batch| {
// apply the batch
Self::apply_batch(model, batch)
@ -129,7 +129,7 @@ impl<F: RawFSInterface> DataBatchRestoreDriver<F> {
}
pub(in crate::engine::storage::v1) fn read_all_batches(
&mut self,
) -> SDSSResult<Vec<NormalBatch>> {
) -> RuntimeResult<Vec<NormalBatch>> {
let mut all_batches = vec![];
self.read_all_batches_and_for_each(|batch| {
all_batches.push(batch);
@ -142,8 +142,8 @@ impl<F: RawFSInterface> DataBatchRestoreDriver<F> {
impl<F: RawFSInterface> DataBatchRestoreDriver<F> {
fn read_all_batches_and_for_each(
&mut self,
mut f: impl FnMut(NormalBatch) -> SDSSResult<()>,
) -> SDSSResult<()> {
mut f: impl FnMut(NormalBatch) -> RuntimeResult<()>,
) -> RuntimeResult<()> {
// begin
let mut closed = false;
while !self.f.is_eof() && !closed {
@ -187,9 +187,9 @@ impl<F: RawFSInterface> DataBatchRestoreDriver<F> {
}
}
// nope, this is a corrupted file
Err(SDSSErrorKind::DataBatchRestoreCorruptedBatchFile.into())
Err(StorageError::DataBatchRestoreCorruptedBatchFile.into())
}
fn handle_reopen_is_actual_close(&mut self) -> SDSSResult<bool> {
fn handle_reopen_is_actual_close(&mut self) -> RuntimeResult<bool> {
if self.f.is_eof() {
// yup, it was closed
Ok(true)
@ -200,7 +200,7 @@ impl<F: RawFSInterface> DataBatchRestoreDriver<F> {
Ok(false)
} else {
// that's just a nice bug
Err(SDSSErrorKind::DataBatchRestoreCorruptedBatchFile.into())
Err(StorageError::DataBatchRestoreCorruptedBatchFile.into())
}
}
}
@ -213,7 +213,7 @@ impl<F: RawFSInterface> DataBatchRestoreDriver<F> {
events,
schema_version,
}: NormalBatch,
) -> SDSSResult<()> {
) -> RuntimeResult<()> {
// NOTE(@ohsayan): current complexity is O(n) which is good enough (in the future I might revise this to a fancier impl)
// pin model
let irm = m.intent_read_model();
@ -298,12 +298,12 @@ impl<F: RawFSInterface> DataBatchRestoreDriver<F> {
}
impl<F: RawFSInterface> DataBatchRestoreDriver<F> {
fn read_batch_summary(&mut self, finished_early: bool) -> SDSSResult<u64> {
fn read_batch_summary(&mut self, finished_early: bool) -> RuntimeResult<u64> {
if !finished_early {
// we must read the batch termination signature
let b = self.f.read_byte()?;
if b != MARKER_END_OF_BATCH {
return Err(SDSSErrorKind::DataBatchRestoreCorruptedBatch.into());
return Err(StorageError::DataBatchRestoreCorruptedBatch.into());
}
}
// read actual commit
@ -320,10 +320,10 @@ impl<F: RawFSInterface> DataBatchRestoreDriver<F> {
if actual_checksum == u64::from_le_bytes(hardcoded_checksum) {
Ok(actual_commit)
} else {
Err(SDSSErrorKind::DataBatchRestoreCorruptedBatch.into())
Err(StorageError::DataBatchRestoreCorruptedBatch.into())
}
}
fn read_batch(&mut self) -> SDSSResult<Batch> {
fn read_batch(&mut self) -> RuntimeResult<Batch> {
let mut this_batch = vec![];
// check batch type
let batch_type = self.f.read_byte()?;
@ -340,7 +340,7 @@ impl<F: RawFSInterface> DataBatchRestoreDriver<F> {
}
_ => {
// this is the only singular byte that is expected to be intact. If this isn't intact either, I'm sorry
return Err(SDSSErrorKind::DataBatchRestoreCorruptedBatch.into());
return Err(StorageError::DataBatchRestoreCorruptedBatch.into());
}
}
// decode batch start block
@ -384,7 +384,7 @@ impl<F: RawFSInterface> DataBatchRestoreDriver<F> {
this_col_cnt -= 1;
}
if this_col_cnt != 0 {
return Err(SDSSErrorKind::DataBatchRestoreCorruptedEntry.into());
return Err(StorageError::DataBatchRestoreCorruptedEntry.into());
}
if change_type == 1 {
this_batch.push(DecodedBatchEvent::new(
@ -402,7 +402,7 @@ impl<F: RawFSInterface> DataBatchRestoreDriver<F> {
processed_in_this_batch += 1;
}
_ => {
return Err(SDSSErrorKind::DataBatchRestoreCorruptedBatch.into());
return Err(StorageError::DataBatchRestoreCorruptedBatch.into());
}
}
}
@ -413,13 +413,13 @@ impl<F: RawFSInterface> DataBatchRestoreDriver<F> {
batch_start_block.schema_version(),
)))
}
fn attempt_recover_data_batch(&mut self) -> SDSSResult<()> {
fn attempt_recover_data_batch(&mut self) -> RuntimeResult<()> {
if let Ok(MARKER_RECOVERY_EVENT) = self.f.inner_file().read_byte() {
return Ok(());
}
Err(SDSSErrorKind::DataBatchRestoreCorruptedBatch.into())
Err(StorageError::DataBatchRestoreCorruptedBatch.into())
}
fn read_start_batch_block(&mut self) -> SDSSResult<BatchStartBlock> {
fn read_start_batch_block(&mut self) -> RuntimeResult<BatchStartBlock> {
let pk_tag = self.f.read_byte()?;
let expected_commit = self.f.read_u64_le()?;
let schema_version = self.f.read_u64_le()?;
@ -465,9 +465,9 @@ impl BatchStartBlock {
}
impl<F: RawFSInterface> DataBatchRestoreDriver<F> {
fn decode_primary_key(&mut self, pk_type: u8) -> SDSSResult<PrimaryIndexKey> {
fn decode_primary_key(&mut self, pk_type: u8) -> RuntimeResult<PrimaryIndexKey> {
let Some(pk_type) = TagUnique::try_from_raw(pk_type) else {
return Err(SDSSErrorKind::DataBatchRestoreCorruptedEntry.into());
return Err(StorageError::DataBatchRestoreCorruptedEntry.into());
};
Ok(match pk_type {
TagUnique::SignedInt | TagUnique::UnsignedInt => {
@ -483,7 +483,7 @@ impl<F: RawFSInterface> DataBatchRestoreDriver<F> {
self.f.read_into_buffer(&mut data)?;
if pk_type == TagUnique::Str {
if core::str::from_utf8(&data).is_err() {
return Err(SDSSErrorKind::DataBatchRestoreCorruptedEntry.into());
return Err(StorageError::DataBatchRestoreCorruptedEntry.into());
}
}
unsafe {
@ -498,17 +498,17 @@ impl<F: RawFSInterface> DataBatchRestoreDriver<F> {
},
})
}
fn decode_cell(&mut self) -> SDSSResult<Datacell> {
fn decode_cell(&mut self) -> RuntimeResult<Datacell> {
let cell_type_sig = self.f.read_byte()?;
let Some(cell_type) = PersistTypeDscr::try_from_raw(cell_type_sig) else {
return Err(SDSSErrorKind::DataBatchRestoreCorruptedEntry.into());
return Err(StorageError::DataBatchRestoreCorruptedEntry.into());
};
Ok(match cell_type {
PersistTypeDscr::Null => Datacell::null(),
PersistTypeDscr::Bool => {
let bool = self.f.read_byte()?;
if bool > 1 {
return Err(SDSSErrorKind::DataBatchRestoreCorruptedEntry.into());
return Err(StorageError::DataBatchRestoreCorruptedEntry.into());
}
Datacell::new_bool(bool == 1)
}
@ -528,7 +528,7 @@ impl<F: RawFSInterface> DataBatchRestoreDriver<F> {
// UNSAFE(@ohsayan): +tagck
if cell_type == PersistTypeDscr::Str {
if core::str::from_utf8(&data).is_err() {
return Err(SDSSErrorKind::DataBatchRestoreCorruptedEntry.into());
return Err(StorageError::DataBatchRestoreCorruptedEntry.into());
}
Datacell::new_str(String::from_utf8_unchecked(data).into_boxed_str())
} else {
@ -543,13 +543,13 @@ impl<F: RawFSInterface> DataBatchRestoreDriver<F> {
list.push(self.decode_cell()?);
}
if len != list.len() as u64 {
return Err(SDSSErrorKind::DataBatchRestoreCorruptedEntry.into());
return Err(StorageError::DataBatchRestoreCorruptedEntry.into());
}
Datacell::new_list(list)
}
PersistTypeDscr::Dict => {
// we don't support dicts just yet
return Err(SDSSErrorKind::DataBatchRestoreCorruptedEntry.into());
return Err(StorageError::DataBatchRestoreCorruptedEntry.into());
}
})
}

@ -38,9 +38,10 @@ use {
tag::{CUTag, TagUnique},
DictGeneric,
},
error::{RuntimeResult, StorageError},
idx::{IndexBaseSpec, IndexSTSeqCns, STIndex, STIndexSeq},
mem::BufferedScanner,
storage::v1::{inf, SDSSError, SDSSErrorKind, SDSSResult},
storage::v1::inf,
},
util::{copy_slice_to_array as memcpy, EndianQW},
},
@ -70,7 +71,7 @@ where
fn meta_enc(buf: &mut VecU8, data: Self::InputType) {
buf.extend(data.st_len().u64_bytes_le());
}
unsafe fn meta_dec(scanner: &mut BufferedScanner) -> SDSSResult<Self::Metadata> {
unsafe fn meta_dec(scanner: &mut BufferedScanner) -> RuntimeResult<Self::Metadata> {
Ok(MapIndexSizeMD(scanner.next_u64_le() as usize))
}
fn obj_enc(buf: &mut VecU8, map: Self::InputType) {
@ -87,17 +88,17 @@ where
unsafe fn obj_dec(
scanner: &mut BufferedScanner,
MapIndexSizeMD(dict_size): Self::Metadata,
) -> SDSSResult<Self::OutputType> {
) -> RuntimeResult<Self::OutputType> {
let mut dict = M::MapType::idx_init();
while M::pretest_entry_metadata(scanner) & (dict.st_len() != dict_size) {
let md = unsafe {
// UNSAFE(@ohsayan): +pretest
M::entry_md_dec(scanner).ok_or::<SDSSError>(
SDSSErrorKind::InternalDecodeStructureCorruptedPayload.into(),
M::entry_md_dec(scanner).ok_or::<StorageError>(
StorageError::InternalDecodeStructureCorruptedPayload.into(),
)?
};
if !M::pretest_entry_data(scanner, &md) {
return Err(SDSSErrorKind::InternalDecodeStructureCorruptedPayload.into());
return Err(StorageError::InternalDecodeStructureCorruptedPayload.into());
}
let key;
let val;
@ -109,9 +110,7 @@ where
val = _v;
}
None => {
return Err(
SDSSErrorKind::InternalDecodeStructureCorruptedPayload.into()
)
return Err(StorageError::InternalDecodeStructureCorruptedPayload.into())
}
}
} else {
@ -123,21 +122,19 @@ where
val = _v;
}
_ => {
return Err(
SDSSErrorKind::InternalDecodeStructureCorruptedPayload.into()
)
return Err(StorageError::InternalDecodeStructureCorruptedPayload.into())
}
}
}
}
if !dict.st_insert(key, val) {
return Err(SDSSErrorKind::InternalDecodeStructureIllegalData.into());
return Err(StorageError::InternalDecodeStructureIllegalData.into());
}
}
if dict.st_len() == dict_size {
Ok(dict)
} else {
Err(SDSSErrorKind::InternalDecodeStructureIllegalData.into())
Err(StorageError::InternalDecodeStructureIllegalData.into())
}
}
}

@ -40,9 +40,9 @@ use {
dict::DictEntryGeneric,
tag::{DataTag, TagClass},
},
error::{RuntimeResult, StorageError},
idx::{AsKey, AsValue},
mem::BufferedScanner,
storage::v1::{SDSSErrorKind, SDSSResult},
},
std::mem,
};
@ -138,7 +138,7 @@ pub trait PersistObject {
/// ## Safety
///
/// Must pass the [`PersistObject::pretest_can_dec_metadata`] assertion
unsafe fn meta_dec(scanner: &mut BufferedScanner) -> SDSSResult<Self::Metadata>;
unsafe fn meta_dec(scanner: &mut BufferedScanner) -> RuntimeResult<Self::Metadata>;
// obj
/// obj enc
fn obj_enc(buf: &mut VecU8, data: Self::InputType);
@ -147,7 +147,10 @@ pub trait PersistObject {
/// ## Safety
///
/// Must pass the [`PersistObject::pretest_can_dec_object`] assertion
unsafe fn obj_dec(s: &mut BufferedScanner, md: Self::Metadata) -> SDSSResult<Self::OutputType>;
unsafe fn obj_dec(
s: &mut BufferedScanner,
md: Self::Metadata,
) -> RuntimeResult<Self::OutputType>;
// default
/// Default routine to encode an object + its metadata
fn default_full_enc(buf: &mut VecU8, data: Self::InputType) {
@ -155,16 +158,16 @@ pub trait PersistObject {
Self::obj_enc(buf, data);
}
/// Default routine to decode an object + its metadata (however, the metadata is used and not returned)
fn default_full_dec(scanner: &mut BufferedScanner) -> SDSSResult<Self::OutputType> {
fn default_full_dec(scanner: &mut BufferedScanner) -> RuntimeResult<Self::OutputType> {
if !Self::pretest_can_dec_metadata(scanner) {
return Err(SDSSErrorKind::InternalDecodeStructureCorrupted.into());
return Err(StorageError::InternalDecodeStructureCorrupted.into());
}
let md = unsafe {
// UNSAFE(@ohsayan): +pretest
Self::meta_dec(scanner)?
};
if !Self::pretest_can_dec_object(scanner, &md) {
return Err(SDSSErrorKind::InternalDecodeStructureCorruptedPayload.into());
return Err(StorageError::InternalDecodeStructureCorruptedPayload.into());
}
unsafe {
// UNSAFE(@ohsayan): +obj pretest
@ -262,39 +265,39 @@ pub mod enc {
pub mod dec {
use {
super::{map, PersistMapSpec, PersistObject},
crate::engine::{mem::BufferedScanner, storage::v1::SDSSResult},
crate::engine::{error::RuntimeResult, mem::BufferedScanner},
};
// obj
pub fn dec_full<Obj: PersistObject>(data: &[u8]) -> SDSSResult<Obj::OutputType> {
pub fn dec_full<Obj: PersistObject>(data: &[u8]) -> RuntimeResult<Obj::OutputType> {
let mut scanner = BufferedScanner::new(data);
dec_full_from_scanner::<Obj>(&mut scanner)
}
pub fn dec_full_from_scanner<Obj: PersistObject>(
scanner: &mut BufferedScanner,
) -> SDSSResult<Obj::OutputType> {
) -> RuntimeResult<Obj::OutputType> {
Obj::default_full_dec(scanner)
}
pub fn dec_full_self<Obj: PersistObject<OutputType = Obj>>(data: &[u8]) -> SDSSResult<Obj> {
pub fn dec_full_self<Obj: PersistObject<OutputType = Obj>>(data: &[u8]) -> RuntimeResult<Obj> {
dec_full::<Obj>(data)
}
// dec
pub fn dec_dict_full<PM: PersistMapSpec>(data: &[u8]) -> SDSSResult<PM::MapType> {
pub fn dec_dict_full<PM: PersistMapSpec>(data: &[u8]) -> RuntimeResult<PM::MapType> {
let mut scanner = BufferedScanner::new(data);
dec_dict_full_from_scanner::<PM>(&mut scanner)
}
fn dec_dict_full_from_scanner<PM: PersistMapSpec>(
scanner: &mut BufferedScanner,
) -> SDSSResult<PM::MapType> {
) -> RuntimeResult<PM::MapType> {
<map::PersistMapImpl<PM> as PersistObject>::default_full_dec(scanner)
}
pub mod utils {
use crate::engine::{
error::{RuntimeResult, StorageError},
mem::BufferedScanner,
storage::v1::{SDSSErrorKind, SDSSResult},
};
pub unsafe fn decode_string(s: &mut BufferedScanner, len: usize) -> SDSSResult<String> {
pub unsafe fn decode_string(s: &mut BufferedScanner, len: usize) -> RuntimeResult<String> {
String::from_utf8(s.next_chunk_variable(len).to_owned())
.map_err(|_| SDSSErrorKind::InternalDecodeStructureCorruptedPayload.into())
.map_err(|_| StorageError::InternalDecodeStructureCorruptedPayload.into())
}
}
}

@ -38,8 +38,9 @@ use {
uuid::Uuid,
DictGeneric,
},
error::{RuntimeResult, StorageError},
mem::{BufferedScanner, VInline},
storage::v1::{inf, SDSSErrorKind, SDSSResult},
storage::v1::inf,
},
util::EndianQW,
},
@ -113,13 +114,16 @@ impl<'a> PersistObject for LayerRef<'a> {
buf.extend(layer.tag().tag_selector().value_qword().to_le_bytes());
buf.extend(0u64.to_le_bytes());
}
unsafe fn meta_dec(scanner: &mut BufferedScanner) -> SDSSResult<Self::Metadata> {
unsafe fn meta_dec(scanner: &mut BufferedScanner) -> RuntimeResult<Self::Metadata> {
Ok(LayerMD::new(scanner.next_u64_le(), scanner.next_u64_le()))
}
fn obj_enc(_: &mut VecU8, _: Self::InputType) {}
unsafe fn obj_dec(_: &mut BufferedScanner, md: Self::Metadata) -> SDSSResult<Self::OutputType> {
unsafe fn obj_dec(
_: &mut BufferedScanner,
md: Self::Metadata,
) -> RuntimeResult<Self::OutputType> {
if (md.type_selector > TagSelector::List.value_qword()) | (md.prop_set_arity != 0) {
return Err(SDSSErrorKind::InternalDecodeStructureCorruptedPayload.into());
return Err(StorageError::InternalDecodeStructureCorruptedPayload.into());
}
Ok(Layer::new_empty_props(
TagSelector::from_raw(md.type_selector as u8).into_full(),
@ -167,7 +171,7 @@ impl<'a> PersistObject for FieldRef<'a> {
buf.extend(slf.layers().len().u64_bytes_le());
buf.push(slf.is_nullable() as u8);
}
unsafe fn meta_dec(scanner: &mut BufferedScanner) -> SDSSResult<Self::Metadata> {
unsafe fn meta_dec(scanner: &mut BufferedScanner) -> RuntimeResult<Self::Metadata> {
Ok(FieldMD::new(
scanner.next_u64_le(),
scanner.next_u64_le(),
@ -182,7 +186,7 @@ impl<'a> PersistObject for FieldRef<'a> {
unsafe fn obj_dec(
scanner: &mut BufferedScanner,
md: Self::Metadata,
) -> SDSSResult<Self::OutputType> {
) -> RuntimeResult<Self::OutputType> {
let mut layers = VInline::new();
let mut fin = false;
while (!scanner.eof())
@ -202,7 +206,7 @@ impl<'a> PersistObject for FieldRef<'a> {
if (field.layers().len() as u64 == md.layer_c) & (md.null <= 1) & (md.prop_c == 0) & fin {
Ok(field)
} else {
Err(SDSSErrorKind::InternalDecodeStructureCorrupted.into())
Err(StorageError::InternalDecodeStructureCorrupted.into())
}
}
}
@ -255,7 +259,7 @@ impl<'a> PersistObject for ModelLayoutRef<'a> {
buf.extend(v.p_tag().tag_selector().value_qword().to_le_bytes());
buf.extend(irm.fields().len().u64_bytes_le());
}
unsafe fn meta_dec(scanner: &mut BufferedScanner) -> SDSSResult<Self::Metadata> {
unsafe fn meta_dec(scanner: &mut BufferedScanner) -> RuntimeResult<Self::Metadata> {
Ok(ModelLayoutMD::new(
Uuid::from_bytes(scanner.next_chunk()),
scanner.next_u64_le(),
@ -273,7 +277,7 @@ impl<'a> PersistObject for ModelLayoutRef<'a> {
unsafe fn obj_dec(
scanner: &mut BufferedScanner,
md: Self::Metadata,
) -> SDSSResult<Self::OutputType> {
) -> RuntimeResult<Self::OutputType> {
let key = inf::dec::utils::decode_string(scanner, md.p_key_len as usize)?;
let fieldmap =
<super::map::PersistMapImpl<super::map::FieldMapSpec> as PersistObject>::obj_dec(
@ -281,7 +285,7 @@ impl<'a> PersistObject for ModelLayoutRef<'a> {
super::map::MapIndexSizeMD(md.field_c as usize),
)?;
let ptag = if md.p_key_tag > TagSelector::MAX as u64 {
return Err(SDSSErrorKind::InternalDecodeStructureCorruptedPayload.into());
return Err(StorageError::InternalDecodeStructureCorruptedPayload.into());
} else {
TagSelector::from_raw(md.p_key_tag as u8)
};
@ -326,7 +330,7 @@ impl<'a> PersistObject for SpaceLayoutRef<'a> {
buf.extend(space.get_uuid().to_le_bytes());
buf.extend(space_meta.len().u64_bytes_le());
}
unsafe fn meta_dec(scanner: &mut BufferedScanner) -> SDSSResult<Self::Metadata> {
unsafe fn meta_dec(scanner: &mut BufferedScanner) -> RuntimeResult<Self::Metadata> {
Ok(SpaceLayoutMD::new(
Uuid::from_bytes(scanner.next_chunk()),
scanner.next_u64_le() as usize,
@ -340,7 +344,7 @@ impl<'a> PersistObject for SpaceLayoutRef<'a> {
unsafe fn obj_dec(
scanner: &mut BufferedScanner,
md: Self::Metadata,
) -> SDSSResult<Self::OutputType> {
) -> RuntimeResult<Self::OutputType> {
let space_meta =
<super::map::PersistMapImpl<super::map::GenericDictSpec> as PersistObject>::obj_dec(
scanner,

@ -44,9 +44,12 @@
use {
super::{
rw::{FileOpen, RawFSInterface, SDSSFileIO},
spec, SDSSErrorKind, SDSSResult,
spec,
},
crate::{
engine::error::{RuntimeResult, StorageError},
util::{compiler, copy_a_into_b, copy_slice_to_array as memcpy},
},
crate::util::{compiler, copy_a_into_b, copy_slice_to_array as memcpy},
std::marker::PhantomData,
};
@ -55,7 +58,7 @@ const CRC: crc::Crc<u32> = crc::Crc::<u32>::new(&crc::CRC_32_ISO_HDLC);
pub fn open_journal<TA: JournalAdapter, Fs: RawFSInterface, F: spec::FileSpec>(
log_file_name: &str,
gs: &TA::GlobalState,
) -> SDSSResult<FileOpen<JournalWriter<Fs, TA>>> {
) -> RuntimeResult<FileOpen<JournalWriter<Fs, TA>>> {
let file = match SDSSFileIO::<Fs>::open_or_create_perm_rw::<F>(log_file_name)? {
FileOpen::Created(f) => return Ok(FileOpen::Created(JournalWriter::new(f, 0, true)?)),
FileOpen::Existing((file, _header)) => file,
@ -188,7 +191,7 @@ pub struct JournalReader<TA, Fs: RawFSInterface> {
}
impl<TA: JournalAdapter, Fs: RawFSInterface> JournalReader<TA, Fs> {
pub fn new(log_file: SDSSFileIO<Fs>) -> SDSSResult<Self> {
pub fn new(log_file: SDSSFileIO<Fs>) -> RuntimeResult<Self> {
let log_size = log_file.file_length()? - spec::SDSSStaticHeaderV1Compact::SIZE as u64;
Ok(Self {
log_file,
@ -200,7 +203,7 @@ impl<TA: JournalAdapter, Fs: RawFSInterface> JournalReader<TA, Fs> {
})
}
/// Read the next event and apply it to the global state
pub fn rapply_next_event(&mut self, gs: &TA::GlobalState) -> SDSSResult<()> {
pub fn rapply_next_event(&mut self, gs: &TA::GlobalState) -> RuntimeResult<()> {
// read metadata
let mut en_jrnl_md = [0u8; JournalEntryMetadata::SIZE];
self.logfile_read_into_buffer(&mut en_jrnl_md)?; // FIXME(@ohsayan): increase tolerance to not just payload
@ -222,7 +225,7 @@ impl<TA: JournalAdapter, Fs: RawFSInterface> JournalReader<TA, Fs> {
}
match entry_metadata
.event_source_marker()
.ok_or(SDSSErrorKind::JournalLogEntryCorrupted)?
.ok_or(StorageError::JournalLogEntryCorrupted)?
{
EventSourceMarker::ServerStandard => {}
EventSourceMarker::DriverClosed => {
@ -237,7 +240,7 @@ impl<TA: JournalAdapter, Fs: RawFSInterface> JournalReader<TA, Fs> {
EventSourceMarker::DriverReopened | EventSourceMarker::RecoveryReverseLastJournal => {
// these two are only taken in close and error paths (respectively) so we shouldn't see them here; this is bad
// two special directives in the middle of nowhere? incredible
return Err(SDSSErrorKind::JournalCorrupted.into());
return Err(StorageError::JournalCorrupted.into());
}
}
// read payload
@ -256,7 +259,7 @@ impl<TA: JournalAdapter, Fs: RawFSInterface> JournalReader<TA, Fs> {
Ok(())
}
/// handle a driver reopen (IMPORTANT: every event is unique so this must be called BEFORE the ID is incremented)
fn handle_driver_reopen(&mut self) -> SDSSResult<()> {
fn handle_driver_reopen(&mut self) -> RuntimeResult<()> {
if self.has_remaining_bytes(JournalEntryMetadata::SIZE as _) {
let mut reopen_block = [0u8; JournalEntryMetadata::SIZE];
self.logfile_read_into_buffer(&mut reopen_block)?; // exit jump -> not our business since we have checked flen and if it changes due to user intervention, that's a you problem
@ -270,10 +273,10 @@ impl<TA: JournalAdapter, Fs: RawFSInterface> JournalReader<TA, Fs> {
Ok(())
} else {
// FIXME(@ohsayan): tolerate loss in this directive too
Err(SDSSErrorKind::JournalCorrupted.into())
Err(StorageError::JournalCorrupted.into())
}
} else {
Err(SDSSErrorKind::JournalCorrupted.into())
Err(StorageError::JournalCorrupted.into())
}
}
#[cold] // FIXME(@ohsayan): how bad can prod systems be? (clue: pretty bad, so look for possible changes)
@ -281,12 +284,12 @@ impl<TA: JournalAdapter, Fs: RawFSInterface> JournalReader<TA, Fs> {
/// attempt to recover the journal using the reverse directive (simple strategy)
/// IMPORTANT: every event is unique so this must be called BEFORE the ID is incremented (remember that we only increment
/// once we **sucessfully** finish processing a normal (aka server event origin) event and not a non-normal branch)
fn try_recover_journal_strategy_simple_reverse(&mut self) -> SDSSResult<()> {
fn try_recover_journal_strategy_simple_reverse(&mut self) -> RuntimeResult<()> {
debug_assert!(TA::RECOVERY_PLUGIN, "recovery plugin not enabled");
self.__record_read_bytes(JournalEntryMetadata::SIZE); // FIXME(@ohsayan): don't assume read length?
let mut entry_buf = [0u8; JournalEntryMetadata::SIZE];
if self.log_file.read_to_buffer(&mut entry_buf).is_err() {
return Err(SDSSErrorKind::JournalCorrupted.into());
return Err(StorageError::JournalCorrupted.into());
}
let entry = JournalEntryMetadata::decode(entry_buf);
let okay = (entry.event_id == self.evid as u128)
@ -297,11 +300,14 @@ impl<TA: JournalAdapter, Fs: RawFSInterface> JournalReader<TA, Fs> {
if okay {
return Ok(());
} else {
Err(SDSSErrorKind::JournalCorrupted.into())
Err(StorageError::JournalCorrupted.into())
}
}
/// Read and apply all events in the given log file to the global state, returning the (open file, last event ID)
pub fn scroll(file: SDSSFileIO<Fs>, gs: &TA::GlobalState) -> SDSSResult<(SDSSFileIO<Fs>, u64)> {
pub fn scroll(
file: SDSSFileIO<Fs>,
gs: &TA::GlobalState,
) -> RuntimeResult<(SDSSFileIO<Fs>, u64)> {
let mut slf = Self::new(file)?;
while !slf.end_of_file() {
slf.rapply_next_event(gs)?;
@ -309,7 +315,7 @@ impl<TA: JournalAdapter, Fs: RawFSInterface> JournalReader<TA, Fs> {
if slf.closed {
Ok((slf.log_file, slf.evid))
} else {
Err(SDSSErrorKind::JournalCorrupted.into())
Err(StorageError::JournalCorrupted.into())
}
}
}
@ -330,7 +336,7 @@ impl<TA, Fs: RawFSInterface> JournalReader<TA, Fs> {
}
impl<TA, Fs: RawFSInterface> JournalReader<TA, Fs> {
fn logfile_read_into_buffer(&mut self, buf: &mut [u8]) -> SDSSResult<()> {
fn logfile_read_into_buffer(&mut self, buf: &mut [u8]) -> RuntimeResult<()> {
if !self.has_remaining_bytes(buf.len() as _) {
// do this right here to avoid another syscall
return Err(std::io::Error::from(std::io::ErrorKind::UnexpectedEof).into());
@ -351,7 +357,7 @@ pub struct JournalWriter<Fs: RawFSInterface, TA> {
}
impl<Fs: RawFSInterface, TA: JournalAdapter> JournalWriter<Fs, TA> {
pub fn new(mut log_file: SDSSFileIO<Fs>, last_txn_id: u64, new: bool) -> SDSSResult<Self> {
pub fn new(mut log_file: SDSSFileIO<Fs>, last_txn_id: u64, new: bool) -> RuntimeResult<Self> {
let log_size = log_file.file_length()?;
log_file.seek_from_start(log_size)?; // avoid jumbling with headers
let mut slf = Self {
@ -366,7 +372,7 @@ impl<Fs: RawFSInterface, TA: JournalAdapter> JournalWriter<Fs, TA> {
}
Ok(slf)
}
pub fn append_event(&mut self, event: TA::JournalEvent) -> SDSSResult<()> {
pub fn append_event(&mut self, event: TA::JournalEvent) -> RuntimeResult<()> {
let encoded = TA::encode(event);
let md = JournalEntryMetadata::new(
self._incr_id() as u128,
@ -380,7 +386,10 @@ impl<Fs: RawFSInterface, TA: JournalAdapter> JournalWriter<Fs, TA> {
self.log_file.fsync_all()?;
Ok(())
}
pub fn append_event_with_recovery_plugin(&mut self, event: TA::JournalEvent) -> SDSSResult<()> {
pub fn append_event_with_recovery_plugin(
&mut self,
event: TA::JournalEvent,
) -> RuntimeResult<()> {
debug_assert!(TA::RECOVERY_PLUGIN);
match self.append_event(event) {
Ok(()) => Ok(()),
@ -390,22 +399,22 @@ impl<Fs: RawFSInterface, TA: JournalAdapter> JournalWriter<Fs, TA> {
}
impl<Fs: RawFSInterface, TA> JournalWriter<Fs, TA> {
pub fn appendrec_journal_reverse_entry(&mut self) -> SDSSResult<()> {
pub fn appendrec_journal_reverse_entry(&mut self) -> RuntimeResult<()> {
let mut entry =
JournalEntryMetadata::new(0, EventSourceMarker::RECOVERY_REVERSE_LAST_JOURNAL, 0, 0);
entry.event_id = self._incr_id() as u128;
if self.log_file.fsynced_write(&entry.encoded()).is_ok() {
return Ok(());
}
Err(SDSSErrorKind::JournalWRecoveryStageOneFailCritical.into())
Err(StorageError::JournalWRecoveryStageOneFailCritical.into())
}
pub fn append_journal_reopen(&mut self) -> SDSSResult<()> {
pub fn append_journal_reopen(&mut self) -> RuntimeResult<()> {
let id = self._incr_id() as u128;
self.log_file.fsynced_write(
&JournalEntryMetadata::new(id, EventSourceMarker::DRIVER_REOPENED, 0, 0).encoded(),
)
}
pub fn __append_journal_close_and_close(&mut self) -> SDSSResult<()> {
pub fn __append_journal_close_and_close(&mut self) -> RuntimeResult<()> {
self.closed = true;
let id = self._incr_id() as u128;
self.log_file.fsynced_write(
@ -413,7 +422,7 @@ impl<Fs: RawFSInterface, TA> JournalWriter<Fs, TA> {
)?;
Ok(())
}
pub fn append_journal_close_and_close(mut self) -> SDSSResult<()> {
pub fn append_journal_close_and_close(mut self) -> RuntimeResult<()> {
self.__append_journal_close_and_close()
}
}

@ -27,12 +27,14 @@
use crate::engine::{
core::GlobalNS,
data::uuid::Uuid,
error::RuntimeResult,
fractal::error::ErrorContext,
fractal::{FractalModelDriver, ModelDrivers, ModelUniqueID},
storage::v1::{
batch_jrnl,
journal::{self, JournalWriter},
rw::{FileOpen, RawFSInterface},
spec, LocalFS, SDSSResult,
spec, LocalFS,
},
txn::gns::{GNSAdapter, GNSTransactionDriverAnyFS},
};
@ -61,7 +63,7 @@ impl SEInitState {
gns,
}
}
pub fn try_init() -> SDSSResult<Self> {
pub fn try_init() -> RuntimeResult<Self> {
let gns = GlobalNS::empty();
let gns_txn_driver = open_gns_driver(GNS_FILE_PATH, &gns)?;
let new_instance = gns_txn_driver.is_created();
@ -73,11 +75,9 @@ impl SEInitState {
for (model_name, model) in space.models().read().iter() {
let path =
Self::model_path(space_name, space_uuid, model_name, model.get_uuid());
let persist_driver = batch_jrnl::reinit(&path, model).map_err(|e| {
e.add_ctx(format!(
"failed to restore model data from journal in `{path}`"
))
})?;
let persist_driver = batch_jrnl::reinit(&path, model).inherit_set_dmsg(
format!("failed to restore model data from journal in `{path}`"),
)?;
let _ = model_drivers.insert(
ModelUniqueID::new(space_name, model_name, model.get_uuid()),
FractalModelDriver::init(persist_driver),
@ -119,6 +119,6 @@ impl SEInitState {
pub fn open_gns_driver<Fs: RawFSInterface>(
path: &str,
gns: &GlobalNS,
) -> SDSSResult<FileOpen<JournalWriter<Fs, GNSAdapter>>> {
) -> RuntimeResult<FileOpen<JournalWriter<Fs, GNSAdapter>>> {
journal::open_journal::<GNSAdapter, Fs, spec::GNSTransactionLogV1>(path, gns)
}

@ -26,12 +26,10 @@
use {
crate::engine::{
storage::v1::{
rw::{
FileOpen, RawFSInterface, RawFileInterface, RawFileInterfaceExt,
RawFileInterfaceRead, RawFileInterfaceWrite, RawFileInterfaceWriteExt,
},
SDSSResult,
error::RuntimeResult,
storage::v1::rw::{
FileOpen, RawFSInterface, RawFileInterface, RawFileInterfaceExt, RawFileInterfaceRead,
RawFileInterfaceWrite, RawFileInterfaceWriteExt,
},
sync::cell::Lazy,
},
@ -81,16 +79,16 @@ impl VNode {
errors
*/
fn err_item_is_not_file<T>() -> super::SDSSResult<T> {
fn err_item_is_not_file<T>() -> RuntimeResult<T> {
Err(Error::new(ErrorKind::InvalidInput, "found directory, not a file").into())
}
fn err_file_in_dir_path<T>() -> super::SDSSResult<T> {
fn err_file_in_dir_path<T>() -> RuntimeResult<T> {
Err(Error::new(ErrorKind::InvalidInput, "found file in directory path").into())
}
fn err_dir_missing_in_path<T>() -> super::SDSSResult<T> {
fn err_dir_missing_in_path<T>() -> RuntimeResult<T> {
Err(Error::new(ErrorKind::InvalidInput, "could not find directory in path").into())
}
fn err_could_not_find_item<T>() -> super::SDSSResult<T> {
fn err_could_not_find_item<T>() -> RuntimeResult<T> {
Err(Error::new(ErrorKind::NotFound, "could not find item").into())
}
@ -117,7 +115,7 @@ pub struct VirtualFS;
impl RawFSInterface for VirtualFS {
type File = VFileDescriptor;
fn fs_rename_file(from: &str, to: &str) -> SDSSResult<()> {
fn fs_rename_file(from: &str, to: &str) -> RuntimeResult<()> {
// get file data
let data = with_file(from, |f| Ok(f.data.clone()))?;
// create new file
@ -134,7 +132,7 @@ impl RawFSInterface for VirtualFS {
// delete old file
Self::fs_remove_file(from)
}
fn fs_remove_file(fpath: &str) -> SDSSResult<()> {
fn fs_remove_file(fpath: &str) -> RuntimeResult<()> {
handle_item_mut(fpath, |e| match e.get() {
VNode::File(_) => {
e.remove();
@ -143,7 +141,7 @@ impl RawFSInterface for VirtualFS {
_ => return err_item_is_not_file(),
})
}
fn fs_create_dir(fpath: &str) -> super::SDSSResult<()> {
fn fs_create_dir(fpath: &str) -> RuntimeResult<()> {
// get vfs
let mut vfs = VFS.write();
// get root dir
@ -167,12 +165,12 @@ impl RawFSInterface for VirtualFS {
}
}
}
fn fs_create_dir_all(fpath: &str) -> super::SDSSResult<()> {
fn fs_create_dir_all(fpath: &str) -> RuntimeResult<()> {
let mut vfs = VFS.write();
fn create_ahead(
mut ahead: &[&str],
current: &mut HashMap<Box<str>, VNode>,
) -> SDSSResult<()> {
) -> RuntimeResult<()> {
if ahead.is_empty() {
return Ok(());
}
@ -197,13 +195,13 @@ impl RawFSInterface for VirtualFS {
let pieces = split_parts(fpath);
create_ahead(&pieces, &mut *vfs)
}
fn fs_delete_dir(fpath: &str) -> super::SDSSResult<()> {
fn fs_delete_dir(fpath: &str) -> RuntimeResult<()> {
delete_dir(fpath, false)
}
fn fs_delete_dir_all(fpath: &str) -> super::SDSSResult<()> {
fn fs_delete_dir_all(fpath: &str) -> RuntimeResult<()> {
delete_dir(fpath, true)
}
fn fs_fopen_or_create_rw(fpath: &str) -> super::SDSSResult<super::rw::FileOpen<Self::File>> {
fn fs_fopen_or_create_rw(fpath: &str) -> RuntimeResult<super::rw::FileOpen<Self::File>> {
let mut vfs = VFS.write();
// components
let (target_file, components) = split_target_and_components(fpath);
@ -223,7 +221,7 @@ impl RawFSInterface for VirtualFS {
}
}
}
fn fs_fcreate_rw(fpath: &str) -> SDSSResult<Self::File> {
fn fs_fcreate_rw(fpath: &str) -> RuntimeResult<Self::File> {
let mut vfs = VFS.write();
let (target_file, components) = split_target_and_components(fpath);
let target_dir = find_target_dir_mut(components, &mut vfs)?;
@ -254,7 +252,7 @@ impl RawFSInterface for VirtualFS {
}
}
}
fn fs_fopen_rw(fpath: &str) -> SDSSResult<Self::File> {
fn fs_fopen_rw(fpath: &str) -> RuntimeResult<Self::File> {
with_file_mut(fpath, |f| {
f.read = true;
f.write = true;
@ -266,7 +264,7 @@ impl RawFSInterface for VirtualFS {
fn find_target_dir_mut<'a>(
components: ComponentIter,
mut current: &'a mut HashMap<Box<str>, VNode>,
) -> Result<&'a mut HashMap<Box<str>, VNode>, super::SDSSError> {
) -> RuntimeResult<&'a mut HashMap<Box<str>, VNode>> {
for component in components {
match current.get_mut(component) {
Some(VNode::Dir(d)) => current = d,
@ -280,7 +278,7 @@ fn find_target_dir_mut<'a>(
fn find_target_dir<'a>(
components: ComponentIter,
mut current: &'a HashMap<Box<str>, VNode>,
) -> Result<&'a HashMap<Box<str>, VNode>, super::SDSSError> {
) -> RuntimeResult<&'a HashMap<Box<str>, VNode>> {
for component in components {
match current.get(component) {
Some(VNode::Dir(d)) => current = d,
@ -293,8 +291,8 @@ fn find_target_dir<'a>(
fn handle_item_mut<T>(
fpath: &str,
f: impl Fn(OccupiedEntry<Box<str>, VNode>) -> super::SDSSResult<T>,
) -> super::SDSSResult<T> {
f: impl Fn(OccupiedEntry<Box<str>, VNode>) -> RuntimeResult<T>,
) -> RuntimeResult<T> {
let mut vfs = VFS.write();
let mut current = &mut *vfs;
// process components
@ -313,7 +311,7 @@ fn handle_item_mut<T>(
Entry::Vacant(_) => return err_could_not_find_item(),
}
}
fn handle_item<T>(fpath: &str, f: impl Fn(&VNode) -> super::SDSSResult<T>) -> super::SDSSResult<T> {
fn handle_item<T>(fpath: &str, f: impl Fn(&VNode) -> RuntimeResult<T>) -> RuntimeResult<T> {
let vfs = VFS.read();
let mut current = &*vfs;
// process components
@ -332,7 +330,7 @@ fn handle_item<T>(fpath: &str, f: impl Fn(&VNode) -> super::SDSSResult<T>) -> su
None => return err_could_not_find_item(),
}
}
fn delete_dir(fpath: &str, allow_if_non_empty: bool) -> Result<(), super::SDSSError> {
fn delete_dir(fpath: &str, allow_if_non_empty: bool) -> RuntimeResult<()> {
handle_item_mut(fpath, |node| match node.get() {
VNode::Dir(d) => {
if allow_if_non_empty || d.is_empty() {
@ -387,7 +385,10 @@ impl Drop for VFileDescriptor {
}
}
fn with_file_mut<T>(fpath: &str, mut f: impl FnMut(&mut VFile) -> SDSSResult<T>) -> SDSSResult<T> {
fn with_file_mut<T>(
fpath: &str,
mut f: impl FnMut(&mut VFile) -> RuntimeResult<T>,
) -> RuntimeResult<T> {
let mut vfs = VFS.write();
let (target_file, components) = split_target_and_components(fpath);
let target_dir = find_target_dir_mut(components, &mut vfs)?;
@ -398,7 +399,7 @@ fn with_file_mut<T>(fpath: &str, mut f: impl FnMut(&mut VFile) -> SDSSResult<T>)
}
}
fn with_file<T>(fpath: &str, mut f: impl FnMut(&VFile) -> SDSSResult<T>) -> SDSSResult<T> {
fn with_file<T>(fpath: &str, mut f: impl FnMut(&VFile) -> RuntimeResult<T>) -> RuntimeResult<T> {
let vfs = VFS.read();
let (target_file, components) = split_target_and_components(fpath);
let target_dir = find_target_dir(components, &vfs)?;
@ -412,16 +413,16 @@ fn with_file<T>(fpath: &str, mut f: impl FnMut(&VFile) -> SDSSResult<T>) -> SDSS
impl RawFileInterface for VFileDescriptor {
type Reader = Self;
type Writer = Self;
fn into_buffered_reader(self) -> super::SDSSResult<Self::Reader> {
fn into_buffered_reader(self) -> RuntimeResult<Self::Reader> {
Ok(self)
}
fn into_buffered_writer(self) -> super::SDSSResult<Self::Writer> {
fn into_buffered_writer(self) -> RuntimeResult<Self::Writer> {
Ok(self)
}
}
impl RawFileInterfaceRead for VFileDescriptor {
fn fr_read_exact(&mut self, buf: &mut [u8]) -> super::SDSSResult<()> {
fn fr_read_exact(&mut self, buf: &mut [u8]) -> RuntimeResult<()> {
with_file_mut(&self.0, |file| {
if !file.read {
return Err(
@ -440,7 +441,7 @@ impl RawFileInterfaceRead for VFileDescriptor {
}
impl RawFileInterfaceWrite for VFileDescriptor {
fn fw_write_all(&mut self, bytes: &[u8]) -> super::SDSSResult<()> {
fn fw_write_all(&mut self, bytes: &[u8]) -> RuntimeResult<()> {
with_file_mut(&self.0, |file| {
if !file.write {
return Err(
@ -458,10 +459,10 @@ impl RawFileInterfaceWrite for VFileDescriptor {
}
impl RawFileInterfaceWriteExt for VFileDescriptor {
fn fw_fsync_all(&mut self) -> super::SDSSResult<()> {
fn fw_fsync_all(&mut self) -> RuntimeResult<()> {
with_file(&self.0, |_| Ok(()))
}
fn fw_truncate_to(&mut self, to: u64) -> super::SDSSResult<()> {
fn fw_truncate_to(&mut self, to: u64) -> RuntimeResult<()> {
with_file_mut(&self.0, |file| {
if !file.write {
return Err(
@ -482,13 +483,13 @@ impl RawFileInterfaceWriteExt for VFileDescriptor {
}
impl RawFileInterfaceExt for VFileDescriptor {
fn fext_file_length(&self) -> super::SDSSResult<u64> {
fn fext_file_length(&self) -> RuntimeResult<u64> {
with_file(&self.0, |f| Ok(f.data.len() as u64))
}
fn fext_cursor(&mut self) -> super::SDSSResult<u64> {
fn fext_cursor(&mut self) -> RuntimeResult<u64> {
with_file(&self.0, |f| Ok(f.pos as u64))
}
fn fext_seek_ahead_from_start_by(&mut self, by: u64) -> super::SDSSResult<()> {
fn fext_seek_ahead_from_start_by(&mut self, by: u64) -> RuntimeResult<()> {
with_file_mut(&self.0, |file| {
if by > file.data.len() as u64 {
return Err(
@ -517,72 +518,72 @@ pub struct NullFile;
impl RawFSInterface for NullFS {
const NOT_NULL: bool = false;
type File = NullFile;
fn fs_rename_file(_: &str, _: &str) -> SDSSResult<()> {
fn fs_rename_file(_: &str, _: &str) -> RuntimeResult<()> {
Ok(())
}
fn fs_remove_file(_: &str) -> SDSSResult<()> {
fn fs_remove_file(_: &str) -> RuntimeResult<()> {
Ok(())
}
fn fs_create_dir(_: &str) -> SDSSResult<()> {
fn fs_create_dir(_: &str) -> RuntimeResult<()> {
Ok(())
}
fn fs_create_dir_all(_: &str) -> SDSSResult<()> {
fn fs_create_dir_all(_: &str) -> RuntimeResult<()> {
Ok(())
}
fn fs_delete_dir(_: &str) -> SDSSResult<()> {
fn fs_delete_dir(_: &str) -> RuntimeResult<()> {
Ok(())
}
fn fs_delete_dir_all(_: &str) -> SDSSResult<()> {
fn fs_delete_dir_all(_: &str) -> RuntimeResult<()> {
Ok(())
}
fn fs_fopen_or_create_rw(_: &str) -> SDSSResult<FileOpen<Self::File>> {
fn fs_fopen_or_create_rw(_: &str) -> RuntimeResult<FileOpen<Self::File>> {
Ok(FileOpen::Created(NullFile))
}
fn fs_fopen_rw(_: &str) -> SDSSResult<Self::File> {
fn fs_fopen_rw(_: &str) -> RuntimeResult<Self::File> {
Ok(NullFile)
}
fn fs_fcreate_rw(_: &str) -> SDSSResult<Self::File> {
fn fs_fcreate_rw(_: &str) -> RuntimeResult<Self::File> {
Ok(NullFile)
}
}
impl RawFileInterfaceRead for NullFile {
fn fr_read_exact(&mut self, _: &mut [u8]) -> SDSSResult<()> {
fn fr_read_exact(&mut self, _: &mut [u8]) -> RuntimeResult<()> {
Ok(())
}
}
impl RawFileInterfaceWrite for NullFile {
fn fw_write_all(&mut self, _: &[u8]) -> SDSSResult<()> {
fn fw_write_all(&mut self, _: &[u8]) -> RuntimeResult<()> {
Ok(())
}
}
impl RawFileInterfaceWriteExt for NullFile {
fn fw_fsync_all(&mut self) -> SDSSResult<()> {
fn fw_fsync_all(&mut self) -> RuntimeResult<()> {
Ok(())
}
fn fw_truncate_to(&mut self, _: u64) -> SDSSResult<()> {
fn fw_truncate_to(&mut self, _: u64) -> RuntimeResult<()> {
Ok(())
}
}
impl RawFileInterfaceExt for NullFile {
fn fext_file_length(&self) -> SDSSResult<u64> {
fn fext_file_length(&self) -> RuntimeResult<u64> {
Ok(0)
}
fn fext_cursor(&mut self) -> SDSSResult<u64> {
fn fext_cursor(&mut self) -> RuntimeResult<u64> {
Ok(0)
}
fn fext_seek_ahead_from_start_by(&mut self, _: u64) -> SDSSResult<()> {
fn fext_seek_ahead_from_start_by(&mut self, _: u64) -> RuntimeResult<()> {
Ok(())
}
}
impl RawFileInterface for NullFile {
type Reader = Self;
type Writer = Self;
fn into_buffered_reader(self) -> SDSSResult<Self::Reader> {
fn into_buffered_reader(self) -> RuntimeResult<Self::Reader> {
Ok(self)
}
fn into_buffered_writer(self) -> SDSSResult<Self::Writer> {
fn into_buffered_writer(self) -> RuntimeResult<Self::Writer> {
Ok(self)
}
}

@ -47,73 +47,3 @@ pub use {
pub mod data_batch {
pub use super::batch_jrnl::{create, reinit, DataBatchPersistDriver, DataBatchRestoreDriver};
}
use crate::{
engine::{
error::{CtxError, CtxResult},
txn::TransactionError,
},
util::os::SysIOError as IoError,
};
pub type SDSSResult<T> = CtxResult<T, SDSSErrorKind>;
pub type SDSSError = CtxError<SDSSErrorKind>;
#[derive(Debug)]
#[cfg_attr(test, derive(PartialEq))]
pub enum SDSSErrorKind {
// IO errors
/// An IO err
IoError(IoError),
OtherError(&'static str),
CorruptedFile(&'static str),
// header
/// version mismatch
HeaderDecodeVersionMismatch,
/// The entire header is corrupted
HeaderDecodeCorruptedHeader,
/// Expected header values were not matched with the current header
HeaderDecodeDataMismatch,
/// The time in the [header/dynrec/rtsig] is in the future
HeaderTimeConflict,
// journal
/// While attempting to handle a basic failure (such as adding a journal entry), the recovery engine ran into an exceptional
/// situation where it failed to make a necessary repair the log
JournalWRecoveryStageOneFailCritical,
/// An entry in the journal is corrupted
JournalLogEntryCorrupted,
/// The structure of the journal is corrupted
JournalCorrupted,
// internal file structures
/// While attempting to decode a structure in an internal segment of a file, the storage engine ran into a possibly irrecoverable error
InternalDecodeStructureCorrupted,
/// the payload (non-static) part of a structure in an internal segment of a file is corrupted
InternalDecodeStructureCorruptedPayload,
/// the data for an internal structure was decoded but is logically invalid
InternalDecodeStructureIllegalData,
/// when attempting to flush a data batch, the batch journal crashed and a recovery event was triggered. But even then,
/// the data batch journal could not be fixed
DataBatchRecoveryFailStageOne,
/// when attempting to restore a data batch from disk, the batch journal crashed and had a corruption, but it is irrecoverable
DataBatchRestoreCorruptedBatch,
/// when attempting to restore a data batch from disk, the driver encountered a corrupted entry
DataBatchRestoreCorruptedEntry,
/// we failed to close the data batch
DataBatchCloseError,
DataBatchRestoreCorruptedBatchFile,
JournalRestoreTxnError,
SysDBCorrupted,
}
impl From<TransactionError> for SDSSErrorKind {
fn from(_: TransactionError) -> Self {
Self::JournalRestoreTxnError
}
}
direct_from! {
SDSSErrorKind => {
std::io::Error as IoError,
IoError as IoError,
}
}

@ -25,11 +25,11 @@
*/
use {
super::{
spec::{FileSpec, Header},
SDSSResult,
super::spec::{FileSpec, Header},
crate::{
engine::{error::RuntimeResult, storage::SCrc},
util::os::SysIOError,
},
crate::{engine::storage::SCrc, util::os::SysIOError},
std::{
fs::{self, File},
io::{BufReader, BufWriter, Read, Seek, SeekFrom, Write},
@ -81,27 +81,27 @@ pub trait RawFSInterface {
/// the file descriptor that is returned by the file system when a file is opened
type File: RawFileInterface;
/// Remove a file
fn fs_remove_file(fpath: &str) -> SDSSResult<()>;
fn fs_remove_file(fpath: &str) -> RuntimeResult<()>;
/// Rename a file
fn fs_rename_file(from: &str, to: &str) -> SDSSResult<()>;
fn fs_rename_file(from: &str, to: &str) -> RuntimeResult<()>;
/// Create a directory
fn fs_create_dir(fpath: &str) -> SDSSResult<()>;
fn fs_create_dir(fpath: &str) -> RuntimeResult<()>;
/// Create a directory and all corresponding path components
fn fs_create_dir_all(fpath: &str) -> SDSSResult<()>;
fn fs_create_dir_all(fpath: &str) -> RuntimeResult<()>;
/// Delete a directory
fn fs_delete_dir(fpath: &str) -> SDSSResult<()>;
fn fs_delete_dir(fpath: &str) -> RuntimeResult<()>;
/// Delete a directory and recursively remove all (if any) children
fn fs_delete_dir_all(fpath: &str) -> SDSSResult<()>;
fn fs_delete_dir_all(fpath: &str) -> RuntimeResult<()>;
/// Open or create a file in R/W mode
///
/// This will:
/// - Create a file if it doesn't exist
/// - Open a file it it does exist
fn fs_fopen_or_create_rw(fpath: &str) -> SDSSResult<FileOpen<Self::File>>;
fn fs_fopen_or_create_rw(fpath: &str) -> RuntimeResult<FileOpen<Self::File>>;
/// Open an existing file
fn fs_fopen_rw(fpath: &str) -> SDSSResult<Self::File>;
fn fs_fopen_rw(fpath: &str) -> RuntimeResult<Self::File>;
/// Create a new file
fn fs_fcreate_rw(fpath: &str) -> SDSSResult<Self::File>;
fn fs_fcreate_rw(fpath: &str) -> RuntimeResult<Self::File>;
}
/// A file (well, probably) that can be used for RW operations along with advanced write and extended operations (such as seeking)
@ -114,46 +114,46 @@ where
{
type Reader: RawFileInterfaceRead + RawFileInterfaceExt;
type Writer: RawFileInterfaceWrite + RawFileInterfaceExt;
fn into_buffered_reader(self) -> SDSSResult<Self::Reader>;
fn into_buffered_writer(self) -> SDSSResult<Self::Writer>;
fn into_buffered_reader(self) -> RuntimeResult<Self::Reader>;
fn into_buffered_writer(self) -> RuntimeResult<Self::Writer>;
}
/// A file interface that supports read operations
pub trait RawFileInterfaceRead {
fn fr_read_exact(&mut self, buf: &mut [u8]) -> SDSSResult<()>;
fn fr_read_exact(&mut self, buf: &mut [u8]) -> RuntimeResult<()>;
}
impl<R: Read> RawFileInterfaceRead for R {
fn fr_read_exact(&mut self, buf: &mut [u8]) -> SDSSResult<()> {
fn fr_read_exact(&mut self, buf: &mut [u8]) -> RuntimeResult<()> {
self.read_exact(buf).map_err(From::from)
}
}
/// A file interface that supports write operations
pub trait RawFileInterfaceWrite {
fn fw_write_all(&mut self, buf: &[u8]) -> SDSSResult<()>;
fn fw_write_all(&mut self, buf: &[u8]) -> RuntimeResult<()>;
}
impl<W: Write> RawFileInterfaceWrite for W {
fn fw_write_all(&mut self, buf: &[u8]) -> SDSSResult<()> {
fn fw_write_all(&mut self, buf: &[u8]) -> RuntimeResult<()> {
self.write_all(buf).map_err(From::from)
}
}
/// A file interface that supports advanced write operations
pub trait RawFileInterfaceWriteExt {
fn fw_fsync_all(&mut self) -> SDSSResult<()>;
fn fw_truncate_to(&mut self, to: u64) -> SDSSResult<()>;
fn fw_fsync_all(&mut self) -> RuntimeResult<()>;
fn fw_truncate_to(&mut self, to: u64) -> RuntimeResult<()>;
}
/// A file interface that supports advanced file operations
pub trait RawFileInterfaceExt {
fn fext_file_length(&self) -> SDSSResult<u64>;
fn fext_cursor(&mut self) -> SDSSResult<u64>;
fn fext_seek_ahead_from_start_by(&mut self, ahead_by: u64) -> SDSSResult<()>;
fn fext_file_length(&self) -> RuntimeResult<u64>;
fn fext_cursor(&mut self) -> RuntimeResult<u64>;
fn fext_seek_ahead_from_start_by(&mut self, ahead_by: u64) -> RuntimeResult<()>;
}
fn cvt<T>(v: std::io::Result<T>) -> SDSSResult<T> {
fn cvt<T>(v: std::io::Result<T>) -> RuntimeResult<T> {
let r = v?;
Ok(r)
}
@ -164,25 +164,25 @@ pub struct LocalFS;
impl RawFSInterface for LocalFS {
type File = File;
fn fs_remove_file(fpath: &str) -> SDSSResult<()> {
fn fs_remove_file(fpath: &str) -> RuntimeResult<()> {
cvt(fs::remove_file(fpath))
}
fn fs_rename_file(from: &str, to: &str) -> SDSSResult<()> {
fn fs_rename_file(from: &str, to: &str) -> RuntimeResult<()> {
cvt(fs::rename(from, to))
}
fn fs_create_dir(fpath: &str) -> SDSSResult<()> {
fn fs_create_dir(fpath: &str) -> RuntimeResult<()> {
cvt(fs::create_dir(fpath))
}
fn fs_create_dir_all(fpath: &str) -> SDSSResult<()> {
fn fs_create_dir_all(fpath: &str) -> RuntimeResult<()> {
cvt(fs::create_dir_all(fpath))
}
fn fs_delete_dir(fpath: &str) -> SDSSResult<()> {
fn fs_delete_dir(fpath: &str) -> RuntimeResult<()> {
cvt(fs::remove_dir(fpath))
}
fn fs_delete_dir_all(fpath: &str) -> SDSSResult<()> {
fn fs_delete_dir_all(fpath: &str) -> RuntimeResult<()> {
cvt(fs::remove_dir_all(fpath))
}
fn fs_fopen_or_create_rw(fpath: &str) -> SDSSResult<FileOpen<Self::File>> {
fn fs_fopen_or_create_rw(fpath: &str) -> RuntimeResult<FileOpen<Self::File>> {
let f = File::options()
.create(true)
.read(true)
@ -195,7 +195,7 @@ impl RawFSInterface for LocalFS {
Ok(FileOpen::Existing(f))
}
}
fn fs_fcreate_rw(fpath: &str) -> SDSSResult<Self::File> {
fn fs_fcreate_rw(fpath: &str) -> RuntimeResult<Self::File> {
let f = File::options()
.create_new(true)
.read(true)
@ -203,7 +203,7 @@ impl RawFSInterface for LocalFS {
.open(fpath)?;
Ok(f)
}
fn fs_fopen_rw(fpath: &str) -> SDSSResult<Self::File> {
fn fs_fopen_rw(fpath: &str) -> RuntimeResult<Self::File> {
let f = File::options().read(true).write(true).open(fpath)?;
Ok(f)
}
@ -212,19 +212,19 @@ impl RawFSInterface for LocalFS {
impl RawFileInterface for File {
type Reader = BufReader<Self>;
type Writer = BufWriter<Self>;
fn into_buffered_reader(self) -> SDSSResult<Self::Reader> {
fn into_buffered_reader(self) -> RuntimeResult<Self::Reader> {
Ok(BufReader::new(self))
}
fn into_buffered_writer(self) -> SDSSResult<Self::Writer> {
fn into_buffered_writer(self) -> RuntimeResult<Self::Writer> {
Ok(BufWriter::new(self))
}
}
impl RawFileInterfaceWriteExt for File {
fn fw_fsync_all(&mut self) -> SDSSResult<()> {
fn fw_fsync_all(&mut self) -> RuntimeResult<()> {
cvt(self.sync_all())
}
fn fw_truncate_to(&mut self, to: u64) -> SDSSResult<()> {
fn fw_truncate_to(&mut self, to: u64) -> RuntimeResult<()> {
cvt(self.set_len(to))
}
}
@ -262,13 +262,13 @@ impl LocalFSFile for BufWriter<File> {
}
impl<F: LocalFSFile> RawFileInterfaceExt for F {
fn fext_file_length(&self) -> SDSSResult<u64> {
fn fext_file_length(&self) -> RuntimeResult<u64> {
Ok(self.file().metadata()?.len())
}
fn fext_cursor(&mut self) -> SDSSResult<u64> {
fn fext_cursor(&mut self) -> RuntimeResult<u64> {
cvt(self.file_mut().stream_position())
}
fn fext_seek_ahead_from_start_by(&mut self, by: u64) -> SDSSResult<()> {
fn fext_seek_ahead_from_start_by(&mut self, by: u64) -> RuntimeResult<()> {
cvt(self.file_mut().seek(SeekFrom::Start(by)).map(|_| ()))
}
}
@ -282,7 +282,7 @@ impl<Fs: RawFSInterface> SDSSFileTrackedWriter<Fs> {
pub fn new(f: SDSSFileIO<Fs>) -> Self {
Self { f, cs: SCrc::new() }
}
pub fn unfsynced_write(&mut self, block: &[u8]) -> SDSSResult<()> {
pub fn unfsynced_write(&mut self, block: &[u8]) -> RuntimeResult<()> {
match self.f.unfsynced_write(block) {
Ok(()) => {
self.cs.recompute_with_new_var_block(block);
@ -291,7 +291,7 @@ impl<Fs: RawFSInterface> SDSSFileTrackedWriter<Fs> {
e => e,
}
}
pub fn fsync_all(&mut self) -> SDSSResult<()> {
pub fn fsync_all(&mut self) -> RuntimeResult<()> {
self.f.fsync_all()
}
pub fn reset_and_finish_checksum(&mut self) -> u64 {
@ -315,7 +315,7 @@ pub struct SDSSFileTrackedReader<Fs: RawFSInterface> {
impl<Fs: RawFSInterface> SDSSFileTrackedReader<Fs> {
/// Important: this will only look at the data post the current cursor!
pub fn new(mut f: SDSSFileIO<Fs>) -> SDSSResult<Self> {
pub fn new(mut f: SDSSFileIO<Fs>) -> RuntimeResult<Self> {
let len = f.file_length()?;
let pos = f.retrieve_cursor()?;
Ok(Self {
@ -334,7 +334,7 @@ impl<Fs: RawFSInterface> SDSSFileTrackedReader<Fs> {
pub fn has_left(&self, v: u64) -> bool {
self.remaining() >= v
}
pub fn read_into_buffer(&mut self, buf: &mut [u8]) -> SDSSResult<()> {
pub fn read_into_buffer(&mut self, buf: &mut [u8]) -> RuntimeResult<()> {
if self.remaining() >= buf.len() as u64 {
match self.f.read_to_buffer(buf) {
Ok(()) => {
@ -348,7 +348,7 @@ impl<Fs: RawFSInterface> SDSSFileTrackedReader<Fs> {
Err(SysIOError::from(std::io::ErrorKind::InvalidInput).into())
}
}
pub fn read_byte(&mut self) -> SDSSResult<u8> {
pub fn read_byte(&mut self) -> RuntimeResult<u8> {
let mut buf = [0u8; 1];
self.read_into_buffer(&mut buf).map(|_| buf[0])
}
@ -366,7 +366,7 @@ impl<Fs: RawFSInterface> SDSSFileTrackedReader<Fs> {
pub fn __cursor_ahead_by(&mut self, sizeof: usize) {
self.pos += sizeof as u64;
}
pub fn read_block<const N: usize>(&mut self) -> SDSSResult<[u8; N]> {
pub fn read_block<const N: usize>(&mut self) -> RuntimeResult<[u8; N]> {
if !self.has_left(N as _) {
return Err(SysIOError::from(std::io::ErrorKind::InvalidInput).into());
}
@ -374,7 +374,7 @@ impl<Fs: RawFSInterface> SDSSFileTrackedReader<Fs> {
self.read_into_buffer(&mut buf)?;
Ok(buf)
}
pub fn read_u64_le(&mut self) -> SDSSResult<u64> {
pub fn read_u64_le(&mut self) -> RuntimeResult<u64> {
Ok(u64::from_le_bytes(self.read_block()?))
}
}
@ -386,19 +386,19 @@ pub struct SDSSFileIO<Fs: RawFSInterface> {
}
impl<Fs: RawFSInterface> SDSSFileIO<Fs> {
pub fn open<F: FileSpec>(fpath: &str) -> SDSSResult<(Self, F::Header)> {
pub fn open<F: FileSpec>(fpath: &str) -> RuntimeResult<(Self, F::Header)> {
let mut f = Self::_new(Fs::fs_fopen_rw(fpath)?);
let header = F::Header::decode_verify(&mut f, F::DECODE_DATA, F::VERIFY_DATA)?;
Ok((f, header))
}
pub fn create<F: FileSpec>(fpath: &str) -> SDSSResult<Self> {
pub fn create<F: FileSpec>(fpath: &str) -> RuntimeResult<Self> {
let mut f = Self::_new(Fs::fs_fcreate_rw(fpath)?);
F::Header::encode(&mut f, F::ENCODE_DATA)?;
Ok(f)
}
pub fn open_or_create_perm_rw<F: FileSpec>(
fpath: &str,
) -> SDSSResult<FileOpen<Self, (Self, F::Header)>> {
) -> RuntimeResult<FileOpen<Self, (Self, F::Header)>> {
match Fs::fs_fopen_or_create_rw(fpath)? {
FileOpen::Created(c) => {
let mut f = Self::_new(c);
@ -421,37 +421,37 @@ impl<Fs: RawFSInterface> SDSSFileIO<Fs> {
_fs: PhantomData,
}
}
pub fn unfsynced_write(&mut self, data: &[u8]) -> SDSSResult<()> {
pub fn unfsynced_write(&mut self, data: &[u8]) -> RuntimeResult<()> {
self.f.fw_write_all(data)
}
pub fn fsync_all(&mut self) -> SDSSResult<()> {
pub fn fsync_all(&mut self) -> RuntimeResult<()> {
self.f.fw_fsync_all()?;
Ok(())
}
pub fn fsynced_write(&mut self, data: &[u8]) -> SDSSResult<()> {
pub fn fsynced_write(&mut self, data: &[u8]) -> RuntimeResult<()> {
self.f.fw_write_all(data)?;
self.f.fw_fsync_all()
}
pub fn read_to_buffer(&mut self, buffer: &mut [u8]) -> SDSSResult<()> {
pub fn read_to_buffer(&mut self, buffer: &mut [u8]) -> RuntimeResult<()> {
self.f.fr_read_exact(buffer)
}
pub fn file_length(&self) -> SDSSResult<u64> {
pub fn file_length(&self) -> RuntimeResult<u64> {
self.f.fext_file_length()
}
pub fn seek_from_start(&mut self, by: u64) -> SDSSResult<()> {
pub fn seek_from_start(&mut self, by: u64) -> RuntimeResult<()> {
self.f.fext_seek_ahead_from_start_by(by)
}
pub fn trim_file_to(&mut self, to: u64) -> SDSSResult<()> {
pub fn trim_file_to(&mut self, to: u64) -> RuntimeResult<()> {
self.f.fw_truncate_to(to)
}
pub fn retrieve_cursor(&mut self) -> SDSSResult<u64> {
pub fn retrieve_cursor(&mut self) -> RuntimeResult<u64> {
self.f.fext_cursor()
}
pub fn read_byte(&mut self) -> SDSSResult<u8> {
pub fn read_byte(&mut self) -> RuntimeResult<u8> {
let mut r = [0; 1];
self.read_to_buffer(&mut r).map(|_| r[0])
}
pub fn load_remaining_into_buffer(&mut self) -> SDSSResult<Vec<u8>> {
pub fn load_remaining_into_buffer(&mut self) -> RuntimeResult<Vec<u8>> {
let len = self.file_length()? - self.retrieve_cursor()?;
let mut buf = vec![0; len as usize];
self.read_to_buffer(&mut buf)?;

@ -33,15 +33,14 @@
*/
use {
super::{
rw::{RawFSInterface, SDSSFileIO},
SDSSResult,
},
super::rw::{RawFSInterface, SDSSFileIO},
crate::{
engine::storage::{
header::{HostArch, HostEndian, HostOS, HostPointerWidth},
v1::SDSSErrorKind,
versions::{self, DriverVersion, HeaderVersion, ServerVersion},
engine::{
error::{RuntimeResult, StorageError},
storage::{
header::{HostArch, HostEndian, HostOS, HostPointerWidth},
versions::{self, DriverVersion, HeaderVersion, ServerVersion},
},
},
util::os,
},
@ -202,21 +201,23 @@ pub trait Header: Sized {
/// Decode verify arguments
type DecodeVerifyArgs;
/// Encode the header
fn encode<Fs: RawFSInterface>(f: &mut SDSSFileIO<Fs>, args: Self::EncodeArgs)
-> SDSSResult<()>;
fn encode<Fs: RawFSInterface>(
f: &mut SDSSFileIO<Fs>,
args: Self::EncodeArgs,
) -> RuntimeResult<()>;
/// Decode the header
fn decode<Fs: RawFSInterface>(
f: &mut SDSSFileIO<Fs>,
args: Self::DecodeArgs,
) -> SDSSResult<Self>;
) -> RuntimeResult<Self>;
/// Verify the header
fn verify(&self, args: Self::DecodeVerifyArgs) -> SDSSResult<()>;
fn verify(&self, args: Self::DecodeVerifyArgs) -> RuntimeResult<()>;
/// Decode and verify the header
fn decode_verify<Fs: RawFSInterface>(
f: &mut SDSSFileIO<Fs>,
d_args: Self::DecodeArgs,
v_args: Self::DecodeVerifyArgs,
) -> SDSSResult<Self> {
) -> RuntimeResult<Self> {
let h = Self::decode(f, d_args)?;
h.verify(v_args)?;
Ok(h)
@ -295,7 +296,7 @@ impl SDSSStaticHeaderV1Compact {
/// - If padding block was not zeroed, handle
/// - No file metadata and is verified. Check!
///
fn _decode(block: [u8; 64]) -> SDSSResult<Self> {
fn _decode(block: [u8; 64]) -> RuntimeResult<Self> {
var!(let raw_magic, raw_header_version, raw_server_version, raw_driver_version, raw_host_os, raw_host_arch,
raw_host_ptr_width, raw_host_endian, raw_file_class, raw_file_specifier, raw_file_specifier_version,
raw_runtime_epoch_time, raw_paddding_block,
@ -375,8 +376,8 @@ impl SDSSStaticHeaderV1Compact {
} else {
let version_okay = okay_header_version & okay_server_version & okay_driver_version;
let md = ManuallyDrop::new([
SDSSErrorKind::HeaderDecodeCorruptedHeader,
SDSSErrorKind::HeaderDecodeVersionMismatch,
StorageError::HeaderDecodeCorruptedHeader,
StorageError::HeaderDecodeVersionMismatch,
]);
Err(unsafe {
// UNSAFE(@ohsayan): while not needed, md for drop safety + correct index
@ -495,23 +496,26 @@ impl Header for SDSSStaticHeaderV1Compact {
fn encode<Fs: RawFSInterface>(
f: &mut SDSSFileIO<Fs>,
(scope, spec, spec_v): Self::EncodeArgs,
) -> SDSSResult<()> {
) -> RuntimeResult<()> {
let b = Self::_encode_auto(scope, spec, spec_v);
f.fsynced_write(&b)
}
fn decode<Fs: RawFSInterface>(f: &mut SDSSFileIO<Fs>, _: Self::DecodeArgs) -> SDSSResult<Self> {
fn decode<Fs: RawFSInterface>(
f: &mut SDSSFileIO<Fs>,
_: Self::DecodeArgs,
) -> RuntimeResult<Self> {
let mut buf = [0u8; 64];
f.read_to_buffer(&mut buf)?;
Self::_decode(buf)
}
fn verify(&self, (scope, spec, spec_v): Self::DecodeVerifyArgs) -> SDSSResult<()> {
fn verify(&self, (scope, spec, spec_v): Self::DecodeVerifyArgs) -> RuntimeResult<()> {
if (self.file_class() == scope)
& (self.file_specifier() == spec)
& (self.file_specifier_version() == spec_v)
{
Ok(())
} else {
Err(SDSSErrorKind::HeaderDecodeDataMismatch.into())
Err(StorageError::HeaderDecodeDataMismatch.into())
}
}
}

@ -25,12 +25,13 @@
*/
use {
super::{rw::FileOpen, SDSSErrorKind},
super::rw::FileOpen,
crate::engine::{
config::ConfigAuth,
data::{cell::Datacell, DictEntryGeneric, DictGeneric},
error::{RuntimeResult, StorageError},
fractal::config::{SysAuth, SysAuthUser, SysConfig, SysHostData},
storage::v1::{inf, spec, RawFSInterface, SDSSFileIO, SDSSResult},
storage::v1::{inf, spec, RawFSInterface, SDSSFileIO},
},
parking_lot::RwLock,
std::collections::HashMap,
@ -73,7 +74,9 @@ impl SystemStoreInit {
///
/// - If it doesn't exist, create it
/// - If it exists, look for config changes and sync them
pub fn open_system_database<Fs: RawFSInterface>(auth: ConfigAuth) -> SDSSResult<SystemStoreInit> {
pub fn open_system_database<Fs: RawFSInterface>(
auth: ConfigAuth,
) -> RuntimeResult<SystemStoreInit> {
open_or_reinit_system_database::<Fs>(auth, SYSDB_PATH, SYSDB_COW_PATH)
}
@ -82,7 +85,7 @@ pub fn open_or_reinit_system_database<Fs: RawFSInterface>(
auth: ConfigAuth,
sysdb_path: &str,
sysdb_path_cow: &str,
) -> SDSSResult<SystemStoreInit> {
) -> RuntimeResult<SystemStoreInit> {
let sysdb_file = match SDSSFileIO::<Fs>::open_or_create_perm_rw::<spec::SysDBV1>(sysdb_path)? {
FileOpen::Created(new) => {
// init new syscfg
@ -130,7 +133,7 @@ pub fn open_or_reinit_system_database<Fs: RawFSInterface>(
pub fn sync_system_database_to<Fs: RawFSInterface>(
cfg: &SysConfig,
mut f: SDSSFileIO<Fs>,
) -> SDSSResult<()> {
) -> RuntimeResult<()> {
// prepare our flat file
let mut map: DictGeneric = into_dict!(
SYS_KEY_SYS => DictEntryGeneric::Map(into_dict!(
@ -172,15 +175,17 @@ fn rkey<T>(
d: &mut DictGeneric,
key: &str,
transform: impl Fn(DictEntryGeneric) -> Option<T>,
) -> SDSSResult<T> {
) -> RuntimeResult<T> {
match d.remove(key).map(transform) {
Some(Some(k)) => Ok(k),
_ => Err(SDSSErrorKind::SysDBCorrupted.into()),
_ => Err(StorageError::SysDBCorrupted.into()),
}
}
/// Decode the system database
pub fn decode_system_database<Fs: RawFSInterface>(mut f: SDSSFileIO<Fs>) -> SDSSResult<SysConfig> {
pub fn decode_system_database<Fs: RawFSInterface>(
mut f: SDSSFileIO<Fs>,
) -> RuntimeResult<SysConfig> {
let mut sysdb_data =
inf::dec::dec_dict_full::<inf::map::GenericDictSpec>(&f.load_remaining_into_buffer()?)?;
// get our auth and sys stores
@ -201,14 +206,14 @@ pub fn decode_system_database<Fs: RawFSInterface>(mut f: SDSSFileIO<Fs>) -> SDSS
let mut userdata = userdata
.into_data()
.and_then(Datacell::into_list)
.ok_or(SDSSErrorKind::SysDBCorrupted)?;
.ok_or(StorageError::SysDBCorrupted)?;
if userdata.len() != 1 {
return Err(SDSSErrorKind::SysDBCorrupted.into());
return Err(StorageError::SysDBCorrupted.into());
}
let user_password = userdata
.remove(0)
.into_bin()
.ok_or(SDSSErrorKind::SysDBCorrupted)?;
.ok_or(StorageError::SysDBCorrupted)?;
loaded_users.insert(username, SysAuthUser::new(user_password.into_boxed_slice()));
}
let sys_auth = SysAuth::new(root_key.into_boxed_slice(), loaded_users);
@ -220,7 +225,7 @@ pub fn decode_system_database<Fs: RawFSInterface>(mut f: SDSSFileIO<Fs>) -> SDSS
d.into_data()?.into_uint()
})?;
if !(sysdb_data.is_empty() & auth_store.is_empty() & sys_store.is_empty()) {
return Err(SDSSErrorKind::SysDBCorrupted.into());
return Err(StorageError::SysDBCorrupted.into());
}
Ok(SysConfig::new(
RwLock::new(sys_auth),

@ -26,14 +26,18 @@
use {
crate::{
engine::storage::v1::{
journal::{self, JournalAdapter, JournalWriter},
spec, SDSSError, SDSSErrorKind, SDSSResult,
engine::{
error::{RuntimeResult, StorageError},
storage::v1::{
journal::{self, JournalAdapter, JournalWriter},
spec,
},
},
util,
},
std::cell::RefCell,
};
pub struct Database {
data: RefCell<[u8; 10]>,
}
@ -52,7 +56,7 @@ impl Database {
fn txn_reset(
&self,
txn_writer: &mut JournalWriter<super::VirtualFS, DatabaseTxnAdapter>,
) -> SDSSResult<()> {
) -> RuntimeResult<()> {
self.reset();
txn_writer.append_event(TxEvent::Reset)
}
@ -64,7 +68,7 @@ impl Database {
pos: usize,
val: u8,
txn_writer: &mut JournalWriter<super::VirtualFS, DatabaseTxnAdapter>,
) -> SDSSResult<()> {
) -> RuntimeResult<()> {
self.set(pos, val);
txn_writer.append_event(TxEvent::Set(pos, val))
}
@ -75,11 +79,11 @@ pub enum TxEvent {
}
#[derive(Debug)]
pub enum TxError {
SDSS(SDSSError),
SDSS(StorageError),
}
direct_from! {
TxError => {
SDSSError as SDSS
StorageError as SDSS
}
}
#[derive(Debug)]
@ -114,22 +118,14 @@ impl JournalAdapter for DatabaseTxnAdapter {
}
fn decode_and_update_state(payload: &[u8], gs: &Self::GlobalState) -> Result<(), TxError> {
if payload.len() != 10 {
return Err(TxError::SDSS(
SDSSErrorKind::CorruptedFile("testtxn.log").into(),
));
}
assert!(payload.len() >= 10, "corrupt file");
let opcode = payload[0];
let index = u64::from_le_bytes(util::copy_slice_to_array(&payload[1..9]));
let new_value = payload[9];
match opcode {
0 if index == 0 && new_value == 0 => gs.reset(),
1 if index < 10 && index < isize::MAX as u64 => gs.set(index as usize, new_value),
_ => {
return Err(TxError::SDSS(
SDSSErrorKind::JournalLogEntryCorrupted.into(),
))
}
_ => return Err(TxError::SDSS(StorageError::JournalLogEntryCorrupted.into())),
}
Ok(())
}
@ -138,7 +134,7 @@ impl JournalAdapter for DatabaseTxnAdapter {
fn open_log(
log_name: &str,
db: &Database,
) -> SDSSResult<JournalWriter<super::VirtualFS, DatabaseTxnAdapter>> {
) -> RuntimeResult<JournalWriter<super::VirtualFS, DatabaseTxnAdapter>> {
journal::open_journal::<DatabaseTxnAdapter, super::VirtualFS, spec::TestFile>(log_name, db)
.map(|v| v.into_inner())
}
@ -147,7 +143,7 @@ fn open_log(
fn first_boot_second_readonly() {
// create log
let db1 = Database::new();
let x = || -> SDSSResult<()> {
let x = || -> RuntimeResult<()> {
let mut log = open_log("testtxn.log", &db1)?;
db1.txn_set(0, 20, &mut log)?;
db1.txn_set(9, 21, &mut log)?;
@ -168,7 +164,7 @@ fn first_boot_second_readonly() {
fn oneboot_mod_twoboot_mod_thirdboot_read() {
// first boot: set all to 1
let db1 = Database::new();
let x = || -> SDSSResult<()> {
let x = || -> RuntimeResult<()> {
let mut log = open_log("duatxn.db-tlog", &db1)?;
for i in 0..10 {
db1.txn_set(i, 1, &mut log)?;
@ -180,7 +176,7 @@ fn oneboot_mod_twoboot_mod_thirdboot_read() {
drop(db1);
// second boot
let db2 = Database::new();
let x = || -> SDSSResult<()> {
let x = || -> RuntimeResult<()> {
let mut log = open_log("duatxn.db-tlog", &db2)?;
assert_eq!(bkp_db1, db2.copy_data());
for i in 0..10 {

@ -27,15 +27,15 @@
#[cfg(test)]
use crate::engine::storage::v1::memfs::VirtualFS;
use {
super::{TransactionError, TransactionResult},
crate::{
engine::{
core::{space::Space, GlobalNS},
data::uuid::Uuid,
error::{RuntimeResult, TransactionError},
mem::BufferedScanner,
storage::v1::{
inf::{self, PersistObject},
JournalAdapter, JournalWriter, LocalFS, RawFSInterface, SDSSResult,
JournalAdapter, JournalWriter, LocalFS, RawFSInterface,
},
},
util::EndianQW,
@ -77,7 +77,7 @@ impl<Fs: RawFSInterface> GNSTransactionDriverAnyFS<Fs> {
}
/// Attempts to commit the given event into the journal, handling any possible recovery triggers and returning
/// errors (if any)
pub fn try_commit<GE: GNSEvent>(&mut self, gns_event: GE) -> TransactionResult<()> {
pub fn try_commit<GE: GNSEvent>(&mut self, gns_event: GE) -> RuntimeResult<()> {
let mut buf = vec![];
buf.extend(GE::OPC.to_le_bytes());
GE::encode_super_event(gns_event, &mut buf);
@ -99,20 +99,20 @@ impl JournalAdapter for GNSAdapter {
const RECOVERY_PLUGIN: bool = true;
type JournalEvent = GNSSuperEvent;
type GlobalState = GlobalNS;
type Error = TransactionError;
type Error = crate::engine::fractal::error::Error;
fn encode(GNSSuperEvent(b): Self::JournalEvent) -> Box<[u8]> {
b
}
fn decode_and_update_state(payload: &[u8], gs: &Self::GlobalState) -> TransactionResult<()> {
fn decode_and_update_state(payload: &[u8], gs: &Self::GlobalState) -> RuntimeResult<()> {
if payload.len() < 2 {
return Err(TransactionError::DecodedUnexpectedEof);
return Err(TransactionError::DecodedUnexpectedEof.into());
}
macro_rules! dispatch {
($($item:ty),* $(,)?) => {
[$(<$item as GNSEvent>::decode_and_update_global_state),*, |_, _| Err(TransactionError::DecodeUnknownTxnOp)]
[$(<$item as GNSEvent>::decode_and_update_global_state),*, |_, _| Err(TransactionError::DecodeUnknownTxnOp.into())]
};
}
static DISPATCH: [fn(&mut BufferedScanner, &GlobalNS) -> TransactionResult<()>; 9] = dispatch!(
static DISPATCH: [fn(&mut BufferedScanner, &GlobalNS) -> RuntimeResult<()>; 9] = dispatch!(
CreateSpaceTxn,
AlterSpaceTxn,
DropSpaceTxn,
@ -129,7 +129,7 @@ impl JournalAdapter for GNSAdapter {
};
match DISPATCH[(opc as usize).min(DISPATCH.len())](&mut scanner, gs) {
Ok(()) if scanner.eof() => return Ok(()),
Ok(_) => Err(TransactionError::DecodeCorruptedPayloadMoreBytes),
Ok(_) => Err(TransactionError::DecodeCorruptedPayloadMoreBytes.into()),
Err(e) => Err(e),
}
}
@ -165,15 +165,15 @@ where
fn decode_and_update_global_state(
scanner: &mut BufferedScanner,
gns: &GlobalNS,
) -> TransactionResult<()> {
) -> RuntimeResult<()> {
Self::update_global_state(Self::decode(scanner)?, gns)
}
/// Attempts to decode the event using the given scanner
fn decode(scanner: &mut BufferedScanner) -> TransactionResult<Self::RestoreType> {
fn decode(scanner: &mut BufferedScanner) -> RuntimeResult<Self::RestoreType> {
inf::dec::dec_full_from_scanner::<Self>(scanner).map_err(|e| e.into())
}
/// Update the global state from the restored event
fn update_global_state(restore: Self::RestoreType, gns: &GlobalNS) -> TransactionResult<()>;
fn update_global_state(restore: Self::RestoreType, gns: &GlobalNS) -> RuntimeResult<()>;
}
#[derive(Debug, Clone, Copy)]
@ -220,7 +220,7 @@ impl<'a> PersistObject for SpaceID<'a> {
buf.extend(data.uuid.to_le_bytes());
buf.extend(data.name.len().u64_bytes_le());
}
unsafe fn meta_dec(scanner: &mut BufferedScanner) -> SDSSResult<Self::Metadata> {
unsafe fn meta_dec(scanner: &mut BufferedScanner) -> RuntimeResult<Self::Metadata> {
Ok(SpaceIDMD {
uuid: Uuid::from_bytes(scanner.next_chunk()),
space_name_l: scanner.next_u64_le(),
@ -229,7 +229,10 @@ impl<'a> PersistObject for SpaceID<'a> {
fn obj_enc(buf: &mut Vec<u8>, data: Self::InputType) {
buf.extend(data.name.as_bytes());
}
unsafe fn obj_dec(s: &mut BufferedScanner, md: Self::Metadata) -> SDSSResult<Self::OutputType> {
unsafe fn obj_dec(
s: &mut BufferedScanner,
md: Self::Metadata,
) -> RuntimeResult<Self::OutputType> {
let str = inf::dec::utils::decode_string(s, md.space_name_l as usize)?;
Ok(SpaceIDRes {
uuid: md.uuid,

@ -25,7 +25,7 @@
*/
use {
super::{GNSEvent, TransactionResult},
super::GNSEvent,
crate::{
engine::{
core::{
@ -34,14 +34,12 @@ use {
GlobalNS,
},
data::uuid::Uuid,
error::TransactionError,
error::{RuntimeResult, StorageError},
idx::{IndexST, IndexSTSeqCns, STIndex, STIndexSeq},
mem::BufferedScanner,
ql::lex::Ident,
storage::v1::{
inf::{self, map, obj, PersistObject},
SDSSErrorKind, SDSSResult,
},
txn::TransactionError,
storage::v1::inf::{self, map, obj, PersistObject},
},
util::EndianQW,
},
@ -130,7 +128,7 @@ impl<'a> PersistObject for ModelID<'a> {
buf.extend(data.model_version.to_le_bytes());
buf.extend(data.model_uuid.to_le_bytes());
}
unsafe fn meta_dec(scanner: &mut BufferedScanner) -> SDSSResult<Self::Metadata> {
unsafe fn meta_dec(scanner: &mut BufferedScanner) -> RuntimeResult<Self::Metadata> {
Ok(ModelIDMD {
space_id: <super::SpaceID as PersistObject>::meta_dec(scanner)?,
model_name_l: scanner.next_u64_le(),
@ -142,7 +140,10 @@ impl<'a> PersistObject for ModelID<'a> {
<super::SpaceID as PersistObject>::obj_enc(buf, data.space_id);
buf.extend(data.model_name.as_bytes());
}
unsafe fn obj_dec(s: &mut BufferedScanner, md: Self::Metadata) -> SDSSResult<Self::OutputType> {
unsafe fn obj_dec(
s: &mut BufferedScanner,
md: Self::Metadata,
) -> RuntimeResult<Self::OutputType> {
Ok(ModelIDRes {
space_id: <super::SpaceID as PersistObject>::obj_dec(s, md.space_id)?,
model_name: inf::dec::utils::decode_string(s, md.model_name_l as usize)?
@ -156,14 +157,14 @@ impl<'a> PersistObject for ModelID<'a> {
fn with_space<T>(
gns: &GlobalNS,
space_id: &super::SpaceIDRes,
mut f: impl FnMut(&Space) -> TransactionResult<T>,
) -> TransactionResult<T> {
mut f: impl FnMut(&Space) -> RuntimeResult<T>,
) -> RuntimeResult<T> {
let spaces = gns.spaces().read();
let Some(space) = spaces.st_get(&space_id.name) else {
return Err(TransactionError::OnRestoreDataMissing);
return Err(TransactionError::OnRestoreDataMissing.into());
};
if space.get_uuid() != space_id.uuid {
return Err(TransactionError::OnRestoreDataConflictMismatch);
return Err(TransactionError::OnRestoreDataConflictMismatch.into());
}
f(space)
}
@ -172,16 +173,16 @@ fn with_model<T>(
gns: &GlobalNS,
space_id: &super::SpaceIDRes,
model_id: &ModelIDRes,
mut f: impl FnMut(&Model) -> TransactionResult<T>,
) -> TransactionResult<T> {
mut f: impl FnMut(&Model) -> RuntimeResult<T>,
) -> RuntimeResult<T> {
with_space(gns, space_id, |space| {
let models = space.models().read();
let Some(model) = models.st_get(&model_id.model_name) else {
return Err(TransactionError::OnRestoreDataMissing);
return Err(TransactionError::OnRestoreDataMissing.into());
};
if model.get_uuid() != model_id.model_uuid {
// this should have been handled by an earlier transaction
return Err(TransactionError::OnRestoreDataConflictMismatch);
return Err(TransactionError::OnRestoreDataConflictMismatch.into());
}
f(model)
})
@ -251,7 +252,7 @@ impl<'a> PersistObject for CreateModelTxn<'a> {
obj::ModelLayoutRef::from((data.model, data.model_read)),
)
}
unsafe fn meta_dec(scanner: &mut BufferedScanner) -> SDSSResult<Self::Metadata> {
unsafe fn meta_dec(scanner: &mut BufferedScanner) -> RuntimeResult<Self::Metadata> {
let space_id = <super::SpaceID as PersistObject>::meta_dec(scanner)?;
let model_name_l = scanner.next_u64_le();
let model_meta = <obj::ModelLayoutRef as PersistObject>::meta_dec(scanner)?;
@ -272,7 +273,10 @@ impl<'a> PersistObject for CreateModelTxn<'a> {
obj::ModelLayoutRef::from((data.model, data.model_read)),
)
}
unsafe fn obj_dec(s: &mut BufferedScanner, md: Self::Metadata) -> SDSSResult<Self::OutputType> {
unsafe fn obj_dec(
s: &mut BufferedScanner,
md: Self::Metadata,
) -> RuntimeResult<Self::OutputType> {
let space_id = <super::SpaceID as PersistObject>::obj_dec(s, md.space_id_meta)?;
let model_name =
inf::dec::utils::decode_string(s, md.model_name_l as usize)?.into_boxed_str();
@ -296,7 +300,7 @@ impl<'a> GNSEvent for CreateModelTxn<'a> {
model,
}: Self::RestoreType,
gns: &GlobalNS,
) -> crate::engine::txn::TransactionResult<()> {
) -> RuntimeResult<()> {
let rgns = gns.spaces().read();
/*
NOTE(@ohsayan):
@ -311,11 +315,11 @@ impl<'a> GNSEvent for CreateModelTxn<'a> {
if space._create_model(&model_name, model).is_ok() {
Ok(())
} else {
Err(TransactionError::OnRestoreDataConflictAlreadyExists)
Err(TransactionError::OnRestoreDataConflictAlreadyExists.into())
}
}
Some(_) => return Err(TransactionError::OnRestoreDataConflictMismatch),
None => return Err(TransactionError::OnRestoreDataMissing),
Some(_) => return Err(TransactionError::OnRestoreDataConflictMismatch.into()),
None => return Err(TransactionError::OnRestoreDataMissing.into()),
}
}
}
@ -366,7 +370,7 @@ impl<'a> PersistObject for AlterModelAddTxn<'a> {
<ModelID as PersistObject>::meta_enc(buf, data.model_id);
buf.extend(data.new_fields.st_len().u64_bytes_le());
}
unsafe fn meta_dec(scanner: &mut BufferedScanner) -> SDSSResult<Self::Metadata> {
unsafe fn meta_dec(scanner: &mut BufferedScanner) -> RuntimeResult<Self::Metadata> {
let model_id_meta = <ModelID as PersistObject>::meta_dec(scanner)?;
let new_field_c = scanner.next_u64_le();
Ok(AlterModelAddTxnMD {
@ -378,7 +382,10 @@ impl<'a> PersistObject for AlterModelAddTxn<'a> {
<ModelID as PersistObject>::obj_enc(buf, data.model_id);
<map::PersistMapImpl<map::FieldMapSpec> as PersistObject>::obj_enc(buf, data.new_fields);
}
unsafe fn obj_dec(s: &mut BufferedScanner, md: Self::Metadata) -> SDSSResult<Self::OutputType> {
unsafe fn obj_dec(
s: &mut BufferedScanner,
md: Self::Metadata,
) -> RuntimeResult<Self::OutputType> {
let model_id = <ModelID as PersistObject>::obj_dec(s, md.model_id_meta)?;
let new_fields = <map::PersistMapImpl<map::FieldMapSpec> as PersistObject>::obj_dec(
s,
@ -401,7 +408,7 @@ impl<'a> GNSEvent for AlterModelAddTxn<'a> {
new_fields,
}: Self::RestoreType,
gns: &GlobalNS,
) -> crate::engine::txn::TransactionResult<()> {
) -> RuntimeResult<()> {
with_model(gns, &model_id.space_id, &model_id, |model| {
let mut wmodel = model.intent_write_model();
for (i, (field_name, field)) in new_fields.stseq_ord_kv().enumerate() {
@ -413,7 +420,7 @@ impl<'a> GNSEvent for AlterModelAddTxn<'a> {
new_fields.stseq_ord_key().take(i).for_each(|field_id| {
let _ = wmodel.fields_mut().st_delete(field_id);
});
return Err(TransactionError::OnRestoreDataConflictMismatch);
return Err(TransactionError::OnRestoreDataConflictMismatch.into());
}
}
// TODO(@ohsayan): avoid double iteration
@ -470,7 +477,7 @@ impl<'a> PersistObject for AlterModelRemoveTxn<'a> {
<ModelID as PersistObject>::meta_enc(buf, data.model_id);
buf.extend(data.removed_fields.len().u64_bytes_le());
}
unsafe fn meta_dec(scanner: &mut BufferedScanner) -> SDSSResult<Self::Metadata> {
unsafe fn meta_dec(scanner: &mut BufferedScanner) -> RuntimeResult<Self::Metadata> {
let model_id_meta = <ModelID as PersistObject>::meta_dec(scanner)?;
Ok(AlterModelRemoveTxnMD {
model_id_meta,
@ -484,7 +491,10 @@ impl<'a> PersistObject for AlterModelRemoveTxn<'a> {
buf.extend(field.as_bytes());
}
}
unsafe fn obj_dec(s: &mut BufferedScanner, md: Self::Metadata) -> SDSSResult<Self::OutputType> {
unsafe fn obj_dec(
s: &mut BufferedScanner,
md: Self::Metadata,
) -> RuntimeResult<Self::OutputType> {
let model_id = <ModelID as PersistObject>::obj_dec(s, md.model_id_meta)?;
let mut removed_fields = Vec::with_capacity(md.remove_field_c as usize);
while !s.eof()
@ -498,7 +508,7 @@ impl<'a> PersistObject for AlterModelRemoveTxn<'a> {
removed_fields.push(inf::dec::utils::decode_string(s, len)?.into_boxed_str());
}
if removed_fields.len() as u64 != md.remove_field_c {
return Err(SDSSErrorKind::InternalDecodeStructureCorruptedPayload.into());
return Err(StorageError::InternalDecodeStructureCorruptedPayload.into());
}
Ok(AlterModelRemoveTxnRestorePL {
model_id,
@ -517,7 +527,7 @@ impl<'a> GNSEvent for AlterModelRemoveTxn<'a> {
removed_fields,
}: Self::RestoreType,
gns: &GlobalNS,
) -> crate::engine::txn::TransactionResult<()> {
) -> RuntimeResult<()> {
with_model(gns, &model_id.space_id, &model_id, |model| {
let mut iwm = model.intent_write_model();
let mut removed_fields_rb = vec![];
@ -531,7 +541,7 @@ impl<'a> GNSEvent for AlterModelRemoveTxn<'a> {
removed_fields_rb.into_iter().for_each(|(field_id, field)| {
let _ = iwm.fields_mut().st_insert(field_id.into(), field);
});
return Err(TransactionError::OnRestoreDataConflictMismatch);
return Err(TransactionError::OnRestoreDataConflictMismatch.into());
}
}
}
@ -593,7 +603,7 @@ impl<'a> PersistObject for AlterModelUpdateTxn<'a> {
<ModelID as PersistObject>::meta_enc(buf, data.model_id);
buf.extend(data.updated_fields.st_len().u64_bytes_le());
}
unsafe fn meta_dec(scanner: &mut BufferedScanner) -> SDSSResult<Self::Metadata> {
unsafe fn meta_dec(scanner: &mut BufferedScanner) -> RuntimeResult<Self::Metadata> {
let model_id_md = <ModelID as PersistObject>::meta_dec(scanner)?;
Ok(AlterModelUpdateTxnMD {
model_id_md,
@ -607,7 +617,10 @@ impl<'a> PersistObject for AlterModelUpdateTxn<'a> {
data.updated_fields,
);
}
unsafe fn obj_dec(s: &mut BufferedScanner, md: Self::Metadata) -> SDSSResult<Self::OutputType> {
unsafe fn obj_dec(
s: &mut BufferedScanner,
md: Self::Metadata,
) -> RuntimeResult<Self::OutputType> {
let model_id = <ModelID as PersistObject>::obj_dec(s, md.model_id_md)?;
let updated_fields = <map::PersistMapImpl<map::FieldMapSpecST> as PersistObject>::obj_dec(
s,
@ -630,7 +643,7 @@ impl<'a> GNSEvent for AlterModelUpdateTxn<'a> {
updated_fields,
}: Self::RestoreType,
gns: &GlobalNS,
) -> TransactionResult<()> {
) -> RuntimeResult<()> {
with_model(gns, &model_id.space_id, &model_id, |model| {
let mut iwm = model.intent_write_model();
let mut fields_rb = vec![];
@ -642,7 +655,7 @@ impl<'a> GNSEvent for AlterModelUpdateTxn<'a> {
fields_rb.into_iter().for_each(|(field_id, field)| {
let _ = iwm.fields_mut().st_update(field_id, field);
});
return Err(TransactionError::OnRestoreDataConflictMismatch);
return Err(TransactionError::OnRestoreDataConflictMismatch.into());
}
}
}
@ -682,14 +695,17 @@ impl<'a> PersistObject for DropModelTxn<'a> {
fn meta_enc(buf: &mut Vec<u8>, data: Self::InputType) {
<ModelID as PersistObject>::meta_enc(buf, data.model_id);
}
unsafe fn meta_dec(scanner: &mut BufferedScanner) -> SDSSResult<Self::Metadata> {
unsafe fn meta_dec(scanner: &mut BufferedScanner) -> RuntimeResult<Self::Metadata> {
let model_id_md = <ModelID as PersistObject>::meta_dec(scanner)?;
Ok(DropModelTxnMD { model_id_md })
}
fn obj_enc(buf: &mut Vec<u8>, data: Self::InputType) {
<ModelID as PersistObject>::obj_enc(buf, data.model_id);
}
unsafe fn obj_dec(s: &mut BufferedScanner, md: Self::Metadata) -> SDSSResult<Self::OutputType> {
unsafe fn obj_dec(
s: &mut BufferedScanner,
md: Self::Metadata,
) -> RuntimeResult<Self::OutputType> {
<ModelID as PersistObject>::obj_dec(s, md.model_id_md)
}
}
@ -706,13 +722,13 @@ impl<'a> GNSEvent for DropModelTxn<'a> {
model_version: _,
}: Self::RestoreType,
gns: &GlobalNS,
) -> TransactionResult<()> {
) -> RuntimeResult<()> {
with_space(gns, &space_id, |space| {
let mut wgns = space.models().write();
match wgns.st_delete_if(&model_name, |mdl| mdl.get_uuid() == model_uuid) {
Some(true) => Ok(()),
Some(false) => return Err(TransactionError::OnRestoreDataConflictMismatch),
None => Err(TransactionError::OnRestoreDataMissing),
Some(false) => return Err(TransactionError::OnRestoreDataConflictMismatch.into()),
None => Err(TransactionError::OnRestoreDataMissing.into()),
}
})
}

@ -30,13 +30,10 @@ use {
engine::{
core::{space::Space, GlobalNS},
data::DictGeneric,
error::{RuntimeResult, TransactionError},
idx::STIndex,
mem::BufferedScanner,
storage::v1::{
inf::{self, map, obj, PersistObject},
SDSSResult,
},
txn::{TransactionError, TransactionResult},
storage::v1::inf::{self, map, obj, PersistObject},
},
util::EndianQW,
},
@ -92,7 +89,7 @@ impl<'a> PersistObject for CreateSpaceTxn<'a> {
obj::SpaceLayoutRef::from((data.space, data.space_meta)),
);
}
unsafe fn meta_dec(scanner: &mut BufferedScanner) -> SDSSResult<Self::Metadata> {
unsafe fn meta_dec(scanner: &mut BufferedScanner) -> RuntimeResult<Self::Metadata> {
let space_name_l = scanner.next_u64_le();
let space_meta = <obj::SpaceLayoutRef as PersistObject>::meta_dec(scanner)?;
Ok(CreateSpaceTxnMD {
@ -104,7 +101,10 @@ impl<'a> PersistObject for CreateSpaceTxn<'a> {
buf.extend(data.space_name.as_bytes());
<obj::SpaceLayoutRef as PersistObject>::obj_enc(buf, (data.space, data.space_meta).into());
}
unsafe fn obj_dec(s: &mut BufferedScanner, md: Self::Metadata) -> SDSSResult<Self::OutputType> {
unsafe fn obj_dec(
s: &mut BufferedScanner,
md: Self::Metadata,
) -> RuntimeResult<Self::OutputType> {
let space_name =
inf::dec::utils::decode_string(s, md.space_name_l as usize)?.into_boxed_str();
let space = <obj::SpaceLayoutRef as PersistObject>::obj_dec(s, md.space_meta)?;
@ -119,12 +119,12 @@ impl<'a> GNSEvent for CreateSpaceTxn<'a> {
fn update_global_state(
CreateSpaceTxnRestorePL { space_name, space }: CreateSpaceTxnRestorePL,
gns: &crate::engine::core::GlobalNS,
) -> crate::engine::txn::TransactionResult<()> {
) -> RuntimeResult<()> {
let mut wgns = gns.spaces().write();
if wgns.st_insert(space_name, space) {
Ok(())
} else {
Err(TransactionError::OnRestoreDataConflictAlreadyExists)
Err(TransactionError::OnRestoreDataConflictAlreadyExists.into())
}
}
}
@ -174,7 +174,7 @@ impl<'a> PersistObject for AlterSpaceTxn<'a> {
<super::SpaceID as PersistObject>::meta_enc(buf, data.space_id);
buf.extend(data.updated_props.len().u64_bytes_le());
}
unsafe fn meta_dec(scanner: &mut BufferedScanner) -> SDSSResult<Self::Metadata> {
unsafe fn meta_dec(scanner: &mut BufferedScanner) -> RuntimeResult<Self::Metadata> {
Ok(AlterSpaceTxnMD {
space_id_meta: <super::SpaceID as PersistObject>::meta_dec(scanner)?,
dict_len: scanner.next_u64_le(),
@ -187,7 +187,10 @@ impl<'a> PersistObject for AlterSpaceTxn<'a> {
data.updated_props,
);
}
unsafe fn obj_dec(s: &mut BufferedScanner, md: Self::Metadata) -> SDSSResult<Self::OutputType> {
unsafe fn obj_dec(
s: &mut BufferedScanner,
md: Self::Metadata,
) -> RuntimeResult<Self::OutputType> {
let space_id = <super::SpaceID as PersistObject>::obj_dec(s, md.space_id_meta)?;
let space_meta = <map::PersistMapImpl<map::GenericDictSpec> as PersistObject>::obj_dec(
s,
@ -211,16 +214,16 @@ impl<'a> GNSEvent for AlterSpaceTxn<'a> {
space_meta,
}: Self::RestoreType,
gns: &crate::engine::core::GlobalNS,
) -> TransactionResult<()> {
) -> RuntimeResult<()> {
let gns = gns.spaces().read();
match gns.st_get(&space_id.name) {
Some(space) => {
let mut wmeta = space.metadata().dict().write();
if !crate::engine::data::dict::rmerge_metadata(&mut wmeta, space_meta) {
return Err(TransactionError::OnRestoreDataConflictMismatch);
return Err(TransactionError::OnRestoreDataConflictMismatch.into());
}
}
None => return Err(TransactionError::OnRestoreDataMissing),
None => return Err(TransactionError::OnRestoreDataMissing.into()),
}
Ok(())
}
@ -253,13 +256,16 @@ impl<'a> PersistObject for DropSpaceTxn<'a> {
fn meta_enc(buf: &mut Vec<u8>, data: Self::InputType) {
<super::SpaceID as PersistObject>::meta_enc(buf, data.space_id);
}
unsafe fn meta_dec(scanner: &mut BufferedScanner) -> SDSSResult<Self::Metadata> {
unsafe fn meta_dec(scanner: &mut BufferedScanner) -> RuntimeResult<Self::Metadata> {
<super::SpaceID as PersistObject>::meta_dec(scanner)
}
fn obj_enc(buf: &mut Vec<u8>, data: Self::InputType) {
<super::SpaceID as PersistObject>::obj_enc(buf, data.space_id)
}
unsafe fn obj_dec(s: &mut BufferedScanner, md: Self::Metadata) -> SDSSResult<Self::OutputType> {
unsafe fn obj_dec(
s: &mut BufferedScanner,
md: Self::Metadata,
) -> RuntimeResult<Self::OutputType> {
<super::SpaceID as PersistObject>::obj_dec(s, md)
}
}
@ -271,7 +277,7 @@ impl<'a> GNSEvent for DropSpaceTxn<'a> {
fn update_global_state(
super::SpaceIDRes { uuid, name }: Self::RestoreType,
gns: &GlobalNS,
) -> TransactionResult<()> {
) -> RuntimeResult<()> {
let mut wgns = gns.spaces().write();
match wgns.entry(name) {
std::collections::hash_map::Entry::Occupied(oe) => {
@ -279,11 +285,11 @@ impl<'a> GNSEvent for DropSpaceTxn<'a> {
oe.remove_entry();
Ok(())
} else {
return Err(TransactionError::OnRestoreDataConflictMismatch);
return Err(TransactionError::OnRestoreDataConflictMismatch.into());
}
}
std::collections::hash_map::Entry::Vacant(_) => {
return Err(TransactionError::OnRestoreDataMissing)
return Err(TransactionError::OnRestoreDataMissing.into())
}
}
}

@ -30,7 +30,7 @@ use crate::engine::{
space::{Space, SpaceMeta},
},
data::{cell::Datacell, tag::TagSelector, uuid::Uuid, DictEntryGeneric},
error::Error,
error::QueryError,
fractal::{test_utils::TestGlobal, GlobalInstanceLike},
idx::STIndex,
ql::{
@ -318,7 +318,7 @@ fn drop_model() {
.namespace()
.with_model(("myspace", "mymodel"), |_| { Ok(()) })
.unwrap_err(),
Error::QPObjectNotFound
QueryError::QPObjectNotFound
);
})
})

@ -25,32 +25,3 @@
*/
pub mod gns;
use super::storage::v1::SDSSError;
pub type TransactionResult<T> = Result<T, TransactionError>;
#[derive(Debug)]
#[cfg_attr(test, derive(PartialEq))]
pub enum TransactionError {
/// corrupted txn payload. has more bytes than expected
DecodeCorruptedPayloadMoreBytes,
/// transaction payload is corrupted. has lesser bytes than expected
DecodedUnexpectedEof,
/// unknown transaction operation. usually indicates a corrupted payload
DecodeUnknownTxnOp,
/// While restoring a certain item, a non-resolvable conflict was encountered in the global state, because the item was
/// already present (when it was expected to not be present)
OnRestoreDataConflictAlreadyExists,
/// On restore, a certain item that was expected to be present was missing in the global state
OnRestoreDataMissing,
/// On restore, a certain item that was expected to match a certain value, has a different value
OnRestoreDataConflictMismatch,
SDSSError(SDSSError),
OutOfMemory,
}
direct_from! {
TransactionError => {
SDSSError as SDSSError
}
}

Loading…
Cancel
Save