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 closed by server with reason
90    #[error("{0}")]
91    ConnectionClosedByServer(String),
92
93    /// Connection not ready for operations
94    #[error("connection not ready")]
95    ConnectionNotReady,
96
97    /// Cursor is closed
98    #[error("cursor is closed")]
99    CursorClosed,
100
101    /// Invalid cursor state
102    #[error("invalid cursor: {0}")]
103    InvalidCursor(String),
104
105    /// Connection timeout
106    #[error("connection timeout after {0:?}")]
107    ConnectionTimeout(std::time::Duration),
108
109    /// Invalid connection string
110    #[error("invalid connection string: {0}")]
111    InvalidConnectionString(String),
112
113    /// Invalid service name (ORA-12514)
114    #[error("invalid service name{}: {}",
115        service_name.as_ref().map(|s| format!(": {}", s)).unwrap_or_default(),
116        message.as_deref().unwrap_or("service not found"))]
117    InvalidServiceName {
118        service_name: Option<String>,
119        message: Option<String>,
120    },
121
122    /// Invalid SID (ORA-12505)
123    #[error("invalid SID{}: {}",
124        sid.as_ref().map(|s| format!(": {}", s)).unwrap_or_default(),
125        message.as_deref().unwrap_or("SID not found"))]
126    InvalidSid {
127        sid: Option<String>,
128        message: Option<String>,
129    },
130
131    // =========================================================================
132    // Authentication Errors
133    // =========================================================================
134    /// Authentication failed
135    #[error("authentication failed: {0}")]
136    AuthenticationFailed(String),
137
138    /// Invalid credentials
139    #[error("invalid username or password")]
140    InvalidCredentials,
141
142    /// Unsupported verifier type
143    #[error("unsupported verifier type: {0:#x}")]
144    UnsupportedVerifierType(u32),
145
146    // =========================================================================
147    // Database Errors
148    // =========================================================================
149    /// Oracle database error with error code
150    #[error("ORA-{code:05}: {message}")]
151    OracleError { code: u32, message: String },
152
153    /// SQL execution error
154    #[error("SQL execution error: {0}")]
155    SqlError(String),
156
157    /// No data found
158    #[error("no data found")]
159    NoDataFound,
160
161    /// Server error with code and message
162    #[error("server error ({code}): {message}")]
163    ServerError { code: u32, message: String },
164
165    // =========================================================================
166    // Data Type Errors
167    // =========================================================================
168    /// Invalid data type
169    #[error("invalid data type: {0}")]
170    InvalidDataType(u16),
171
172    /// Invalid Oracle type number
173    #[error("invalid Oracle type: {0}")]
174    InvalidOracleType(u8),
175
176    /// Data conversion error
177    #[error("data conversion error: {0}")]
178    DataConversionError(String),
179
180    /// NULL value encountered where not expected
181    #[error("unexpected NULL value")]
182    UnexpectedNull,
183
184    // =========================================================================
185    // I/O Errors
186    // =========================================================================
187    /// Underlying I/O error
188    #[error("I/O error: {0}")]
189    Io(#[from] io::Error),
190
191    // =========================================================================
192    // Feature Errors
193    // =========================================================================
194    /// Feature not supported
195    #[error("feature not supported: {0}")]
196    FeatureNotSupported(String),
197
198    /// Native network encryption required but not supported
199    #[error("native network encryption and data integrity is required but not supported")]
200    NativeNetworkEncryptionRequired,
201
202    // =========================================================================
203    // Internal Errors
204    // =========================================================================
205    /// Internal error (should not happen)
206    #[error("internal error: {0}")]
207    Internal(String),
208}
209
210impl Error {
211    /// Create a new Oracle database error
212    pub fn oracle(code: u32, message: impl Into<String>) -> Self {
213        Error::OracleError {
214            code,
215            message: message.into(),
216        }
217    }
218
219    /// Check if this is a "no data found" error
220    pub fn is_no_data_found(&self) -> bool {
221        matches!(self, Error::NoDataFound)
222            || matches!(self, Error::OracleError { code, .. } if *code == crate::constants::error_code::NO_DATA_FOUND)
223    }
224
225    /// Check if this is a connection-related error
226    pub fn is_connection_error(&self) -> bool {
227        matches!(
228            self,
229            Error::ConnectionRefused { .. }
230                | Error::ConnectionClosed
231                | Error::ConnectionClosedByServer(_)
232                | Error::ConnectionTimeout(_)
233                | Error::Io(_)
234        )
235    }
236
237    /// Check if this error is recoverable (can retry)
238    pub fn is_recoverable(&self) -> bool {
239        matches!(self, Error::ConnectionTimeout(_) | Error::ConnectionClosed)
240    }
241}
242
243#[cfg(test)]
244mod tests {
245    use super::*;
246
247    #[test]
248    fn test_oracle_error_display() {
249        let err = Error::oracle(1017, "invalid username/password");
250        assert_eq!(
251            err.to_string(),
252            "ORA-01017: invalid username/password"
253        );
254    }
255
256    #[test]
257    fn test_is_no_data_found() {
258        assert!(Error::NoDataFound.is_no_data_found());
259        assert!(Error::oracle(1403, "no data found").is_no_data_found());
260        assert!(!Error::oracle(1017, "test").is_no_data_found());
261    }
262
263    #[test]
264    fn test_is_connection_error() {
265        assert!(Error::ConnectionClosed.is_connection_error());
266        assert!(Error::ConnectionRefused {
267            error_code: Some(12514),
268            message: Some("test".to_string()),
269        }
270        .is_connection_error());
271        assert!(!Error::NoDataFound.is_connection_error());
272    }
273}