scylla/
errors.rs

1//! This module contains various errors which can be returned by [`Session`](crate::client::session::Session).
2
3use std::error::Error;
4use std::io::ErrorKind;
5use std::net::{AddrParseError, IpAddr, SocketAddr};
6use std::num::ParseIntError;
7use std::sync::Arc;
8use thiserror::Error;
9
10use crate::frame::response;
11
12// Re-export error types from pager module.
13pub use crate::client::pager::{NextPageError, NextRowError};
14
15use crate::statement::prepared::TokenCalculationError;
16// Re-export error types from query_result module.
17pub use crate::response::query_result::{
18    FirstRowError, IntoRowsResultError, MaybeFirstRowError, ResultNotRowsError, RowsError,
19    SingleRowError,
20};
21
22// Re-export error type from authentication module.
23pub use crate::authentication::AuthError;
24
25// Re-export error type from network module.
26pub use crate::network::tls::TlsError;
27
28// Re-export error types from scylla-cql.
29pub use scylla_cql::deserialize::{DeserializationError, TypeCheckError};
30pub use scylla_cql::frame::frame_errors::{
31    CqlAuthChallengeParseError, CqlAuthSuccessParseError, CqlAuthenticateParseError,
32    CqlErrorParseError, CqlEventParseError, CqlRequestSerializationError, CqlResponseParseError,
33    CqlResultParseError, CqlSupportedParseError, FrameBodyExtensionsParseError,
34    FrameHeaderParseError,
35};
36pub use scylla_cql::frame::request::CqlRequestKind;
37pub use scylla_cql::frame::response::error::{DbError, OperationType, WriteType};
38pub use scylla_cql::frame::response::CqlResponseKind;
39pub use scylla_cql::serialize::SerializationError;
40
41/// Error that occurred during request execution
42#[derive(Error, Debug, Clone)]
43#[non_exhaustive]
44pub enum ExecutionError {
45    /// Caller passed an invalid statement.
46    #[error(transparent)]
47    BadQuery(#[from] BadQuery),
48
49    /// Load balancing policy returned an empty plan.
50    #[error(
51        "Load balancing policy returned an empty plan.\
52        First thing to investigate should be the logic of custom LBP implementation.\
53        If you think that your LBP implementation is correct, or you make use of `DefaultPolicy`,\
54        then this is most probably a driver bug!"
55    )]
56    EmptyPlan,
57
58    /// Failed to prepare the statement.
59    /// Applies to unprepared statements with non-empty value parameters.
60    #[error("Failed to prepare the statement: {0}")]
61    PrepareError(#[from] PrepareError),
62
63    /// Selected node's connection pool is in invalid state.
64    #[error("No connections in the pool: {0}")]
65    ConnectionPoolError(#[from] ConnectionPoolError),
66
67    /// An error returned by last attempt of request execution.
68    #[error(transparent)]
69    LastAttemptError(#[from] RequestAttemptError),
70
71    /// Failed to run a request within a provided client timeout.
72    #[error(
73        "Request execution exceeded a client timeout of {}ms",
74        std::time::Duration::as_millis(.0)
75    )]
76    RequestTimeout(std::time::Duration),
77
78    /// 'USE KEYSPACE <>' request failed.
79    #[error("'USE KEYSPACE <>' request failed: {0}")]
80    UseKeyspaceError(#[from] UseKeyspaceError),
81
82    /// Failed to await automatic schema agreement.
83    #[error("Failed to await schema agreement: {0}")]
84    SchemaAgreementError(#[from] SchemaAgreementError),
85
86    /// A metadata error occurred during schema agreement.
87    #[error("Cluster metadata fetch error occurred during automatic schema agreement: {0}")]
88    MetadataError(#[from] MetadataError),
89}
90
91impl From<SerializationError> for ExecutionError {
92    fn from(serialized_err: SerializationError) -> ExecutionError {
93        ExecutionError::BadQuery(BadQuery::SerializationError(serialized_err))
94    }
95}
96
97/// An error returned by [`Session::prepare()`][crate::client::session::Session::prepare].
98#[derive(Error, Debug, Clone)]
99#[non_exhaustive]
100pub enum PrepareError {
101    /// Failed to find a node with working connection pool.
102    #[error("Failed to find a node with working connection pool: {0}")]
103    ConnectionPoolError(#[from] ConnectionPoolError),
104
105    /// Failed to prepare statement on every connection from the pool.
106    #[error("Preparation failed on every connection from the selected pool. First attempt error: {first_attempt}")]
107    AllAttemptsFailed { first_attempt: RequestAttemptError },
108
109    /// Prepared statement id mismatch.
110    #[error(
111        "Prepared statement id mismatch between multiple connections - all result ids should be equal."
112    )]
113    PreparedStatementIdsMismatch,
114}
115
116/// An error that occurred during construction of [`QueryPager`][crate::client::pager::QueryPager].
117#[derive(Error, Debug, Clone)]
118#[non_exhaustive]
119pub enum PagerExecutionError {
120    /// Failed to prepare the statement.
121    #[error("Failed to prepare the statement to be used by the pager: {0}")]
122    PrepareError(#[from] PrepareError),
123
124    /// Failed to serialize statement parameters.
125    #[error("Failed to serialize statement parameters: {0}")]
126    SerializationError(#[from] SerializationError),
127
128    /// Failed to fetch the first page of the result.
129    #[error("Failed to fetch the first page of the result: {0}")]
130    NextPageError(#[from] NextPageError),
131}
132
133/// Error that occurred during session creation
134#[derive(Error, Debug, Clone)]
135#[non_exhaustive]
136pub enum NewSessionError {
137    /// Failed to resolve hostname passed in Session creation
138    #[error("Couldn't resolve any hostname: {0:?}")]
139    FailedToResolveAnyHostname(Vec<String>),
140
141    /// List of known nodes passed to Session constructor is empty
142    /// There needs to be at least one node to connect to
143    #[error("Empty known nodes list")]
144    EmptyKnownNodesList,
145
146    /// Failed to perform initial cluster metadata fetch.
147    #[error("Failed to perform initial cluster metadata fetch: {0}")]
148    MetadataError(#[from] MetadataError),
149
150    /// 'USE KEYSPACE <>' request failed.
151    #[error("'USE KEYSPACE <>' request failed: {0}")]
152    UseKeyspaceError(#[from] UseKeyspaceError),
153}
154
155/// An error that occurred during `USE KEYSPACE <>` request.
156#[derive(Error, Debug, Clone)]
157#[non_exhaustive]
158pub enum UseKeyspaceError {
159    /// Passed invalid keyspace name to use.
160    #[error("Passed invalid keyspace name to use: {0}")]
161    BadKeyspaceName(#[from] BadKeyspaceName),
162
163    /// An error during request execution.
164    #[error(transparent)]
165    RequestError(#[from] RequestAttemptError),
166
167    /// Keyspace name mismatch.
168    #[error("Keyspace name mismtach; expected: {expected_keyspace_name_lowercase}, received: {result_keyspace_name_lowercase}")]
169    KeyspaceNameMismatch {
170        expected_keyspace_name_lowercase: String,
171        result_keyspace_name_lowercase: String,
172    },
173
174    /// Failed to run a request within a provided client timeout.
175    #[error(
176        "Request execution exceeded a client timeout of {}ms",
177        std::time::Duration::as_millis(.0)
178    )]
179    RequestTimeout(std::time::Duration),
180}
181
182/// An error that occurred when awating schema agreement.
183#[derive(Error, Debug, Clone)]
184#[non_exhaustive]
185pub enum SchemaAgreementError {
186    /// Failed to find a node with working connection pool.
187    #[error("Failed to find a node with working connection pool: {0}")]
188    ConnectionPoolError(#[from] ConnectionPoolError),
189
190    /// Failed to execute schema version query on one of the connections.
191    ///
192    /// The driver attempts to fetch schema version on all connections in the pool (for all nodes).
193    /// It expects all of the requests to succeed. If at least one request fails, schema version
194    /// fetch is considered failed. This variant contains an error from one of the failing request attempts.
195    #[error("Failed to execute schema version query: {0}")]
196    RequestError(#[from] RequestAttemptError),
197
198    /// Failed to convert schema version query result into rows result.
199    #[error("Failed to convert schema version query result into rows result: {0}")]
200    TracesEventsIntoRowsResultError(IntoRowsResultError),
201
202    /// Failed to deserialize a single row from schema version query response.
203    #[error(transparent)]
204    SingleRowError(SingleRowError),
205
206    /// Schema agreement timed out.
207    #[error("Schema agreement exceeded {}ms", std::time::Duration::as_millis(.0))]
208    Timeout(std::time::Duration),
209}
210
211/// An error that occurred during tracing info fetch.
212#[derive(Error, Debug, Clone)]
213#[non_exhaustive]
214pub enum TracingError {
215    /// Failed to execute query to either "system_traces.sessions" or "system_traces.events".
216    #[error(
217        "Failed to execute queries to \"system_traces.sessions\" or \"system_traces.events\" system tables: {0}"
218    )]
219    ExecutionError(#[from] ExecutionError),
220
221    /// Failed to convert result of system_traces.session query to rows result.
222    #[error("Failed to convert result of system_traces.session query to rows result")]
223    TracesSessionIntoRowsResultError(IntoRowsResultError),
224
225    /// system_traces.session has invalid column type.
226    #[error("system_traces.session has invalid column type: {0}")]
227    TracesSessionInvalidColumnType(TypeCheckError),
228
229    /// Response to system_traces.session failed to deserialize.
230    #[error("Response to system_traces.session failed to deserialize: {0}")]
231    TracesSessionDeserializationFailed(DeserializationError),
232
233    /// Failed to convert result of system_traces.events query to rows result.
234    #[error("Failed to convert result of system_traces.events query to rows result")]
235    TracesEventsIntoRowsResultError(IntoRowsResultError),
236
237    /// system_traces.events has invalid column type.
238    #[error("system_traces.events has invalid column type: {0}")]
239    TracesEventsInvalidColumnType(TypeCheckError),
240
241    /// Response to system_traces.events failed to deserialize.
242    #[error("Response to system_traces.events failed to deserialize: {0}")]
243    TracesEventsDeserializationFailed(DeserializationError),
244
245    /// All tracing queries returned an empty result.
246    #[error(
247        "All tracing queries returned an empty result, \
248        maybe the trace information didn't propagate yet. \
249        Consider configuring Session with \
250        a longer fetch interval (tracing_info_fetch_interval)"
251    )]
252    EmptyResults,
253}
254
255/// An error that occurred during metadata fetch and verification.
256///
257/// The driver performs metadata fetch and verification of the cluster's schema
258/// and topology. This includes:
259/// - keyspaces
260/// - UDTs
261/// - tables
262/// - views
263/// - peers (topology)
264///
265/// The errors that occur during metadata fetch are contained in [`MetadataFetchError`].
266/// Remaining errors (logical errors) are contained in the variants corresponding to the
267/// specific part of the metadata.
268#[derive(Error, Debug, Clone)]
269#[non_exhaustive]
270pub enum MetadataError {
271    /// Control connection pool error.
272    #[error("Control connection pool error: {0}")]
273    ConnectionPoolError(#[from] ConnectionPoolError),
274
275    /// Failed to fetch metadata.
276    #[error("transparent")]
277    FetchError(#[from] MetadataFetchError),
278
279    /// Bad peers metadata.
280    #[error("Bad peers metadata: {0}")]
281    Peers(#[from] PeersMetadataError),
282
283    /// Bad keyspaces metadata.
284    #[error("Bad keyspaces metadata: {0}")]
285    Keyspaces(#[from] KeyspacesMetadataError),
286
287    /// Bad UDTs metadata.
288    #[error("Bad UDTs metadata: {0}")]
289    Udts(#[from] UdtMetadataError),
290
291    /// Bad tables metadata.
292    #[error("Bad tables metadata: {0}")]
293    Tables(#[from] TablesMetadataError),
294}
295
296/// An error occurred during metadata fetch.
297#[derive(Error, Debug, Clone)]
298#[error("Metadata fetch failed for table \"{table}\": {error}")]
299#[non_exhaustive]
300pub struct MetadataFetchError {
301    /// Reason why metadata fetch failed.
302    pub error: MetadataFetchErrorKind,
303    /// Table name for which metadata fetch failed.
304    pub table: &'static str,
305}
306
307/// Specific reason why metadata fetch failed.
308#[derive(Error, Debug, Clone)]
309#[non_exhaustive]
310pub enum MetadataFetchErrorKind {
311    /// Queried table has invalid column type.
312    #[error("The table has invalid column type: {0}")]
313    InvalidColumnType(#[from] TypeCheckError),
314
315    /// Failed to prepare the statement for metadata fetch.
316    #[error("Failed to prepare the statement: {0}")]
317    PrepareError(#[from] RequestAttemptError),
318
319    /// Failed to serialize statement parameters.
320    #[error("Failed to serialize statement parameters: {0}")]
321    SerializationError(#[from] SerializationError),
322
323    /// Failed to obtain next row from response to the metadata fetch query.
324    #[error("Failed to obtain next row from response to the query: {0}")]
325    NextRowError(#[from] NextRowError),
326}
327
328/// An error that occurred during peers metadata fetch.
329#[derive(Error, Debug, Clone)]
330#[non_exhaustive]
331pub enum PeersMetadataError {
332    /// Empty peers list returned during peers metadata fetch.
333    #[error("Peers list is empty")]
334    EmptyPeers,
335
336    /// All peers have empty token lists.
337    #[error("All peers have empty token lists")]
338    EmptyTokenLists,
339}
340
341/// An error that occurred during keyspaces metadata fetch.
342#[derive(Error, Debug, Clone)]
343#[non_exhaustive]
344pub enum KeyspacesMetadataError {
345    /// Bad keyspace replication strategy.
346    #[error("Bad keyspace <{keyspace}> replication strategy: {error}")]
347    Strategy {
348        keyspace: String,
349        error: KeyspaceStrategyError,
350    },
351}
352
353/// An error that occurred during specific keyspace's metadata fetch.
354#[derive(Error, Debug, Clone)]
355#[non_exhaustive]
356pub enum KeyspaceStrategyError {
357    /// Keyspace strategy map missing a `class` field.
358    #[error("keyspace strategy definition is missing a 'class' field")]
359    MissingClassForStrategyDefinition,
360
361    /// Missing replication factor for SimpleStrategy.
362    #[error("Missing replication factor field for SimpleStrategy")]
363    MissingReplicationFactorForSimpleStrategy,
364
365    /// Replication factor could not be parsed as unsigned integer.
366    #[error("Failed to parse a replication factor as unsigned integer: {0}")]
367    ReplicationFactorParseError(ParseIntError),
368
369    /// Received an unexpected NTS option.
370    /// Driver expects only 'class' and replication factor per dc ('dc': rf)
371    #[error("Unexpected NetworkTopologyStrategy option: '{key}': '{value}'")]
372    UnexpectedNetworkTopologyStrategyOption { key: String, value: String },
373}
374
375/// An error that occurred during UDTs metadata fetch.
376#[derive(Error, Debug, Clone)]
377#[non_exhaustive]
378pub enum UdtMetadataError {
379    /// Failed to parse CQL type returned from system_schema.types query.
380    #[error(
381        "Failed to parse a CQL type returned from system_schema.types query. \
382        Type '{typ}', at position {position}: {reason}"
383    )]
384    InvalidCqlType {
385        typ: String,
386        position: usize,
387        reason: String,
388    },
389
390    /// Circular UDT dependency detected.
391    #[error("Detected circular dependency between user defined types - toposort is impossible!")]
392    CircularTypeDependency,
393}
394
395/// An error that occurred during tables metadata fetch.
396#[derive(Error, Debug, Clone)]
397#[non_exhaustive]
398pub enum TablesMetadataError {
399    /// Failed to parse CQL type returned from system_schema.columns query.
400    #[error(
401        "Failed to parse a CQL type returned from system_schema.columns query. \
402        Type '{typ}', at position {position}: {reason}"
403    )]
404    InvalidCqlType {
405        typ: String,
406        position: usize,
407        reason: String,
408    },
409
410    /// Unknown column kind.
411    #[error("Unknown column kind '{column_kind}' for {keyspace_name}.{table_name}.{column_name}")]
412    UnknownColumnKind {
413        keyspace_name: String,
414        table_name: String,
415        column_name: String,
416        column_kind: String,
417    },
418}
419
420/// Error caused by caller creating an invalid statement.
421#[derive(Error, Debug, Clone)]
422#[error("Invalid statement passed to Session")]
423#[non_exhaustive]
424pub enum BadQuery {
425    /// Unable extract a partition key based on prepared statement's metadata.
426    #[error("Unable extract a partition key based on prepared statement's metadata")]
427    PartitionKeyExtraction,
428
429    /// "Serializing values failed.
430    #[error("Serializing values failed: {0} ")]
431    SerializationError(#[from] SerializationError),
432
433    /// Serialized values are too long to compute partition key.
434    #[error("Serialized values are too long to compute partition key! Length: {0}, Max allowed length: {1}")]
435    ValuesTooLongForKey(usize, usize),
436
437    /// Too many statements in the batch statement.
438    #[error("Number of statements in Batch Statement supplied is {0} which has exceeded the max value of 65,535")]
439    TooManyQueriesInBatchStatement(usize),
440}
441
442/// Invalid keyspace name given to `Session::use_keyspace()`
443#[derive(Debug, Error, Clone)]
444#[non_exhaustive]
445pub enum BadKeyspaceName {
446    /// Keyspace name is empty
447    #[error("Keyspace name is empty")]
448    Empty,
449
450    /// Keyspace name too long, must be up to 48 characters
451    #[error("Keyspace name too long, must be up to 48 characters, found {1} characters. Bad keyspace name: '{0}'")]
452    TooLong(String, usize),
453
454    /// Illegal character - only alphanumeric and underscores allowed.
455    #[error("Illegal character found: '{1}', only alphanumeric and underscores allowed. Bad keyspace name: '{0}'")]
456    IllegalCharacter(String, char),
457}
458
459/// An error that occurred when selecting a node connection
460/// to perform a request on.
461#[derive(Error, Debug, Clone)]
462#[non_exhaustive]
463pub enum ConnectionPoolError {
464    /// A connection pool is broken. Includes an error of a last connection.
465    #[error("The pool is broken; Last connection failed with: {last_connection_error}")]
466    Broken {
467        last_connection_error: ConnectionError,
468    },
469
470    /// A connection pool is still being initialized.
471    #[error("Pool is still being initialized")]
472    Initializing,
473
474    /// A corresponding node was disabled by a host filter.
475    #[error("The node has been disabled by a host filter")]
476    NodeDisabledByHostFilter,
477}
478
479/// An error that appeared on a connection level.
480/// It indicated that connection can no longer be used
481/// and should be dropped.
482#[derive(Error, Debug, Clone)]
483#[non_exhaustive]
484pub enum ConnectionError {
485    /// Provided connect timeout elapsed.
486    #[error("Connect timeout elapsed")]
487    ConnectTimeout,
488
489    /// Input/Output error occurred.
490    #[error(transparent)]
491    IoError(Arc<std::io::Error>),
492
493    /// Driver was unable to find a free source port for given shard.
494    #[error("Could not find free source port for shard {0}")]
495    NoSourcePortForShard(u32),
496
497    /// Failed to translate an address before establishing a connection.
498    #[error("Address translation failed: {0}")]
499    TranslationError(#[from] TranslationError),
500
501    /// A connection has been broken after being established.
502    #[error(transparent)]
503    BrokenConnection(#[from] BrokenConnectionError),
504
505    /// A request required to initialize a connection failed.
506    #[error(transparent)]
507    ConnectionSetupRequestError(#[from] ConnectionSetupRequestError),
508}
509
510impl From<std::io::Error> for ConnectionError {
511    fn from(value: std::io::Error) -> Self {
512        ConnectionError::IoError(Arc::new(value))
513    }
514}
515
516impl ConnectionError {
517    /// Checks if this error indicates that a chosen source port/address cannot be bound.
518    /// This is caused by one of the following:
519    /// - The source address is already used by another socket,
520    /// - The source address is reserved and the process does not have sufficient privileges to use it.
521    pub fn is_address_unavailable_for_use(&self) -> bool {
522        if let ConnectionError::IoError(io_error) = self {
523            match io_error.kind() {
524                ErrorKind::AddrInUse | ErrorKind::PermissionDenied => return true,
525                _ => {}
526            }
527        }
528
529        false
530    }
531}
532
533/// Error caused by failed address translation done before establishing connection
534#[non_exhaustive]
535#[derive(Debug, Clone, Error)]
536pub enum TranslationError {
537    /// Driver failed to find a translation rule for a provided address.
538    #[error("No rule for address {0}")]
539    NoRuleForAddress(SocketAddr),
540
541    /// A translation rule for a provided address was found, but the translated address was invalid.
542    #[error("Failed to parse translated address: {translated_addr_str}, reason: {reason}")]
543    InvalidAddressInRule {
544        translated_addr_str: &'static str,
545        reason: AddrParseError,
546    },
547
548    /// An I/O error occurred during address translation.
549    #[error("An I/O error occurred during address translation: {0}")]
550    IoError(Arc<std::io::Error>),
551}
552
553/// An error that occurred during connection setup request execution.
554/// It indicates that request needed to initiate a connection failed.
555#[derive(Error, Debug, Clone)]
556#[error("Failed to perform a connection setup request. Request: {request_kind}, reason: {error}")]
557#[non_exhaustive]
558pub struct ConnectionSetupRequestError {
559    pub request_kind: CqlRequestKind,
560    pub error: ConnectionSetupRequestErrorKind,
561}
562
563#[derive(Error, Debug, Clone)]
564#[non_exhaustive]
565pub enum ConnectionSetupRequestErrorKind {
566    /// Failed to serialize CQL request.
567    #[error("Failed to serialize CQL request: {0}")]
568    CqlRequestSerialization(#[from] CqlRequestSerializationError),
569
570    /// Failed to deserialize frame body extensions.
571    #[error(transparent)]
572    BodyExtensionsParseError(#[from] FrameBodyExtensionsParseError),
573
574    /// Driver was unable to allocate a stream id to execute a setup request on.
575    #[error("Unable to allocate stream id")]
576    UnableToAllocStreamId,
577
578    /// A connection was broken during setup request execution.
579    #[error(transparent)]
580    BrokenConnection(#[from] BrokenConnectionError),
581
582    /// Received a server error in response to connection setup request.
583    #[error("Database returned an error: {0}, Error message: {1}")]
584    DbError(DbError, String),
585
586    /// Received an unexpected response from the server.
587    #[error("Received unexpected response from the server: {0}")]
588    UnexpectedResponse(CqlResponseKind),
589
590    /// Received a response to OPTIONS request, but failed to deserialize its body.
591    #[error("Failed to deserialize SUPPORTED response: {0}")]
592    CqlSupportedParseError(#[from] CqlSupportedParseError),
593
594    /// Received an AUTHENTICATE response, but failed to deserialize its body.
595    #[error("Failed to deserialize AUTHENTICATE response: {0}")]
596    CqlAuthenticateParseError(#[from] CqlAuthenticateParseError),
597
598    /// Received an AUTH_SUCCESS response, but failed to deserialize its body.
599    #[error("Failed to deserialize AUTH_SUCCESS response: {0}")]
600    CqlAuthSuccessParseError(#[from] CqlAuthSuccessParseError),
601
602    /// Received an AUTH_CHALLENGE response, but failed to deserialize its body.
603    #[error("Failed to deserialize AUTH_CHALLENGE response: {0}")]
604    CqlAuthChallengeParseError(#[from] CqlAuthChallengeParseError),
605
606    /// Received server ERROR response, but failed to deserialize its body.
607    #[error("Failed to deserialize ERROR response: {0}")]
608    CqlErrorParseError(#[from] CqlErrorParseError),
609
610    /// An error returned by [`AuthenticatorProvider::start_authentication_session`](crate::authentication::AuthenticatorProvider::start_authentication_session).
611    #[error("Failed to start client's auth session: {0}")]
612    StartAuthSessionError(AuthError),
613
614    /// An error returned by [`AuthenticatorSession::evaluate_challenge`](crate::authentication::AuthenticatorSession::evaluate_challenge).
615    #[error("Failed to evaluate auth challenge on client side: {0}")]
616    AuthChallengeEvaluationError(AuthError),
617
618    /// An error returned by [`AuthenticatorSession::success`](crate::authentication::AuthenticatorSession::success).
619    #[error("Failed to finish auth challenge on client side: {0}")]
620    AuthFinishError(AuthError),
621
622    /// User did not provide authentication while the cluster requires it.
623    /// See [`SessionBuilder::user`](crate::client::session_builder::SessionBuilder::user)
624    /// and/or [`SessionBuilder::authenticator_provider`](crate::client::session_builder::SessionBuilder::authenticator_provider).
625    #[error("Authentication is required. You can use SessionBuilder::user(\"user\", \"pass\") to provide credentials or SessionBuilder::authenticator_provider to provide custom authenticator")]
626    MissingAuthentication,
627}
628
629impl ConnectionSetupRequestError {
630    pub(crate) fn new(
631        request_kind: CqlRequestKind,
632        error: ConnectionSetupRequestErrorKind,
633    ) -> Self {
634        ConnectionSetupRequestError {
635            request_kind,
636            error,
637        }
638    }
639
640    pub fn get_error(&self) -> &ConnectionSetupRequestErrorKind {
641        &self.error
642    }
643}
644
645/// An error indicating that a connection was broken.
646/// Possible error reasons:
647/// - keepalive query errors - driver failed to sent a keepalive query, or the query timed out
648/// - received a frame with unexpected stream id
649/// - failed to handle a server event (message received on stream -1)
650/// - some low-level IO errors - e.g. driver failed to write data via socket
651#[derive(Error, Debug, Clone)]
652#[error("Connection broken, reason: {0}")]
653pub struct BrokenConnectionError(Arc<dyn Error + Sync + Send>);
654
655impl BrokenConnectionError {
656    /// Retrieve an error reason by downcasting to specific type.
657    pub fn downcast_ref<T: Error + 'static>(&self) -> Option<&T> {
658        self.0.downcast_ref()
659    }
660}
661
662/// A reason why connection was broken.
663///
664/// See [`BrokenConnectionError::downcast_ref()`].
665/// You can retrieve the actual type by downcasting `Arc<dyn Error>`.
666#[derive(Error, Debug)]
667#[non_exhaustive]
668pub enum BrokenConnectionErrorKind {
669    /// Driver sent a keepalive request to the database, but the request timed out.
670    #[error("Timed out while waiting for response to keepalive request on connection to node {0}")]
671    KeepaliveTimeout(IpAddr),
672
673    /// Driver sent a keepalive request to the database, but request execution failed.
674    #[error("Failed to execute keepalive request: {0}")]
675    KeepaliveRequestError(Arc<dyn Error + Sync + Send>),
676
677    /// Failed to deserialize response frame header.
678    #[error("Failed to deserialize frame: {0}")]
679    FrameHeaderParseError(FrameHeaderParseError),
680
681    /// Failed to handle a CQL event (server response received on stream -1).
682    #[error("Failed to handle server event: {0}")]
683    CqlEventHandlingError(#[from] CqlEventHandlingError),
684
685    /// Received a server frame with unexpected stream id.
686    #[error("Received a server frame with unexpected stream id: {0}")]
687    UnexpectedStreamId(i16),
688
689    /// IO error - server failed to write data to the socket.
690    #[error("Failed to write data: {0}")]
691    WriteError(std::io::Error),
692
693    /// Maximum number of orphaned streams exceeded.
694    #[error("Too many orphaned stream ids: {0}")]
695    TooManyOrphanedStreamIds(u16),
696
697    /// Failed to send data via tokio channel. This implies
698    /// that connection was probably already broken for some other reason.
699    #[error(
700        "Failed to send/receive data needed to perform a request via tokio channel.
701        It implies that other half of the channel has been dropped.
702        The connection was already broken for some other reason."
703    )]
704    ChannelError,
705}
706
707impl From<BrokenConnectionErrorKind> for BrokenConnectionError {
708    fn from(value: BrokenConnectionErrorKind) -> Self {
709        BrokenConnectionError(Arc::new(value))
710    }
711}
712
713/// Failed to handle a CQL event received on a stream -1.
714/// Possible error kinds are:
715/// - failed to deserialize response's frame header
716/// - failed to deserialize CQL event response
717/// - received invalid server response
718/// - failed to send an event info via channel (connection is probably broken)
719#[derive(Error, Debug)]
720#[non_exhaustive]
721pub enum CqlEventHandlingError {
722    /// Received an EVENT server response, but failed to deserialize it.
723    #[error("Failed to deserialize EVENT response: {0}")]
724    CqlEventParseError(#[from] CqlEventParseError),
725
726    /// Received an unexpected response on stream -1.
727    #[error("Received unexpected server response on stream -1: {0}. Expected EVENT response")]
728    UnexpectedResponse(CqlResponseKind),
729
730    /// Failed to deserialize body extensions of frame received on stream -1.
731    #[error("Failed to deserialize a header of frame received on stream -1: {0}")]
732    BodyExtensionParseError(#[from] FrameBodyExtensionsParseError),
733
734    /// Driver failed to send event data between the internal tasks.
735    /// It implies that connection was broken for some reason.
736    #[error("Failed to send event info via channel. The channel is probably closed, which is caused by connection being broken")]
737    SendError,
738}
739
740/// An error that occurred during execution of
741/// - `QUERY`
742/// - `PREPARE`
743/// - `EXECUTE`
744/// - `BATCH`
745///
746/// request. This error represents a definite request failure, unlike
747/// [`RequestAttemptError`] which represents a failure of a single
748/// attempt.
749#[derive(Error, Debug, Clone)]
750#[non_exhaustive]
751pub enum RequestError {
752    /// Load balancing policy returned an empty plan.
753    #[error(
754            "Load balancing policy returned an empty plan.\
755            First thing to investigate should be the logic of custom LBP implementation.\
756            If you think that your LBP implementation is correct, or you make use of `DefaultPolicy`,\
757            then this is most probably a driver bug!"
758        )]
759    EmptyPlan,
760
761    /// Selected node's connection pool is in invalid state.
762    #[error("No connections in the pool: {0}")]
763    ConnectionPoolError(#[from] ConnectionPoolError),
764
765    /// Failed to run a request within a provided client timeout.
766    #[error(
767            "Request execution exceeded a client timeout of {}ms",
768            std::time::Duration::as_millis(.0)
769        )]
770    RequestTimeout(std::time::Duration),
771
772    /// Failed to execute request.
773    #[error(transparent)]
774    LastAttemptError(#[from] RequestAttemptError),
775}
776
777impl RequestError {
778    pub fn into_execution_error(self) -> ExecutionError {
779        match self {
780            RequestError::EmptyPlan => ExecutionError::EmptyPlan,
781            RequestError::ConnectionPoolError(e) => e.into(),
782            RequestError::RequestTimeout(dur) => ExecutionError::RequestTimeout(dur),
783            RequestError::LastAttemptError(e) => ExecutionError::LastAttemptError(e),
784        }
785    }
786}
787
788/// An error that occurred during a single attempt of:
789/// - `QUERY`
790/// - `PREPARE`
791/// - `EXECUTE`
792/// - `BATCH`
793///
794/// requests. The retry decision is made based
795/// on this error.
796#[derive(Error, Debug, Clone)]
797#[non_exhaustive]
798pub enum RequestAttemptError {
799    /// Failed to serialize query parameters. This error occurs, when user executes
800    /// a CQL `QUERY` request with non-empty parameter's value list and the serialization
801    /// of provided values fails during statement preparation.
802    #[error("Failed to serialize query parameters: {0}")]
803    SerializationError(#[from] SerializationError),
804
805    /// Failed to serialize CQL request.
806    #[error("Failed to serialize CQL request: {0}")]
807    CqlRequestSerialization(#[from] CqlRequestSerializationError),
808
809    /// Driver was unable to allocate a stream id to execute a query on.
810    #[error("Unable to allocate stream id")]
811    UnableToAllocStreamId,
812
813    /// A connection has been broken during query execution.
814    #[error(transparent)]
815    BrokenConnectionError(#[from] BrokenConnectionError),
816
817    /// Failed to deserialize frame body extensions.
818    #[error(transparent)]
819    BodyExtensionsParseError(#[from] FrameBodyExtensionsParseError),
820
821    /// Received a RESULT server response, but failed to deserialize it.
822    #[error(transparent)]
823    CqlResultParseError(#[from] CqlResultParseError),
824
825    /// Received an ERROR server response, but failed to deserialize it.
826    #[error("Failed to deserialize ERROR response: {0}")]
827    CqlErrorParseError(#[from] CqlErrorParseError),
828
829    /// Database sent a response containing some error with a message
830    #[error("Database returned an error: {0}, Error message: {1}")]
831    DbError(DbError, String),
832
833    /// Received an unexpected response from the server.
834    #[error(
835        "Received unexpected response from the server: {0}. Expected RESULT or ERROR response."
836    )]
837    UnexpectedResponse(CqlResponseKind),
838
839    /// Prepared statement id changed after repreparation.
840    #[error(
841        "Prepared statement id changed after repreparation; md5 sum (computed from the query string) should stay the same;\
842        Statement: \"{statement}\"; expected id: {expected_id:?}; reprepared id: {reprepared_id:?}"
843    )]
844    RepreparedIdChanged {
845        statement: String,
846        expected_id: Vec<u8>,
847        reprepared_id: Vec<u8>,
848    },
849
850    /// Driver tried to reprepare a statement in the batch, but the reprepared
851    /// statement's id is not included in the batch.
852    #[error("Reprepared statement's id does not exist in the batch.")]
853    RepreparedIdMissingInBatch,
854
855    /// A result with nonfinished paging state received for unpaged query.
856    #[error("Unpaged query returned a non-empty paging state! This is a driver-side or server-side bug.")]
857    NonfinishedPagingState,
858}
859
860impl From<response::error::Error> for RequestAttemptError {
861    fn from(value: response::error::Error) -> Self {
862        RequestAttemptError::DbError(value.error, value.reason)
863    }
864}
865
866impl From<InternalRequestError> for RequestAttemptError {
867    fn from(value: InternalRequestError) -> Self {
868        match value {
869            InternalRequestError::CqlRequestSerialization(e) => e.into(),
870            InternalRequestError::BodyExtensionsParseError(e) => e.into(),
871            InternalRequestError::CqlResponseParseError(e) => match e {
872                // Only possible responses are RESULT and ERROR. If we failed parsing
873                // other response, treat it as unexpected response.
874                CqlResponseParseError::CqlErrorParseError(e) => e.into(),
875                CqlResponseParseError::CqlResultParseError(e) => e.into(),
876                _ => RequestAttemptError::UnexpectedResponse(e.to_response_kind()),
877            },
878            InternalRequestError::BrokenConnection(e) => e.into(),
879            InternalRequestError::UnableToAllocStreamId => {
880                RequestAttemptError::UnableToAllocStreamId
881            }
882        }
883    }
884}
885
886/// An error that occurred when performing a request.
887///
888/// Possible error kinds:
889/// - Connection is broken
890/// - Response's frame header deserialization error
891/// - CQL response (frame body) deserialization error
892/// - Driver was unable to allocate a stream id for a request
893///
894/// This is driver's internal low-level error type. It can occur
895/// during any request execution in connection layer.
896#[derive(Error, Debug)]
897#[non_exhaustive]
898pub(crate) enum InternalRequestError {
899    /// Failed to serialize CQL request.
900    #[error("Failed to serialize CQL request: {0}")]
901    CqlRequestSerialization(#[from] CqlRequestSerializationError),
902
903    /// Failed to deserialize frame body extensions.
904    #[error(transparent)]
905    BodyExtensionsParseError(#[from] FrameBodyExtensionsParseError),
906
907    /// Failed to deserialize a CQL response (frame body).
908    #[error(transparent)]
909    CqlResponseParseError(#[from] CqlResponseParseError),
910
911    /// A connection was broken during request execution.
912    #[error(transparent)]
913    BrokenConnection(#[from] BrokenConnectionError),
914
915    /// Driver was unable to allocate a stream id to execute a request on.
916    #[error("Unable to allocate a stream id")]
917    UnableToAllocStreamId,
918}
919
920impl From<ResponseParseError> for InternalRequestError {
921    fn from(value: ResponseParseError) -> Self {
922        match value {
923            ResponseParseError::BodyExtensionsParseError(e) => e.into(),
924            ResponseParseError::CqlResponseParseError(e) => e.into(),
925        }
926    }
927}
928
929/// An error type returned from `Connection::parse_response`.
930/// This is driver's internal type.
931#[derive(Error, Debug)]
932pub(crate) enum ResponseParseError {
933    #[error(transparent)]
934    BodyExtensionsParseError(#[from] FrameBodyExtensionsParseError),
935    #[error(transparent)]
936    CqlResponseParseError(#[from] CqlResponseParseError),
937}
938
939/// Error returned from [ClusterState](crate::cluster::ClusterState) APIs.
940#[derive(Clone, Debug, Error)]
941#[non_exhaustive]
942pub enum ClusterStateTokenError {
943    /// Failed to calculate token.
944    #[error(transparent)]
945    TokenCalculation(#[from] TokenCalculationError),
946
947    /// Failed to serialize values required to compute partition key.
948    #[error(transparent)]
949    Serialization(#[from] SerializationError),
950
951    /// ClusterState doesn't currently have metadata for the requested table.
952    #[error("Can't find metadata for requested table ({keyspace}.{table}).")]
953    UnknownTable { keyspace: String, table: String },
954}
955
956#[cfg(test)]
957mod tests {
958    use scylla_cql::Consistency;
959
960    use super::{DbError, ExecutionError, RequestAttemptError, WriteType};
961
962    #[test]
963    fn write_type_from_str() {
964        let test_cases: [(&str, WriteType); 9] = [
965            ("SIMPLE", WriteType::Simple),
966            ("BATCH", WriteType::Batch),
967            ("UNLOGGED_BATCH", WriteType::UnloggedBatch),
968            ("COUNTER", WriteType::Counter),
969            ("BATCH_LOG", WriteType::BatchLog),
970            ("CAS", WriteType::Cas),
971            ("VIEW", WriteType::View),
972            ("CDC", WriteType::Cdc),
973            ("SOMEOTHER", WriteType::Other("SOMEOTHER".to_string())),
974        ];
975
976        for (write_type_str, expected_write_type) in &test_cases {
977            let write_type = WriteType::from(*write_type_str);
978            assert_eq!(write_type, *expected_write_type);
979        }
980    }
981
982    // A test to check that displaying DbError and ExecutionError::DbError works as expected
983    // - displays error description
984    // - displays error parameters
985    // - displays error message
986    // - indented multiline strings don't cause whitespace gaps
987    #[test]
988    fn dberror_full_info() {
989        // Test that DbError::Unavailable is displayed correctly
990        let db_error = DbError::Unavailable {
991            consistency: Consistency::Three,
992            required: 3,
993            alive: 2,
994        };
995
996        let db_error_displayed: String = format!("{}", db_error);
997
998        let mut expected_dberr_msg =
999            "Not enough nodes are alive to satisfy required consistency level ".to_string();
1000        expected_dberr_msg += "(consistency: Three, required: 3, alive: 2)";
1001
1002        assert_eq!(db_error_displayed, expected_dberr_msg);
1003
1004        // Test that ExecutionError::DbError::(DbError::Unavailable) is displayed correctly
1005        let execution_error = ExecutionError::LastAttemptError(RequestAttemptError::DbError(
1006            db_error,
1007            "a message about unavailable error".to_string(),
1008        ));
1009        let execution_error_displayed: String = format!("{}", execution_error);
1010
1011        let mut expected_execution_err_msg = "Database returned an error: ".to_string();
1012        expected_execution_err_msg += &expected_dberr_msg;
1013        expected_execution_err_msg += ", Error message: a message about unavailable error";
1014
1015        assert_eq!(execution_error_displayed, expected_execution_err_msg);
1016    }
1017}