Skip to main content

scylla_cql/frame/response/
error.rs

1//! CQL protocol-level representation of an `ERROR` response.
2
3use crate::Consistency;
4use crate::frame::frame_errors::{CqlErrorParseError, LowLevelDeserializationError};
5use crate::frame::protocol_features::ProtocolFeatures;
6use crate::frame::types;
7use byteorder::ReadBytesExt;
8use bytes::Bytes;
9use thiserror::Error;
10
11/// Represents a CQL protocol-level error that is sent by the database in response to a query
12/// that failed to execute successfully.
13#[derive(Debug, Clone)]
14pub struct Error {
15    /// Error code and other context of the error.
16    pub error: DbError,
17
18    /// The reason for the error, typically a human-readable message.
19    pub reason: String,
20}
21
22fn make_error_field_err(
23    db_error: &'static str,
24    field: &'static str,
25    err: impl Into<LowLevelDeserializationError>,
26) -> CqlErrorParseError {
27    CqlErrorParseError::MalformedErrorField {
28        db_error,
29        field,
30        err: err.into(),
31    }
32}
33
34impl Error {
35    /// Deserializes the error response from the provided buffer.
36    pub fn deserialize(
37        features: &ProtocolFeatures,
38        buf: &mut &[u8],
39    ) -> Result<Self, CqlErrorParseError> {
40        let code = types::read_int(buf)
41            .map_err(|err| CqlErrorParseError::ErrorCodeParseError(err.into()))?;
42        let reason = types::read_string(buf)
43            .map_err(CqlErrorParseError::ReasonParseError)?
44            .to_owned();
45
46        let error: DbError = match code {
47            0x0000 => DbError::ServerError,
48            0x000A => DbError::ProtocolError,
49            0x0100 => DbError::AuthenticationError,
50            0x1000 => DbError::Unavailable {
51                consistency: types::read_consistency(buf)
52                    .map_err(|err| make_error_field_err("UNAVAILABLE", "CONSISTENCY", err))?,
53                required: types::read_int(buf)
54                    .map_err(|err| make_error_field_err("UNAVAILABLE", "REQUIRED", err))?,
55                alive: types::read_int(buf)
56                    .map_err(|err| make_error_field_err("UNAVAILABLE", "ALIVE", err))?,
57            },
58            0x1001 => DbError::Overloaded,
59            0x1002 => DbError::IsBootstrapping,
60            0x1003 => DbError::TruncateError,
61            0x1100 => DbError::WriteTimeout {
62                consistency: types::read_consistency(buf)
63                    .map_err(|err| make_error_field_err("WRITE_TIMEOUT", "CONSISTENCY", err))?,
64                received: types::read_int(buf)
65                    .map_err(|err| make_error_field_err("WRITE_TIMEOUT", "RECEIVED", err))?,
66                required: types::read_int(buf)
67                    .map_err(|err| make_error_field_err("WRITE_TIMEOUT", "REQUIRED", err))?,
68                write_type: WriteType::from(
69                    types::read_string(buf)
70                        .map_err(|err| make_error_field_err("WRITE_TIMEOUT", "WRITE_TYPE", err))?,
71                ),
72            },
73            0x1200 => DbError::ReadTimeout {
74                consistency: types::read_consistency(buf)
75                    .map_err(|err| make_error_field_err("READ_TIMEOUT", "CONSISTENCY", err))?,
76                received: types::read_int(buf)
77                    .map_err(|err| make_error_field_err("READ_TIMEOUT", "RECEIVED", err))?,
78                required: types::read_int(buf)
79                    .map_err(|err| make_error_field_err("READ_TIMEOUT", "REQUIRED", err))?,
80                data_present: buf
81                    .read_u8()
82                    .map_err(|err| make_error_field_err("READ_TIMEOUT", "DATA_PRESENT", err))?
83                    != 0,
84            },
85            0x1300 => DbError::ReadFailure {
86                consistency: types::read_consistency(buf)
87                    .map_err(|err| make_error_field_err("READ_FAILURE", "CONSISTENCY", err))?,
88                received: types::read_int(buf)
89                    .map_err(|err| make_error_field_err("READ_FAILURE", "RECEIVED", err))?,
90                required: types::read_int(buf)
91                    .map_err(|err| make_error_field_err("READ_FAILURE", "REQUIRED", err))?,
92                numfailures: types::read_int(buf)
93                    .map_err(|err| make_error_field_err("READ_FAILURE", "NUM_FAILURES", err))?,
94                data_present: buf
95                    .read_u8()
96                    .map_err(|err| make_error_field_err("READ_FAILURE", "DATA_PRESENT", err))?
97                    != 0,
98            },
99            0x1400 => DbError::FunctionFailure {
100                keyspace: types::read_string(buf)
101                    .map_err(|err| make_error_field_err("FUNCTION_FAILURE", "KEYSPACE", err))?
102                    .to_string(),
103                function: types::read_string(buf)
104                    .map_err(|err| make_error_field_err("FUNCTION_FAILURE", "FUNCTION", err))?
105                    .to_string(),
106                arg_types: types::read_string_list(buf)
107                    .map_err(|err| make_error_field_err("FUNCTION_FAILURE", "ARG_TYPES", err))?,
108            },
109            0x1500 => DbError::WriteFailure {
110                consistency: types::read_consistency(buf)
111                    .map_err(|err| make_error_field_err("WRITE_FAILURE", "CONSISTENCY", err))?,
112                received: types::read_int(buf)
113                    .map_err(|err| make_error_field_err("WRITE_FAILURE", "RECEIVED", err))?,
114                required: types::read_int(buf)
115                    .map_err(|err| make_error_field_err("WRITE_FAILURE", "REQUIRED", err))?,
116                numfailures: types::read_int(buf)
117                    .map_err(|err| make_error_field_err("WRITE_FAILURE", "NUM_FAILURES", err))?,
118                write_type: WriteType::from(
119                    types::read_string(buf)
120                        .map_err(|err| make_error_field_err("WRITE_FAILURE", "WRITE_TYPE", err))?,
121                ),
122            },
123            0x2000 => DbError::SyntaxError,
124            0x2100 => DbError::Unauthorized,
125            0x2200 => DbError::Invalid,
126            0x2300 => DbError::ConfigError,
127            0x2400 => DbError::AlreadyExists {
128                keyspace: types::read_string(buf)
129                    .map_err(|err| make_error_field_err("ALREADY_EXISTS", "KEYSPACE", err))?
130                    .to_string(),
131                table: types::read_string(buf)
132                    .map_err(|err| make_error_field_err("ALREADY_EXISTS", "TABLE", err))?
133                    .to_string(),
134            },
135            0x2500 => DbError::Unprepared {
136                statement_id: Bytes::from(
137                    types::read_short_bytes(buf)
138                        .map_err(|err| make_error_field_err("UNPREPARED", "STATEMENT_ID", err))?
139                        .to_owned(),
140                ),
141            },
142            code if Some(code) == features.rate_limit_error => {
143                DbError::RateLimitReached {
144                    op_type: OperationType::from(buf.read_u8().map_err(|err| {
145                        make_error_field_err("RATE_LIMIT_REACHED", "OP_TYPE", err)
146                    })?),
147                    rejected_by_coordinator: buf.read_u8().map_err(|err| {
148                        make_error_field_err("RATE_LIMIT_REACHED", "REJECTED_BY_COORDINATOR", err)
149                    })? != 0,
150                }
151            }
152            _ => DbError::Other(code),
153        };
154
155        Ok(Error { error, reason })
156    }
157}
158
159/// An error sent from the database in response to a query
160/// as described in the [specification](https://github.com/apache/cassandra/blob/5ed5e84613ef0e9664a774493db7d2604e3596e0/doc/native_protocol_v4.spec#L1029)\
161#[derive(Error, Debug, Clone, PartialEq, Eq)]
162#[non_exhaustive]
163pub enum DbError {
164    /// The submitted query has a syntax error
165    #[error("The submitted query has a syntax error")]
166    SyntaxError,
167
168    /// The query is syntactically correct but invalid
169    #[error("The query is syntactically correct but invalid")]
170    Invalid,
171
172    /// Attempted to create a keyspace or a table that was already existing
173    #[error(
174        "Attempted to create a keyspace or a table that was already existing \
175        (keyspace: {keyspace}, table: {table})"
176    )]
177    AlreadyExists {
178        /// Created keyspace name or name of the keyspace in which table was created
179        keyspace: String,
180        /// Name of the table created, in case of keyspace creation it's an empty string
181        table: String,
182    },
183
184    /// User defined function failed during execution
185    #[error(
186        "User defined function failed during execution \
187        (keyspace: {keyspace}, function: {function}, arg_types: {arg_types:?})"
188    )]
189    FunctionFailure {
190        /// Keyspace of the failed function
191        keyspace: String,
192        /// Name of the failed function
193        function: String,
194        /// Types of arguments passed to the function
195        arg_types: Vec<String>,
196    },
197
198    /// Authentication failed - bad credentials
199    #[error("Authentication failed - bad credentials")]
200    AuthenticationError,
201
202    /// The logged user doesn't have the right to perform the query
203    #[error("The logged user doesn't have the right to perform the query")]
204    Unauthorized,
205
206    /// The query is invalid because of some configuration issue
207    #[error("The query is invalid because of some configuration issue")]
208    ConfigError,
209
210    /// Not enough nodes are alive to satisfy required consistency level
211    #[error(
212        "Not enough nodes are alive to satisfy required consistency level \
213        (consistency: {consistency}, required: {required}, alive: {alive})"
214    )]
215    Unavailable {
216        /// Consistency level of the query
217        consistency: Consistency,
218        /// Number of nodes required to be alive to satisfy required consistency level
219        required: i32,
220        /// Found number of active nodes
221        alive: i32,
222    },
223
224    /// The request cannot be processed because the coordinator node is overloaded
225    #[error("The request cannot be processed because the coordinator node is overloaded")]
226    Overloaded,
227
228    /// The coordinator node is still bootstrapping
229    #[error("The coordinator node is still bootstrapping")]
230    IsBootstrapping,
231
232    /// Error during truncate operation
233    #[error("Error during truncate operation")]
234    TruncateError,
235
236    /// Not enough nodes responded to the read request in time to satisfy required consistency level
237    #[error(
238        "Not enough nodes responded to the read request in time to satisfy required consistency level \
239            (consistency: {consistency}, received: {received}, required: {required}, data_present: {data_present})"
240    )]
241    ReadTimeout {
242        /// Consistency level of the query
243        consistency: Consistency,
244        /// Number of nodes that responded to the read request
245        received: i32,
246        /// Number of nodes required to respond to satisfy required consistency level
247        required: i32,
248        /// Replica that was asked for data has responded
249        data_present: bool,
250    },
251
252    /// Not enough nodes responded to the write request in time to satisfy required consistency level
253    #[error(
254        "Not enough nodes responded to the write request in time to satisfy required consistency level \
255            (consistency: {consistency}, received: {received}, required: {required}, write_type: {write_type})"
256    )]
257    WriteTimeout {
258        /// Consistency level of the query
259        consistency: Consistency,
260        /// Number of nodes that responded to the write request
261        received: i32,
262        /// Number of nodes required to respond to satisfy required consistency level
263        required: i32,
264        /// Type of write operation requested
265        write_type: WriteType,
266    },
267
268    /// A non-timeout error during a read request
269    #[error(
270        "A non-timeout error during a read request \
271        (consistency: {consistency}, received: {received}, required: {required}, \
272        numfailures: {numfailures}, data_present: {data_present})"
273    )]
274    ReadFailure {
275        /// Consistency level of the query
276        consistency: Consistency,
277        /// Number of nodes that responded to the read request
278        received: i32,
279        /// Number of nodes required to respond to satisfy required consistency level
280        required: i32,
281        /// Number of nodes that experience a failure while executing the request
282        numfailures: i32,
283        /// Replica that was asked for data has responded
284        data_present: bool,
285    },
286
287    /// A non-timeout error during a write request
288    #[error(
289        "A non-timeout error during a write request \
290        (consistency: {consistency}, received: {received}, required: {required}, \
291        numfailures: {numfailures}, write_type: {write_type}"
292    )]
293    WriteFailure {
294        /// Consistency level of the query
295        consistency: Consistency,
296        /// Number of nodes that responded to the read request
297        received: i32,
298        /// Number of nodes required to respond to satisfy required consistency level
299        required: i32,
300        /// Number of nodes that experience a failure while executing the request
301        numfailures: i32,
302        /// Type of write operation requested
303        write_type: WriteType,
304    },
305
306    /// Tried to execute a prepared statement that is not prepared. Driver should prepare it again
307    #[error(
308        "Tried to execute a prepared statement that is not prepared. Driver should prepare it again"
309    )]
310    Unprepared {
311        /// Statement id of the requested prepared query
312        statement_id: Bytes,
313    },
314
315    /// Internal server error. This indicates a server-side bug
316    #[error("Internal server error. This indicates a server-side bug")]
317    ServerError,
318
319    /// Invalid protocol message received from the driver
320    #[error("Invalid protocol message received from the driver")]
321    ProtocolError,
322
323    /// Rate limit was exceeded for a partition affected by the request.
324    /// (Scylla-specific)
325    /// TODO: Should this have a "Scylla" prefix?
326    #[error("Rate limit was exceeded for a partition affected by the request")]
327    RateLimitReached {
328        /// Type of the operation rejected by rate limiting.
329        op_type: OperationType,
330        /// Whether the operation was rate limited on the coordinator or not.
331        /// Writes rejected on the coordinator are guaranteed not to be applied
332        /// on any replica.
333        rejected_by_coordinator: bool,
334    },
335
336    /// Other error code not specified in the specification
337    #[error("Other error not specified in the specification. Error code: {0}")]
338    Other(i32),
339}
340
341impl DbError {
342    /// Returns the error code for this error, as defined in the CQL protocol specification.
343    pub fn code(&self, protocol_features: &ProtocolFeatures) -> i32 {
344        match self {
345            DbError::ServerError => 0x0000,
346            DbError::ProtocolError => 0x000A,
347            DbError::AuthenticationError => 0x0100,
348            DbError::Unavailable {
349                consistency: _,
350                required: _,
351                alive: _,
352            } => 0x1000,
353            DbError::Overloaded => 0x1001,
354            DbError::IsBootstrapping => 0x1002,
355            DbError::TruncateError => 0x1003,
356            DbError::WriteTimeout {
357                consistency: _,
358                received: _,
359                required: _,
360                write_type: _,
361            } => 0x1100,
362            DbError::ReadTimeout {
363                consistency: _,
364                received: _,
365                required: _,
366                data_present: _,
367            } => 0x1200,
368            DbError::ReadFailure {
369                consistency: _,
370                received: _,
371                required: _,
372                numfailures: _,
373                data_present: _,
374            } => 0x1300,
375            DbError::FunctionFailure {
376                keyspace: _,
377                function: _,
378                arg_types: _,
379            } => 0x1400,
380            DbError::WriteFailure {
381                consistency: _,
382                received: _,
383                required: _,
384                numfailures: _,
385                write_type: _,
386            } => 0x1500,
387            DbError::SyntaxError => 0x2000,
388            DbError::Unauthorized => 0x2100,
389            DbError::Invalid => 0x2200,
390            DbError::ConfigError => 0x2300,
391            DbError::AlreadyExists {
392                keyspace: _,
393                table: _,
394            } => 0x2400,
395            DbError::Unprepared { statement_id: _ } => 0x2500,
396            DbError::Other(code) => *code,
397            DbError::RateLimitReached {
398                op_type: _,
399                rejected_by_coordinator: _,
400            } => protocol_features.rate_limit_error.unwrap(),
401        }
402    }
403
404    /// Decides whether the error can be ignored. If true, the driver can perform
405    /// a speculative retry to the next target.
406    pub fn can_speculative_retry(&self) -> bool {
407        // Do not remove this lint!
408        // It's there for a reason - we don't want new variants
409        // automatically fall under `_` pattern when they are introduced.
410        #[deny(clippy::wildcard_enum_match_arm)]
411        match self {
412            // Errors that will almost certainly appear on other nodes as well
413            DbError::SyntaxError
414            | DbError::Invalid
415            | DbError::AlreadyExists { .. }
416            | DbError::Unauthorized
417            | DbError::ProtocolError => false,
418
419            // Errors that should not appear there - thus, should not be ignored.
420            DbError::AuthenticationError | DbError::Other(_) => false,
421
422            // For now, let's assume that UDF failure is not transient - don't ignore it
423            // TODO: investigate
424            DbError::FunctionFailure { .. } => false,
425
426            // Not sure when these can appear - don't ignore them
427            // TODO: Investigate these errors
428            DbError::ConfigError | DbError::TruncateError => false,
429
430            // Errors that we can ignore and perform a retry on some other node
431            DbError::Unavailable { .. }
432            | DbError::Overloaded
433            | DbError::IsBootstrapping
434            | DbError::ReadTimeout { .. }
435            | DbError::WriteTimeout { .. }
436            | DbError::ReadFailure { .. }
437            | DbError::WriteFailure { .. }
438            // Preparation may succeed on some other node.
439            | DbError::Unprepared { .. }
440            | DbError::ServerError
441            | DbError::RateLimitReached { .. } => true,
442        }
443    }
444}
445
446/// Type of the operation rejected by rate limiting
447#[derive(Debug, Clone, PartialEq, Eq)]
448pub enum OperationType {
449    Read,
450    Write,
451    Other(u8),
452}
453
454/// Type of write operation requested
455#[derive(Debug, Clone, PartialEq, Eq)]
456pub enum WriteType {
457    /// Non-batched non-counter write
458    Simple,
459    /// Logged batch write. If this type is received, it means the batch log has been successfully written
460    /// (otherwise BatchLog type would be present)
461    Batch,
462    /// Unlogged batch. No batch log write has been attempted.
463    UnloggedBatch,
464    /// Counter write (batched or not)
465    Counter,
466    /// Timeout occurred during the write to the batch log when a logged batch was requested
467    BatchLog,
468    /// Timeout occurred during Compare And Set write/update
469    Cas,
470    /// Write involves VIEW update and failure to acquire local view(MV) lock for key within timeout
471    View,
472    /// Timeout occurred when a cdc_total_space_in_mb is exceeded when doing a write to data tracked by cdc
473    Cdc,
474    /// Other type not specified in the specification
475    Other(String),
476}
477
478impl std::fmt::Display for WriteType {
479    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
480        write!(f, "{self:?}")
481    }
482}
483
484impl From<u8> for OperationType {
485    fn from(operation_type: u8) -> OperationType {
486        match operation_type {
487            0 => OperationType::Read,
488            1 => OperationType::Write,
489            other => OperationType::Other(other),
490        }
491    }
492}
493
494impl From<&str> for WriteType {
495    fn from(write_type_str: &str) -> WriteType {
496        match write_type_str {
497            "SIMPLE" => WriteType::Simple,
498            "BATCH" => WriteType::Batch,
499            "UNLOGGED_BATCH" => WriteType::UnloggedBatch,
500            "COUNTER" => WriteType::Counter,
501            "BATCH_LOG" => WriteType::BatchLog,
502            "CAS" => WriteType::Cas,
503            "VIEW" => WriteType::View,
504            "CDC" => WriteType::Cdc,
505            _ => WriteType::Other(write_type_str.to_string()),
506        }
507    }
508}
509
510impl WriteType {
511    /// Returns the string representation of the write type as defined in the CQL protocol specification.
512    pub fn as_str(&self) -> &str {
513        match self {
514            WriteType::Simple => "SIMPLE",
515            WriteType::Batch => "BATCH",
516            WriteType::UnloggedBatch => "UNLOGGED_BATCH",
517            WriteType::Counter => "COUNTER",
518            WriteType::BatchLog => "BATCH_LOG",
519            WriteType::Cas => "CAS",
520            WriteType::View => "VIEW",
521            WriteType::Cdc => "CDC",
522            WriteType::Other(write_type) => write_type.as_str(),
523        }
524    }
525}
526
527#[cfg(test)]
528mod tests {
529    use super::{DbError, Error, OperationType, WriteType};
530    use crate::Consistency;
531    use crate::frame::protocol_features::ProtocolFeatures;
532    use bytes::Bytes;
533    use std::convert::TryInto;
534
535    // Serializes the beginning of an ERROR response - error code and message
536    // All custom data depending on the error type is appended after these bytes
537    fn make_error_request_bytes(error_code: i32, message: &str) -> Vec<u8> {
538        let mut bytes: Vec<u8> = Vec::new();
539        let message_len: u16 = message.len().try_into().unwrap();
540
541        bytes.extend(error_code.to_be_bytes());
542        bytes.extend(message_len.to_be_bytes());
543        bytes.extend(message.as_bytes());
544
545        bytes
546    }
547
548    // Tests deserialization of all errors without and additional data
549    #[test]
550    fn deserialize_simple_errors() {
551        let simple_error_mappings: [(i32, DbError); 11] = [
552            (0x0000, DbError::ServerError),
553            (0x000A, DbError::ProtocolError),
554            (0x0100, DbError::AuthenticationError),
555            (0x1001, DbError::Overloaded),
556            (0x1002, DbError::IsBootstrapping),
557            (0x1003, DbError::TruncateError),
558            (0x2000, DbError::SyntaxError),
559            (0x2100, DbError::Unauthorized),
560            (0x2200, DbError::Invalid),
561            (0x2300, DbError::ConfigError),
562            (0x1234, DbError::Other(0x1234)),
563        ];
564
565        let features = ProtocolFeatures::default();
566
567        for (error_code, expected_error) in &simple_error_mappings {
568            let bytes: Vec<u8> = make_error_request_bytes(*error_code, "simple message");
569            let error: Error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
570            assert_eq!(error.error, *expected_error);
571            assert_eq!(error.reason, "simple message");
572        }
573    }
574
575    #[test]
576    fn deserialize_unavailable() {
577        let features = ProtocolFeatures::default();
578
579        let mut bytes = make_error_request_bytes(0x1000, "message 2");
580        bytes.extend(1_i16.to_be_bytes());
581        bytes.extend(2_i32.to_be_bytes());
582        bytes.extend(3_i32.to_be_bytes());
583
584        let error: Error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
585
586        assert_eq!(
587            error.error,
588            DbError::Unavailable {
589                consistency: Consistency::One,
590                required: 2,
591                alive: 3,
592            }
593        );
594        assert_eq!(error.reason, "message 2");
595    }
596
597    #[test]
598    fn deserialize_write_timeout() {
599        let features = ProtocolFeatures::default();
600
601        let mut bytes = make_error_request_bytes(0x1100, "message 2");
602        bytes.extend(0x0004_i16.to_be_bytes());
603        bytes.extend((-5_i32).to_be_bytes());
604        bytes.extend(100_i32.to_be_bytes());
605
606        let write_type_str = "SIMPLE";
607        let write_type_str_len: u16 = write_type_str.len().try_into().unwrap();
608        bytes.extend(write_type_str_len.to_be_bytes());
609        bytes.extend(write_type_str.as_bytes());
610
611        let error: Error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
612
613        assert_eq!(
614            error.error,
615            DbError::WriteTimeout {
616                consistency: Consistency::Quorum,
617                received: -5, // Allow negative values when they don't make sense, it's better than crashing with ProtocolError
618                required: 100,
619                write_type: WriteType::Simple,
620            }
621        );
622        assert_eq!(error.reason, "message 2");
623    }
624
625    #[test]
626    fn deserialize_read_timeout() {
627        let features = ProtocolFeatures::default();
628
629        let mut bytes = make_error_request_bytes(0x1200, "message 2");
630        bytes.extend(0x0002_i16.to_be_bytes());
631        bytes.extend(8_i32.to_be_bytes());
632        bytes.extend(32_i32.to_be_bytes());
633        bytes.push(0_u8);
634
635        let error: Error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
636
637        assert_eq!(
638            error.error,
639            DbError::ReadTimeout {
640                consistency: Consistency::Two,
641                received: 8,
642                required: 32,
643                data_present: false,
644            }
645        );
646        assert_eq!(error.reason, "message 2");
647    }
648
649    #[test]
650    fn deserialize_read_failure() {
651        let features = ProtocolFeatures::default();
652
653        let mut bytes = make_error_request_bytes(0x1300, "message 2");
654        bytes.extend(0x0003_i16.to_be_bytes());
655        bytes.extend(4_i32.to_be_bytes());
656        bytes.extend(5_i32.to_be_bytes());
657        bytes.extend(6_i32.to_be_bytes());
658        bytes.push(123_u8); // Any non-zero value means data_present is true
659
660        let error: Error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
661
662        assert_eq!(
663            error.error,
664            DbError::ReadFailure {
665                consistency: Consistency::Three,
666                received: 4,
667                required: 5,
668                numfailures: 6,
669                data_present: true,
670            }
671        );
672        assert_eq!(error.reason, "message 2");
673    }
674
675    #[test]
676    fn deserialize_function_failure() {
677        let features = ProtocolFeatures::default();
678
679        let mut bytes = make_error_request_bytes(0x1400, "message 2");
680
681        let keyspace_name: &str = "keyspace_name";
682        let keyspace_name_len: u16 = keyspace_name.len().try_into().unwrap();
683
684        let function_name: &str = "function_name";
685        let function_name_len: u16 = function_name.len().try_into().unwrap();
686
687        let type1: &str = "type1";
688        let type1_len: u16 = type1.len().try_into().unwrap();
689
690        let type2: &str = "type2";
691        let type2_len: u16 = type1.len().try_into().unwrap();
692
693        bytes.extend(keyspace_name_len.to_be_bytes());
694        bytes.extend(keyspace_name.as_bytes());
695        bytes.extend(function_name_len.to_be_bytes());
696        bytes.extend(function_name.as_bytes());
697        bytes.extend(2_i16.to_be_bytes());
698        bytes.extend(type1_len.to_be_bytes());
699        bytes.extend(type1.as_bytes());
700        bytes.extend(type2_len.to_be_bytes());
701        bytes.extend(type2.as_bytes());
702
703        let error: Error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
704
705        assert_eq!(
706            error.error,
707            DbError::FunctionFailure {
708                keyspace: "keyspace_name".to_string(),
709                function: "function_name".to_string(),
710                arg_types: vec!["type1".to_string(), "type2".to_string()]
711            }
712        );
713        assert_eq!(error.reason, "message 2");
714    }
715
716    #[test]
717    fn deserialize_write_failure() {
718        let features = ProtocolFeatures::default();
719
720        let mut bytes = make_error_request_bytes(0x1500, "message 2");
721
722        bytes.extend(0x0000_i16.to_be_bytes());
723        bytes.extend(2_i32.to_be_bytes());
724        bytes.extend(4_i32.to_be_bytes());
725        bytes.extend(8_i32.to_be_bytes());
726
727        let write_type_str = "COUNTER";
728        let write_type_str_len: u16 = write_type_str.len().try_into().unwrap();
729        bytes.extend(write_type_str_len.to_be_bytes());
730        bytes.extend(write_type_str.as_bytes());
731
732        let error: Error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
733
734        assert_eq!(
735            error.error,
736            DbError::WriteFailure {
737                consistency: Consistency::Any,
738                received: 2,
739                required: 4,
740                numfailures: 8,
741                write_type: WriteType::Counter,
742            }
743        );
744        assert_eq!(error.reason, "message 2");
745    }
746
747    #[test]
748    fn deserialize_already_exists() {
749        let features = ProtocolFeatures::default();
750
751        let mut bytes = make_error_request_bytes(0x2400, "message 2");
752
753        let keyspace_name: &str = "keyspace_name";
754        let keyspace_name_len: u16 = keyspace_name.len().try_into().unwrap();
755
756        let table_name: &str = "table_name";
757        let table_name_len: u16 = table_name.len().try_into().unwrap();
758
759        bytes.extend(keyspace_name_len.to_be_bytes());
760        bytes.extend(keyspace_name.as_bytes());
761        bytes.extend(table_name_len.to_be_bytes());
762        bytes.extend(table_name.as_bytes());
763
764        let error: Error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
765
766        assert_eq!(
767            error.error,
768            DbError::AlreadyExists {
769                keyspace: "keyspace_name".to_string(),
770                table: "table_name".to_string(),
771            }
772        );
773        assert_eq!(error.reason, "message 2");
774    }
775
776    #[test]
777    fn deserialize_unprepared() {
778        let features = ProtocolFeatures::default();
779
780        let mut bytes = make_error_request_bytes(0x2500, "message 3");
781        let statement_id = b"deadbeef";
782        bytes.extend((statement_id.len() as i16).to_be_bytes());
783        bytes.extend(statement_id);
784
785        let error: Error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
786
787        assert_eq!(
788            error.error,
789            DbError::Unprepared {
790                statement_id: Bytes::from_static(b"deadbeef")
791            }
792        );
793        assert_eq!(error.reason, "message 3");
794    }
795
796    #[test]
797    fn deserialize_rate_limit_error() {
798        let features = ProtocolFeatures {
799            rate_limit_error: Some(0x4321),
800            ..Default::default()
801        };
802        let mut bytes = make_error_request_bytes(0x4321, "message 1");
803        bytes.extend([0u8]); // Read type
804        bytes.extend([1u8]); // Rejected by coordinator
805        let error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
806
807        assert_eq!(
808            error.error,
809            DbError::RateLimitReached {
810                op_type: OperationType::Read,
811                rejected_by_coordinator: true,
812            }
813        );
814        assert_eq!(error.reason, "message 1");
815
816        let features = ProtocolFeatures {
817            rate_limit_error: Some(0x8765),
818            ..Default::default()
819        };
820        let mut bytes = make_error_request_bytes(0x8765, "message 2");
821        bytes.extend([1u8]); // Write type
822        bytes.extend([0u8]); // Not rejected by coordinator
823        let error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
824
825        assert_eq!(
826            error.error,
827            DbError::RateLimitReached {
828                op_type: OperationType::Write,
829                rejected_by_coordinator: false,
830            }
831        );
832        assert_eq!(error.reason, "message 2");
833    }
834}