From 17f662f81aca15423fdf9f6d4aafcaa5ea6fb92c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojciech=20Przytu=C5=82a?= Date: Sun, 27 Jul 2025 17:17:01 +0200 Subject: [PATCH 1/7] tree: merge execution_error.rs into cass_error.rs The `CassError`-related items were arbitrarily split between two modules: `cass_error.rs` and `execution_error.rs`. As this division is artificial, `execution_error.rs` contents have been moved to `cass_error.rs`. --- scylla-rust-wrapper/src/api.rs | 2 +- scylla-rust-wrapper/src/cass_error.rs | 432 ++++++++++++++++++++- scylla-rust-wrapper/src/execution_error.rs | 428 -------------------- scylla-rust-wrapper/src/future.rs | 5 +- scylla-rust-wrapper/src/lib.rs | 1 - scylla-rust-wrapper/src/query_result.rs | 4 +- 6 files changed, 434 insertions(+), 438 deletions(-) delete mode 100644 scylla-rust-wrapper/src/execution_error.rs diff --git a/scylla-rust-wrapper/src/api.rs b/scylla-rust-wrapper/src/api.rs index 6e28bc8e..0fbbb754 100644 --- a/scylla-rust-wrapper/src/api.rs +++ b/scylla-rust-wrapper/src/api.rs @@ -713,7 +713,7 @@ pub mod result { pub mod error { // Disabling rustfmt to have one item per line for better readability. #[rustfmt::skip] - pub use crate::execution_error::{ + pub use crate::cass_error::{ CassError, CassErrorResult, CassErrorSource, diff --git a/scylla-rust-wrapper/src/cass_error.rs b/scylla-rust-wrapper/src/cass_error.rs index 26a49cc0..ca0641f1 100644 --- a/scylla-rust-wrapper/src/cass_error.rs +++ b/scylla-rust-wrapper/src/cass_error.rs @@ -1,8 +1,20 @@ -use scylla::errors::*; +use crate::argconv::*; +use crate::cass_types::CassConsistency; +use crate::misc::CassWriteType; +use crate::types::*; +use libc::c_char; +use scylla::deserialize::DeserializationError; +use scylla::errors::{ + BadKeyspaceName, BadQuery, ConnectionPoolError, DbError, ExecutionError, MetadataError, + MetadataFetchErrorKind, NewSessionError, PrepareError, RequestAttemptError, SerializationError, + WriteType, +}; +use scylla::frame::frame_errors::ResultMetadataAndRowsCountParseError; +use scylla::statement::Consistency; +use thiserror::Error; // Re-export error types. pub use crate::cass_error_types::{CassError, CassErrorSource}; -use crate::execution_error::CassErrorResult; use crate::statement::UnknownNamedParameterError; pub(crate) trait ToCassError { @@ -274,3 +286,419 @@ impl CassErrorMessage for str { self.to_string() } } + +#[derive(Error, Debug)] +pub enum CassErrorResult { + #[error(transparent)] + Execution(#[from] ExecutionError), + #[error(transparent)] + ResultMetadataLazyDeserialization(#[from] ResultMetadataAndRowsCountParseError), + #[error("Failed to deserialize first row: {0}")] + Deserialization(#[from] DeserializationError), +} + +impl FFI for CassErrorResult { + type Origin = FromArc; +} + +impl From for CassConsistency { + fn from(c: Consistency) -> CassConsistency { + match c { + Consistency::Any => CassConsistency::CASS_CONSISTENCY_ANY, + Consistency::One => CassConsistency::CASS_CONSISTENCY_ONE, + Consistency::Two => CassConsistency::CASS_CONSISTENCY_TWO, + Consistency::Three => CassConsistency::CASS_CONSISTENCY_THREE, + Consistency::Quorum => CassConsistency::CASS_CONSISTENCY_QUORUM, + Consistency::All => CassConsistency::CASS_CONSISTENCY_ALL, + Consistency::LocalQuorum => CassConsistency::CASS_CONSISTENCY_LOCAL_QUORUM, + Consistency::EachQuorum => CassConsistency::CASS_CONSISTENCY_EACH_QUORUM, + Consistency::LocalOne => CassConsistency::CASS_CONSISTENCY_LOCAL_ONE, + Consistency::Serial => CassConsistency::CASS_CONSISTENCY_SERIAL, + Consistency::LocalSerial => CassConsistency::CASS_CONSISTENCY_LOCAL_SERIAL, + } + } +} + +impl From<&WriteType> for CassWriteType { + fn from(c: &WriteType) -> CassWriteType { + match c { + WriteType::Simple => CassWriteType::CASS_WRITE_TYPE_SIMPLE, + WriteType::Batch => CassWriteType::CASS_WRITE_TYPE_BATCH, + WriteType::UnloggedBatch => CassWriteType::CASS_WRITE_TYPE_UNLOGGED_BATCH, + WriteType::Counter => CassWriteType::CASS_WRITE_TYPE_COUNTER, + WriteType::BatchLog => CassWriteType::CASS_WRITE_TYPE_BATCH_LOG, + WriteType::Cas => CassWriteType::CASS_WRITE_TYPE_CAS, + WriteType::View => CassWriteType::CASS_WRITE_TYPE_VIEW, + WriteType::Cdc => CassWriteType::CASS_WRITE_TYPE_CDC, + WriteType::Other(_) => CassWriteType::CASS_WRITE_TYPE_UNKNOWN, + } + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cass_error_result_free( + error_result: CassOwnedSharedPtr, +) { + ArcFFI::free(error_result); +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cass_error_result_code( + error_result: CassBorrowedSharedPtr, +) -> CassError { + let Some(error_result) = ArcFFI::as_ref(error_result) else { + tracing::error!("Provided null error result pointer to cass_error_result_code!"); + return CassError::CASS_ERROR_LIB_BAD_PARAMS; + }; + + error_result.to_cass_error() +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cass_error_result_consistency( + error_result: CassBorrowedSharedPtr, +) -> CassConsistency { + let Some(error_result) = ArcFFI::as_ref(error_result) else { + tracing::error!("Provided null error result pointer to cass_error_result_consistency!"); + return CassConsistency::CASS_CONSISTENCY_UNKNOWN; + }; + + match error_result { + CassErrorResult::Execution(ExecutionError::LastAttemptError( + RequestAttemptError::DbError(DbError::Unavailable { consistency, .. }, _) + | RequestAttemptError::DbError(DbError::ReadTimeout { consistency, .. }, _) + | RequestAttemptError::DbError(DbError::WriteTimeout { consistency, .. }, _) + | RequestAttemptError::DbError(DbError::ReadFailure { consistency, .. }, _) + | RequestAttemptError::DbError(DbError::WriteFailure { consistency, .. }, _), + )) => CassConsistency::from(*consistency), + _ => CassConsistency::CASS_CONSISTENCY_UNKNOWN, + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cass_error_result_responses_received( + error_result: CassBorrowedSharedPtr, +) -> cass_int32_t { + let Some(error_result) = ArcFFI::as_ref(error_result) else { + tracing::error!( + "Provided null error result pointer to cass_error_result_responses_received!" + ); + return -1; + }; + + match error_result { + CassErrorResult::Execution(ExecutionError::LastAttemptError(attempt_error)) => { + match attempt_error { + RequestAttemptError::DbError(DbError::Unavailable { alive, .. }, _) => *alive, + RequestAttemptError::DbError(DbError::ReadTimeout { received, .. }, _) => *received, + RequestAttemptError::DbError(DbError::WriteTimeout { received, .. }, _) => { + *received + } + RequestAttemptError::DbError(DbError::ReadFailure { received, .. }, _) => *received, + RequestAttemptError::DbError(DbError::WriteFailure { received, .. }, _) => { + *received + } + _ => -1, + } + } + _ => -1, + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cass_error_result_responses_required( + error_result: CassBorrowedSharedPtr, +) -> cass_int32_t { + let Some(error_result) = ArcFFI::as_ref(error_result) else { + tracing::error!( + "Provided null error result pointer to cass_error_result_responses_required!" + ); + return -1; + }; + + match error_result { + CassErrorResult::Execution(ExecutionError::LastAttemptError(attempt_error)) => { + match attempt_error { + RequestAttemptError::DbError(DbError::Unavailable { required, .. }, _) => *required, + RequestAttemptError::DbError(DbError::ReadTimeout { required, .. }, _) => *required, + RequestAttemptError::DbError(DbError::WriteTimeout { required, .. }, _) => { + *required + } + RequestAttemptError::DbError(DbError::ReadFailure { required, .. }, _) => *required, + RequestAttemptError::DbError(DbError::WriteFailure { required, .. }, _) => { + *required + } + _ => -1, + } + } + _ => -1, + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cass_error_result_num_failures( + error_result: CassBorrowedSharedPtr, +) -> cass_int32_t { + let Some(error_result) = ArcFFI::as_ref(error_result) else { + tracing::error!("Provided null error result pointer to cass_error_result_num_failures!"); + return -1; + }; + + match error_result { + CassErrorResult::Execution(ExecutionError::LastAttemptError( + RequestAttemptError::DbError(DbError::ReadFailure { numfailures, .. }, _), + )) => *numfailures, + CassErrorResult::Execution(ExecutionError::LastAttemptError( + RequestAttemptError::DbError(DbError::WriteFailure { numfailures, .. }, _), + )) => *numfailures, + _ => -1, + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cass_error_result_data_present( + error_result: CassBorrowedSharedPtr, +) -> cass_bool_t { + let Some(error_result) = ArcFFI::as_ref(error_result) else { + tracing::error!("Provided null error result pointer to cass_error_result_data_present!"); + return cass_false; + }; + + match error_result { + CassErrorResult::Execution(ExecutionError::LastAttemptError( + RequestAttemptError::DbError(DbError::ReadTimeout { data_present, .. }, _), + )) => { + if *data_present { + cass_true + } else { + cass_false + } + } + CassErrorResult::Execution(ExecutionError::LastAttemptError( + RequestAttemptError::DbError(DbError::ReadFailure { data_present, .. }, _), + )) => { + if *data_present { + cass_true + } else { + cass_false + } + } + _ => cass_false, + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cass_error_result_write_type( + error_result: CassBorrowedSharedPtr, +) -> CassWriteType { + let Some(error_result) = ArcFFI::as_ref(error_result) else { + tracing::error!("Provided null error result pointer to cass_error_result_write_type!"); + return CassWriteType::CASS_WRITE_TYPE_UNKNOWN; + }; + + match error_result { + CassErrorResult::Execution(ExecutionError::LastAttemptError( + RequestAttemptError::DbError(DbError::WriteTimeout { write_type, .. }, _), + )) => CassWriteType::from(write_type), + CassErrorResult::Execution(ExecutionError::LastAttemptError( + RequestAttemptError::DbError(DbError::WriteFailure { write_type, .. }, _), + )) => CassWriteType::from(write_type), + _ => CassWriteType::CASS_WRITE_TYPE_UNKNOWN, + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cass_error_result_keyspace( + error_result: CassBorrowedSharedPtr, + c_keyspace: *mut *const ::std::os::raw::c_char, + c_keyspace_len: *mut size_t, +) -> CassError { + let Some(error_result) = ArcFFI::as_ref(error_result) else { + tracing::error!("Provided null error result pointer to cass_error_result_keyspace!"); + return CassError::CASS_ERROR_LIB_BAD_PARAMS; + }; + + match error_result { + CassErrorResult::Execution(ExecutionError::LastAttemptError( + RequestAttemptError::DbError(DbError::AlreadyExists { keyspace, .. }, _), + )) => { + unsafe { write_str_to_c(keyspace.as_str(), c_keyspace, c_keyspace_len) }; + CassError::CASS_OK + } + CassErrorResult::Execution(ExecutionError::LastAttemptError( + RequestAttemptError::DbError(DbError::FunctionFailure { keyspace, .. }, _), + )) => { + unsafe { write_str_to_c(keyspace.as_str(), c_keyspace, c_keyspace_len) }; + CassError::CASS_OK + } + _ => CassError::CASS_ERROR_LIB_INVALID_ERROR_RESULT_TYPE, + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cass_error_result_table( + error_result: CassBorrowedSharedPtr, + c_table: *mut *const ::std::os::raw::c_char, + c_table_len: *mut size_t, +) -> CassError { + let Some(error_result) = ArcFFI::as_ref(error_result) else { + tracing::error!("Provided null error result pointer to cass_error_result_table!"); + return CassError::CASS_ERROR_LIB_BAD_PARAMS; + }; + + match error_result { + CassErrorResult::Execution(ExecutionError::LastAttemptError( + RequestAttemptError::DbError(DbError::AlreadyExists { table, .. }, _), + )) => { + unsafe { write_str_to_c(table.as_str(), c_table, c_table_len) }; + CassError::CASS_OK + } + _ => CassError::CASS_ERROR_LIB_INVALID_ERROR_RESULT_TYPE, + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cass_error_result_function( + error_result: CassBorrowedSharedPtr, + c_function: *mut *const ::std::os::raw::c_char, + c_function_len: *mut size_t, +) -> CassError { + let Some(error_result) = ArcFFI::as_ref(error_result) else { + tracing::error!("Provided null error result pointer to cass_error_result_function!"); + return CassError::CASS_ERROR_LIB_BAD_PARAMS; + }; + + match error_result { + CassErrorResult::Execution(ExecutionError::LastAttemptError( + RequestAttemptError::DbError(DbError::FunctionFailure { function, .. }, _), + )) => { + unsafe { write_str_to_c(function.as_str(), c_function, c_function_len) }; + CassError::CASS_OK + } + _ => CassError::CASS_ERROR_LIB_INVALID_ERROR_RESULT_TYPE, + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cass_error_num_arg_types( + error_result: CassBorrowedSharedPtr, +) -> size_t { + let Some(error_result) = ArcFFI::as_ref(error_result) else { + tracing::error!("Provided null error result pointer to cass_error_num_arg_types!"); + return 0; + }; + + match error_result { + CassErrorResult::Execution(ExecutionError::LastAttemptError( + RequestAttemptError::DbError(DbError::FunctionFailure { arg_types, .. }, _), + )) => arg_types.len() as size_t, + _ => 0, + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cass_error_result_arg_type( + error_result: CassBorrowedSharedPtr, + index: size_t, + arg_type: *mut *const ::std::os::raw::c_char, + arg_type_length: *mut size_t, +) -> CassError { + let Some(error_result) = ArcFFI::as_ref(error_result) else { + tracing::error!("Provided null error result pointer to cass_error_result_arg_type!"); + return CassError::CASS_ERROR_LIB_BAD_PARAMS; + }; + + match error_result { + CassErrorResult::Execution(ExecutionError::LastAttemptError( + RequestAttemptError::DbError(DbError::FunctionFailure { arg_types, .. }, _), + )) => { + if index >= arg_types.len() as size_t { + return CassError::CASS_ERROR_LIB_INDEX_OUT_OF_BOUNDS; + } + unsafe { + write_str_to_c( + arg_types[index as usize].as_str(), + arg_type, + arg_type_length, + ) + }; + CassError::CASS_OK + } + _ => CassError::CASS_ERROR_LIB_INVALID_ERROR_RESULT_TYPE, + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cass_error_desc(error: CassError) -> *const c_char { + let desc = match error { + CassError::CASS_ERROR_LIB_BAD_PARAMS => c"Bad parameters", + CassError::CASS_ERROR_LIB_NO_STREAMS => c"No streams available", + CassError::CASS_ERROR_LIB_UNABLE_TO_INIT => c"Unable to initialize", + CassError::CASS_ERROR_LIB_MESSAGE_ENCODE => c"Unable to encode message", + CassError::CASS_ERROR_LIB_HOST_RESOLUTION => c"Unable to resolve host", + CassError::CASS_ERROR_LIB_UNEXPECTED_RESPONSE => c"Unexpected response from server", + CassError::CASS_ERROR_LIB_REQUEST_QUEUE_FULL => c"The request queue is full", + CassError::CASS_ERROR_LIB_NO_AVAILABLE_IO_THREAD => c"No available IO threads", + CassError::CASS_ERROR_LIB_WRITE_ERROR => c"Write error", + CassError::CASS_ERROR_LIB_NO_HOSTS_AVAILABLE => c"No hosts available", + CassError::CASS_ERROR_LIB_INDEX_OUT_OF_BOUNDS => c"Index out of bounds", + CassError::CASS_ERROR_LIB_INVALID_ITEM_COUNT => c"Invalid item count", + CassError::CASS_ERROR_LIB_INVALID_VALUE_TYPE => c"Invalid value type", + CassError::CASS_ERROR_LIB_REQUEST_TIMED_OUT => c"Request timed out", + CassError::CASS_ERROR_LIB_UNABLE_TO_SET_KEYSPACE => c"Unable to set keyspace", + CassError::CASS_ERROR_LIB_CALLBACK_ALREADY_SET => c"Callback already set", + CassError::CASS_ERROR_LIB_INVALID_STATEMENT_TYPE => c"Invalid statement type", + CassError::CASS_ERROR_LIB_NAME_DOES_NOT_EXIST => c"No value or column for name", + CassError::CASS_ERROR_LIB_UNABLE_TO_DETERMINE_PROTOCOL => { + c"Unable to find supported protocol version" + } + CassError::CASS_ERROR_LIB_NULL_VALUE => c"NULL value specified", + CassError::CASS_ERROR_LIB_NOT_IMPLEMENTED => c"Not implemented", + CassError::CASS_ERROR_LIB_UNABLE_TO_CONNECT => c"Unable to connect", + CassError::CASS_ERROR_LIB_UNABLE_TO_CLOSE => c"Unable to close", + CassError::CASS_ERROR_LIB_NO_PAGING_STATE => c"No paging state", + CassError::CASS_ERROR_LIB_PARAMETER_UNSET => c"Parameter unset", + CassError::CASS_ERROR_LIB_INVALID_ERROR_RESULT_TYPE => c"Invalid error result type", + CassError::CASS_ERROR_LIB_INVALID_FUTURE_TYPE => c"Invalid future type", + CassError::CASS_ERROR_LIB_INTERNAL_ERROR => c"Internal error", + CassError::CASS_ERROR_LIB_INVALID_CUSTOM_TYPE => c"Invalid custom type", + CassError::CASS_ERROR_LIB_INVALID_DATA => c"Invalid data", + CassError::CASS_ERROR_LIB_NOT_ENOUGH_DATA => c"Not enough data", + CassError::CASS_ERROR_LIB_INVALID_STATE => c"Invalid state", + CassError::CASS_ERROR_LIB_NO_CUSTOM_PAYLOAD => c"No custom payload", + CassError::CASS_ERROR_LIB_EXECUTION_PROFILE_INVALID => { + c"Invalid execution profile specified" + } + CassError::CASS_ERROR_LIB_NO_TRACING_ID => c"No tracing ID", + CassError::CASS_ERROR_SERVER_SERVER_ERROR => c"Server error", + CassError::CASS_ERROR_SERVER_PROTOCOL_ERROR => c"Protocol error", + CassError::CASS_ERROR_SERVER_BAD_CREDENTIALS => c"Bad credentials", + CassError::CASS_ERROR_SERVER_UNAVAILABLE => c"Unavailable", + CassError::CASS_ERROR_SERVER_OVERLOADED => c"Overloaded", + CassError::CASS_ERROR_SERVER_IS_BOOTSTRAPPING => c"Is bootstrapping", + CassError::CASS_ERROR_SERVER_TRUNCATE_ERROR => c"Truncate error", + CassError::CASS_ERROR_SERVER_WRITE_TIMEOUT => c"Write timeout", + CassError::CASS_ERROR_SERVER_READ_TIMEOUT => c"Read timeout", + CassError::CASS_ERROR_SERVER_READ_FAILURE => c"Read failure", + CassError::CASS_ERROR_SERVER_FUNCTION_FAILURE => c"Function failure", + CassError::CASS_ERROR_SERVER_WRITE_FAILURE => c"Write failure", + CassError::CASS_ERROR_SERVER_SYNTAX_ERROR => c"Syntax error", + CassError::CASS_ERROR_SERVER_UNAUTHORIZED => c"Unauthorized", + CassError::CASS_ERROR_SERVER_INVALID_QUERY => c"Invalid query", + CassError::CASS_ERROR_SERVER_CONFIG_ERROR => c"Configuration error", + CassError::CASS_ERROR_SERVER_ALREADY_EXISTS => c"Already exists", + CassError::CASS_ERROR_SERVER_UNPREPARED => c"Unprepared", + CassError::CASS_ERROR_SSL_INVALID_CERT => c"Unable to load certificate", + CassError::CASS_ERROR_SSL_INVALID_PRIVATE_KEY => c"Unable to load private key", + CassError::CASS_ERROR_SSL_NO_PEER_CERT => c"No peer certificate", + CassError::CASS_ERROR_SSL_INVALID_PEER_CERT => c"Invalid peer certificate", + CassError::CASS_ERROR_SSL_IDENTITY_MISMATCH => { + c"Certificate does not match host or IP address" + } + CassError::CASS_ERROR_SSL_PROTOCOL_ERROR => c"Protocol error", + CassError::CASS_ERROR_SSL_CLOSED => c"Connection closed", + _ => c"", + }; + + desc.as_ptr() +} diff --git a/scylla-rust-wrapper/src/execution_error.rs b/scylla-rust-wrapper/src/execution_error.rs deleted file mode 100644 index 32767163..00000000 --- a/scylla-rust-wrapper/src/execution_error.rs +++ /dev/null @@ -1,428 +0,0 @@ -use crate::argconv::*; -use crate::cass_error::*; -pub use crate::cass_error::{CassError, CassErrorSource}; -use crate::cass_error_types::CassWriteType; -use crate::cass_types::CassConsistency; -use crate::types::*; -use libc::c_char; -use scylla::deserialize::DeserializationError; -use scylla::errors::{DbError, ExecutionError, RequestAttemptError, WriteType}; -use scylla::frame::frame_errors::ResultMetadataAndRowsCountParseError; -use scylla::statement::Consistency; -use thiserror::Error; - -#[derive(Error, Debug)] -pub enum CassErrorResult { - #[error(transparent)] - Execution(#[from] ExecutionError), - #[error(transparent)] - ResultMetadataLazyDeserialization(#[from] ResultMetadataAndRowsCountParseError), - #[error("Failed to deserialize first row: {0}")] - Deserialization(#[from] DeserializationError), -} - -impl FFI for CassErrorResult { - type Origin = FromArc; -} - -impl From for CassConsistency { - fn from(c: Consistency) -> CassConsistency { - match c { - Consistency::Any => CassConsistency::CASS_CONSISTENCY_ANY, - Consistency::One => CassConsistency::CASS_CONSISTENCY_ONE, - Consistency::Two => CassConsistency::CASS_CONSISTENCY_TWO, - Consistency::Three => CassConsistency::CASS_CONSISTENCY_THREE, - Consistency::Quorum => CassConsistency::CASS_CONSISTENCY_QUORUM, - Consistency::All => CassConsistency::CASS_CONSISTENCY_ALL, - Consistency::LocalQuorum => CassConsistency::CASS_CONSISTENCY_LOCAL_QUORUM, - Consistency::EachQuorum => CassConsistency::CASS_CONSISTENCY_EACH_QUORUM, - Consistency::LocalOne => CassConsistency::CASS_CONSISTENCY_LOCAL_ONE, - Consistency::Serial => CassConsistency::CASS_CONSISTENCY_SERIAL, - Consistency::LocalSerial => CassConsistency::CASS_CONSISTENCY_LOCAL_SERIAL, - } - } -} - -impl From<&WriteType> for CassWriteType { - fn from(c: &WriteType) -> CassWriteType { - match c { - WriteType::Simple => CassWriteType::CASS_WRITE_TYPE_SIMPLE, - WriteType::Batch => CassWriteType::CASS_WRITE_TYPE_BATCH, - WriteType::UnloggedBatch => CassWriteType::CASS_WRITE_TYPE_UNLOGGED_BATCH, - WriteType::Counter => CassWriteType::CASS_WRITE_TYPE_COUNTER, - WriteType::BatchLog => CassWriteType::CASS_WRITE_TYPE_BATCH_LOG, - WriteType::Cas => CassWriteType::CASS_WRITE_TYPE_CAS, - WriteType::View => CassWriteType::CASS_WRITE_TYPE_VIEW, - WriteType::Cdc => CassWriteType::CASS_WRITE_TYPE_CDC, - WriteType::Other(_) => CassWriteType::CASS_WRITE_TYPE_UNKNOWN, - } - } -} - -#[unsafe(no_mangle)] -pub unsafe extern "C" fn cass_error_result_free( - error_result: CassOwnedSharedPtr, -) { - ArcFFI::free(error_result); -} - -#[unsafe(no_mangle)] -pub unsafe extern "C" fn cass_error_result_code( - error_result: CassBorrowedSharedPtr, -) -> CassError { - let Some(error_result) = ArcFFI::as_ref(error_result) else { - tracing::error!("Provided null error result pointer to cass_error_result_code!"); - return CassError::CASS_ERROR_LIB_BAD_PARAMS; - }; - - error_result.to_cass_error() -} - -#[unsafe(no_mangle)] -pub unsafe extern "C" fn cass_error_result_consistency( - error_result: CassBorrowedSharedPtr, -) -> CassConsistency { - let Some(error_result) = ArcFFI::as_ref(error_result) else { - tracing::error!("Provided null error result pointer to cass_error_result_consistency!"); - return CassConsistency::CASS_CONSISTENCY_UNKNOWN; - }; - - match error_result { - CassErrorResult::Execution(ExecutionError::LastAttemptError( - RequestAttemptError::DbError(DbError::Unavailable { consistency, .. }, _) - | RequestAttemptError::DbError(DbError::ReadTimeout { consistency, .. }, _) - | RequestAttemptError::DbError(DbError::WriteTimeout { consistency, .. }, _) - | RequestAttemptError::DbError(DbError::ReadFailure { consistency, .. }, _) - | RequestAttemptError::DbError(DbError::WriteFailure { consistency, .. }, _), - )) => CassConsistency::from(*consistency), - _ => CassConsistency::CASS_CONSISTENCY_UNKNOWN, - } -} - -#[unsafe(no_mangle)] -pub unsafe extern "C" fn cass_error_result_responses_received( - error_result: CassBorrowedSharedPtr, -) -> cass_int32_t { - let Some(error_result) = ArcFFI::as_ref(error_result) else { - tracing::error!( - "Provided null error result pointer to cass_error_result_responses_received!" - ); - return -1; - }; - - match error_result { - CassErrorResult::Execution(ExecutionError::LastAttemptError(attempt_error)) => { - match attempt_error { - RequestAttemptError::DbError(DbError::Unavailable { alive, .. }, _) => *alive, - RequestAttemptError::DbError(DbError::ReadTimeout { received, .. }, _) => *received, - RequestAttemptError::DbError(DbError::WriteTimeout { received, .. }, _) => { - *received - } - RequestAttemptError::DbError(DbError::ReadFailure { received, .. }, _) => *received, - RequestAttemptError::DbError(DbError::WriteFailure { received, .. }, _) => { - *received - } - _ => -1, - } - } - _ => -1, - } -} - -#[unsafe(no_mangle)] -pub unsafe extern "C" fn cass_error_result_responses_required( - error_result: CassBorrowedSharedPtr, -) -> cass_int32_t { - let Some(error_result) = ArcFFI::as_ref(error_result) else { - tracing::error!( - "Provided null error result pointer to cass_error_result_responses_required!" - ); - return -1; - }; - - match error_result { - CassErrorResult::Execution(ExecutionError::LastAttemptError(attempt_error)) => { - match attempt_error { - RequestAttemptError::DbError(DbError::Unavailable { required, .. }, _) => *required, - RequestAttemptError::DbError(DbError::ReadTimeout { required, .. }, _) => *required, - RequestAttemptError::DbError(DbError::WriteTimeout { required, .. }, _) => { - *required - } - RequestAttemptError::DbError(DbError::ReadFailure { required, .. }, _) => *required, - RequestAttemptError::DbError(DbError::WriteFailure { required, .. }, _) => { - *required - } - _ => -1, - } - } - _ => -1, - } -} - -#[unsafe(no_mangle)] -pub unsafe extern "C" fn cass_error_result_num_failures( - error_result: CassBorrowedSharedPtr, -) -> cass_int32_t { - let Some(error_result) = ArcFFI::as_ref(error_result) else { - tracing::error!("Provided null error result pointer to cass_error_result_num_failures!"); - return -1; - }; - - match error_result { - CassErrorResult::Execution(ExecutionError::LastAttemptError( - RequestAttemptError::DbError(DbError::ReadFailure { numfailures, .. }, _), - )) => *numfailures, - CassErrorResult::Execution(ExecutionError::LastAttemptError( - RequestAttemptError::DbError(DbError::WriteFailure { numfailures, .. }, _), - )) => *numfailures, - _ => -1, - } -} - -#[unsafe(no_mangle)] -pub unsafe extern "C" fn cass_error_result_data_present( - error_result: CassBorrowedSharedPtr, -) -> cass_bool_t { - let Some(error_result) = ArcFFI::as_ref(error_result) else { - tracing::error!("Provided null error result pointer to cass_error_result_data_present!"); - return cass_false; - }; - - match error_result { - CassErrorResult::Execution(ExecutionError::LastAttemptError( - RequestAttemptError::DbError(DbError::ReadTimeout { data_present, .. }, _), - )) => { - if *data_present { - cass_true - } else { - cass_false - } - } - CassErrorResult::Execution(ExecutionError::LastAttemptError( - RequestAttemptError::DbError(DbError::ReadFailure { data_present, .. }, _), - )) => { - if *data_present { - cass_true - } else { - cass_false - } - } - _ => cass_false, - } -} - -#[unsafe(no_mangle)] -pub unsafe extern "C" fn cass_error_result_write_type( - error_result: CassBorrowedSharedPtr, -) -> CassWriteType { - let Some(error_result) = ArcFFI::as_ref(error_result) else { - tracing::error!("Provided null error result pointer to cass_error_result_write_type!"); - return CassWriteType::CASS_WRITE_TYPE_UNKNOWN; - }; - - match error_result { - CassErrorResult::Execution(ExecutionError::LastAttemptError( - RequestAttemptError::DbError(DbError::WriteTimeout { write_type, .. }, _), - )) => CassWriteType::from(write_type), - CassErrorResult::Execution(ExecutionError::LastAttemptError( - RequestAttemptError::DbError(DbError::WriteFailure { write_type, .. }, _), - )) => CassWriteType::from(write_type), - _ => CassWriteType::CASS_WRITE_TYPE_UNKNOWN, - } -} - -#[unsafe(no_mangle)] -pub unsafe extern "C" fn cass_error_result_keyspace( - error_result: CassBorrowedSharedPtr, - c_keyspace: *mut *const ::std::os::raw::c_char, - c_keyspace_len: *mut size_t, -) -> CassError { - let Some(error_result) = ArcFFI::as_ref(error_result) else { - tracing::error!("Provided null error result pointer to cass_error_result_keyspace!"); - return CassError::CASS_ERROR_LIB_BAD_PARAMS; - }; - - match error_result { - CassErrorResult::Execution(ExecutionError::LastAttemptError( - RequestAttemptError::DbError(DbError::AlreadyExists { keyspace, .. }, _), - )) => { - unsafe { write_str_to_c(keyspace.as_str(), c_keyspace, c_keyspace_len) }; - CassError::CASS_OK - } - CassErrorResult::Execution(ExecutionError::LastAttemptError( - RequestAttemptError::DbError(DbError::FunctionFailure { keyspace, .. }, _), - )) => { - unsafe { write_str_to_c(keyspace.as_str(), c_keyspace, c_keyspace_len) }; - CassError::CASS_OK - } - _ => CassError::CASS_ERROR_LIB_INVALID_ERROR_RESULT_TYPE, - } -} - -#[unsafe(no_mangle)] -pub unsafe extern "C" fn cass_error_result_table( - error_result: CassBorrowedSharedPtr, - c_table: *mut *const ::std::os::raw::c_char, - c_table_len: *mut size_t, -) -> CassError { - let Some(error_result) = ArcFFI::as_ref(error_result) else { - tracing::error!("Provided null error result pointer to cass_error_result_table!"); - return CassError::CASS_ERROR_LIB_BAD_PARAMS; - }; - - match error_result { - CassErrorResult::Execution(ExecutionError::LastAttemptError( - RequestAttemptError::DbError(DbError::AlreadyExists { table, .. }, _), - )) => { - unsafe { write_str_to_c(table.as_str(), c_table, c_table_len) }; - CassError::CASS_OK - } - _ => CassError::CASS_ERROR_LIB_INVALID_ERROR_RESULT_TYPE, - } -} - -#[unsafe(no_mangle)] -pub unsafe extern "C" fn cass_error_result_function( - error_result: CassBorrowedSharedPtr, - c_function: *mut *const ::std::os::raw::c_char, - c_function_len: *mut size_t, -) -> CassError { - let Some(error_result) = ArcFFI::as_ref(error_result) else { - tracing::error!("Provided null error result pointer to cass_error_result_function!"); - return CassError::CASS_ERROR_LIB_BAD_PARAMS; - }; - - match error_result { - CassErrorResult::Execution(ExecutionError::LastAttemptError( - RequestAttemptError::DbError(DbError::FunctionFailure { function, .. }, _), - )) => { - unsafe { write_str_to_c(function.as_str(), c_function, c_function_len) }; - CassError::CASS_OK - } - _ => CassError::CASS_ERROR_LIB_INVALID_ERROR_RESULT_TYPE, - } -} - -#[unsafe(no_mangle)] -pub unsafe extern "C" fn cass_error_num_arg_types( - error_result: CassBorrowedSharedPtr, -) -> size_t { - let Some(error_result) = ArcFFI::as_ref(error_result) else { - tracing::error!("Provided null error result pointer to cass_error_num_arg_types!"); - return 0; - }; - - match error_result { - CassErrorResult::Execution(ExecutionError::LastAttemptError( - RequestAttemptError::DbError(DbError::FunctionFailure { arg_types, .. }, _), - )) => arg_types.len() as size_t, - _ => 0, - } -} - -#[unsafe(no_mangle)] -pub unsafe extern "C" fn cass_error_result_arg_type( - error_result: CassBorrowedSharedPtr, - index: size_t, - arg_type: *mut *const ::std::os::raw::c_char, - arg_type_length: *mut size_t, -) -> CassError { - let Some(error_result) = ArcFFI::as_ref(error_result) else { - tracing::error!("Provided null error result pointer to cass_error_result_arg_type!"); - return CassError::CASS_ERROR_LIB_BAD_PARAMS; - }; - - match error_result { - CassErrorResult::Execution(ExecutionError::LastAttemptError( - RequestAttemptError::DbError(DbError::FunctionFailure { arg_types, .. }, _), - )) => { - if index >= arg_types.len() as size_t { - return CassError::CASS_ERROR_LIB_INDEX_OUT_OF_BOUNDS; - } - unsafe { - write_str_to_c( - arg_types[index as usize].as_str(), - arg_type, - arg_type_length, - ) - }; - CassError::CASS_OK - } - _ => CassError::CASS_ERROR_LIB_INVALID_ERROR_RESULT_TYPE, - } -} - -#[unsafe(no_mangle)] -pub unsafe extern "C" fn cass_error_desc(error: CassError) -> *const c_char { - let desc = match error { - CassError::CASS_ERROR_LIB_BAD_PARAMS => c"Bad parameters", - CassError::CASS_ERROR_LIB_NO_STREAMS => c"No streams available", - CassError::CASS_ERROR_LIB_UNABLE_TO_INIT => c"Unable to initialize", - CassError::CASS_ERROR_LIB_MESSAGE_ENCODE => c"Unable to encode message", - CassError::CASS_ERROR_LIB_HOST_RESOLUTION => c"Unable to resolve host", - CassError::CASS_ERROR_LIB_UNEXPECTED_RESPONSE => c"Unexpected response from server", - CassError::CASS_ERROR_LIB_REQUEST_QUEUE_FULL => c"The request queue is full", - CassError::CASS_ERROR_LIB_NO_AVAILABLE_IO_THREAD => c"No available IO threads", - CassError::CASS_ERROR_LIB_WRITE_ERROR => c"Write error", - CassError::CASS_ERROR_LIB_NO_HOSTS_AVAILABLE => c"No hosts available", - CassError::CASS_ERROR_LIB_INDEX_OUT_OF_BOUNDS => c"Index out of bounds", - CassError::CASS_ERROR_LIB_INVALID_ITEM_COUNT => c"Invalid item count", - CassError::CASS_ERROR_LIB_INVALID_VALUE_TYPE => c"Invalid value type", - CassError::CASS_ERROR_LIB_REQUEST_TIMED_OUT => c"Request timed out", - CassError::CASS_ERROR_LIB_UNABLE_TO_SET_KEYSPACE => c"Unable to set keyspace", - CassError::CASS_ERROR_LIB_CALLBACK_ALREADY_SET => c"Callback already set", - CassError::CASS_ERROR_LIB_INVALID_STATEMENT_TYPE => c"Invalid statement type", - CassError::CASS_ERROR_LIB_NAME_DOES_NOT_EXIST => c"No value or column for name", - CassError::CASS_ERROR_LIB_UNABLE_TO_DETERMINE_PROTOCOL => { - c"Unable to find supported protocol version" - } - CassError::CASS_ERROR_LIB_NULL_VALUE => c"NULL value specified", - CassError::CASS_ERROR_LIB_NOT_IMPLEMENTED => c"Not implemented", - CassError::CASS_ERROR_LIB_UNABLE_TO_CONNECT => c"Unable to connect", - CassError::CASS_ERROR_LIB_UNABLE_TO_CLOSE => c"Unable to close", - CassError::CASS_ERROR_LIB_NO_PAGING_STATE => c"No paging state", - CassError::CASS_ERROR_LIB_PARAMETER_UNSET => c"Parameter unset", - CassError::CASS_ERROR_LIB_INVALID_ERROR_RESULT_TYPE => c"Invalid error result type", - CassError::CASS_ERROR_LIB_INVALID_FUTURE_TYPE => c"Invalid future type", - CassError::CASS_ERROR_LIB_INTERNAL_ERROR => c"Internal error", - CassError::CASS_ERROR_LIB_INVALID_CUSTOM_TYPE => c"Invalid custom type", - CassError::CASS_ERROR_LIB_INVALID_DATA => c"Invalid data", - CassError::CASS_ERROR_LIB_NOT_ENOUGH_DATA => c"Not enough data", - CassError::CASS_ERROR_LIB_INVALID_STATE => c"Invalid state", - CassError::CASS_ERROR_LIB_NO_CUSTOM_PAYLOAD => c"No custom payload", - CassError::CASS_ERROR_LIB_EXECUTION_PROFILE_INVALID => { - c"Invalid execution profile specified" - } - CassError::CASS_ERROR_LIB_NO_TRACING_ID => c"No tracing ID", - CassError::CASS_ERROR_SERVER_SERVER_ERROR => c"Server error", - CassError::CASS_ERROR_SERVER_PROTOCOL_ERROR => c"Protocol error", - CassError::CASS_ERROR_SERVER_BAD_CREDENTIALS => c"Bad credentials", - CassError::CASS_ERROR_SERVER_UNAVAILABLE => c"Unavailable", - CassError::CASS_ERROR_SERVER_OVERLOADED => c"Overloaded", - CassError::CASS_ERROR_SERVER_IS_BOOTSTRAPPING => c"Is bootstrapping", - CassError::CASS_ERROR_SERVER_TRUNCATE_ERROR => c"Truncate error", - CassError::CASS_ERROR_SERVER_WRITE_TIMEOUT => c"Write timeout", - CassError::CASS_ERROR_SERVER_READ_TIMEOUT => c"Read timeout", - CassError::CASS_ERROR_SERVER_READ_FAILURE => c"Read failure", - CassError::CASS_ERROR_SERVER_FUNCTION_FAILURE => c"Function failure", - CassError::CASS_ERROR_SERVER_WRITE_FAILURE => c"Write failure", - CassError::CASS_ERROR_SERVER_SYNTAX_ERROR => c"Syntax error", - CassError::CASS_ERROR_SERVER_UNAUTHORIZED => c"Unauthorized", - CassError::CASS_ERROR_SERVER_INVALID_QUERY => c"Invalid query", - CassError::CASS_ERROR_SERVER_CONFIG_ERROR => c"Configuration error", - CassError::CASS_ERROR_SERVER_ALREADY_EXISTS => c"Already exists", - CassError::CASS_ERROR_SERVER_UNPREPARED => c"Unprepared", - CassError::CASS_ERROR_SSL_INVALID_CERT => c"Unable to load certificate", - CassError::CASS_ERROR_SSL_INVALID_PRIVATE_KEY => c"Unable to load private key", - CassError::CASS_ERROR_SSL_NO_PEER_CERT => c"No peer certificate", - CassError::CASS_ERROR_SSL_INVALID_PEER_CERT => c"Invalid peer certificate", - CassError::CASS_ERROR_SSL_IDENTITY_MISMATCH => { - c"Certificate does not match host or IP address" - } - CassError::CASS_ERROR_SSL_PROTOCOL_ERROR => c"Protocol error", - CassError::CASS_ERROR_SSL_CLOSED => c"Connection closed", - _ => c"", - }; - - desc.as_ptr() -} diff --git a/scylla-rust-wrapper/src/future.rs b/scylla-rust-wrapper/src/future.rs index dd8d9a5b..7034476c 100644 --- a/scylla-rust-wrapper/src/future.rs +++ b/scylla-rust-wrapper/src/future.rs @@ -1,9 +1,6 @@ use crate::RUNTIME; use crate::argconv::*; -use crate::cass_error::CassError; -use crate::cass_error::CassErrorMessage; -use crate::cass_error::ToCassError; -use crate::execution_error::CassErrorResult; +use crate::cass_error::{CassError, CassErrorMessage, CassErrorResult, ToCassError as _}; use crate::prepared::CassPrepared; use crate::query_result::{CassNode, CassResult}; use crate::types::*; diff --git a/scylla-rust-wrapper/src/lib.rs b/scylla-rust-wrapper/src/lib.rs index 621f1027..537bc741 100644 --- a/scylla-rust-wrapper/src/lib.rs +++ b/scylla-rust-wrapper/src/lib.rs @@ -19,7 +19,6 @@ pub(crate) mod collection; pub(crate) mod config_value; pub(crate) mod date_time; pub(crate) mod exec_profile; -pub(crate) mod execution_error; pub(crate) mod future; pub(crate) mod inet; #[cfg(cpp_integration_testing)] diff --git a/scylla-rust-wrapper/src/query_result.rs b/scylla-rust-wrapper/src/query_result.rs index 06fe47a3..a9339553 100644 --- a/scylla-rust-wrapper/src/query_result.rs +++ b/scylla-rust-wrapper/src/query_result.rs @@ -1,11 +1,11 @@ use crate::argconv::*; +use crate::cass_error::CassErrorResult; use crate::cass_error::{CassError, ToCassError}; pub use crate::cass_types::CassValueType; use crate::cass_types::{ CassColumnSpec, CassDataType, CassDataTypeInner, MapDataType, cass_data_type_type, get_column_type, }; -use crate::execution_error::CassErrorResult; use crate::inet::CassInet; use crate::types::*; use crate::uuid::CassUuid; @@ -229,7 +229,7 @@ mod row_with_self_borrowed_result_data { use scylla::errors::DeserializationError; use yoke::{Yoke, Yokeable}; - use crate::execution_error::CassErrorResult; + use crate::cass_error::CassErrorResult; use crate::query_result::CassRawRow; use super::{CassRow, CassRowsResultSharedData}; From 70809012f3c164623202d1747db55a7701c8f5a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojciech=20Przytu=C5=82a?= Date: Mon, 28 Jul 2025 06:41:18 +0200 Subject: [PATCH 2/7] cass_error: return INVALID_DATA where appropriate `CASS_ERROR_LIB_INVALID_DATA` is returned in CPP Driver in cases where the CQL values to be serialized/deserialized are not valid. OTOH, `CASS_ERROR_LIB_UNEXPECTED_RESPONSE` is used when the message is valid in terms of binary protocol, but the driver does not expect it as a response in the current context. I decided that errors in frame deserialization warrant `INVALID_DATA` more then `UNEXPECTED_RESPONSE`. --- scylla-rust-wrapper/src/cass_error.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/scylla-rust-wrapper/src/cass_error.rs b/scylla-rust-wrapper/src/cass_error.rs index ca0641f1..7273db1c 100644 --- a/scylla-rust-wrapper/src/cass_error.rs +++ b/scylla-rust-wrapper/src/cass_error.rs @@ -90,14 +90,10 @@ impl ToCassError for RequestAttemptError { CassError::CASS_ERROR_LIB_NO_HOSTS_AVAILABLE } RequestAttemptError::BodyExtensionsParseError(_) => { - CassError::CASS_ERROR_LIB_MESSAGE_ENCODE - } - RequestAttemptError::CqlResultParseError(_) => { - CassError::CASS_ERROR_LIB_UNEXPECTED_RESPONSE - } - RequestAttemptError::CqlErrorParseError(_) => { - CassError::CASS_ERROR_LIB_UNEXPECTED_RESPONSE + CassError::CASS_ERROR_LIB_INVALID_DATA } + RequestAttemptError::CqlResultParseError(_) => CassError::CASS_ERROR_LIB_INVALID_DATA, + RequestAttemptError::CqlErrorParseError(_) => CassError::CASS_ERROR_LIB_INVALID_DATA, RequestAttemptError::DbError(db_error, _) => db_error.to_cass_error(), RequestAttemptError::UnexpectedResponse(_) => { CassError::CASS_ERROR_LIB_UNEXPECTED_RESPONSE From 91403855eab58bda4a4e527456d254886cd14692 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojciech=20Przytu=C5=82a?= Date: Mon, 28 Jul 2025 06:48:10 +0200 Subject: [PATCH 3/7] cass_error: stop abusing server protocol error `CassError` is split into categories, by the issuer of the error: - `CASS_ERROR_LIB_*` for errors from the driver, - `CASS_ERROR_SERVER_*` for errors from the server, - `CASS_ERROR_SSL_*` for errors from the SSL library. The `CASS_ERROR_SERVER_PROTOCOL_ERROR` was used for errors that are not from the server, but from the driver, which is not correct. Among other things, it confuses users of the library, who expect SERVER errors to have come from the server, but they are actually from the driver. To fix this, such usages of the error were replaced with `CASS_ERROR_LIB_INVALID_STATE`. --- scylla-rust-wrapper/src/cass_error.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/scylla-rust-wrapper/src/cass_error.rs b/scylla-rust-wrapper/src/cass_error.rs index 7273db1c..e369f996 100644 --- a/scylla-rust-wrapper/src/cass_error.rs +++ b/scylla-rust-wrapper/src/cass_error.rs @@ -99,14 +99,12 @@ impl ToCassError for RequestAttemptError { CassError::CASS_ERROR_LIB_UNEXPECTED_RESPONSE } RequestAttemptError::RepreparedIdChanged { .. } => { - CassError::CASS_ERROR_SERVER_PROTOCOL_ERROR + CassError::CASS_ERROR_LIB_INVALID_STATE } RequestAttemptError::RepreparedIdMissingInBatch => { - CassError::CASS_ERROR_SERVER_PROTOCOL_ERROR - } - RequestAttemptError::NonfinishedPagingState => { - CassError::CASS_ERROR_SERVER_PROTOCOL_ERROR + CassError::CASS_ERROR_LIB_INVALID_STATE } + RequestAttemptError::NonfinishedPagingState => CassError::CASS_ERROR_LIB_INVALID_STATE, // RequestAttemptError is non_exhaustive. _ => CassError::CASS_ERROR_LAST_ENTRY, From a3c6827b696293a2c84aa57a9b122f37455448d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojciech=20Przytu=C5=82a?= Date: Mon, 28 Jul 2025 12:11:17 +0200 Subject: [PATCH 4/7] cass_error: BadQuery -> MESSAGE_ENCODE conversion I believe that the `BadQuery` enum variants that were previously returning `CASS_ERROR_LAST_ENTRY` (as an obvious placeholder) should instead return `CASS_ERROR_LIB_MESSAGE_ENCODE`. This change is made to ensure that the error handling is more specific and meaningful, particularly for cases where the query is malformed or exceeds certain limits. I understand `CASS_ERROR_LIB_MESSAGE_ENCODE` as a general error for issues around serialization. --- scylla-rust-wrapper/src/cass_error.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/scylla-rust-wrapper/src/cass_error.rs b/scylla-rust-wrapper/src/cass_error.rs index e369f996..2fd97d4c 100644 --- a/scylla-rust-wrapper/src/cass_error.rs +++ b/scylla-rust-wrapper/src/cass_error.rs @@ -147,13 +147,13 @@ impl ToCassError for DbError { impl ToCassError for BadQuery { fn to_cass_error(&self) -> CassError { match self { - BadQuery::ValuesTooLongForKey(_usize, _usize2) => CassError::CASS_ERROR_LAST_ENTRY, - BadQuery::PartitionKeyExtraction => CassError::CASS_ERROR_LAST_ENTRY, + BadQuery::ValuesTooLongForKey(_usize, _usize2) => { + CassError::CASS_ERROR_LIB_MESSAGE_ENCODE + } + BadQuery::PartitionKeyExtraction => CassError::CASS_ERROR_LIB_MESSAGE_ENCODE, BadQuery::SerializationError(e) => e.to_cass_error(), - BadQuery::TooManyQueriesInBatchStatement(_) => CassError::CASS_ERROR_LAST_ENTRY, + BadQuery::TooManyQueriesInBatchStatement(_) => CassError::CASS_ERROR_LIB_MESSAGE_ENCODE, // BadQuery is non_exhaustive - // For now, since all other variants return LAST_ENTRY, - // let's do it here as well. _ => CassError::CASS_ERROR_LAST_ENTRY, } } From 3f9351b20ee36191d152cd5f1c1d6f37828f46ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojciech=20Przytu=C5=82a?= Date: Mon, 28 Jul 2025 12:14:20 +0200 Subject: [PATCH 5/7] cass_error: MetadataError better conversion `MetadataFetchErrorKind::NextRowError` was previously always converted to `CassError::CASS_ERROR_LIB_UNEXPECTED_RESPONSE`, which is not appropriate for all cases. This commit refactors the conversion to handle different cases of `NextRowError` more appropriately, depending on the specific error encountered. --- scylla-rust-wrapper/src/cass_error.rs | 39 ++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/scylla-rust-wrapper/src/cass_error.rs b/scylla-rust-wrapper/src/cass_error.rs index 2fd97d4c..fed9f6fe 100644 --- a/scylla-rust-wrapper/src/cass_error.rs +++ b/scylla-rust-wrapper/src/cass_error.rs @@ -6,8 +6,8 @@ use libc::c_char; use scylla::deserialize::DeserializationError; use scylla::errors::{ BadKeyspaceName, BadQuery, ConnectionPoolError, DbError, ExecutionError, MetadataError, - MetadataFetchErrorKind, NewSessionError, PrepareError, RequestAttemptError, SerializationError, - WriteType, + MetadataFetchErrorKind, NewSessionError, NextRowError, PrepareError, RequestAttemptError, + RequestError, SerializationError, WriteType, }; use scylla::frame::frame_errors::ResultMetadataAndRowsCountParseError; use scylla::statement::Consistency; @@ -187,8 +187,39 @@ impl ToCassError for MetadataError { } MetadataFetchErrorKind::PrepareError(e) => e.to_cass_error(), MetadataFetchErrorKind::SerializationError(e) => e.to_cass_error(), - MetadataFetchErrorKind::NextRowError(_) => { - CassError::CASS_ERROR_LIB_UNEXPECTED_RESPONSE + MetadataFetchErrorKind::NextRowError(e) => { + match e { + // CassError::CASS_ERROR_LIB_UNEXPECTED_RESPONSE + NextRowError::NextPageError(next_page_error) => match next_page_error { + scylla::errors::NextPageError::PartitionKeyError(_) => { + CassError::CASS_ERROR_LIB_MESSAGE_ENCODE + } + scylla::errors::NextPageError::RequestFailure(e) => { + match e { + RequestError::EmptyPlan => { + CassError::CASS_ERROR_LIB_NO_HOSTS_AVAILABLE + } + RequestError::ConnectionPoolError(e) => e.to_cass_error(), + RequestError::RequestTimeout(_) => { + CassError::CASS_ERROR_LIB_REQUEST_TIMED_OUT + } + RequestError::LastAttemptError(e) => e.to_cass_error(), + // non_exhaustive + _ => CassError::CASS_ERROR_LAST_ENTRY, + } + } + scylla::errors::NextPageError::ResultMetadataParseError(_) => { + CassError::CASS_ERROR_LIB_INVALID_DATA + } + // non_exhaustive + _ => CassError::CASS_ERROR_LAST_ENTRY, + }, + NextRowError::RowDeserializationError(_) => { + CassError::CASS_ERROR_LIB_INVALID_DATA + } + // non_exhaustive + _ => CassError::CASS_ERROR_LAST_ENTRY, + } } // non_exhaustive From fd0131a796b683bb95a14e2916ff883209f7ad3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojciech=20Przytu=C5=82a?= Date: Mon, 28 Jul 2025 16:30:55 +0200 Subject: [PATCH 6/7] cass_error: BadKeyspaceName -> UNABLE_TO_SET_KEYSPACE This seems to be the most natural mapping for the `BadKeyspaceName` error. --- scylla-rust-wrapper/src/cass_error.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/scylla-rust-wrapper/src/cass_error.rs b/scylla-rust-wrapper/src/cass_error.rs index fed9f6fe..983af3d0 100644 --- a/scylla-rust-wrapper/src/cass_error.rs +++ b/scylla-rust-wrapper/src/cass_error.rs @@ -240,9 +240,11 @@ impl ToCassError for MetadataError { impl ToCassError for BadKeyspaceName { fn to_cass_error(&self) -> CassError { match self { - BadKeyspaceName::Empty => CassError::CASS_ERROR_LAST_ENTRY, - BadKeyspaceName::TooLong(_string, _usize) => CassError::CASS_ERROR_LAST_ENTRY, - BadKeyspaceName::IllegalCharacter(_string, _char) => CassError::CASS_ERROR_LAST_ENTRY, + BadKeyspaceName::Empty + | BadKeyspaceName::TooLong(_, _) + | BadKeyspaceName::IllegalCharacter(_, _) => { + CassError::CASS_ERROR_LIB_UNABLE_TO_SET_KEYSPACE + } // non_exhaustive _ => CassError::CASS_ERROR_LAST_ENTRY, } From 3ab31592e4e4a5e7d23c24cc6017a4d04a51b03c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojciech=20Przytu=C5=82a?= Date: Mon, 28 Jul 2025 16:31:38 +0200 Subject: [PATCH 7/7] cass_error: SerializationError -> INVALID_DATA INVALID_DATA seems to be an appropriate error code for serialization errors. --- scylla-rust-wrapper/src/cass_error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scylla-rust-wrapper/src/cass_error.rs b/scylla-rust-wrapper/src/cass_error.rs index 983af3d0..fb05a183 100644 --- a/scylla-rust-wrapper/src/cass_error.rs +++ b/scylla-rust-wrapper/src/cass_error.rs @@ -257,7 +257,7 @@ impl ToCassError for SerializationError { // It means that our custom `UnknownNamedParameterError` was returned. CassError::CASS_ERROR_LIB_NAME_DOES_NOT_EXIST } else { - CassError::CASS_ERROR_LAST_ENTRY + CassError::CASS_ERROR_LIB_INVALID_DATA } } }