Skip to content

Simplify error using thiserror crate #7974

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ arrow-schema = { version = "48.0.0", default-features = false }
parquet = { version = "48.0.0", features = ["arrow", "async", "object_store"] }
sqlparser = { version = "0.38.0", features = ["visitor"] }
chrono = { version = "0.4.31", default-features = false }
thiserror = { version = "1.0.44" }

[profile.release]
codegen-units = 1
Expand Down
1 change: 1 addition & 0 deletions datafusion-cli/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions datafusion/common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ object_store = { version = "0.7.0", default-features = false, optional = true }
parquet = { workspace = true, optional = true }
pyo3 = { version = "0.20.0", optional = true }
sqlparser = { workspace = true }
thiserror = { workspace = true }

[dev-dependencies]
rand = "0.8.4"
177 changes: 45 additions & 132 deletions datafusion/common/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,56 +44,90 @@ pub type SharedResult<T> = result::Result<T, Arc<DataFusionError>>;
pub type GenericError = Box<dyn Error + Send + Sync>;

/// DataFusion error
#[derive(Debug)]
#[derive(Debug, thiserror::Error)]
pub enum DataFusionError {
/// Error returned by arrow.
ArrowError(ArrowError),
#[error("Arrow error: {0}")]
ArrowError(#[from] ArrowError),

/// Wraps an error from the Parquet crate
#[cfg(feature = "parquet")]
ParquetError(ParquetError),
#[error("Parquet error: {0}")]
ParquetError(#[from] ParquetError),

/// Wraps an error from the Avro crate
#[cfg(feature = "avro")]
AvroError(AvroError),
#[error("Avro error: {0}")]
AvroError(#[from] AvroError),

/// Wraps an error from the object_store crate
#[cfg(feature = "object_store")]
#[error("Object Store error: {0}")]
ObjectStore(object_store::Error),

/// Error associated to I/O operations and associated traits.
IoError(io::Error),
#[error("IO error: {0}")]
IoError(#[from] io::Error),

/// Error returned when SQL is syntactically incorrect.
SQL(ParserError),
#[error("SQL error: {0:?}")]
SQL(#[from] ParserError),

/// Error returned on a branch that we know it is possible
/// but to which we still have no implementation for.
/// Often, these errors are tracked in our issue tracker.
#[error("This feature is not implemented: {0}")]
NotImplemented(String),

/// Error returned as a consequence of an error in DataFusion.
/// This error should not happen in normal usage of DataFusion.
///
/// DataFusions has internal invariants that the compiler is not
/// always able to check. This error is raised when one of those
/// invariants is not verified during execution.
#[error(
"Internal error: {0}.\nThis was likely caused by a bug in DataFusion's \
code and we would welcome that you file an bug report in our issue tracker"
)]
Internal(String),

/// This error happens whenever a plan is not valid. Examples include
/// impossible casts.
#[error("Error during planning: {0}")]
Plan(String),

/// This error happens when an invalid or unsupported option is passed
/// in a SQL statement
#[error("Invalid or Unsupported Configuration: {0}")]
Configuration(String),

/// This error happens with schema-related errors, such as schema inference not possible
/// and non-unique column names.
SchemaError(SchemaError),
#[error("Schema error: {0}")]
SchemaError(#[from] SchemaError),

/// Error returned during execution of the query.
/// Examples include files not found, errors in parsing certain types.
#[error("Execution error: {0}")]
Execution(String),

/// This error is thrown when a consumer cannot acquire memory from the Memory Manager
/// we can just cancel the execution of the partition.
#[error("Resources exhausted: {0}")]
ResourcesExhausted(String),

/// Errors originating from outside DataFusion's core codebase.
/// For example, a custom S3Error from the crate datafusion-objectstore-s3
External(GenericError),
#[error("External error: {0}")]
External(#[from] GenericError),

/// Error with additional context
Context(String, Box<DataFusionError>),
#[error("{0}\ncaused by\n{1}")]
Context(String, #[source] Box<DataFusionError>),

/// Errors originating from either mapping LogicalPlans to/from Substrait plans
/// or serializing/deserializing protobytes to Substrait plans
#[error("Substrait error: {0}")]
Substrait(String),
}

Expand Down Expand Up @@ -215,18 +249,6 @@ impl From<std::fmt::Error> for DataFusionError {
}
}

impl From<io::Error> for DataFusionError {
fn from(e: io::Error) -> Self {
DataFusionError::IoError(e)
}
}

impl From<ArrowError> for DataFusionError {
fn from(e: ArrowError) -> Self {
DataFusionError::ArrowError(e)
}
}

impl From<DataFusionError> for ArrowError {
fn from(e: DataFusionError) -> Self {
match e {
Expand All @@ -237,20 +259,6 @@ impl From<DataFusionError> for ArrowError {
}
}

#[cfg(feature = "parquet")]
impl From<ParquetError> for DataFusionError {
fn from(e: ParquetError) -> Self {
DataFusionError::ParquetError(e)
}
}

#[cfg(feature = "avro")]
impl From<AvroError> for DataFusionError {
fn from(e: AvroError) -> Self {
DataFusionError::AvroError(e)
}
}

#[cfg(feature = "object_store")]
impl From<object_store::Error> for DataFusionError {
fn from(e: object_store::Error) -> Self {
Expand All @@ -265,103 +273,6 @@ impl From<object_store::path::Error> for DataFusionError {
}
}

impl From<ParserError> for DataFusionError {
fn from(e: ParserError) -> Self {
DataFusionError::SQL(e)
}
}

impl From<GenericError> for DataFusionError {
fn from(err: GenericError) -> Self {
DataFusionError::External(err)
}
}

impl Display for DataFusionError {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
match *self {
DataFusionError::ArrowError(ref desc) => {
write!(f, "Arrow error: {desc}")
}
#[cfg(feature = "parquet")]
DataFusionError::ParquetError(ref desc) => {
write!(f, "Parquet error: {desc}")
}
#[cfg(feature = "avro")]
DataFusionError::AvroError(ref desc) => {
write!(f, "Avro error: {desc}")
}
DataFusionError::IoError(ref desc) => {
write!(f, "IO error: {desc}")
}
DataFusionError::SQL(ref desc) => {
write!(f, "SQL error: {desc:?}")
}
DataFusionError::Configuration(ref desc) => {
write!(f, "Invalid or Unsupported Configuration: {desc}")
}
DataFusionError::NotImplemented(ref desc) => {
write!(f, "This feature is not implemented: {desc}")
}
DataFusionError::Internal(ref desc) => {
write!(f, "Internal error: {desc}.\nThis was likely caused by a bug in DataFusion's \
code and we would welcome that you file an bug report in our issue tracker")
}
DataFusionError::Plan(ref desc) => {
write!(f, "Error during planning: {desc}")
}
DataFusionError::SchemaError(ref desc) => {
write!(f, "Schema error: {desc}")
}
DataFusionError::Execution(ref desc) => {
write!(f, "Execution error: {desc}")
}
DataFusionError::ResourcesExhausted(ref desc) => {
write!(f, "Resources exhausted: {desc}")
}
DataFusionError::External(ref desc) => {
write!(f, "External error: {desc}")
}
#[cfg(feature = "object_store")]
DataFusionError::ObjectStore(ref desc) => {
write!(f, "Object Store error: {desc}")
}
DataFusionError::Context(ref desc, ref err) => {
write!(f, "{}\ncaused by\n{}", desc, *err)
}
DataFusionError::Substrait(ref desc) => {
write!(f, "Substrait error: {desc}")
}
}
}
}

impl Error for DataFusionError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
DataFusionError::ArrowError(e) => Some(e),
#[cfg(feature = "parquet")]
DataFusionError::ParquetError(e) => Some(e),
#[cfg(feature = "avro")]
DataFusionError::AvroError(e) => Some(e),
#[cfg(feature = "object_store")]
DataFusionError::ObjectStore(e) => Some(e),
DataFusionError::IoError(e) => Some(e),
DataFusionError::SQL(e) => Some(e),
DataFusionError::NotImplemented(_) => None,
DataFusionError::Internal(_) => None,
DataFusionError::Configuration(_) => None,
DataFusionError::Plan(_) => None,
DataFusionError::SchemaError(e) => Some(e),
DataFusionError::Execution(_) => None,
DataFusionError::ResourcesExhausted(_) => None,
DataFusionError::External(e) => Some(e.as_ref()),
DataFusionError::Context(_, e) => Some(e.as_ref()),
DataFusionError::Substrait(_) => None,
}
}
}

impl From<DataFusionError> for io::Error {
fn from(e: DataFusionError) -> Self {
io::Error::new(io::ErrorKind::Other, e)
Expand Down Expand Up @@ -398,6 +309,8 @@ impl DataFusionError {
// remember the lowest datafusion error so far
if let Some(e) = root_error.downcast_ref::<DataFusionError>() {
last_datafusion_error = e;
} else if let Some(e) = root_error.downcast_ref::<Box<DataFusionError>>() {
last_datafusion_error = e;
} else if let Some(e) = root_error.downcast_ref::<Arc<DataFusionError>>() {
// As `Arc<T>::source()` calls through to `T::source()` we need to
// explicitly match `Arc<DataFusionError>` to capture it
Expand Down