1use crate::types::FromSqlError;
2use crate::types::Type;
3use crate::{errmsg_to_string, ffi, Result};
4use std::error;
5use std::fmt;
6use std::os::raw::c_int;
7use std::path::PathBuf;
8use std::str;
9
10#[derive(Debug)]
12#[allow(clippy::enum_variant_names)]
13#[non_exhaustive]
14pub enum Error {
15 SqliteFailure(ffi::Error, Option<String>),
17
18 SqliteSingleThreadedMode,
21
22 FromSqlConversionFailure(usize, Type, Box<dyn error::Error + Send + Sync + 'static>),
25
26 IntegralValueOutOfRange(usize, i64),
31
32 Utf8Error(str::Utf8Error),
34
35 NulError(std::ffi::NulError),
38
39 InvalidParameterName(String),
42
43 InvalidPath(PathBuf),
45
46 ExecuteReturnedResults,
49
50 QueryReturnedNoRows,
53
54 InvalidColumnIndex(usize),
57
58 InvalidColumnName(String),
61
62 InvalidColumnType(usize, String, Type),
66
67 StatementChangedRows(usize),
70
71 #[cfg(feature = "functions")]
75 #[cfg_attr(docsrs, doc(cfg(feature = "functions")))]
76 InvalidFunctionParameterType(usize, Type),
77 #[cfg(feature = "vtab")]
80 #[cfg_attr(docsrs, doc(cfg(feature = "vtab")))]
81 InvalidFilterParameterType(usize, Type),
82
83 #[cfg(feature = "functions")]
86 #[cfg_attr(docsrs, doc(cfg(feature = "functions")))]
87 #[allow(dead_code)]
88 UserFunctionError(Box<dyn error::Error + Send + Sync + 'static>),
89
90 ToSqlConversionFailure(Box<dyn error::Error + Send + Sync + 'static>),
93
94 InvalidQuery,
96
97 #[cfg(feature = "vtab")]
100 #[cfg_attr(docsrs, doc(cfg(feature = "vtab")))]
101 #[allow(dead_code)]
102 ModuleError(String),
103
104 #[cfg(feature = "functions")]
106 #[cfg_attr(docsrs, doc(cfg(feature = "functions")))]
107 UnwindingPanic,
108
109 #[cfg(feature = "functions")]
114 #[cfg_attr(docsrs, doc(cfg(feature = "functions")))]
115 GetAuxWrongType,
116
117 MultipleStatement,
119 InvalidParameterCount(usize, usize),
123
124 #[cfg(feature = "blob")]
129 #[cfg_attr(docsrs, doc(cfg(feature = "blob")))]
130 BlobSizeError,
131 #[cfg(feature = "modern_sqlite")] #[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))]
134 SqlInputError {
135 error: ffi::Error,
137 msg: String,
139 sql: String,
141 offset: c_int,
143 },
144 #[cfg(feature = "loadable_extension")]
146 #[cfg_attr(docsrs, doc(cfg(feature = "loadable_extension")))]
147 InitError(ffi::InitError),
148}
149
150impl PartialEq for Error {
151 fn eq(&self, other: &Error) -> bool {
152 match (self, other) {
153 (Error::SqliteFailure(e1, s1), Error::SqliteFailure(e2, s2)) => e1 == e2 && s1 == s2,
154 (Error::SqliteSingleThreadedMode, Error::SqliteSingleThreadedMode) => true,
155 (Error::IntegralValueOutOfRange(i1, n1), Error::IntegralValueOutOfRange(i2, n2)) => {
156 i1 == i2 && n1 == n2
157 }
158 (Error::Utf8Error(e1), Error::Utf8Error(e2)) => e1 == e2,
159 (Error::NulError(e1), Error::NulError(e2)) => e1 == e2,
160 (Error::InvalidParameterName(n1), Error::InvalidParameterName(n2)) => n1 == n2,
161 (Error::InvalidPath(p1), Error::InvalidPath(p2)) => p1 == p2,
162 (Error::ExecuteReturnedResults, Error::ExecuteReturnedResults) => true,
163 (Error::QueryReturnedNoRows, Error::QueryReturnedNoRows) => true,
164 (Error::InvalidColumnIndex(i1), Error::InvalidColumnIndex(i2)) => i1 == i2,
165 (Error::InvalidColumnName(n1), Error::InvalidColumnName(n2)) => n1 == n2,
166 (Error::InvalidColumnType(i1, n1, t1), Error::InvalidColumnType(i2, n2, t2)) => {
167 i1 == i2 && t1 == t2 && n1 == n2
168 }
169 (Error::StatementChangedRows(n1), Error::StatementChangedRows(n2)) => n1 == n2,
170 #[cfg(feature = "functions")]
171 (
172 Error::InvalidFunctionParameterType(i1, t1),
173 Error::InvalidFunctionParameterType(i2, t2),
174 ) => i1 == i2 && t1 == t2,
175 #[cfg(feature = "vtab")]
176 (
177 Error::InvalidFilterParameterType(i1, t1),
178 Error::InvalidFilterParameterType(i2, t2),
179 ) => i1 == i2 && t1 == t2,
180 (Error::InvalidQuery, Error::InvalidQuery) => true,
181 #[cfg(feature = "vtab")]
182 (Error::ModuleError(s1), Error::ModuleError(s2)) => s1 == s2,
183 #[cfg(feature = "functions")]
184 (Error::UnwindingPanic, Error::UnwindingPanic) => true,
185 #[cfg(feature = "functions")]
186 (Error::GetAuxWrongType, Error::GetAuxWrongType) => true,
187 (Error::InvalidParameterCount(i1, n1), Error::InvalidParameterCount(i2, n2)) => {
188 i1 == i2 && n1 == n2
189 }
190 #[cfg(feature = "blob")]
191 (Error::BlobSizeError, Error::BlobSizeError) => true,
192 #[cfg(feature = "modern_sqlite")]
193 (
194 Error::SqlInputError {
195 error: e1,
196 msg: m1,
197 sql: s1,
198 offset: o1,
199 },
200 Error::SqlInputError {
201 error: e2,
202 msg: m2,
203 sql: s2,
204 offset: o2,
205 },
206 ) => e1 == e2 && m1 == m2 && s1 == s2 && o1 == o2,
207 #[cfg(feature = "loadable_extension")]
208 (Error::InitError(e1), Error::InitError(e2)) => e1 == e2,
209 (..) => false,
210 }
211 }
212}
213
214impl From<str::Utf8Error> for Error {
215 #[cold]
216 fn from(err: str::Utf8Error) -> Error {
217 Error::Utf8Error(err)
218 }
219}
220
221impl From<std::ffi::NulError> for Error {
222 #[cold]
223 fn from(err: std::ffi::NulError) -> Error {
224 Error::NulError(err)
225 }
226}
227
228const UNKNOWN_COLUMN: usize = usize::MAX;
229
230impl From<FromSqlError> for Error {
233 #[cold]
234 fn from(err: FromSqlError) -> Error {
235 match err {
238 FromSqlError::OutOfRange(val) => Error::IntegralValueOutOfRange(UNKNOWN_COLUMN, val),
239 FromSqlError::InvalidBlobSize { .. } => {
240 Error::FromSqlConversionFailure(UNKNOWN_COLUMN, Type::Blob, Box::new(err))
241 }
242 FromSqlError::Other(source) => {
243 Error::FromSqlConversionFailure(UNKNOWN_COLUMN, Type::Null, source)
244 }
245 _ => Error::FromSqlConversionFailure(UNKNOWN_COLUMN, Type::Null, Box::new(err)),
246 }
247 }
248}
249
250#[cfg(feature = "loadable_extension")]
251impl From<ffi::InitError> for Error {
252 #[cold]
253 fn from(err: ffi::InitError) -> Error {
254 Error::InitError(err)
255 }
256}
257
258impl fmt::Display for Error {
259 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
260 match *self {
261 Error::SqliteFailure(ref err, None) => err.fmt(f),
262 Error::SqliteFailure(_, Some(ref s)) => write!(f, "{s}"),
263 Error::SqliteSingleThreadedMode => write!(
264 f,
265 "SQLite was compiled or configured for single-threaded use only"
266 ),
267 Error::FromSqlConversionFailure(i, ref t, ref err) => {
268 if i != UNKNOWN_COLUMN {
269 write!(f, "Conversion error from type {t} at index: {i}, {err}")
270 } else {
271 err.fmt(f)
272 }
273 }
274 Error::IntegralValueOutOfRange(col, val) => {
275 if col != UNKNOWN_COLUMN {
276 write!(f, "Integer {val} out of range at index {col}")
277 } else {
278 write!(f, "Integer {val} out of range")
279 }
280 }
281 Error::Utf8Error(ref err) => err.fmt(f),
282 Error::NulError(ref err) => err.fmt(f),
283 Error::InvalidParameterName(ref name) => write!(f, "Invalid parameter name: {name}"),
284 Error::InvalidPath(ref p) => write!(f, "Invalid path: {}", p.to_string_lossy()),
285 Error::ExecuteReturnedResults => {
286 write!(f, "Execute returned results - did you mean to call query?")
287 }
288 Error::QueryReturnedNoRows => write!(f, "Query returned no rows"),
289 Error::InvalidColumnIndex(i) => write!(f, "Invalid column index: {i}"),
290 Error::InvalidColumnName(ref name) => write!(f, "Invalid column name: {name}"),
291 Error::InvalidColumnType(i, ref name, ref t) => {
292 write!(f, "Invalid column type {t} at index: {i}, name: {name}")
293 }
294 Error::InvalidParameterCount(i1, n1) => write!(
295 f,
296 "Wrong number of parameters passed to query. Got {i1}, needed {n1}"
297 ),
298 Error::StatementChangedRows(i) => write!(f, "Query changed {i} rows"),
299
300 #[cfg(feature = "functions")]
301 Error::InvalidFunctionParameterType(i, ref t) => {
302 write!(f, "Invalid function parameter type {t} at index {i}")
303 }
304 #[cfg(feature = "vtab")]
305 Error::InvalidFilterParameterType(i, ref t) => {
306 write!(f, "Invalid filter parameter type {t} at index {i}")
307 }
308 #[cfg(feature = "functions")]
309 Error::UserFunctionError(ref err) => err.fmt(f),
310 Error::ToSqlConversionFailure(ref err) => err.fmt(f),
311 Error::InvalidQuery => write!(f, "Query is not read-only"),
312 #[cfg(feature = "vtab")]
313 Error::ModuleError(ref desc) => write!(f, "{desc}"),
314 #[cfg(feature = "functions")]
315 Error::UnwindingPanic => write!(f, "unwinding panic"),
316 #[cfg(feature = "functions")]
317 Error::GetAuxWrongType => write!(f, "get_aux called with wrong type"),
318 Error::MultipleStatement => write!(f, "Multiple statements provided"),
319 #[cfg(feature = "blob")]
320 Error::BlobSizeError => "Blob size is insufficient".fmt(f),
321 #[cfg(feature = "modern_sqlite")]
322 Error::SqlInputError {
323 ref msg,
324 offset,
325 ref sql,
326 ..
327 } => write!(f, "{msg} in {sql} at offset {offset}"),
328 #[cfg(feature = "loadable_extension")]
329 Error::InitError(ref err) => err.fmt(f),
330 }
331 }
332}
333
334impl error::Error for Error {
335 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
336 match *self {
337 Error::SqliteFailure(ref err, _) => Some(err),
338 Error::Utf8Error(ref err) => Some(err),
339 Error::NulError(ref err) => Some(err),
340
341 Error::IntegralValueOutOfRange(..)
342 | Error::SqliteSingleThreadedMode
343 | Error::InvalidParameterName(_)
344 | Error::ExecuteReturnedResults
345 | Error::QueryReturnedNoRows
346 | Error::InvalidColumnIndex(_)
347 | Error::InvalidColumnName(_)
348 | Error::InvalidColumnType(..)
349 | Error::InvalidPath(_)
350 | Error::InvalidParameterCount(..)
351 | Error::StatementChangedRows(_)
352 | Error::InvalidQuery
353 | Error::MultipleStatement => None,
354
355 #[cfg(feature = "functions")]
356 Error::InvalidFunctionParameterType(..) => None,
357 #[cfg(feature = "vtab")]
358 Error::InvalidFilterParameterType(..) => None,
359
360 #[cfg(feature = "functions")]
361 Error::UserFunctionError(ref err) => Some(&**err),
362
363 Error::FromSqlConversionFailure(_, _, ref err)
364 | Error::ToSqlConversionFailure(ref err) => Some(&**err),
365
366 #[cfg(feature = "vtab")]
367 Error::ModuleError(_) => None,
368
369 #[cfg(feature = "functions")]
370 Error::UnwindingPanic => None,
371
372 #[cfg(feature = "functions")]
373 Error::GetAuxWrongType => None,
374
375 #[cfg(feature = "blob")]
376 Error::BlobSizeError => None,
377 #[cfg(feature = "modern_sqlite")]
378 Error::SqlInputError { ref error, .. } => Some(error),
379 #[cfg(feature = "loadable_extension")]
380 Error::InitError(ref err) => Some(err),
381 }
382 }
383}
384
385impl Error {
386 #[inline]
388 #[must_use]
389 pub fn sqlite_error(&self) -> Option<&ffi::Error> {
390 match self {
391 Self::SqliteFailure(error, _) => Some(error),
392 _ => None,
393 }
394 }
395
396 #[inline]
399 #[must_use]
400 pub fn sqlite_error_code(&self) -> Option<ffi::ErrorCode> {
401 self.sqlite_error().map(|error| error.code)
402 }
403}
404
405#[cold]
408pub fn error_from_sqlite_code(code: c_int, message: Option<String>) -> Error {
409 Error::SqliteFailure(ffi::Error::new(code), message)
410}
411
412#[cold]
413pub unsafe fn error_from_handle(db: *mut ffi::sqlite3, code: c_int) -> Error {
414 let message = if db.is_null() {
415 None
416 } else {
417 Some(errmsg_to_string(ffi::sqlite3_errmsg(db)))
418 };
419 error_from_sqlite_code(code, message)
420}
421
422#[cold]
423#[cfg(not(feature = "modern_sqlite"))] pub unsafe fn error_with_offset(db: *mut ffi::sqlite3, code: c_int, _sql: &str) -> Error {
425 error_from_handle(db, code)
426}
427
428#[cold]
429#[cfg(feature = "modern_sqlite")] pub unsafe fn error_with_offset(db: *mut ffi::sqlite3, code: c_int, sql: &str) -> Error {
431 if db.is_null() {
432 error_from_sqlite_code(code, None)
433 } else {
434 let error = ffi::Error::new(code);
435 let msg = errmsg_to_string(ffi::sqlite3_errmsg(db));
436 if ffi::ErrorCode::Unknown == error.code {
437 let offset = ffi::sqlite3_error_offset(db);
438 if offset >= 0 {
439 return Error::SqlInputError {
440 error,
441 msg,
442 sql: sql.to_owned(),
443 offset,
444 };
445 }
446 }
447 Error::SqliteFailure(error, Some(msg))
448 }
449}
450
451pub fn check(code: c_int) -> Result<()> {
452 if code != crate::ffi::SQLITE_OK {
453 Err(error_from_sqlite_code(code, None))
454 } else {
455 Ok(())
456 }
457}
458
459pub unsafe fn to_sqlite_error(e: &Error, err_msg: *mut *mut std::os::raw::c_char) -> c_int {
463 use crate::util::alloc;
464 match e {
465 Error::SqliteFailure(err, s) => {
466 if let Some(s) = s {
467 *err_msg = alloc(s);
468 }
469 err.extended_code
470 }
471 err => {
472 *err_msg = alloc(&err.to_string());
473 ffi::SQLITE_ERROR
474 }
475 }
476}