use std::error::Error;
use std::io::ErrorKind;
use std::net::{AddrParseError, IpAddr, SocketAddr};
use std::num::ParseIntError;
use std::sync::Arc;
use thiserror::Error;
use uuid::Uuid;
use crate::frame::response;
pub use crate::client::pager::{NextPageError, NextRowError};
use crate::statement::prepared::TokenCalculationError;
pub use crate::response::query_result::{
FirstRowError, IntoRowsResultError, MaybeFirstRowError, ResultNotRowsError, RowsError,
SingleRowError,
};
pub use crate::authentication::AuthError;
pub use crate::network::tls::TlsError;
pub use scylla_cql::deserialize::{DeserializationError, TypeCheckError};
pub use scylla_cql::frame::frame_errors::{
CqlAuthChallengeParseError, CqlAuthSuccessParseError, CqlAuthenticateParseError,
CqlErrorParseError, CqlEventParseError, CqlRequestSerializationError, CqlResponseParseError,
CqlResultParseError, CqlSupportedParseError, FrameBodyExtensionsParseError,
FrameHeaderParseError,
};
pub use scylla_cql::frame::request::CqlRequestKind;
pub use scylla_cql::frame::response::CqlResponseKind;
pub use scylla_cql::frame::response::error::{DbError, OperationType, WriteType};
pub use scylla_cql::serialize::SerializationError;
#[derive(Error, Debug, Clone)]
#[non_exhaustive]
pub enum ExecutionError {
#[error(transparent)]
BadQuery(#[from] BadQuery),
#[error(
"Load balancing policy returned an empty plan. \
If you are using DefaultPolicy, ensure that you have not selected a nonexistent datacenter as preferred. \
If you are using a custom LBP implementation, ensure that your LBP implementation is correct. \
If neither of the above suggestions is the cause, then this is most likely a driver bug!"
)]
EmptyPlan,
#[error("Failed to prepare the statement: {0}")]
PrepareError(#[from] PrepareError),
#[error("No connections in the pool: {0}")]
ConnectionPoolError(#[from] ConnectionPoolError),
#[error(transparent)]
LastAttemptError(#[from] RequestAttemptError),
#[error(
"Request execution exceeded a client timeout of {}ms",
std::time::Duration::as_millis(.0)
)]
RequestTimeout(std::time::Duration),
#[error("'USE KEYSPACE <>' request failed: {0}")]
UseKeyspaceError(#[from] UseKeyspaceError),
#[error("Failed to await schema agreement: {0}")]
SchemaAgreementError(#[from] SchemaAgreementError),
#[error("Cluster metadata fetch error occurred during automatic schema agreement: {0}")]
MetadataError(#[from] MetadataError),
}
impl From<SerializationError> for ExecutionError {
fn from(serialized_err: SerializationError) -> ExecutionError {
ExecutionError::BadQuery(BadQuery::SerializationError(serialized_err))
}
}
#[derive(Error, Debug, Clone)]
#[non_exhaustive]
pub enum PrepareError {
#[error("Failed to find a node with working connection pool: {0}")]
ConnectionPoolError(#[from] ConnectionPoolError),
#[error(
"Preparation failed on every connection from the selected pool. First attempt error: {first_attempt}"
)]
AllAttemptsFailed {
first_attempt: RequestAttemptError,
},
#[error(
"Prepared statement id mismatch between multiple connections - all result ids should be equal."
)]
PreparedStatementIdsMismatch,
}
#[derive(Error, Debug, Clone)]
#[non_exhaustive]
#[expect(clippy::enum_variant_names)]
pub enum PagerExecutionError {
#[error("Failed to prepare the statement to be used by the pager: {0}")]
PrepareError(#[from] PrepareError),
#[error("Failed to serialize statement parameters: {0}")]
SerializationError(#[from] SerializationError),
#[error("Failed to fetch the first page of the result: {0}")]
NextPageError(#[from] NextPageError),
#[error("'USE KEYSPACE <>' request failed: {0}")]
UseKeyspaceError(#[from] UseKeyspaceError),
#[error("Failed to await schema agreement: {0}")]
SchemaAgreementError(#[from] SchemaAgreementError),
#[error("Failed when refreshing metadata after schema agreement was reached: {0}")]
MetadataError(#[from] MetadataError),
}
#[derive(Error, Debug, Clone)]
#[non_exhaustive]
pub enum NewSessionError {
#[error("Couldn't resolve any hostname: {0:?}")]
FailedToResolveAnyHostname(Vec<String>),
#[error("Empty known nodes list")]
EmptyKnownNodesList,
#[error("Failed to perform initial cluster metadata fetch: {0}")]
MetadataError(#[from] MetadataError),
#[error("'USE KEYSPACE <>' request failed: {0}")]
UseKeyspaceError(#[from] UseKeyspaceError),
#[error("Provided combination of Session configuration options is unsupported: {0}")]
IllegalConfig(Box<str>),
}
#[derive(Error, Debug, Clone)]
#[non_exhaustive]
pub enum UseKeyspaceError {
#[error("Passed invalid keyspace name to use: {0}")]
BadKeyspaceName(#[from] BadKeyspaceName),
#[error(transparent)]
RequestError(#[from] RequestAttemptError),
#[error(
"Keyspace name mismatch; expected: {expected_keyspace_name_lowercase}, received: {result_keyspace_name_lowercase}"
)]
KeyspaceNameMismatch {
expected_keyspace_name_lowercase: String,
result_keyspace_name_lowercase: String,
},
#[error(
"Request execution exceeded a client timeout of {}ms",
std::time::Duration::as_millis(.0)
)]
RequestTimeout(std::time::Duration),
}
#[derive(Error, Debug, Clone)]
#[non_exhaustive]
pub enum SchemaAgreementError {
#[error("Failed to find a node with working connection pool: {0}")]
ConnectionPoolError(#[from] ConnectionPoolError),
#[error("Failed to prepare schema version query: {0}")]
PrepareError(#[from] PrepareError),
#[error("Failed to execute schema version query: {0}")]
RequestError(#[from] RequestAttemptError),
#[error("Failed to convert schema version query result into rows result: {0}")]
TracesEventsIntoRowsResultError(IntoRowsResultError),
#[error(transparent)]
SingleRowError(SingleRowError),
#[error("Schema agreement exceeded {}ms", std::time::Duration::as_millis(.0))]
Timeout(std::time::Duration),
#[error(
"Host with id {} required for schema agreement is not present in connection pool",
0
)]
RequiredHostAbsent(Uuid),
}
#[derive(Error, Debug, Clone)]
#[non_exhaustive]
pub enum TracingError {
#[error(
"Failed to execute queries to \"system_traces.sessions\" or \"system_traces.events\" system tables: {0}"
)]
ExecutionError(#[from] ExecutionError),
#[error("Failed to prepare tracing query: {0}")]
PrepareError(#[from] PrepareError),
#[error("Failed to convert result of system_traces.session query to rows result")]
TracesSessionIntoRowsResultError(IntoRowsResultError),
#[error("system_traces.session has invalid column type: {0}")]
TracesSessionInvalidColumnType(TypeCheckError),
#[error("Response to system_traces.session failed to deserialize: {0}")]
TracesSessionDeserializationFailed(DeserializationError),
#[error("Failed to convert result of system_traces.events query to rows result")]
TracesEventsIntoRowsResultError(IntoRowsResultError),
#[error("system_traces.events has invalid column type: {0}")]
TracesEventsInvalidColumnType(TypeCheckError),
#[error("Response to system_traces.events failed to deserialize: {0}")]
TracesEventsDeserializationFailed(DeserializationError),
#[error(
"All tracing queries returned an empty result, \
maybe the trace information didn't propagate yet. \
Consider configuring Session with \
a longer fetch interval (tracing_info_fetch_interval)"
)]
EmptyResults,
}
#[derive(Error, Debug, Clone)]
#[non_exhaustive]
pub enum MetadataError {
#[error("Control connection pool error: {0}")]
ConnectionPoolError(#[from] ConnectionPoolError),
#[error(transparent)]
FetchError(#[from] MetadataFetchError),
#[error("Bad peers metadata: {0}")]
Peers(#[from] PeersMetadataError),
#[error("Bad keyspaces metadata: {0}")]
Keyspaces(#[from] KeyspacesMetadataError),
#[error("Bad UDTs metadata: {0}")]
Udts(#[from] UdtMetadataError),
#[error("Bad tables metadata: {0}")]
Tables(#[from] TablesMetadataError),
#[error("Bad client routes metadata: {0}")]
ClientRoutes(#[from] ClientRoutesMetadataError),
}
#[derive(Error, Debug, Clone)]
#[error("Metadata fetch failed for table \"{table}\": {error}")]
#[non_exhaustive]
pub struct MetadataFetchError {
pub error: MetadataFetchErrorKind,
pub table: &'static str,
}
#[derive(Error, Debug, Clone)]
#[non_exhaustive]
pub enum MetadataFetchErrorKind {
#[error("The table has invalid column type: {0}")]
InvalidColumnType(#[from] TypeCheckError),
#[error("Failed to prepare the statement: {0}")]
PrepareError(#[from] RequestAttemptError),
#[error("Failed to serialize statement parameters: {0}")]
SerializationError(#[from] SerializationError),
#[error("Failed to obtain next row from response to the query: {0}")]
NextRowError(#[from] NextRowError),
}
#[derive(Error, Debug, Clone)]
#[non_exhaustive]
pub enum PeersMetadataError {
#[error("Peers list is empty")]
EmptyPeers,
#[error("All peers have empty token lists")]
EmptyTokenLists,
}
#[derive(Error, Debug, Clone)]
#[non_exhaustive]
pub enum KeyspacesMetadataError {
#[error("Bad keyspace <{keyspace}> replication strategy: {error}")]
Strategy {
keyspace: String,
error: KeyspaceStrategyError,
},
}
#[derive(Error, Debug, Clone)]
#[non_exhaustive]
pub enum KeyspaceStrategyError {
#[error("keyspace strategy definition is missing a 'class' field")]
MissingClassForStrategyDefinition,
#[error("Missing replication factor field for SimpleStrategy")]
MissingReplicationFactorForSimpleStrategy,
#[error("Failed to parse a replication factor as unsigned integer: {0}")]
ReplicationFactorParseError(ParseIntError),
#[error("Unexpected NetworkTopologyStrategy option: '{key}': '{value}'")]
UnexpectedNetworkTopologyStrategyOption {
key: String,
value: String,
},
}
#[derive(Error, Debug, Clone)]
#[non_exhaustive]
pub enum UdtMetadataError {
#[error(
"Failed to parse a CQL type returned from system_schema.types query. \
Type '{typ}', at position {position}: {reason}"
)]
InvalidCqlType {
typ: String,
position: usize,
reason: String,
},
#[error("Detected circular dependency between user defined types - toposort is impossible!")]
CircularTypeDependency,
}
#[derive(Error, Debug, Clone)]
#[non_exhaustive]
pub enum TablesMetadataError {
#[error(
"Failed to parse a CQL type returned from system_schema.columns query. \
Type '{typ}', at position {position}: {reason}"
)]
InvalidCqlType {
typ: String,
position: usize,
reason: String,
},
#[error("Unknown column kind '{column_kind}' for {keyspace_name}.{table_name}.{column_name}")]
UnknownColumnKind {
keyspace_name: String,
table_name: String,
column_name: String,
column_kind: String,
},
}
#[derive(Error, Debug, Clone)]
#[non_exhaustive]
pub enum ClientRoutesMetadataError {
#[error("Invalid port value: {port}, is TLS port? {is_tls}")]
InvalidPortValue {
port: i32,
is_tls: bool,
},
}
#[derive(Error, Debug, Clone)]
#[error("Invalid statement passed to Session")]
#[non_exhaustive]
pub enum BadQuery {
#[error("Unable extract a partition key based on prepared statement's metadata")]
PartitionKeyExtraction,
#[error("Serializing values failed: {0} ")]
SerializationError(#[from] SerializationError),
#[error(
"Serialized values are too long to compute partition key! Length: {0}, Max allowed length: {1}"
)]
ValuesTooLongForKey(usize, usize),
#[error(
"Number of statements in Batch Statement supplied is {0} which has exceeded the max value of 65,535"
)]
TooManyQueriesInBatchStatement(usize),
}
#[derive(Debug, Error, Clone)]
#[non_exhaustive]
pub enum BadKeyspaceName {
#[error("Keyspace name is empty")]
Empty,
#[error(
"Keyspace name too long, must be up to 48 characters, found {1} characters. Bad keyspace name: '{0}'"
)]
TooLong(String, usize),
#[error(
"Illegal character found: '{1}', only alphanumeric and underscores allowed. Bad keyspace name: '{0}'"
)]
IllegalCharacter(String, char),
}
#[derive(Error, Debug, Clone)]
#[non_exhaustive]
pub enum ConnectionPoolError {
#[error("The pool is broken; Last connection failed with: {last_connection_error}")]
Broken {
last_connection_error: ConnectionError,
},
#[error("Pool is still being initialized")]
Initializing,
#[error("The node has been disabled by a host filter")]
NodeDisabledByHostFilter,
}
#[derive(Error, Debug, Clone)]
#[non_exhaustive]
pub enum ConnectionError {
#[error("Connect timeout elapsed")]
ConnectTimeout,
#[error(transparent)]
IoError(Arc<std::io::Error>),
#[error("Could not find free source port for shard {0}")]
NoSourcePortForShard(u32),
#[error("Address translation failed: {0}")]
TranslationError(#[from] TranslationError),
#[error(transparent)]
BrokenConnection(#[from] BrokenConnectionError),
#[error(transparent)]
ConnectionSetupRequestError(#[from] ConnectionSetupRequestError),
}
impl From<std::io::Error> for ConnectionError {
fn from(value: std::io::Error) -> Self {
ConnectionError::IoError(Arc::new(value))
}
}
impl ConnectionError {
pub fn is_address_unavailable_for_use(&self) -> bool {
if let ConnectionError::IoError(io_error) = self {
match io_error.kind() {
ErrorKind::AddrInUse | ErrorKind::PermissionDenied => return true,
_ => {}
}
}
false
}
}
#[derive(Debug, Clone, Error)]
#[error("Custom translation error: {0}")]
pub struct CustomTranslationError(Arc<dyn Error + Send + Sync>);
impl CustomTranslationError {
#[inline]
pub fn new(err: impl Error + Send + Sync + 'static) -> CustomTranslationError {
CustomTranslationError(Arc::new(err))
}
pub fn downcast_ref<T: Error + 'static>(&self) -> Option<&T> {
self.0.downcast_ref()
}
}
#[non_exhaustive]
#[derive(Debug, Clone, Error)]
pub enum TranslationError {
#[error("No rule for address {0}")]
NoRuleForAddress(SocketAddr),
#[error("No rule for host with id {0}")]
NoRuleForHost(Uuid),
#[error("Failed to parse translated address: {translated_addr_str}, reason: {reason}")]
InvalidAddressInRule {
translated_addr_str: &'static str,
reason: AddrParseError,
},
#[error("An I/O error occurred during address translation: {0}")]
IoError(Arc<std::io::Error>),
#[error("DNS lookup failed: {0}")]
DnsLookupFailed(DnsLookupError),
#[error("Missing port for host {0} in system.client_routes")]
MissingPortForHost(Uuid),
#[error(transparent)]
Custom(#[from] CustomTranslationError),
}
#[derive(Error, Debug, Clone)]
#[non_exhaustive]
pub enum DnsLookupError {
#[error("Failed to perform DNS lookup within {0}ms")]
Timeout(u128),
#[error("Empty address list returned by DNS for {0}")]
EmptyAddressListForHost(Box<str>),
#[error("An I/O error occurred during DNS lookup: {0}")]
IoError(Arc<std::io::Error>),
}
#[derive(Error, Debug, Clone)]
#[error("Failed to perform a connection setup request. Request: {request_kind}, reason: {error}")]
#[non_exhaustive]
pub struct ConnectionSetupRequestError {
pub request_kind: CqlRequestKind,
pub error: ConnectionSetupRequestErrorKind,
}
#[derive(Error, Debug, Clone)]
#[non_exhaustive]
pub enum ConnectionSetupRequestErrorKind {
#[error("Failed to serialize CQL request: {0}")]
CqlRequestSerialization(#[from] CqlRequestSerializationError),
#[error(transparent)]
BodyExtensionsParseError(#[from] FrameBodyExtensionsParseError),
#[error("Unable to allocate stream id")]
UnableToAllocStreamId,
#[error(transparent)]
BrokenConnection(#[from] BrokenConnectionError),
#[error("Database returned an error: {0}, Error message: {1}")]
DbError(DbError, String),
#[error("Received unexpected response from the server: {0}")]
UnexpectedResponse(CqlResponseKind),
#[error("Failed to deserialize SUPPORTED response: {0}")]
CqlSupportedParseError(#[from] CqlSupportedParseError),
#[error("Failed to deserialize AUTHENTICATE response: {0}")]
CqlAuthenticateParseError(#[from] CqlAuthenticateParseError),
#[error("Failed to deserialize AUTH_SUCCESS response: {0}")]
CqlAuthSuccessParseError(#[from] CqlAuthSuccessParseError),
#[error("Failed to deserialize AUTH_CHALLENGE response: {0}")]
CqlAuthChallengeParseError(#[from] CqlAuthChallengeParseError),
#[error("Failed to deserialize ERROR response: {0}")]
CqlErrorParseError(#[from] CqlErrorParseError),
#[error("Failed to start client's auth session: {0}")]
StartAuthSessionError(AuthError),
#[error("Failed to evaluate auth challenge on client side: {0}")]
AuthChallengeEvaluationError(AuthError),
#[error("Failed to finish auth challenge on client side: {0}")]
AuthFinishError(AuthError),
#[error(
"Authentication is required. You can use SessionBuilder::user(\"user\", \"pass\") to provide credentials or SessionBuilder::authenticator_provider to provide custom authenticator"
)]
MissingAuthentication,
}
impl ConnectionSetupRequestError {
pub(crate) fn new(
request_kind: CqlRequestKind,
error: ConnectionSetupRequestErrorKind,
) -> Self {
ConnectionSetupRequestError {
request_kind,
error,
}
}
pub fn get_error(&self) -> &ConnectionSetupRequestErrorKind {
&self.error
}
}
#[derive(Error, Debug, Clone)]
#[error("Connection broken, reason: {0}")]
pub struct BrokenConnectionError(Arc<dyn Error + Sync + Send>);
impl BrokenConnectionError {
pub fn downcast_ref<T: Error + 'static>(&self) -> Option<&T> {
self.0.downcast_ref()
}
}
#[derive(Error, Debug)]
#[non_exhaustive]
pub enum BrokenConnectionErrorKind {
#[error("Timed out while waiting for response to keepalive request on connection to node {0}")]
KeepaliveTimeout(IpAddr),
#[error("Failed to execute keepalive request: {0}")]
KeepaliveRequestError(Arc<dyn Error + Sync + Send>),
#[error("Failed to deserialize frame: {0}")]
FrameHeaderParseError(FrameHeaderParseError),
#[error("Failed to handle server event: {0}")]
CqlEventHandlingError(#[from] CqlEventHandlingError),
#[error("Received a server frame with unexpected stream id: {0}")]
UnexpectedStreamId(i16),
#[error("Failed to write data: {0}")]
WriteError(std::io::Error),
#[error("Too many orphaned stream ids: {0}")]
TooManyOrphanedStreamIds(u16),
#[error(
"Failed to send/receive data needed to perform a request via tokio channel.
It implies that other half of the channel has been dropped.
The connection was already broken for some other reason."
)]
ChannelError,
}
impl From<BrokenConnectionErrorKind> for BrokenConnectionError {
fn from(value: BrokenConnectionErrorKind) -> Self {
BrokenConnectionError(Arc::new(value))
}
}
#[derive(Error, Debug)]
#[non_exhaustive]
pub enum CqlEventHandlingError {
#[error("Failed to deserialize EVENT response: {0}")]
CqlEventParseError(#[from] CqlEventParseError),
#[error("Received unexpected server response on stream -1: {0}. Expected EVENT response")]
UnexpectedResponse(CqlResponseKind),
#[error("Failed to deserialize a header of frame received on stream -1: {0}")]
BodyExtensionParseError(#[from] FrameBodyExtensionsParseError),
#[error(
"Failed to send event info via channel. The channel is probably closed, which is caused by connection being broken"
)]
SendError,
}
#[derive(Error, Debug, Clone)]
#[non_exhaustive]
pub enum RequestError {
#[error(
"Load balancing policy returned an empty plan.\
First thing to investigate should be the logic of custom LBP implementation.\
If you think that your LBP implementation is correct, or you make use of `DefaultPolicy`,\
then this is most probably a driver bug!"
)]
EmptyPlan,
#[error("No connections in the pool: {0}")]
ConnectionPoolError(#[from] ConnectionPoolError),
#[error(
"Request execution exceeded a client timeout of {}ms",
std::time::Duration::as_millis(.0)
)]
RequestTimeout(std::time::Duration),
#[error(transparent)]
LastAttemptError(#[from] RequestAttemptError),
}
impl RequestError {
pub fn into_execution_error(self) -> ExecutionError {
match self {
RequestError::EmptyPlan => ExecutionError::EmptyPlan,
RequestError::ConnectionPoolError(e) => e.into(),
RequestError::RequestTimeout(dur) => ExecutionError::RequestTimeout(dur),
RequestError::LastAttemptError(e) => ExecutionError::LastAttemptError(e),
}
}
}
#[derive(Error, Debug, Clone)]
#[non_exhaustive]
pub enum RequestAttemptError {
#[error("Failed to serialize query parameters: {0}")]
SerializationError(#[from] SerializationError),
#[error("Failed to serialize CQL request: {0}")]
CqlRequestSerialization(#[from] CqlRequestSerializationError),
#[error("Unable to allocate stream id")]
UnableToAllocStreamId,
#[error(transparent)]
BrokenConnectionError(#[from] BrokenConnectionError),
#[error(transparent)]
BodyExtensionsParseError(#[from] FrameBodyExtensionsParseError),
#[error(transparent)]
CqlResultParseError(#[from] CqlResultParseError),
#[error("Failed to deserialize ERROR response: {0}")]
CqlErrorParseError(#[from] CqlErrorParseError),
#[error("Database returned an error: {0}, Error message: {1}")]
DbError(DbError, String),
#[error(
"Received unexpected response from the server: {0}. Expected RESULT or ERROR response."
)]
UnexpectedResponse(CqlResponseKind),
#[error(
"Prepared statement id changed after repreparation; md5 sum (computed from the query string) should stay the same;\
Statement: \"{statement}\"; expected id: {expected_id:?}; reprepared id: {reprepared_id:?}"
)]
RepreparedIdChanged {
statement: String,
expected_id: Vec<u8>,
reprepared_id: Vec<u8>,
},
#[error("Reprepared statement's id does not exist in the batch.")]
RepreparedIdMissingInBatch,
#[error(
"Unpaged query returned a non-empty paging state! This is a driver-side or server-side bug."
)]
NonfinishedPagingState,
}
impl From<response::error::Error> for RequestAttemptError {
fn from(value: response::error::Error) -> Self {
RequestAttemptError::DbError(value.error, value.reason)
}
}
impl From<InternalRequestError> for RequestAttemptError {
fn from(value: InternalRequestError) -> Self {
match value {
InternalRequestError::CqlRequestSerialization(e) => e.into(),
InternalRequestError::BodyExtensionsParseError(e) => e.into(),
InternalRequestError::CqlResponseParseError(e) => match e {
CqlResponseParseError::CqlErrorParseError(e) => e.into(),
CqlResponseParseError::CqlResultParseError(e) => e.into(),
_ => RequestAttemptError::UnexpectedResponse(e.to_response_kind()),
},
InternalRequestError::BrokenConnection(e) => e.into(),
InternalRequestError::UnableToAllocStreamId => {
RequestAttemptError::UnableToAllocStreamId
}
}
}
}
#[derive(Error, Debug)]
#[non_exhaustive]
pub(crate) enum InternalRequestError {
#[error("Failed to serialize CQL request: {0}")]
CqlRequestSerialization(#[from] CqlRequestSerializationError),
#[error(transparent)]
BodyExtensionsParseError(#[from] FrameBodyExtensionsParseError),
#[error(transparent)]
CqlResponseParseError(#[from] CqlResponseParseError),
#[error(transparent)]
BrokenConnection(#[from] BrokenConnectionError),
#[error("Unable to allocate a stream id")]
UnableToAllocStreamId,
}
impl From<ResponseParseError> for InternalRequestError {
fn from(value: ResponseParseError) -> Self {
match value {
ResponseParseError::BodyExtensionsParseError(e) => e.into(),
ResponseParseError::CqlResponseParseError(e) => e.into(),
}
}
}
#[derive(Error, Debug)]
pub(crate) enum ResponseParseError {
#[error(transparent)]
BodyExtensionsParseError(#[from] FrameBodyExtensionsParseError),
#[error(transparent)]
CqlResponseParseError(#[from] CqlResponseParseError),
}
#[derive(Clone, Debug, Error)]
#[non_exhaustive]
pub enum ClusterStateTokenError {
#[error(transparent)]
TokenCalculation(#[from] TokenCalculationError),
#[error(transparent)]
Serialization(#[from] SerializationError),
#[error("Can't find metadata for requested table ({keyspace}.{table}).")]
UnknownTable {
keyspace: String,
table: String,
},
}
#[cfg(test)]
mod tests {
use scylla_cql::Consistency;
use super::{DbError, ExecutionError, RequestAttemptError, WriteType};
#[test]
fn write_type_from_str() {
let test_cases: [(&str, WriteType); 9] = [
("SIMPLE", WriteType::Simple),
("BATCH", WriteType::Batch),
("UNLOGGED_BATCH", WriteType::UnloggedBatch),
("COUNTER", WriteType::Counter),
("BATCH_LOG", WriteType::BatchLog),
("CAS", WriteType::Cas),
("VIEW", WriteType::View),
("CDC", WriteType::Cdc),
("SOMEOTHER", WriteType::Other("SOMEOTHER".to_string())),
];
for (write_type_str, expected_write_type) in &test_cases {
let write_type = WriteType::from(*write_type_str);
assert_eq!(write_type, *expected_write_type);
}
}
#[test]
fn dberror_full_info() {
let db_error = DbError::Unavailable {
consistency: Consistency::Three,
required: 3,
alive: 2,
};
let db_error_displayed: String = format!("{db_error}");
let mut expected_dberr_msg =
"Not enough nodes are alive to satisfy required consistency level ".to_string();
expected_dberr_msg += "(consistency: Three, required: 3, alive: 2)";
assert_eq!(db_error_displayed, expected_dberr_msg);
let execution_error = ExecutionError::LastAttemptError(RequestAttemptError::DbError(
db_error,
"a message about unavailable error".to_string(),
));
let execution_error_displayed: String = format!("{execution_error}");
let mut expected_execution_err_msg = "Database returned an error: ".to_string();
expected_execution_err_msg += &expected_dberr_msg;
expected_execution_err_msg += ", Error message: a message about unavailable error";
assert_eq!(execution_error_displayed, expected_execution_err_msg);
}
}