1use thiserror::Error;
6
7#[derive(Error, Debug)]
9pub enum ExasolError {
10 #[error(transparent)]
12 Connection(#[from] ConnectionError),
13
14 #[error(transparent)]
16 Query(#[from] QueryError),
17
18 #[error(transparent)]
20 Conversion(#[from] ConversionError),
21
22 #[error(transparent)]
24 Transport(#[from] TransportError),
25}
26
27#[derive(Error, Debug)]
29pub enum ConnectionError {
30 #[error("Failed to connect to {host}:{port}: {message}")]
32 ConnectionFailed {
33 host: String,
34 port: u16,
35 message: String,
36 },
37
38 #[error("Authentication failed: {0}")]
40 AuthenticationFailed(String),
41
42 #[error("Invalid connection parameter '{parameter}': {message}")]
44 InvalidParameter { parameter: String, message: String },
45
46 #[error("Failed to parse connection string: {0}")]
48 ParseError(String),
49
50 #[error("Connection timeout after {timeout_ms}ms")]
52 Timeout { timeout_ms: u64 },
53
54 #[error("Connection is closed")]
56 ConnectionClosed,
57
58 #[error("TLS error: {0}")]
60 TlsError(String),
61}
62
63#[derive(Error, Debug)]
65pub enum QueryError {
66 #[error("SQL syntax error at position {position}: {message}")]
68 SyntaxError { position: usize, message: String },
69
70 #[error("Query execution failed: {0}")]
72 ExecutionFailed(String),
73
74 #[error("Query timeout after {timeout_ms}ms")]
76 Timeout { timeout_ms: u64 },
77
78 #[error("Invalid query state: {0}")]
80 InvalidState(String),
81
82 #[error("Parameter binding error for parameter {index}: {message}")]
84 ParameterBindingError { index: usize, message: String },
85
86 #[error("Result set not available: {0}")]
88 NoResultSet(String),
89
90 #[error("Transaction error: {0}")]
92 TransactionError(String),
93
94 #[error("Potential SQL injection detected")]
96 SqlInjectionDetected,
97
98 #[error("Prepared statement has been closed")]
100 StatementClosed,
101
102 #[error("Expected row count but received result set")]
104 UnexpectedResultSet,
105}
106
107#[derive(Error, Debug)]
109pub enum ConversionError {
110 #[error("Unsupported Exasol type: {exasol_type}")]
112 UnsupportedType { exasol_type: String },
113
114 #[error("Failed to convert value at row {row}, column {column}: {message}")]
116 ValueConversionFailed {
117 row: usize,
118 column: usize,
119 message: String,
120 },
121
122 #[error("Schema mismatch: {0}")]
124 SchemaMismatch(String),
125
126 #[error("Invalid data format: {0}")]
128 InvalidFormat(String),
129
130 #[error("Numeric overflow at row {row}, column {column}")]
132 NumericOverflow { row: usize, column: usize },
133
134 #[error("Invalid UTF-8 string at row {row}, column {column}")]
136 InvalidUtf8 { row: usize, column: usize },
137
138 #[error("Arrow error: {0}")]
140 ArrowError(String),
141}
142
143#[derive(Error, Debug)]
145pub enum TransportError {
146 #[error("WebSocket error: {0}")]
148 WebSocketError(String),
149
150 #[error("Serialization error: {0}")]
152 SerializationError(String),
153
154 #[error("Deserialization error: {0}")]
156 DeserializationError(String),
157
158 #[error("Protocol error: {0}")]
160 ProtocolError(String),
161
162 #[error("Invalid server response: {0}")]
164 InvalidResponse(String),
165
166 #[error("Network I/O error: {0}")]
168 IoError(String),
169
170 #[error("Failed to send message: {0}")]
172 SendError(String),
173
174 #[error("Failed to receive message: {0}")]
176 ReceiveError(String),
177
178 #[error("TLS error: {0}")]
180 TlsError(String),
181}
182
183impl From<arrow::error::ArrowError> for ConversionError {
185 fn from(err: arrow::error::ArrowError) -> Self {
186 ConversionError::ArrowError(err.to_string())
187 }
188}
189
190impl From<serde_json::Error> for TransportError {
191 fn from(err: serde_json::Error) -> Self {
192 TransportError::SerializationError(err.to_string())
193 }
194}
195
196#[cfg(feature = "websocket")]
197impl From<tokio_tungstenite::tungstenite::Error> for TransportError {
198 fn from(err: tokio_tungstenite::tungstenite::Error) -> Self {
199 TransportError::WebSocketError(err.to_string())
200 }
201}
202
203#[cfg(test)]
204mod tests {
205 use super::*;
206
207 #[test]
208 fn test_connection_error_display() {
209 let err = ConnectionError::ConnectionFailed {
210 host: "localhost".to_string(),
211 port: 8563,
212 message: "Connection refused".to_string(),
213 };
214 assert!(err.to_string().contains("localhost"));
215 assert!(err.to_string().contains("8563"));
216 }
217
218 #[test]
219 fn test_query_error_display() {
220 let err = QueryError::SyntaxError {
221 position: 10,
222 message: "Unexpected token".to_string(),
223 };
224 assert!(err.to_string().contains("position 10"));
225 }
226
227 #[test]
228 fn test_conversion_error_display() {
229 let err = ConversionError::ValueConversionFailed {
230 row: 5,
231 column: 2,
232 message: "Invalid number format".to_string(),
233 };
234 assert!(err.to_string().contains("row 5"));
235 assert!(err.to_string().contains("column 2"));
236 }
237
238 #[test]
239 fn test_transport_tls_error() {
240 let err = TransportError::TlsError("Certificate validation failed".to_string());
241 assert!(err.to_string().contains("TLS error"));
242 assert!(err.to_string().contains("Certificate validation failed"));
243 }
244
245 #[test]
246 fn test_statement_closed_error() {
247 let err = QueryError::StatementClosed;
248 assert!(err.to_string().contains("closed"));
249 }
250
251 #[test]
252 fn test_unexpected_result_set_error() {
253 let err = QueryError::UnexpectedResultSet;
254 assert!(err.to_string().contains("result set"));
255 }
256}