scylla_cql/frame/response/
error.rs

1//! CQL protocol-level representation of an `ERROR` response.
2
3use crate::frame::frame_errors::{CqlErrorParseError, LowLevelDeserializationError};
4use crate::frame::protocol_features::ProtocolFeatures;
5use crate::frame::types;
6use crate::Consistency;
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("Not enough nodes responded to the read request in time to satisfy required consistency level \
238            (consistency: {consistency}, received: {received}, required: {required}, data_present: {data_present})")]
239    ReadTimeout {
240        /// Consistency level of the query
241        consistency: Consistency,
242        /// Number of nodes that responded to the read request
243        received: i32,
244        /// Number of nodes required to respond to satisfy required consistency level
245        required: i32,
246        /// Replica that was asked for data has responded
247        data_present: bool,
248    },
249
250    /// Not enough nodes responded to the write request in time to satisfy required consistency level
251    #[error("Not enough nodes responded to the write request in time to satisfy required consistency level \
252            (consistency: {consistency}, received: {received}, required: {required}, write_type: {write_type})")]
253    WriteTimeout {
254        /// Consistency level of the query
255        consistency: Consistency,
256        /// Number of nodes that responded to the write request
257        received: i32,
258        /// Number of nodes required to respond to satisfy required consistency level
259        required: i32,
260        /// Type of write operation requested
261        write_type: WriteType,
262    },
263
264    /// A non-timeout error during a read request
265    #[error(
266        "A non-timeout error during a read request \
267        (consistency: {consistency}, received: {received}, required: {required}, \
268        numfailures: {numfailures}, data_present: {data_present})"
269    )]
270    ReadFailure {
271        /// Consistency level of the query
272        consistency: Consistency,
273        /// Number of nodes that responded to the read request
274        received: i32,
275        /// Number of nodes required to respond to satisfy required consistency level
276        required: i32,
277        /// Number of nodes that experience a failure while executing the request
278        numfailures: i32,
279        /// Replica that was asked for data has responded
280        data_present: bool,
281    },
282
283    /// A non-timeout error during a write request
284    #[error(
285        "A non-timeout error during a write request \
286        (consistency: {consistency}, received: {received}, required: {required}, \
287        numfailures: {numfailures}, write_type: {write_type}"
288    )]
289    WriteFailure {
290        /// Consistency level of the query
291        consistency: Consistency,
292        /// Number of nodes that responded to the read request
293        received: i32,
294        /// Number of nodes required to respond to satisfy required consistency level
295        required: i32,
296        /// Number of nodes that experience a failure while executing the request
297        numfailures: i32,
298        /// Type of write operation requested
299        write_type: WriteType,
300    },
301
302    /// Tried to execute a prepared statement that is not prepared. Driver should prepare it again
303    #[error(
304        "Tried to execute a prepared statement that is not prepared. Driver should prepare it again"
305    )]
306    Unprepared {
307        /// Statement id of the requested prepared query
308        statement_id: Bytes,
309    },
310
311    /// Internal server error. This indicates a server-side bug
312    #[error("Internal server error. This indicates a server-side bug")]
313    ServerError,
314
315    /// Invalid protocol message received from the driver
316    #[error("Invalid protocol message received from the driver")]
317    ProtocolError,
318
319    /// Rate limit was exceeded for a partition affected by the request.
320    /// (Scylla-specific)
321    /// TODO: Should this have a "Scylla" prefix?
322    #[error("Rate limit was exceeded for a partition affected by the request")]
323    RateLimitReached {
324        /// Type of the operation rejected by rate limiting.
325        op_type: OperationType,
326        /// Whether the operation was rate limited on the coordinator or not.
327        /// Writes rejected on the coordinator are guaranteed not to be applied
328        /// on any replica.
329        rejected_by_coordinator: bool,
330    },
331
332    /// Other error code not specified in the specification
333    #[error("Other error not specified in the specification. Error code: {0}")]
334    Other(i32),
335}
336
337impl DbError {
338    /// Returns the error code for this error, as defined in the CQL protocol specification.
339    pub fn code(&self, protocol_features: &ProtocolFeatures) -> i32 {
340        match self {
341            DbError::ServerError => 0x0000,
342            DbError::ProtocolError => 0x000A,
343            DbError::AuthenticationError => 0x0100,
344            DbError::Unavailable {
345                consistency: _,
346                required: _,
347                alive: _,
348            } => 0x1000,
349            DbError::Overloaded => 0x1001,
350            DbError::IsBootstrapping => 0x1002,
351            DbError::TruncateError => 0x1003,
352            DbError::WriteTimeout {
353                consistency: _,
354                received: _,
355                required: _,
356                write_type: _,
357            } => 0x1100,
358            DbError::ReadTimeout {
359                consistency: _,
360                received: _,
361                required: _,
362                data_present: _,
363            } => 0x1200,
364            DbError::ReadFailure {
365                consistency: _,
366                received: _,
367                required: _,
368                numfailures: _,
369                data_present: _,
370            } => 0x1300,
371            DbError::FunctionFailure {
372                keyspace: _,
373                function: _,
374                arg_types: _,
375            } => 0x1400,
376            DbError::WriteFailure {
377                consistency: _,
378                received: _,
379                required: _,
380                numfailures: _,
381                write_type: _,
382            } => 0x1500,
383            DbError::SyntaxError => 0x2000,
384            DbError::Unauthorized => 0x2100,
385            DbError::Invalid => 0x2200,
386            DbError::ConfigError => 0x2300,
387            DbError::AlreadyExists {
388                keyspace: _,
389                table: _,
390            } => 0x2400,
391            DbError::Unprepared { statement_id: _ } => 0x2500,
392            DbError::Other(code) => *code,
393            DbError::RateLimitReached {
394                op_type: _,
395                rejected_by_coordinator: _,
396            } => protocol_features.rate_limit_error.unwrap(),
397        }
398    }
399
400    /// Decides whether the error can be ignored. If true, the driver can perform
401    /// a speculative retry to the next target.
402    pub fn can_speculative_retry(&self) -> bool {
403        // Do not remove this lint!
404        // It's there for a reason - we don't want new variants
405        // automatically fall under `_` pattern when they are introduced.
406        #[deny(clippy::wildcard_enum_match_arm)]
407        match self {
408            // Errors that will almost certainly appear on other nodes as well
409            DbError::SyntaxError
410            | DbError::Invalid
411            | DbError::AlreadyExists { .. }
412            | DbError::Unauthorized
413            | DbError::ProtocolError => false,
414
415            // Errors that should not appear there - thus, should not be ignored.
416            DbError::AuthenticationError | DbError::Other(_) => false,
417
418            // For now, let's assume that UDF failure is not transient - don't ignore it
419            // TODO: investigate
420            DbError::FunctionFailure { .. } => false,
421
422            // Not sure when these can appear - don't ignore them
423            // TODO: Investigate these errors
424            DbError::ConfigError | DbError::TruncateError => false,
425
426            // Errors that we can ignore and perform a retry on some other node
427            DbError::Unavailable { .. }
428            | DbError::Overloaded
429            | DbError::IsBootstrapping
430            | DbError::ReadTimeout { .. }
431            | DbError::WriteTimeout { .. }
432            | DbError::ReadFailure { .. }
433            | DbError::WriteFailure { .. }
434            // Preparation may succeed on some other node.
435            | DbError::Unprepared { .. }
436            | DbError::ServerError
437            | DbError::RateLimitReached { .. } => true,
438        }
439    }
440}
441
442/// Type of the operation rejected by rate limiting
443#[derive(Debug, Clone, PartialEq, Eq)]
444pub enum OperationType {
445    Read,
446    Write,
447    Other(u8),
448}
449
450/// Type of write operation requested
451#[derive(Debug, Clone, PartialEq, Eq)]
452pub enum WriteType {
453    /// Non-batched non-counter write
454    Simple,
455    /// Logged batch write. If this type is received, it means the batch log has been successfully written
456    /// (otherwise BatchLog type would be present)
457    Batch,
458    /// Unlogged batch. No batch log write has been attempted.
459    UnloggedBatch,
460    /// Counter write (batched or not)
461    Counter,
462    /// Timeout occurred during the write to the batch log when a logged batch was requested
463    BatchLog,
464    /// Timeout occurred during Compare And Set write/update
465    Cas,
466    /// Write involves VIEW update and failure to acquire local view(MV) lock for key within timeout
467    View,
468    /// Timeout occurred when a cdc_total_space_in_mb is exceeded when doing a write to data tracked by cdc
469    Cdc,
470    /// Other type not specified in the specification
471    Other(String),
472}
473
474impl std::fmt::Display for WriteType {
475    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
476        write!(f, "{self:?}")
477    }
478}
479
480impl From<u8> for OperationType {
481    fn from(operation_type: u8) -> OperationType {
482        match operation_type {
483            0 => OperationType::Read,
484            1 => OperationType::Write,
485            other => OperationType::Other(other),
486        }
487    }
488}
489
490impl From<&str> for WriteType {
491    fn from(write_type_str: &str) -> WriteType {
492        match write_type_str {
493            "SIMPLE" => WriteType::Simple,
494            "BATCH" => WriteType::Batch,
495            "UNLOGGED_BATCH" => WriteType::UnloggedBatch,
496            "COUNTER" => WriteType::Counter,
497            "BATCH_LOG" => WriteType::BatchLog,
498            "CAS" => WriteType::Cas,
499            "VIEW" => WriteType::View,
500            "CDC" => WriteType::Cdc,
501            _ => WriteType::Other(write_type_str.to_string()),
502        }
503    }
504}
505
506impl WriteType {
507    /// Returns the string representation of the write type as defined in the CQL protocol specification.
508    pub fn as_str(&self) -> &str {
509        match self {
510            WriteType::Simple => "SIMPLE",
511            WriteType::Batch => "BATCH",
512            WriteType::UnloggedBatch => "UNLOGGED_BATCH",
513            WriteType::Counter => "COUNTER",
514            WriteType::BatchLog => "BATCH_LOG",
515            WriteType::Cas => "CAS",
516            WriteType::View => "VIEW",
517            WriteType::Cdc => "CDC",
518            WriteType::Other(write_type) => write_type.as_str(),
519        }
520    }
521}
522
523#[cfg(test)]
524mod tests {
525    use super::{DbError, Error, OperationType, WriteType};
526    use crate::frame::protocol_features::ProtocolFeatures;
527    use crate::Consistency;
528    use bytes::Bytes;
529    use std::convert::TryInto;
530
531    // Serializes the beginning of an ERROR response - error code and message
532    // All custom data depending on the error type is appended after these bytes
533    fn make_error_request_bytes(error_code: i32, message: &str) -> Vec<u8> {
534        let mut bytes: Vec<u8> = Vec::new();
535        let message_len: u16 = message.len().try_into().unwrap();
536
537        bytes.extend(error_code.to_be_bytes());
538        bytes.extend(message_len.to_be_bytes());
539        bytes.extend(message.as_bytes());
540
541        bytes
542    }
543
544    // Tests deserialization of all errors without and additional data
545    #[test]
546    fn deserialize_simple_errors() {
547        let simple_error_mappings: [(i32, DbError); 11] = [
548            (0x0000, DbError::ServerError),
549            (0x000A, DbError::ProtocolError),
550            (0x0100, DbError::AuthenticationError),
551            (0x1001, DbError::Overloaded),
552            (0x1002, DbError::IsBootstrapping),
553            (0x1003, DbError::TruncateError),
554            (0x2000, DbError::SyntaxError),
555            (0x2100, DbError::Unauthorized),
556            (0x2200, DbError::Invalid),
557            (0x2300, DbError::ConfigError),
558            (0x1234, DbError::Other(0x1234)),
559        ];
560
561        let features = ProtocolFeatures::default();
562
563        for (error_code, expected_error) in &simple_error_mappings {
564            let bytes: Vec<u8> = make_error_request_bytes(*error_code, "simple message");
565            let error: Error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
566            assert_eq!(error.error, *expected_error);
567            assert_eq!(error.reason, "simple message");
568        }
569    }
570
571    #[test]
572    fn deserialize_unavailable() {
573        let features = ProtocolFeatures::default();
574
575        let mut bytes = make_error_request_bytes(0x1000, "message 2");
576        bytes.extend(1_i16.to_be_bytes());
577        bytes.extend(2_i32.to_be_bytes());
578        bytes.extend(3_i32.to_be_bytes());
579
580        let error: Error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
581
582        assert_eq!(
583            error.error,
584            DbError::Unavailable {
585                consistency: Consistency::One,
586                required: 2,
587                alive: 3,
588            }
589        );
590        assert_eq!(error.reason, "message 2");
591    }
592
593    #[test]
594    fn deserialize_write_timeout() {
595        let features = ProtocolFeatures::default();
596
597        let mut bytes = make_error_request_bytes(0x1100, "message 2");
598        bytes.extend(0x0004_i16.to_be_bytes());
599        bytes.extend((-5_i32).to_be_bytes());
600        bytes.extend(100_i32.to_be_bytes());
601
602        let write_type_str = "SIMPLE";
603        let write_type_str_len: u16 = write_type_str.len().try_into().unwrap();
604        bytes.extend(write_type_str_len.to_be_bytes());
605        bytes.extend(write_type_str.as_bytes());
606
607        let error: Error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
608
609        assert_eq!(
610            error.error,
611            DbError::WriteTimeout {
612                consistency: Consistency::Quorum,
613                received: -5, // Allow negative values when they don't make sense, it's better than crashing with ProtocolError
614                required: 100,
615                write_type: WriteType::Simple,
616            }
617        );
618        assert_eq!(error.reason, "message 2");
619    }
620
621    #[test]
622    fn deserialize_read_timeout() {
623        let features = ProtocolFeatures::default();
624
625        let mut bytes = make_error_request_bytes(0x1200, "message 2");
626        bytes.extend(0x0002_i16.to_be_bytes());
627        bytes.extend(8_i32.to_be_bytes());
628        bytes.extend(32_i32.to_be_bytes());
629        bytes.push(0_u8);
630
631        let error: Error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
632
633        assert_eq!(
634            error.error,
635            DbError::ReadTimeout {
636                consistency: Consistency::Two,
637                received: 8,
638                required: 32,
639                data_present: false,
640            }
641        );
642        assert_eq!(error.reason, "message 2");
643    }
644
645    #[test]
646    fn deserialize_read_failure() {
647        let features = ProtocolFeatures::default();
648
649        let mut bytes = make_error_request_bytes(0x1300, "message 2");
650        bytes.extend(0x0003_i16.to_be_bytes());
651        bytes.extend(4_i32.to_be_bytes());
652        bytes.extend(5_i32.to_be_bytes());
653        bytes.extend(6_i32.to_be_bytes());
654        bytes.push(123_u8); // Any non-zero value means data_present is true
655
656        let error: Error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
657
658        assert_eq!(
659            error.error,
660            DbError::ReadFailure {
661                consistency: Consistency::Three,
662                received: 4,
663                required: 5,
664                numfailures: 6,
665                data_present: true,
666            }
667        );
668        assert_eq!(error.reason, "message 2");
669    }
670
671    #[test]
672    fn deserialize_function_failure() {
673        let features = ProtocolFeatures::default();
674
675        let mut bytes = make_error_request_bytes(0x1400, "message 2");
676
677        let keyspace_name: &str = "keyspace_name";
678        let keyspace_name_len: u16 = keyspace_name.len().try_into().unwrap();
679
680        let function_name: &str = "function_name";
681        let function_name_len: u16 = function_name.len().try_into().unwrap();
682
683        let type1: &str = "type1";
684        let type1_len: u16 = type1.len().try_into().unwrap();
685
686        let type2: &str = "type2";
687        let type2_len: u16 = type1.len().try_into().unwrap();
688
689        bytes.extend(keyspace_name_len.to_be_bytes());
690        bytes.extend(keyspace_name.as_bytes());
691        bytes.extend(function_name_len.to_be_bytes());
692        bytes.extend(function_name.as_bytes());
693        bytes.extend(2_i16.to_be_bytes());
694        bytes.extend(type1_len.to_be_bytes());
695        bytes.extend(type1.as_bytes());
696        bytes.extend(type2_len.to_be_bytes());
697        bytes.extend(type2.as_bytes());
698
699        let error: Error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
700
701        assert_eq!(
702            error.error,
703            DbError::FunctionFailure {
704                keyspace: "keyspace_name".to_string(),
705                function: "function_name".to_string(),
706                arg_types: vec!["type1".to_string(), "type2".to_string()]
707            }
708        );
709        assert_eq!(error.reason, "message 2");
710    }
711
712    #[test]
713    fn deserialize_write_failure() {
714        let features = ProtocolFeatures::default();
715
716        let mut bytes = make_error_request_bytes(0x1500, "message 2");
717
718        bytes.extend(0x0000_i16.to_be_bytes());
719        bytes.extend(2_i32.to_be_bytes());
720        bytes.extend(4_i32.to_be_bytes());
721        bytes.extend(8_i32.to_be_bytes());
722
723        let write_type_str = "COUNTER";
724        let write_type_str_len: u16 = write_type_str.len().try_into().unwrap();
725        bytes.extend(write_type_str_len.to_be_bytes());
726        bytes.extend(write_type_str.as_bytes());
727
728        let error: Error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
729
730        assert_eq!(
731            error.error,
732            DbError::WriteFailure {
733                consistency: Consistency::Any,
734                received: 2,
735                required: 4,
736                numfailures: 8,
737                write_type: WriteType::Counter,
738            }
739        );
740        assert_eq!(error.reason, "message 2");
741    }
742
743    #[test]
744    fn deserialize_already_exists() {
745        let features = ProtocolFeatures::default();
746
747        let mut bytes = make_error_request_bytes(0x2400, "message 2");
748
749        let keyspace_name: &str = "keyspace_name";
750        let keyspace_name_len: u16 = keyspace_name.len().try_into().unwrap();
751
752        let table_name: &str = "table_name";
753        let table_name_len: u16 = table_name.len().try_into().unwrap();
754
755        bytes.extend(keyspace_name_len.to_be_bytes());
756        bytes.extend(keyspace_name.as_bytes());
757        bytes.extend(table_name_len.to_be_bytes());
758        bytes.extend(table_name.as_bytes());
759
760        let error: Error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
761
762        assert_eq!(
763            error.error,
764            DbError::AlreadyExists {
765                keyspace: "keyspace_name".to_string(),
766                table: "table_name".to_string(),
767            }
768        );
769        assert_eq!(error.reason, "message 2");
770    }
771
772    #[test]
773    fn deserialize_unprepared() {
774        let features = ProtocolFeatures::default();
775
776        let mut bytes = make_error_request_bytes(0x2500, "message 3");
777        let statement_id = b"deadbeef";
778        bytes.extend((statement_id.len() as i16).to_be_bytes());
779        bytes.extend(statement_id);
780
781        let error: Error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
782
783        assert_eq!(
784            error.error,
785            DbError::Unprepared {
786                statement_id: Bytes::from_static(b"deadbeef")
787            }
788        );
789        assert_eq!(error.reason, "message 3");
790    }
791
792    #[test]
793    fn deserialize_rate_limit_error() {
794        let features = ProtocolFeatures {
795            rate_limit_error: Some(0x4321),
796            ..Default::default()
797        };
798        let mut bytes = make_error_request_bytes(0x4321, "message 1");
799        bytes.extend([0u8]); // Read type
800        bytes.extend([1u8]); // Rejected by coordinator
801        let error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
802
803        assert_eq!(
804            error.error,
805            DbError::RateLimitReached {
806                op_type: OperationType::Read,
807                rejected_by_coordinator: true,
808            }
809        );
810        assert_eq!(error.reason, "message 1");
811
812        let features = ProtocolFeatures {
813            rate_limit_error: Some(0x8765),
814            ..Default::default()
815        };
816        let mut bytes = make_error_request_bytes(0x8765, "message 2");
817        bytes.extend([1u8]); // Write type
818        bytes.extend([0u8]); // Not rejected by coordinator
819        let error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
820
821        assert_eq!(
822            error.error,
823            DbError::RateLimitReached {
824                op_type: OperationType::Write,
825                rejected_by_coordinator: false,
826            }
827        );
828        assert_eq!(error.reason, "message 2");
829    }
830}