From 54eb688b6bfb2c9c2a8c9a44cfa9438db616b2e7 Mon Sep 17 00:00:00 2001 From: "ms.evilhat" <ms.evilhat@gmail.com> Date: Mon, 9 Jan 2023 14:06:52 +0300 Subject: [PATCH] refactor(errors): refactor errors module to have a clear classification instead of custom error --- sbroad-benches/src/engine.rs | 47 +- .../src/api/calculate_bucket_id.rs | 19 +- sbroad-cartridge/src/api/exec_query.rs | 58 ++- .../src/api/exec_query/protocol.rs | 67 +-- sbroad-cartridge/src/cartridge.rs | 13 +- sbroad-cartridge/src/cartridge/config.rs | 69 +-- .../src/cartridge/config/tests.rs | 4 +- sbroad-cartridge/src/cartridge/router.rs | 189 ++++---- sbroad-cartridge/src/cartridge/storage.rs | 90 ++-- .../test_app/test/data/config.yml | 1 + .../test_app/test/integration/api_test.lua | 90 +++- .../test/integration/validate_schema_test.lua | 2 +- sbroad-core/src/backend/sql/ir.rs | 77 +-- sbroad-core/src/backend/sql/tree.rs | 183 +++++--- sbroad-core/src/errors.rs | 344 +++++++++----- sbroad-core/src/executor.rs | 30 +- sbroad-core/src/executor/bucket.rs | 146 +++--- sbroad-core/src/executor/engine.rs | 88 ++-- sbroad-core/src/executor/engine/mock.rs | 53 +-- sbroad-core/src/executor/ir.rs | 189 ++++---- sbroad-core/src/executor/lru.rs | 65 ++- sbroad-core/src/executor/lru/tests.rs | 10 +- sbroad-core/src/executor/result.rs | 14 +- sbroad-core/src/executor/shard.rs | 14 +- sbroad-core/src/executor/tests.rs | 9 +- sbroad-core/src/executor/tests/frontend.rs | 16 +- sbroad-core/src/executor/vtable.rs | 24 +- sbroad-core/src/frontend.rs | 6 +- sbroad-core/src/frontend/sql.rs | 369 ++++++++------- sbroad-core/src/frontend/sql/ast.rs | 439 +++++++++++------- sbroad-core/src/frontend/sql/ast/tests.rs | 12 +- sbroad-core/src/frontend/sql/ir.rs | 118 ++--- sbroad-core/src/ir.rs | 209 +++++---- sbroad-core/src/ir/api/constant.rs | 31 +- sbroad-core/src/ir/api/parameter.rs | 15 +- sbroad-core/src/ir/distribution.rs | 96 ++-- sbroad-core/src/ir/explain.rs | 104 +++-- sbroad-core/src/ir/expression.rs | 262 ++++++----- sbroad-core/src/ir/expression/cast.rs | 14 +- sbroad-core/src/ir/expression/concat.rs | 8 +- sbroad-core/src/ir/expression/tests.rs | 6 +- sbroad-core/src/ir/function.rs | 12 +- sbroad-core/src/ir/operator.rs | 265 +++++------ sbroad-core/src/ir/operator/tests.rs | 138 +++++- sbroad-core/src/ir/relation.rs | 61 +-- sbroad-core/src/ir/relation/tests.rs | 62 ++- sbroad-core/src/ir/tests.rs | 6 +- sbroad-core/src/ir/transformation.rs | 58 ++- sbroad-core/src/ir/transformation/bool_in.rs | 18 +- sbroad-core/src/ir/transformation/dnf.rs | 30 +- .../ir/transformation/equality_propagation.rs | 40 +- .../src/ir/transformation/merge_tuples.rs | 70 +-- .../src/ir/transformation/redistribution.rs | 200 ++++---- .../ir/transformation/redistribution/tests.rs | 7 +- .../src/ir/transformation/split_columns.rs | 25 +- .../ir/transformation/split_columns/tests.rs | 5 +- sbroad-core/src/ir/value/double.rs | 10 +- sbroad-core/src/otm/statistics.rs | 4 +- sbroad-core/src/otm/statistics/eviction.rs | 8 +- 59 files changed, 2685 insertions(+), 1934 deletions(-) diff --git a/sbroad-benches/src/engine.rs b/sbroad-benches/src/engine.rs index e87a52b35e..f768da0a7e 100644 --- a/sbroad-benches/src/engine.rs +++ b/sbroad-benches/src/engine.rs @@ -3,7 +3,7 @@ use std::cell::RefCell; use std::collections::HashMap; use sbroad::backend::sql::tree::{OrderedSyntaxNodes, SyntaxPlan}; -use sbroad::errors::QueryPlannerError; +use sbroad::errors::{Entity, SbroadError}; use sbroad::executor::bucket::Buckets; use sbroad::executor::engine::{ normalize_name_from_sql, sharding_keys_from_map, sharding_keys_from_tuple, Configuration, @@ -38,25 +38,22 @@ impl CoordinatorMetadata for RouterConfigurationMock { /// /// # Errors /// - Failed to get table by name from the metadata. - fn get_table_segment(&self, table_name: &str) -> Result<Table, QueryPlannerError> { + fn get_table_segment(&self, table_name: &str) -> Result<Table, SbroadError> { let name = normalize_name_from_sql(table_name); match self.tables.get(&name) { Some(v) => Ok(v.clone()), - None => Err(QueryPlannerError::CustomError(format!( - "Space {} not found", - table_name - ))), + None => Err(SbroadError::NotFound( + Entity::Space, + String::from(table_name), + )), } } - fn get_function(&self, fn_name: &str) -> Result<&Function, QueryPlannerError> { + fn get_function(&self, fn_name: &str) -> Result<&Function, SbroadError> { let name = normalize_name_from_sql(fn_name); match self.functions.get(&name) { Some(v) => Ok(v), - None => Err(QueryPlannerError::CustomError(format!( - "Function {} not found", - name - ))), + None => Err(SbroadError::NotFound(Entity::SQLFunction, name)), } } @@ -68,20 +65,17 @@ impl CoordinatorMetadata for RouterConfigurationMock { self.sharding_column.as_str() } - fn get_sharding_key_by_space(&self, space: &str) -> Result<Vec<String>, QueryPlannerError> { + fn get_sharding_key_by_space(&self, space: &str) -> Result<Vec<String>, SbroadError> { let table = self.get_table_segment(space)?; table.get_sharding_column_names() } - fn get_sharding_positions_by_space( - &self, - space: &str, - ) -> Result<Vec<usize>, QueryPlannerError> { + fn get_sharding_positions_by_space(&self, space: &str) -> Result<Vec<usize>, SbroadError> { let table = self.get_table_segment(space)?; Ok(table.get_sharding_positions().to_vec()) } - fn get_fields_amount_by_space(&self, space: &str) -> Result<usize, QueryPlannerError> { + fn get_fields_amount_by_space(&self, space: &str) -> Result<usize, SbroadError> { let table = self.get_table_segment(space)?; Ok(table.columns.len()) } @@ -387,7 +381,7 @@ impl Configuration for RouterRuntimeMock { self.metadata.tables.is_empty() } - fn get_config(&self) -> Result<Option<Self::Configuration>, QueryPlannerError> { + fn get_config(&self) -> Result<Option<Self::Configuration>, SbroadError> { let config = RouterConfigurationMock::new(); Ok(Some(config)) } @@ -401,7 +395,7 @@ impl Coordinator for RouterRuntimeMock { type ParseTree = AbstractSyntaxTree; type Cache = LRUCache<String, Plan>; - fn clear_ir_cache(&self) -> Result<(), QueryPlannerError> { + fn clear_ir_cache(&self) -> Result<(), SbroadError> { *self.ir_cache.borrow_mut() = Self::Cache::new(DEFAULT_CAPACITY, None)?; Ok(()) } @@ -415,12 +409,13 @@ impl Coordinator for RouterRuntimeMock { _plan: &mut ExecutionPlan, motion_node_id: usize, _buckets: &Buckets, - ) -> Result<VirtualTable, QueryPlannerError> { + ) -> Result<VirtualTable, SbroadError> { if let Some(virtual_table) = self.virtual_tables.get(&motion_node_id) { Ok(virtual_table.clone()) } else { - Err(QueryPlannerError::CustomError( - "No virtual table found for motion node".to_string(), + Err(SbroadError::NotFound( + Entity::VirtualTable, + format!("for motion node {}", motion_node_id), )) } } @@ -430,7 +425,7 @@ impl Coordinator for RouterRuntimeMock { plan: &mut ExecutionPlan, top_id: usize, buckets: &Buckets, - ) -> Result<Box<dyn Any>, QueryPlannerError> { + ) -> Result<Box<dyn Any>, SbroadError> { let result = ProducerResult::new(); let sp = SyntaxPlan::new(plan, top_id, Snapshot::Oldest)?; let ordered = OrderedSyntaxNodes::try_from(sp)?; @@ -440,7 +435,7 @@ impl Coordinator for RouterRuntimeMock { Ok(Box::new(result)) } - fn explain_format(&self, explain: String) -> Result<Box<dyn Any>, QueryPlannerError> { + fn explain_format(&self, explain: String) -> Result<Box<dyn Any>, SbroadError> { Ok(Box::new(explain)) } @@ -448,7 +443,7 @@ impl Coordinator for RouterRuntimeMock { &'engine self, space: String, args: &'rec HashMap<String, Value>, - ) -> Result<Vec<&'rec Value>, QueryPlannerError> { + ) -> Result<Vec<&'rec Value>, SbroadError> { sharding_keys_from_map(self.cached_config(), &space, args) } @@ -456,7 +451,7 @@ impl Coordinator for RouterRuntimeMock { &'engine self, space: String, rec: &'rec [Value], - ) -> Result<Vec<&'rec Value>, QueryPlannerError> { + ) -> Result<Vec<&'rec Value>, SbroadError> { sharding_keys_from_tuple(self.cached_config(), &space, rec) } diff --git a/sbroad-cartridge/src/api/calculate_bucket_id.rs b/sbroad-cartridge/src/api/calculate_bucket_id.rs index 3e7e11f02a..7103c62403 100644 --- a/sbroad-cartridge/src/api/calculate_bucket_id.rs +++ b/sbroad-cartridge/src/api/calculate_bucket_id.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use std::os::raw::c_int; -use sbroad::errors::QueryPlannerError; +use sbroad::errors::{Entity, SbroadError}; use tarantool::tuple::{FunctionArgs, FunctionCtx, Tuple}; use serde::{de::Deserializer, Deserialize, Serialize}; @@ -88,7 +88,7 @@ enum Args { } impl TryFrom<FunctionArgs> for Args { - type Error = QueryPlannerError; + type Error = SbroadError; fn try_from(value: FunctionArgs) -> Result<Self, Self::Error> { if let Ok(args) = Tuple::from(&value).decode::<ArgsString>() { @@ -101,11 +101,14 @@ impl TryFrom<FunctionArgs> for Args { return Ok(Self::Map(args)); } - Err(QueryPlannerError::CustomError(format!( - "Parsing args {:?} error, \ - expected string, tuple with a space name, or map with a space name as an argument", - &value - ))) + Err(SbroadError::ParsingError( + Entity::Args, + format!( + "expected string, tuple with a space name, or map with a space name as an argument, \ + got args {:?}", + &value + ), + )) } } @@ -160,7 +163,7 @@ pub extern "C" fn calculate_bucket_id(ctx: FunctionCtx, args: FunctionArgs) -> c ctx.return_mp(&bucket_id).unwrap(); 0 } - Err(e) => tarantool_error(&format!("{e:?}")), + Err(e) => tarantool_error(&format!("{e}")), } }) } diff --git a/sbroad-cartridge/src/api/exec_query.rs b/sbroad-cartridge/src/api/exec_query.rs index c8f8950c52..bf4ced65ed 100644 --- a/sbroad-cartridge/src/api/exec_query.rs +++ b/sbroad-cartridge/src/api/exec_query.rs @@ -6,7 +6,7 @@ use crate::api::helper::load_config; use crate::api::{COORDINATOR_ENGINE, SEGMENT_ENGINE}; use protocol::RequiredData; use sbroad::backend::sql::ir::PatternWithParams; -use sbroad::errors::QueryPlannerError; +use sbroad::errors::{Action, Entity, SbroadError}; use sbroad::executor::Query; use sbroad::log::tarantool_error; use sbroad::otm::{child_span, query_span}; @@ -88,34 +88,54 @@ pub extern "C" fn dispatch_query(f_ctx: FunctionCtx, args: FunctionArgs) -> c_in ) } -fn decode_msgpack(args: FunctionArgs) -> Result<(Vec<u8>, Vec<u8>), QueryPlannerError> { +fn decode_msgpack(args: FunctionArgs) -> Result<(Vec<u8>, Vec<u8>), SbroadError> { debug!(Option::from("decode_msgpack"), &format!("args: {args:?}")); let tuple_buf: Vec<u8> = TupleBuffer::from(Tuple::from(args)).into(); let mut stream = rmp::decode::Bytes::from(tuple_buf.as_slice()); - let array_len = rmp::decode::read_array_len(&mut stream) - .map_err(|e| QueryPlannerError::CustomError(format!("array length: {e:?}")))? - as usize; + let array_len = rmp::decode::read_array_len(&mut stream).map_err(|e| { + SbroadError::FailedTo( + Action::Decode, + Some(Entity::MsgPack), + format!("array length: {e:?}"), + ) + })? as usize; if array_len != 2 { - return Err(QueryPlannerError::CustomError(format!( - "Expected tuple of 2 elements, got {}", - array_len - ))); + return Err(SbroadError::Invalid( + Entity::Tuple, + Some(format!("expected tuple of 2 elements, got {array_len}")), + )); } - let req_len = rmp::decode::read_str_len(&mut stream) - .map_err(|e| QueryPlannerError::CustomError(format!("read required data length: {e:?}")))? - as usize; + let req_len = rmp::decode::read_str_len(&mut stream).map_err(|e| { + SbroadError::FailedTo( + Action::Decode, + Some(Entity::MsgPack), + format!("read required data length: {e:?}"), + ) + })? as usize; let mut required: Vec<u8> = vec![0_u8; req_len]; - stream - .read_exact_buf(&mut required) - .map_err(|e| QueryPlannerError::CustomError(format!("read required data: {e:?}")))?; + stream.read_exact_buf(&mut required).map_err(|e| { + SbroadError::FailedTo( + Action::Decode, + Some(Entity::MsgPack), + format!("read required data: {e:?}"), + ) + })?; let opt_len = rmp::decode::read_str_len(&mut stream).map_err(|e| { - QueryPlannerError::CustomError(format!("read optional data string length: {e:?}")) + SbroadError::FailedTo( + Action::Decode, + Some(Entity::MsgPack), + format!("read optional data string length: {e:?}"), + ) })? as usize; let mut optional: Vec<u8> = vec![0_u8; opt_len]; - stream - .read_exact_buf(&mut optional) - .map_err(|e| QueryPlannerError::CustomError(format!("read optional data: {e:?}")))?; + stream.read_exact_buf(&mut optional).map_err(|e| { + SbroadError::FailedTo( + Action::Decode, + Some(Entity::MsgPack), + format!("read optional data: {e:?}"), + ) + })?; Ok((required, optional)) } diff --git a/sbroad-cartridge/src/api/exec_query/protocol.rs b/sbroad-cartridge/src/api/exec_query/protocol.rs index 1879e3bef6..c63ee88168 100644 --- a/sbroad-cartridge/src/api/exec_query/protocol.rs +++ b/sbroad-cartridge/src/api/exec_query/protocol.rs @@ -6,7 +6,7 @@ use tarantool::tlua::{self, AsLua, PushGuard, PushInto, PushOneInto, Void}; use sbroad::backend::sql::tree::OrderedSyntaxNodes; use sbroad::debug; -use sbroad::errors::QueryPlannerError; +use sbroad::errors::{Action, Entity, SbroadError}; use sbroad::executor::ir::{ExecutionPlan, QueryType}; use sbroad::ir::value::Value; use sbroad::otm::{ @@ -77,24 +77,29 @@ impl Default for RequiredData { } impl TryFrom<RequiredData> for Vec<u8> { - type Error = QueryPlannerError; + type Error = SbroadError; fn try_from(value: RequiredData) -> Result<Self, Self::Error> { bincode::serialize(&value).map_err(|e| { - QueryPlannerError::CustomError(format!( - "Failed to serialize required data to binary: {:?}", - e - )) + SbroadError::FailedTo( + Action::Serialize, + Some(Entity::RequiredData), + format!("to binary: {e:?}"), + ) }) } } impl TryFrom<&[u8]> for RequiredData { - type Error = QueryPlannerError; + type Error = SbroadError; fn try_from(value: &[u8]) -> Result<Self, Self::Error> { bincode::deserialize(value).map_err(|e| { - QueryPlannerError::CustomError(format!("Failed to deserialize required data: {e:?}")) + SbroadError::FailedTo( + Action::Deserialize, + Some(Entity::RequiredData), + format!("{e:?}"), + ) }) } } @@ -167,7 +172,7 @@ impl From<EncodedRequiredData> for Vec<u8> { } impl TryFrom<RequiredData> for EncodedRequiredData { - type Error = QueryPlannerError; + type Error = SbroadError; fn try_from(value: RequiredData) -> Result<Self, Self::Error> { let bytes: Vec<u8> = value.try_into()?; @@ -176,7 +181,7 @@ impl TryFrom<RequiredData> for EncodedRequiredData { } impl TryFrom<EncodedRequiredData> for RequiredData { - type Error = QueryPlannerError; + type Error = SbroadError; fn try_from(value: EncodedRequiredData) -> Result<Self, Self::Error> { let ir: RequiredData = value.0.as_slice().try_into()?; @@ -191,24 +196,29 @@ pub struct OptionalData { } impl TryFrom<OptionalData> for Vec<u8> { - type Error = QueryPlannerError; + type Error = SbroadError; fn try_from(value: OptionalData) -> Result<Self, Self::Error> { bincode::serialize(&value).map_err(|e| { - QueryPlannerError::CustomError(format!( - "Failed to serialize required data to binary: {:?}", - e - )) + SbroadError::FailedTo( + Action::Serialize, + Some(Entity::RequiredData), + format!("to binary: {e:?}"), + ) }) } } impl TryFrom<&[u8]> for OptionalData { - type Error = QueryPlannerError; + type Error = SbroadError; fn try_from(value: &[u8]) -> Result<Self, Self::Error> { bincode::deserialize(value).map_err(|e| { - QueryPlannerError::CustomError(format!("Failed to deserialize required data: {e:?}")) + SbroadError::FailedTo( + Action::Deserialize, + Some(Entity::RequiredData), + format!("{e:?}"), + ) }) } } @@ -218,18 +228,23 @@ impl OptionalData { OptionalData { exec_plan, ordered } } - pub fn to_bytes(&self) -> Result<Vec<u8>, QueryPlannerError> { + pub fn to_bytes(&self) -> Result<Vec<u8>, SbroadError> { bincode::serialize(self).map_err(|e| { - QueryPlannerError::CustomError(format!( - "Failed to serialize required data to binary: {:?}", - e - )) + SbroadError::FailedTo( + Action::Serialize, + Some(Entity::RequiredData), + format!("to binary: {e:?}"), + ) }) } - pub fn try_from_bytes(bytes: &[u8]) -> Result<Self, QueryPlannerError> { + pub fn try_from_bytes(bytes: &[u8]) -> Result<Self, SbroadError> { bincode::deserialize(bytes).map_err(|e| { - QueryPlannerError::CustomError(format!("Failed to deserialize required data: {e:?}")) + SbroadError::FailedTo( + Action::Deserialize, + Some(Entity::RequiredData), + format!("{e:?}"), + ) }) } } @@ -249,7 +264,7 @@ impl From<EncodedOptionalData> for Vec<u8> { } impl TryFrom<OptionalData> for EncodedOptionalData { - type Error = QueryPlannerError; + type Error = SbroadError; fn try_from(value: OptionalData) -> Result<Self, Self::Error> { let bytes: Vec<u8> = value.try_into()?; @@ -258,7 +273,7 @@ impl TryFrom<OptionalData> for EncodedOptionalData { } impl TryFrom<EncodedOptionalData> for OptionalData { - type Error = QueryPlannerError; + type Error = SbroadError; fn try_from(value: EncodedOptionalData) -> Result<Self, Self::Error> { let ir: OptionalData = value.0.as_slice().try_into()?; diff --git a/sbroad-cartridge/src/cartridge.rs b/sbroad-cartridge/src/cartridge.rs index d848ff4c6f..f96852b47d 100644 --- a/sbroad-cartridge/src/cartridge.rs +++ b/sbroad-cartridge/src/cartridge.rs @@ -6,7 +6,7 @@ pub mod storage; use opentelemetry::global::{set_text_map_propagator, set_tracer_provider}; use opentelemetry::sdk::propagation::{TextMapCompositePropagator, TraceContextPropagator}; -use sbroad::errors::QueryPlannerError; +use sbroad::errors::{Action, SbroadError}; use sbroad::otm::update_global_tracer; static SERVICE_NAME: &str = "sbroad"; @@ -15,7 +15,7 @@ static SERVICE_NAME: &str = "sbroad"; /// /// # Errors /// - failed to build OTM global trace provider -pub fn update_tracing(host: &str, port: u16) -> Result<(), QueryPlannerError> { +pub fn update_tracing(host: &str, port: u16) -> Result<(), SbroadError> { let propagator = TextMapCompositePropagator::new(vec![Box::new(TraceContextPropagator::new())]); set_text_map_propagator(propagator); let provider = opentelemetry_jaeger::new_pipeline() @@ -23,10 +23,11 @@ pub fn update_tracing(host: &str, port: u16) -> Result<(), QueryPlannerError> { .with_service_name(SERVICE_NAME) .build_simple() .map_err(|e| { - QueryPlannerError::CustomError(format!( - "Failed to build OTM global trace provider: {}", - e - )) + SbroadError::FailedTo( + Action::Build, + None, + format!("OTM global trace provider: {e}"), + ) })?; set_tracer_provider(provider); update_global_tracer(); diff --git a/sbroad-cartridge/src/cartridge/config.rs b/sbroad-cartridge/src/cartridge/config.rs index 994eb087b3..b0417828b2 100644 --- a/sbroad-cartridge/src/cartridge/config.rs +++ b/sbroad-cartridge/src/cartridge/config.rs @@ -5,7 +5,7 @@ extern crate yaml_rust; use std::collections::HashMap; use yaml_rust::{Yaml, YamlLoader}; -use sbroad::errors::QueryPlannerError; +use sbroad::errors::{Entity, SbroadError}; use sbroad::executor::engine::CoordinatorMetadata; use sbroad::executor::engine::{normalize_name_from_schema, normalize_name_from_sql}; use sbroad::executor::lru::DEFAULT_CAPACITY; @@ -64,8 +64,8 @@ impl RouterConfiguration { /// Parse and load yaml cartridge schema to cache /// /// # Errors - /// Returns `QueryPlannerError` when process was terminated. - pub fn load_schema(&mut self, s: &str) -> Result<(), QueryPlannerError> { + /// Returns `SbroadError` when process was terminated. + pub fn load_schema(&mut self, s: &str) -> Result<(), SbroadError> { self.init_core_functions(); if let Ok(docs) = YamlLoader::load_from_str(s) { if let Some(schema) = docs.get(0) { @@ -74,7 +74,7 @@ impl RouterConfiguration { } } - Err(QueryPlannerError::InvalidClusterSchema) + Err(SbroadError::Invalid(Entity::ClusterSchema, None)) } pub(crate) fn is_empty(&self) -> bool { @@ -93,13 +93,18 @@ impl RouterConfiguration { /// Transform space information from schema to table segments /// /// # Errors - /// Returns `QueryPlannerError` when schema contains errors. + /// Returns `SbroadError` when schema contains errors. #[allow(clippy::too_many_lines)] - fn init_table_segments(&mut self, schema: &Yaml) -> Result<(), QueryPlannerError> { + fn init_table_segments(&mut self, schema: &Yaml) -> Result<(), SbroadError> { self.tables.clear(); let spaces = match schema["spaces"].as_hash() { Some(v) => v, - None => return Err(QueryPlannerError::InvalidSchemaSpaces), + None => { + return Err(SbroadError::Invalid( + Entity::ClusterSchema, + Some("schema.spaces is invalid".into()), + )) + } }; for (space_name, params) in spaces.iter() { @@ -109,15 +114,25 @@ impl RouterConfiguration { for val in fields { let name: &str = match val["name"].as_str() { Some(s) => s, - None => return Err(QueryPlannerError::InvalidColumnName), + None => { + return Err(SbroadError::Invalid( + Entity::ClusterSchema, + Some(format!( + "column name of table {current_space_name} is invalid" + )), + )) + } }; let t = match val["type"].as_str() { Some(t) => Type::new(t)?, None => { - return Err(QueryPlannerError::CustomError(format!( - "Type not found for columns {}", - name - ))) + return Err(SbroadError::Invalid( + Entity::ClusterSchema, + Some(format!( + "Type not found for columns {} of table {}", + name, current_space_name + )), + )) } }; let qualified_name = normalize_name_from_schema(name); @@ -183,7 +198,10 @@ impl RouterConfiguration { let t = Table::new_seg(&table_name, fields, keys_str.as_slice())?; self.tables.insert(table_name, t); } else { - return Err(QueryPlannerError::InvalidSpaceName); + return Err(SbroadError::Invalid( + Entity::ClusterSchema, + Some("space name is invalid".into()), + )); } } @@ -232,27 +250,21 @@ impl CoordinatorMetadata for RouterConfiguration { /// Get table segment form cache by table name /// /// # Errors - /// Returns `QueryPlannerError` when table was not found. + /// Returns `SbroadError` when table was not found. #[allow(dead_code)] - fn get_table_segment(&self, table_name: &str) -> Result<Table, QueryPlannerError> { + fn get_table_segment(&self, table_name: &str) -> Result<Table, SbroadError> { let name = normalize_name_from_sql(table_name); match self.tables.get(&name) { Some(v) => Ok(v.clone()), - None => Err(QueryPlannerError::CustomError(format!( - "Space {} not found", - name - ))), + None => Err(SbroadError::NotFound(Entity::Space, name)), } } - fn get_function(&self, fn_name: &str) -> Result<&Function, QueryPlannerError> { + fn get_function(&self, fn_name: &str) -> Result<&Function, SbroadError> { let name = normalize_name_from_sql(fn_name); match self.functions.get(&name) { Some(v) => Ok(v), - None => Err(QueryPlannerError::CustomError(format!( - "Function {} not found", - name - ))), + None => Err(SbroadError::NotFound(Entity::SQLFunction, name)), } } @@ -266,20 +278,17 @@ impl CoordinatorMetadata for RouterConfiguration { } /// Get sharding key's column names by a space name - fn get_sharding_key_by_space(&self, space: &str) -> Result<Vec<String>, QueryPlannerError> { + fn get_sharding_key_by_space(&self, space: &str) -> Result<Vec<String>, SbroadError> { let table = self.get_table_segment(space)?; table.get_sharding_column_names() } - fn get_sharding_positions_by_space( - &self, - space: &str, - ) -> Result<Vec<usize>, QueryPlannerError> { + fn get_sharding_positions_by_space(&self, space: &str) -> Result<Vec<usize>, SbroadError> { let table = self.get_table_segment(space)?; Ok(table.get_sharding_positions().to_vec()) } - fn get_fields_amount_by_space(&self, space: &str) -> Result<usize, QueryPlannerError> { + fn get_fields_amount_by_space(&self, space: &str) -> Result<usize, SbroadError> { let table = self.get_table_segment(space)?; Ok(table.columns.len()) } diff --git a/sbroad-cartridge/src/cartridge/config/tests.rs b/sbroad-cartridge/src/cartridge/config/tests.rs index 455fbc968f..589cef3b87 100644 --- a/sbroad-cartridge/src/cartridge/config/tests.rs +++ b/sbroad-cartridge/src/cartridge/config/tests.rs @@ -156,7 +156,7 @@ fn test_getting_table_segment() { assert_eq!( s.get_table_segment("invalid_table").unwrap_err(), - QueryPlannerError::CustomError(r#"Space "INVALID_TABLE" not found"#.into()) + SbroadError::NotFound(Entity::Space, r#""INVALID_TABLE""#.into()) ); assert_eq!(s.get_table_segment("\"hash_testing\"").unwrap(), expected); } @@ -241,6 +241,6 @@ fn test_invalid_schema() { assert_eq!( s.load_schema(test_schema).unwrap_err(), - QueryPlannerError::CustomError("type `map` not implemented".into()) + SbroadError::NotImplemented(Entity::Type, "map".into()) ); } diff --git a/sbroad-cartridge/src/cartridge/router.rs b/sbroad-cartridge/src/cartridge/router.rs index a68dd1f8ce..06484b6067 100644 --- a/sbroad-cartridge/src/cartridge/router.rs +++ b/sbroad-cartridge/src/cartridge/router.rs @@ -18,7 +18,7 @@ use crate::cartridge::config::RouterConfiguration; use crate::cartridge::update_tracing; use sbroad::backend::sql::tree::{OrderedSyntaxNodes, SyntaxPlan}; -use sbroad::errors::QueryPlannerError; +use sbroad::errors::{Action, Entity, SbroadError}; use sbroad::executor::bucket::Buckets; use sbroad::executor::engine::{ normalize_name_from_schema, sharding_keys_from_map, sharding_keys_from_tuple, Configuration, @@ -65,7 +65,7 @@ impl Configuration for RouterRuntime { } #[allow(clippy::too_many_lines)] - fn get_config(&self) -> Result<Option<Self::Configuration>, QueryPlannerError> { + fn get_config(&self) -> Result<Option<Self::Configuration>, SbroadError> { if self.is_config_empty() { let lua = tarantool::lua_state(); @@ -74,7 +74,7 @@ impl Configuration for RouterRuntime { Ok(res) => res, Err(e) => { error!(Option::from("getting schema"), &format!("{e:?}")); - return Err(QueryPlannerError::LuaError(format!("Lua error: {e:?}"))); + return Err(SbroadError::LuaError(format!("Lua error: {e:?}"))); } }; @@ -84,7 +84,7 @@ impl Configuration for RouterRuntime { Ok(res) => res, Err(e) => { error!(Option::from("getting jaeger agent host"), &format!("{e:?}"),); - return Err(QueryPlannerError::LuaError(format!("Lua error: {e:?}"))); + return Err(SbroadError::LuaError(format!("Lua error: {e:?}"))); } }; @@ -94,7 +94,7 @@ impl Configuration for RouterRuntime { Ok(res) => res, Err(e) => { error!(Option::from("getting jaeger agent port"), &format!("{e:?}"),); - return Err(QueryPlannerError::LuaError(format!("Lua error: {e:?}"))); + return Err(SbroadError::LuaError(format!("Lua error: {e:?}"))); } }; @@ -103,7 +103,7 @@ impl Configuration for RouterRuntime { Ok(res) => res, Err(e) => { error!(Option::from("getting waiting timeout"), &format!("{e:?}")); - return Err(QueryPlannerError::LuaError(format!("Lua error: {e:?}"))); + return Err(SbroadError::LuaError(format!("Lua error: {e:?}"))); } }; @@ -113,10 +113,10 @@ impl Configuration for RouterRuntime { Ok(capacity) => { let val: u64 = capacity; usize::try_from(val).map_err(|_| { - QueryPlannerError::CustomError(format!( - "Router cache capacity is too big: {}", - capacity - )) + SbroadError::Invalid( + Entity::Cache, + Some(format!("router cache capacity is too big: {capacity}")), + ) })? } Err(e) => { @@ -124,7 +124,7 @@ impl Configuration for RouterRuntime { Option::from("getting router cache capacity"), &format!("{e:?}"), ); - return Err(QueryPlannerError::LuaError(format!("Lua error: {e:?}"))); + return Err(SbroadError::LuaError(format!("Lua error: {e:?}"))); } }; @@ -133,7 +133,7 @@ impl Configuration for RouterRuntime { Ok(column) => column, Err(e) => { error!(Option::from("getting sharding column"), &format!("{e:?}")); - return Err(QueryPlannerError::LuaError(format!("Lua error: {e:?}"))); + return Err(SbroadError::LuaError(format!("Lua error: {e:?}"))); } }; @@ -164,9 +164,9 @@ impl Coordinator for RouterRuntime { type ParseTree = AbstractSyntaxTree; type Cache = LRUCache<String, Plan>; - fn clear_ir_cache(&self) -> Result<(), QueryPlannerError> { + fn clear_ir_cache(&self) -> Result<(), SbroadError> { *self.ir_cache.try_borrow_mut().map_err(|e| { - QueryPlannerError::CustomError(format!("Failed to clear the cache: {e:?}")) + SbroadError::FailedTo(Action::Clear, Some(Entity::Cache), format!("{e:?}")) })? = Self::Cache::new(DEFAULT_CAPACITY, None)?; Ok(()) } @@ -182,7 +182,7 @@ impl Coordinator for RouterRuntime { plan: &mut ExecutionPlan, top_id: usize, buckets: &Buckets, - ) -> Result<Box<dyn Any>, QueryPlannerError> { + ) -> Result<Box<dyn Any>, SbroadError> { debug!( Option::from("dispatch"), &format!("dispatching plan: {plan:?}") @@ -211,28 +211,27 @@ impl Coordinator for RouterRuntime { let can_be_cached = |plan: &ExecutionPlan| plan.get_vtables().map_or(true, HashMap::is_empty); - let encode_plan = - |exec_plan: ExecutionPlan| -> Result<(Binary, Binary), QueryPlannerError> { - let sp_top_id = exec_plan.get_ir_plan().get_top()?; - let sp = SyntaxPlan::new(&exec_plan, sp_top_id, Snapshot::Oldest)?; - let ordered = OrderedSyntaxNodes::try_from(sp)?; - let nodes = ordered.to_syntax_data()?; - // Virtual tables in the plan must be already filtered, so we can use all buckets here. - let params = exec_plan.to_params(&nodes, &Buckets::All)?; - let query_type = exec_plan.query_type()?; - let required_data = RequiredData::new( - sub_plan_id.clone(), - params, - query_type, - can_be_cached(&exec_plan), - ); - let encoded_required_data = EncodedRequiredData::try_from(required_data)?; - let raw_required_data: Vec<u8> = encoded_required_data.into(); - let optional_data = OptionalData::new(exec_plan, ordered); - let encoded_optional_data = EncodedOptionalData::try_from(optional_data)?; - let raw_optional_data: Vec<u8> = encoded_optional_data.into(); - Ok((raw_required_data.into(), raw_optional_data.into())) - }; + let encode_plan = |exec_plan: ExecutionPlan| -> Result<(Binary, Binary), SbroadError> { + let sp_top_id = exec_plan.get_ir_plan().get_top()?; + let sp = SyntaxPlan::new(&exec_plan, sp_top_id, Snapshot::Oldest)?; + let ordered = OrderedSyntaxNodes::try_from(sp)?; + let nodes = ordered.to_syntax_data()?; + // Virtual tables in the plan must be already filtered, so we can use all buckets here. + let params = exec_plan.to_params(&nodes, &Buckets::All)?; + let query_type = exec_plan.query_type()?; + let required_data = RequiredData::new( + sub_plan_id.clone(), + params, + query_type, + can_be_cached(&exec_plan), + ); + let encoded_required_data = EncodedRequiredData::try_from(required_data)?; + let raw_required_data: Vec<u8> = encoded_required_data.into(); + let optional_data = OptionalData::new(exec_plan, ordered); + let encoded_optional_data = EncodedOptionalData::try_from(optional_data)?; + let raw_optional_data: Vec<u8> = encoded_optional_data.into(); + Ok((raw_required_data.into(), raw_optional_data.into())) + }; if let Buckets::Filtered(bucket_set) = buckets { let random_bucket = self.get_random_bucket(); @@ -252,8 +251,8 @@ impl Coordinator for RouterRuntime { let mut rs_ir: HashMap<String, Message> = HashMap::new(); let rs_bucket_vec: Vec<(String, Vec<u64>)> = group(buckets)?.drain().collect(); if rs_bucket_vec.is_empty() { - return Err(QueryPlannerError::CustomError(format!( - "No replica sets were found for the buckets {:?} to execute the query on", + return Err(SbroadError::UnexpectedNumberOfValues(format!( + "no replica sets were found for the buckets {:?} to execute the query on", buckets ))); } @@ -277,15 +276,16 @@ impl Coordinator for RouterRuntime { self.exec_ir_on_all(required, optional, query_type, conn_type) } - fn explain_format(&self, explain: String) -> Result<Box<dyn Any>, QueryPlannerError> { + fn explain_format(&self, explain: String) -> Result<Box<dyn Any>, SbroadError> { let e = explain.lines().collect::<Vec<&str>>(); match Tuple::new(&vec![e]) { Ok(t) => Ok(Box::new(t)), - Err(e) => Err(QueryPlannerError::CustomError(format!( - "Tuple creation error: {}", - e - ))), + Err(e) => Err(SbroadError::FailedTo( + Action::Create, + Some(Entity::Tuple), + format!("{e}"), + )), } } @@ -296,7 +296,7 @@ impl Coordinator for RouterRuntime { plan: &mut ExecutionPlan, motion_node_id: usize, buckets: &Buckets, - ) -> Result<VirtualTable, QueryPlannerError> { + ) -> Result<VirtualTable, SbroadError> { let top_id = plan.get_motion_subtree_root(motion_node_id)?; // We should get a motion alias name before we take the subtree in dispatch. let alias = plan.get_motion_alias(motion_node_id)?.map(String::from); @@ -305,18 +305,21 @@ impl Coordinator for RouterRuntime { plan.unlink_motion_subtree(motion_node_id)?; let mut vtable = if let Ok(tuple) = result.downcast::<Tuple>() { let data = tuple.decode::<Vec<ProducerResult>>().map_err(|e| { - QueryPlannerError::CustomError(format!("Motion node {motion_node_id}. {e}")) + SbroadError::FailedTo( + Action::Decode, + Some(Entity::Tuple), + format!("motion node {motion_node_id}. {e}"), + ) })?; data.get(0) .ok_or_else(|| { - QueryPlannerError::CustomError( - "Failed to retrieve producer result from the tuple".into(), - ) + SbroadError::NotFound(Entity::ProducerResult, "from the tuple".into()) })? .as_virtual_table()? } else { - return Err(QueryPlannerError::CustomError( - "The result of the motion is not a tuple".to_string(), + return Err(SbroadError::Invalid( + Entity::Motion, + Some("the result of the motion is not a tuple".to_string()), )); }; if let Some(name) = alias { @@ -330,7 +333,7 @@ impl Coordinator for RouterRuntime { &'engine self, space: String, map: &'rec HashMap<String, Value>, - ) -> Result<Vec<&'rec Value>, QueryPlannerError> { + ) -> Result<Vec<&'rec Value>, SbroadError> { sharding_keys_from_map(&self.metadata, &space, map) } @@ -338,7 +341,7 @@ impl Coordinator for RouterRuntime { &'engine self, space: String, rec: &'rec [Value], - ) -> Result<Vec<&'rec Value>, QueryPlannerError> { + ) -> Result<Vec<&'rec Value>, SbroadError> { sharding_keys_from_tuple(self.cached_config(), &space, rec) } @@ -353,7 +356,7 @@ impl RouterRuntime { /// /// # Errors /// - Failed to detect the correct amount of buckets. - pub fn new() -> Result<Self, QueryPlannerError> { + pub fn new() -> Result<Self, SbroadError> { let cache: LRUCache<String, Plan> = LRUCache::new(DEFAULT_CAPACITY, None)?; let mut result = RouterRuntime { metadata: RouterConfiguration::new(), @@ -375,7 +378,7 @@ impl RouterRuntime { } /// Function get summary count of bucket from `vshard` - fn set_bucket_count(&mut self) -> Result<(), QueryPlannerError> { + fn set_bucket_count(&mut self) -> Result<(), SbroadError> { let lua = tarantool::lua_state(); let bucket_count_fn: LuaFunction<_> = @@ -383,7 +386,7 @@ impl RouterRuntime { Ok(v) => v, Err(e) => { error!(Option::from("set_bucket_count"), &format!("{e:?}")); - return Err(QueryPlannerError::LuaError(format!( + return Err(SbroadError::LuaError(format!( "Failed lua function load: {}", e ))); @@ -394,16 +397,17 @@ impl RouterRuntime { Ok(r) => r, Err(e) => { error!(Option::from("set_bucket_count"), &format!("{e:?}")); - return Err(QueryPlannerError::LuaError(e.to_string())); + return Err(SbroadError::LuaError(e.to_string())); } }; self.bucket_count = match bucket_count.try_into() { Ok(v) => v, Err(_) => { - return Err(QueryPlannerError::CustomError(String::from( - "Invalid bucket count", - ))); + return Err(SbroadError::Invalid( + Entity::Runtime, + Some("invalid bucket count".into()), + )); } }; @@ -413,12 +417,12 @@ impl RouterRuntime { fn read_dql_on_some( &self, rs_ir: HashMap<String, Message>, - ) -> Result<Box<dyn Any>, QueryPlannerError> { + ) -> Result<Box<dyn Any>, SbroadError> { let lua = tarantool::lua_state(); - let exec_sql: LuaFunction<_> = lua.get("read_dql_on_some").ok_or_else(|| { - QueryPlannerError::LuaError("Lua function `read_on_some` not found".into()) - })?; + let exec_sql: LuaFunction<_> = lua + .get("read_dql_on_some") + .ok_or_else(|| SbroadError::LuaError("Lua function `read_on_some` not found".into()))?; let waiting_timeout = &self.cached_config().get_exec_waiting_timeout(); match exec_sql.call_with_args::<Tuple, _>((rs_ir, waiting_timeout)) { @@ -431,7 +435,7 @@ impl RouterRuntime { } Err(e) => { error!(Option::from("read_dql_on_some"), &format!("{e:?}")); - Err(QueryPlannerError::LuaError(format!( + Err(SbroadError::LuaError(format!( "Lua error (IR dispatch): {:?}", e ))) @@ -442,11 +446,11 @@ impl RouterRuntime { fn write_dml_on_some( &self, rs_ir: HashMap<String, Message>, - ) -> Result<Box<dyn Any>, QueryPlannerError> { + ) -> Result<Box<dyn Any>, SbroadError> { let lua = tarantool::lua_state(); let exec_sql: LuaFunction<_> = lua.get("write_dml_on_some").ok_or_else(|| { - QueryPlannerError::LuaError("Lua function `write_dml_on_some` not found".into()) + SbroadError::LuaError("Lua function `write_dml_on_some` not found".into()) })?; let waiting_timeout = &self.cached_config().get_exec_waiting_timeout(); @@ -454,7 +458,7 @@ impl RouterRuntime { Ok(v) => Ok(Box::new(v)), Err(e) => { error!(Option::from("write_on_some"), &format!("{e:?}")); - Err(QueryPlannerError::LuaError(format!( + Err(SbroadError::LuaError(format!( "Lua error (IR dispatch): {:?}", e ))) @@ -468,14 +472,17 @@ impl RouterRuntime { rs_ir: HashMap<String, Message>, query_type: QueryType, conn_type: ConnectionType, - ) -> Result<Box<dyn Any>, QueryPlannerError> { + ) -> Result<Box<dyn Any>, SbroadError> { match (&query_type, &conn_type) { (QueryType::DQL, ConnectionType::Read) => self.read_dql_on_some(rs_ir), (QueryType::DML, ConnectionType::Write) => self.write_dml_on_some(rs_ir), - _ => Err(QueryPlannerError::CustomError(format!( - "Unsupported combination of the query type: {:?} and connection type: {:?}", - query_type, conn_type - ))), + _ => Err(SbroadError::Unsupported( + Entity::Type, + Some(format!( + "unsupported combination of the query type: {:?} and connection type: {:?}", + query_type, conn_type + )), + )), } } @@ -483,10 +490,10 @@ impl RouterRuntime { &self, required: Binary, optional: Binary, - ) -> Result<Box<dyn Any>, QueryPlannerError> { + ) -> Result<Box<dyn Any>, SbroadError> { let lua = tarantool::lua_state(); let exec_sql: LuaFunction<_> = lua.get("read_dql_on_all").ok_or_else(|| { - QueryPlannerError::LuaError("Lua function `read_dql_on_all` not found".into()) + SbroadError::LuaError("Lua function `read_dql_on_all` not found".into()) })?; let waiting_timeout = &self.cached_config().get_exec_waiting_timeout(); @@ -500,7 +507,7 @@ impl RouterRuntime { } Err(e) => { error!(Option::from("read_dql_on_all"), &format!("{e:?}")); - Err(QueryPlannerError::LuaError(format!( + Err(SbroadError::LuaError(format!( "Lua error (dispatch IR): {:?}", e ))) @@ -512,11 +519,11 @@ impl RouterRuntime { &self, required: Binary, optional: Binary, - ) -> Result<Box<dyn Any>, QueryPlannerError> { + ) -> Result<Box<dyn Any>, SbroadError> { let lua = tarantool::lua_state(); let exec_sql: LuaFunction<_> = lua.get("write_dml_on_all").ok_or_else(|| { - QueryPlannerError::LuaError("Lua function `write_dml_on_all` not found".into()) + SbroadError::LuaError("Lua function `write_dml_on_all` not found".into()) })?; let waiting_timeout = &self.cached_config().get_exec_waiting_timeout(); @@ -524,7 +531,7 @@ impl RouterRuntime { Ok(v) => Ok(Box::new(v)), Err(e) => { error!(Option::from("write_dml_on_all"), &format!("{e:?}")); - Err(QueryPlannerError::LuaError(format!( + Err(SbroadError::LuaError(format!( "Lua error (dispatch IR): {:?}", e ))) @@ -539,24 +546,28 @@ impl RouterRuntime { optional: Binary, query_type: QueryType, conn_type: ConnectionType, - ) -> Result<Box<dyn Any>, QueryPlannerError> { + ) -> Result<Box<dyn Any>, SbroadError> { match (&query_type, &conn_type) { (QueryType::DQL, ConnectionType::Read) => self.read_dql_on_all(required, optional), (QueryType::DML, ConnectionType::Write) => self.write_dml_on_all(required, optional), - _ => Err(QueryPlannerError::CustomError(format!( - "Unsupported combination of the query type: {:?} and connection type: {:?}", - query_type, conn_type - ))), + _ => Err(SbroadError::Unsupported( + Entity::Type, + Some(format!( + "unsupported combination of the query type: {:?} and connection type: {:?}", + query_type, conn_type + )), + )), } } } #[otm_child_span("buckets.group")] -fn group(buckets: &Buckets) -> Result<HashMap<String, Vec<u64>>, QueryPlannerError> { +fn group(buckets: &Buckets) -> Result<HashMap<String, Vec<u64>>, SbroadError> { let lua_buckets: Vec<u64> = match buckets { Buckets::All => { - return Err(QueryPlannerError::CustomError( - "Grouping buckets is not supported for all buckets".into(), + return Err(SbroadError::Unsupported( + Entity::Buckets, + Some("grouping buckets is not supported for all buckets".into()), )) } Buckets::Filtered(list) => list.iter().copied().collect(), @@ -565,14 +576,14 @@ fn group(buckets: &Buckets) -> Result<HashMap<String, Vec<u64>>, QueryPlannerErr let lua = tarantool::lua_state(); let fn_group: LuaFunction<_> = lua.get("group_buckets_by_replicasets").ok_or_else(|| { - QueryPlannerError::LuaError("Lua function `group_buckets_by_replicasets` not found".into()) + SbroadError::LuaError("Lua function `group_buckets_by_replicasets` not found".into()) })?; let res: GroupedBuckets = match fn_group.call_with_args(lua_buckets) { Ok(v) => v, Err(e) => { error!(Option::from("buckets group"), &format!("{e:?}")); - return Err(QueryPlannerError::LuaError(format!("Lua error: {e:?}"))); + return Err(SbroadError::LuaError(format!("Lua error: {e:?}"))); } }; diff --git a/sbroad-cartridge/src/cartridge/storage.rs b/sbroad-cartridge/src/cartridge/storage.rs index 5664b0dada..807a649ffc 100644 --- a/sbroad-cartridge/src/cartridge/storage.rs +++ b/sbroad-cartridge/src/cartridge/storage.rs @@ -1,7 +1,7 @@ use crate::api::exec_query::protocol::{EncodedOptionalData, OptionalData, RequiredData}; use crate::cartridge::config::StorageConfiguration; use crate::cartridge::update_tracing; -use sbroad::errors::QueryPlannerError; +use sbroad::errors::{Action, Entity, SbroadError}; use sbroad::executor::bucket::Buckets; use sbroad::executor::engine::Configuration; use sbroad::executor::ir::QueryType; @@ -29,17 +29,20 @@ impl PreparedStmt { /// /// # Errors /// - Returns None instead of a regular statement (sentinel node in the cache). - fn statement(&self) -> Result<&Statement, QueryPlannerError> { - self.0 - .as_ref() - .ok_or_else(|| QueryPlannerError::CustomError("Statement is not prepared".to_string())) + fn statement(&self) -> Result<&Statement, SbroadError> { + self.0.as_ref().ok_or_else(|| { + SbroadError::Invalid( + Entity::Statement, + Some("Statement is not prepared".to_string()), + ) + }) } - fn id(&self) -> Result<u32, QueryPlannerError> { + fn id(&self) -> Result<u32, SbroadError> { Ok(self.statement()?.id) } - fn pattern(&self) -> Result<&str, QueryPlannerError> { + fn pattern(&self) -> Result<&str, SbroadError> { Ok(&self.statement()?.pattern) } } @@ -75,7 +78,7 @@ impl Configuration for StorageRuntime { self.metadata.is_empty() } - fn get_config(&self) -> Result<Option<Self::Configuration>, QueryPlannerError> { + fn get_config(&self) -> Result<Option<Self::Configuration>, SbroadError> { if self.is_config_empty() { let lua = tarantool::lua_state(); @@ -88,11 +91,11 @@ impl Configuration for StorageRuntime { Option::from("getting storage cache capacity"), &format!("{e:?}"), ); - return Err(QueryPlannerError::LuaError(format!("Lua error: {e:?}"))); + return Err(SbroadError::LuaError(format!("Lua error: {e:?}"))); } }; let storage_capacity = usize::try_from(capacity) - .map_err(|e| QueryPlannerError::CustomError(format!("{e:?}")))?; + .map_err(|e| SbroadError::Invalid(Entity::Cache, Some(format!("{e:?}"))))?; let storage_cache_size_bytes: LuaFunction<_> = lua.eval("return get_storage_cache_size_bytes;").unwrap(); @@ -103,11 +106,11 @@ impl Configuration for StorageRuntime { Option::from("getting storage cache size bytes"), &format!("{e:?}"), ); - return Err(QueryPlannerError::LuaError(format!("Lua error: {e:?}"))); + return Err(SbroadError::LuaError(format!("Lua error: {e:?}"))); } }; let storage_size_bytes = usize::try_from(cache_size_bytes) - .map_err(|e| QueryPlannerError::CustomError(format!("{e}")))?; + .map_err(|e| SbroadError::Invalid(Entity::Cache, Some(format!("{e}"))))?; let jaeger_agent_host: LuaFunction<_> = lua.eval("return get_jaeger_agent_host;").unwrap(); @@ -115,7 +118,7 @@ impl Configuration for StorageRuntime { Ok(res) => res, Err(e) => { error!(Option::from("getting jaeger agent host"), &format!("{e:?}"),); - return Err(QueryPlannerError::LuaError(format!("Lua error: {e:?}"))); + return Err(SbroadError::LuaError(format!("Lua error: {e:?}"))); } }; @@ -125,7 +128,7 @@ impl Configuration for StorageRuntime { Ok(res) => res, Err(e) => { error!(Option::from("getting jaeger agent port"), &format!("{e:?}"),); - return Err(QueryPlannerError::LuaError(format!("Lua error: {e:?}"))); + return Err(SbroadError::LuaError(format!("Lua error: {e:?}"))); } }; @@ -152,7 +155,7 @@ impl StorageRuntime { /// /// # Errors /// - Failed to initialize the LRU cache. - pub fn new() -> Result<Self, QueryPlannerError> { + pub fn new() -> Result<Self, SbroadError> { let cache: LRUCache<String, PreparedStmt> = LRUCache::new(DEFAULT_CAPACITY, Some(Box::new(unprepare)))?; let result = StorageRuntime { @@ -168,7 +171,7 @@ impl StorageRuntime { &self, required: &mut RequiredData, raw_optional: &mut Vec<u8>, - ) -> Result<Box<dyn Any>, QueryPlannerError> { + ) -> Result<Box<dyn Any>, SbroadError> { let plan_id = required.plan_id.clone(); // Use all buckets as we don't want to filter any data from the execution plan @@ -181,7 +184,7 @@ impl StorageRuntime { .cache .try_borrow_mut() .map_err(|e| { - QueryPlannerError::CustomError(format!("Failed to borrow cache: {e}")) + SbroadError::FailedTo(Action::Borrow, Some(Entity::Cache), format!("{e}")) })? .get(&plan_id)? { @@ -230,10 +233,11 @@ impl StorageRuntime { self.cache .try_borrow_mut() .map_err(|e| { - QueryPlannerError::CustomError(format!( - "Failed to put prepared statement {:?} into the cache: {:?}", - stmt, e - )) + SbroadError::FailedTo( + Action::Put, + None, + format!("prepared statement {stmt:?} into the cache: {e:?}"), + ) })? .put(plan_id, stmt)?; } @@ -279,12 +283,12 @@ impl StorageRuntime { } #[otm_child_span("tarantool.statement.prepare")] -fn prepare(pattern: &str) -> Result<PreparedStmt, QueryPlannerError> { +fn prepare(pattern: &str) -> Result<PreparedStmt, SbroadError> { let lua = tarantool::lua_state(); let prepare_stmt: LuaFunction<_> = lua .get("prepare") - .ok_or_else(|| QueryPlannerError::LuaError("Lua function `prepare` not found".into()))?; + .ok_or_else(|| SbroadError::LuaError("Lua function `prepare` not found".into()))?; match prepare_stmt.call_with_args::<u32, _>(pattern) { Ok(stmt_id) => { @@ -296,100 +300,92 @@ fn prepare(pattern: &str) -> Result<PreparedStmt, QueryPlannerError> { } Err(e) => { error!(Option::from("prepare"), &format!("{e:?}")); - Err(QueryPlannerError::LuaError(format!("Lua error: {e:?}"))) + Err(SbroadError::LuaError(format!("Lua error: {e:?}"))) } } } #[otm_child_span("tarantool.statement.unprepare")] -fn unprepare(stmt: &mut PreparedStmt) -> Result<(), QueryPlannerError> { +fn unprepare(stmt: &mut PreparedStmt) -> Result<(), SbroadError> { let lua = tarantool::lua_state(); let unprepare_stmt: LuaFunction<_> = lua .get("unprepare") - .ok_or_else(|| QueryPlannerError::LuaError("Lua function `unprepare` not found".into()))?; + .ok_or_else(|| SbroadError::LuaError("Lua function `unprepare` not found".into()))?; match unprepare_stmt.call_with_args::<(), _>(stmt.id()?) { Ok(_) => Ok(()), Err(e) => { error!(Option::from("unprepare"), &format!("{e:?}")); - Err(QueryPlannerError::LuaError(format!("Lua error: {e:?}"))) + Err(SbroadError::LuaError(format!("Lua error: {e:?}"))) } } } #[otm_child_span("tarantool.statement.prepared.read")] -fn read_prepared( - stmt_id: u32, - stmt: &str, - params: &[Value], -) -> Result<Box<dyn Any>, QueryPlannerError> { +fn read_prepared(stmt_id: u32, stmt: &str, params: &[Value]) -> Result<Box<dyn Any>, SbroadError> { let lua = tarantool::lua_state(); let exec_sql: LuaFunction<_> = lua .get("read") - .ok_or_else(|| QueryPlannerError::LuaError("Lua function `read` not found".into()))?; + .ok_or_else(|| SbroadError::LuaError("Lua function `read` not found".into()))?; match exec_sql.call_with_args::<Tuple, _>((stmt_id, stmt, params)) { Ok(v) => Ok(Box::new(v) as Box<dyn Any>), Err(e) => { error!(Option::from("read_prepared"), &format!("{e:?}")); - Err(QueryPlannerError::LuaError(format!("Lua error: {e:?}"))) + Err(SbroadError::LuaError(format!("Lua error: {e:?}"))) } } } #[otm_child_span("tarantool.statement.unprepared.read")] -fn read_unprepared(stmt: &str, params: &[Value]) -> Result<Box<dyn Any>, QueryPlannerError> { +fn read_unprepared(stmt: &str, params: &[Value]) -> Result<Box<dyn Any>, SbroadError> { let lua = tarantool::lua_state(); let exec_sql: LuaFunction<_> = lua .get("read") - .ok_or_else(|| QueryPlannerError::LuaError("Lua function `read` not found".into()))?; + .ok_or_else(|| SbroadError::LuaError("Lua function `read` not found".into()))?; match exec_sql.call_with_args::<Tuple, _>((0, stmt, params)) { Ok(v) => Ok(Box::new(v) as Box<dyn Any>), Err(e) => { error!(Option::from("read_unprepared"), &format!("{e:?}")); - Err(QueryPlannerError::LuaError(format!("Lua error: {e:?}"))) + Err(SbroadError::LuaError(format!("Lua error: {e:?}"))) } } } #[otm_child_span("tarantool.statement.prepared.write")] -fn write_prepared( - stmt_id: u32, - stmt: &str, - params: &[Value], -) -> Result<Box<dyn Any>, QueryPlannerError> { +fn write_prepared(stmt_id: u32, stmt: &str, params: &[Value]) -> Result<Box<dyn Any>, SbroadError> { let lua = tarantool::lua_state(); let exec_sql: LuaFunction<_> = lua .get("write") - .ok_or_else(|| QueryPlannerError::LuaError("Lua function `write` not found".into()))?; + .ok_or_else(|| SbroadError::LuaError("Lua function `write` not found".into()))?; match exec_sql.call_with_args::<Tuple, _>((stmt_id, stmt, params)) { Ok(v) => Ok(Box::new(v) as Box<dyn Any>), Err(e) => { error!(Option::from("write_prepared"), &format!("{e:?}")); - Err(QueryPlannerError::LuaError(format!("Lua error: {e:?}"))) + Err(SbroadError::LuaError(format!("Lua error: {e:?}"))) } } } #[otm_child_span("tarantool.statement.unprepared.write")] -fn write_unprepared(stmt: &str, params: &[Value]) -> Result<Box<dyn Any>, QueryPlannerError> { +fn write_unprepared(stmt: &str, params: &[Value]) -> Result<Box<dyn Any>, SbroadError> { let lua = tarantool::lua_state(); let exec_sql: LuaFunction<_> = lua .get("write") - .ok_or_else(|| QueryPlannerError::LuaError("Lua function `write` not found".into()))?; + .ok_or_else(|| SbroadError::LuaError("Lua function `write` not found".into()))?; match exec_sql.call_with_args::<Tuple, _>((0, stmt, params)) { Ok(v) => Ok(Box::new(v) as Box<dyn Any>), Err(e) => { error!(Option::from("write_unprepared"), &format!("{e:?}")); - Err(QueryPlannerError::LuaError(format!("Lua error: {e:?}"))) + Err(SbroadError::LuaError(format!("Lua error: {e:?}"))) } } } diff --git a/sbroad-cartridge/test_app/test/data/config.yml b/sbroad-cartridge/test_app/test/data/config.yml index 182b10a171..773f37221f 100644 --- a/sbroad-cartridge/test_app/test/data/config.yml +++ b/sbroad-cartridge/test_app/test/data/config.yml @@ -867,3 +867,4 @@ schema: is_nullable: true name: bucket_id type: TREE + diff --git a/sbroad-cartridge/test_app/test/integration/api_test.lua b/sbroad-cartridge/test_app/test/integration/api_test.lua index 4e6c169704..28d7c5476e 100644 --- a/sbroad-cartridge/test_app/test/integration/api_test.lua +++ b/sbroad-cartridge/test_app/test/integration/api_test.lua @@ -1,7 +1,9 @@ local t = require('luatest') +local datetime = require("datetime") local g = t.group('integration_api') local helper = require('test.helper.cluster_no_replication') +local config_handler = require('test.helper.config_handler') local cluster = nil g.before_all(function() @@ -81,6 +83,11 @@ g.after_each( end ) +g.after_test("test_unsupported_column", function() + local default_config = config_handler.get_init_config(helper.root) + cluster:upload_config(default_config) +end) + g.after_all(function() helper.stop_test_cluster() end) @@ -159,9 +166,13 @@ g.test_bucket_id_calculation = function() t.assert_equals(r, nil) t.assert_str_contains(tostring(err), [[expected to have 3 filed(s), got 5]]) + -- luacheck: max line length 150 r, err = api:call("sbroad.calculate_bucket_id", { { id = 1 }, "testing_space" }) t.assert_equals(r, nil) - t.assert_str_contains(tostring(err), [[Missing quoted sharding key column]]) + t.assert_equals( + tostring(err), + [["sharding key (quoted) column \"\\\"name\\\"\" in the quoted map {\"\\\"id\\\"\": \"id\"} (original map: {\"id\": Integer(1)}) not found"]] + ) r, err = api:call("sbroad.calculate_bucket_id", { { id = 1, "123" }, "testing_space" }) t.assert_equals(r, nil) @@ -176,7 +187,82 @@ g.test_incorrect_query = function() local api = cluster:server("api-1").net_box local _, err = api:call("sbroad.execute", { [[SELECT * FROM "testing_space" INNER JOIN "testing_space"]], {} }) - t.assert_str_contains(tostring(err), "Parsing error") + t.assert_str_contains(tostring(err), "parsing error") +end + +g.test_query_errored = function() + local api = cluster:server("api-1").net_box + + local _, err = api:call("sbroad.execute", { [[SELECT * FROM "NotFoundSpace"]], {} }) + t.assert_str_contains(tostring(err), "\"space \\\"NotFoundSpace\\\" not found\"") + + -- luacheck: max line length 140 + local _, err = api:call("sbroad.execute", { [[SELECT "NotFoundColumn" FROM "testing_space"]], {} }) + t.assert_equals(tostring(err), [["column with name [\"\\\"NotFoundColumn\\\"\"] not found"]]) + + local invalid_type_param = datetime.new{ + nsec = 123456789, + sec = 20, + min = 25, + hour = 18, + day = 20, + month = 8, + year = 2021, + tzoffset = 180 + } + + local _, err = api:call("sbroad.execute", { [[SELECT * FROM "testing_space" where "id" = ?]], {invalid_type_param} }) + t.assert_equals( + tostring(err), + "\"pattern with parameters parsing error: Decode(Syntax(\\\"data did not match any variant of untagged enum EncodedValue\\\"))\"" + ) + + -- check err when params lenght is less then amount of sign `?` + local _, err = api:call("sbroad.execute", { [[SELECT * FROM "testing_space" where "id" = ?]], {} }) + t.assert_equals( + tostring(err), + "\"invalid node: parameter node does not refer to an expression\"" + ) +end + +g.test_unsupported_column = function() + local api = cluster:server("api-1").net_box + + local config = cluster:download_config() + local space_with_unsupported_column = { + format = { + { type = "integer", name = "id", is_nullable = false }, + { type = "datetime", name = "unsupported_column", is_nullable = false }, + { type = "unsigned", name = "bucket_id", is_nullable = true }, + }, + temporary = false, + engine = "memtx", + is_local = false, + sharding_key = { "id" }, + indexes = { + { + unique = true, + parts = {{ path = "id", type = "integer", is_nullable = false}}, + name = "id", + type = "TREE" + }, + { + unique = false, + parts = { { path = "bucket_id", type = "unsigned", is_nullable = true } }, + name = "bucket_id", + type = "TREE" + } + } + } + + config["schema"]["spaces"]["space_with_unsupported_column"] = space_with_unsupported_column + cluster:upload_config(config) + + local _, err = api:call("sbroad.execute", { [[SELECT * FROM "space_with_unsupported_column"]], {} }) + t.assert_str_contains( + tostring(err), + "type datetime not implemented" + ) end g.test_join_query_is_valid = function() diff --git a/sbroad-cartridge/test_app/test/integration/validate_schema_test.lua b/sbroad-cartridge/test_app/test/integration/validate_schema_test.lua index 785c365653..3a31fe2614 100644 --- a/sbroad-cartridge/test_app/test/integration/validate_schema_test.lua +++ b/sbroad-cartridge/test_app/test/integration/validate_schema_test.lua @@ -73,5 +73,5 @@ g.test_schema_invalid = function () local api = helper.cluster:server("api-1").net_box local _, err = api:call("sbroad.execute", { [[select * from "t"]], {}}) - t.assert_str_contains(tostring(err), "Failed to get configuration: type `map` not implemented") + t.assert_str_contains(tostring(err), "Failed to get configuration: type map not implemented") end diff --git a/sbroad-core/src/backend/sql/ir.rs b/sbroad-core/src/backend/sql/ir.rs index 3420580c30..9def19f811 100644 --- a/sbroad-core/src/backend/sql/ir.rs +++ b/sbroad-core/src/backend/sql/ir.rs @@ -7,7 +7,7 @@ use tarantool::tlua::{self, Push}; use tarantool::tuple::{FunctionArgs, Tuple}; use crate::debug; -use crate::errors::QueryPlannerError; +use crate::errors::{Action, Entity, SbroadError}; use crate::executor::bucket::Buckets; use crate::executor::ir::ExecutionPlan; use crate::ir::expression::Expression; @@ -37,19 +37,19 @@ impl PartialEq for PatternWithParams { } impl TryFrom<FunctionArgs> for PatternWithParams { - type Error = QueryPlannerError; + type Error = SbroadError; fn try_from(value: FunctionArgs) -> Result<Self, Self::Error> { debug!( Option::from("argument parsing"), - &format!("Query parameters: {:?}", value), + &format!("Query parameters: {value:?}"), ); match Tuple::from(value).decode::<EncodedPatternWithParams>() { Ok(encoded) => Ok(PatternWithParams::from(encoded)), - Err(e) => Err(QueryPlannerError::CustomError(format!( - "Parsing error (pattern with parameters): {:?}", - e - ))), + Err(e) => Err(SbroadError::ParsingError( + Entity::PatternWithParams, + format!("{e:?}"), + )), } } } @@ -143,7 +143,7 @@ impl ExecutionPlan { &self, nodes: &[&SyntaxData], buckets: &Buckets, - ) -> Result<Vec<Value>, QueryPlannerError> { + ) -> Result<Vec<Value>, SbroadError> { let ir_plan = self.get_ir_plan(); let mut params: Vec<Value> = Vec::new(); @@ -154,10 +154,10 @@ impl ExecutionPlan { if let Expression::Constant { value, .. } = value { params.push(value.clone()); } else { - return Err(QueryPlannerError::CustomError(format!( - "Parameter {:?} is not a constant", - value - ))); + return Err(SbroadError::Invalid( + Entity::Expression, + Some(format!("parameter {value:?} is not a constant")), + )); } } SyntaxData::VTable(motion_id) => { @@ -185,7 +185,7 @@ impl ExecutionPlan { &self, nodes: &[&SyntaxData], buckets: &Buckets, - ) -> Result<PatternWithParams, QueryPlannerError> { + ) -> Result<PatternWithParams, SbroadError> { let (sql, params) = child_span("\"syntax.ordered.sql\"", || { let mut params: Vec<Value> = Vec::new(); @@ -242,8 +242,11 @@ impl ExecutionPlan { let node = ir_plan.get_node(*id)?; match node { Node::Parameter => { - return Err(QueryPlannerError::CustomError( - "Parameters are not supported in the generated SQL".into(), + return Err(SbroadError::Unsupported( + Entity::Node, + Some( + "Parameters are not supported in the generated SQL".into(), + ), )); } Node::Relational(rel) => match rel { @@ -274,9 +277,11 @@ impl ExecutionPlan { | Expression::Unary { .. } => {} Expression::Constant { value, .. } => { write!(sql, "{value}").map_err(|e| { - QueryPlannerError::CustomError(format!( - "Failed to write constant value to SQL: {e}" - )) + SbroadError::FailedTo( + Action::Put, + Some(Entity::Value), + format!("constant value to SQL: {e}"), + ) })?; } Expression::Reference { position, .. } => { @@ -286,7 +291,18 @@ impl ExecutionPlan { if rel_node.is_motion() { if let Ok(vt) = self.get_motion_vtable(*id) { - let alias = (*vt).get_columns().get(*position).map(|column| &column.name).ok_or_else(|| QueryPlannerError::CustomError(format!("Failed to get column name for position {position}")))?; + let alias = (*vt) + .get_columns() + .get(*position) + .map(|column| &column.name) + .ok_or_else(|| { + SbroadError::NotFound( + Entity::Name, + format!( + "for column at position {position}" + ), + ) + })?; if let Some(name) = (*vt).get_alias() { sql.push_str(name); sql.push('.'); @@ -326,10 +342,10 @@ impl ExecutionPlan { if let Expression::Constant { value, .. } = value { params.push(value.clone()); } else { - return Err(QueryPlannerError::CustomError(format!( - "Parameter {:?} is not a constant", - value - ))); + return Err(SbroadError::Invalid( + Entity::Expression, + Some(format!("parameter {value:?} is not a constant")), + )); } } SyntaxData::VTable(motion_id) => { @@ -362,7 +378,9 @@ impl ExecutionPlan { cols(&mut anonymous_col_idx_base), values ) - .map_err(|e| QueryPlannerError::CustomError(e.to_string()))?; + .map_err(|e| { + SbroadError::Invalid(Entity::VirtualTable, Some(e.to_string())) + })?; } else { let values = tuples .iter() @@ -379,10 +397,11 @@ impl ExecutionPlan { values ) .map_err(|e| { - QueryPlannerError::CustomError(format!( - "Failed to generate SQL for VTable: {}", - e - )) + SbroadError::FailedTo( + Action::Build, + None, + format!("SQL for VTable: {e}"), + ) })?; for t in tuples { @@ -404,7 +423,7 @@ impl ExecutionPlan { /// /// # Errors /// - If the subtree top is not a relational node. - pub fn subtree_modifies_data(&self, top_id: usize) -> Result<bool, QueryPlannerError> { + pub fn subtree_modifies_data(&self, top_id: usize) -> Result<bool, SbroadError> { // Tarantool doesn't support `INSERT`, `UPDATE` and `DELETE` statements // with `RETURNING` clause. That is why it is enough to check if the top // node is a data modification statement or not. diff --git a/sbroad-core/src/backend/sql/tree.rs b/sbroad-core/src/backend/sql/tree.rs index 63e0d8347d..e16e65bb4a 100644 --- a/sbroad-core/src/backend/sql/tree.rs +++ b/sbroad-core/src/backend/sql/tree.rs @@ -5,7 +5,7 @@ use std::mem::take; use serde::{Deserialize, Serialize}; use traversal::DftPost; -use crate::errors::QueryPlannerError; +use crate::errors::{Action, Entity, SbroadError}; use crate::executor::ir::ExecutionPlan; use crate::ir::expression::Expression; use crate::ir::operator::{Bool, Relational}; @@ -148,11 +148,12 @@ impl SyntaxNode { } } - fn left_id_or_err(&self) -> Result<usize, QueryPlannerError> { + fn left_id_or_err(&self) -> Result<usize, SbroadError> { match self.left { Some(id) => Ok(id), - None => Err(QueryPlannerError::CustomError( - "Left node is not set.".into(), + None => Err(SbroadError::Invalid( + Entity::Node, + Some("left node is not set.".into()), )), } } @@ -178,13 +179,13 @@ impl SyntaxNodes { /// /// # Errors /// - sub-query in plan tree is invalid - fn add_sq(&mut self, rel: &Relational, id: usize) -> Result<usize, QueryPlannerError> { + fn add_sq(&mut self, rel: &Relational, id: usize) -> Result<usize, SbroadError> { if let Relational::ScanSubQuery { children, alias, .. } = rel { let right_id = *children.first().ok_or_else(|| { - QueryPlannerError::CustomError("Sub-query has no children.".into()) + SbroadError::UnexpectedNumberOfValues("Sub-query has no children.".into()) })?; let mut children: Vec<usize> = vec![ self.push_syntax_node(SyntaxNode::new_open()), @@ -197,8 +198,9 @@ impl SyntaxNodes { let sn = SyntaxNode::new_pointer(id, None, children); Ok(self.push_syntax_node(sn)) } else { - Err(QueryPlannerError::CustomError( - "Current node is not a sub-query".into(), + Err(SbroadError::Invalid( + Entity::SyntaxNode, + Some("current node is not a sub-query".into()), )) } } @@ -206,12 +208,18 @@ impl SyntaxNodes { /// Construct syntax nodes from the YAML file. /// /// # Errors - /// Returns `QueryPlannerError` when the YAML nodes arena is invalid. + /// Returns `SbroadError` when the YAML nodes arena is invalid. #[allow(dead_code)] - pub fn from_yaml(s: &str) -> Result<Self, QueryPlannerError> { + pub fn from_yaml(s: &str) -> Result<Self, SbroadError> { let nodes: SyntaxNodes = match serde_yaml::from_str(s) { Ok(p) => p, - Err(_) => return Err(QueryPlannerError::Serialization), + Err(e) => { + return Err(SbroadError::FailedTo( + Action::Serialize, + Some(Entity::SyntaxNodes), + format!("{e:?}"), + )) + } }; Ok(nodes) } @@ -220,29 +228,34 @@ impl SyntaxNodes { /// /// # Errors /// - current node is invalid (doesn't exist in arena) - pub fn get_syntax_node(&self, id: usize) -> Result<&SyntaxNode, QueryPlannerError> { - self.arena.get(id).ok_or(QueryPlannerError::InvalidNode) + pub fn get_syntax_node(&self, id: usize) -> Result<&SyntaxNode, SbroadError> { + self.arena.get(id).ok_or_else(|| { + SbroadError::NotFound(Entity::Node, format!("from arena with index {id}")) + }) } /// Get a mutable syntax node from arena /// /// # Errors /// - current node is invalid (doesn't exist in arena) - pub fn get_mut_syntax_node(&mut self, id: usize) -> Result<&mut SyntaxNode, QueryPlannerError> { - self.arena.get_mut(id).ok_or(QueryPlannerError::InvalidNode) + pub fn get_mut_syntax_node(&mut self, id: usize) -> Result<&mut SyntaxNode, SbroadError> { + self.arena.get_mut(id).ok_or_else(|| { + SbroadError::NotFound( + Entity::Node, + format!("(mutable) from arena with index {id}"), + ) + }) } /// Get syntax node id by the plan node's one /// /// # Errors /// - nothing was found - fn get_syntax_node_id(&self, plan_id: usize) -> Result<usize, QueryPlannerError> { - self.map.get(&plan_id).copied().ok_or_else(|| { - QueryPlannerError::CustomError(format!( - "Current plan node ({}) is absent in the map", - plan_id - )) - }) + fn get_syntax_node_id(&self, plan_id: usize) -> Result<usize, SbroadError> { + self.map + .get(&plan_id) + .copied() + .ok_or_else(|| SbroadError::NotFound(Entity::Node, format!("({plan_id}) in the map"))) } /// Push a new syntax node to arena @@ -313,7 +326,7 @@ impl Select { parent: Option<usize>, branch: Option<Branch>, id: usize, - ) -> Result<Option<Select>, QueryPlannerError> { + ) -> Result<Option<Select>, SbroadError> { let sn = sp.nodes.get_syntax_node(id)?; // Expecting projection // projection -> ... @@ -374,7 +387,13 @@ impl Select { })); } _ => { - return Err(QueryPlannerError::InvalidPlan); + return Err(SbroadError::Invalid( + Entity::Plan, + Some( + "current node must be InnerJoin, ScanSubQuery or ScanRelation" + .into(), + ), + )); } } } @@ -414,9 +433,17 @@ impl Select { return Ok(Some(select)); } } - _ => return Err(QueryPlannerError::InvalidPlan), + _ => { + return Err(SbroadError::Invalid( + Entity::Plan, + Some("current node must be Selection, ScanRelation, or InnerJoin".into()), + )) + } } - Err(QueryPlannerError::InvalidPlan) + Err(SbroadError::Invalid( + Entity::Plan, + Some("invalid combination of the select command".into()), + )) } } @@ -438,7 +465,7 @@ impl<'p> SyntaxPlan<'p> { /// # Errors /// - Failed to translate an IR plan node to a syntax node. #[allow(clippy::too_many_lines)] - pub fn add_plan_node(&mut self, id: usize) -> Result<usize, QueryPlannerError> { + pub fn add_plan_node(&mut self, id: usize) -> Result<usize, SbroadError> { let ir_plan = self.plan.get_ir_plan(); let node = ir_plan.get_node(id)?; match node { @@ -456,12 +483,13 @@ impl<'p> SyntaxPlan<'p> { let row = ir_plan.get_expression_node(*output)?; let aliases: &[usize] = row.get_row_list()?; - let get_col_sn = |col_pos: &usize| -> Result<SyntaxNode, QueryPlannerError> { + let get_col_sn = |col_pos: &usize| -> Result<SyntaxNode, SbroadError> { let alias_id = *aliases.get(*col_pos).ok_or_else(|| { - QueryPlannerError::CustomError(format!( - "Failed to get insert output column at position {}", - col_pos, - )) + SbroadError::FailedTo( + Action::Get, + None, + format!("insert output column at position {col_pos}"), + ) })?; let alias = ir_plan.get_expression_node(alias_id)?; if let Expression::Alias { child, .. } = alias { @@ -469,13 +497,15 @@ impl<'p> SyntaxPlan<'p> { if let Expression::Reference { .. } = col_ref { Ok(SyntaxNode::new_pointer(*child, None, vec![])) } else { - Err(QueryPlannerError::CustomError( - "Expected a reference expression".into(), + Err(SbroadError::Invalid( + Entity::Expression, + Some("expected a reference expression".into()), )) } } else { - Err(QueryPlannerError::CustomError( - "Expected an alias expression".into(), + Err(SbroadError::Invalid( + Entity::Expression, + Some("expected an alias expression".into()), )) } }; @@ -492,8 +522,9 @@ impl<'p> SyntaxPlan<'p> { } if children.is_empty() { - return Err(QueryPlannerError::CustomError( - "Insert node has no children".into(), + return Err(SbroadError::Invalid( + Entity::Node, + Some("insert node has no children".into()), )); } nodes.reserve(children.len()); @@ -509,13 +540,12 @@ impl<'p> SyntaxPlan<'p> { .. } => { let left_id = *children.first().ok_or_else(|| { - QueryPlannerError::CustomError( - "Inner join doesn't have a left child.".into(), - ) + SbroadError::UnexpectedNumberOfValues("Inner Join has no children.".into()) })?; let right_id = *children.get(1).ok_or_else(|| { - QueryPlannerError::CustomError( - "Inner join doesn't have a right child.".into(), + SbroadError::NotFound( + Entity::Node, + "that is Inner Join right child.".into(), ) })?; let condition_id = match self.snapshot { @@ -541,7 +571,7 @@ impl<'p> SyntaxPlan<'p> { children, output, .. } => { let left_id = *children.first().ok_or_else(|| { - QueryPlannerError::CustomError("Projection has no children.".into()) + SbroadError::UnexpectedNumberOfValues("Projection has no children.".into()) })?; // We don't need the row node itself, only its children. // Otherwise we'll produce redundant parentheses between @@ -564,14 +594,14 @@ impl<'p> SyntaxPlan<'p> { return Ok(self.nodes.push_syntax_node(sn)); } } - Err(QueryPlannerError::InvalidPlan) + Err(SbroadError::Invalid(Entity::Node, None)) } Relational::ScanSubQuery { .. } => self.nodes.add_sq(rel, id), Relational::Selection { children, filter, .. } => { let left_id = *children.first().ok_or_else(|| { - QueryPlannerError::CustomError("Selection has no children.".into()) + SbroadError::UnexpectedNumberOfValues("Selection has no children.".into()) })?; let filter_id = match self.snapshot { Snapshot::Latest => *filter, @@ -589,13 +619,14 @@ impl<'p> SyntaxPlan<'p> { } Relational::Except { children, .. } | Relational::UnionAll { children, .. } => { let left_id = *children.first().ok_or_else(|| { - QueryPlannerError::CustomError( - "Union/except doesn't have a left child.".into(), + SbroadError::UnexpectedNumberOfValues( + "Union/Except has no children.".into(), ) })?; let right_id = *children.get(1).ok_or_else(|| { - QueryPlannerError::CustomError( - "Union/except doesn't have a right child.".into(), + SbroadError::NotFound( + Entity::Node, + "that is Union/Except right child.".into(), ) })?; let sn = SyntaxNode::new_pointer( @@ -761,7 +792,7 @@ impl<'p> SyntaxPlan<'p> { let sn = SyntaxNode::new_pointer(id, None, nodes); return Ok(self.nodes.push_syntax_node(sn)); } - Err(QueryPlannerError::InvalidRow) + Err(SbroadError::Invalid(Entity::Expression, None)) } Expression::Bool { left, right, op, .. @@ -819,7 +850,7 @@ impl<'p> SyntaxPlan<'p> { /// /// # Errors /// - plan node is invalid - pub fn get_plan_node(&self, data: &SyntaxData) -> Result<Option<&Node>, QueryPlannerError> { + pub fn get_plan_node(&self, data: &SyntaxData) -> Result<Option<&Node>, SbroadError> { if let SyntaxData::PlanId(id) = data { Ok(Some(self.plan.get_ir_plan().get_node(*id)?)) } else { @@ -832,16 +863,20 @@ impl<'p> SyntaxPlan<'p> { /// # Errors /// - plan node is invalid /// - syntax tree node doesn't have a plan node - pub fn plan_node_or_err(&self, data: &SyntaxData) -> Result<&Node, QueryPlannerError> { - self.get_plan_node(data)? - .ok_or_else(|| QueryPlannerError::CustomError("Plan node is not found.".into())) + pub fn plan_node_or_err(&self, data: &SyntaxData) -> Result<&Node, SbroadError> { + self.get_plan_node(data)?.ok_or_else(|| { + SbroadError::Invalid( + Entity::SyntaxPlan, + Some("Plan node is not found in syntax tree".into()), + ) + }) } /// Set top of the tree. /// /// # Errors /// - top is invalid node - pub fn set_top(&mut self, top: usize) -> Result<(), QueryPlannerError> { + pub fn set_top(&mut self, top: usize) -> Result<(), SbroadError> { self.nodes.get_syntax_node(top)?; self.top = Some(top); Ok(()) @@ -852,13 +887,14 @@ impl<'p> SyntaxPlan<'p> { /// # Errors /// - top is not set /// - top is not a valid node - pub fn get_top(&self) -> Result<usize, QueryPlannerError> { + pub fn get_top(&self) -> Result<usize, SbroadError> { if let Some(top) = self.top { self.nodes.get_syntax_node(top)?; Ok(top) } else { - Err(QueryPlannerError::CustomError( - "Syntax tree has an invalid top.".into(), + Err(SbroadError::Invalid( + Entity::SyntaxPlan, + Some("Syntax tree has an invalid top.".into()), )) } } @@ -868,7 +904,7 @@ impl<'p> SyntaxPlan<'p> { /// /// # Errors /// - got unexpected nodes under projection - fn gather_selects(&self) -> Result<Option<Vec<Select>>, QueryPlannerError> { + fn gather_selects(&self) -> Result<Option<Vec<Select>>, SbroadError> { let mut selects: Vec<Select> = Vec::new(); let top = self.get_top()?; for (pos, node) in self.nodes.arena.iter().enumerate() { @@ -903,7 +939,7 @@ impl<'p> SyntaxPlan<'p> { /// /// # Errors /// - got unexpected nodes under some projection - fn move_proj_under_scan(&mut self) -> Result<(), QueryPlannerError> { + fn move_proj_under_scan(&mut self) -> Result<(), SbroadError> { let selects = self.gather_selects()?; if let Some(selects) = selects { for select in &selects { @@ -933,7 +969,7 @@ impl<'p> SyntaxPlan<'p> { plan: &'p ExecutionPlan, top: usize, snapshot: Snapshot, - ) -> Result<Self, QueryPlannerError> { + ) -> Result<Self, SbroadError> { let mut sp = SyntaxPlan::empty(plan); sp.snapshot = snapshot.clone(); let ir_plan = plan.get_ir_plan(); @@ -972,7 +1008,7 @@ impl<'p> SyntaxPlan<'p> { /// /// # Errors /// - select nodes (parent, scan, projection, selection) are invalid - fn reorder(&mut self, select: &Select) -> Result<(), QueryPlannerError> { + fn reorder(&mut self, select: &Select) -> Result<(), SbroadError> { // Move projection under scan. let mut proj = self.nodes.get_mut_syntax_node(select.proj)?; proj.left = None; @@ -1022,14 +1058,19 @@ impl<'p> SyntaxPlan<'p> { } } if !found { - return Err(QueryPlannerError::CustomError( - "Parent node doesn't contain projection in its right children".into(), + return Err(SbroadError::Invalid( + Entity::SyntaxNode, + Some( + "Parent node doesn't contain projection in its right children" + .into(), + ), )); } } None => { - return Err(QueryPlannerError::CustomError( - "Selection structure is in inconsistent state.".into(), + return Err(SbroadError::Invalid( + Entity::SyntaxNode, + Some("Selection structure is in inconsistent state.".into()), )) } } @@ -1056,16 +1097,14 @@ impl OrderedSyntaxNodes { /// /// # Errors /// - internal error (positions point to invalid nodes in the arena) - pub fn to_syntax_data(&self) -> Result<Vec<&SyntaxData>, QueryPlannerError> { + pub fn to_syntax_data(&self) -> Result<Vec<&SyntaxData>, SbroadError> { let mut result: Vec<&SyntaxData> = Vec::with_capacity(self.positions.len()); for id in &self.positions { result.push( &self .arena .get(*id) - .ok_or_else(|| { - QueryPlannerError::CustomError(format!("Invalid syntax node id: {id}")) - })? + .ok_or_else(|| SbroadError::NotFound(Entity::SyntaxNode, format!("(id {id})")))? .data, ); } @@ -1074,7 +1113,7 @@ impl OrderedSyntaxNodes { } impl TryFrom<SyntaxPlan<'_>> for OrderedSyntaxNodes { - type Error = QueryPlannerError; + type Error = SbroadError; #[otm_child_span("syntax.ordered")] fn try_from(mut sp: SyntaxPlan) -> Result<Self, Self::Error> { diff --git a/sbroad-core/src/errors.rs b/sbroad-core/src/errors.rs index 2843dba55c..a079592054 100644 --- a/sbroad-core/src/errors.rs +++ b/sbroad-core/src/errors.rs @@ -1,140 +1,234 @@ -use std::fmt; - use serde::Serialize; +use std::fmt; -const BUCKET_ID_ERROR: &str = "field doesn't contains sharding key value"; -const DUPLICATE_COLUMN_ERROR: &str = "duplicate column"; const DO_SKIP: &str = "do skip"; -const EMPTY_PLAN_RELATION: &str = "empty plan relations"; -const EMPTY_RESULT: &str = "empty result"; -const INCORRECT_BUCKET_ID_ERROR: &str = "incorrect bucket id"; -const INVALID_AST: &str = "invalid AST"; -const INVALID_AST_CONDITION_NODE: &str = "Invalid selection condition part"; -const INVALID_AST_SCAN_NODE: &str = "Invalid scan node"; -const INVALID_AST_SELECTION_NODE: &str = "Selection node not found"; -const INVALID_AST_SUBQUERY_NODE: &str = "Invalid subquery node"; -const INVALID_AST_TOP_NODE: &str = "Top node not found"; -const INVALID_BOOL_ERROR: &str = "invalid boolean"; -const INVALID_CONSTANT: &str = "invalid constant"; -const INVALID_COLUMN_NAME: &str = "invalid column name"; -const INVALID_CLUSTER_SCHEMA: &str = "cluster schema is invalid"; -const INVALID_INPUT: &str = "invalid input"; -const INVALID_NAME_ERROR: &str = "invalid name"; -const INVALID_NODE: &str = "invalid node"; -const INVALID_NUMBER_ERROR: &str = "invalid number"; -const INVALID_PLAN_ERROR: &str = "invalid plan"; -const INVALID_REFERENCE: &str = "invalid reference"; -const INVALID_RELATION_ERROR: &str = "invalid relation"; -const INVALID_ROW_ERROR: &str = "invalid row"; -const INVALID_SCHEMA_SPACES: &str = "not found spaces in schema"; -const INVALID_SHARDING_KEY_ERROR: &str = "invalid sharding key"; -const INVALID_SPACE_NAME: &str = "invalid space name"; -const INVALID_SUBQUERY: &str = "invalid sub-query"; -const NOT_EQUAL_ROWS: &str = "not equal rows"; -const QUERY_NOT_IMPLEMENTED: &str = "query wasn't implemented"; -const REDUNDANT_TRANSFORMATION: &str = "redundant transformation"; -const REQUIRE_MOTION: &str = "require motion"; -const SERIALIZATION_ERROR: &str = "serialization"; -const SIMPLE_QUERY_ERROR: &str = "query doesn't simple"; -const SIMPLE_UNION_QUERY_ERROR: &str = "query doesn't simple union"; -const SPACE_NOT_FOUND: &str = "space not found"; -const SPACE_FORMAT_NOT_FOUND: &str = "space format not found"; -const UNINITIALIZED_DISTRIBUTION: &str = "uninitialized distribution"; -const UNSUPPORTED_TYPE_IR_VALUE: &str = "unsupported type ir value"; -const VALUE_OUT_OF_RANGE_ERROR: &str = "value out of range"; +/// Reason or object of errors. +#[derive(Clone, Debug, PartialEq, Eq, Serialize)] +pub enum Entity { + /// corresponding to enum Args + Args, + /// corresponding to struct AbstractSyntaxTree + AST, + /// corresponding to struct Buckets + Buckets, + /// general variant for cache + Cache, + /// corresponding to struct Chain + Chain, + /// cartridge cluster schema + ClusterSchema, + /// general variant + Column, + /// corresponds to enum Distribution + Distribution, + /// tarantool distribution key + DistributionKey, + /// corresponds to enum Expression + Expression, + /// corresponds to metadata field of struct ProducerResult + Metadata, + /// corresponds to enum MotionPolicy + Motion, + /// tarantool msgpack + MsgPack, + /// general variant for Name of some object + Name, + /// variant for node of tree + Node, + /// SQL operator + Operator, + /// corresponds to struct PatternWithParams + PatternWithParams, + /// corresponds to struct Plan + Plan, + /// corresponds to struct ProducerResult + ProducerResult, + /// SQL query + Query, + /// corresponds to enum Relational + Relational, + /// corresponds to struct RequiredData + RequiredData, + /// parser rule + Rule, + /// corresponds to struct RouterRuntime + Runtime, + /// sharding key of tarantool space + ShardingKey, + /// tarantool space + Space, + /// corresponds to Function structs + SQLFunction, + /// corresponds to struct Statement + Statement, + /// SQL sub-query + SubQuery, + /// sub-tree of the Plan + SubTree, + /// corresponds to sctruct SyntaxNode + SyntaxNode, + /// corresponds to struct SyntaxNodes + SyntaxNodes, + /// corresponds to struct SyntaxPlan + SyntaxPlan, + /// corresponds to struct Table + Table, + /// corresponds to struct Target + Target, + /// general variant for tuple + Tuple, + /// general variant for type of some object + Type, + /// general variant for value of some object + Value, + /// corresponds to struct VirtualTable + VirtualTable, +} + +impl fmt::Display for Entity { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let p = match self { + Entity::Args => "args".to_string(), + Entity::AST => "AST".to_string(), + Entity::Buckets => "buckets".to_string(), + Entity::Cache => "cache".to_string(), + Entity::Chain => "chain".to_string(), + Entity::ClusterSchema => "cluster schema".to_string(), + Entity::Column => "column".to_string(), + Entity::Distribution => "distribution".to_string(), + Entity::DistributionKey => "distribution key".to_string(), + Entity::Expression => "expression".to_string(), + Entity::Metadata => "metadata".to_string(), + Entity::Motion => "motion".to_string(), + Entity::MsgPack => "msgpack".to_string(), + Entity::Name => "name".to_string(), + Entity::Node => "node".to_string(), + Entity::Operator => "operator".to_string(), + Entity::PatternWithParams => "pattern with parameters".to_string(), + Entity::Plan => "plan".to_string(), + Entity::ProducerResult => "producer result".to_string(), + Entity::Query => "query".to_string(), + Entity::Relational => "relational".to_string(), + Entity::RequiredData => "required data".to_string(), + Entity::Rule => "rule".to_string(), + Entity::Runtime => "runtime".to_string(), + Entity::ShardingKey => "sharding key".to_string(), + Entity::Space => "space".to_string(), + Entity::SQLFunction => "SQL function".to_string(), + Entity::Statement => "statement".to_string(), + Entity::SubQuery => "sub-query plan subtree".to_string(), + Entity::SubTree => "execution plan subtree".to_string(), + Entity::SyntaxNode => "syntax node".to_string(), + Entity::SyntaxNodes => "syntax nodes".to_string(), + Entity::SyntaxPlan => "syntax plan".to_string(), + Entity::Table => "table".to_string(), + Entity::Target => "target".to_string(), + Entity::Tuple => "tuple".to_string(), + Entity::Type => "type".to_string(), + Entity::Value => "value".to_string(), + Entity::VirtualTable => "virtual table".to_string(), + }; + write!(f, "{p}") + } +} + +/// Action that failed +#[derive(Clone, Debug, PartialEq, Eq, Serialize)] +pub enum Action { + Add, + Borrow, + Build, + Clear, + Create, + Decode, + Deserialize, + Get, + Insert, + Put, + Retrieve, + Serialize, +} + +impl fmt::Display for Action { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let p = match self { + Action::Add => "add".to_string(), + Action::Borrow => "borrow".to_string(), + Action::Build => "build".to_string(), + Action::Clear => "clear".to_string(), + Action::Create => "create".to_string(), + Action::Decode => "decode".to_string(), + Action::Deserialize => "deserialize".to_string(), + Action::Get => "get".to_string(), + Action::Insert => "insert".to_string(), + Action::Put => "put".to_string(), + Action::Retrieve => "retrieve".to_string(), + Action::Serialize => "serialize".to_string(), + }; + write!(f, "{p}") + } +} + +/// Types of error #[derive(Clone, Debug, PartialEq, Eq, Serialize)] -pub enum QueryPlannerError { - BucketIdError, - CustomError(String), +pub enum SbroadError { + /// DoSkip is a special case of an error - nothing bad had happened, the target node doesn't contain + /// anything interesting for us, skip it without any serious error. DoSkip, - DuplicateColumn, - EmptyPlanRelations, - EmptyResult, - IncorrectBucketIdError, - InvalidAst, - InvalidAstConditionNode, - InvalidAstScanNode, - InvalidAstSelectionNode, - InvalidAstSubQueryNode, - InvalidAstTopNode, - InvalidBool, - InvalidConstant, - InvalidColumnName, - InvalidClusterSchema, - InvalidInput, - InvalidName, - InvalidNode, - InvalidNumber, - InvalidPlan, - InvalidReference, - InvalidRelation, - InvalidRow, - InvalidSchemaSpaces, - InvalidShardingKey, - InvalidSpaceName, - InvalidSubQuery, + /// Some value that is considered to be unique is duplicated. + /// Second param represents description. + DuplicatedValue(String), + /// Process of Action variant failed. + /// Second param represents object of action. + /// Third param represents reason of fail. + FailedTo(Action, Option<Entity>, String), + /// Object is invalid. + /// Second param represents description and can be empty (None). + Invalid(Entity, Option<String>), LuaError(String), - NotEqualRows, - QueryNotImplemented, - RequireMotion, - RedundantTransformation, - Serialization, - SimpleQueryError, - SimpleUnionQueryError, - SpaceFormatNotFound, - SpaceNotFound, - UninitializedDistribution, - ValueOutOfRange, - UnsupportedValueType, + /// Object not found. + /// Second param represents description or name that let to identify object. + NotFound(Entity, String), + /// Object is not implemented yet. + /// Second param represents description or name. + NotImplemented(Entity, String), + /// Error raised by object parsing. + /// Second param represents error description. + ParsingError(Entity, String), + /// Unexpected number of values (list length etc.). + /// Second param is information what was expected and what got. + UnexpectedNumberOfValues(String), + /// Object is not supported. + /// Second param represents description or name that let to identify object. + /// and can be empty (None). + Unsupported(Entity, Option<String>), } -impl fmt::Display for QueryPlannerError { +impl fmt::Display for SbroadError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let p = match self { - QueryPlannerError::CustomError(s) => s.as_str(), - QueryPlannerError::BucketIdError => BUCKET_ID_ERROR, - QueryPlannerError::DoSkip => DO_SKIP, - QueryPlannerError::DuplicateColumn => DUPLICATE_COLUMN_ERROR, - QueryPlannerError::EmptyPlanRelations => EMPTY_PLAN_RELATION, - QueryPlannerError::EmptyResult => EMPTY_RESULT, - QueryPlannerError::IncorrectBucketIdError => INCORRECT_BUCKET_ID_ERROR, - QueryPlannerError::InvalidAst => INVALID_AST, - QueryPlannerError::InvalidAstConditionNode => INVALID_AST_CONDITION_NODE, - QueryPlannerError::InvalidAstScanNode => INVALID_AST_SCAN_NODE, - QueryPlannerError::InvalidAstSelectionNode => INVALID_AST_SELECTION_NODE, - QueryPlannerError::InvalidAstSubQueryNode => INVALID_AST_SUBQUERY_NODE, - QueryPlannerError::InvalidAstTopNode => INVALID_AST_TOP_NODE, - QueryPlannerError::InvalidBool => INVALID_BOOL_ERROR, - QueryPlannerError::InvalidConstant => INVALID_CONSTANT, - QueryPlannerError::InvalidColumnName => INVALID_COLUMN_NAME, - QueryPlannerError::InvalidClusterSchema => INVALID_CLUSTER_SCHEMA, - QueryPlannerError::InvalidInput => INVALID_INPUT, - QueryPlannerError::InvalidName => INVALID_NAME_ERROR, - QueryPlannerError::InvalidNode => INVALID_NODE, - QueryPlannerError::InvalidNumber => INVALID_NUMBER_ERROR, - QueryPlannerError::InvalidPlan => INVALID_PLAN_ERROR, - QueryPlannerError::InvalidReference => INVALID_REFERENCE, - QueryPlannerError::InvalidRelation => INVALID_RELATION_ERROR, - QueryPlannerError::InvalidRow => INVALID_ROW_ERROR, - QueryPlannerError::InvalidSchemaSpaces => INVALID_SCHEMA_SPACES, - QueryPlannerError::InvalidShardingKey => INVALID_SHARDING_KEY_ERROR, - QueryPlannerError::InvalidSpaceName => INVALID_SPACE_NAME, - QueryPlannerError::InvalidSubQuery => INVALID_SUBQUERY, - QueryPlannerError::LuaError(e) => e.as_str(), - QueryPlannerError::NotEqualRows => NOT_EQUAL_ROWS, - QueryPlannerError::QueryNotImplemented => QUERY_NOT_IMPLEMENTED, - QueryPlannerError::RedundantTransformation => REDUNDANT_TRANSFORMATION, - QueryPlannerError::RequireMotion => REQUIRE_MOTION, - QueryPlannerError::Serialization => SERIALIZATION_ERROR, - QueryPlannerError::SimpleQueryError => SIMPLE_QUERY_ERROR, - QueryPlannerError::SimpleUnionQueryError => SIMPLE_UNION_QUERY_ERROR, - QueryPlannerError::SpaceFormatNotFound => SPACE_FORMAT_NOT_FOUND, - QueryPlannerError::SpaceNotFound => SPACE_NOT_FOUND, - QueryPlannerError::UninitializedDistribution => UNINITIALIZED_DISTRIBUTION, - QueryPlannerError::ValueOutOfRange => VALUE_OUT_OF_RANGE_ERROR, - QueryPlannerError::UnsupportedValueType => UNSUPPORTED_TYPE_IR_VALUE, + SbroadError::DoSkip => DO_SKIP.to_string(), + SbroadError::DuplicatedValue(s) => format!("duplicated value: {s}"), + SbroadError::FailedTo(a, e, s) => match e { + Some(entity) => format!("failed to {a} {entity}: {s}"), + None => format!("failed to {a} {s}"), + }, + SbroadError::Invalid(e, s) => match s { + Some(msg) => format!("invalid {e}: {msg}"), + None => format!("invalid {e}"), + }, + SbroadError::NotFound(e, s) => format!("{e} {s} not found"), + SbroadError::NotImplemented(e, s) => format!("{e} {s} not implemented"), + SbroadError::ParsingError(e, s) => format!("{e} parsing error: {s}"), + SbroadError::Unsupported(e, s) => match s { + Some(msg) => format!("unsupported {e}: {msg}"), + None => format!("unsupported {e}"), + }, + SbroadError::UnexpectedNumberOfValues(s) => format!("unexpected number of values: {s}"), + SbroadError::LuaError(e) => e.clone(), }; + write!(f, "{p}") } } diff --git a/sbroad-core/src/executor.rs b/sbroad-core/src/executor.rs index 9606c91495..9668a19fb4 100644 --- a/sbroad-core/src/executor.rs +++ b/sbroad-core/src/executor.rs @@ -27,7 +27,7 @@ use std::any::Any; use std::collections::{hash_map::Entry, HashMap}; use std::rc::Rc; -use crate::errors::QueryPlannerError; +use crate::errors::{Action, Entity, SbroadError}; use crate::executor::bucket::Buckets; use crate::executor::engine::Coordinator; use crate::executor::engine::CoordinatorMetadata; @@ -54,7 +54,7 @@ pub mod vtable; impl Plan { /// Apply optimization rules to the plan. - pub(crate) fn optimize(&mut self) -> Result<(), QueryPlannerError> { + pub(crate) fn optimize(&mut self) -> Result<(), SbroadError> { self.replace_in_operator()?; self.split_columns()?; self.set_dnf()?; @@ -93,7 +93,7 @@ where /// - Failed to build IR plan. /// - Failed to apply optimizing transformations to IR plan. #[otm_child_span("query.new")] - pub fn new(coordinator: &'a C, sql: &str, params: Vec<Value>) -> Result<Self, QueryPlannerError> + pub fn new(coordinator: &'a C, sql: &str, params: Vec<Value>) -> Result<Self, SbroadError> where C::Configuration: CoordinatorMetadata, C::Cache: Cache<String, Plan>, @@ -104,7 +104,7 @@ where let mut plan = Plan::new(); let mut cache = ir_cache.try_borrow_mut().map_err(|e| { - QueryPlannerError::CustomError(format!("Failed to create a new query: {e:?}")) + SbroadError::FailedTo(Action::Create, Some(Entity::Query), format!("{e:?}")) })?; if let Some(cached_plan) = cache.get(&key)? { plan = cached_plan.clone(); @@ -151,7 +151,7 @@ where /// - Failed to materialize motion result and build a virtual table. /// - Failed to get plan top. #[otm_child_span("query.dispatch")] - pub fn dispatch(&mut self) -> Result<Box<dyn Any>, QueryPlannerError> { + pub fn dispatch(&mut self) -> Result<Box<dyn Any>, SbroadError> { if self.is_explain() { return self.coordinator.explain_format(self.to_explain()?); } @@ -206,7 +206,7 @@ where &mut self, motion_id: usize, mut vtable: VirtualTable, - ) -> Result<(), QueryPlannerError> { + ) -> Result<(), SbroadError> { let (policy, generation) = if let Relational::Motion { policy, generation, .. } = self @@ -216,8 +216,9 @@ where { (policy.clone(), generation.clone()) } else { - return Err(QueryPlannerError::CustomError( - "Invalid motion node".to_string(), + return Err(SbroadError::Invalid( + Entity::Node, + Some("invalid motion node".to_string()), )); }; if let MotionPolicy::Segment(shard_key) = policy { @@ -249,7 +250,7 @@ where vtable: &mut VirtualTable, sharding_key: &MotionKey, generation: &DataGeneration, - ) -> Result<(), QueryPlannerError> { + ) -> Result<(), SbroadError> { vtable.set_motion_key(sharding_key); let mut index: HashMap<u64, Vec<usize>> = HashMap::new(); @@ -259,10 +260,13 @@ where match target { Target::Reference(col_idx) => { let part = tuple.get(*col_idx).ok_or_else(|| { - QueryPlannerError::CustomError(format!( - "Failed to find a distribution key column {} in the tuple {:?}.", + SbroadError::NotFound( + Entity::DistributionKey, + format!( + "failed to find a distribution key column {} in the tuple {:?}.", pos, tuple - )) + ), + ) })?; shard_key_tuple.push(part); } @@ -309,7 +313,7 @@ where /// /// # Errors /// - Failed to build explain - pub fn to_explain(&self) -> Result<String, QueryPlannerError> { + pub fn to_explain(&self) -> Result<String, SbroadError> { self.exec_plan.get_ir_plan().as_explain() } diff --git a/sbroad-core/src/executor/bucket.rs b/sbroad-core/src/executor/bucket.rs index 6eb7e5e9e8..45ddb92f4a 100644 --- a/sbroad-core/src/executor/bucket.rs +++ b/sbroad-core/src/executor/bucket.rs @@ -2,7 +2,7 @@ use std::collections::HashSet; use traversal::DftPost; -use crate::errors::QueryPlannerError; +use crate::errors::{Action, Entity, SbroadError}; use crate::executor::engine::Coordinator; use crate::executor::Query; use crate::ir::distribution::Distribution; @@ -74,7 +74,7 @@ impl<'a, T> Query<'a, T> where T: Coordinator, { - fn get_buckets_from_expr(&self, expr_id: usize) -> Result<Buckets, QueryPlannerError> { + fn get_buckets_from_expr(&self, expr_id: usize) -> Result<Buckets, SbroadError> { let mut buckets: Vec<Buckets> = Vec::new(); let ir_plan = self.exec_plan.get_ir_plan(); let expr = ir_plan.get_expression_node(expr_id)?; @@ -89,19 +89,23 @@ where for (left_id, right_id) in pairs { let left_expr = ir_plan.get_expression_node(left_id)?; if !left_expr.is_row() { - return Err(QueryPlannerError::CustomError(format!( - "Left side of equality expression is not a row: {:?}", - left_expr - ))); + return Err(SbroadError::Invalid( + Entity::Expression, + Some(format!( + "left side of equality expression is not a row: {left_expr:?}" + )), + )); } let right_expr = ir_plan.get_expression_node(right_id)?; let right_columns = if let Expression::Row { list, .. } = right_expr { list.clone() } else { - return Err(QueryPlannerError::CustomError(format!( - "Right side of equality expression is not a row: {:?}", - right_expr - ))); + return Err(SbroadError::Invalid( + Entity::Expression, + Some(format!( + "right side of equality expression is not a row: {right_expr:?}" + )), + )); }; // Get the distribution of the left row. @@ -128,10 +132,10 @@ where for position in &key.positions { let right_column_id = *right_columns.get(*position).ok_or_else(|| { - QueryPlannerError::CustomError(format!( - "Right row does not have column at position {}", - position - )) + SbroadError::NotFound( + Entity::Column, + format!("at position {} for right row", position), + ) })?; let right_column_expr = ir_plan.get_expression_node(right_column_id)?; if let Expression::Constant { .. } = right_column_expr { @@ -162,7 +166,7 @@ where } } - fn get_expression_tree_buckets(&self, expr_id: usize) -> Result<Buckets, QueryPlannerError> { + fn get_expression_tree_buckets(&self, expr_id: usize) -> Result<Buckets, SbroadError> { let ir_plan = self.exec_plan.get_ir_plan(); let chains = ir_plan.get_dnf_chains(expr_id)?; let mut result: Vec<Buckets> = Vec::new(); @@ -196,7 +200,7 @@ where /// - Relational nodes contain invalid children. #[allow(clippy::too_many_lines)] #[otm_child_span("query.bucket.discovery")] - pub fn bucket_discovery(&mut self, top_id: usize) -> Result<Buckets, QueryPlannerError> { + pub fn bucket_discovery(&mut self, top_id: usize) -> Result<Buckets, SbroadError> { let ir_plan = self.exec_plan.get_ir_plan(); // We use a `subtree_iter()` because we need DNF version of the filter/condition // expressions to determine buckets. @@ -236,8 +240,9 @@ where .insert(*output, Buckets::new_filtered(buckets)); } MotionPolicy::Local => { - return Err(QueryPlannerError::CustomError( - "Local motion policy should never appear in the plan".to_string(), + return Err(SbroadError::Invalid( + Entity::Motion, + Some("local motion policy should never appear in the plan".to_string()), )); } }, @@ -251,7 +256,7 @@ where children, output, .. } => { let child_id = children.first().ok_or_else(|| { - QueryPlannerError::CustomError( + SbroadError::UnexpectedNumberOfValues( "Current node should have exactly one child".to_string(), ) })?; @@ -260,9 +265,10 @@ where .bucket_map .get(&child_rel.output()) .ok_or_else(|| { - QueryPlannerError::CustomError( - "Failed to retrieve buckets of the child from the bucket map." - .to_string(), + SbroadError::FailedTo( + Action::Retrieve, + Some(Entity::Buckets), + "of the child from the bucket map.".to_string(), ) })? .clone(); @@ -281,16 +287,21 @@ where // child's buckets here. let first_rel = self.exec_plan.get_ir_plan().get_relation_node(*first_id)?; - let first_buckets = self.bucket_map.get(&first_rel.output()).ok_or_else(|| { - QueryPlannerError::CustomError( - "Failed to retrieve buckets of the first except child from the bucket map." - .to_string(), - ) - })?.clone(); + let first_buckets = self + .bucket_map + .get(&first_rel.output()) + .ok_or_else(|| { + SbroadError::FailedTo( + Action::Retrieve, + Some(Entity::Buckets), + "of the first except child from the bucket map.".to_string(), + ) + })? + .clone(); self.bucket_map.insert(*output, first_buckets); } else { - return Err(QueryPlannerError::CustomError( - "Current node should have exactly two children".to_string(), + return Err(SbroadError::UnexpectedNumberOfValues( + "current node should have exactly two children".to_string(), )); } } @@ -304,23 +315,28 @@ where self.exec_plan.get_ir_plan().get_relation_node(*first_id)?; let second_rel = self.exec_plan.get_ir_plan().get_relation_node(*second_id)?; - let first_buckets = self.bucket_map.get(&first_rel.output()).ok_or_else(|| { - QueryPlannerError::CustomError( - "Failed to retrieve buckets of the first union all child from the bucket map." - .to_string(), - ) - })?; - let second_buckets = self.bucket_map.get(&second_rel.output()).ok_or_else(|| { - QueryPlannerError::CustomError( - "Failed to retrieve buckets of the second union all child from the bucket map." - .to_string(), - ) - })?; + let first_buckets = + self.bucket_map.get(&first_rel.output()).ok_or_else(|| { + SbroadError::FailedTo( + Action::Retrieve, + Some(Entity::Buckets), + "of the first union all child from the bucket map.".to_string(), + ) + })?; + let second_buckets = + self.bucket_map.get(&second_rel.output()).ok_or_else(|| { + SbroadError::FailedTo( + Action::Retrieve, + Some(Entity::Buckets), + "of the second union all child from the bucket map." + .to_string(), + ) + })?; let buckets = first_buckets.conjunct(second_buckets); self.bucket_map.insert(*output, buckets); } else { - return Err(QueryPlannerError::CustomError( - "Current node should have exactly two children".to_string(), + return Err(SbroadError::UnexpectedNumberOfValues( + "current node should have exactly two children".to_string(), )); } } @@ -333,8 +349,8 @@ where // We need to get the buckets of the child node for the case // when the filter returns no buckets to reduce. let child_id = children.first().ok_or_else(|| { - QueryPlannerError::CustomError( - "Current node should have exactly one child".to_string(), + SbroadError::UnexpectedNumberOfValues( + "current node should have exactly one child".to_string(), ) })?; let child_rel = self.exec_plan.get_ir_plan().get_relation_node(*child_id)?; @@ -342,10 +358,11 @@ where .bucket_map .get(&child_rel.output()) .ok_or_else(|| { - QueryPlannerError::CustomError( - "Failed to retrieve buckets of the selection child from the bucket map." - .to_string(), - ) + SbroadError::FailedTo( + Action::Retrieve, + Some(Entity::Buckets), + "of the selection child from the bucket map.".to_string(), + ) })? .clone(); let output_id = *output; @@ -369,20 +386,22 @@ where .bucket_map .get(&inner_rel.output()) .ok_or_else(|| { - QueryPlannerError::CustomError( - "Failed to retrieve buckets of the inner child from the bucket map." - .to_string(), - ) + SbroadError::FailedTo( + Action::Retrieve, + Some(Entity::Buckets), + "of the inner child from the bucket map.".to_string(), + ) })? .clone(); let outer_buckets = self .bucket_map .get(&outer_rel.output()) .ok_or_else(|| { - QueryPlannerError::CustomError( - "Failed to retrieve buckets of the outer child from the bucket map." - .to_string(), - ) + SbroadError::FailedTo( + Action::Retrieve, + Some(Entity::Buckets), + "of the outer child from the bucket map.".to_string(), + ) })? .clone(); let output_id = *output; @@ -395,8 +414,8 @@ where .disjunct(&filter_buckets), ); } else { - return Err(QueryPlannerError::CustomError( - "Current node should have at least two children".to_string(), + return Err(SbroadError::UnexpectedNumberOfValues( + "current node should have at least two children".to_string(), )); } } @@ -413,9 +432,10 @@ where .bucket_map .get(&top_rel.output()) .ok_or_else(|| { - QueryPlannerError::CustomError( - "Failed to retrieve buckets of the top relation from the bucket map." - .to_string(), + SbroadError::FailedTo( + Action::Retrieve, + Some(Entity::Buckets), + "of the top relation from the bucket map.".to_string(), ) })? .clone(); diff --git a/sbroad-core/src/executor/engine.rs b/sbroad-core/src/executor/engine.rs index 59b63d391a..ec7a9fc2f8 100644 --- a/sbroad-core/src/executor/engine.rs +++ b/sbroad-core/src/executor/engine.rs @@ -7,7 +7,7 @@ use std::cell::RefCell; use std::cmp::Ordering; use std::collections::HashMap; -use crate::errors::QueryPlannerError; +use crate::errors::{Entity, SbroadError}; use crate::executor::bucket::Buckets; use crate::executor::ir::ExecutionPlan; use crate::executor::vtable::VirtualTable; @@ -24,13 +24,13 @@ pub trait CoordinatorMetadata { /// /// # Errors /// - Failed to get table by name from the metadata. - fn get_table_segment(&self, table_name: &str) -> Result<Table, QueryPlannerError>; + fn get_table_segment(&self, table_name: &str) -> Result<Table, SbroadError>; /// Lookup for a function in the metadata cache. /// /// # Errors /// - Failed to get function by name from the metadata. - fn get_function(&self, fn_name: &str) -> Result<&Function, QueryPlannerError>; + fn get_function(&self, fn_name: &str) -> Result<&Function, SbroadError>; fn get_exec_waiting_timeout(&self) -> u64; @@ -41,20 +41,19 @@ pub trait CoordinatorMetadata { /// # Errors /// - Metadata does not contain space /// - Metadata contains incorrect sharding keys format - fn get_sharding_key_by_space(&self, space: &str) -> Result<Vec<String>, QueryPlannerError>; + fn get_sharding_key_by_space(&self, space: &str) -> Result<Vec<String>, SbroadError>; /// Provides vector of the sharding key column positions in a tuple or an error /// /// # Errors /// - Metadata does not contain space - fn get_sharding_positions_by_space(&self, space: &str) - -> Result<Vec<usize>, QueryPlannerError>; + fn get_sharding_positions_by_space(&self, space: &str) -> Result<Vec<usize>, SbroadError>; /// Provides amlount of table columns /// /// # Errors /// - Metadata does not contain space - fn get_fields_amount_by_space(&self, space: &str) -> Result<usize, QueryPlannerError>; + fn get_fields_amount_by_space(&self, space: &str) -> Result<usize, SbroadError>; } /// Cluster configuration. @@ -77,7 +76,7 @@ pub trait Configuration: Sized { /// /// # Errors /// - Internal error. - fn get_config(&self) -> Result<Option<Self::Configuration>, QueryPlannerError>; + fn get_config(&self) -> Result<Option<Self::Configuration>, SbroadError>; /// Update cached cluster configuration. fn update_config(&mut self, metadata: Self::Configuration); @@ -92,7 +91,7 @@ pub trait Coordinator: Configuration { /// /// # Errors /// - Invalid capacity (zero). - fn clear_ir_cache(&self) -> Result<(), QueryPlannerError>; + fn clear_ir_cache(&self) -> Result<(), SbroadError>; fn ir_cache(&self) -> &RefCell<Self::Cache> where @@ -107,7 +106,7 @@ pub trait Coordinator: Configuration { plan: &mut ExecutionPlan, motion_node_id: usize, buckets: &Buckets, - ) -> Result<VirtualTable, QueryPlannerError>; + ) -> Result<VirtualTable, SbroadError>; /// Dispatch a sql query to the shards in cluster and get the results. /// @@ -118,13 +117,13 @@ pub trait Coordinator: Configuration { plan: &mut ExecutionPlan, top_id: usize, buckets: &Buckets, - ) -> Result<Box<dyn Any>, QueryPlannerError>; + ) -> Result<Box<dyn Any>, SbroadError>; /// Setup output format of query explain /// /// # Errors /// - internal executor errors - fn explain_format(&self, explain: String) -> Result<Box<dyn Any>, QueryPlannerError>; + fn explain_format(&self, explain: String) -> Result<Box<dyn Any>, SbroadError>; /// Extract a list of the sharding keys from a map for the given space. /// @@ -134,7 +133,7 @@ pub trait Coordinator: Configuration { &'engine self, space: String, args: &'rec HashMap<String, Value>, - ) -> Result<Vec<&'rec Value>, QueryPlannerError>; + ) -> Result<Vec<&'rec Value>, SbroadError>; /// Extract a list of the sharding key values from a tuple for the given space. /// @@ -144,7 +143,7 @@ pub trait Coordinator: Configuration { &'engine self, space: String, args: &'rec [Value], - ) -> Result<Vec<&'rec Value>, QueryPlannerError>; + ) -> Result<Vec<&'rec Value>, SbroadError>; /// Determine shard for query execution by sharding key value fn determine_bucket_id(&self, s: &[&Value]) -> u64; @@ -159,7 +158,7 @@ pub fn sharding_keys_from_tuple<'rec>( conf: &impl CoordinatorMetadata, space: &str, tuple: &'rec [Value], -) -> Result<Vec<&'rec Value>, QueryPlannerError> { +) -> Result<Vec<&'rec Value>, SbroadError> { let quoted_space = normalize_name_from_schema(space); let sharding_positions = conf.get_sharding_positions_by_space("ed_space)?; let mut sharding_tuple = Vec::with_capacity(sharding_positions.len()); @@ -168,10 +167,10 @@ pub fn sharding_keys_from_tuple<'rec>( // The tuple contains a "bucket_id" column. for position in &sharding_positions { let value = tuple.get(*position).ok_or_else(|| { - QueryPlannerError::CustomError(format!( - "Missing sharding key position {:?} in the tuple {:?}", - position, tuple - )) + SbroadError::NotFound( + Entity::ShardingKey, + format!("position {:?} in the tuple {:?}", position, tuple), + ) })?; sharding_tuple.push(value); } @@ -190,29 +189,35 @@ pub fn sharding_keys_from_tuple<'rec>( let corrected_pos = match position.cmp(&bucket_position) { Ordering::Less => *position, Ordering::Equal => { - return Err(QueryPlannerError::CustomError(format!( - r#"The tuple {:?} contains a "bucket_id" position {} in a sharding key {:?}"#, - tuple, position, sharding_positions - ))) + return Err(SbroadError::Invalid( + Entity::Tuple, + Some(format!( + r#"the tuple {:?} contains a "bucket_id" position {} in a sharding key {:?}"#, + tuple, position, sharding_positions + )), + )) } Ordering::Greater => *position - 1, }; let value = tuple.get(corrected_pos).ok_or_else(|| { - QueryPlannerError::CustomError(format!( - "Missing sharding key position {:?} in the tuple {:?}", - corrected_pos, tuple - )) + SbroadError::NotFound( + Entity::ShardingKey, + format!("position {corrected_pos:?} in the tuple {tuple:?}"), + ) })?; sharding_tuple.push(value); } Ok(sharding_tuple) } else { - Err(QueryPlannerError::CustomError(format!( - "The tuple {:?} was expected to have {} filed(s), got {}.", - tuple, - table_col_amount - 1, - tuple.len() - ))) + Err(SbroadError::Invalid( + Entity::Tuple, + Some(format!( + "the tuple {:?} was expected to have {} filed(s), got {}.", + tuple, + table_col_amount - 1, + tuple.len() + )), + )) } } @@ -225,7 +230,7 @@ pub fn sharding_keys_from_map<'rec, S: ::std::hash::BuildHasher>( conf: &impl CoordinatorMetadata, space: &str, map: &'rec HashMap<String, Value, S>, -) -> Result<Vec<&'rec Value>, QueryPlannerError> { +) -> Result<Vec<&'rec Value>, SbroadError> { let quoted_space = normalize_name_from_schema(space); let sharding_key = conf.get_sharding_key_by_space("ed_space)?; let quoted_map = map @@ -236,16 +241,17 @@ pub fn sharding_keys_from_map<'rec, S: ::std::hash::BuildHasher>( for quoted_column in &sharding_key { if let Some(column) = quoted_map.get(quoted_column) { let value = map.get(*column).ok_or_else(|| { - QueryPlannerError::CustomError(format!( - "Missing sharding key column {:?} in the map {:?}", - column, map - )) + SbroadError::NotFound( + Entity::ShardingKey, + format!("column {column:?} in the map {map:?}"), + ) })?; tuple.push(value); } else { - return Err(QueryPlannerError::CustomError(format!( - "Missing quoted sharding key column {:?} in the quoted map {:?}. Original map: {:?}", - quoted_column, quoted_map, map + return Err(SbroadError::NotFound( + Entity::ShardingKey, + format!( + "(quoted) column {quoted_column:?} in the quoted map {quoted_map:?} (original map: {map:?})" ))); } } diff --git a/sbroad-core/src/executor/engine/mock.rs b/sbroad-core/src/executor/engine/mock.rs index 3490e8d7eb..39e6993aea 100644 --- a/sbroad-core/src/executor/engine/mock.rs +++ b/sbroad-core/src/executor/engine/mock.rs @@ -4,7 +4,7 @@ use std::collections::{HashMap, HashSet}; use crate::backend::sql::tree::{OrderedSyntaxNodes, SyntaxPlan}; use crate::collection; -use crate::errors::QueryPlannerError; +use crate::errors::{Entity, SbroadError}; use crate::executor::bucket::Buckets; use crate::executor::engine::{ normalize_name_from_sql, sharding_keys_from_map, sharding_keys_from_tuple, Configuration, @@ -34,25 +34,19 @@ pub struct RouterConfigurationMock { } impl CoordinatorMetadata for RouterConfigurationMock { - fn get_table_segment(&self, table_name: &str) -> Result<Table, QueryPlannerError> { + fn get_table_segment(&self, table_name: &str) -> Result<Table, SbroadError> { let name = normalize_name_from_sql(table_name); match self.tables.get(&name) { Some(v) => Ok(v.clone()), - None => Err(QueryPlannerError::CustomError(format!( - "Space {} not found", - table_name - ))), + None => Err(SbroadError::NotFound(Entity::Space, table_name.to_string())), } } - fn get_function(&self, fn_name: &str) -> Result<&Function, QueryPlannerError> { + fn get_function(&self, fn_name: &str) -> Result<&Function, SbroadError> { let name = normalize_name_from_sql(fn_name); match self.functions.get(&name) { Some(v) => Ok(v), - None => Err(QueryPlannerError::CustomError(format!( - "Function {} not found", - name - ))), + None => Err(SbroadError::NotFound(Entity::SQLFunction, name)), } } @@ -64,20 +58,17 @@ impl CoordinatorMetadata for RouterConfigurationMock { self.sharding_column.as_str() } - fn get_sharding_key_by_space(&self, space: &str) -> Result<Vec<String>, QueryPlannerError> { + fn get_sharding_key_by_space(&self, space: &str) -> Result<Vec<String>, SbroadError> { let table = self.get_table_segment(space)?; table.get_sharding_column_names() } - fn get_sharding_positions_by_space( - &self, - space: &str, - ) -> Result<Vec<usize>, QueryPlannerError> { + fn get_sharding_positions_by_space(&self, space: &str) -> Result<Vec<usize>, SbroadError> { let table = self.get_table_segment(space)?; Ok(table.get_sharding_positions().to_vec()) } - fn get_fields_amount_by_space(&self, space: &str) -> Result<usize, QueryPlannerError> { + fn get_fields_amount_by_space(&self, space: &str) -> Result<usize, SbroadError> { let table = self.get_table_segment(space)?; Ok(table.columns.len()) } @@ -242,15 +233,16 @@ impl ProducerResult { /// /// # Errors /// - metadata isn't equal. - fn extend(&mut self, result: ProducerResult) -> Result<(), QueryPlannerError> { + fn extend(&mut self, result: ProducerResult) -> Result<(), SbroadError> { if self.metadata.is_empty() { self.metadata = result.clone().metadata; } if self.metadata != result.metadata { - return Err(QueryPlannerError::CustomError(String::from( - "Metadata mismatch. Producer results can't be extended", - ))); + return Err(SbroadError::Invalid( + Entity::Metadata, + Some("Metadata mismatch. Producer results can't be extended".into()), + )); } self.rows.extend(result.rows); Ok(()) @@ -272,7 +264,7 @@ impl Configuration for RouterRuntimeMock { self.metadata.tables.is_empty() } - fn get_config(&self) -> Result<Option<Self::Configuration>, QueryPlannerError> { + fn get_config(&self) -> Result<Option<Self::Configuration>, SbroadError> { let config = RouterConfigurationMock::new(); Ok(Some(config)) } @@ -286,7 +278,7 @@ impl Coordinator for RouterRuntimeMock { type ParseTree = AbstractSyntaxTree; type Cache = LRUCache<String, Plan>; - fn clear_ir_cache(&self) -> Result<(), QueryPlannerError> { + fn clear_ir_cache(&self) -> Result<(), SbroadError> { *self.ir_cache.borrow_mut() = Self::Cache::new(DEFAULT_CAPACITY, None)?; Ok(()) } @@ -300,14 +292,15 @@ impl Coordinator for RouterRuntimeMock { plan: &mut ExecutionPlan, motion_node_id: usize, _buckets: &Buckets, - ) -> Result<VirtualTable, QueryPlannerError> { + ) -> Result<VirtualTable, SbroadError> { plan.get_motion_subtree_root(motion_node_id)?; if let Some(virtual_table) = self.virtual_tables.borrow().get(&motion_node_id) { Ok(virtual_table.clone()) } else { - Err(QueryPlannerError::CustomError( - "No virtual table found for motion node".to_string(), + Err(SbroadError::NotFound( + Entity::VirtualTable, + format!("for motion node {}", motion_node_id), )) } } @@ -317,7 +310,7 @@ impl Coordinator for RouterRuntimeMock { plan: &mut ExecutionPlan, top_id: usize, buckets: &Buckets, - ) -> Result<Box<dyn Any>, QueryPlannerError> { + ) -> Result<Box<dyn Any>, SbroadError> { let mut result = ProducerResult::new(); let sp = SyntaxPlan::new(plan, top_id, Snapshot::Oldest)?; let ordered = OrderedSyntaxNodes::try_from(sp)?; @@ -343,7 +336,7 @@ impl Coordinator for RouterRuntimeMock { Ok(Box::new(result)) } - fn explain_format(&self, explain: String) -> Result<Box<dyn Any>, QueryPlannerError> { + fn explain_format(&self, explain: String) -> Result<Box<dyn Any>, SbroadError> { Ok(Box::new(explain)) } @@ -351,7 +344,7 @@ impl Coordinator for RouterRuntimeMock { &'engine self, space: String, args: &'rec HashMap<String, Value>, - ) -> Result<Vec<&'rec Value>, QueryPlannerError> { + ) -> Result<Vec<&'rec Value>, SbroadError> { sharding_keys_from_map(&self.metadata, &space, args) } @@ -359,7 +352,7 @@ impl Coordinator for RouterRuntimeMock { &'engine self, space: String, rec: &'rec [Value], - ) -> Result<Vec<&'rec Value>, QueryPlannerError> { + ) -> Result<Vec<&'rec Value>, SbroadError> { sharding_keys_from_tuple(self.cached_config(), &space, rec) } diff --git a/sbroad-core/src/executor/ir.rs b/sbroad-core/src/executor/ir.rs index a089b2b296..d92ad547d7 100644 --- a/sbroad-core/src/executor/ir.rs +++ b/sbroad-core/src/executor/ir.rs @@ -5,8 +5,7 @@ use ahash::AHashMap; use serde::{Deserialize, Serialize}; use traversal::DftPost; -use crate::errors::QueryPlannerError; -use crate::errors::QueryPlannerError::CustomError; +use crate::errors::{Action, Entity, SbroadError}; use crate::executor::vtable::{VirtualTable, VirtualTableMap}; use crate::ir::expression::Expression; use crate::ir::operator::Relational; @@ -72,20 +71,17 @@ impl ExecutionPlan { /// /// # Errors /// - Failed to find a virtual table for the motion node. - pub fn get_motion_vtable( - &self, - motion_id: usize, - ) -> Result<Rc<VirtualTable>, QueryPlannerError> { + pub fn get_motion_vtable(&self, motion_id: usize) -> Result<Rc<VirtualTable>, SbroadError> { if let Some(vtable) = self.get_vtables() { if let Some(result) = vtable.get(&motion_id) { return Ok(Rc::clone(result)); } } - Err(QueryPlannerError::CustomError(format!( - "Motion node ({}) doesn't have a corresponding virtual table", - motion_id - ))) + Err(SbroadError::NotFound( + Entity::VirtualTable, + format!("for Motion node ({motion_id})"), + )) } /// Extract policy from motion node @@ -93,21 +89,22 @@ impl ExecutionPlan { /// # Errors /// - node is not `Relation` type /// - node is not `Motion` type - pub fn get_motion_policy(&self, node_id: usize) -> Result<MotionPolicy, QueryPlannerError> { + pub fn get_motion_policy(&self, node_id: usize) -> Result<MotionPolicy, SbroadError> { if let Relational::Motion { policy, .. } = &self.plan.get_relation_node(node_id)? { return Ok(policy.clone()); } - Err(QueryPlannerError::CustomError(String::from( - "Invalid motion", - ))) + Err(SbroadError::Invalid( + Entity::Relational, + Some("invalid motion".into()), + )) } /// Get motion alias name /// /// # Errors /// - node is not valid - pub fn get_motion_alias(&self, node_id: usize) -> Result<Option<&String>, QueryPlannerError> { + pub fn get_motion_alias(&self, node_id: usize) -> Result<Option<&String>, SbroadError> { let sq_id = &self.get_motion_child(node_id)?; if let Relational::ScanSubQuery { alias, .. } = self.get_ir_plan().get_relation_node(*sq_id)? @@ -122,7 +119,7 @@ impl ExecutionPlan { /// /// # Errors /// - node is not valid - pub fn get_motion_subtree_root(&self, node_id: usize) -> Result<usize, QueryPlannerError> { + pub fn get_motion_subtree_root(&self, node_id: usize) -> Result<usize, SbroadError> { let top_id = &self.get_motion_child(node_id)?; let rel = self.get_ir_plan().get_relation_node(*top_id)?; match rel { @@ -135,9 +132,10 @@ impl ExecutionPlan { | Relational::UnionAll { .. } | Relational::Values { .. } | Relational::ValuesRow { .. } => Ok(*top_id), - Relational::Motion { .. } | Relational::Insert { .. } => Err( - QueryPlannerError::CustomError("Invalid motion child node".to_string()), - ), + Relational::Motion { .. } | Relational::Insert { .. } => Err(SbroadError::Invalid( + Entity::Relational, + Some("invalid motion child node".to_string()), + )), } } @@ -146,21 +144,21 @@ impl ExecutionPlan { /// # Errors /// - node is not `Relation` type /// - node does not contain children - pub(crate) fn get_motion_child(&self, node_id: usize) -> Result<usize, QueryPlannerError> { + pub(crate) fn get_motion_child(&self, node_id: usize) -> Result<usize, SbroadError> { let node = self.get_ir_plan().get_relation_node(node_id)?; if !node.is_motion() { - return Err(CustomError(format!( - "Current node ({}) is not motion", - node_id - ))); + return Err(SbroadError::Invalid( + Entity::Relational, + Some(format!("current node ({node_id}) is not motion")), + )); } let children = self.plan.get_relational_children(node_id)?.ok_or_else(|| { - QueryPlannerError::CustomError("Could not get motion children".to_string()) + SbroadError::NotFound(Entity::Node, format!("that is Motion {node_id} child(ren)")) })?; if children.len() != 1 { - return Err(CustomError(format!( + return Err(SbroadError::UnexpectedNumberOfValues(format!( "Motion node ({}) must have once child only (actual {})", node_id, children.len() @@ -168,7 +166,7 @@ impl ExecutionPlan { } let child_id = children.first().ok_or_else(|| { - QueryPlannerError::CustomError("Failed to get the first motion child".to_string()) + SbroadError::UnexpectedNumberOfValues("Motion has no children".to_string()) })?; Ok(*child_id) @@ -179,21 +177,24 @@ impl ExecutionPlan { /// # Errors /// - node is not `Relation` type /// - node does not contain children - fn get_subquery_child(&self, node_id: usize) -> Result<usize, QueryPlannerError> { + fn get_subquery_child(&self, node_id: usize) -> Result<usize, SbroadError> { let node = self.get_ir_plan().get_relation_node(node_id)?; if !node.is_subquery() { - return Err(CustomError(format!( - "Current node ({}) is not sub query", - node_id - ))); + return Err(SbroadError::Invalid( + Entity::Node, + Some(format!("current node ({node_id}) is not sub query")), + )); } let children = self.plan.get_relational_children(node_id)?.ok_or_else(|| { - QueryPlannerError::CustomError("Could not get subquery children".to_string()) + SbroadError::NotFound( + Entity::Node, + format!("that is Subquery {node_id} child(ren)"), + ) })?; if children.len() != 1 { - return Err(CustomError(format!( + return Err(SbroadError::UnexpectedNumberOfValues(format!( "Sub query node ({}) must have once child only (actual {})", node_id, children.len() @@ -201,7 +202,7 @@ impl ExecutionPlan { } let child_id = children.first().ok_or_else(|| { - QueryPlannerError::CustomError("Could not find subquery child".to_string()) + SbroadError::UnexpectedNumberOfValues("could not find subquery child".to_string()) })?; Ok(*child_id) @@ -211,7 +212,7 @@ impl ExecutionPlan { /// /// # Errors /// - not a motion node - pub fn unlink_motion_subtree(&mut self, motion_id: usize) -> Result<(), QueryPlannerError> { + pub fn unlink_motion_subtree(&mut self, motion_id: usize) -> Result<(), SbroadError> { let motion = self.get_mut_ir_plan().get_mut_relation_node(motion_id)?; if let Relational::Motion { ref mut children, .. @@ -219,10 +220,10 @@ impl ExecutionPlan { { *children = vec![]; } else { - return Err(QueryPlannerError::CustomError(format!( - "Node ({}) is not motion", - motion_id - ))); + return Err(SbroadError::Invalid( + Entity::Relational, + Some(format!("node ({motion_id}) is not motion")), + )); } Ok(()) } @@ -233,7 +234,7 @@ impl ExecutionPlan { /// # Errors /// - the original execution plan is invalid #[allow(clippy::too_many_lines)] - pub fn take_subtree(&mut self, top_id: usize) -> Result<Self, QueryPlannerError> { + pub fn take_subtree(&mut self, top_id: usize) -> Result<Self, SbroadError> { let nodes_capacity = self.get_ir_plan().nodes.len(); // Translates the original plan's node id to the new sub-plan one. let mut translation: AHashMap<usize, usize> = AHashMap::with_capacity(nodes_capacity); @@ -262,10 +263,11 @@ impl ExecutionPlan { Node::Relational(ref mut rel) => { if let Relational::ValuesRow { data, .. } = rel { *data = *translation.get(data).ok_or_else(|| { - QueryPlannerError::CustomError(format!( - "Failed to build an execution plan subtree: could not find data node id {} in the map", - data - )) + SbroadError::FailedTo( + Action::Build, + Some(Entity::SubTree), + format!("could not find data node id {data} in the map"), + ) })?; } @@ -281,20 +283,21 @@ impl ExecutionPlan { if let Some(children) = rel.mut_children() { for child_id in children { *child_id = *translation.get(child_id).ok_or_else(|| { - QueryPlannerError::CustomError(format!( - "Failed to build an execution plan subtree: could not find child node id {} in the map", - child_id - )) + SbroadError::FailedTo( + Action::Build, + Some(Entity::SubTree), + format!("could not find child node id {child_id} in the map"), + ) })?; } } let output = rel.output(); *rel.mut_output() = *translation.get(&output).ok_or_else(|| { - QueryPlannerError::CustomError(format!( - "Failed to find an output node {} in relational node {:?}", - output, rel - )) + SbroadError::NotFound( + Entity::Node, + format!("as output node {output} in relational node {rel:?}"), + ) })?; new_plan.replace_parent_in_subtree(rel.output(), None, Some(next_id))?; @@ -315,10 +318,14 @@ impl ExecutionPlan { // for filter/condition (but then the UNDO logic should be changed as well). let undo_expr_id = ir_plan.undo.get_oldest(expr_id).unwrap_or(expr_id); *expr_id = *translation.get(undo_expr_id).ok_or_else(|| { - QueryPlannerError::CustomError(format!( - "Failed to build an execution plan subtree: could not find filter/condition node id {} in the map", - undo_expr_id - )) + SbroadError::FailedTo( + Action::Build, + Some(Entity::SubTree), + format!( + "could not find filter/condition node id {} in the map", + undo_expr_id + ), + ) })?; new_plan.replace_parent_in_subtree(*expr_id, None, Some(next_id))?; } @@ -326,12 +333,20 @@ impl ExecutionPlan { if let Relational::ScanRelation { relation, .. } | Relational::Insert { relation, .. } = rel { - let table = ir_plan.relations.get(relation).ok_or_else(|| { - QueryPlannerError::CustomError(format!( - "Failed to build an execution plan subtree: could not find relation {} in the original plan", - relation - )) - })?.clone(); + let table = ir_plan + .relations + .get(relation) + .ok_or_else(|| { + SbroadError::FailedTo( + Action::Build, + Some(Entity::SubTree), + format!( + "could not find relation {} in the original plan", + relation + ), + ) + })? + .clone(); new_plan.add_rel(table); } } @@ -340,11 +355,12 @@ impl ExecutionPlan { | Expression::Cast { ref mut child, .. } | Expression::Unary { ref mut child, .. } => { *child = *translation.get(child).ok_or_else(|| { - QueryPlannerError::CustomError(format!( - "Failed to build an execution plan subtree: could not find child node id {} in the map", - child - )) - })?; + SbroadError::FailedTo( + Action::Build, + Some(Entity::SubTree), + format!("could not find child node id {child} in the map"), + ) + })?; } Expression::Bool { ref mut left, @@ -357,17 +373,19 @@ impl ExecutionPlan { .. } => { *left = *translation.get(left).ok_or_else(|| { - QueryPlannerError::CustomError(format!( - "Failed to build an execution plan subtree: could not find left child node id {} in the map", - left - )) - })?; + SbroadError::FailedTo( + Action::Build, + Some(Entity::SubTree), + format!("could not find left child node id {left} in the map"), + ) + })?; *right = *translation.get(right).ok_or_else(|| { - QueryPlannerError::CustomError(format!( - "Failed to build an execution plan subtree: could not find right child node id {} in the map", - right - )) - })?; + SbroadError::FailedTo( + Action::Build, + Some(Entity::SubTree), + format!("could not find right child node id {right} in the map"), + ) + })?; } Expression::Reference { ref mut parent, .. } => { // The new parent node id MUST be set while processing the relational nodes. @@ -382,11 +400,12 @@ impl ExecutionPlan { } => { for child in children { *child = *translation.get(child).ok_or_else(|| { - QueryPlannerError::CustomError(format!( - "Failed to build an execution plan subtree: could not find child node id {} in the map", - child - )) - })?; + SbroadError::FailedTo( + Action::Build, + Some(Entity::SubTree), + format!("could not find child node id {child} in the map"), + ) + })?; } } Expression::Constant { .. } => {} @@ -417,7 +436,7 @@ impl ExecutionPlan { /// # Errors /// - execution plan is invalid - pub fn query_type(&self) -> Result<QueryType, QueryPlannerError> { + pub fn query_type(&self) -> Result<QueryType, SbroadError> { let top_id = self.get_ir_plan().get_top()?; let top = self.get_ir_plan().get_relation_node(top_id)?; if top.is_insert() { @@ -429,7 +448,7 @@ impl ExecutionPlan { /// # Errors /// - execution plan is invalid - pub fn connection_type(&self) -> Result<ConnectionType, QueryPlannerError> { + pub fn connection_type(&self) -> Result<ConnectionType, SbroadError> { match self.query_type()? { QueryType::DML => Ok(ConnectionType::Write), QueryType::DQL => Ok(ConnectionType::Read), diff --git a/sbroad-core/src/executor/lru.rs b/sbroad-core/src/executor/lru.rs index 318fe1eb48..8e207197e1 100644 --- a/sbroad-core/src/executor/lru.rs +++ b/sbroad-core/src/executor/lru.rs @@ -1,17 +1,17 @@ -use crate::errors::QueryPlannerError; +use crate::errors::{Action, Entity, SbroadError}; use std::collections::{hash_map::Entry, HashMap}; use std::fmt::Debug; pub const DEFAULT_CAPACITY: usize = 50; -pub type EvictFn<Value> = Box<dyn Fn(&mut Value) -> Result<(), QueryPlannerError>>; +pub type EvictFn<Value> = Box<dyn Fn(&mut Value) -> Result<(), SbroadError>>; pub trait Cache<Key, Value> { /// Builds a new cache with the given capacity. /// /// # Errors /// - Capacity is not valid (zero). - fn new(capacity: usize, evict_fn: Option<EvictFn<Value>>) -> Result<Self, QueryPlannerError> + fn new(capacity: usize, evict_fn: Option<EvictFn<Value>>) -> Result<Self, SbroadError> where Self: Sized; @@ -19,13 +19,13 @@ pub trait Cache<Key, Value> { /// /// # Errors /// - Internal error (should never happen). - fn get(&mut self, key: &Key) -> Result<Option<&Value>, QueryPlannerError>; + fn get(&mut self, key: &Key) -> Result<Option<&Value>, SbroadError>; /// Inserts a key-value pair into the cache. /// /// # Errors /// - Internal error (should never happen). - fn put(&mut self, key: Key, value: Value) -> Result<(), QueryPlannerError>; + fn put(&mut self, key: Key, value: Value) -> Result<(), SbroadError>; } #[derive(Debug)] @@ -92,22 +92,19 @@ where self.map.get(key) } - fn get_node(&self, key: &Option<Key>) -> Result<&LRUNode<Key, Value>, QueryPlannerError> { - self.map.get(key).ok_or_else(|| { - QueryPlannerError::CustomError(format!("LRU node with key {key:?} not found")) - }) + fn get_node(&self, key: &Option<Key>) -> Result<&LRUNode<Key, Value>, SbroadError> { + self.map + .get(key) + .ok_or_else(|| SbroadError::NotFound(Entity::Node, format!("(LRU) with key {key:?}"))) } - fn get_node_mut( - &mut self, - key: &Option<Key>, - ) -> Result<&mut LRUNode<Key, Value>, QueryPlannerError> { + fn get_node_mut(&mut self, key: &Option<Key>) -> Result<&mut LRUNode<Key, Value>, SbroadError> { self.map.get_mut(key).ok_or_else(|| { - QueryPlannerError::CustomError(format!("Mutable LRU node with key {key:?} not found")) + SbroadError::NotFound(Entity::Node, format!("(mutable LRU) with key {key:?}")) }) } - fn add_first(&mut self, key: Key, value: Value) -> Result<(), QueryPlannerError> { + fn add_first(&mut self, key: Key, value: Value) -> Result<(), SbroadError> { let new_node = LRUNode::new(value); self.map.insert(Some(key.clone()), new_node); self.size += 1; @@ -117,7 +114,7 @@ where Ok(()) } - fn make_first(&mut self, key: &Key) -> Result<(), QueryPlannerError> { + fn make_first(&mut self, key: &Key) -> Result<(), SbroadError> { self.unlink_node(&Some(key.clone()))?; let head_node = self.get_node(&None)?; let head_next_id = head_node.next.clone(); @@ -125,7 +122,7 @@ where Ok(()) } - fn is_first(&self, key: &Key) -> Result<bool, QueryPlannerError> { + fn is_first(&self, key: &Key) -> Result<bool, SbroadError> { let head_node = self.get_node(&None)?; Ok(head_node.next == Some(key.clone())) } @@ -135,7 +132,7 @@ where key: Key, prev: &Option<Key>, next: &Option<Key>, - ) -> Result<(), QueryPlannerError> { + ) -> Result<(), SbroadError> { let node = self.get_node_mut(&Some(key.clone()))?; node.replace_prev(prev.clone()); node.replace_next(next.clone()); @@ -146,7 +143,7 @@ where Ok(()) } - fn unlink_node(&mut self, key: &Option<Key>) -> Result<(), QueryPlannerError> { + fn unlink_node(&mut self, key: &Option<Key>) -> Result<(), SbroadError> { // We don't want to remove sentinel. if key.is_none() { return Ok(()); @@ -162,7 +159,7 @@ where Ok(()) } - fn remove_last(&mut self) -> Result<(), QueryPlannerError> { + fn remove_last(&mut self) -> Result<(), SbroadError> { let head_node = self.get_node(&None)?; let head_prev_id = head_node.prev.clone(); if head_prev_id.is_none() { @@ -172,10 +169,10 @@ where let map = &mut self.map; if let Some(evict_fn) = &self.evict_fn { let head_prev = map.get_mut(&head_prev_id).ok_or_else(|| { - QueryPlannerError::CustomError(format!( - "Mutable LRU node with key {:?} not found", - &head_prev_id - )) + SbroadError::NotFound( + Entity::Node, + format!("(mutable LRU) with key {:?}", &head_prev_id), + ) })?; evict_fn(&mut head_prev.value)?; } @@ -194,10 +191,11 @@ where Value: Default + Debug, Key: Clone + Eq + std::hash::Hash + std::fmt::Debug, { - fn new(capacity: usize, evict_fn: Option<EvictFn<Value>>) -> Result<Self, QueryPlannerError> { + fn new(capacity: usize, evict_fn: Option<EvictFn<Value>>) -> Result<Self, SbroadError> { if capacity == 0 { - return Err(QueryPlannerError::CustomError( - "LRU cache capacity must be greater than zero".to_string(), + return Err(SbroadError::Invalid( + Entity::Cache, + Some("LRU cache capacity must be greater than zero".to_string()), )); } let head = LRUNode::sentinel(); @@ -213,7 +211,7 @@ where }) } - fn get(&mut self, key: &Key) -> Result<Option<&Value>, QueryPlannerError> { + fn get(&mut self, key: &Key) -> Result<Option<&Value>, SbroadError> { if self.get_node_or_none(&Some(key.clone())).is_none() { return Ok(None); } @@ -227,14 +225,15 @@ where if let Some(node) = self.get_node_or_none(&Some(key.clone())) { Ok(Some(&node.value)) } else { - Err(QueryPlannerError::CustomError(format!( - "Failed to retrieve a value from the LRU cache for a key {:?}", - key - ))) + Err(SbroadError::FailedTo( + Action::Retrieve, + Some(Entity::Value), + format!("from the LRU cache for a key {key:?}"), + )) } } - fn put(&mut self, key: Key, value: Value) -> Result<(), QueryPlannerError> { + fn put(&mut self, key: Key, value: Value) -> Result<(), SbroadError> { if let Entry::Occupied(mut entry) = self.map.entry(Some(key.clone())) { let node = entry.get_mut(); node.value = value; diff --git a/sbroad-core/src/executor/lru/tests.rs b/sbroad-core/src/executor/lru/tests.rs index c79d6d472b..681a8d8f84 100644 --- a/sbroad-core/src/executor/lru/tests.rs +++ b/sbroad-core/src/executor/lru/tests.rs @@ -1,5 +1,5 @@ use super::{Cache, LRUCache}; -use crate::errors::QueryPlannerError; +use crate::errors::SbroadError; use crate::ir::Plan; use pretty_assertions::assert_eq; @@ -17,7 +17,7 @@ fn lru1() { #[test] fn lru2() { - let cache_err: Result<LRUCache<usize, Plan>, QueryPlannerError> = LRUCache::new(0, None); + let cache_err: Result<LRUCache<usize, Plan>, SbroadError> = LRUCache::new(0, None); assert_eq!(cache_err.is_err(), true); } @@ -26,7 +26,7 @@ fn lru3() { let evict_fn = Box::new(|value: &mut String| { let value_old = value.clone(); value.push_str("_old"); - Err(QueryPlannerError::CustomError(format!( + Err(SbroadError::UnexpectedNumberOfValues(format!( "changed {} to {} during cache eviction", value_old, value ))) @@ -34,7 +34,9 @@ fn lru3() { let mut cache: LRUCache<usize, String> = LRUCache::new(1, Some(evict_fn)).unwrap(); cache.put(1, "one".to_string()).unwrap(); assert_eq!( - QueryPlannerError::CustomError("changed one to one_old during cache eviction".to_string()), + SbroadError::UnexpectedNumberOfValues( + "changed one to one_old during cache eviction".to_string() + ), cache.put(2, "two".to_string()).unwrap_err() ); } diff --git a/sbroad-core/src/executor/result.rs b/sbroad-core/src/executor/result.rs index b6818e4df2..1714ceea79 100644 --- a/sbroad-core/src/executor/result.rs +++ b/sbroad-core/src/executor/result.rs @@ -3,7 +3,7 @@ use serde::ser::{Serialize, SerializeMap, Serializer}; use serde::Deserialize; use tarantool::tlua::{self, LuaRead}; -use crate::errors::QueryPlannerError; +use crate::errors::{Entity, SbroadError}; use crate::executor::vtable::VirtualTable; use crate::ir::relation::{Column, ColumnRole, Type}; use crate::ir::value::{EncodedValue, Value}; @@ -36,7 +36,7 @@ impl Serialize for MetadataColumn { } impl TryInto<Column> for &MetadataColumn { - type Error = QueryPlannerError; + type Error = SbroadError; fn try_into(self) -> Result<Column, Self::Error> { match self.r#type.as_str() { @@ -50,10 +50,10 @@ impl TryInto<Column> for &MetadataColumn { Ok(Column::new(&self.name, Type::String, ColumnRole::User)) } "unsigned" => Ok(Column::new(&self.name, Type::Unsigned, ColumnRole::User)), - _ => Err(QueryPlannerError::CustomError(format!( - "unsupported column type: {}", - self.r#type - ))), + _ => Err(SbroadError::Unsupported( + Entity::Type, + Some(format!("column type {}", self.r#type)), + )), } } } @@ -87,7 +87,7 @@ impl ProducerResult { /// /// # Errors /// - convert to virtual table error - pub fn as_virtual_table(&self) -> Result<VirtualTable, QueryPlannerError> { + pub fn as_virtual_table(&self) -> Result<VirtualTable, SbroadError> { let mut result = VirtualTable::new(); for col in &self.metadata { diff --git a/sbroad-core/src/executor/shard.rs b/sbroad-core/src/executor/shard.rs index 99d0f03e8d..6f80b84485 100644 --- a/sbroad-core/src/executor/shard.rs +++ b/sbroad-core/src/executor/shard.rs @@ -4,7 +4,7 @@ use traversal::DftPost; use crate::executor::ir::ExecutionPlan; use crate::{ - errors::QueryPlannerError, + errors::{SbroadError, Entity}, ir::{distribution::Distribution, expression::Expression}, }; @@ -32,7 +32,7 @@ impl<'e> ExecutionPlan<'e> { fn get_motion_sharding_keys( &self, top_node_id: usize, - ) -> Result<HashSet<String>, QueryPlannerError> { + ) -> Result<HashSet<String>, SbroadError> { let mut result = HashSet::new(); let ir_plan = self.get_ir_plan(); @@ -55,7 +55,7 @@ impl<'e> ExecutionPlan<'e> { pub fn discovery( &mut self, node_id: usize, - ) -> Result<Option<HashSet<String>>, QueryPlannerError> { + ) -> Result<Option<HashSet<String>>, SbroadError> { let mut dist_keys: HashSet<String> = HashSet::new(); let motion_shard_keys = self.get_motion_sharding_keys(node_id)?; @@ -88,7 +88,7 @@ impl<'e> ExecutionPlan<'e> { }; // Get the distribution of the left row. - if let Err(QueryPlannerError::UninitializedDistribution) = + if let Err(SbroadError::Invalid(Entity::Distribution, _)) = ir_plan.get_distribution(left_id) { ir_plan.set_distribution(left_id)?; @@ -103,12 +103,10 @@ impl<'e> ExecutionPlan<'e> { return Ok(None); } let position = positions.get(0).ok_or_else(|| { - QueryPlannerError::CustomError("Invalid distribution key".into()) + SbroadError::NotFound(Entity::DistributionKey, "with index 0".into()) })?; let right_column_id = *right_columns.get(*position).ok_or_else(|| { - QueryPlannerError::CustomError( - "Left and right rows have different length.".into(), - ) + SbroadError::UnexpectedNumberOfValues("left and right rows have different length.".into()) })?; let right_column = ir_plan.get_expression_node(right_column_id)?; if right_column.is_const() { diff --git a/sbroad-core/src/executor/tests.rs b/sbroad-core/src/executor/tests.rs index 2903d60b18..149891b671 100644 --- a/sbroad-core/src/executor/tests.rs +++ b/sbroad-core/src/executor/tests.rs @@ -1288,10 +1288,11 @@ fn insert7_test() { let result = Query::new(&coordinator, sql, vec![]).unwrap_err(); assert_eq!( - QueryPlannerError::CustomError(format!( - "System column {} cannot be inserted", - "\"bucket_id\"" - )), + SbroadError::FailedTo( + Action::Insert, + Some(Entity::Column), + format!("system column {} cannot be inserted", "\"bucket_id\""), + ), result ); } diff --git a/sbroad-core/src/executor/tests/frontend.rs b/sbroad-core/src/executor/tests/frontend.rs index c80b30ce7b..31cae0fd47 100644 --- a/sbroad-core/src/executor/tests/frontend.rs +++ b/sbroad-core/src/executor/tests/frontend.rs @@ -19,8 +19,8 @@ fn front_ivalid_sql1() { let plan_err = Query::new(metadata, query, vec![]).unwrap_err(); assert_eq!( - QueryPlannerError::CustomError( - "Row can't be added because `\"sys_op\"` already has an alias".into() + SbroadError::DuplicatedValue( + "row can't be added because `\"sys_op\"` already has an alias".into() ), plan_err ); @@ -34,8 +34,8 @@ fn front_invalid_sql2() { let plan_err = Query::new(metadata, query, vec![]).unwrap_err(); assert_eq!( - QueryPlannerError::CustomError( - r#"Invalid number of values: 2. Table "t" expects 1 column(s)."#.into() + SbroadError::UnexpectedNumberOfValues( + r#"invalid number of values: 2. Table "t" expects 1 column(s)."#.into() ), plan_err ); @@ -49,8 +49,8 @@ fn front_invalid_sql3() { let plan_err = Query::new(metadata, query, vec![]).unwrap_err(); assert_eq!( - QueryPlannerError::CustomError( - r#"Invalid number of values: 2. Table "t" expects 4 column(s)."#.into() + SbroadError::UnexpectedNumberOfValues( + r#"invalid number of values: 2. Table "t" expects 4 column(s)."#.into() ), plan_err ); @@ -64,8 +64,8 @@ fn front_invalid_sql4() { let plan_err = Query::new(metadata, query, vec![]).unwrap_err(); assert_eq!( - QueryPlannerError::CustomError( - r#"Invalid number of values: 2. Table "t" expects 4 column(s)."#.into() + SbroadError::UnexpectedNumberOfValues( + r#"invalid number of values: 2. Table "t" expects 4 column(s)."#.into() ), plan_err ); diff --git a/sbroad-core/src/executor/vtable.rs b/sbroad-core/src/executor/vtable.rs index 0449052120..b1b2a8de06 100644 --- a/sbroad-core/src/executor/vtable.rs +++ b/sbroad-core/src/executor/vtable.rs @@ -4,7 +4,7 @@ use std::vec; use serde::{Deserialize, Serialize}; -use crate::errors::QueryPlannerError; +use crate::errors::{Entity, SbroadError}; use crate::executor::bucket::Buckets; use crate::ir::relation::Column; use crate::ir::transformation::redistribution::{MotionKey, Target}; @@ -156,7 +156,7 @@ impl VirtualTable { /// /// # Errors /// - Failed to find a distribution key. - pub fn get_tuple_distribution(&self) -> Result<HashSet<Vec<&Value>>, QueryPlannerError> { + pub fn get_tuple_distribution(&self) -> Result<HashSet<Vec<&Value>>, SbroadError> { let mut result: HashSet<Vec<&Value>> = HashSet::with_capacity(self.tuples.len()); for tuple in &self.tuples { @@ -166,10 +166,10 @@ impl VirtualTable { match target { Target::Reference(pos) => { let part = tuple.get(*pos).ok_or_else(|| { - QueryPlannerError::CustomError(format!( - "Failed to find a distribution key column {} in the tuple {:?}.", - pos, tuple - )) + SbroadError::NotFound( + Entity::DistributionKey, + format!("column {pos} in the tuple {tuple:?}."), + ) })?; shard_key_tuple.push(part); } @@ -179,8 +179,9 @@ impl VirtualTable { } } } else { - return Err(QueryPlannerError::CustomError( - "Distribution key not found".into(), + return Err(SbroadError::NotFound( + Entity::DistributionKey, + "for virtual table".into(), )); } result.insert(shard_key_tuple); @@ -193,10 +194,11 @@ impl VirtualTable { /// /// # Errors /// - Try to set an empty alias name to the virtual table. - pub fn set_alias(&mut self, name: &str) -> Result<(), QueryPlannerError> { + pub fn set_alias(&mut self, name: &str) -> Result<(), SbroadError> { if name.is_empty() { - return Err(QueryPlannerError::CustomError( - "Can't set empty alias for virtual table".into(), + return Err(SbroadError::Invalid( + Entity::Value, + Some("can't set empty alias for virtual table".into()), )); } diff --git a/sbroad-core/src/frontend.rs b/sbroad-core/src/frontend.rs index acfaaf90e7..39aad965c6 100644 --- a/sbroad-core/src/frontend.rs +++ b/sbroad-core/src/frontend.rs @@ -3,7 +3,7 @@ //! A list of different frontend implementations //! to build the intermediate representation (IR). -use crate::errors::QueryPlannerError; +use crate::errors::SbroadError; use crate::executor::engine::CoordinatorMetadata; use crate::ir::Plan; @@ -16,7 +16,7 @@ pub trait Ast { /// /// # Errors /// - SQL query is not valid or not supported. - fn new(query: &str) -> Result<Self, QueryPlannerError> + fn new(query: &str) -> Result<Self, SbroadError> where Self: Sized; @@ -27,7 +27,7 @@ pub trait Ast { /// /// # Errors /// - Failed to resolve AST nodes with cluster metadata. - fn resolve_metadata<M>(&self, metadata: &M) -> Result<Plan, QueryPlannerError> + fn resolve_metadata<M>(&self, metadata: &M) -> Result<Plan, SbroadError> where M: CoordinatorMetadata; } diff --git a/sbroad-core/src/frontend/sql.rs b/sbroad-core/src/frontend/sql.rs index a507c61f78..710e9ce079 100644 --- a/sbroad-core/src/frontend/sql.rs +++ b/sbroad-core/src/frontend/sql.rs @@ -7,7 +7,7 @@ use pest::Parser; use std::collections::{HashMap, HashSet}; use traversal::DftPost; -use crate::errors::QueryPlannerError; +use crate::errors::{Entity, SbroadError}; use crate::executor::engine::{normalize_name_from_sql, CoordinatorMetadata}; use crate::frontend::sql::ast::{ AbstractSyntaxTree, ParseNode, ParseNodes, ParseTree, Rule, StackParseNode, Type, @@ -59,20 +59,15 @@ impl Ast for AbstractSyntaxTree { /// # Errors /// - Failed to parse an SQL query. #[otm_child_span("ast.parse")] - fn new(query: &str) -> Result<Self, QueryPlannerError> { + fn new(query: &str) -> Result<Self, SbroadError> { let mut ast = AbstractSyntaxTree::empty(); let mut command_pair = match ParseTree::parse(Rule::Command, query) { Ok(p) => p, - Err(e) => { - return Err(QueryPlannerError::CustomError(format!( - "Parsing error: {}", - e - ))) - } + Err(e) => return Err(SbroadError::ParsingError(Entity::Rule, format!("{e}"))), }; let top_pair = command_pair.next().ok_or_else(|| { - QueryPlannerError::CustomError("No query found in the parse tree.".to_string()) + SbroadError::UnexpectedNumberOfValues("no query found in the parse tree.".to_string()) })?; let top = StackParseNode::new(top_pair, None); @@ -118,7 +113,7 @@ impl Ast for AbstractSyntaxTree { #[allow(dead_code)] #[allow(clippy::too_many_lines)] #[otm_child_span("ast.resolve")] - fn resolve_metadata<M>(&self, metadata: &M) -> Result<Plan, QueryPlannerError> + fn resolve_metadata<M>(&self, metadata: &M) -> Result<Plan, SbroadError> where M: CoordinatorMetadata, { @@ -126,7 +121,7 @@ impl Ast for AbstractSyntaxTree { let top = match self.top { Some(t) => t, - None => return Err(QueryPlannerError::InvalidAst), + None => return Err(SbroadError::Invalid(Entity::AST, None)), }; let dft_post = DftPost::new(&top, |node| self.nodes.ast_iter(node)); let mut map = Translation::with_capacity(self.nodes.next_id()); @@ -140,8 +135,8 @@ impl Ast for AbstractSyntaxTree { match &node.rule { Type::Scan => { let ast_child_id = node.children.first().ok_or_else(|| { - QueryPlannerError::CustomError( - "Could not find child id in scan node".to_string(), + SbroadError::UnexpectedNumberOfValues( + "could not find child id in scan node".to_string(), ) })?; let plan_child_id = map.get(*ast_child_id)?; @@ -155,8 +150,9 @@ impl Ast for AbstractSyntaxTree { let scan = plan.get_mut_relation_node(plan_child_id)?; scan.set_scan_name(ast_scan_name)?; } else { - return Err(QueryPlannerError::CustomError( - "Expected scan name AST node.".into(), + return Err(SbroadError::Invalid( + Entity::Type, + Some("expected scan name AST node.".into()), )); } } @@ -169,15 +165,16 @@ impl Ast for AbstractSyntaxTree { let scan_id = plan.add_scan(&normalize_name_from_sql(table), None)?; map.add(*id, scan_id); } else { - return Err(QueryPlannerError::CustomError( - "Table name is not found.".into(), + return Err(SbroadError::Invalid( + Entity::Type, + Some("Table name is not found.".into()), )); } } Type::SubQuery => { let ast_child_id = node.children.first().ok_or_else(|| { - QueryPlannerError::CustomError( - "Child node id is not found among sub-query children.".into(), + SbroadError::UnexpectedNumberOfValues( + "child node id is not found among sub-query children.".into(), ) })?; let plan_child_id = map.get(*ast_child_id)?; @@ -186,10 +183,13 @@ impl Ast for AbstractSyntaxTree { let ast_alias = self.nodes.get_node(*ast_name_id)?; if let Type::SubQueryName = ast_alias.rule { } else { - return Err(QueryPlannerError::CustomError(format!( - "Expected a sub-query name, got {:?}.", - ast_alias.rule - ))); + return Err(SbroadError::Invalid( + Entity::Type, + Some(format!( + "expected a sub-query name, got {:?}.", + ast_alias.rule + )), + )); } ast_alias.value.as_deref().map(normalize_name_from_sql) } else { @@ -206,25 +206,27 @@ impl Ast for AbstractSyntaxTree { plan_rel_list.push(plan_id); } - let get_column_name = |ast_id: usize| -> Result<String, QueryPlannerError> { + let get_column_name = |ast_id: usize| -> Result<String, SbroadError> { let ast_col_name = self.nodes.get_node(ast_id)?; if let Type::ColumnName = ast_col_name.rule { let name: Option<String> = ast_col_name.value.as_deref().map(normalize_name_from_sql); Ok(name.ok_or_else(|| { - QueryPlannerError::CustomError("Empty AST column name".into()) + SbroadError::Invalid( + Entity::Name, + Some("empty AST column name".into()), + ) })?) } else { - Err(QueryPlannerError::CustomError( - "Expected column name AST node.".into(), + Err(SbroadError::Invalid( + Entity::Type, + Some("expected column name AST node.".into()), )) } }; let get_scan_name = - |col_name: &str, - plan_id: usize| - -> Result<Option<String>, QueryPlannerError> { + |col_name: &str, plan_id: usize| -> Result<Option<String>, SbroadError> { let child = plan.get_relation_node(plan_id)?; let col_position = child .output_alias_position_map(&plan.nodes)? @@ -269,10 +271,13 @@ impl Ast for AbstractSyntaxTree { rows.insert(ref_id); map.add(*id, ref_id); } else { - return Err(QueryPlannerError::CustomError(format!( - "Column '{}' not found in for the join left child '{:?}'.", - col_name, left_name - ))); + return Err(SbroadError::NotFound( + Entity::Column, + format!( + "'{}' in the join left child '{:?}'", + col_name, left_name + ), + )); } } else if right_name == ast_scan_name { let right_col_map = plan @@ -287,19 +292,24 @@ impl Ast for AbstractSyntaxTree { rows.insert(ref_id); map.add(*id, ref_id); } else { - return Err(QueryPlannerError::CustomError(format!( - "Column '{}' not found in for the join right child '{:?}'.", - col_name, right_name - ))); + return Err(SbroadError::NotFound( + Entity::Column, + format!( + "'{}' in the join right child '{:?}'", + col_name, right_name + ), + )); } } else { - return Err(QueryPlannerError::CustomError( - format!("Left and right plan nodes do not match the AST scan name: {ast_scan_name:?}"), + return Err(SbroadError::Invalid( + Entity::Plan, + Some(format!("left and right plan nodes do not match the AST scan name: {ast_scan_name:?}")), )); } } else { - return Err(QueryPlannerError::CustomError( - "Expected AST node to be a scan name.".into(), + return Err(SbroadError::Invalid( + Entity::Node, + Some("expected AST node to be a scan name.".into()), )); } } else if let (Some(ast_col_name_id), None) = @@ -331,13 +341,13 @@ impl Ast for AbstractSyntaxTree { rows.insert(ref_id); map.add(*id, ref_id); } - return Err(QueryPlannerError::CustomError(format!( - "Column '{}' not found in for the join left or right children.", - col_name - ))); + return Err(SbroadError::NotFound( + Entity::Column, + format!("'{}' for the join left or right children", col_name), + )); } else { - return Err(QueryPlannerError::CustomError( - "Expected children nodes contain a column name.".into(), + return Err(SbroadError::UnexpectedNumberOfValues( + "expected children nodes contain a column name.".into(), )); }; @@ -355,22 +365,22 @@ impl Ast for AbstractSyntaxTree { if let Type::ScanName = ast_scan.rule { let ast_scan_name = Some(normalize_name_from_sql( ast_scan.value.as_ref().ok_or_else(|| { - QueryPlannerError::CustomError( - "Expected AST node to have a non-empty scan name." - .into(), + SbroadError::UnexpectedNumberOfValues( + "empty scan name for AST node.".into(), ) })?, )); let plan_scan_name = get_scan_name(&col_name, *plan_rel_id)?; if plan_scan_name != ast_scan_name { - return Err(QueryPlannerError::CustomError( + return Err(SbroadError::UnexpectedNumberOfValues( format!("Scan name for the column {:?} doesn't match: expected {plan_scan_name:?}, found {ast_scan_name:?}", get_column_name(*ast_col_id) ))); } } else { - return Err(QueryPlannerError::CustomError( - "Expected AST node to be a scan name.".into(), + return Err(SbroadError::Invalid( + Entity::Node, + Some("expected AST node to be a scan name.".into()), )); }; col_name @@ -380,8 +390,8 @@ impl Ast for AbstractSyntaxTree { // Get the column name. get_column_name(*ast_col_id)? } else { - return Err(QueryPlannerError::CustomError( - "No child node found in the AST reference.".into(), + return Err(SbroadError::UnexpectedNumberOfValues( + "no child node found in the AST reference.".into(), )); }; @@ -394,12 +404,14 @@ impl Ast for AbstractSyntaxTree { true, )?; let ref_id = *ref_list.first().ok_or_else(|| { - QueryPlannerError::CustomError("Referred column is not found.".into()) + SbroadError::UnexpectedNumberOfValues( + "Referred column is not found.".into(), + ) })?; map.add(*id, ref_id); } else { - return Err(QueryPlannerError::CustomError( - "Expected one or two referred relational nodes, got less or more." + return Err(SbroadError::UnexpectedNumberOfValues( + "expected one or two referred relational nodes, got less or more." .into(), )); } @@ -427,13 +439,14 @@ impl Ast for AbstractSyntaxTree { plan_rel_list.push(plan_id); } if plan_rel_list.len() > 1 { - return Err(QueryPlannerError::CustomError( - "Sub-queries in projections are not implemented yet.".into(), + return Err(SbroadError::NotImplemented( + Entity::SubQuery, + "in projections".into(), )); } let plan_rel_id = *plan_rel_list.first().ok_or_else(|| { - QueryPlannerError::CustomError( - "Referred relational node is not found.".into(), + SbroadError::UnexpectedNumberOfValues( + "list of referred relational nodes is empty.".into(), ) })?; let plan_asterisk_id = plan.add_row_for_output(plan_rel_id, &[], false)?; @@ -441,24 +454,21 @@ impl Ast for AbstractSyntaxTree { } Type::Alias => { let ast_ref_id = node.children.first().ok_or_else(|| { - QueryPlannerError::CustomError( - "Reference node id is not found among alias children.".into(), + SbroadError::UnexpectedNumberOfValues( + "list of alias children is empty, Reference node id is not found." + .into(), ) })?; let plan_ref_id = map.get(*ast_ref_id)?; let ast_name_id = node.children.get(1).ok_or_else(|| { - QueryPlannerError::CustomError( - "Alias name node id is not found among alias children.".into(), - ) + SbroadError::NotFound(Entity::Node, "(Alias name) with index 1".into()) })?; let name = self .nodes .get_node(*ast_name_id)? .value .as_ref() - .ok_or_else(|| { - QueryPlannerError::CustomError("Alias name is not found.".into()) - })?; + .ok_or_else(|| SbroadError::NotFound(Entity::Name, "of Alias".into()))?; let plan_alias_id = plan .nodes .add_alias(&normalize_name_from_sql(name), plan_ref_id)?; @@ -466,7 +476,7 @@ impl Ast for AbstractSyntaxTree { } Type::Column => { let ast_child_id = node.children.first().ok_or_else(|| { - QueryPlannerError::CustomError("Column has no children.".into()) + SbroadError::UnexpectedNumberOfValues("Column has no children.".into()) })?; let plan_child_id = map.get(*ast_child_id)?; map.add(*id, plan_child_id); @@ -480,7 +490,7 @@ impl Ast for AbstractSyntaxTree { let plan_id = if rows.get(&plan_child_id).is_some() { let plan_inner_expr = plan.get_expression_node(plan_child_id)?; *plan_inner_expr.get_row_list()?.first().ok_or_else(|| { - QueryPlannerError::CustomError("Row is empty.".into()) + SbroadError::UnexpectedNumberOfValues("Row is empty.".into()) })? } else { plan_child_id @@ -501,14 +511,13 @@ impl Ast for AbstractSyntaxTree { | Type::NotEq | Type::NotIn => { let ast_left_id = node.children.first().ok_or_else(|| { - QueryPlannerError::CustomError( - "Left node id is not found among comparison children.".into(), - ) + SbroadError::UnexpectedNumberOfValues("Comparison has no children.".into()) })?; let plan_left_id = plan.as_row(map.get(*ast_left_id)?, &mut rows)?; let ast_right_id = node.children.get(1).ok_or_else(|| { - QueryPlannerError::CustomError( - "Right node id is not found among comparison children.".into(), + SbroadError::NotFound( + Entity::Node, + "that is right node with index 1 among comparison children".into(), ) })?; let plan_right_id = plan.as_row(map.get(*ast_right_id)?, &mut rows)?; @@ -518,7 +527,10 @@ impl Ast for AbstractSyntaxTree { } Type::IsNull | Type::IsNotNull => { let ast_child_id = node.children.first().ok_or_else(|| { - QueryPlannerError::CustomError(format!("{:?} has no children.", &node.rule)) + SbroadError::UnexpectedNumberOfValues(format!( + "{:?} has no children.", + &node.rule + )) })?; let plan_child_id = plan.as_row(map.get(*ast_child_id)?, &mut rows)?; let op = Unary::from_node_type(&node.rule)?; @@ -528,21 +540,18 @@ impl Ast for AbstractSyntaxTree { Type::Between => { // left BETWEEN center AND right let ast_left_id = node.children.first().ok_or_else(|| { - QueryPlannerError::CustomError( - "Left node id is not found among between children.".into(), - ) + SbroadError::UnexpectedNumberOfValues("Between has no children.".into()) })?; let plan_left_id = plan.as_row(map.get(*ast_left_id)?, &mut rows)?; let ast_center_id = node.children.get(1).ok_or_else(|| { - QueryPlannerError::CustomError( - "Center node id is not found among between children.".into(), + SbroadError::NotFound( + Entity::Node, + "(center) among between children".into(), ) })?; let plan_center_id = plan.as_row(map.get(*ast_center_id)?, &mut rows)?; let ast_right_id = node.children.get(2).ok_or_else(|| { - QueryPlannerError::CustomError( - "Right node id is not found among between children.".into(), - ) + SbroadError::NotFound(Entity::Node, "(right) among between children".into()) })?; let plan_right_id = plan.as_row(map.get(*ast_right_id)?, &mut rows)?; @@ -554,20 +563,22 @@ impl Ast for AbstractSyntaxTree { } Type::Cast => { let ast_child_id = node.children.first().ok_or_else(|| { - QueryPlannerError::CustomError("Condition has no children.".into()) + SbroadError::UnexpectedNumberOfValues("Condition has no children.".into()) })?; let plan_child_id = map.get(*ast_child_id)?; let ast_type_id = node.children.get(1).ok_or_else(|| { - QueryPlannerError::CustomError( - "Cast type node id is not found among cast children.".into(), + SbroadError::NotFound( + Entity::Node, + "(Cast type) among cast children".into(), ) })?; let ast_type = self.nodes.get_node(*ast_type_id)?; let cast_type = if ast_type.rule == Type::TypeVarchar { // Get the length of the varchar. let ast_len_id = ast_type.children.first().ok_or_else(|| { - QueryPlannerError::CustomError( - "Cast type length node id is not found among cast children.".into(), + SbroadError::UnexpectedNumberOfValues( + "Cast has no children. Cast type length node id is not found." + .into(), ) })?; let ast_len = self.nodes.get_node(*ast_len_id)?; @@ -575,14 +586,16 @@ impl Ast for AbstractSyntaxTree { .value .as_ref() .ok_or_else(|| { - QueryPlannerError::CustomError("Varchar length is empty".into()) + SbroadError::UnexpectedNumberOfValues( + "Varchar length is empty".into(), + ) })? .parse::<usize>() .map_err(|e| { - QueryPlannerError::CustomError(format!( - "Failed to parse varchar length: {:?}", - e - )) + SbroadError::ParsingError( + Entity::Value, + format!("failed to parse varchar length: {e:?}"), + ) })?; Ok(CastType::Varchar(len)) } else { @@ -593,15 +606,11 @@ impl Ast for AbstractSyntaxTree { } Type::Concat => { let ast_left_id = node.children.first().ok_or_else(|| { - QueryPlannerError::CustomError( - "Left node id is not found among concat children.".into(), - ) + SbroadError::UnexpectedNumberOfValues("Concat has no children.".into()) })?; let plan_left_id = plan.as_row(map.get(*ast_left_id)?, &mut rows)?; let ast_right_id = node.children.get(1).ok_or_else(|| { - QueryPlannerError::CustomError( - "Right node id is not found among concat children.".into(), - ) + SbroadError::NotFound(Entity::Node, "(right) among concat children".into()) })?; let plan_right_id = plan.as_row(map.get(*ast_right_id)?, &mut rows)?; let concat_id = plan.add_concat(plan_left_id, plan_right_id)?; @@ -609,7 +618,7 @@ impl Ast for AbstractSyntaxTree { } Type::Condition => { let ast_child_id = node.children.first().ok_or_else(|| { - QueryPlannerError::CustomError("Condition has no children.".into()) + SbroadError::UnexpectedNumberOfValues("Condition has no children.".into()) })?; let plan_child_id = map.get(*ast_child_id)?; map.add(*id, plan_child_id); @@ -623,7 +632,7 @@ impl Ast for AbstractSyntaxTree { } let function_name = self.nodes.get_node(*first)?.value.as_ref().ok_or_else(|| { - QueryPlannerError::CustomError("Function name is not found.".into()) + SbroadError::NotFound(Entity::Name, "of sql function".into()) })?; let func = metadata.get_function(function_name)?; if func.is_stable() { @@ -632,33 +641,30 @@ impl Ast for AbstractSyntaxTree { } else { // At the moment we don't support any non-stable functions. // Later this code block should handle other function behaviors. - return Err(QueryPlannerError::CustomError(format!( - "Function {} is not stable.", - function_name - ))); + return Err(SbroadError::Invalid( + Entity::SQLFunction, + Some(format!("function {function_name} is not stable.")), + )); } } else { - return Err(QueryPlannerError::CustomError( - "Function has no children.".into(), + return Err(SbroadError::UnexpectedNumberOfValues( + "function has no children.".into(), )); } } Type::InnerJoin => { let ast_left_id = node.children.first().ok_or_else(|| { - QueryPlannerError::CustomError( - "Left node id is not found among join children.".into(), - ) + SbroadError::UnexpectedNumberOfValues("Join has no children.".into()) })?; let plan_left_id = map.get(*ast_left_id)?; let ast_right_id = node.children.get(1).ok_or_else(|| { - QueryPlannerError::CustomError( - "Right node id is not found among join children.".into(), - ) + SbroadError::NotFound(Entity::Node, "(right) among Join children.".into()) })?; let plan_right_id = map.get(*ast_right_id)?; let ast_cond_id = node.children.get(2).ok_or_else(|| { - QueryPlannerError::CustomError( - "Condition node id is not found among join children.".into(), + SbroadError::NotFound( + Entity::Node, + "(Condition) among Join children".into(), ) })?; let plan_cond_id = map.get(*ast_cond_id)?; @@ -667,14 +673,13 @@ impl Ast for AbstractSyntaxTree { } Type::Selection => { let ast_child_id = node.children.first().ok_or_else(|| { - QueryPlannerError::CustomError( - "Child node id is not found among selection children.".into(), - ) + SbroadError::UnexpectedNumberOfValues("Selection has no children.".into()) })?; let plan_child_id = map.get(*ast_child_id)?; let ast_filter_id = node.children.get(1).ok_or_else(|| { - QueryPlannerError::CustomError( - "Filter node id is not found among selection children.".into(), + SbroadError::NotFound( + Entity::Node, + "(Filter) among Selection children".into(), ) })?; let plan_filter_id = map.get(*ast_filter_id)?; @@ -683,9 +688,7 @@ impl Ast for AbstractSyntaxTree { } Type::Projection => { let ast_child_id = node.children.first().ok_or_else(|| { - QueryPlannerError::CustomError( - "Child node id is not found among projection children.".into(), - ) + SbroadError::UnexpectedNumberOfValues("Projection has no children.".into()) })?; let plan_child_id = map.get(*ast_child_id)?; let mut columns: Vec<usize> = Vec::new(); @@ -695,9 +698,8 @@ impl Ast for AbstractSyntaxTree { Type::Column => { let ast_alias_id = *ast_column.children.first().ok_or_else(|| { - QueryPlannerError::CustomError( - "Alias node id is not found among column children." - .into(), + SbroadError::UnexpectedNumberOfValues( + "Column has no children.".into(), ) })?; let plan_alias_id = map.get(ast_alias_id)?; @@ -712,17 +714,23 @@ impl Ast for AbstractSyntaxTree { columns.push(*row_id); } } else { - return Err(QueryPlannerError::CustomError( - "A plan node corresponding to asterisk is not a row." - .into(), + return Err(SbroadError::Invalid( + Entity::Node, + Some( + "a plan node corresponding to asterisk is not a Row." + .into(), + ), )); } } _ => { - return Err(QueryPlannerError::CustomError(format!( - "Expected a column in projection, got {:?}.", - ast_column.rule - ))); + return Err(SbroadError::Invalid( + Entity::Type, + Some(format!( + "expected a Column in projection, got {:?}.", + ast_column.rule + )), + )); } } } @@ -731,15 +739,11 @@ impl Ast for AbstractSyntaxTree { } Type::Except => { let ast_left_id = node.children.first().ok_or_else(|| { - QueryPlannerError::CustomError( - "Left node id is not found among except children.".into(), - ) + SbroadError::UnexpectedNumberOfValues("Except has no children.".into()) })?; let plan_left_id = map.get(*ast_left_id)?; let ast_right_id = node.children.get(1).ok_or_else(|| { - QueryPlannerError::CustomError( - "Right node id is not found among except children.".into(), - ) + SbroadError::NotFound(Entity::Node, "(right) among Except children".into()) })?; let plan_right_id = map.get(*ast_right_id)?; let plan_except_id = plan.add_except(plan_left_id, plan_right_id)?; @@ -747,14 +751,13 @@ impl Ast for AbstractSyntaxTree { } Type::UnionAll => { let ast_left_id = node.children.first().ok_or_else(|| { - QueryPlannerError::CustomError( - "Left node id is not found among union all children.".into(), - ) + SbroadError::UnexpectedNumberOfValues("Union All has no children.".into()) })?; let plan_left_id = map.get(*ast_left_id)?; let ast_right_id = node.children.get(1).ok_or_else(|| { - QueryPlannerError::CustomError( - "Right node id is not found among union all children.".into(), + SbroadError::NotFound( + Entity::Node, + "(right) among Union All children".into(), ) })?; let plan_right_id = map.get(*ast_right_id)?; @@ -763,7 +766,7 @@ impl Ast for AbstractSyntaxTree { } Type::ValuesRow => { let ast_child_id = node.children.first().ok_or_else(|| { - QueryPlannerError::CustomError("Values row has no children.".into()) + SbroadError::UnexpectedNumberOfValues("Values Row has no children.".into()) })?; let plan_child_id = map.get(*ast_child_id)?; let values_row_id = plan.add_values_row(plan_child_id, &mut col_idx)?; @@ -780,27 +783,24 @@ impl Ast for AbstractSyntaxTree { } Type::Insert => { let ast_table_id = node.children.first().ok_or_else(|| { - QueryPlannerError::CustomError( - "Table node id is not found among insert children.".into(), - ) + SbroadError::UnexpectedNumberOfValues("Insert has no children.".into()) })?; let ast_table = self.nodes.get_node(*ast_table_id)?; if let Type::Table = ast_table.rule { } else { - return Err(QueryPlannerError::CustomError(format!( - "Expected a table in insert, got {:?}.", - ast_table - ))); + return Err(SbroadError::Invalid( + Entity::Type, + Some(format!("expected a Table in insert, got {ast_table:?}.",)), + )); } let relation: &str = ast_table.value.as_ref().ok_or_else(|| { - QueryPlannerError::CustomError( - "Table name was not found in the AST.".into(), - ) + SbroadError::NotFound(Entity::Name, "of table in the AST".into()) })?; let ast_child_id = node.children.get(1).ok_or_else(|| { - QueryPlannerError::CustomError( - "Second child is not found among insert children.".into(), + SbroadError::NotFound( + Entity::Node, + "(second child) among insert children".into(), ) })?; let ast_child = self.nodes.get_node(*ast_child_id)?; @@ -810,20 +810,23 @@ impl Ast for AbstractSyntaxTree { for col_id in &ast_child.children { let col = self.nodes.get_node(*col_id)?; if let Type::ColumnName = col.rule { - col_names.push(col.value.as_ref().ok_or_else(|| - QueryPlannerError::CustomError( - "Column name was not found among the AST target columns (insert).".into(), - ))?); + col_names.push(col.value.as_ref().ok_or_else(|| { + SbroadError::NotFound( + Entity::Name, + "of Column among the AST target columns (insert)".into(), + ) + })?); } else { - return Err(QueryPlannerError::CustomError(format!( - "Expected a column name in insert, got {:?}.", - col - ))); + return Err(SbroadError::Invalid( + Entity::Type, + Some(format!("expected a Column name in insert, got {col:?}.")), + )); } } let ast_rel_child_id = node.children.get(2).ok_or_else(|| { - QueryPlannerError::CustomError( - "Third child is not found among insert children.".into(), + SbroadError::NotFound( + Entity::Node, + "(third child) among Insert children".into(), ) })?; let plan_rel_child_id = map.get(*ast_rel_child_id)?; @@ -839,7 +842,7 @@ impl Ast for AbstractSyntaxTree { plan.mark_as_explain(); let ast_child_id = node.children.first().ok_or_else(|| { - QueryPlannerError::CustomError("Explain has no children.".into()) + SbroadError::UnexpectedNumberOfValues("Explain has no children.".into()) })?; map.add(0, map.get(*ast_child_id)?); } @@ -863,19 +866,19 @@ impl Ast for AbstractSyntaxTree { | Type::TypeUnsigned | Type::TypeVarchar => {} rule => { - return Err(QueryPlannerError::CustomError(format!( - "Not implements type: {:?}", - rule - ))); + return Err(SbroadError::NotImplemented( + Entity::Type, + format!("{rule:?}"), + )); } } } // get root node id - let plan_top_id = map.get( - self.top - .ok_or_else(|| QueryPlannerError::CustomError("No top in AST.".into()))?, - )?; + let plan_top_id = map + .get(self.top.ok_or_else(|| { + SbroadError::Invalid(Entity::AST, Some("no top in AST".into())) + })?)?; plan.set_top(plan_top_id)?; let replaces = plan.replace_sq_with_references()?; plan.fix_betweens(&betweens, &replaces)?; @@ -886,11 +889,7 @@ impl Ast for AbstractSyntaxTree { impl Plan { /// Wrap references, constants, functions, concatenations and casts in the plan into rows. - fn as_row( - &mut self, - expr_id: usize, - rows: &mut HashSet<usize>, - ) -> Result<usize, QueryPlannerError> { + fn as_row(&mut self, expr_id: usize, rows: &mut HashSet<usize>) -> Result<usize, SbroadError> { if let Node::Expression( Expression::Reference { .. } | Expression::Constant { .. } diff --git a/sbroad-core/src/frontend/sql/ast.rs b/sbroad-core/src/frontend/sql/ast.rs index 1f954ee95a..98fad9d621 100644 --- a/sbroad-core/src/frontend/sql/ast.rs +++ b/sbroad-core/src/frontend/sql/ast.rs @@ -12,7 +12,7 @@ use pest::iterators::Pair; use serde::{Deserialize, Serialize}; use traversal::DftPost; -use crate::errors::QueryPlannerError; +use crate::errors::{Action, Entity, SbroadError}; /// Parse tree #[derive(Parser)] @@ -94,7 +94,7 @@ pub enum Type { impl Type { #[allow(dead_code)] - fn from_rule(rule: Rule) -> Result<Self, QueryPlannerError> { + fn from_rule(rule: Rule) -> Result<Self, SbroadError> { match rule { Rule::Alias => Ok(Type::Alias), Rule::AliasName => Ok(Type::AliasName), @@ -161,7 +161,10 @@ impl Type { Rule::Value => Ok(Type::Value), Rule::Values => Ok(Type::Values), Rule::ValuesRow => Ok(Type::ValuesRow), - _ => Err(QueryPlannerError::InvalidAst), + _ => Err(SbroadError::Invalid( + Entity::AST, + Some("got unexpected rule".into()), + )), } } } @@ -176,7 +179,7 @@ pub struct ParseNode { #[allow(dead_code)] impl ParseNode { - pub(super) fn new(rule: Rule, value: Option<String>) -> Result<Self, QueryPlannerError> { + pub(super) fn new(rule: Rule, value: Option<String>) -> Result<Self, SbroadError> { Ok(ParseNode { children: vec![], rule: Type::from_rule(rule)?, @@ -204,18 +207,23 @@ impl ParseNodes { /// /// # Errors /// - Failed to get a node from arena. - pub fn get_node(&self, node: usize) -> Result<&ParseNode, QueryPlannerError> { - self.arena.get(node).ok_or(QueryPlannerError::InvalidNode) + pub fn get_node(&self, node: usize) -> Result<&ParseNode, SbroadError> { + self.arena.get(node).ok_or_else(|| { + SbroadError::NotFound(Entity::Node, format!("from arena with index {node}")) + }) } /// Get a mutable node from arena /// /// # Errors /// - Failed to get a node from arena. - pub fn get_mut_node(&mut self, node: usize) -> Result<&mut ParseNode, QueryPlannerError> { - self.arena - .get_mut(node) - .ok_or(QueryPlannerError::InvalidNode) + pub fn get_mut_node(&mut self, node: usize) -> Result<&mut ParseNode, SbroadError> { + self.arena.get_mut(node).ok_or_else(|| { + SbroadError::NotFound( + Entity::Node, + format!("(mutable) from arena with index {node}"), + ) + }) } /// Push a new node to arena @@ -243,17 +251,15 @@ impl ParseNodes { /// /// # Errors /// - Failed to retrieve node from arena. - pub fn add_child( - &mut self, - node: Option<usize>, - child: usize, - ) -> Result<(), QueryPlannerError> { + pub fn add_child(&mut self, node: Option<usize>, child: usize) -> Result<(), SbroadError> { if let Some(parent) = node { self.get_node(child)?; - let parent_node = self - .arena - .get_mut(parent) - .ok_or(QueryPlannerError::InvalidNode)?; + let parent_node = self.arena.get_mut(parent).ok_or_else(|| { + SbroadError::NotFound( + Entity::Node, + format!("(mutable) from arena with index {parent}"), + ) + })?; parent_node.children.insert(0, child); } Ok(()) @@ -263,15 +269,13 @@ impl ParseNodes { /// /// # Errors /// - Target node is present in the arena. - pub fn update_value( - &mut self, - node: usize, - value: Option<String>, - ) -> Result<(), QueryPlannerError> { - let mut node = self - .arena - .get_mut(node) - .ok_or(QueryPlannerError::InvalidNode)?; + pub fn update_value(&mut self, node: usize, value: Option<String>) -> Result<(), SbroadError> { + let mut node = self.arena.get_mut(node).ok_or_else(|| { + SbroadError::NotFound( + Entity::Node, + format!("(mutable) from arena with index {node}"), + ) + })?; node.value = value; Ok(()) } @@ -310,7 +314,7 @@ impl AbstractSyntaxTree { /// /// # Errors /// - The new top is not in the arena. - pub fn set_top(&mut self, top: usize) -> Result<(), QueryPlannerError> { + pub fn set_top(&mut self, top: usize) -> Result<(), SbroadError> { self.nodes.get_node(top)?; self.top = Some(top); Ok(()) @@ -320,26 +324,33 @@ impl AbstractSyntaxTree { /// /// # Errors /// - AST tree doesn't have a top node. - pub fn get_top(&self) -> Result<usize, QueryPlannerError> { - self.top - .ok_or_else(|| QueryPlannerError::CustomError("No top node found in AST".to_string())) + pub fn get_top(&self) -> Result<usize, SbroadError> { + self.top.ok_or_else(|| { + SbroadError::Invalid(Entity::AST, Some("no top node found in AST".into())) + }) } /// Serialize AST from YAML. /// /// # Errors /// - Failed to parse YAML. - pub fn from_yaml(s: &str) -> Result<Self, QueryPlannerError> { + pub fn from_yaml(s: &str) -> Result<Self, SbroadError> { let ast: AbstractSyntaxTree = match serde_yaml::from_str(s) { Ok(p) => p, - Err(_) => return Err(QueryPlannerError::Serialization), + Err(e) => { + return Err(SbroadError::FailedTo( + Action::Serialize, + Some(Entity::AST), + format!("{e:?}"), + )) + } }; Ok(ast) } /// `Select` node is not IR-friendly as it can have up to five children. /// Transform this node in IR-way (to a binary sub-tree). - pub(super) fn transform_select(&mut self) -> Result<(), QueryPlannerError> { + pub(super) fn transform_select(&mut self) -> Result<(), SbroadError> { let mut selects: HashSet<usize> = HashSet::new(); for (id, node) in self.nodes.arena.iter().enumerate() { if node.rule == Type::Select { @@ -354,7 +365,7 @@ impl AbstractSyntaxTree { 3 => self.transform_select_3(*node, &children)?, 4 => self.transform_select_4(*node, &children)?, 5 => self.transform_select_5(*node, &children)?, - _ => return Err(QueryPlannerError::InvalidAst), + _ => return Err(SbroadError::Invalid(Entity::AST, None)), } } @@ -371,15 +382,14 @@ impl AbstractSyntaxTree { for (parent_id, children_pos) in parents { let parent = self.nodes.get_node(parent_id)?; let child_id = *parent.children.get(children_pos).ok_or_else(|| { - QueryPlannerError::CustomError( - "Parent node doesn't contain a child at expected position.".into(), + SbroadError::NotFound( + Entity::Node, + format!("at expected position {}", children_pos), ) })?; let child = self.nodes.get_node(child_id)?; let mut node_id = *child.children.first().ok_or_else(|| { - QueryPlannerError::CustomError( - "Selection node doesn't contain any children.".into(), - ) + SbroadError::UnexpectedNumberOfValues("Selection node has no children.".into()) })?; let parent = self.nodes.get_mut_node(parent_id)?; swap(&mut parent.children[children_pos], &mut node_id); @@ -389,7 +399,7 @@ impl AbstractSyntaxTree { if selects.contains(&top_id) { let top = self.nodes.get_node(top_id)?; let child_id = *top.children.first().ok_or_else(|| { - QueryPlannerError::CustomError( + SbroadError::UnexpectedNumberOfValues( "Selection node doesn't contain any children.".into(), ) })?; @@ -403,38 +413,53 @@ impl AbstractSyntaxTree { &mut self, select_id: usize, children: &[usize], - ) -> Result<(), QueryPlannerError> { + ) -> Result<(), SbroadError> { if children.len() != 2 { - return Err(QueryPlannerError::InvalidInput); + return Err(SbroadError::UnexpectedNumberOfValues(format!( + "expect children list len 2, got {}", + children.len() + ))); } // Check that the second child is `Scan`. - let scan_id: usize = *children.get(1).ok_or(QueryPlannerError::ValueOutOfRange)?; + let scan_id: usize = *children.get(1).ok_or_else(|| { + SbroadError::NotFound(Entity::Node, "from children list with index 1".into()) + })?; let scan = self.nodes.get_node(scan_id)?; if scan.rule != Type::Scan { - return Err(QueryPlannerError::InvalidAst); + return Err(SbroadError::Invalid( + Entity::AST, + Some("scan.rule is not Scan type".into()), + )); } // Check that the first child is `Projection`. - let proj_id: usize = *children.first().ok_or(QueryPlannerError::ValueOutOfRange)?; - let proj = self - .nodes - .arena - .get_mut(proj_id) - .ok_or(QueryPlannerError::ValueOutOfRange)?; + let proj_id: usize = *children.first().ok_or_else(|| { + SbroadError::UnexpectedNumberOfValues("children list is empty".into()) + })?; + let proj = self.nodes.arena.get_mut(proj_id).ok_or_else(|| { + SbroadError::NotFound( + Entity::Node, + format!("(mutable) from arena with index {proj_id}"), + ) + })?; if proj.rule != Type::Projection { - return Err(QueryPlannerError::InvalidAst); + return Err(SbroadError::Invalid( + Entity::AST, + Some("proj.rule is not Projection type".into()), + )); } // Append `Scan` to the `Projection` children (zero position) proj.children.insert(0, scan_id); // Leave `Projection` the only child of `Select`. - let mut select = self - .nodes - .arena - .get_mut(select_id) - .ok_or(QueryPlannerError::ValueOutOfRange)?; + let mut select = self.nodes.arena.get_mut(select_id).ok_or_else(|| { + SbroadError::NotFound( + Entity::Node, + format!("(mutable) from arena with index {select_id}"), + ) + })?; select.children = vec![proj_id]; Ok(()) @@ -445,52 +470,73 @@ impl AbstractSyntaxTree { &mut self, select_id: usize, children: &[usize], - ) -> Result<(), QueryPlannerError> { + ) -> Result<(), SbroadError> { if children.len() != 3 { - return Err(QueryPlannerError::InvalidInput); + return Err(SbroadError::UnexpectedNumberOfValues(format!( + "expect children list len 3, got {}", + children.len() + ))); } // Check that the second child is `Scan`. - let scan_id: usize = *children.get(1).ok_or(QueryPlannerError::ValueOutOfRange)?; + let scan_id: usize = *children.get(1).ok_or_else(|| { + SbroadError::NotFound(Entity::Node, "from children list with index 1".into()) + })?; let scan = self.nodes.get_node(scan_id)?; if scan.rule != Type::Scan { - return Err(QueryPlannerError::InvalidAst); + return Err(SbroadError::Invalid( + Entity::AST, + Some("scan.rule is not Scan type".into()), + )); } // Check that the third child is `Selection`. - let selection_id: usize = *children.get(2).ok_or(QueryPlannerError::ValueOutOfRange)?; - let selection = self - .nodes - .arena - .get_mut(selection_id) - .ok_or(QueryPlannerError::ValueOutOfRange)?; + let selection_id: usize = *children.get(2).ok_or_else(|| { + SbroadError::NotFound(Entity::Node, "from children list with index 2".into()) + })?; + let selection = self.nodes.arena.get_mut(selection_id).ok_or_else(|| { + SbroadError::NotFound( + Entity::Node, + format!("(mutable) from arena with index {selection_id}"), + ) + })?; if selection.rule != Type::Selection { - return Err(QueryPlannerError::InvalidAst); + return Err(SbroadError::Invalid( + Entity::AST, + Some("selection.rule is not Selection type".into()), + )); } // Append `Scan` to the `Selection` children (zero position) selection.children.insert(0, scan_id); // Check that the first child is `Projection`. - let proj_id: usize = *children.first().ok_or(QueryPlannerError::ValueOutOfRange)?; - let proj = self - .nodes - .arena - .get_mut(proj_id) - .ok_or(QueryPlannerError::ValueOutOfRange)?; + let proj_id: usize = *children.first().ok_or_else(|| { + SbroadError::UnexpectedNumberOfValues("children list is empty".into()) + })?; + let proj = self.nodes.arena.get_mut(proj_id).ok_or_else(|| { + SbroadError::NotFound( + Entity::Node, + format!("(mutable) from arena with index {proj_id}"), + ) + })?; if proj.rule != Type::Projection { - return Err(QueryPlannerError::InvalidAst); + return Err(SbroadError::Invalid( + Entity::AST, + Some("proj.rule is not Projection type".into()), + )); } // Append `Selection` to the `Projection` children (zero position) proj.children.insert(0, selection_id); // Leave `Projection` the only child of `Select`. - let mut select = self - .nodes - .arena - .get_mut(select_id) - .ok_or(QueryPlannerError::ValueOutOfRange)?; + let mut select = self.nodes.arena.get_mut(select_id).ok_or_else(|| { + SbroadError::NotFound( + Entity::Node, + format!("(mutable) from arena with index {select_id}"), + ) + })?; select.children = vec![proj_id]; Ok(()) @@ -501,34 +547,53 @@ impl AbstractSyntaxTree { &mut self, select_id: usize, children: &[usize], - ) -> Result<(), QueryPlannerError> { + ) -> Result<(), SbroadError> { if children.len() != 4 { - return Err(QueryPlannerError::InvalidInput); + return Err(SbroadError::UnexpectedNumberOfValues(format!( + "expect children list len 4, got {}", + children.len() + ))); } // Check that the second child is `Scan`. - let scan_id: usize = *children.get(1).ok_or(QueryPlannerError::ValueOutOfRange)?; + let scan_id: usize = *children.get(1).ok_or_else(|| { + SbroadError::NotFound(Entity::Node, "from children list with index 1".into()) + })?; let scan = self.nodes.get_node(scan_id)?; if scan.rule != Type::Scan { - return Err(QueryPlannerError::InvalidAst); + return Err(SbroadError::Invalid( + Entity::AST, + Some("scan.rule is not Scan type".into()), + )); } // Check that the forth child is `Condition`. - let cond_id: usize = *children.get(3).ok_or(QueryPlannerError::ValueOutOfRange)?; + let cond_id: usize = *children.get(3).ok_or_else(|| { + SbroadError::NotFound(Entity::Node, "from children list with index 3".into()) + })?; let cond = self.nodes.get_node(cond_id)?; if cond.rule != Type::Condition { - return Err(QueryPlannerError::InvalidAst); + return Err(SbroadError::Invalid( + Entity::AST, + Some("cond.rule is not Condition type".into()), + )); } // Check that the third child is `InnerJoin`. - let join_id: usize = *children.get(2).ok_or(QueryPlannerError::ValueOutOfRange)?; - let join = self - .nodes - .arena - .get_mut(join_id) - .ok_or(QueryPlannerError::ValueOutOfRange)?; + let join_id: usize = *children + .get(2) + .ok_or_else(|| SbroadError::NotFound(Entity::Node, "with index 2".into()))?; + let join = self.nodes.arena.get_mut(join_id).ok_or_else(|| { + SbroadError::NotFound( + Entity::Node, + format!("(mutable) from arena with index {join_id}"), + ) + })?; if join.rule != Type::InnerJoin { - return Err(QueryPlannerError::InvalidAst); + return Err(SbroadError::Invalid( + Entity::AST, + Some("join.rule is not InnerJoin type".into()), + )); } // Push `Condition` (forth child) to the end of th `InnerJoin` children list. @@ -538,25 +603,32 @@ impl AbstractSyntaxTree { join.children.insert(0, scan_id); // Check that the first child is `Projection`. - let proj_id: usize = *children.first().ok_or(QueryPlannerError::ValueOutOfRange)?; - let proj = self - .nodes - .arena - .get_mut(proj_id) - .ok_or(QueryPlannerError::ValueOutOfRange)?; + let proj_id: usize = *children.first().ok_or_else(|| { + SbroadError::UnexpectedNumberOfValues("children list is empty".into()) + })?; + let proj = self.nodes.arena.get_mut(proj_id).ok_or_else(|| { + SbroadError::NotFound( + Entity::Node, + format!("(mutable) from arena with index {proj_id}"), + ) + })?; if proj.rule != Type::Projection { - return Err(QueryPlannerError::InvalidAst); + return Err(SbroadError::Invalid( + Entity::AST, + Some("proj.rule is not Projection type".into()), + )); } // Append `InnerJoin` to the `Projection` children (zero position) proj.children.insert(0, join_id); // Leave `Projection` the only child of `Select`. - let mut select = self - .nodes - .arena - .get_mut(select_id) - .ok_or(QueryPlannerError::ValueOutOfRange)?; + let mut select = self.nodes.arena.get_mut(select_id).ok_or_else(|| { + SbroadError::NotFound( + Entity::Node, + format!("(mutable) from arena with index {select_id}"), + ) + })?; select.children = vec![proj_id]; Ok(()) @@ -567,34 +639,53 @@ impl AbstractSyntaxTree { &mut self, select_id: usize, children: &[usize], - ) -> Result<(), QueryPlannerError> { + ) -> Result<(), SbroadError> { if children.len() != 5 { - return Err(QueryPlannerError::InvalidInput); + return Err(SbroadError::UnexpectedNumberOfValues(format!( + "expect children list len 5, got {}", + children.len() + ))); } // Check that the second child is `Scan`. - let scan_id: usize = *children.get(1).ok_or(QueryPlannerError::ValueOutOfRange)?; + let scan_id: usize = *children.get(1).ok_or_else(|| { + SbroadError::NotFound(Entity::Node, "from children list with index 1".into()) + })?; let scan = self.nodes.get_node(scan_id)?; if scan.rule != Type::Scan { - return Err(QueryPlannerError::InvalidAst); + return Err(SbroadError::Invalid( + Entity::AST, + Some("scan.rule is not Scan type".into()), + )); } // Check that the forth child is `Condition`. - let cond_id: usize = *children.get(3).ok_or(QueryPlannerError::ValueOutOfRange)?; + let cond_id: usize = *children.get(3).ok_or_else(|| { + SbroadError::NotFound(Entity::Node, "from children list with index 3".into()) + })?; let cond = self.nodes.get_node(cond_id)?; if cond.rule != Type::Condition { - return Err(QueryPlannerError::InvalidAst); + return Err(SbroadError::Invalid( + Entity::AST, + Some("cond.rule is not Condition type".into()), + )); } // Check that the third child is `InnerJoin`. - let join_id: usize = *children.get(2).ok_or(QueryPlannerError::ValueOutOfRange)?; - let join = self - .nodes - .arena - .get_mut(join_id) - .ok_or(QueryPlannerError::ValueOutOfRange)?; + let join_id: usize = *children.get(2).ok_or_else(|| { + SbroadError::NotFound(Entity::Node, "from children list with index 2".into()) + })?; + let join = self.nodes.arena.get_mut(join_id).ok_or_else(|| { + SbroadError::NotFound( + Entity::Node, + format!("(mutable) from arena with index {join_id}"), + ) + })?; if join.rule != Type::InnerJoin { - return Err(QueryPlannerError::InvalidAst); + return Err(SbroadError::Invalid( + Entity::AST, + Some("join.rule is not InnerJoin type".into()), + )); } // Push `Condition` (forth child) to the end of the `InnerJoin` children list. @@ -604,39 +695,52 @@ impl AbstractSyntaxTree { join.children.insert(0, scan_id); // Check that the fifth child is `Selection`. - let selection_id: usize = *children.get(4).ok_or(QueryPlannerError::ValueOutOfRange)?; - let selection = self - .nodes - .arena - .get_mut(selection_id) - .ok_or(QueryPlannerError::ValueOutOfRange)?; + let selection_id: usize = *children.get(4).ok_or_else(|| { + SbroadError::NotFound(Entity::Node, "from children list with index 4".into()) + })?; + let selection = self.nodes.arena.get_mut(selection_id).ok_or_else(|| { + SbroadError::NotFound( + Entity::Node, + format!("(mutable) from arena with index {selection_id}"), + ) + })?; if selection.rule != Type::Selection { - return Err(QueryPlannerError::InvalidAst); + return Err(SbroadError::Invalid( + Entity::AST, + Some("selection.rule is not Selection type".into()), + )); } // Append `InnerJoin` to the `Selection` children (zero position) selection.children.insert(0, join_id); // Check that the first child is `Projection`. - let proj_id: usize = *children.first().ok_or(QueryPlannerError::ValueOutOfRange)?; - let proj = self - .nodes - .arena - .get_mut(proj_id) - .ok_or(QueryPlannerError::ValueOutOfRange)?; + let proj_id: usize = *children.first().ok_or_else(|| { + SbroadError::UnexpectedNumberOfValues("children list is empty".into()) + })?; + let proj = self.nodes.arena.get_mut(proj_id).ok_or_else(|| { + SbroadError::NotFound( + Entity::Node, + format!("(mutable) from arena with index {proj_id}"), + ) + })?; if proj.rule != Type::Projection { - return Err(QueryPlannerError::InvalidAst); + return Err(SbroadError::Invalid( + Entity::AST, + Some("proj.rule is not Projection type".into()), + )); } // Append `Selection` to the `Projection` children (zero position) proj.children.insert(0, selection_id); // Leave `Projection` the only child of `Select`. - let mut select = self - .nodes - .arena - .get_mut(select_id) - .ok_or(QueryPlannerError::ValueOutOfRange)?; + let mut select = self.nodes.arena.get_mut(select_id).ok_or_else(|| { + SbroadError::NotFound( + Entity::Node, + format!("(mutable) from arena with index {select_id}"), + ) + })?; select.children = vec![proj_id]; Ok(()) } @@ -645,7 +749,7 @@ impl AbstractSyntaxTree { /// /// # Errors /// - columns are invalid - pub(super) fn add_aliases_to_projection(&mut self) -> Result<(), QueryPlannerError> { + pub(super) fn add_aliases_to_projection(&mut self) -> Result<(), SbroadError> { let mut columns: Vec<(usize, Option<String>)> = Vec::new(); // Collect projection columns and their names. for (_, node) in self.nodes.arena.iter().enumerate() { @@ -655,9 +759,7 @@ impl AbstractSyntaxTree { let child = self.nodes.get_node(*child_id)?; if let Type::Column = child.rule { let col_child_id = *child.children.first().ok_or_else(|| { - QueryPlannerError::CustomError( - "Column doesn't have any children".into(), - ) + SbroadError::UnexpectedNumberOfValues("Column has no children".into()) })?; let col_child = self.nodes.get_node(col_child_id)?; match &col_child.rule { @@ -674,8 +776,9 @@ impl AbstractSyntaxTree { { *col_name_id } else { - return Err(QueryPlannerError::CustomError( - "Column doesn't have any children".into(), + return Err(SbroadError::NotFound( + Entity::Node, + "that is first child of the Column".into(), )); }; let col_name = self.nodes.get_node(col_name_id)?; @@ -683,8 +786,9 @@ impl AbstractSyntaxTree { .value .as_ref() .ok_or_else(|| { - QueryPlannerError::CustomError( - "Column name is empty".into(), + SbroadError::Invalid( + Entity::Name, + Some("of Column is empty".into()), ) })? .clone(); @@ -702,12 +806,13 @@ impl AbstractSyntaxTree { for (id, name) in columns { let node = self.nodes.get_node(id)?; if node.rule != Type::Column { - return Err(QueryPlannerError::CustomError( - "Parsed node is not a column.".into(), + return Err(SbroadError::Invalid( + Entity::Node, + Some("Parsed node is not a column.".into()), )); } let child_id = *node.children.first().ok_or_else(|| { - QueryPlannerError::CustomError("Column doesn't have any children".into()) + SbroadError::UnexpectedNumberOfValues("Column has no children".into()) })?; let child = self.nodes.get_node(child_id)?; if let Type::Alias | Type::Asterisk = child.rule { @@ -731,7 +836,7 @@ impl AbstractSyntaxTree { /// /// # Errors /// - Projection, selection and inner join nodes don't have valid children. - pub(super) fn build_ref_to_relation_map(&mut self) -> Result<(), QueryPlannerError> { + pub(super) fn build_ref_to_relation_map(&mut self) -> Result<(), SbroadError> { let mut map: HashMap<usize, Vec<usize>> = HashMap::new(); // Traverse relational nodes in Post Order and then enter their subtrees // and map expressions to relational nodes. @@ -742,8 +847,8 @@ impl AbstractSyntaxTree { match rel_node.rule { Type::Projection => { let rel_id = rel_node.children.first().ok_or_else(|| { - QueryPlannerError::CustomError( - "AST projection doesn't have any children.".into(), + SbroadError::UnexpectedNumberOfValues( + "AST Projection has no children.".into(), ) })?; for top in rel_node.children.iter().skip(1) { @@ -760,13 +865,14 @@ impl AbstractSyntaxTree { } Type::Selection => { let rel_id = rel_node.children.first().ok_or_else(|| { - QueryPlannerError::CustomError( - "AST selection doesn't have any children.".into(), + SbroadError::UnexpectedNumberOfValues( + "AST selection has no children.".into(), ) })?; let filter = rel_node.children.get(1).ok_or_else(|| { - QueryPlannerError::CustomError( - "AST selection doesn't have a filter child.".into(), + SbroadError::NotFound( + Entity::Node, + "that is AST selection filter child with index 1".into(), ) })?; let subtree = DftPost::new(filter, |node| self.nodes.ast_iter(node)); @@ -781,18 +887,20 @@ impl AbstractSyntaxTree { } Type::InnerJoin => { let left_id = rel_node.children.first().ok_or_else(|| { - QueryPlannerError::CustomError( - "AST inner join doesn't have a left child.".into(), + SbroadError::UnexpectedNumberOfValues( + "AST inner join has no children.".into(), ) })?; let right_id = rel_node.children.get(1).ok_or_else(|| { - QueryPlannerError::CustomError( - "AST inner join doesn't have a right child.".into(), + SbroadError::NotFound( + Entity::Node, + "that is AST inner join right child with index 1".into(), ) })?; let cond_id = rel_node.children.get(2).ok_or_else(|| { - QueryPlannerError::CustomError( - "AST inner join doesn't have a condition child.".into(), + SbroadError::NotFound( + Entity::Node, + "that is AST inner join condition child with index 2".into(), ) })?; // ast_iter is not working here - we have to ignore sub-queries in the join condition. @@ -817,14 +925,11 @@ impl AbstractSyntaxTree { /// /// # Errors /// - Reference is not found. - pub fn get_referred_relational_nodes( - &self, - id: usize, - ) -> Result<Vec<usize>, QueryPlannerError> { + pub fn get_referred_relational_nodes(&self, id: usize) -> Result<Vec<usize>, SbroadError> { self.map .get(&id) .cloned() - .ok_or_else(|| QueryPlannerError::CustomError("Reference is not found.".into())) + .ok_or_else(|| SbroadError::NotFound(Entity::Relational, format!("(id {id})"))) } } diff --git a/sbroad-core/src/frontend/sql/ast/tests.rs b/sbroad-core/src/frontend/sql/ast/tests.rs index a2edc6feeb..caf4329ade 100644 --- a/sbroad-core/src/frontend/sql/ast/tests.rs +++ b/sbroad-core/src/frontend/sql/ast/tests.rs @@ -18,6 +18,12 @@ fn ast() { let s = fs::read_to_string(path).unwrap(); let expected: AbstractSyntaxTree = AbstractSyntaxTree::from_yaml(&s).unwrap(); assert_eq!(expected, ast); + + let empty_yaml = ""; + assert_eq!( + SbroadError::FailedTo(Action::Serialize, Some(Entity::AST), "EndOfStream".into(),), + AbstractSyntaxTree::from_yaml(empty_yaml).unwrap_err() + ); } #[test] @@ -148,7 +154,7 @@ fn invalid_query() { let ast = AbstractSyntaxTree::new(query).unwrap_err(); assert_eq!( format!( - r#"Parsing error: --> 1:8 + r#"rule parsing error: --> 1:8 | 1 | select a frAm t | ^--- @@ -166,13 +172,13 @@ fn invalid_condition() { let ast = AbstractSyntaxTree::new(query).unwrap_err(); assert_eq!( format!( - r#"Parsing error: --> 2:33 + r#"rule parsing error: --> 2:33 | 2 | "identification_number" = 1 "product_code" = 2 | ^--- | = expected EOI"#, ), - format!("{}", ast), + format!("{ast}"), ) } diff --git a/sbroad-core/src/frontend/sql/ir.rs b/sbroad-core/src/frontend/sql/ir.rs index 5a44249c14..5791ae48b3 100644 --- a/sbroad-core/src/frontend/sql/ir.rs +++ b/sbroad-core/src/frontend/sql/ir.rs @@ -4,7 +4,7 @@ use ahash::AHashMap; use tarantool::decimal::Decimal; use traversal::DftPost; -use crate::errors::QueryPlannerError; +use crate::errors::{Action, Entity, SbroadError}; use crate::frontend::sql::ast::{ParseNode, Type}; use crate::ir::expression::Expression; use crate::ir::helpers::RepeatableState; @@ -19,9 +19,9 @@ impl Bool { /// Creates `Bool` from ast node type. /// /// # Errors - /// Returns `QueryPlannerError` when the operator is invalid. + /// Returns `SbroadError` when the operator is invalid. #[allow(dead_code)] - pub(super) fn from_node_type(s: &Type) -> Result<Self, QueryPlannerError> { + pub(super) fn from_node_type(s: &Type) -> Result<Self, SbroadError> { match s { Type::And => Ok(Bool::And), Type::Or => Ok(Bool::Or), @@ -33,7 +33,10 @@ impl Bool { Type::LtEq => Ok(Bool::LtEq), Type::NotEq => Ok(Bool::NotEq), Type::NotIn => Ok(Bool::NotIn), - _ => Err(QueryPlannerError::InvalidBool), + _ => Err(SbroadError::Invalid( + Entity::Operator, + Some(format!("bool operator: {s:?}")), + )), } } } @@ -42,16 +45,16 @@ impl Unary { /// Creates `Unary` from ast node type. /// /// # Errors - /// Returns `QueryPlannerError` when the operator is invalid. + /// Returns `SbroadError` when the operator is invalid. #[allow(dead_code)] - pub(super) fn from_node_type(s: &Type) -> Result<Self, QueryPlannerError> { + pub(super) fn from_node_type(s: &Type) -> Result<Self, SbroadError> { match s { Type::IsNull => Ok(Unary::IsNull), Type::IsNotNull => Ok(Unary::IsNotNull), - _ => Err(QueryPlannerError::CustomError(format!( - "Invalid unary operator: {:?}", - s - ))), + _ => Err(SbroadError::Invalid( + Entity::Operator, + Some(format!("unary operator: {s:?}")), + )), } } } @@ -60,9 +63,9 @@ impl Value { /// Creates `Value` from ast node type and text. /// /// # Errors - /// Returns `QueryPlannerError` when the operator is invalid. + /// Returns `SbroadError` when the operator is invalid. #[allow(dead_code)] - pub(super) fn from_node(s: &ParseNode) -> Result<Self, QueryPlannerError> { + pub(super) fn from_node(s: &ParseNode) -> Result<Self, SbroadError> { let val = match &s.value { Some(v) => v.clone(), None => String::new(), @@ -73,25 +76,34 @@ impl Value { Type::Null => Ok(Value::Null), Type::Integer => Ok(val .parse::<i64>() - .map_err(|e| QueryPlannerError::CustomError(format!("i64 parsing error {e}")))? + .map_err(|e| { + SbroadError::ParsingError(Entity::Value, format!("i64 parsing error {e}")) + })? .into()), Type::Decimal => Ok(val .parse::<Decimal>() .map_err(|e| { - QueryPlannerError::CustomError(format!("decimal parsing error {e:?}")) + SbroadError::ParsingError(Entity::Value, format!("decimal parsing error {e:?}")) })? .into()), Type::Double => Ok(val .parse::<Double>() - .map_err(|e| QueryPlannerError::CustomError(format!("double parsing error {e}")))? + .map_err(|e| { + SbroadError::ParsingError(Entity::Value, format!("double parsing error {e}")) + })? .into()), Type::Unsigned => Ok(val .parse::<u64>() - .map_err(|e| QueryPlannerError::CustomError(format!("u64 parsing error {e}")))? + .map_err(|e| { + SbroadError::ParsingError(Entity::Value, format!("u64 parsing error {e}")) + })? .into()), Type::String => Ok(val.into()), Type::True => Ok(true.into()), - _ => Err(QueryPlannerError::UnsupportedValueType), + _ => Err(SbroadError::Unsupported( + Entity::Type, + Some("can not create Value from ParseNode".into()), + )), } } } @@ -112,12 +124,12 @@ impl Translation { self.map.insert(parse_id, plan_id); } - pub(super) fn get(&self, old: usize) -> Result<usize, QueryPlannerError> { + pub(super) fn get(&self, old: usize) -> Result<usize, SbroadError> { self.map.get(&old).copied().ok_or_else(|| { - QueryPlannerError::CustomError(format!( - "Could not find parse node [{}] in translation map", - old - )) + SbroadError::NotFound( + Entity::Node, + format!("(parse node) [{}] in translation map", old), + ) }) } } @@ -141,9 +153,7 @@ impl SubQuery { } impl Plan { - fn gather_sq_for_replacement( - &self, - ) -> Result<HashSet<SubQuery, RepeatableState>, QueryPlannerError> { + fn gather_sq_for_replacement(&self) -> Result<HashSet<SubQuery, RepeatableState>, SbroadError> { let mut set: HashSet<SubQuery, RepeatableState> = HashSet::with_hasher(RepeatableState); let top = self.get_top()?; let rel_post = DftPost::new(&top, |node| self.nodes.rel_iter(node)); @@ -182,7 +192,7 @@ impl Plan { /// Replace sub-queries with references to the sub-query. pub(super) fn replace_sq_with_references( &mut self, - ) -> Result<AHashMap<usize, usize>, QueryPlannerError> { + ) -> Result<AHashMap<usize, usize>, SbroadError> { let set = self.gather_sq_for_replacement()?; let mut replaces: AHashMap<usize, usize> = AHashMap::with_capacity(set.len()); for sq in set { @@ -197,8 +207,9 @@ impl Plan { } } _ => { - return Err(QueryPlannerError::CustomError( - "Sub-query is not in selection or join node".into(), + return Err(SbroadError::Invalid( + Entity::Relational, + Some("Sub-query is not in selection or join node".into()), )) } } @@ -220,22 +231,26 @@ impl Plan { // TODO: should we add current row_id to the set of the generated rows? let position: usize = children.iter().position(|&x| x == sq.sq).ok_or_else(|| { - QueryPlannerError::CustomError( - "Failed to generate a reference to the sub-query".into(), + SbroadError::FailedTo( + Action::Build, + None, + "a reference to the sub-query".into(), ) })?; let row_id = self.add_row_from_sub_query(&nodes, position, &names_str)?; self.replace_parent_in_subtree(row_id, None, Some(sq.relational))?; row_id } else { - return Err(QueryPlannerError::CustomError( - "Sub-query output is not a row".into(), + return Err(SbroadError::Invalid( + Entity::Expression, + Some("Sub-query output is not a row".into()), )); } } _ => { - return Err(QueryPlannerError::CustomError( - "Sub-query is not in selection or join node".into(), + return Err(SbroadError::Invalid( + Entity::Node, + Some("Sub-query is not in selection or join node".into()), )) } }; @@ -253,14 +268,16 @@ impl Plan { } else if *right == sq.sq { *right = row_id; } else { - return Err(QueryPlannerError::CustomError( - "Sub-query is not a left or right operand".into(), + return Err(SbroadError::Invalid( + Entity::Expression, + Some("Sub-query is not a left or right operand".into()), )); } replaces.insert(sq.sq, row_id); } else { - return Err(QueryPlannerError::CustomError( - "Sub-query is not in a boolean expression".into(), + return Err(SbroadError::Invalid( + Entity::Expression, + Some("Sub-query is not in a boolean expression".into()), )); } } @@ -281,7 +298,7 @@ impl Plan { &mut self, betweens: &[Between], replaces: &AHashMap<usize, usize>, - ) -> Result<(), QueryPlannerError> { + ) -> Result<(), SbroadError> { for between in betweens { let left_id: usize = if let Some(id) = replaces.get(&between.left_id) { self.clone_expr_subtree(*id)? @@ -292,15 +309,16 @@ impl Plan { if let Expression::Bool { ref mut left, .. } = less_eq_expr { *left = left_id; } else { - return Err(QueryPlannerError::CustomError( - "Expected a boolean expression".into(), + return Err(SbroadError::Invalid( + Entity::Expression, + Some("expected a boolean Expression".into()), )); } } Ok(()) } - fn clone_expr_subtree(&mut self, top_id: usize) -> Result<usize, QueryPlannerError> { + fn clone_expr_subtree(&mut self, top_id: usize) -> Result<usize, SbroadError> { let subtree = DftPost::new(&top_id, |node| self.nodes.expr_iter(node, false)); let nodes = subtree.map(|(_, node_id)| *node_id).collect::<Vec<_>>(); let mut map = HashMap::new(); @@ -313,9 +331,7 @@ impl Plan { | Expression::Cast { ref mut child, .. } | Expression::Unary { ref mut child, .. } => { *child = *map.get(child).ok_or_else(|| { - QueryPlannerError::CustomError(format!( - "Failed to clone expression subtree (id {id})" - )) + SbroadError::NotFound(Entity::SubTree, format!("(id {id})")) })?; } Expression::Bool { @@ -329,14 +345,10 @@ impl Plan { .. } => { *left = *map.get(left).ok_or_else(|| { - QueryPlannerError::CustomError(format!( - "Failed to clone expression subtree (id {id})" - )) + SbroadError::NotFound(Entity::SubTree, format!("(id {id})")) })?; *right = *map.get(right).ok_or_else(|| { - QueryPlannerError::CustomError(format!( - "Failed to clone expression subtree (id {id})" - )) + SbroadError::NotFound(Entity::SubTree, format!("(id {id})")) })?; } Expression::Row { @@ -348,9 +360,7 @@ impl Plan { } => { for child in children { *child = *map.get(child).ok_or_else(|| { - QueryPlannerError::CustomError(format!( - "Failed to clone expression subtree (id {id})" - )) + SbroadError::NotFound(Entity::SubTree, format!("(id {id})")) })?; } } diff --git a/sbroad-core/src/ir.rs b/sbroad-core/src/ir.rs index 9553dbd1ee..5d3e9c11eb 100644 --- a/sbroad-core/src/ir.rs +++ b/sbroad-core/src/ir.rs @@ -9,7 +9,7 @@ use expression::Expression; use operator::Relational; use relation::Table; -use crate::errors::QueryPlannerError; +use crate::errors::{Action, Entity, SbroadError}; use crate::ir::undo::TransformationLog; use self::parameters::Parameters; @@ -105,11 +105,10 @@ impl Nodes { /// /// # Errors /// - The node with the given position doesn't exist. - pub fn replace(&mut self, id: usize, node: Node) -> Result<Node, QueryPlannerError> { + pub fn replace(&mut self, id: usize, node: Node) -> Result<Node, SbroadError> { if id >= self.arena.len() { - return Err(QueryPlannerError::CustomError(format!( - "Can't replace node with id {} as it is out of arena bounds", - id + return Err(SbroadError::UnexpectedNumberOfValues(format!( + "can't replace node with id {id} as it is out of arena bounds" ))); } let old_node = std::mem::replace(&mut self.arena[id], node); @@ -231,13 +230,21 @@ impl Plan { /// Check that plan tree is valid. /// /// # Errors - /// Returns `QueryPlannerError` when the plan tree check fails. - pub fn check(&self) -> Result<(), QueryPlannerError> { + /// Returns `SbroadError` when the plan tree check fails. + pub fn check(&self) -> Result<(), SbroadError> { match self.top { - None => return Err(QueryPlannerError::InvalidPlan), + None => { + return Err(SbroadError::Invalid( + Entity::Plan, + Some("plan tree top is None".into()), + )) + } Some(top) => { if self.nodes.arena.get(top).is_none() { - return Err(QueryPlannerError::ValueOutOfRange); + return Err(SbroadError::NotFound( + Entity::Node, + format!("from arena with index {top}"), + )); } } } @@ -270,11 +277,14 @@ impl Plan { /// Get a node by its pointer (position in the node arena). /// /// # Errors - /// Returns `QueryPlannerError` when the node with requested index + /// Returns `SbroadError` when the node with requested index /// doesn't exist. - pub fn get_node(&self, id: usize) -> Result<&Node, QueryPlannerError> { + pub fn get_node(&self, id: usize) -> Result<&Node, SbroadError> { match self.nodes.arena.get(id) { - None => Err(QueryPlannerError::ValueOutOfRange), + None => Err(SbroadError::NotFound( + Entity::Node, + format!("from arena with index {id}"), + )), Some(node) => Ok(node), } } @@ -282,11 +292,14 @@ impl Plan { /// Get a mutable node by its pointer (position in the node arena). /// /// # Errors - /// Returns `QueryPlannerError` when the node with requested index + /// Returns `SbroadError` when the node with requested index /// doesn't exist. - pub fn get_mut_node(&mut self, id: usize) -> Result<&mut Node, QueryPlannerError> { + pub fn get_mut_node(&mut self, id: usize) -> Result<&mut Node, SbroadError> { match self.nodes.arena.get_mut(id) { - None => Err(QueryPlannerError::ValueOutOfRange), + None => Err(SbroadError::NotFound( + Entity::Node, + format!("(mutable) from arena with index {id}"), + )), Some(node) => Ok(node), } } @@ -295,8 +308,9 @@ impl Plan { /// /// # Errors /// - top node is None (i.e. invalid plan) - pub fn get_top(&self) -> Result<usize, QueryPlannerError> { - self.top.ok_or(QueryPlannerError::InvalidPlan) + pub fn get_top(&self) -> Result<usize, SbroadError> { + self.top + .ok_or_else(|| SbroadError::Invalid(Entity::Plan, Some("plan tree top is None".into()))) } /// Clone plan slices. @@ -314,11 +328,11 @@ impl Plan { /// Construct a plan from the YAML file. /// /// # Errors - /// Returns `QueryPlannerError` when the YAML plan is invalid. - pub fn from_yaml(s: &str) -> Result<Self, QueryPlannerError> { + /// Returns `SbroadError` when the YAML plan is invalid. + pub fn from_yaml(s: &str) -> Result<Self, SbroadError> { let plan: Plan = match serde_yaml::from_str(s) { Ok(p) => p, - Err(e) => return Err(QueryPlannerError::CustomError(e.to_string())), + Err(e) => return Err(SbroadError::Invalid(Entity::Plan, Some(e.to_string()))), }; plan.check()?; Ok(plan) @@ -329,7 +343,7 @@ impl Plan { /// # Errors /// - node is not relational /// - node's output is not a row of aliases - pub fn get_row_from_rel_node(&mut self, node: usize) -> Result<usize, QueryPlannerError> { + pub fn get_row_from_rel_node(&mut self, node: usize) -> Result<usize, SbroadError> { if let Node::Relational(rel) = self.get_node(node)? { if let Node::Expression(Expression::Row { list, .. }) = self.get_node(rel.output())? { let mut cols: Vec<usize> = Vec::with_capacity(list.len()); @@ -339,14 +353,18 @@ impl Plan { { cols.push(*child); } else { - return Err(QueryPlannerError::InvalidNode); + return Err(SbroadError::Invalid( + Entity::Node, + Some("node's output is not a row of aliases".into()), + )); } } return Ok(self.nodes.add_row(cols, None)); } } - Err(QueryPlannerError::CustomError( - "Node is not relational".into(), + Err(SbroadError::Invalid( + Entity::Node, + Some("node is not Relational type".into()), )) } @@ -358,13 +376,13 @@ impl Plan { /// Add condition node to the plan. /// /// # Errors - /// Returns `QueryPlannerError` when the condition node can't append'. + /// Returns `SbroadError` when the condition node can't append'. pub fn add_cond( &mut self, left: usize, op: operator::Bool, right: usize, - ) -> Result<usize, QueryPlannerError> { + ) -> Result<usize, SbroadError> { self.nodes.add_bool(left, op, right) } @@ -372,11 +390,7 @@ impl Plan { /// /// # Errors /// - Child node is invalid - pub fn add_unary( - &mut self, - op: operator::Unary, - child: usize, - ) -> Result<usize, QueryPlannerError> { + pub fn add_unary(&mut self, op: operator::Unary, child: usize) -> Result<usize, SbroadError> { self.nodes.add_unary_bool(op, child) } @@ -394,7 +408,7 @@ impl Plan { /// Set top node of plan /// # Errors /// - top node doesn't exist in the plan. - pub fn set_top(&mut self, top: usize) -> Result<(), QueryPlannerError> { + pub fn set_top(&mut self, top: usize) -> Result<(), SbroadError> { self.get_node(top)?; self.top = Some(top); Ok(()) @@ -405,11 +419,12 @@ impl Plan { /// # Errors /// - node doesn't exist in the plan /// - node is not a relational type - pub fn get_relation_node(&self, node_id: usize) -> Result<&Relational, QueryPlannerError> { + pub fn get_relation_node(&self, node_id: usize) -> Result<&Relational, SbroadError> { match self.get_node(node_id)? { Node::Relational(rel) => Ok(rel), - Node::Expression(_) | Node::Parameter => Err(QueryPlannerError::CustomError( - "Node isn't relational".into(), + Node::Expression(_) | Node::Parameter => Err(SbroadError::Invalid( + Entity::Node, + Some("node is not Relational type".into()), )), } } @@ -422,11 +437,12 @@ impl Plan { pub fn get_mut_relation_node( &mut self, node_id: usize, - ) -> Result<&mut Relational, QueryPlannerError> { + ) -> Result<&mut Relational, SbroadError> { match self.get_mut_node(node_id)? { Node::Relational(rel) => Ok(rel), - Node::Expression(_) | Node::Parameter => Err(QueryPlannerError::CustomError( - "Node isn't relational".into(), + Node::Expression(_) | Node::Parameter => Err(SbroadError::Invalid( + Entity::Node, + Some("Node is not relational".into()), )), } } @@ -436,7 +452,7 @@ impl Plan { /// # Errors /// - node doesn't exist in the plan /// - node is not expression type - pub fn get_expression_node(&self, node_id: usize) -> Result<&Expression, QueryPlannerError> { + pub fn get_expression_node(&self, node_id: usize) -> Result<&Expression, SbroadError> { match self.get_node(node_id)? { Node::Expression(exp) => Ok(exp), Node::Parameter => { @@ -444,13 +460,15 @@ impl Plan { if let Some(Node::Expression(exp)) = node { Ok(exp) } else { - Err(QueryPlannerError::CustomError( - "Parameter node does not refer to an expression".into(), + Err(SbroadError::Invalid( + Entity::Node, + Some("parameter node does not refer to an expression".into()), )) } } - Node::Relational(_) => Err(QueryPlannerError::CustomError( - "Node isn't expression".into(), + Node::Relational(_) => Err(SbroadError::Invalid( + Entity::Node, + Some("node is not Expression type".into()), )), } } @@ -463,11 +481,12 @@ impl Plan { pub fn get_mut_expression_node( &mut self, node_id: usize, - ) -> Result<&mut Expression, QueryPlannerError> { + ) -> Result<&mut Expression, SbroadError> { match self.get_mut_node(node_id)? { Node::Expression(exp) => Ok(exp), - Node::Relational(_) | Node::Parameter => Err(QueryPlannerError::CustomError( - "Node isn't expression".into(), + Node::Relational(_) | Node::Parameter => Err(SbroadError::Invalid( + Entity::Node, + Some("node is not expression type".into()), )), } } @@ -478,10 +497,7 @@ impl Plan { /// - node doesn't exist in the plan /// - node is not `Reference` /// - invalid references between nodes - pub fn get_alias_from_reference_node( - &self, - node: &Expression, - ) -> Result<&str, QueryPlannerError> { + pub fn get_alias_from_reference_node(&self, node: &Expression) -> Result<&str, SbroadError> { if let Expression::Reference { targets, position, @@ -491,7 +507,7 @@ impl Plan { let ref_node = if let Some(parent) = parent { self.get_relation_node(*parent)? } else { - return Err(QueryPlannerError::CustomError( + return Err(SbroadError::UnexpectedNumberOfValues( "Reference node has no parent".into(), )); }; @@ -499,17 +515,18 @@ impl Plan { // In a case of insert we don't inspect children output tuple // but rather use target relation columns. if let Relational::Insert { ref relation, .. } = ref_node { - let rel = self.relations.get(relation).ok_or_else(|| { - QueryPlannerError::CustomError(format!("Relation {relation} doesn't exist")) - })?; + let rel = self + .relations + .get(relation) + .ok_or_else(|| SbroadError::NotFound(Entity::Table, relation.to_string()))?; let col_name = rel .columns .get(*position) .ok_or_else(|| { - QueryPlannerError::CustomError(format!( - "Relation {} has no column {}", - relation, position - )) + SbroadError::NotFound( + Entity::Table, + "{relation}'s column {position}".into(), + ) })? .name .as_str(); @@ -518,19 +535,22 @@ impl Plan { if let Some(list_of_column_nodes) = ref_node.children() { let child_ids = targets.as_ref().ok_or_else(|| { - QueryPlannerError::CustomError("Node refs to scan node, not alias".into()) + SbroadError::Invalid( + Entity::Target, + Some("node refs to scan node, not alias".into()), + ) })?; let column_index_in_list = child_ids.first().ok_or_else(|| { - QueryPlannerError::CustomError("Invalid child index in target".into()) + SbroadError::UnexpectedNumberOfValues("Target has no children".into()) })?; let col_idx_in_rel = list_of_column_nodes .get(*column_index_in_list) .ok_or_else(|| { - QueryPlannerError::CustomError(format!( - "Not found column node with index {}", - column_index_in_list - )) + SbroadError::NotFound( + Entity::Node, + format!("type Column with index {column_index_in_list}"), + ) })?; let column_rel_node = self.get_relation_node(*col_idx_in_rel)?; @@ -541,23 +561,34 @@ impl Plan { .get_row_list()? .get(*position) .ok_or_else(|| { - QueryPlannerError::CustomError("Invalid position in row list".into()) + SbroadError::NotFound( + Entity::Column, + format!("at position {} in row list", position), + ) })?; let col_alias_node = self.get_expression_node(*col_alias_idx)?; match col_alias_node { Expression::Alias { name, .. } => return Ok(name), - _ => return Err(QueryPlannerError::CustomError("Expected alias node".into())), + _ => { + return Err(SbroadError::Invalid( + Entity::Expression, + Some("expected alias node".into()), + )) + } } } - return Err(QueryPlannerError::CustomError( - "Failed to get a referred relational node".into(), + return Err(SbroadError::FailedTo( + Action::Get, + None, + "a referred relational node".into(), )); } - Err(QueryPlannerError::CustomError( - "Node is not of a reference type".into(), + Err(SbroadError::Invalid( + Entity::Node, + Some("node is not of a reference type".into()), )) } @@ -568,30 +599,34 @@ impl Plan { /// # Errors /// - serialization error (to binary) - pub fn pattern_id(&self) -> Result<String, QueryPlannerError> { + pub fn pattern_id(&self) -> Result<String, SbroadError> { let mut bytes: Vec<u8> = bincode::serialize(&self.nodes).map_err(|e| { - QueryPlannerError::CustomError(format!( - "Failed to serialize plan nodes to binary: {:?}", - e - )) + SbroadError::FailedTo( + Action::Serialize, + None, + format!("plan nodes to binary: {e:?}"), + ) })?; let mut relation_bytes: Vec<u8> = bincode::serialize(&self.relations).map_err(|e| { - QueryPlannerError::CustomError(format!( - "Failed to serialize plan relations to binary: {:?}", - e - )) + SbroadError::FailedTo( + Action::Serialize, + None, + format!("plan relations to binary: {e:?}"), + ) })?; let mut slice_bytes: Vec<u8> = bincode::serialize(&self.slices).map_err(|e| { - QueryPlannerError::CustomError(format!( - "Failed to serialize plan slices to binary: {:?}", - e - )) + SbroadError::FailedTo( + Action::Serialize, + None, + format!("plan slices to binary: {e:?}"), + ) })?; let mut top_bytes: Vec<u8> = bincode::serialize(&self.top).map_err(|e| { - QueryPlannerError::CustomError(format!( - "Failed to serialize plan top to binary: {:?}", - e - )) + SbroadError::FailedTo( + Action::Serialize, + None, + format!("plan top to binary: {e:?}"), + ) })?; bytes.append(&mut relation_bytes); bytes.append(&mut slice_bytes); diff --git a/sbroad-core/src/ir/api/constant.rs b/sbroad-core/src/ir/api/constant.rs index d2108fae34..1f314bb17d 100644 --- a/sbroad-core/src/ir/api/constant.rs +++ b/sbroad-core/src/ir/api/constant.rs @@ -1,4 +1,4 @@ -use crate::errors::QueryPlannerError; +use crate::errors::{Entity, SbroadError}; use crate::ir::expression::Expression; use crate::ir::value::Value; use crate::ir::{Node, Nodes, Plan}; @@ -8,13 +8,14 @@ impl Expression { /// /// # Errors /// - node isn't constant type - pub fn as_const_value(&self) -> Result<Value, QueryPlannerError> { + pub fn as_const_value(&self) -> Result<Value, SbroadError> { if let Expression::Constant { value } = self.clone() { return Ok(value); } - Err(QueryPlannerError::CustomError( - "Node isn't const type".into(), + Err(SbroadError::Invalid( + Entity::Node, + Some("node is not Const type".into()), )) } @@ -22,13 +23,14 @@ impl Expression { /// /// # Errors /// - node isn't constant type - pub fn as_const_value_ref(&self) -> Result<&Value, QueryPlannerError> { + pub fn as_const_value_ref(&self) -> Result<&Value, SbroadError> { if let Expression::Constant { value } = self { return Ok(value); } - Err(QueryPlannerError::CustomError( - "Node isn't const type".into(), + Err(SbroadError::Invalid( + Entity::Node, + Some("node is not Const type".into()), )) } @@ -72,14 +74,17 @@ impl Plan { /// /// # Errors /// - The parameters map is corrupted (parameters map points to invalid nodes). - pub fn restore_constants(&mut self) -> Result<(), QueryPlannerError> { + pub fn restore_constants(&mut self) -> Result<(), SbroadError> { for (id, const_node) in self.constants.drain() { if let Node::Expression(Expression::Constant { .. }) = const_node { } else { - return Err(QueryPlannerError::CustomError(format!( - "Restoring parameters filed: node {:?} (id: {}) is not of a constant type", - const_node, id - ))); + return Err(SbroadError::Invalid( + Entity::Expression, + Some(format!( + "Restoring parameters filed: node {:?} (id: {}) is not of a constant type", + const_node, id + )), + )); } self.nodes.replace(id, const_node)?; } @@ -90,7 +95,7 @@ impl Plan { /// /// # Errors /// - The plan is corrupted (collected constants point to invalid arena positions). - pub fn stash_constants(&mut self) -> Result<(), QueryPlannerError> { + pub fn stash_constants(&mut self) -> Result<(), SbroadError> { let constants = self.get_const_list(); for const_id in constants { let const_node = self.nodes.replace(const_id, Node::Parameter)?; diff --git a/sbroad-core/src/ir/api/parameter.rs b/sbroad-core/src/ir/api/parameter.rs index 212c8a9c34..6921c38d23 100644 --- a/sbroad-core/src/ir/api/parameter.rs +++ b/sbroad-core/src/ir/api/parameter.rs @@ -1,4 +1,4 @@ -use crate::errors::QueryPlannerError; +use crate::errors::{Entity, SbroadError}; use crate::ir::expression::Expression; use crate::ir::operator::Relational; use crate::ir::value::Value; @@ -41,7 +41,7 @@ impl Plan { /// - Internal errors. #[allow(clippy::too_many_lines)] #[otm_child_span("plan.bind")] - pub fn bind_params(&mut self, mut params: Vec<Value>) -> Result<(), QueryPlannerError> { + pub fn bind_params(&mut self, mut params: Vec<Value>) -> Result<(), SbroadError> { // Nothing to do here. if params.is_empty() { return Ok(()); @@ -68,12 +68,9 @@ impl Plan { let param_set = self.get_param_set(); // Closure to retrieve a corresponding value for a parameter node. - let get_value = |pos: usize| -> Result<usize, QueryPlannerError> { + let get_value = |pos: usize| -> Result<usize, SbroadError> { let val_id = value_ids.get(pos).ok_or_else(|| { - QueryPlannerError::CustomError(format!( - "Parameter in position {} is not found.", - pos - )) + SbroadError::NotFound(Entity::Node, format!("(Parameter) in position {pos}")) })?; Ok(*val_id) }; @@ -154,9 +151,9 @@ impl Plan { } } - let get_row = |idx: usize| -> Result<usize, QueryPlannerError> { + let get_row = |idx: usize| -> Result<usize, SbroadError> { let row_id = row_ids.get(&idx).ok_or_else(|| { - QueryPlannerError::CustomError(format!("Row in position {idx} is not found.")) + SbroadError::NotFound(Entity::Node, format!("(Row) at position {idx}")) })?; Ok(*row_id) }; diff --git a/sbroad-core/src/ir/distribution.rs b/sbroad-core/src/ir/distribution.rs index 2d6ef68cbb..0cbfbc0f26 100644 --- a/sbroad-core/src/ir/distribution.rs +++ b/sbroad-core/src/ir/distribution.rs @@ -6,7 +6,7 @@ use std::collections::{HashMap, HashSet}; use serde::{Deserialize, Serialize}; use crate::collection; -use crate::errors::QueryPlannerError; +use crate::errors::{Entity, SbroadError}; use super::expression::Expression; use super::operator::Relational; @@ -219,15 +219,15 @@ impl Plan { /// Calculate and set tuple distribution. /// /// # Errors - /// Returns `QueryPlannerError` when current expression is not a `Row` or contains broken references. + /// Returns `SbroadError` when current expression is not a `Row` or contains broken references. #[allow(clippy::too_many_lines)] - pub fn set_distribution(&mut self, row_id: usize) -> Result<(), QueryPlannerError> { + pub fn set_distribution(&mut self, row_id: usize) -> Result<(), SbroadError> { let row_children = self.get_expression_node(row_id)?.get_row_list()?; // There are two kinds of rows: constructed from aliases (projections) // and constructed from any other expressions (selection filters, join conditions, etc). // This closure returns the proper child id for the row. - let row_child_id = |col_id: usize| -> Result<usize, QueryPlannerError> { + let row_child_id = |col_id: usize| -> Result<usize, SbroadError> { match self.get_expression_node(col_id) { Ok(Expression::Alias { child, .. }) => Ok(*child), Ok(_) => Ok(col_id), @@ -268,14 +268,17 @@ impl Plan { { // As the row is located in the branch relational node, the targets should be non-empty. let targets = targets.as_ref().ok_or_else(|| { - QueryPlannerError::CustomError("Reference targets are empty".to_string()) + SbroadError::UnexpectedNumberOfValues( + "Reference targets are empty".to_string(), + ) })?; ref_map.reserve(targets.len()); ref_nodes.reserve(targets.len()); for target in targets { let referred_id = *parent_children.get(*target).ok_or_else(|| { - QueryPlannerError::CustomError( - "The reference points to invalid column".to_string(), + SbroadError::NotFound( + Entity::Expression, + "reference points to invalid column".to_string(), ) })?; ref_nodes.append(referred_id); @@ -307,8 +310,9 @@ impl Plan { ReferredNodes::None => { // We should never get here as we have already handled the case // when there are no references in the row (a row of constants). - return Err(QueryPlannerError::CustomError( - "The row contains no references".to_string(), + return Err(SbroadError::Invalid( + Entity::Expression, + Some("the row contains no references".to_string()), )); } ReferredNodes::Single(child_id) => { @@ -329,7 +333,7 @@ impl Plan { self.set_two_children_node_dist(&ref_map, n1, n2, parent_id, row_id)?; } ReferredNodes::Multiple(_) => { - return Err(QueryPlannerError::CustomError( + return Err(SbroadError::DuplicatedValue( "Row contains multiple references to the same node (and in is not VALUES)" .to_string(), )); @@ -346,8 +350,9 @@ impl Plan { } = self.get_expression_node(child_id)? { if targets.is_some() { - return Err(QueryPlannerError::CustomError( - "References to the children targets in the leaf (relation scan) node are not supported".to_string(), + return Err(SbroadError::Invalid( + Entity::Expression, + Some("References to the children targets in the leaf (relation scan) node are not supported".to_string()), )); } table_map.insert(*position, pos); @@ -358,8 +363,9 @@ impl Plan { let table_name: String = if let Relational::ScanRelation { relation, .. } = parent { relation.clone() } else { - return Err(QueryPlannerError::CustomError( - "The parent node is not a relation scan node".to_string(), + return Err(SbroadError::Invalid( + Entity::Node, + Some("the parent node is not a relation scan node".to_string()), )); }; self.set_scan_dist(&table_name, &table_map, row_id)?; @@ -373,7 +379,7 @@ impl Plan { &self, child_rel_node: usize, child_pos_map: &HashMap<(usize, usize), usize, RandomState>, - ) -> Result<Distribution, QueryPlannerError> { + ) -> Result<Distribution, SbroadError> { if let Node::Relational(relational_op) = self.get_node(child_rel_node)? { if let Node::Expression(Expression::Row { distribution: child_dist, @@ -381,7 +387,12 @@ impl Plan { }) = self.get_node(relational_op.output())? { match child_dist { - None => return Err(QueryPlannerError::UninitializedDistribution), + None => { + return Err(SbroadError::Invalid( + Entity::Distribution, + Some("distribution is uninitialized".into()), + )) + } Some(Distribution::Any) => return Ok(Distribution::Any), Some(Distribution::Replicated) => return Ok(Distribution::Replicated), Some(Distribution::Segment { keys }) => { @@ -412,14 +423,14 @@ impl Plan { } } } - Err(QueryPlannerError::InvalidRow) + Err(SbroadError::Invalid(Entity::Relational, None)) } /// Sets row distribution to replicated. /// /// # Errors /// - Node is not of a row type. - pub fn set_const_dist(&mut self, row_id: usize) -> Result<(), QueryPlannerError> { + pub fn set_const_dist(&mut self, row_id: usize) -> Result<(), SbroadError> { if let Expression::Row { ref mut distribution, .. @@ -430,8 +441,9 @@ impl Plan { } return Ok(()); } - Err(QueryPlannerError::CustomError( - "The node is not a row type".to_string(), + Err(SbroadError::Invalid( + Entity::Expression, + Some("the node is not a row type".to_string()), )) } @@ -440,11 +452,13 @@ impl Plan { table_name: &str, table_pos_map: &HashMap<usize, usize, RandomState>, row_id: usize, - ) -> Result<(), QueryPlannerError> { - let table: &Table = self - .relations - .get(table_name) - .ok_or(QueryPlannerError::InvalidRelation)?; + ) -> Result<(), SbroadError> { + let table: &Table = self.relations.get(table_name).ok_or_else(|| { + SbroadError::NotFound( + Entity::Table, + format!("{} among plan relations", table_name), + ) + })?; let mut new_key: Key = Key::new(Vec::new()); let all_found = table.key.positions.iter().all(|pos| { table_pos_map.get(pos).map_or(false, |v| { @@ -472,7 +486,7 @@ impl Plan { right_id: usize, parent_id: usize, row_id: usize, - ) -> Result<(), QueryPlannerError> { + ) -> Result<(), SbroadError> { let left_dist = self.dist_from_child(left_id, child_pos_map)?; let right_dist = self.dist_from_child(right_id, child_pos_map)?; @@ -483,8 +497,9 @@ impl Plan { } Relational::InnerJoin { .. } => Distribution::join(&left_dist, &right_dist), _ => { - return Err(QueryPlannerError::CustomError( - "Invalid row: expected Except, UnionAll or InnerJoin".to_string(), + return Err(SbroadError::Invalid( + Entity::Relational, + Some("expected Except, UnionAll or InnerJoin".to_string()), )) } }; @@ -496,8 +511,9 @@ impl Plan { { *distribution = Some(new_dist); } else { - return Err(QueryPlannerError::CustomError( - "Invalid row: expected Row".to_string(), + return Err(SbroadError::Invalid( + Entity::Expression, + Some("expected Row".to_string()), )); }; @@ -508,15 +524,19 @@ impl Plan { /// /// # Errors /// - Node is not of a row type. - pub fn get_distribution(&self, row_id: usize) -> Result<&Distribution, QueryPlannerError> { + pub fn get_distribution(&self, row_id: usize) -> Result<&Distribution, SbroadError> { match self.get_node(row_id)? { Node::Expression(expr) => expr.distribution(), - Node::Relational(_) => Err(QueryPlannerError::CustomError( - "Failed to get distribution for a relational node (try its row output tuple)." - .to_string(), + Node::Relational(_) => Err(SbroadError::Invalid( + Entity::Distribution, + Some( + "Failed to get distribution for a relational node (try its row output tuple)." + .to_string(), + ), )), - Node::Parameter => Err(QueryPlannerError::CustomError( - "Failed to get distribution for a parameter node.".to_string(), + Node::Parameter => Err(SbroadError::Invalid( + Entity::Distribution, + Some("Failed to get distribution for a parameter node.".to_string()), )), } } @@ -530,8 +550,8 @@ impl Plan { pub fn get_or_init_distribution( &mut self, row_id: usize, - ) -> Result<&Distribution, QueryPlannerError> { - if let Err(QueryPlannerError::UninitializedDistribution) = self.get_distribution(row_id) { + ) -> Result<&Distribution, SbroadError> { + if let Err(SbroadError::Invalid(Entity::Distribution, _)) = self.get_distribution(row_id) { self.set_distribution(row_id)?; } self.get_distribution(row_id) diff --git a/sbroad-core/src/ir/explain.rs b/sbroad-core/src/ir/explain.rs index 1a7edaaa7e..b3312ab4f4 100644 --- a/sbroad-core/src/ir/explain.rs +++ b/sbroad-core/src/ir/explain.rs @@ -5,7 +5,7 @@ use itertools::Itertools; use serde::Serialize; use traversal::DftPost; -use crate::errors::QueryPlannerError; +use crate::errors::{Entity, SbroadError}; use crate::ir::expression::cast::Type as CastType; use crate::ir::expression::Expression; use crate::ir::operator::Relational; @@ -56,7 +56,7 @@ impl Default for ColExpr { impl ColExpr { #[allow(dead_code)] - fn new(plan: &Plan, subtree_top: usize) -> Result<Self, QueryPlannerError> { + fn new(plan: &Plan, subtree_top: usize) -> Result<Self, SbroadError> { let dft_post = DftPost::new(&subtree_top, |node| plan.nodes.expr_iter(node, false)); let mut stack: Vec<ColExpr> = Vec::new(); @@ -66,8 +66,8 @@ impl ColExpr { match ¤t_node { Expression::Cast { to, .. } => { let expr = stack.pop().ok_or_else(|| { - QueryPlannerError::CustomError( - "Failed to pop from stack while processing CAST expression".to_string(), + SbroadError::UnexpectedNumberOfValues( + "stack is empty while processing CAST expression".to_string(), ) })?; let cast_expr = ColExpr::Cast(Box::new(expr), to.clone()); @@ -91,13 +91,13 @@ impl ColExpr { } Expression::Concat { .. } => { let right = stack.pop().ok_or_else(|| { - QueryPlannerError::CustomError( - "Failed to pop right child from stack while processing CONCAT expression".to_string(), + SbroadError::UnexpectedNumberOfValues( + "stack is empty while processing CONCAT expression".to_string(), ) })?; let left = stack.pop().ok_or_else(|| { - QueryPlannerError::CustomError( - "Failed to pop left child from stack while processing CONCAT expression".to_string(), + SbroadError::UnexpectedNumberOfValues( + "stack is empty while processing CONCAT expression".to_string(), ) })?; let concat_expr = ColExpr::Concat(Box::new(left), Box::new(right)); @@ -109,8 +109,8 @@ impl ColExpr { } Expression::StableFunction { name, .. } => { let expr = stack.pop().ok_or_else(|| { - QueryPlannerError::CustomError( - "Failed to pop from stack while processing STABLE FUNCTION expression" + SbroadError::UnexpectedNumberOfValues( + "stack is empty while processing STABLE FUNCTION expression" .to_string(), ) })?; @@ -122,8 +122,8 @@ impl ColExpr { let mut row: Vec<ColExpr> = Vec::with_capacity(len); while len > 0 { let expr = stack.pop().ok_or_else(|| { - QueryPlannerError::CustomError( - format!("Failed to pop {len} element from stack while processing ROW expression"), + SbroadError::UnexpectedNumberOfValues( + format!("stack is empty, expected to pop {len} element while processing ROW expression"), ) })?; row.push(expr); @@ -133,17 +133,19 @@ impl ColExpr { stack.push(row_expr); } Expression::Alias { .. } | Expression::Bool { .. } | Expression::Unary { .. } => { - return Err(QueryPlannerError::CustomError(format!( - "Column expression node [{:?}] is not supported for yet", - current_node - ))); + return Err(SbroadError::Unsupported( + Entity::Expression, + Some(format!( + "Column expression node [{current_node:?}] is not supported for yet" + )), + )); } } } stack .pop() - .ok_or_else(|| QueryPlannerError::CustomError("Failed to pop from stack".to_string())) + .ok_or_else(|| SbroadError::UnexpectedNumberOfValues("stack is empty".to_string())) } } @@ -158,7 +160,7 @@ struct Col { impl Col { #[allow(dead_code)] - fn new(plan: &Plan, subtree_top: usize) -> Result<Self, QueryPlannerError> { + fn new(plan: &Plan, subtree_top: usize) -> Result<Self, SbroadError> { let mut column = Col::default(); let dft_post = DftPost::new(&subtree_top, |node| plan.nodes.expr_iter(node, true)); @@ -196,7 +198,7 @@ struct Projection { impl Projection { #[allow(dead_code)] - fn new(plan: &Plan, output_id: usize) -> Result<Self, QueryPlannerError> { + fn new(plan: &Plan, output_id: usize) -> Result<Self, SbroadError> { let mut result = Projection { cols: vec![] }; let alias_list = plan.get_expression_node(output_id)?; @@ -314,7 +316,7 @@ impl Row { plan: &Plan, node_ids: &[usize], ref_map: &HashMap<usize, usize>, - ) -> Result<Self, QueryPlannerError> { + ) -> Result<Self, SbroadError> { let mut row = Row::new(); for child in node_ids { @@ -337,8 +339,9 @@ impl Row { row.add_col(RowVal::Column(col)); } else { let sq_offset = ref_map.get(&rel_id).ok_or_else(|| { - QueryPlannerError::CustomError( - "The sub-query was not found in the map".into(), + SbroadError::NotFound( + Entity::SubQuery, + format!("with index {rel_id} in the map"), ) })?; @@ -396,7 +399,7 @@ impl Selection { plan: &Plan, subtree_node_id: usize, ref_map: &HashMap<usize, usize>, - ) -> Result<Self, QueryPlannerError> { + ) -> Result<Self, SbroadError> { let current_node = plan.get_expression_node(subtree_node_id)?; let result = match current_node { @@ -662,7 +665,7 @@ impl Display for FullExplain { impl FullExplain { #[allow(dead_code)] #[allow(clippy::too_many_lines)] - pub fn new(ir: &Plan, top_id: usize) -> Result<Self, QueryPlannerError> { + pub fn new(ir: &Plan, top_id: usize) -> Result<Self, SbroadError> { let mut stack: Vec<ExplainTreePart> = Vec::with_capacity(ir.nodes.relation_node_amount()); let mut result = FullExplain::default(); @@ -676,7 +679,7 @@ impl FullExplain { current_node.children.push(left); current_node.children.push(right); } else { - return Err(QueryPlannerError::CustomError( + return Err(SbroadError::UnexpectedNumberOfValues( "Exception node must have exactly two children".into(), )); } @@ -685,7 +688,7 @@ impl FullExplain { Relational::Projection { output, .. } => { // TODO: change this logic when we'll enable sub-queries in projection let child = stack.pop().ok_or_else(|| { - QueryPlannerError::CustomError( + SbroadError::UnexpectedNumberOfValues( "Projection node must have exactly one child".into(), ) })?; @@ -710,8 +713,8 @@ impl FullExplain { if let Some((_, other)) = children.split_first() { for sq_id in other.iter().rev() { let sq_node = stack.pop().ok_or_else(|| { - QueryPlannerError::CustomError( - "Selection node failed to get a sub-query.".into(), + SbroadError::UnexpectedNumberOfValues( + "Selection node failed to pop a sub-query.".into(), ) })?; result.subqueries.push(sq_node); @@ -719,13 +722,13 @@ impl FullExplain { sq_ref_map.insert(*sq_id, offset); } let child = stack.pop().ok_or_else(|| { - QueryPlannerError::CustomError( + SbroadError::UnexpectedNumberOfValues( "Selection node must have exactly one child".into(), ) })?; current_node.children.push(child); } else { - return Err(QueryPlannerError::CustomError( + return Err(SbroadError::UnexpectedNumberOfValues( "Selection node doesn't have any children".into(), )); } @@ -738,7 +741,7 @@ impl FullExplain { current_node.children.push(left); current_node.children.push(right); } else { - return Err(QueryPlannerError::CustomError( + return Err(SbroadError::UnexpectedNumberOfValues( "Union all node must have exactly two children".into(), )); } @@ -746,7 +749,7 @@ impl FullExplain { } Relational::ScanSubQuery { alias, .. } => { let child = stack.pop().ok_or_else(|| { - QueryPlannerError::CustomError( + SbroadError::UnexpectedNumberOfValues( "ScanSubQuery node must have exactly one child".into(), ) })?; @@ -761,7 +764,7 @@ impl FullExplain { .. } => { let child = stack.pop().ok_or_else(|| { - QueryPlannerError::CustomError( + SbroadError::UnexpectedNumberOfValues( "Motion node must have exactly one child".into(), ) })?; @@ -770,8 +773,8 @@ impl FullExplain { let p = match policy { IrMotionPolicy::Segment(s) => { let child_id = children.first().ok_or_else(|| { - QueryPlannerError::CustomError( - "Current node should have exactly one child".to_string(), + SbroadError::UnexpectedNumberOfValues( + "current node should have exactly one child".to_string(), ) })?; @@ -784,9 +787,10 @@ impl FullExplain { .map(|r| match r { IrTarget::Reference(pos) => { let col_id = *col_list.get(*pos).ok_or_else(|| { - QueryPlannerError::CustomError(String::from( - "Invalid position in list", - )) + SbroadError::NotFound( + Entity::Target, + format!("reference with position {}", pos), + ) })?; let col_name = ir .get_expression_node(col_id)? @@ -814,7 +818,7 @@ impl FullExplain { .. } => { if children.len() < 2 { - return Err(QueryPlannerError::CustomError( + return Err(SbroadError::UnexpectedNumberOfValues( "Join must have at least two children".into(), )); } @@ -824,8 +828,8 @@ impl FullExplain { for sq_id in subquery_ids.iter().rev() { let sq_node = stack.pop().ok_or_else(|| { - QueryPlannerError::CustomError( - "Join node failed to get a sub-query.".into(), + SbroadError::UnexpectedNumberOfValues( + "Join node failed to pop a sub-query.".into(), ) })?; result.subqueries.push(sq_node); @@ -837,7 +841,7 @@ impl FullExplain { current_node.children.push(left); current_node.children.push(right); } else { - return Err(QueryPlannerError::CustomError( + return Err(SbroadError::UnexpectedNumberOfValues( "Join node must have exactly two children".into(), )); } @@ -851,8 +855,8 @@ impl FullExplain { for sq_id in children.iter().rev() { let sq_node = stack.pop().ok_or_else(|| { - QueryPlannerError::CustomError( - "Insert node failed to get a sub-query.".into(), + SbroadError::UnexpectedNumberOfValues( + "Insert node failed to pop a sub-query.".into(), ) })?; @@ -871,8 +875,8 @@ impl FullExplain { while amount_values > 0 { let value_row = stack.pop().ok_or_else(|| { - QueryPlannerError::CustomError( - "Insert node failed to get a value row.".into(), + SbroadError::UnexpectedNumberOfValues( + "Insert node failed to pop a value row.".into(), ) })?; @@ -883,8 +887,8 @@ impl FullExplain { } Relational::Insert { relation, .. } => { let values = stack.pop().ok_or_else(|| { - QueryPlannerError::CustomError( - "Insert node failed to get a value row.".into(), + SbroadError::UnexpectedNumberOfValues( + "Insert node failed to pop a value row.".into(), ) })?; @@ -897,7 +901,7 @@ impl FullExplain { } result.main_query = stack .pop() - .ok_or_else(|| QueryPlannerError::CustomError("Invalid explain top node.".into()))?; + .ok_or_else(|| SbroadError::NotFound(Entity::Node, "that is explain top".into()))?; Ok(result) } } @@ -908,7 +912,7 @@ impl Plan { /// # Errors /// - Failed to get top node /// - Failed to build explain - pub fn as_explain(&self) -> Result<String, QueryPlannerError> { + pub fn as_explain(&self) -> Result<String, SbroadError> { let top_id = self.get_top()?; let explain = FullExplain::new(self, top_id)?; Ok(explain.to_string()) diff --git a/sbroad-core/src/ir/expression.rs b/sbroad-core/src/ir/expression.rs index 496a14c5fe..ef8db278af 100644 --- a/sbroad-core/src/ir/expression.rs +++ b/sbroad-core/src/ir/expression.rs @@ -11,7 +11,7 @@ use serde::{Deserialize, Serialize}; use std::collections::{HashMap, HashSet}; use traversal::DftPost; -use crate::errors::QueryPlannerError; +use crate::errors::{Entity, SbroadError}; use crate::ir::operator::{Bool, Relational}; use super::distribution::Distribution; @@ -137,27 +137,35 @@ impl Expression { /// Gets current row distribution. /// /// # Errors - /// Returns `QueryPlannerError` when the function is called on expression + /// Returns `SbroadError` when the function is called on expression /// other than `Row` or a node doesn't know its distribution yet. - pub fn distribution(&self) -> Result<&Distribution, QueryPlannerError> { + pub fn distribution(&self) -> Result<&Distribution, SbroadError> { if let Expression::Row { distribution, .. } = self { let dist = match distribution { Some(d) => d, - None => return Err(QueryPlannerError::UninitializedDistribution), + None => { + return Err(SbroadError::Invalid( + Entity::Distribution, + Some("distribution is uninitialized".into()), + )) + } }; return Ok(dist); } - Err(QueryPlannerError::InvalidRow) + Err(SbroadError::Invalid(Entity::Expression, None)) } /// Clone the row children list. /// /// # Errors /// - node isn't `Row` - pub fn clone_row_list(&self) -> Result<Vec<usize>, QueryPlannerError> { + pub fn clone_row_list(&self) -> Result<Vec<usize>, SbroadError> { match self { Expression::Row { list, .. } => Ok(list.clone()), - _ => Err(QueryPlannerError::CustomError("Node isn't row type".into())), + _ => Err(SbroadError::Invalid( + Entity::Expression, + Some("node isn't Row type".into()), + )), } } @@ -165,10 +173,13 @@ impl Expression { /// /// # Errors /// - node isn't `Row` - pub fn get_row_list(&self) -> Result<&[usize], QueryPlannerError> { + pub fn get_row_list(&self) -> Result<&[usize], SbroadError> { match self { Expression::Row { ref list, .. } => Ok(list), - _ => Err(QueryPlannerError::CustomError("Node isn't row type".into())), + _ => Err(SbroadError::Invalid( + Entity::Expression, + Some("node isn't Row type".into()), + )), } } @@ -176,11 +187,12 @@ impl Expression { /// /// # Errors /// - node isn't `Alias` - pub fn get_alias_name(&self) -> Result<&str, QueryPlannerError> { + pub fn get_alias_name(&self) -> Result<&str, SbroadError> { match self { Expression::Alias { name, .. } => Ok(name.as_str()), - _ => Err(QueryPlannerError::CustomError( - "Node isn't alias type".into(), + _ => Err(SbroadError::Invalid( + Entity::Node, + Some("node is not Alias type".into()), )), } } @@ -189,7 +201,7 @@ impl Expression { /// /// # Errors /// - distribution isn't set - pub fn has_unknown_distribution(&self) -> Result<bool, QueryPlannerError> { + pub fn has_unknown_distribution(&self) -> Result<bool, SbroadError> { let d = self.distribution()?; Ok(d.is_unknown()) } @@ -199,13 +211,15 @@ impl Expression { /// # Errors /// - node isn't reference type /// - reference doesn't have a parent - pub fn get_parent(&self) -> Result<usize, QueryPlannerError> { + pub fn get_parent(&self) -> Result<usize, SbroadError> { if let Expression::Reference { parent, .. } = self { - return parent - .ok_or_else(|| QueryPlannerError::CustomError("Reference has no parent".into())); + return parent.ok_or_else(|| { + SbroadError::Invalid(Entity::Expression, Some("Reference has no parent".into())) + }); } - Err(QueryPlannerError::CustomError( - "Node isn't reference type".into(), + Err(SbroadError::Invalid( + Entity::Expression, + Some("node is not Reference type".into()), )) } @@ -231,12 +245,15 @@ impl Nodes { /// # Errors /// - child node is invalid /// - name is empty - pub fn add_alias(&mut self, name: &str, child: usize) -> Result<usize, QueryPlannerError> { - self.arena - .get(child) - .ok_or(QueryPlannerError::InvalidNode)?; + pub fn add_alias(&mut self, name: &str, child: usize) -> Result<usize, SbroadError> { + self.arena.get(child).ok_or_else(|| { + SbroadError::NotFound(Entity::Node, format!("from arena with index {}", child)) + })?; if name.is_empty() { - return Err(QueryPlannerError::InvalidName); + return Err(SbroadError::Invalid( + Entity::Plan, + Some(String::from("name is empty")), + )); } let alias = Expression::Alias { name: String::from(name), @@ -254,11 +271,25 @@ impl Nodes { left: usize, op: operator::Bool, right: usize, - ) -> Result<usize, QueryPlannerError> { - self.arena.get(left).ok_or(QueryPlannerError::InvalidNode)?; - self.arena - .get(right) - .ok_or(QueryPlannerError::InvalidNode)?; + ) -> Result<usize, SbroadError> { + self.arena.get(left).ok_or_else(|| { + SbroadError::NotFound( + Entity::Node, + format!( + "(left child of boolean node) from arena with index {}", + left + ), + ) + })?; + self.arena.get(right).ok_or_else(|| { + SbroadError::NotFound( + Entity::Node, + format!( + "(right child of boolean node) from arena with index {}", + right + ), + ) + })?; Ok(self.push(Node::Expression(Expression::Bool { left, op, right }))) } @@ -293,23 +324,29 @@ impl Nodes { &mut self, list: Vec<usize>, distribution: Option<Distribution>, - ) -> Result<usize, QueryPlannerError> { + ) -> Result<usize, SbroadError> { let mut names: HashSet<String> = HashSet::with_capacity(list.len()); for alias_node in &list { - if let Node::Expression(Expression::Alias { name, .. }) = self - .arena - .get(*alias_node) - .ok_or(QueryPlannerError::InvalidNode)? + if let Node::Expression(Expression::Alias { name, .. }) = + self.arena.get(*alias_node).ok_or_else(|| { + SbroadError::NotFound( + Entity::Node, + format!("(Alias) from arena with index {}", alias_node), + ) + })? { if !names.insert(String::from(name)) { - return Err(QueryPlannerError::CustomError(format!( - "Row can't be added because `{}` already has an alias", + return Err(SbroadError::DuplicatedValue(format!( + "row can't be added because `{}` already has an alias", name ))); } } else { - return Err(QueryPlannerError::InvalidRow); + return Err(SbroadError::Invalid( + Entity::Node, + Some("node is not Alias type".into()), + )); } } Ok(self.add_row(list, distribution)) @@ -323,9 +360,9 @@ impl Nodes { &mut self, op: operator::Unary, child: usize, - ) -> Result<usize, QueryPlannerError> { + ) -> Result<usize, SbroadError> { self.arena.get(child).ok_or_else(|| { - QueryPlannerError::CustomError(format!("Invalid node in the plan arena:{child}")) + SbroadError::NotFound(Entity::Node, format!("from arena with index {}", child)) })?; Ok(self.push(Node::Expression(Expression::Unary { op, child }))) } @@ -340,7 +377,7 @@ impl Plan { /// appends the right child's one to it. Otherwise we build an output tuple /// only from the first (left) child. /// # Errors - /// Returns `QueryPlannerError`: + /// Returns `SbroadError`: /// - relation node contains invalid `Row` in the output /// - targets and children are inconsistent /// - column names don't exist @@ -353,7 +390,7 @@ impl Plan { col_names: &[&str], need_aliases: bool, need_sharding_column: bool, - ) -> Result<Vec<usize>, QueryPlannerError> { + ) -> Result<Vec<usize>, SbroadError> { // We can pass two target children nodes only in case // of `UnionAll` and `InnerJoin`. // - For the `UnionAll` operator we need only the first @@ -362,16 +399,16 @@ impl Plan { // to both children to give us additional information // during transformations. if (targets.len() > 2) || targets.is_empty() { - return Err(QueryPlannerError::CustomError(format!( - "Invalid target length: {}", + return Err(SbroadError::UnexpectedNumberOfValues(format!( + "invalid target length: {}", targets.len() ))); } if let Some(max) = targets.iter().max() { if *max >= children.len() { - return Err(QueryPlannerError::CustomError(format!( - "Invalid children length: {}", + return Err(SbroadError::UnexpectedNumberOfValues(format!( + "invalid children length: {}", children.len() ))); } @@ -384,15 +421,17 @@ impl Plan { let target_child: usize = if let Some(target) = targets.get(*target_idx) { *target } else { - return Err(QueryPlannerError::CustomError( - "Failed to find the child node pointed by target index".into(), + return Err(SbroadError::NotFound( + Entity::Node, + "(child) pointed by target index".into(), )); }; let child_node: usize = if let Some(child) = children.get(target_child) { *child } else { - return Err(QueryPlannerError::CustomError( - "Child node not found".into(), + return Err(SbroadError::NotFound( + Entity::Node, + format!("pointed by target child {}", target_child), )); }; let relational_op = self.get_relation_node(child_node)?; @@ -421,7 +460,7 @@ impl Plan { { *sel_child_id } else { - return Err(QueryPlannerError::CustomError(format!( + return Err(SbroadError::UnexpectedNumberOfValues(format!( "Selection node has invalid children: {:?}", sel_child_ids ))); @@ -438,10 +477,10 @@ impl Plan { }; if let Some(relation) = table_name { let table = self.get_relation(relation).ok_or_else(|| { - QueryPlannerError::CustomError(format!( - "Failed to find table {} in the plan", - relation - )) + SbroadError::NotFound( + Entity::Table, + format!("{} among the plan relations", relation), + ) })?; let sharding_column_pos = table.get_bucket_id_position()?; // Take an advantage of the fact that the output aliases @@ -459,8 +498,9 @@ impl Plan { } } } else { - return Err(QueryPlannerError::CustomError( - "Child node is not a row".into(), + return Err(SbroadError::Invalid( + Entity::Expression, + Some("child node is not a row".into()), )); }; result.reserve(child_row_list.len()); @@ -471,8 +511,9 @@ impl Plan { { String::from(name) } else { - return Err(QueryPlannerError::CustomError( - "Child node is not an alias".into(), + return Err(SbroadError::Invalid( + Entity::Expression, + Some("child node is not an Alias".into()), )); }; let new_targets: Vec<usize> = if is_join { @@ -501,13 +542,16 @@ impl Plan { let target_child: usize = if let Some(target) = targets.first() { *target } else { - return Err(QueryPlannerError::CustomError("Target is empty".into())); + return Err(SbroadError::UnexpectedNumberOfValues( + "Target is empty".into(), + )); }; let child_node: usize = if let Some(child) = children.get(target_child) { *child } else { - return Err(QueryPlannerError::CustomError( - "Failed to get a child pointed by the target".into(), + return Err(SbroadError::NotFound( + Entity::Node, + "pointed by the target".into(), )); }; @@ -531,7 +575,7 @@ impl Plan { continue; } if map.insert(name, pos).is_some() { - return Err(QueryPlannerError::CustomError(format!( + return Err(SbroadError::DuplicatedValue(format!( "Duplicate column name {} at position {}", name, pos ))); @@ -539,8 +583,9 @@ impl Plan { } map } else { - return Err(QueryPlannerError::CustomError( - "Relational output tuple is not a row".into(), + return Err(SbroadError::Invalid( + Entity::Node, + Some("Relational output tuple is not a row".into()), )); }; @@ -552,10 +597,10 @@ impl Plan { }) }); if !all_found { - return Err(QueryPlannerError::CustomError(format!( - "Some of the columns {:?} were not found in the table", - col_names, - ))); + return Err(SbroadError::NotFound( + Entity::Column, + format!("with name {:?}", col_names), + )); } for (col, new_targets, pos) in refs { @@ -575,7 +620,7 @@ impl Plan { /// /// If column names are empty, copy all the columns from the child. /// # Errors - /// Returns `QueryPlannerError`: + /// Returns `SbroadError`: /// - child is an inconsistent relational node /// - column names don't exist pub fn add_row_for_output( @@ -583,7 +628,7 @@ impl Plan { child: usize, col_names: &[&str], need_sharding_column: bool, - ) -> Result<usize, QueryPlannerError> { + ) -> Result<usize, SbroadError> { let list = self.new_columns(&[child], false, &[0], col_names, true, need_sharding_column)?; self.nodes.add_row_of_aliases(list, None) @@ -592,13 +637,13 @@ impl Plan { /// New output row for union node. /// /// # Errors - /// Returns `QueryPlannerError`: + /// Returns `SbroadError`: /// - children are inconsistent relational nodes pub fn add_row_for_union_except( &mut self, left: usize, right: usize, - ) -> Result<usize, QueryPlannerError> { + ) -> Result<usize, SbroadError> { let list = self.new_columns(&[left, right], false, &[0, 1], &[], true, true)?; self.nodes.add_row_of_aliases(list, None) } @@ -608,13 +653,9 @@ impl Plan { /// Contains all the columns from left and right children. /// /// # Errors - /// Returns `QueryPlannerError`: + /// Returns `SbroadError`: /// - children are inconsistent relational nodes - pub fn add_row_for_join( - &mut self, - left: usize, - right: usize, - ) -> Result<usize, QueryPlannerError> { + pub fn add_row_for_join(&mut self, left: usize, right: usize) -> Result<usize, SbroadError> { let list = self.new_columns(&[left, right], true, &[0, 1], &[], true, true)?; self.nodes.add_row_of_aliases(list, None) } @@ -624,14 +665,14 @@ impl Plan { /// New columns don't have aliases. If column names are empty, /// copy all the columns from the child. /// # Errors - /// Returns `QueryPlannerError`: + /// Returns `SbroadError`: /// - child is an inconsistent relational node /// - column names don't exist pub fn add_row_from_child( &mut self, child: usize, col_names: &[&str], - ) -> Result<usize, QueryPlannerError> { + ) -> Result<usize, SbroadError> { let list = self.new_columns(&[child], false, &[0], col_names, false, true)?; Ok(self.nodes.add_row(list, None)) } @@ -641,7 +682,7 @@ impl Plan { /// New columns don't have aliases. If column names are empty, /// copy all the columns from the child. /// # Errors - /// Returns `QueryPlannerError`: + /// Returns `SbroadError`: /// - children nodes are not a relational /// - column names don't exist pub fn add_row_from_sub_query( @@ -649,7 +690,7 @@ impl Plan { children: &[usize], children_pos: usize, col_names: &[&str], - ) -> Result<usize, QueryPlannerError> { + ) -> Result<usize, SbroadError> { let list = self.new_columns(children, false, &[children_pos], col_names, false, true)?; Ok(self.nodes.add_row(list, None)) } @@ -659,7 +700,7 @@ impl Plan { /// New columns don't have aliases. If column names are empty, /// copy all the columns from the left child. /// # Errors - /// Returns `QueryPlannerError`: + /// Returns `SbroadError`: /// - children are inconsistent relational nodes /// - column names don't exist pub fn add_row_from_left_branch( @@ -667,7 +708,7 @@ impl Plan { left: usize, right: usize, col_names: &[&str], - ) -> Result<usize, QueryPlannerError> { + ) -> Result<usize, SbroadError> { let list = self.new_columns(&[left, right], true, &[0], col_names, false, true)?; Ok(self.nodes.add_row(list, None)) } @@ -677,7 +718,7 @@ impl Plan { /// New columns don't have aliases. If column names are empty, /// copy all the columns from the right child. /// # Errors - /// Returns `QueryPlannerError`: + /// Returns `SbroadError`: /// - children are inconsistent relational nodes /// - column names don't exist pub fn add_row_from_right_branch( @@ -685,7 +726,7 @@ impl Plan { left: usize, right: usize, col_names: &[&str], - ) -> Result<usize, QueryPlannerError> { + ) -> Result<usize, SbroadError> { let list = self.new_columns(&[left, right], true, &[1], col_names, false, true)?; Ok(self.nodes.add_row(list, None)) } @@ -697,10 +738,7 @@ impl Plan { /// /// # Errors /// - reference is invalid - pub fn get_relational_from_reference_node( - &self, - ref_id: usize, - ) -> Result<&usize, QueryPlannerError> { + pub fn get_relational_from_reference_node(&self, ref_id: usize) -> Result<&usize, SbroadError> { if let Node::Expression(Expression::Reference { targets, parent, .. }) = self.get_node(ref_id)? @@ -708,8 +746,9 @@ impl Plan { let referred_rel_id = if let Some(parent) = parent { parent } else { - return Err(QueryPlannerError::CustomError( - "Reference node has no parent".into(), + return Err(SbroadError::NotFound( + Entity::Node, + "that is Reference parent".into(), )); }; let rel = self.get_relation_node(*referred_rel_id)?; @@ -718,7 +757,7 @@ impl Plan { } else if let Some(children) = rel.children() { match targets { None => { - return Err(QueryPlannerError::CustomError( + return Err(SbroadError::UnexpectedNumberOfValues( "Reference node has no targets".into(), )) } @@ -735,13 +774,13 @@ impl Plan { if let Relational::Motion { .. } = rel { return Ok(referred_rel_id); } - return Err(QueryPlannerError::CustomError(format!( - "Relational node {:?} has no child at first position", + return Err(SbroadError::UnexpectedNumberOfValues(format!( + "Relational node {:?} has no children", rel ))); } _ => { - return Err(QueryPlannerError::CustomError( + return Err(SbroadError::UnexpectedNumberOfValues( "Reference expected to point exactly a single relational node" .into(), )) @@ -750,7 +789,7 @@ impl Plan { } } } - Err(QueryPlannerError::InvalidReference) + Err(SbroadError::Invalid(Entity::Expression, None)) } /// Get relational nodes referenced in the row. @@ -762,11 +801,14 @@ impl Plan { pub fn get_relational_from_row_nodes( &self, row_id: usize, - ) -> Result<HashSet<usize, RandomState>, QueryPlannerError> { + ) -> Result<HashSet<usize, RandomState>, SbroadError> { let row = self.get_expression_node(row_id)?; if let Expression::Row { .. } = row { } else { - return Err(QueryPlannerError::CustomError("Node is not a row".into())); + return Err(SbroadError::Invalid( + Entity::Node, + Some("Node is not a row".into()), + )); } let post_tree = DftPost::new(&row_id, |node| self.nodes.expr_iter(node, false)); let nodes: Vec<usize> = post_tree.map(|(_, id)| *id).collect(); @@ -778,8 +820,9 @@ impl Plan { targets, parent, .. } = reference { - let referred_rel_id = parent.ok_or(QueryPlannerError::CustomError( - "Reference node has no parent".into(), + let referred_rel_id = parent.ok_or(SbroadError::NotFound( + Entity::Node, + "that is Reference parent".into(), ))?; let rel = self.get_relation_node(referred_rel_id)?; if let Some(children) = rel.children() { @@ -831,7 +874,7 @@ impl Plan { /// /// # Errors /// - If node is not an expression. - pub fn is_trivalent(&self, expr_id: usize) -> Result<bool, QueryPlannerError> { + pub fn is_trivalent(&self, expr_id: usize) -> Result<bool, SbroadError> { let expr = self.get_expression_node(expr_id)?; match expr { Expression::Bool { .. } @@ -854,7 +897,7 @@ impl Plan { /// /// # Errors /// - If node is not an expression. - pub fn is_ref(&self, expr_id: usize) -> Result<bool, QueryPlannerError> { + pub fn is_ref(&self, expr_id: usize) -> Result<bool, SbroadError> { let expr = self.get_expression_node(expr_id)?; match expr { Expression::Reference { .. } => return Ok(true), @@ -879,18 +922,21 @@ impl Plan { &self, row_id: usize, child_num: usize, - ) -> Result<Value, QueryPlannerError> { + ) -> Result<Value, SbroadError> { let node = self.get_expression_node(row_id)?; if let Expression::Row { list, .. } = node { - let const_node_id = list.get(child_num).ok_or_else(|| { - QueryPlannerError::CustomError(format!("Child {child_num} wasn't found")) - })?; + let const_node_id = list + .get(child_num) + .ok_or_else(|| SbroadError::NotFound(Entity::Node, format!("{child_num}")))?; let v = self.get_expression_node(*const_node_id)?.as_const_value()?; return Ok(v); } - Err(QueryPlannerError::InvalidRow) + Err(SbroadError::Invalid( + Entity::Node, + Some("node is not Row type".into()), + )) } /// Replace parent for all references in the expression subtree of the current node. @@ -903,7 +949,7 @@ impl Plan { node_id: usize, from_id: Option<usize>, to_id: Option<usize>, - ) -> Result<(), QueryPlannerError> { + ) -> Result<(), SbroadError> { let mut references: Vec<usize> = Vec::new(); let subtree = DftPost::new(&node_id, |node| self.nodes.expr_iter(node, false)); for (_, id) in subtree { diff --git a/sbroad-core/src/ir/expression/cast.rs b/sbroad-core/src/ir/expression/cast.rs index cf7117c37a..90ef2de506 100644 --- a/sbroad-core/src/ir/expression/cast.rs +++ b/sbroad-core/src/ir/expression/cast.rs @@ -1,6 +1,6 @@ use std::fmt::{Display, Formatter}; -use crate::errors::QueryPlannerError; +use crate::errors::{Entity, SbroadError}; use crate::frontend::sql::ast::Type as AstType; use crate::ir::expression::Expression; use crate::ir::{Node, Plan}; @@ -22,7 +22,7 @@ pub enum Type { } impl TryFrom<&AstType> for Type { - type Error = QueryPlannerError; + type Error = SbroadError; /// Pay attention that we can't build `Type::Varchar(length)` from string /// because it has an additional length parameter. It should be constructed @@ -39,10 +39,10 @@ impl TryFrom<&AstType> for Type { AstType::TypeString => Ok(Type::String), AstType::TypeText => Ok(Type::Text), AstType::TypeUnsigned => Ok(Type::Unsigned), - _ => Err(QueryPlannerError::CustomError(format!( - "Unsupported type: {:?}", - ast_type - ))), + _ => Err(SbroadError::Unsupported( + Entity::Type, + Some(format!("{ast_type:?}")), + )), } } } @@ -76,7 +76,7 @@ impl Plan { /// /// # Errors /// - Child node is not of the expression type. - pub fn add_cast(&mut self, expr_id: usize, to_type: Type) -> Result<usize, QueryPlannerError> { + pub fn add_cast(&mut self, expr_id: usize, to_type: Type) -> Result<usize, SbroadError> { self.get_expression_node(expr_id)?; let cast_expr = Expression::Cast { child: expr_id, diff --git a/sbroad-core/src/ir/expression/concat.rs b/sbroad-core/src/ir/expression/concat.rs index 30dda78f75..021ee508ce 100644 --- a/sbroad-core/src/ir/expression/concat.rs +++ b/sbroad-core/src/ir/expression/concat.rs @@ -1,4 +1,4 @@ -use crate::errors::QueryPlannerError; +use crate::errors::SbroadError; use crate::ir::expression::Expression; use crate::ir::{Node, Plan}; @@ -7,11 +7,7 @@ impl Plan { /// /// # Errors /// - Left or right child nodes are not of the expression type. - pub fn add_concat( - &mut self, - left_id: usize, - right_id: usize, - ) -> Result<usize, QueryPlannerError> { + pub fn add_concat(&mut self, left_id: usize, right_id: usize) -> Result<usize, SbroadError> { // Check that both children are of expression type. for child_id in &[left_id, right_id] { self.get_expression_node(*child_id)?; diff --git a/sbroad-core/src/ir/expression/tests.rs b/sbroad-core/src/ir/expression/tests.rs index 7af4fb63dd..d398544934 100644 --- a/sbroad-core/src/ir/expression/tests.rs +++ b/sbroad-core/src/ir/expression/tests.rs @@ -2,7 +2,7 @@ use pretty_assertions::assert_eq; use crate::ir::relation::{Column, ColumnRole, Table, Type}; use crate::ir::value::Value; -use crate::ir::{Plan, QueryPlannerError}; +use crate::ir::{Plan, SbroadError}; #[test] fn row_duplicate_column_names() { @@ -13,9 +13,7 @@ fn row_duplicate_column_names() { let c2 = plan.nodes.add_const(Value::from(2_u64)); let c2_alias_a = plan.nodes.add_alias("a", c2).unwrap(); assert_eq!( - QueryPlannerError::CustomError( - "Row can't be added because `a` already has an alias".into() - ), + SbroadError::DuplicatedValue("row can't be added because `a` already has an alias".into()), plan.nodes .add_row_of_aliases(vec![c1_alias_a, c2_alias_a], None) .unwrap_err() diff --git a/sbroad-core/src/ir/function.rs b/sbroad-core/src/ir/function.rs index d45bbea7ad..9413c23241 100644 --- a/sbroad-core/src/ir/function.rs +++ b/sbroad-core/src/ir/function.rs @@ -1,4 +1,4 @@ -use crate::errors::QueryPlannerError; +use crate::errors::{Entity, SbroadError}; use crate::ir::expression::Expression; use crate::ir::{Node, Plan}; use serde::{Deserialize, Serialize}; @@ -46,12 +46,12 @@ impl Plan { &mut self, function: &Function, children: Vec<usize>, - ) -> Result<usize, QueryPlannerError> { + ) -> Result<usize, SbroadError> { if !function.is_stable() { - return Err(QueryPlannerError::CustomError(format!( - "Function {} is not stable", - function.name - ))); + return Err(SbroadError::Invalid( + Entity::SQLFunction, + Some(format!("function {} is not stable", function.name)), + )); } let func_expr = Expression::StableFunction { name: function.name.to_string(), diff --git a/sbroad-core/src/ir/operator.rs b/sbroad-core/src/ir/operator.rs index 62a9aec67f..91538ca33b 100644 --- a/sbroad-core/src/ir/operator.rs +++ b/sbroad-core/src/ir/operator.rs @@ -4,10 +4,11 @@ use ahash::RandomState; use serde::{Deserialize, Serialize}; +use serde_yaml::mapping::Entry; use std::collections::{HashMap, HashSet}; use std::fmt::{Display, Formatter}; -use crate::errors::QueryPlannerError; +use crate::errors::{Action, Entity, SbroadError}; use super::expression::Expression; use super::transformation::redistribution::{DataGeneration, MotionPolicy}; @@ -47,8 +48,8 @@ impl Bool { /// Creates `Bool` from the operator string. /// /// # Errors - /// Returns `QueryPlannerError` when the operator is invalid. - pub fn from(s: &str) -> Result<Self, QueryPlannerError> { + /// Returns `SbroadError` when the operator is invalid. + pub fn from(s: &str) -> Result<Self, SbroadError> { match s.to_lowercase().as_str() { "and" => Ok(Bool::And), "or" => Ok(Bool::Or), @@ -60,7 +61,7 @@ impl Bool { "<=" => Ok(Bool::LtEq), "!=" | "<>" => Ok(Bool::NotEq), "not in" => Ok(Bool::NotIn), - _ => Err(QueryPlannerError::InvalidBool), + _ => Err(SbroadError::Unsupported(Entity::Operator, None)), } } } @@ -98,15 +99,17 @@ impl Unary { /// Creates `Unary` from the operator string. /// /// # Errors - /// Returns `QueryPlannerError` when the operator is invalid. - pub fn from(s: &str) -> Result<Self, QueryPlannerError> { + /// Returns `SbroadError` when the operator is invalid. + pub fn from(s: &str) -> Result<Self, SbroadError> { match s.to_lowercase().as_str() { "is null" => Ok(Unary::IsNull), "is not null" => Ok(Unary::IsNotNull), - _ => Err(QueryPlannerError::CustomError(format!( - "Invalid unary operator: {}", - s - ))), + _ => Err(SbroadError::Invalid( + Entity::Operator, + Some(format!( + "expected `is null` or `is not null`, got unary operator `{s}`" + )), + )), } } } @@ -242,11 +245,11 @@ impl Relational { /// is a row of aliases with unique names. /// /// # Errors - /// Returns `QueryPlannerError` when the output tuple is invalid. + /// Returns `SbroadError` when the output tuple is invalid. pub fn output_alias_position_map<'rel_op, 'nodes>( &'rel_op self, nodes: &'nodes Nodes, - ) -> Result<HashMap<&'nodes str, usize, RandomState>, QueryPlannerError> { + ) -> Result<HashMap<&'nodes str, usize, RandomState>, SbroadError> { if let Some(Node::Expression(Expression::Row { list, .. })) = nodes.arena.get(self.output()) { let state = RandomState::new(); @@ -267,12 +270,14 @@ impl Relational { if valid { return Ok(map); } - return Err(QueryPlannerError::CustomError( - "Invalid output tuple".to_string(), + return Err(SbroadError::Invalid( + Entity::Expression, + Some("invalid output tuple".to_string()), )); } - Err(QueryPlannerError::CustomError( - "Failed to find an output tuple node in the arena".to_string(), + Err(SbroadError::NotFound( + Entity::Node, + "(an output tuple) from the arena".to_string(), )) } @@ -390,7 +395,7 @@ impl Relational { /// /// # Errors /// - try to set children for the scan node (it is always a leaf node) - pub fn set_children(&mut self, children: Vec<usize>) -> Result<(), QueryPlannerError> { + pub fn set_children(&mut self, children: Vec<usize>) -> Result<(), SbroadError> { match self { Relational::Except { children: ref mut old, @@ -435,9 +440,10 @@ impl Relational { *old = children; Ok(()) } - Relational::ScanRelation { .. } => Err(QueryPlannerError::CustomError(String::from( - "Scan is a leaf node", - ))), + Relational::ScanRelation { .. } => Err(SbroadError::Invalid( + Entity::Relational, + Some("Scan is a leaf node".into()), + )), } } @@ -449,7 +455,7 @@ impl Relational { &'n self, plan: &'n Plan, position: usize, - ) -> Result<Option<&'n str>, QueryPlannerError> { + ) -> Result<Option<&'n str>, SbroadError> { match self { Relational::Insert { relation, .. } => Ok(Some(relation.as_str())), Relational::ScanRelation { @@ -461,10 +467,10 @@ impl Relational { let output_row = plan.get_expression_node(self.output())?; let list = output_row.get_row_list()?; let col_id = *list.get(position).ok_or_else(|| { - QueryPlannerError::CustomError(format!( - "Row doesn't has a column at position {}", - position - )) + SbroadError::NotFound( + Entity::Column, + format!("at position {} of Row", position), + ) })?; let col_node = plan.get_expression_node(col_id)?; if let Expression::Alias { child, .. } = col_node { @@ -473,7 +479,7 @@ impl Relational { let rel_id = *plan.get_relational_from_reference_node(*child)?; let rel_node = plan.get_relation_node(rel_id)?; if rel_node == self { - return Err(QueryPlannerError::CustomError(format!( + return Err(SbroadError::DuplicatedValue(format!( "Reference to the same node {:?} at position {}", rel_node, position ))); @@ -481,9 +487,10 @@ impl Relational { return rel_node.scan_name(plan, *pos); } } else { - return Err(QueryPlannerError::CustomError(String::from( - "Expected an alias in the output row", - ))); + return Err(SbroadError::Invalid( + Entity::Expression, + Some("expected an alias in the output row".into()), + )); } Ok(None) } @@ -501,15 +508,16 @@ impl Relational { /// /// # Errors /// - relational node is not a scan. - pub fn set_scan_name(&mut self, name: Option<String>) -> Result<(), QueryPlannerError> { + pub fn set_scan_name(&mut self, name: Option<String>) -> Result<(), SbroadError> { match self { Relational::ScanRelation { ref mut alias, .. } | Relational::ScanSubQuery { ref mut alias, .. } => { *alias = name; Ok(()) } - _ => Err(QueryPlannerError::CustomError( - "Relational node is not a scan.".into(), + _ => Err(SbroadError::Invalid( + Entity::Relational, + Some("Relational node is not a Scan.".into()), )), } } @@ -522,8 +530,8 @@ impl Plan { /// - children nodes are not relational /// - children tuples are invalid /// - children tuples have mismatching structure - pub fn add_except(&mut self, left: usize, right: usize) -> Result<usize, QueryPlannerError> { - let child_row_len = |child: usize, plan: &Plan| -> Result<usize, QueryPlannerError> { + pub fn add_except(&mut self, left: usize, right: usize) -> Result<usize, SbroadError> { + let child_row_len = |child: usize, plan: &Plan| -> Result<usize, SbroadError> { let child_output = plan.get_relation_node(child)?.output(); Ok(plan .get_expression_node(child_output)? @@ -534,8 +542,8 @@ impl Plan { let left_row_len = child_row_len(left, self)?; let right_row_len = child_row_len(right, self)?; if left_row_len != right_row_len { - return Err(QueryPlannerError::CustomError(format!( - "Children tuples have mismatching amount of columns in except node: left {}, right {}", + return Err(SbroadError::UnexpectedNumberOfValues(format!( + "children tuples have mismatching amount of columns in except node: left {}, right {}", left_row_len, right_row_len ))); } @@ -560,9 +568,9 @@ impl Plan { relation: &str, child: usize, columns: &[&str], - ) -> Result<usize, QueryPlannerError> { + ) -> Result<usize, SbroadError> { let rel = self.relations.get(relation).ok_or_else(|| { - QueryPlannerError::CustomError(format!("Invalid relation: {relation}")) + SbroadError::NotFound(Entity::Table, format!("{relation} among plan relations")) })?; let columns: Vec<usize> = if columns.is_empty() { rel.columns @@ -582,17 +590,13 @@ impl Plan { match names.get(name) { Some((&ColumnRole::User, pos)) => cols.push(*pos), Some((&ColumnRole::Sharding, _)) => { - return Err(QueryPlannerError::CustomError(format!( - "System column {} cannot be inserted", - name - ))) - } - None => { - return Err(QueryPlannerError::CustomError(format!( - "Column {} does not exist", - name - ))) + return Err(SbroadError::FailedTo( + Action::Insert, + Some(Entity::Column), + format!("system column {name} cannot be inserted"), + )) } + None => return Err(SbroadError::NotFound(Entity::Column, (*name).to_string())), } } cols @@ -602,13 +606,14 @@ impl Plan { let child_output_list_len = if let Expression::Row { list, .. } = child_output { list.len() } else { - return Err(QueryPlannerError::CustomError(String::from( - "Child output is not a row.", - ))); + return Err(SbroadError::Invalid( + Entity::Expression, + Some("child output is not a Row.".into()), + )); }; if child_output_list_len != columns.len() { - return Err(QueryPlannerError::CustomError(format!( - "Invalid number of values: {}. Table {} expects {} column(s).", + return Err(SbroadError::UnexpectedNumberOfValues(format!( + "invalid number of values: {}. Table {} expects {} column(s).", child_output_list_len, relation, columns.len() @@ -641,11 +646,7 @@ impl Plan { /// /// # Errors /// - relation is invalid - pub fn add_scan( - &mut self, - table: &str, - alias: Option<&str>, - ) -> Result<usize, QueryPlannerError> { + pub fn add_scan(&mut self, table: &str, alias: Option<&str>) -> Result<usize, SbroadError> { let nodes = &mut self.nodes; if let Some(rel) = self.relations.get(table) { @@ -667,10 +668,10 @@ impl Plan { self.replace_parent_in_subtree(output_id, None, Some(scan_id))?; return Ok(scan_id); } - Err(QueryPlannerError::CustomError(format!( - "Failed to find relation {} among the plan relations", - table - ))) + Err(SbroadError::NotFound( + Entity::Table, + format!("{} among the plan relations", table), + )) } /// Adds inner join node. @@ -684,11 +685,12 @@ impl Plan { left: usize, right: usize, condition: usize, - ) -> Result<usize, QueryPlannerError> { + ) -> Result<usize, SbroadError> { if !self.is_trivalent(condition)? { - return Err(QueryPlannerError::CustomError(String::from( - "Condition is not a trivalent expression", - ))); + return Err(SbroadError::Invalid( + Entity::Expression, + Some("Condition is not a trivalent expression".into()), + )); } // For any child in a relational scan, we need to @@ -707,10 +709,10 @@ impl Plan { { // We'll need it later to update the condition expression (borrow checker). let table = self.get_relation(relation).ok_or_else(|| { - QueryPlannerError::CustomError(format!( - "Failed to find relation {} in the plan", - relation - )) + SbroadError::NotFound( + Entity::Table, + format!("{} among plan relations", relation), + ) })?; let sharding_column_pos = table.get_bucket_id_position()?; @@ -761,8 +763,9 @@ impl Plan { self.replace_parent_in_subtree(output, None, Some(join_id))?; return Ok(join_id); } - Err(QueryPlannerError::CustomError( - "Invalid children for join".to_string(), + Err(SbroadError::Invalid( + Entity::Expression, + Some("invalid children for join".to_string()), )) } @@ -776,11 +779,11 @@ impl Plan { child_id: usize, policy: &MotionPolicy, generation: &DataGeneration, - ) -> Result<usize, QueryPlannerError> { + ) -> Result<usize, SbroadError> { let alias = if let Node::Relational(rel) = self.get_node(child_id)? { rel.scan_name(self, 0)?.map(String::from) } else { - return Err(QueryPlannerError::InvalidRelation); + return Err(SbroadError::Invalid(Entity::Relational, None)); }; let output = self.add_row_for_output(child_id, &[], true)?; @@ -805,11 +808,7 @@ impl Plan { /// - child node is not relational /// - child output tuple is invalid /// - column name do not match the ones in the child output tuple - pub fn add_proj( - &mut self, - child: usize, - col_names: &[&str], - ) -> Result<usize, QueryPlannerError> { + pub fn add_proj(&mut self, child: usize, col_names: &[&str]) -> Result<usize, SbroadError> { let output = self.add_row_for_output(child, col_names, false)?; let proj = Relational::Projection { children: vec![child], @@ -831,7 +830,7 @@ impl Plan { &mut self, child: usize, columns: &[usize], - ) -> Result<usize, QueryPlannerError> { + ) -> Result<usize, SbroadError> { let output = self.nodes.add_row_of_aliases(columns.to_vec(), None)?; let proj = Relational::Projection { children: vec![child], @@ -850,26 +849,27 @@ impl Plan { /// - filter expression is not boolean /// - children nodes are not relational /// - first child output tuple is not valid - pub fn add_select( - &mut self, - children: &[usize], - filter: usize, - ) -> Result<usize, QueryPlannerError> { + pub fn add_select(&mut self, children: &[usize], filter: usize) -> Result<usize, SbroadError> { let first_child: usize = match children.len() { - 0 => return Err(QueryPlannerError::InvalidInput), + 0 => { + return Err(SbroadError::UnexpectedNumberOfValues( + "children list is empty".into(), + )) + } _ => children[0], }; if !self.is_trivalent(filter)? { - return Err(QueryPlannerError::CustomError( - "Filter expression is not a trivalent expression.".into(), + return Err(SbroadError::Invalid( + Entity::Expression, + Some("filter expression is not a trivalent expression.".into()), )); } for child in children { if let Node::Relational(_) = self.get_node(*child)? { } else { - return Err(QueryPlannerError::InvalidRelation); + return Err(SbroadError::Invalid(Entity::Relational, None)); } } @@ -895,10 +895,10 @@ impl Plan { &mut self, child: usize, alias: Option<&str>, - ) -> Result<usize, QueryPlannerError> { + ) -> Result<usize, SbroadError> { let name: Option<String> = if let Some(name) = alias { if name.is_empty() { - return Err(QueryPlannerError::InvalidName); + return Err(SbroadError::Invalid(Entity::Name, None)); } Some(String::from(name)) } else { @@ -923,8 +923,8 @@ impl Plan { /// - children nodes are not relational /// - children tuples are invalid /// - children tuples have mismatching structure - pub fn add_union_all(&mut self, left: usize, right: usize) -> Result<usize, QueryPlannerError> { - let child_row_len = |child: usize, plan: &Plan| -> Result<usize, QueryPlannerError> { + pub fn add_union_all(&mut self, left: usize, right: usize) -> Result<usize, SbroadError> { + let child_row_len = |child: usize, plan: &Plan| -> Result<usize, SbroadError> { let child_output = plan.get_relation_node(child)?.output(); Ok(plan .get_expression_node(child_output)? @@ -935,8 +935,8 @@ impl Plan { let left_row_len = child_row_len(left, self)?; let right_row_len = child_row_len(right, self)?; if left_row_len != right_row_len { - return Err(QueryPlannerError::CustomError(format!( - "Children tuples have mismatching amount of columns in union all node: left {}, right {}", + return Err(SbroadError::UnexpectedNumberOfValues(format!( + "children tuples have mismatching amount of columns in union all node: left {}, right {}", left_row_len, right_row_len ))); } @@ -960,7 +960,7 @@ impl Plan { &mut self, row_id: usize, col_idx: &mut usize, - ) -> Result<usize, QueryPlannerError> { + ) -> Result<usize, SbroadError> { let row = self.get_expression_node(row_id)?; let columns = row.clone_row_list()?; let mut aliases: Vec<usize> = Vec::with_capacity(columns.len()); @@ -988,21 +988,21 @@ impl Plan { /// # Errors /// - No child nodes /// - Child node is not relational - pub fn add_values(&mut self, children: Vec<usize>) -> Result<usize, QueryPlannerError> { + pub fn add_values(&mut self, children: Vec<usize>) -> Result<usize, SbroadError> { // Get the last row of the children list. We need it to // get the correct anonymous column names. let last_id = if let Some(last_id) = children.last() { *last_id } else { - return Err(QueryPlannerError::CustomError( - "Values node must have at least one child.".into(), + return Err(SbroadError::UnexpectedNumberOfValues( + "Values node has no children, expected at least one child.".into(), )); }; let child_last = self.get_relation_node(last_id)?; let last_output_id = if let Relational::ValuesRow { output, .. } = child_last { *output } else { - return Err(QueryPlannerError::CustomError( + return Err(SbroadError::UnexpectedNumberOfValues( "Values node must have at least one child row.".into(), )); }; @@ -1014,12 +1014,15 @@ impl Plan { if let Expression::Alias { name, .. } = alias { aliases.push(name.clone()); } else { - return Err(QueryPlannerError::CustomError("Expected an alias".into())); + return Err(SbroadError::Invalid( + Entity::Expression, + Some("Expected an alias".into()), + )); } } aliases } else { - return Err(QueryPlannerError::CustomError( + return Err(SbroadError::UnexpectedNumberOfValues( "Values node must have at least one child row.".into(), )); }; @@ -1045,11 +1048,11 @@ impl Plan { /// /// # Errors /// - node is not relational - pub fn get_relational_output(&self, rel_id: usize) -> Result<usize, QueryPlannerError> { + pub fn get_relational_output(&self, rel_id: usize) -> Result<usize, SbroadError> { if let Node::Relational(rel) = self.get_node(rel_id)? { Ok(rel.output()) } else { - Err(QueryPlannerError::InvalidRelation) + Err(SbroadError::Invalid(Entity::Relational, None)) } } @@ -1057,15 +1060,13 @@ impl Plan { /// /// # Errors /// - node is not relational - pub fn get_relational_children( - &self, - rel_id: usize, - ) -> Result<Option<&[usize]>, QueryPlannerError> { + pub fn get_relational_children(&self, rel_id: usize) -> Result<Option<&[usize]>, SbroadError> { if let Node::Relational(rel) = self.get_node(rel_id)? { Ok(rel.children()) } else { - Err(QueryPlannerError::CustomError( - "Invalid relational node".into(), + Err(SbroadError::Invalid( + Entity::Node, + Some("invalid relational node".into()), )) } } @@ -1076,33 +1077,33 @@ impl Plan { /// - Node is not values row /// - Output and data tuples have different number of columns /// - Output is not a row of aliases - pub fn update_values_row(&mut self, id: usize) -> Result<(), QueryPlannerError> { + pub fn update_values_row(&mut self, id: usize) -> Result<(), SbroadError> { let values_row = self.get_node(id)?; let (output_id, data_id) = if let Node::Relational(Relational::ValuesRow { output, data, .. }) = values_row { (*output, *data) } else { - return Err(QueryPlannerError::CustomError(format!( - "Expected a values row: {:?}", - values_row - ))); + return Err(SbroadError::Invalid( + Entity::Expression, + Some(format!("Expected a values row: {values_row:?}")), + )); }; let data = self.get_expression_node(data_id)?; let data_list = data.clone_row_list()?; let output = self.get_expression_node(output_id)?; let output_list = output.clone_row_list()?; for (pos, alias_id) in output_list.iter().enumerate() { - let new_child_id = *data_list.get(pos).ok_or_else(|| { - QueryPlannerError::CustomError(format!("Expected a child at position {pos}")) - })?; + let new_child_id = *data_list + .get(pos) + .ok_or_else(|| SbroadError::NotFound(Entity::Node, format!("at position {pos}")))?; let alias = self.get_mut_expression_node(*alias_id)?; if let Expression::Alias { ref mut child, .. } = alias { *child = new_child_id; } else { - return Err(QueryPlannerError::CustomError(format!( - "Expected an alias: {:?}", - alias - ))); + return Err(SbroadError::Invalid( + Entity::Expression, + Some(format!("expected an alias: {alias:?}")), + )); } } Ok(()) @@ -1117,17 +1118,19 @@ impl Plan { &mut self, rel_id: usize, children: Vec<usize>, - ) -> Result<(), QueryPlannerError> { - if let Node::Relational(ref mut rel) = self - .nodes - .arena - .get_mut(rel_id) - .ok_or(QueryPlannerError::ValueOutOfRange)? + ) -> Result<(), SbroadError> { + if let Node::Relational(ref mut rel) = + self.nodes.arena.get_mut(rel_id).ok_or_else(|| { + SbroadError::NotFound( + Entity::Node, + format!("(mutable) from arena with index {rel_id}"), + ) + })? { rel.set_children(children)?; Ok(()) } else { - Err(QueryPlannerError::InvalidRelation) + Err(SbroadError::Invalid(Entity::Relational, None)) } } @@ -1136,7 +1139,7 @@ impl Plan { /// # Errors /// - Failed to get plan top /// - Node returned by the relational iterator is not relational (bug) - pub fn is_additional_child(&self, node_id: usize) -> Result<bool, QueryPlannerError> { + pub fn is_additional_child(&self, node_id: usize) -> Result<bool, SbroadError> { let top_id = self.get_top()?; let rel_tree = Bft::new(&top_id, |node| self.nodes.rel_iter(node)); for (_, id) in rel_tree { @@ -1166,7 +1169,7 @@ impl Plan { &self, rel_id: usize, sq_id: usize, - ) -> Result<bool, QueryPlannerError> { + ) -> Result<bool, SbroadError> { let children = if let Some(children) = self.get_relational_children(rel_id)? { children } else { diff --git a/sbroad-core/src/ir/operator/tests.rs b/sbroad-core/src/ir/operator/tests.rs index 52a3b2739b..e3c12e58fc 100644 --- a/sbroad-core/src/ir/operator/tests.rs +++ b/sbroad-core/src/ir/operator/tests.rs @@ -4,7 +4,7 @@ use std::path::Path; use pretty_assertions::assert_eq; use crate::collection; -use crate::errors::QueryPlannerError; +use crate::errors::{Entity, SbroadError}; use crate::ir::distribution::{Distribution, Key}; use crate::ir::relation::{Column, ColumnRole, Table, Type}; use crate::ir::value::Value; @@ -103,21 +103,19 @@ fn projection() { // Invalid alias names in the output assert_eq!( - QueryPlannerError::CustomError( - r#"Some of the columns ["a", "e"] were not found in the table"#.into() - ), + SbroadError::NotFound(Entity::Column, r#"with name ["a", "e"]"#.into()), plan.add_proj(scan_id, &["a", "e"]).unwrap_err() ); // Expression node instead of relational one assert_eq!( - QueryPlannerError::CustomError("Node isn't relational".into()), + SbroadError::Invalid(Entity::Node, Some("node is not Relational type".into())), plan.add_proj(1, &["a"]).unwrap_err() ); // Try to build projection from the non-existing node assert_eq!( - QueryPlannerError::ValueOutOfRange, + SbroadError::NotFound(Entity::Node, format!("from arena with index 42")), plan.add_proj(42, &["a"]).unwrap_err() ); } @@ -164,15 +162,24 @@ fn selection() { // Correct Selection operator plan.add_select(&[scan_id], gt_id).unwrap(); + // Invalid children list len + assert_eq!( + SbroadError::UnexpectedNumberOfValues("children list is empty".into(),), + plan.add_select(&[], gt_id).unwrap_err() + ); + // Non-trivalent filter assert_eq!( - QueryPlannerError::CustomError("Filter expression is not a trivalent expression.".into()), + SbroadError::Invalid( + Entity::Expression, + Some("filter expression is not a trivalent expression.".into()) + ), plan.add_select(&[scan_id], const_row).unwrap_err() ); // Non-relational child assert_eq!( - QueryPlannerError::InvalidRelation, + SbroadError::Invalid(Entity::Relational, None), plan.add_select(&[const_row], gt_id).unwrap_err() ); } @@ -189,6 +196,109 @@ fn selection_serialize() { Plan::from_yaml(&s).unwrap(); } +#[test] +fn except() { + let mut valid_plan = Plan::default(); + + let t1 = Table::new_seg( + "t1", + vec![Column::new("a", Type::Unsigned, ColumnRole::User)], + &["a"], + ) + .unwrap(); + let t1_copy = t1.clone(); + valid_plan.add_rel(t1); + let scan_t1_id = valid_plan.add_scan("t1", None).unwrap(); + + let t2 = Table::new_seg( + "t2", + vec![Column::new("a", Type::Unsigned, ColumnRole::User)], + &["a"], + ) + .unwrap(); + valid_plan.add_rel(t2); + let scan_t2_id = valid_plan.add_scan("t2", None).unwrap(); + + // Correct Except operator + valid_plan.add_except(scan_t1_id, scan_t2_id).unwrap(); + + let mut invalid_plan = Plan::default(); + + invalid_plan.add_rel(t1_copy); + let scan_t1_id = invalid_plan.add_scan("t1", None).unwrap(); + + let t3 = Table::new_seg( + "t3", + vec![ + Column::new("a", Type::Unsigned, ColumnRole::User), + Column::new("b", Type::Unsigned, ColumnRole::User), + ], + &["a"], + ) + .unwrap(); + invalid_plan.add_rel(t3); + let scan_t3_id = invalid_plan.add_scan("t3", None).unwrap(); + + assert_eq!( + SbroadError::UnexpectedNumberOfValues( + "children tuples have mismatching amount of columns in except node: left 1, right 2" + .into() + ), + invalid_plan.add_except(scan_t1_id, scan_t3_id).unwrap_err() + ); +} + +#[test] +fn insert() { + let mut plan = Plan::default(); + + let t1 = Table::new_seg( + "t1", + vec![Column::new("a", Type::Unsigned, ColumnRole::User)], + &["a"], + ) + .unwrap(); + + plan.add_rel(t1); + let scan_t1_id = plan.add_scan("t1", None).unwrap(); + + let t2 = Table::new_seg( + "t2", + vec![ + Column::new("a", Type::Unsigned, ColumnRole::User), + Column::new("b", Type::Unsigned, ColumnRole::User), + Column::new("c", Type::Unsigned, ColumnRole::Sharding), + ], + &["a"], + ) + .unwrap(); + plan.add_rel(t2); + + assert_eq!( + SbroadError::NotFound(Entity::Table, "t4 among plan relations".into()), + plan.add_insert("t4", scan_t1_id, &["a"]).unwrap_err() + ); + + assert_eq!( + SbroadError::FailedTo( + Action::Insert, + Some(Entity::Column), + "system column c cannot be inserted".into(), + ), + plan.add_insert("t2", scan_t1_id, &["a", "b", "c"]) + .unwrap_err() + ); + + assert_eq!( + SbroadError::UnexpectedNumberOfValues( + "invalid number of values: 1. Table t2 expects 2 column(s).".into() + ), + plan.add_insert("t2", scan_t1_id, &["a", "b"]).unwrap_err() + ); + + plan.add_insert("t1", scan_t1_id, &["a"]).unwrap(); +} + #[test] fn union_all() { let mut plan = Plan::default(); @@ -242,8 +352,8 @@ fn union_all_col_amount_mismatch() { let scan_t2_id = plan.add_scan("t2", None).unwrap(); assert_eq!( - QueryPlannerError::CustomError( - "Children tuples have mismatching amount of columns in union all node: left 1, right 2" + SbroadError::UnexpectedNumberOfValues( + "children tuples have mismatching amount of columns in union all node: left 1, right 2" .into() ), plan.add_union_all(scan_t2_id, scan_t1_id).unwrap_err() @@ -271,13 +381,13 @@ fn sub_query() { // Non-relational child node let a = 1; assert_eq!( - QueryPlannerError::CustomError("Node isn't relational".into()), + SbroadError::Invalid(Entity::Node, Some("node is not Relational type".into())), plan.add_sub_query(a, Some("sq")).unwrap_err() ); // Invalid name assert_eq!( - QueryPlannerError::InvalidName, + SbroadError::Invalid(Entity::Name, None), plan.add_sub_query(scan_id, Some("")).unwrap_err() ); } @@ -445,9 +555,7 @@ fn join_duplicate_columns() { .unwrap(); let condition = plan.nodes.add_bool(a_row, Bool::Eq, d_row).unwrap(); assert_eq!( - QueryPlannerError::CustomError( - "Row can't be added because `a` already has an alias".into() - ), + SbroadError::DuplicatedValue("row can't be added because `a` already has an alias".into()), plan.add_join(scan_t1, scan_t2, condition).unwrap_err() ); } diff --git a/sbroad-core/src/ir/relation.rs b/sbroad-core/src/ir/relation.rs index 00f5f0a67a..9f935ce918 100644 --- a/sbroad-core/src/ir/relation.rs +++ b/sbroad-core/src/ir/relation.rs @@ -8,7 +8,7 @@ use serde::de::{Error, MapAccess, Visitor}; use serde::ser::{Serialize as SerSerialize, SerializeMap, Serializer}; use serde::{Deserialize, Deserializer, Serialize}; -use crate::errors::QueryPlannerError; +use crate::errors::{Action, Entity, SbroadError}; use crate::ir::value::Value; use super::distribution::Key; @@ -34,8 +34,8 @@ impl Type { /// Type constructor /// /// # Errors - /// Returns `QueryPlannerError` when the input arguments are invalid. - pub fn new(s: &str) -> Result<Self, QueryPlannerError> { + /// Returns `SbroadError` when the input arguments are invalid. + pub fn new(s: &str) -> Result<Self, SbroadError> { match s.to_string().to_lowercase().as_str() { "boolean" => Ok(Type::Boolean), "decimal" => Ok(Type::Decimal), @@ -46,10 +46,7 @@ impl Type { "string" => Ok(Type::String), "unsigned" => Ok(Type::Unsigned), "array" => Ok(Type::Array), - v => Err(QueryPlannerError::CustomError(format!( - "type `{}` not implemented", - v - ))), + v => Err(SbroadError::NotImplemented(Entity::Type, v.to_string())), } } } @@ -199,12 +196,8 @@ impl Table { /// Table segment constructor. /// /// # Errors - /// Returns `QueryPlannerError` when the input arguments are invalid. - pub fn new_seg( - name: &str, - columns: Vec<Column>, - keys: &[&str], - ) -> Result<Self, QueryPlannerError> { + /// Returns `SbroadError` when the input arguments are invalid. + pub fn new_seg(name: &str, columns: Vec<Column>, keys: &[&str]) -> Result<Self, SbroadError> { let mut pos_map: HashMap<&str, usize> = HashMap::new(); let no_duplicates = &columns .iter() @@ -212,7 +205,7 @@ impl Table { .all(|(pos, col)| matches!(pos_map.insert(&col.name, pos), None)); if !no_duplicates { - return Err(QueryPlannerError::CustomError( + return Err(SbroadError::DuplicatedValue( "Table has duplicated columns and couldn't be loaded".into(), )); } @@ -221,7 +214,7 @@ impl Table { .iter() .map(|name| match pos_map.get(*name) { Some(pos) => Ok(*pos), - None => Err(QueryPlannerError::InvalidShardingKey), + None => Err(SbroadError::Invalid(Entity::ShardingKey, None)), }) .collect::<Result<Vec<usize>, _>>()?; @@ -235,11 +228,17 @@ impl Table { /// Table segment from YAML. /// /// # Errors - /// Returns `QueryPlannerError` when the YAML-serialized table is invalid. - pub fn seg_from_yaml(s: &str) -> Result<Self, QueryPlannerError> { + /// Returns `SbroadError` when the YAML-serialized table is invalid. + pub fn seg_from_yaml(s: &str) -> Result<Self, SbroadError> { let ts: Table = match serde_yaml::from_str(s) { Ok(t) => t, - Err(_) => return Err(QueryPlannerError::Serialization), + Err(e) => { + return Err(SbroadError::FailedTo( + Action::Serialize, + Some(Entity::Table), + format!("{e:?}"), + )) + } }; let mut uniq_cols: HashSet<&str> = HashSet::new(); let cols = ts.columns.clone(); @@ -247,7 +246,7 @@ impl Table { let no_duplicates = cols.iter().all(|col| uniq_cols.insert(&col.name)); if !no_duplicates { - return Err(QueryPlannerError::CustomError( + return Err(SbroadError::DuplicatedValue( "Table contains duplicate columns. Unable to convert to YAML.".into(), )); } @@ -255,7 +254,10 @@ impl Table { let in_range = ts.key.positions.iter().all(|pos| *pos < cols.len()); if !in_range { - return Err(QueryPlannerError::ValueOutOfRange); + return Err(SbroadError::Invalid( + Entity::Value, + Some(format!("key positions must be less than {}", cols.len())), + )); } Ok(ts) @@ -265,7 +267,7 @@ impl Table { /// /// # Errors /// - Table doesn't have an exactly one `bucket_id` column. - pub fn get_bucket_id_position(&self) -> Result<usize, QueryPlannerError> { + pub fn get_bucket_id_position(&self) -> Result<usize, SbroadError> { let positions: Vec<usize> = self .columns .iter() @@ -275,10 +277,10 @@ impl Table { .collect(); match positions.len().cmp(&1) { Ordering::Equal => Ok(positions[0]), - Ordering::Greater => Err(QueryPlannerError::CustomError( + Ordering::Greater => Err(SbroadError::UnexpectedNumberOfValues( "Table has more than one bucket_id column".into(), )), - Ordering::Less => Err(QueryPlannerError::CustomError( + Ordering::Less => Err(SbroadError::UnexpectedNumberOfValues( "Table has no bucket_id columns".into(), )), } @@ -288,17 +290,20 @@ impl Table { /// /// # Errors /// - Table internal inconsistency. - pub fn get_sharding_column_names(&self) -> Result<Vec<String>, QueryPlannerError> { + pub fn get_sharding_column_names(&self) -> Result<Vec<String>, SbroadError> { let mut names: Vec<String> = Vec::with_capacity(self.key.positions.len()); for pos in &self.key.positions { names.push( self.columns .get(*pos) .ok_or_else(|| { - QueryPlannerError::CustomError(format!( - "Table {} has no distribution column at position {}", - self.name, *pos - )) + SbroadError::NotFound( + Entity::Column, + format!( + "(distribution column) at position {} for Table {}", + *pos, self.name + ), + ) })? .name .clone(), diff --git a/sbroad-core/src/ir/relation/tests.rs b/sbroad-core/src/ir/relation/tests.rs index 96bbc00fcc..0d2c467080 100644 --- a/sbroad-core/src/ir/relation/tests.rs +++ b/sbroad-core/src/ir/relation/tests.rs @@ -61,9 +61,44 @@ fn table_seg_duplicate_columns() { &["b", "a"], ) .unwrap_err(), - QueryPlannerError::CustomError( - "Table has duplicated columns and couldn't be loaded".into() - ) + SbroadError::DuplicatedValue("Table has duplicated columns and couldn't be loaded".into()) + ); +} + +#[test] +fn table_seg_dno_bucket_id_column() { + let t1 = Table::new_seg( + "t", + vec![ + Column::new("a", Type::Boolean, ColumnRole::User), + Column::new("b", Type::Unsigned, ColumnRole::User), + Column::new("c", Type::String, ColumnRole::User), + ], + &["b", "a"], + ) + .unwrap(); + + assert_eq!( + SbroadError::UnexpectedNumberOfValues("Table has no bucket_id columns".into()), + t1.get_bucket_id_position().unwrap_err() + ); + + let t2 = Table::new_seg( + "t", + vec![ + Column::new("a", Type::Boolean, ColumnRole::User), + Column::new("b", Type::Unsigned, ColumnRole::User), + Column::new("c", Type::String, ColumnRole::User), + Column::new("bucket_id", Type::String, ColumnRole::Sharding), + Column::new("bucket_id2", Type::String, ColumnRole::Sharding), + ], + &["b", "a"], + ) + .unwrap(); + + assert_eq!( + SbroadError::UnexpectedNumberOfValues("Table has more than one bucket_id column".into()), + t2.get_bucket_id_position().unwrap_err() ); } @@ -81,7 +116,7 @@ fn table_seg_wrong_key() { &["a", "e"], ) .unwrap_err(), - QueryPlannerError::InvalidShardingKey + SbroadError::Invalid(Entity::ShardingKey, None) ); } @@ -122,7 +157,7 @@ fn table_seg_serialized_duplicate_columns() { let s = fs::read_to_string(path).unwrap(); assert_eq!( Table::seg_from_yaml(&s).unwrap_err(), - QueryPlannerError::CustomError( + SbroadError::DuplicatedValue( "Table contains duplicate columns. Unable to convert to YAML.".into() ) ); @@ -139,7 +174,10 @@ fn table_seg_serialized_out_of_range_key() { let s = fs::read_to_string(path).unwrap(); assert_eq!( Table::seg_from_yaml(&s).unwrap_err(), - QueryPlannerError::ValueOutOfRange + SbroadError::Invalid( + Entity::Value, + Some("key positions must be less than 1".into()) + ) ); } @@ -153,7 +191,11 @@ fn table_seg_serialized_no_key() { .join("table_seg_serialized_no_key.yaml"); let s = fs::read_to_string(path).unwrap(); let t = Table::seg_from_yaml(&s); - assert_eq!(t.unwrap_err(), QueryPlannerError::Serialization); + assert_eq!(t.unwrap_err(), SbroadError::FailedTo( + Action::Serialize, + Some(Entity::Table), + "Message(\"invalid type: unit value, expected struct Key\", Some(Pos { marker: Marker { index: 52, line: 5, col: 5 }, path: \"key\" }))".into()) + ); } #[test] @@ -167,7 +209,11 @@ fn table_seg_serialized_no_columns() { let s = fs::read_to_string(path).unwrap(); assert_eq!( Table::seg_from_yaml(&s).unwrap_err(), - QueryPlannerError::Serialization + SbroadError::FailedTo( + Action::Serialize, + Some(Entity::Table), + "Message(\"invalid type: unit value, expected a sequence\", Some(Pos { marker: Marker { index: 9, line: 1, col: 9 }, path: \"columns\" }))".into() + ) ); } diff --git a/sbroad-core/src/ir/tests.rs b/sbroad-core/src/ir/tests.rs index d88ab46284..e7d8f69e27 100644 --- a/sbroad-core/src/ir/tests.rs +++ b/sbroad-core/src/ir/tests.rs @@ -13,7 +13,7 @@ fn plan_no_top() { .join("plan_no_top.yaml"); let s = fs::read_to_string(path).unwrap(); assert_eq!( - QueryPlannerError::InvalidPlan, + SbroadError::Invalid(Entity::Plan, Some("plan tree top is None".into())), Plan::from_yaml(&s).unwrap_err() ); } @@ -27,7 +27,7 @@ fn plan_oor_top() { .join("plan_oor_top.yaml"); let s = fs::read_to_string(path).unwrap(); assert_eq!( - QueryPlannerError::ValueOutOfRange, + SbroadError::NotFound(Entity::Node, "from arena with index 42".into()), Plan::from_yaml(&s).unwrap_err() ); } @@ -59,7 +59,7 @@ fn get_node() { fn get_node_oor() { let plan = Plan::default(); assert_eq!( - QueryPlannerError::ValueOutOfRange, + SbroadError::NotFound(Entity::Node, "from arena with index 42".into()), plan.get_node(42).unwrap_err() ); } diff --git a/sbroad-core/src/ir/transformation.rs b/sbroad-core/src/ir/transformation.rs index 28cb3401a3..654333e688 100644 --- a/sbroad-core/src/ir/transformation.rs +++ b/sbroad-core/src/ir/transformation.rs @@ -9,7 +9,7 @@ pub mod merge_tuples; pub mod redistribution; pub mod split_columns; -use crate::errors::QueryPlannerError; +use crate::errors::{Entity, SbroadError}; use crate::ir::expression::Expression; use crate::ir::operator::{Bool, Relational}; use crate::ir::Plan; @@ -25,18 +25,24 @@ impl Plan { &mut self, left_expr_id: usize, right_expr_id: usize, - ) -> Result<usize, QueryPlannerError> { + ) -> Result<usize, SbroadError> { if !self.is_trivalent(left_expr_id)? { - return Err(QueryPlannerError::CustomError(format!( - "Left expression is not a boolean expression or NULL: {:?}", - self.get_expression_node(left_expr_id)? - ))); + return Err(SbroadError::Invalid( + Entity::Expression, + Some(format!( + "Left expression is not a boolean expression or NULL: {:?}", + self.get_expression_node(left_expr_id)? + )), + )); } if !self.is_trivalent(right_expr_id)? { - return Err(QueryPlannerError::CustomError(format!( - "Right expression is not a boolean expression or NULL: {:?}", - self.get_expression_node(right_expr_id)? - ))); + return Err(SbroadError::Invalid( + Entity::Expression, + Some(format!( + "Right expression is not a boolean expression or NULL: {:?}", + self.get_expression_node(right_expr_id)? + )), + )); } self.add_cond(left_expr_id, Bool::And, right_expr_id) } @@ -49,18 +55,24 @@ impl Plan { &mut self, left_expr_id: usize, right_expr_id: usize, - ) -> Result<usize, QueryPlannerError> { + ) -> Result<usize, SbroadError> { if !self.is_trivalent(left_expr_id)? { - return Err(QueryPlannerError::CustomError(format!( - "Left expression is not a boolean expression or NULL: {:?}", - self.get_expression_node(left_expr_id)? - ))); + return Err(SbroadError::Invalid( + Entity::Expression, + Some(format!( + "left expression is not a boolean expression or NULL: {:?}", + self.get_expression_node(left_expr_id)? + )), + )); } if !self.is_trivalent(right_expr_id)? { - return Err(QueryPlannerError::CustomError(format!( - "Right expression is not a boolean expression or NULL: {:?}", - self.get_expression_node(right_expr_id)? - ))); + return Err(SbroadError::Invalid( + Entity::Expression, + Some(format!( + "right expression is not a boolean expression or NULL: {:?}", + self.get_expression_node(right_expr_id)? + )), + )); } self.add_cond(left_expr_id, Bool::Or, right_expr_id) } @@ -75,8 +87,8 @@ impl Plan { /// - If failed to transform the expression subtree. pub fn transform_expr_trees( &mut self, - f: &dyn Fn(&mut Plan, usize) -> Result<usize, QueryPlannerError>, - ) -> Result<(), QueryPlannerError> { + f: &dyn Fn(&mut Plan, usize) -> Result<usize, SbroadError>, + ) -> Result<(), SbroadError> { let top_id = self.get_top()?; let ir_tree = DftPost::new(&top_id, |node| self.nodes.rel_iter(node)); let nodes: Vec<usize> = ir_tree.map(|(_, id)| *id).collect(); @@ -124,9 +136,9 @@ impl Plan { pub fn expr_tree_replace_bool( &mut self, top_id: usize, - f: &dyn Fn(&mut Plan, usize) -> Result<usize, QueryPlannerError>, + f: &dyn Fn(&mut Plan, usize) -> Result<usize, SbroadError>, ops: &[Bool], - ) -> Result<usize, QueryPlannerError> { + ) -> Result<usize, SbroadError> { let mut map: HashMap<usize, usize> = HashMap::new(); let subtree = DftPost::new(&top_id, |node| self.nodes.expr_iter(node, false)); let nodes: Vec<usize> = subtree.map(|(_, id)| *id).collect(); diff --git a/sbroad-core/src/ir/transformation/bool_in.rs b/sbroad-core/src/ir/transformation/bool_in.rs index 4ca5fc8ad0..5142e1ab49 100644 --- a/sbroad-core/src/ir/transformation/bool_in.rs +++ b/sbroad-core/src/ir/transformation/bool_in.rs @@ -10,7 +10,7 @@ //! SELECT * FROM t WHERE (a = 1) or (a = 2) or (a = 3) //! ``` -use crate::errors::QueryPlannerError; +use crate::errors::{Entity, SbroadError}; use crate::ir::expression::Expression; use crate::ir::operator::Bool; use crate::ir::Plan; @@ -18,17 +18,17 @@ use crate::otm::child_span; use sbroad_proc::otm_child_span; /// Replace IN operator with the chain of the OR-ed equalities in the expression tree. -fn call_expr_tree_replace_in(plan: &mut Plan, top_id: usize) -> Result<usize, QueryPlannerError> { +fn call_expr_tree_replace_in(plan: &mut Plan, top_id: usize) -> Result<usize, SbroadError> { plan.expr_tree_replace_bool(top_id, &call_from_in, &[Bool::In]) } -fn call_from_in(plan: &mut Plan, top_id: usize) -> Result<usize, QueryPlannerError> { +fn call_from_in(plan: &mut Plan, top_id: usize) -> Result<usize, SbroadError> { plan.in_to_or(top_id) } impl Plan { /// Convert the IN operator to the chain of the OR-ed equalities. - fn in_to_or(&mut self, expr_id: usize) -> Result<usize, QueryPlannerError> { + fn in_to_or(&mut self, expr_id: usize) -> Result<usize, SbroadError> { let expr = self.get_expression_node(expr_id)?; let (left_id, right_id) = match expr { Expression::Bool { @@ -38,10 +38,10 @@ impl Plan { .. } => (*left, *right), _ => { - return Err(QueryPlannerError::CustomError(format!( - "Node is not a boolean IN expression: {:?}", - expr - ))); + return Err(SbroadError::Invalid( + Entity::Expression, + Some(format!("Node is not a boolean IN expression: {expr:?}")), + )); } }; @@ -85,7 +85,7 @@ impl Plan { /// # Errors /// - If the plan tree is invalid (doesn't contain correct nodes where we expect it to). #[otm_child_span("plan.transformation.replace_in_operator")] - pub fn replace_in_operator(&mut self) -> Result<(), QueryPlannerError> { + pub fn replace_in_operator(&mut self) -> Result<(), SbroadError> { self.transform_expr_trees(&call_expr_tree_replace_in) } } diff --git a/sbroad-core/src/ir/transformation/dnf.rs b/sbroad-core/src/ir/transformation/dnf.rs index e7ed0ba594..ca485b6de9 100644 --- a/sbroad-core/src/ir/transformation/dnf.rs +++ b/sbroad-core/src/ir/transformation/dnf.rs @@ -70,7 +70,7 @@ //! └─────┘ └─────┘ //! ``` -use crate::errors::QueryPlannerError; +use crate::errors::{Entity, SbroadError}; use crate::ir::expression::Expression; use crate::ir::operator::Bool; use crate::ir::{Node, Plan}; @@ -101,7 +101,7 @@ impl Chain { /// Append a new node to the chain. Keep AND and OR nodes in the back, /// while other nodes in the front of the chain double-ended queue. - fn push(&mut self, expr_id: usize, plan: &Plan) -> Result<(), QueryPlannerError> { + fn push(&mut self, expr_id: usize, plan: &Plan) -> Result<(), SbroadError> { let expr = plan.get_expression_node(expr_id)?; if let Expression::Bool { op: Bool::And | Bool::Or, @@ -116,7 +116,7 @@ impl Chain { } /// Pop AND and OR nodes (we append them to the back). - fn pop_back(&mut self, plan: &Plan) -> Result<Option<usize>, QueryPlannerError> { + fn pop_back(&mut self, plan: &Plan) -> Result<Option<usize>, SbroadError> { if let Some(expr_id) = self.nodes.back() { let expr = plan.get_expression_node(*expr_id)?; if let Expression::Bool { @@ -136,7 +136,7 @@ impl Chain { } /// Convert a chain to a new expression tree (reuse trivalent expressions). - fn as_plan(&mut self, plan: &mut Plan) -> Result<usize, QueryPlannerError> { + fn as_plan(&mut self, plan: &mut Plan) -> Result<usize, SbroadError> { let mut top_id: Option<usize> = None; while let Some(expr_id) = self.pop_front() { match top_id { @@ -149,8 +149,8 @@ impl Chain { } } } - let new_top_id = - top_id.ok_or_else(|| QueryPlannerError::CustomError("Empty chain".into()))?; + let new_top_id = top_id + .ok_or_else(|| SbroadError::Invalid(Entity::Chain, Some("Empty chain".into())))?; Ok(new_top_id) } @@ -160,7 +160,7 @@ impl Chain { } } -fn call_expr_tree_to_dnf(plan: &mut Plan, top_id: usize) -> Result<usize, QueryPlannerError> { +fn call_expr_tree_to_dnf(plan: &mut Plan, top_id: usize) -> Result<usize, SbroadError> { plan.expr_tree_to_dnf(top_id) } @@ -170,7 +170,7 @@ impl Plan { /// # Errors /// - If the expression tree is not a trivalent expression. /// - Failed to append node to the AND chain. - pub fn get_dnf_chains(&self, top_id: usize) -> Result<VecDeque<Chain>, QueryPlannerError> { + pub fn get_dnf_chains(&self, top_id: usize) -> Result<VecDeque<Chain>, SbroadError> { let capacity: usize = self.nodes.arena.iter().fold(0_usize, |acc, node| { acc + match node { Node::Expression(Expression::Bool { @@ -212,8 +212,9 @@ impl Plan { chain.push(*left, self)?; } _ => { - return Err(QueryPlannerError::CustomError( - "Chain returned unexpected boolean operator".into(), + return Err(SbroadError::Invalid( + Entity::Chain, + Some("Chain returned unexpected boolean operator".into()), )) } } @@ -230,7 +231,7 @@ impl Plan { /// - Failed to retrieve DNF chains. /// - Failed to convert the AND chain to a new expression tree. /// - Failed to concatenate the AND expression trees to the OR tree. - pub fn expr_tree_to_dnf(&mut self, top_id: usize) -> Result<usize, QueryPlannerError> { + pub fn expr_tree_to_dnf(&mut self, top_id: usize) -> Result<usize, SbroadError> { let mut result = self.get_dnf_chains(top_id)?; let mut new_top_id: Option<usize> = None; @@ -242,8 +243,9 @@ impl Plan { } } - new_top_id - .ok_or_else(|| QueryPlannerError::CustomError("Chain returned no expressions".into())) + new_top_id.ok_or_else(|| { + SbroadError::Invalid(Entity::Chain, Some("Chain returned no expressions".into())) + }) } /// Convert an expression tree of trivalent nodes to a conjunctive @@ -254,7 +256,7 @@ impl Plan { /// - If the plan doesn't contain relational operators where expected. /// - If failed to convert an expression tree to a CNF. #[otm_child_span("plan.transformation.set_dnf")] - pub fn set_dnf(&mut self) -> Result<(), QueryPlannerError> { + pub fn set_dnf(&mut self) -> Result<(), SbroadError> { self.transform_expr_trees(&call_expr_tree_to_dnf) } } diff --git a/sbroad-core/src/ir/transformation/equality_propagation.rs b/sbroad-core/src/ir/transformation/equality_propagation.rs index aec7b8d1f1..48c4d3d046 100644 --- a/sbroad-core/src/ir/transformation/equality_propagation.rs +++ b/sbroad-core/src/ir/transformation/equality_propagation.rs @@ -89,7 +89,7 @@ //! 6. Finally, we transform the "AND"-ed chains into a plan subtree and attach them back //! to the plan tree. -use crate::errors::QueryPlannerError; +use crate::errors::{Entity, SbroadError}; use crate::ir::expression::Expression; use crate::ir::helpers::RepeatableState; use crate::ir::operator::Bool; @@ -110,7 +110,7 @@ struct EqClassRef { } impl EqClassRef { - fn from_ref(expr: &Expression) -> Result<Self, QueryPlannerError> { + fn from_ref(expr: &Expression) -> Result<Self, SbroadError> { if let Expression::Reference { targets: expr_tgt, position: expr_pos, @@ -123,7 +123,7 @@ impl EqClassRef { parent: *expr_prt, }); } - Err(QueryPlannerError::InvalidReference) + Err(SbroadError::Invalid(Entity::Expression, None)) } fn to_single_col_row(&self, plan: &mut Plan) -> usize { @@ -147,13 +147,16 @@ struct EqClassConst { impl Eq for EqClassConst {} impl EqClassConst { - fn from_const(expr: &Expression) -> Result<Self, QueryPlannerError> { + fn from_const(expr: &Expression) -> Result<Self, SbroadError> { if let Expression::Constant { value: expr_value } = expr { return Ok(EqClassConst { value: expr_value.clone(), }); } - Err(QueryPlannerError::InvalidConstant) + Err(SbroadError::Invalid( + Entity::Expression, + Some("invaid Constant".into()), + )) } fn to_const(&self, plan: &mut Plan) -> usize { @@ -339,17 +342,14 @@ impl EqClassChain { } /// Replace IN operator with the chain of the OR-ed equalities in the expression tree. -fn call_expr_tree_derive_equalities( - plan: &mut Plan, - top_id: usize, -) -> Result<usize, QueryPlannerError> { +fn call_expr_tree_derive_equalities(plan: &mut Plan, top_id: usize) -> Result<usize, SbroadError> { plan.expr_tree_modify_and_chains(top_id, &call_build_and_chains, &call_as_plan) } fn call_build_and_chains( plan: &mut Plan, nodes: &[usize], -) -> Result<HashMap<usize, Chain, RepeatableState>, QueryPlannerError> { +) -> Result<HashMap<usize, Chain, RepeatableState>, SbroadError> { let mut chains = plan.populate_and_chains(nodes)?; for chain in chains.values_mut() { chain.extend_equality_operator(plan)?; @@ -357,19 +357,19 @@ fn call_build_and_chains( Ok(chains) } -fn call_as_plan(chain: &Chain, plan: &mut Plan) -> Result<usize, QueryPlannerError> { +fn call_as_plan(chain: &Chain, plan: &mut Plan) -> Result<usize, SbroadError> { chain.as_plan_ecs(plan) } impl Chain { - fn extend_equality_operator(&mut self, plan: &mut Plan) -> Result<(), QueryPlannerError> { + fn extend_equality_operator(&mut self, plan: &mut Plan) -> Result<(), SbroadError> { if let Some((left_vec, right_vec)) = self.get_grouped().get(&Bool::Eq) { let mut eq_classes = EqClassChain::new(); for (left_id, right_id) in left_vec.iter().zip(right_vec.iter()) { let left_eqe = plan.try_to_eq_class_expr(*left_id); let right_eqe = plan.try_to_eq_class_expr(*right_id); - if let (Err(QueryPlannerError::DoSkip), _) | (_, Err(QueryPlannerError::DoSkip)) = + if let (Err(SbroadError::DoSkip), _) | (_, Err(SbroadError::DoSkip)) = (&left_eqe, &right_eqe) { continue; @@ -397,7 +397,7 @@ impl Chain { Ok(()) } - fn as_plan_ecs(&self, plan: &mut Plan) -> Result<usize, QueryPlannerError> { + fn as_plan_ecs(&self, plan: &mut Plan) -> Result<usize, SbroadError> { let other_top_id = match self.get_other().split_first() { Some((first, other)) => { let mut top_id = *first; @@ -459,8 +459,8 @@ impl Chain { } (Some(grouped_top_id), None) => Ok(grouped_top_id), (None, Some(other_top_id)) => Ok(other_top_id), - (None, None) => Err(QueryPlannerError::CustomError( - "No expressions to merge".to_string(), + (None, None) => Err(SbroadError::UnexpectedNumberOfValues( + "no expressions to merge, expected one or two".to_string(), )), } } @@ -469,7 +469,7 @@ impl Chain { impl Plan { // DoSkip is a special case of an error - nothing bad had happened, the target node doesn't contain // anything interesting for us, skip it without any serious error. - fn try_to_eq_class_expr(&self, expr_id: usize) -> Result<EqClassExpr, QueryPlannerError> { + fn try_to_eq_class_expr(&self, expr_id: usize) -> Result<EqClassExpr, SbroadError> { let expr = self.get_expression_node(expr_id)?; match expr { Expression::Constant { .. } => { @@ -483,10 +483,10 @@ impl Plan { self.try_to_eq_class_expr(*col_id) } else { // We don't support more than a single column in a row. - Err(QueryPlannerError::DoSkip) + Err(SbroadError::DoSkip) } } - _ => Err(QueryPlannerError::DoSkip), + _ => Err(SbroadError::DoSkip), } } @@ -495,7 +495,7 @@ impl Plan { /// # Errors /// - If the plan tree is invalid (doesn't contain correct nodes where we expect it to). #[otm_child_span("plan.transformation.derive_equalities")] - pub fn derive_equalities(&mut self) -> Result<(), QueryPlannerError> { + pub fn derive_equalities(&mut self) -> Result<(), SbroadError> { self.transform_expr_trees(&call_expr_tree_derive_equalities) } } diff --git a/sbroad-core/src/ir/transformation/merge_tuples.rs b/sbroad-core/src/ir/transformation/merge_tuples.rs index 2c0e8242eb..9bbd1b1e4e 100644 --- a/sbroad-core/src/ir/transformation/merge_tuples.rs +++ b/sbroad-core/src/ir/transformation/merge_tuples.rs @@ -10,7 +10,7 @@ //! select * from t where (a, b, c) = (1, 2, 3) //! ``` -use crate::errors::QueryPlannerError; +use crate::errors::{Entity, SbroadError}; use crate::ir::expression::Expression; use crate::ir::helpers::RepeatableState; use crate::ir::operator::Bool; @@ -20,18 +20,18 @@ use sbroad_proc::otm_child_span; use std::collections::{hash_map::Entry, HashMap, HashSet}; use traversal::Bft; -fn call_expr_tree_merge_tuples(plan: &mut Plan, top_id: usize) -> Result<usize, QueryPlannerError> { +fn call_expr_tree_merge_tuples(plan: &mut Plan, top_id: usize) -> Result<usize, SbroadError> { plan.expr_tree_modify_and_chains(top_id, &call_build_and_chains, &call_as_plan) } fn call_build_and_chains( plan: &mut Plan, nodes: &[usize], -) -> Result<HashMap<usize, Chain, RepeatableState>, QueryPlannerError> { +) -> Result<HashMap<usize, Chain, RepeatableState>, SbroadError> { plan.populate_and_chains(nodes) } -fn call_as_plan(chain: &Chain, plan: &mut Plan) -> Result<usize, QueryPlannerError> { +fn call_as_plan(chain: &Chain, plan: &mut Plan) -> Result<usize, SbroadError> { chain.as_plan(plan) } @@ -61,15 +61,17 @@ impl Chain { /// - Failed if the node is not an expression. /// - Failed if expression is not an "AND" or "OR". /// - There is something wrong with our sub-queries. - pub fn insert(&mut self, plan: &mut Plan, expr_id: usize) -> Result<(), QueryPlannerError> { + pub fn insert(&mut self, plan: &mut Plan, expr_id: usize) -> Result<(), SbroadError> { let bool_expr = plan.get_expression_node(expr_id)?; if let Expression::Bool { left, op, right } = bool_expr { if let Bool::And | Bool::Or = op { // We don't expect nested AND/OR expressions in DNF. - return Err(QueryPlannerError::CustomError(format!( - "AND/OR expressions are not supported: {:?}", - bool_expr - ))); + return Err(SbroadError::Unsupported( + Entity::Operator, + Some(format!( + "AND/OR expressions are not supported: {bool_expr:?}" + )), + )); } // Merge expression into tuples only for equality operators. @@ -125,7 +127,7 @@ impl Chain { Ok(()) } - fn as_plan(&self, plan: &mut Plan) -> Result<usize, QueryPlannerError> { + fn as_plan(&self, plan: &mut Plan) -> Result<usize, SbroadError> { let other_top_id = match self.other.split_first() { Some((first, other)) => { let mut top_id = *first; @@ -163,8 +165,8 @@ impl Chain { } (Some(grouped_top_id), None) => Ok(grouped_top_id), (None, Some(other_top_id)) => Ok(other_top_id), - (None, None) => Err(QueryPlannerError::CustomError( - "No expressions to merge".to_string(), + (None, None) => Err(SbroadError::UnexpectedNumberOfValues( + "no expressions to merge, expected one or twoe".to_string(), )), } } @@ -187,7 +189,7 @@ impl Chain { } impl Plan { - fn get_columns_or_self(&self, expr_id: usize) -> Result<Vec<usize>, QueryPlannerError> { + fn get_columns_or_self(&self, expr_id: usize) -> Result<Vec<usize>, SbroadError> { let expr = self.get_expression_node(expr_id)?; match expr { Expression::Row { list, .. } => Ok(list.clone()), @@ -203,7 +205,7 @@ impl Plan { pub fn populate_and_chains( &mut self, nodes: &[usize], - ) -> Result<HashMap<usize, Chain, RepeatableState>, QueryPlannerError> { + ) -> Result<HashMap<usize, Chain, RepeatableState>, SbroadError> { let mut visited: HashSet<usize> = HashSet::with_capacity(self.nodes.next_id()); let mut chains: HashMap<usize, Chain, RepeatableState> = HashMap::with_capacity_and_hasher(nodes.len(), RepeatableState); @@ -267,12 +269,10 @@ impl Plan { f_build_chains: &dyn Fn( &mut Plan, &[usize], - ) -> Result< - HashMap<usize, Chain, RepeatableState>, - QueryPlannerError, - >, - f_to_plan: &dyn Fn(&Chain, &mut Plan) -> Result<usize, QueryPlannerError>, - ) -> Result<usize, QueryPlannerError> { + ) + -> Result<HashMap<usize, Chain, RepeatableState>, SbroadError>, + f_to_plan: &dyn Fn(&Chain, &mut Plan) -> Result<usize, SbroadError>, + ) -> Result<usize, SbroadError> { let tree = Bft::new(&expr_id, |node| self.nodes.expr_iter(node, false)); let nodes: Vec<usize> = tree.map(|(_, id)| *id).collect(); let chains = f_build_chains(self, &nodes)?; @@ -293,10 +293,10 @@ impl Plan { { *child_id = new_child_id; } else { - return Err(QueryPlannerError::CustomError(format!( - "Expected alias expression: {:?}", - expr_mut - ))); + return Err(SbroadError::Invalid( + Entity::Expression, + Some(format!("expected alias expression: {expr_mut:?}")), + )); } } } @@ -319,10 +319,10 @@ impl Plan { *right_id = new_child_id; } } else { - return Err(QueryPlannerError::CustomError(format!( - "Expected boolean expression: {:?}", - expr_mut - ))); + return Err(SbroadError::Invalid( + Entity::Expression, + Some(format!("expected boolean expression: {expr_mut:?}")), + )); } } } @@ -338,16 +338,16 @@ impl Plan { if let Some(child_id) = list.get_mut(pos) { *child_id = new_child_id; } else { - return Err(QueryPlannerError::CustomError(format!( - "Expected a column at position {} in the row {:?}", + return Err(SbroadError::UnexpectedNumberOfValues(format!( + "expected a column at position {} in the row {:?}", pos, expr_mut ))); } } else { - return Err(QueryPlannerError::CustomError(format!( - "Expected row expression: {:?}", - expr_mut - ))); + return Err(SbroadError::Invalid( + Entity::Expression, + Some(format!("expected row expression: {expr_mut:?}")), + )); } } } @@ -371,7 +371,7 @@ impl Plan { /// # Errors /// - If the plan tree is invalid (doesn't contain correct nodes where we expect it to). #[otm_child_span("plan.transformation.merge_tuples")] - pub fn merge_tuples(&mut self) -> Result<(), QueryPlannerError> { + pub fn merge_tuples(&mut self) -> Result<(), SbroadError> { self.transform_expr_trees(&call_expr_tree_merge_tuples) } } diff --git a/sbroad-core/src/ir/transformation/redistribution.rs b/sbroad-core/src/ir/transformation/redistribution.rs index 4ae9e1758a..537de38813 100644 --- a/sbroad-core/src/ir/transformation/redistribution.rs +++ b/sbroad-core/src/ir/transformation/redistribution.rs @@ -7,7 +7,7 @@ use std::collections::{hash_map::Entry, HashMap, HashSet}; use std::fmt::{Display, Formatter}; use traversal::{Bft, DftPost}; -use crate::errors::QueryPlannerError; +use crate::errors::{Action, Entity, SbroadError}; use crate::ir::distribution::{Distribution, Key, KeySet}; use crate::ir::expression::Expression; use crate::ir::operator::{Bool, Relational}; @@ -100,7 +100,7 @@ struct BoolOp { } impl BoolOp { - fn from_expr(plan: &Plan, expr_id: usize) -> Result<Self, QueryPlannerError> { + fn from_expr(plan: &Plan, expr_id: usize) -> Result<Self, SbroadError> { if let Expression::Bool { left, op, right, .. } = plan.get_expression_node(expr_id)? @@ -111,7 +111,7 @@ impl BoolOp { right: *right, }) } else { - Err(QueryPlannerError::InvalidBool) + Err(SbroadError::Invalid(Entity::Operator, None)) } } } @@ -155,7 +155,7 @@ impl Plan { /// /// # Errors /// - plan doesn't contain the top node - fn get_relational_nodes_dfs_post(&self) -> Result<Vec<usize>, QueryPlannerError> { + fn get_relational_nodes_dfs_post(&self) -> Result<Vec<usize>, SbroadError> { let top = self.get_top()?; let post_tree = DftPost::new(&top, |node| self.nodes.rel_iter(node)); let nodes: Vec<usize> = post_tree.map(|(_, id)| *id).collect(); @@ -169,7 +169,7 @@ impl Plan { pub(crate) fn get_bool_nodes_with_row_children( &self, top: usize, - ) -> Result<Vec<usize>, QueryPlannerError> { + ) -> Result<Vec<usize>, SbroadError> { let mut nodes: Vec<usize> = Vec::new(); let post_tree = DftPost::new(&top, |node| self.nodes.expr_iter(node, false)); @@ -197,10 +197,7 @@ impl Plan { /// # Errors /// - Row node is not of a row type /// There are more than one sub-queries in the row node. - pub fn get_sub_query_from_row_node( - &self, - row_id: usize, - ) -> Result<Option<usize>, QueryPlannerError> { + pub fn get_sub_query_from_row_node(&self, row_id: usize) -> Result<Option<usize>, SbroadError> { let rel_ids = self.get_relational_from_row_nodes(row_id)?; self.get_sub_query_among_rel_nodes(&rel_ids) } @@ -213,7 +210,7 @@ impl Plan { pub fn get_sub_query_among_rel_nodes( &self, rel_nodes: &HashSet<usize, RandomState>, - ) -> Result<Option<usize>, QueryPlannerError> { + ) -> Result<Option<usize>, SbroadError> { let mut sq_set: HashSet<usize, RandomState> = HashSet::with_hasher(RandomState::new()); for rel_id in rel_nodes { if let Node::Relational(Relational::ScanSubQuery { .. }) = self.get_node(*rel_id)? { @@ -223,7 +220,7 @@ impl Plan { match sq_set.len().cmp(&1) { Ordering::Equal => sq_set.iter().next().map_or_else( || { - Err(QueryPlannerError::CustomError(format!( + Err(SbroadError::UnexpectedNumberOfValues(format!( "Failed to get the first sub-query node from the list of relational nodes: {:?}.", rel_nodes ))) @@ -231,7 +228,7 @@ impl Plan { |sq_id| Ok(Some(*sq_id)), ), Ordering::Less => Ok(None), - Ordering::Greater => Err(QueryPlannerError::CustomError(format!( + Ordering::Greater => Err(SbroadError::UnexpectedNumberOfValues(format!( "Found multiple sub-queries in a list of relational nodes: {:?}.", rel_nodes ))), @@ -243,7 +240,7 @@ impl Plan { /// # Errors /// - Row node is not of a row type /// - There are more than one motion nodes in the row node - pub fn get_motion_from_row(&self, node_id: usize) -> Result<Option<usize>, QueryPlannerError> { + pub fn get_motion_from_row(&self, node_id: usize) -> Result<Option<usize>, SbroadError> { let rel_nodes = self.get_relational_from_row_nodes(node_id)?; self.get_motion_among_rel_nodes(&rel_nodes) } @@ -256,7 +253,7 @@ impl Plan { pub fn get_motion_among_rel_nodes( &self, rel_nodes: &HashSet<usize, RandomState>, - ) -> Result<Option<usize>, QueryPlannerError> { + ) -> Result<Option<usize>, SbroadError> { let mut motion_set: HashSet<usize> = HashSet::new(); for child in rel_nodes { @@ -268,14 +265,14 @@ impl Plan { match motion_set.len().cmp(&1) { Ordering::Equal => { let motion_id = motion_set.iter().next().ok_or_else(|| { - QueryPlannerError::CustomError( - "Failed to get the first motion node from the set.".into(), + SbroadError::UnexpectedNumberOfValues( + "failed to get the first Motion node from the set.".into(), ) })?; Ok(Some(*motion_id)) } Ordering::Less => Ok(None), - Ordering::Greater => Err(QueryPlannerError::CustomError( + Ordering::Greater => Err(SbroadError::UnexpectedNumberOfValues( "Node must contain only a single motion".into(), )), } @@ -291,7 +288,7 @@ impl Plan { outer_id: usize, inner_id: usize, op: &Bool, - ) -> Result<MotionPolicy, QueryPlannerError> { + ) -> Result<MotionPolicy, SbroadError> { let outer_dist = self.get_distribution(outer_id)?; let inner_dist = self.get_distribution(inner_id)?; if Bool::Eq == *op || Bool::In == *op || Bool::NotEq == *op || Bool::NotIn == *op { @@ -311,7 +308,7 @@ impl Plan { // Redistribute the inner tuples using the first key from the outer tuple. return keys_outer.iter().next().map_or_else( || { - Err(QueryPlannerError::CustomError(String::from( + Err(SbroadError::UnexpectedNumberOfValues(String::from( "Failed to get the first distribution key from the outer row.", ))) }, @@ -327,13 +324,15 @@ impl Plan { &mut self, rel_id: usize, strategy: &Strategy, - ) -> Result<(), QueryPlannerError> { + ) -> Result<(), SbroadError> { let children: Vec<usize> = if let Some(children) = self.get_relational_children(rel_id)? { children.to_vec() } else { - return Err(QueryPlannerError::CustomError(String::from( - "Trying to add motions under the leaf relational node.", - ))); + return Err(SbroadError::FailedTo( + Action::Add, + Some(Entity::Motion), + "trying to add motions under the leaf relational node".into(), + )); }; // Check that all children we need to add motions exist in the current relational node. @@ -342,9 +341,11 @@ impl Plan { .iter() .all(|(node, _)| children_set.get(node).is_some()) { - return Err(QueryPlannerError::CustomError(String::from( - "Trying to add motions for non-existing children in relational node.", - ))); + return Err(SbroadError::FailedTo( + Action::Add, + Some(Entity::Motion), + "trying to add motions for non-existing children in relational node".into(), + )); } // Add motions. @@ -368,7 +369,7 @@ impl Plan { &self, rel_id: usize, row_id: usize, - ) -> Result<Option<usize>, QueryPlannerError> { + ) -> Result<Option<usize>, SbroadError> { if self.get_expression_node(row_id)?.is_row() { if let Some(sq_id) = self.get_sub_query_from_row_node(row_id)? { if self.is_additional_child_of_rel(rel_id, sq_id)? { @@ -383,7 +384,7 @@ impl Plan { &self, rel_id: usize, node_id: usize, - ) -> Result<Vec<(usize, MotionPolicy)>, QueryPlannerError> { + ) -> Result<Vec<(usize, MotionPolicy)>, SbroadError> { let mut strategies: Vec<(usize, MotionPolicy)> = Vec::new(); let bool_op = BoolOp::from_expr(self, node_id)?; let left = self.get_additional_sq(rel_id, bool_op.left)?; @@ -431,7 +432,7 @@ impl Plan { &mut self, rel_id: usize, expr_id: usize, - ) -> Result<Strategy, QueryPlannerError> { + ) -> Result<Strategy, SbroadError> { let nodes = self.get_bool_nodes_with_row_children(expr_id)?; for node in &nodes { let bool_op = BoolOp::from_expr(self, *node)?; @@ -454,16 +455,17 @@ impl Plan { /// # Errors /// - If the node is not a join node. /// - Join node has no children. - fn get_join_children(&self, join_id: usize) -> Result<&[usize], QueryPlannerError> { + fn get_join_children(&self, join_id: usize) -> Result<&[usize], SbroadError> { let join = self.get_relation_node(join_id)?; if let Relational::InnerJoin { .. } = join { } else { - return Err(QueryPlannerError::CustomError( - "Join node is not an inner join.".into(), + return Err(SbroadError::Invalid( + Entity::Relational, + Some("Join node is not an inner join.".into()), )); } let children = join.children().ok_or_else(|| { - QueryPlannerError::CustomError("Join node doesn't have any children.".into()) + SbroadError::UnexpectedNumberOfValues("Join node has no children.".into()) })?; Ok(children) } @@ -474,40 +476,38 @@ impl Plan { key: &Key, row_map: &HashMap<usize, usize>, join_children: &[usize], - ) -> Result<usize, QueryPlannerError> { + ) -> Result<usize, SbroadError> { let mut children_set: HashSet<usize> = HashSet::new(); for pos in &key.positions { let column_id = *row_map.get(pos).ok_or_else(|| { - QueryPlannerError::CustomError(format!( - "Column {} not found in row map {:?}.", - pos, row_map - )) + SbroadError::NotFound(Entity::Column, format!("{} in row map {:?}", pos, row_map)) })?; if let Expression::Reference { targets, .. } = self.get_expression_node(column_id)? { if let Some(targets) = targets { for target in targets { let child_id = *join_children.get(*target).ok_or_else(|| { - QueryPlannerError::CustomError(format!( - "Target {} not found in join children {:?}.", - target, join_children - )) + SbroadError::NotFound( + Entity::Target, + format!("{} in join children {:?}", target, join_children), + ) })?; children_set.insert(child_id); } } } else { - return Err(QueryPlannerError::CustomError( - "Row column is not a reference.".into(), + return Err(SbroadError::Invalid( + Entity::Expression, + Some("Row column is not a reference.".into()), )); } } if children_set.len() > 1 { - return Err(QueryPlannerError::CustomError( + return Err(SbroadError::UnexpectedNumberOfValues( "Distribution key in the join condition has more than one child.".into(), )); } children_set.iter().next().copied().ok_or_else(|| { - QueryPlannerError::CustomError( + SbroadError::UnexpectedNumberOfValues( "Distribution key in the join condition has no children.".into(), ) }) @@ -517,7 +517,7 @@ impl Plan { /// /// # Errors /// - If the node is not a row node. - fn build_row_map(&self, row_id: usize) -> Result<HashMap<usize, usize>, QueryPlannerError> { + fn build_row_map(&self, row_id: usize) -> Result<HashMap<usize, usize>, SbroadError> { let columns = self.get_expression_node(row_id)?.get_row_list()?; let mut map: HashMap<usize, usize> = HashMap::new(); for (pos, col) in columns.iter().enumerate() { @@ -538,16 +538,16 @@ impl Plan { join_id: usize, keys: &[Key], row_map: &HashMap<usize, usize>, - ) -> Result<(Vec<Key>, Vec<Key>), QueryPlannerError> { + ) -> Result<(Vec<Key>, Vec<Key>), SbroadError> { let mut outer_keys: Vec<Key> = Vec::new(); let mut inner_keys: Vec<Key> = Vec::new(); let children = self.get_join_children(join_id)?; let outer_child = *children.first().ok_or_else(|| { - QueryPlannerError::CustomError("Join node doesn't have an outer child.".into()) + SbroadError::UnexpectedNumberOfValues("Join node has no children.".into()) })?; let inner_child = *children.get(1).ok_or_else(|| { - QueryPlannerError::CustomError("Join node doesn't have an inner child.".into()) + SbroadError::NotFound(Entity::Node, "that is Join node inner child".into()) })?; for key in keys { @@ -558,8 +558,12 @@ impl Plan { inner_keys.push(key.clone()); } else { // It can be only a sub-query, but we have already processed it. - return Err(QueryPlannerError::CustomError( - "Distribution key doesn't correspond to inner or outer join children.".into(), + return Err(SbroadError::Invalid( + Entity::DistributionKey, + Some( + "distribution key doesn't correspond to inner or outer join children." + .into(), + ), )); } } @@ -577,13 +581,13 @@ impl Plan { join_id: usize, left_row_id: usize, right_row_id: usize, - ) -> Result<MotionPolicy, QueryPlannerError> { + ) -> Result<MotionPolicy, SbroadError> { let left_dist = self.get_distribution(left_row_id)?; let right_dist = self.get_distribution(right_row_id)?; let get_policy_for_one_side_segment = |row_map: &HashMap<usize, usize>, keys_set: &KeySet| - -> Result<MotionPolicy, QueryPlannerError> { + -> Result<MotionPolicy, SbroadError> { let keys = keys_set.iter().map(Clone::clone).collect::<Vec<_>>(); let (outer_keys, _) = self.split_join_keys_to_inner_and_outer(join_id, &keys, row_map)?; @@ -655,7 +659,7 @@ impl Plan { &mut self, rel_id: usize, expr_id: usize, - ) -> Result<Strategy, QueryPlannerError> { + ) -> Result<Strategy, SbroadError> { // First, we need to set the motion policy for each boolean expression in the join condition. let nodes = self.get_bool_nodes_with_row_children(expr_id)?; for node in &nodes { @@ -672,14 +676,17 @@ impl Plan { strategy.insert(*child_id, (MotionPolicy::Full, DataGeneration::None)); } } else { - return Err(QueryPlannerError::CustomError( + return Err(SbroadError::UnexpectedNumberOfValues( "Join node doesn't have any children.".into(), )); } // Let's improve the full motion policy for the join children (sub-queries and the inner child). let inner_child = *join_children.get(1).ok_or_else(|| { - QueryPlannerError::CustomError("Join node doesn't have an inner child.".into()) + SbroadError::NotFound( + Entity::Node, + "that is Join node inner child with index 1.".into(), + ) })?; let mut inner_map: HashMap<usize, MotionPolicy> = HashMap::new(); let mut new_inner_policy = MotionPolicy::Full; @@ -722,8 +729,9 @@ impl Plan { Bool::And => join_policy_for_and(&left_policy, &right_policy), Bool::Or => join_policy_for_or(&left_policy, &right_policy), _ => { - return Err(QueryPlannerError::CustomError( - "Unsupported boolean operation".into(), + return Err(SbroadError::Unsupported( + Entity::Operator, + Some("unsupported boolean operation, expected And or Or".into()), )) } } @@ -736,8 +744,9 @@ impl Plan { Bool::Gt | Bool::GtEq | Bool::Lt | Bool::LtEq => MotionPolicy::Full, Bool::And | Bool::Or => { // "a and 1" or "a or 1" expressions make no sense. - return Err(QueryPlannerError::CustomError( - "Unsupported boolean operation".into(), + return Err(SbroadError::Unsupported( + Entity::Operator, + Some("unsupported boolean operation And or Or".into()), )); } } @@ -751,8 +760,9 @@ impl Plan { .cloned() .unwrap_or(MotionPolicy::Full), _ => { - return Err(QueryPlannerError::CustomError( - "Unsupported boolean operation".into(), + return Err(SbroadError::Unsupported( + Entity::Operator, + Some("unsupported boolean operation".into()), )) } }; @@ -762,7 +772,8 @@ impl Plan { Ok(strategy) } - fn resolve_insert_conflicts(&mut self, rel_id: usize) -> Result<Strategy, QueryPlannerError> { + #[allow(clippy::too_many_lines)] + fn resolve_insert_conflicts(&mut self, rel_id: usize) -> Result<Strategy, SbroadError> { let mut map: Strategy = HashMap::new(); match self.get_relation_node(rel_id)? { Relational::Insert { @@ -775,7 +786,7 @@ impl Plan { { *child } else { - return Err(QueryPlannerError::CustomError( + return Err(SbroadError::UnexpectedNumberOfValues( "Insert node doesn't have exactly a single child.".into(), )); }; @@ -787,12 +798,16 @@ impl Plan { { (list, distribution) } else { - return Err(QueryPlannerError::CustomError( - "Insert child node has an invalid node instead of the output row".into(), + return Err(SbroadError::Invalid( + Entity::Node, + Some( + "Insert child node has an invalid node instead of the output row" + .into(), + ), )); }; if list.len() != columns.len() { - return Err(QueryPlannerError::CustomError(format!( + return Err(SbroadError::UnexpectedNumberOfValues(format!( "Insert node expects {} columns instead of {}", list.len(), columns.len() @@ -805,7 +820,7 @@ impl Plan { .collect::<HashMap<_, _>>(); let mut motion_key: MotionKey = MotionKey::new(); let rel = self.get_relation(relation).ok_or_else(|| { - QueryPlannerError::CustomError(format!("Relation {relation} not found")) + SbroadError::NotFound(Entity::Table, format!("{relation} among plan relations")) })?; for pos in &rel.key.positions { if let Some(child_pos) = columns_map.get(pos) { @@ -816,9 +831,10 @@ impl Plan { } else { // Check that the column exists on the requested position. rel.columns.get(*pos).ok_or_else(|| { - QueryPlannerError::CustomError(format!( - "Column {pos} not found in relation {relation}" - )) + SbroadError::NotFound( + Entity::Column, + format!("{pos} in relation {relation}"), + ) })?; // We need a default value for the key column. motion_key @@ -827,10 +843,10 @@ impl Plan { } } if distribution.is_none() { - return Err(QueryPlannerError::CustomError(format!( - "Insert node child {} has no distribution", - child - ))); + return Err(SbroadError::Invalid( + Entity::Distribution, + Some(format!("Insert node child {child} has no distribution")), + )); } // At the moment we always add a segment motion policy under the @@ -849,8 +865,9 @@ impl Plan { ); } _ => { - return Err(QueryPlannerError::CustomError( - "Expected insert node".into(), + return Err(SbroadError::Invalid( + Entity::Relational, + Some("expected insert node".into()), )) } } @@ -860,12 +877,13 @@ impl Plan { let sharding_pos = if let Relational::Insert { relation, .. } = self.get_relation_node(rel_id)? { let rel = self.get_relation(relation).ok_or_else(|| { - QueryPlannerError::CustomError(format!("Relation {relation} not found")) + SbroadError::NotFound(Entity::Table, format!("{relation} among plan relations")) })?; rel.get_bucket_id_position()? } else { - return Err(QueryPlannerError::CustomError( - "Expected insert node".into(), + return Err(SbroadError::Invalid( + Entity::Relational, + Some("expected insert node".into()), )); }; if let Relational::Insert { @@ -878,7 +896,7 @@ impl Plan { Ok(map) } - fn resolve_except_conflicts(&mut self, rel_id: usize) -> Result<Strategy, QueryPlannerError> { + fn resolve_except_conflicts(&mut self, rel_id: usize) -> Result<Strategy, SbroadError> { let mut map: Strategy = HashMap::new(); match self.get_relation_node(rel_id)? { Relational::Except { children, .. } => { @@ -892,7 +910,7 @@ impl Plan { let right_output_row = self.get_expression_node(right_output_id)?.get_row_list()?; if left_output_row.len() != right_output_row.len() { - return Err(QueryPlannerError::CustomError(format!( + return Err(SbroadError::UnexpectedNumberOfValues(format!( "Except node children have different row lengths: left {}, right {}", left_output_row.len(), right_output_row.len() @@ -913,8 +931,9 @@ impl Plan { return Ok(map); } } - let key = left_keys.iter().next().ok_or_else(|| QueryPlannerError::CustomError( - "Left child's segment distribution is invalid: no keys found in the set".into() + let key = left_keys.iter().next().ok_or_else(|| SbroadError::Invalid( + Entity::Distribution, + Some("left child's segment distribution is invalid: no keys found in the set".into()) ))?; map.insert( *right, @@ -927,12 +946,13 @@ impl Plan { } return Ok(map); } - Err(QueryPlannerError::CustomError( + Err(SbroadError::UnexpectedNumberOfValues( "Except node doesn't have exactly two children.".into(), )) } - _ => Err(QueryPlannerError::CustomError( - "Expected except node".into(), + _ => Err(SbroadError::Invalid( + Entity::Relational, + Some("expected Except node".into()), )), } } @@ -944,7 +964,7 @@ impl Plan { /// - failed to resolve distribution conflicts /// - failed to set distribution #[otm_child_span("plan.transformation.add_motions")] - pub fn add_motions(&mut self) -> Result<(), QueryPlannerError> { + pub fn add_motions(&mut self) -> Result<(), SbroadError> { let nodes = self.get_relational_nodes_dfs_post()?; for id in &nodes { match self.get_relation_node(*id)?.clone() { @@ -962,7 +982,7 @@ impl Plan { Relational::Motion { .. } => { // We can apply this transformation only once, // i.e. to the plan without any motion nodes. - return Err(QueryPlannerError::CustomError(String::from( + return Err(SbroadError::DuplicatedValue(String::from( "IR already has Motion nodes.", ))); } diff --git a/sbroad-core/src/ir/transformation/redistribution/tests.rs b/sbroad-core/src/ir/transformation/redistribution/tests.rs index 5e42e14b31..19fec9f4c3 100644 --- a/sbroad-core/src/ir/transformation/redistribution/tests.rs +++ b/sbroad-core/src/ir/transformation/redistribution/tests.rs @@ -1,6 +1,6 @@ use super::*; use crate::collection; -use crate::errors::QueryPlannerError; +use crate::errors::SbroadError; use crate::ir::distribution::{Distribution, Key}; use crate::ir::operator::Relational; use crate::ir::relation::{Column, ColumnRole, Table, Type}; @@ -65,7 +65,10 @@ fn segment_motion_for_sub_query() { assert_eq!(Some(sq_id), plan.get_sub_query_from_row_node(b_id).unwrap()); assert_eq!( - QueryPlannerError::UninitializedDistribution, + SbroadError::Invalid( + Entity::Distribution, + Some("distribution is uninitialized".into()), + ), plan.resolve_sub_query_conflicts(select_id, eq_id) .unwrap_err() ); diff --git a/sbroad-core/src/ir/transformation/split_columns.rs b/sbroad-core/src/ir/transformation/split_columns.rs index 6ce693ae38..b712c2d1a8 100644 --- a/sbroad-core/src/ir/transformation/split_columns.rs +++ b/sbroad-core/src/ir/transformation/split_columns.rs @@ -12,17 +12,14 @@ //! select a from t where (a) = (1) and (2) = (b) //! ``` -use crate::errors::QueryPlannerError; +use crate::errors::{Entity, SbroadError}; use crate::ir::expression::Expression; use crate::ir::operator::Bool; use crate::ir::Plan; use crate::otm::child_span; use sbroad_proc::otm_child_span; -fn call_expr_tree_split_columns( - plan: &mut Plan, - top_id: usize, -) -> Result<usize, QueryPlannerError> { +fn call_expr_tree_split_columns(plan: &mut Plan, top_id: usize) -> Result<usize, SbroadError> { plan.expr_tree_replace_bool( top_id, &call_split_bool, @@ -37,7 +34,7 @@ fn call_expr_tree_split_columns( ) } -fn call_split_bool(plan: &mut Plan, top_id: usize) -> Result<usize, QueryPlannerError> { +fn call_split_bool(plan: &mut Plan, top_id: usize) -> Result<usize, SbroadError> { plan.split_bool(top_id) } @@ -49,17 +46,17 @@ impl Plan { /// - If the operator is not a boolean operator. /// - If left and right tuples have different number of columns. /// - If the plan is invalid for some unknown reason. - fn split_bool(&mut self, expr_id: usize) -> Result<usize, QueryPlannerError> { + fn split_bool(&mut self, expr_id: usize) -> Result<usize, SbroadError> { let expr = self.get_expression_node(expr_id)?; let (left_id, right_id, op) = match expr { Expression::Bool { left, op, right, .. } => (*left, *right, op.clone()), _ => { - return Err(QueryPlannerError::CustomError(format!( - "Node is not a boolean expression: {:?}", - expr - ))); + return Err(SbroadError::Invalid( + Entity::Expression, + Some(format!("node is not a boolean expression: {expr:?}")), + )); } }; let left_expr = self.get_expression_node(left_id)?; @@ -74,8 +71,8 @@ impl Plan { ) = (left_expr, right_expr) { if left_list.len() != right_list.len() { - return Err(QueryPlannerError::CustomError(format!( - "Left and right rows have different number of columns: {:?}, {:?}", + return Err(SbroadError::UnexpectedNumberOfValues(format!( + "left and right rows have different number of columns: {:?}, {:?}", left_expr, right_expr ))); } @@ -111,7 +108,7 @@ impl Plan { /// # Errors /// - If the plan tree is invalid (doesn't contain correct nodes where we expect it to). #[otm_child_span("plan.transformation.split_columns")] - pub fn split_columns(&mut self) -> Result<(), QueryPlannerError> { + pub fn split_columns(&mut self) -> Result<(), SbroadError> { self.transform_expr_trees(&call_expr_tree_split_columns) } } diff --git a/sbroad-core/src/ir/transformation/split_columns/tests.rs b/sbroad-core/src/ir/transformation/split_columns/tests.rs index 4afc205575..7725f9503d 100644 --- a/sbroad-core/src/ir/transformation/split_columns/tests.rs +++ b/sbroad-core/src/ir/transformation/split_columns/tests.rs @@ -50,8 +50,9 @@ fn split_columns3() { let plan_err = plan.split_columns().unwrap_err(); assert_eq!( format!( - "{} {} {}", - r#"Left and right rows have different number of columns:"#, + "{} {} {} {}", + r#"unexpected number of values:"#, + r#"left and right rows have different number of columns:"#, r#"Row { list: [12, 13, 14], distribution: None },"#, r#"Row { list: [16, 17], distribution: None }"#, ), diff --git a/sbroad-core/src/ir/value/double.rs b/sbroad-core/src/ir/value/double.rs index cc7ac85b9a..e332144c91 100644 --- a/sbroad-core/src/ir/value/double.rs +++ b/sbroad-core/src/ir/value/double.rs @@ -5,7 +5,7 @@ use std::hash::{Hash, Hasher}; use std::num::NonZeroI32; use std::str::FromStr; -use crate::errors::QueryPlannerError; +use crate::errors::{Entity, SbroadError}; use serde::{Deserialize, Serialize}; use tarantool::tlua; @@ -51,13 +51,13 @@ impl From<u64> for Double { } impl FromStr for Double { - type Err = QueryPlannerError; + type Err = SbroadError; fn from_str(s: &str) -> Result<Self, Self::Err> { Ok(Double { - value: s - .parse::<f64>() - .map_err(|_| QueryPlannerError::CustomError(format!("{s} is not a valid f64")))?, + value: s.parse::<f64>().map_err(|_| { + SbroadError::ParsingError(Entity::Value, format!("{s} is not a valid f64")) + })?, }) } } diff --git a/sbroad-core/src/otm/statistics.rs b/sbroad-core/src/otm/statistics.rs index 9bbad3b7f6..e6e41b0ccc 100644 --- a/sbroad-core/src/otm/statistics.rs +++ b/sbroad-core/src/otm/statistics.rs @@ -83,7 +83,7 @@ impl SpanProcessor for StatCollector { let tuple = (id.to_string(), query_sql.to_string(), 2); query_space.borrow_mut().upsert(tuple); }); - debug!(Option::from("on start"), &format!("query: {}", query_sql)); + debug!(Option::from("on start"), &format!("query: {query_sql}")); } // Register current span mapping (span id: span name). @@ -92,7 +92,7 @@ impl SpanProcessor for StatCollector { let value = SpanName::from(span_data.name.clone()); debug!( Option::from("on start"), - &format!("key: {:?}, value: {:?}", key, value) + &format!("key: {key:?}, value: {value:?}") ); span_table.borrow_mut().push(key, value); }); diff --git a/sbroad-core/src/otm/statistics/eviction.rs b/sbroad-core/src/otm/statistics/eviction.rs index bb1bfe76c9..e2e4aa6532 100644 --- a/sbroad-core/src/otm/statistics/eviction.rs +++ b/sbroad-core/src/otm/statistics/eviction.rs @@ -6,7 +6,7 @@ //! space. use crate::debug; -use crate::errors::QueryPlannerError; +use crate::errors::SbroadError; use crate::executor::lru::{Cache, LRUCache}; use crate::otm::statistics::table::{TarantoolSpace, QUERY}; use std::cell::RefCell; @@ -19,12 +19,12 @@ pub struct TrackedQueries { } #[allow(clippy::unnecessary_wraps)] -fn remove_query(query_id: &mut String) -> Result<(), QueryPlannerError> { +fn remove_query(query_id: &mut String) -> Result<(), SbroadError> { QUERY.with(|query_space| { let mut query_space = query_space.borrow_mut(); debug!( Option::from("tracked queries"), - &format!("remove query: {}", query_id) + &format!("remove query: {query_id}") ); let key = std::mem::take(query_id); query_space.delete(&(key,)); @@ -54,7 +54,7 @@ impl TrackedQueries { /// /// # Errors /// - Internal error in the eviction function. - pub fn push(&mut self, key: String) -> Result<(), QueryPlannerError> { + pub fn push(&mut self, key: String) -> Result<(), SbroadError> { self.queries.put(key.clone(), key) } } -- GitLab