oracle_rs/
error.rs

1//! Error types for the Oracle driver
2//!
3//! This module defines all error types that can occur during Oracle database
4//! operations, from low-level protocol errors to high-level connection errors.
5
6use std::io;
7use thiserror::Error;
8
9/// Result type alias using our Error type
10pub type Result<T> = std::result::Result<T, Error>;
11
12/// Main error type for the Oracle driver
13#[derive(Error, Debug)]
14#[allow(missing_docs)]
15pub enum Error {
16    // =========================================================================
17    // Protocol Errors
18    // =========================================================================
19    /// Invalid packet type received
20    #[error("invalid packet type: {0}")]
21    InvalidPacketType(u8),
22
23    /// Invalid message type received
24    #[error("invalid message type: {0}")]
25    InvalidMessageType(u8),
26
27    /// Packet too short to contain valid header
28    #[error("packet too short: expected at least {expected} bytes, got {actual}")]
29    PacketTooShort { expected: usize, actual: usize },
30
31    /// Unexpected packet type received
32    #[error("unexpected packet type: expected {expected:?}, got {actual:?}")]
33    UnexpectedPacketType {
34        expected: crate::constants::PacketType,
35        actual: crate::constants::PacketType,
36    },
37
38    /// Protocol version not supported
39    #[error("server protocol version {0} not supported (minimum: {1})")]
40    ProtocolVersionNotSupported(u16, u16),
41
42    /// General protocol error
43    #[error("protocol error: {0}")]
44    Protocol(String),
45
46    /// Protocol error (alternate form)
47    #[error("protocol error: {0}")]
48    ProtocolError(String),
49
50    // =========================================================================
51    // Buffer Errors
52    // =========================================================================
53    /// Buffer underflow - not enough data to read
54    #[error("buffer underflow: need {needed} bytes but only {available} available")]
55    BufferUnderflow { needed: usize, available: usize },
56
57    /// Buffer overflow - not enough space to write
58    #[error("buffer overflow: need {needed} bytes but only {available} available")]
59    BufferOverflow { needed: usize, available: usize },
60
61    /// Invalid length indicator
62    #[error("invalid length indicator: {0}")]
63    InvalidLengthIndicator(u8),
64
65    // =========================================================================
66    // Connection Errors
67    // =========================================================================
68    /// Connection refused by server
69    #[error("connection refused{}: {}",
70        error_code.map(|c| format!(" (error {})", c)).unwrap_or_default(),
71        message.as_deref().unwrap_or("unknown reason"))]
72    ConnectionRefused {
73        error_code: Option<u32>,
74        message: Option<String>,
75    },
76
77    /// Connection redirected
78    #[error("connection redirected to: {address}")]
79    ConnectionRedirected { address: String },
80
81    /// Connection redirect (simple form)
82    #[error("connection redirected to: {0}")]
83    ConnectionRedirect(String),
84
85    /// Connection closed unexpectedly
86    #[error("connection closed unexpectedly")]
87    ConnectionClosed,
88
89    /// Connection not ready for operations
90    #[error("connection not ready")]
91    ConnectionNotReady,
92
93    /// Cursor is closed
94    #[error("cursor is closed")]
95    CursorClosed,
96
97    /// Invalid cursor state
98    #[error("invalid cursor: {0}")]
99    InvalidCursor(String),
100
101    /// Connection timeout
102    #[error("connection timeout after {0:?}")]
103    ConnectionTimeout(std::time::Duration),
104
105    /// Invalid connection string
106    #[error("invalid connection string: {0}")]
107    InvalidConnectionString(String),
108
109    /// Invalid service name (ORA-12514)
110    #[error("invalid service name{}: {}",
111        service_name.as_ref().map(|s| format!(": {}", s)).unwrap_or_default(),
112        message.as_deref().unwrap_or("service not found"))]
113    InvalidServiceName {
114        service_name: Option<String>,
115        message: Option<String>,
116    },
117
118    /// Invalid SID (ORA-12505)
119    #[error("invalid SID{}: {}",
120        sid.as_ref().map(|s| format!(": {}", s)).unwrap_or_default(),
121        message.as_deref().unwrap_or("SID not found"))]
122    InvalidSid {
123        sid: Option<String>,
124        message: Option<String>,
125    },
126
127    // =========================================================================
128    // Authentication Errors
129    // =========================================================================
130    /// Authentication failed
131    #[error("authentication failed: {0}")]
132    AuthenticationFailed(String),
133
134    /// Invalid credentials
135    #[error("invalid username or password")]
136    InvalidCredentials,
137
138    /// Unsupported verifier type
139    #[error("unsupported verifier type: {0:#x}")]
140    UnsupportedVerifierType(u32),
141
142    // =========================================================================
143    // Database Errors
144    // =========================================================================
145    /// Oracle database error with error code
146    #[error("ORA-{code:05}: {message}")]
147    OracleError { code: u32, message: String },
148
149    /// SQL execution error
150    #[error("SQL execution error: {0}")]
151    SqlError(String),
152
153    /// No data found
154    #[error("no data found")]
155    NoDataFound,
156
157    /// Server error with code and message
158    #[error("server error ({code}): {message}")]
159    ServerError { code: u32, message: String },
160
161    // =========================================================================
162    // Data Type Errors
163    // =========================================================================
164    /// Invalid data type
165    #[error("invalid data type: {0}")]
166    InvalidDataType(u16),
167
168    /// Invalid Oracle type number
169    #[error("invalid Oracle type: {0}")]
170    InvalidOracleType(u8),
171
172    /// Data conversion error
173    #[error("data conversion error: {0}")]
174    DataConversionError(String),
175
176    /// NULL value encountered where not expected
177    #[error("unexpected NULL value")]
178    UnexpectedNull,
179
180    // =========================================================================
181    // I/O Errors
182    // =========================================================================
183    /// Underlying I/O error
184    #[error("I/O error: {0}")]
185    Io(#[from] io::Error),
186
187    // =========================================================================
188    // Feature Errors
189    // =========================================================================
190    /// Feature not supported
191    #[error("feature not supported: {0}")]
192    FeatureNotSupported(String),
193
194    /// Native network encryption required but not supported
195    #[error("native network encryption and data integrity is required but not supported")]
196    NativeNetworkEncryptionRequired,
197
198    // =========================================================================
199    // Internal Errors
200    // =========================================================================
201    /// Internal error (should not happen)
202    #[error("internal error: {0}")]
203    Internal(String),
204}
205
206impl Error {
207    /// Create a new Oracle database error
208    pub fn oracle(code: u32, message: impl Into<String>) -> Self {
209        Error::OracleError {
210            code,
211            message: message.into(),
212        }
213    }
214
215    /// Check if this is a "no data found" error
216    pub fn is_no_data_found(&self) -> bool {
217        matches!(self, Error::NoDataFound)
218            || matches!(self, Error::OracleError { code, .. } if *code == crate::constants::error_code::NO_DATA_FOUND)
219    }
220
221    /// Check if this is a connection-related error
222    pub fn is_connection_error(&self) -> bool {
223        matches!(
224            self,
225            Error::ConnectionRefused { .. }
226                | Error::ConnectionClosed
227                | Error::ConnectionTimeout(_)
228                | Error::Io(_)
229        )
230    }
231
232    /// Check if this error is recoverable (can retry)
233    pub fn is_recoverable(&self) -> bool {
234        matches!(self, Error::ConnectionTimeout(_) | Error::ConnectionClosed)
235    }
236}
237
238#[cfg(test)]
239mod tests {
240    use super::*;
241
242    #[test]
243    fn test_oracle_error_display() {
244        let err = Error::oracle(1017, "invalid username/password");
245        assert_eq!(
246            err.to_string(),
247            "ORA-01017: invalid username/password"
248        );
249    }
250
251    #[test]
252    fn test_is_no_data_found() {
253        assert!(Error::NoDataFound.is_no_data_found());
254        assert!(Error::oracle(1403, "no data found").is_no_data_found());
255        assert!(!Error::oracle(1017, "test").is_no_data_found());
256    }
257
258    #[test]
259    fn test_is_connection_error() {
260        assert!(Error::ConnectionClosed.is_connection_error());
261        assert!(Error::ConnectionRefused {
262            error_code: Some(12514),
263            message: Some("test".to_string()),
264        }
265        .is_connection_error());
266        assert!(!Error::NoDataFound.is_connection_error());
267    }
268}