1use crate::types::FromSqlError;
2use crate::types::Type;
3use crate::{Result, errmsg_to_string, ffi};
4use std::error;
5use std::ffi::{NulError, c_char, c_int};
6use std::fmt;
7use std::path::PathBuf;
8use std::str;
9
10macro_rules! cfg_select {
12 ({ $($tt:tt)* }) => {{
13 $crate::cfg_select! { $($tt)* }
14 }};
15 (_ => { $($output:tt)* }) => {
16 $($output)*
17 };
18 (
19 $cfg:meta => $output:tt
20 $($( $rest:tt )+)?
21 ) => {{
22 #[cfg($cfg)]
23 cfg_select! { _ => $output }
24 $(
25 #[cfg(not($cfg))]
26 cfg_select! { $($rest)+ }
27 )?
28 }}
29}
30
31#[derive(Debug)]
33#[non_exhaustive]
34pub enum Error {
35 SqliteFailure(ffi::Error, Option<String>),
37
38 SqliteSingleThreadedMode,
41
42 FromSqlConversionFailure(usize, Type, Box<dyn error::Error + Send + Sync + 'static>),
45
46 IntegralValueOutOfRange(usize, i64),
51
52 Utf8Error(usize, str::Utf8Error),
54
55 NulError(NulError),
58
59 InvalidParameterName(String),
62
63 InvalidPath(PathBuf),
65
66 ExecuteReturnedResults,
69
70 QueryReturnedNoRows,
73
74 QueryReturnedMoreThanOneRow,
77
78 InvalidColumnIndex(usize),
81
82 InvalidColumnName(String),
85
86 InvalidColumnType(usize, String, Type),
90
91 StatementChangedRows(usize),
94
95 #[cfg(feature = "functions")]
99 InvalidFunctionParameterType(usize, Type),
100 #[cfg(feature = "vtab")]
103 InvalidFilterParameterType(usize, Type),
104
105 #[cfg(feature = "functions")]
108 UserFunctionError(Box<dyn error::Error + Send + Sync + 'static>),
109
110 ToSqlConversionFailure(Box<dyn error::Error + Send + Sync + 'static>),
113
114 InvalidQuery,
116
117 #[cfg(feature = "vtab")]
120 ModuleError(String),
121
122 UnwindingPanic,
124
125 #[cfg(feature = "functions")]
130 GetAuxWrongType,
131
132 MultipleStatement,
134 InvalidParameterCount(usize, usize),
138
139 #[cfg(feature = "blob")]
144 BlobSizeError,
145 SqlInputError {
147 error: ffi::Error,
149 msg: String,
151 sql: String,
153 offset: c_int,
155 },
156 #[cfg(feature = "loadable_extension")]
158 InitError(ffi::InitError),
159 InvalidDatabaseIndex(usize),
162}
163
164impl PartialEq for Error {
165 fn eq(&self, other: &Self) -> bool {
166 match (self, other) {
167 (Self::SqliteFailure(e1, s1), Self::SqliteFailure(e2, s2)) => e1 == e2 && s1 == s2,
168 (Self::SqliteSingleThreadedMode, Self::SqliteSingleThreadedMode) => true,
169 (Self::IntegralValueOutOfRange(i1, n1), Self::IntegralValueOutOfRange(i2, n2)) => {
170 i1 == i2 && n1 == n2
171 }
172 (Self::Utf8Error(i1, e1), Self::Utf8Error(i2, e2)) => i1 == i2 && e1 == e2,
173 (Self::NulError(e1), Self::NulError(e2)) => e1 == e2,
174 (Self::InvalidParameterName(n1), Self::InvalidParameterName(n2)) => n1 == n2,
175 (Self::InvalidPath(p1), Self::InvalidPath(p2)) => p1 == p2,
176 (Self::ExecuteReturnedResults, Self::ExecuteReturnedResults) => true,
177 (Self::QueryReturnedNoRows, Self::QueryReturnedNoRows) => true,
178 (Self::QueryReturnedMoreThanOneRow, Self::QueryReturnedMoreThanOneRow) => true,
179 (Self::InvalidColumnIndex(i1), Self::InvalidColumnIndex(i2)) => i1 == i2,
180 (Self::InvalidColumnName(n1), Self::InvalidColumnName(n2)) => n1 == n2,
181 (Self::InvalidColumnType(i1, n1, t1), Self::InvalidColumnType(i2, n2, t2)) => {
182 i1 == i2 && t1 == t2 && n1 == n2
183 }
184 (Self::StatementChangedRows(n1), Self::StatementChangedRows(n2)) => n1 == n2,
185 #[cfg(feature = "functions")]
186 (
187 Self::InvalidFunctionParameterType(i1, t1),
188 Self::InvalidFunctionParameterType(i2, t2),
189 ) => i1 == i2 && t1 == t2,
190 #[cfg(feature = "vtab")]
191 (
192 Self::InvalidFilterParameterType(i1, t1),
193 Self::InvalidFilterParameterType(i2, t2),
194 ) => i1 == i2 && t1 == t2,
195 (Self::InvalidQuery, Self::InvalidQuery) => true,
196 #[cfg(feature = "vtab")]
197 (Self::ModuleError(s1), Self::ModuleError(s2)) => s1 == s2,
198 (Self::UnwindingPanic, Self::UnwindingPanic) => true,
199 #[cfg(feature = "functions")]
200 (Self::GetAuxWrongType, Self::GetAuxWrongType) => true,
201 (Self::InvalidParameterCount(i1, n1), Self::InvalidParameterCount(i2, n2)) => {
202 i1 == i2 && n1 == n2
203 }
204 #[cfg(feature = "blob")]
205 (Self::BlobSizeError, Self::BlobSizeError) => true,
206 (
207 Self::SqlInputError {
208 error: e1,
209 msg: m1,
210 sql: s1,
211 offset: o1,
212 },
213 Self::SqlInputError {
214 error: e2,
215 msg: m2,
216 sql: s2,
217 offset: o2,
218 },
219 ) => e1 == e2 && m1 == m2 && s1 == s2 && o1 == o2,
220 #[cfg(feature = "loadable_extension")]
221 (Self::InitError(e1), Self::InitError(e2)) => e1 == e2,
222 (Self::InvalidDatabaseIndex(i1), Self::InvalidDatabaseIndex(i2)) => i1 == i2,
223 (..) => false,
224 }
225 }
226}
227
228impl From<str::Utf8Error> for Error {
229 #[cold]
230 fn from(err: str::Utf8Error) -> Self {
231 Self::Utf8Error(UNKNOWN_COLUMN, err)
232 }
233}
234
235impl From<NulError> for Error {
236 #[cold]
237 fn from(err: NulError) -> Self {
238 Self::NulError(err)
239 }
240}
241
242const UNKNOWN_COLUMN: usize = usize::MAX;
243
244impl From<FromSqlError> for Error {
247 #[cold]
248 fn from(err: FromSqlError) -> Self {
249 match err {
252 FromSqlError::OutOfRange(val) => Self::IntegralValueOutOfRange(UNKNOWN_COLUMN, val),
253 FromSqlError::InvalidBlobSize { .. } => {
254 Self::FromSqlConversionFailure(UNKNOWN_COLUMN, Type::Blob, Box::new(err))
255 }
256 FromSqlError::Other(source) => {
257 Self::FromSqlConversionFailure(UNKNOWN_COLUMN, Type::Null, source)
258 }
259 _ => Self::FromSqlConversionFailure(UNKNOWN_COLUMN, Type::Null, Box::new(err)),
260 }
261 }
262}
263
264#[cfg(feature = "loadable_extension")]
265impl From<ffi::InitError> for Error {
266 #[cold]
267 fn from(err: ffi::InitError) -> Self {
268 Self::InitError(err)
269 }
270}
271
272impl fmt::Display for Error {
273 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
274 match *self {
275 Self::SqliteFailure(ref err, None) => err.fmt(f),
276 Self::SqliteFailure(_, Some(ref s)) => write!(f, "{s}"),
277 Self::SqliteSingleThreadedMode => write!(
278 f,
279 "SQLite was compiled or configured for single-threaded use only"
280 ),
281 Self::FromSqlConversionFailure(i, ref t, ref err) => {
282 if i == UNKNOWN_COLUMN {
283 err.fmt(f)
284 } else {
285 write!(f, "Conversion error from type {t} at index: {i}, {err}")
286 }
287 }
288 Self::IntegralValueOutOfRange(col, val) => {
289 if col == UNKNOWN_COLUMN {
290 write!(f, "Integer {val} out of range")
291 } else {
292 write!(f, "Integer {val} out of range at index {col}")
293 }
294 }
295 Self::Utf8Error(col, ref err) => {
296 if col == UNKNOWN_COLUMN {
297 err.fmt(f)
298 } else {
299 write!(f, "{err} at index {col}")
300 }
301 }
302 Self::NulError(ref err) => err.fmt(f),
303 Self::InvalidParameterName(ref name) => write!(f, "Invalid parameter name: {name}"),
304 Self::InvalidPath(ref p) => write!(f, "Invalid path: {}", p.to_string_lossy()),
305 Self::ExecuteReturnedResults => {
306 write!(f, "Execute returned results - did you mean to call query?")
307 }
308 Self::QueryReturnedNoRows => write!(f, "Query returned no rows"),
309 Self::QueryReturnedMoreThanOneRow => write!(f, "Query returned more than one row"),
310 Self::InvalidColumnIndex(i) => write!(f, "Invalid column index: {i}"),
311 Self::InvalidColumnName(ref name) => write!(f, "Invalid column name: {name}"),
312 Self::InvalidColumnType(i, ref name, ref t) => {
313 write!(f, "Invalid column type {t} at index: {i}, name: {name}")
314 }
315 Self::InvalidParameterCount(i1, n1) => write!(
316 f,
317 "Wrong number of parameters passed to query. Got {i1}, needed {n1}"
318 ),
319 Self::StatementChangedRows(i) => write!(f, "Query changed {i} rows"),
320
321 #[cfg(feature = "functions")]
322 Self::InvalidFunctionParameterType(i, ref t) => {
323 write!(f, "Invalid function parameter type {t} at index {i}")
324 }
325 #[cfg(feature = "vtab")]
326 Self::InvalidFilterParameterType(i, ref t) => {
327 write!(f, "Invalid filter parameter type {t} at index {i}")
328 }
329 #[cfg(feature = "functions")]
330 Self::UserFunctionError(ref err) => err.fmt(f),
331 Self::ToSqlConversionFailure(ref err) => err.fmt(f),
332 Self::InvalidQuery => write!(f, "Query is not read-only"),
333 #[cfg(feature = "vtab")]
334 Self::ModuleError(ref desc) => write!(f, "{desc}"),
335 Self::UnwindingPanic => write!(f, "unwinding panic"),
336 #[cfg(feature = "functions")]
337 Self::GetAuxWrongType => write!(f, "get_aux called with wrong type"),
338 Self::MultipleStatement => write!(f, "Multiple statements provided"),
339 #[cfg(feature = "blob")]
340 Self::BlobSizeError => "Blob size is insufficient".fmt(f),
341 Self::SqlInputError {
342 ref msg,
343 offset,
344 ref sql,
345 ..
346 } => write!(f, "{msg} in {sql} at offset {offset}"),
347 #[cfg(feature = "loadable_extension")]
348 Self::InitError(ref err) => err.fmt(f),
349 Self::InvalidDatabaseIndex(i) => write!(f, "Invalid database index: {i}"),
350 }
351 }
352}
353
354impl error::Error for Error {
355 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
356 match *self {
357 Self::SqliteFailure(ref err, _) => Some(err),
358 Self::Utf8Error(_, ref err) => Some(err),
359 Self::NulError(ref err) => Some(err),
360
361 Self::IntegralValueOutOfRange(..)
362 | Self::SqliteSingleThreadedMode
363 | Self::InvalidParameterName(_)
364 | Self::ExecuteReturnedResults
365 | Self::QueryReturnedNoRows
366 | Self::QueryReturnedMoreThanOneRow
367 | Self::InvalidColumnIndex(_)
368 | Self::InvalidColumnName(_)
369 | Self::InvalidColumnType(..)
370 | Self::InvalidPath(_)
371 | Self::InvalidParameterCount(..)
372 | Self::StatementChangedRows(_)
373 | Self::InvalidQuery
374 | Self::MultipleStatement => None,
375
376 #[cfg(feature = "functions")]
377 Self::InvalidFunctionParameterType(..) => None,
378 #[cfg(feature = "vtab")]
379 Self::InvalidFilterParameterType(..) => None,
380
381 #[cfg(feature = "functions")]
382 Self::UserFunctionError(ref err) => Some(&**err),
383
384 Self::FromSqlConversionFailure(_, _, ref err)
385 | Self::ToSqlConversionFailure(ref err) => Some(&**err),
386
387 #[cfg(feature = "vtab")]
388 Self::ModuleError(_) => None,
389
390 Self::UnwindingPanic => None,
391
392 #[cfg(feature = "functions")]
393 Self::GetAuxWrongType => None,
394
395 #[cfg(feature = "blob")]
396 Self::BlobSizeError => None,
397 Self::SqlInputError { ref error, .. } => Some(error),
398 #[cfg(feature = "loadable_extension")]
399 Self::InitError(ref err) => Some(err),
400 Self::InvalidDatabaseIndex(_) => None,
401 }
402 }
403}
404
405impl Error {
406 #[inline]
408 #[must_use]
409 pub fn sqlite_error(&self) -> Option<&ffi::Error> {
410 match self {
411 Self::SqliteFailure(error, _) => Some(error),
412 _ => None,
413 }
414 }
415
416 #[inline]
419 #[must_use]
420 pub fn sqlite_error_code(&self) -> Option<ffi::ErrorCode> {
421 self.sqlite_error().map(|error| error.code)
422 }
423
424 #[inline]
427 #[must_use]
428 pub fn sqlite_extended_error_code(&self) -> Option<c_int> {
429 self.sqlite_error().map(|error| error.extended_code)
430 }
431}
432
433#[cold]
436pub fn error_from_sqlite_code(code: c_int, message: Option<String>) -> Error {
437 Error::SqliteFailure(ffi::Error::new(code), message)
438}
439
440macro_rules! err {
441 ($code:expr $(,)?) => {
442 $crate::error::error_from_sqlite_code($code, None)
443 };
444 ($code:expr, $msg:literal $(,)?) => {
445 $crate::error::error_from_sqlite_code($code, Some(format!($msg)))
446 };
447 ($code:expr, $err:expr $(,)?) => {
448 $crate::error::error_from_sqlite_code($code, Some(format!($err)))
449 };
450 ($code:expr, $fmt:expr, $($arg:tt)*) => {
451 $crate::error::error_from_sqlite_code($code, Some(format!($fmt, $($arg)*)))
452 };
453}
454
455#[cold]
456pub unsafe fn error_from_handle(db: *mut ffi::sqlite3, code: c_int) -> Error {
457 error_from_sqlite_code(code, unsafe { error_msg(db, code) })
458}
459
460unsafe fn error_msg(db: *mut ffi::sqlite3, code: c_int) -> Option<String> {
461 unsafe {
462 if db.is_null() || ffi::sqlite3_errcode(db) != code {
463 let err_str = ffi::sqlite3_errstr(code);
464 if err_str.is_null() {
465 None
466 } else {
467 Some(errmsg_to_string(err_str))
468 }
469 } else {
470 Some(errmsg_to_string(ffi::sqlite3_errmsg(db)))
471 }
472 }
473}
474
475pub unsafe fn decode_result_raw(db: *mut ffi::sqlite3, code: c_int) -> Result<()> {
476 if code == ffi::SQLITE_OK {
477 Ok(())
478 } else {
479 Err(unsafe { error_from_handle(db, code) })
480 }
481}
482
483#[cold]
484#[allow(unused_variables)]
485pub unsafe fn error_with_offset(db: *mut ffi::sqlite3, code: c_int, sql: &str) -> Error {
486 unsafe {
487 if db.is_null() {
488 error_from_sqlite_code(code, None)
489 } else {
490 let error = ffi::Error::new(code);
491 let msg = error_msg(db, code);
492 if ffi::ErrorCode::Unknown == error.code {
493 let offset = ffi::sqlite3_error_offset(db);
494 if offset >= 0 {
495 return Error::SqlInputError {
496 error,
497 msg: msg.unwrap_or("error".to_owned()),
498 sql: sql.to_owned(),
499 offset,
500 };
501 }
502 }
503 Error::SqliteFailure(error, msg)
504 }
505 }
506}
507
508pub fn check(code: c_int) -> Result<()> {
509 if code == ffi::SQLITE_OK {
510 Ok(())
511 } else {
512 Err(error_from_sqlite_code(code, None))
513 }
514}
515
516pub unsafe fn to_sqlite_error(e: &Error, err_msg: *mut *mut c_char) -> c_int {
520 use crate::util::alloc;
521
522 match e {
523 Error::SqliteFailure(err, s) => {
524 if let Some(s) = s {
525 unsafe { *err_msg = alloc(s) };
526 }
527 err.extended_code
528 }
529 err => {
530 unsafe { *err_msg = alloc(&err.to_string()) };
531 ffi::SQLITE_ERROR
532 }
533 }
534}
535
536#[cfg(feature = "modern_sqlite")] pub unsafe fn set_errmsg(
541 db: *mut ffi::sqlite3,
542 code: c_int,
543 msg: Option<&std::ffi::CStr>,
544) -> Result<()> {
545 unsafe {
546 decode_result_raw(
547 db,
548 ffi::sqlite3_set_errmsg(
549 db,
550 code,
551 msg.map_or(std::ptr::null(), std::ffi::CStr::as_ptr),
552 ),
553 )
554 }
555}