From d786f29b1fe44c5af4f974fc30683b65318d8a12 Mon Sep 17 00:00:00 2001 From: muzarski Date: Tue, 6 Aug 2024 20:08:42 +0200 Subject: [PATCH 01/22] f_errors: add information about accepted bytes lengths for inet --- scylla-cql/src/frame/frame_errors.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scylla-cql/src/frame/frame_errors.rs b/scylla-cql/src/frame/frame_errors.rs index 491ebda82a..d7d9af68e7 100644 --- a/scylla-cql/src/frame/frame_errors.rs +++ b/scylla-cql/src/frame/frame_errors.rs @@ -80,7 +80,7 @@ pub enum LowLevelDeserializationError { InvalidValueLength(i32), #[error("Unknown consistency: {0}")] UnknownConsistency(#[from] TryFromPrimitiveError), - #[error("Invalid inet bytes length: {0}")] + #[error("Invalid inet bytes length: {0}. Accepted lengths are 4 and 16 bytes.")] InvalidInetLength(u8), #[error("UTF8 deserialization failed: {0}")] UTF8DeserializationError(#[from] std::str::Utf8Error), From 1c2c5f2fe48b9b432a1480bc8c2f9c307297fe50 Mon Sep 17 00:00:00 2001 From: muzarski Date: Tue, 6 Aug 2024 20:09:26 +0200 Subject: [PATCH 02/22] f_errors: remove context for TryFromSliceError The Display implementation for this error already says: "could not convert slice to array". No need to provide more context. --- scylla-cql/src/frame/frame_errors.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scylla-cql/src/frame/frame_errors.rs b/scylla-cql/src/frame/frame_errors.rs index d7d9af68e7..287628eb4d 100644 --- a/scylla-cql/src/frame/frame_errors.rs +++ b/scylla-cql/src/frame/frame_errors.rs @@ -72,7 +72,7 @@ pub enum LowLevelDeserializationError { IoError(#[from] std::io::Error), #[error(transparent)] TryFromIntError(#[from] std::num::TryFromIntError), - #[error("Failed to convert slice into array: {0}")] + #[error(transparent)] TryFromSliceError(#[from] std::array::TryFromSliceError), #[error("Not enough bytes! expected: {expected}, received: {received}")] TooFewBytesReceived { expected: usize, received: usize }, From 3f50fe9843592f1c54d0c15b3b50abd6312c593b Mon Sep 17 00:00:00 2001 From: muzarski Date: Wed, 26 Jun 2024 13:26:59 +0200 Subject: [PATCH 03/22] f_errors: SetKeyspaceParseError Introduced an error type returned when we fail to deserialize a RESULT response type to SET KEYSPACE request. --- scylla-cql/src/frame/frame_errors.rs | 9 +++++++++ scylla-cql/src/frame/response/result.rs | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/scylla-cql/src/frame/frame_errors.rs b/scylla-cql/src/frame/frame_errors.rs index 287628eb4d..d82e7ea5f1 100644 --- a/scylla-cql/src/frame/frame_errors.rs +++ b/scylla-cql/src/frame/frame_errors.rs @@ -35,6 +35,8 @@ pub enum FrameError { #[derive(Error, Debug)] pub enum ParseError { + #[error("Set keyspace response deserialization failed: {0}")] + SetKeyspaceParseError(#[from] SetKeyspaceParseError), #[error("Low-level deserialization failed: {0}")] LowLevelDeserializationError(#[from] LowLevelDeserializationError), #[error("Could not serialize frame: {0}")] @@ -55,6 +57,13 @@ pub enum ParseError { CqlTypeError(#[from] CqlTypeError), } +#[non_exhaustive] +#[derive(Error, Debug)] +pub enum SetKeyspaceParseError { + #[error("Malformed keyspace name: {0}")] + MalformedKeyspaceName(#[from] LowLevelDeserializationError), +} + /// A low level deserialization error. /// /// This type of error is returned when deserialization diff --git a/scylla-cql/src/frame/response/result.rs b/scylla-cql/src/frame/response/result.rs index b335b11b9f..d5251b0ccc 100644 --- a/scylla-cql/src/frame/response/result.rs +++ b/scylla-cql/src/frame/response/result.rs @@ -1,4 +1,5 @@ use crate::cql_to_rust::{FromRow, FromRowError}; +use crate::frame::frame_errors::SetKeyspaceParseError; use crate::frame::response::event::SchemaChangeEvent; use crate::frame::value::{ Counter, CqlDate, CqlDecimal, CqlDuration, CqlTime, CqlTimestamp, CqlTimeuuid, CqlVarint, @@ -852,7 +853,7 @@ fn deser_rows( }) } -fn deser_set_keyspace(buf: &mut &[u8]) -> StdResult { +fn deser_set_keyspace(buf: &mut &[u8]) -> StdResult { let keyspace_name = types::read_string(buf)?.to_string(); Ok(SetKeyspace { keyspace_name }) From c55298c296718be812a750e05b460bb0d617bca1 Mon Sep 17 00:00:00 2001 From: muzarski Date: Wed, 26 Jun 2024 13:51:14 +0200 Subject: [PATCH 04/22] f_errors: SchemaChangeEventParseError Introduced an error type returned when we fail to deserialize a RESULT::Schema_change response. --- scylla-cql/src/frame/frame_errors.rs | 27 ++++++++++++ scylla-cql/src/frame/response/event.rs | 57 +++++++++++++++++-------- scylla-cql/src/frame/response/result.rs | 4 +- 3 files changed, 69 insertions(+), 19 deletions(-) diff --git a/scylla-cql/src/frame/frame_errors.rs b/scylla-cql/src/frame/frame_errors.rs index d82e7ea5f1..701beaff32 100644 --- a/scylla-cql/src/frame/frame_errors.rs +++ b/scylla-cql/src/frame/frame_errors.rs @@ -37,6 +37,8 @@ pub enum FrameError { pub enum ParseError { #[error("Set keyspace response deserialization failed: {0}")] SetKeyspaceParseError(#[from] SetKeyspaceParseError), + #[error("Schema change event deserialization failed: {0}")] + SchemaChangeEventParseError(#[from] SchemaChangeEventParseError), #[error("Low-level deserialization failed: {0}")] LowLevelDeserializationError(#[from] LowLevelDeserializationError), #[error("Could not serialize frame: {0}")] @@ -64,6 +66,31 @@ pub enum SetKeyspaceParseError { MalformedKeyspaceName(#[from] LowLevelDeserializationError), } +/// An error type returned when deserialization of +/// `RESULT::Schema_change` response fails. +#[non_exhaustive] +#[derive(Error, Debug)] +pub enum SchemaChangeEventParseError { + #[error("Malformed schema change type string: {0}")] + TypeOfChangeParseError(LowLevelDeserializationError), + #[error("Malformed schema change target string:: {0}")] + TargetTypeParseError(LowLevelDeserializationError), + #[error("Malformed name of keyspace affected by schema change: {0}")] + AffectedKeyspaceParseError(LowLevelDeserializationError), + #[error("Malformed name of the table affected by schema change: {0}")] + AffectedTableNameParseError(LowLevelDeserializationError), + #[error("Malformed name of the target affected by schema change: {0}")] + AffectedTargetNameParseError(LowLevelDeserializationError), + #[error( + "Malformed number of arguments of the function/aggregate affected by schema change: {0}" + )] + ArgumentCountParseError(LowLevelDeserializationError), + #[error("Malformed argument of the function/aggregate affected by schema change: {0}")] + FunctionArgumentParseError(LowLevelDeserializationError), + #[error("Unknown target of schema change: {0}")] + UnknownTargetOfSchemaChange(String), +} + /// A low level deserialization error. /// /// This type of error is returned when deserialization diff --git a/scylla-cql/src/frame/response/event.rs b/scylla-cql/src/frame/response/event.rs index d738ad7297..a976e2fd15 100644 --- a/scylla-cql/src/frame/response/event.rs +++ b/scylla-cql/src/frame/response/event.rs @@ -1,4 +1,4 @@ -use crate::frame::frame_errors::ParseError; +use crate::frame::frame_errors::{ParseError, SchemaChangeEventParseError}; use crate::frame::server_event_type::EventType; use crate::frame::types; use std::net::SocketAddr; @@ -74,8 +74,9 @@ impl Event { } impl SchemaChangeEvent { - pub fn deserialize(buf: &mut &[u8]) -> Result { - let type_of_change_string = types::read_string(buf)?; + pub fn deserialize(buf: &mut &[u8]) -> Result { + let type_of_change_string = + types::read_string(buf).map_err(SchemaChangeEventParseError::TypeOfChangeParseError)?; let type_of_change = match type_of_change_string { "CREATED" => SchemaChangeType::Created, "UPDATED" => SchemaChangeType::Updated, @@ -83,8 +84,11 @@ impl SchemaChangeEvent { _ => SchemaChangeType::Invalid, }; - let target = types::read_string(buf)?; - let keyspace_affected = types::read_string(buf)?.to_string(); + let target = + types::read_string(buf).map_err(SchemaChangeEventParseError::TargetTypeParseError)?; + let keyspace_affected = types::read_string(buf) + .map_err(SchemaChangeEventParseError::AffectedKeyspaceParseError)? + .to_string(); match target { "KEYSPACE" => Ok(Self::KeyspaceChange { @@ -92,7 +96,9 @@ impl SchemaChangeEvent { keyspace_name: keyspace_affected, }), "TABLE" => { - let table_name = types::read_string(buf)?.to_string(); + let table_name = types::read_string(buf) + .map_err(SchemaChangeEventParseError::AffectedTargetNameParseError)? + .to_string(); Ok(Self::TableChange { change_type: type_of_change, keyspace_name: keyspace_affected, @@ -100,7 +106,9 @@ impl SchemaChangeEvent { }) } "TYPE" => { - let changed_type = types::read_string(buf)?.to_string(); + let changed_type = types::read_string(buf) + .map_err(SchemaChangeEventParseError::AffectedTargetNameParseError)? + .to_string(); Ok(Self::TypeChange { change_type: type_of_change, keyspace_name: keyspace_affected, @@ -108,13 +116,21 @@ impl SchemaChangeEvent { }) } "FUNCTION" => { - let function = types::read_string(buf)?.to_string(); - let number_of_arguments = types::read_short(buf)?; + let function = types::read_string(buf) + .map_err(SchemaChangeEventParseError::AffectedTargetNameParseError)? + .to_string(); + let number_of_arguments = types::read_short(buf).map_err(|err| { + SchemaChangeEventParseError::ArgumentCountParseError(err.into()) + })?; let mut argument_vector = Vec::with_capacity(number_of_arguments as usize); for _ in 0..number_of_arguments { - argument_vector.push(types::read_string(buf)?.to_string()); + argument_vector.push( + types::read_string(buf) + .map_err(SchemaChangeEventParseError::FunctionArgumentParseError)? + .to_string(), + ); } Ok(Self::FunctionChange { @@ -125,13 +141,21 @@ impl SchemaChangeEvent { }) } "AGGREGATE" => { - let name = types::read_string(buf)?.to_string(); - let number_of_arguments = types::read_short(buf)?; + let name = types::read_string(buf) + .map_err(SchemaChangeEventParseError::AffectedTargetNameParseError)? + .to_string(); + let number_of_arguments = types::read_short(buf).map_err(|err| { + SchemaChangeEventParseError::ArgumentCountParseError(err.into()) + })?; let mut argument_vector = Vec::with_capacity(number_of_arguments as usize); for _ in 0..number_of_arguments { - argument_vector.push(types::read_string(buf)?.to_string()); + argument_vector.push( + types::read_string(buf) + .map_err(SchemaChangeEventParseError::FunctionArgumentParseError)? + .to_string(), + ); } Ok(Self::AggregateChange { @@ -142,10 +166,9 @@ impl SchemaChangeEvent { }) } - _ => Err(ParseError::BadIncomingData(format!( - "Invalid type of schema change ({}) in SchemaChangeEvent", - target - ))), + _ => Err(SchemaChangeEventParseError::UnknownTargetOfSchemaChange( + target.to_string(), + )), } } } diff --git a/scylla-cql/src/frame/response/result.rs b/scylla-cql/src/frame/response/result.rs index d5251b0ccc..01eabaaccb 100644 --- a/scylla-cql/src/frame/response/result.rs +++ b/scylla-cql/src/frame/response/result.rs @@ -1,5 +1,5 @@ use crate::cql_to_rust::{FromRow, FromRowError}; -use crate::frame::frame_errors::SetKeyspaceParseError; +use crate::frame::frame_errors::{SchemaChangeEventParseError, SetKeyspaceParseError}; use crate::frame::response::event::SchemaChangeEvent; use crate::frame::value::{ Counter, CqlDate, CqlDecimal, CqlDuration, CqlTime, CqlTimestamp, CqlTimeuuid, CqlVarint, @@ -873,7 +873,7 @@ fn deser_prepared(buf: &mut &[u8]) -> StdResult { } #[allow(clippy::unnecessary_wraps)] -fn deser_schema_change(buf: &mut &[u8]) -> StdResult { +fn deser_schema_change(buf: &mut &[u8]) -> StdResult { Ok(SchemaChange { event: SchemaChangeEvent::deserialize(buf)?, }) From b2504a8899273d1b4b44549d98167581342c9f43 Mon Sep 17 00:00:00 2001 From: muzarski Date: Mon, 15 Jul 2024 15:28:49 +0200 Subject: [PATCH 05/22] frame/result: enable clippy lint The `allow` is not needed anymore. --- scylla-cql/src/frame/response/result.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/scylla-cql/src/frame/response/result.rs b/scylla-cql/src/frame/response/result.rs index 01eabaaccb..930202f58a 100644 --- a/scylla-cql/src/frame/response/result.rs +++ b/scylla-cql/src/frame/response/result.rs @@ -872,7 +872,6 @@ fn deser_prepared(buf: &mut &[u8]) -> StdResult { }) } -#[allow(clippy::unnecessary_wraps)] fn deser_schema_change(buf: &mut &[u8]) -> StdResult { Ok(SchemaChange { event: SchemaChangeEvent::deserialize(buf)?, From 866b79dd0a54495703aed872636f9a9123437be0 Mon Sep 17 00:00:00 2001 From: muzarski Date: Wed, 26 Jun 2024 13:59:40 +0200 Subject: [PATCH 06/22] f_errors: TableSpecParseError Introduced an error type returned when deserialization of table spec fails. --- scylla-cql/src/frame/frame_errors.rs | 13 +++++++++++++ scylla-cql/src/frame/response/result.rs | 14 ++++++++++---- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/scylla-cql/src/frame/frame_errors.rs b/scylla-cql/src/frame/frame_errors.rs index 701beaff32..b9ad616493 100644 --- a/scylla-cql/src/frame/frame_errors.rs +++ b/scylla-cql/src/frame/frame_errors.rs @@ -39,6 +39,8 @@ pub enum ParseError { SetKeyspaceParseError(#[from] SetKeyspaceParseError), #[error("Schema change event deserialization failed: {0}")] SchemaChangeEventParseError(#[from] SchemaChangeEventParseError), + #[error("Table spec deserialization failed: {0}")] + TableSpecParseError(#[from] TableSpecParseError), #[error("Low-level deserialization failed: {0}")] LowLevelDeserializationError(#[from] LowLevelDeserializationError), #[error("Could not serialize frame: {0}")] @@ -91,6 +93,17 @@ pub enum SchemaChangeEventParseError { UnknownTargetOfSchemaChange(String), } +/// An error type returned when deserialization +/// of table specification fails. +#[non_exhaustive] +#[derive(Error, Debug)] +pub enum TableSpecParseError { + #[error("Malformed keyspace name: {0}")] + MalformedKeyspaceName(LowLevelDeserializationError), + #[error("Malformed table name: {0}")] + MalformedTableName(LowLevelDeserializationError), +} + /// A low level deserialization error. /// /// This type of error is returned when deserialization diff --git a/scylla-cql/src/frame/response/result.rs b/scylla-cql/src/frame/response/result.rs index 930202f58a..d2c8742019 100644 --- a/scylla-cql/src/frame/response/result.rs +++ b/scylla-cql/src/frame/response/result.rs @@ -1,5 +1,7 @@ use crate::cql_to_rust::{FromRow, FromRowError}; -use crate::frame::frame_errors::{SchemaChangeEventParseError, SetKeyspaceParseError}; +use crate::frame::frame_errors::{ + SchemaChangeEventParseError, SetKeyspaceParseError, TableSpecParseError, +}; use crate::frame::response::event::SchemaChangeEvent; use crate::frame::value::{ Counter, CqlDate, CqlDecimal, CqlDuration, CqlTime, CqlTimestamp, CqlTimeuuid, CqlVarint, @@ -478,9 +480,13 @@ pub enum Result { SchemaChange(SchemaChange), } -fn deser_table_spec(buf: &mut &[u8]) -> StdResult, ParseError> { - let ks_name = types::read_string(buf)?.to_owned(); - let table_name = types::read_string(buf)?.to_owned(); +fn deser_table_spec(buf: &mut &[u8]) -> StdResult, TableSpecParseError> { + let ks_name = types::read_string(buf) + .map_err(TableSpecParseError::MalformedKeyspaceName)? + .to_owned(); + let table_name = types::read_string(buf) + .map_err(TableSpecParseError::MalformedTableName)? + .to_owned(); Ok(TableSpec::owned(ks_name, table_name)) } From 8a22e09c43ffb6fdc6c3421fc6375c645adc731b Mon Sep 17 00:00:00 2001 From: muzarski Date: Wed, 26 Jun 2024 14:08:47 +0200 Subject: [PATCH 07/22] f_errors: CqlTypeParseError Introduced an error type returned when we fail to deserialize a CQL type. --- scylla-cql/src/frame/frame_errors.rs | 26 +++++++++++++++++-- scylla-cql/src/frame/response/result.rs | 34 +++++++++++++++++-------- 2 files changed, 47 insertions(+), 13 deletions(-) diff --git a/scylla-cql/src/frame/frame_errors.rs b/scylla-cql/src/frame/frame_errors.rs index b9ad616493..e2d1cbd9b4 100644 --- a/scylla-cql/src/frame/frame_errors.rs +++ b/scylla-cql/src/frame/frame_errors.rs @@ -41,6 +41,8 @@ pub enum ParseError { SchemaChangeEventParseError(#[from] SchemaChangeEventParseError), #[error("Table spec deserialization failed: {0}")] TableSpecParseError(#[from] TableSpecParseError), + #[error(transparent)] + TypeParseError(#[from] CqlTypeParseError), #[error("Low-level deserialization failed: {0}")] LowLevelDeserializationError(#[from] LowLevelDeserializationError), #[error("Could not serialize frame: {0}")] @@ -51,8 +53,6 @@ pub enum ParseError { DeserializationError(#[from] DeserializationError), #[error(transparent)] IoError(#[from] std::io::Error), - #[error("type not yet implemented, id: {0}")] - TypeNotImplemented(u16), #[error(transparent)] SerializeValuesError(#[from] SerializeValuesError), #[error(transparent)] @@ -104,6 +104,28 @@ pub enum TableSpecParseError { MalformedTableName(LowLevelDeserializationError), } +/// An error type returned when deserialization of CQL type name fails. +#[non_exhaustive] +#[derive(Error, Debug)] +pub enum CqlTypeParseError { + #[error("Malformed type id: {0}")] + TypeIdParseError(LowLevelDeserializationError), + #[error("Malformed custom type name: {0}")] + CustomTypeNameParseError(LowLevelDeserializationError), + #[error("Malformed name of UDT keyspace: {0}")] + UdtKeyspaceNameParseError(LowLevelDeserializationError), + #[error("Malformed UDT name: {0}")] + UdtNameParseError(LowLevelDeserializationError), + #[error("Malformed UDT fields count: {0}")] + UdtFieldsCountParseError(LowLevelDeserializationError), + #[error("Malformed UDT's field name: {0}")] + UdtFieldNameParseError(LowLevelDeserializationError), + #[error("Malformed tuple length: {0}")] + TupleLengthParseError(LowLevelDeserializationError), + #[error("CQL Type not yet implemented, id: {0}")] + TypeNotImplemented(u16), +} + /// A low level deserialization error. /// /// This type of error is returned when deserialization diff --git a/scylla-cql/src/frame/response/result.rs b/scylla-cql/src/frame/response/result.rs index d2c8742019..dfd094ef4d 100644 --- a/scylla-cql/src/frame/response/result.rs +++ b/scylla-cql/src/frame/response/result.rs @@ -1,6 +1,6 @@ use crate::cql_to_rust::{FromRow, FromRowError}; use crate::frame::frame_errors::{ - SchemaChangeEventParseError, SetKeyspaceParseError, TableSpecParseError, + CqlTypeParseError, SchemaChangeEventParseError, SetKeyspaceParseError, TableSpecParseError, }; use crate::frame::response::event::SchemaChangeEvent; use crate::frame::value::{ @@ -490,12 +490,15 @@ fn deser_table_spec(buf: &mut &[u8]) -> StdResult, TableSpecP Ok(TableSpec::owned(ks_name, table_name)) } -fn deser_type(buf: &mut &[u8]) -> StdResult { +fn deser_type(buf: &mut &[u8]) -> StdResult { use ColumnType::*; - let id = types::read_short(buf)?; + let id = + types::read_short(buf).map_err(|err| CqlTypeParseError::TypeIdParseError(err.into()))?; Ok(match id { 0x0000 => { - let type_str: String = types::read_string(buf)?.to_string(); + let type_str: String = types::read_string(buf) + .map_err(CqlTypeParseError::CustomTypeNameParseError)? + .to_string(); match type_str.as_str() { "org.apache.cassandra.db.marshal.DurationType" => Duration, _ => Custom(type_str), @@ -525,14 +528,22 @@ fn deser_type(buf: &mut &[u8]) -> StdResult { 0x0021 => Map(Box::new(deser_type(buf)?), Box::new(deser_type(buf)?)), 0x0022 => Set(Box::new(deser_type(buf)?)), 0x0030 => { - let keyspace_name: String = types::read_string(buf)?.to_string(); - let type_name: String = types::read_string(buf)?.to_string(); - let fields_size: usize = types::read_short(buf)?.into(); + let keyspace_name: String = types::read_string(buf) + .map_err(CqlTypeParseError::UdtKeyspaceNameParseError)? + .to_string(); + let type_name: String = types::read_string(buf) + .map_err(CqlTypeParseError::UdtNameParseError)? + .to_string(); + let fields_size: usize = types::read_short(buf) + .map_err(|err| CqlTypeParseError::UdtFieldsCountParseError(err.into()))? + .into(); let mut field_types: Vec<(String, ColumnType)> = Vec::with_capacity(fields_size); for _ in 0..fields_size { - let field_name: String = types::read_string(buf)?.to_string(); + let field_name: String = types::read_string(buf) + .map_err(CqlTypeParseError::UdtFieldNameParseError)? + .to_string(); let field_type: ColumnType = deser_type(buf)?; field_types.push((field_name, field_type)); @@ -545,7 +556,9 @@ fn deser_type(buf: &mut &[u8]) -> StdResult { } } 0x0031 => { - let len: usize = types::read_short(buf)?.into(); + let len: usize = types::read_short(buf) + .map_err(|err| CqlTypeParseError::TupleLengthParseError(err.into()))? + .into(); let mut types = Vec::with_capacity(len); for _ in 0..len { types.push(deser_type(buf)?); @@ -553,8 +566,7 @@ fn deser_type(buf: &mut &[u8]) -> StdResult { Tuple(types) } id => { - // TODO implement other types - return Err(ParseError::TypeNotImplemented(id)); + return Err(CqlTypeParseError::TypeNotImplemented(id)); } }) } From 335e05b95397290106af8d9a383bbef921547123 Mon Sep 17 00:00:00 2001 From: muzarski Date: Wed, 26 Jun 2024 14:50:44 +0200 Subject: [PATCH 08/22] f_errors: ColumnSpecsParseError An error type returned when we fail to deserialize table's column specs. --- scylla-cql/src/frame/frame_errors.rs | 27 +++++++++++++++++++++++-- scylla-cql/src/frame/response/result.rs | 25 +++++++++++++++++------ 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/scylla-cql/src/frame/frame_errors.rs b/scylla-cql/src/frame/frame_errors.rs index e2d1cbd9b4..d74c5cc4ab 100644 --- a/scylla-cql/src/frame/frame_errors.rs +++ b/scylla-cql/src/frame/frame_errors.rs @@ -41,8 +41,8 @@ pub enum ParseError { SchemaChangeEventParseError(#[from] SchemaChangeEventParseError), #[error("Table spec deserialization failed: {0}")] TableSpecParseError(#[from] TableSpecParseError), - #[error(transparent)] - TypeParseError(#[from] CqlTypeParseError), + #[error("Failed to deserialize column spec: {0}")] + ColumnSpecParseError(#[from] ColumnSpecParseError), #[error("Low-level deserialization failed: {0}")] LowLevelDeserializationError(#[from] LowLevelDeserializationError), #[error("Could not serialize frame: {0}")] @@ -104,6 +104,29 @@ pub enum TableSpecParseError { MalformedTableName(LowLevelDeserializationError), } +/// An error type returned when deserialization +/// of table column specifications fails. +#[non_exhaustive] +#[derive(Error, Debug)] +#[error("Column spec deserialization failed, column index: {column_index}, error: {kind}")] +pub struct ColumnSpecParseError { + pub column_index: usize, + pub kind: ColumnSpecParseErrorKind, +} + +/// The type of error that appeared during deserialization +/// of a column specification. +#[non_exhaustive] +#[derive(Error, Debug)] +pub enum ColumnSpecParseErrorKind { + #[error("Invalid table spec: {0}")] + TableSpecParseError(#[from] TableSpecParseError), + #[error("Malformed column name: {0}")] + ColumnNameParseError(#[from] LowLevelDeserializationError), + #[error("Invalid column type: {0}")] + ColumnTypeParseError(#[from] CqlTypeParseError), +} + /// An error type returned when deserialization of CQL type name fails. #[non_exhaustive] #[derive(Error, Debug)] diff --git a/scylla-cql/src/frame/response/result.rs b/scylla-cql/src/frame/response/result.rs index dfd094ef4d..6e6bf9fbe3 100644 --- a/scylla-cql/src/frame/response/result.rs +++ b/scylla-cql/src/frame/response/result.rs @@ -1,6 +1,7 @@ use crate::cql_to_rust::{FromRow, FromRowError}; use crate::frame::frame_errors::{ - CqlTypeParseError, SchemaChangeEventParseError, SetKeyspaceParseError, TableSpecParseError, + ColumnSpecParseError, ColumnSpecParseErrorKind, CqlTypeParseError, SchemaChangeEventParseError, + SetKeyspaceParseError, TableSpecParseError, }; use crate::frame::response::event::SchemaChangeEvent; use crate::frame::value::{ @@ -571,20 +572,32 @@ fn deser_type(buf: &mut &[u8]) -> StdResult { }) } +fn mk_col_spec_parse_error( + col_idx: usize, + err: impl Into, +) -> ColumnSpecParseError { + ColumnSpecParseError { + column_index: col_idx, + kind: err.into(), + } +} + fn deser_col_specs( buf: &mut &[u8], global_table_spec: &Option>, col_count: usize, -) -> StdResult, ParseError> { +) -> StdResult, ColumnSpecParseError> { let mut col_specs = Vec::with_capacity(col_count); - for _ in 0..col_count { + for col_idx in 0..col_count { let table_spec = if let Some(spec) = global_table_spec { spec.clone() } else { - deser_table_spec(buf)? + deser_table_spec(buf).map_err(|err| mk_col_spec_parse_error(col_idx, err))? }; - let name = types::read_string(buf)?.to_owned(); - let typ = deser_type(buf)?; + let name = types::read_string(buf) + .map_err(|err| mk_col_spec_parse_error(col_idx, err))? + .to_owned(); + let typ = deser_type(buf).map_err(|err| mk_col_spec_parse_error(col_idx, err))?; col_specs.push(ColumnSpec { table_spec, name, From 85ac6bed0d64db4be1a69b16b41fe114a544cce2 Mon Sep 17 00:00:00 2001 From: muzarski Date: Wed, 26 Jun 2024 15:27:18 +0200 Subject: [PATCH 09/22] result: read_int().try_into() -> read_int_length() Replaced `read_int().try_into()` with a utility function that does exactly the same. This way, the caller doesn't have to handle two separate errors. --- scylla-cql/src/frame/response/result.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scylla-cql/src/frame/response/result.rs b/scylla-cql/src/frame/response/result.rs index 6e6bf9fbe3..a172f6f44c 100644 --- a/scylla-cql/src/frame/response/result.rs +++ b/scylla-cql/src/frame/response/result.rs @@ -15,7 +15,7 @@ use crate::types::deserialize::value::{ use crate::types::deserialize::{DeserializationError, FrameSlice}; use bytes::{Buf, Bytes}; use std::borrow::Cow; -use std::{convert::TryInto, net::IpAddr, result::Result as StdResult, str}; +use std::{net::IpAddr, result::Result as StdResult, str}; use uuid::Uuid; #[derive(Debug)] @@ -613,7 +613,7 @@ fn deser_result_metadata(buf: &mut &[u8]) -> StdResult StdResult Date: Wed, 26 Jun 2024 15:32:16 +0200 Subject: [PATCH 10/22] f_errors: ResultMetadataParseError Introduced an error returned when deserialization of Result/PreparedMetadata fails. --- scylla-cql/src/frame/frame_errors.rs | 27 ++++++++++++++++--- scylla-cql/src/frame/response/result.rs | 36 +++++++++++++++++-------- 2 files changed, 48 insertions(+), 15 deletions(-) diff --git a/scylla-cql/src/frame/frame_errors.rs b/scylla-cql/src/frame/frame_errors.rs index d74c5cc4ab..9c9861325f 100644 --- a/scylla-cql/src/frame/frame_errors.rs +++ b/scylla-cql/src/frame/frame_errors.rs @@ -39,10 +39,8 @@ pub enum ParseError { SetKeyspaceParseError(#[from] SetKeyspaceParseError), #[error("Schema change event deserialization failed: {0}")] SchemaChangeEventParseError(#[from] SchemaChangeEventParseError), - #[error("Table spec deserialization failed: {0}")] - TableSpecParseError(#[from] TableSpecParseError), - #[error("Failed to deserialize column spec: {0}")] - ColumnSpecParseError(#[from] ColumnSpecParseError), + #[error("Failed to deserialize result metadata: {0}")] + ResultMetadataParseError(#[from] ResultMetadataParseError), #[error("Low-level deserialization failed: {0}")] LowLevelDeserializationError(#[from] LowLevelDeserializationError), #[error("Could not serialize frame: {0}")] @@ -93,6 +91,27 @@ pub enum SchemaChangeEventParseError { UnknownTargetOfSchemaChange(String), } +/// An error type returned when deserialization +/// of `[Result/Prepared]Metadata` failed. +#[non_exhaustive] +#[derive(Error, Debug)] +pub enum ResultMetadataParseError { + #[error("Malformed metadata flags: {0}")] + FlagsParseError(LowLevelDeserializationError), + #[error("Malformed column count: {0}")] + ColumnCountParseError(LowLevelDeserializationError), + #[error("Malformed partition key count: {0}")] + PkCountParseError(LowLevelDeserializationError), + #[error("Malformed partition key index: {0}")] + PkIndexParseError(LowLevelDeserializationError), + #[error("Malformed paging state: {0}")] + PagingStateParseError(LowLevelDeserializationError), + #[error("Invalid global table spec: {0}")] + GlobalTableSpecParseError(#[from] TableSpecParseError), + #[error("Invalid column spec: {0}")] + ColumnSpecParseError(#[from] ColumnSpecParseError), +} + /// An error type returned when deserialization /// of table specification fails. #[non_exhaustive] diff --git a/scylla-cql/src/frame/response/result.rs b/scylla-cql/src/frame/response/result.rs index a172f6f44c..49ef824f1a 100644 --- a/scylla-cql/src/frame/response/result.rs +++ b/scylla-cql/src/frame/response/result.rs @@ -1,7 +1,7 @@ use crate::cql_to_rust::{FromRow, FromRowError}; use crate::frame::frame_errors::{ - ColumnSpecParseError, ColumnSpecParseErrorKind, CqlTypeParseError, SchemaChangeEventParseError, - SetKeyspaceParseError, TableSpecParseError, + ColumnSpecParseError, ColumnSpecParseErrorKind, CqlTypeParseError, ResultMetadataParseError, + SchemaChangeEventParseError, SetKeyspaceParseError, TableSpecParseError, }; use crate::frame::response::event::SchemaChangeEvent; use crate::frame::value::{ @@ -607,16 +607,23 @@ fn deser_col_specs( Ok(col_specs) } -fn deser_result_metadata(buf: &mut &[u8]) -> StdResult { - let flags = types::read_int(buf)?; +fn deser_result_metadata(buf: &mut &[u8]) -> StdResult { + let flags = types::read_int(buf) + .map_err(|err| ResultMetadataParseError::FlagsParseError(err.into()))?; let global_tables_spec = flags & 0x0001 != 0; let has_more_pages = flags & 0x0002 != 0; let no_metadata = flags & 0x0004 != 0; - let col_count = types::read_int_length(buf)?; + let col_count = + types::read_int_length(buf).map_err(ResultMetadataParseError::ColumnCountParseError)?; let paging_state = if has_more_pages { - Some(types::read_bytes(buf)?.to_owned().into()) + Some( + types::read_bytes(buf) + .map_err(ResultMetadataParseError::PagingStateParseError)? + .to_owned() + .into(), + ) } else { None }; @@ -644,18 +651,25 @@ fn deser_result_metadata(buf: &mut &[u8]) -> StdResult StdResult { - let flags = types::read_int(buf)?; +fn deser_prepared_metadata( + buf: &mut &[u8], +) -> StdResult { + let flags = types::read_int(buf) + .map_err(|err| ResultMetadataParseError::FlagsParseError(err.into()))?; let global_tables_spec = flags & 0x0001 != 0; - let col_count = types::read_int_length(buf)?; + let col_count = + types::read_int_length(buf).map_err(ResultMetadataParseError::ColumnCountParseError)?; - let pk_count: usize = types::read_int_length(buf)?; + let pk_count: usize = + types::read_int_length(buf).map_err(ResultMetadataParseError::PkCountParseError)?; let mut pk_indexes = Vec::with_capacity(pk_count); for i in 0..pk_count { pk_indexes.push(PartitionKeyIndex { - index: types::read_short(buf)? as u16, + index: types::read_short(buf) + .map_err(|err| ResultMetadataParseError::PkIndexParseError(err.into()))? + as u16, sequence: i as u16, }); } From 94d7d9f3d752884d4e038dd4c11340e2f45773dd Mon Sep 17 00:00:00 2001 From: muzarski Date: Wed, 26 Jun 2024 15:40:49 +0200 Subject: [PATCH 11/22] f_errors: PreparedParseError Error returned when we fail to deserialize a RESULT::Prepared CQL response. --- scylla-cql/src/frame/frame_errors.rs | 15 +++++++++++++++ scylla-cql/src/frame/response/result.rs | 17 +++++++++++------ 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/scylla-cql/src/frame/frame_errors.rs b/scylla-cql/src/frame/frame_errors.rs index 9c9861325f..5588dca0d8 100644 --- a/scylla-cql/src/frame/frame_errors.rs +++ b/scylla-cql/src/frame/frame_errors.rs @@ -39,6 +39,8 @@ pub enum ParseError { SetKeyspaceParseError(#[from] SetKeyspaceParseError), #[error("Schema change event deserialization failed: {0}")] SchemaChangeEventParseError(#[from] SchemaChangeEventParseError), + #[error("PREPARED response deserialization failed: {0}")] + PreparedParseError(#[from] PreparedParseError), #[error("Failed to deserialize result metadata: {0}")] ResultMetadataParseError(#[from] ResultMetadataParseError), #[error("Low-level deserialization failed: {0}")] @@ -91,6 +93,19 @@ pub enum SchemaChangeEventParseError { UnknownTargetOfSchemaChange(String), } +/// An error type returned when deserialization +/// of `RESULT::`Prepared` response fails. +#[non_exhaustive] +#[derive(Debug, Error)] +pub enum PreparedParseError { + #[error("Malformed prepared statement's id length: {0}")] + IdLengthParseError(LowLevelDeserializationError), + #[error("Invalid result metadata: {0}")] + ResultMetadataParseError(ResultMetadataParseError), + #[error("Invalid prepared metadata: {0}")] + PreparedMetadataParseError(ResultMetadataParseError), +} + /// An error type returned when deserialization /// of `[Result/Prepared]Metadata` failed. #[non_exhaustive] diff --git a/scylla-cql/src/frame/response/result.rs b/scylla-cql/src/frame/response/result.rs index 49ef824f1a..87054aa95c 100644 --- a/scylla-cql/src/frame/response/result.rs +++ b/scylla-cql/src/frame/response/result.rs @@ -1,7 +1,8 @@ use crate::cql_to_rust::{FromRow, FromRowError}; use crate::frame::frame_errors::{ - ColumnSpecParseError, ColumnSpecParseErrorKind, CqlTypeParseError, ResultMetadataParseError, - SchemaChangeEventParseError, SetKeyspaceParseError, TableSpecParseError, + ColumnSpecParseError, ColumnSpecParseErrorKind, CqlTypeParseError, PreparedParseError, + ResultMetadataParseError, SchemaChangeEventParseError, SetKeyspaceParseError, + TableSpecParseError, }; use crate::frame::response::event::SchemaChangeEvent; use crate::frame::value::{ @@ -904,12 +905,16 @@ fn deser_set_keyspace(buf: &mut &[u8]) -> StdResult StdResult { - let id_len = types::read_short(buf)? as usize; +fn deser_prepared(buf: &mut &[u8]) -> StdResult { + let id_len = types::read_short(buf) + .map_err(|err| PreparedParseError::IdLengthParseError(err.into()))? + as usize; let id: Bytes = buf[0..id_len].to_owned().into(); buf.advance(id_len); - let prepared_metadata = deser_prepared_metadata(buf)?; - let result_metadata = deser_result_metadata(buf)?; + let prepared_metadata = + deser_prepared_metadata(buf).map_err(PreparedParseError::PreparedMetadataParseError)?; + let result_metadata = + deser_result_metadata(buf).map_err(PreparedParseError::ResultMetadataParseError)?; Ok(Prepared { id, prepared_metadata, From 51b69f479424d2bbc37c92dc77004ad8562d2939 Mon Sep 17 00:00:00 2001 From: muzarski Date: Wed, 26 Jun 2024 15:52:11 +0200 Subject: [PATCH 12/22] f_errors: RowsParseError An error type returned when deserialization of RESULT::Rows response fails. --- scylla-cql/src/frame/frame_errors.rs | 22 ++++++++++++++++++++-- scylla-cql/src/frame/response/result.rs | 16 ++++++++-------- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/scylla-cql/src/frame/frame_errors.rs b/scylla-cql/src/frame/frame_errors.rs index 5588dca0d8..81c3f03456 100644 --- a/scylla-cql/src/frame/frame_errors.rs +++ b/scylla-cql/src/frame/frame_errors.rs @@ -41,8 +41,8 @@ pub enum ParseError { SchemaChangeEventParseError(#[from] SchemaChangeEventParseError), #[error("PREPARED response deserialization failed: {0}")] PreparedParseError(#[from] PreparedParseError), - #[error("Failed to deserialize result metadata: {0}")] - ResultMetadataParseError(#[from] ResultMetadataParseError), + #[error("ROWS response deserialization failed: {0}")] + RowsParseError(#[from] RowsParseError), #[error("Low-level deserialization failed: {0}")] LowLevelDeserializationError(#[from] LowLevelDeserializationError), #[error("Could not serialize frame: {0}")] @@ -106,6 +106,24 @@ pub enum PreparedParseError { PreparedMetadataParseError(ResultMetadataParseError), } +/// An error type returned when deserialization +/// of `RESULT::Rows` response fails. +#[non_exhaustive] +#[derive(Debug, Error)] +pub enum RowsParseError { + #[error("Invalid result metadata: {0}")] + ResultMetadataParseError(#[from] ResultMetadataParseError), + #[error("Invalid result metadata, server claims {col_count} columns, received {col_specs_count} col specs.")] + ColumnCountMismatch { + col_count: usize, + col_specs_count: usize, + }, + #[error("Malformed rows count: {0}")] + RowsCountParseError(LowLevelDeserializationError), + #[error("Data deserialization failed: {0}")] + DataDeserializationError(#[from] DeserializationError), +} + /// An error type returned when deserialization /// of `[Result/Prepared]Metadata` failed. #[non_exhaustive] diff --git a/scylla-cql/src/frame/response/result.rs b/scylla-cql/src/frame/response/result.rs index 87054aa95c..556c068ab6 100644 --- a/scylla-cql/src/frame/response/result.rs +++ b/scylla-cql/src/frame/response/result.rs @@ -1,7 +1,7 @@ use crate::cql_to_rust::{FromRow, FromRowError}; use crate::frame::frame_errors::{ ColumnSpecParseError, ColumnSpecParseErrorKind, CqlTypeParseError, PreparedParseError, - ResultMetadataParseError, SchemaChangeEventParseError, SetKeyspaceParseError, + ResultMetadataParseError, RowsParseError, SchemaChangeEventParseError, SetKeyspaceParseError, TableSpecParseError, }; use crate::frame::response::event::SchemaChangeEvent; @@ -855,7 +855,7 @@ pub fn deser_cql_value( fn deser_rows( buf: &mut &[u8], cached_metadata: Option<&ResultMetadata>, -) -> StdResult { +) -> StdResult { let server_metadata = deser_result_metadata(buf)?; let metadata = match cached_metadata { @@ -867,11 +867,10 @@ fn deser_rows( None => { // No cached_metadata provided. Server is supposed to provide the result metadata. if server_metadata.col_count != server_metadata.col_specs.len() { - return Err(ParseError::BadIncomingData(format!( - "Bad result metadata provided in the response. Expected {} column specifications, received: {}", - server_metadata.col_count, - server_metadata.col_specs.len() - ))); + return Err(RowsParseError::ColumnCountMismatch { + col_count: server_metadata.col_count, + col_specs_count: server_metadata.col_specs.len(), + }); } server_metadata } @@ -879,7 +878,8 @@ fn deser_rows( let original_size = buf.len(); - let rows_count: usize = types::read_int_length(buf)?; + let rows_count: usize = + types::read_int_length(buf).map_err(RowsParseError::RowsCountParseError)?; let raw_rows_iter = RowIterator::new( rows_count, From ff3d733ddb02b2b5c3824115e1b8163321d7922a Mon Sep 17 00:00:00 2001 From: muzarski Date: Wed, 26 Jun 2024 16:07:20 +0200 Subject: [PATCH 13/22] f_errors: CqlResultParseError An error type returned when RESULT response deserialization fails. --- scylla-cql/src/frame/frame_errors.rs | 28 +++++++++++++++----- scylla-cql/src/frame/response/result.rs | 35 ++++++++++++------------- 2 files changed, 39 insertions(+), 24 deletions(-) diff --git a/scylla-cql/src/frame/frame_errors.rs b/scylla-cql/src/frame/frame_errors.rs index 81c3f03456..e38a66076f 100644 --- a/scylla-cql/src/frame/frame_errors.rs +++ b/scylla-cql/src/frame/frame_errors.rs @@ -35,14 +35,10 @@ pub enum FrameError { #[derive(Error, Debug)] pub enum ParseError { - #[error("Set keyspace response deserialization failed: {0}")] - SetKeyspaceParseError(#[from] SetKeyspaceParseError), + #[error(transparent)] + CqlResultParseError(#[from] CqlResultParseError), #[error("Schema change event deserialization failed: {0}")] SchemaChangeEventParseError(#[from] SchemaChangeEventParseError), - #[error("PREPARED response deserialization failed: {0}")] - PreparedParseError(#[from] PreparedParseError), - #[error("ROWS response deserialization failed: {0}")] - RowsParseError(#[from] RowsParseError), #[error("Low-level deserialization failed: {0}")] LowLevelDeserializationError(#[from] LowLevelDeserializationError), #[error("Could not serialize frame: {0}")] @@ -61,6 +57,26 @@ pub enum ParseError { CqlTypeError(#[from] CqlTypeError), } +/// An error type returned when deserialization of RESULT response fails. +#[non_exhaustive] +#[derive(Error, Debug)] +pub enum CqlResultParseError { + #[error("Malformed RESULT response id: {0}")] + ResultIdParseError(LowLevelDeserializationError), + #[error("Unknown RESULT response id: {0}")] + UnknownResultId(i32), + #[error("'Set_keyspace' response deserialization failed: {0}")] + SetKeyspaceParseError(#[from] SetKeyspaceParseError), + // This is an error returned during deserialization of + // `RESULT::Schema_change` response, and not `EVENT` response. + #[error("'Schema_change' response deserialization failed: {0}")] + SchemaChangeParseError(#[from] SchemaChangeEventParseError), + #[error("'Prepared' response deserialization failed: {0}")] + PreparedParseError(#[from] PreparedParseError), + #[error("'Rows' response deserialization failed: {0}")] + RowsParseError(#[from] RowsParseError), +} + #[non_exhaustive] #[derive(Error, Debug)] pub enum SetKeyspaceParseError { diff --git a/scylla-cql/src/frame/response/result.rs b/scylla-cql/src/frame/response/result.rs index 556c068ab6..8c89f2ad6d 100644 --- a/scylla-cql/src/frame/response/result.rs +++ b/scylla-cql/src/frame/response/result.rs @@ -1,14 +1,14 @@ use crate::cql_to_rust::{FromRow, FromRowError}; use crate::frame::frame_errors::{ - ColumnSpecParseError, ColumnSpecParseErrorKind, CqlTypeParseError, PreparedParseError, - ResultMetadataParseError, RowsParseError, SchemaChangeEventParseError, SetKeyspaceParseError, - TableSpecParseError, + ColumnSpecParseError, ColumnSpecParseErrorKind, CqlResultParseError, CqlTypeParseError, + PreparedParseError, ResultMetadataParseError, RowsParseError, SchemaChangeEventParseError, + SetKeyspaceParseError, TableSpecParseError, }; use crate::frame::response::event::SchemaChangeEvent; +use crate::frame::types; use crate::frame::value::{ Counter, CqlDate, CqlDecimal, CqlDuration, CqlTime, CqlTimestamp, CqlTimeuuid, CqlVarint, }; -use crate::frame::{frame_errors::ParseError, types}; use crate::types::deserialize::result::{RowIterator, TypedRowIterator}; use crate::types::deserialize::value::{ mk_deser_err, BuiltinDeserializationErrorKind, DeserializeValue, MapIterator, UdtIterator, @@ -931,21 +931,20 @@ fn deser_schema_change(buf: &mut &[u8]) -> StdResult, -) -> StdResult { +) -> StdResult { use self::Result::*; - Ok(match types::read_int(buf)? { - 0x0001 => Void, - 0x0002 => Rows(deser_rows(buf, cached_metadata)?), - 0x0003 => SetKeyspace(deser_set_keyspace(buf)?), - 0x0004 => Prepared(deser_prepared(buf)?), - 0x0005 => SchemaChange(deser_schema_change(buf)?), - k => { - return Err(ParseError::BadIncomingData(format!( - "Unknown query result id: {}", - k - ))) - } - }) + Ok( + match types::read_int(buf) + .map_err(|err| CqlResultParseError::ResultIdParseError(err.into()))? + { + 0x0001 => Void, + 0x0002 => Rows(deser_rows(buf, cached_metadata)?), + 0x0003 => SetKeyspace(deser_set_keyspace(buf)?), + 0x0004 => Prepared(deser_prepared(buf)?), + 0x0005 => SchemaChange(deser_schema_change(buf)?), + id => return Err(CqlResultParseError::UnknownResultId(id)), + }, + ) } #[cfg(test)] From 94f90b5e117a75cccbac59f91eeaccc20274e72b Mon Sep 17 00:00:00 2001 From: muzarski Date: Wed, 26 Jun 2024 16:18:58 +0200 Subject: [PATCH 14/22] f_errors: ClusterChangeParseError Introduced an error type returned when we fail to deserialize either StatusChangeEvent or TopologyChangeEvent. --- scylla-cql/src/frame/frame_errors.rs | 14 +++++++++++ scylla-cql/src/frame/response/event.rs | 34 ++++++++++++++------------ 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/scylla-cql/src/frame/frame_errors.rs b/scylla-cql/src/frame/frame_errors.rs index e38a66076f..abd1bd0381 100644 --- a/scylla-cql/src/frame/frame_errors.rs +++ b/scylla-cql/src/frame/frame_errors.rs @@ -39,6 +39,8 @@ pub enum ParseError { CqlResultParseError(#[from] CqlResultParseError), #[error("Schema change event deserialization failed: {0}")] SchemaChangeEventParseError(#[from] SchemaChangeEventParseError), + #[error("Failed to deserialize cluster change event: {0}")] + ClusterChangeEventParseError(#[from] ClusterChangeEventParseError), #[error("Low-level deserialization failed: {0}")] LowLevelDeserializationError(#[from] LowLevelDeserializationError), #[error("Could not serialize frame: {0}")] @@ -109,6 +111,18 @@ pub enum SchemaChangeEventParseError { UnknownTargetOfSchemaChange(String), } +/// An error type returned when deserialization of [Status/Topology]ChangeEvent fails. +#[non_exhaustive] +#[derive(Error, Debug)] +pub enum ClusterChangeEventParseError { + #[error("Malformed type of change: {0}")] + TypeOfChangeParseError(LowLevelDeserializationError), + #[error("Malformed node address: {0}")] + NodeAddressParseError(LowLevelDeserializationError), + #[error("Unknown type of change: {0}")] + UnknownTypeOfChange(String), +} + /// An error type returned when deserialization /// of `RESULT::`Prepared` response fails. #[non_exhaustive] diff --git a/scylla-cql/src/frame/response/event.rs b/scylla-cql/src/frame/response/event.rs index a976e2fd15..b62356497f 100644 --- a/scylla-cql/src/frame/response/event.rs +++ b/scylla-cql/src/frame/response/event.rs @@ -1,4 +1,6 @@ -use crate::frame::frame_errors::{ParseError, SchemaChangeEventParseError}; +use crate::frame::frame_errors::{ + ClusterChangeEventParseError, ParseError, SchemaChangeEventParseError, +}; use crate::frame::server_event_type::EventType; use crate::frame::types; use std::net::SocketAddr; @@ -174,33 +176,35 @@ impl SchemaChangeEvent { } impl TopologyChangeEvent { - pub fn deserialize(buf: &mut &[u8]) -> Result { - let type_of_change = types::read_string(buf)?; - let addr = types::read_inet(buf)?; + pub fn deserialize(buf: &mut &[u8]) -> Result { + let type_of_change = types::read_string(buf) + .map_err(ClusterChangeEventParseError::TypeOfChangeParseError)?; + let addr = + types::read_inet(buf).map_err(ClusterChangeEventParseError::NodeAddressParseError)?; match type_of_change { "NEW_NODE" => Ok(Self::NewNode(addr)), "REMOVED_NODE" => Ok(Self::RemovedNode(addr)), - _ => Err(ParseError::BadIncomingData(format!( - "Invalid type of change ({}) in TopologyChangeEvent", - type_of_change - ))), + _ => Err(ClusterChangeEventParseError::UnknownTypeOfChange( + type_of_change.to_string(), + )), } } } impl StatusChangeEvent { - pub fn deserialize(buf: &mut &[u8]) -> Result { - let type_of_change = types::read_string(buf)?; - let addr = types::read_inet(buf)?; + pub fn deserialize(buf: &mut &[u8]) -> Result { + let type_of_change = types::read_string(buf) + .map_err(ClusterChangeEventParseError::TypeOfChangeParseError)?; + let addr = + types::read_inet(buf).map_err(ClusterChangeEventParseError::NodeAddressParseError)?; match type_of_change { "UP" => Ok(Self::Up(addr)), "DOWN" => Ok(Self::Down(addr)), - _ => Err(ParseError::BadIncomingData(format!( - "Invalid type of status change ({}) in StatusChangeEvent", - type_of_change - ))), + _ => Err(ClusterChangeEventParseError::UnknownTypeOfChange( + type_of_change.to_string(), + )), } } } From fdb6f50fee267b2d424a68292e7eaa2411d621d5 Mon Sep 17 00:00:00 2001 From: muzarski Date: Wed, 26 Jun 2024 16:29:46 +0200 Subject: [PATCH 15/22] f_errors: CqlEventParseError Introduced an error type returned when we fail to deserialize an EVENT response. --- scylla-cql/src/frame/frame_errors.rs | 25 ++++++++++++++++++----- scylla-cql/src/frame/response/event.rs | 20 +++++++++++------- scylla-cql/src/frame/server_event_type.rs | 10 ++++----- 3 files changed, 37 insertions(+), 18 deletions(-) diff --git a/scylla-cql/src/frame/frame_errors.rs b/scylla-cql/src/frame/frame_errors.rs index abd1bd0381..8f90c1780c 100644 --- a/scylla-cql/src/frame/frame_errors.rs +++ b/scylla-cql/src/frame/frame_errors.rs @@ -35,12 +35,10 @@ pub enum FrameError { #[derive(Error, Debug)] pub enum ParseError { + #[error(transparent)] + CqlEventParseError(#[from] CqlEventParseError), #[error(transparent)] CqlResultParseError(#[from] CqlResultParseError), - #[error("Schema change event deserialization failed: {0}")] - SchemaChangeEventParseError(#[from] SchemaChangeEventParseError), - #[error("Failed to deserialize cluster change event: {0}")] - ClusterChangeEventParseError(#[from] ClusterChangeEventParseError), #[error("Low-level deserialization failed: {0}")] LowLevelDeserializationError(#[from] LowLevelDeserializationError), #[error("Could not serialize frame: {0}")] @@ -87,7 +85,24 @@ pub enum SetKeyspaceParseError { } /// An error type returned when deserialization of -/// `RESULT::Schema_change` response fails. +/// `EVENT` response fails. +#[non_exhaustive] +#[derive(Error, Debug)] +pub enum CqlEventParseError { + #[error("Malformed event type string: {0}")] + EventTypeParseError(LowLevelDeserializationError), + #[error("Unknown event type: {0}")] + UnknownEventType(String), + #[error("Failed to deserialize schema change event: {0}")] + SchemaChangeEventParseError(#[from] SchemaChangeEventParseError), + #[error("Failed to deserialize topology change event: {0}")] + TopologyChangeEventParseError(ClusterChangeEventParseError), + #[error("Failed to deserialize status change event: {0}")] + StatusChangeEventParseError(ClusterChangeEventParseError), +} + +/// An error type returned when deserialization of +/// SchemaChangeEvent fails. #[non_exhaustive] #[derive(Error, Debug)] pub enum SchemaChangeEventParseError { diff --git a/scylla-cql/src/frame/response/event.rs b/scylla-cql/src/frame/response/event.rs index b62356497f..a5c483d483 100644 --- a/scylla-cql/src/frame/response/event.rs +++ b/scylla-cql/src/frame/response/event.rs @@ -1,5 +1,5 @@ use crate::frame::frame_errors::{ - ClusterChangeEventParseError, ParseError, SchemaChangeEventParseError, + ClusterChangeEventParseError, CqlEventParseError, SchemaChangeEventParseError, }; use crate::frame::server_event_type::EventType; use crate::frame::types; @@ -63,13 +63,19 @@ pub enum SchemaChangeType { } impl Event { - pub fn deserialize(buf: &mut &[u8]) -> Result { - let event_type: EventType = types::read_string(buf)?.parse()?; + pub fn deserialize(buf: &mut &[u8]) -> Result { + let event_type: EventType = types::read_string(buf) + .map_err(CqlEventParseError::EventTypeParseError)? + .parse()?; match event_type { - EventType::TopologyChange => { - Ok(Self::TopologyChange(TopologyChangeEvent::deserialize(buf)?)) - } - EventType::StatusChange => Ok(Self::StatusChange(StatusChangeEvent::deserialize(buf)?)), + EventType::TopologyChange => Ok(Self::TopologyChange( + TopologyChangeEvent::deserialize(buf) + .map_err(CqlEventParseError::TopologyChangeEventParseError)?, + )), + EventType::StatusChange => Ok(Self::StatusChange( + StatusChangeEvent::deserialize(buf) + .map_err(CqlEventParseError::StatusChangeEventParseError)?, + )), EventType::SchemaChange => Ok(Self::SchemaChange(SchemaChangeEvent::deserialize(buf)?)), } } diff --git a/scylla-cql/src/frame/server_event_type.rs b/scylla-cql/src/frame/server_event_type.rs index 1a4d5bf447..379f9bc3cc 100644 --- a/scylla-cql/src/frame/server_event_type.rs +++ b/scylla-cql/src/frame/server_event_type.rs @@ -1,7 +1,8 @@ -use crate::frame::frame_errors::ParseError; use std::fmt; use std::str::FromStr; +use super::frame_errors::CqlEventParseError; + pub enum EventType { TopologyChange, StatusChange, @@ -21,17 +22,14 @@ impl fmt::Display for EventType { } impl FromStr for EventType { - type Err = ParseError; + type Err = CqlEventParseError; fn from_str(s: &str) -> Result { match s { "TOPOLOGY_CHANGE" => Ok(Self::TopologyChange), "STATUS_CHANGE" => Ok(Self::StatusChange), "SCHEMA_CHANGE" => Ok(Self::SchemaChange), - _ => Err(ParseError::BadIncomingData(format!( - "Invalid type event type: {}", - s - ))), + _ => Err(CqlEventParseError::UnknownEventType(s.to_string())), } } } From a8169633c2fd3869d1193c6b565fdb4f458c3f06 Mon Sep 17 00:00:00 2001 From: muzarski Date: Mon, 1 Jul 2024 09:38:48 +0200 Subject: [PATCH 16/22] f_errors: CqlSupportedParseError --- scylla-cql/src/frame/frame_errors.rs | 10 ++++++++++ scylla-cql/src/frame/response/supported.rs | 7 ++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/scylla-cql/src/frame/frame_errors.rs b/scylla-cql/src/frame/frame_errors.rs index 8f90c1780c..fbfd538305 100644 --- a/scylla-cql/src/frame/frame_errors.rs +++ b/scylla-cql/src/frame/frame_errors.rs @@ -35,6 +35,8 @@ pub enum FrameError { #[derive(Error, Debug)] pub enum ParseError { + #[error(transparent)] + CqlSupportedParseError(#[from] CqlSupportedParseError), #[error(transparent)] CqlEventParseError(#[from] CqlEventParseError), #[error(transparent)] @@ -57,6 +59,14 @@ pub enum ParseError { CqlTypeError(#[from] CqlTypeError), } +/// An error type returned when deserialization of SUPPORTED response fails. +#[non_exhaustive] +#[derive(Error, Debug)] +pub enum CqlSupportedParseError { + #[error("Malformed options map: {0}")] + OptionsMapDeserialization(LowLevelDeserializationError), +} + /// An error type returned when deserialization of RESULT response fails. #[non_exhaustive] #[derive(Error, Debug)] diff --git a/scylla-cql/src/frame/response/supported.rs b/scylla-cql/src/frame/response/supported.rs index a2e5a56efa..055c821731 100644 --- a/scylla-cql/src/frame/response/supported.rs +++ b/scylla-cql/src/frame/response/supported.rs @@ -1,4 +1,4 @@ -use crate::frame::frame_errors::ParseError; +use crate::frame::frame_errors::CqlSupportedParseError; use crate::frame::types; use std::collections::HashMap; @@ -8,8 +8,9 @@ pub struct Supported { } impl Supported { - pub fn deserialize(buf: &mut &[u8]) -> Result { - let options = types::read_string_multimap(buf)?; + pub fn deserialize(buf: &mut &[u8]) -> Result { + let options = types::read_string_multimap(buf) + .map_err(CqlSupportedParseError::OptionsMapDeserialization)?; Ok(Supported { options }) } From 2d9e955ff5c226d730bb46adafcd3bfbcd2c3a09 Mon Sep 17 00:00:00 2001 From: muzarski Date: Mon, 1 Jul 2024 09:44:10 +0200 Subject: [PATCH 17/22] f_errors: CqlAuthenticateParseError --- scylla-cql/src/frame/frame_errors.rs | 10 ++++++++++ scylla-cql/src/frame/response/authenticate.rs | 8 +++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/scylla-cql/src/frame/frame_errors.rs b/scylla-cql/src/frame/frame_errors.rs index fbfd538305..faddd3d026 100644 --- a/scylla-cql/src/frame/frame_errors.rs +++ b/scylla-cql/src/frame/frame_errors.rs @@ -35,6 +35,8 @@ pub enum FrameError { #[derive(Error, Debug)] pub enum ParseError { + #[error(transparent)] + CqlAuthenticateParseError(#[from] CqlAuthenticateParseError), #[error(transparent)] CqlSupportedParseError(#[from] CqlSupportedParseError), #[error(transparent)] @@ -59,6 +61,14 @@ pub enum ParseError { CqlTypeError(#[from] CqlTypeError), } +/// An error type returned when deserialization of AUTHENTICATE response fails. +#[non_exhaustive] +#[derive(Error, Debug)] +pub enum CqlAuthenticateParseError { + #[error("Malformed authenticator name: {0}")] + AuthNameParseError(LowLevelDeserializationError), +} + /// An error type returned when deserialization of SUPPORTED response fails. #[non_exhaustive] #[derive(Error, Debug)] diff --git a/scylla-cql/src/frame/response/authenticate.rs b/scylla-cql/src/frame/response/authenticate.rs index 5489a06ffd..f5503adbfc 100644 --- a/scylla-cql/src/frame/response/authenticate.rs +++ b/scylla-cql/src/frame/response/authenticate.rs @@ -1,4 +1,4 @@ -use crate::frame::frame_errors::ParseError; +use crate::frame::frame_errors::{CqlAuthenticateParseError, ParseError}; use crate::frame::types; // Implements Authenticate message. @@ -8,8 +8,10 @@ pub struct Authenticate { } impl Authenticate { - pub fn deserialize(buf: &mut &[u8]) -> Result { - let authenticator_name = types::read_string(buf)?.to_string(); + pub fn deserialize(buf: &mut &[u8]) -> Result { + let authenticator_name = types::read_string(buf) + .map_err(CqlAuthenticateParseError::AuthNameParseError)? + .to_string(); Ok(Authenticate { authenticator_name }) } From f1c92cc6a7fa27b3e5de21ae06fb92971ea536a3 Mon Sep 17 00:00:00 2001 From: muzarski Date: Mon, 1 Jul 2024 09:51:21 +0200 Subject: [PATCH 18/22] f_errors: CqlAuthSuccessParseError --- scylla-cql/src/frame/frame_errors.rs | 10 ++++++++++ scylla-cql/src/frame/response/authenticate.rs | 8 +++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/scylla-cql/src/frame/frame_errors.rs b/scylla-cql/src/frame/frame_errors.rs index faddd3d026..bd4709353e 100644 --- a/scylla-cql/src/frame/frame_errors.rs +++ b/scylla-cql/src/frame/frame_errors.rs @@ -35,6 +35,8 @@ pub enum FrameError { #[derive(Error, Debug)] pub enum ParseError { + #[error(transparent)] + CqlAuthSuccessParseError(#[from] CqlAuthSuccessParseError), #[error(transparent)] CqlAuthenticateParseError(#[from] CqlAuthenticateParseError), #[error(transparent)] @@ -61,6 +63,14 @@ pub enum ParseError { CqlTypeError(#[from] CqlTypeError), } +/// An error type returned when deserialization of AUTH_SUCCESS response fails. +#[non_exhaustive] +#[derive(Error, Debug)] +pub enum CqlAuthSuccessParseError { + #[error("Malformed success message: {0}")] + SuccessMessageParseError(LowLevelDeserializationError), +} + /// An error type returned when deserialization of AUTHENTICATE response fails. #[non_exhaustive] #[derive(Error, Debug)] diff --git a/scylla-cql/src/frame/response/authenticate.rs b/scylla-cql/src/frame/response/authenticate.rs index f5503adbfc..5bceabad6f 100644 --- a/scylla-cql/src/frame/response/authenticate.rs +++ b/scylla-cql/src/frame/response/authenticate.rs @@ -1,4 +1,4 @@ -use crate::frame::frame_errors::{CqlAuthenticateParseError, ParseError}; +use crate::frame::frame_errors::{CqlAuthSuccessParseError, CqlAuthenticateParseError, ParseError}; use crate::frame::types; // Implements Authenticate message. @@ -23,8 +23,10 @@ pub struct AuthSuccess { } impl AuthSuccess { - pub fn deserialize(buf: &mut &[u8]) -> Result { - let success_message = types::read_bytes_opt(buf)?.map(|b| b.to_owned()); + pub fn deserialize(buf: &mut &[u8]) -> Result { + let success_message = types::read_bytes_opt(buf) + .map_err(CqlAuthSuccessParseError::SuccessMessageParseError)? + .map(ToOwned::to_owned); Ok(AuthSuccess { success_message }) } From 6fcbe9ae19d47e5bd8e38f9565037fd13e6585c7 Mon Sep 17 00:00:00 2001 From: muzarski Date: Mon, 1 Jul 2024 09:53:23 +0200 Subject: [PATCH 19/22] f_errors: CqlAuthChallengeParseError --- scylla-cql/src/frame/frame_errors.rs | 10 ++++++++++ scylla-cql/src/frame/response/authenticate.rs | 10 +++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/scylla-cql/src/frame/frame_errors.rs b/scylla-cql/src/frame/frame_errors.rs index bd4709353e..9c4e471022 100644 --- a/scylla-cql/src/frame/frame_errors.rs +++ b/scylla-cql/src/frame/frame_errors.rs @@ -35,6 +35,8 @@ pub enum FrameError { #[derive(Error, Debug)] pub enum ParseError { + #[error(transparent)] + CqlAuthChallengeParseError(#[from] CqlAuthChallengeParseError), #[error(transparent)] CqlAuthSuccessParseError(#[from] CqlAuthSuccessParseError), #[error(transparent)] @@ -63,6 +65,14 @@ pub enum ParseError { CqlTypeError(#[from] CqlTypeError), } +/// An error type returned when deserialization of AUTH_CHALLENGE response fails. +#[non_exhaustive] +#[derive(Error, Debug)] +pub enum CqlAuthChallengeParseError { + #[error("Malformed authenticate message: {0}")] + AuthMessageParseError(LowLevelDeserializationError), +} + /// An error type returned when deserialization of AUTH_SUCCESS response fails. #[non_exhaustive] #[derive(Error, Debug)] diff --git a/scylla-cql/src/frame/response/authenticate.rs b/scylla-cql/src/frame/response/authenticate.rs index 5bceabad6f..2bfa26e674 100644 --- a/scylla-cql/src/frame/response/authenticate.rs +++ b/scylla-cql/src/frame/response/authenticate.rs @@ -1,4 +1,6 @@ -use crate::frame::frame_errors::{CqlAuthSuccessParseError, CqlAuthenticateParseError, ParseError}; +use crate::frame::frame_errors::{ + CqlAuthChallengeParseError, CqlAuthSuccessParseError, CqlAuthenticateParseError, +}; use crate::frame::types; // Implements Authenticate message. @@ -38,8 +40,10 @@ pub struct AuthChallenge { } impl AuthChallenge { - pub fn deserialize(buf: &mut &[u8]) -> Result { - let authenticate_message = types::read_bytes_opt(buf)?.map(|b| b.to_owned()); + pub fn deserialize(buf: &mut &[u8]) -> Result { + let authenticate_message = types::read_bytes_opt(buf) + .map_err(CqlAuthChallengeParseError::AuthMessageParseError)? + .map(|b| b.to_owned()); Ok(AuthChallenge { authenticate_message, From fc8fe803ec9b2094d24439133e3ffcea5f86dff9 Mon Sep 17 00:00:00 2001 From: muzarski Date: Mon, 1 Jul 2024 10:03:24 +0200 Subject: [PATCH 20/22] f_errors: CqlErrorParseError --- scylla-cql/src/frame/frame_errors.rs | 18 ++++ scylla-cql/src/frame/response/error.rs | 136 ++++++++++++++++++------- 2 files changed, 119 insertions(+), 35 deletions(-) diff --git a/scylla-cql/src/frame/frame_errors.rs b/scylla-cql/src/frame/frame_errors.rs index 9c4e471022..3c6dd36b2f 100644 --- a/scylla-cql/src/frame/frame_errors.rs +++ b/scylla-cql/src/frame/frame_errors.rs @@ -35,6 +35,8 @@ pub enum FrameError { #[derive(Error, Debug)] pub enum ParseError { + #[error(transparent)] + CqlErrorParseError(#[from] CqlErrorParseError), #[error(transparent)] CqlAuthChallengeParseError(#[from] CqlAuthChallengeParseError), #[error(transparent)] @@ -65,6 +67,22 @@ pub enum ParseError { CqlTypeError(#[from] CqlTypeError), } +/// An error type returned when deserialization of ERROR response fails. +#[non_exhaustive] +#[derive(Error, Debug)] +pub enum CqlErrorParseError { + #[error("Malformed error code: {0}")] + ErrorCodeParseError(LowLevelDeserializationError), + #[error("Malformed error reason: {0}")] + ReasonParseError(LowLevelDeserializationError), + #[error("Malformed error field {field} of DB error {db_error}: {err}")] + MalformedErrorField { + db_error: &'static str, + field: &'static str, + err: LowLevelDeserializationError, + }, +} + /// An error type returned when deserialization of AUTH_CHALLENGE response fails. #[non_exhaustive] #[derive(Error, Debug)] diff --git a/scylla-cql/src/frame/response/error.rs b/scylla-cql/src/frame/response/error.rs index e99d5686d5..2bd4e56c5b 100644 --- a/scylla-cql/src/frame/response/error.rs +++ b/scylla-cql/src/frame/response/error.rs @@ -1,5 +1,5 @@ use crate::errors::{DbError, OperationType, QueryError, WriteType}; -use crate::frame::frame_errors::ParseError; +use crate::frame::frame_errors::{CqlErrorParseError, LowLevelDeserializationError}; use crate::frame::protocol_features::ProtocolFeatures; use crate::frame::types; use byteorder::ReadBytesExt; @@ -11,69 +11,135 @@ pub struct Error { pub reason: String, } +fn make_error_field_err( + db_error: &'static str, + field: &'static str, + err: impl Into, +) -> CqlErrorParseError { + CqlErrorParseError::MalformedErrorField { + db_error, + field, + err: err.into(), + } +} + impl Error { - pub fn deserialize(features: &ProtocolFeatures, buf: &mut &[u8]) -> Result { - let code = types::read_int(buf)?; - let reason = types::read_string(buf)?.to_owned(); + pub fn deserialize( + features: &ProtocolFeatures, + buf: &mut &[u8], + ) -> Result { + let code = types::read_int(buf) + .map_err(|err| CqlErrorParseError::ErrorCodeParseError(err.into()))?; + let reason = types::read_string(buf) + .map_err(CqlErrorParseError::ReasonParseError)? + .to_owned(); let error: DbError = match code { 0x0000 => DbError::ServerError, 0x000A => DbError::ProtocolError, 0x0100 => DbError::AuthenticationError, 0x1000 => DbError::Unavailable { - consistency: types::read_consistency(buf)?, - required: types::read_int(buf)?, - alive: types::read_int(buf)?, + consistency: types::read_consistency(buf) + .map_err(|err| make_error_field_err("UNAVAILABLE", "CONSISTENCY", err))?, + required: types::read_int(buf) + .map_err(|err| make_error_field_err("UNAVAILABLE", "REQUIRED", err))?, + alive: types::read_int(buf) + .map_err(|err| make_error_field_err("UNAVAILABLE", "ALIVE", err))?, }, 0x1001 => DbError::Overloaded, 0x1002 => DbError::IsBootstrapping, 0x1003 => DbError::TruncateError, 0x1100 => DbError::WriteTimeout { - consistency: types::read_consistency(buf)?, - received: types::read_int(buf)?, - required: types::read_int(buf)?, - write_type: WriteType::from(types::read_string(buf)?), + consistency: types::read_consistency(buf) + .map_err(|err| make_error_field_err("WRITE_TIMEOUT", "CONSISTENCY", err))?, + received: types::read_int(buf) + .map_err(|err| make_error_field_err("WRITE_TIMEOUT", "RECEIVED", err))?, + required: types::read_int(buf) + .map_err(|err| make_error_field_err("WRITE_TIMEOUT", "REQUIRED", err))?, + write_type: WriteType::from( + types::read_string(buf) + .map_err(|err| make_error_field_err("WRITE_TIMEOUT", "WRITE_TYPE", err))?, + ), }, 0x1200 => DbError::ReadTimeout { - consistency: types::read_consistency(buf)?, - received: types::read_int(buf)?, - required: types::read_int(buf)?, - data_present: buf.read_u8()? != 0, + consistency: types::read_consistency(buf) + .map_err(|err| make_error_field_err("READ_TIMEOUT", "CONSISTENCY", err))?, + received: types::read_int(buf) + .map_err(|err| make_error_field_err("READ_TIMEOUT", "RECEIVED", err))?, + required: types::read_int(buf) + .map_err(|err| make_error_field_err("READ_TIMEOUT", "REQUIRED", err))?, + data_present: buf + .read_u8() + .map_err(|err| make_error_field_err("READ_TIMEOUT", "DATA_PRESENT", err))? + != 0, }, 0x1300 => DbError::ReadFailure { - consistency: types::read_consistency(buf)?, - received: types::read_int(buf)?, - required: types::read_int(buf)?, - numfailures: types::read_int(buf)?, - data_present: buf.read_u8()? != 0, + consistency: types::read_consistency(buf) + .map_err(|err| make_error_field_err("READ_FAILURE", "CONSISTENCY", err))?, + received: types::read_int(buf) + .map_err(|err| make_error_field_err("READ_FAILURE", "RECEIVED", err))?, + required: types::read_int(buf) + .map_err(|err| make_error_field_err("READ_FAILURE", "REQUIRED", err))?, + numfailures: types::read_int(buf) + .map_err(|err| make_error_field_err("READ_FAILURE", "NUM_FAILURES", err))?, + data_present: buf + .read_u8() + .map_err(|err| make_error_field_err("READ_FAILURE", "DATA_PRESENT", err))? + != 0, }, 0x1400 => DbError::FunctionFailure { - keyspace: types::read_string(buf)?.to_string(), - function: types::read_string(buf)?.to_string(), - arg_types: types::read_string_list(buf)?, + keyspace: types::read_string(buf) + .map_err(|err| make_error_field_err("FUNCTION_FAILURE", "KEYSPACE", err))? + .to_string(), + function: types::read_string(buf) + .map_err(|err| make_error_field_err("FUNCTION_FAILURE", "FUNCTION", err))? + .to_string(), + arg_types: types::read_string_list(buf) + .map_err(|err| make_error_field_err("FUNCTION_FAILURE", "ARG_TYPES", err))?, }, 0x1500 => DbError::WriteFailure { - consistency: types::read_consistency(buf)?, - received: types::read_int(buf)?, - required: types::read_int(buf)?, - numfailures: types::read_int(buf)?, - write_type: WriteType::from(types::read_string(buf)?), + consistency: types::read_consistency(buf) + .map_err(|err| make_error_field_err("WRITE_FAILURE", "CONSISTENCY", err))?, + received: types::read_int(buf) + .map_err(|err| make_error_field_err("WRITE_FAILURE", "RECEIVED", err))?, + required: types::read_int(buf) + .map_err(|err| make_error_field_err("WRITE_FAILURE", "REQUIRED", err))?, + numfailures: types::read_int(buf) + .map_err(|err| make_error_field_err("WRITE_FAILURE", "NUM_FAILURES", err))?, + write_type: WriteType::from( + types::read_string(buf) + .map_err(|err| make_error_field_err("WRITE_FAILURE", "WRITE_TYPE", err))?, + ), }, 0x2000 => DbError::SyntaxError, 0x2100 => DbError::Unauthorized, 0x2200 => DbError::Invalid, 0x2300 => DbError::ConfigError, 0x2400 => DbError::AlreadyExists { - keyspace: types::read_string(buf)?.to_string(), - table: types::read_string(buf)?.to_string(), + keyspace: types::read_string(buf) + .map_err(|err| make_error_field_err("ALREADY_EXISTS", "KEYSPACE", err))? + .to_string(), + table: types::read_string(buf) + .map_err(|err| make_error_field_err("ALREADY_EXISTS", "TABLE", err))? + .to_string(), }, 0x2500 => DbError::Unprepared { - statement_id: Bytes::from(types::read_short_bytes(buf)?.to_owned()), - }, - code if Some(code) == features.rate_limit_error => DbError::RateLimitReached { - op_type: OperationType::from(buf.read_u8()?), - rejected_by_coordinator: buf.read_u8()? != 0, + statement_id: Bytes::from( + types::read_short_bytes(buf) + .map_err(|err| make_error_field_err("UNPREPARED", "STATEMENT_ID", err))? + .to_owned(), + ), }, + code if Some(code) == features.rate_limit_error => { + DbError::RateLimitReached { + op_type: OperationType::from(buf.read_u8().map_err(|err| { + make_error_field_err("RATE_LIMIT_REACHED", "OP_TYPE", err) + })?), + rejected_by_coordinator: buf.read_u8().map_err(|err| { + make_error_field_err("RATE_LIMIT_REACHED", "REJECTED_BY_COORDINATOR", err) + })? != 0, + } + } _ => DbError::Other(code), }; From 3f7a8e08e6f36e56cae1ce59f00989e07be94d6e Mon Sep 17 00:00:00 2001 From: muzarski Date: Mon, 1 Jul 2024 10:51:16 +0200 Subject: [PATCH 21/22] f_errors: derive Clone In the following commit we want to introduce a CqlResponseParseError type which will be a new variant of `[Query/NewSession]Error`. These two error types implement a `Clone` trait. They require a `Clone`, since there is some cloning of errors in `history` module. This is why, we need to derive Clone for all of the types introduces in previous commits. Unfortunately, the `std::io::Error` does not implement Clone. This is why we need to wrap any appearing `std::io::Error` with `Arc`, so the enum types encapsulating it can derive Clone. --- scylla-cql/src/frame/frame_errors.rs | 46 ++++++++++++++++------------ 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/scylla-cql/src/frame/frame_errors.rs b/scylla-cql/src/frame/frame_errors.rs index 3c6dd36b2f..2edf8bcc7a 100644 --- a/scylla-cql/src/frame/frame_errors.rs +++ b/scylla-cql/src/frame/frame_errors.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use super::TryFromPrimitiveError; use crate::cql_to_rust::CqlTypeError; use crate::frame::value::SerializeValuesError; @@ -69,7 +71,7 @@ pub enum ParseError { /// An error type returned when deserialization of ERROR response fails. #[non_exhaustive] -#[derive(Error, Debug)] +#[derive(Error, Debug, Clone)] pub enum CqlErrorParseError { #[error("Malformed error code: {0}")] ErrorCodeParseError(LowLevelDeserializationError), @@ -85,7 +87,7 @@ pub enum CqlErrorParseError { /// An error type returned when deserialization of AUTH_CHALLENGE response fails. #[non_exhaustive] -#[derive(Error, Debug)] +#[derive(Error, Debug, Clone)] pub enum CqlAuthChallengeParseError { #[error("Malformed authenticate message: {0}")] AuthMessageParseError(LowLevelDeserializationError), @@ -93,7 +95,7 @@ pub enum CqlAuthChallengeParseError { /// An error type returned when deserialization of AUTH_SUCCESS response fails. #[non_exhaustive] -#[derive(Error, Debug)] +#[derive(Error, Debug, Clone)] pub enum CqlAuthSuccessParseError { #[error("Malformed success message: {0}")] SuccessMessageParseError(LowLevelDeserializationError), @@ -101,7 +103,7 @@ pub enum CqlAuthSuccessParseError { /// An error type returned when deserialization of AUTHENTICATE response fails. #[non_exhaustive] -#[derive(Error, Debug)] +#[derive(Error, Debug, Clone)] pub enum CqlAuthenticateParseError { #[error("Malformed authenticator name: {0}")] AuthNameParseError(LowLevelDeserializationError), @@ -109,7 +111,7 @@ pub enum CqlAuthenticateParseError { /// An error type returned when deserialization of SUPPORTED response fails. #[non_exhaustive] -#[derive(Error, Debug)] +#[derive(Error, Debug, Clone)] pub enum CqlSupportedParseError { #[error("Malformed options map: {0}")] OptionsMapDeserialization(LowLevelDeserializationError), @@ -117,7 +119,7 @@ pub enum CqlSupportedParseError { /// An error type returned when deserialization of RESULT response fails. #[non_exhaustive] -#[derive(Error, Debug)] +#[derive(Error, Debug, Clone)] pub enum CqlResultParseError { #[error("Malformed RESULT response id: {0}")] ResultIdParseError(LowLevelDeserializationError), @@ -136,7 +138,7 @@ pub enum CqlResultParseError { } #[non_exhaustive] -#[derive(Error, Debug)] +#[derive(Error, Debug, Clone)] pub enum SetKeyspaceParseError { #[error("Malformed keyspace name: {0}")] MalformedKeyspaceName(#[from] LowLevelDeserializationError), @@ -145,7 +147,7 @@ pub enum SetKeyspaceParseError { /// An error type returned when deserialization of /// `EVENT` response fails. #[non_exhaustive] -#[derive(Error, Debug)] +#[derive(Error, Debug, Clone)] pub enum CqlEventParseError { #[error("Malformed event type string: {0}")] EventTypeParseError(LowLevelDeserializationError), @@ -162,7 +164,7 @@ pub enum CqlEventParseError { /// An error type returned when deserialization of /// SchemaChangeEvent fails. #[non_exhaustive] -#[derive(Error, Debug)] +#[derive(Error, Debug, Clone)] pub enum SchemaChangeEventParseError { #[error("Malformed schema change type string: {0}")] TypeOfChangeParseError(LowLevelDeserializationError), @@ -186,7 +188,7 @@ pub enum SchemaChangeEventParseError { /// An error type returned when deserialization of [Status/Topology]ChangeEvent fails. #[non_exhaustive] -#[derive(Error, Debug)] +#[derive(Error, Debug, Clone)] pub enum ClusterChangeEventParseError { #[error("Malformed type of change: {0}")] TypeOfChangeParseError(LowLevelDeserializationError), @@ -199,7 +201,7 @@ pub enum ClusterChangeEventParseError { /// An error type returned when deserialization /// of `RESULT::`Prepared` response fails. #[non_exhaustive] -#[derive(Debug, Error)] +#[derive(Debug, Error, Clone)] pub enum PreparedParseError { #[error("Malformed prepared statement's id length: {0}")] IdLengthParseError(LowLevelDeserializationError), @@ -212,7 +214,7 @@ pub enum PreparedParseError { /// An error type returned when deserialization /// of `RESULT::Rows` response fails. #[non_exhaustive] -#[derive(Debug, Error)] +#[derive(Debug, Error, Clone)] pub enum RowsParseError { #[error("Invalid result metadata: {0}")] ResultMetadataParseError(#[from] ResultMetadataParseError), @@ -230,7 +232,7 @@ pub enum RowsParseError { /// An error type returned when deserialization /// of `[Result/Prepared]Metadata` failed. #[non_exhaustive] -#[derive(Error, Debug)] +#[derive(Error, Debug, Clone)] pub enum ResultMetadataParseError { #[error("Malformed metadata flags: {0}")] FlagsParseError(LowLevelDeserializationError), @@ -251,7 +253,7 @@ pub enum ResultMetadataParseError { /// An error type returned when deserialization /// of table specification fails. #[non_exhaustive] -#[derive(Error, Debug)] +#[derive(Error, Debug, Clone)] pub enum TableSpecParseError { #[error("Malformed keyspace name: {0}")] MalformedKeyspaceName(LowLevelDeserializationError), @@ -262,7 +264,7 @@ pub enum TableSpecParseError { /// An error type returned when deserialization /// of table column specifications fails. #[non_exhaustive] -#[derive(Error, Debug)] +#[derive(Error, Debug, Clone)] #[error("Column spec deserialization failed, column index: {column_index}, error: {kind}")] pub struct ColumnSpecParseError { pub column_index: usize, @@ -272,7 +274,7 @@ pub struct ColumnSpecParseError { /// The type of error that appeared during deserialization /// of a column specification. #[non_exhaustive] -#[derive(Error, Debug)] +#[derive(Error, Debug, Clone)] pub enum ColumnSpecParseErrorKind { #[error("Invalid table spec: {0}")] TableSpecParseError(#[from] TableSpecParseError), @@ -284,7 +286,7 @@ pub enum ColumnSpecParseErrorKind { /// An error type returned when deserialization of CQL type name fails. #[non_exhaustive] -#[derive(Error, Debug)] +#[derive(Error, Debug, Clone)] pub enum CqlTypeParseError { #[error("Malformed type id: {0}")] TypeIdParseError(LowLevelDeserializationError), @@ -315,10 +317,10 @@ pub enum CqlTypeParseError { /// - conversion errors - e.g. slice-to-array or primitive-to-enum /// - not enough bytes in the buffer to deserialize a value #[non_exhaustive] -#[derive(Error, Debug)] +#[derive(Error, Debug, Clone)] pub enum LowLevelDeserializationError { #[error(transparent)] - IoError(#[from] std::io::Error), + IoError(Arc), #[error(transparent)] TryFromIntError(#[from] std::num::TryFromIntError), #[error(transparent)] @@ -334,3 +336,9 @@ pub enum LowLevelDeserializationError { #[error("UTF8 deserialization failed: {0}")] UTF8DeserializationError(#[from] std::str::Utf8Error), } + +impl From for LowLevelDeserializationError { + fn from(value: std::io::Error) -> Self { + Self::IoError(Arc::new(value)) + } +} From 46d639bd5407c518f94e9a048d2a1472457d0604 Mon Sep 17 00:00:00 2001 From: muzarski Date: Mon, 1 Jul 2024 10:58:52 +0200 Subject: [PATCH 22/22] f_errors: CqlResponseParseError --- scylla-cql/src/errors.rs | 11 +++++- scylla-cql/src/frame/frame_errors.rs | 35 +++++++++++-------- scylla-cql/src/frame/response/mod.rs | 6 ++-- .../src/transport/load_balancing/default.rs | 1 + 4 files changed, 36 insertions(+), 17 deletions(-) diff --git a/scylla-cql/src/errors.rs b/scylla-cql/src/errors.rs index e884e37ad5..1ebf9cfede 100644 --- a/scylla-cql/src/errors.rs +++ b/scylla-cql/src/errors.rs @@ -1,6 +1,6 @@ //! This module contains various errors which can be returned by `scylla::Session` -use crate::frame::frame_errors::{FrameError, ParseError}; +use crate::frame::frame_errors::{CqlResponseParseError, FrameError, ParseError}; use crate::frame::protocol_features::ProtocolFeatures; use crate::frame::value::SerializeValuesError; use crate::types::serialize::SerializationError; @@ -21,6 +21,10 @@ pub enum QueryError { #[error(transparent)] BadQuery(#[from] BadQuery), + /// Failed to deserialize a CQL response from the server. + #[error(transparent)] + CqlResponseParseError(#[from] CqlResponseParseError), + /// Input/Output error has occurred, connection broken etc. #[error("IO Error: {0}")] IoError(Arc), @@ -381,6 +385,10 @@ pub enum NewSessionError { #[error(transparent)] BadQuery(#[from] BadQuery), + /// Failed to deserialize a CQL response from the server. + #[error(transparent)] + CqlResponseParseError(#[from] CqlResponseParseError), + /// Input/Output error has occurred, connection broken etc. #[error("IO Error: {0}")] IoError(Arc), @@ -482,6 +490,7 @@ impl From for NewSessionError { match query_error { QueryError::DbError(e, msg) => NewSessionError::DbError(e, msg), QueryError::BadQuery(e) => NewSessionError::BadQuery(e), + QueryError::CqlResponseParseError(e) => NewSessionError::CqlResponseParseError(e), QueryError::IoError(e) => NewSessionError::IoError(e), QueryError::ProtocolError(m) => NewSessionError::ProtocolError(m), QueryError::InvalidMessage(m) => NewSessionError::InvalidMessage(m), diff --git a/scylla-cql/src/frame/frame_errors.rs b/scylla-cql/src/frame/frame_errors.rs index 2edf8bcc7a..b90eef3a79 100644 --- a/scylla-cql/src/frame/frame_errors.rs +++ b/scylla-cql/src/frame/frame_errors.rs @@ -37,20 +37,6 @@ pub enum FrameError { #[derive(Error, Debug)] pub enum ParseError { - #[error(transparent)] - CqlErrorParseError(#[from] CqlErrorParseError), - #[error(transparent)] - CqlAuthChallengeParseError(#[from] CqlAuthChallengeParseError), - #[error(transparent)] - CqlAuthSuccessParseError(#[from] CqlAuthSuccessParseError), - #[error(transparent)] - CqlAuthenticateParseError(#[from] CqlAuthenticateParseError), - #[error(transparent)] - CqlSupportedParseError(#[from] CqlSupportedParseError), - #[error(transparent)] - CqlEventParseError(#[from] CqlEventParseError), - #[error(transparent)] - CqlResultParseError(#[from] CqlResultParseError), #[error("Low-level deserialization failed: {0}")] LowLevelDeserializationError(#[from] LowLevelDeserializationError), #[error("Could not serialize frame: {0}")] @@ -69,6 +55,27 @@ pub enum ParseError { CqlTypeError(#[from] CqlTypeError), } +/// An error type returned when deserialization of CQL +/// server response fails. +#[non_exhaustive] +#[derive(Error, Debug, Clone)] +pub enum CqlResponseParseError { + #[error("Failed to deserialize ERROR response: {0}")] + CqlErrorParseError(#[from] CqlErrorParseError), + #[error("Failed to deserialize AUTH_CHALLENGE response: {0}")] + CqlAuthChallengeParseError(#[from] CqlAuthChallengeParseError), + #[error("Failed to deserialize AUTH_SUCCESS response: {0}")] + CqlAuthSuccessParseError(#[from] CqlAuthSuccessParseError), + #[error("Failed to deserialize AUTHENTICATE response: {0}")] + CqlAuthenticateParseError(#[from] CqlAuthenticateParseError), + #[error("Failed to deserialize SUPPORTED response: {0}")] + CqlSupportedParseError(#[from] CqlSupportedParseError), + #[error("Failed to deserialize EVENT response: {0}")] + CqlEventParseError(#[from] CqlEventParseError), + #[error(transparent)] + CqlResultParseError(#[from] CqlResultParseError), +} + /// An error type returned when deserialization of ERROR response fails. #[non_exhaustive] #[derive(Error, Debug, Clone)] diff --git a/scylla-cql/src/frame/response/mod.rs b/scylla-cql/src/frame/response/mod.rs index 569a14f401..8e6e7ff335 100644 --- a/scylla-cql/src/frame/response/mod.rs +++ b/scylla-cql/src/frame/response/mod.rs @@ -8,10 +8,12 @@ pub mod supported; pub use error::Error; pub use supported::Supported; +use crate::errors::QueryError; use crate::frame::protocol_features::ProtocolFeatures; use crate::frame::response::result::ResultMetadata; use crate::frame::TryFromPrimitiveError; -use crate::{errors::QueryError, frame::frame_errors::ParseError}; + +use super::frame_errors::CqlResponseParseError; #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] #[repr(u8)] @@ -65,7 +67,7 @@ impl Response { opcode: ResponseOpcode, buf: &mut &[u8], cached_metadata: Option<&ResultMetadata>, - ) -> Result { + ) -> Result { let response = match opcode { ResponseOpcode::Error => Response::Error(Error::deserialize(features, buf)?), ResponseOpcode::Ready => Response::Ready, diff --git a/scylla/src/transport/load_balancing/default.rs b/scylla/src/transport/load_balancing/default.rs index 46aa282992..519edf5869 100644 --- a/scylla/src/transport/load_balancing/default.rs +++ b/scylla/src/transport/load_balancing/default.rs @@ -2689,6 +2689,7 @@ mod latency_awareness { // "slow" errors, i.e. ones that are returned after considerable time of query being run QueryError::DbError(_, _) + | QueryError::CqlResponseParseError(_) | QueryError::InvalidMessage(_) | QueryError::IoError(_) | QueryError::ProtocolError(_)