#![allow(dead_code)]
use crate::ffi::{duckdb_type, Error as FFIError};
use std::{error, fmt, path::PathBuf, result, str};
#[derive(Debug)]
pub enum DuckDBConversionError {
TypeMismatch {
expected: duckdb_type,
found: duckdb_type,
},
ConversionError(String),
NullValue,
PrecisionLoss(String),
}
#[derive(Debug)]
#[allow(clippy::enum_variant_names)]
#[non_exhaustive]
pub enum Error {
DuckDBFailure(FFIError, Option<String>),
IntegralValueOutOfRange(usize, i128),
Utf8Error(str::Utf8Error),
NulError(::std::ffi::NulError),
InvalidParameterName(String),
InvalidPath(PathBuf),
ExecuteReturnedResults,
QueryReturnedNoRows,
InvalidColumnIndex(usize),
InvalidColumnName(String),
StatementChangedRows(usize),
ToSqlConversionFailure(Box<dyn error::Error + Send + Sync + 'static>),
InvalidQuery,
MultipleStatement,
InvalidParameterCount(usize, usize),
AppendError,
ConversionError(DuckDBConversionError),
#[allow(non_camel_case_types)]
UNKNOWN(Box<dyn ::std::error::Error + Send + Sync + 'static>),
BackgroundTaskFailed(String),
Pool(String),
}
pub type Result<T, E = Error> = result::Result<T, E>;
impl PartialEq for Error {
fn eq(
&self,
other: &Error,
) -> bool {
match (self, other) {
(Error::DuckDBFailure(e1, s1), Error::DuckDBFailure(e2, s2)) => e1 == e2 && s1 == s2,
(Error::IntegralValueOutOfRange(i1, n1), Error::IntegralValueOutOfRange(i2, n2)) => {
i1 == i2 && n1 == n2
},
(Error::Utf8Error(e1), Error::Utf8Error(e2)) => e1 == e2,
(Error::NulError(e1), Error::NulError(e2)) => e1 == e2,
(Error::InvalidParameterName(n1), Error::InvalidParameterName(n2)) => n1 == n2,
(Error::InvalidPath(p1), Error::InvalidPath(p2)) => p1 == p2,
(Error::ExecuteReturnedResults, Error::ExecuteReturnedResults) => true,
(Error::QueryReturnedNoRows, Error::QueryReturnedNoRows) => true,
(Error::InvalidColumnIndex(i1), Error::InvalidColumnIndex(i2)) => i1 == i2,
(Error::InvalidColumnName(n1), Error::InvalidColumnName(n2)) => n1 == n2,
(Error::StatementChangedRows(n1), Error::StatementChangedRows(n2)) => n1 == n2,
(Error::InvalidParameterCount(i1, n1), Error::InvalidParameterCount(i2, n2)) => {
i1 == i2 && n1 == n2
},
(..) => false,
}
}
}
impl From<str::Utf8Error> for Error {
#[cold]
fn from(err: str::Utf8Error) -> Error {
Error::Utf8Error(err)
}
}
impl From<::std::ffi::NulError> for Error {
#[cold]
fn from(err: ::std::ffi::NulError) -> Error {
Error::NulError(err)
}
}
const UNKNOWN_COLUMN: usize = usize::MAX;
impl fmt::Display for Error {
fn fmt(
&self,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
match self {
Error::DuckDBFailure(ref err, None) => err.fmt(f),
Error::DuckDBFailure(_, Some(ref s)) => write!(f, "{s}"),
Error::IntegralValueOutOfRange(col, val) => {
if *col != UNKNOWN_COLUMN {
write!(f, "Integer {val} out of range at index {col}")
} else {
write!(f, "Integer {val} out of range")
}
},
Error::Utf8Error(ref err) => err.fmt(f),
Error::NulError(ref err) => err.fmt(f),
Error::InvalidParameterName(ref name) => write!(f, "Invalid parameter name: {name}"),
Error::InvalidPath(ref p) => write!(f, "Invalid path: {}", p.to_string_lossy()),
Error::ExecuteReturnedResults => {
write!(f, "Execute returned results - did you mean to call query?")
},
Error::QueryReturnedNoRows => write!(f, "Query returned no rows"),
Error::InvalidColumnIndex(i) => write!(f, "Invalid column index: {i}"),
Error::InvalidColumnName(ref name) => write!(f, "Invalid column name: {name}"),
Error::InvalidParameterCount(i1, n1) => {
write!(f, "Wrong number of parameters passed to query. Got {i1}, needed {n1}")
},
Error::StatementChangedRows(i) => write!(f, "Query changed {i} rows"),
Error::ToSqlConversionFailure(ref err) => err.fmt(f),
Error::InvalidQuery => write!(f, "Query is not read-only"),
Error::MultipleStatement => write!(f, "Multiple statements provided"),
Error::AppendError => write!(f, "Append error"),
Error::ConversionError(ref err) => match err {
DuckDBConversionError::TypeMismatch { expected, found } => {
write!(f, "Type mismatch: expected {expected}, found {found}")
},
DuckDBConversionError::ConversionError(ref msg) => {
write!(f, "Conversion error: {msg}")
},
DuckDBConversionError::NullValue => write!(f, "Null value encountered"),
DuckDBConversionError::PrecisionLoss(ref msg) => write!(f, "Precision loss: {msg}"),
},
Error::UNKNOWN(e) => write!(f, "Unknown error: {e}"),
Error::BackgroundTaskFailed(ref msg) => write!(f, "Background task failed: {msg}"),
Error::Pool(ref msg) => write!(f, "Connection pool error: {msg}"),
}
}
}
impl error::Error for Error {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
Error::DuckDBFailure(ref err, _) => Some(err),
Error::Utf8Error(ref err) => Some(err),
Error::NulError(ref err) => Some(err),
Error::IntegralValueOutOfRange(..)
| Error::InvalidParameterName(_)
| Error::ExecuteReturnedResults
| Error::QueryReturnedNoRows
| Error::InvalidColumnIndex(_)
| Error::InvalidColumnName(_)
| Error::InvalidPath(_)
| Error::InvalidParameterCount(..)
| Error::StatementChangedRows(_)
| Error::InvalidQuery
| Error::AppendError
| Error::MultipleStatement
| Error::ConversionError(_) => None,
Error::ToSqlConversionFailure(ref err) => Some(&**err),
Error::UNKNOWN(e) => Some(e.as_ref()),
Error::BackgroundTaskFailed(_) | Error::Pool(_) => None,
}
}
}