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..fb05a183 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, NextRowError, PrepareError, RequestAttemptError, + RequestError, 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 { @@ -78,27 +90,21 @@ 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 } 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, @@ -141,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, } } @@ -181,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 @@ -203,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, } @@ -218,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 } } } @@ -274,3 +313,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};